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