From:	MERC::"uunet!alcatel.co.@!Dietmar.Berg"  9-JUN-1992 07:54:45.10
To:	uucm000@uxa.ecn.bgu.edu, osyjm@schizo.coe.montana.edu,
	BEAR@nucvs7.anu.edu.au, martinea@ireq.hydro.qc.ca, ONASCH@ira.uka.de,
	klein@saturn.glassboro.edu, powers@elros.stg.trw.com, galaxy::gleeve
CC:	
Subj:	LPD for UCX Part01 of 6

#! /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 1 (of 6)."
# Contents:  lpd.h lpd.c showjob.c deljob.c cmd.c descrip.mms lpd.opt
#   baseline.c
# Wrapped by berg@valide on Tue Jun  9 12:28:48 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'lpd.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lpd.h'\"
else
echo shar: Extracting \"'lpd.h'\" \(28429 characters\)
sed "s/^X//" >'lpd.h' <<'END_OF_FILE'
X/* @(#)lpd.h 1.9 1/30/92 15:34:38 */
X
X#define DEBUG 1
X
X/*
X    Definitions for ILPD/DLPD
X
X    Copyright (C) 1991 Dietmar Berg <berg@alcatel.co.at>.
X    All rights reserved.
X
X    Permission to use, copy, and  distribute  this  software  and  its
X    documentation   without   fee   for   any   purpose   other   than
X    commercialisation is  hereby  granted,  provided  that  the  above
X    copyright  notice  appears  in  all  copies  and  that  both  that
X    copyright notice and this permission notice appear  in  supporting
X    documentation.    This   software  is  provided  "as  is"  without
X    expressed or implied warranty.
X*/
X
X
X#include <rms.h>			/* we need RMS structures here */
X
X
X
X/* Logical names */
X/*
X  All names consist of a network-specific prefix and a fixed part.
X  They are assembled at run-time, but with compile-time constants.
X*/
X#define DLPD$CMD       "CMD"		/* requests for DLPD go here */
Xextern char dlpd$cmd[];
X#define DLPD$CONFIG    "CONFIG"   	/* proxy users and printer options */
Xextern char dlpd$config[];
X#define DLPD$SCRATCH   "SCRATCH" 	/* scratch directory */
Xextern char dlpd$scratch[];
X#define DLPD$DEBUG     "DEBUG"		/* debug level */
Xextern char dlpd$debug[];
X
X#define DLPD$MAXCONNS   "MAXCONNS"	/* Max # of simultaneous conns */
Xextern char dlpd$maxconns[];
X#define MAXCONNS_DEF    8		/* default value if unspecified */
X#define MAXCONNS_MIN    1		/* at least these */
X#define MAXCONNS_MAX   16		/* never more than these */
X
X#define NETOBJ_NAM     "DLPD"	    	/* network object name (DECnet) */
X
X#define DLPD$INET_PORT      "INET_PORT"	/* Internet port number (UCX) */
Xextern char dlpd$inet_port[];
X#define INET_PORT_DEF       515		/* default if unspecified */
X#define DLPD$MAIL_FORMAT    "MAIL_FORMAT"
Xextern char dlpd$mail_format[];		/* mail_addr() format string for mail */
X
X#define DLPD$MAILER    "MAILER"		/* process name for mailer */
Xextern char dlpd$mailer[];
X
X
X
X/* min # of remaining ASTs when doing sych to job */
X#define MIN_AST_REMAIN		(MAX_ACTIVE_CONN+3)
X
X
X/* debug bit-masks */
X#define DBG_JOB_HOLD		0x00000001	/* set /HOLD */
X#define DBG_JBC_JOB		0x00000002	/* log job create/close/abort */
X#define DBG_JBC_FILE		0x00000004	/* log add_file() */
X#define DBG_RCV			0x00000008	/* log receiving of file(s) */
X#define DBG_GETQUI		0x00000010	/* log $GETQUI ops */
X#define DBG_DELJOB		0x00000020	/* log job-deletions */
X#define DBG_NET			0x00000100	/* log network activity */
X#define DBG_CONN_Q		0x00000200	/* print connection queue */
X#define DBG_REQUEST		0x00000400	/* log incoming requests */
X#define DBG_FN			0x00001000	/* log names of control and data files */
X#define DBG_CF			0x00002000	/* log control file */
X#define DBG_MAIL		0x00010000	/* log mail */
X#define DBG_MAILER_OUTPUT	0x00020000	/* log output of mailer process */
X#define DBG_CONFIG		0x00100000	/* configuration database */
X#define DBG_VMSQTYPES 		0x00200000	/* VMS queue database */
X
X
X/* String lengths (with room for trailing '\0') */
X
X#define LOGNAM_LEN	 (32+1) /* logical names */
X#define Q_NAM_LEN	 (32+1)	/* queue name */
X#define DEV_NAM_LEN	 (32+1)	/* device name */
X#define ACC_NAM_LEN       (8+1)	/* account */
X#define J_NAM_LEN	 (40+1)	/* jobname */
X#define U_NAM_LEN	 (12+1)	/* username */
X#define PRC_NAM_LEN      (12+1) /* process name */
X#define HOST_NAM_LEN	 (16+1)	/* UNIX-host name */
X#define NODE_NAM_LEN	  (8+1)	/* VMS node name */
X#define VMS_STR		(255+1)
X#define SPOOL_FN_LEN	 (20+1)	/* spool-filename (dfxnnnnnhost) */
X#define NET_BUF_LEN	  1024	/* network I/O */
X#define MAIL_MBX_BUF_LEN  2048	/* max length of message to UNIX-user */
X#define RES_NAM_LEN	 (32+1)	/* resource name */
X
X
X
X/* Privileges */
X
X#define DEFAULT_PRIVS  (PRV$M_TMPMBX|PRV$M_NETMBX)
X#define EXTENDED_PRIVS (PRV$M_CMKRNL|PRV$M_SYSNAM|PRV$M_SYSPRV)
X
X
X
Xtypedef unsigned long  u_long;
Xtypedef unsigned short u_short;
Xtypedef unsigned char  u_char;
X
X
X
X/* Timeouts */
X
X#define TIMER_DELAY     "0 00:05:00.00"		/* DELTA-time in ASCII fmt */
X#define CONN_TO_TICKS	             4		/* 20 mins for disconnect */
X#define FLUSH_LOG_TO_TICKS          72		/* 6 hours for logfile flush */
X#define PRINT_STAT_TO_TICKS       2016		/* 1 week for stat. display */
X
X
X/* Definitions for user-database */
X
X
X/* dummy VMS user(s) which must always start with '$' and be uppercase only */
X
X/*
X   when this name is found at the left side, notification mail is sent
X   to the user at the right side who corresponds to the machine from
X   which the current job comes.
X*/
X#define DUMMY_NOTIFY_VMS_USER	"$NOTIFY"
X#define DUMMY_DEFAULT_PERM	"$DEFAULT"
X
X/* built-in default permission when nothing else is specified */
X#define DEFAULT_PERM		PERM_PRINT
X#define PERM_UNSPEC		0x80	/* not specified */
X
X
Xtypedef struct host_tab_entry
X    {
X	char host [HOST_NAM_LEN];	/* name of host */
X	short equiv_idx;		/* index to equivalence_name */
X	short notify_user_idx;		/* index to mail-rvcr for notify */
X    } HOST_TAB_ENTRY;
X
X
Xtypedef struct
X    {
X	char equiv [HOST_NAM_LEN];	/* equivalence name */
X	short first_host_idx;		/* index to first name in tab */
X    } EQUIV_TAB_ENTRY;
X
X
Xtypedef struct user_tab_entry
X    {
X	char user [U_NAM_LEN];		/* name of UNIX-user */
X	short host_idx;			/* index to name of UNIX-host */
X	short equiv_idx;		/* index to equiv name */
X	short vms_user_idx;		/* index to VMS-name */
X	u_char perm;			/* permissions for submitting of jobs */
X#define   PERM_PRINT  0x1		  /* printing allowed */
X#define   PERM_BATCH  0x2		  /* BATCH execution allowed */
X    } USER_TAB_ENTRY;
X
X
Xtypedef struct vms_user_tab_entry
X    {
X	char vms_user [U_NAM_LEN];	/* name of VMS-user */
X	char account [ACC_NAM_LEN];	/* name of VMS-account */
X	long uic;			/* UIC */
X    } VMS_USER_TAB_ENTRY;
X
X
X/* Structures for describing VMS queue (static info, built at startup) */
X
X
Xtypedef struct target_q_entry
X    {
X	/* the fwd/backward link *MUST* be the first entries */
X	struct target_q_entry * next;
X	struct target_q_entry * last;
X	char queue[Q_NAM_LEN];
X	struct q_tab_entry * qte;	/* backwd link */
X    } TARGET_Q_ENTRY;
X
X
Xtypedef struct q_tab_entry
X    {
X	u_char q_type;			/* queue type (Batch/Print) */
X#define       QT_PRINT  PERM_PRINT	  /* simplified perm. check */
X#define       QT_BATCH  PERM_BATCH	  /* simplified perm. check */
X#define       QT_OTHER  0x80
X	char queue[Q_NAM_LEN];		/* name of queue */
X	u_long queue_flags;		/* queue flags */
X	TARGET_Q_ENTRY * targets[2];	/* list of generic-targets */
X	u_long ops;			/* operations (statistics) */
X    } QUEUE_TAB_ENTRY;
X
X
X/* Definitions for statistics */
X
X
Xtypedef struct
X    {
X	char timestamp[64];		/* last time shown */
X        long req_rej_overload;		/* # of rejected requests (overload) */
X	long total_requests;		/* total # of requests */
X	long req_chk; 			/* # of REQ_CHK  requests */
X	long req_lpr; 			/* # of REQ_LPR  requests */
X	long req_lpqs;			/* # of REQ_LPQS requests */
X	long req_lpql;			/* # of REQ_LPQL requests */
X	long req_lprm;			/* # of REQ_LPRM requests */
X	long bytes_rcvd;		/* total # of data-bytes received */
X	long files_rcvd;		/* total # of data-files received */
X	long jobs_rcvd_nok;		/* jobs received incorrectly */
X	long jobs_queued;		/* jobs queued successfully */
X	long jobs_dequeued;		/* jobs removed via lprm(1) */
X	short peak_conns;		/* peak # of simultaneous conns */
X    } STATISTIC_STRUCT;
X
X
X
Xtypedef struct proc_info
X    {
X        long pid;
X        char prcnam[PRC_NAM_LEN];
X	char username[U_NAM_LEN];
X	char nodename[NODE_NAM_LEN];
X        long astact, asten;
X        long astcnt, astlm;
X        long biocnt, biolm;
X        long bytcnt, bytlm;
X        long diocnt, diolm;
X        long enqcnt, enqlm;
X        long filcnt, fillm;
X        long tqcnt, tqlm;
X        long authprv[2];
X        long curprv [2];
X       	long dirio, bufio;
X	long pagfilcnt, pgflquota;
X	long wssize, wspeak, wsauthext;
X 	long virtpeak;
X	u_long cputim;			/* 10ms ticks */
X	u_long logintim[2];		/* standard 64-bit time */
X    } PROC_INFO;
X
X
X/* Definitions for printer configuration-database */
X
X
Xtypedef struct
X    {
X	char *abbrev;			/* abbreviation of setup */
X	char *setup;			/* full string */
X    } SETUP_TAB_ENTRY;
X
X
X
Xtypedef struct
X    {
X	char *abbrev;			/* abbreviation of param */
X	char *param;			/* full string */
X    } PARAM_TAB_ENTRY;
X
X
X
X/* structure describing a printer */
Xtypedef struct
X    {
X	char * setup;			/* /SETUP=... */
X	char * param[8];		/* /PARAMETERS=... */
X	char queue[Q_NAM_LEN];		/* /QUEUE=... */
X	char wildc_queue[Q_NAM_LEN];	/* wildcard queue name */
X	QUEUE_TAB_ENTRY * queue_tab_ptr;/* ptr into queue_tab */
X    } PRINTER_CONFIG_STRUCT;
X
X
X/*  Lock services  */
X
Xtypedef struct lksb
X    {
X	u_short status;
X	u_short reserved;		/* reserved for DEC */
X	u_long  lock_id;
X    } LKSB;
X
X
Xtypedef struct lock_info
X    {
X	char resnam[RES_NAM_LEN];	/* name of resource */
X	short blockedby;		/* # of locks blocking this one */
X	short blocking;			/* # of locks blocked by this one */
X    } LOCK_INFO;
X
X
X/*  Structures describing files  */
X
X
X/* short file identification */
Xtypedef struct
X    {
X	/* elements *must* be in this order, they map to the
X	   fields of the RMS NAM-block */
X	char t_dvi[16];			/* ~ nam$t_dvi */
X	u_short w_fid[3];		/* ~ nam$w_fid */
X	u_short w_did[3];		/* ~ nam$w_did */
X    } FILE_ID;
X
X
X/* files comprising a print job */
Xtypedef struct file_list_entry
X    {
X	/* the fwd/backward link *MUST* be the first entries */
X	struct file_list * next;
X	struct file_list * last;
X	char vms_fn[VMS_STR];		/* VMS-filename */
X	FILE_ID file_id;		/* RMS NAM-fields */
X    } FILE_LIST_ENTRY;
X
X
X
X/* Structures describing a print job for queueing it. */
X
X
X/* Data for job synchronization AST */
Xtypedef struct
X    {
X	char mail_fmt[VMS_STR];		/* printf() string for MAIL addr */
X	char mail_recipient[VMS_STR];	/* UNIX owner or $NOTIFY-user */
X	char owner[VMS_STR];		/* UNIX owner of job */
X	char host[HOST_NAM_LEN];	/* UNIX host for this job */
X	char job_status[VMS_STR];	/* filled in by close_job() */
X	long iosb[2];			/* for asynchronuous I/O */
X    } SYNCH_AST_DATA;
X
X
Xtypedef enum { RCV_WAIT_1, RCV_DF, RCV_WAIT_2, RCV_CF, RCV_DONE } RCV_STATE;
X
Xtypedef struct
X    {
X	PRINTER_CONFIG_STRUCT * config;	/* /QUEUE=/SETUP=/PARAM=... */
X	char * user_params; 		/* supplied via -C option */
X	char * user_setup; 		/* supplied via -C option */
X	char job_name[J_NAM_LEN];	/* /NAME=... */
X	u_char header_flag;		/* /HEAD if TRUE */
X	u_char flagp_flag;		/* /FLAG if TRUE */
X	u_char passall_flag;		/* /PASSALL if TRUE */
X	long job_copies;		/* /COPIES=... */
X	char owner[U_NAM_LEN];		/* /NOTE=... */
X	long uic;			/* /USER=... */
X	char * vms_user;		/* VMS username */
X	char * account;			/* VMS account */
X	FILE_LIST_ENTRY * file_list[2];	/* converted UNIX filenames */
X	char host[HOST_NAM_LEN];	/* host invoking lpr(1) */
X	char mail_recipient[VMS_STR];	/* MAIL recipient (name only) */
X	SYNCH_AST_DATA * synch_ast_data;/* for notification */
X	long job_entry;			/* filled in by create_job() */
X	char job_status[VMS_STR];	/* filled in by close_job() */
X	char note[VMS_STR];		/* /NOTE (holding UNIX-owner) */
X	struct FAB fab;			/* for RMS operations */
X	struct NAM nam;			/* for RMS operations */
X	struct RAB rab;			/* for RMS operations */
X	struct XABPRO xabpro;		/* for RMS operations */
X	char spool_fn[SPOOL_FN_LEN];	/* file being received */
X	char job_id[SPOOL_FN_LEN];	/* name of first spool-file */
X	long byte_count;		/* bytes to receive */
X	char netbuf[NET_BUF_LEN];	/* for network I/O */
X	short len;			/* current buffer length */
X	short idx;			/* current buffer index (get) */
X	long iosb[2];			/* for async. ops */
X	RCV_STATE rcv_state;		/* context for ASTs */
X    } PRINT_JOB_STRUCT;
X
X
X/*
X  Data structures for describing queues, print-jobs and
X  their files. They are filled by $GETQUI-calls and used for
X  both display and removal of jobs.
X*/
X
X
X/* List of jobs in a given queue */
X
Xtypedef struct job_list_entry
X    {
X	/* the fwd/backward link *MUST* be the first entries */
X	struct job_list_entry * next;	/* ctrl blk for job-list */
X	struct job_list_entry * last;
X	FILE_LIST_ENTRY * file_list[2];	/* ctrl blk for file-list */
X	long entry;			/* entry # of job */
X	char queue[Q_NAM_LEN];		/* queue where it has been found */
X	char subm_queue[Q_NAM_LEN];	/* queue where it was submitted */
X	long uic;			/* VMS-owner of job */
X	u_long size;			/* job size goes here */
X	u_long jstat;			/* job status goes here */
X	char jobname[J_NAM_LEN+1];	/* job name goes here */
X	char username[VMS_STR+1];	/* job username goes here */
X	char note[VMS_STR];		/* string is returned herein */
X	char owner[U_NAM_LEN];		/* UNIX user */
X	char host[HOST_NAM_LEN];	/* UNIX host */
X	u_long intervj;			/* intervening jobs */
X	u_long intervsize;		/* size of intervening jobs */
X	char selected;			/* 1: ok, -1: nopriv */
X    } JOB_LIST_ENTRY;
X
X
X
X/* Queue description */
X
Xtypedef struct queue_list_entry
X    {
X	/* the fwd/backward link *MUST* be the first entries */
X	struct queue_list_entry * next;	/* ctrl blk for queue-list */
X	struct queue_list_entry * last;
X	JOB_LIST_ENTRY * job_list[2];	/* ctrl blk for job-list */
X	char queue[Q_NAM_LEN];		/* name of queue */
X	char node[NODE_NAM_LEN+2];	/* node (+ "::") goes here */
X	char device[DEV_NAM_LEN+1];	/* device (+ ":") goes here */
X	u_long qstat;			/* queue status goes here */
X	u_long flags;			/* flags go here */
X    } QUEUE_LIST_ENTRY;
X
X
X
X/* List of selection criteria for $GETQUI */
X
Xtypedef enum {ENTRY, USER, ALL, REMOTE_QUERY} SELECT_MODE;
X
Xtypedef struct sel_tab_entry
X    {
X	SELECT_MODE sel_mode;
X	long sel;			/* entry # or vms_user-tab_idx */
X    } SEL_TAB_ENTRY;
X
X
X/* Structure describing queue for purpose of lpq, lprm */
X
Xtypedef struct queue_info_struct
X    {
X	PRINTER_CONFIG_STRUCT * config;	/* /QUEUE=/SETUP=/PARAM=... */
X	QUEUE_LIST_ENTRY * queue_list[2];/* ctrl blk for queue-list */
X	u_long requestor_uic;		/* UIC of lprm user */
X	u_char root_flag;		/* lprm-request from root */
X	char * search_queue;		/* search-spec for $GETQUI */
X	TARGET_Q_ENTRY * target_q_ptr;	/* ptr to list of generic targets */
X	SEL_TAB_ENTRY * sel_tab;	/* ptr to table of sel criteria */
X	short n_sel_tab_entries;	/* size of above table */
X	int (*select_fun)(/*Q_ENTRY * q, JOB_LIST_ENTRY * jle*/);
X	long iosb[2];			/* for $GETQUI */
X	u_long intervj, intervsize;	/* temporary storage */
X    } QUEUE_INFO_STRUCT;
X
X
X/* Definitions for connection queue */
X
X
Xtypedef enum link_state {IDLE, CONNECTED, DISC_REQ, CLOSED} LINK_STATE;
Xtypedef enum proc_state
X    {
X	STARTED,
X	RECEIV_PND, RECEIV_ACT,
X	SNDJBC_PND, SNDJBC_ACT,
X	GETQUI_PND, GETQUI_ACT, GETQUI_DONE,
X	SEND_ACT,   SEND_DONE,
X	DONE
X     } PROC_STATE;
X
X
X
X/* lpd-protocol request codes */
X
X#define REQ_CHK	 	'\01'		/* check queue */
X#define REQ_LPR 	'\02'		/* print job */
X#define REQ_LPQS 	'\03'		/* show queue, short fmt */
X#define REQ_LPQL 	'\04'		/* show queue, long fmt */
X#define REQ_LPRM 	'\05'		/* remove job */
X
X
Xtypedef struct q_entry
X    {
X	/* the fwd/backward link *MUST* be the first entries */
X	struct q_entry *next;		/* forward link  */
X	struct q_entry *last;		/* backward link */
X	u_short channel;		/* I/O channel */
X	u_short unit;			/* I/O unit number */
X	short iosb[4];			/* network iosb */
X	u_long timestamp[2];		/* connection time */
X	LINK_STATE link_state;		/* link state */
X	PROC_STATE proc_state;		/* processing state */
X	u_char timeout_ctr;		/* for shutting down */
X	u_char retry_counter;		/* for network retransmission */
X	char request[1024];		/* lpd-protocol request */
X	char * msgbuf;			/* ptr to output buffer */
X	long msglen, msgoffs;		/* length, offset for next op */
X	char host[HOST_NAM_LEN];	/* name of originating host */
X	char mail_fmt[VMS_STR];		/* printf() string for MAIL addr */
X	PRINT_JOB_STRUCT * job;		/* ptr to associated job */
X	QUEUE_INFO_STRUCT * qui;	/* for GETQUI-based service */
X	LKSB config_lksb;		/* lock-sblk for config */
X	LKSB proc_lksb;			/* lock-sblk for getqui, sndjbc */
X    } Q_ENTRY;
X
X
X/*   M e s s a g e s   a n d   F o r m a t - s t r i n g s   */
X
X
X
X/* format string for print/scan of /NOTE */
X#define NOTE_FMT		"Job queued by user %s from host %s"
X
X
X
X#define UNKNOWN_USER	"\
XUnknown user %s, please check configuration file."
X
X#define DISABLED_USER	"\
XUser %s disabled, please check configuration file and/or UAF."
X
X#define NONETWORK_USER	"\
XUser %s has no network access, please check configuration file and/or UAF."
X
X#define NOBATCH_USER	"\
XUser %s has BATCH qualifier but no batch access, please check\r\n\
Xconfiguration file and/or UAF."
X
X#define BATCHPRIV_USER	"\
XPRIVILEGED user %s has BATCH qualifier requested, disallowing access\r\n"
X
X#define	NOPRIVS_OPMSG		"\
XNot all required privileges enabled: \r\n\
XTMPMBX, NETMBX, SYSPRV, SYSNAM, CMKRNL.\r\n\
XTerminating..."
X
X
X#define ERR_IN_CONFIG_OPMSG	"\
XError opening configuration file, check logical name.\r\n\
XTerminating..."
X
X
X#define NOMAILER_OPMSG		"\
XError creating mailer process.\r\n\
XTerminating..."
X
X
X#define UNKN_SETUP_OPMSG	"\
XUnknown setup \"=%s\" requested for printer %s,\r\n\
Xfrom node %s, please check configuration files."
X
X
X#define UNKN_PARAM_OPMSG	"\
XUnknown parameter \"/%s\" requested for printer %s\r\n\
Xfrom node %s, please check configuration files."
X
X
X#define NO_ACCNT_OPMSG		"\
X%s attempt from %s at %s:\r\n\
XNo proxy account defined in %s for this type of request."
X
X
X#define NO_PRINT_ACCNT_MAIL	"\r\n\
XSorry, you don't have a proxy account set up for\r\n\
Xprinting. Check with your VMS system manager!\r\n\
XYour job has been flushed...\r\n"
X
X#define NO_BATCH_ACCNT_MAIL	"\r\n\
XSorry, you don't have a proxy account set up for\r\n\
Xsubmitting jobs. Check with your VMS system manager!\r\n\
XYour job has been flushed...\r\n"
X
X
X/*   M a c r o s   */
X
X#define free_if_non_null(p)	  if(p) {free(p); p=NULL;}
X
X#define odd(i)	  		  ((i)&1)
X
X#define min(a,b)		  (((a)<(b))?(a):(b))
X
X#define ACK_CHAR		  '\0'
X#define write_ack(chan)	  	  net_write(chan,"",1)
X#define NAK_CHAR		  '\014'
X#define write_nak(chan)	  	  net_write(chan,"\014",1)
X#define write_ack_nak(stat,chan)  net_write(chan,(stat)?"":"\014",1)
X#define EOF_CHAR		  '\032'
X
X
X/* Macro to force VMS condition codes to a certain severity */
X#define $W(x)	(((x)&0xFFFFFFFC))
X#define $S(x)	(((x)&0xFFFFFFFC)|1)
X#define $I(x)	(((x)&0xFFFFFFFC)|3)
X#define $E(x)	(((x)&0xFFFFFFFC)|2)
X#define $F(x)	(((x)&0xFFFFFFFC)|4)
X
X
X/* my own replacement of the assert-macro, with some side-effects */
X#ifdef assert
X#undef assert
X#endif
X#ifdef NDEBUG
X#define assert(expr)
X#define assert_oddstat(stat)
X#define assert_showval(expr,val)
X#else
X#define assert(expr) 					\
X	if	(expr) ;				\
X	else {  extern int _assert ();			\
X		extern char assertion_msg[];		\
X		sprintf (assertion_msg, 		\
X		"Assertion failed:\r\n  %s\r\n  %s, Line %d\r\n  \"expr\"\r\n",\
X			sccsid, __FILE__, __LINE__);	\
X		_assert ("expr", __FILE__, __LINE__); abort(); }
X#define assert_oddstat(stat) 				\
X	if (!odd(stat))					\
X	{  extern int _assert ();			\
X	   extern char assertion_msg[];			\
X	   sprintf (assertion_msg, 			\
X		"Assertion failed: odd(0x%X) --- %s\r\n  %s\r\n  %s, Line %d\r\n", \
X		    (stat), getmsg(stat), sccsid, __FILE__, __LINE__);	\
X		_assert (getmsg(stat), __FILE__, __LINE__); abort(); }
X#define assert_showval(expr,val) 			\
X	if	(expr) ;				\
X	else {  extern int _assert ();			\
X		extern char assertion_msg[];		\
X		sprintf (assertion_msg, 		\
X		"Assertion failed: (value=%d)\r\n  %s\r\n  %s, Line %d\r\n  \"expr\"\r\n",	\
X		    (val), sccsid, __FILE__, __LINE__);	\
X		_assert ("expr", __FILE__, __LINE__); abort(); }
X# endif
X
X
X/*
X * Macro to construct a string descriptor from a non-static string.
X * It differs from $DESCRIPTOR in taking strlen() instead of sizeof()
X*/
X#define $DYN_DESCRIPTOR(name,string) \
X struct dsc$descriptor_s name = { strlen(string), DSC$K_DTYPE_T, DSC$K_CLASS_S, string }
X
X
X/*   F u n c t i o n   P r o t o t y p e s   */
X
X
X/* module LPD */
X
Xlong decode_request (Q_ENTRY * q);	/* decode first char of first msg */
Xvoid free_q_entry (Q_ENTRY * q);	/* dealloc memory with cleanup */
Xlong lock_config_shr (Q_ENTRY * q);	/* lock config resource shared */
Xlong lock_config_excl (LKSB * lksb);	/* lock config resource exclusively */
Xlong lock_sndjbc (Q_ENTRY * q);		/* lock $SNDJBC resource */
Xlong lock_getqui (Q_ENTRY * q);		/* lock $GETQUI resource */
Xlong unlock (LKSB * lksb);		/* unlock resource currently held */
Xlong get_lock_info (u_long lkid, LOCK_INFO * lki); /* get info about lock */
Xchar * fmt_q_entry (Q_ENTRY * q);	/* dump q-entry to string */
Xvoid   show_q ();			/* dump conn-queue to stderr */
Xchar * fmt_q ();			/* dump conn-queue to string */
X
X
X/* module UTIL */
X
Xchar * strdup (const char * s);			/* return duplicate of s */
Xchar * strupr (char * s);			/* convert s to uppercase */
Xchar * strtrm (char * s);			/* strip leading and trailing space */
X
Xlong set_acl (long objtyp, u_short obj_chan, char *aclstr); /* add ACL */
Xchar * translate_lognam (char * lognam);	/* translate via FILE_DEV */
Xchar * get_timestamp (void);			/* get current time */
X
Xu_short get_unit_number (u_short channel);	/* return unit # to channel */
Xchar * get_devnam (u_short channel);		/* get device name to channel */
Xlong get_ast_count ();				/* get # of remaining ASTs */
Xchar * getmsg (long status);			/* get VMS error msg */
Xchar * getmsgid (long status);			/* get VMS error msg ID */
X
Xvoid flush_logfile (void);			/* re-open stderr */
X
Xlong delete_file (FILE_ID * file_id);		/* delete file by id */
X
Xlong start_mailer ();				/* start DCL subprocess */
Xint check_mail_format (char *fmt);			/* format check */
Xchar * mail_addr (char *fmt, char *user, char *host);	/* formatter */
Xvoid send_mail (char *dst, char *subj, char *text);	/* call MAIL11 */
Xvoid mail_logfile (char *dst, char *filename);		/* call DCL/MAIL */
Xvoid stop_mailer ();				/* close mbx to subprocess */
X
Xlong start_timer ();
X
Xvoid op_msg (char * msg);			/* send msg to operator */
X
Xchar * format_statistics (STATISTIC_STRUCT * st);/* print to static string */
Xvoid reset_statistics (STATISTIC_STRUCT * st);	/* reset to 0; timestamp */
Xvoid add_statistics (STATISTIC_STRUCT * st1, STATISTIC_STRUCT * st2);
X						/* add *st2 to *st1 */
Xvoid show_statistics (void);			/* print complete info */
X
Xlong do_remote_query (Q_ENTRY * q);		/* set up response to query */
X
Xchar * file_to_string (char * fn);		/* read file into dyn. string */
XPROC_INFO * get_proc_info ();			/* get accounting info */
Xchar * format_proc_info ();			/*  ... into a string */
Xchar * format_queue_tab ();			/*  ... into a string */
X
Xint net_read (u_short channel, char * buf, int count);
Xint net_write (u_short channel, char * buf, int count);
X
Xlong send_msg (Q_ENTRY * q);			/* send msg to link (async) */
Xvoid send_errmsg_eof (Q_ENTRY * q, long status);/* send errmsg (async), close link */
X
Xchar * fmt_queue_flags (u_long qfl);		/* format VMS-queue flags */
Xchar * fmt_vms_queue_entry (QUEUE_TAB_ENTRY * qte); /* format VMS-queue entry */
X
X
X
X/* module CONFIG */
X
Xint parse_config_file (void);
Xshort find_host_idx (char * host);		/* return index of matching entry */
Xshort find_equiv_idx (char * equiv);		/* return index of matching entry */
Xint equivalent_hosts (char * host_a, char * host_b);
X
Xshort get_user_idx (char * unix_user, char * unix_host);
Xshort get_vms_user_idx (char * unix_user, char * unix_host);
X
Xchar * translate_setup (char * p);		/* return full string for abbrev. */
Xchar * translate_param (char * p);		/* return full string for abbrev. */
X
Xvoid dump_user_database (void);			/* dump to stderr */
X
X
X
X/* module CMD */
X
Xlong create_cmd_mbx ();
Xvoid read_cmd_mbx ();
Xvoid delete_cmd_mbx ();
X
X
X
X/* module DNET/INET */
X
Xlong network_init (void);
Xlong send_eof (Q_ENTRY * q);			/* send EOF to link */
Xvoid close_conn (Q_ENTRY * q);			/* close connection (forced) */
Xvoid network_shutdown (void);
X
X
X
X/* module SNDJBC */
X
Xint send_job_to_jbc (Q_ENTRY * q);		/* start a new job */
X
X
X
X/* module GETQUI */
X
Xvoid build_vms_queue_tab (u_char q_types);	/* get available queues */
Xlong get_job_list   (Q_ENTRY * q);		/* start $GETQUI calls */
Xvoid getqui_cleanup (Q_ENTRY * q, long status);	/* final cleanup */
X
X
X
X/* module SHOWJOB */
X
Xvoid show_queue (Q_ENTRY * q);			/* format and display result */
X
X
X
X/* module DELJOB */
X
Xvoid delete_jobs (Q_ENTRY * q); 		/* delete jobs in list */
X
X
X/*   V M S   S y s t e m   S e r v i c e s   */
X
X/* definitions for VMS system services */
Xtypedef struct
X    {
X	u_short buflen;
X	u_short item_code;
X	char * bufadr;
X	int * retlen_addr;
X    } ITEM_LIST_3;
X
X
Xextern long SYS$ASSIGN (struct dsc$descriptor * dev_descr,
X			u_short * channel,
X			long accmode,
X			struct dsc$descriptor * mbx_descr);
X
Xextern long SYS$ASCTIM (u_short * timlen,
X			struct dsc$descriptor * timbuf_descr,
X			u_long * timadr,
X			u_long cvtflg);
X
Xextern long SYS$CHANGE_ACL (u_short channel,
X			u_long * objtyp,
X			struct dsc$descriptor * objnam_descr,
X			ITEM_LIST_3 * itmlst,
X			u_long * acmode,
X			long nullarg1,
X			u_long * contxt);
X
Xextern long SYS$DASSGN (u_short channel);
X
Xextern long SYS$DCLAST (void * astadr, void * astprm, u_long * acmode);
X
Xextern long SYS$CANCEL (u_short channel);
Xextern long SYS$CLREF  (u_long efn);
X
Xextern long SYS$CRELNM (u_long * attr,
X			struct dsc$descriptor * tabnam,
X			struct dsc$descriptor * lognam,
X			u_char * acmode,
X			ITEM_LIST_3 * itmlst);
X
Xextern long SYS$CREMBX (u_char prmflg, u_short * chan,
X			u_long maxmsg, u_long bufquo,
X			u_long promsk, u_long acmode,
X			struct dsc$descriptor * lognam);
X
Xextern long SYS$CREPRC (u_long * pid,
X			struct dsc$descriptor * image,
X			struct dsc$descriptor * input,
X			struct dsc$descriptor * output,
X			struct dsc$descriptor * error,
X			u_long * prvadr,
X			u_long * quota,
X			struct dsc$descriptor * prcnam,
X			u_long baspri,
X			u_long uic,
X			u_short mbxunt,
X			u_long stsflg);
X
Xextern long SYS$DELLNM (struct dsc$descriptor * tabnam,
X			struct dsc$descriptor * lognam,
X			u_char * acmode);
X
Xextern long SYS$DEQ    (u_long lkid, u_long * valblk,
X			u_long acmode, u_long flags);
X
Xextern long SYS$ENQ    (u_long efn, u_long lkmode,
X			LKSB * lksb,
X			u_long flags,
X			struct dsc$descriptor * resnam,
X			u_long parid,
X			void * astadr, void * astprm, void * blkast,
X			u_long acmode, long nullarg);
X
Xextern long SYS$GETDVI (u_long efn, u_short chan,
X			struct dsc$descriptor * devnam,
X			ITEM_LIST_3 * itmlst, short * iosb,
X			void * astadr, void * astprm, long nullarg);
X
Xextern long SYS$GETJPIW (u_long efn, u_long * pidadr,
X			struct dsc$descriptor * devnam,
X			ITEM_LIST_3 * itmlst, short * iosb,
X			void * astadr, void * astprm);
X
Xextern long SYS$GETLKI (u_long efn, long * lkidadr,
X			ITEM_LIST_3 * itmlst, short * iosb,
X			void * astadr, void * astprm, long nullarg);
X
Xextern long SYS$GETMSG (long status, long * msglen,
X			struct dsc$descriptor * bufadr,
X			long flags, u_char * outadr);
X
Xextern long SYS$GETQUI  (u_long efn, long func, long nullarg,
X			 ITEM_LIST_3 * itmlst, short * iosb,
X		         void * astadr, void * astprm);
X
Xextern long SYS$GETQUIW (u_long efn, long func, long nullarg,
X			 ITEM_LIST_3 * itmlst, short * iosb,
X		         void * astadr, void * astprm);
X
Xextern long SYS$GETUAI (long nullarg1, long nullarg2,
X			struct dsc$descriptor * usrnam,
X			ITEM_LIST_3 * itmlst,
X			long nullarg3, long nullarg4, long nullarg5);
X
Xextern long SYS$HIBER ();
X
Xextern long SYS$PARSE_ACL (struct dsc$descriptor * aclstr,
X			   struct dsc$descriptor * aclent,
X			   u_short * errpos,
X		  	   u_long * accnam,
X			   long nullarg);
X
Xextern long SYS$QIO  (u_long efn, u_short chan, long func, short * iosb,
X		      void * astadr, void * astprm, ...);
X
Xextern long SYS$QIOW (u_long efn, u_short chan, long func, short * iosb,
X		      void * astadr, void * astprm, ...);
X
Xextern long SYS$SETAST (u_char enblfg);
Xextern long SYS$SETEF  (u_long efn);
Xextern long SYS$SETPRV (u_char on_off, long * prvadr,
X			u_char prmflg, long * prvprv);
X
Xextern long SYS$SNDJBC  (u_long efn, long func, long nullarg,
X			 ITEM_LIST_3 * itmlst, long * iosb,
X			 void * astadr, void * astprm);
X
Xextern long SYS$SNDJBCW (u_long efn, long func, long nullarg,
X			 ITEM_LIST_3 * itmlst, long * iosb,
X			 void * astadr, void * astprm);
X
X
Xextern long SYS$SNDOPR (void * msgbuf, u_short channel);
X
Xextern long SYS$TRNLNM (u_long * attr,
X			struct dsc$descriptor * tabnam,
X			struct dsc$descriptor * lognam,
X			u_char * acmode,
X			ITEM_LIST_3 * itmlst);
X
Xextern long SYS$WAKE (long * pid, struct dsc$descriptor * prcnam);
Xextern long SYS$WFLOR (u_long efn, u_long mask);
X
X
X
Xextern long LIB$ASN_WTH_MBX ();
Xextern long LIB$GET_EF (u_long * efn);
X
Xextern long LIB$SIGNAL (long status, long argc, ...);
Xextern long LIB$STOP (long status);
X
Xextern long SYS$EXIT (long status);
END_OF_FILE
if test 28429 -ne `wc -c <'lpd.h'`; then
    echo shar: \"'lpd.h'\" unpacked with wrong size!
fi
# end of 'lpd.h'
fi
if test -f 'lpd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lpd.c'\"
else
echo shar: Extracting \"'lpd.c'\" \(38129 characters\)
sed "s/^X//" >'lpd.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)lpd.c 1.11 1/30/92 15:34:35";
X/*
X    UNIX Line Printer/Server Daemon for VAX/VMS
X
X    Copyright (C) 1991 Dietmar Berg <berg@alcatel.co.at>.
X    All rights reserved.
X
X    Permission to use, copy, and  distribute  this  software  and  its
X    documentation   without   fee   for   any   purpose   other   than
X    commercialisation is  hereby  granted,  provided  that  the  above
X    copyright  notice  appears  in  all  copies  and  that  both  that
X    copyright notice and this permission notice appear  in  supporting
X    documentation.    This   software  is  provided  "as  is"  without
X    expressed or implied warranty.
X*/
X
X
X#include <stdio.h>
X#include <stdlib.h>
X#include <ctype.h>
X#include <string.h>
X#include <errno.h>
X
X#include <ssdef.h>
X#include <descrip.h>
X#include <ctype.h>
X#include <lckdef.h>
X#include <lkidef.h>
X#include <jbcmsgdef.h>
X#include <prvdef.h>
X
X#include "lpd.h"
X
X
X/* local event-flags for synchronization */
Xu_long dispatch_ef;		/* set to indicate work for background */
X
X
X/* debug mask, assertion message */
Xlong debug;
Xchar assertion_msg[512];	/* filled in when assertion fails */
X
X/* logical names, assembled at run-time */
Xextern char * prefix;		/* net-specific prefix (DLPD/ILPD) */
Xchar dlpd$cmd[LOGNAM_LEN];
Xchar dlpd$config[LOGNAM_LEN];
Xchar dlpd$scratch[LOGNAM_LEN];
Xchar dlpd$debug[LOGNAM_LEN];
Xchar dlpd$maxconns[LOGNAM_LEN];
Xchar dlpd$inet_port[LOGNAM_LEN];
Xchar dlpd$mailer[LOGNAM_LEN];
Xchar dlpd$mail_format[LOGNAM_LEN];
X
X/* Connection queue */
XQ_ENTRY * q_hdr[2] = { NULL, NULL };	/* "next", "last" ptr */
Xint max_conns;				/* max # of simult. conns */
Xint min_ast_remain;			/* min # of remaining ASTs (set by NW) */
Xextern char * netdev;			/* net-dev name (NET/BG) for display */
X
X
X/* Configuration database */
XLKSB config_lksb = {0, 0, 0};		/* lock-sb for configuration */
X
Xextern HOST_TAB_ENTRY * host_tab;	/* table of equivalent host names */
Xextern EQUIV_TAB_ENTRY * equiv_tab;	/* table of equivalent machines */
Xextern USER_TAB_ENTRY * user_tab;		/* table of UNIX users */
Xextern VMS_USER_TAB_ENTRY * vms_user_tab; 	/* table of VMS users */
X
Xextern short n_host_tab_entries;	/* current # of entries */
Xextern short n_equiv_tab_entries;	/* current # of entries */
Xextern short n_user_tab_entries;	/* current # of entries */
Xextern short n_vms_user_tab_entries;	/* current # of entries */
X
X
X/* VMS Queue database */
Xextern QUEUE_TAB_ENTRY * queue_tab;	/* table of VMS queues */
Xextern short n_queue_tab_entries; 	/* current # of entries */
Xextern u_char q_types;			/* useable queues (PRINT/BATCH) */
X
X
X/* Statistics */
XSTATISTIC_STRUCT statistics;		/* since last displayed */
XSTATISTIC_STRUCT total_statistics;	/* since startup */
X
X
X/* Module-local variables */
Xstatic long exit_status; 		/* for exit-handler (written to by VMS) */
Xstatic char getqui_res_name[RES_NAM_LEN];
Xstatic $DESCRIPTOR (getqui_res_descr, getqui_res_name);
Xstatic char sndjbc_res_name[RES_NAM_LEN];
Xstatic $DESCRIPTOR (sndjbc_res_descr, sndjbc_res_name);
Xstatic char config_res_name[RES_NAM_LEN];
Xstatic $DESCRIPTOR (config_res_descr, config_res_name);
X
X
X/* Declarations for module-local functions */
Xextern long init (void);
Xextern void exit_handler (void);
Xextern char * fn_convert (char * fn);
Xextern char * link_state_enum_to_ascii (LINK_STATE ls);
Xextern char * proc_state_enum_to_ascii (PROC_STATE ps);
Xextern PRINTER_CONFIG_STRUCT * parse_printer_spec (Q_ENTRY * q);
Xextern int q_tab_compar (QUEUE_TAB_ENTRY * p1, QUEUE_TAB_ENTRY * p2);
Xextern long parse_getqui_spec (Q_ENTRY * q);
Xextern int select_job_lpq  (Q_ENTRY * q, JOB_LIST_ENTRY * jle);
Xextern int select_job_lprm (Q_ENTRY * q, JOB_LIST_ENTRY * jle);
Xextern void lock_getqui_ast (Q_ENTRY * q);
Xextern void lock_sndjbc_ast (Q_ENTRY * q);
Xextern void lock_config_shr_ast  (Q_ENTRY * q);
Xextern void lock_config_excl_ast (LKSB * lksb);
Xextern char * proc_state_enum_to_ascii (PROC_STATE ps);
Xextern char * link_state_enum_to_ascii (LINK_STATE ls);
Xextern char * fmt_job_entry (Q_ENTRY * q);
Xextern char * fmt_qui_entry (Q_ENTRY * q);
X
X
X/* Local Macros */
X#define S(link,proc)	((link<<8)|(proc))  /* combine states into a single int */
X
Xint
Xmain ()
X{
X    long status;
X    register Q_ENTRY * q, * n;
X    char * s;
X
X    /* before doing anything, reopen stderr with special RMS options */
X    flush_logfile ();
X
X    SYS$SETAST (0);			/* disable ASTs */
X
X    if (!odd (status = init ()))	/* start network, subproc, ... */
X	return (status);
X
X    SYS$SETAST (1);			/* enable ASTs */
X
X    while (TRUE)			/* loop forever... */
X    {
X	fflush (stderr);
X	SYS$WAITFR (dispatch_ef);		/* wait for work */
X        SYS$CLREF (dispatch_ef);
X	fflush (stderr);
X
X        debug = strtol (translate_lognam (dlpd$debug), NULL, 0);
X
X#ifdef DEBUG
X        if (debug & DBG_CONN_Q)
X	{
X	    SYS$SETAST (0);			/* disable ASTs */
X            if (q_hdr[0] != NULL)		/* do not display empty queue */
X		show_q ();
X	    SYS$SETAST (1);			/* enable ASTs */
X	} /*if*/
X#endif DEBUG
X
X        /* check all timeouts */
X        for (q = q_hdr[0]; q != NULL; q = q->next)
X	    if (q->timeout_ctr > CONN_TO_TICKS)
X	    {
X		fprintf (stderr,
X		    "[%s]  main: timeout exceeded:\n%s",
X					  get_timestamp(), fmt_q_entry(q));
X		if (q->link_state != CLOSED)
X		    /*
X		      call close_conn() which results in DISC_REQ,
X		      but allow some more time for ASTs to clean up
X		    */
X	            SYS$DCLAST (close_conn, q, NULL);
X
X		/* unlock resources after grace period */
X		if (q->timeout_ctr > CONN_TO_TICKS+1)
X		{
X		    unlock (&q->config_lksb);	/* avoid deadlock */
X		    unlock (&q->proc_lksb);	/* avoid deadlock */
X		} /*if*/
X	    } /*if*/
X
X        for (q = q_hdr[0]; q; q = n)
X        {
X	    n = q->next;	/* save ptr to next element, could be removed */
X	    switch (S (q->link_state, q->proc_state))
X	    {
X		case S (DISC_REQ, RECEIV_ACT) :
X			    if (q->job->rcv_state != RCV_DONE)
X			        break;
X		case S (DISC_REQ, STARTED)    :
X		case S (DISC_REQ, GETQUI_PND) :
X		case S (DISC_REQ, GETQUI_DONE):
X		case S (DISC_REQ, SNDJBC_PND) :
X		case S (DISC_REQ, SEND_DONE)  :
X			    q->proc_state = DONE;   /* enable shutdown of link */
X		case S (DISC_REQ, DONE)	      :     /* now really close the link */
X			    SYS$DCLAST (close_conn, q, NULL);
X			    break;
X
X	        case S (CLOSED, STARTED)      :
X	        case S (CLOSED, RECEIV_ACT)   :
X	        case S (CLOSED, GETQUI_PND)   :
X		case S (CLOSED, GETQUI_DONE)  :
X	        case S (CLOSED, SNDJBC_PND)   :
X	        case S (CLOSED, SEND_DONE)    :
X			    fprintf (stderr,
X				    "[%s]  WARNING: Deallocating entry in suspicious state combination:\r\n%s",
X					    get_timestamp(), fmt_q_entry (q));
X	        case S (CLOSED, DONE)         :
X			    SYS$SETAST (0);		/* disable ASTs */
X		    	    rem_2ll (&q_hdr, q);	/* remove from list */
X			    SYS$SETAST (1);		/* enable ASTs */
X	    		    free_q_entry (q);		/* dealloc momory */
X			    break;
X
X	        default			      :  /* state not needing action */
X			    break;
X	    } /*switch*/
X        } /*for*/
X    } /*while(forever)*/
X} /* main() */
X
X/*
X * This exit-handler closes the mailboxes with which it communicates
X * with the mailer-subprocess and the operator and deletes the
X * logical name of the cmd-mbx from the LNM$_SYSTEM_TABLE.
X */
X
Xvoid
Xexit_handler ()
X{
X    char * msgtext;
X    register Q_ENTRY * q;
X    extern char * baseline;		/* describes source-file revisions */
X
X/*    SYS$SETAST (0);			/* no more ASTs */
X
X    delete_cmd_mbx ();
X
X    fputs ("\n\f\n", stderr);
X    if (!odd (exit_status))
X	show_q ();
X
X    for (q = q_hdr[0]; q; q = q->next)
X    {
X	close_conn (q);
X	if (q->proc_state != DONE)
X	{   /* force abort */
X	    q->proc_state = DONE;
X	    close_conn (q);
X	} /*if*/
X    } /*while*/
X
X    network_shutdown ();
X
X    show_statistics ();
X
X    if (! odd (exit_status))
X    {
X        msgtext = ((exit_status & 0xFFFF) == SS$_OPCDEC)
X			? assertion_msg
X			: getmsg (exit_status);
X	strcat (msgtext, "\r\n");
X	fputs (msgtext, stderr);
X	op_msg (msgtext);
X#ifdef CRASH_MAILER
X	send_mail (CRASH_MAILER, "LPD Server crashed", msgtext);
X#endif
X	fputs (format_proc_info (), stderr);
X	fputs (baseline, stderr);
X    } /*if*/
X
X    stop_mailer ();
X} /* exit_handler */
X
Xstatic long
Xinit (void)
X{
X    static char fn[VMS_STR];
X    long status;			/* VMS condition code */
X    char * s;
X    long pid = getpid();		/* this processes pid */
X    static struct
X        {
X	    void * link;		/* for VMS usage */
X	    void * hdlraddr;		/* addr of handler */
X	    long argcnt;		/* # of args to pass (0) */
X	    long * status;		/* ptr to cond code (written to by VMS) */
X        } exh_ctrlblk = {NULL, exit_handler, 0, &exit_status};
X
X    /* build network specific logical names */
X    sprintf (dlpd$cmd,          "%s$%s", prefix, DLPD$CMD);
X    sprintf (dlpd$config,       "%s$%s", prefix, DLPD$CONFIG);
X    sprintf (dlpd$scratch,      "%s$%s", prefix, DLPD$SCRATCH);
X    sprintf (dlpd$debug,        "%s$%s", prefix, DLPD$DEBUG);
X    sprintf (dlpd$maxconns,     "%s$%s", prefix, DLPD$MAXCONNS);
X    sprintf (dlpd$inet_port,    "%s$%s", prefix, DLPD$INET_PORT);
X    sprintf (dlpd$mailer,       "%s$%s", prefix, DLPD$MAILER);
X    sprintf (dlpd$mail_format,  "%s$%s", prefix, DLPD$MAIL_FORMAT);
X
X    debug = strtol (translate_lognam (dlpd$debug), NULL, 0);
X    strcpy (assertion_msg, "(No assertion check failure)\r\n");
X
X    chdir (dlpd$scratch);		/* set default directory */
X    umask (~0600);			/* set default file protection mask */
X
X    LIB$GET_EF (&dispatch_ef);
X    SYS$CLREF (dispatch_ef);
X
X    SYS$SETRWM (1);			/* disable resource wait mode */
X#ifdef RWM_ON
X    SYS$SETRWM (0);			/* enable resource wait mode */
X#endif
X
X    /* construct unique resource names (for lock-services) */
X    sprintf (sndjbc_res_name, "%s$SNDJBC_%08X", prefix, pid);
X    sndjbc_res_descr.dsc$w_length = strlen (sndjbc_res_name);
X    sprintf (getqui_res_name, "%s$GETQUI_%08X", prefix, pid);
X    getqui_res_descr.dsc$w_length = strlen (getqui_res_name);
X    sprintf (config_res_name, "%s$CONFIG_%08X", prefix, pid);
X    config_res_descr.dsc$w_length = strlen (config_res_name);
X
X    reset_statistics (&statistics);	/* init statistics records */
X    reset_statistics (&total_statistics);
X    strcat (total_statistics.timestamp, " (startup)");
X
X    status = check_privs ();
X    if (!odd (status) || (status == SS$_NOTALLPRIV))
X    {
X        op_msg (NOPRIVS_OPMSG);
X	return (status);
X    } /*if*/
X
X    if (!odd (status = parse_config_file()))	/* load configuration */
X    {
X        op_msg (ERR_IN_CONFIG_OPMSG);
X	return (status);
X    } /*if*/
X
X    build_vms_queue_tab (QT_BATCH|QT_PRINT);	/* find *all* available queues */
X
X    if (!odd (status = start_mailer ()))	/* start mailer process */
X    {
X	op_msg (NOMAILER_OPMSG);
X	return (status);
X    } /*if*/
X
X    /* from now on, there might be some cleanup necessary */
X    SYS$DCLEXH (&exh_ctrlblk);			/* declare exit handler */
X
X    if (!odd (status = network_init ()))	/* start network */
X	return (status);
X
X    if (!odd (status = create_cmd_mbx ()))	/* create comm. path to process */
X	return (status);
X
X    read_cmd_mbx ();				/* start listening for requests */
X
X    if (!odd (status = start_timer ()))		/* start periodic timer */
X	return (status);
X} /* init() */
X
X/*
X * This function must be used *whenever* a Q_ENTRY is to be deallocated
X * It knows about all components which are dynamic in nature and frees them
X * as well as releasing any locks that might be held.
X */
X
Xvoid
Xfree_q_entry (Q_ENTRY * q)
X{
X    if (q == NULL)
X	return;			/* already done */
X
X    free_if_non_null (q->msgbuf);
X    if (q->proc_lksb.lock_id != 0)
X	unlock (&q->proc_lksb);		/* still holding a resource */
X    if (q->config_lksb.lock_id != 0)
X	unlock (&q->config_lksb);	/* still holding a resource */
X
X    if (q->job)
X    {
X	register FILE_LIST_ENTRY * f;
X	register PRINT_JOB_STRUCT * j = q->job;
X	free_if_non_null (j->config);
X	free_if_non_null (j->user_params);
X	free_if_non_null (j->user_setup);
X	for (f=j->file_list[0]; f; f = f->next)
X	    delete_file (&f->file_id);
X	free_2ll (&j->file_list);
X	free (q->job);
X    } /*if*/
X    if (q->qui)
X    {
X	register QUEUE_LIST_ENTRY * qle;
X	register JOB_LIST_ENTRY   * jle;
X	while ((qle = q->qui->queue_list[0]) != NULL)
X	{
X	    while ((jle = qle->job_list[0]) != NULL)
X	    {
X	        while (jle->file_list[0] != NULL)
X		    free (rem_2ll_h (jle->file_list));
X	        free (rem_2ll_h (qle->job_list));
X	    } /*while*/
X	    free (rem_2ll_h (q->qui->queue_list));
X        } /*while*/
X	free_if_non_null (q->qui->sel_tab);
X	free (q->qui);
X    } /*if*/
X
X    memset (q, 0, sizeof (Q_ENTRY));		/* invalidate contents */
X
X    free (q);
X} /* free_q_entry() */
X
X/*
X * This function is called (at AST level) when a request has been
X * received to decode it. Data structures are initialized and
X * proc_state is switched accordingly. Further processing is initiated
X * by either starting async I/O when receiving a job or queueing for
X * exclusive access to $GETQUI for lpr/lprm-requests.
X */
X
Xint
Xdecode_request (Q_ENTRY * q)
X{
X    long status = SS$_NORMAL;
X
X    if (q == NULL)
X	return ($F(SS$_ABORT));			/* disappeared */
X
X    statistics.total_requests++;
X    switch (q->request[0])
X    {
X        case REQ_CHK  : /* ^A: check queue */
X			statistics.req_chk++;
X			send_errmsg_eof (q, 0);
X			break;
X
X    	case REQ_LPR  : /* ^B: queue job for printing */
X			statistics.req_lpr++;
X			q->job = (PRINT_JOB_STRUCT*) calloc (1, sizeof(PRINT_JOB_STRUCT));
X			assert_showval (q->job != NULL, sizeof(PRINT_JOB_STRUCT));
X			/*
X			   Build mail_addr() format string for mail to the user.
X			   In the format string, "%u" will be replaced by the
X			   username, "%h " will be replaced by the
X			   (UNIX-)hostname. Everything else (gateways, ...)
X			   must already be included in the string.
X			*/
X			{
X			    register char * s =
X					translate_lognam (dlpd$mail_format);
X			    if (*s != '\0')
X			        if (check_mail_format (s))
X				    strcpy (q->mail_fmt, s);
X			}
X
X			q->job->config = parse_printer_spec (q);
X			if (q->job->config == NULL)
X			{   /* abort */
X			    q->proc_state = DONE;
X			    break;
X			} /*if*/
X
X			/* count ops on queue */
X			q->job->config->queue_tab_ptr->ops++;
X
X			q->proc_state = RECEIV_PND;
X			status = receive_print_job (q);	  /* start processing */
X			if (!odd (status))
X			    q->proc_state = DONE;
X			break;
X
X        case REQ_LPQS : /* ^C: list queue in short format */
X			statistics.req_lpqs++;
X			q->qui = (QUEUE_INFO_STRUCT*) calloc (1, sizeof(QUEUE_INFO_STRUCT));
X			assert_showval (q->qui != NULL, sizeof(QUEUE_INFO_STRUCT));
X			q->qui->config = parse_printer_spec (q);
X			if (q->qui->config == NULL)
X			{   /* send msg, abort */
X			    send_errmsg_eof (q, JBC$_NOSUCHQUE);
X			    break;
X			} /*if*/
X			/* count ops on queue */
X			q->qui->config->queue_tab_ptr->ops++;
X			if (*q->qui->config->wildc_queue != '\0')
X		            q->qui->search_queue = q->qui->config->wildc_queue;
X			else
X			{
X			    q->qui->target_q_ptr = q->qui->config->queue_tab_ptr->targets[0];
X			    if (q->qui->target_q_ptr != NULL)
X			    {   /* take first entry in gen. targets as "wildcard" */
X				q->qui->search_queue = q->qui->target_q_ptr->queue;
X				q->qui->target_q_ptr = q->qui->target_q_ptr->next;
X			    }
X			    else
X		                q->qui->search_queue = q->qui->config->queue;
X			} /*else*/
X			if (odd (status = parse_getqui_spec (q)))
X			{
X			    q->proc_state = GETQUI_PND;
X			    status = lock_getqui (q);	/* queue for $GETQUI */
X			} /*if*/
X			if (! odd (status))
X			    send_errmsg_eof (q, status); /* send msg, abort */
X		 	break;
X
X        case REQ_LPQL : /* ^D: list queue in long format */
X			statistics.req_lpql++;
X			q->qui = (QUEUE_INFO_STRUCT*) calloc (1, sizeof(QUEUE_INFO_STRUCT));
X			assert_showval (q->qui != NULL, sizeof(QUEUE_INFO_STRUCT));
X			q->qui->config = parse_printer_spec (q);
X			if (q->qui->config == NULL)
X			{   /* send msg, abort */
X			    send_errmsg_eof (q, JBC$_NOSUCHQUE);
X			    break;
X			} /*if*/
X			/* count ops on queue */
X			q->qui->config->queue_tab_ptr->ops++;
X			if (*q->qui->config->wildc_queue != '\0')
X		            q->qui->search_queue = q->qui->config->wildc_queue;
X			else
X			{
X			    q->qui->target_q_ptr = q->qui->config->queue_tab_ptr->targets[0];
X			    if (q->qui->target_q_ptr != NULL)
X			    {   /* take first entry in gen. targets as "wildcard" */
X				q->qui->search_queue = q->qui->target_q_ptr->queue;
X				q->qui->target_q_ptr = q->qui->target_q_ptr->next;
X			    }
X			    else
X		                q->qui->search_queue = q->qui->config->queue;
X			} /*else*/
X			if (odd (status = parse_getqui_spec (q)))
X			{
X			    if (status == SS$_DBGEVENT)
X			    {	/* special remote query/debug event */
X				if (odd (status = do_remote_query (q)))
X				{   /* text set up for sending */
X				    q->proc_state = SEND_ACT;
X				    send_msg (q);		/* send it */
X				} /*if*/
X				else
X				    send_errmsg_eof (q, status);/* send msg, abort */
X				break;
X			    } /*if*/
X			    else
X			    {	/* "normal" request */
X				q->proc_state = GETQUI_PND;
X			        status= lock_getqui (q);	/* queue for $GETQUI */
X			    } /*else*/
X			} /*if*/
X			if (! odd (status))
X			    send_errmsg_eof (q, status); /* send msg, abort */
X			break;
X
X        case REQ_LPRM : /* ^E: dequeue job */
X			statistics.req_lprm++;
X			q->qui = (QUEUE_INFO_STRUCT*) calloc (1, sizeof(QUEUE_INFO_STRUCT));
X			assert_showval (q->qui != NULL, sizeof(QUEUE_INFO_STRUCT));
X			q->qui->config = parse_printer_spec (q);
X			if (q->qui->config == NULL)
X			{   /* send msg, abort */
X			    send_errmsg_eof (q, JBC$_NOSUCHQUE);
X			    break;
X			} /*if*/
X			/* count ops on queue */
X			q->qui->config->queue_tab_ptr->ops++;
X			if (*q->qui->config->wildc_queue != '\0')
X		            q->qui->search_queue = q->qui->config->wildc_queue;
X			else
X			{
X			    q->qui->target_q_ptr = q->qui->config->queue_tab_ptr->targets[0];
X			    if (q->qui->target_q_ptr != NULL)
X			    {   /* take first entry in gen. targets as "wildcard" */
X				q->qui->search_queue = q->qui->target_q_ptr->queue;
X				q->qui->target_q_ptr = q->qui->target_q_ptr->next;
X			    }
X			    else
X		                q->qui->search_queue = q->qui->config->queue;
X			} /*else*/
X			if (odd (status = parse_getqui_spec (q)))
X			{
X			    q->proc_state = GETQUI_PND;
X			    status = lock_getqui (q);	/* queue for $GETQUI */
X			} /*if*/
X			if (! odd (status))
X			    send_errmsg_eof (q, status); /* send msg, abort */
X			break;
X
X     	default       : /* unrecognized command */
X			fprintf (stderr, "Unrecognized request: 0x%02X\n",
X								q->request[0]);
X			status = $F(SS$_ILLIOFUNC);
X	        	break;
X    } /*switch*/
X    return (status);
X} /* decode_request() */
X
X/*
X * The printer-spec as received is parsed according to the following
X * syntax. All fields but the printer_name are optional, the host field
X * should be stripped by the gateway but is tolerated:
X *
X *	printer_name|search_name=setup/param1/param2.../param8@host
X */
X
Xstatic PRINTER_CONFIG_STRUCT *		/* returns ptr to dynamic struct */
Xparse_printer_spec (Q_ENTRY * qe)
X{
X    char printer[1024];
X    register char *p, *q;
X    register PRINTER_CONFIG_STRUCT * pc =
X	(PRINTER_CONFIG_STRUCT*) calloc (1, sizeof (PRINTER_CONFIG_STRUCT));
X    register int i;
X    char * wildc_queue = NULL;
X    char * setup = NULL;
X    char * param[8];
X    QUEUE_TAB_ENTRY key;	/* for using bsearch() */
X    char msg [VMS_STR];		/* for diagnostic message to operator */
X
X    strcpy (printer, &qe->request[1]);	/* copy to local storage */
X
X    if ((q = strrchr (printer, '@')) != NULL)
X	*q = '\0';			/* strip trailing host-name */
X
X    if ((q = strchr (printer, ' ')) != NULL)
X	*q = '\0';			/* isolate printer from arg(s) */
X    q = printer;
X    strupr (printer);
X
X    if ((p = strchr (printer, '|')) != NULL)
X    {	    /* search name found */
X	*p = '\0';			/* isolate queue name */
X        strcpy (key.queue, printer);	/* init key for bsearch() */
X	wildc_queue = q = p+1;		/* save pointer to search name */
X    } /*if*/
X    else	/* check for other delimiters */
X	if ((p = strchr (printer, '=')) != NULL)
X    {
X	*p = '\0';			/* delimit queue name */
X        strcpy (key.queue, printer);	/* init key for bsearch() */
X	*p = '=';			/* restore delimiter */
X	q = p;				/* ptr to rest of string */
X    } /*if*/
X    else
X	if ((p = strchr (printer, '/')) != NULL)
X    {
X	*p = '\0';			/* delimit queue name */
X        strcpy (key.queue, printer);	/* init key for bsearch() */
X	*p = '/';			/* restore delimiter */
X	q = p;				/* ptr to rest of string */
X    } /*if*/
X    else
X        strcpy (key.queue, printer);	/* init key for bsearch() */
X
X    /* look in queue database for this one, save ptr to it */
X    if ((pc->queue_tab_ptr = (QUEUE_TAB_ENTRY*) bsearch (&key, queue_tab,
X				n_queue_tab_entries, sizeof (QUEUE_TAB_ENTRY),
X                                                        q_tab_compar)) == NULL)
X    {   /* no entry found */
X	sprintf (msg, "Request for unknown queue %s from host %s, check /etc/printcap",
X								printer, qe->host);
X	op_msg (msg);
X	free (pc);
X	return (NULL);
X    } /*if*/
X
X    if ((p = strchr (q, '=')) != NULL)
X    {	    /* setup spec found */
X	*p = '\0';			/* isolate preceding string */
X	setup = q = p+1;		/* save pointer to setup */
X    } /*if*/
X
X    for (i=0; i<8; i++)
X    {
X	param[i] = NULL;		/* clear parameter pointers */
X	pc->param[i] = "";
X    } /*for*/
X
X    for (i=0; (i<8) && ((p = strchr (q, '/')) != NULL); i++)
X    {	    /* parameter found */
X	*p = '\0';			/* isolate preceding string */
X	param[i] = q = p+1;		/* save pointer to parameter */
X    } /*for*/
X
X    if ((pc->setup = translate_setup (setup)) == NULL)
X    {
X	sprintf (msg, UNKN_SETUP_OPMSG, setup, printer, qe->host);
X	op_msg (msg);
X	pc->setup = "";		/* ignore it */
X    } /*if*/
X
X    while (--i >= 0)
X	if ((pc->param[i] = translate_param (param[i])) == NULL)
X        {
X	    sprintf (msg, UNKN_PARAM_OPMSG, param[i], printer, qe->host);
X	    op_msg (msg);
X	    pc->param[i] = "";		/* ignore it */
X        } /*if*/
X
X    /* do final copying here, after all items have been separated! */
X    strcpy (pc->queue, key.queue);
X    if (wildc_queue != NULL)
X	strcpy (pc->wildc_queue, wildc_queue);
X    else
X	*pc->wildc_queue = '\0';
X
X
X    return (pc);
X} /* parse_printer_spec() */
X
X
Xstatic int
Xq_tab_compar (QUEUE_TAB_ENTRY * p1, QUEUE_TAB_ENTRY * p2)
X{
X    return (strcmp (p1->queue, p2->queue));
X} /* q_tab_compar() */
X
X/*
X * This function parses the arguments sent by lpq or lprm requests.
X *
X * The following formats are possible:
X *   % lprm -				<username> <username>
X *   % lprm <user1>  <user2>   		<username> <user1> <user2>
X *   % lprm <entry1> <entry2>  		<username> <entry1> <entry2>
X *   % lprm <user1>  <entry2>		<username> <user1> <entry2>
X *   % lprm <entry1> <user2> 		<username> <user2> <entry1>
X *   # lprm -				-all
X *   # lprm <...>			root <...>
X *
X *   % lpq
X *   % lpq <user1>  <user2>   		<user1> <user2>
X *   % lpq <entry1> <entry2>  		<entry1> <entry2>
X *   % lpq <user1>  <entry2>		<entry2> <user1>
X *   % lpq <entry1> <user2> 		<entry1> <user2>
X *
X * Elements of the string are formatted into SEL_TAB_ENTRIES in a
X * dynamically allocated table q->qui->sel_tab.
X * If insufficient args are present for lprm, this function
X * returns SS$_INSUFARG. If the user who issues the lprm-request
X * is unknown, the status SS$_NOSUCHUSER is returned.
X *
X * Entry numbers > 0x10000 terminate the scan of the string and
X * cause the status SS$_DBGEVENT to be returned (remote debugging/query).
X */
X
Xstatic long
Xparse_getqui_spec (Q_ENTRY * q)
X{
X    register char * p = &q->request[1], *t;
X    long status = SS$_NORMAL;
X    short max_entries, idx;
X    register QUEUE_INFO_STRUCT * u = q->qui;
X    register SEL_TAB_ENTRY * s;
X
X    /* initial argument */
X    while (*p && !isspace(*p))		/* skip printer spec */
X	p++;
X    p = strtrm (p);			/* skip space after printer spec */
X
X    /* parse first argument */
X    if (q->request[0] == REQ_LPRM)
X    {	/* request from lprm() */
X	u->select_fun = select_job_lprm;
X	if (strcmp (p, "-all") == 0)
X	{	/* request by root to delete all */
X	    u->root_flag = TRUE;
X	    s = u->sel_tab = (SEL_TAB_ENTRY*) calloc (1,sizeof(SEL_TAB_ENTRY));
X	    assert_showval (s != NULL, sizeof(SEL_TAB_ENTRY));
X	    u->n_sel_tab_entries = 1;
X	    s->sel_mode = ALL;
X	    return (SS$_NORMAL);
X	} /*if*/
X	else
X	{	/* request of "user" for same/other user(s) */
X	    if ((t=strchr (p, ' ')) != NULL)
X		*t = '\0';		/* temporarily isolate username */
X	    u->root_flag = (strcmp (p, "root") == 0);
X	    idx = get_vms_user_idx (p, q->host);
X	    if ((idx < 0) && ! u->root_flag)
X		/* other user than root, but unknown -- reject */
X		status = $F(SS$_NOSUCHUSER);
X	    p = strtrm (p + strlen(p));
X	    if (t != NULL)
X		*t = ' ';		/* join with rest of string */
X	    u->requestor_uic = (idx < 0) ? 0 : vms_user_tab[idx].uic;
X	    if (strlen (p) == 0)
X		status = $F(SS$_INSFARG);
X	} /*else*/
X    } /*if lprm*/
X
X    else	/* request from lpq() */
X    {
X	u->select_fun = select_job_lpq;
X	if (*p == '\0')
X	{   /* no special requests, all entries */
X	    s = u->sel_tab = (SEL_TAB_ENTRY*) calloc (1,sizeof(SEL_TAB_ENTRY));
X	    assert_showval (s != NULL, sizeof(SEL_TAB_ENTRY));
X	    u->n_sel_tab_entries = 1;
X	    s->sel_mode = ALL;
X	    return (SS$_NORMAL);
X	} /*if*/
X    } /*else*/
X
X    p = strtrm (p);
X    if (odd (status))
X    {
X        /* count # of spaces to know how many entries to allocate for sel_tab */
X        for (max_entries=1, t=p; *t; t++)
X	    if (*t == ' ')
X	        max_entries++;
X
X        /* allocate memory for sel_tab */
X        u->sel_tab = (SEL_TAB_ENTRY*) calloc (max_entries,sizeof(SEL_TAB_ENTRY));
X        assert_showval (u->sel_tab != NULL, max_entries*sizeof(SEL_TAB_ENTRY));
X        u->n_sel_tab_entries = 0;		/* no entries yet */
X    } /*if*/
X
X    while (odd(status) && (*p != '\0'))
X    {
X	if (isdigit (*p))
X        {	/* entry # given */
X	    static char * next;
X	    s = &u->sel_tab[u->n_sel_tab_entries++];
X	    s->sel = strtol (p, &next, 10);
X	    if ((s->sel > 0x00010000) && (q->request[0] == REQ_LPQL))
X	    {   /* invalid VMS entry #, but special request code */
X		/* put this in the first entry of sel_tab, return */
X		u->n_sel_tab_entries = 1;	/* discard all we had */
X	        u->sel_tab[0].sel = s->sel;
X	        u->sel_tab[0].sel_mode = REMOTE_QUERY;
X		return (SS$_DBGEVENT);	/* premature end, parse elsewhere */
X	    } /*if*/
X	    else
X	        s->sel_mode = ENTRY;
X	    p = next;
X	} /*if*/
X	else
X	{	/* UNIX username given */
X	    if ((t=strchr (p, ' ')) != NULL)
X		*t = '\0';		/* temporarily isolate username */
X	    idx = get_vms_user_idx (p, q->host);
X	    p += strlen(p);
X	    if (t != NULL)
X		*t = ' ';		/* join with rest of string */
X	    s = &u->sel_tab[u->n_sel_tab_entries++];
X	    if (idx >= 0)
X	    {
X	        s->sel      = idx;
X	        s->sel_mode = USER;
X 	    } /*if*/
X	    else
X		--u->n_sel_tab_entries;	/* ignore this entry */
X	} /*else*/
X        p = strtrm (p);
X    } /*while*/
X
X    return (status);
X} /* parse_getqui_spec() */
X
X/*
X * Selection functions for filtering job-entries from the list
X * produced by $GETQUI services.
X */
X
Xstatic int
Xselect_job_lpq (Q_ENTRY * q, JOB_LIST_ENTRY * jle)
X{
X    register SEL_TAB_ENTRY * s = q->qui->sel_tab;
X    register short n = q->qui->n_sel_tab_entries - 1;
X
X    assert (s != NULL);
X    for ( ; n >= 0; s++, --n)
X	switch (s->sel_mode)
X	{
X	    case ALL      :	return (1);
X	    case USER     :	if (jle->uic == vms_user_tab[s->sel].uic)
X				    return (1);
X				break;
X	    case ENTRY    :	if (jle->entry == s->sel)
X				    return (1);
X				break;
X	    default	  :	/* should never come here */
X				fprintf (stderr, "select_job_lpq: sel_mode=%d ?!\n%s",
X						  s->sel_mode, fmt_q_entry (q));
X				assert (FALSE);	/* programming error */
X	} /*switch*/
X    return (0);
X} /* select_job_lpq() */
X
X/*
X * Delete-permission for a job is determined as follows:
X *
X *   Single entry:
X *      - requestor has same UIC as job owner
X *	- job queued from UNIX, root from an equivalent host
X *
X *   All jobs of a user:
X *      - job queued from UNIX, requestor has same UIC as job owner
X *	- job queued from UNIX, root from an equivalent host
X *      - RESTART_QUEUE is same as "official" queue name
X *
X *   All jobs:
X *	- job queued from UNIX, root from an equivalent host
X *      - RESTART_QUEUE is same as "official" queue name
X */
X
Xstatic int		/* -1: no perm, 0: no match, 1: ok */
Xselect_job_lprm (Q_ENTRY * q, JOB_LIST_ENTRY * jle)
X{
X    register SEL_TAB_ENTRY * s = q->qui->sel_tab;
X    short n = q->qui->n_sel_tab_entries - 1;
X    u_char root = (q->qui->root_flag && equivalent_hosts (jle->host, q->host));
X    u_char uic  = (jle->uic == q->qui->requestor_uic);
X    u_char que  = (strcmp (q->qui->config->queue, jle->subm_queue) == 0);
X
X    assert (s != NULL);
X    for ( ; n >= 0; s++, --n)
X	switch (s->sel_mode)
X	{
X	    case ALL      :	if (que)
X				    return ((root) ? 1 : -1);
X				break;
X	    case USER     :	if (   *jle->host && que
X				    && (jle->uic == vms_user_tab[s->sel].uic))
X				    return ((uic || root) ? 1 : -1);
X				break;
X	    case ENTRY    :	if (jle->entry == s->sel)
X				    return ((uic || root) ? 1 : -1);
X				break;
X	    default	  :	/* should never come here */
X				fprintf (stderr, "select_job_lprm: sel_mode=%d ?!\n%s",
X						  s->sel_mode, fmt_q_entry (q));
X				assert (FALSE);	/* programming error */
X	} /*switch*/
X    return (0);
X} /* select_job_lprm() */
X
X/*
X *  Lock services
X */
X
Xlong
Xlock_getqui (Q_ENTRY * q)
X{
X    return (q->proc_lksb.status = 		/* save for diagnostics */
X		SYS$ENQ (0, LCK$K_EXMODE, &q->proc_lksb, LCK$M_NODLCKWT,
X		     &getqui_res_descr, 0, lock_getqui_ast, q, NULL, 0, 0));
X} /* lock_getqui() */
X
X
X/* AST queued when process is granted exclusive access to $GETQUI */
Xstatic void
Xlock_getqui_ast (Q_ENTRY * q)
X{
X    if (q->link_state != CONNECTED)
X    {	/* connection already disappeared, give up */
X	unlock (&q->proc_lksb);
X	q->proc_state = DONE;
X    } /*if*/
X    else
X    {	/* start asynch processing */
X        assert_oddstat (q->proc_lksb.status);
X        q->proc_state = GETQUI_ACT;
X        get_job_list (q);
X    } /*else*/
X} /* lock_getqui_ast() */
X
X
X
Xlong
Xlock_sndjbc (Q_ENTRY * q)
X{
X    return (SYS$ENQ (0, LCK$K_EXMODE, &q->proc_lksb, LCK$M_NODLCKWT,
X		&sndjbc_res_descr, 0, lock_sndjbc_ast, q, NULL, 0, 0));
X} /* lock_sndjbc() */
X
X
X
X/* AST queued when process is granted exclusive access to $SNDJBC */
Xstatic void
Xlock_sndjbc_ast (Q_ENTRY * q)
X{
X    if (q->link_state != CONNECTED)
X    {	/* connection already disappeared, give up */
X	unlock (&q->proc_lksb);
X	q->proc_state = DONE;
X    } /*if*/
X    else
X    {	/* start asynch processing */
X        q->proc_state = SNDJBC_ACT;
X	send_job_to_jbc (q);
X    } /*else*/
X} /* lock_sndjbc_ast() */
X
X/*
X * Locking on configuration database
X *
X * Active connections queue for shared access,
X * configuration request needs exclusive access.
X */
X
Xlong
Xlock_config_shr (Q_ENTRY * q)
X{
X    return (SYS$ENQ (0, LCK$K_PRMODE, &q->config_lksb, LCK$M_NODLCKWT,
X		&config_res_descr, 0, lock_config_shr_ast, q, NULL, 0, 0));
X} /* lock_config_shr() */
X
X
X/* AST queued when process is granted shared access to configuration */
Xstatic void
Xlock_config_shr_ast (Q_ENTRY * q)
X{
X    if (q->link_state != CONNECTED)
X    {	/* connection already disappeared, give up */
X	unlock (&q->config_lksb);
X	q->proc_state = DONE;
X    } /*if*/
X    else
X    {
X	assert_oddstat (q->config_lksb.status);
X        decode_request (q);    	/* start asynch processing */
X    } /*else*/
X} /* lock_config_shr_ast() */
X
X
X
Xlong
Xlock_config_excl (LKSB * lksb)
X{
X    if (lksb->lock_id == 0)	/* prevent overwriting of (global) lksb */
X        return (lksb->status =  /* save for diagnostics */
X	        SYS$ENQ (0, LCK$K_EXMODE, lksb, LCK$M_NODLCKWT,
X	         &config_res_descr, 0, lock_config_excl_ast, lksb, NULL, 0, 0));
X    else
X	return (lksb->status = $S(SS$_OBJLOCKHELD));
X} /* lock_config_excl() */
X
X
X/* AST queued when process is granted exclusive access to configuration */
Xstatic void
Xlock_config_excl_ast (LKSB * lksb)
X{
X    assert_oddstat (lksb->status);
X    parse_config_file ();		/* load new configuration */
X    unlock (lksb);			/* release lock */
X} /* lock_config_excl_ast() */
X
X
Xlong
Xunlock (LKSB * lksb)
X{
X     long status;
X     status = SYS$DEQ (lksb->lock_id, NULL, 0, 0);
X     lksb->lock_id = 0;			/* invalidate lock-id */
X     return (status);
X} /* unlock() */
X
X
X
Xlong
Xget_lock_info (u_long lkid, LOCK_INFO * lki)
X{
X    u_char blockedby_buf[(MAXCONNS_MAX+2)*24], blocking_buf[(MAXCONNS_MAX+2)*24];
X    long status, blockedby_retlen, blocking_retlen, resnam_retlen;
X    u_short iosb[4];
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(blockedby_buf), LKI$_BLOCKEDBY, blockedby_buf, &blockedby_retlen,
X	sizeof(blocking_buf),  LKI$_BLOCKING,  blocking_buf,  &blocking_retlen,
X	sizeof(lki->resnam)-1, LKI$_RESNAM,    lki->resnam,   &resnam_retlen,
X	0, 0, NULL, NULL
X    };
X
X    if (lkid == 0)
X	return (SS$_IVLOCKID);		/* shortcut */
X
X    *lki->resnam = '\0';
X    lki->blockedby = lki->blocking = 0;
X    status = SYS$GETLKIW (0, &lkid, item_list, iosb, NULL, NULL, NULL);
X    if (! odd(status))
X	return (status);
X    if (! odd (iosb[0]))
X	return (iosb[0]);
X
X    /* get # of locks by dividing field length into total */
X    lki->blockedby = (blockedby_retlen & 0xFFFF) /
X			((blockedby_retlen >> 16) & 0x7FFF);
X    if (blockedby_retlen & 0x8000000)
X	lki->blockedby = -lki->blockedby ;	/* indicate overflow */
X
X    lki->blocking = (blocking_retlen & 0xFFFF) /
X			((blocking_retlen >> 16) & 0x7FFF);
X    if (blocking_retlen & 0x8000000)
X	lki->blocking = -lki->blocking ;	/* indicate overflow */
X
X    lki->resnam[resnam_retlen] = '\0';		/* terminate string */
X    return (SS$_NORMAL);
X} /* get_lock_info() */
X
X/* Debugging aids */
X
X/*
X  These functions format the contents of connection queue entries
X  to a printable representation.
X*/
X
X
Xstatic char *
Xlink_state_enum_to_ascii (LINK_STATE ls)
X{
X    switch (ls)
X    {
X	case IDLE	:	return ("IDLE");
X	case CONNECTED	:	return ("CONNECTED");
X	case DISC_REQ	:	return ("DISC_REQ");
X	case CLOSED	:	return ("CLOSED");
X	default		:	return ("?");
X    } /*switch*/
X} /* link_state_enum_to_ascii() */
X
X
Xstatic char *
Xproc_state_enum_to_ascii (PROC_STATE ps)
X{
X    switch (ps)
X    {
X	case STARTED	   :	return ("STARTED");
X	case RECEIV_PND    :	return ("RECEIV_PND");
X	case RECEIV_ACT    :	return ("RECEIV_ACT");
X	case SNDJBC_PND    :	return ("SNDJBC_PND");
X	case SNDJBC_ACT    :	return ("SNDJBC_ACT");
X	case GETQUI_PND    :	return ("GETQUI_PND");
X	case GETQUI_ACT    :	return ("GETQUI_ACT");
X	case GETQUI_DONE   :	return ("GETQUI_DONE");
X	case SEND_ACT 	   :	return ("SEND_ACT");
X	case SEND_DONE	   :	return ("SEND_DONE");
X	case DONE	   :	return ("DONE");
X	default		   :	return ("?");
X    } /*switch*/
X} /* proc_state_enum_to_ascii() */
X
Xstatic char *
Xfmt_job_entry (Q_ENTRY * q)
X{
X    static char s[256], *p;
X    if (q->job == NULL)
X	return ("");
X    switch (q->job->rcv_state)
X    {
X	case RCV_WAIT_1  :    p = "RCV_WAIT_1";	break;
X	case RCV_WAIT_2  :    p = "RCV_WAIT_2";	break;
X	case RCV_DF      :    p = "RCV_DF";  	break;
X	case RCV_CF      :    p = "RCV_CF";  	break;
X	case RCV_DONE    :    p = "RCV_DONE";	break;
X	default          :    p = "RCV_???";	break;
X    } /*switch*/
X    sprintf (s, "            %s", p);
X    p = s + strlen (s);
X    if ((q->job->iosb[0] != 0) && (!odd (q->job->iosb[0])))
X        sprintf (p, ", %s", getmsgid (q->job->iosb[0]));
X    strcat (p, "\n");
X    return (s);
X} /* fmt_job_entry() */
X
X
Xstatic char *
Xfmt_qui_entry (Q_ENTRY * q)
X{
X    static char s[256];
X    if (q->qui == NULL)
X	return ("");
X    s[0] = '\0';
X    if ((q->qui->iosb[0] != 0) && (!odd (q->qui->iosb[0])))
X        sprintf (s, "            %s\n", getmsgid (q->qui->iosb[0]));
X    return (s);
X} /* fmt_qui_entry() */
X
X/* format a single q-entry into a static string */
X
Xchar *
Xfmt_q_entry (Q_ENTRY * q)		/* returns ptr to static string */
X{
X    static char s[1024], timbuf[24];
X    static $DESCRIPTOR (t_descr, timbuf);
X    char *p;
X    long bintim[2], delta[2];
X    short timlen = 0;
X    LOCK_INFO lki;
X
X    /* get delta time of this q-entry */
X    SYS$GETTIM (bintim);
X    LIB$SUBX (q->timestamp, bintim, delta, NULL);
X    SYS$ASCTIM (&timlen, &t_descr, delta, 0);
X    timbuf[timlen] = '\0';
X
X    sprintf (s,
X	" %08X: %s%u: (%u), %s, %s, %s, TO=%d, nxt=%08X\n",
X		q, netdev, q->unit, q->channel, q->host,
X		link_state_enum_to_ascii(q->link_state),
X		proc_state_enum_to_ascii(q->proc_state),
X		q->timeout_ctr,
X		q->next);
X    p = s + strlen(s);
X    sprintf (p, "            %s\n%13.13c%c %.132s\n",
X		strtrm (timbuf), '^', q->request[0]+'@', &q->request[1]);
X    p += strlen(p);
X
X    if (odd (get_lock_info (q->config_lksb.lock_id, &lki)))
X	sprintf (p, "            %s, %d/%d\n",
X			lki.resnam, lki.blockedby, lki.blocking);
X    p += strlen (p);
X    if ((q->config_lksb.status != 0) && ! odd (q->config_lksb.status))
X	sprintf (p, "            %s\n", getmsgid (q->config_lksb.status));
X    p += strlen (p);
X
X    if (odd (get_lock_info (q->proc_lksb.lock_id, &lki)))
X	sprintf (p, "            %s, %d/%d\n",
X			lki.resnam, lki.blockedby, lki.blocking);
X    p += strlen (p);
X    if ((q->proc_lksb.status != 0) && ! odd (q->proc_lksb.status))
X	sprintf (p, "            %s\n", getmsgid (q->proc_lksb.status));
X    p += strlen (p);
X
X    if (q->msgbuf != NULL)
X    {
X	register char * u;
X	register int i;
X	strcat (p, "            \"");
X	p += strlen (p);
X	u = q->msgbuf;
X	for (i = 28; --i > 0; )
X	{
X	    if (*u == '\0')
X		break;			/* end of string */
X	    if (*u >= ' ')
X		*p++ = *u++;		/* printable char */
X	    else
X	    {				/* control char */
X		*p++ = '^';
X		*p++ = *u++ + 'A';
X	    } /*else*/
X	} /*for*/
X	sprintf (p, "...\" (%d/%d chars)\n", q->msgoffs, q->msglen);
X     } /*if*/
X
X    strcat (p, fmt_job_entry(q));
X    strcat (p, fmt_qui_entry(q));
X    return (s);
X} /* fmt_q_entry() */
X
X/* dump connection queue to a formatted (static) string */
X
Xchar *
Xfmt_q (void)		/* returns ptr to static string */
X{
X    register Q_ENTRY * q;
X    static char s[1024*(MAXCONNS_MAX+4)];
X    sprintf (s, "\n\n[%s]  Connection-list:%s\n",
X			get_timestamp(), (q_hdr[0]==NULL)?" Empty":"");
X    for (q = q_hdr[0]; q; q = q->next)
X	strcat (s, fmt_q_entry (q));
X    return (s);
X} /* fmt_q() */
X
X
X
X/* show connection queue on stderr */
X
Xvoid
Xshow_q (void)
X{
X    fputs (fmt_q(), stderr);
X} /* show_q() */
END_OF_FILE
if test 38129 -ne `wc -c <'lpd.c'`; then
    echo shar: \"'lpd.c'\" unpacked with wrong size!
fi
# end of 'lpd.c'
fi
if test -f 'showjob.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'showjob.c'\"
else
echo shar: Extracting \"'showjob.c'\" \(6869 characters\)
sed "s/^X//" >'showjob.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)showjob.c 1.4 1/30/92 15:34:47";
X/*
X    Show print-job for VMS/UNIX printer daemon
X
X    Copyright (C) 1991 Dietmar Berg <berg@alcatel.co.at>.
X    All rights reserved.
X
X    Permission to use, copy, and  distribute  this  software  and  its
X    documentation   without   fee   for   any   purpose   other   than
X    commercialisation is  hereby  granted,  provided  that  the  above
X    copyright  notice  appears  in  all  copies  and  that  both  that
X    copyright notice and this permission notice appear  in  supporting
X    documentation.    This   software  is  provided  "as  is"  without
X    expressed or implied warranty.
X*/
X
X
X#include <stdio.h>
X#include <ssdef.h>
X#include <stdlib.h>
X#include <ctype.h>
X#include <string.h>
X#include <ctype.h>
X#include <jbcmsgdef.h>
X#include <quidef.h>
X
X#include "lpd.h"
X
X
X/* Debug mask */
Xextern int debug;
X
X
X/* Proxy database */
Xextern VMS_USER_TAB_ENTRY * vms_user_tab; 	/* table of VMS users */
X
X
X/* Local definitions */
X#define Q_PAUSED (QUI$M_QUEUE_PAUSED|QUI$M_QUEUE_PAUSING)
X#define Q_STOPPED (QUI$M_QUEUE_STOPPED|QUI$M_QUEUE_STOPPING)
X
X#define J_EXECUTING (QUI$M_JOB_EXECUTING|QUI$M_JOB_STARTING)
X#define J_HOLDING   (QUI$M_JOB_HOLDING|QUI$M_JOB_TIMED)
X
X
X/* Prototypes for module-local functions */
Xextern char * format_q  (QUEUE_LIST_ENTRY * qle);
Xextern char * format_j  (JOB_LIST_ENTRY * jle, u_char q_type);
X
X/*
X * This function formats and outputs the result of a sequence of $GETQUI-calls.
X * The text is put in a dynamically allocated buffer (q->msgbuf) and given
X * to send_msg() which sends it to the remote side. The trailing EOF-
X * char is included in the message, so sending this will automatically end
X * the connection.
X */
X
X#define JOB_HEAD_PRINT \
X"\n\
X  Jobname               Username           Entry  Blocks  Status\n\
X  --------------------  -----------------  -----  ------  ----------\n"
X
X#define JOB_HEAD_BATCH \
X"\n\
X  Jobname               Username           Entry  Status\n\
X  --------------------  -----------------  -----  ----------\n"
X
Xvoid
Xshow_queue (Q_ENTRY * q)
X{
X    register QUEUE_LIST_ENTRY * qle;
X    register JOB_LIST_ENTRY   * jle;
X    register FILE_LIST_ENTRY  * fle;
X    int n_lines;
X    u_char q_type;
X    char * t, first;
X
X    if (q->link_state != CONNECTED)
X    {
X	send_eof (q);
X	return;		/* short-cut */
X    } /*if*/
X
X    /* count # of output lines for memory allocation */
X    for (n_lines=0, qle=q->qui->queue_list[0]; qle; qle = qle->next)
X	for (first=TRUE, jle=qle->job_list[0]; jle; jle = jle->next)
X	    if (jle->selected)
X	    {
X		if (first)
X		{
X		    n_lines += 3;
X		    first = FALSE;
X		} /*if*/
X	        n_lines++;
X	        if (q->request[0] == REQ_LPQL)
X	            for (fle=jle->file_list[0]; fle; fle = fle->next)
X		        n_lines++;
X	    } /*if*/
X
X    if (n_lines == 0)			/* nothing found */
X    {
X	if (odd(q->qui->iosb[0]))	/* special form of "error" */
X	    q->qui->iosb[0] = $I(JBC$_NOSUCHENT);
X	send_errmsg_eof (q, q->qui->iosb[0]);
X	return;
X    } /*if*/
X
X    if (q->msgbuf != NULL)
X	free (q->msgbuf);
X    q->msgbuf = t = (char*) malloc ((n_lines+2) * VMS_STR);
X    assert_showval (t != NULL, (n_lines+2)*VMS_STR);
X
X    for (qle=q->qui->queue_list[0]; qle; qle = qle->next)
X	for (first=TRUE, jle=qle->job_list[0]; jle; jle = jle->next)
X	    if (jle->selected)
X	    {
X		if (first)
X		{
X	            q_type = (qle->flags & QUI$M_QUEUE_BATCH)
X	                        ? QT_BATCH
X				: (qle->flags & (QUI$M_QUEUE_PRINTER|QUI$M_QUEUE_TERMINAL))
X				   ? QT_PRINT
X				   : QT_OTHER;
X		    strcpy (t, format_q (qle));
X		    t += strlen (t);
X		    strcpy (t, (q_type&QT_PRINT) ? JOB_HEAD_PRINT : JOB_HEAD_BATCH);
X		    t += strlen (t);
X		    first = FALSE;
X		} /*if*/
X	        strcpy (t, format_j (jle, q_type));
X	        t += strlen (t);
X	        if (q->request[0] == REQ_LPQL)
X		    for (fle=jle->file_list[0]; fle; fle = fle->next)
X		    {
X		        sprintf (t, "    %s\n", fle->vms_fn);
X		        t += strlen (t);
X		    } /* for all files */
X	    } /*if job selected*/
X
X    *t++ = EOF_CHAR;			/* last msg for this conn */
X    *t = '\0';				/* terminate string */
X    q->msglen = t - q->msgbuf;  	/* this # of bytes to send */
X    q->msgoffs = 0;			/* start at first byte */
X    q->proc_state = SEND_ACT;		/* advance to next state */
X    send_msg (q);			/* start sending of message */
X} /* show_queue() */
X
Xstatic char *
Xformat_q (QUEUE_LIST_ENTRY * qle)
X{
X    char node[NODE_NAM_LEN+8], *t;
X    static char text[VMS_STR];
X
X    if (*qle->node)
X        sprintf (node, ", on %s::", qle->node);
X    if (*qle->device)
X        strcat (qle->device, ":");
X
X    strcpy (text, "\n");
X    if (qle->flags & QUI$M_QUEUE_GENERIC)
X	strcat (text, "Generic ");
X    if (qle->flags & QUI$M_QUEUE_PRINTER)
X	strcat (text, "printer");
X    else
X    if (qle->flags & QUI$M_QUEUE_TERMINAL)
X	strcat (text, "terminal");
X    else
X    if (qle->flags & QUI$M_QUEUE_BATCH)
X	strcat (text, "batch");
X    else
X    if (qle->flags & QUI$M_QUEUE_SERVER)
X	strcat (text, "server");
X    if (islower (text[1]))
X	text[1] += 'A' - 'a';
X    t = text + strlen (text);
X
X    sprintf (t, " queue %s%s%s%s\n",
X	qle->queue,
X	(qle->qstat & QUI$M_QUEUE_IDLE) ? ", idle" :
X	    (qle->qstat & QUI$M_QUEUE_STALLED) ? ", stalled" :
X	    	(qle->qstat & Q_STOPPED) ? ", stopped" :
X	            (qle->qstat & Q_PAUSED) ? ", paused" : "",
X	node,
X	qle->device);
X    return (text);
X} /* format_q() */
X
Xstatic char *
Xformat_j (JOB_LIST_ENTRY * jle, u_char q_type)
X{
X    static char text[2*VMS_STR];
X    char * t = text;
X    if (*jle->owner && *jle->host)
X	sprintf (jle->username, "%s@%s", jle->owner, jle->host);
X
X    if (jle->intervj > 0)
X    {
X	sprintf (t, "  (%d intervening job%s)%40.40s\n", jle->intervj,
X					(jle->intervj>1) ? "s" : "", " ");
X	if (q_type & QT_PRINT)
X	    sprintf (t+50, "%6u\n", jle->intervsize);
X	t += strlen (t);
X    } /*if*/
X
X    if (q_type & QT_PRINT)
X        sprintf (t, "  %-20.20s  %-17.17s  %5u  %6u  %s\n",
X	  (jle->jstat & QUI$M_JOB_INACCESSIBLE) ? "  (no privilege)" : jle->jobname,
X	  jle->username,
X	  jle->entry,
X	  jle->size,
X	  (jle->jstat & QUI$M_JOB_ABORTING) ? "Aborting" :
X	   (jle->jstat & J_EXECUTING)         ? "Printing" :
X	     (jle->jstat & J_HOLDING)          ? "Holding"  :
X	      (jle->jstat & QUI$M_JOB_REFUSED)  ? "Refused"  :
X	       (jle->jstat & QUI$M_JOB_RETAINED) ? "Retained" : "Pending");
X
X    else   /* BATCH or other type of queue */
X        sprintf (t, "  %-20.20s  %-17.17s  %5u  %s\n",
X	  (jle->jstat & QUI$M_JOB_INACCESSIBLE) ? "  (no privilege)" : jle->jobname,
X	  jle->username,
X	  jle->entry,
X	  (jle->jstat & QUI$M_JOB_ABORTING) ? "Aborting" :
X	   (jle->jstat & J_EXECUTING)         ? "Executing" :
X	     (jle->jstat & J_HOLDING)          ? "Holding"  :
X	      (jle->jstat & QUI$M_JOB_REFUSED)  ? "Refused"  :
X	       (jle->jstat & QUI$M_JOB_RETAINED) ? "Retained" : "Pending");
X
X    return (text);
X} /* format_j() */
END_OF_FILE
if test 6869 -ne `wc -c <'showjob.c'`; then
    echo shar: \"'showjob.c'\" unpacked with wrong size!
fi
# end of 'showjob.c'
fi
if test -f 'deljob.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'deljob.c'\"
else
echo shar: Extracting \"'deljob.c'\" \(5765 characters\)
sed "s/^X//" >'deljob.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)deljob.c 1.5 1/30/92 15:34:47";
X/*
X    Delete print-job for VMS/UNIX printer daemon
X
X    Copyright (C) 1991 Dietmar Berg <berg@alcatel.co.at>.
X    All rights reserved.
X
X    Permission to use, copy, and  distribute  this  software  and  its
X    documentation   without   fee   for   any   purpose   other   than
X    commercialisation is  hereby  granted,  provided  that  the  above
X    copyright  notice  appears  in  all  copies  and  that  both  that
X    copyright notice and this permission notice appear  in  supporting
X    documentation.    This   software  is  provided  "as  is"  without
X    expressed or implied warranty.
X*/
X
X
X#include <stdio.h>
X#include <iodef.h>
X#include <ssdef.h>
X#include <stdlib.h>
X#include <ctype.h>
X#include <string.h>
X#include <prvdef.h>
X#include <ctype.h>
X#include <jbcmsgdef.h>
X#include <sjcdef.h>
X
X#include "lpd.h"
X
X
Xextern int debug;				/* debug mask */
Xextern char * netdev;				/* network device name */
X
X
X/* Statistics */
Xextern STATISTIC_STRUCT statistics;		/* since last displayed */
X
X
X/* Prototypes for module-local functions */
Xextern void delete_job     (Q_ENTRY * q);
Xextern void delete_job_ast (Q_ENTRY * q);
Xextern void msg_delete     (Q_ENTRY * q);
Xextern void msg_delete_ast (Q_ENTRY * q);
Xextern JOB_LIST_ENTRY * purge_entries (Q_ENTRY * q);
X
X/*
X * This function formats and outputs the result of a sequence of $GETQUI-calls.
X *
X * Input is a list of jobs in q->qui->queue_list[0]->job_list.
X * A "selected" value > 0 indicates permission to remove the file,
X * a value < 0 inhibits deletion.
X * For each job, a SNDJBC-request is issued. The completion AST for
X * this sends a message to the link, the completion AST for this
X * again schedules a job for deletion, and so on. Finally, EOF is sent
X * to the link.
X */
X
Xvoid
Xdelete_jobs (Q_ENTRY * q)
X{
X    if (purge_entries (q) == NULL)	/* remove unselected leading entries */
X    {	/* no selected entries at all */
X	if (odd(q->qui->iosb[0]))	/* no other error */
X	    q->qui->iosb[0] = $F(JBC$_NOSUCHENT);	/* give message */
X	send_errmsg_eof (q, q->qui->iosb[0]);
X	return;
X    } /*if*/
X
X    /* allocate buffer for messages */
X    if (q->msgbuf != NULL)
X	free (q->msgbuf);
X    q->msgbuf = (char*) malloc (VMS_STR);
X    assert_showval (q->msgbuf != NULL, VMS_STR);
X
X    delete_job (q);			/* start asynch processing */
X} /* delete_jobs() */
X
Xstatic void
Xdelete_job (Q_ENTRY * q)
X{
X    static long sysprv[2]  = {PRV$M_SYSPRV, 0};
X    long prvprv[2];
X    register long status;
X    register JOB_LIST_ENTRY * jle = q->qui->queue_list[0]->job_list[0];
X
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(jle->entry), SJC$_ENTRY_NUMBER, &jle->entry, NULL,
X	0, 0, NULL, NULL
X    };
X
X    if (q->link_state != CONNECTED)
X    {	/* command aborted */
X	q->proc_state = DONE;		/* do nothing */
X	fprintf(stderr, "[%s]  delete_job: %s%u: aborted by DISC_REQ\n",
X				    	  get_timestamp(), netdev, q->unit);
X	return;
X    } /*if*/
X
X    if (jle->selected > 0)
X    {	/* permission ok */
X        SYS$SETPRV (1, sysprv, 0, prvprv);		/* enable privs */
X        status = SYS$SNDJBC (0, SJC$_DELETE_JOB, 0, item_list, q->qui->iosb,
X							  delete_job_ast, q);
X        prvprv[0] = sysprv[0] & ~prvprv[0];		/* restore privs */
X        SYS$SETPRV (0, prvprv, 1, NULL);
X    }
X    else
X    {	/* no permission, just call AST for continuation */
X	q->qui->iosb[0] = JBC$_NOPRIV;		/* this caused the problem */
X	SYS$DCLAST (delete_job_ast, q, NULL);	/* fake completion */
X	status = $I(JBC$_NOPRIV);		/* "success" */
X    } /*else*/
X
X#ifdef DEBUG
X    if (debug & (DBG_DELJOB))
X	fprintf(stderr, "[%s]  delete_job: %s%u: %s (Entry %u), %s\n",
X		    	get_timestamp(), netdev, q->unit,
X			jle->queue, jle->entry, getmsgid(status));
X#endif DEBUG
X
X    if (!odd(status))
X	send_errmsg_eof (q, status);		/* abort */
X} /* delete_job() */
X
Xvoid
Xdelete_job_ast (Q_ENTRY * q)
X{
X    register long status = q->qui->iosb[0];
X    JOB_LIST_ENTRY * jle = q->qui->queue_list[0]->job_list[0];
X
X    if (odd (status))
X    {
X	sprintf (q->msgbuf, "Job %s (entry %u) dequeued from %s\n",
X		jle->jobname, jle->entry, jle->queue);
X	statistics.jobs_dequeued++;
X    } /*if*/
X    else
X	sprintf (q->msgbuf, "Cannot dequeue job %s (entry %u) from %s: %s\n",
X		      jle->jobname, jle->entry, jle->queue, getmsgid (status));
X#ifdef DEBUG
X    if (debug & (DBG_DELJOB))
X	fprintf(stderr, "[%s]  delete_job_ast: %s%u: %s",
X				get_timestamp(), netdev, q->unit, q->msgbuf);
X#endif DEBUG
X    msg_delete (q);
X} /* delete_job_ast() */
X
Xstatic void
Xmsg_delete (Q_ENTRY * q)
X{
X    register long status;
X    status = SYS$QIO (0, q->channel, IO$_WRITEVBLK, q->iosb,
X		      msg_delete_ast,  q,
X		      q->msgbuf, strlen(q->msgbuf), 0, 0, 0, 0);
X    if (!odd (status))
X	send_errmsg_eof (q, status);	/* abort */
X} /* msg_delete() */
X
X
X
Xstatic void
Xmsg_delete_ast (Q_ENTRY * q)
X{
X    register QUEUE_LIST_ENTRY * qle = q->qui->queue_list[0];
X
X    free (rem_2ll_h (qle->job_list));		/* done with current entry */
X
X    if (purge_entries (q) != NULL)
X	delete_job (q);				/* more to do */
X    else
X	send_errmsg_eof (q, 0);			/* finished */
X} /* msg_delete_ast() */
X
X/*
X * Remove all queue/job entries up to the first selected
X * (positive or negative) one from the list(s).
X * The function returns either the address of the first
X * selected entry or NULL.
X */
X
Xstatic JOB_LIST_ENTRY *
Xpurge_entries (Q_ENTRY * q)
X{
X    register QUEUE_LIST_ENTRY * qle;
X    register JOB_LIST_ENTRY   * jle;
X
X    while ((qle = q->qui->queue_list[0]) != NULL)
X    {
X        while ((jle = qle->job_list[0]) != NULL)
X	    if (jle->selected == 0)
X	        free (rem_2ll_h (qle->job_list));
X	    else
X	        return (jle);			/* found selected entry */
X	free (rem_2ll_h (q->qui->queue_list));
X    } /*while*/
X    return (NULL);
X} /* purge_entries() */
END_OF_FILE
if test 5765 -ne `wc -c <'deljob.c'`; then
    echo shar: \"'deljob.c'\" unpacked with wrong size!
fi
# end of 'deljob.c'
fi
if test -f 'cmd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cmd.c'\"
else
echo shar: Extracting \"'cmd.c'\" \(6607 characters\)
sed "s/^X//" >'cmd.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)cmd.c 1.10 1/30/92 15:34:45";
X/*
X    Operator Communication with VMS/UNIX printer daemon
X
X    Copyright (C) 1991 Dietmar Berg <berg@alcatel.co.at>.
X    All rights reserved.
X
X    Permission to use, copy, and  distribute  this  software  and  its
X    documentation   without   fee   for   any   purpose   other   than
X    commercialisation is  hereby  granted,  provided  that  the  above
X    copyright  notice  appears  in  all  copies  and  that  both  that
X    copyright notice and this permission notice appear  in  supporting
X    documentation.    This   software  is  provided  "as  is"  without
X    expressed or implied warranty.
X*/
X
X
X#include <stdio.h>
X#include <unixio.h>
X#include <descrip.h>
X#include <ssdef.h>
X#include <ctype.h>
X#include <stdlib.h>
X#include <iodef.h>
X#include <lnmdef.h>
X#include <psldef.h>
X#include <prvdef.h>
X#include <string.h>
X#include <acldef.h>
X
X#include "lpd.h"
X
X
Xextern int debug;			/* debug-level */
X
Xextern LKSB config_lksb;		/* locking on configuration */
X
Xextern char * version_str;		/* version string */
Xextern char * baseline;			/* full revision info */
X
X/* Local data */
Xstatic short cmd_mbx_chan = 0;		/* channel for command-mailbox */
X
Xstatic $DESCRIPTOR (tabnam_descr, "LNM$SYSTEM_TABLE");
Xstatic u_char cmd_defined = FALSE;	/* flag if dlpd$cmd has been def'd */
X
Xstatic short cmd_mbx_iosb[4];		/* I/O status block */
Xstatic char dlpd_cmd[VMS_STR];		/* command for DLPD */
X
Xlong
Xcreate_cmd_mbx ()
X{
X    long status;
X    char * devnam;		/* name of mailbox */
X    char aclstr[VMS_STR];	/* ACL for mbx */
X    u_char acmode = PSL$C_USER;
X    long old_privs[2];		/* privilege vectors */
X    static long new_privs[2] = {PRV$M_SYSNAM, 0};
X    $DYN_DESCRIPTOR (lognam_descr, dlpd$cmd);
X
X    ITEM_LIST_3 item_list[] =
X    {
X	0, LNM$_STRING, devnam, NULL,
X	0, 0, 0, 0
X    };
X
X    status = SYS$CREMBX (0, &cmd_mbx_chan, sizeof(dlpd_cmd), sizeof(dlpd_cmd), 0xFF00, 0, NULL);
X    if (!odd (status))
X	return (status);
X
X    devnam = get_devnam (cmd_mbx_chan);
X
X#ifdef CMD_MBX_ACL_ID
X    /* set ACL to mbx */
X    sprintf (aclstr, "(IDENTIFIER=%s,ACCESS=READ+WRITE+EXECUTE+DELETE)", CMD_MBX_ACL_ID);
X    set_acl (ACL$C_DEVICE, cmd_mbx_chan, aclstr);
X#endif
X
X    item_list[0].buflen = strlen (devnam);
X    item_list[0].bufadr = devnam;
X
X    SYS$SETPRV (1, new_privs, 0, old_privs);		/* enable privs */
X    status = SYS$CRELNM (NULL, &tabnam_descr, &lognam_descr, &acmode, item_list);
X    cmd_defined = odd (status);
X    old_privs[0] = new_privs[0] & ~old_privs[0];	/* restore privs */
X    SYS$SETPRV (0, old_privs, 0, NULL);
X    return (status);
X} /* create_cmd_mbx() */
X
X
X
Xvoid
Xdelete_cmd_mbx ()
X{
X    u_char acmode = PSL$C_USER;
X    static long old_privs[2], new_privs[2] = {PRV$M_SYSNAM, 0};
X    static $DESCRIPTOR (tabnam_descr, "LNM$SYSTEM_TABLE");
X    $DYN_DESCRIPTOR (lognam_descr, dlpd$cmd);
X
X    if (cmd_mbx_chan != 0)
X	SYS$DASSGN (cmd_mbx_chan);
X
X    if (cmd_defined)
X    {	/* remove logical name dlpd$cmd pointing to mbx */
X        SYS$SETPRV (1, new_privs, 0, old_privs);	/* enable privs */
X        SYS$DELLNM (&tabnam_descr, &lognam_descr, &acmode);
X        old_privs[0] = new_privs[0] & ~old_privs[0];	/* restore privs */
X        SYS$SETPRV (0, old_privs, 0, NULL);
X    } /*if*/
X} /* delete_cmd_mbx() */
X
X/* Initiate reading from cmd_mbx, result is parsed in cmd_mbx_ast() */
X
Xvoid
Xread_cmd_mbx ()
X{
X    extern void cmd_mbx_ast();
X    SYS$CANCEL (cmd_mbx_chan);		/* cancel outstanding I/O */
X    SYS$QIO (0, cmd_mbx_chan, IO$_READVBLK, cmd_mbx_iosb,
X        	   cmd_mbx_ast, NULL, &dlpd_cmd, sizeof(dlpd_cmd)-1, 0, 0, 0, 0);
X} /* read_cmd_mbx() */
X
X
X/*
X * This AST is queued when something has been written to the cmd_mbx.
X * The received string is parsed and action taken if it can be recognized
X * as one of the keywords.
X * The logfile is closed and reopened to have it written to disk.
X */
Xstatic void
Xcmd_mbx_ast ()
X{
X    register char * p;
X    extern char assertion_msg[];	/* debug info */
X    char msg[VMS_STR];
X
X    if (cmd_mbx_iosb[0] == 0)		/* ????? */
X    {
X	    fprintf (stderr, "[%s]  cmd_mbx_ast: iosb[0]=%u, iosb[1]=%u\n",
X		get_timestamp(), (u_long)cmd_mbx_iosb[0], (u_long)cmd_mbx_iosb[1]);
X	return;				/* ??? */
X    } /*if*/
X
X    if (!odd(cmd_mbx_iosb[0]))		/* most likely EOF */
X    {
X	read_cmd_mbx ();		/* requeue the AST */
X	return;
X    } /*if*/
X
X    dlpd_cmd [cmd_mbx_iosb[1]] = '\0';	/* terminate string */
X    p = strupr (strtrm (dlpd_cmd));	/* isolate cmd */
X
X    sprintf (msg, "Received command \"%s\"", p);
X    op_msg (msg);
X
X    if (strcmp (p, "STAT") == 0)
X        show_statistics ();			/* display statistics */
X    else
X
X    if (strcmp (p, "CONFIG") == 0)
X    {
X	LOCK_INFO lki;
X
X	lock_config_excl (&config_lksb);	/* reinitialize database */
X	get_lock_info (config_lksb.lock_id, &lki);
X#ifdef DEBUG
X	if (debug && (config_lksb.status == $S(SS$_OBJLOCKHELD)))
X	    fprintf (stderr, "[%s]  Lock on %s already held! (%d/%d)\r\n",
X		get_timestamp(), lki.resnam, lki.blockedby, lki.blocking);
X#endif DEBUG
X	if (lki.blockedby > 0)
X	{
X	    sprintf (msg,
X		"[%s]  Exclusive lock on %s queued; blocked by %d requests\r\n",
X				    get_timestamp(), lki.resnam, lki.blockedby);
X	    op_msg (msg);
X	} /*if*/
X    } /*if*/
X    else
X
X    if (strcmp (p, "CLEAR") == 0)
X    {	    	/* get rid of all expired connections */
X	register Q_ENTRY * q;
X	extern Q_ENTRY * q_hdr[2];
X	SYS$SETAST (0);			/* disable ASTs */
X	for (q = q_hdr[0]; q != NULL; q = q->next)
X	    if (q->timeout_ctr > CONN_TO_TICKS+3)
X	    {
X		fputs ("Forcing abort:\n", stderr);
X		fputs (fmt_q_entry (q), stderr);
X		q->proc_state = DONE;		/* BRUTE FORCE */
X		close_conn (q);
X	    } /*if*/
X	SYS$SETAST (1);			/* enable ASTs */
X    } /*if*/
X    else
X
X    if (strcmp (p, "EXIT") == 0)
X	SYS$EXIT (SS$_NORMAL);		/* exit gracefully */
X    else
X
X    if (strcmp (p, "ABORT") == 0)
X    {
X	sprintf (assertion_msg, "Forced abort by message to %s\r\n", dlpd$cmd);
X	abort ();    	 		/* raise an exception */
X    } /*if*/
X    else
X
X    if (strcmp (p, "VERSION") == 0)
X    {
X	op_msg (version_str);		/* display version # */
X	fputs (baseline, stderr);	/* full version info */
X    } /*if*/
X    else
X
X    if (strcmp (p, "SHOW_Q") == 0)
X	show_q ();
X    else
X
X    if (strcmp (p, "ACCOUNTING") == 0)
X	fputs (format_proc_info (), stderr);
X    else
X
X    if (strcmp (p, "DUMP_USERS") == 0)
X	dump_user_database ();		/* dump to stderr */
X    else
X
X    if (strcmp (p, "DUMP_QUEUES") == 0)
X	fputs (format_queue_tab (), stderr);
X
X    read_cmd_mbx ();			/* requeue the AST */
X
X    /* always flush logfile (reopen stderr if not a terminal device) */
X    flush_logfile ();
X} /* cmd_mbx_ast() */
END_OF_FILE
if test 6607 -ne `wc -c <'cmd.c'`; then
    echo shar: \"'cmd.c'\" unpacked with wrong size!
fi
# end of 'cmd.c'
fi
if test -f 'descrip.mms' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'descrip.mms'\"
else
echo shar: Extracting \"'descrip.mms'\" \(1968 characters\)
sed "s/^X//" >'descrip.mms' <<'END_OF_FILE'
X#
X#  Makefile for the VMS/UNIX printer server
X#
X#  Dietmar Berg / Alcatel Austria
X#
X#
X
X# configurable items:
X#  CMD_MBX_ACL_ID: user-id of somewhone who may write to cmd-mailbox
X#  CRASH_MAILER: mail-addr of someone to be notified when process dies
X#  OP_DEBUG: operator msgs written by BRKTHRU to this user 
XCFLAGS = /noopt /list /debug=all -
X         /define=( -
X                    CRASH_MAILER="""SYSTEM" -
X		 )
X#		  , OP_DEBUG="""X100_BERG"""  -
X#                 , CRASH_MAILER="""DVAX01::X100_BERG""" -
X#                 , CMD_MBX_ACL_ID="""[X100,X100_BERG]""" -
X
X
X#   uncomment the following to compile debug-version for DECnet:
X#NET    = dnet.obj
X#   uncomment the following to compile debug-version for UXC/Internet:
XNET    = inet.obj
XNETLIB = ,sys$library:ucx$ipc/lib
X
X
X
XSRCS   = lpd.h, -
X	 lpd.c, -
X	 dnet.c, -
X         inet.c, -
X	 config.c, -
X	 util.c, -
X	 cmd.c, -
X	 rcvjob.c, -
X	 showjob.c, -
X	 deljob.c, -
X	 sndjbc.c, -
X	 getqui.c, -
X	 llist.c, -
X	 baseline.c
X
XOBJS   = lpd.obj, -
X	 config.obj, -
X	 util.obj, -
X	 cmd.obj, -
X	 rcvjob.obj, -
X	 showjob.obj, -
X	 deljob.obj, -
X	 sndjbc.obj, -
X	 getqui.obj, -
X	 llist.obj, -
X	 baseline.obj
X
X
X# Implicit Production rules
X#.c.obj  :
X#	$(CC) $(CFLAGS) $<
X
X
X
X#debug version
Xlpd.exe  : $(OBJS) $(NET)
X	link /nomap /debug /trace /exec=$@ $(OBJS), $(NET), lpd/opt $(NETLIB)
X
X
X
X# production version
Xdlpd.exe : $(OBJS) dnet.obj
X	link /map=dlpd /full /trace /nodebug /exec=$@ $(OBJS), dnet.obj, lpd/opt
X
Xilpd.exe : $(OBJS) inet.obj
X	link /map=ilpd /full /trace /nodebug /exec=$@ $(OBJS), inet.obj, lpd/opt, -
X	sys$library:ucx$ipc/lib
X
X
X
Xlpd.obj      : lpd.c lpd.h
X
Xrcvjob.obj   : rcvjob.c lpd.h
X
Xdnet.obj     : dnet.c lpd.h
X
Xinet.obj     : inet.c lpd.h
X
Xgetqui.obj   : getqui.c lpd.h
X
Xsndjbc.obj   : sndjbc.c lpd.h
X
Xconfig.obj   : config.c lpd.h
X
Xshowjob.obj  : showjob.c lpd.h
X
Xdeljob.obj   : deljob.c lpd.h
X
Xcmd.obj      : cmd.c lpd.h
X
Xutil.obj     : util.c lpd.h
X
Xllist.obj    : llist.c
X
Xbaseline.obj : baseline.c
END_OF_FILE
if test 1968 -ne `wc -c <'descrip.mms'`; then
    echo shar: \"'descrip.mms'\" unpacked with wrong size!
fi
# end of 'descrip.mms'
fi
if test -f 'lpd.opt' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lpd.opt'\"
else
echo shar: Extracting \"'lpd.opt'\" \(28 characters\)
sed "s/^X//" >'lpd.opt' <<'END_OF_FILE'
XSYS$SHARE:VAXCRTL.EXE/SHARE
END_OF_FILE
if test 28 -ne `wc -c <'lpd.opt'`; then
    echo shar: \"'lpd.opt'\" unpacked with wrong size!
fi
# end of 'lpd.opt'
fi
if test -f 'baseline.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'baseline.c'\"
else
echo shar: Extracting \"'baseline.c'\" \(724 characters\)
sed "s/^X//" >'baseline.c' <<'END_OF_FILE'
Xchar*version_no="2.5";
Xchar*version_str="2.5 92/01/30 15:55:54";
Xchar*baseline="Baseline for Version 2.5 92/01/30 15:55:54\n\
X lpd.h \t1.9\t92/01/30 15:34:38\n\
X lpd.c \t1.11\t92/01/30 15:34:35\n\
X rcvjob.c \t1.8\t92/06/03 18:27:42\n\
X showjob.c \t1.4\t92/01/30 15:34:47\n\
X deljob.c \t1.5\t92/01/30 15:34:47\n\
X inet.c \t1.9\t92/01/30 15:34:44\n\
X dnet.c \t1.10\t92/01/30 15:34:43\n\
X sndjbc.c \t1.10\t92/01/30 15:34:37\n\
X getqui.c \t1.9\t92/01/30 15:34:33\n\
X cmd.c \t1.10\t92/01/30 15:34:45\n\
X config.c \t1.7\t92/01/30 15:34:32\n\
X util.c \t1.19\t92/01/30 15:39:27\n\
X llist.c \t1.4\t92/01/30 15:34:40\n\
X lpdgw.c \t1.2\t92/01/30 15:42:23\n\
X dlpd.tex \t1.7\t92/04/21 16:29:19\n\
X README \t1.1\t92/01/30 15:55:21\n\
X";
END_OF_FILE
if test 724 -ne `wc -c <'baseline.c'`; then
    echo shar: \"'baseline.c'\" unpacked with wrong size!
fi
# end of 'baseline.c'
fi
echo shar: End of archive 1 \(of 6\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
