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