[comp.protocols.nfs] Latest pcnfsd - reposted

geoff@vindaloo.East.Sun.COM (Geoff Arnold @ Sun BOS - R.H. coast near the top) (10/17/90)

Here's the repost of the latest pcnfsd that I promised. This is NOT the
pcnfsd protocol rev.: it's simply the Nth revision of the original code
with numerous cleanups, options, variants, etc. It still doesn't use
syslog(), and if anyone has a syslogging version I'd really apreciate a
copy.

A couple of comments are in order. First, I don't think that I posted
the adpcnfsd.csh script last time: for readers of this group who
probably adb kernels without symbols in their sleep :-) the script is
simplistic, but hey... Secondly, I finally got my hands on a PC running
SVR3 (SVR4 soon, honest!) and ran into some trouble with the System V
variant code which people have passed back to me. If anyone wants to
take the time to hack together the definitive System V pcnfsd, please
let me know.

Geoff

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	r_pcnfsd.c
#	adpcnfsd.csh
# This archive created: Wed Oct 17 08:46:58 1990
export PATH; PATH=/bin:$PATH
if test -f 'r_pcnfsd.c'
then
	echo shar: will not over-write existing file "'r_pcnfsd.c'"
else
sed 's/^X//' << \SHAR_EOF > 'r_pcnfsd.c'
X#ifdef sccs
Xstatic char     sccsid[] = "@(#)r_pcnfsd.c	1.1 9/6/90";
X#endif
X
X/*
X * Copyright (c) 1986, 1987, 1988, 1989, 1990 by Sun Microsystems, Inc.
X */
X
X/**********************************************************************
X *             C U S T O M I Z A T I O N   S E C T I O N              *
X *                                                                    *
X * You may change the following #defines to build different versions  *
X * of rpc.pcnfsd. Note that this version _must_ be run by the         *
X * super-user (either from inetd or at system start-up). Future       *
X * clients may elect to verify that the service is provided on a      *
X * reserved port for added security.                                  *
X *                                                                    *
X **********************************************************************
X */
X
X/*
X * Uncomment the following #define to enable the use of a shadow password file
X */
X/* #define SHADOW_SUPPORT 1 */
X/*
X * Uncomment the following #define to conform to Interactive System's 2.0
X */
X/* #define INTERACTIVE_2POINT0 1 */
X/*
X * Uncomment the following #define to use a cache of recently-used
X * user names. This has certain uses in university and other settings
X * where (1) the pasword file is very large, and (2) a group of users
X * frequently logs in together using the same account (for example,
X * a class userid).
X */
X/* #define USER_CACHE 1 */
X/*
X * Uncomment the following #define to build a System V version
X */
X/* #define SYS5 1 */
X/*
X * Uncomment the following #define to build a typical "local feature":
X * in this case recognizing the special printer names "rotated"
X * and "2column" and using the Adobe "enscript" command to
X * format the output appropriately.
X */
X/* #define HACK_FOR_ROTATED_TRANSCRIPT 1 */
X
X/*
X * The following definitions may be overridden is desired.
X */
X#ifndef SPOOLDIR
X#define SPOOLDIR	"/usr/spool/pcnfs"
X#endif	/* SPOOLDIR */
X
X#ifndef PRINT_COMMAND
X#define PRINT_COMMAND	"lpr"
X#endif	/* PRINT_COMMAND */
X
X 
X#include <sys/types.h>
X#include <stdio.h>
X#include <rpc/rpc.h>
X#include <pwd.h>
X#include <sys/file.h>
X#include <signal.h>
X#include <sys/stat.h>
X#include <sys/ioctl.h>
X#include <sys/socket.h>
X#include <netdb.h>
X#include <errno.h>
X#include <sys/wait.h>
X
X
X#ifdef SHADOW_SUPPORT
X#include <shadow.h>
X#endif
X
X#ifdef INTERACTIVE_2POINT0
X#include <sys/fcntl.h>
X#include <shadow.h>
X#endif
X
X#ifdef USER_CACHE
X#include <string.h>
X#endif
X
X#ifdef SYS5
X#define SIGCHLD SIGCLD
X#endif
X
Xextern char *crypt();
Xextern int errno;
Xint             buggit = 0;
X
X/*
X * *************** RPC parameters ********************
X */
X#define	PCNFSDPROG	(long)150001
X#define	PCNFSDVERS	(long)1
X#define	PCNFSD_AUTH	(long)1
X#define	PCNFSD_PR_INIT	(long)2
X#define	PCNFSD_PR_START	(long)3
X
X/*
X * ************* Other #define's **********************
X */
X#ifndef SPOOLDIR
X#define SPOOLDIR	"/usr/spool/pcnfs"
X#endif	/* SPOOLDIR */
X
X#ifndef PRINT_COMMAND
X#define PRINT_COMMAND	"lpr"
X#endif	/* PRINT_COMMAND */
X
X#ifndef MAXPATHLEN
X#define MAXPATHLEN 1024
X#endif
X#define	zchar		0x5b
X
X#define assert(ex) {if (!(ex)) \
X    {(void)fprintf(stderr,"rpc.pcnfsd: Assertion failed: line %d of %s: \"%s\"\n", \
X    __LINE__, __FILE__, "ex"); \
X    sleep (30); exit(1);}}
X
X
X/*
X * *********** XDR structures, etc. ********************
X */
Xenum arstat {
X	AUTH_RES_OK, AUTH_RES_FAKE, AUTH_RES_FAIL
X};
Xenum pirstat {
X	PI_RES_OK, PI_RES_NO_SUCH_PRINTER, PI_RES_FAIL
X};
Xenum psrstat {
X	PS_RES_OK, PS_RES_ALREADY, PS_RES_NULL, PS_RES_NO_FILE,
X	PS_RES_FAIL
X};
X
Xstruct auth_args {
X	char           *aa_ident;
X	char           *aa_password;
X};
X
Xstruct auth_results {
X	enum arstat     ar_stat;
X	long            ar_uid;
X	long            ar_gid;
X};
X
Xstruct pr_init_args {
X	char           *pia_client;
X	char           *pia_printername;
X};
X
Xstruct pr_init_results {
X	enum pirstat    pir_stat;
X	char           *pir_spooldir;
X};
X
Xstruct pr_start_args {
X	char           *psa_client;
X	char           *psa_printername;
X	char           *psa_username;
X	char           *psa_filename;	/* within the spooldir */
X	char           *psa_options;
X};
X
Xstruct pr_start_results {
X	enum psrstat    psr_stat;
X};
X
X#ifdef USER_CACHE
X#define CACHE_SIZE 16		/* keep it small, as linear searches are
X				 * done */
Xstruct cache {
X	int             uid;
X	int             gid;
X	char            passwd[32];
X	char            username[10];	/* keep this even for machines
X					 * with alignment problems */
X}               User_cache[CACHE_SIZE];
X
X#endif
X
X/*
X * ****************** Misc. ************************
X */
X
Xchar           *authproc();
Xchar           *pr_start();
Xchar           *pr_init();
Xstruct stat     statbuf;
X
Xchar            pathname[MAXPATHLEN];
Xchar            new_pathname[MAXPATHLEN];
Xchar            spoolname[MAXPATHLEN];
X
X
X#ifdef SYS5
Xstruct passwd  *getpwnam();
X
X#endif
X
X
X/*
X * ************** Support procedures ***********************
X */
Xscramble(s1, s2)
X	char           *s1;
X	char           *s2;
X{
X	while (*s1) {
X		*s2++ = (*s1 ^ zchar) & 0x7f;
X		s1++;
X	}
X	*s2 = 0;
X}
X
Xvoid free_child()
X{
X	int             pid;
X	union wait status;
X
X	while((pid = wait(&status)) != -1) {
X
X		if (buggit || status.w_retcode)
X		(void)fprintf(stderr, "FREE_CHILD: process #%d exited with status 0X%x\n",
X			pid, status.w_retcode);
X	}
X	return;
X}
X
Xour_rresvport()
X{
X	struct sockaddr_in sin;
X	int             s, alport = IPPORT_RESERVED - 1;
X
X	sin.sin_family = AF_INET;
X	sin.sin_addr.s_addr = 0;
X	s = socket(AF_INET, SOCK_DGRAM, 0);
X	if (s < 0)
X		return (-1);
X	for (;;) {
X		sin.sin_port = htons((u_short) alport);
X		if (bind(s, (caddr_t) & sin, sizeof(sin)) >= 0)
X			return (s);
X		if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
X			perror("socket");
X			return (-1);
X		}
X		(alport)--;
X		if (alport == IPPORT_RESERVED / 2) {
X			(void)fprintf(stderr, "socket: All ports in use\n");
X			return (-1);
X		}
X	}
X}
X
X
X/*
X * *************** XDR procedures *****************
X */
Xbool_t
Xxdr_auth_args(xdrs, aap)
X	XDR            *xdrs;
X	struct auth_args *aap;
X{
X	return (xdr_string(xdrs, &aap->aa_ident, 32) &&
X		xdr_string(xdrs, &aap->aa_password, 64));
X}
X
Xbool_t
Xxdr_auth_results(xdrs, arp)
X	XDR            *xdrs;
X	struct auth_results *arp;
X{
X	return (xdr_enum(xdrs, (enum_t *)&arp->ar_stat) &&
X		xdr_long(xdrs, &arp->ar_uid) &&
X		xdr_long(xdrs, &arp->ar_gid));
X}
X
Xbool_t
Xxdr_pr_init_args(xdrs, aap)
X	XDR            *xdrs;
X	struct pr_init_args *aap;
X{
X	return (xdr_string(xdrs, &aap->pia_client, 64) &&
X		xdr_string(xdrs, &aap->pia_printername, 64));
X}
X
Xbool_t
Xxdr_pr_init_results(xdrs, arp)
X	XDR            *xdrs;
X	struct pr_init_results *arp;
X{
X	return (xdr_enum(xdrs, (enum_t *)&arp->pir_stat) &&
X		xdr_string(xdrs, &arp->pir_spooldir, 255));
X}
X
Xbool_t
Xxdr_pr_start_args(xdrs, aap)
X	XDR            *xdrs;
X	struct pr_start_args *aap;
X{
X	return (xdr_string(xdrs, &aap->psa_client, 64) &&
X		xdr_string(xdrs, &aap->psa_printername, 64) &&
X		xdr_string(xdrs, &aap->psa_username, 64) &&
X		xdr_string(xdrs, &aap->psa_filename, 64) &&
X		xdr_string(xdrs, &aap->psa_options, 64));
X}
X
Xbool_t
Xxdr_pr_start_results(xdrs, arp)
X	XDR            *xdrs;
X	struct pr_start_results *arp;
X{
X	return (xdr_enum(xdrs, (enum_t *)&arp->psr_stat));
X}
X
X
X
X
X/*
X * ********************** main *********************
X */
X
Xmain(Argc, Argv)
X	int             Argc;
X	char           *Argv[];
X{
X	int             f1, f2, f3;
X	int             FromInetd;
X	int             socknum;
X	SVCXPRT        *TransportHandle;
X	void            Dispatch();
X
X	(void)strcpy(spoolname, SPOOLDIR);
X
X	setbuf(stderr, (char *)NULL);
X
X	if (geteuid() != 0) {
X		(void)fprintf(stderr, "rpc.pcnfsd must be run by 'root'\n");
X		exit(1);
X	}
X	/*
X	 * If we're called from inetd: - an open RPC socket is passed as
X	 * fd 0. and note that we are already registered with the
X	 * portmapper. Otherwise: - we must parse any command-line
X	 * arguments which may be present. - we must create an RPC socket
X	 * (svcudp_create will do this). - we are not yet registered with
X	 * the portmapper, and must do so.
X	 */
X	FromInetd = issock(0);
X
X
X	if (!FromInetd) {
X		while (++Argv, --Argc > 0) {
X			if (strcmp(*Argv, "-d") == 0) {
X				++buggit;
X				continue;
X			}
X			if (strcmp(*Argv, "-s") == 0) {
X				if (!(++Argv, --Argc > 0)) {
X					(void)fprintf(stderr,
X						"pc-nfsd error: -s option must be followed by a spooling directory path\n");
X					exit(1);
X				}
X				(void)strcpy(spoolname, *Argv);
X				continue;
X			}
X			if (strncmp(*Argv, "-s", 2) == 0) {
X				(void)strcpy(spoolname, &(*Argv)[2]);
X				continue;
X			}
X		}
X	}
X	if (!FromInetd && !buggit) {
X		switch (fork()) {
X		case 0:
X			break;
X		case -1:
X			perror("pc-nfsd: fork failed");
X			exit(1);
X		default:
X			exit(0);
X		}
X	}
X	if (!buggit) {
X		/*
X		 * Can't mess with STDIN if invoked from inetd, 'cause our
X		 * incoming RPC request datagram is passed in on STDIN.
X		 */
X		if (!FromInetd) {
X			if ((f1 = open("/dev/null", O_RDONLY)) == -1) {
X				(void)fprintf(stderr, "pc-nfsd: couldn't open /dev/null\n");
X				exit(1);
X			}
X			(void) dup2(f1, 0);
X			(void) close(f1);
X		}
X		if ((f2 = open("/dev/console", O_WRONLY)) == -1) {
X			(void)fprintf(stderr, "pc-nfsd: couldn't open /dev/console\n");
X			exit(1);
X		}
X		(void) dup2(f2, 1);
X		(void) close(f2);
X
X		if ((f3 = open("/dev/console", O_WRONLY)) == -1) {
X			(void)fprintf(stderr, "pc-nfsd: couldn't open /dev/console\n");
X			exit(1);
X		}
X		(void) dup2(f3, 2);
X		(void) close(f3);
X
X#ifdef TIOCNOTTY
X		/*
X		 * Disconnect ourself from the control tty:
X		 */
X		if ((f1 = open("/dev/tty", O_RDONLY)) >= 0) {
X			(void) ioctl(f1, TIOCNOTTY, (char *) 0);
X			(void) close(f1);
X		}
X#endif
X	}
X	/*
X	 * Set up our RPC environment:
X	 */
X	if (FromInetd) {
X		assert((TransportHandle = svcudp_create(0)) != (SVCXPRT *)NULL);
X		assert(svc_register(TransportHandle, PCNFSDPROG, PCNFSDVERS, Dispatch, 0) != 0);
X	} else {
X		assert((socknum = our_rresvport()) >= 0);
X		assert((TransportHandle = svcudp_create(socknum)) != (SVCXPRT *)NULL);
X		pmap_unset(PCNFSDPROG, PCNFSDVERS);
X		assert(svc_register(TransportHandle, PCNFSDPROG, PCNFSDVERS, Dispatch, IPPROTO_UDP) != 0);
X	}
X
X	(void)mkdir(spoolname, 0777);	/* just in case, ignoring the result */
X	if (stat(spoolname, &statbuf) || !(statbuf.st_mode & S_IFDIR)) {
X		(void)fprintf(stderr, "pc-nfsd: invalid spool directory %s\n", spoolname);
X		exit(1);
X	}
X	svc_run();
X	(void)fprintf(stderr, "pc-nfsd: error: svc_run returned\n");
X	sleep(30);		/* just in case inetd wants to fork us
X				 * again */
X	exit(1);
X/*NOTREACHED*/
X
X	return(0);
X}
X
X
X/*
X * ******************* RPC procedures **************
X */
Xvoid 
XDispatch(ServiceRequest, Transport)
X	struct svc_req *ServiceRequest;
X	SVCXPRT        *Transport;
X{
X	char           *outdata;
X 	union {
X 		struct auth_args xdrb_aa;
X 		struct pr_init_args xdrb_pia;
X 		struct pr_start_args xdrb_psa;
X 		char	xdrb_buf[UDPMSGSIZE];
X 	} xdrbuf;
X
X
X	bzero(xdrbuf.xdrb_buf, sizeof(xdrbuf));	/* said to be required... */
X
X	switch (ServiceRequest->rq_proc) {
X	case 0:
X		assert(svc_sendreply(Transport, xdr_void, (caddr_t)NULL));
X		break;
X	case PCNFSD_AUTH:
X		assert(svc_getargs(Transport, xdr_auth_args, xdrbuf.xdrb_buf));
X		outdata = authproc(&xdrbuf.xdrb_aa);
X		assert(svc_sendreply(Transport, xdr_auth_results, outdata));
X		break;
X	case PCNFSD_PR_INIT:
X		assert(svc_getargs(Transport, xdr_pr_init_args, xdrbuf.xdrb_buf));
X		outdata = pr_init(&xdrbuf.xdrb_pia);
X		assert(svc_sendreply(Transport, xdr_pr_init_results, outdata));
X		break;
X	case PCNFSD_PR_START:
X		assert(svc_getargs(Transport, xdr_pr_start_args, xdrbuf.xdrb_buf));
X		outdata = pr_start(&xdrbuf.xdrb_psa);
X		assert(svc_sendreply(Transport, xdr_pr_start_results, outdata));
X		break;
X	default:
X		(void)fprintf(stderr,
X			"pc-nfsd error: unknown function %d called in Dispatch()\n",
X			ServiceRequest->rq_proc);
X		break;
X	}
X	return;
X}
X
X
Xstruct passwd  *
Xget_password(username)
X	char           *username;
X{
X	struct passwd  *p;
X	static struct passwd localp;
X	char           *pswd;
X
X#ifdef SHADOW_SUPPORT
X	struct spwd    *sp;
X	int             shadowfile;
X
X#endif
X
X#ifdef SHADOW_SUPPORT
X	/*
X	 * Check the existence of SHADOW.  If it is there, then we are
X	 * running a two-password-file system.
X	 */
X	if (access(SHADOW, 0))
X		shadowfile = 0;	/* SHADOW is not there */
X	else
X		shadowfile = 1;
X
X	setpwent();
X	if (shadowfile)
X		(void) setspent();	/* Setting the shadow password
X					 * file */
X	if ((p = getpwnam(username)) == (struct passwd *)NULL ||
X		(shadowfile && (sp = getspnam(username)) == (struct spwd *)NULL))
X		return ((struct passwd *)NULL);
X
X	if (shadowfile) {
X		pswd = sp->sp_pwdp;
X		(void) endspent();
X	} else
X		pswd = p->pw_passwd;
X
X
X#else
X	p = getpwnam(username);
X	if (p == (struct passwd *)NULL)
X		return ((struct passwd *)NULL);
X	pswd = p->pw_passwd;
X#endif
X
X#ifdef INTERACTIVE_2POINT0
X	/* We may have an 'x' in which case look in /etc/shadow .. */
X	if (((strlen(pswd)) == 1) && pswd[0] == 'x') {
X		struct spwd    *shadow = getspnam(username);
X
X		if (!shadow)
X			return ((struct passwd *)NULL);
X		pswd = shadow->sp_pwdp;
X	}
X#endif
X	localp = *p;
X	localp.pw_passwd = pswd;
X	return (&localp);
X}
X
X
Xchar           *
Xauthproc(a)
X	struct auth_args *a;
X{
X	static struct auth_results r;
X	char            username[32];
X	char            password[64];
X	int             c1, c2;
X	struct passwd  *p;
X
X#ifdef SHADOW_SUPPORT
X	struct spwd    *sp;
X	int             shadowfile;
X
X#endif
X#ifdef USER_CACHE
X	int             cache_entry;;
X#endif
X
X	r.ar_stat = AUTH_RES_FAIL;	/* assume failure */
X	r.ar_uid = -2;
X	r.ar_gid = -2;
X
X	scramble(a->aa_ident, username);
X	scramble(a->aa_password, password);
X
X	if (buggit)
X		(void)fprintf(stderr, "AUTHPROC username=%s\n", username);
X
X#ifdef USER_CACHE
X	cache_entry = check_cache(username);
X	if (cache_entry >= 0) {
X		if (buggit)
X			(void)fprintf(stderr, "...cache hit?\n");
X		c1 = strlen(password);
X		c2 = strlen(User_cache[cache_entry].passwd);
X		if ((!c1 && !c2) ||
X			!(strcmp(User_cache[cache_entry].passwd,
X					crypt(password, User_cache[cache_entry].passwd)))) {
X			if (buggit)
X				(void)fprintf(stderr, "...cache hit\n");
X			r.ar_stat = AUTH_RES_OK;
X			r.ar_uid = User_cache[cache_entry].uid;
X			r.ar_gid = User_cache[cache_entry].gid;
X			return ((char *) &r);
X		}
X		User_cache[cache_entry].username[0] = '\0';	/* nuke entry */
X	}
X	if (buggit)
X		(void)fprintf(stderr, "...cache miss\n");
X#endif
X	p = get_password(username);
X	if (p == (struct passwd *)NULL)
X		return ((char *) &r);
X
X	c1 = strlen(password);
X	c2 = strlen(p->pw_passwd);
X	if ((c1 && !c2) || (c2 && !c1) ||
X		(strcmp(p->pw_passwd, crypt(password, p->pw_passwd)))) {
X		return ((char *) &r);
X	}
X	r.ar_stat = AUTH_RES_OK;
X	r.ar_uid = p->pw_uid;
X	r.ar_gid = p->pw_gid;
X#ifdef USER_CACHE
X	add_cache_entry(p);
X#endif
X	return ((char *) &r);
X}
X
X
Xchar           *
Xpr_init(pi_arg)
X	struct pr_init_args *pi_arg;
X{
X	int             dir_mode = 0777;
X	static struct pr_init_results pi_res;
X
X	mkdir(spoolname, dir_mode);	/* just in case, ignoring the result */
X	chmod(spoolname, dir_mode);
X
X	/* get pathname of current directory and return to client */
X	(void)strcpy(pathname, spoolname);	/* first the spool area */
X	(void)strcat(pathname, "/");	/* append a slash */
X	(void)strcat(pathname, pi_arg->pia_client);
X	/* now the host name */
X	mkdir(pathname, dir_mode);	/* ignore the return code */
X	if (stat(pathname, &statbuf) || !(statbuf.st_mode & S_IFDIR)) {
X		(void)fprintf(stderr,
X			"pc-nfsd: unable to create spool directory %s\n",
X			pathname);
X		pathname[0] = 0;/* null to tell client bad vibes */
X		pi_res.pir_stat = PI_RES_FAIL;
X	} else {
X		pi_res.pir_stat = PI_RES_OK;
X	}
X	pi_res.pir_spooldir = &pathname[0];
X	chmod(pathname, dir_mode);
X
X	if (buggit)
X		(void)fprintf(stderr, "PR_INIT pathname=%s\n", pathname);
X
X	return ((char *) &pi_res);
X}
X
Xchar           *
Xpr_start(ps_arg)
X	struct pr_start_args *ps_arg;
X{
X	static struct pr_start_results ps_res;
X	int             pid;
X	char            printer_opt[64];
X	char            jobname_opt[64];
X	char            clientname_opt[64];
X	char            snum[20];
X#ifdef HACK_FOR_ROTATED_TRANSCRIPT
X	char            scratch[512];
X#endif
X
X
X
X	signal(SIGCHLD, free_child);	/* when child terminates it sends */
X	/* a signal which we must get */
X	(void)strcpy(pathname, spoolname);	/* build filename */
X	(void)strcat(pathname, "/");
X	(void)strcat(pathname, ps_arg->psa_client);	/* /spool/host */
X	(void)strcat(pathname, "/");	/* /spool/host/ */
X	(void)strcat(pathname, ps_arg->psa_filename);	/* /spool/host/file */
X
X	if (buggit) {
X		(void)fprintf(stderr, "PR_START pathname=%s\n", pathname);
X		(void)fprintf(stderr, "PR_START username= %s\n", ps_arg->psa_username);
X		(void)fprintf(stderr, "PR_START client= %s\n", ps_arg->psa_client);
X	}
X	if (stat(pathname, &statbuf)) {
X		/*
X		 * We can't stat the file. Let's try appending '.spl' and
X		 * see if it's already in progress.
X		 */
X
X		if (buggit)
X			(void)fprintf(stderr, "...can't stat it.\n");
X
X		(void)strcat(pathname, ".spl");
X		if (stat(pathname, &statbuf)) {
X			/*
X			 * It really doesn't exist.
X			 */
X
X			if (buggit)
X				(void)fprintf(stderr, "...PR_START returns PS_RES_NO_FILE\n");
X
X			ps_res.psr_stat = PS_RES_NO_FILE;
X			return ((char *) &ps_res);
X		}
X		/*
X		 * It is already on the way.
X		 */
X
X		if (buggit)
X			(void)fprintf(stderr, "...PR_START returns PS_RES_ALREADY\n");
X
X		ps_res.psr_stat = PS_RES_ALREADY;
X		return ((char *) &ps_res);
X	}
X	if (statbuf.st_size == 0) {
X		/*
X		 * Null file - don't print it, just kill it.
X		 */
X		unlink(pathname);
X
X		if (buggit)
X			(void)fprintf(stderr, "...PR_START returns PS_RES_NULL\n");
X
X		ps_res.psr_stat = PS_RES_NULL;
X		return ((char *) &ps_res);
X	}
X	/*
X	 * The file is real, has some data, and is not already going out.
X	 * We rename it by appending '.spl' and exec "lpr" to do the
X	 * actual work.
X	 */
X	(void)strcpy(new_pathname, pathname);
X	(void)strcat(new_pathname, ".spl");
X
X	if (buggit)
X		(void)fprintf(stderr, "...renaming %s -> %s\n", pathname, new_pathname);
X
X	/*
X	 * See if the new filename exists so as not to overwrite it.
X	 */
X
X
X	if (!stat(new_pathname, &statbuf)) {
X		(void)strcpy(new_pathname, pathname);	/* rebuild a new name */
X		(void)sprintf(snum, "%d", rand());	/* get some number */
X		(void)strncat(new_pathname, snum, 3);
X		(void)strcat(new_pathname, ".spl");	/* new spool file */
X		if (buggit)
X			(void)fprintf(stderr, "...created new spl file -> %s\n", new_pathname);
X
X	}
X	if (rename(pathname, new_pathname)) {
X		/*
X		 * CAVEAT: Microsoft changed rename for Microsoft C V3.0.
X		 * Check this if porting to Xenix.
X		 */
X		/*
X		 * Should never happen.
X		 */
X		(void)fprintf(stderr, "pc-nfsd: spool file rename (%s->%s) failed.\n",
X			pathname, new_pathname);
X		ps_res.psr_stat = PS_RES_FAIL;
X		return ((char *) &ps_res);
X	}
X	pid = fork();
X	if (pid == 0) {
X		/*
X		 * FLUKE jps 28-jul-86 - Invoke lpr as the requesting
X		 * user.
X		 * 
X		 * If possible, invoke lpr under the user-id/group-id of the
X		 * person (apparently) making this RPC request.  Good for
X		 * accounting, proper banner page, etc.  It is not
X		 * mandatory.
X		 */
X		struct passwd  *pw = getpwnam(ps_arg->psa_username);
X
X		if (buggit)
X			(void)fprintf(stderr, "username is %s\n", ps_arg->psa_username);
X		if (pw) {
X			if (buggit)
X				(void)fprintf(stderr, "uid is %d\ngid is %d\n",
X					pw->pw_uid, pw->pw_gid);
X			setreuid(pw->pw_uid, pw->pw_uid);
X			setregid(pw->pw_gid, pw->pw_gid);
X
X			/*
X			 * PC-NFS doesn't pass us any filename to show on
X			 * the banner page, so we blank this field out.
X			 * That's batter than showing the pseudo-random
X			 * temporary file name used internally (or the
X			 * UNIX-ism "(stdin)").
X			 */
X			sprintf(printer_opt, "-P%s", ps_arg->psa_printername);
X			sprintf(jobname_opt, "-J ");
X			sprintf(clientname_opt, "-C%s", ps_arg->psa_client);
X		} else {
X			/*
X			 * We don't know the user's identity, so the
X			 * printout will end up being enqueued by root.
X			 * We do want the user's name to appear on the
X			 * banner page, so we slip it in via the -J
X			 * option.
X			 */
X			sprintf(printer_opt, "-P%s", ps_arg->psa_printername);
X			sprintf(jobname_opt, "-J%s", ps_arg->psa_username);
X			sprintf(clientname_opt, "-C%s", ps_arg->psa_client);
X		}
X#ifdef HACK_FOR_ROTATED_TRANSCRIPT
X		if (!strcmp(ps_arg->psa_printername, "rotated")) {
X			sprintf(scratch, "enscript -lrq -fCourier7  %s",
X				new_pathname);
X			if (buggit)
X				(void)fprintf(stderr, "system(%s)\n", scratch);
X			system(scratch);
X			unlink(new_pathname);
X			exit(0);
X		}
X		if (!strcmp(ps_arg->psa_printername, "2column")) {
X			sprintf(scratch, "enscript -2rqG -J\"PC-NFS spool file\" %s",
X				new_pathname);
X			if (buggit)
X				(void)fprintf(stderr, "system(%s)\n", scratch);
X			system(scratch);
X			unlink(new_pathname);
X			exit(0);
X		}
X#endif /* HACK_FOR_ROTATED_TRANSCRIPT */
X
X		if (ps_arg->psa_options[1] == 'd') {
X			/*
X			 * This is a Diablo print stream. Apply the ps630
X			 * filter with the appropriate arguments.
X			 */
X			if (buggit)
X				(void)fprintf(stderr, "...run_ps630 invoked\n");
X			(void)run_ps630(new_pathname, ps_arg->psa_options);
X		}
X		/*
X		 * New - let's close up 0, 1 and 2 to force getlogin to
X		 * fail in lpr
X		 */
X		close(0);
X		close(1);
X		close(2);
X
X		execlp("/usr/ucb/lpr",
X			PRINT_COMMAND,
X			"-s",
X			"-r",
X			printer_opt,
X			jobname_opt,
X			clientname_opt,
X			new_pathname,
X			0);
X		perror("pc-nfsd: exec lpr failed");
X		exit(1);	/* end of child process */
X	} else if (pid == -1) {
X		perror("pc-nfsd: fork failed");
X
X		if (buggit)
X			(void)fprintf(stderr, "...PR_START returns PS_RES_FAIL\n");
X
X		ps_res.psr_stat = PS_RES_FAIL;
X	} else {
X
X		if (buggit)
X			(void)fprintf(stderr, "...forked child #%d\n", pid);
X
X
X		if (buggit)
X			(void)fprintf(stderr, "...PR_START returns PS_RES_OK\n");
X
X		ps_res.psr_stat = PS_RES_OK;
X	}
X	return ((char *) &ps_res);
X}
X
X
Xchar           *
Xmapfont(f, i, b)
X	char            f;
X	char            i;
X	char            b;
X{
X	static char     fontname[64];
X
X	fontname[0] = 0;	/* clear it out */
X
X	switch (f) {
X	case 'c':
X		(void)strcpy(fontname, "Courier");
X		break;
X	case 'h':
X		(void)strcpy(fontname, "Helvetica");
X		break;
X	case 't':
X		(void)strcpy(fontname, "Times");
X		break;
X	default:
X		(void)strcpy(fontname, "Times-Roman");
X		goto finis ;
X	}
X	if (i != 'o' && b != 'b') {	/* no bold or oblique */
X		if (f == 't')	/* special case Times */
X			(void)strcat(fontname, "-Roman");
X		goto finis;
X	}
X	(void)strcat(fontname, "-");
X	if (b == 'b')
X		(void)strcat(fontname, "Bold");
X	if (i == 'o')		/* o-blique */
X		(void)strcat(fontname, f == 't' ? "Italic" : "Oblique");
X
Xfinis:	return (&fontname[0]);
X}
X
X/*
X * run_ps630 performs the Diablo 630 emulation filtering process. ps630
X * was broken in certain Sun releases: it would not accept point size or
X * font changes. If your version is fixed, undefine the symbol
X * PS630_IS_BROKEN and rebuild pc-nfsd.
X */
X/* #define PS630_IS_BROKEN 1 */
X
Xrun_ps630(file, options)
X	char           *file;
X	char           *options;
X{
X	char            temp_file[256];
X	char            commbuf[256];
X	int             i;
X
X	(void)strcpy(temp_file, file);
X	(void)strcat(temp_file, "X");	/* intermediate file name */
X
X#ifndef PS630_IS_BROKEN
X	(void)sprintf(commbuf, "ps630 -s %c%c -p %s -f ",
X		options[2], options[3], temp_file);
X	(void)strcat(commbuf, mapfont(options[4], options[5], options[6]));
X	(void)strcat(commbuf, " -F ");
X	(void)strcat(commbuf, mapfont(options[7], options[8], options[9]));
X	(void)strcat(commbuf, "  ");
X	(void)strcat(commbuf, file);
X#else	/* PS630_IS_BROKEN */
X	/*
X	 * The pitch and font features of ps630 appear to be broken at
X	 * this time.
X	 */
X	sprintf(commbuf, "ps630 -p %s %s", temp_file, file);
X#endif	/* PS630_IS_BROKEN */
X
X
X	if (i = system(commbuf)) {
X		/*
X		 * Under (un)certain conditions, ps630 may return -1 even
X		 * if it worked. Hence the commenting out of this error
X		 * report.
X		 */
X		 /* (void)fprintf(stderr, "\n\nrun_ps630 rc = %d\n", i) */ ;
X		/* exit(1); */
X	}
X	if (rename(temp_file, file)) {
X		perror("run_ps630: rename");
X		exit(1);
X	}
X	return(i); /* never used, but keeps lint happy */
X}
X
X/*
X * Determine if a descriptor belongs to a socket or not
X */
Xissock(fd)
X	int             fd;
X{
X	struct stat     st;
X
X	if (fstat(fd, &st) < 0) {
X		return (0);
X	}
X	/*
X	 * SunOS returns S_IFIFO for sockets, while 4.3 returns 0 and does
X	 * not even have an S_IFIFO mode.  Since there is confusion about
X	 * what the mode is, we check for what it is not instead of what
X	 * it is.
X	 */
X	switch (st.st_mode & S_IFMT) {
X	case S_IFCHR:
X	case S_IFREG:
X	case S_IFLNK:
X	case S_IFDIR:
X	case S_IFBLK:
X		return (0);
X	default:
X		return (1);
X	}
X}
X
X
X
X#ifdef USER_CACHE
Xint
Xcheck_cache(name)
X	char           *name;
X{
X	int             i;
X
X	for (i = 0; i < CACHE_SIZE; i++) {
X		if (!strcmp(User_cache[i].username, name))
X			return (i);
X	}
X	return (-1);
X}
X
Xadd_cache_entry(p)
X	struct passwd  *p;
X{
X	int             i;
X
X	for (i = CACHE_SIZE - 1; i > 0; i--)
X		User_cache[i] = User_cache[i - 1];
X	User_cache[0].uid = p->pw_uid;
X	User_cache[0].gid = p->pw_gid;
X	(void)strcpy(User_cache[0].passwd, p->pw_passwd);
X	(void)strcpy(User_cache[0].username, p->pw_name);
X}
X
X
X#endif				/* USER_CACHE */
SHAR_EOF
fi # end of overwriting check
if test -f 'adpcnfsd.csh'
then
	echo shar: will not over-write existing file "'adpcnfsd.csh'"
else
sed 's/^X//' << \SHAR_EOF > 'adpcnfsd.csh'
X#!/bin/csh
X#  @(#)adpcnfsd.csh	9.5 	10/17/90
X#
X# adpcnfsd.csh
X#
X# This script will make rpc.pcnfsd, move it to the correct place on your
X# system, then edit the appropriate file to allow the daemon to start up
X# whenever you reboot your system.
X#
X# The preferred method of starting rpc.pcnfsd is from inetd.  This script will
X# modify either your /etc/servers file (for SunOS 3.X) or /etc/inetd.conf
X# file (for SunOS 4.X).  If you wish to instead start rpc.pcnfsd from 
X# rc.local as a normal daemon, then you should refer to the section in the
X# Installing PC-NFS guide that discusses installing server software.  Also
X# make sure you read the READ THIS FIRST guide.
X#
X#
X# To use this script you must first have copied the following files from the
X# PC-NFS "Server 1" diskette:
X#		adpcnfsd.csh   ( this file )
X#		r_pcnfsd.c
X# to some place on the system you are going to perform the install.  Usually
X# /usr/tmp is a good place. (Note that the file name "r_pcnfsd.c"
X# is dictated by DOS file name conventions: this script will rename
X# the file to "rpc.pcnfsd.c".)
X#
X# You then must become root on the machine you are intending to add the
X# rpc.pcnfsd to.  Then run this script.  There are no arguments.
X#
X
X
Xif ( $#argv > 0 ) then
X	echo " "
X	echo "   Usage: adpcnfsd"
X	echo " "
X	exit (1)
Xendif
X
Xecho " "
Xecho "Add RPC.PCNFSD shell script"
Xecho " "
Xecho
X
Xecho "Renaming R_PCNFSD.C as RPC.PCNFSD.C"
Xmv r_pcnfsd.c rpc.pcnfsd.c
Xecho "Compiling RPC.PCNFSD"
Xcc -o rpc.pcnfsd rpc.pcnfsd.c
Xstrip rpc.pcnfsd
X
Xecho " "
Xecho "Compilation complete."
Xecho "Moving RPC.PCNFSD to /usr/etc directory."
X
Xmv rpc.pcnfsd /usr/etc
X
Xecho " "
X
Xif ( -f /etc/servers ) then
Xecho "Editing servers file."
Xed  - /etc/servers  <<EOFSTRING
Xa
Xrpc	udp	/usr/etc/rpc.pcnfsd	150001	1
X.
Xw
Xq
XEOFSTRING
X#
X
Xelse if ( -f /etc/inetd.conf ) then
Xecho "Editing inetd.conf file."
Xed  - /etc/inetd.conf  <<EOFSTRING
Xa
Xpcnfsd/1 dgram	rpc/udp	wait	root	/usr/etc/rpc.pcnfsd	rpc.pcnfsd
X.
Xw
Xq
XEOFSTRING
X#
Xelse
Xecho "No /etc/servers or /etc/inetd.conf file."
Xecho "RPC.PCNFSD built - you should make sure it is started each time"
Xecho "your system is booted."
Xecho " "
Xexit(1)
Xendif
X
Xecho " "
Xecho "ADPCNFSD.CSH script completed."
Xecho "You can start the daemon now by typing /usr/etc/rpc.pcnfsd."
Xecho " "
Xecho " "
X
X
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- Geoff Arnold, PC-NFS architect, Sun Microsystems. (geoff@East.Sun.COM)   --
   *** "Now is no time to speculate or hypothecate, but rather a time ***
   *** for action, or at least not a time to rule it out, though not  ***
   *** necessarily a time to rule it in, either." - George Bush       ***