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
do_printer()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
configure_printer(list)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
fullpath(str)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
nameit(cmd)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
update_dev_dbs(char * prtname,char * devname,char * func)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
pack_white(char * ptr)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