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