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