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