1 /* $NetBSD: pcnfsd_print.c,v 1.13 2014/03/29 18:54:36 apb Exp $ */ 2 3 /* RE_SID: @(%)/usr/dosnfs/shades_SCCS/unix/pcnfsd/v2/src/SCCS/s.pcnfsd_print.c 1.7 92/01/24 19:58:58 SMI */ 4 /* 5 **===================================================================== 6 ** Copyright (c) 1986,1987,1988,1989,1990,1991 by Sun Microsystems, Inc. 7 ** @(#)pcnfsd_print.c 1.7 1/24/92 8 **===================================================================== 9 */ 10 /* 11 **===================================================================== 12 ** I N C L U D E F I L E S E C T I O N * 13 ** * 14 ** If your port requires different include files, add a suitable * 15 ** #define in the customization section, and make the inclusion or * 16 ** exclusion of the files conditional on this. * 17 **===================================================================== 18 */ 19 20 #include <sys/file.h> 21 #include <sys/ioctl.h> 22 #include <sys/stat.h> 23 24 #include <ctype.h> 25 #include <errno.h> 26 #include <netdb.h> 27 #include <pwd.h> 28 #include <signal.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #ifndef SYSV 35 #include <sys/wait.h> 36 #endif 37 38 #ifdef ISC_2_0 39 #include <sys/fcntl.h> 40 #endif 41 42 #ifdef SHADOW_SUPPORT 43 #include <shadow.h> 44 #endif 45 46 #include "paths.h" 47 48 #include "common.h" 49 #include "pcnfsd.h" 50 #include "extern.h" 51 52 /* 53 **--------------------------------------------------------------------- 54 ** Other #define's 55 **--------------------------------------------------------------------- 56 */ 57 #ifndef MAXPATHLEN 58 #define MAXPATHLEN 1024 59 #endif 60 61 /* 62 ** The following definitions give the maximum time allowed for 63 ** an external command to run (in seconds) 64 */ 65 #define MAXTIME_FOR_PRINT 10 66 #define MAXTIME_FOR_QUEUE 10 67 #define MAXTIME_FOR_CANCEL 10 68 #define MAXTIME_FOR_STATUS 10 69 70 #define QMAX 50 71 72 /* 73 ** The following is derived from ucb/lpd/displayq.c 74 */ 75 #define SIZECOL 62 76 #define FILECOL 24 77 78 char *expand_alias __P((char *, char *, char *, char *)); 79 pr_list list_virtual_printers __P((void)); 80 char *map_printer_name __P((char *)); 81 void substitute __P((char *, const char *, const char *)); 82 int suspicious __P((char *)); 83 int valid_pr __P((char *)); 84 85 /* 86 **--------------------------------------------------------------------- 87 ** Misc. variable definitions 88 **--------------------------------------------------------------------- 89 */ 90 91 struct stat statbuf; 92 char pathname[MAXPATHLEN]; 93 char new_pathname[MAXPATHLEN]; 94 char sp_name[MAXPATHLEN] = SPOOLDIR; 95 char tempstr[256]; 96 char delims[] = " \t\r\n:()"; 97 98 pr_list printers = NULL; 99 pr_queue queue = NULL; 100 101 /* 102 **===================================================================== 103 ** C O D E S E C T I O N * 104 **===================================================================== 105 */ 106 107 /* 108 * This is the latest word on the security check. The following 109 * routine "suspicious()" returns non-zero if the character string 110 * passed to it contains any shell metacharacters. 111 * Callers will typically code 112 * 113 * if(suspicious(some_parameter)) reject(); 114 */ 115 116 int 117 suspicious(s) 118 char *s; 119 { 120 if (strpbrk(s, ";|&<>`'#!?*()[]^/${}\n\r\"\\:") != NULL) 121 return 1; 122 return 0; 123 } 124 125 126 int 127 valid_pr(pr) 128 char *pr; 129 { 130 char *p; 131 pr_list curr; 132 if (printers == NULL) 133 build_pr_list(); 134 135 if (printers == NULL) 136 return (1); /* can't tell - assume it's good */ 137 138 p = map_printer_name(pr); 139 if (p == NULL) 140 return (1); /* must be ok is maps to NULL! */ 141 curr = printers; 142 while (curr) { 143 if (!strcmp(p, curr->pn)) 144 return (1); 145 curr = curr->pr_next; 146 } 147 148 return (0); 149 } 150 /* 151 * get pathname of current directory and return to client 152 * 153 * Note: This runs as root on behalf of a client request. 154 * As described in CERT advisory CA-96.08, be careful about 155 * doing a chmod on something that could be a symlink... 156 */ 157 pirstat 158 pr_init(sys, pr, sp) 159 char *sys; 160 char *pr; 161 char **sp; 162 { 163 int dir_mode = 0777; 164 int rc; 165 mode_t oldmask; 166 167 *sp = &pathname[0]; 168 pathname[0] = '\0'; 169 170 if (suspicious(sys) || suspicious(pr)) 171 return (PI_RES_FAIL); 172 173 /* 174 * Make sure the server spool directory exists. 175 * Never create it here - the sysadmin does that. 176 */ 177 if (stat(sp_name, &statbuf) || !S_ISDIR(statbuf.st_mode)) 178 goto badspool; 179 180 /* 181 * Create the client spool directory if needed. 182 * Just do the mkdir call and ignore EEXIST. 183 * Mode of client directory should be 777. 184 */ 185 (void) snprintf(pathname, sizeof(pathname), "%s/%s", sp_name, sys); 186 oldmask = umask(0); 187 rc = mkdir(pathname, dir_mode); /* DON'T ignore this return code */ 188 umask(oldmask); 189 if ((rc < 0) && (errno != EEXIST)) 190 goto badspool; 191 192 /* By this point the client spool dir should exist. */ 193 if (stat(pathname, &statbuf) || !S_ISDIR(statbuf.st_mode)) { 194 /* No spool directory... */ 195 badspool: 196 (void) snprintf(tempstr, sizeof(tempstr), 197 "rpc.pcnfsd: unable to set up spool directory %s\n", 198 pathname); 199 msg_out(tempstr); 200 pathname[0] = '\0'; /* null to tell client bad vibes */ 201 return (PI_RES_FAIL); 202 } 203 /* OK, we have a spool directory. */ 204 if (!valid_pr(pr)) { 205 pathname[0] = '\0'; /* null to tell client bad vibes */ 206 return (PI_RES_NO_SUCH_PRINTER); 207 } 208 return (PI_RES_OK); 209 } 210 psrstat 211 pr_start2(sys, pr, user, fname, opts, id) 212 char *sys; 213 char *pr; 214 char *user; 215 char *fname; 216 char *opts; 217 char **id; 218 { 219 char snum[20]; 220 static char req_id[256]; 221 char cmdbuf[256]; 222 char resbuf[256]; 223 FILE *fd; 224 int i; 225 char *xcmd; 226 int failed = 0; 227 228 #ifdef HACK_FOR_ROTATED_TRANSCRIPT 229 char scratch[512]; 230 #endif 231 232 233 if (suspicious(sys) || 234 suspicious(pr) || 235 suspicious(user) || 236 suspicious(fname)) 237 return (PS_RES_FAIL); 238 239 (void) snprintf(pathname, sizeof(pathname), "%s/%s/%s", sp_name, 240 sys, 241 fname); 242 243 *id = &req_id[0]; 244 req_id[0] = '\0'; 245 246 if (stat(pathname, &statbuf)) { 247 /* 248 **----------------------------------------------------------------- 249 ** We can't stat the file. Let's try appending '.spl' and 250 ** see if it's already in progress. 251 **----------------------------------------------------------------- 252 */ 253 254 (void) strlcat(pathname, ".spl", sizeof(pathname)); 255 if (stat(pathname, &statbuf)) { 256 /* 257 **---------------------------------------------------------------- 258 ** It really doesn't exist. 259 **---------------------------------------------------------------- 260 */ 261 262 263 return (PS_RES_NO_FILE); 264 } 265 /* 266 **------------------------------------------------------------- 267 ** It is already on the way. 268 **------------------------------------------------------------- 269 */ 270 271 272 return (PS_RES_ALREADY); 273 } 274 if (statbuf.st_size == 0) { 275 /* 276 **------------------------------------------------------------- 277 ** Null file - don't print it, just kill it. 278 **------------------------------------------------------------- 279 */ 280 (void) unlink(pathname); 281 282 return (PS_RES_NULL); 283 } 284 /* 285 **------------------------------------------------------------- 286 ** The file is real, has some data, and is not already going out. 287 ** We rename it by appending '.spl' and exec "lpr" to do the 288 ** actual work. 289 **------------------------------------------------------------- 290 */ 291 (void) strlcpy(new_pathname, pathname, sizeof(new_pathname)); 292 (void) strlcat(new_pathname, ".spl", sizeof(new_pathname)); 293 294 /* 295 **------------------------------------------------------------- 296 ** See if the new filename exists so as not to overwrite it. 297 **------------------------------------------------------------- 298 */ 299 300 301 if (!stat(new_pathname, &statbuf)) { 302 (void) strlcpy(new_pathname, pathname, sizeof(new_pathname)); /* rebuild a new name */ 303 (void) snprintf(snum, sizeof(snum), "%d", rand()); /* get some number */ 304 (void) strlcat(new_pathname, snum, 4); 305 (void) strlcat(new_pathname, ".spl", sizeof(new_pathname)); /* new spool file */ 306 } 307 if (rename(pathname, new_pathname)) { 308 /* 309 **--------------------------------------------------------------- 310 ** Should never happen. 311 **--------------------------------------------------------------- 312 */ 313 (void) snprintf(tempstr, sizeof(tempstr), 314 "rpc.pcnfsd: spool file rename (%s->%s) failed.\n", 315 pathname, new_pathname); 316 msg_out(tempstr); 317 return (PS_RES_FAIL); 318 } 319 if (*opts == 'd') { 320 /* 321 **------------------------------------------------------ 322 ** This is a Diablo print stream. Apply the ps630 323 ** filter with the appropriate arguments. 324 **------------------------------------------------------ 325 */ 326 #if 0 /* XXX: Temporary fix for CERT advisory 327 * CA-96.08 */ 328 (void) run_ps630(new_pathname, opts); 329 #else 330 (void) snprintf(tempstr, sizeof(tempstr), 331 "rpc.pcnfsd: ps630 filter disabled for %s\n", pathname); 332 msg_out(tempstr); 333 return (PS_RES_FAIL); 334 #endif 335 } 336 /* 337 ** Try to match to an aliased printer 338 */ 339 xcmd = expand_alias(pr, new_pathname, user, sys); 340 if (!xcmd) { 341 #ifdef SVR4 342 /* 343 * Use the copy option so we can remove the orignal 344 * spooled nfs file from the spool directory. 345 */ 346 snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/lp -c -d%s %s", 347 pr, new_pathname); 348 #else /* SVR4 */ 349 /* BSD way: lpr */ 350 snprintf(cmdbuf, sizeof(cmdbuf), "%s/lpr -P%s %s", 351 LPRDIR, pr, new_pathname); 352 #endif /* SVR4 */ 353 xcmd = cmdbuf; 354 } 355 if ((fd = su_popen(user, xcmd, MAXTIME_FOR_PRINT)) == NULL) { 356 msg_out("rpc.pcnfsd: su_popen failed"); 357 return (PS_RES_FAIL); 358 } 359 req_id[0] = '\0'; /* assume failure */ 360 while (fgets(resbuf, 255, fd) != NULL) { 361 i = strlen(resbuf); 362 if (i) 363 resbuf[i - 1] = '\0'; /* trim NL */ 364 if (!strncmp(resbuf, "request id is ", 14)) 365 /* New - just the first word is needed */ 366 strlcpy(req_id, strtok(&resbuf[14], delims), 367 sizeof(req_id)); 368 else 369 if (strembedded("disabled", resbuf)) 370 failed = 1; 371 } 372 if (su_pclose(fd) == 255) 373 msg_out("rpc.pcnfsd: su_pclose alert"); 374 (void) unlink(new_pathname); 375 return ((failed | interrupted) ? PS_RES_FAIL : PS_RES_OK); 376 } 377 /* 378 * build_pr_list: determine which printers are valid. 379 * on SVR4 use "lpstat -v" 380 * on BSD use "lpc status" 381 */ 382 383 #ifdef SVR4 384 /* 385 * In SVR4 the command to determine which printers are 386 * valid is lpstat -v. The output is something like this: 387 * 388 * device for lp: /dev/lp0 389 * system for pcdslw: hinode 390 * system for bletch: hinode (as printer hisname) 391 * 392 * On SunOS using the SysV compatibility package, the output 393 * is more like: 394 * 395 * device for lp is /dev/lp0 396 * device for pcdslw is the remote printer pcdslw on hinode 397 * device for bletch is the remote printer hisname on hinode 398 * 399 * It is fairly simple to create logic that will handle either 400 * possibility: 401 */ 402 int 403 build_pr_list() 404 { 405 pr_list last = NULL; 406 pr_list curr = NULL; 407 char buff[256]; 408 FILE *p; 409 char *cp; 410 int saw_system; 411 412 p = popen("lpstat -v", "r"); 413 if (p == NULL) { 414 msg_out("rpc.pcnfsd: unable to popen() lp status"); 415 return (0); 416 } 417 while (fgets(buff, 255, p) != NULL) { 418 cp = strtok(buff, delims); 419 if (!cp) 420 continue; 421 if (!strcmp(cp, "device")) 422 saw_system = 0; 423 else 424 if (!strcmp(cp, "system")) 425 saw_system = 1; 426 else 427 continue; 428 cp = strtok(NULL, delims); 429 if (!cp || strcmp(cp, "for")) 430 continue; 431 cp = strtok(NULL, delims); 432 if (!cp) 433 continue; 434 curr = (struct pr_list_item *) 435 grab(sizeof(struct pr_list_item)); 436 437 curr->pn = strdup(cp); 438 curr->device = NULL; 439 curr->remhost = NULL; 440 curr->cm = strdup("-"); 441 curr->pr_next = NULL; 442 443 cp = strtok(NULL, delims); 444 445 if (cp && !strcmp(cp, "is")) 446 cp = strtok(NULL, delims); 447 448 if (!cp) { 449 free_pr_list_item(curr); 450 continue; 451 } 452 if (saw_system) { 453 /* "system" OR "system (as printer pname)" */ 454 curr->remhost = strdup(cp); 455 cp = strtok(NULL, delims); 456 if (!cp) { 457 /* simple format */ 458 curr->device = strdup(curr->pn); 459 } else { 460 /* "sys (as printer pname)" */ 461 if (strcmp(cp, "as")) { 462 free_pr_list_item(curr); 463 continue; 464 } 465 cp = strtok(NULL, delims); 466 if (!cp || strcmp(cp, "printer")) { 467 free_pr_list_item(curr); 468 continue; 469 } 470 cp = strtok(NULL, delims); 471 if (!cp) { 472 free_pr_list_item(curr); 473 continue; 474 } 475 curr->device = strdup(cp); 476 } 477 } else 478 if (!strcmp(cp, "the")) { 479 /* start of "the remote printer foo on bar" */ 480 cp = strtok(NULL, delims); 481 if (!cp || strcmp(cp, "remote")) { 482 free_pr_list_item(curr); 483 continue; 484 } 485 cp = strtok(NULL, delims); 486 if (!cp || strcmp(cp, "printer")) { 487 free_pr_list_item(curr); 488 continue; 489 } 490 cp = strtok(NULL, delims); 491 if (!cp) { 492 free_pr_list_item(curr); 493 continue; 494 } 495 curr->device = strdup(cp); 496 cp = strtok(NULL, delims); 497 if (!cp || strcmp(cp, "on")) { 498 free_pr_list_item(curr); 499 continue; 500 } 501 cp = strtok(NULL, delims); 502 if (!cp) { 503 free_pr_list_item(curr); 504 continue; 505 } 506 curr->remhost = strdup(cp); 507 } else { 508 /* the local name */ 509 curr->device = strdup(cp); 510 curr->remhost = strdup(""); 511 } 512 513 if (last == NULL) 514 printers = curr; 515 else 516 last->pr_next = curr; 517 last = curr; 518 519 } 520 (void) pclose(p); 521 522 /* 523 ** Now add on the virtual printers, if any 524 */ 525 if (last == NULL) 526 printers = list_virtual_printers(); 527 else 528 last->pr_next = list_virtual_printers(); 529 530 return (1); 531 } 532 #else /* SVR4 */ 533 534 /* 535 * BSD way: lpc stat 536 */ 537 int 538 build_pr_list() 539 { 540 pr_list last = NULL; 541 pr_list curr = NULL; 542 char buff[256]; 543 FILE *p; 544 char *cp; 545 546 snprintf(buff, sizeof(buff), "%s/lpc status", LPCDIR); 547 p = popen(buff, "r"); 548 if (p == NULL) { 549 msg_out("rpc.pcnfsd: unable to popen lpc stat"); 550 return (0); 551 } 552 while (fgets(buff, 255, p) != NULL) { 553 if (isspace((unsigned char)buff[0])) 554 continue; 555 556 if ((cp = strtok(buff, delims)) == NULL) 557 continue; 558 559 curr = (struct pr_list_item *) 560 grab(sizeof(struct pr_list_item)); 561 562 /* XXX - Should distinguish remote printers. */ 563 curr->pn = strdup(cp); 564 curr->device = strdup(cp); 565 curr->remhost = strdup(""); 566 curr->cm = strdup("-"); 567 curr->pr_next = NULL; 568 569 if (last == NULL) 570 printers = curr; 571 else 572 last->pr_next = curr; 573 last = curr; 574 575 } 576 (void) pclose(p); 577 578 /* 579 ** Now add on the virtual printers, if any 580 */ 581 if (last == NULL) 582 printers = list_virtual_printers(); 583 else 584 last->pr_next = list_virtual_printers(); 585 586 return (1); 587 } 588 #endif /* SVR4 */ 589 590 void * 591 grab(n) 592 int n; 593 { 594 void *p; 595 596 p = (void *) malloc(n); 597 if (p == NULL) { 598 msg_out("rpc.pcnfsd: malloc failure"); 599 exit(1); 600 } 601 return (p); 602 } 603 604 void 605 free_pr_list_item(curr) 606 pr_list curr; 607 { 608 if (curr->pn) 609 free(curr->pn); 610 if (curr->device) 611 free(curr->device); 612 if (curr->remhost) 613 free(curr->remhost); 614 if (curr->cm) 615 free(curr->cm); 616 if (curr->pr_next) 617 free_pr_list_item(curr->pr_next); /* recurse */ 618 free(curr); 619 } 620 /* 621 * build_pr_queue: used to show the print queue. 622 * 623 * Note that the first thing we do is to discard any 624 * existing queue. 625 */ 626 #ifdef SVR4 627 628 /* 629 ** In SVR4 the command to list the print jobs for printer 630 ** lp is "lpstat lp" (or, equivalently, "lpstat -p lp"). 631 ** The output looks like this: 632 ** 633 ** lp-2 root 939 Jul 10 21:56 634 ** lp-5 geoff 15 Jul 12 23:23 635 ** lp-6 geoff 15 Jul 12 23:23 636 ** 637 ** If the first job is actually printing the first line 638 ** is modified, as follows: 639 ** 640 ** lp-2 root 939 Jul 10 21:56 on lp 641 ** 642 ** I don't yet have any info on what it looks like if the printer 643 ** is remote and we're spooling over the net. However for 644 ** the purposes of rpc.pcnfsd we can simply say that field 1 is the 645 ** job ID, field 2 is the submitter, and field 3 is the size. 646 ** We can check for the presence of the string " on " in the 647 ** first record to determine if we should count it as rank 0 or rank 1, 648 ** but it won't hurt if we get it wrong. 649 **/ 650 651 pirstat 652 build_pr_queue(pn, user, just_mine, p_qlen, p_qshown) 653 printername pn; 654 username user; 655 int just_mine; 656 int *p_qlen; 657 int *p_qshown; 658 { 659 pr_queue last = NULL; 660 pr_queue curr = NULL; 661 char buff[256]; 662 FILE *p; 663 char *owner; 664 char *job; 665 char *totsize; 666 667 if (queue) { 668 free_pr_queue_item(queue); 669 queue = NULL; 670 } 671 *p_qlen = 0; 672 *p_qshown = 0; 673 674 pn = map_printer_name(pn); 675 if (pn == NULL || !valid_pr(pn) || suspicious(pn)) 676 return (PI_RES_NO_SUCH_PRINTER); 677 678 snprintf(buff, sizeof(buff), "/usr/bin/lpstat %s", pn); 679 p = su_popen(user, buff, MAXTIME_FOR_QUEUE); 680 if (p == NULL) { 681 msg_out("rpc.pcnfsd: unable to popen() lpstat queue query"); 682 return (PI_RES_FAIL); 683 } 684 while (fgets(buff, 255, p) != NULL) { 685 job = strtok(buff, delims); 686 if (!job) 687 continue; 688 689 owner = strtok(NULL, delims); 690 if (!owner) 691 continue; 692 693 totsize = strtok(NULL, delims); 694 if (!totsize) 695 continue; 696 697 *p_qlen += 1; 698 699 if (*p_qshown > QMAX) 700 continue; 701 702 if (just_mine && strcasecmp(owner, user)) 703 continue; 704 705 *p_qshown += 1; 706 707 curr = (struct pr_queue_item *) 708 grab(sizeof(struct pr_queue_item)); 709 710 curr->position = *p_qlen; 711 curr->id = strdup(job); 712 curr->size = strdup(totsize); 713 curr->status = strdup(""); 714 curr->system = strdup(""); 715 curr->user = strdup(owner); 716 curr->file = strdup(""); 717 curr->cm = strdup("-"); 718 curr->pr_next = NULL; 719 720 if (last == NULL) 721 queue = curr; 722 else 723 last->pr_next = curr; 724 last = curr; 725 726 } 727 (void) su_pclose(p); 728 return (PI_RES_OK); 729 } 730 #else /* SVR4 */ 731 732 pirstat 733 build_pr_queue(pn, user, just_mine, p_qlen, p_qshown) 734 printername pn; 735 username user; 736 int just_mine; 737 int *p_qlen; 738 int *p_qshown; 739 { 740 pr_queue last = NULL; 741 pr_queue curr = NULL; 742 char buff[256]; 743 FILE *p; 744 char *cp; 745 int i; 746 char *rank; 747 char *owner; 748 char *job; 749 char *files; 750 char *totsize; 751 752 if (queue) { 753 free_pr_queue_item(queue); 754 queue = NULL; 755 } 756 *p_qlen = 0; 757 *p_qshown = 0; 758 pn = map_printer_name(pn); 759 if (pn == NULL || suspicious(pn)) 760 return (PI_RES_NO_SUCH_PRINTER); 761 762 snprintf(buff, sizeof(buff), "%s/lpq -P%s", LPRDIR, pn); 763 764 p = su_popen(user, buff, MAXTIME_FOR_QUEUE); 765 if (p == NULL) { 766 msg_out("rpc.pcnfsd: unable to popen() lpq"); 767 return (PI_RES_FAIL); 768 } 769 while (fgets(buff, 255, p) != NULL) { 770 i = strlen(buff) - 1; 771 buff[i] = '\0'; /* zap trailing NL */ 772 if (i < SIZECOL) 773 continue; 774 if (!strncasecmp(buff, "rank", 4)) 775 continue; 776 777 totsize = &buff[SIZECOL - 1]; 778 files = &buff[FILECOL - 1]; 779 cp = totsize; 780 cp--; 781 while (cp > files && isspace((unsigned char)*cp)) 782 *cp-- = '\0'; 783 784 buff[FILECOL - 2] = '\0'; 785 786 cp = strtok(buff, delims); 787 if (!cp) 788 continue; 789 rank = cp; 790 791 cp = strtok(NULL, delims); 792 if (!cp) 793 continue; 794 owner = cp; 795 796 cp = strtok(NULL, delims); 797 if (!cp) 798 continue; 799 job = cp; 800 801 *p_qlen += 1; 802 803 if (*p_qshown > QMAX) 804 continue; 805 806 if (just_mine && strcasecmp(owner, user)) 807 continue; 808 809 *p_qshown += 1; 810 811 curr = (struct pr_queue_item *) 812 grab(sizeof(struct pr_queue_item)); 813 814 curr->position = atoi(rank); /* active -> 0 */ 815 curr->id = strdup(job); 816 curr->size = strdup(totsize); 817 curr->status = strdup(rank); 818 curr->system = strdup(""); 819 curr->user = strdup(owner); 820 curr->file = strdup(files); 821 curr->cm = strdup("-"); 822 curr->pr_next = NULL; 823 824 if (last == NULL) 825 queue = curr; 826 else 827 last->pr_next = curr; 828 last = curr; 829 830 } 831 (void) su_pclose(p); 832 return (PI_RES_OK); 833 } 834 #endif /* SVR4 */ 835 836 void 837 free_pr_queue_item(curr) 838 pr_queue curr; 839 { 840 if (curr->id) 841 free(curr->id); 842 if (curr->size) 843 free(curr->size); 844 if (curr->status) 845 free(curr->status); 846 if (curr->system) 847 free(curr->system); 848 if (curr->user) 849 free(curr->user); 850 if (curr->file) 851 free(curr->file); 852 if (curr->cm) 853 free(curr->cm); 854 if (curr->pr_next) 855 free_pr_queue_item(curr->pr_next); /* recurse */ 856 free(curr); 857 } 858 #ifdef SVR4 859 860 /* 861 ** New - SVR4 printer status handling. 862 ** 863 ** The command we'll use for checking the status of printer "lp" 864 ** is "lpstat -a lp -p lp". Here are some sample outputs: 865 ** 866 ** 867 ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991 868 ** printer lp disabled since Thu Feb 21 22:52:36 EST 1991. available. 869 ** new printer 870 ** --- 871 ** pcdslw not accepting requests since Fri Jul 12 22:30:00 EDT 1991 - 872 ** unknown reason 873 ** printer pcdslw disabled since Fri Jul 12 22:15:37 EDT 1991. available. 874 ** new printer 875 ** --- 876 ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991 877 ** printer lp now printing lp-2. enabled since Sat Jul 13 12:02:17 EDT 1991. available. 878 ** --- 879 ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991 880 ** printer lp now printing lp-2. enabled since Sat Jul 13 12:02:17 EDT 1991. available. 881 ** --- 882 ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991 883 ** printer lp disabled since Sat Jul 13 12:05:20 EDT 1991. available. 884 ** unknown reason 885 ** --- 886 ** pcdslw not accepting requests since Fri Jul 12 22:30:00 EDT 1991 - 887 ** unknown reason 888 ** printer pcdslw is idle. enabled since Sat Jul 13 12:05:28 EDT 1991. available. 889 ** 890 ** Note that these are actual outputs. The format (which is totally 891 ** different from the lpstat in SunOS) seems to break down as 892 ** follows: 893 ** (1) The first line has the form "printername [not] accepting requests,,," 894 ** This is trivial to decode. 895 ** (2) The second line has several forms, all beginning "printer printername": 896 ** (2.1) "... disabled" 897 ** (2.2) "... is idle" 898 ** (2.3) "... now printing jobid" 899 ** The "available" comment seems to be meaningless. The next line 900 ** is the "reason" code which the operator can supply when issuing 901 ** a "disable" or "reject" command. 902 ** Note that there is no way to check the number of entries in the 903 ** queue except to ask for the queue and count them. 904 */ 905 906 pirstat 907 get_pr_status(pn, avail, printing, qlen, needs_operator, status, statuslen) 908 printername pn; 909 bool_t *avail; 910 bool_t *printing; 911 int *qlen; 912 bool_t *needs_operator; 913 char *status; 914 size_t statuslen; 915 { 916 char buff[256]; 917 char cmd[64]; 918 FILE *p; 919 int n; 920 pirstat stat = PI_RES_NO_SUCH_PRINTER; 921 922 /* assume the worst */ 923 *avail = FALSE; 924 *printing = FALSE; 925 *needs_operator = FALSE; 926 *qlen = 0; 927 *status = '\0'; 928 929 pn = map_printer_name(pn); 930 if (pn == NULL || !valid_pr(pn) || suspicious(pn)) 931 return (PI_RES_NO_SUCH_PRINTER); 932 n = strlen(pn); 933 934 snprintf(cmd, sizeof(cmd), "/usr/bin/lpstat -a %s -p %s", pn, pn); 935 936 p = popen(cmd, "r"); 937 if (p == NULL) { 938 msg_out("rpc.pcnfsd: unable to popen() lp status"); 939 return (PI_RES_FAIL); 940 } 941 stat = PI_RES_OK; 942 943 while (fgets(buff, 255, p) != NULL) { 944 if (!strncmp(buff, pn, n)) { 945 if (!strstr(buff, "not accepting")) 946 *avail = TRUE; 947 continue; 948 } 949 if (!strncmp(buff, "printer ", 8)) { 950 if (!strstr(buff, "disabled")) 951 *printing = TRUE; 952 if (strstr(buff, "printing")) 953 strlcpy(status, "printing", statuslen); 954 else 955 if (strstr(buff, "idle")) 956 strlcpy(status, "idle", statuslen); 957 continue; 958 } 959 if (!strncmp(buff, "UX:", 3)) { 960 stat = PI_RES_NO_SUCH_PRINTER; 961 } 962 } 963 (void) pclose(p); 964 return (stat); 965 } 966 #else /* SVR4 */ 967 968 /* 969 * BSD way: lpc status 970 */ 971 pirstat 972 get_pr_status(pn, avail, printing, qlen, needs_operator, status, statuslen) 973 printername pn; 974 bool_t *avail; 975 bool_t *printing; 976 int *qlen; 977 bool_t *needs_operator; 978 char *status; 979 size_t statuslen; 980 { 981 char cmd[128]; 982 char buff[256]; 983 char buff2[256]; 984 char pname[64]; 985 FILE *p; 986 char *cp; 987 char *cp1; 988 char *cp2; 989 int n; 990 pirstat pstat = PI_RES_NO_SUCH_PRINTER; 991 992 /* assume the worst */ 993 *avail = FALSE; 994 *printing = FALSE; 995 *needs_operator = FALSE; 996 *qlen = 0; 997 *status = '\0'; 998 999 pn = map_printer_name(pn); 1000 if (pn == NULL || suspicious(pn)) 1001 return (PI_RES_NO_SUCH_PRINTER); 1002 1003 snprintf(pname, sizeof(pname), "%s:", pn); 1004 n = strlen(pname); 1005 1006 snprintf(cmd, sizeof(cmd), "%s/lpc status %s", LPCDIR, pn); 1007 p = popen(cmd, "r"); 1008 if (p == NULL) { 1009 msg_out("rpc.pcnfsd: unable to popen() lp status"); 1010 return (PI_RES_FAIL); 1011 } 1012 while (fgets(buff, 255, p) != NULL) { 1013 if (strncmp(buff, pname, n)) 1014 continue; 1015 /* 1016 ** We have a match. The only failure now is PI_RES_FAIL if 1017 ** lpstat output cannot be decoded 1018 */ 1019 pstat = PI_RES_FAIL; 1020 /* 1021 ** The next four lines are usually if the form 1022 ** 1023 ** queuing is [enabled|disabled] 1024 ** printing is [enabled|disabled] 1025 ** [no entries | N entr[y|ies] in spool area] 1026 ** <status message, may include the word "attention"> 1027 */ 1028 while (fgets(buff, 255, p) != NULL && isspace((unsigned char)buff[0])) { 1029 cp = buff; 1030 while (isspace((unsigned char)*cp)) 1031 cp++; 1032 if (*cp == '\0') 1033 break; 1034 cp1 = cp; 1035 cp2 = buff2; 1036 while (*cp1 && *cp1 != '\n') { 1037 *cp2++ = tolower((unsigned char)*cp1); 1038 cp1++; 1039 } 1040 *cp1 = '\0'; 1041 *cp2 = '\0'; 1042 /* 1043 ** Now buff2 has a lower-cased copy and cp points at the original; 1044 ** both are null terminated without any newline 1045 */ 1046 if (!strncmp(buff2, "queuing", 7)) { 1047 *avail = (strstr(buff2, "enabled") != NULL); 1048 continue; 1049 } 1050 if (!strncmp(buff2, "printing", 8)) { 1051 *printing = (strstr(buff2, "enabled") != NULL); 1052 continue; 1053 } 1054 if (isdigit((unsigned char)buff2[0]) && (strstr(buff2, "entr") != NULL)) { 1055 1056 *qlen = atoi(buff2); 1057 continue; 1058 } 1059 if (strstr(buff2, "attention") != NULL || 1060 strstr(buff2, "error") != NULL) 1061 *needs_operator = TRUE; 1062 if (*needs_operator || strstr(buff2, "waiting") != NULL) 1063 strlcpy(status, cp, statuslen); 1064 } 1065 pstat = PI_RES_OK; 1066 break; 1067 } 1068 (void) pclose(p); 1069 return (pstat); 1070 } 1071 #endif /* SVR4 */ 1072 1073 /* 1074 * pr_cancel: cancel a print job 1075 */ 1076 #ifdef SVR4 1077 1078 /* 1079 ** For SVR4 we have to be prepared for the following kinds of output: 1080 ** 1081 ** # cancel lp-6 1082 ** request "lp-6" cancelled 1083 ** # cancel lp-33 1084 ** UX:cancel: WARNING: Request "lp-33" doesn't exist. 1085 ** # cancel foo-88 1086 ** UX:cancel: WARNING: Request "foo-88" doesn't exist. 1087 ** # cancel foo 1088 ** UX:cancel: WARNING: "foo" is not a request id or a printer. 1089 ** TO FIX: Cancel requests by id or by 1090 ** name of printer where printing. 1091 ** # su geoff 1092 ** $ cancel lp-2 1093 ** UX:cancel: WARNING: Can't cancel request "lp-2". 1094 ** TO FIX: You are not allowed to cancel 1095 ** another's request. 1096 ** 1097 ** There are probably other variations for remote printers. 1098 ** Basically, if the reply begins with the string 1099 ** "UX:cancel: WARNING: " 1100 ** we can strip this off and look for one of the following 1101 ** (1) 'R' - should be part of "Request "xxxx" doesn't exist." 1102 ** (2) '"' - should be start of ""foo" is not a request id or..." 1103 ** (3) 'C' - should be start of "Can't cancel request..." 1104 ** 1105 ** The fly in the ointment: all of this can change if these 1106 ** messages are localized..... :-( 1107 */ 1108 pcrstat 1109 pr_cancel(pr, user, id) 1110 char *pr; 1111 char *user; 1112 char *id; 1113 { 1114 char cmdbuf[256]; 1115 char resbuf[256]; 1116 FILE *fd; 1117 pcrstat stat = PC_RES_NO_SUCH_JOB; 1118 1119 pr = map_printer_name(pr); 1120 if (pr == NULL || suspicious(pr)) 1121 return (PC_RES_NO_SUCH_PRINTER); 1122 if (suspicious(id)) 1123 return (PC_RES_NO_SUCH_JOB); 1124 1125 snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/cancel %s", id); 1126 if ((fd = su_popen(user, cmdbuf, MAXTIME_FOR_CANCEL)) == NULL) { 1127 msg_out("rpc.pcnfsd: su_popen failed"); 1128 return (PC_RES_FAIL); 1129 } 1130 if (fgets(resbuf, 255, fd) == NULL) 1131 stat = PC_RES_FAIL; 1132 else 1133 if (!strstr(resbuf, "UX:")) 1134 stat = PC_RES_OK; 1135 else 1136 if (strstr(resbuf, "doesn't exist")) 1137 stat = PC_RES_NO_SUCH_JOB; 1138 else 1139 if (strstr(resbuf, "not a request id")) 1140 stat = PC_RES_NO_SUCH_JOB; 1141 else 1142 if (strstr(resbuf, "Can't cancel request")) 1143 stat = PC_RES_NOT_OWNER; 1144 else 1145 stat = PC_RES_FAIL; 1146 1147 if (su_pclose(fd) == 255) 1148 msg_out("rpc.pcnfsd: su_pclose alert"); 1149 return (stat); 1150 } 1151 #else /* SVR4 */ 1152 1153 /* 1154 * BSD way: lprm 1155 */ 1156 pcrstat 1157 pr_cancel(pr, user, id) 1158 char *pr; 1159 char *user; 1160 char *id; 1161 { 1162 char cmdbuf[256]; 1163 char resbuf[256]; 1164 FILE *fd; 1165 int i; 1166 pcrstat pstat = PC_RES_NO_SUCH_JOB; 1167 1168 pr = map_printer_name(pr); 1169 if (pr == NULL || suspicious(pr)) 1170 return (PC_RES_NO_SUCH_PRINTER); 1171 if (suspicious(id)) 1172 return (PC_RES_NO_SUCH_JOB); 1173 1174 snprintf(cmdbuf, sizeof(cmdbuf), "%s/lprm -P%s %s", LPRDIR, pr, id); 1175 if ((fd = su_popen(user, cmdbuf, MAXTIME_FOR_CANCEL)) == NULL) { 1176 msg_out("rpc.pcnfsd: su_popen failed"); 1177 return (PC_RES_FAIL); 1178 } 1179 while (fgets(resbuf, 255, fd) != NULL) { 1180 i = strlen(resbuf); 1181 if (i) 1182 resbuf[i - 1] = '\0'; /* trim NL */ 1183 if (strstr(resbuf, "dequeued") != NULL) 1184 pstat = PC_RES_OK; 1185 if (strstr(resbuf, "unknown printer") != NULL) 1186 pstat = PC_RES_NO_SUCH_PRINTER; 1187 if (strstr(resbuf, "Permission denied") != NULL) 1188 pstat = PC_RES_NOT_OWNER; 1189 } 1190 if (su_pclose(fd) == 255) 1191 msg_out("rpc.pcnfsd: su_pclose alert"); 1192 return (pstat); 1193 } 1194 #endif /* SVR4 */ 1195 1196 /* 1197 ** New subsystem here. We allow the administrator to define 1198 ** up to NPRINTERDEFS aliases for printer names. This is done 1199 ** using the "/etc/pcnfsd.conf" file, which is read at startup. 1200 ** There are three entry points to this subsystem 1201 ** 1202 ** void add_printer_alias(char *printer, char *alias_for, char *command) 1203 ** 1204 ** This is invoked from "config_from_file()" for each 1205 ** "printer" line. "printer" is the name of a printer; note that 1206 ** it is possible to redefine an existing printer. "alias_for" 1207 ** is the name of the underlying printer, used for queue listing 1208 ** and other control functions. If it is "-", there is no 1209 ** underlying printer, or the administrative functions are 1210 ** not applicable to this printer. "command" 1211 ** is the command which should be run (via "su_popen()") if a 1212 ** job is printed on this printer. The following tokens may be 1213 ** embedded in the command, and are substituted as follows: 1214 ** 1215 ** $FILE - path to the file containing the print data 1216 ** $USER - login of user 1217 ** $HOST - hostname from which job originated 1218 ** 1219 ** Tokens may occur multiple times. If The command includes no 1220 ** $FILE token, the string " $FILE" is silently appended. 1221 ** 1222 ** pr_list list_virtual_printers() 1223 ** 1224 ** This is invoked from build_pr_list to generate a list of aliased 1225 ** printers, so that the client that asks for a list of valid printers 1226 ** will see these ones. 1227 ** 1228 ** char *map_printer_name(char *printer) 1229 ** 1230 ** If "printer" identifies an aliased printer, this function returns 1231 ** the "alias_for" name, or NULL if the "alias_for" was given as "-". 1232 ** Otherwise it returns its argument. 1233 ** 1234 ** char *expand_alias(char *printer, char *file, char *user, char *host) 1235 ** 1236 ** If "printer" is an aliased printer, this function returns a 1237 ** pointer to a static string in which the corresponding command 1238 ** has been expanded. Otherwise ot returns NULL. 1239 */ 1240 #define NPRINTERDEFS 16 1241 int num_aliases = 0; 1242 struct { 1243 char *a_printer; 1244 char *a_alias_for; 1245 char *a_command; 1246 } alias[NPRINTERDEFS]; 1247 1248 void 1249 add_printer_alias(printer, alias_for, command) 1250 char *printer; 1251 char *alias_for; 1252 char *command; 1253 { 1254 size_t l; 1255 1256 if (num_aliases < NPRINTERDEFS) { 1257 alias[num_aliases].a_printer = strdup(printer); 1258 alias[num_aliases].a_alias_for = 1259 (strcmp(alias_for, "-") ? strdup(alias_for) : NULL); 1260 if (strstr(command, "$FILE")) 1261 alias[num_aliases].a_command = strdup(command); 1262 else { 1263 l = strlen(command) + 8; 1264 alias[num_aliases].a_command = (char *) grab(l); 1265 strlcpy(alias[num_aliases].a_command, command, l); 1266 strlcat(alias[num_aliases].a_command, " $FILE", l); 1267 } 1268 num_aliases++; 1269 } 1270 } 1271 1272 pr_list 1273 list_virtual_printers() 1274 { 1275 pr_list first = NULL; 1276 pr_list last = NULL; 1277 pr_list curr = NULL; 1278 int i; 1279 1280 1281 if (num_aliases == 0) 1282 return (NULL); 1283 1284 for (i = 0; i < num_aliases; i++) { 1285 curr = (struct pr_list_item *) 1286 grab(sizeof(struct pr_list_item)); 1287 1288 curr->pn = strdup(alias[i].a_printer); 1289 if (alias[i].a_alias_for == NULL) 1290 curr->device = strdup(""); 1291 else 1292 curr->device = strdup(alias[i].a_alias_for); 1293 curr->remhost = strdup(""); 1294 curr->cm = strdup("(alias)"); 1295 curr->pr_next = NULL; 1296 if (last == NULL) 1297 first = curr; 1298 else 1299 last->pr_next = curr; 1300 last = curr; 1301 1302 } 1303 return (first); 1304 } 1305 1306 1307 char * 1308 map_printer_name(printer) 1309 char *printer; 1310 { 1311 int i; 1312 for (i = 0; i < num_aliases; i++) { 1313 if (!strcmp(printer, alias[i].a_printer)) 1314 return (alias[i].a_alias_for); 1315 } 1316 return (printer); 1317 } 1318 1319 void 1320 substitute(string, token, data) 1321 char *string; 1322 const char *token; 1323 const char *data; 1324 { 1325 char temp[512]; 1326 char *c; 1327 1328 while ((c = strstr(string, token)) != NULL) { 1329 *c = '\0'; 1330 strlcpy(temp, string, sizeof(temp)); 1331 strlcat(temp, data, sizeof(temp)); 1332 c += strlen(token); 1333 strlcat(temp, c, sizeof(temp)); 1334 strcpy(string, temp); 1335 } 1336 } 1337 1338 char * 1339 expand_alias(printer, file, user, host) 1340 char *printer; 1341 char *file; 1342 char *user; 1343 char *host; 1344 { 1345 static char expansion[512]; 1346 int i; 1347 for (i = 0; i < num_aliases; i++) { 1348 if (!strcmp(printer, alias[i].a_printer)) { 1349 strlcpy(expansion, alias[i].a_command, 1350 sizeof(expansion)); 1351 substitute(expansion, "$FILE", file); 1352 substitute(expansion, "$USER", user); 1353 substitute(expansion, "$HOST", host); 1354 return (expansion); 1355 } 1356 } 1357 return (NULL); 1358 } 1359