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