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