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