xref: /illumos-gate/usr/src/cmd/lp/cmd/lpadmin/do_printer.c (revision 48bbca816818409505a6e214d0911fda44e622e3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
22 /*	  All Rights Reserved  	*/
23 
24 
25 /*
26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  * Copyright (c) 2016 by Delphix. All rights reserved.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <stdio.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <sys/types.h>
38 #include <sys/zone.h>
39 #include <stdlib.h>
40 #include <libintl.h>
41 #include <sys/tsol/label_macro.h>
42 #include <bsm/devices.h>
43 #include "lp.h"
44 #include "class.h"
45 #if defined PS_FAULTED
46 #undef	PS_FAULTED
47 #endif
48 #include "printers.h"
49 #include "msgs.h"
50 
51 #define	WHO_AM_I	I_AM_LPADMIN
52 #include	"oam.h"
53 
54 #include "lpadmin.h"
55 
56 extern	void	fromallclasses();
57 
58 #if     !defined(PATH_MAX)
59 # define PATH_MAX       1024
60 #endif
61 #if     PATH_MAX < 1024
62 # undef PATH_MAX
63 # define PATH_MAX       1024
64 #endif
65 
66 extern char		*label;
67 
68 static void		configure_printer();
69 static char		*fullpath();
70 char			*nameit();
71 static void		pack_white(char *ptr);
72 
73 /**
74  ** do_printer() - CREATE OR CHANGE PRINTER
75  **/
76 
77 void			do_printer ()
78 {
79 	int			rc;
80 
81 	/*
82 	 * Set or change the printer configuration.
83 	 */
84 	if (strlen(modifications))
85 		configure_printer (modifications);
86 
87 	/*
88 	 * Allow/deny forms.
89 	 */
90 	BEGIN_CRITICAL
91 		if (!oldp)
92 			if (allow_form_printer(getlist(NAME_NONE, "", ","), p) == -1) {
93 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
94 				done(1);
95 			}
96 
97 		if (f_allow || f_deny) {
98 			if (f_allow && allow_form_printer(f_allow, p) == -1) {
99 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
100 				done(1);
101 			}
102 
103 			if (f_deny && deny_form_printer(f_deny, p) == -1) {
104 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
105 				done(1);
106 			}
107 		}
108 	END_CRITICAL
109 
110 	/* Add/remove types of paper */
111 
112 	BEGIN_CRITICAL
113 		if (!oldp)
114 			if (add_paper_to_printer(getlist(NAME_NONE, "", ","),p) == -1) {
115 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
116 				done(1);
117 			}
118 
119 
120 			if (p_add && add_paper_to_printer(p_add, p) == -1) {
121 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
122 				done(1);
123 			}
124 
125 			if (p_remove && remove_paper_from_printer(p_remove, p) == -1) {
126 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
127 				done(1);
128 			}
129 	END_CRITICAL
130 
131 	/*
132 	 * Allow/deny users.
133 	 */
134 	BEGIN_CRITICAL
135 		if (!oldp)
136 			if (allow_user_printer(getlist(NAME_ALL, "", ","), p) == -1) {
137 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
138 				done(1);
139 			}
140 
141 		if (u_allow || u_deny) {
142 			if (u_allow && allow_user_printer(u_allow, p) == -1) {
143 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
144 				done(1);
145 			}
146 
147 			if (u_deny && deny_user_printer(u_deny, p) == -1) {
148 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
149 				done(1);
150 			}
151 		}
152 	END_CRITICAL
153 
154 	/*
155 	 * Tell the Spooler about the printer
156 	 */
157 	send_message(S_LOAD_PRINTER, p, "", "");
158 	rc = output(R_LOAD_PRINTER);
159 
160 	switch (rc) {
161 	case MOK:
162 		break;
163 
164 	case MNODEST:
165 	case MERRDEST:
166 		LP_ERRMSG (ERROR, E_ADM_ERRDEST);
167 		done (1);
168 		/*NOTREACHED*/
169 
170 	case MNOSPACE:
171 		LP_ERRMSG (WARNING, E_ADM_NOPSPACE);
172 		break;
173 
174 	case MNOPERM:	/* taken care of up front */
175 	default:
176 		LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc);
177 		done (1);
178 		/*NOTREACHED*/
179 	}
180 
181 	/*
182 	 * Now that the Spooler knows about the printer,
183 	 * we can do the balance of the changes.
184 	 */
185 
186 	/*
187 	 * Mount or unmount form, print-wheel.
188 	 */
189 	if (M)
190 		do_mount(p, (f? f : (char *)0), (S? *S : (char *)0));
191 	else if (t) do_max_trays(p);
192 
193 	/*
194 	 * Display the alert type.
195 	 */
196 	if (A && STREQU(A, NAME_LIST)) {
197 		if (label)
198 			(void) printf(gettext("Printer %s: "), label);
199 		printalert (stdout, &(oldp->fault_alert), 1);
200 	}
201 
202 	/*
203 	 * -A quiet.
204 	 */
205 	if (A && STREQU(A, NAME_QUIET)) {
206 
207 		send_message(S_QUIET_ALERT, p, (char *)QA_PRINTER, "");
208 		rc = output(R_QUIET_ALERT);
209 
210 		switch(rc) {
211 		case MOK:
212 			break;
213 
214 		case MNODEST:	/* not quite, but not a lie either */
215 		case MERRDEST:
216 			LP_ERRMSG1 (WARNING, E_LP_NOQUIET, p);
217 			break;
218 
219 		case MNOPERM:	/* taken care of up front */
220 		default:
221 			LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc);
222 			done (1);
223 			/*NOTREACHED*/
224 		}
225 	}
226 
227 	/*
228 	 * Add printer p to class c
229 	 */
230 	if (c)  {
231 		CLASS			*pc,
232 					clsbuf;
233 
234 		if (STREQU(c, NAME_ANY))
235 			c = NAME_ALL;
236 
237 Loop:		if (!(pc = getclass(c))) {
238 			if (STREQU(c, NAME_ALL))
239 				goto Done;
240 
241 			if (errno != ENOENT) {
242 				LP_ERRMSG2 (
243 					ERROR,
244 					E_LP_GETCLASS,
245 					c,
246 					PERROR
247 				);
248 				done (1);
249 			}
250 
251 			/*
252 			 * Create the class
253 			 */
254 			clsbuf.name = strdup(c);
255 			clsbuf.members = 0;
256 			if (addlist(&clsbuf.members, p) == -1) {
257 				LP_ERRMSG (ERROR, E_LP_MALLOC);
258 				done (1);
259 			}
260 			pc = &clsbuf;
261 
262 		} else if (searchlist(p, pc->members))
263 			LP_ERRMSG2 (WARNING, E_ADM_INCLASS, p, pc->name);
264 
265 		else if (addlist(&pc->members, p) == -1) {
266 			LP_ERRMSG (ERROR, E_LP_MALLOC);
267 			done (1);
268 		}
269 
270 		BEGIN_CRITICAL
271 			if (putclass(pc->name, pc) == -1) {
272 				LP_ERRMSG2 (
273 					ERROR,
274 					E_LP_PUTCLASS,
275 					pc->name,
276 					PERROR
277 				);
278 				done(1);
279 			}
280 		END_CRITICAL
281 
282 		send_message (S_LOAD_CLASS, pc->name);
283 		rc = output(R_LOAD_CLASS);
284 
285 		switch(rc) {
286 		case MOK:
287 			break;
288 
289 		case MNODEST:
290 		case MERRDEST:
291 			LP_ERRMSG (ERROR, E_ADM_ERRDEST);
292 			done (1);
293 			/*NOTREACHED*/
294 
295 		case MNOSPACE:
296 			LP_ERRMSG (WARNING, E_ADM_NOCSPACE);
297 			break;
298 
299 		case MNOPERM:	/* taken care of up front */
300 		default:
301 			LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc);
302 			done (1);
303 			/*NOTREACHED*/
304 		}
305 
306 		if (STREQU(c, NAME_ALL))
307 			goto Loop;
308 	}
309 Done:
310 	/*
311 	 * Remove printer p from class r
312 	 */
313 	if (r) {
314 		if (STREQU(r, NAME_ALL) || STREQU(r, NAME_ANY))
315 			fromallclasses(p);
316 		else
317 			fromclass(p, r);
318 	}
319 
320 	return;
321 }
322 
323 /**
324  ** configure_printer() - SET OR CHANGE CONFIGURATION OF PRINTER
325  **/
326 
327 static void		configure_printer (list)
328 	char			*list;
329 {
330 	register PRINTER	*prbufp;
331 
332 	PRINTER			printer_struct;
333 
334 	char			type;
335 	char *	infile_opts = NULL;
336 
337 
338 	if (oldp) {
339 
340 		prbufp = oldp;
341 
342 		if (!T)
343 			T = prbufp->printer_types;
344 
345 		if (!i && !e && !m)
346 			/*
347 			 * Don't copy the original interface program
348 			 * again, but do keep the name of the original.
349 			 */
350 			ignprinter = BAD_INTERFACE;
351 		else
352 			ignprinter = 0;
353 
354 		/*
355 		 * If we are making this a remote printer,
356 		 * make sure that local-only attributes are
357 		 * cleared.
358 		 */
359 		if (s) {
360 			prbufp->banner = 0;
361 			prbufp->cpi.val = 0;
362 			prbufp->cpi.sc = 0;
363 			prbufp->device = 0;
364 			prbufp->dial_info = 0;
365 			prbufp->fault_rec = 0;
366 			prbufp->interface = 0;
367 			prbufp->lpi.val = 0;
368 			prbufp->lpi.sc = 0;
369 			prbufp->plen.val = 0;
370 			prbufp->plen.sc = 0;
371 			prbufp->login = 0;
372 			prbufp->speed = 0;
373 			prbufp->stty = 0;
374 			prbufp->pwid.val = 0;
375 			prbufp->pwid.sc = 0;
376 			prbufp->fault_alert.shcmd = strdup(NAME_NONE);
377 			prbufp->fault_alert.Q = 0;
378 			prbufp->fault_alert.W = 0;
379 #if	defined(CAN_DO_MODULES)
380 			prbufp->modules = 0;
381 #endif
382 
383 		/*
384 		 * If we are making this a local printer, make
385 		 * sure that some local-only attributes are set.
386 		 * (If the user has specified these as well, their
387 		 * values will overwrite what we set here.)
388 		 */
389 		} else if (oldp->remote) {
390 			prbufp->banner = BAN_ALWAYS;
391 			prbufp->interface = makepath(Lp_Model, STANDARD, (char *)0);
392 			prbufp->fault_alert.shcmd = nameit(NAME_MAIL);
393 
394 			/*
395 			 * Being here means "!s && oldp->remote" is true,
396 			 * i.e. this printer never had an interface pgm
397 			 * before. Thus we can safely clear the following.
398 			 * This is needed to let "putprinter()" copy the
399 			 * (default) interface program.
400 			 */
401 			ignprinter = 0;
402 		}
403 
404 	} else {
405 		/*
406 		 * The following takes care of the lion's share
407 		 * of the initialization of a new printer structure.
408 		 * However, special initialization (e.g. non-zero,
409 		 * or substructure members) needs to be considered
410 		 * for EACH NEW MEMBER added to the structure.
411 		 */
412 		(void)memset (&printer_struct, 0, sizeof(printer_struct));
413 
414 		prbufp = &printer_struct;
415 		prbufp->banner = BAN_ALWAYS;
416 		prbufp->cpi.val = 0;
417 		prbufp->cpi.sc = 0;
418 		if (!s)
419 			prbufp->interface = makepath(Lp_Model, m, (char *)0);
420 		prbufp->lpi.val = 0;
421 		prbufp->lpi.sc = 0;
422 		prbufp->plen.val = 0;
423 		prbufp->plen.sc = 0;
424 		prbufp->pwid.val = 0;
425 		prbufp->pwid.sc = 0;
426 		if (!s && !A)
427 			prbufp->fault_alert.shcmd = nameit(NAME_MAIL);
428 		prbufp->fault_alert.Q = 0;
429 		prbufp->fault_alert.W = 0;
430 		prbufp->options = NULL;
431 	}
432 
433 	while ((type = *list++) != '\0')  switch(type) {
434 
435 	case 'A':
436 		if (!s) {
437 			if (STREQU(A, NAME_MAIL) || STREQU(A, NAME_WRITE))
438 				prbufp->fault_alert.shcmd = nameit(A);
439 			else if (!STREQU(A, NAME_QUIET))
440 				prbufp->fault_alert.shcmd = A;
441 		}
442 		break;
443 
444 	case 'b':
445 		if (!s)
446 			prbufp->banner = banner;
447 		break;
448 
449 	case 'c':
450 		if (!s)
451 			prbufp->cpi = cpi_sdn;
452 		break;
453 
454 	case 'D':
455 		prbufp->description = D;
456 		break;
457 
458 	case 'e':
459 		if (!s) {
460 			prbufp->interface = makepath(
461 				Lp_A_Interfaces,
462 				e,
463 				(char *)0
464 			);
465 		}
466 		break;
467 
468 	case 'F':
469 		if (!s)
470 			prbufp->fault_rec = F;
471 		break;
472 
473 #if	defined(CAN_DO_MODULES)
474 	case 'H':
475 		if (!s)
476 			prbufp->modules = H;
477 		break;
478 #endif
479 
480 	case 'h':
481 		if (!s)
482 			prbufp->login = 0;
483 		break;
484 
485 	case 'i':
486 		if (!s)
487 			prbufp->interface = fullpath(i);
488 		break;
489 
490 	case 'I':
491 		prbufp->input_types = I;
492 		break;
493 
494 	case 'l':
495 		if (!s)
496 			prbufp->login = 1;
497 		break;
498 
499 	case 'L':
500 		if (!s)
501 			prbufp->plen = length_sdn;
502 		break;
503 
504 	case 'm':
505 		if (!s)
506 			prbufp->interface = makepath(Lp_Model, m, (char *)0);
507 		break;
508 
509 	case 'M':
510 		if (!s)
511 			prbufp->lpi = lpi_sdn;
512 		break;
513 
514 #ifdef LP_USE_PAPI_ATTR
515 	case 'n':
516 	{
517 		if (n_opt != NULL)
518 		{
519 			if (*n_opt == '/')
520 			{
521 				prbufp->ppd = fullpath(n_opt);
522 			}
523 			else
524 			{
525 				prbufp->ppd =
526 				    makepath(Lp_Model, "ppd", n_opt, (char *)0);
527 			}
528 			ppdopt = 1;
529 		}
530 		break;
531 	}
532 #endif
533 
534 	case 'o':
535 		/*
536 		 * The "undefined" key-value -o options
537 		 *
538 		 * Options requires special handling. It is a
539 		 * list whose members are to be handled
540 		 * individually.
541 		 *
542 		 * Need to: set new options, keep old options if not
543 		 * redefined, remove old options if defined as "key=".
544 		 *
545 		 *
546 		 * "p" is a global containing the printer name
547 		 */
548 
549 		if (!s) {
550 
551 			if ((infile_opts = getpentry(p, PR_OPTIONS)) == NULL)
552 				prbufp->options = o_options;
553 			else {
554 				prbufp->options =
555 					pick_opts(infile_opts, o_options);
556 			}
557 		}
558 		break;
559 
560 	case 'R':
561 		if (s) {
562 			prbufp->remote = s;
563 			prbufp->dial_info = 0;
564 			prbufp->device = 0;
565 		} else
566 			prbufp->remote = 0;
567 		break;
568 
569 	case 's':
570 		if (!s) {
571 			/*
572 			 * lpadmin always defers to stty
573 			 */
574 			prbufp->speed = 0;
575 			prbufp->stty = stty_opt;
576 		}
577 		break;
578 
579 	case 'S':
580 		if (!M)
581 			if (STREQU(*S, NAME_NONE))
582 				prbufp->char_sets = 0;
583 			else
584 				prbufp->char_sets = S;
585 		break;
586 
587 	case 'T':
588 		prbufp->printer_types = T;
589 		break;
590 
591 	case 'U':
592 		if (!s) {
593 			prbufp->dial_info = U;
594 			prbufp->device = 0;
595 			prbufp->remote = 0;
596 		}
597 		break;
598 
599 	case 'v':
600 		if (!s) {
601 			prbufp->device = v;
602 			prbufp->dial_info = 0;
603 			prbufp->remote = 0;
604 		}
605 		break;
606 
607 	case 'w':
608 		if (!s)
609 			prbufp->pwid = width_sdn;
610 		break;
611 
612 	case 'W':
613 		if (!s)
614 			prbufp->fault_alert.W = W;
615 		break;
616 
617 	}
618 
619 
620 	BEGIN_CRITICAL
621 		if (putprinter(p, prbufp) == -1) {
622 			if (
623 				errno == EINVAL
624 			     && (badprinter & BAD_INTERFACE)
625 			)
626 				LP_ERRMSG1 (
627 					ERROR,
628 					E_ADM_BADINTF,
629 					prbufp->interface
630 				);
631 			else
632 				LP_ERRMSG2 (
633 					ERROR,
634 					E_LP_PUTPRINTER,
635 					p,
636 					PERROR
637 				);
638 			done(1);
639 		}
640 
641 		if ((getzoneid() == GLOBAL_ZONEID) && system_labeled &&
642 		    (prbufp->device != NULL))
643 			update_dev_dbs(p, prbufp->device, "ADD");
644 
645 	END_CRITICAL
646 
647 	return;
648 }
649 
650 /**
651  ** fullpath()
652  **/
653 
654 static char		*fullpath (str)
655 	char			*str;
656 {
657 	register char		*cur_dir,
658 				*path;
659 
660 
661 	while (*str && *str == ' ')
662 		str++;
663 	if (*str == '/')
664 		return (str);
665 
666 	if (!(cur_dir = malloc(PATH_MAX + 1)))
667 		return (str);
668 
669 	getcwd (cur_dir, PATH_MAX);
670 	path = makepath(cur_dir, str, (char *)0);
671 
672 	/*
673 	 * Here we could be nice and strip out /./ and /../
674 	 * stuff, but it isn't necessary.
675 	 */
676 
677 	return (path);
678 }
679 
680 /**
681  ** nameit() - ADD USER NAME TO COMMAND
682  **/
683 
684 char			*nameit (cmd)
685 	char			*cmd;
686 {
687 	register char		*nm = getname(),
688 				*copy = malloc(
689 					(unsigned) (strlen(cmd) + 1 +
690 					strlen(nm) + 1)
691 	);
692 
693 	(void) strcpy (copy, cmd);
694 	(void) strcat (copy, " ");
695 	(void) strcat (copy, nm);
696 	return (copy);
697 }
698 
699 /*
700  * update_dev_dbs - ADD/REMOVE ENTRIES FOR THE PRINTER IN DEVICE
701  * 			ALLOCATION FILES
702  *
703  * We intentionally ignore errors, since we don't want the printer
704  * installation to be viewed as failing just because we didn't add
705  * the device_allocate entry.
706  *
707  *	Input:
708  *		prtname - printer name
709  *		devname - device associated w/ this printer
710  *		func - [ADD|REMOVE] entries in /etc/security/device_allocate
711  *			and /etc/security/device_maps
712  *
713  *	Return:
714  *		Always 'quiet' return.  Failures are ignored.
715  */
716 void
717 update_dev_dbs(char *prtname, char *devname, char *func)
718 {
719 	int		fd, status;
720 	pid_t		pid;
721 
722 	pid = fork();
723 	switch (pid) {
724 	case -1:
725 		/* fork failed, just return quietly */
726 		return;
727 	case 0:
728 		/* child */
729 		/* redirect to /dev/null */
730 		(void) close(1);
731 		(void) close(2);
732 		fd = open("/dev/null", O_WRONLY);
733 		fd = dup(fd);
734 
735 		if (strcmp(func, "ADD") == 0) {
736 			execl("/usr/sbin/add_allocatable", "add_allocatable",
737 			    "-n", prtname, "-t", "lp", "-l", devname,
738 			    "-o", "minlabel=admin_low:maxlabel=admin_high",
739 			    "-a", "*", "-c", "/bin/true", NULL);
740 		} else {
741 			if (strcmp(func, "REMOVE") == 0) {
742 				execl("/usr/sbin/remove_allocatable",
743 				    "remove_allocatable", "-n", prtname, NULL);
744 			}
745 		}
746 		_exit(1);
747 		/* NOT REACHED */
748 	default:
749 		waitpid(pid, &status, 0);
750 		return;
751 	}
752 }
753 
754 /*
755  * pack_white(ptr) trims off multiple occurances of white space from a NULL
756  * terminated string pointed to by "ptr".
757  */
758 static void
759 pack_white(char *ptr)
760 {
761 	char	*tptr;
762 	char	*mptr;
763 	int	cnt;
764 
765 	if (ptr == NULL)
766 		return;
767 	cnt = strlen(ptr);
768 	if (cnt == 0)
769 		return;
770 	mptr = (char *)calloc((unsigned)cnt+1, sizeof (char));
771 	if (mptr == NULL)
772 		return;
773 	tptr = strtok(ptr, " \t");
774 	while (tptr != NULL) {
775 		(void) strcat(mptr, tptr);
776 		(void) strcat(mptr, " ");
777 		tptr = strtok(NULL, " \t");
778 	}
779 	cnt = strlen(mptr);
780 	(void) strcpy(ptr, mptr);
781 	free(mptr);
782 }
783