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