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