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