v16i028: Public lineprinter spooler package, Part15/16

Rich Salz rsalz at uunet.uu.net
Sat Sep 17 04:28:42 AEST 1988


Submitted-by: papowell at julius.cs.umn.edu
Posting-number: Volume 16, Issue 28
Archive-name: plp/part15

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 15 (of 16)."
# Contents:  src/lp.h src/utils.c
# Wrapped by papowell at attila on Wed Aug 10 10:45:12 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/lp.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/lp.h'\"
else
echo shar: Extracting \"'src/lp.h'\" \(23350 characters\)
sed "s/^X//" >'src/lp.h' <<'END_OF_FILE'
X/*************************************************************************
X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
X *************************************************************************
X * MODULE: lp.h
X * Includes and definitions for all programs
X *************************************************************************
X * Revision History: Created Sun Jan  3 15:37:08 CST 1988
X * $Header: lp.h,v 3.2 88/06/24 17:15:52 papowell Exp $
X * $Log:	lp.h,v $
X * Revision 3.2  88/06/24  17:15:52  papowell
X * MODS for VAX 4.3BSD UNIX
X * 
X * 
X * Revision 3.1  88/06/18  09:34:30  papowell
X * Version 3.0- Distributed Sat Jun 18 1988
X * 
X * Revision 2.3  88/05/29  13:09:55  papowell
X * Added Header string for RCS purposes
X * 
X * Revision 2.2  88/05/14  10:18:16  papowell
X * Use long format for job file names;
X * Added 'fd', no forward flag;
X * Control file has to have hostname and origination agree.
X * 
X * Revision 2.1  88/05/09  10:10:48  papowell
X * PLP: Released Version
X * 
X * Revision 1.12  88/05/09  10:03:39  papowell
X * Revised effects of -h option
X * 
X * Revision 1.11  88/05/05  20:08:53  papowell
X * Added a NOHEADER option that allows user to suppress banner
X * 
X * Revision 1.10  88/04/29  07:48:55  papowell
X * Added USE_LOCKF flags
X * 
X * Revision 1.9  88/04/28  17:32:47  papowell
X * fixed Data General Options
X * 
X * Revision 1.8  88/04/21  21:49:40  papowell
X * BADSETREUID: this flag is provided for situations where setreuid()
X * does not allow changing back to root after changing to a user.
X * This is a problem in security,  and should be avoided.
X * 
X * Revision 1.7  88/04/07  12:29:13  papowell
X * Removed AF_UNIX socket definitions
X * 
X * Revision 1.6  88/04/07  09:09:23  papowell
X * Apollo Workstation Modifications
X * 
X * Revision 1.5  88/04/06  12:13:40  papowell
X * Minor updates, changes in error message formats.
X * Elimination of the AF_UNIX connections, use AF_INET only.
X * Better error messages.
X * 
X * Revision 1.4  88/03/25  14:59:54  papowell
X * Debugged Version:
X * 1. Added the PLP control file first transfer
X * 2. Checks for MX during file transfers
X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities;
X * 	apparently they open files and then assume that they will stay
X * 	open.
X * 4. Made sure that stdin, stdout, stderr was available at all times.
X * 
X * Revision 1.3  88/03/11  19:29:10  papowell
X * Minor Changes, Updates
X * 
X * Revision 1.2  88/03/05  15:02:09  papowell
X * Minor Corrections,  Lint Problems
X * 
X * Revision 1.1  88/03/01  12:42:54  papowell
X * Initial revision
X * 
X *************************************************************************/
X
X/*************************************************************************
X * Make sure that you set the DEBUG flag if XPERIMENT is set
X *
X *************************************************************************/
X#ifdef XPERIMENT
X#define DEBUG 1
X#endif XPERIMENT
X/*************************************************************************
X * Portability Definitions
X * Several systems do not have the same definitions for these items.
X * You will have to add your own local definitions.
X * Ahh...  Dream of the day of a standard set of definitions
X *************************************************************************/
X#ifdef IS_SUN
X#define uid_t	int	/* keep lint on the sun happy */
X#endif IS_SUN
X
X#ifdef IS_VAX4BSD
X#define SETREUID4BSD 1
X#endif IS_VAX4BSD
X
X#ifdef IS_DATAGEN
X#define fd_set	int	/* keep lint happy */
X#define uid_t	int	/* keep lint happy */
X#define NOSYMLINK	/* no symbolic links */
X#define USE_LOCKF	/* use the lockf() function for locking */
X#define F_TLOCK	2	/* test and lock a section (non-blocking) */
X#endif IS_DATAGEN
X
X#ifdef IS_UMAX
X#undef B19200
X#undef B38400
X#define B19200 EXTA
X#define B38400 EXTB
X#define time_t long
X#define uid_t int
X#endif IS_UMAX
X
X#ifdef IS_APOLLO
X#undef B19200
X#undef B38400
X#define B19200 EXTA
X#define B38400 EXTB
X#define uid_t int
X#define USE_STRINGS		/* use <strings.h> instead of <string.h> */
X#define NO_A_OUT_H		/* no a.out.h include file */
X#endif IS_APOLLO
X
X/******************************************************************
X * System Include Files
X ******************************************************************/
X#include <stdio.h>
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/file.h>
X#include <sys/dir.h>
X#include <sys/time.h>
X#include <sys/stat.h>
X#include <sys/socket.h>
X#include <sys/resource.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <pwd.h>
X#include <signal.h>
X#include <sys/wait.h>
X#include <sgtty.h>
X#include <ctype.h>
X#ifdef USE_STRINGS
X#include <strings.h>
X#else USE_STRINGS
X#include <string.h>
X#endif USE_STRINGS
X#include <errno.h>
X#include <grp.h>
X
X
X/******************************************************************
X * Declare Standard Routines.
X * Note: most of these are not declared in include files.
X * This leads to some interesting problems with LINT. Declare these
X * here.  Some specialized routines are declared in the individual
X * routines that use them.
X ******************************************************************/
extern char	*malloc(), *realloc();
extern char	*getenv();
extern char	*gets();
extern int errno, sys_nerr;
extern char *sys_errlist[];
extern char *index(), *rindex();
extern char *sprintf();	/* sigh... sprintf strikes again */
extern long lseek(), atol();
X
extern uid_t getuid(), geteuid();
extern time_t time();	/* watch this one */
X
X/*
X * This should be declared in <signal.h>
X */
X#ifndef sigmask
X#define sigmask(m)	(1 << ((m)-1))
X#endif
X
X/*************************************************************************
X * PRINTCAP entries and associated variables.
X * Each printcap entry has a corresponding variable; for example
X * ":af=accnt:"
X *   -> AF = "accnt";
X *************************************************************************/
extern int    AB;		/* Always have a banner flag, ignore lpr -h option */
extern char  *AF;		/* accounting file */
extern char  *BP;		/* banner printer filter */
extern int    BK;		/* Berkeley compatible remote and local */
extern int    BR;		/* baud rate if lp is a tty */
extern int    CO;		/* cost of printing in dollars */
extern char  *EP;		/* end printer filter */
extern int    FD;		/* No forwarding, accept only from original site */
extern int    FO;		/* print a form feed when device is opened */
extern int    FC;		/* flags to clear if lp is a tty */
extern char  *FF;		/* form feed string */
extern int    FJ;		/* send control file  first to remote site */
extern int    FQ;		/* form feed on quitting */
extern int    FS;		/* flags to set if lp is a tty */
extern char  *FX;		/* allowable formats */
extern char  *LD;		/* leader string on opening */
extern char  *LF;		/* log file for error messages */
extern int    LH;		/* use long host name */
extern char  *LO;		/* lock file name */
extern char  *LN;		/* group allowed to use links */
extern char  *LP;		/* line printer device name */
extern int    MC;		/* maximum number of copies allowed */
extern int    MX;		/* maximum number of blocks to copy */
extern int    NW;		/* Networked file system, do not make copies */
extern int    PL;		/* page length */
extern char  *PR;		/* pr program name name */
extern char  *PS;		/* printer status file name */
extern int    PW;		/* page width */
extern int    PX;		/* page width in pixels */
extern int    PY;		/* page length in pixels */
extern char  *QH;		/* queue handler */
extern char  *RG;		/* restrict use to group */
extern char  *RM;		/* remote machine name */
extern char  *RP;		/* remote printer name */
extern int    RT;		/* max retries */
extern int    RW;		/* open LP for reading and writing */
extern int    SB;		/* short banner instead of normal header */
extern int    SC;		/* suppress multiple copies */
extern char  *SD;		/* spool directory */
extern int    SF;		/* suppress FF on each print job */
extern int    SH;		/* suppress header page */
extern char  *SS;		/* name of queue that the server serves  */
extern char  *ST;		/* status file name */
extern char  *SV;		/* names of servers */
extern char  *TR;		/* trailer string to be output when Q empties */
extern char  *TY;		/* terminal characteristics, stty options */
extern int    XC;		/* flags to clear for local mode */
extern int    XS;		/* flags to set for local mode */
extern char  *XT;		/* format checked for text only */
extern char  *XU;		/* restrict use to users whose names are in file */
X/*
X * Filter_name[] is an array of filter names, indexed by the first letter
X * of the printcap tag. For example:
X * ":if=/usr/lib/iffilter -n -a:"
X *     -> Filter_name['i'-'a'] = "/usr/lib/iffilter -n -a";
X */
extern char *Filter_name[26];
X#define	IF	(Filter_name['i'-'a'])
X#define	OF	(Filter_name['o'-'a'])
X/*
X * Prefilter_name[] is similar to Filter_name[], but for prefilter entries.
X */
extern char *Prefilter_name[26];
X
X/**********************************************************************
X * Globally accessed variables and routines
X **********************************************************************/
extern char	*Name;				/* program name */
extern char	*Printer;			/* printer name */
extern char *Person;			/* user name */
extern char	Host[64];			/* Host machine name */
extern char	*From;				/* client's machine name */
extern int	Debug;				/* Debugging level */
extern int	Echo_on_stdout;		/* stderr output on stdout as well */
extern char *First_name;		/* first name in printcap entry */
extern FILE *Lfd;				/* lock file */
extern int Optind, Opterr;		/*  option index, error message suppression */
extern char *Optarg;			/*  option value */
extern char *Opt_flag;			/*  option staring flags */
extern char Lpdlogf[];		/* = DEFLPDLOGF; */
extern char Masterlock[];		/*  = MASTERLOCK; */
extern int	Lpr_port_num;		/* port number to connect to */
extern int Maxportno;			/*  = MAXPORTNO; */
extern int Minportno;			/*  = MINPORTNO; */
extern char Permfile[];			/*  = PERMFILE; */
extern char Printcap[];			/*  = PRINTCAP; */
extern char Last_errormsg[];	/* last errormessage */
extern int Request;				/* current lpd request code */
extern int Print_fd;			/* output printer FILE */
extern int Short_format;		/* short status format */
extern int  Is_root;			/* is this the root user? */
extern int  Is_local;			/* doing this locally or remotely? */
extern struct stat LO_statb;	/* used to stat lockfile */
extern void Print_close(), Link_close(); /* close these things */
extern int Daemon_uid;			/* daemon UID */
extern int Daemon_gid;			/* daemon GID */
extern char *estrcp();			/* handy form of strcp() */
extern char *Time_str();		/* ctime with \n removed */
extern char *Setup_filter();	/* set up argv for a filter */
extern int Reapchild();         /* wait3() for a child */
extern int Getopt();			/* get command line option */
extern char *Errormsg();		/* printable error message */
extern int	Errorcode;			/* exit() value on fatal termination */
extern char *Decode_status();	/* decode a status returned by wait() */
extern FILE	*fopen_daemon();	/* open a file as daemon */
extern int	open_daemon();		/* open a file as daemon */
X
X/*******************************************************************
X *  struct queue
X *    used to record entries in the spool directory for printing
X *    and status reporting.
X *  NOTE: control and data file names in spool directory have the form
X *  c f <priority> <job number> <Hostname>  <terminating 0>
X *  d f <seq>      <job number> <Hostname>  <terminating 0>
X *  1 1 1               3         max 64           1
X * eg: dfA001attila.cs.umn.edu         (attila the SUN, of course)
X * eg: cfZ001attila.cs.umn.edu         (attila the SUN, of course)
X *******************************************************************/
X#define CFNAMELEN (sizeof(Host)+8)	/* control or data file name length */
X/*
X * fields in the name
X */
X#define STARTPR	2	/* df[] */
X#define STARTID	3	/* dfX[] */
X#define IDLEN	3	/* dfX[NNN] */
X#define	STARTFR	6	/* dfXNNN[] */
X#define MAXPARMLEN	80
struct queue {
X	char	q_name[CFNAMELEN+1];	/* name of the control file */
X	time_t	q_time;					/* modification time */
X	time_t	q_sp;					/* spooled at time */
X	time_t	q_unsp;					/* unspool at time */
X	int		q_priority;				/* priority */
X	long	q_size;					/* size of the data files */
X	char	q_user[32];				/* user name */
X	int		q_num;					/* job number */
X	char	q_data[MAXPARMLEN+1];	/* data files name */
X	int		q_daemon;				/* daemon for this entry */
X	char	*q_server;				/* server for this entry */
X};
X
X#define	q_from	q_name[STARTFR]
extern int Getq();					/* find queue entries */
extern struct queue *Jobentry;		/* current job entry in queue */
extern struct queue *Queue;			/* Getq generates an array of jobs */
extern int Jobcount;				/* and returns the number of jobs  */
X
extern int Rec_cnt;					/* number of files in a job */
X
X/********************************************************************
X * File Permissions
X ********************************************************************/
X
X#define LFMODE      0644    /* log file permissions */
X#define FILMOD      0600	/* spooling directory file permission */
X
X/*
X * Queue Control is done by using the permissions of the lock file
X * for the queue.  The following permissions are used:
X * 	Disable Printing: owner execute or 0100
X * 	Disable Queing:	group execute or 0010
X * 	Queue handler active (antique) 0001
X */
X#define	DISABLE_PRINT	(0100)
X#define	ENABLE_PRINT	(0777 & ~ DISABLE_PRINT)
X#define	DISABLE_QUEUE	(0010)
X#define	ENABLE_QUEUE	(0777 & ~ DISABLE_QUEUE)
X#define	FORCE_REQUE		(0001)
X#define	CLEAR_REQUE		(0777 & ~ FORCE_REQUE)
X
X/*
X *  syslog(8) message priorities.  These are defined by the values of
X *  global variables.  The variables are initialized either with values
X *  in the <syslog.h> include file,  or by a set of default values according
X *  to the NOSYSLOG option in the Makefile.
X */
extern int XLOG_ERR;			/*4 synonym of LOG_ERROR */
extern int XLOG_CRIT;			/*5	critical information */
extern int XLOG_WARNING;		/*6	warning */
extern int XLOG_NOTICE;			/*7	important information */
extern int XLOG_INFO;			/*8	informational message */
extern int XLOG_DEBUG;			/*9	Debug level info */
X
X/*******************************************************************
X * Printcap Data Structures
X * The printcap file information is extracted using a single pass of
X * the printcap database.  As each entry is found,  it is looked up
X * in a table which has the entry tag,  the type of entry,
X * a default value,  and the variable used to hold the value.
X *
X * The table is sorted alphabetically by the tag values and the update
X * routines use a modified binary search.  The time taken to read the
X * printcap entry is proportional to
X * K  log( M ), where K is the length of the printcap file and M is
X * the number of printcap variables.  Note that this appears to be
X * a much simpler and easier way to implement printcap information
X * extraction.  The "initialization" of the entire set of variables
X * is extremely fast,  apparently taking about the same time  as reading
X * two variables using the original "termcap" code.
X *******************************************************************/
typedef struct pc_entry{
X	char pc_name[3];	/* two character name, and the last char */
X	int	kind;	/* the kind of entry */
X#		define PC_NUM		0	/* integer */
X#		define PC_FLAG		1	/* integer */
X#		define PC_STRING	2	/* string */
X	int idefault;	/* this is the default integer or flag value */
X	char *sdefault;	/* this is the default integer or flag value */
X	/*
X	 * the variable is assumed to be a pointer to char;
X	 * this is cast to pointer to int if neccessary. 
X	 */
X	char **var;
X} PC_ENTRY;
X
X/*
X * Routines to look up printer names and printcap entries
X * char ** All_printers():  reads the printcap database and extracts the
X *   names of all the printer entries.
X * char *First_printer(): gets name of first printer
X * Get_pc_entry(): given name of a printer and an array  of PC_ENTRIES,
X *  it finds the printcap entry and extracts all the information
X *  for the variables listed in the array.
X * Set_pc_entry(): same functionality, does not initialize variables.
X */
X#define PRNAMELEN	32	/* Maximum of 32 characters for printer name */
X#define MAXPCNAMES	100	/* Maximum of 100 printers */
extern int Get_pc_entry( /* char *name; PC_ENTRY *pc_vars; int pc_len */);
extern int Set_pc_entry( /* char *name; PC_ENTRY *pc_vars; int pc_len */);
extern char **All_printers();	/* returns array printer names */
extern char *First_printer();	/* returns array printer names */
X/*
X * All_pc_vars[]: all printcap variables, and is used by lpr and lpd;
X * Status_pc_vars[]: a short set needed by lpq, lprm, and lpc.
X * Server_pc_vars[]: a few needed by servers only
X */
extern PC_ENTRY All_pc_vars[ /* All_pc_len */ ];
extern int	All_pc_len;
extern PC_ENTRY Status_pc_vars[ /* Status_pc_len */ ];
extern int	Status_pc_len;
extern PC_ENTRY Server_pc_vars[ /* Server_pc_len */ ];
extern int	Server_pc_len;
X
X/***********************************************************************
X * File Locking Support:
X * see lockfile.c for details.
X ***********************************************************************/
extern FILE *Readlockfile();	/* reads file, returns FILE * */
extern FILE *Getlockfile();		/* locks file, returns FILE * */
extern int   Checklockfile();	/* checks for lock and daemon */
extern FILE *Lockcf();			/* lock a control file */
extern int   Exlockcf();		/* create and lock a control file */
X
X/***********************************************************************
X * Parameter List Support
X * struct parm parmlist[]
X *  is used when parsing a command received from a remote Host
X * It is also used by lpr to determine the command file, and to keep
X * track of the file/datafile name correspondence.
X ***********************************************************************/
X#define MAXPARMS 50
struct parm{
X	char *str;					/* string parameter */
X	int   num;					/* number parameter */
X	char filename[CFNAMELEN+1];	/* file name for parameter */
X	long size;					/* size of file */
X};
extern struct parm Parms[MAXPARMS];		/* array of parmaters */
extern int    Parmcount;				/* number of parameters */
X
X/***********************************************************************
X * Request types
X * A request sent to the LPD daemon has the format:
X * \Xprinter [options],  where \X is a single character or byte value.
X * The following are the values and commands
X ***********************************************************************/
X#define REQ_START	1	/* start printer */
X#define REQ_RECV	2	/* transfer a job to the Host from a remote site */
X#define REQ_DSHORT	3	/* print short form of queue status */
X#define REQ_DLONG	4	/* print long form of queue status */
X#define REQ_REMOVE	5	/* remove jobs */
X#define REQ_CONTROL	6	/* do control operation */
X/**************************************************************************
X *      Control file format
X *      First character is kind of entry, remainder of line is
X *          the argument.
X *
X *		1 -- "R font file" for troff -ignore
X *		2 -- "I font file" for troff -ignore
X *		3 -- "B font file" for troff -ignore
X *		4 -- "S font file" for troff -ignore
X *		C -- "class name" on banner page
X *		H -- "Host name" of machine where lpr was done
X *		I -- "indent" amount to indent output
X *		J -- "job name" on banner page
X *		L -- "literal" user's name to print on banner
X *		M -- "mail" to user when done printing
X *		N -- "name" of file (used by lpq)
X *		P -- "Person" user's login name
X *		R -- account id  for charging 
X *		U -- "unlink" name of file to remove after we print it
X *		W -- "width" page width for PR
X *		X -- "header" header title for PR
X *		Z -- xtra options to filters 
X *
X *		Lower case letters are formats
X *		f -- "file name" name of text file to print
X *		l -- "file name" text file with control chars
X *		p -- "file name" text file to print with pr(1)
X *		t -- "file name" troff(1) file to print
X *		n -- "file name" ditroff(1) file to print
X *		d -- "file name" dvi file to print
X *		g -- "file name" plot(1G) file to print
X *		v -- "file name" plain raster file to print
X *		c -- "file name" cifplot file to print
X *
X * CFparm is used to hold the upper case parameters
X */
extern char CFparm[26][MAXPARMLEN+1];
X#define CLASSNAME CFparm['C'-'A']
X#define WHENSP    CFparm['D'-'A']
X#define FROMHOST  CFparm['H'-'A']
X#define INDENT    CFparm['I'-'A']
X#define JOBNAME   CFparm['J'-'A']
X#define BNRNAME   CFparm['L'-'A']
X#define FILENAME  CFparm['N'-'A']
X#define MAILNAME  CFparm['M'-'A']
X#define LOGNAME   CFparm['P'-'A']
X#define ACCNTNAME CFparm['R'-'A']
X#define NOHEADER  CFparm['S'-'A']
X#define WHENUNSP  CFparm['T'-'A']
X#define UNLNKFILE CFparm['U'-'A']
X#define PWIDTH    CFparm['W'-'A']
X#define PRTITLE   CFparm['X'-'A']
X#define ZOPTS     CFparm['Z'-'A']
X
X/*
X * printjob return codes
X */
X#define JBUSY	-1              /* being done by other means */
X#define JFAIL	 0				/* repeat with retry */
X#define JSUCC	 1				/* done */
X#define JABORT   2				/* done, but problems */
X
X/*
X * Mail program
X */
X#ifndef MAIL
X#define MAIL "/usr/lib/sendmail"
X#endif MAIL
X
X/*
X * File Transfer Protocol Flags
X */
X#define CEND 5		/* last file */
X#define CNAME 4		/* control file */
X#define DFILE 3		/* data file */
X#define CFILE 2		/* control file */
X
X/*
X * Local parameters to the spooling system
X * Defaults for line printer capabilities data base
X */
X#define DEFLOGF		"log"
X#define DEFLOCK		"lock"
X#define DEFSTAT		"status"
X#define DEFACCT		"acct"
X#define	DEFFORMATS	"flp"	/* default formats */
X#define	DEFMX		1000
X#define	DEFRETRY	3		/* maximum number of retries */
X#define DEFMAXCOPIES	0
X#define DEFFF		"\f"
X#define DEFWIDTH	132
X#define DEFLENGTH	66
X#define DAEMON		"daemon"	/* daemon user id */
X#define DEFPRICE	20	/* cost in dollars per thousand pages */
X#define DEFPRIORITY	'Z'	/* default priority level */
X#define DEFPR		"/bin/pr"	/* default pr program */
X
X/*
X * REAL, not TEST version
X */
X
X/*
X * path name of files created by lpd.
X */
X#define RMASTERLOCK	"/usr/spool/lpd/lpd.lock."
X#define	RDEFLPDLOGF		"/usr/spool/lpd/lpd.log."
X#ifdef	ETCPC
X#	define RPRINTCAP	ETCPC
X#else
X#	define RPRINTCAP	"/usr/spool/lpd/printcap."
X#endif
X/*
X * connection services to be used
X */
X#define SERVERNAME	"printer"
X#define SERVERPROT	"tcp"
X/* name of the printer permissions file */
X#ifdef	ETCPERMS
X#	define	RPERMFILE   ETCPERMS
X#else
X#	define	RPERMFILE   "/usr/spool/lpd/printer_perms."
X#endif
X/*
X *	The remote client must call from a port in the following range.
X *	This is used for authentication purposes.  The ports less than
X *	IPPORT_RESERVED are accessible only to SU processes.
X */
X#define RMINPORTNO	(IPPORT_RESERVED/2)
X#define RMAXPORTNO	(IPPORT_RESERVED)
X
X/*
X *	DEBUG Version, use to avoid screwing up normal operations
X * path name of files created by lpd.
X */
X#define TMASTERLOCK 	"/tmp/lpd.lock."
X#define	TDEFLPDLOGF		"/tmp/lpd.log."
X#define TPRINTCAP	 	"/tmp/printcap."
X/*
X * connection services to be used
X */
X#define TSERVICENAME	"testlpr"
X#define TPORTNUM	1600	/* test port number */
X/* name of the printer permissions file */
X#define	TPERMFILE   "/tmp/printer_perms."
X/*
X *	The remote client must call from a port in the following range.
X *	This is used for authentication purposes.  The ports less than
X *	IPPORT_RESERVED are accessible only to SU processes.
X */
X#define TMINPORTNO	(IPPORT_RESERVED+1)
X#define TMAXPORTNO	(2*IPPORT_RESERVED)
END_OF_FILE
if test 23350 -ne `wc -c <'src/lp.h'`; then
    echo shar: \"'src/lp.h'\" unpacked with wrong size!
fi
# end of 'src/lp.h'
fi
if test -f 'src/utils.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/utils.c'\"
else
echo shar: Extracting \"'src/utils.c'\" \(25393 characters\)
sed "s/^X//" >'src/utils.c' <<'END_OF_FILE'
X/***************************************************************************
X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
X ***************************************************************************
X * MODULE: utils.c
X * Utility routines used by several programs
X ***************************************************************************
X * Revision History: Created Sat Jan  9 15:02:12 CST 1988
X * $Log:	utils.c,v $
X * Revision 3.2  88/06/24  17:55:27  papowell
X * MODS for VAX 4.3BSD UNIX
X * 
X * Revision 3.1  88/06/18  09:35:55  papowell
X * Version 3.0- Distributed Sat Jun 18 1988
X * 
X * Revision 2.4  88/05/19  10:34:26  papowell
X * Fixed open() calls to have a 0 parameter, ie: open(f, perms, 0), where needed
X * 
X * Revision 2.3  88/05/16  12:09:27  papowell
X * Spelling mistake in error messages
X * 
X * Revision 2.2  88/05/14  10:18:31  papowell
X * Use long format for job file names;
X * Added 'fd', no forward flag;
X * Control file has to have hostname and origination agree.
X * 
X * Revision 2.1  88/05/09  10:10:42  papowell
X * PLP: Released Version
X * 
X * Revision 1.7  88/04/27  20:27:34  papowell
X * Modified to remove unused variables
X * 
X * Revision 1.6  88/04/15  13:06:55  papowell
X * Std_environ() call added, to ensure that fd 0 (stdin), 1 (stdout), 2(stderr)
X * have valid file descriptors;  if not open, then /dev/null is used.
X * 
X * Revision 1.5  88/03/25  15:01:57  papowell
X * Debugged Version:
X * 1. Added the PLP control file first transfer
X * 2. Checks for MX during file transfers
X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities;
X * 	apparently they open files and then assume that they will stay
X * 	open.
X * 4. Made sure that stdin, stdout, stderr was available at all times.
X * 
X * Revision 1.4  88/03/12  10:03:45  papowell
X * *** empty log message ***
X * 
X * Revision 1.3  88/03/11  19:29:25  papowell
X * Minor Changes, Updates
X * 
X * Revision 1.2  88/03/05  15:01:28  papowell
X * Minor Corrections,  Lint Problems
X * 
X * Revision 1.1  88/03/01  11:09:27  papowell
X * Initial revision
X * 
X ***************************************************************************/
X#ifndef lint
static char id_str1[] =
X	"$Header: utils.c,v 3.2 88/06/24 17:55:27 papowell Exp $ PLP Copyright 1988 Patrick Powell";
X#endif lint
X
X#include "lp.h"
X/*
X * Time_str: return ctime() value, but without terminating '\n'
X */
char *
Time_str()
X{
X	time_t tvec;		/* time */
X	char *ctime();
X	static char s[40];
X
X	(void)time(&tvec);
X	(void)strcpy(s,ctime(&tvec));
X	s[strlen(s)-1] = 0;
X	return( s );
X}
X
X/***************************************************************************
X * Remove_job( FILE *fp; struct queue *q )
X * Remove a job and all associated files; if success == JSUCC, remove
X * U links as well
X * ACTIONS:
X * 1. Read the control file
X * 2. get data file names and unlink them if they match the job
X * 3. Unlink the control file
X ***************************************************************************/
X
Remove_job( fp, q )
X	FILE *fp;
X	struct queue *q;
X{
X	char parm[BUFSIZ];	/* line buffer */
X	char *arg;			/* pointer to the argument */
X	int c;				/* ACME Integers, Inc. */
X	
X	if(Debug>5)log(XLOG_DEBUG,"Remove_job: %s", q->q_name );
X	clearerr( fp );
X	if( fseek( fp, 0L, 0 ) < 0 ){
X		logerr_die( XLOG_INFO,
X			"Remove_job: fseek failed (%s)", q->q_name );
X	} else {
X		/*
X		 * read through the file, looking for things to remove
X		 */
X		while(fgets( parm, sizeof(parm), fp )){
X			if( (arg = index(parm, '\n')) == 0 ){
X				log( XLOG_INFO, "Remove_job: file bad (%s), no endline",
X					q->q_name);
X				continue;
X			}
X			*arg = 0;
X			arg = parm+1;
X			c = parm[0];
X			/*
X			 * is it a format line?
X			 */
X			if( !isascii( c ) || !islower( c ) ){
X				continue;
X			}
X			if( Job_match( q->q_name, arg ) == 0){
X				if(Debug>4)log(XLOG_DEBUG,"Remove_job: bad file name '%s'",
X					arg );
X				continue;
X			}
X			(void)unlink_daemon( arg );
X		}
X	}
X	if( unlink_daemon( q->q_name ) < 0 ){
X		logerr( XLOG_INFO, "Remove_job: cannot unlink %s", q->q_name );
X	}
X}
X/***************************************************************************
X * int Getq();
X * Scan the spool directory and make a list of server files sorted by
X * priority, creation time, etc.
X * Returns: the number of entries
X * Side Effects: sets struct queue *Queue to point to an array of pointers
X *   to queue entries
X ***************************************************************************/
static int arraysz;				/* number of entries in array */
static struct queue qb;			/* for zeroing purposes */
X
Getq()
X{
X	struct direct *d;		/* directory entry */
X	int nitems;				/* ACME Integers, Inc. */
X	struct stat stbuf;		/* for statting files */
X	int compar();			/* for sorting files */
X	DIR *dirp;				/* used by directory routines */
X	char l[BUFSIZ];			/* for reading the control file */
X	char fullname[BUFSIZ];	/* for the full pathname */
X	char *filepart;			/* end of the pathname */
X	FILE *cfp;				/* control file FP */
X	struct queue *q;		/* pointer to queue entry */
X	char *bp, *ep;			/* ACME Pointers, Inc. */
X
X	if (SD == 0 || *SD == 0 ){
X		fatal( XLOG_INFO, "no directory entry" );
X	}
X	if ((dirp = opendir(SD)) == NULL){
X		logerr_die( XLOG_INFO, "cannot open spool directory %s", SD );
X	}
X
X	/*
X	 * get the directory name and copy to buffer
X	 */
X	if( strlen(SD) + sizeof(q->q_name) + 2 > sizeof(fullname) ){
X		logerr_die( XLOG_NOTICE,"INTERNAL: Getq, file name too big" );
X	}
X	(void)strcpy(fullname,SD);
X	filepart = &fullname[strlen(fullname)];
X	*filepart++ = '/';
X	/*
X	 * assume you have 100 entries to start with
X	 */
X	if( arraysz == 0 ){
X		arraysz = 100;
X		Queue = (struct queue *)malloc(
X				(unsigned)(arraysz * sizeof(struct queue)));
X		if (Queue == NULL){
X			logerr_die( XLOG_INFO, "Getq: malloc failed" );
X		}
X	}
X	/*
X	 * set the item count to 0 and scan the directory
X	 */
X	nitems = 0;
X	while ((d = readdir(dirp)) != NULL) {
X		if(Debug>5)log(XLOG_DEBUG,"Getq: file %s", d->d_name );
X
X		if (d->d_name[0] != 'c' || d->d_name[1] != 'f'){
X			continue;	/* server control files only */
X		}
X		/*
X		 * Check to make sure the array has space left and
X		 * realloc the maximum size.
X		 */
X		if( nitems >= arraysz ){
X			/* try to get another buffer */
X			arraysz = arraysz + 20;
X			Queue = (struct queue *)realloc((char *)Queue,
X				(unsigned)((arraysz)*sizeof(struct queue)));
X			if (Queue == NULL){
X				logerr_die(XLOG_NOTICE,"malloc failed in Getq" );
X			}
X		}
X		/*
X		 * get next queue entry and clear it
X		 */
X		q = &Queue[nitems];
X		*q = qb;	/* zero it out the easy way */
X
X		if(Debug>5)log(XLOG_DEBUG,"Getq: control file %s", d->d_name );
X
X		/* copy the name of the file */
X		if( strlen( d->d_name ) >= CFNAMELEN){
X			logerr_die(XLOG_NOTICE,"control file name %s too long", d->d_name );
X		}
X		(void)strcpy(q->q_name, d->d_name);
X		(void)strcpy(filepart,d->d_name);
X		if (stat(fullname, &stbuf) < 0){
X			continue;	/* Doesn't exist */
X		}
X		/* get the modification time */
X		q->q_time = stbuf.st_mtime;
X		/* set a priority */
X		q->q_priority = d->d_name[2];
X		/* get the job number */
X		q->q_num = atoi( &d->d_name[3] );
X		
X		/*
X		 * now we open the control file and read the information
X		 */
X		if( (cfp = fopen_daemon( fullname, "r" )) == NULL ){
X			/* it disappeared */
X			continue;
X		}
X		if(Debug>5)log(XLOG_DEBUG,"Getq: control file %s readable", d->d_name );
X		bp = q->q_data;
X		ep = bp + sizeof(q->q_data);
X		while( fgets( l, sizeof(l), cfp) ){
X			/* clobber the \n */
X			l[strlen(l)-1] = 0;
X			if( islower( l[0] ) ){
X				/* get size of file to be printed */
X				(void)strcpy(filepart, &l[1] );
X				if( stat( fullname,  &stbuf) < 0){
X					(void)fclose(cfp);
X					if(Debug>5)log(XLOG_DEBUG,
X						"Getq: data file %s does not exist", l );
X				} else {
X					q->q_size += stbuf.st_size;
X				}
X			} else switch( l[0] ){
X				case 'N':	/* name of file */
X					bp = estrcp( bp, &l[1], ep );
X					bp = estrcp( bp, " ", ep );
X					break;
X				case 'P':	/* name of user */
X					(void)strncpy( q->q_user, &l[1], sizeof(q->q_user)-1);
X					break;
X				case 'D':	/* when spooled */
X					q->q_sp = (time_t)atol( &l[1] );
X					break;
X				case 'T':	/* when to be unspooled */
X					q->q_unsp = (time_t)atol( &l[1] );
X					break;
X				default: break;
X			}
X		}
X		if(Debug>5)log(XLOG_DEBUG,"Getq: control file %s is ok", d->d_name );
X
X		(void)fclose(cfp);
X		++nitems;
X	}
X	closedir(dirp);
X
X	/*
X	 * sort the queue
X	 */
X	if (nitems){
X		qsort((char *)Queue, nitems, sizeof(struct queue), compar);
X	}
X	return(nitems);
X}
X
X
X/*
X * Compare queue entries by
X * 1. Priority
X * 2. Time
X */
static
compar(c1, c2)
X	char *c1, *c2;
X{
X	struct queue *p1, *p2;
X
X	p1 = (struct queue *)c1;
X	p2 = (struct queue *)c2;
X	if ((p1)->q_priority < (p2)->q_priority)
X		return(-1);
X	if ((p1)->q_priority > (p2)->q_priority)
X		return(1);
X	if ((p1)->q_time < (p2)->q_time)
X		return(-1);
X	if ((p1)->q_time > (p2)->q_time)
X		return(1);
X	return(0);
X}
X
X/***************************************************************************
X * Match_entry( struct queue *q )
X * check the user name and job number against the parmlist starting at start;
X * Return: 1 if match found, 0 otherwise 
X ***************************************************************************/
int
Match_entry( q )
X	struct queue *q;
X{
X	int i;		/* ACME Integer, Inc. */
X
X	if(Debug>4)log(XLOG_DEBUG,"Match_entry on %s, %d", q->q_user, q->q_num );
X	for( i = 0; i < Parmcount; ++i ){
X		if( (Parms[i].num >= 0 && Parms[i].num == q->q_num)
X			|| strcmp( Parms[i].str, q->q_user ) == 0 ){
X			return(1);
X		}
X	}
X	return(0);
X}
X
X/***************************************************************************
X * bpread( int fd ; char *buf; int size )
X * Almost bombproof way to read command line from socket
X * Returns: -1 if unable to read a line terminated with \n
X *     0 if 0 length line or first character is null (i.e.- no input)
X *     otherwise: length of line
X ***************************************************************************/
bpread( fd, buf, size )
X	int fd;
X	char *buf;
X	int size;
X{
X	int n;			/* ACME Integer, Inc. */
X	int i;
X
X	/*
X	 * Bombproof line reading
X	 */
X	for( i = 0; i < size; ++i ){
X		n = read(fd, &buf[i], 1);
X		if( n < 0 ){
X			logerr(XLOG_INFO,"bpread: lost connection");
X			return( -1 );
X		} else if( n == 0 ){
X			log(XLOG_INFO,"bpread: bad format %s", buf );
X			return( 0 );
X		}
X		if( buf[i] == '\n' ){
X			buf[i] = 0;
X			if(Debug>3)log(XLOG_DEBUG,"bpread: '%d' %s", buf[0], &buf[1]);
X			return( i );
X		} else if( buf[i] == 0 && i == 0 ){
X			if(Debug>3)log(XLOG_DEBUG,"bpread: zero byte first");
X			return( i );
X		}
X	}
X	log(XLOG_INFO,"bpread: bad line %s", buf );
X	return( -1 );
X}
X/***************************************************************************
X * estrcp(char *s, *t, *e)
X * copies t to s, and returns the end position
X * Returns: end of string if the result is shorter than e, NULL otherwise
X * Note: this is a very useful routine.  Think about adding it to string
X * functions.
X ***************************************************************************/
char *
estrcp( s,t,e )
X	char *s, *t, *e;
X{
X	if( s ){
X		while( (*s++ = *t++) && (s < e) );
X		if( s < e ){
X			return( s-1 );
X		} else {
X			return( (char *)0 );
X		}
X	}
X	return( s );
X}
X/***************************************************************************
X * splitline( char *line )
X *    splits a line into tokens, and places them in the Parms[] array.
X *    if firstarg is present then it is set to the first argument
X * Side Effects: modifies Parms, Parmcount
X ***************************************************************************/
X
splitline( bp )
X	char *bp;
X{
X	/* find the start */
X	while (*bp) {
X		while( *bp && isspace(*bp))
X			bp++;
X		if( *bp ){
X			if( Parmcount >= MAXPARMS ){
X				fatal(XLOG_INFO,"splitline: too many requests");
X			}
X			Parms[Parmcount].str = bp;
X			if (isdigit(*bp)) {
X				Parms[Parmcount].num = atoi(bp);
X			} else {
X				Parms[Parmcount].num = -1;
X			}
X			Parmcount++;
X		}
X		while( *bp && !isspace(*bp))
X			bp++;
X		if( *bp )
X			*bp++ = 0;
X	}
X}
X/**********************************************************************
X * Shift_parms( int n )
X * left shift the entries in the Parms[] array by n
X * Side Effects: modifies Parms and Parmcount
X **********************************************************************/
Shift_parms( n )
X	int n;
X{
X	int i;		/* ACME Chain and Integer, Inc. */
X	while( Parmcount > 0 && n > 0 ){
X		for( i = 1; i < Parmcount; ++i ){
X			Parms[i-1] = Parms[i];
X		}
X		--Parmcount;
X		--n;
X	}
X}
X
printstatus()
X{
X	if( ST && *ST ){
X		if(Debug>4)log(XLOG_DEBUG,"printstatus: ST %s", ST );
X		pr_stat_file( ST );
X	}
X	if( PS && *PS ){
X		if(Debug>4)log(XLOG_DEBUG,"printstatus: PS %s", PS );
X		pr_stat_file( PS );
X	}
X}
X
pr_stat_file( file )
X	char *file;
X{
X	FILE *fp;
X	char buf[BUFSIZ];
X
X	if( (fp = fopen_daemon( file, "r" )) != NULL ){
X		while( fgets( buf, sizeof(buf), fp) != NULL ){
X			(void)fprintf(stdout, "  %s", buf );
X		}
X		(void)fflush(stdout);
X		(void)fclose( fp );
X	}
X}
X/***************************************************************************
X * char *Sigstr(n)
X * Return a printable form the the signal
X ***************************************************************************/
X
struct signame{
X	char *str;
X	int value;
X} signals[] = {
X{ "SIGHUP", SIGHUP }, { "SIGINT", SIGINT }, { "SIGQUIT", SIGQUIT },
X{ "SIGILL", SIGILL }, { "SIGTRAP", SIGTRAP }, { "SIGIOT", SIGIOT },
X{ "SIGEMT", SIGEMT }, { "SIGFPE", SIGFPE }, { "SIGKILL", SIGKILL },
X{ "SIGBUS", SIGBUS }, { "SIGSEGV", SIGSEGV }, { "SIGSYS", SIGSYS },
X{ "SIGPIPE", SIGPIPE }, { "SIGALRM", SIGALRM }, { "SIGTERM", SIGTERM },
X{ "SIGURG", SIGURG }, { "SIGSTOP", SIGSTOP }, { "SIGTSTP", SIGTSTP },
X{ "SIGCONT", SIGCONT }, { "SIGCHLD", SIGCHLD }, { "SIGTTIN", SIGTTIN },
X{ "SIGTTOU", SIGTTOU }, { "SIGIO", SIGIO }, { "SIGXCPU", SIGXCPU },
X{ "SIGXFSZ", SIGXFSZ }, { "SIGVTALRM", SIGVTALRM }, { "SIGPROF", SIGPROF } };
int nsignals = sizeof( signals )/ sizeof( struct signame );
char *
Sigstr( n )
X	int n;
X{
X	int i;
X	static char buf[40];
X
X	for( i = 0; i < nsignals; ++i ){
X		if( signals[i].value == n ){
X			return( signals[i].str );
X		}
X	}
X	(void)sprintf(buf,"unknown signal (%d)", n );
X	return( buf );
X}
X/***************************************************************************
X * Decode_status( union wait *status )
X * returns a printable string encoding return status
X ***************************************************************************/
X
char *
Decode_status( status )
X	union wait *status;
X{
X	static char msg[BUFSIZ];
X
X	(void)sprintf( msg, "user: %d, system: %d%s",
X		status->w_retcode, status->w_termsig,
X		status->w_coredump ? ", core dump, " : ", " );
X	if( status->w_termsig ){
X		(void)sprintf(msg+strlen(msg),"error- %s",
X			Sigstr((int)status->w_termsig));
X	}
X	return( msg );
X}
X
X/*
X * Checkactive()
X * find the currently active files in the spool queue.
X * 1.  check for the single server possibility first.
X * 2.  find the Names of the servers
X * 3.  find the active files for each of the servers
X */
X
int 
Checkactive()
X{
X	int i;				/* ACME Integers, Inc. */
X	char buf[BUFSIZ];	/* Name of active file */
X	static char server[BUFSIZ];	/* Name of server file */
X	char *sp, *ep;			/* ACME Pointer */
X	int pid;
X
X	pid = 0;
X	buf[0] = 0;
X	if(Checklockfile(LO,&pid,buf,sizeof(buf),&LO_statb) && buf[0] ){
X		for( i = 0; i < Jobcount; ++i ){
X			if( strcmp( buf, Queue[i].q_name ) == 0 ){
X				Queue[i].q_daemon = pid;
X				Queue[i].q_server = Printer;
X			} else {
X				Queue[i].q_daemon = 0;
X				Queue[i].q_server = 0;
X			}
X		}
X	}
X	/*
X	 * check for each of the servers
X	 */
X	if( SV && *SV ){
X		(void)strcpy( server, SV );
X		for( sp = server; sp; sp = ep ){
X			if( ep = index( sp, ',' ) ){
X				*ep = 0;
X				++ep;
X			}
X			/*
X			 * get the lock file and the status from the server
X			 */
X			buf[0] = 0;
X			if(Checklockfile(sp,&pid,buf,sizeof(buf),(struct stat *)0)
X				&& buf[0] ){
X				for( i = 0; i < Jobcount; ++i ){
X					if( strcmp( buf, Queue[i].q_name ) == 0 ){
X						Queue[i].q_daemon = pid;
X						Queue[i].q_server = sp;
X					} else {
X						Queue[i].q_daemon = 0;
X						Queue[i].q_server = 0;
X					}
X				}
X			}
X		}
X	}
X	return(pid);
X}
X
X
X/***************************************************************************
X * Get_Daemon()
X * Get the DAEMON uid and gid
X * Assume password entry has both values set for the user daemon
X ***************************************************************************/
Get_Daemon()
X{
X	struct passwd *passwd, *getpwnam();
X	if( Daemon_uid == 0 ){
X		if( (passwd = getpwnam(DAEMON)) == 0 ){
X			logerr_die( XLOG_INFO,"Get_Daemon: getpwnam(%s) failed", DAEMON );
X		}
X		Daemon_uid = passwd->pw_uid;
X		Daemon_gid = passwd->pw_gid;
X		if(Debug>4)log(XLOG_INFO,"Get_Daemon: uid=%d, gid=%d",
X			Daemon_uid, Daemon_gid);
X	}
X}
X
X/***************************************************************************
X * Job_match( char *control, *data)
X * Check to see if the control and data file names are matching
X * 1. data file must start with "df"
X * 2. next must be letter
X * 3. next must be 3 digit sequence number
X * 4. control and data file sequence number must match
X * return 1 if OK, 0 if not
X ***************************************************************************/
X
Job_match( control, data )
X	char *control;
X	char *data;
X{
X	int c, i, j;	/* ACME Chain and Integers, Inc. */
X
X	i = strlen( data );
X	for( j = 0; j < i ; ++j ){
X		c = data[j];
X		if( !isascii( c ) || !isprint( c ) ){
X			log(XLOG_INFO, "Job_match: bad char in '%s'", data );
X			return( 0 );
X		}
X	}
X	if( index( data, '/' ) ){
X		/*
X		 * tried to embed a / in the file name
X		 */
X		log(XLOG_INFO, "Job_match: / present '%s'",	data );
X		return( 0 );
X	}
X	if( i > CFNAMELEN
X		|| i <= STARTFR
X		|| i != strlen( control )
X		|| index( "cd", data[0] ) == 0
X		|| (data[1] != 'f')
X		|| !isalpha( data[STARTPR] )
X		|| strcmp( control+STARTID, data+STARTID ) ){
X		log(XLOG_INFO, "Job_match: bad match control '%s', data '%s'",	
X			control, data );
X		return( 0 );
X	}
X	for( j = 0; j < IDLEN; ++ j ){
X		if( !isdigit( data[j+STARTID] ) ){
X			log(XLOG_INFO, "Job_match: bad sequence control '%s', data '%s'",	
X				control, data );
X			return( 0 );
X		}
X	}
X	if(Debug>5)log(XLOG_DEBUG,"Job_match: OK control '%s', data '%s'",	
X		control, data );
X	return( 1 );
X}
X
X/***************************************************************************
X * Std_environ()
X * Make sure that fd 0, 1, 2 exist by opening /dev/null for them if they
X * have not been provided.
X * Set up the Host information as well
X ***************************************************************************/
X
Std_environ()
X{
X	int fd;
X	struct hostent *host_ent;	/* host entry from data base */
X
X	/*
X	 * close all file descriptors up to NOFILE
X	 */
X	for (fd = 3; fd < NOFILE; fd++){
X		(void) close(fd);
X	}
X	while( (fd = open( "/dev/null", O_RDWR, 0 )) >= 0 && fd < 3 );
X	if( fd < 0 ){
X		logerr_die( XLOG_CRIT, "Std_environ: cannot open /dev/null" );
X	} else {
X		(void)close(fd);
X	}
X	/*
X	 * get the Host computer Name
X	 */
X	if( gethostname(Host, sizeof(Host)) < 0 ){
X		logerr_die( XLOG_INFO, "Std_environ: gethostname failed" );
X	}
X	host_ent = gethostbyname( Host );
X	if( host_ent == 0 ){
X		fatal( XLOG_INFO, "Std_environ: host entry for '%' not found", Host );
X	}
X	if( strlen( host_ent->h_name ) > sizeof( Host ) ){
X		fatal( XLOG_INFO, "Std_enviorn: host name is too long: '%s'",
X			host_ent->h_name);
X	}
X	(void)strcpy(Host, host_ent->h_name );
X	/*
X	 * Get Daemon UID
X	 */
X	Get_Daemon();
X}
X
X/***************************************************************************
X * int open_daemon()
X * FILE *fopen_daemon()
X * int unlink_daemon()
X * Do the above actions as DAEMON.
X * Note that this is very difficult to do,  and ensure that all the error
X * conditions are met.  What you want to do is perform the following as
X * an uninterrupted critical section.
X * 1. setreuid( root, daemon)
X * 2. perform the action
X * 3. setreuid( whatever, root )
X * This will only work if
X * 1. you are really root
X * 2. you do not ever get interrupts
X ***************************************************************************/
static	uid_t ruid, euid;	/* real and effective UIDs */
static	int uid_parm;		/* must be able to hold -1 */
static	int omask;			/* block these signals */
X
X
void
Set_uid( new_uid )
X	int new_uid;
X{
X	/*
X	 * get the DAEMON UID, GID
X	 */
X	omask = sigblock(sigmask(SIGCHLD)|sigmask(SIGHUP)
X		|sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM));
X	ruid = getuid();
X	euid = geteuid();
X
X#	ifdef SETREUID4BSD
X	uid_parm = euid;
X#	else
X	uid_parm = -1;
X#	endif SETREUID4BSD
X	/*
X	 * check to see if we are running without SUID
X	 */
X	if( euid && euid != getuid() && euid != new_uid ){
X		log( XLOG_INFO, "Set_uid: euid %d, ruid %d, bad SUID?",
X			euid, getuid() );
X		/*
X		 * NOTE: do not try and take normal exit!!!
X		 */
X		exit(1);
X	} else if( euid == 0 && setreuid( uid_parm, new_uid ) < 0 ){
X		logerr( XLOG_INFO, "Set_uid: setreuid( %d, %d) failed",
X			uid_parm, new_uid );
X		exit(1);
X	}
X	if(Debug>4)log(XLOG_DEBUG,"Set_uid: uid_parm %d, uid %d, euid %d",
X		uid_parm, getuid(), geteuid());
X}
X/*
X * set the UID to DAEMON
X */
void
Set_daemon()
X{
X	/*
X	 * get the DAEMON UID, GID
X	 */
X	Get_Daemon();
X	Set_uid( Daemon_uid );
X}
X/*
X * set UID back to normal.  Note that there are two versions of setreuid
X * available.  The VAX 4.2/4.3 version allows you to swap RUID/EUID
X * and will allow you only to set your UID if you are EUID root.
X * The SUN 3.X and others will allow you ONE call to the SUID function,
X * and will allow you to set the UID back to the original.  The
X * uid_parm flag determines which strategy to use.
X * If uid_parm is -1,  then you have to use setreuid( -1, euid )
X * If it is not,  then we have to swap the EUID/UID, then set
X * the UID and EUID explicitly
X */
void
Clear_uid()
X{
X	/*
X	 * handle the easy one first: set euid back to original
X	 */
X	if( uid_parm == -1 ){
X		if( setreuid( -1, euid ) < 0 ){
X			logerr( XLOG_INFO,
X			"Clear_uid: setreuid( %d, %d) from %d, %d failed",
X				-1, euid, getuid(), geteuid() );
X			/*
X			 * avoid problems, exit early
X			 */
X			exit(1);
X		}
X		(void)sigsetmask(omask);
X		return;
X	}
X	/*
X	 * otherwise we swap the UID and EUID, but first make sure that it is
X	 * consistent;  uid should be the old (original) euid on entry to set_uid
X	 */
X	if( getuid() != euid ){
X		logerr( XLOG_INFO,
X			"Clear_uid: uid is %d, euid is %d, and uid should be %d",
X				getuid(), geteuid(), euid );
X		exit(1);
X	}
X	/*
X	 * swap the RUID and EUID
X	 */
X	if( setreuid( geteuid(), getuid() ) < 0 ){
X		logerr( XLOG_INFO,
X			"Clear_uid: setreuid( %d, %d) from %d, %d failed",
X				geteuid(), getuid(), getuid(), geteuid() );
X		/*
X		 * avoid problems, exit early
X		 */
X		exit(1);
X	}
X	/*
X	 * Set the RUID and EUID explicitly
X	 */
X	if( setreuid( ruid, euid ) < 0 ){
X		logerr( XLOG_INFO,
X			"Clear_uid: setreuid( %d, %d) from %d, %d failed",
X				ruid, euid, getuid(), geteuid() );
X		/*
X		 * avoid problems, exit early
X		 */
X		exit(1);
X	}
X	(void)sigsetmask(omask);
X}
X
X/*
X * Open a file as daemon.
X */
int
open_daemon( name, flag, perms )
X	char *name;
X	int flag;
X	int perms;
X{
X	int fd, err;
X	/*
X	 * establish UID
X	 */
X	Set_daemon();
X	/*
X	 * now for the plunge into the unknown
X	 */
X	fd = open( name, flag, perms );
X	err = errno;
X	Clear_uid();
X	errno = err;
X	return( fd );
X}
X
X/*
X * fopen a file; the file has been created, and we want to do this as
X * daemon for permissions reasons.
X */
XFILE *
fopen_daemon( name, how )
X	char *name;
X	char *how;
X{
X	int err;
X	FILE *fp;
X	/*
X	 * establish UID
X	 */
X	Set_daemon();
X	/*
X	 * now for the plunge into the unknown
X	 * NOTE: we are assuming that we only use this to READ a file already
X	 * created.
X	 */
X	fp = fopen( name, how );
X	err = errno;
X	/*
X	 * done with the daemon stuff
X	 */
X	Clear_uid();
X	errno = err;
X	return( fp );
X}
X
X/*
X * UNLINK a file as DAEMON
X */
unlink_daemon( name )
X	char *name;
X{
X	int f, err;
X	/*
X	 * establish UID
X	 */
X	Set_daemon();
X	/*
X	 * now for the plunge into the unknown
X	 */
X	f = unlink( name );
X	err = errno;
X	/*
X	 * done with the daemon stuff
X	 */
X	Clear_uid();
X	errno = err;
X	return( f );
X}
X
X
X/*
X * CHMOD a file as DAEMON
X */
chmod_daemon( name, perms )
X	char *name;
X	int perms;
X{
X	int f, err;
X	/*
X	 * establish UID
X	 */
X	Set_daemon();
X	/*
X	 * now for the plunge into the unknown
X	 */
X	f = chmod( name, perms );
X	err = errno;
X	/*
X	 * done with the daemon stuff
X	 */
X	Clear_uid();
X	errno = err;
X	return( f );
X}
X
X/***************************************************************************
X * Rename_cf( char *tf )
X * rename the temporary file to a control file
X ***************************************************************************/
X
Rename_cf( tf )
X	char *tf;
X{
X	char from[MAXPATHLEN+1];
X	char to[MAXPATHLEN+1];
X	int i, err;
X
X	(void)sprintf( from, "%s/%s", SD, tf );
X	(void)sprintf( to, "%s/c%s", SD, tf+1 );
X	if(Debug>4)log(XLOG_DEBUG,"Rename_cf: from '%s' to '%s'",from, to);
X	Set_daemon();
X	i = rename( from, to );
X	err = errno;
X	Clear_uid();
X	errno = err;
X	if( i < 0 ){
X		logerr_die(XLOG_INFO,"Rename_cf: rename '%s' to '%s' failed",from, to);
X	}
X}
X
X/***************************************************************************
X * Tailor_names()
X * Appends the Host information to several file names.
X ***************************************************************************/
static
append_name( s, t )
X	char *s, *t;
X{
X	if( (strlen(s) + strlen(t) + 2) > MAXPATHLEN ){
X		fatal( XLOG_INFO,"append_name: names too long 's' + 't'", s, t);
X	}
X	(void)strcat( s, t);
X}
Tailor_names()
X{
X	append_name( Lpdlogf, Host );
X	append_name( Masterlock, Host );
X	append_name( Permfile, Host );
X	append_name( Printcap, Host );
X}
END_OF_FILE
if test 25393 -ne `wc -c <'src/utils.c'`; then
    echo shar: \"'src/utils.c'\" unpacked with wrong size!
fi
# end of 'src/utils.c'
fi
echo shar: End of archive 15 \(of 16\).
cp /dev/null ark15isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 16 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.



More information about the Comp.sources.unix mailing list