1 /* $NetBSD: pcnfsd_print.c,v 1.9 2009/04/18 13:02:36 lukem Exp $ */
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 */
5 **=====================================================================
6 ** Copyright (c) 1986,1987,1988,1989,1990,1991 by Sun Microsystems, Inc.
7 ** @(#)pcnfsd_print.c 1.7 1/24/92
8 **=====================================================================
11 **=====================================================================
12 ** I N C L U D E F I L E S E C T I O N *
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 **=====================================================================
21 #include <sys/ioctl.h>
39 #include <sys/fcntl.h>
53 **---------------------------------------------------------------------
55 **---------------------------------------------------------------------
58 #define MAXPATHLEN 1024
62 ** The following definitions give the maximum time allowed for
63 ** an external command to run (in seconds)
65 #define MAXTIME_FOR_PRINT 10
66 #define MAXTIME_FOR_QUEUE 10
67 #define MAXTIME_FOR_CANCEL 10
68 #define MAXTIME_FOR_STATUS 10
73 ** The following is derived from ucb/lpd/displayq.c
78 char *expand_alias
__P((char *, char *, char *, char *));
79 pr_list list_virtual_printers
__P((void));
80 char *map_printer_name
__P((char *));
81 void substitute
__P((char *, const char *, const char *));
82 int suspicious
__P((char *));
83 int valid_pr
__P((char *));
86 **---------------------------------------------------------------------
87 ** Misc. variable definitions
88 **---------------------------------------------------------------------
92 char pathname
[MAXPATHLEN
];
93 char new_pathname
[MAXPATHLEN
];
94 char sp_name
[MAXPATHLEN
] = SPOOLDIR
;
96 char delims
[] = " \t\r\n:()";
98 pr_list printers
= NULL
;
99 pr_queue queue
= NULL
;
102 **=====================================================================
103 ** C O D E S E C T I O N *
104 **=====================================================================
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
113 * if(suspicious(some_parameter)) reject();
120 if (strpbrk(s
, ";|&<>`'#!?*()[]^/${}\n\r\"\\:") != NULL
)
132 if (printers
== NULL
)
135 if (printers
== NULL
)
136 return (1); /* can't tell - assume it's good */
138 p
= map_printer_name(pr
);
140 return (1); /* must be ok is maps to NULL! */
143 if (!strcmp(p
, curr
->pn
))
145 curr
= curr
->pr_next
;
151 * get pathname of current directory and return to client
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...
170 if (suspicious(sys
) || suspicious(pr
))
171 return (PI_RES_FAIL
);
174 * Make sure the server spool directory exists.
175 * Never create it here - the sysadmin does that.
177 if (stat(sp_name
, &statbuf
) || !S_ISDIR(statbuf
.st_mode
))
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.
185 (void) snprintf(pathname
, sizeof(pathname
), "%s/%s", sp_name
, sys
);
187 rc
= mkdir(pathname
, dir_mode
); /* DON'T ignore this return code */
189 if ((rc
< 0) && (errno
!= EEXIST
))
192 /* By this point the client spool dir should exist. */
193 if (stat(pathname
, &statbuf
) || !S_ISDIR(statbuf
.st_mode
)) {
194 /* No spool directory... */
196 (void) snprintf(tempstr
, sizeof(tempstr
),
197 "rpc.pcnfsd: unable to set up spool directory %s\n",
200 pathname
[0] = '\0'; /* null to tell client bad vibes */
201 return (PI_RES_FAIL
);
203 /* OK, we have a spool directory. */
205 pathname
[0] = '\0'; /* null to tell client bad vibes */
206 return (PI_RES_NO_SUCH_PRINTER
);
211 pr_start2(sys
, pr
, user
, fname
, opts
, id
)
220 static char req_id
[256];
228 #ifdef HACK_FOR_ROTATED_TRANSCRIPT
233 if (suspicious(sys
) ||
237 return (PS_RES_FAIL
);
239 (void) snprintf(pathname
, sizeof(pathname
), "%s/%s/%s", sp_name
,
246 if (stat(pathname
, &statbuf
)) {
248 **-----------------------------------------------------------------
249 ** We can't stat the file. Let's try appending '.spl' and
250 ** see if it's already in progress.
251 **-----------------------------------------------------------------
254 (void) strlcat(pathname
, ".spl", sizeof(pathname
));
255 if (stat(pathname
, &statbuf
)) {
257 **----------------------------------------------------------------
258 ** It really doesn't exist.
259 **----------------------------------------------------------------
263 return (PS_RES_NO_FILE
);
266 **-------------------------------------------------------------
267 ** It is already on the way.
268 **-------------------------------------------------------------
272 return (PS_RES_ALREADY
);
274 if (statbuf
.st_size
== 0) {
276 **-------------------------------------------------------------
277 ** Null file - don't print it, just kill it.
278 **-------------------------------------------------------------
280 (void) unlink(pathname
);
282 return (PS_RES_NULL
);
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
289 **-------------------------------------------------------------
291 (void) strlcpy(new_pathname
, pathname
, sizeof(new_pathname
));
292 (void) strlcat(new_pathname
, ".spl", sizeof(new_pathname
));
295 **-------------------------------------------------------------
296 ** See if the new filename exists so as not to overwrite it.
297 **-------------------------------------------------------------
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 */
307 if (rename(pathname
, new_pathname
)) {
309 **---------------------------------------------------------------
310 ** Should never happen.
311 **---------------------------------------------------------------
313 (void) snprintf(tempstr
, sizeof(tempstr
),
314 "rpc.pcnfsd: spool file rename (%s->%s) failed.\n",
315 pathname
, new_pathname
);
317 return (PS_RES_FAIL
);
321 **------------------------------------------------------
322 ** This is a Diablo print stream. Apply the ps630
323 ** filter with the appropriate arguments.
324 **------------------------------------------------------
326 #if 0 /* XXX: Temporary fix for CERT advisory
328 (void) run_ps630(new_pathname
, opts
);
330 (void) snprintf(tempstr
, sizeof(tempstr
),
331 "rpc.pcnfsd: ps630 filter disabled for %s\n", pathname
);
333 return (PS_RES_FAIL
);
337 ** Try to match to an aliased printer
339 xcmd
= expand_alias(pr
, new_pathname
, user
, sys
);
343 * Use the copy option so we can remove the orignal
344 * spooled nfs file from the spool directory.
346 snprintf(cmdbuf
, sizeof(cmdbuf
), "/usr/bin/lp -c -d%s %s",
350 snprintf(cmdbuf
, sizeof(cmdbuf
), "%s/lpr -P%s %s",
351 LPRDIR
, pr
, new_pathname
);
355 if ((fd
= su_popen(user
, xcmd
, MAXTIME_FOR_PRINT
)) == NULL
) {
356 msg_out("rpc.pcnfsd: su_popen failed");
357 return (PS_RES_FAIL
);
359 req_id
[0] = '\0'; /* asume failure */
360 while (fgets(resbuf
, 255, fd
) != NULL
) {
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
),
369 if (strembedded("disabled", resbuf
))
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
);
378 * build_pr_list: determine which printers are valid.
379 * on SVR4 use "lpstat -v"
380 * on BSD use "lpc status"
385 * In SVR4 the command to determine which printers are
386 * valid is lpstat -v. The output is something like this:
388 * device for lp: /dev/lp0
389 * system for pcdslw: hinode
390 * system for bletch: hinode (as printer hisname)
392 * On SunOS using the SysV compatibility package, the output
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
399 * It is fairly simple to create logic that will handle either
412 p
= popen("lpstat -v", "r");
414 msg_out("rpc.pcnfsd: unable to popen() lp status");
417 while (fgets(buff
, 255, p
) != NULL
) {
418 cp
= strtok(buff
, delims
);
421 if (!strcmp(cp
, "device"))
424 if (!strcmp(cp
, "system"))
428 cp
= strtok(NULL
, delims
);
429 if (!cp
|| strcmp(cp
, "for"))
431 cp
= strtok(NULL
, delims
);
434 curr
= (struct pr_list_item
*)
435 grab(sizeof(struct pr_list_item
));
437 curr
->pn
= strdup(cp
);
439 curr
->remhost
= NULL
;
440 curr
->cm
= strdup("-");
441 curr
->pr_next
= NULL
;
443 cp
= strtok(NULL
, delims
);
445 if (cp
&& !strcmp(cp
, "is"))
446 cp
= strtok(NULL
, delims
);
449 free_pr_list_item(curr
);
453 /* "system" OR "system (as printer pname)" */
454 curr
->remhost
= strdup(cp
);
455 cp
= strtok(NULL
, delims
);
458 curr
->device
= strdup(curr
->pn
);
460 /* "sys (as printer pname)" */
461 if (strcmp(cp
, "as")) {
462 free_pr_list_item(curr
);
465 cp
= strtok(NULL
, delims
);
466 if (!cp
|| strcmp(cp
, "printer")) {
467 free_pr_list_item(curr
);
470 cp
= strtok(NULL
, delims
);
472 free_pr_list_item(curr
);
475 curr
->device
= strdup(cp
);
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
);
485 cp
= strtok(NULL
, delims
);
486 if (!cp
|| strcmp(cp
, "printer")) {
487 free_pr_list_item(curr
);
490 cp
= strtok(NULL
, delims
);
492 free_pr_list_item(curr
);
495 curr
->device
= strdup(cp
);
496 cp
= strtok(NULL
, delims
);
497 if (!cp
|| strcmp(cp
, "on")) {
498 free_pr_list_item(curr
);
501 cp
= strtok(NULL
, delims
);
503 free_pr_list_item(curr
);
506 curr
->remhost
= strdup(cp
);
509 curr
->device
= strdup(cp
);
510 curr
->remhost
= strdup("");
516 last
->pr_next
= curr
;
523 ** Now add on the virtual printers, if any
526 printers
= list_virtual_printers();
528 last
->pr_next
= list_virtual_printers();
546 snprintf(buff
, sizeof(buff
), "%s/lpc status", LPCDIR
);
547 p
= popen(buff
, "r");
549 msg_out("rpc.pcnfsd: unable to popen lpc stat");
552 while (fgets(buff
, 255, p
) != NULL
) {
553 if (isspace((unsigned char)buff
[0]))
556 if ((cp
= strtok(buff
, delims
)) == NULL
)
559 curr
= (struct pr_list_item
*)
560 grab(sizeof(struct pr_list_item
));
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
;
572 last
->pr_next
= curr
;
579 ** Now add on the virtual printers, if any
582 printers
= list_virtual_printers();
584 last
->pr_next
= list_virtual_printers();
596 p
= (void *) malloc(n
);
598 msg_out("rpc.pcnfsd: malloc failure");
605 free_pr_list_item(curr
)
617 free_pr_list_item(curr
->pr_next
); /* recurse */
621 * build_pr_queue: used to show the print queue.
623 * Note that the first thing we do is to discard any
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:
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
637 ** If the first job is actually printing the first line
638 ** is modified, as follows:
640 ** lp-2 root 939 Jul 10 21:56 on lp
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.
652 build_pr_queue(pn
, user
, just_mine
, p_qlen
, p_qshown
)
659 pr_queue last
= NULL
;
660 pr_queue curr
= NULL
;
668 free_pr_queue_item(queue
);
674 pn
= map_printer_name(pn
);
675 if (pn
== NULL
|| !valid_pr(pn
) || suspicious(pn
))
676 return (PI_RES_NO_SUCH_PRINTER
);
678 snprintf(buff
, sizeof(buff
), "/usr/bin/lpstat %s", pn
);
679 p
= su_popen(user
, buff
, MAXTIME_FOR_QUEUE
);
681 msg_out("rpc.pcnfsd: unable to popen() lpstat queue query");
682 return (PI_RES_FAIL
);
684 while (fgets(buff
, 255, p
) != NULL
) {
685 job
= strtok(buff
, delims
);
689 owner
= strtok(NULL
, delims
);
693 totsize
= strtok(NULL
, delims
);
699 if (*p_qshown
> QMAX
)
702 if (just_mine
&& strcasecmp(owner
, user
))
707 curr
= (struct pr_queue_item
*)
708 grab(sizeof(struct pr_queue_item
));
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
;
723 last
->pr_next
= curr
;
733 build_pr_queue(pn
, user
, just_mine
, p_qlen
, p_qshown
)
740 pr_queue last
= NULL
;
741 pr_queue curr
= NULL
;
753 free_pr_queue_item(queue
);
758 pn
= map_printer_name(pn
);
759 if (pn
== NULL
|| suspicious(pn
))
760 return (PI_RES_NO_SUCH_PRINTER
);
762 snprintf(buff
, sizeof(buff
), "%s/lpq -P%s", LPRDIR
, pn
);
764 p
= su_popen(user
, buff
, MAXTIME_FOR_QUEUE
);
766 msg_out("rpc.pcnfsd: unable to popen() lpq");
767 return (PI_RES_FAIL
);
769 while (fgets(buff
, 255, p
) != NULL
) {
770 i
= strlen(buff
) - 1;
771 buff
[i
] = '\0'; /* zap trailing NL */
774 if (!strncasecmp(buff
, "rank", 4))
777 totsize
= &buff
[SIZECOL
- 1];
778 files
= &buff
[FILECOL
- 1];
781 while (cp
> files
&& isspace((unsigned char)*cp
))
784 buff
[FILECOL
- 2] = '\0';
786 cp
= strtok(buff
, delims
);
791 cp
= strtok(NULL
, delims
);
796 cp
= strtok(NULL
, delims
);
803 if (*p_qshown
> QMAX
)
806 if (just_mine
&& strcasecmp(owner
, user
))
811 curr
= (struct pr_queue_item
*)
812 grab(sizeof(struct pr_queue_item
));
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
;
827 last
->pr_next
= curr
;
837 free_pr_queue_item(curr
)
855 free_pr_queue_item(curr
->pr_next
); /* recurse */
861 ** New - SVR4 printer status handling.
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:
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.
871 ** pcdslw not accepting requests since Fri Jul 12 22:30:00 EDT 1991 -
873 ** printer pcdslw disabled since Fri Jul 12 22:15:37 EDT 1991. available.
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.
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.
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.
886 ** pcdslw not accepting requests since Fri Jul 12 22:30:00 EDT 1991 -
888 ** printer pcdslw is idle. enabled since Sat Jul 13 12:05:28 EDT 1991. available.
890 ** Note that these are actual outputs. The format (which is totally
891 ** different from the lpstat in SunOS) seems to break down as
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.
907 get_pr_status(pn
, avail
, printing
, qlen
, needs_operator
, status
)
912 bool_t
*needs_operator
;
919 pirstat stat
= PI_RES_NO_SUCH_PRINTER
;
921 /* assume the worst */
924 *needs_operator
= FALSE
;
928 pn
= map_printer_name(pn
);
929 if (pn
== NULL
|| !valid_pr(pn
) || suspicious(pn
))
930 return (PI_RES_NO_SUCH_PRINTER
);
933 snprintf(cmd
, sizeof(cmd
), "/usr/bin/lpstat -a %s -p %s", pn
, pn
);
937 msg_out("rpc.pcnfsd: unable to popen() lp status");
938 return (PI_RES_FAIL
);
942 while (fgets(buff
, 255, p
) != NULL
) {
943 if (!strncmp(buff
, pn
, n
)) {
944 if (!strstr(buff
, "not accepting"))
948 if (!strncmp(buff
, "printer ", 8)) {
949 if (!strstr(buff
, "disabled"))
951 if (strstr(buff
, "printing"))
952 strlcpy(status
, "printing", sizeof(status
));
954 if (strstr(buff
, "idle"))
955 strlcpy(status
, "idle", sizeof(status
));
958 if (!strncmp(buff
, "UX:", 3)) {
959 stat
= PI_RES_NO_SUCH_PRINTER
;
968 * BSD way: lpc status
971 get_pr_status(pn
, avail
, printing
, qlen
, needs_operator
, status
)
976 bool_t
*needs_operator
;
988 pirstat pstat
= PI_RES_NO_SUCH_PRINTER
;
990 /* assume the worst */
993 *needs_operator
= FALSE
;
997 pn
= map_printer_name(pn
);
998 if (pn
== NULL
|| suspicious(pn
))
999 return (PI_RES_NO_SUCH_PRINTER
);
1001 snprintf(pname
, sizeof(pname
), "%s:", pn
);
1004 snprintf(cmd
, sizeof(cmd
), "%s/lpc status %s", LPCDIR
, pn
);
1005 p
= popen(cmd
, "r");
1007 msg_out("rpc.pcnfsd: unable to popen() lp status");
1008 return (PI_RES_FAIL
);
1010 while (fgets(buff
, 255, p
) != NULL
) {
1011 if (strncmp(buff
, pname
, n
))
1014 ** We have a match. The only failure now is PI_RES_FAIL if
1015 ** lpstat output cannot be decoded
1017 pstat
= PI_RES_FAIL
;
1019 ** The next four lines are usually if the form
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">
1026 while (fgets(buff
, 255, p
) != NULL
&& isspace((unsigned char)buff
[0])) {
1028 while (isspace((unsigned char)*cp
))
1034 while (*cp1
&& *cp1
!= '\n') {
1035 *cp2
++ = tolower((unsigned char)*cp1
);
1041 ** Now buff2 has a lower-cased copy and cp points at the original;
1042 ** both are null terminated without any newline
1044 if (!strncmp(buff2
, "queuing", 7)) {
1045 *avail
= (strstr(buff2
, "enabled") != NULL
);
1048 if (!strncmp(buff2
, "printing", 8)) {
1049 *printing
= (strstr(buff2
, "enabled") != NULL
);
1052 if (isdigit((unsigned char)buff2
[0]) && (strstr(buff2
, "entr") != NULL
)) {
1054 *qlen
= atoi(buff2
);
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
));
1072 * pr_cancel: cancel a print job
1077 ** For SVR4 we have to be prepared for the following kinds of output:
1080 ** request "lp-6" cancelled
1082 ** UX:cancel: WARNING: Request "lp-33" doesn't exist.
1084 ** UX:cancel: WARNING: Request "foo-88" doesn't exist.
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.
1091 ** UX:cancel: WARNING: Can't cancel request "lp-2".
1092 ** TO FIX: You are not allowed to cancel
1093 ** another's request.
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..."
1103 ** The fly in the ointment: all of this can change if these
1104 ** messages are localized..... :-(
1107 pr_cancel(pr
, user
, id
)
1115 pcrstat stat
= PC_RES_NO_SUCH_JOB
;
1117 pr
= map_printer_name(pr
);
1118 if (pr
== NULL
|| suspicious(pr
))
1119 return (PC_RES_NO_SUCH_PRINTER
);
1121 return (PC_RES_NO_SUCH_JOB
);
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
);
1128 if (fgets(resbuf
, 255, fd
) == NULL
)
1131 if (!strstr(resbuf
, "UX:"))
1134 if (strstr(resbuf
, "doesn't exist"))
1135 stat
= PC_RES_NO_SUCH_JOB
;
1137 if (strstr(resbuf
, "not a request id"))
1138 stat
= PC_RES_NO_SUCH_JOB
;
1140 if (strstr(resbuf
, "Can't cancel request"))
1141 stat
= PC_RES_NOT_OWNER
;
1145 if (su_pclose(fd
) == 255)
1146 msg_out("rpc.pcnfsd: su_pclose alert");
1155 pr_cancel(pr
, user
, id
)
1164 pcrstat pstat
= PC_RES_NO_SUCH_JOB
;
1166 pr
= map_printer_name(pr
);
1167 if (pr
== NULL
|| suspicious(pr
))
1168 return (PC_RES_NO_SUCH_PRINTER
);
1170 return (PC_RES_NO_SUCH_JOB
);
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
);
1177 while (fgets(resbuf
, 255, fd
) != NULL
) {
1180 resbuf
[i
- 1] = '\0'; /* trim NL */
1181 if (strstr(resbuf
, "dequeued") != NULL
)
1183 if (strstr(resbuf
, "unknown printer") != NULL
)
1184 pstat
= PC_RES_NO_SUCH_PRINTER
;
1185 if (strstr(resbuf
, "Permission denied") != NULL
)
1186 pstat
= PC_RES_NOT_OWNER
;
1188 if (su_pclose(fd
) == 255)
1189 msg_out("rpc.pcnfsd: su_pclose alert");
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
1200 ** void add_printer_alias(char *printer, char *alias_for, char *command)
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:
1213 ** $FILE - path to the file containing the print data
1214 ** $USER - login of user
1215 ** $HOST - hostname from which job originated
1217 ** Tokens may occur multiple times. If The command includes no
1218 ** $FILE token, the string " $FILE" is silently appended.
1220 ** pr_list list_virtual_printers()
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.
1226 ** char *map_printer_name(char *printer)
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.
1232 ** char *expand_alias(char *printer, char *file, char *user, char *host)
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.
1238 #define NPRINTERDEFS 16
1239 int num_aliases
= 0;
1244 } alias
[NPRINTERDEFS
];
1247 add_printer_alias(printer
, alias_for
, command
)
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
);
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
);
1271 list_virtual_printers()
1273 pr_list first
= NULL
;
1274 pr_list last
= NULL
;
1275 pr_list curr
= NULL
;
1279 if (num_aliases
== 0)
1282 for (i
= 0; i
< num_aliases
; i
++) {
1283 curr
= (struct pr_list_item
*)
1284 grab(sizeof(struct pr_list_item
));
1286 curr
->pn
= strdup(alias
[i
].a_printer
);
1287 if (alias
[i
].a_alias_for
== NULL
)
1288 curr
->device
= strdup("");
1290 curr
->device
= strdup(alias
[i
].a_alias_for
);
1291 curr
->remhost
= strdup("");
1292 curr
->cm
= strdup("(alias)");
1293 curr
->pr_next
= NULL
;
1297 last
->pr_next
= curr
;
1306 map_printer_name(printer
)
1310 for (i
= 0; i
< num_aliases
; i
++) {
1311 if (!strcmp(printer
, alias
[i
].a_printer
))
1312 return (alias
[i
].a_alias_for
);
1318 substitute(string
, token
, data
)
1326 while ((c
= strstr(string
, token
)) != NULL
) {
1328 strlcpy(temp
, string
, sizeof(temp
));
1329 strlcat(temp
, data
, sizeof(temp
));
1331 strlcat(temp
, c
, sizeof(temp
));
1332 strcpy(string
, temp
);
1337 expand_alias(printer
, file
, user
, host
)
1343 static char expansion
[512];
1345 for (i
= 0; i
< num_aliases
; i
++) {
1346 if (!strcmp(printer
, alias
[i
].a_printer
)) {
1347 strlcpy(expansion
, alias
[i
].a_command
,
1349 substitute(expansion
, "$FILE", file
);
1350 substitute(expansion
, "$USER", user
);
1351 substitute(expansion
, "$HOST", host
);