pcnfsd_print.c revision 1.5
1/* $NetBSD: pcnfsd_print.c,v 1.5 1997/10/25 13:45:58 lukem 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 defintions 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 78char *expand_alias __P((char *, char *, char *, char *)); 79pr_list list_virtual_printers __P((void)); 80char *map_printer_name __P((char *)); 81void substitute __P((char *, char *, char *)); 82int suspicious __P((char *)); 83int valid_pr __P((char *)); 84 85/* 86**--------------------------------------------------------------------- 87** Misc. variable definitions 88**--------------------------------------------------------------------- 89*/ 90 91struct stat statbuf; 92char pathname[MAXPATHLEN]; 93char new_pathname[MAXPATHLEN]; 94char sp_name[MAXPATHLEN] = SPOOLDIR; 95char tempstr[256]; 96char delims[] = " \t\r\n:()"; 97 98pr_list printers = NULL; 99pr_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 116int 117suspicious(s) 118 char *s; 119{ 120 if (strpbrk(s, ";|&<>`'#!?*()[]^/${}\n\r\"\\:") != NULL) 121 return 1; 122 return 0; 123} 124 125 126int 127valid_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 */ 157pirstat 158pr_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... */ 195badspool: 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} 210psrstat 211pr_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 */ 400int 401build_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 */ 535int 536build_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 588void * 589grab(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 602void 603free_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 649pirstat 650build_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 730pirstat 731build_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 834void 835free_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 904pirstat 905get_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 */ 968pirstat 969get_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*/ 1104pcrstat 1105pr_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 */ 1152pcrstat 1153pr_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 1237int num_aliases = 0; 1238struct { 1239 char *a_printer; 1240 char *a_alias_for; 1241 char *a_command; 1242} alias[NPRINTERDEFS]; 1243 1244void 1245add_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 1265pr_list 1266list_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 1300char * 1301map_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 1312void 1313substitute(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 1331char * 1332expand_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