From:	MERC::"uunet!alcatel.co.@!Dietmar.Berg"  9-JUN-1992 08:52:06.27
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 Part02 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 2 (of 6)."
# Contents:  rcvjob.c inet.c dnet.c sndjbc.c llist.c README COPYRIGHT
# Wrapped by berg@valide on Tue Jun  9 12:28:49 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'rcvjob.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rcvjob.c'\"
else
echo shar: Extracting \"'rcvjob.c'\" \(26535 characters\)
sed "s/^X//" >'rcvjob.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)rcvjob.c 1.8 6/3/92 18:27:42";
X/*
X    Receive 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 <iodef.h>
X#include <stdlib.h>
X#include <ctype.h>
X#include <string.h>
X#include <ctype.h>
X
X#include "lpd.h"
X
X
X
X/* Debug mask */
Xextern int debug;
Xextern char * netdev;				/* network device name */
X
X
X/* Proxy database */
Xextern HOST_TAB_ENTRY * host_tab;		/* equivalent host names */
Xextern USER_TAB_ENTRY * user_tab;		/* table of UNIX users */
Xextern VMS_USER_TAB_ENTRY * vms_user_tab; 	/* table of VMS users */
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 */
X
X
X/* Statistics */
Xextern STATISTIC_STRUCT statistics;		/* since last displayed */
X
X/* Prototypes for module-local functions */
Xextern long receive_file (Q_ENTRY * q);
Xextern long rcvjob_cleanup (Q_ENTRY * q, long status);
Xextern long rcv_fspec (Q_ENTRY * q);
Xextern void rcv_fspec_ast (Q_ENTRY * q);
Xextern long sync_write_ack (Q_ENTRY * q);
Xextern long net_readblk (Q_ENTRY * q);
Xextern void net_readblk_ast (Q_ENTRY * q);
Xextern long rms_writeblk (Q_ENTRY * q);
Xextern void rms_writeblk_ast (struct RAB * rab);
Xextern long parse_control_file (Q_ENTRY * q);
Xextern char * sep_search (char * string, char * separators);
Xextern void save_user_params_and_setup (PRINT_JOB_STRUCT * pj, char * buf);
Xextern char * fn_convert (char * fn);
Xextern char * net_gets (char * buf, int maxlen, Q_ENTRY * q);
X
X
X/* Macros */
X#ifdef DEBUG
X#define dbg_msg(f,m,q,stat) {if (debug & (m))				\
X				fprintf(stderr, "[%s]  %s: %s%u: %s\n",	\
X				get_timestamp(),f,netdev,q->unit,getmsgid(stat));}
X#else
X#define dbg_msg(fun,m,q,stat) 	/* no-op */
X#endif DEBUG
X
X
X/*
X * This function starts receiving a print-job.
X */
X
Xlong
Xreceive_print_job (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * pj = q->job; /* ptr to job-struct */
X    register long status;
X
X    pj->rcv_state = RCV_WAIT_1;
X
X#ifdef XXXXX
X    if (!check_queue (pj->config->queue))
X	return FALSE;			/* no such queue */
X#endif
X
X    if (! odd (status = rcv_fspec (q)))
X	rcvjob_cleanup (q, status);
X    else
X	q->proc_state = RECEIV_ACT;	/* continues asynchronuously... */
X
X    return (status);
X} /* receive_print_job() */
X
X/*
X * This function *must* be called whenever receiving of a job is
X * done (either ok or in error). Additionally, it is also called
X * after a data-file has been received (for switching to the next one).
X */
X
Xstatic long
Xrcvjob_cleanup (Q_ENTRY * q, long status)
X{
X    register FILE_LIST_ENTRY * f;
X
X    /* special error handling if net_gets() returned NULL due to EOF */
X    if (odd (status) && (q->link_state != CONNECTED))
X	status = SS$_LINKDISCON;
X
X    if (odd (status))
X    {
X	dbg_msg ("rcvjob_cleanup", DBG_RCV|DBG_NET, q, status);
X	switch (q->job->rcv_state)
X	{
X	    case RCV_WAIT_2 :	/* data file received, waiting for fspec */
X	    case RCV_DF     :	/* data file received successfully */
X			        memcpy (&q->job->file_list[1]->file_id,
X				  &q->job->nam.nam$t_dvi, sizeof(FILE_ID));
X				q->job->rcv_state = RCV_WAIT_2;
X				status = rcv_fspec (q);	 /* read next filespec */
X				break;
X
X	    case RCV_CF     :	/* control file parsed successfuly */
X				q->proc_state = SNDJBC_PND;
X				status = lock_sndjbc (q);	/* queue for resource */
X				break;
X
X	    case RCV_DONE   :	/* already finished */
X				break;
X
X	    case RCV_WAIT_1 :	/* not started yet, should not terminate! */
X	    default         :  	/* should never come here */
X				fprintf (stderr, "rcvjob_cleanup: RCV_STATE=%d ?!\n%s",
X					q->job->rcv_state, fmt_q_entry(q));
X				assert (FALSE);	 /* programming error */
X	} /*switch*/
X    } /*if*/
X
X    if (!odd (status))		/* (not ELSE, to catch errs from above) */
X    {	/* error case */
X	fprintf (stderr, "[%s]  rcvjob_cleanup: %s\n%s",
X			  get_timestamp(), getmsgid(status), fmt_q_entry(q));
X	switch (q->job->rcv_state)
X	{
X	    case RCV_DF     :	/* error receiving file, delete it */
X			q->job->fab.fab$l_fop = FAB$M_DLT;
X			sys$close (&q->job->fab, NULL, NULL);
X			free (rem_2ll_t (q->job->file_list));
X
X			/* fall through to next label... */
X
X	    case RCV_WAIT_1 :	/* error while starting */
X	    case RCV_WAIT_2 :	/* error while waiting for fspec */
X	    case RCV_CF     :	/* error parsing control file */
X			if (status == SS$_NOPRIV)
X			{
X			    char t[VMS_STR];
X		 	    u_char q_type = q->job->config->queue_tab_ptr->q_type;
X			    if (*q->mail_fmt)  /* MAIL path to user? */
X			    { /* send mail */
X				char * dst;
X			        dst = mail_addr (q->mail_fmt, q->job->owner, q->job->host);
X			        send_mail (dst,
X					     (q_type & QT_PRINT)
X					        ? "Printing on VAX: Access denied"
X					        : "Batch submission on VAX: Access denied",
X				   	     (q_type & QT_PRINT)
X				          	? NO_PRINT_ACCNT_MAIL
X						: NO_BATCH_ACCNT_MAIL);
X			    } /*if*/
X			    sprintf (t, NO_ACCNT_OPMSG,
X				(q_type & QT_PRINT) ? "Print" : "Batch submission",
X					q->job->owner, q->job->host, dlpd$config);
X			    op_msg (t);
X			    write_ack (q->channel); /* fake success to stop lpr */
X			} /*if*/
X			else
X			{   /* "real" error */
X		            statistics.jobs_rcvd_nok++;	/* count errors */
X			    write_nak (q->channel);
X			} /*else*/
X
X		        send_eof (q);
X		        for (f = q->job->file_list[0]; f; f = f->next)
X			    delete_file (&f->file_id);	/* cleanup */
X
X			break;
X
X	    default  	  :	/* should never come here */
X			{
X			    char msg[VMS_STR];
X			    sprintf (msg, "rcvjob_cleanup: RCV_STATE=%d ?!\n%s",
X					q->job->rcv_state, fmt_q_entry(q));
X			    op_msg (msg);
X			}
X			/* be not that strict in case of error... */
X			/* assert (FALSE); */
X
X			break;
X        } /*switch*/
X    } /*else*/
X    return (status);
X} /* rcvjob_cleanup() */
X
X/*
X * This function first issues an ACK (lpd-protocol) and then starts
X * an async READ operation on the network channel to receive code,
X * filename and byte-count.
X */
X
Xstatic long
Xrcv_fspec (Q_ENTRY * q)
X{
X    register long status;
X
X    if (q->link_state != CONNECTED)
X	return (SS$_LINKDISCON);		/* premature EOF */
X
X    /* request transmission (error-checking version of write_ack()) */
X    status = sync_write_ack (q);
X
X    if (odd (status))
X	/* read from net (expecting filespc and bytecount) */
X        status = SYS$QIO (0, q->channel, IO$_READVBLK, q->iosb,
X		        rcv_fspec_ast, q,
X			q->job->netbuf, sizeof (q->job->netbuf), 0, 0, 0, 0);
X    return(status);
X} /* rcv_fspec() */
X
X
X/*
X * This AST is queued when code, filename and byte-count has been
X * received from the net. The spec is parsed and further ops scheduled
X * accordingly.
X */
X
Xstatic void
Xrcv_fspec_ast (Q_ENTRY * q)
X{
X    register long status = q->iosb[0];
X    register PRINT_JOB_STRUCT * pj = q->job;
X    register FILE_LIST_ENTRY * fle;
X
X    /* sometimes there is a one-byte message preceding
X       the next filespec -- discard it and restart QIO */
X    if (odd (status) && (q->iosb[1] == 1))
X    {
X#ifdef DEBUG
X   	if (debug & DBG_RCV)
X	    fprintf (stderr, "[%s]  rcv_fspec_ast: %s%u: 1-byte msg: 0x%02x\n",
X			      get_timestamp(), netdev, q->unit, pj->netbuf[0]);
X#endif DEBUG
X        status = SYS$QIO (0, q->channel, IO$_READVBLK, q->iosb,
X		        rcv_fspec_ast, q,
X			q->job->netbuf, sizeof (q->job->netbuf), 0, 0, 0, 0);
X        if (!odd (status))
X	    rcvjob_cleanup (q, status);		/* AST would not be called */
X	return;
X    } /*if*/
X
X    if (odd (status))
X    {
X   	pj->netbuf[q->iosb[1]] = '\0';	/* terminate string */
X
X        if (sscanf (&pj->netbuf[1], "%d %s", &(pj->byte_count), pj->spool_fn) == 2)
X	{
X#ifdef DEBUG
X	    if (debug & DBG_FN)
X                fprintf (stderr, "[%s]  rcv_fspec_ast: %s%u: %s, %u chars\n",
X		   		get_timestamp(), netdev, q->unit,
X				pj->spool_fn, pj->byte_count);
X#endif DEBUG
X	    switch (pj->netbuf[0])
X	    {
X	        case '\003' : /* data file */
X			      pj->rcv_state = RCV_DF;
X			      statistics.files_rcvd ++;
X	    		      statistics.bytes_rcvd += pj->byte_count;
X			      /* make new entry, append to list */
X			      fle = (FILE_LIST_ENTRY*)
X					calloc (1, sizeof(FILE_LIST_ENTRY));
X			      assert (fle != NULL);
X			      ins_2ll_t (pj->file_list, fle);
X	    		      strcpy (fle->vms_fn, pj->spool_fn);
X			      status = receive_file (q);   /* start async I/O */
X			      break;
X
X	        case '\002' : /* control file */
X			      pj->rcv_state = RCV_CF;
X    			      status = parse_control_file (q);  /* *synch* I/O */
X			      rcvjob_cleanup (q, status);	/* ALWAYS */
X			      pj->rcv_state = RCV_DONE;
X			      return;
X
X    	        default     : /* lpd protocol error */
X			      status = $E(SS$_PROTOCOL);
X			      break;
X	    } /* switch */
X        } /*if*/
X    } /*if*/
X
X    if (!odd (status))
X	rcvjob_cleanup (q, status);
X} /* rcv_fspec_ast() */
X
X/* Synchronuous writing of ACKnowledge with full status checking */
Xstatic long
Xsync_write_ack (Q_ENTRY * q)
X{
X    static char ack_char = ACK_CHAR;
X    long st;
X    if (q->link_state != CONNECTED)
X	return (SS$_LINKDISCON);
X
X    st = SYS$QIOW (0, q->channel, IO$_WRITEVBLK, q->iosb, NULL, NULL,
X						&ack_char, 1, 0, 0, 0, 0);
X    return ((odd(st)) ? q->iosb[0] : st);
X} /* sync_write_ack() */
X
X/*
X * This function starts receiving one data-file from the network link and
X * saves it to a temporary file. The file is first entered in the default
X * directory, but removed from there before submission,
X * so the file-id must be saved to get hands on it later on.
X * The file is written blockwise with an underlying format of stream-lf.
X */
X
Xstatic long
Xreceive_file (Q_ENTRY * q)
X{
X    register long status;
X    register PRINT_JOB_STRUCT * j = q->job;
X    struct FAB * fab = &j->fab;
X    struct NAM * nam = &j->nam;
X    struct RAB * rab = &j->rab;
X    struct XABPRO * xabpro = &j->xabpro;
X
X    *fab = cc$rms_fab;				/* RMS initialization */
X    *nam = cc$rms_nam;				/* RMS initialization */
X    *rab = cc$rms_rab;				/* RMS initialization */
X    *xabpro = cc$rms_xabpro;			/* RMS initialization */
X
X    fab->fab$l_nam = nam;			/* link NAM to FAB */
X    rab->rab$l_fab = fab;			/* link FAB to RAB */
X    rab->rab$l_ctx = q;				/* link q-entry to RAB */
X    rab->rab$l_rop = RAB$M_BIO;			/* block I/O only */
X    rab->rab$l_rbf = j->netbuf;			/* buffer addr */
X    fab->fab$b_fac = FAB$M_BIO | FAB$M_PUT;	/* block write */
X    fab->fab$b_rfm = FAB$C_STMLF;		/* stream_lf format */
X    fab->fab$b_shr = FAB$M_NIL;			/* no sharing */
X    fab->fab$l_fop = FAB$M_TMP;			/* no dir-entry */
X    fab->fab$l_xab = xabpro;			/* set protections */
X    xabpro->xab$w_pro = 0xFF00;			/* S:RWED,O:RWED,G:,W: */
X
X    /* start with empty FID */
X    memset (&j->file_list[1]->file_id, 0, sizeof(FILE_ID));
X    status = sys$create (fab, NULL, NULL);	/* cannot create file */
X
X    if (odd (status))
X        status = sync_write_ack (q);		/* request transmission */
X
X    if (odd (status))
X    {
X        sys$connect (rab, NULL, NULL);
X        rab->rab$l_rop |= RAB$M_ASY;		/* asynch from now on */
X	q->job->idx = 0;			/* init index into netbuf */
X        status = net_readblk (q);		/* start asynch I/O */
X    } /*if*/
X    return (status);
X} /* receive_file() */
X
X/*
X * This function reads a block (for a data-file) from the net.
X */
X
Xstatic long
Xnet_readblk (Q_ENTRY * q)
X{
X    register long status;
X
X    if (q->link_state != CONNECTED)
X	return (SS$_LINKDISCON);	/* premature EOF */
X
X    status = SYS$QIO (0, q->channel, IO$_READVBLK, q->iosb,
X		        net_readblk_ast, q,
X			q->job->netbuf + q->job->idx,
X			sizeof (q->job->netbuf) - q->job->idx,
X			0, 0, 0, 0);
X
X    dbg_msg ("net_readblk", DBG_RCV, q, status);
X    return (status);
X} /* net_readblk() */
X
X
X
X/*
X * This AST is called when a block has been read from the net.
X * If no error occured, this block is passed to RMS to be written
X * to the spool-file.
X */
X
Xstatic void
Xnet_readblk_ast (Q_ENTRY * q)
X{
X    register long status = q->iosb[0];
X
X    dbg_msg ("net_readblk_ast", DBG_RCV, q, status);
X    if (odd (status))
X    {
X	if (q->iosb[1] > q->job->byte_count)
X	    q->iosb[1] = q->job->byte_count;	/* sometimes one too many in last block */
X	q->job->byte_count -= q->iosb[1];	/* remaining bytes to read */
X	q->job->idx += q->iosb[1];		/* advance buffer ptr */
X	q->job->rab.rab$w_rsz = q->job->idx;	/* # of bytes to write */
X
X	/*
X	   RMS likes the buffer-length to be a multiple of 512;
X	   continue filling if necessary
X	*/
X	if (((q->job->idx % 512) != 0) && (q->job->byte_count > 0))
X	{
X#ifdef DEBUG
X   	if (debug & DBG_RCV)
X	    fprintf (stderr,
X		"[%s]  net_readblk_ast: refill for %s%u: idx=%d, rem=%d\n",
X		      	get_timestamp(), netdev, q->unit,
X			q->job->idx, q->job->byte_count);
X#endif /* DEBUG */
X	    status = net_readblk (q);
X	} /*if*/
X	else
X	    status = rms_writeblk (q);
X    } /*if*/
X
X    if (!odd (status))
X	rcvjob_cleanup (q, status);
X} /* net_readblk_ast() */
X
X/* This function passes a block to RMS to be written to the spool-file */
X
Xstatic long
Xrms_writeblk (Q_ENTRY * q)
X{
X    register long status;
X    status = sys$write (&q->job->rab, rms_writeblk_ast, rms_writeblk_ast);
X
X    dbg_msg ("rms_writeblk", DBG_RCV, q, status);
X    return (status);
X} /* rms_writeblk() */
X
X
X
X/*
X * This AST is called when a block has been written by RMS.
X * If no error occured and there is more data for the spool-file
X * the next read from net is started.
X */
X
Xstatic void
Xrms_writeblk_ast (struct RAB * rab)
X{
X    register long status = rab->rab$l_sts;
X    register Q_ENTRY * q = rab->rab$l_ctx;
X
X    dbg_msg ("rms_writeblk_ast", DBG_RCV, q, status);
X
X    if (odd (status))
X    {
X	q->job->idx = 0;			/* reset index into netbuf */
X	if (q->job->byte_count > 0)
X	{
X	    status = net_readblk (q);		/* more to get */
X	    if (!odd (status))
X 		rcvjob_cleanup (q, status);
X	    return;
X	} /*if*/
X        else
X	{    /* close received file */
X            status = sys$close (&q->job->fab, NULL, NULL);
X	    rcvjob_cleanup (q, status);
X	    return;
X	} /*else*/
X    } /*if*/
X    rcvjob_cleanup (q, status);
X} /* rms_writeblk_ast() */
X
X/*
X * This function receives the control file for the current job
X * from the network link. It is parsed while being read.
X * Each df-file is renamed as soon as a name is known for it.
X * Files which cannot be printed (special format, e.g. DVI, ...)
X * are ignored.
X * Return codes:
X *   SS$_NOPRIV  no proxy for this user
X *   SS$_ABORT   could not rename file
X *   SS$_NORMAL  ok
X *
X * This function is executed at AST level!
X */
X
Xstatic long		/* "file" directly read from network */
Xparse_control_file (Q_ENTRY * q)
X{
X    PRINT_JOB_STRUCT * pj = q->job;	/* struct describing the print-job */
X    register FILE_LIST_ENTRY * f;	/* ptr to files for this job */
X    long status = SS$_NORMAL;
X    char buf[VMS_STR], spool_fn[VMS_STR], title[VMS_STR];
X    u_char found, perm, first=TRUE, ignore=FALSE;
X    u_char header_flag=FALSE, passall_flag=FALSE;
X    long job_copies = 0;
X    short idx;
X    u_char type = pj->config->queue_tab_ptr->q_type;
X
X    pj->flagp_flag = FALSE;
X    pj->mail_recipient[0] = pj->job_name[0] = title[0] = '\0';
X
X    status = sync_write_ack (q);	/* request transmission */
X
X    pj->len = -1;			/* start "buffered" I/O */
X    pj->idx =  0;
X    while (odd(status) && (net_gets (buf, min(sizeof(buf)-1, pj->byte_count), q) != NULL))
X    {
X   	pj->byte_count -= strlen (buf);
X	strtrm (buf);
X#ifdef DEBUG
X	if (debug & DBG_CF)
X	    fprintf (stderr, "%s\n", buf);
X#endif DEBUG
X	switch (buf[0])
X	{
X	    case 'J' :  /* jobname */
X			buf[J_NAM_LEN] = '\0';		/* limit length */
X			strcpy (pj->job_name, &buf[1]);
X			break;
X
X	    case 'C' :  /* classification */
X#ifdef ASDF
X			if (pj->config->queue_tab_ptr->q_type & QT_BATCH)
X			{   /* used as parameter 1 for batch jobs */
X			    strcpy (pj->user_params, &buf[1]);
X			    pj->config->param[0] = pj->user_params;
X			} /*if*/
X#endif
X			save_user_params_and_setup (pj, buf+1);
X			break;
X
X	    case 'L' :  /* literal (identification from /etc/passwd) */
X			pj->flagp_flag = TRUE;
X			break;
X
X	    case 'T' :  /* title for pr(1) */
X			if ((buf[1] != ' ') && (buf[1] != '\0')) /* title given */
X			    strcpy (title, fn_convert(&buf[1]));
X			break;
X
X	    case 'H' :  /* host name */
X			strcpy (pj->host, &buf[1]);
X			break;
X
X	    case 'P' :  /* person (owner for lprm(1)) */
X			strcpy (pj->owner, &buf[1]);
X			break;
X
X	    case 'M' :  /* send mail upon completion */
X			/* if there is a "notify-user" for this host,
X			   send completion mail there instead */
X			if ((idx = find_host_idx (pj->host)) >= 0)
X			    idx = host_tab[idx].notify_user_idx;
X			if (idx < 0)
X			    strcpy (pj->mail_recipient,  &buf[1]); /* default */
X			else
X			    strcpy (pj->mail_recipient,  user_tab[idx].user);
X			break;
X
X	    case 'f' :  /* already formatted file */
X			strcpy (spool_fn, &buf[1]);
X			job_copies++;
X			break;
X
X	    case 'l' :  /* already formatted file, print w. passall */
X			strcpy (spool_fn, &buf[1]);
X			job_copies++;
X			passall_flag++;
X			break;
X
X	    case 'p' :  /* print file using pr(1) */
X			strcpy (spool_fn, &buf[1]);
X			job_copies++;
X			header_flag++;
X			break;
X
X	    case 't' :  /* troff output */
X	    case 'n' :  /* ditroff output */
X	    case 'd' :  /* TeX output */
X	    case 'g' :  /* plot(3X) output */
X	    case 'c' :  /* cifplot() output */
X	    case 'v' :  /* raster image */
X			strcpy (spool_fn, &buf[1]);
X			ignore++; 	/* ignored */
X			break;
X
X	    case '1' :  /* alternate troff font 1 */
X	    case '2' :  /* alternate troff font 2 */
X	    case '3' :  /* alternate troff font 3 */
X	    case '4' :  /* alternate troff font 4 */
X			break;		/* ignored */
X
X	    case 'W' :  /* page width */
X	    case 'I' :  /* indentation */
X			break;		/* ignored */
X
X	    case 'U' :  /* unlink file after printing (always done) */
X			break;
X
X	    case 'N' :  /* name of file being printed */
X			/* LAST ENTRY FOR ONE FILE (hopefully) */
X
X			if (first)
X			{	/* first file for this job, check access */
X			    if ((idx = get_user_idx (pj->owner, pj->host)) < 0)
X		    	    {
X				status = SS$_NOPRIV;
X			        break;
X			    } /*if*/
X
X 			    perm = user_tab[idx].perm;
X			    if (   (type == QT_OTHER)
X			        || (type == QT_BATCH) && ! (perm & PERM_BATCH)
X			        || (type == QT_PRINT) && ! (perm & PERM_PRINT)
X				|| (user_tab[idx].vms_user_idx < 0))
X		    	    {   /* no permission for this type of request */
X				status = SS$_NOPRIV;
X			        break;
X			    } /*if*/
X
X			    sprintf (pj->note, NOTE_FMT, pj->owner, pj->host);
X			    idx = user_tab[idx].vms_user_idx;
X			    pj->uic = vms_user_tab[idx].uic;
X			    pj->vms_user = vms_user_tab[idx].vms_user;
X			    pj->account  = vms_user_tab[idx].account;
X#ifdef DEBUG
X			    if (debug & DBG_CF)
X				fprintf (stderr, "%s ---> %s\n", pj->owner, pj->vms_user);
X#endif DEBUG
X			    if (type != QT_PRINT)
X				strcpy (pj->job_id, spool_fn); /* for name of logfile */
X			    first = FALSE;
X			} /*if first*/
X
X			/* find ptr to this spool-file in FILE_LIST */
X			for (f=pj->file_list[0]; f != NULL; f=f->next)
X			    if (found = (strcmp (f->vms_fn, spool_fn) == 0))
X				break;
X
X			if (ignore)
X			{	/* cannot handle this one */
X#ifdef DEBUG
X			    if (debug & (DBG_CF|DBG_JBC_JOB|DBG_JBC_FILE))
X				fprintf (stderr, "Ignored file: %s\n", f->vms_fn);
X#endif DEBUG
X			    delete_file (&f->file_id);      	/* cleanup */
X			    if (found)
X				rem_2ll (&pj->file_list[0], f); /* remove from list */
X			    ignore = FALSE;
X			    header_flag = passall_flag = FALSE;
X			    job_copies = 0;
X			    break;
X		 	} /*if*/
X
X			/* restore filename */
X			if (title[0] == '\0')	/* explicitely set by 'T'? */
X			    if ( (buf[1]!=' ')&&(buf[1]!='\0')	/* name given */
X			       &&(strcmp(&buf[1],"standard input")!=0))
X				strcpy (f->vms_fn, fn_convert(&buf[1]));
X			    else
X				/* stdin: no filename available, use name of the job */
X				if (pj->job_name[0] != '\0')
X				    strcpy (f->vms_fn, fn_convert(pj->job_name));
X				else
X			    	    strcpy (f->vms_fn, "stdin.");
X			else	/* explicit title set by 'T' */
X			    strcpy (f->vms_fn, fn_convert(title));
X
X			/* copy fields to job-struct, reset */
X			pj->job_copies = job_copies;
X			pj->header_flag = header_flag;
X			pj->passall_flag = passall_flag;
X			job_copies = 0;
X			header_flag = passall_flag = FALSE;
X			break;
X
X	    default  :  /* unrecognized command */
X			fprintf (stderr, "Unrecognized cmd in control file: %s\n", buf);
X			break;
X	} /*switch*/
X    } /*while*/
X
X    return (status);
X} /* parse_control_file() */
X
X/*
X *  Search for next separator char without looking into quoted strings (")
X *  or at quoted (\) characters.
X *  Addr of separator is returned if found, NULL otherwise.
X */
X
Xstatic char *
Xsep_search (char * str, char * sep)
X{
X    register char *s = str, inquote = FALSE;
X    for (s = str; *s; s++)
X    {
X	if (*s == '\\')
X	{   /* escaped char, continue */
X	    if (*(++s) == '\0')
X		return (NULL);
X	    continue;
X	} /*if*/
X	if (*s == '"')
X	    inquote = !inquote;
X	if (! inquote)			/* separator not hidden by '"' */
X	    if (strchr (sep, *s))
X		return (s);		/* return addr where found */
X    } /*for*/
X    return (NULL);
X} /* sep_search() */
X
X/*
X *  This function allows to convey setup and (VMS-)parameter information
X *  to the job.
X *  A setup-specification must start with '+' (which is stripped),
X *  it is added to the one associated with the queue (if there is one).
X *  and copied into pj->user_setup, with the pointer in pj->config->setup
X *  being readjusted.
X *
X *  A parameter specification must start with '/' (which is stripped).
X *  Parameters which come from the general configuration info
X *  for this queue are shifted "up" (and fall out at the top).
X *  The parameters are transferred into pj->user_params, with '\0'
X *  inserted where an unescaped '/' or '+' is found.
X *
X *  e.g.  "+LPS$ERRORHANDLER/MESS=PRINT/OUTPUT_TRAY=SIDE"
X *        The pointer in pj->config->setup is set to point to the
X *        first substring
X *        The first two pointers in pj->config_params
X *        are readjusted to point into pj->user_params.
X */
X
Xstatic void
Xsave_user_params_and_setup (PRINT_JOB_STRUCT * pj, char * buf)
X{
X    int n=0;			/* # of user params accumulated so far */
X    char *s, *t, *up, ch;
X
X    if ((*buf != '/') && (*buf != '+'))
X	return;			/* not meant for us... */
X
X    /* should never be needed, but just in case... */
X    free_if_non_null (pj->user_setup);
X    free_if_non_null (pj->user_params);
X
X    for (s=buf; s; )
X    {
X	ch = '\0';
X	if (*s == '+')
X	{   /* setup follows */
X	    register int l1, l2;
X	    /* allocate sufficient memory on first request */
X	    if (pj->user_setup == NULL)
X	    {
X	        register char *p;
X		l1 = (pj->config->setup) ? strlen (pj->config->setup) : 0;
X		p = pj->user_setup = (char*) calloc (1, l2=l1+strlen(s)+1);
X		assert_showval (p, l2);
X		/* copy configuration-default setup string */
X	        if (l1 > 0)
X		    strcpy (p, pj->config->setup);
X	        pj->config->setup = p;	/* point to here now */
X	    } /*if*/
X	    if (t = sep_search (++s, "+/"))
X	    {
X		ch = *t;		/* save separator */
X		*t = '\0';
X	    } /*if*/
X	    l1 = strlen (pj->user_setup);
X	    l2 = strlen (s = strtrm (s));
X	    /* append new setup */
X	    if (l2 > 0)
X	        sprintf (pj->user_setup+l1, "%s%s", ((l1 > 0) ? "," : ""), s);
X	    if (t)
X		*t = ch;		/* restore separator */
X	    s = t;
X	} /*if*/
X	else
X        if (*s == '/')
X	{   /* parameter follows */
X	    /* allocate sufficient memory on first request */
X	    register int l;
X	    if (pj->user_params == NULL)
X	    {
X		up = pj->user_params = (char*) calloc (1, l = strlen (s));
X		assert_showval (pj->user_params, l);
X	    } /*if*/
X	    if (t = sep_search (++s, "+/"))
X	    {
X		ch = *t;		/* save separator */
X		*t = '\0';
X	    } /*if*/
X	    if ((l = strlen (s = strtrm (s))) > 0)
X	    {   /* more to come... */
X		/* shift other params up to make room for user-supplied one */
X		register int i;
X		for (i=7; --i >= n; )
X		    pj->config->param[i+1] = pj->config->param[i];
X		/* let pj->config->param[n] point into pj->user_params */
X		/* same buffer, but separate string! */
X		pj->config->param[n++] = strcpy (up, s);
X		up += l + 1;		/* point past this string */
X	    } /*if*/
X	    if (t)
X		*t = ch;		/* restore separator */
X	    s = t;
X	} /*if*/
X	else
X	    break;
X    } /*for*/
X} /* save_user_params_and_setup() */
X
Xstatic char *
Xfn_convert (char * fn)		/* convert UNIX-filespec to valid VMS name */
X{
X    char buf[40];		/* short enough to avoid problems */
X    register char *p, *q, *last_dot;
X
X    if ((p = strrchr (fn, '/')) != NULL)
X	p++;			/* skip leading path */
X    else
X	p = fn;			/* name only */
X
X    q = buf;
X    last_dot = NULL;
X    while (*p && ! isalnum (*p))
X	p++;			/* first char must be alphanumeric */
X    while ((*p) && (q-buf < sizeof(buf)-1))
X    {
X	if (isalnum (*p) || (*p == '_') || (*p == '-') || (*p == '$'))
X	    *q++ = *p++;
X	else
X	    switch (*p++)
X	    {
X	        case ' ' :                *q++ = '_';  break;
X	        case ':' :                *q++ = '-';  break;
X	        case '/' :                *q++ = '-';  break;
X	        case '.' : last_dot = q;  *q++ = '$';  break;
X	        default  : /* ignore char */  	       break;
X	    } /*switch*/
X    } /*while*/
X
X    if (last_dot)
X	*last_dot = '.';	/* restore last dot */
X    *q = '\0';
X
X    return (buf);
X} /* fn_convert */
X
Xstatic char *
Xnet_gets (char * buf, int maxlen, Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * pj = q->job;
X    register char * p;
X    register int i;
X
X    if (q->link_state != CONNECTED)
X	return (NULL);
X
X    if (pj->len < 0)			/* first call, fill buffer */
X    {
X	if ((pj->len = net_read (q->channel, pj->netbuf, sizeof(pj->netbuf))) <= 0)
X	    return (NULL);		/* EOF or error */
X	pj->idx = 0;
X    } /*if*/
X
X    for (i=0, p=buf; i<maxlen; i++)
X    {
X	*p++ = pj->netbuf[pj->idx];
X	if (pj->netbuf[pj->idx++] == '\n')
X	    break;			/* stop when newline found */
X        if (pj->idx >= pj->len)
X	{	/* refill the buffer */
X	    pj->idx = 0;
X	    if ((pj->len = net_read (q->channel, pj->netbuf, sizeof(pj->netbuf))) <= 0)
X		break;			/* EOF or error */
X	} /*if*/
X    } /*for*/
X
X    *p = '\0';				/* terminate string */
X    return ((i==0) ? NULL : buf);
X} /* net_gets() */
END_OF_FILE
if test 26535 -ne `wc -c <'rcvjob.c'`; then
    echo shar: \"'rcvjob.c'\" unpacked with wrong size!
fi
# end of 'rcvjob.c'
fi
if test -f 'inet.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'inet.c'\"
else
echo shar: Extracting \"'inet.c'\" \(16734 characters\)
sed "s/^X//" >'inet.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)inet.c 1.9 1/30/92 15:34:44";
X/*
X    Internet/UCX interface to 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 <descrip.h>
X#include <msgdef.h>
X#include <iodef.h>
X#include <lnmdef.h>
X#include <ssdef.h>
X#include <prvdef.h>
X
X#include "lpd.h"
X
X#define __SOCKET_TYPEDEFS	/* <in.h> redefines u_short, u_long */
X#include <in.h>
X
X#include <inet.h>
X#include <netdb.h>
X#include <ucx$inetdef.h>
X/* #include <socket.h> */
X
X
X/* These variables supply global names */
Xchar * prefix = "ILPD";			/* net-specific prefix (DLPD/ILPD) */
Xchar * netdev = "_BG";			/* device name for network */
X
X
Xextern Q_ENTRY * q_hdr[2];		/* conn-q header */
Xextern int maxconns;			/* max # of active conns */
Xextern int min_ast_remain;		/* min # of free ASTs */
X
Xextern u_long dispatch_ef;		/* communication with main() */
X
Xextern int debug;			/* debug mask */
X
Xextern STATISTIC_STRUCT statistics;	/* since last displayed */
X
X
X/* Macros */
X#ifdef DEBUG
X#define dbg_msg(f,q,stat) {if (debug & DBG_NET)				\
X				fprintf(stderr, "[%s]  %s: %s%u: %s\n",	\
X				get_timestamp(),f,netdev,q->unit,getmsgid(stat));}
X#else
X#define dbg_msg(fun,q,stat) 	/* no-op */
X#endif DEBUG
X
X/* Local data */
X
X
X#define BACKLOG			5		/* # of pending conns */
X
X#define MAX_ACCEPT_RETRIES 	20		/* # of retries for accept() */
Xstatic short accept_retry_ctr = 0;
X
X
Xtypedef struct
X    {
X	u_long buflen;
X	struct sockaddr_in * bufadr;
X	u_long * retlen_addr;
X    } SOCK_DESCR;
X
X
Xtypedef struct
X    {
X	struct sockaddr_in sa; 		/* internet socket address */
X	long retlen;			/* return length arg for accept() */
X	SOCK_DESCR rhst_addr;		/* descriptor */
X	u_short channel;		/* VMS channel to dev */
X	u_short unit;			/* pseudo-dev unit # */
X	short iosb[4];			/* IOSB for accept() */
X    } INET_ENTRY;
X
X
Xtypedef struct supv_entry
X    {
X	Q_ENTRY * q_entry;		/* ptr to assoc Q_ENTRY */
X	u_short unit;			/* for consistency check */
X	u_short iosb[4];		/* local iosb */
X    } SUPV_ENTRY;
X
X
Xtypedef struct host_entry
X    {
X	/* the fwd/backward link *MUST* be the first entries */
X	struct host_entry *next;		/* forward link  */
X	struct host_entry *last;		/* backward link */
X	struct in_addr sin_addr;		/* address of host */
X	struct hostent he;			/* from network database */
X    } HOST_ENTRY;
X
Xstatic HOST_ENTRY * he_hdr[2];		/* header for linked list */
X
X
X
Xstatic $DESCRIPTOR (netdev_descr, "UCX$DEVICE");/* string descr for network */
Xstatic u_short netdev_chan;			/* channel number returned */
Xstatic u_char shutdown = FALSE;
X
X/* Prototypes for module-local functions */
Xextern long tcp_accept (void);
Xextern void netmbx_read (void);
Xextern void netmbx_ast (char * buf);
Xextern void accept_conn (INET_ENTRY * ie);
Xextern void accept_retry (long status);
Xextern void accept_retry_ast (void);
Xextern void req_read_ast (Q_ENTRY * q);
Xextern long supv_start   (Q_ENTRY * q);
Xextern void supv_ast     (SUPV_ENTRY * sv);
Xextern void send_eof_ast (Q_ENTRY * q);
Xextern struct hostent * cached_gethostbyaddr (struct in_addr * sin_addr);
X
Xlong
Xnetwork_init ()
X{
X    register long status;		/* VMS condition code */
X    register char * s;			/* string pointer */
X    short iosb[4];			/* IOSB for QIO */
X    long old_privs[2];			/* privilege vectors */
X    static long new_privs[2] = {PRV$M_SYSPRV, 0};
X    u_short port_no;
X
X    static u_short sock_param[2] = { INET$C_TCP, INET_PROTYP$C_STREAM };
X    static struct sockaddr_in local_host;
X    static SOCK_DESCR lhst_addr  = {sizeof(local_host), &local_host, 0};
X
X    if (*(s = translate_lognam (dlpd$inet_port)))
X	port_no = atoi (s);
X    else
X	port_no = INET_PORT_DEF;
X
X    local_host.sin_family      = INET$C_AF_INET;
X    local_host.sin_port        = htons (port_no);
X    local_host.sin_addr.s_addr = INET$C_INADDR_ANY;
X
X    status = SYS$ASSIGN (&netdev_descr, &netdev_chan, NULL, NULL);
X    if (!odd (status))
X	return (status);
X
X    /* create (privileged) server socket, bind it */
X    SYS$SETPRV (1, new_privs, 0, old_privs);
X
X    status = SYS$QIOW (0, netdev_chan, IO$_SETMODE, iosb,
X		NULL, NULL, &sock_param, 0, &lhst_addr, BACKLOG, 0, 0);
X    old_privs[0] = new_privs[0] & ~old_privs[0];	/* restore privs */
X    SYS$SETPRV (0, old_privs, 0, NULL);
X
X    status = (odd(status)) ? iosb[0] : status;
X
X    fprintf (stderr, "network_init: %s%d: (chan=%d) %s\n",
X	  netdev, get_unit_number(netdev_chan), netdev_chan, getmsgid (status));
X
X    if (!odd (status))
X	return (status);
X
X    status = tcp_accept ();		/* queue AST */
X
X    return (status);
X} /* network_init() */
X
X
X
X
Xvoid
Xnetwork_shutdown (void)
X{
X    shutdown = TRUE;
X    /* free the socket */
X    SYS$CANCEL (netdev_chan);
X    SYS$QIOW (0, netdev_chan, IO$_DEACCESS|IO$M_SHUTDOWN, 0,0,0,
X					0,0,0, UCX$C_DSC_ALL, 0, 0);
X    SYS$QIOW (0, netdev_chan, IO$_DEACCESS, 0, 0, 0, 0, 0, 0, 0, 0, 0);
X    SYS$DASSGN (netdev_chan);
X} /* network_shutdown() */
X
X/*
X * Initiate accepting connections.
X */
X
X
Xstatic long
Xtcp_accept (void)
X{
X    register long status;
X    register INET_ENTRY * ie = (INET_ENTRY*) calloc (1, sizeof(INET_ENTRY));
X    ie->rhst_addr.buflen = sizeof (ie->sa);	/* build descriptor */
X    ie->rhst_addr.bufadr = &ie->sa;
X    ie->rhst_addr.retlen_addr = &ie->retlen;
X
X    status = SYS$ASSIGN (&netdev_descr, &ie->channel, NULL, NULL);
X    if (!odd (status))
X    {
X	fprintf (stderr, "[%s]  tcp_accept/ASSIGN: %s\n",
X				get_timestamp(), getmsgid (status));
X	free (ie);
X	return (status);
X    } /*if*/
X
X    status = SYS$QIO (0, netdev_chan, IO$_ACCESS|IO$M_ACCEPT, &ie->iosb,
X		accept_conn, ie, 0, 0, &ie->rhst_addr, &ie->channel, 0, 0);
X    if (!odd (status))
X    {
X	fprintf (stderr, "[%s]  tcp_accept/QIO: %s\n",
X				get_timestamp(), getmsgid (status));
X	SYS$DASSGN (ie->channel);
X	free (ie);
X    } /*if*/
X    return (status);
X} /* tcp_accept() */
X
X/*
X * Once tcp_accept() has failed, there cannot be a network connection any
X * more, so have a few retries before giving up...
X */
Xstatic void
Xaccept_retry (long status)
X{
X    static long bintim[2] = {-600000000, -1};		/* 1 minute */
X    char msg[VMS_STR];
X
X    if (odd(status))
X	return;				/* ???! */
X
X    sprintf (msg, "[%s]  TCP Accept failed, retrying: %s\n",
X				get_timestamp(), getmsgid (status));
X    op_msg (msg);
X
X    if (accept_retry_ctr++ > MAX_ACCEPT_RETRIES)
X	SYS$EXIT ($I(SS$_NODEVAVL));		/* give up */
X
X    SYS$SETIMR (0, bintim, accept_retry_ast, NULL);
X} /* accept_retry() */
X
X
X
Xstatic void
Xaccept_retry_ast (void)
X{
X    long status;
X    status = tcp_accept ();		/* try once more */
X    if (!odd (status))
X	accept_retry (status);		/* schedule another retry */
X} /* accept_retry_ast() */
X
X/*
X * This function is called from an AST when a connection request has been
X * received. If not too many conns are already open, the conn is confirmed.
X * Data structure for the connection is allocated and inserted at the tail
X * of the list.
X * A read-operation on the link is initiated to obtain the first msg, which
X * will be the request code of the lpd-protocol.
X */
X
Xstatic void
Xaccept_conn (INET_ENTRY * ie)
X{
X    register Q_ENTRY *q, *r;		/* conn queue entry */
X    register struct hostent * he;
X    register char * s;
X    register long status;		/* system service status */
X    short n_q_entries;			/* # of entries in conn queue */
X
X    if (! shutdown)			/* terminating? */
X    {
X	status = tcp_accept ();		/* restart accept function */
X	if (!odd (status))
X	    accept_retry (status);	/* retry on failure */
X	else
X	    accept_retry_ctr = 0;	/* reset retry counter */
X    } /*if*/
X
X    status = ie->iosb[0];
X    if (!odd (status))
X    {
X	fprintf (stderr, "[%s]  accept_conn: %s\n",
X				get_timestamp(), getmsgid (status));
X	free (ie);
X	return;
X    } /*if*/
X
X    ie->unit = get_unit_number (ie->channel);
X    dbg_msg ("accept_conn", ie, status);
X
X    q = (Q_ENTRY*) calloc (1, sizeof (Q_ENTRY));
X    assert_showval (q != NULL, sizeof (Q_ENTRY));
X
X    q->channel = ie->channel;
X    q->unit = ie->unit;
X    q->link_state = IDLE;
X    q->proc_state = DONE;
X    SYS$GETTIM (&q->timestamp);
X
X    /* check # of active requests */
X    if (*(s = translate_lognam (dlpd$maxconns)))
X    {
X	maxconns = atoi (s);		/* get value from outside */
X        if (maxconns > MAXCONNS_MAX)
X	    maxconns = MAXCONNS_MAX;	/* limit to maximum */
X        if (maxconns < MAXCONNS_MIN)
X	    maxconns = MAXCONNS_MIN;	/* limit to minimum */
X    } /*if*/
X    else
X	maxconns = MAXCONNS_DEF;	/* set to default */
X    min_ast_remain = maxconns + 4;
X    for (r = q_hdr[0], n_q_entries = 0; r != NULL; r = r->next)
X	if (r->link_state == CONNECTED)
X	    n_q_entries++;
X    if (n_q_entries > maxconns)
X    {
X	fprintf (stderr, "[%s]  accept_conn: more than %d, closing %s%u:\n",
X				    get_timestamp(), maxconns, netdev, q->unit);
X	show_q ();
X
X	close_conn (q);
X    	statistics.req_rej_overload++;		/* count failures */
X        free (ie);
X	free (q);
X	return;
X    } /*if*/
X
X    /* get remote hostname for a first access check */
X    if (he = cached_gethostbyaddr (&ie->sa.sin_addr))
X	strcpy (q->host, he->h_name);
X    else
X    {
X	char msg[VMS_STR];
X        sprintf (msg, "Request from unknown host %s\n",
X					inet_ntoa (ie->sa.sin_addr.s_addr));
X	op_msg (msg);
X 	close_conn (q);
X	free (ie);
X	free (q);
X	return;
X    } /*else*/
X
X#ifdef DEBUG
X    if (debug & DBG_NET)
X        fprintf (stderr, "[%s]  accept_conn: %s%d: (chan=%d), host %s\n",
X    			get_timestamp(), netdev, q->unit, q->channel, q->host);
X#endif DEBUG
X
X    /* insert at the tail of the queue */
X    ins_2ll_t (&q_hdr, q);
X
X    if (n_q_entries > statistics.peak_conns)
X        statistics.peak_conns = n_q_entries;
X
X    /* initiate reading the first message (request code) from link */
X    status = SYS$QIO (0, q->channel, IO$_READVBLK, q->iosb,
X		  req_read_ast, q, q->request, sizeof(q->request), 0, 0, 0, 0);
X    if (odd (status))
X    {
X        q->link_state = CONNECTED;
X        q->proc_state = STARTED;
X    } /*if*/
X    else
X    {
X	fprintf (stderr, "[%s]  accept_conn: %s %s\n",
X		get_timestamp(), q->host, getmsgid (status));
X	close_conn (q);
X    } /*else*/
X    free (ie);
X} /* accept_conn() */
X
X/*
X * Send EOF to the network.
X * This is specific to the underlying network protocol.
X */
X
Xlong
Xsend_eof (Q_ENTRY * q)
X{
X    long status;
X    q->proc_state = SEND_ACT;		/* just to make sure */
X    status = SYS$QIO (0, q->channel, IO$_DEACCESS, q->iosb,
X				send_eof_ast, q, 0,0,0,0,0,0);
X#ifdef DEBUG
X    if (debug & DBG_NET)
X	fprintf (stderr, "[%s]  send_eof: %s%d: %s\n",
X			get_timestamp(), netdev, q->unit, getmsgid (status));
X#endif DEBUG
X    if (! odd(status))
X        send_eof_ast (q);		/* AST would not be called */
X    return (status);
X} /* send_eof() */
X
X
X
Xstatic void
Xsend_eof_ast (Q_ENTRY * q)
X{
X#ifdef DEBUG
X    if (debug & DBG_NET)
X	fprintf (stderr, "[%s]  send_eof_ast: %s%d: %s\n",
X		get_timestamp(), netdev, q->unit, getmsgid (q->iosb[0]));
X#endif DEBUG
X    q->proc_state = DONE;
X    q->link_state = DISC_REQ;		/* fake disconn from other side */
X    SYS$SETEF (dispatch_ef);		/* wake scheduler */
X} /* send_eof_ast() */
X
X/*
X * This function is called to close the network connection for a given unit-#.
X * When called from supv_ast(), this indicates a disconnect request from
X * the remote side. When the unit is still active, the link_state is
X * just set to DISC_REQ, actual closing is delayed until processing
X * has been done.
X * After closing the link, link_state is set to CLOSED. Data structures
X * are *not* deallocated, since they might still be in use for processing
X * a request, but locks are released. Deallocation is done by main().
X */
X
Xvoid
Xclose_conn (Q_ENTRY * q)
X{
X    switch (q->proc_state)
X    {
X	case STARTED   :
X	case DONE      :
X			/* close immediately */
X#ifdef DEBUG
X			if (debug & DBG_NET)
X			   fprintf (stderr, "[%s]  close_conn: %s%d: CLOSED\n",
X					      get_timestamp(), netdev, q->unit);
X#endif DEBUG
X			SYS$CANCEL (q->channel);
X			SYS$QIOW (0, q->channel, IO$_DEACCESS|IO$M_SHUTDOWN,
X					  0,0,0, 0,0,0, UCX$C_DSC_ALL, 0, 0);
X			SYS$DASSGN (q->channel);
X			q->channel = q->unit = 0;
X			q->link_state = CLOSED;
X			unlock (&q->proc_lksb);		/* dequeue all locks */
X			unlock (&q->config_lksb);
X			SYS$SETEF (dispatch_ef);	/* wake main() */
X			break;
X
X	default       : /* do not break the link yet */
X			if (q->link_state != DISC_REQ)
X			{   /* avoid loop */
X			    q->link_state = DISC_REQ;
X			    SYS$SETEF (dispatch_ef);	/* wake main() */
X			} /*if*/
X			else
X			    /* second attempt, close now to fire pending ASTs */
X			    SYS$QIOW (0, q->channel, IO$_DEACCESS|IO$M_SHUTDOWN,
X					     		 0,0,0, 0,0,0, 0, 0, 0);
X#ifdef DEBUG
X		    	if (debug & DBG_NET)
X			    fprintf (stderr, "[%s]  close_conn: %s%d: DISC_REQ\n",
X					     get_timestamp(), netdev, q->unit);
X#endif DEBUG
X			SYS$CANCEL (q->channel);
X			break;
X    } /*switch*/
X} /* close_conn() */
X
X/*
X * This AST is queued when the first message has been read from the
X * link, containing the function code and the arguments according to
X * the lpd-protocol.
X * Upon receiving the initial message, it queues for shared lock
X * on the configuration
X */
X
Xstatic void
Xreq_read_ast (Q_ENTRY * q)
X{
X    if (! odd (q->iosb[0]))
X    {
X        if (!odd (q->iosb[0]))
X	    fprintf (stderr, "[%s]  req_read_ast: %s%d: (%s) %s\n",
X		get_timestamp(), netdev, q->unit, q->host, getmsgid (q->iosb[0]));
X	close_conn (q);			/* error reading from link, disconn */
X	return;
X    } /*if*/
X
X    q->request[q->iosb[1]] = '\0';		/* terminate string */
X    if (q->request[q->iosb[1]-1] == '\n')
X	q->request[q->iosb[1]-1] = '\0';	/* strip trailing '\n' */
X    else
X	fprintf (stderr, "Request too long:\r\n%s", q->request);
X
X#ifdef DEBUG
X    if (debug & DBG_REQUEST)
X	fprintf (stderr, "[%s]  %s%u: (%s) ^%c %s\n",
X	  get_timestamp(), netdev, q->unit,
X	  q->host, q->request[0]+'@', &q->request[1]);
X#endif DEBUG
X
X    if (*q->request != REQ_LPR)
X        supv_start (q);		/* start dummy READ on channel */
X
X    lock_config_shr (q);	/* queue until configuration DB is avail */
X} /* req_read_ast() */
X
X/*
X * Since there is no function to immediately recognize the disapperance
X * of a link, a dummy READ-op is set up. When the associated AST is
X $ delivered with a status of SS$_LINKDISCON, link_state is set
X * to DISC_REQ, which allows processing functions to return earlier.
X */
X
Xstatic long
Xsupv_start (Q_ENTRY * q)
X{
X    static char dummy_buf[4];
X    register long status;
X    register SUPV_ENTRY * sv = (SUPV_ENTRY*) malloc (sizeof (SUPV_ENTRY));
X
X    assert_showval (sv != NULL, sizeof(SUPV_ENTRY));
X
X    sv->q_entry = q;
X    sv->unit = q->unit;		/* for consistency check */
X    status = SYS$QIO (0, q->channel, IO$_READVBLK, sv->iosb,
X        supv_ast, sv, &dummy_buf, sizeof(dummy_buf), 0, UCX$C_MSG_PEEK, 0, 0);
X
X    return status;
X} /* supv_start() */
X
X
X
Xstatic void
Xsupv_ast (SUPV_ENTRY * sv)
X{
X#ifdef DEBUG
X    if (   (debug & DBG_NET)
X	|| (debug && ! odd (sv->iosb[0]))
X	   && (sv->iosb[0] != SS$_CANCEL)
X	   && (sv->iosb[0] != SS$_ABORT)
X	   && (sv->iosb[0] != SS$_LINKDISCON))
X	fprintf (stderr, "[%s]  supv_ast: %s%d: %s\n",
X		get_timestamp(), netdev, get_unit_number(sv->q_entry->channel),
X						  getmsgid((long)sv->iosb[0]));
X#endif DEBUG
X
X    if (   ! odd (sv->iosb[0])
X	&& (sv->iosb[0] != SS$_CANCEL)
X	&& (sv->iosb[0] != SS$_ABORT))
X        if (sv->q_entry->unit == sv->unit) 	/* consistency check */
X	    close_conn (sv->q_entry);		/* mark DISC_REQ */
X
X    free (sv);
X}  /* supv_ast() */
X
X/*
X * This function checks if a host-addr is found in the local database,
X * otherwise it tries to obtain it from the net-database, saving it for
X * later reuse.
X */
X
Xstatic struct hostent *
Xcached_gethostbyaddr (struct in_addr * sin_addr)
X{
X    register HOST_ENTRY * h;
X    register struct hostent * he;
X    for (h = he_hdr[0]; h; h = h->next)
X	if (h->sin_addr.s_addr == sin_addr->s_addr)
X	    return (&h->he);
X
X    if ((he = gethostbyaddr (sin_addr, sizeof(*sin_addr), AF_INET)) != NULL)
X    {
X	h = (HOST_ENTRY *) calloc (1, sizeof (HOST_ENTRY));
X	assert_showval (h != NULL, sizeof (HOST_ENTRY));
X	h->he = *he;			/* copy info */
X	h->sin_addr = *sin_addr;	/* copy addr (the one actually used) */
X 	ins_2ll_t (he_hdr, h);		/* cache it for next time */
X#ifdef DEBUG
X 	if (debug & DBG_NET)
X	    fprintf (stderr, "Caching new host-entry: %s\n", he->h_name);
X#endif
X	return (he);
X    } /*if*/
X    return (NULL);
X} /* cached_gethostbyaddr() */
END_OF_FILE
if test 16734 -ne `wc -c <'inet.c'`; then
    echo shar: \"'inet.c'\" unpacked with wrong size!
fi
# end of 'inet.c'
fi
if test -f 'dnet.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dnet.c'\"
else
echo shar: Extracting \"'dnet.c'\" \(14005 characters\)
sed "s/^X//" >'dnet.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)dnet.c 1.10 1/30/92 15:34:43";
X/*
X    DECnet interface to 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 <descrip.h>
X#include <msgdef.h>
X#include <iodef.h>
X#include <nfbdef.h>
X#include <lnmdef.h>
X#include <ssdef.h>
X#include <prvdef.h>
X
X#include "lpd.h"
X
X
X/* These variables supply global names */
Xchar * prefix = "DLPD";			/* net-specific prefix (DLPD/ILPD) */
Xchar * netdev = "_NET";			/* device name for network */
X
X
X#define NETMBX_BUF_LEN	128
X
X
Xextern Q_ENTRY * q_hdr[2];		/* conn-q header */
Xextern int maxconns;			/* max # of active conns */
Xextern int min_ast_remain;		/* min # of remaining ASTs */
X
Xextern u_long dispatch_ef;		/* communication with main() */
X
Xextern int debug;			/* debug mask */
X
Xextern STATISTIC_STRUCT statistics;	/* since last displayed */
X
X
X#ifdef DEBUG
X#define dbg_msg(s)	{if (debug & DBG_NET) fprintf (stderr, s);}
X#else
X#define dbg_msg(s)	/* no action */
X#endif
X
X/* Local data */
X
X/* network control block */
Xtypedef struct ncb_descr
X	{
X	    long len;
X	    u_char * ncb;
X	} NCB_DESCR;
X
X
X/* network function block (to declare object name) */
Xtypedef struct nfb
X	{
X	    u_char function_code;
X	    long object_number;
X	} NFB;
X
X
Xtypedef struct nfb_descr
X	{
X	    long size;		/* sizeof(NFB) */
X	    NFB * nfb_ptr;	/* &NFB        */
X	} NFB_DESCR;
X
X
X
Xstatic $DESCRIPTOR (netobj_descr, NETOBJ_NAM);	/* string descr for net obj name*/
Xstatic $DESCRIPTOR (netmbx_descr, "");		/* string descr for netmbx */
Xstatic $DESCRIPTOR (netdev_descr, "_NET:");	/* string descr for network */
X
X
Xstatic NFB nfb = {NFB$C_DECLNAME, 0};  		/* declare netobj by name */
Xstatic NFB_DESCR nfb_descr = {sizeof(nfb), &nfb};
X
X
Xstatic u_char ncb[NETMBX_BUF_LEN];		/* NCB */
Xstatic NCB_DESCR ncb_descr = {sizeof(ncb), &ncb[0]};
X
X
Xstatic u_short netdev_chan, netmbx_chan;  /* channel numbers returned */
Xstatic short netmbx_iosb[4];		  /* IOSB for netmbx-I/O */
Xstatic netmbx_buf_len = NETMBX_BUF_LEN;	  /* message length for netmbx-I/O */
Xstatic netmbx_buf_quo = 4*NETMBX_BUF_LEN; /* msg buffer quota for netmbx-I/O */
Xstatic u_char netmbx_buf[NETMBX_BUF_LEN];
X
X
Xstatic int shutdown = 0;		/* already shutting down */
X
X
X/* Prototypes for module-local functions */
Xextern void netmbx_read (void);
Xextern void netmbx_ast (char * buf);
Xextern void accept_conn (void);
Xextern void req_read_ast (Q_ENTRY * q);
Xextern void send_eof_ast (Q_ENTRY * q);
X
Xlong
Xnetwork_init ()
X{
X    register long status;		/* VMS condition code */
X    register char * s;			/* string pointer */
X    short iosb[4];			/* IOSB for QIO */
X    long old_privs[2];			/* privilege vectors */
X    static long new_privs[2] = {PRV$M_SYSNAM, 0};
X
X    status = LIB$ASN_WTH_MBX (&netdev_descr, 		/*  get channel  */
X		&netmbx_buf_len, &netmbx_buf_quo, 	/*    to DECnet  */
X		&netdev_chan, &netmbx_chan);
X    if (!odd(status))
X        return (status);
X
X    /* declare network task name (SYSNAM-priv required) */
X    SYS$SETPRV (1, new_privs, 0, old_privs);
X    status = SYS$QIOW (0, netdev_chan, IO$_ACPCONTROL, iosb,
X			NULL, NULL, &nfb_descr, &netobj_descr, 0, 0, 0, 0, 0);
X    old_privs[0] = new_privs[0] & ~old_privs[0];	/* restore privs */
X    SYS$SETPRV (0, old_privs, 0, NULL);
X
X    if (!odd(status))
X	return (status);
X    if (iosb[0] == SS$_BADPARAM)
X	op_msg ("Cannot establish network task name, probably running already");
X    if (!odd(iosb[0]))
X	return (iosb[0]);
X
X    if ((s = get_devnam (netmbx_chan)) != NULL)		/* get mailbox name */
X    {
X        netmbx_descr.dsc$a_pointer = strdup (s);
X        netmbx_descr.dsc$w_length  = strlen (s);
X    } /*if*/
X
X    fprintf (stderr, "network_init: _MBA%d: (chan=%d), %s%d: (chan=%d)\n",
X		           get_unit_number(netmbx_chan), netmbx_chan,
X	      		   netdev, get_unit_number(netdev_chan), netdev_chan);
X
X    netmbx_read ();		/* queue AST */
X    return (status);
X} /* network_init() */
X
Xvoid
Xnetwork_shutdown (void)
X{
X    shutdown = 1;			/* set internal flag */
X    SYS$CANCEL (netdev_chan);
X    SYS$CANCEL (netmbx_chan);
X    SYS$QIOW   (0, netdev_chan, IO$_DEACCESS|IO$M_ABORT, 0,0,0,0,0,0,0,0,0);
X    SYS$DASSGN (netdev_chan);
X    SYS$DASSGN (netmbx_chan);
X} /* network_shutdown() */
X
X/* initiate reading from net-mbx, netmbx_ast() does the rest */
Xstatic void
Xnetmbx_read (void)
X{
X    long status;
X    status = SYS$QIO (0, netmbx_chan, IO$_READVBLK, netmbx_iosb,
X       	   netmbx_ast, netmbx_buf, netmbx_buf, sizeof(netmbx_buf), 0, 0, 0, 0);
X    assert_oddstat (status);
X} /* netmbx_read() */
X
X
X/*
X * This AST is queued whenever there is a message in the mbx
X * associated with the generic NET-device. It parses the message
X * and calls appropriate functions to accept the connection, etc.
X */
X
Xstatic void
Xnetmbx_ast (char * netmbx_buf)
X{
X    register Q_ENTRY * q;			/* connection queue entry */
X    u_short msg_code = *(u_short*)netmbx_buf;
X    u_short unit = *((u_short*)(&netmbx_buf[2]));
X#ifdef DEBUG
X    if (debug & DBG_NET)
X        fprintf(stderr, "[%s]  netmbx_ast:  %s%d: msg_code=0x%02X\n",
X				   get_timestamp(), netdev, unit, msg_code);
X#endif
X    switch (msg_code)
X    {
X	case MSG$_CONNECT     :
X				accept_conn ();	/* accept connection */
X				break;
X
X	case MSG$_CONFIRM     :
X	case MSG$_REJECT      :
X				dbg_msg ("netmbx_ast: REJECT/CONFIRM\n");
X				break;
X
X	case MSG$_INTMSG      :
X				dbg_msg ("netmbx_ast: INTMSG\n");
X				break;
X
X	case MSG$_ABORT	      :
X	case MSG$_DISCON      :
X	case MSG$_EXIT	      :
X	case MSG$_PATHLOST    :
X	case MSG$_PROTOCOL    :
X	case MSG$_THIRDPARTY  :
X	case MSG$_TIMEOUT     : /* close connection */
X				/* search for this unit# */
X			    	for (q = q_hdr[0]; q != NULL; q = q->next)
X				    if (q->unit == unit)
X				    {
X				 	close_conn (q);
X				        break;
X				    }
X				break;
X
X	case MSG$_NETSHUT     :
X				dbg_msg ("netmbx_ast: net-shutdown\n");
X				SYS$EXIT ($I(SS$_SHUT));
X
X	default		      : /* unrecognized (non-network) message */
X				dbg_msg ("netmbx_ast: unknown msg\n");
X				break;
X    } /*switch*/
X    if (! shutdown)
X	netmbx_read ();		/* requeue the AST */
X} /* netmbx_ast() */
X
X/*
X * This function is called from an AST when a connection request has been
X * received. If not too many conns are already open, the conn is confirmed.
X * Data structure for the connection is allocated and inserted at the tail
X * of the list.
X * A read-operation on the link is initiated to obtain the first msg, which
X * will be the request code of the lpd-protocol.
X */
X
Xstatic void
Xaccept_conn (void)
X{
X    register Q_ENTRY *q, *r;		/* conn queue entry */
X    register u_char * s;
X    register long status;		/* system services status */
X    short iosb[4];			/* I/O status block */
X    short n_q_entries;			/* # of entries in conn queue */
X
X    s = netmbx_buf + netmbx_buf[4] + 5;	/* save NCB */
X    memcpy (ncb, s+1, (int)*s);
X    ncb_descr.len = *s;
X
X    q = (Q_ENTRY*) calloc (1, sizeof(Q_ENTRY));
X    assert_showval (q != NULL, sizeof(Q_ENTRY));
X
X    status = SYS$ASSIGN (&netdev_descr, &(q->channel), NULL, &netmbx_descr);
X    if (!odd (status))
X    {
X	static char msg[VMS_STR];
X	sprintf (msg, "ACCEPT/SYS$ASSIGN failed: %s", getmsgid (status));
X      	op_msg (msg);
X    	statistics.req_rej_overload++;		/* count as failure */
X	free (q);
X    	return;		/* leave a dangling DECnet connection...? */
X    } /*if*/
X
X    q->unit = get_unit_number (q->channel);
X    q->link_state = IDLE;
X    q->proc_state = DONE;
X    SYS$GETTIM (&q->timestamp);
X
X    /* check # of active requests */
X    if (*(s = translate_lognam (dlpd$maxconns)) != '\0')
X    {
X	maxconns = atoi (s);		/* get value from outside */
X        if (maxconns > MAXCONNS_MAX)
X	    maxconns = MAXCONNS_MAX;	/* limit to maximum */
X        if (maxconns < MAXCONNS_MIN)
X	    maxconns = MAXCONNS_MIN;	/* limit to minimum */
X    } /*if*/
X    else
X	maxconns = MAXCONNS_DEF;	/* set to default */
X
X    for (r = q_hdr[0], n_q_entries = 0; r != NULL; r = r->next)
X	if (r->link_state == CONNECTED)
X	    n_q_entries++;
X    if ((n_q_entries > maxconns) || shutdown)
X    {
X	fprintf (stderr, "[%s]  accept_conn: more than %d, closing %s%u:\n",
X				    get_timestamp(), maxconns, netdev, q->unit);
X	show_q ();
X
X        SYS$QIOW (0, q->channel, IO$_ACCESS | IO$M_ABORT, NULL,
X			  NULL, NULL, 0, &ncb_descr, 0, 0, 0, 0);
X    	statistics.req_rej_overload++;		/* count failures */
X	close_conn (q);
X	free (q);
X	return;
X    } /*if*/
X
X    min_ast_remain = n_q_entries + 4;
X    assert_showval (min_ast_remain > 0, min_ast_remain);
X
X    if (n_q_entries > statistics.peak_conns)
X        statistics.peak_conns = n_q_entries;
X
X    /* confirm connection */
X    status = SYS$QIOW (0, q->channel, IO$_ACCESS, q->iosb,
X			  NULL, NULL, 0, &ncb_descr, 0, 0, 0, 0);
X
X    if (odd(status) && odd(q->iosb[0]))
X    {	/* connection confirmed */
X	register char * t;
X
X	q->link_state = CONNECTED;
X
X	/*
X    	  Build mail_addr() format string for mail to the user.
X	  In the format string, "%u" will be replaced by the username,
X	  "%h " will be replaced by the (UNIX-)hostname.
X	*/
X    	for (s=ncb, t=q->mail_fmt; *s != ':'; )  /* extract name of gateway */
X	    *t++ = *s++;
X	strcpy (t, "::\"\"\"%u@%h\"\"\"");
X
X	for ( ; *s != '/'; s++)			/* search for OPT_DATA */
X	    ;
X	strncpy (q->host, s+4, *(s+3));		/* extract host-name */
X	q->host[*(s+3)] = '\0';			/* terminate string */
X
X	/* insert at the tail of the queue */
X	ins_2ll_t (&q_hdr, q);
X
X#ifdef DEBUG
X        if (debug & DBG_NET)
X            fprintf (stderr, "[%s]  accept_conn: %s%d: (chan=%d), host %s\n",
X    			get_timestamp(), netdev, q->unit, q->channel, q->host);
X#endif DEBUG
X
X 	/* initiate reading the first message (request code) from link */
X        status = SYS$QIO (0, q->channel, IO$_READVBLK, q->iosb,
X		  req_read_ast, q, q->request, sizeof(q->request), 0, 0, 0, 0);
X	if (!odd (status))
X	{
X	    fprintf (stderr, "[%s]  accept_conn/read_request: %s\n",
X					get_timestamp(), getmsgid (status));
X 	    close_conn (q);
X	} /*if*/
X	q->proc_state = STARTED;
X        return;
X    } /*if*/
X    else
X    {   /* could not confirm the connection */
X	fprintf (stderr, "[%s]  accept_conn: %s\n", get_timestamp(),
X				getmsgid (!odd(status)?status:q->iosb[0]));
X 	close_conn (q);
X	free (q);
X    } /*else*/
X} /* accept_conn() */
X
X/*
X * Send EOF to the network.
X * This is specific to the underlying network protocol
X * and the gateway-software.
X */
X
Xlong
Xsend_eof (Q_ENTRY * q)
X{
X    long status;
X    static char msg = EOF_CHAR;
X    q->proc_state = SEND_ACT;		/* just to make sure */
X    status = SYS$QIO (0, q->channel, IO$_WRITEVBLK, q->iosb,
X		          send_eof_ast,  q,
X		          &msg, 1, 0, 0, 0, 0);
X#ifdef DEBUG
X    if (debug & DBG_NET)
X        fprintf (stderr, "[%s]  send_eof: %s%u: %s\n",
X			get_timestamp(), netdev, q->unit, getmsgid(status));
X#endif DEBUG
X    if (! odd(status))
X	send_eof_ast (q);	/* AST would not be called */
X    return (status);
X} /* send_eof() */
X
X
X
Xstatic void
Xsend_eof_ast (Q_ENTRY * q)
X{
X    q->proc_state = DONE;		/* advance state */
X    SYS$SETEF (dispatch_ef);		/* wake scheduler */
X} /* send_eof_ast() */
X
X/*
X * This function is called to close the network connection for a given unit-#.
X * When called from net_mbx_ast(), this indicates a disconnect request from
X * the remote side. When the unit is still active, the link_state is
X * just set to DISC_REQ, actual closing is delayed until processing
X * has been done.
X * After closing the link, link_state is set to CLOSED. Data structures
X * are *not* deallocated, since they might still be in use for processing
X * a request. Deallocation is done by main().
X */
X
Xvoid
Xclose_conn (Q_ENTRY * q)
X{
X    switch (q->proc_state)
X    {
X	case STARTED   :
X	case DONE      :
X			/* close immediately */
X#ifdef DEBUG
X			if (debug & DBG_NET)
X			    fprintf (stderr, "[%s]  close_conn:  %s%d: CLOSED\n",
X						get_timestamp(), netdev, q->unit);
X#endif DEBUG
X			SYS$CANCEL (q->channel);
X			SYS$DASSGN (q->channel);
X			q->channel = q->unit = 0;
X			q->link_state = CLOSED;
X			unlock (&q->proc_lksb);		/* dequeue all locks */
X			unlock (&q->config_lksb);
X			SYS$SETEF (dispatch_ef);	/* wake main() */
X			break;
X
X	default       : /* do not break the link yet */
X			if (q->link_state != DISC_REQ)
X			{   /* avoid loop */
X			    q->link_state = DISC_REQ;
X			    SYS$SETEF (dispatch_ef);	/* wake main() */
X			} /*if*/
X#ifdef DEBUG
X			if (debug & DBG_NET)
X			    fprintf (stderr, "[%s]  close_conn:  %s%d: DISC_REQ\n",
X					      get_timestamp(), netdev, q->unit);
X#endif DEBUG
X			SYS$CANCEL (q->channel);
X			break;
X    } /*switch*/
X} /* close_conn() */
X
X/*
X * This AST is queued when the first message has been read from the
X * link, containing the function code and the arguments according to
X * the lpd-protocol.
X * Upon receiving the initial message, it queues for shared lock
X * on the configuration
X */
X
Xstatic void
Xreq_read_ast (Q_ENTRY * q)
X{
X    if (! odd (q->iosb[0]) || shutdown)
X    {
X	if (!odd (q->iosb[0]))
X	    fprintf (stderr, "[%s]  req_read_ast: %s\n",
X			get_timestamp(), getmsgid (q->iosb[0]));
X
X	close_conn (q);		/* error reading from link, disconn */
X	return;
X    } /*if*/
X
X    q->request[q->iosb[1]] = '\0';	/* terminate string */
X    if (q->request[q->iosb[1]-1] == '\n')
X	q->request[q->iosb[1]-1] = '\0';	/* strip trailing '\n' */
X    else
X	fprintf (stderr, "Request too long:\r\n%s", q->request);
X
X#ifdef DEBUG
X    if (debug & DBG_REQUEST)
X	fprintf (stderr, "[%s]  %s%u: (%s) ^%c %s\n",
X	   get_timestamp(), netdev, q->unit,
X	   q->host, q->request[0]+'@', &q->request[1]);
X#endif DEBUG
X
X    lock_config_shr (q);	/* queue until configuration DB is avail */
X} /* req_read_ast() */
END_OF_FILE
if test 14005 -ne `wc -c <'dnet.c'`; then
    echo shar: \"'dnet.c'\" unpacked with wrong size!
fi
# end of 'dnet.c'
fi
if test -f 'sndjbc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sndjbc.c'\"
else
echo shar: Extracting \"'sndjbc.c'\" \(22557 characters\)
sed "s/^X//" >'sndjbc.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)sndjbc.c 1.10 1/30/92 15:34:37";
X/*
X    JBC-interface 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#include <stdio.h>
X#include <stdlib.h>
X#include <ssdef.h>
X#include <descrip.h>
X#include <prvdef.h>
X#include <string.h>
X#include <jbcmsgdef.h>
X#include <sjcdef.h>
X#include <quidef.h>
X
X#include "lpd.h"
X
X
Xextern int debug;				/* debug mask */
Xextern char * netdev;				/* network device name */
Xextern int min_ast_remain;			/* configuration param */
Xextern STATISTIC_STRUCT statistics;		/* job statistics */
X
X
X/* Local data */
Xstatic long cmkrnl[2]  = {PRV$M_CMKRNL, 0};	/* privilege vectors */
Xstatic long sysprv[2]  = {PRV$M_SYSPRV, 0};
Xstatic long cmk_sys[2] = {PRV$M_CMKRNL | PRV$M_SYSPRV, 0};
X
X
X/* Declarations for module-local functions */
Xextern long enter_file (Q_ENTRY * q);
Xextern long create_job (Q_ENTRY * q);
Xextern long add_file   (Q_ENTRY * q);
Xextern long close_job  (Q_ENTRY * q);
Xextern void close_delete_job ();
Xextern void enter_file_ast (Q_ENTRY * q);
Xextern void create_job_ast (Q_ENTRY * q);
Xextern void add_file_ast   (Q_ENTRY * q);
Xextern void close_job_ast  (Q_ENTRY * q);
Xextern void sndjbc_cleanup (Q_ENTRY * q, long status);
Xextern void synch_to_job (Q_ENTRY * q);
Xextern void synch_ast_data_setup (Q_ENTRY * q);
Xextern void synch_ast (SYNCH_AST_DATA * p);
Xextern void create_blm_ast (struct RAB * rab);
Xextern void create_batch_log_mailer_job (Q_ENTRY * q);
Xextern long set_file_name_and_owner (Q_ENTRY * q);
X
X
X/* Macros, Constants */
X
X#define QUI$$BURST  (QUI$M_QUEUE_FILE_BURST|QUI$M_QUEUE_FILE_BURST_ONE|QUI$M_QUEUE_JOB_BURST)
X
X#ifdef DEBUG
X#define dbg_msg(f,m,q,stat) {if (debug & (m))				\
X				fprintf(stderr, "[%s]  %s: %s%u: %s\n",	\
X				get_timestamp(),f,netdev,q->unit,getmsgid(stat));}
X#else
X#define dbg_msg(fun,m,q,stat) 	/* no-op */
X#endif DEBUG
X
X/*
X * This is the main entry point for sending a job to the job-controller.
X *
X * For a job with a single file only, enter_file() is called; in
X * all other cases there is sequence of create_job(), add_file() for
X * each file in the job and finally close_job(). In any case, once
X * started, processing is done asynchronuously by ASTs.
X * If an error occurs, the job is deleted (if it is a multi-file job)
X * and error status sent to the remote side.
X * If the last operation (ENTER or CLOSE) completes successfully,
X * the final completion status is sent to the remote side.
X * In both cases, EOF is written to the link.
X */
X
Xlong
Xsend_job_to_jbc (Q_ENTRY * q)		/* pass job to JBC */
X{
X    long status = SS$_NORMAL;
X
X    assert (q!= NULL);
X    assert (q->job != NULL);
X
X    synch_ast_data_setup (q);		/* prepare for mailing */
X
X    if (q->job->file_list[0] == NULL)
X    {
X	status = $I(JBC$_EMPTYJOB);	/* "success" */
X	sndjbc_cleanup (q, status);
X	return (status);
X    } /*if*/
X
X    if (q->job->file_list[0] == q->job->file_list[1])
X    	/* exactly one file in job */
X	status = enter_file (q);
X    else
X    	/* more than one file in job */
X    	status = create_job (q);	/* add_files() called by ASTs */
X
X    if (!odd(status)) 			/* returned early, with error */
X	sndjbc_cleanup (q, status);
X    return (status);
X} /* send_job_to_jbc() */
X
X/*
X * This function *must* be called at the end of a series of $SNDJBC-calls
X * (be they successful or not) to do final cleanup and release the lock
X * on the sndjbc-service.
X *
X * If DISCONNECT had already been requested, the connection is
X * closed from here, otherwise it will be done when the other
X * side closes upon receiving EOF (by another AST).
X */
X
Xstatic void
Xsndjbc_cleanup (Q_ENTRY * q, long status)
X{
X    if (!odd(status))
X    {
X	fprintf (stderr, "[%s]  sndjbc_cleanup: %s\n%s",
X			  get_timestamp(), getmsgid(status), fmt_q_entry(q));
X	if (q->job->synch_ast_data)
X	{   /* do notification from here */
X	    q->job->synch_ast_data->iosb[0] = status; /* cause of the problem */
X	    SYS$DCLAST (synch_ast, q->job->synch_ast_data, NULL);
X	} /*if*/
X        close_delete_job ();
X        write_nak (q->channel);
X	fprintf (stderr, "[%s]  sndjbc_cleanup: %s\n%s\n",
X			get_timestamp(), getmsgid(status), fmt_q_entry(q));
X    } /*if*/
X    else
X	write_ack (q->channel);		/* terminated successfully */
X
X    unlock (&q->proc_lksb);		/* release resource */
X    q->proc_state = SEND_ACT;		/* almost finished */
X    send_eof (q);			/* close link */
X    q->job->iosb[0] = status;		/* save for traceback */
X} /* sndjbc_cleanup() */
X
X/* create a one-file job */
Xstatic long
Xenter_file (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * j = q->job;
X    register long status;
X    long prvprv[2];
X    register u_char ntfy = (*j->mail_recipient != '\0');
X    /*
X     Flag/burst page strategy:
X       If separation is requested, the queue default is used.
X       If separation is turned *off*, this is done by NO_BURST/NO_FLAG,
X       depending on whether BURST or FLAG is the default separator;
X    */
X    u_char burst = (j->config->queue_tab_ptr->queue_flags & QUI$$BURST) != 0;
X    u_long flagp_off = (burst) ? SJC$_NO_FILE_BURST : SJC$_NO_FILE_FLAG;
X
X    ITEM_LIST_3 item_list[] =
X    {
X	    /* create */
X	strlen(j->config->queue), SJC$_QUEUE,	        j->config->queue, NULL,
X	strlen(j->job_name),  SJC$_JOB_NAME, 	   	j->job_name, 	NULL,
X	sizeof(j->job_entry), SJC$_ENTRY_NUMBER_OUTPUT,	&j->job_entry,	NULL,
X	strlen(j->vms_user),  SJC$_USERNAME,		j->vms_user,	NULL,
X	strlen(j->account),   SJC$_ACCOUNT_NAME,	j->account,	NULL,
X	sizeof(j->uic),	      SJC$_UIC,			&j->uic,	NULL,
X	strlen(j->note),      SJC$_NOTE,		j->note,	NULL,
X	strlen(j->config->param[0]),  SJC$_PARAMETER_1,	j->config->param[0], NULL,
X	strlen(j->config->param[1]),  SJC$_PARAMETER_2,	j->config->param[1], NULL,
X	strlen(j->config->param[2]),  SJC$_PARAMETER_3,	j->config->param[2], NULL,
X	strlen(j->config->param[3]),  SJC$_PARAMETER_4,	j->config->param[3], NULL,
X	strlen(j->config->param[4]),  SJC$_PARAMETER_5,	j->config->param[4], NULL,
X	strlen(j->config->param[5]),  SJC$_PARAMETER_6,	j->config->param[5], NULL,
X	strlen(j->config->param[6]),  SJC$_PARAMETER_7,	j->config->param[6], NULL,
X	strlen(j->config->param[7]),  SJC$_PARAMETER_8,	j->config->param[7], NULL,
X	0,                    SJC$_NO_LOG_SPOOL,	NULL,		NULL,
X        (ntfy) ? strlen(j->job_id) : 0,
X			(ntfy) ? SJC$_LOG_SPECIFICATION : SJC$_NO_LOG_SPECIFICATION,
X				             (ntfy) ? j->job_id : NULL,	NULL,
X	0,  (ntfy) ? SJC$_NO_LOG_DELETE : SJC$_LOG_DELETE,      NULL,   NULL,
X	0,  (ntfy) ? SJC$_NOTIFY : SJC$_NO_NOTIFY, 	        NULL, 	NULL,
X#ifdef DEBUG
X	0,  (debug&DBG_JOB_HOLD)?SJC$_HOLD:SJC$_NO_HOLD,  	NULL,  	NULL,
X#endif DEBUG
X	    /* add_file */
X	sizeof(FILE_ID), SJC$_FILE_IDENTIFICATION,   &j->file_list[0]->file_id,  NULL,
X	sizeof(j->job_copies),    SJC$_FILE_COPIES,	   &j->job_copies,   NULL,
X	strlen(j->config->setup), SJC$_FILE_SETUP_MODULES, j->config->setup, NULL,
X	0, 		          SJC$_DELETE_FILE,	          NULL,	     NULL,
X	0, (j->header_flag)?SJC$_PAGE_HEADER:SJC$_NO_PAGE_HEADER, NULL,      NULL,
X	0, (j->passall_flag)?SJC$_PASSALL:SJC$_NO_PASSALL, 	  NULL,      NULL,
X	    /* close */
X      	sizeof(j->job_status), SJC$_JOB_STATUS_OUTPUT, j->job_status, NULL,
X
X	    /* the following *must* be the last entry since item-code could be 0 */
X	    /* to obtain default burst/flag behaviour */
X	0,                        (j->flagp_flag)?0:flagp_off,    NULL,      NULL,
X	    /* terminator */
X	0, 0, NULL, NULL
X    };
X
X    /* change file name and ownership */
X    set_file_name_and_owner (q);
X
X    /* prepare msg in case something goes wrong */
X    sprintf (j->job_status, "Job %s", j->job_name);
X
X    SYS$SETPRV (1, cmk_sys, 0, prvprv);		/* enable privs */
X    status = SYS$SNDJBC (0, SJC$_ENTER_FILE, 0, item_list, j->iosb,
X							enter_file_ast, q);
X    prvprv[0] = cmk_sys[0] & ~prvprv[0];		/* restore privs */
X    SYS$SETPRV (0, prvprv, 1, NULL);
X
X    dbg_msg ("enter_file", DBG_JBC_JOB|DBG_JBC_FILE, q, status);
X
X    return (status);
X} /* enter_file() */
X
X
X
Xstatic void
Xenter_file_ast (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * j = q->job;
X    register FILE_LIST_ENTRY * f;
X    register long status = j->iosb[0];
X
X    dbg_msg ("enter_file_ast", DBG_JBC_JOB|DBG_JBC_FILE, q, status);
X
X    if (odd (status))
X    {
X	statistics.jobs_queued++;
X        free (rem_2ll_h (j->file_list));
X	if (   (*q->job->mail_recipient != '\0')
X            && (*q->mail_fmt)
X	    && (get_ast_count() > min_ast_remain))
X	    synch_to_job (q);	    /* notification */
X
X        if (   (*q->job->mail_recipient != '\0')  /* notification requested */
X            && (*q->mail_fmt)
X	    && (q->job->config->queue_tab_ptr->q_type & QT_BATCH))
X	{
X	    unlock (&q->proc_lksb);	/* release resource */
X	    create_batch_log_mailer_job (q);
X	    return;			/* NO cleanup yet!! */
X	} /*if*/
X     } /*if*/
X
X    sndjbc_cleanup (q, status);		/* finished */
X} /* enter_file_ast() */
X
X/* create a multi-file job */
Xstatic long
Xcreate_job (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * p = q->job;
X    register long status;
X    long prvprv[2];
X    register u_char ntfy = (*p->mail_recipient != '\0');
X
X    ITEM_LIST_3 item_list[] =
X    {
X	strlen(p->config->queue), SJC$_QUEUE,	        p->config->queue, NULL,
X	strlen(p->job_name),  SJC$_JOB_NAME, 	   	p->job_name, 	NULL,
X	sizeof(p->job_entry), SJC$_ENTRY_NUMBER_OUTPUT,	&p->job_entry,	NULL,
X	strlen(p->vms_user),  SJC$_USERNAME,		p->vms_user,	NULL,
X	strlen(p->account),   SJC$_ACCOUNT_NAME,	p->account,	NULL,
X	sizeof(p->uic),	      SJC$_UIC,			&p->uic,	NULL,
X	strlen(p->note),      SJC$_NOTE,		p->note,	NULL,
X	0,                    SJC$_NO_LOG_SPOOL,	NULL,		NULL,
X        (ntfy) ? strlen(p->job_id) : 0,
X			(ntfy) ? SJC$_LOG_SPECIFICATION : SJC$_NO_LOG_SPECIFICATION,
X				             (ntfy) ? p->job_id : NULL,	NULL,
X	0,  (ntfy) ? SJC$_NO_LOG_DELETE : SJC$_LOG_DELETE,      NULL,   NULL,
X	0,  (ntfy) ? SJC$_NOTIFY : SJC$_NO_NOTIFY, 	        NULL, 	NULL,
X	strlen(p->config->param[0]),  SJC$_PARAMETER_1,	p->config->param[0], NULL,
X	strlen(p->config->param[1]),  SJC$_PARAMETER_2,	p->config->param[1], NULL,
X	strlen(p->config->param[2]),  SJC$_PARAMETER_3,	p->config->param[2], NULL,
X	strlen(p->config->param[3]),  SJC$_PARAMETER_4,	p->config->param[3], NULL,
X	strlen(p->config->param[4]),  SJC$_PARAMETER_5,	p->config->param[4], NULL,
X	strlen(p->config->param[5]),  SJC$_PARAMETER_6,	p->config->param[5], NULL,
X	strlen(p->config->param[6]),  SJC$_PARAMETER_7,	p->config->param[6], NULL,
X	strlen(p->config->param[7]),  SJC$_PARAMETER_8,	p->config->param[7], NULL,
X#ifdef DEBUG
X	0,  (debug&DBG_JOB_HOLD)?SJC$_HOLD:SJC$_NO_HOLD,  	NULL,  	NULL,
X#endif DEBUG
X	0, 0, NULL, NULL	/* terminator */
X    };
X
X    sprintf (p->job_status, "Job %s", p->job_name);
X    SYS$SETPRV (1, cmkrnl, 0, prvprv);		/* enable privs */
X    status = SYS$SNDJBC (0, SJC$_CREATE_JOB, 0, item_list, p->iosb,
X						     create_job_ast, q);
X    prvprv[0] = cmkrnl[0] & ~prvprv[0];		/* restore privs */
X    SYS$SETPRV (0, prvprv, 1, NULL);
X
X    dbg_msg ("create_job", DBG_JBC_JOB|DBG_JBC_FILE, q, status);
X
X    return (status);
X} /* create_job() */
X
Xstatic void
Xcreate_job_ast (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * j = q->job;
X    long status = j->iosb[0];
X
X    dbg_msg ("create_job_ast", DBG_JBC_JOB|DBG_JBC_FILE, q, status);
X
X    if (odd (status))		/* last operation terminated successfully */
X	status = add_file (q);
X
X    if (!odd (status))
X	sndjbc_cleanup (q, status);
X} /* create_job_ast() */
X
Xstatic long
Xadd_file (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * p = q->job;
X    register FILE_LIST_ENTRY * fl = p->file_list[0]; /* next file to add */
X    register long status;
X    long prvprv[2];			/* current privs */
X    /*
X     Flag/burst page strategy:
X       If separation is requested, the queue default is used.
X       If separation is turned *off*, this is done by NO_BURST/NO_FLAG,
X       depending on whether BURST or FLAG is the default separator;
X    */
X    u_char burst  = (p->config->queue_tab_ptr->queue_flags & QUI$$BURST)  != 0;
X    u_long flagp_off = (burst) ? SJC$_NO_FILE_BURST : SJC$_NO_FILE_FLAG;
X
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(FILE_ID), SJC$_FILE_IDENTIFICATION,         &fl->file_id,     NULL,
X	sizeof(p->job_copies),    SJC$_FILE_COPIES,	   &p->job_copies,   NULL,
X	strlen(p->config->setup), SJC$_FILE_SETUP_MODULES, p->config->setup, NULL,
X	0, 		          SJC$_DELETE_FILE,	          NULL,	     NULL,
X	0, (p->header_flag)?SJC$_PAGE_HEADER:SJC$_NO_PAGE_HEADER, NULL,      NULL,
X	0, (p->passall_flag)?SJC$_PASSALL:SJC$_NO_PASSALL, 	  NULL,      NULL,
X	    /* this *must* be the last entry since item-code could be 0 */
X	    /* to obtain default burst/flag behaviour */
X	0,                        (p->flagp_flag)?0:flagp_off,    NULL,      NULL,
X	0, 0, NULL, NULL
X    };
X
X    /* change file ownership */
X    set_file_name_and_owner (q);
X
X    /* add to print job */
X    SYS$SETPRV (1, sysprv, 0, prvprv);		/* enable privs */
X    status = SYS$SNDJBC (0, SJC$_ADD_FILE, 0, item_list, p->iosb,
X						   add_file_ast, q);
X    prvprv[0] = sysprv[0] & ~prvprv[0];		/* restore privs */
X    SYS$SETPRV (0, prvprv, 1, NULL);
X
X#ifdef DEBUG
X    if (debug & DBG_JBC_FILE)
X        fprintf(stderr, "[%s]  add_file: %s%u: %s, %s\n",
X		get_timestamp(), netdev, q->unit, getmsgid(status), fl->vms_fn);
X#endif DEBUG
X
X    if (!odd (status))
X	sndjbc_cleanup (q, status);		/* AST would not be called */
X
X    return (status);
X} /* add_file() */
X
Xstatic void
Xadd_file_ast (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * j = q->job;
X    long status = j->iosb[0];
X
X    if (odd(status))
X    {
X	free (rem_2ll_h (j->file_list));
X	if (j->file_list[0])
X	    status = add_file (q);	/* more files for this job */
X	else
X	    status = close_job (q);	/* no more files */
X    } /*if*/
X
X    if (!odd (status))
X	sndjbc_cleanup (q, status);	/* AST would not be called */
X} /* add_file_ast() */
X
Xstatic long
Xclose_job (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * p = q->job;
X    register long status;
X    ITEM_LIST_3 close_item_list[] =
X    {
X      	sizeof(p->job_status), SJC$_JOB_STATUS_OUTPUT, p->job_status, NULL,
X	0, 0, NULL, NULL
X    };
X
X    p->job_status[0] = '\0';
X    status = SYS$SNDJBC (0, SJC$_CLOSE_JOB, 0, close_item_list, p->iosb,
X							  close_job_ast, q);
X    dbg_msg ("close_job", DBG_JBC_JOB|DBG_JBC_FILE, q, status);
X    if (!odd (status))
X	sndjbc_cleanup (q, status);		/* AST would not be queued */
X
X    return (status);
X} /* close_job() */
X
X
X
Xstatic void
Xclose_job_ast (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * j = q->job;
X    register FILE_LIST_ENTRY * f;
X    register long status = j->iosb[0];
X
X    dbg_msg ("close_job_ast", DBG_JBC_JOB, q, status);
X    if (odd (status))
X    {
X	statistics.jobs_queued++;
X        free (rem_2ll_h (j->file_list));
X	if (   (q->job->synch_ast_data != NULL)
X	    && (get_ast_count() > min_ast_remain))
X	    synch_to_job (q);	    	/* notification */
X
X 	if (   (q->job->synch_ast_data != NULL)  /* notification requested */
X	    && (q->job->config->queue_tab_ptr->q_type & QT_BATCH))
X	{
X	    unlock (&q->proc_lksb);	/* release resource */
X	    create_batch_log_mailer_job (q);
X	    return;			/* NO cleanup yet!! */
X	} /*if*/
X    } /*if*/
X    sndjbc_cleanup (q, status);		/* finished */
X} /* close_job_ast() */
X
X
X
Xstatic void
Xclose_delete_job ()
X{
X    SYS$SNDJBCW (0, SJC$_CLOSE_DELETE, 0, NULL, NULL, NULL, NULL);
X#ifdef DEBUG
X    if (debug & (DBG_JBC_JOB|DBG_JBC_FILE))
X	fprintf(stderr, "abort_job\n");
X#endif DEBUG
X} /* close_delete_job() */
X
Xstatic void
Xsynch_ast_data_setup (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * p = q->job;
X    if (*p->mail_recipient && *q->mail_fmt)
X    {
X        register SYNCH_AST_DATA * ast_data;
X
X	ast_data = (SYNCH_AST_DATA*) calloc (1, sizeof(SYNCH_AST_DATA));
X	strcpy (ast_data->mail_fmt,       q->mail_fmt);
X	strcpy (ast_data->mail_recipient, p->mail_recipient);
X	strcpy (ast_data->owner,          p->owner);
X	strcpy (ast_data->host,           p->host);
X	sprintf (ast_data->job_status, "Job %s", p->job_name);
X	p->synch_ast_data = ast_data;
X    } /*if*/
X} /* synch_ast_data_setup() */
X
X
X
Xstatic void
Xsynch_to_job (Q_ENTRY * q)
X{
X    register PRINT_JOB_STRUCT * p = q->job;
X    register SYNCH_AST_DATA * ast_data = p->synch_ast_data;
X    register char * s;
X    register long status;
X    long prvprv[2];
X    ITEM_LIST_3 synch_item_list[] =
X    {
X	sizeof(p->job_entry), SJC$_ENTRY_NUMBER, &p->job_entry,	NULL,
X	0, 0, NULL, NULL
X    };
X
X    strcpy (ast_data->job_status, p->job_status);
X    if ((s = strrchr(ast_data->job_status, ')')) != NULL)
X        *(s+1) = '\0';				/* shorten string */
X
X    SYS$SETPRV (1, sysprv, 0, prvprv);		/* enable privs */
X    status = SYS$SNDJBC (0, SJC$_SYNCHRONIZE_JOB, 0,
X		synch_item_list, ast_data->iosb, synch_ast, ast_data);
X    prvprv[0] = sysprv[0] & ~prvprv[0];		/* restore privs */
X    SYS$SETPRV (0, prvprv, 1, NULL);
X
X    if (! odd (status))
X	free (ast_data);			/* something went wrong */
X} /* synch_to_job() */
X
X/* AST called after SYNC completes */
Xstatic void
Xsynch_ast (SYNCH_AST_DATA * p)
X{
X    char subj[VMS_STR], text[2*VMS_STR], *dst, *t;
X
X    t = (odd(p->iosb[0])) ? " completed" : " terminated with error status";
X    /* mail to notifier on success, to owner if error */
X    dst = mail_addr (p->mail_fmt,
X	(odd(p->iosb[0])) ? p->mail_recipient : p->owner, p->host);
X    sprintf (text, "\r\n%s%s\r\n%s\r\n", p->job_status, t, getmsg (p->iosb[0]));
X    sprintf (subj, "Printer/Batch job from %s%s", p->owner, t);
X    send_mail (dst, subj, text);
X
X    free (p);
X} /* synch_ast() */
X
Xstatic void
Xcreate_batch_log_mailer_job (Q_ENTRY * q)
X{
X
X#define VMS_PROC_1 "\
X$SET NOON\n\
X$SYNCHRONIZE/ENTRY=%u\n"
X
X#define VMS_PROC_2 "\
X$MAIL/SUBJECT=\"Batch Log\"/NOSELF %s.LOG %s\n\
X$DELETE/NOCONFIRM %s.LOG;*\n"
X
X    register long status;
X    char vms_procedure[2048], *dst, *t;
X    register PRINT_JOB_STRUCT * j = q->job;
X    register FILE_LIST_ENTRY * fle;
X    struct FAB * fab = &j->fab;
X    struct NAM * nam = &j->nam;
X    struct RAB * rab = &j->rab;
X
X    /* build the message (split in 2 strings due to VMS sprintf() limit) */
X    sprintf (vms_procedure, VMS_PROC_1, j->job_entry);
X    t = vms_procedure + strlen (vms_procedure);
X    dst = mail_addr (q->mail_fmt, j->owner, q->host);
X    sprintf (t, VMS_PROC_2, j->job_id, dst, j->job_id);
X
X    *fab = cc$rms_fab;				/* RMS initialization */
X    *nam = cc$rms_nam;				/* RMS initialization */
X    *rab = cc$rms_rab;				/* RMS initialization */
X
X    fab->fab$l_nam = nam;			/* link NAM to FAB */
X    rab->rab$l_fab = fab;			/* link FAB to RAB */
X    rab->rab$l_ctx = q;				/* link q-entry to RAB */
X    rab->rab$l_rop = RAB$M_BIO;			/* block I/O only */
X    rab->rab$l_rbf = vms_procedure;		/* buffer addr */
X    rab->rab$w_rsz = strlen (vms_procedure);	/* msg length */
X    fab->fab$b_fac = FAB$M_BIO | FAB$M_PUT;	/* block write */
X    fab->fab$b_rfm = FAB$C_STMLF;		/* stream_lf format */
X    fab->fab$b_shr = FAB$M_NIL;			/* no sharing */
X    fab->fab$l_fop = FAB$M_TMP;			/* no dir-entry */
X
X    /* allocate new entry */
X    fle = (FILE_LIST_ENTRY*) calloc (1, sizeof (FILE_LIST_ENTRY));
X    assert_showval (fle != NULL, sizeof (FILE_LIST_ENTRY));
X    ins_2ll_h (j->file_list, fle);
X    status = sys$create (fab, NULL, NULL);
X
X    if (odd (status))
X    {
X        sys$connect (rab, NULL, NULL);
X        rab->rab$l_rop |= RAB$M_ASY;		/* asynch from now on */
X        sys$write (rab, create_blm_ast, create_blm_ast);
X	memcpy (&fle->file_id, &nam->nam$t_dvi, sizeof(FILE_ID));
X        *q->job->mail_recipient = '\0';		/* NO more notification! */
X    } /*if*/
X    else
X	sndjbc_cleanup (q, status);
X} /* create_batch_log_mailer_job() */
X
X/*
X * This AST is queued when the command file for the follow-up batch job
X * has been written. It queues again for SNDJBC.
X */
Xstatic void
Xcreate_blm_ast (struct RAB * rab)
X{
X    register long status = rab->rab$l_sts;
X    register Q_ENTRY * q = rab->rab$l_ctx;
X
X    if (odd (status))
X    {    /* close received file */
X        sys$close (&q->job->fab, NULL, NULL);
X	q->proc_state = SNDJBC_PND;
X        assert (q->proc_lksb.lock_id == 0);	/* should be unlocked! */
X	status = lock_sndjbc (q);		/* queue again for resource */
X    } /*if*/
X
X    if (! odd (status))
X    {   /* close/delete file */
X	q->job->fab.fab$l_fop = FAB$M_DLT;
X        sys$close (&q->job->fab, NULL, NULL);
X	sndjbc_cleanup (q, status);
X    } /*else*/
X} /* create_blm_ast() */
X
Xstatic long
Xset_file_name_and_owner (Q_ENTRY * q)
X{
X    PRINT_JOB_STRUCT * j = q->job;
X    FILE_LIST_ENTRY * ft = j->file_list[0];
X    long prvprv[2];
X    struct FAB * fab = &j->fab;
X    struct NAM * nam = &j->nam;
X    struct XABPRO xab = cc$rms_xabpro;
X    char expnam[NAM$C_MAXRSS];		/* RMS working area */
X    char resnam[NAM$C_MAXRSS];		/* RMS result area */
X
X    *fab = cc$rms_fab;				/* RMS initialization */
X    *nam = cc$rms_nam;				/* RMS initialization */
X    fab->fab$l_nam = nam;			/* link them together */
X    fab->fab$l_fna = ft->vms_fn;
X    fab->fab$b_fns = strlen(fab->fab$l_fna);
X    nam->nam$l_esa = expnam;
X    nam->nam$b_ess = sizeof(expnam);
X    nam->nam$l_rsa = resnam;
X    nam->nam$b_rss = sizeof(resnam);
X
X    if (!odd (j->iosb[0]=sys$parse (fab, NULL, NULL)))
X	return (j->iosb[0]);			/* error in filespec ? */
X
X    /* copy file-id to NAM block */
X    memcpy (nam->nam$w_fid, ft->file_id.w_fid, sizeof (nam->nam$w_fid));
X    if (!odd (j->iosb[0]=sys$enter (fab, NULL, NULL)))	/* make dir-entry */
X	return (j->iosb[0]);
X    nam->nam$l_rsa[nam->nam$b_rsl] = '\0';
X    strcpy (ft->vms_fn, nam->nam$l_rsa);	/* save in file_list */
X    sys$remove (fab, NULL, NULL);		/* remove dir-entry again */
X    nam->nam$w_did[0] = nam->nam$w_did[1] = nam->nam$w_did[2] = 0;
X    ft->file_id.w_did[0] = ft->file_id.w_did[1] = ft->file_id.w_did[2] = 0;
X
X    /* change file ownership */
X    fab->fab$b_fac = FAB$M_UPD;			/* update file */
X    fab->fab$l_fop = FAB$M_NAM;			/* use fields from NAM block */
X
X    SYS$SETPRV (1, sysprv, 0, prvprv);		/* enable privs */
X    sys$open  (fab, NULL, NULL);
X    fab->fab$l_xab = &xab;
X    xab.xab$l_uic = j->uic;
X    sys$close (fab, NULL, NULL);
X    prvprv[0] = sysprv[0] & ~prvprv[0];		/* reset privs */
X    SYS$SETPRV (0, prvprv, 1, NULL);
X    return (SS$_NORMAL);
X} /* set_file_name_and_owner() */
END_OF_FILE
if test 22557 -ne `wc -c <'sndjbc.c'`; then
    echo shar: \"'sndjbc.c'\" unpacked with wrong size!
fi
# end of 'sndjbc.c'
fi
if test -f 'llist.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'llist.c'\"
else
echo shar: Extracting \"'llist.c'\" \(3849 characters\)
sed "s/^X//" >'llist.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)llist.c 1.4 1/30/92 15:34:40";
X/*
X    Generic functions to operate on single and double linked lists.
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/*
X * Formats:
X *
X *          HEADER          ENTRY
X *         +------+        +------+
X *         | next |        | next |
X *         +------+        +------+
X *         | last |        | last |
X *         +------+        +------+
X *                         |      |
X *                         |      |
X *                         |      |
X *                         |~~~~~~|
X *
X *
X */
X
X/*
X * This declaration just serves to make the module compilable.
X * The only important thing is that the layout of your data structure
X * matches the one above.
X */
Xtypedef struct q_hdr
X    {
X	struct q_hdr * next;
X	struct q_hdr * last;
X    } Q_HDR;
X
X
X
Xint
Xins_2ll_h (Q_HDR * h, Q_HDR * e)	/* insert at head of list */
X{
X    if ((h == 0) || (e == 0))
X	return (-1);
X    if (h->next == 0)			/* no entry yet */
X    {
X	e->next = e->last = 0;		/* clear "next" and "last" of entry */
X	h->next = h->last = e;		/* fwd/backwd link */
X	return (0);			/* one entry in queue */
X    } /*if*/
X    else
X    {
X	h->next->last = e;		/* backward link */
X	e->next = h->next;		/* forward link */
X	h->next = e;			/* ptr to first elem. */
X	return (1);			/* more than one entry */
X    } /*else*/
X} /* ins_2ll_h() */
X
X
Xint
Xins_2ll_t (Q_HDR * h, Q_HDR * e)	/* insert at tail of list */
X{
X    if ((h == 0) || (e == 0))
X	return (-1);
X    if (h->last == 0)			/* no entry yet */
X    {
X	e->next = e->last = 0;		/* clear "next" and "last" of entry */
X	h->next = h->last = e;		/* fwd/backwd link */
X	return (0);			/* one entry in queue */
X    } /*if*/
X    else
X    {
X	h->last->next = e;		/* forward link */
X	e->last = h->last;		/* backward link */
X	h->last = e;			/* ptr to last entry */
X	return (1);			/* more than one entry */
X    } /*else*/
X} /* ins_2ll_t() */
X
X
X
Xvoid *
Xrem_2ll_h (Q_HDR * h)			/* remove first entry */
X{
X    register Q_HDR * e;
X    if (h == 0)
X	return (0);
X
X    e = h->next;
X    if (e == 0)
X	return (0);
X
X    h->next = e->next;			/* modify "next" of hdr */
X
X    if (e->next != 0)			/* not last entry */
X	e->next->last = e->last;	/* break backward link */
X    else
X	h->last = e->last;		/* modify "last" of hdr */
X
X    return (e);
X} /* rem_2ll_h() */
X
X
X
Xvoid *
Xrem_2ll_t (Q_HDR * h)			/* remove last entry */
X{
X    register Q_HDR * e;
X    if (h == 0)
X	return (0);
X
X    e = h->last;
X    if (e == 0)
X	return (0);
X
X    h->last = e->last;			/* modify "last" of hdr */
X
X    if (e->last != 0)			/* not first entry */
X	e->last->next = e->next;	/* break forward link */
X    else
X	h->next = e->next;		/* modify "next" of hdr */
X
X    return (e);
X} /* rem_2ll_t() */
X
X
X
Xvoid *
Xrem_2ll (Q_HDR * h, Q_HDR * e)
X{
X    if ((h == 0) || (e == 0))
X	return (0);
X
X    if (e->last != 0)			/* not first entry */
X	e->last->next = e->next;	/* break forward link */
X    else
X	h->next = e->next;		/* modify "next" of hdr */
X
X    if (e->next != 0)			/* not last entry */
X	e->next->last = e->last;	/* break backward link */
X    else
X	h->last = e->last;		/* modify "last" of hdr */
X
X    return (e);
X} /* rem_2ll() */
X
X
X
Xvoid
Xfree_2ll (Q_HDR * h)
X{
X    register Q_HDR *p, *n;
X    if (h == 0)
X	return;
X    for (p = h->next; p != 0; p = n)
X    {
X	n = p->next;			/* save "next" ptr */
X	free (p);
X    } /*for*/
X} /* free_2ll() */
END_OF_FILE
if test 3849 -ne `wc -c <'llist.c'`; then
    echo shar: \"'llist.c'\" unpacked with wrong size!
fi
# end of 'llist.c'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2922 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X
X
X      lpd (BSD-style line printer daemon) for VMS 
X      -------------------------------------------
X
XCopyright (C) 1991 Dietmar Berg <berg@alcatel.co.at>.
XAll rights reserved.
X
XAbstract:
X  This package enables users on a UNIX-system to access printers
X  (or more general, queues) on a VMS system. All UNIX utilities
X  (lpr, lpq, lprm, lpd) can be used without any changes.
X  Access privileges and job-ownership are retained by mapping UNIX- 
X  to VMS-users through a configuration file. The daemon on VMS must 
X  be run with appropriate privileges.
X
X
XTwo servers can be built from this package:
X   DLPD    DECnet version
X           used with a protocol-converter daemon (lpdgw) running on an
X           ULTRIX-machine acting as gateway between Internet and DECnet
X   ILPD    Internet version
X           used with UCX-software on VMS
X
XTo build:
X  on VMS:
X    $ MMS dlpd.exe
X    $ MMS ilpd.exe
X
X  on ULTRIX (gateway process):
X    % cc -o lpdgw lpdgw.c -ldnet
X
XFor installation and configuration, see the documentation dlpd.tex (source)
Xor dlpd.ps (PostScript output)
X
X
X   File Name		Archive #	Description
X-----------------------------------------------------------
X README                     1		This file
X COPYRIGHT		    1		(my company requires this)
X descrip.mms                1		"makefile" for VMS
X build.com		    1		output of MMS/NOACTION
X lpd.h                      1		header file
X lpd.c                      1		main program
X rcvjob.c                   2		receive a job
X showjob.c                  1		show queue
X deljob.c                   1		delete job
X inet.c                     2		UCX / TCP network stuff
X dnet.c                     2		DECnet network stuff
X sndjbc.c                   2		send job to job-controller
X getqui.c                   3		get queue info
X cmd.c                      1		operator-comm
X config.c                   3		configuration
X util.c                     3		utility functions
X llist.c                    2		list handling routines
X baseline.c                 2		list of modules
X lpd.opt                    1		just for VMS-linker
X dlpd.tex                   4		doc in LaTeX-source
X dlpd.ps.A                  5		doc in PostScript, part A
X dlpd.ps.B                  5		doc in PostScript, part B (concat A+B)
X lpdgw.c                    4		ULTRIX gateway program
X ilpd_start.com             2		sample startup routine ILPD
X dlpd_start.com             2		sample startup routine DLPD
X dlpd.config                4		sample config file
X cmd.com		    5		sample proc to interface to cmd-mbx
X
X
X--
X           V             Dietmar Berg          C=AT;A=ADA;P=Alcatel-W01 (X.400)
X+---------------------+  Telecom Engineer          <Dietmar.Berg@alcatel.co.at>
X| A  L  C  A  T  E  L |  Alcatel Austria AG              10741::X100_BERG (ANS)
X+---------------------+  Scheydgasse 41                  +43-1-27722-2788 (tel)
X     A U S T R I A       A-1210 Vienna-Austria/Europe    +43-1-27722-148  (fax)
X
END_OF_FILE
if test 2922 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'COPYRIGHT' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'COPYRIGHT'\"
else
echo shar: Extracting \"'COPYRIGHT'\" \(1461 characters\)
sed "s/^X//" >'COPYRIGHT' <<'END_OF_FILE'
X
X
X
X                    DLPD / ILPD General Public License
X
X
X
XCopyright (C) 1991 Dietmar Berg <berg@alcatel.co.at>. All rights reserved.
X
X
XPermission  to  use,  copy,  and  distribute   this   software   and   its
Xdocumentation  without fee for any purpose other than commercialisation is
Xhereby granted, provided that the above copyright notice  appears  in  all
Xcopies  and  that  both  that  copyright notice and this permission notice
Xappear in supporting documentation.  This software  is  provided  "as  is"
Xwithout expressed or implied warranty.
X
XThe permission for modification is  granted  provided  that  the  modified
Xfiles  carry prominent notices stating who changed what and when.  To help
Xkeeping the world in synch, bug fixes should also be communicated  to  the
Xauthor for incorporation into a possible next release or official patch.
X
X
X
X                          DISCLAIMER OF WARRANTY
X
X  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
X  NO  WARRANTY,  TO  THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  THIS
X  PROGRAM IS PROVIDED "AS IS"  WITHOUT  WARRANTY  OF  ANY  KIND,  EITHER
X  EXPRESSED  OR  IMPLIED,  INCLUDING  BUT  NOT  LIMITED  TO, THE IMPLIED
X  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  A  PARTICULAR  PURPOSE.
X  THE  ENTIRE  RISK AS TO THE QUALITY AND PERFORMANCE OF THIS PROGRAM IS
X  WITH YOU.  SHOULD IT PROVE DEFECTIVE,  YOU  ASSUME  THE  COST  OF  ALL
X  NECESSARY SERVICING, REPAIR OR CORRECTION.
X
END_OF_FILE
if test 1461 -ne `wc -c <'COPYRIGHT'`; then
    echo shar: \"'COPYRIGHT'\" unpacked with wrong size!
fi
chmod +x 'COPYRIGHT'
# end of 'COPYRIGHT'
fi
echo shar: End of archive 2 \(of 6\).
cp /dev/null ark2isdone
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
