From:	MERC::"uunet!alcatel.co.@!Dietmar.Berg"  9-JUN-1992 08:53:02.76
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 Part03 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 3 (of 6)."
# Contents:  getqui.c config.c util.c
# Wrapped by berg@valide on Tue Jun  9 12:28:50 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'getqui.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getqui.c'\"
else
echo shar: Extracting \"'getqui.c'\" \(21975 characters\)
sed "s/^X//" >'getqui.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)getqui.c 1.9 1/30/92 15:34:33";
X/*
X    Get queue information 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 <descrip.h>
X#include <jbcmsgdef.h>
X#include <quidef.h>
X#include <prvdef.h>
X
X#include "lpd.h"
X
X
Xextern long debug;
Xextern char * netdev;			/* name of network device */
X
X
X/* Proxy database */
Xextern VMS_USER_TAB_ENTRY * vms_user_tab; 	/* table of VMS users */
X
X
X/* VMS queue database */
XQUEUE_TAB_ENTRY * queue_tab = NULL;	/* table of VMS queues */
Xshort n_queue_tab_entries = 0;		/* current # of entries */
Xshort max_queue_tab_entries = 0; 	/* max # of entries */
X
X
X/* Local data */
Xstatic long sysprv[2]  = {PRV$M_SYSPRV, 0};
X
X
X/* Prototypes for module-local functions */
Xextern long display_queue      (Q_ENTRY * q);
Xextern void display_queue_ast  (Q_ENTRY * q);
Xextern long display_job        (Q_ENTRY * q);
Xextern void display_job_ast    (Q_ENTRY * q);
Xextern long display_file       (Q_ENTRY * q);
Xextern void display_file_ast   (Q_ENTRY * q);
Xextern long display_entry      (Q_ENTRY * q);
Xextern void display_entry_ast  (Q_ENTRY * q);
Xextern int queue_tab_compare (QUEUE_TAB_ENTRY * p1, QUEUE_TAB_ENTRY * p2);
Xextern void free_target_list (TARGET_Q_ENTRY * t);
X
X
X/* Macros, Constants */
X
X#define QUI$$FLAG ( \
X	  QUI$M_QUEUE_FILE_BURST     | QUI$M_QUEUE_FILE_FLAG      | \
X	  QUI$M_QUEUE_FILE_BURST_ONE | QUI$M_QUEUE_FILE_FLAG_ONE  | \
X	  QUI$M_QUEUE_JOB_BURST      | QUI$M_QUEUE_JOB_FLAG )
X
X
X#define conn_check(q)	    {if (q->link_state != CONNECTED)		\
X				{getqui_cleanup (q, SS$_ABORT); return;}}
X
X#ifdef DEBUG
X#define dbg_msg(f,q,stat)   {if (debug & DBG_GETQUI)			\
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/*
X * This function builds a table of all VMS queues in the system
X * If the queue is a generic one, a list of target queue names is
X * constructed.
X */
X
Xvoid
Xbuild_vms_queue_tab (u_char q_types)
X{
X    register long status, n;
X    QUEUE_TAB_ENTRY qte;
X    static char * srch_q = "*";
X    long iosb[2];
X    static u_long searchflags =   QUI$M_SEARCH_WILDCARD | QUI$M_SEARCH_BATCH
X				| QUI$M_SEARCH_SYMBIONT | QUI$M_SEARCH_PRINTER;
X    static char generic_target[Q_NAM_LEN * 124 + 1];
X    int n_print=0, n_batch=0;		/* statistics */
X    char msg[VMS_STR];
X    ITEM_LIST_3 item_list[] =
X    {
X	strlen(srch_q), 	 QUI$_SEARCH_NAME,    srch_q,           NULL,
X	sizeof(searchflags),	 QUI$_SEARCH_FLAGS,   &searchflags,     NULL,
X	sizeof(qte.queue), 	 QUI$_QUEUE_NAME,     qte.queue,        NULL,
X	sizeof(qte.queue_flags), QUI$_QUEUE_FLAGS,    &qte.queue_flags, NULL,
X        sizeof(generic_target),  QUI$_GENERIC_TARGET, generic_target,   NULL,
X	0, 0, NULL, NULL
X    };
X
X    op_msg ("Building VMS queue table...");
X
X    memset (&qte, 0, sizeof(QUEUE_TAB_ENTRY));		/* clear all fields */
X
X    n_queue_tab_entries = 0;		/* reset */
X    SYS$GETQUIW (0, QUI$_CANCEL_OPERATION, 0, NULL, iosb, NULL, NULL);
X    status = SYS$GETQUIW (0, QUI$_DISPLAY_QUEUE, 0, item_list,
X			                                iosb, NULL, NULL);
X    while (odd (status) && odd (iosb[0]))
X    {
X        if (n_queue_tab_entries >= max_queue_tab_entries)
X        {	    /* increase table size */
X	    queue_tab = realloc (queue_tab,
X	 		sizeof(QUEUE_TAB_ENTRY) * (max_queue_tab_entries+=20));
X	    assert_showval (queue_tab != NULL, sizeof(QUEUE_TAB_ENTRY)*max_queue_tab_entries);
X	    /* other than documented, realloc does *NOT*
X               initialize newly allocated space with 0 */
X	    memset (&queue_tab[n_queue_tab_entries], 0,
X		sizeof(QUEUE_TAB_ENTRY) * (max_queue_tab_entries-n_queue_tab_entries));
X        } /*if*/
X
X	qte.q_type = (qte.queue_flags & QUI$M_QUEUE_BATCH)
X                        ? QT_BATCH
X			: (qte.queue_flags & (QUI$M_QUEUE_PRINTER|QUI$M_QUEUE_TERMINAL))
X			   ? QT_PRINT
X			   : QT_OTHER;
X
X	if (qte.q_type & q_types)
X	{   /* select queues of type we need */
X	    if (qte.queue_flags & QUI$M_QUEUE_GENERIC)
X	    {   /* generic q, save list of targets */
X		register char *c, *t = generic_target;
X		register TARGET_Q_ENTRY *p;
X		while ((c = strchr (t, ',')) != NULL)
X		{   /* make linked list of target queue names */
X		    p = (TARGET_Q_ENTRY*) calloc (1, sizeof(TARGET_Q_ENTRY));
X		    assert_showval (p != NULL, sizeof(TARGET_Q_ENTRY));
X		    ins_2ll_t (qte.targets, p);
X		    *c++ = '\0';
X		    strcpy (p->queue, t);
X		    t = c;
X		} /*while*/
X		p = (TARGET_Q_ENTRY*) calloc (1, sizeof(TARGET_Q_ENTRY));
X		assert_showval (p != NULL, sizeof(TARGET_Q_ENTRY));
X		strcpy (p->queue, t);	 	/* (last element) */
X		ins_2ll_t (qte.targets, p);
X	    } /*if*/
X	    else
X	  	qte.targets[0] = qte.targets[1] = NULL;
X
X#ifdef DEBUG
X            if (debug & DBG_VMSQTYPES)
X	    {
X	        fprintf (stderr, "%3d: (%02x) ",
X			n_queue_tab_entries, (int)qte.q_type);
X		fputs (fmt_vms_queue_entry(&qte), stderr);
X  	    } /*if*/
X#endif DEBUG
X
X	    free_target_list (queue_tab[n_queue_tab_entries].targets[0]);
X            queue_tab[n_queue_tab_entries++] = qte;
X	    if (qte.q_type & QT_PRINT)
X		n_print++;
X	    if (qte.q_type & QT_BATCH)
X		n_batch++;
X	    memset (&qte, 0, sizeof(QUEUE_TAB_ENTRY));
X        } /*if*/
X        status = SYS$GETQUIW (0, QUI$_DISPLAY_QUEUE, 0, item_list,
X		                                iosb, NULL, NULL);
X    } /*while*/
X
X    SYS$GETQUIW (0, QUI$_CANCEL_OPERATION, 0, NULL, iosb, NULL, NULL);
X
X    qsort ((char*) queue_tab, n_queue_tab_entries,
X		sizeof (QUEUE_TAB_ENTRY), queue_tab_compare);
X
X    /*
X       Back-link from the target-list-entry to the corresponding
X       queue_tab_entry. Queue job/file burst/flag settings are
X       back propagated to the generic queue to facilitate its
X       handling.
X    */
X    for (n = 0; n < n_queue_tab_entries; n++)
X    {
X	register QUEUE_TAB_ENTRY * qt;
X	register TARGET_Q_ENTRY * target;
X	QUEUE_TAB_ENTRY key;			/* for using bsearch() */
X
X	if (queue_tab[n].queue_flags & QUI$M_QUEUE_GENERIC)
X	    for (target=queue_tab[n].targets[0]; target; target=target->next)
X	    {
X                strcpy (key.queue, target->queue);	/* init key for bsearch() */
X	        if ((target->qte = (QUEUE_TAB_ENTRY*) bsearch (&key, queue_tab,
X				n_queue_tab_entries, sizeof (QUEUE_TAB_ENTRY),
X                                                   queue_tab_compare)) != NULL)
X		    queue_tab[n].queue_flags |= (target->qte->queue_flags & QUI$$FLAG);
X	    } /*for*/
X    } /*for*/
X
X    sprintf (msg, "VMS queue table (re-)built: %d queues (%d PRINT, %d BATCH)",
X					n_queue_tab_entries, n_print, n_batch);
X    op_msg (msg);
X} /* build_vms_queue_tab() */
X
Xstatic int	/* comparison function for qsort(), bsearch() */
Xqueue_tab_compare (QUEUE_TAB_ENTRY * p1, QUEUE_TAB_ENTRY * p2)
X{
X    return (strcmp (p1->queue, p2->queue));
X} /* queue_tab_compare() */
X
X
Xstatic void		/* return dynamic memory */
Xfree_target_list (TARGET_Q_ENTRY * t)
X{
X    register TARGET_Q_ENTRY * q;
X    for ( ; t != NULL; t = q)
X    {
X	q = t->next;			/* save ptr to next element */
X	free (t);
X    } /*for*/
X} /* free_target_list() */
X
X/*
X * This function starts a series of $GETQUI services to obtain a
X * list of jobs (queues & files) for display or removal.
X *
X * For lpq-requests, get_job_list always returns the full list of
X * jobs in a sequence of nested display_queue/display_job/display_file
X * calls.
X * For lprm-requests with entry number or username given, $GETQUI is
X * called in a series of display_entry calls (for optimization).
X * The selection flag is set for jobs actually meeting the search
X * criteria although unused entries are currently removed from the list
X * (need not be in future releases -- caching).
X */
X
Xlong
Xget_job_list (Q_ENTRY * q)
X{
X    long status;
X    if ((q->request[0] == REQ_LPRM) && (q->qui->sel_tab[0].sel_mode != ALL))
X    {	/* create dummy queue-entry to keep job-entries together */
X	ins_2ll_h (q->qui->queue_list,
X		      (QUEUE_LIST_ENTRY*) calloc (1, sizeof(QUEUE_LIST_ENTRY)));
X	status = display_entry (q);
X    } /*if*/
X    else
X	status = display_queue (q);
X
X    if (! odd (status))
X	getqui_cleanup (q, status);	/* finish up */
X    return (status);
X} /* get_job_list() */
X
X/*
X * This function *must* be called at the end of a series of $GETQUI-calls
X * (be they successful or not) to do final cleanup and release the lock
X * on the getqui-service.
X * Further scheduling depending on the type of request (lpq/lprm) is
X * also done here.
X */
X
Xvoid
Xgetqui_cleanup (Q_ENTRY * q, long status)
X{
X    long iosb[2];			/* I/O status block */
X
X    /* clear context, dissolve QCB */
X    SYS$GETQUIW (0, QUI$_CANCEL_OPERATION, 0, NULL, iosb, NULL, NULL);
X
X    q->qui->iosb[0] = status;		/* save for later diagnostics */
X    q->proc_state = GETQUI_DONE;	/* release GETQUI-lock */
X    unlock (&q->proc_lksb);
X
X    if (odd (status))
X        /* do (asynchronuous) scheduling here */
X        switch (q->request[0])
X        {
X	    case REQ_LPQS   :	/* show queue, short format */
X	    case REQ_LPQL   :	/* show queue, long format */
X			  	show_queue (q);
X				break;
X	    case REQ_LPRM   :	/* delete jobs */
X				delete_jobs (q);
X				break;
X	    default	    :	/* should not come here */
X				fprintf (stderr, "getqui_cleanup: REQUEST=%d ?!\n%s",
X						q->request[0], fmt_q_entry(q));
X				assert (FALSE);	 /* programming error */
X	} /*switch*/
X    else
X    {
X	fprintf (stderr, "[%s]  getqui_cleanup: %s\n%s",
X			  get_timestamp(), getmsgid(status), fmt_q_entry(q));
X	send_errmsg_eof (q, status);
X    } /*else*/
X} /* getqui_cleanup() */
X
Xstatic long
Xdisplay_queue (Q_ENTRY * q)
X{
X    register long status;			/* VMS condition code */
X    char * srch_q = q->qui->search_queue;
X    u_char q_type = q->qui->config->queue_tab_ptr->q_type;
X    u_long searchflags = QUI$M_SEARCH_WILDCARD
X			 | ((q_type & QT_BATCH)
X			     ? QUI$M_SEARCH_BATCH
X			     : (q_type & QT_PRINT)
X				 ? QUI$M_SEARCH_SYMBIONT : 0);
X    register QUEUE_LIST_ENTRY * qle =		/* new mem for this entry */
X			(QUEUE_LIST_ENTRY*) calloc (1, sizeof(QUEUE_LIST_ENTRY));
X
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(searchflags),	QUI$_SEARCH_FLAGS,	&searchflags, 	NULL,
X	strlen(srch_q), 	QUI$_SEARCH_NAME,  	srch_q,		NULL,
X	sizeof(qle->queue), 	QUI$_QUEUE_NAME,	qle->queue,  	NULL,
X	sizeof(qle->node), 	QUI$_SCSNODE_NAME,	qle->node,  	NULL,
X	sizeof(qle->device), 	QUI$_DEVICE_NAME,	qle->device,  	NULL,
X	sizeof(qle->qstat), 	QUI$_QUEUE_STATUS,	&qle->qstat,  	NULL,
X	sizeof(qle->flags), 	QUI$_QUEUE_FLAGS,	&qle->flags,  	NULL,
X	0, 0, NULL, NULL
X    };
X
X    ins_2ll_t (q->qui->queue_list, qle);
X    status = SYS$GETQUI (0, QUI$_DISPLAY_QUEUE, 0, item_list, q->qui->iosb,
X							display_queue_ast, q);
X
X    if (!odd (status))
X	free (rem_2ll_t (q->qui->queue_list));
X
X    return (status);
X} /* display_queue() */
X
Xstatic void
Xdisplay_queue_ast (Q_ENTRY * q)		/* called when JBC finishes */
X{
X    register QUEUE_LIST_ENTRY  * qle = q->qui->queue_list[1]; /* last elem */
X    register long status = q->qui->iosb[0];
X
X    conn_check (q);
X    dbg_msg ("display_queue_ast", q, status);
X
X    if (status == JBC$_NOMOREQUE)
X    {	/* done with (wildcard?) search */
X	free (rem_2ll_t (q->qui->queue_list));	/* last entry not used */
X
X	if (q->qui->search_queue != q->qui->config->queue)
X	{    /* not yet finished with wildcard search -- check if this
X		also included generic targets and "official" queue name */
X	    register u_char found;
X	    register char * sq;
X	    if (q->qui->target_q_ptr != NULL)
X	    {   /* advance ptr in generic queue list */
X	        sq = q->qui->target_q_ptr->queue;
X                q->qui->target_q_ptr = q->qui->target_q_ptr->next;
X	    } /*if*/
X	    else
X	        sq = q->qui->config->queue;
X	    for (found=FALSE, qle=q->qui->queue_list[0]; qle; qle = qle->next)
X		if (found = (strcmp (qle->queue, sq) == 0))
X		    break;
X
X	    if (! found)
X	    {	/* one more pass to get main q */
X		q->qui->search_queue = sq;
X		if (!odd (status = display_queue (q)))
X		    getqui_cleanup (q, status);
X		return;
X	    } /*if*/
X	} /*if*/
X	getqui_cleanup (q, SS$_NORMAL);		/* completed */
X	return;
X    } /*if*/
X
X    if (status == JBC$_NOSUCHQUE)		/* done with error */
X	free (rem_2ll_t (q->qui->queue_list));	/* last entry unused */
X
X    if (odd (status))
X    {
X	q->qui->intervj = q->qui->intervsize = 0;/* reset */
X	status = display_job (q);		/* get jobs in this queue */
X    } /*if*/
X
X    if (!odd (status))
X	getqui_cleanup (q, status);		/* final cleanup @ error */
X} /* display_queue_ast() */
X
Xstatic long
Xdisplay_job (Q_ENTRY * q)
X{
X    register long status;		/* VMS condition code */
X    long prvprv[2];			/* current privs */
X    u_char q_type = q->qui->config->queue_tab_ptr->q_type;
X    u_long searchflags = QUI$M_SEARCH_WILDCARD | QUI$M_SEARCH_ALL_JOBS
X			 | ((q_type & QT_BATCH)
X			     ? QUI$M_SEARCH_BATCH
X			     : (q_type & QT_PRINT)
X				 ? QUI$M_SEARCH_SYMBIONT : 0);
X    register JOB_LIST_ENTRY * jle =		/* new mem for this entry */
X		   (JOB_LIST_ENTRY*) calloc (1, sizeof(JOB_LIST_ENTRY));
X
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(searchflags),	QUI$_SEARCH_FLAGS,	&searchflags, 	 NULL,
X	sizeof(jle->jobname),	QUI$_JOB_NAME,		jle->jobname,	 NULL,
X	sizeof(jle->username), 	QUI$_USERNAME,		jle->username,   NULL,
X	sizeof(jle->size), 	QUI$_JOB_SIZE,	  	&jle->size,  	 NULL,
X	sizeof(jle->jstat), 	QUI$_JOB_STATUS,	&jle->jstat,  	 NULL,
X	sizeof(jle->note),	QUI$_NOTE,		jle->note,	 NULL,
X	sizeof(jle->uic),	QUI$_UIC,		&jle->uic,	 NULL,
X	sizeof(jle->entry),   	QUI$_ENTRY_NUMBER,	&jle->entry,  	 NULL,
X	sizeof(jle->queue),	QUI$_QUEUE_NAME,	jle->queue,	 NULL,
X	sizeof(jle->subm_queue),QUI$_RESTART_QUEUE_NAME,jle->subm_queue, NULL,
X	0, 0, NULL, NULL
X    };
X
X    ins_2ll_t (q->qui->queue_list[1]->job_list, jle);
X    SYS$SETPRV (1, sysprv, 0, prvprv);		/* enable privs */
X    status = SYS$GETQUI (0, QUI$_DISPLAY_JOB, 0, item_list, q->qui->iosb,
X							display_job_ast, q);
X    prvprv[0] = sysprv[0] & ~prvprv[0];		/* restore privs */
X    SYS$SETPRV (0, prvprv, 1, NULL);
X
X    if (!odd (status))
X	free (rem_2ll_t (q->qui->queue_list[1]->job_list));
X
X    return (status);
X} /* display_job() */
X
Xstatic void
Xdisplay_job_ast (Q_ENTRY * q)		/* called when JBC finishes */
X{
X    register QUEUE_LIST_ENTRY  * qle = q->qui->queue_list[1]; /* last elem */
X    register JOB_LIST_ENTRY    * jle = qle->job_list[1];      /* last elem */
X    register long status = q->qui->iosb[0];
X
X    conn_check (q);
X    dbg_msg ("display_job_ast", q, status);
X
X    if ((status == JBC$_NOMOREJOB) || (status == JBC$_NOSUCHJOB))
X    {	/* recursion back to queue-level */
X	free (rem_2ll_t (qle->job_list)); 		/* free unused entry */
X#ifdef NO_EMPTY_LIST
X	if (qle->job_list[0] == NULL)			/* no jobs ? */
X	    free (rem_2ll_t (q->qui->queue_list));	/* free unused entry */
X#endif NO_EMPTY_LIST
X	if (!odd (status = display_queue (q)))
X	    getqui_cleanup (q, status);
X	return;
X    } /*if*/
X
X    if (odd (status))
X    {   /* entry found, look for more */
X 	if (sscanf (jle->note, NOTE_FMT, jle->owner, jle->host) != 2)
X	    *jle->owner = *jle->owner = '\0';
X	if ((jle->selected = (q->qui->select_fun)(q, jle)) != 0)
X	{   /* job meets selection criteria */
X	    /* store # and size of jobs before this one */
X	    jle->intervj    = q->qui->intervj;
X	    jle->intervsize = q->qui->intervsize;
X	    q->qui->intervj = q->qui->intervsize = 0;	/* reset */
X	    if (q->request[0] == REQ_LPQL)
X	        status = display_file (q);	/* get the files in this job */
X	    else
X	        status = display_job (q);	/* continue looping over jobs */
X	} /*if*/
X	else
X	{   /* job does not meet selection criteria */
X	    /* continue looping over jobs */
X	    q->qui->intervj++;			/* save for display */
X	    q->qui->intervsize += jle->size;
X	    free (rem_2ll_t (qle->job_list));	/* free unused entry */
X	    status = display_job (q);
X	} /*else*/
X    } /*if*/
X
X    if (!odd (status))
X	getqui_cleanup (q, status);		/* final cleanup @ error */
X} /* display_job_ast() */
X
Xstatic long
Xdisplay_file (Q_ENTRY * q)
X{
X    register long status;			/* VMS condition code */
X    long prvprv[2];				/* current privs */
X
X    register FILE_LIST_ENTRY * fle =		/* new mem for this entry */
X			(FILE_LIST_ENTRY*) calloc (1, sizeof(FILE_LIST_ENTRY));
X
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(fle->vms_fn), QUI$_FILE_SPECIFICATION, fle->vms_fn, NULL,
X	0, 0, NULL, NULL
X    };
X
X    ins_2ll_t (q->qui->queue_list[1]->job_list[1]->file_list, fle);
X    SYS$SETPRV (1, sysprv, 0, prvprv);		/* enable privs */
X    status = SYS$GETQUI (0, QUI$_DISPLAY_FILE, 0, item_list, q->qui->iosb,
X							display_file_ast, q);
X    prvprv[0] = sysprv[0] & ~prvprv[0];		/* restore privs */
X    SYS$SETPRV (0, prvprv, 1, NULL);
X
X    if (!odd (status))
X	free (rem_2ll_t (q->qui->queue_list[1]->job_list[1]->file_list));
X    return (status);
X} /* display_file() */
X
X
X
Xstatic void
Xdisplay_file_ast (Q_ENTRY * q)		/* called when JBC finishes */
X{
X    register long status = q->qui->iosb[0];
X
X    conn_check (q);
X    dbg_msg ("display_file_ast", q, status);
X
X    if (status == JBC$_NOMOREFILE)
X    {	/* recursion back to job-level */
X	free (rem_2ll_t (q->qui->queue_list[1]->job_list[1]->file_list));
X	if (!odd (status = display_job (q)))
X	    getqui_cleanup (q, status);		/* error cleanup */
X	return;
X    } /*if*/
X
X    if (odd (status))
X	status = display_file (q);		/* get more files */
X
X    if (!odd (status))
X	getqui_cleanup (q, status);		/* final cleanup @ error */
X} /* display_file_ast() */
X
Xstatic long
Xdisplay_entry (Q_ENTRY * q)
X{
X    register long status;		/* VMS condition code */
X    long prvprv[2];			/* current privs */
X    static u_long searchflags = QUI$M_SEARCH_WILDCARD;
X    register JOB_LIST_ENTRY * jle =		/* new mem for this entry */
X		   (JOB_LIST_ENTRY*) calloc (1, sizeof(JOB_LIST_ENTRY));
X
X    /* search spec assembled from *last* entry in sel_tab */
X    register SEL_TAB_ENTRY * st = &q->qui->sel_tab[q->qui->n_sel_tab_entries-1];
X    long size = (st->sel_mode == USER)
X		? strlen (vms_user_tab[st->sel].vms_user)
X		: sizeof (long);
X    long code = (st->sel_mode == USER)
X		? QUI$_SEARCH_USERNAME
X		: QUI$_SEARCH_NUMBER;
X    long addr = (st->sel_mode == USER)
X		? (long) vms_user_tab[st->sel].vms_user
X		: (long) &st->sel;
X
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(searchflags),	QUI$_SEARCH_FLAGS,	&searchflags, 	 NULL,
X        size,			code,			addr,		 NULL,
X	sizeof(jle->jobname),	QUI$_JOB_NAME,		jle->jobname,	 NULL,
X	sizeof(jle->username), 	QUI$_USERNAME,		jle->username,   NULL,
X	sizeof(jle->size), 	QUI$_JOB_SIZE,	  	&jle->size,  	 NULL,
X	sizeof(jle->jstat), 	QUI$_JOB_STATUS,	&jle->jstat,  	 NULL,
X	sizeof(jle->note),	QUI$_NOTE,	        jle->note,       NULL,
X	sizeof(jle->uic),	QUI$_UIC,		&jle->uic,	 NULL,
X	sizeof(jle->entry),   	QUI$_ENTRY_NUMBER,	&jle->entry,  	 NULL,
X	sizeof(jle->queue),	QUI$_QUEUE_NAME,	jle->queue,	 NULL,
X	sizeof(jle->subm_queue),QUI$_RESTART_QUEUE_NAME,jle->subm_queue, NULL,
X	0, 0, NULL, NULL
X    };
X
X    ins_2ll_t (q->qui->queue_list[1]->job_list, jle);
X    SYS$SETPRV (1, sysprv, 0, prvprv);		/* enable privs */
X    status = SYS$GETQUI (0, QUI$_DISPLAY_ENTRY, 0, item_list, q->qui->iosb,
X							display_entry_ast, q);
X    prvprv[0] = sysprv[0] & ~prvprv[0];		/* restore privs */
X    SYS$SETPRV (0, prvprv, 1, NULL);
X
X    if (!odd (status))
X	free (rem_2ll_t (q->qui->queue_list[1]->job_list));
X
X    return (status);
X} /* display_entry() */
X
Xstatic void
Xdisplay_entry_ast (Q_ENTRY * q)		/* called when JBC finishes */
X{
X    register QUEUE_LIST_ENTRY  * qle = q->qui->queue_list[1]; /* last elem */
X    register JOB_LIST_ENTRY    * jle = qle->job_list[1];      /* last elem */
X    register long status = q->qui->iosb[0];
X
X    conn_check (q);
X    dbg_msg ("display_entry_ast", q, status);
X
X    if (  (status == JBC$_NOMOREENT) || (status == JBC$_NOSUCHENT)
X       || (status == JBC$_NOMOREJOB) || (status == JBC$_NOSUCHJOB))
X    {	/* loop over all search-criteria */
X	free (rem_2ll_t (qle->job_list)); 		/* free unused entry */
X
X	if (--q->qui->n_sel_tab_entries > 0)
X	{	/* more selection criteria, continue */
X	    if (!odd (status = display_entry (q)))
X	        getqui_cleanup (q, status);
X	    return;
X	} /*if*/
X	else
X	{   /* finished, final cleanup */
X#ifdef NO_EMPTY_LIST
X	    if (qle->job_list[0] == NULL)		/* no jobs at all ?  */
X	        free (rem_2ll_t (q->qui->queue_list));	/* free unused entry */
X#endif NO_EMPTY_LIST
X	    getqui_cleanup (q, SS$_NORMAL);		/* completed */
X	} /*else*/
X	return;
X    } /*if*/
X
X    if (odd (status))
X    {   /* entry found, look for more */
X
X	/* look if entry already found by other criteria */
X	register u_char found;
X	register JOB_LIST_ENTRY * j;
X	for (found=FALSE, j=qle->job_list[0]; j; j=j->next)
X	    if (found = (jle != j) && (jle->entry == j->entry))
X	 	break;
X
X 	if (found)
X	    free (rem_2ll_t (qle->job_list));	/* free unused entry */
X	else
X	    /* get user/host-info from NOTE; needed by select_fun() */
X	    if (sscanf (jle->note, NOTE_FMT, jle->owner, jle->host) != 2)
X	        *jle->owner = *jle->owner = '\0';
X
X	/* continue looping over jobs */
X	if ((jle->selected = (q->qui->select_fun)(q, jle)) != 0)
X        {
X	    if (q->request[0] == REQ_LPQL)
X	        status = display_file (q);	/* get the files in this job */
X	    else
X	        status = display_entry (q);	/* continue looping over jobs */
X	} /*if*/
X	else
X	{   /* job does not meet selection criteria */
X/*	    free (rem_2ll_t (qle->job_list));	/* free unused entry */
X	    status = display_entry (q);
X	} /*else*/
X    } /*if*/
X
X    if (!odd (status))
X	getqui_cleanup (q, status);		/* final cleanup @ error */
X} /* display_entry_ast() */
END_OF_FILE
if test 21975 -ne `wc -c <'getqui.c'`; then
    echo shar: \"'getqui.c'\" unpacked with wrong size!
fi
# end of 'getqui.c'
fi
if test -f 'config.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'config.c'\"
else
echo shar: Extracting \"'config.c'\" \(26158 characters\)
sed "s/^X//" >'config.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)config.c 1.7 1/30/92 15:34:32";
X/*
X    Configuration / Access check 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 <descrip.h>
X#include <ssdef.h>
X#include <ctype.h>
X#include <stdlib.h>
X#include <iodef.h>
X#include <lnmdef.h>
X#include <psldef.h>
X#include <prvdef.h>
X#include <uaidef.h>
X
X#include "lpd.h"
X
X
X
Xextern int debug;			/* debug-level */
X
X
Xstatic short cmd_mbx_chan = 0;		/* channel for command-mailbox */
Xstatic char dlpd_cmd[VMS_STR];		/* command for DLPD */
Xstatic short cmd_mbx_iosb[4];		/* I/O status block */
X
X
X/* Proxy database */
XHOST_TAB_ENTRY * host_tab = NULL;	/* table of equivalent host names */
XEQUIV_TAB_ENTRY * equiv_tab = NULL;	/* table of equivalent machines */
XUSER_TAB_ENTRY * user_tab = NULL;	/* table of UNIX users */
XVMS_USER_TAB_ENTRY * vms_user_tab = NULL; 	/* table of VMS users */
X
Xshort n_host_tab_entries = 0;		/* current # of entries */
Xshort max_host_tab_entries = 0;		/* max # of entries */
X
Xshort n_equiv_tab_entries = 0;		/* current # of entries */
Xshort max_equiv_tab_entries = 0;	/* max # of entries */
X
Xshort n_user_tab_entries = 0;		/* current # of entries */
Xshort max_user_tab_entries = 0;		/* max # of entries */
X
Xshort n_vms_user_tab_entries = 0;	/* current # of entries */
Xshort max_vms_user_tab_entries = 0; 	/* max # of entries */
X
Xu_char q_types = 0;			/* types of permissions used */
X
X
X/* Hashing of usernames */
X#define HASH_PRIME2   17		/* fixed modulus for collisions */
Xstatic int primes[] =			/* possible table sizes */
X	{  67, 109, 167, 227, 269, 331, 389, 449,
X	  509, 587, 631, 691, 761, 829, 907, 997, 0 };
X
Xstatic short * user_hash_tab = NULL;
Xstatic short user_hash_tab_size = 0;
X
X
X/* Printer database */
XSETUP_TAB_ENTRY * setup_tab = NULL;	/* table of setup strings */
XPARAM_TAB_ENTRY * param_tab = NULL;	/* table of parameter strings */
X
Xshort n_setup_tab_entries = 0;		/* current # of entries */
Xshort max_setup_tab_entries = 0;	/* max # of entries */
X
Xshort n_param_tab_entries = 0;		/* current # of entries */
Xshort max_param_tab_entries = 0;	/* max # of entries */
X
X
X/* printer database */
X
Xextern SETUP_TAB_ENTRY * setup_tab;	/* table of setup strings */
Xextern PARAM_TAB_ENTRY * param_tab;	/* table of parameter strings */
X
Xextern short n_setup_tab_entries;	/* current # of entries */
Xextern short n_param_tab_entries;	/* current # of entries */
X
X
X/* Prototypes for module-local functions */
Xextern short new_host_tab_entry (char * host, short equiv_idx);
Xextern short new_equiv_tab_entry (char * equiv, short first_host_idx);
Xextern u_char get_permission (char * s);
Xextern short new_vms_user_tab_entry (char * vms_user);
Xextern long  get_uai (VMS_USER_TAB_ENTRY * vt, u_char * perm);
Xextern short new_user_tab_entry (char * user,
X		    short host_idx, short equiv_idx, short vms_user_idx, u_char perm);
Xextern void  hash_user_tab (void);
Xextern u_long charsum (char * s);
Xextern short new_param_tab_entry (void);
Xextern short new_setup_tab_entry (void);
Xextern void  free_param_tab (void);
Xextern void  free_setup_tab (void);
X
Xint
Xparse_config_file ()			/* read and build the configuration */
X{
X    int lineno = 0;			/* line number */
X    register FILE * fp;
X    short equiv_idx, vms_user_idx;
X    char *p, *fn, s[8*1024], msg[VMS_STR], user[VMS_STR], host[VMS_STR];
X    int n_print=0, n_batch=0;		/* count permissions */
X    u_char default_perm = DEFAULT_PERM;	/* default perms if unspecified */
X
X    fn = translate_lognam (dlpd$config);
X    if ((fp = fopen (dlpd$config, "r")) == NULL)
X    {
X	perror (dlpd$config);
X	sprintf (msg, "Error opening configuration file %s\r\n(%s)",
X							dlpd$config, fn);
X	op_msg (msg);
X     	return (SS$_NOSUCHFILE);	/* most likely cause */
X    } /*if*/
X
X    sprintf (msg, "Starting to process configuration file %s\r\n(%s)",
X							dlpd$config, fn);
X    op_msg (msg);
X
X    n_equiv_tab_entries    = 0;		/* clear old tables */
X    n_host_tab_entries     = 0;
X    n_user_tab_entries     = 0;
X    n_vms_user_tab_entries = 0;
X    free_setup_tab ();
X    free_param_tab ();
X
X    while ((p = fgets (s, sizeof(s), fp)) != NULL)
X    {
X	register char * t;
X	lineno++;			/* count lines for diagnostics */
X
X	/* strip comments */
X	if ((t = strchr (p, '#')) != NULL)
X	    *t = '\0';
X	if ((t = strchr (p, '!')) != NULL)
X	    *t = '\0';
X
X	if (strlen (p = strtrm(p)) < 1)
X	    continue;				/* short-cut */
X
X	while (strchr ("-\\", p[strlen(p)-1]))	/* continuation line(s) */
X	{
X	    register int l;
X	    if (sizeof(s) - (l=strlen(s)-1) <= 0)
X		break;				/* buffer full */
X	    p[l++] = ' ';			/* insert separator */
X	    if (fgets (s+l, sizeof(s)-l, fp) == NULL)
X		break;				/* last line */
X	    lineno++;
X	    p = strtrm (p);
X	} /*while*/
X
X	if (sscanf (p, "%s", user) < 1)
X	    continue;				/* blank line */
X
X	p = strtrm (p + strlen(user) + 1);	/* skip matched chars */
X	if (islower (user[0]))
X	{	    /* equivalence name for host(s) */
X	    char ok = FALSE;
X	    equiv_idx = new_equiv_tab_entry (user, n_host_tab_entries);
X	    while (sscanf (p, "%s", host) == 1)
X	    {
X		ok = TRUE;
X		p = strtrm (p + strlen(host) + 1);	/* skip matched chars */
X		new_host_tab_entry (host, equiv_idx);
X	    } /*while*/
X
X	    if (!ok)
X	    {
X	    	sprintf (msg, "Syntax error in %s, line %d: no host name given",
X							    dlpd$config, lineno);
X		op_msg (msg);
X	    	--n_equiv_tab_entries;	/* skip erroneouos entry */
X	    } /*if*/
X	    continue;
X	} /*if*/
X
X	if (isupper (user[0]) || (user[0] == '$'))
X	{	    /* equivalence name for user or special name */
X	    char notify, ok = FALSE;
X	    u_char user_perm = 0;		/* permissions at VMS username */
X	    u_char total_perm = 0;		/* accumulated permissions */
X	    /* save # of entries in tables which could be modified
X               to restore them in case of an invalid VMS-username */
X	    short last_n_user_tab = n_user_tab_entries;
X	    short last_n_host_tab = n_host_tab_entries;
X
X	    strupr (user);			/* just to make sure */
X
X	    /* check for 'specials' */
X	    if (strncmp (DUMMY_DEFAULT_PERM, user, strlen (DUMMY_DEFAULT_PERM)) == 0)
X	    {
X		u_char perm = get_permission (user);
X		if (! (perm & PERM_UNSPEC))
X		    default_perm = perm;
X		continue;
X	    } /*if*/
X	    notify = (strcmp (DUMMY_NOTIFY_VMS_USER, user) == 0);
X
X            /* qualifiers indicating BATCH or PRINT permission can
X               (immediately) follow the VMS-username */
X	    if ((user_perm = get_permission (user)) & PERM_UNSPEC)
X		user_perm = default_perm;	/* unspecified, use default */
X
X	    if ((vms_user_idx = new_vms_user_tab_entry (user)) < 0)
X		continue;		/* skip erroneouos entry */
X
X	    while (sscanf (p, "%[^@]@%s", user, host) == 2)
X	    {
X		register short i, m, n;
X		u_char perm;
X		ok = TRUE;		/* at least one entry */
X	    	p = strtrm (p + strlen(host) + strlen(user) + 2);
X
X		/* qualifiers indicating BATCH or PRINT permission can
X                   (immediately) follow the user@host entry, applying
X		   *only* to this host/equiv */
X		perm = get_permission (host);
X		if (perm & PERM_UNSPEC) /* nothing specified, use next level default */
X		    perm = user_perm;
X		total_perm |= perm;	/* inclusive OR of all perms */
X
X		if ((n = find_equiv_idx (host)) >= 0)
X		{
X		    m = new_user_tab_entry (user, -1, n, vms_user_idx, perm);
X		    if (notify)
X			for (i=equiv_tab[n].first_host_idx;
X				host_tab[i].equiv_idx == n; i++)
X			    host_tab[i].notify_user_idx = m;
X		} /*if*/
X	        else
X		{
X		    if ((n = find_host_idx (host)) < 0)
X			n = new_host_tab_entry (host, -1);
X	    	    m = new_user_tab_entry (user, n, -1, vms_user_idx, perm);
X		    if (notify)
X			host_tab[n].notify_user_idx = m;
X		} /*else*/
X	    } /*while*/
X
X	    if (!ok)
X	    {	/* syntax error on *right* side of proxy definitions */
X	        sprintf (msg,
X		"Syntax error in %s, line %d: no username given\r\n(line ignored)",
X							    dlpd$config, lineno);
X		op_msg (msg);
X	    	--n_vms_user_tab_entries;	/* strip erroneouos entry */
X		continue;
X	    } /*if*/
X
X	    if (notify)
X		continue;		/* "special user" */
X
X	    /* try to get UIC + account */
X	    if (! odd(get_uai (&vms_user_tab[vms_user_idx], &total_perm)))
X	    {
X		--n_vms_user_tab_entries;	/* strip erroneouos entry */
X		n_user_tab_entries  = last_n_user_tab;	/* restore */
X		n_host_tab_entries  = last_n_host_tab;	/* restore */
X		continue;
X	    } /*if*/
X	    else
X	    {
X		/* reduce perms to authorized ones */
X		register short n;
X		for (n = last_n_user_tab; n < n_user_tab_entries; n++)
X		    user_tab[n].perm &= total_perm;
X
X		/* statistics only */
X		if (total_perm & PERM_PRINT)
X		    n_print++;
X		if (total_perm & PERM_BATCH)
X		    n_batch++;
X	    } /*else*/
X	    continue;
X	} /*if*/
X
X	if (user[0] == '=')
X	{	    /* setup definition follows */
X	    register short i;
X	    if ((i = new_setup_tab_entry ()) >= 0)
X	    {
X		setup_tab[i].abbrev = strdup (&user[1]);
X		setup_tab[i].setup = strdup (p);
X	    } /*if*/
X	    continue;
X	} /*if*/
X
X	if (user[0] == '/')
X	{	    /* parameter definition follows */
X	    register short i;
X	    if ((i = new_param_tab_entry ()) >= 0)
X	    {
X		param_tab[i].abbrev = strdup (&user[1]);
X		param_tab[i].param = strdup (p);
X	    } /*if*/
X	    continue;
X	} /*if*/
X
X    } /*while*/
X
X    fclose (fp);
X
X    /* post-processing of table for faster access */
X    hash_user_tab ();
X
X    sprintf (msg, "%s successfully read: \r\n  %d proxies (%d PRINT, %d BATCH) for %d hosts with %d users",
X			dlpd$config,
X			(int) n_vms_user_tab_entries,
X			n_print, n_batch,
X			(int) n_host_tab_entries,
X			(int) n_user_tab_entries);
X    op_msg (msg);
X
X    q_types = ((n_print > 0) ? QT_PRINT : 0) | ((n_batch > 0) ? QT_BATCH : 0);
X
X    return (SS$_NORMAL);
X} /* parse_config_file() */
X
X/*
X * Functions for managing the host-database
X */
X
Xstatic short 			/* return index of new host_tab_entry */
Xnew_host_tab_entry (char * host, short equiv_idx)
X{
X    register HOST_TAB_ENTRY * ht;
X
X    if (n_host_tab_entries >= max_host_tab_entries)
X    {	    /* increase table size */
X	host_tab = realloc (host_tab,
X			sizeof(HOST_TAB_ENTRY) * (max_host_tab_entries+=20));
X	assert (host_tab != NULL);
X    } /*if*/
X
X    ht = &host_tab[n_host_tab_entries];
X    strcpy (ht->host, host);		/* insert own name */
X    ht->equiv_idx = equiv_idx;		/* index to equiv name */
X    ht->notify_user_idx = -1;		/* no notify-user yet */
X#ifdef DEBUG
X    if (debug & DBG_CONFIG)
X        fprintf(stderr,"new_host_entry: %2d: %s --> %d\n",
X 				n_host_tab_entries, ht->host, ht->equiv_idx);
X#endif /* DEBUG */
X    return (n_host_tab_entries++);
X} /* new_host_tab_entry() */
X
Xstatic short 			/* return index of new equiv_tab_entry */
Xnew_equiv_tab_entry (char * equiv, short first_host_idx)
X{
X    register EQUIV_TAB_ENTRY * et;
X
X    if (n_equiv_tab_entries >= max_equiv_tab_entries)
X    {	    /* increase table size */
X	equiv_tab = realloc (equiv_tab,
X			sizeof(EQUIV_TAB_ENTRY) * (max_equiv_tab_entries+=6));
X	assert (equiv_tab != NULL);
X    } /*if*/
X
X    et = &equiv_tab[n_equiv_tab_entries];
X    strcpy (et->equiv, equiv);		 /* insert own name */
X    et->first_host_idx = first_host_idx; /* insert index to first equiv host */
X#ifdef DEBUG
X    if (debug & DBG_CONFIG)
X        fprintf(stderr,"new_equiv_entry: %2d: %s\n",
X				n_equiv_tab_entries, et->equiv);
X#endif /*DEBUG*/
X    return (n_equiv_tab_entries++);
X} /* new_equiv_tab_entry() */
X
Xshort 			/* return index of matching entry */
Xfind_host_idx (char * host)
X{
X    register short i;
X    for (i=n_host_tab_entries; --i >= 0; )
X	if (strcmp (host_tab[i].host, host) == 0)
X	    return (i);
X    return (-1);
X} /* find_host_idx() */
X
X
X
Xshort 			/* return index of matching entry */
Xfind_equiv_idx (char * equiv)
X{
X    register short i;
X    for (i=n_equiv_tab_entries; --i >= 0; )
X	if (strcmp (equiv_tab[i].equiv, equiv) == 0)
X	    return (i);
X    return (-1);
X} /* find_equiv_idx() */
X
X/*
X  The routine  equivalent_hosts()  checks the equivalence
X  of two UNIX-machine names. It returns TRUE if
X    - their names match literally
X    - they lead to the SAME ENTRY in the equiv_tab
X*/
X
Xint
Xequivalent_hosts (char * host_a, char * host_b)
X{
X    register int a, b;
X    if (strcmp (host_a, host_b) == 0)
X	return (TRUE);				/* literal match */
X
X    if ((a = find_host_idx (host_a)) < 0)
X	return (FALSE);				/* not in table */
X    if ((a = host_tab [a].equiv_idx) < 0)
X	return (FALSE);				/* not in any equiv group */
X    if ((b = find_host_idx (host_b)) < 0)
X	return (FALSE);				/* not in table */
X    if ((b = host_tab [b].equiv_idx) < 0)
X	return (FALSE);				/* not in any equiv group */
X    return (a == b);				/* same entry in equiv_tab ? */
X} /* equivalent_hosts() */
X
X/*
X * Functions for managing the user-database
X */
X
X/*
X  Function to return a permission mask if the string arg <s> contains
X  qualifiers in the form /Print or /Batch (only first char is checked)
X  When no '/' is contained in the string, PERM_UNSPEC is returned.
X  The substring beginning with the first '/' is stripped from the argument.
X*/
Xstatic u_char
Xget_permission (char * s)
X{
X    u_char perm;
X    register char *p;
X
X    if (s == NULL)
X	return (PERM_UNSPEC);
X    if ((p = strchr (s, '/')) == NULL)
X	return (PERM_UNSPEC);
X    for (perm=0, *p='\0'; p; p=strchr (p, '/'))
X	switch (*(++p))
X	{
X	    case 'B' :	/* BATCH permission */
X	    case 'b' :	perm |= PERM_BATCH;	break;
X
X	    case 'P' :	/* PRINT permission */
X	    case 'p' :	perm |= PERM_PRINT;	break;
X
X	    default  :	/* ignore everything else */
X			break;
X	} /*switch*/
X    return (perm);
X} /* get_permission() */
X
X
Xstatic short			/* allocate a new user_entry, fill it */
Xnew_vms_user_tab_entry (char * vms_user)
X{
X    register VMS_USER_TAB_ENTRY * vt;
X
X    if (n_vms_user_tab_entries >= max_vms_user_tab_entries)
X    {	    /* increase table size */
X	vms_user_tab = realloc (vms_user_tab,
X		sizeof(VMS_USER_TAB_ENTRY) * (max_vms_user_tab_entries+=50));
X	assert (vms_user_tab != NULL);
X    } /*if*/
X
X    vt = &vms_user_tab[n_vms_user_tab_entries];
X    strcpy (vt->vms_user, vms_user);	/* save name */
X
X#ifdef DEBUG
X    if (debug & DBG_CONFIG)
X	fprintf (stderr, "new_vms_user_entry: %2d: %s\n",
X	    		  n_vms_user_tab_entries, vt->vms_user);
X#endif /*DEBUG*/
X    return (n_vms_user_tab_entries++);
X} /* new_vms_user_tab_entry() */
X
Xstatic long		/* get UIC and ACCOUNT_NAME */
Xget_uai (VMS_USER_TAB_ENTRY * vt, u_char * perm)
X{
X    $DYN_DESCRIPTOR (unam_descr, vt->vms_user);		/* $GETUAI argument */
X    long acc_len = 0;			/* length returned here */
X    long status;			/* system service return code */
X    u_long flags = 0xFFFF;		/* UAI-flags */
X    long old_privs[2];			/* privilege vector */
X    static long new_privs[2] = {PRV$M_SYSPRV, 0};
X    long network_access_p = -1;		/* hourly access @ prime days */
X    long network_access_s = -1;		/* hourly access @ sec days */
X    long batch_access_p = -1;		/* hourly access @ prime days */
X    long batch_access_s = -1;		/* hourly access @ sec days */
X    char msg[VMS_STR];			/* for formatting operator msgs */
X    long privs[2];			/* authorized privileges */
X
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(vt->uic),	  UAI$_UIC,	&vt->uic, 	NULL,
X	sizeof(vt->account)-1,	  UAI$_ACCOUNT,	vt->account, 	&acc_len,
X	sizeof(network_access_p), UAI$_NETWORK_ACCESS_P,
X					&network_access_p,	NULL,
X	sizeof(network_access_s), UAI$_NETWORK_ACCESS_S,
X					&network_access_s,	NULL,
X	sizeof(batch_access_p),   UAI$_BATCH_ACCESS_P,
X					&batch_access_p,	NULL,
X	sizeof(batch_access_s),   UAI$_BATCH_ACCESS_S,
X					&batch_access_s,	NULL,
X	sizeof(flags),		  UAI$_FLAGS,	&flags,		NULL,
X	sizeof(privs),		  UAI$_PRIV,    privs,		NULL,
X	0, 0, NULL, NULL
X    };
X
X    SYS$SETPRV (1, new_privs, 0, old_privs);		/* enable privs */
X    status = SYS$GETUAI (0, 0, &unam_descr, item_list, 0, 0, 0);
X    old_privs[0] = new_privs[0] & ~old_privs[0];	/* restore privs */
X    SYS$SETPRV (0, old_privs, 1, NULL);
X
X    vt->account[acc_len] = '\0';	/* terminate string */
X    strtrm (vt->account);		/* get rid of spaces */
X
X    if (odd(status))
X    {
X#ifdef DEBUG
X        if (debug & DBG_CONFIG)
X	    fprintf(stderr,"get_uai: %s [%o,%o]%s%s\n",
X	        	vt->vms_user, vt->uic>>16, vt->uic & 0xffff,
X			(*perm & PERM_PRINT) ? " /PRINT" : "",
X			(*perm & PERM_BATCH) ? " /BATCH" : ""  );
X#endif /*DEBUG*/
X
X        if (flags & UAI$M_DISACNT)	/* user enabled ? */
X        {
X	    sprintf (msg, DISABLED_USER, vt->vms_user);
X	    op_msg (msg);
X#ifdef DEBUG
X            if (debug & DBG_CONFIG)
X	        fprintf(stderr,"Disabled user %s\n",vt->vms_user);
X#endif /*DEBUG*/
X	    return (SS$_NOPRIV);		/* account disabled */
X        } /*if*/
X
X        if (  ((network_access_p & 0x00FFFFFF) == 0x00FFFFFF)
X            &&((network_access_s & 0x00FFFFFF) == 0x00FFFFFF))
X        {   /* no network access at any time */
X	    sprintf (msg, NONETWORK_USER, vt->vms_user);
X	    op_msg (msg);
X#ifdef DEBUG
X            if (debug & DBG_CONFIG)
X	        fprintf (stderr,"No network access for user %s\n",vt->vms_user);
X#endif /*DEBUG*/
X	    return (SS$_NOPRIV);		/* no network access at all */
X        } /*if*/
X
X        if (  (*perm & PERM_BATCH)
X            &&((batch_access_p & 0x00FFFFFF) == 0x00FFFFFF)
X            &&((batch_access_s & 0x00FFFFFF) == 0x00FFFFFF))
X        {   /* no batch access at any time */
X	    *perm &= ~PERM_BATCH;		/* reduce permissions */
X	    sprintf (msg, NOBATCH_USER, vt->vms_user);
X	    op_msg (msg);
X#ifdef DEBUG
X            if (debug & DBG_CONFIG)
X	        fprintf (stderr,"No batch access for user %s\n",vt->vms_user);
X#endif /*DEBUG*/
X	    if (*perm == 0)
X		return (SS$_NOPRIV);
X        } /*if*/
X
X/*
X * There is a way to enforce security by restricting BATCH access to users
X * which have no other privilege bit set than given by the mask MAX_PRIVS_0.
X */
X#ifndef MAX_PRIVS_0
X/* #define MAX_PRIVS_0 (PRV$M_TMPMBX|PRV$M_NETMBX) */	/* restrictive */
X#define MAX_PRIVS_0 (-1)				/* permissive */
X#endif
X#define MAX_PRIVS_1 0
X
X	if (   (*perm & PERM_BATCH)
X	    &&((privs[0] & ~(MAX_PRIVS_0)) || (privs[1] & ~(MAX_PRIVS_1))))
X	{   /* privileged account, disallow BATCH access */
X	    *perm &= ~PERM_BATCH;		/* reduce permissions */
X	    sprintf (msg, BATCHPRIV_USER, vt->vms_user);
X	    op_msg (msg);
X#ifdef DEBUG
X            if (debug & DBG_CONFIG)
X	        fprintf (stderr,
X			"Disallowing batch access for PRIVILEGED user %s\n",
X								vt->vms_user);
X#endif /*DEBUG*/
X	    if (*perm == 0)
X		return (SS$_NOPRIV);
X	} /*if*/
X	return (SS$_NORMAL);
X    } /*if*/
X    else
X    {
X	sprintf (msg, UNKNOWN_USER, vt->vms_user);
X	op_msg (msg);
X#ifdef DEBUG
X 	if (debug & DBG_CONFIG)
X	    fprintf (stderr,"Unknown user %s\n",vt->vms_user);
X#endif /*DEBUG*/
X	return (status);
X    } /*else*/
X} /* get_uai() */
X
Xstatic short
Xnew_user_tab_entry (char * user, short host_idx, short equiv_idx, short vms_user_idx, u_char perm)
X{
X    register USER_TAB_ENTRY * ut;
X
X    if (n_user_tab_entries >= max_user_tab_entries)
X    {	    /* increase table size */
X	user_tab = realloc (user_tab,
X			sizeof(USER_TAB_ENTRY) * (max_user_tab_entries+=50));
X	assert (user_tab != NULL);
X    } /*if*/
X
X    ut = &user_tab[n_user_tab_entries];
X    strcpy (ut->user, user);		/* insert name */
X    ut->perm = perm;			/* insert permissions */
X    ut->host_idx = host_idx;		/* insert idx of host-name */
X    ut->equiv_idx = equiv_idx;		/* insert idx of equiv. host-name */
X    ut->vms_user_idx = vms_user_idx;	/* insert idx of equiv. user-name */
X
X#ifdef DEBUG
X    if (debug & DBG_CONFIG)
X	fprintf(stderr,"new_user_entry: %2d: %s (%d,%d) --> %d%s%s\n",
X				n_user_tab_entries, ut->user,
X				ut->host_idx, ut->equiv_idx,
X				ut->vms_user_idx,
X				(perm & PERM_PRINT) ? " /PRINT" : "",
X				(perm & PERM_BATCH) ? " /BATCH" : ""  );
X#endif /*DEBUG*/
X    return (n_user_tab_entries++);
X} /* new_user_tab_entry() */
X
Xstatic u_long
Xcharsum (char * s)
X{
X    register u_long sum = 0;
X    while (*s)
X	sum += *s++;
X    return (sum);
X} /* charsum() */
X
X
X
Xstatic void
Xhash_user_tab ()
X{
X    register short i, p;
X
X    user_hash_tab_size = 2 * n_user_tab_entries;	/* keep population low */
X    for (i=0; p=primes[i], (p != 0) && (p <= user_hash_tab_size); i++)
X	;						/* make size a prime */
X    if (p != 0)
X	user_hash_tab_size = p;
X
X    user_hash_tab = (short*) realloc (user_hash_tab, sizeof(short) * user_hash_tab_size);
X    assert (user_hash_tab != NULL);
X
X    for (i=user_hash_tab_size; --i >= 0; )
X	user_hash_tab[i] = -1;			/* invalidate entries */
X
X    for (i=n_user_tab_entries; --i >= 0; )
X    {	/* insert entry for each user */
X	register u_short h, d;
X	register u_long s;
X        s = charsum (user_tab[i].user);
X        h = s % user_hash_tab_size;
X        d = s % HASH_PRIME2 + 1;
X	while (user_hash_tab[h] >= 0)		/* collision treatment */
X	    h = (h+d) % user_hash_tab_size;
X	user_hash_tab[h] = i;			/* new entry */
X    } /*for*/
X} /* hash_user_tab() */
X
X/*
X  The function get_user_idx() returns the index into <user_tab> if
X    - <unix_user> has an index to <host_tab> which matches <unix_host>
X    - <unix_user> has an index to <equiv_tab> and one of the hosts
X      in <host_tab> starting at <equiv_tab[].first_host_idx> matches <unix_host>
X  If entries in both <host_tab> and <equiv_tab> exist, an explicit host-entry
X  (in <host_tab>) is preferred.
X
X  -1 is returned on error
X
X  Similarily, get_vms_usr_idx() returns the index into <vms_user_tab>
X*/
X
X
Xshort
Xget_user_idx (char * unix_user, char * unix_host)
X{
X    register u_short h, d;
X    register u_long s;
X    register short u, e, o;
X
X    s = charsum (unix_user);
X    d = s % HASH_PRIME2 + 1;
X
X    /* search for username associated with explicit host entries first */
X    h = s % user_hash_tab_size;
X    for ( ; (u=user_hash_tab[h]) >= 0; h = (h+d) % user_hash_tab_size)
X	if (strcmp (user_tab[u].user, unix_user) == 0)
X	    if ((o = user_tab[u].host_idx) >= 0)
X	    {	    /* check unix_host in its table */
X		if (strcmp (host_tab[o].host, unix_host) == 0)
X		    return (u);
X	    } /*if*/
X
X    /* search for username associated with equivalence group */
X    h = s % user_hash_tab_size;
X    for ( ; (u=user_hash_tab[h]) >= 0; h = (h+d) % user_hash_tab_size)
X	if (strcmp (user_tab[u].user, unix_user) == 0)
X	    if ((e = user_tab[u].equiv_idx) >= 0)
X	    {	    /* check "unix_host" via equiv's */
X		o = equiv_tab[e].first_host_idx;
X		for ( ; (o<n_host_tab_entries) && (host_tab[o].equiv_idx==e); o++)
X		    if (strcmp (host_tab[o].host, unix_host) == 0)
X		        return (u);
X	    } /*if*/
X
X    return (-1);
X} /* get_user_idx() */
X
X
Xshort
Xget_vms_user_idx (char * unix_user, char * unix_host)
X{
X    register short i;
X    if ((i = get_user_idx (unix_user, unix_host)) != -1)
X	return (user_tab[i].vms_user_idx);
X    else
X	return (-1);
X} /* get_vms_user_idx() */
X
X/*
X * Functions for managing printer params and setups
X */
X
Xstatic short 			/* return index of new setup_tab_entry */
Xnew_setup_tab_entry (void)
X{
X    if (n_setup_tab_entries >= max_setup_tab_entries)
X    {	    /* increase table size */
X	setup_tab = realloc (setup_tab, sizeof(SETUP_TAB_ENTRY) * (max_setup_tab_entries+=4));
X	assert (setup_tab != NULL);
X    } /*if*/
X
X    return (n_setup_tab_entries++);
X} /* new_setup_tab_entry() */
X
X
Xstatic void
Xfree_setup_tab ()			/* return dynamic memory */
X{
X    while (--n_setup_tab_entries >= 0)
X    {
X	free (setup_tab[n_setup_tab_entries].abbrev);
X	free (setup_tab[n_setup_tab_entries].setup);
X    } /*while*/
X    n_setup_tab_entries = 0;
X} /* free_setup_tab() */
X
X
X
Xstatic short 			/* return index of new param_tab_entry */
Xnew_param_tab_entry (void)
X{
X    if (n_param_tab_entries >= max_param_tab_entries)
X    {	    /* increase table size */
X	param_tab = realloc (param_tab, sizeof(PARAM_TAB_ENTRY) * (max_param_tab_entries+=6));
X	assert (param_tab != NULL);
X    } /*if*/
X
X    return (n_param_tab_entries++);
X} /* new_param_tab_entry() */
X
X
X
Xstatic void
Xfree_param_tab ()			/* return dynamic memory */
X{
X    while (--n_param_tab_entries >= 0)
X    {
X	free (param_tab[n_param_tab_entries].abbrev);
X	free (param_tab[n_param_tab_entries].param);
X    } /*while*/
X    n_param_tab_entries = 0;
X} /* free_param_tab() */
X
Xchar *
Xtranslate_setup (char * p)	/* return full string for setup abbrev. */
X{
X    register short i;
X    if (p == NULL)
X	return ("");
X    for (i=0; i<n_setup_tab_entries; i++)
X	if (strcmp (p, setup_tab[i].abbrev) == 0)
X	    return (setup_tab[i].setup);
X    return (NULL);
X} /* translate_setup() */
X
X
X
Xchar *
Xtranslate_param (char * p)	/* return full string for parameter abbrev. */
X{
X    register short i;
X    if (p == NULL)
X	return ("");
X    for (i=0; i<n_param_tab_entries; i++)
X	if (strcmp (p, param_tab[i].abbrev) == 0)
X	    return (param_tab[i].param);
X    return (NULL);
X} /* translate_param() */
X
X/*
X *  Dump the user-database to the logfile (stderr).
X *  Order is as found in the hash-table; all indices are shown.
X */
X
Xvoid
Xdump_user_database (void)
X{
X    register short h, u;
X    fprintf (stderr, "\n\f\nDumping user database:\n\n");
X    fprintf (stderr, "Hash-table size = %d, %.1f%% used (%d entries)\n\n",
X		user_hash_tab_size,
X		(float)user_hash_tab_size / (float)n_user_tab_entries,
X		n_user_tab_entries);
X    for (h = 0; h < user_hash_tab_size; h++)
X	if ((u = user_hash_tab[h]) >= 0)
X	{
X	    register short v, i;
X	    v = user_tab[u].vms_user_idx;
X	    fprintf (stderr, "(%3d) --> (%3d) %-8s --> (%3d) %-12s %s%s\n",
X			h, u, user_tab[u].user, v, vms_user_tab[v].vms_user,
X			(user_tab[u].perm & PERM_PRINT) ? " /PRINT" : "",
X			(user_tab[u].perm & PERM_BATCH) ? " /BATCH" : "");
X	    if ((i = user_tab[u].equiv_idx) >= 0)
X	        fprintf (stderr, "   (equiv %2d) %-8s   -->       {%s, ...}\n",
X				    i, equiv_tab[i].equiv,
X				    host_tab[equiv_tab[i].first_host_idx].host);
X	    else
X	    {
X		i = user_tab[u].host_idx;
X	        fprintf (stderr, "   (host  %2d) %s\n", i, host_tab[i].host);
X	    } /*else*/
X	} /*if*/
X    fflush (stderr);
X} /* dump_user_database() */
X
END_OF_FILE
if test 26158 -ne `wc -c <'config.c'`; then
    echo shar: \"'config.c'\" unpacked with wrong size!
fi
# end of 'config.c'
fi
if test -f 'util.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'util.c'\"
else
echo shar: Extracting \"'util.c'\" \(41419 characters\)
sed "s/^X//" >'util.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)util.c 1.19 1/30/92 15:39:27";
X/*
X    Miscellaneouos utility routines 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/* UCX bug ???? */
X#define MAX_SEND_RETRIES    8	   /* retransm. for send_msg/SS$_EXQUOTA */
X
X
X#include <stdio.h>
X#include <unixio.h>
X#include <stat.h>
X#include <string.h>
X#include <descrip.h>
X#include <ctype.h>
X#include <stdlib.h>
X#include <errno.h>
X#include <accdef.h>
X#include <quidef.h>
X
X#include <ssdef.h>
X#include <dvidef.h>
X#include <iodef.h>
X#include <lnmdef.h>
X#include <prcdef.h>
X#include <psldef.h>
X#include <acldef.h>
X#include <prvdef.h>
X#include <jpidef.h>
X#include <opcdef.h>
X#include <brkdef.h>
X#include <msgdef.h>
X#include <jbcmsgdef.h>
X
X
X#include "lpd.h"
X
X
Xextern int debug;			/* debug mask */
X
Xextern long dispatch_ef;		/* event-flag */
X
Xextern Q_ENTRY * q_hdr[2];		/* connection queue */
X
Xextern char * netdev;			/* network device name */
X
Xextern char assertion_msg[];		/* debug info */
X
Xextern char * version_no;		/* version number */
Xextern char * version_str;		/* full version string */
Xextern char * baseline;			/* module info */
X
X
X/* statistics */
Xextern STATISTIC_STRUCT statistics;		/* since last displayed */
Xextern STATISTIC_STRUCT total_statistics;	/* since startup */
X
X/* VMS Queue database (for statistics) */
Xextern QUEUE_TAB_ENTRY * queue_tab;		/* table of VMS queues */
Xextern short n_queue_tab_entries;		/* current # of entries */
X
X/* Local data */
Xstatic u_short mailer_mbx_chan = 0;	/* channel to mailer-mbx */
Xstatic u_long  mailer_pid = 0;		/* PID of subproc */
Xstatic u_short term_mbx_chan   = 0;	/* channel to termination mbx */
Xstatic u_short term_mbx_unit;		/* unit-# of termination mbx */
Xstatic u_short term_mbx_iosb[4];
Xstatic char term_mbx_buf[ACC$C_TERMLEN+ACC$C_JOB_LEN+ACC$C_PRT_LEN];
Xstatic u_char mailer_died = TRUE;	/* flag, set if subproc dies */
X
X
X/* Prototypes for module-local functions */
Xextern void timer_ast (void);
Xextern void sendmsg_ast (Q_ENTRY * q);
Xextern long sendmsg_cleanup (Q_ENTRY * q, long status);
Xextern char * format_queue_statistics (void);
Xextern char * query_queue_tab (void);
Xextern void term_mbx_ast ();
X
Xchar *
Xstrdup (const char * s)		/* return newly allocated duplicate of s */
X{
X    register char * p;
X    if (s==NULL)
X	return (NULL);
X    if ((p = malloc (strlen(s)+1)) != NULL)
X        strcpy (p, s);
X    return(p);
X} /* strdup() */
X
X
X
Xchar *
Xstrupr (char * s)		/* convert string to all upper case */
X{
X    register char * p;
X    if (s == NULL)
X	return (NULL);
X    for (p=s; *p; p++)
X	if ((*p >= 'a') && (*p <= 'z'))
X	    *p += 'A' - 'a';
X    return (s);
X} /* strupr() */
X
X
X
Xchar *
Xstrtrm (char * s)		/* remove leading and trailing space */
X{
X    register char * p;
X    for (p = s+strlen(s)-1; isspace (*p) && (p >= s); --p)
X	*p = '\0';		/* strip space from end */
X    for (p=s; *p && isspace(*p); )
X	p++;			/* strip space from beginning */
X    return (p);
X} /* strtrm() */
X
Xlong
Xcheck_privs ()			/* check privileges */
X{
X    long priv_vect[2] = {DEFAULT_PRIVS | EXTENDED_PRIVS, 0};
X    long status;
X
X    status = SYS$SETPRV (1, priv_vect, 0, NULL);	/* set all privs */
X    priv_vect[0] = EXTENDED_PRIVS;	  /* clear all but default privs */
X    SYS$SETPRV (0, priv_vect, 1, NULL);
X    return (status);
X} /* check_privs() */
X
X
Xlong
Xset_acl (long objtyp, u_short obj_chan, char *aclstr)
X{
X    long status;
X    long contxt = 0;		/* add ACE at beginning of list */
X    long acmode = PSL$C_USER;
X    $DYN_DESCRIPTOR (aclstr_descr, aclstr);
X    char aclent[VMS_STR+1];
X    ITEM_LIST_3 item_list[] =
X    {
X        sizeof(aclent)-1, ACL$C_ADDACLENT, aclent, 0,
X        0, 0, 0, 0
X    };
X
X    status = SYS$PARSE_ACL (&aclstr_descr, item_list, NULL, 0, 0);
X    if (!odd (status))
X        return (status);
X
X    item_list[0].buflen = (u_short) aclent[0];	/* insert "exact" length */
X    status = SYS$CHANGE_ACL (obj_chan, &objtyp, NULL, item_list,
X     			                        &acmode, 0, &contxt);
X    return (status);
X} /* set_acl() */
X
X
Xchar *
Xtranslate_lognam (char * lognam)
X{
X    static $DESCRIPTOR (tabnam_descr, "LNM$FILE_DEV");
X    $DYN_DESCRIPTOR (lognam_descr, lognam);
X    static char return_string[VMS_STR];
X    short len=0;
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof (return_string)-1, LNM$_STRING, return_string, &len,
X	0, 0, 0, 0
X    };
X
X    if (!odd (SYS$TRNLNM (NULL, &tabnam_descr, &lognam_descr, NULL, item_list)))
X	return ("");
X    return_string[len] = '\0';
X    return (return_string);
X} /* translate_lognam() */
X
X
X
Xchar *
Xget_timestamp (void)		/* return ptr to string with current time  */
X{
X    static char s[24];				/* string is returned here */
X    $DESCRIPTOR (s_descr, s);
X    SYS$ASCTIM (NULL, &s_descr, NULL, 0);	/* get current time, ASCII */
X    s[23] = '\0';
X    return (s);
X} /* get_timestamp() */
X
Xstatic char *
X__getmsg (long status, long flags)
X{
X    static char msgtext[VMS_STR];
X    static $DESCRIPTOR (msg, msgtext);
X    short msglen;
X    SYS$GETMSG (status, &msglen, &msg, flags, NULL);
X    msgtext[msglen] = '\0';
X    return (msgtext);
X} /* __getmsg() */
X
X
Xchar *
Xgetmsg (long status)
X{
X    return (__getmsg (status, 0));
X} /* getmsg() */
X
X
Xchar *
Xgetmsgid (long status)		/* return symbolic message ID */
X{
X    return (__getmsg (status, 0xE));
X} /* getmsgid() */
X
Xlong
Xstart_timer ()
X{
X    static $DESCRIPTOR (s_descr, TIMER_DELAY);	/* ASCII time */
X    static u_long bintim[2] = {0, 0};		/* converted value */
X
X    if ((bintim[0] | bintim[1]) == 0)
X    {	/* first call, convert ASCII to binary fmt */
X	long status;
X	if (!odd (status = SYS$BINTIM (&s_descr, bintim)))
X	    return (status);
X    } /*if*/
X
X    return (SYS$SETIMR (0, bintim, timer_ast, NULL));
X} /* start_timer() */
X
X
X
X/*
X * This AST is queued when the timer expires.
X * If the flag mailer_died is set, an attempt to restart the mailer is done.
X * The timeout values for all entries in the queue are incremented.
X * dispatch_ef is set to wake the main process which does the rest.
X */
X
Xstatic void
Xtimer_ast ()
X{
X    Q_ENTRY * q;
X    static int flush_log_ctr = 0, print_stat_ctr = 0;
X
X    start_timer ();				/* restart the timer */
X
X    if (mailer_died)
X    {
X	long status = start_mailer ();		/* resets 'mailer_died' if ok */
X	if (!odd(status))
X	{
X	    char msg[VMS_STR];
X	    sprintf (msg, "Cannot re-start mailer-subprocess %s: %s",
X					dlpd$mailer, getmsgid (status));
X	    op_msg (msg);
X	} /*if*/
X    } /*if*/
X
X    for (q = q_hdr[0]; q; q = q->next)
X    {
X	q->timeout_ctr++;			/* count up */
X#ifdef DEBUG
X	/* show only those which are here for at least the second time */
X        if ((debug & (DBG_NET|DBG_CONN_Q)) && (q->timeout_ctr > 1))
X	    fprintf (stderr, " %08X: %s%d: TO set to %d\n", q,
X				netdev, q->unit, q->timeout_ctr);
X#endif
X    } /*if*/
X
X    if (print_stat_ctr++ >= PRINT_STAT_TO_TICKS)
X    {
X	print_stat_ctr = flush_log_ctr = 0;
X	show_statistics ();
X	flush_logfile ();
X    } /*if*/
X
X    if (flush_log_ctr++ >= FLUSH_LOG_TO_TICKS)
X    {
X	flush_log_ctr = 0;
X	flush_logfile ();
X    } /*if*/
X
X    SYS$SETEF (dispatch_ef);			/* wake main() */
X} /* timer_ast() */
X
Xu_short		/* return unit # associated with channel */
Xget_unit_number (u_short channel)
X{
X    long unit = 0;
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(unit), DVI$_UNIT, &unit, NULL,
X	0, 0, NULL, NULL
X    };
X
X    SYS$GETDVI (0, channel, NULL, item_list, NULL, NULL, NULL, 0);
X    return (unit);
X} /* get_unit_number() */
X
X
X
Xchar *
Xget_devnam (u_short channel)
X{
X    static char devnam[DEV_NAM_LEN];
X    short iosb[4];
X    long status;
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(devnam), DVI$_DEVNAM, devnam, NULL,
X	0, 0, NULL, NULL
X    };
X
X    status = SYS$GETDVI (0, channel, NULL, item_list, iosb, NULL, NULL, 0);
X    return ((odd(status) && odd(iosb[0])) ? devnam : "");
X} /* get_devnam() */
X
X
X
Xlong
Xget_ast_count ()		/* return # of remaining AST quota */
X{
X    long astcnt = 0;		/* remaining ASTs */
X    ITEM_LIST_3 item_list[] =
X    {
X	sizeof(astcnt),	JPI$_ASTCNT, &astcnt, NULL,
X	0, 0, NULL, NULL
X    };
X
X    SYS$GETJPIW (0, NULL, NULL, item_list, NULL, NULL, NULL);
X    return (astcnt);
X} /* get_ast_count() */
X
X/*
X * Re-open the logfile if it is not a terminal device
X * to get the data actually written to it.
X */
Xvoid
Xflush_logfile ()
X{
X    static char fn[VMS_STR];
X    fflush (stderr);
X    if ((isatty (fileno(stderr)) == 0) && (fgetname (stderr, fn, 1) != NULL))
X    {
X        long prvprv[2];				/* current privs */
X	register FILE * fp;
X        static long sysprv[2]  = {PRV$M_SYSPRV, 0};
X        SYS$SETPRV (1, sysprv, 0, prvprv);	/* enable privs */
X	/* close and reopen the file with more processing options */
X    	fp = freopen (fn, "a", stderr, "ctx=rec", "rfm=stmlf", "shr=upi");
X	prvprv[0] = sysprv[0] & ~prvprv[0];	/* restore privs */
X    	SYS$SETPRV (0, prvprv, 1, NULL);
X	if (fp != NULL)
X	{   /* ok, use "new" file for stderr */
X	    stderr = fp;		/* use this one as stderr */
X	    fseek (stderr, 0, 2);	/* seek to EOF (to make sure) */
X	} /*if*/
X	else
X	{   /* error, give message */
X	    register char * m;
X	    sprintf (assertion_msg, "Cannot re-open stderr (%s): %s",
X					fn, (m=strerror(errno))?m:"???");
X	    op_msg (assertion_msg);
X	    perror ("flush_logfile");
X	} /*else*/
X    } /*if*/
X} /* flush_logfile() */
X
X
Xlong
Xdelete_file (FILE_ID * file_id)
X{
X    long status;			/* VMS condition code */
X    long prvprv[2];			/* current privs */
X    static long sysprv[2]  = {PRV$M_SYSPRV, 0};
X    struct FAB fab = cc$rms_fab;	/* RMS */
X    struct NAM nam = cc$rms_nam;	/* RMS */
X
X    if (file_id->t_dvi[0] == '\0')
X	return (SS$_NOSUCHFILE);	/* invalid file_id */
X
X    fab.fab$l_nam = &nam;
X    fab.fab$l_fop = FAB$M_NAM;		/* use NAM block inputs */
X    memcpy (&nam.nam$t_dvi, file_id, sizeof(FILE_ID));
X    SYS$SETPRV (1, sysprv, 0, prvprv);		/* enable privs */
X    if (odd (status = sys$erase (&fab, NULL, NULL)))
X    {
X#ifdef DEBUG
X        if (debug & (DBG_JBC_JOB|DBG_JBC_FILE))
X	     fprintf (stderr, "deleting file...\n");
X#endif DEBUG
X	memset (file_id, 0, sizeof (FILE_ID));	/* invalidate file_id */
X    } /*if*/
X    else
X	fprintf (stderr, "delete_file: %s\n", getmsgid(status));
X    prvprv[0] = sysprv[0] & ~prvprv[0];		/* restore privs */
X    SYS$SETPRV (0, prvprv, 1, NULL);
X
X    return (status);
X} /* delete_file() */
X
X/*
X * Start the mailer-subprocess which will send messages
X * to the remote user calling MAIL via a DCL reading its
X * input from a mailbox.
X */
X
Xlong
Xstart_mailer ()
X{
X    long status;			/* VMS condition code */
X    long mbx_len;			/* size of mbx */
X    short iosb[4];			/* I/O status block */
X    static long privs[2] = {DEFAULT_PRIVS, 0};
X    static $DESCRIPTOR (image_descr, "SYS$SYSTEM:LOGINOUT.EXE");
X    $DYN_DESCRIPTOR (prcnam_descr, dlpd$mailer);
X    static $DESCRIPTOR (input_descr, "");	/* filled in later */
X    static $DESCRIPTOR (nl_descr, "NL:");	/* data sink */
X#ifdef DEBUG
X    $DYN_DESCRIPTOR (out_descr, dlpd$mailer);	/* standard output */
X#endif DEBUG
X
X    /* mailbox for comm with mailer process */
X    if (mailer_mbx_chan != 0)
X	SYS$DASSGN (mailer_mbx_chan);		/* get rid of old mbx */
X    mbx_len = 2048;
X    if (!odd (status=SYS$CREMBX (0, &mailer_mbx_chan, mbx_len, mbx_len, 0xFF02, 0, NULL)))
X	return (status);
X    input_descr.dsc$a_pointer = get_devnam (mailer_mbx_chan);
X    input_descr.dsc$w_length  = strlen (input_descr.dsc$a_pointer);
X
X    /* termination mailbox, used when mailer process dies */
X    if (term_mbx_chan == 0)		/* no term mbx yet (first call) */
X    {
X	mbx_len = ACC$C_TERMLEN+ACC$C_JOB_LEN+ACC$C_PRT_LEN;
X        if (!odd (status=SYS$CREMBX (0, &term_mbx_chan, mbx_len, mbx_len, 0xFF02, 0, NULL)))
X	    return (status);
X	term_mbx_unit = get_unit_number (term_mbx_chan);
X    } /*if*/
X
X    if (odd (status=SYS$CREPRC (&mailer_pid, &image_descr, &input_descr,
X#ifdef DEBUG
X		(debug&DBG_MAILER_OUTPUT) ? &out_descr : &nl_descr,
X#else
X		&nl_descr,
X#endif DEBUG
X	  	&nl_descr, privs, NULL, &prcnam_descr, 6, 0, term_mbx_unit, 0)))
X    {
X	char s[256], *p;
X        sprintf (s, "$ DEFINE SYS$SCRATCH %s", dlpd$scratch);
X        SYS$QIOW (0, mailer_mbx_chan, IO$_WRITEVBLK, iosb,	/* synchronize */
X	         	   NULL, NULL, s, strlen(s), 0, 0, 0, 0);
X        p = "$ SET NOON";
X        SYS$QIOW (0, mailer_mbx_chan, IO$_WRITEVBLK, iosb,	/* synchronize */
X	         	   NULL, NULL, p, strlen(p), 0, 0, 0, 0);
X#ifdef DEBUG
X	if (debug & DBG_MAILER_OUTPUT)
X	{
X            p = "$ SET VERIFY";
X            SYS$QIOW (0, mailer_mbx_chan, IO$_WRITEVBLK, iosb,	/* synchronize */
X	         	   NULL, NULL, p, strlen(p), 0, 0, 0, 0);
X	} /*if*/
X#endif DEBUG
X	fprintf (stderr, "Subprocess %s (%0X) started, mbx is %s\n",
X			dlpd$mailer, mailer_pid, input_descr.dsc$a_pointer);
X	mailer_died = FALSE;
X	memset (term_mbx_buf, 0, sizeof(term_mbx_buf));
X        SYS$QIO (0, term_mbx_chan, IO$_READVBLK, term_mbx_iosb,
X       	     term_mbx_ast, NULL, term_mbx_buf, sizeof(term_mbx_buf), 0, 0, 0, 0);
X    } /*if*/
X    else
X    {
X        SYS$DASSGN (mailer_mbx_chan);
X	mailer_mbx_chan = 0;			/* invalidate channel # */
X    } /*else*/
X    return (status);
X} /* start_mailer() */
X
X
X
Xstatic void
Xterm_mbx_ast ()
X{
X    struct accdef * acc = (struct accdef*) &term_mbx_buf;
X    register long status = term_mbx_iosb[0];
X    char msg[VMS_STR];
X
X#ifdef DEBUG
X    if (   debug && !odd(status)
X	&& (status != SS$_CANCEL)
X	&& (status != SS$_ABORT))
X	fprintf (stderr, "[%s]  term_mbx_ast: QIO completed with %s\n",
X		   		      get_timestamp (), getmsgid(status));
X#endif DEBUG
X
X    if (odd (status))
X    {
X        if (  (acc->acc$w_msgtyp != MSG$_DELPROC)
X	    ||(acc->acc$l_pid    != mailer_pid))
X        {
X	    fprintf (stderr,
X			"[%s]  term_mbx_ast: msgtyp=0x%04X, pid=0x%08X ?!\n",
X	  		get_timestamp(), acc->acc$w_msgtyp, acc->acc$l_pid);
X	    memset (term_mbx_buf, 0, sizeof(term_mbx_buf));
X            SYS$QIO (0, term_mbx_chan, IO$_READVBLK,
X		     term_mbx_iosb, term_mbx_ast, NULL,
X		     term_mbx_buf, sizeof(term_mbx_buf), 0, 0, 0, 0);
X	    return;
X        } /*if*/
X
X	sprintf (msg, "Mailer-Subprocess exited with %s; restarting",
X					getmsgid(acc->acc$l_finalsts));
X	op_msg (msg);
X        mailer_died = TRUE;		/* set flag */
X    } /*if*/
X} /* term_mbx_ast() */
X
X
X
X/*
X * Stop the mailer-subprocess by closing its main input stream,
X * wait for msg in termination mbx.
X */
X
Xvoid
Xstop_mailer ()
X{
X    if (mailer_mbx_chan != 0)
X    {
X	SYS$CANCEL (term_mbx_chan);
X        SYS$QIOW (0, mailer_mbx_chan, IO$_WRITEOF | IO$M_NOW, term_mbx_iosb,
X			       	     NULL, NULL, 0, 0, 0, 0, 0, 0);
X
X	/* just wait for *any* msg in the termination mbx */
X        SYS$QIOW (0, term_mbx_chan, IO$_READVBLK, term_mbx_iosb,
X        	   NULL, NULL, term_mbx_buf, sizeof(term_mbx_buf), 0, 0, 0, 0);
X    } /*if*/
X} /* stop_mailer() */
X
X/*
X * This function formats a MAIL address by analyzing the format string
X * for characteristic patterns indicating either Internet or DECnet
X * style mail addresses. Note that the order of arguments changes!
X */
Xchar *
Xmail_addr (char * fmt, char * user, char * host)
X{
X    static char dst[VMS_STR];
X    char s[VMS_STR];
X    register char *p, *u=NULL, *h=NULL;
X
X    strcpy (s, fmt);		/* make my private copy */
X    for (p=s; *p; p++)
X	if (*p == '%')
X	    switch (*(++p))
X	    {
X	 	case 'U'  :
X	 	case 'u'  :	*p = 's';	/* make it a printf() fmt */
X				u = p;
X				break;
X		case 'H'  :
X	 	case 'h'  :	*p = 's';	/* make it a printf() fmt */
X				h = p;
X	 	case '%'  :	/* escaped '%' forming part of the address */
X				break;
X	 	default   :	/* format error */
X				assert (FALSE);
X				break;
X	    } /*switch*/
X
X    assert (u != NULL);
X    if (h == NULL)		/* allow null hostname */
X        sprintf (dst, s, user);
X    else			/* print args in correct order */
X	sprintf (dst, s, (h<u)?host:user, (h<u)?user:host);
X    return (dst);
X} /* mail_addr() */
X
X
X/*
X  This function parses the mail_format string to have exactly one of
X  %h and %u and no other printf() conversion specifiers.
X  It returns TRUE if the string is ok.
X*/
Xint
Xcheck_mail_format (char * fmt)
X{
X    register char *p, *u=NULL, *h=NULL, *f=NULL, err=0;
X    for (p=fmt; *p; p++)
X	if (*p == '%')
X	    switch (*(++p))
X	    {
X		case 'U'  :
X	 	case 'u'  :	if (u)		/* already seen */
X				    err++;
X				u = p;
X				break;
X		case 'H'  :
X	 	case 'h'  :	if (h)		/* already seen */
X				    err++;
X				h = p;
X	 	case '%'  :	/* escaped '%' forming part of the address */
X				break;
X	 	default   :	/* format error */
X				f = p;
X				err++;
X				break;
X	    } /*switch*/
X    if (err)
X    {
X	char msg[VMS_STR];
X	if ((h && !u) || (!h && !u))
X	{
X	    sprintf (msg,
X		"Format error in %s: at least %%u (optionally also %%h) must be present",
X							      dlpd$mail_format);
X	    op_msg(msg);
X	} /*if*/
X	if (f)
X	{
X	    sprintf (msg, "Format error in %s: no %%%c specifier allowed",
X						      dlpd$mail_format, *f);
X	    op_msg(msg);
X	} /*if*/
X    } /*if*/
X    return (! err);
X} /* check_mail_format() */
X
X
Xvoid
Xsend_mail (char * dst, char * subject, char * text)
X{
X    short iosb[4];			/* I/O status block */
X    char s[2*VMS_STR];
X
X    if (mailer_died)
X	return;				/* no use trying... */
X
X    sprintf (s, "$ MAIL /SUBJECT=\"%s\" /NOSELF SYS$INPUT %s", subject, dst);
X    SYS$QIOW (0, mailer_mbx_chan, IO$_WRITEVBLK | IO$M_NOW, iosb,
X	         	   NULL, NULL, s, strlen(s), 0, 0, 0, 0);
X
X    SYS$QIOW (0, mailer_mbx_chan, IO$_WRITEVBLK | IO$M_NOW, iosb,
X	         	   NULL, NULL, text, strlen(text), 0, 0, 0, 0);
X
X    SYS$QIOW (0, mailer_mbx_chan, IO$_WRITEOF | IO$M_NOW, iosb,
X		       	   NULL, NULL, 0, 0, 0, 0, 0, 0);	/* stop MAIL */
X#ifdef DEBUG
X    if (debug & DBG_MAIL)
X 	fprintf (stderr, "%s\n%s", s, text);
X#endif DEBUG
X} /* send_mail() */
X
X#ifdef OP_DEBUG		/* less annoying */
X
Xvoid
Xop_msg (char * msg)	/* the quiet way */
X{
X    char s[2*VMS_STR];
X    $DESCRIPTOR (msg_descr, s);
X    $DESCRIPTOR (dst_descr, OP_DEBUG);
X    static char prcnam[PRC_NAM_LEN] = {0};	/* initially blank */
X    static long prcnam_len = 0;
X    static ITEM_LIST_3 item_list[] =
X    {
X      sizeof(prcnam)-1, JPI$_PRCNAM, prcnam, &prcnam_len,
X      0, 0, 0, 0
X    };
X
X    /* get process name on first call */
X    if (prcnam_len == 0)
X    {
X        SYS$GETJPIW (0, 0, 0, item_list, 0, 0, 0);
X	prcnam [prcnam_len] = '\0';	/* terminate string */
X	strtrm (prcnam);
X    } /*if*/
X
X    sprintf (s, "\r\n****** Message from %s at %s ******\r\n",
X					prcnam, get_timestamp());
X    strcat (s, msg);
X    strcat (s, "\r\n");
X    msg_descr.dsc$w_length = strlen(s);
X    SYS$BRKTHRUW (0, &msg_descr, &dst_descr, BRK$C_USERNAME, NULL,
X				  0, BRK$M_CLUSTER /* 0 */, 0, 10, NULL, NULL);
X    fprintf (stderr, "[%s]  op_msg: %s\n", get_timestamp(), msg);
X} /* op_msg() */
X
X#else			/* the real thing */
X
Xvoid
Xop_msg (char * msg)
X{
X    static u_char buf[VMS_STR+sizeof(struct OPC)];
X    static $DESCRIPTOR (msg_descr, buf);
X    struct OPC * opc;
X    static char prcnam[PRC_NAM_LEN] = {0};	/* initially blank */
X    static long prcnam_len = 0;
X    static ITEM_LIST_3 item_list[] =
X    {
X      sizeof(prcnam)-1, JPI$_PRCNAM, prcnam, &prcnam_len,
X      0, 0, 0, 0
X    };
X
X    /* get process name on first call */
X    if (prcnam_len == 0)
X    {
X        SYS$GETJPIW (0, 0, 0, item_list, 0, 0, 0);
X	prcnam [prcnam_len] = '\0';	/* terminate string */
X	strtrm (prcnam);
X    } /*if*/
X
X    opc = (struct OPC *) buf;
X
X    fprintf (stderr, "[%s]  op_msg: %s\n", get_timestamp(), msg);
X
X    opc->opc$b_ms_type = OPC$_RQ_RQST;
X    opc->opc$b_ms_target = OPC$M_NM_CENTRL;
X    opc->opc$l_ms_rqstid = 1;
X
X    strcpy (&opc->opc$l_ms_text, prcnam);
X    strcat (&opc->opc$l_ms_text, ": ");
X    strcat (&opc->opc$l_ms_text, msg);
X    msg_descr.dsc$w_length = (char*)(&opc->opc$l_ms_text) - buf
X				+ strlen(&opc->opc$l_ms_text);
X    SYS$SNDOPR (&msg_descr, 0);
X} /* op_msg() */
X
X#endif
X
X/*
X * Functions for managing statistic records
X */
X
Xvoid
Xreset_statistics (STATISTIC_STRUCT * st)
X{
X    strcpy (st->timestamp, get_timestamp());
X    st->req_rej_overload = 0;
X    st->total_requests   = 0;
X    st->req_chk          = 0;
X    st->req_lpr          = 0;
X    st->req_lpqs         = 0;
X    st->req_lpql         = 0;
X    st->req_lprm         = 0;
X    st->bytes_rcvd       = 0;
X    st->files_rcvd       = 0;
X    st->jobs_queued      = 0;
X    st->jobs_rcvd_nok    = 0;
X    st->jobs_dequeued    = 0;
X    st->peak_conns       = 0;
X} /* reset_statistics */
X
X
X
Xvoid
Xadd_statistics (STATISTIC_STRUCT * st1, STATISTIC_STRUCT * st2) /* st1+=st2 */
X{
X    st1->req_rej_overload += st2->req_rej_overload;
X    st1->total_requests   += st2->total_requests;
X    st1->req_chk          += st2->req_chk;
X    st1->req_lpr          += st2->req_lpr;
X    st1->req_lpqs         += st2->req_lpqs;
X    st1->req_lpql         += st2->req_lpql;
X    st1->req_lprm         += st2->req_lprm;
X    st1->bytes_rcvd       += st2->bytes_rcvd;
X    st1->files_rcvd       += st2->files_rcvd;
X    st1->jobs_queued      += st2->jobs_queued;
X    st1->jobs_rcvd_nok    += st2->jobs_rcvd_nok;
X    st1->jobs_dequeued    += st2->jobs_dequeued;
X    if (st2->peak_conns > st1->peak_conns)
X        st1->peak_conns = st2->peak_conns;		/* save maximum */
X} /* add_statistics */
X
X
Xchar *
Xformat_statistics (STATISTIC_STRUCT * st)
X{
X    static char text[21*80];
X    register char * p = text;
X
X    sprintf (p, "\nStatistics since %s:\n", st->timestamp);
X    p += strlen (p);
X    sprintf (p, "  Accepted requests:             %8d\n", st->total_requests);
X    p += strlen (p);
X    sprintf (p, "  Rejected requests (overload):  %8d\n", st->req_rej_overload);
X    p += strlen (p);
X    sprintf (p, "    REQ_CHK:                     %8d\n", st->req_chk);
X    p += strlen (p);
X    sprintf (p, "    REQ_LPR:                     %8d\n", st->req_lpr);
X    p += strlen (p);
X    sprintf (p, "    REQ_LPQS:                    %8d\n", st->req_lpqs);
X    p += strlen (p);
X    sprintf (p, "    REQ_LPQL:                    %8d\n", st->req_lpql);
X    p += strlen (p);
X    sprintf (p, "    REQ_LPRM:                    %8d\n", st->req_lprm);
X    p += strlen (p);
X    sprintf (p, "  Size of data received [kB]:    %8d\n", (st->bytes_rcvd+512)/1024);
X    p += strlen (p);
X    sprintf (p, "  Number of data-files received: %8d\n", st->files_rcvd);
X    p += strlen (p);
X    sprintf (p, "  Number of jobs queued:         %8d\n", st->jobs_queued);
X    p += strlen (p);
X    sprintf (p, "  Number of jobs rcvd incorr.:   %8d\n", st->jobs_rcvd_nok);
X    p += strlen (p);
X    sprintf (p, "  Number of jobs dequeued:       %8d\n", st->jobs_dequeued);
X    p += strlen (p);
X    sprintf (p, "  Peak number of simult. conns:  %8d\n", st->peak_conns);
X    p += strlen (p);
X    *p++ = '\n';
X    *p++ = '\0';
X    return (text);
X} /* format_statistics() */
X
X
X
Xvoid
Xshow_statistics (void)
X{
X    add_statistics (&total_statistics, &statistics);
X    fputs (format_statistics (&statistics), stderr);
X    fputs (format_statistics (&total_statistics), stderr);
X    reset_statistics (&statistics);
X    fputs (format_queue_statistics (), stderr);
X} /* show_statistics() */
X
X
X
Xchar *
Xformat_queue_statistics ()
X{
X#define MAX_QUEUE_STAT_LINES 128
X    register short i, n, n_entries = n_queue_tab_entries;
X    static char s[(MAX_QUEUE_STAT_LINES+3)*48+10], *p;
X    register QUEUE_TAB_ENTRY * qt = queue_tab;
X
X    /* count non-null entries */
X    for (i = n = 0; i < n_entries; i++)
X	if (qt[i].ops != 0)
X	    n++;
X
X    if (n == 0)
X	return ("");
X
X    if (n > MAX_QUEUE_STAT_LINES)
X	n = MAX_QUEUE_STAT_LINES;	/* avoid text buffer overflow */
X
X    strcpy (s, "\n  Queue                        Operations\n");
X    strcat (s,   "  ---------------------------------------\n");
X    p = s + strlen (s);
X    for (i = 0; n > 0; i++)
X	if (qt[i].ops > 0)
X        {
X	    sprintf (p, "  %-32.32s%7d\n", qt[i].queue, qt[i].ops);
X	    p += strlen (p);
X	    --n;
X        } /*if*/
X    strcat (p, "\n");
X    return (s);
X} /* format_queue_statistics() */
X
X/* "Unbuffered-I/O" over network channel */
X
Xint
Xnet_read (u_short channel, char * buf, int count)
X{
X    long status;
X    short iosb[4] = {0, 0, 0, 0};
X
X    if (channel == 0)
X	return (0);		/* already closed */
X
X    status = SYS$QIOW (0, channel, IO$_READVBLK, iosb,
X		      NULL, NULL, buf, count, 0, 0, 0, 0);
X    if (status == SS$_IVCHAN)
X	return (0);		/* already closed ? */
X
X    return (iosb[1]);		/* 0 if error */
X} /* net_read() */
X
X
X
Xint
Xnet_write (u_short channel, char * buf, int count)
X{
X    long status;
X    short iosb[4] = {0, 0, 0, 0};
X
X    if (channel == 0)
X	return (0);		/* already closed */
X
X    status = SYS$QIOW (0, channel, IO$_WRITEVBLK, iosb,
X		       NULL, NULL, buf, count, 0, 0, 0, 0);
X    if (status == SS$_IVCHAN)
X	return (0);		/* already closed ? */
X    assert_oddstat (status);
X    return (iosb[1]);		/* 0 if error */
X} /* net_write() */
X
X/*
X * Asynchronuous output to net.
X *
X * When data in q->msgbuf has been written, q->proc_state is set
X * to SEND_DONE unless the last char was EOF_CHAR. In this case,
X * EOF is indicated to the link (DECnet/gateway: EOF-msg; UCX: IO$_DEACCESS)
X * and proc-state is set to DONE.
X */
X
Xlong
Xsend_msg (Q_ENTRY * q)
X{
X#define MAX_BLK_LEN  1024			/* limit block size */
X    register long status = SS$_NORMAL, len;
X
X    if (q->link_state != CONNECTED)		/* shortcut ? */
X	return (sendmsg_cleanup (q, SS$_LINKDISCON));
X
X    if (q->msgbuf == NULL)
X	return (sendmsg_cleanup (q, SS$_IVADDR));	/* nothing to do ?! */
X
X    if (q->msgoffs >= q->msglen)
X	return (sendmsg_cleanup (q, SS$_IVBUFLEN)); 	/* already finished ?! */
X
X    /* limit message size per QIO invocation */
X    len = q->msglen - q->msgoffs;
X    if (len > MAX_BLK_LEN)
X	len = MAX_BLK_LEN;
X
X    /* send EOF-char as individual message (would not be caught by gateway) */
X    if ((len > 1) && (q->msgbuf[q->msgoffs+len-1] == EOF_CHAR))
X        --len;		/* keep the EOF-char for the next message */
X
X    if ((len == 1) && (q->msgbuf[q->msgoffs+len-1] == EOF_CHAR))
X        return (send_eof (q));		/* network protocol specific */
X    else
X        status = SYS$QIO (0, q->channel, IO$_WRITEVBLK, q->iosb,
X		          sendmsg_ast,  q,
X		          q->msgbuf + q->msgoffs, len, 0, 0, 0, 0);
X
X    if (!odd (status))
X	return (sendmsg_cleanup (q, status)); /* error in system call */
X    return (status);
X} /* send_msg() */
X
X/*
X * If status is non-zero, the corresponding VMS msg is sent to the link.
X * EOF is sent afterwards (by appending an EOF-char to the msg) to
X * close the link.
X */
Xvoid
Xsend_errmsg_eof (Q_ENTRY * q, long status)
X{
X    if (q->msgbuf != NULL)
X	free (q->msgbuf);			/* dealloc any "old" buffer */
X    q->proc_state = SEND_ACT;
X    q->msgbuf = (char *) malloc (VMS_STR+2);
X    assert_showval (q->msgbuf != NULL, VMS_STR+2);
X    if (status != 0)
X        sprintf (q->msgbuf, "%s\n%c", getmsg (status), EOF_CHAR);
X    else
X	sprintf (q->msgbuf, "%c", EOF_CHAR);
X    q->msglen = strlen (q->msgbuf);
X    q->msgoffs = 0;
X    send_msg (q);		/* start sending */
X} /* send_errmsg_eof() */
X
Xstatic void
Xsendmsg_ast (Q_ENTRY * q)
X{
X    register long status = q->iosb[0];
X    register long len    = q->iosb[1];
X    if (odd (status))
X    {   /* last write ok, check for more */
X	if (len <= 0)
X	{
X	    fprintf (stderr, "[%s]  sendmsg_ast: %d bytes sent ?!\n",
X						  get_timestamp (), len);
X	    len = 1;				/* avoid endless loop */
X	} /*if*/
X	q->msgoffs += len;			/* advance pointer */
X        if (q->msglen - q->msgoffs > 0)
X	{
X	    /* it seems that there are too many I/O-ops when I call
X               send_msg directly from within this AST !?! */
X	    q->retry_counter = 0;		/* reset error count */
X	    send_msg (q);			/* more to do */
X	    return;				/* will come back.. (or not) */
X	} /*if*/
X    } /*if*/
X
X
X#ifdef DEBUG
X    if (!odd (status))
X	fprintf (stderr, "[%s]  sendmsg_ast: %s%u: len=%d/%d, offs=%d, last=%02X, %s\n",
X		get_timestamp(), netdev, q->unit, q->msglen, len, q->msgoffs,
X		q->msgbuf[q->msglen-1], getmsgid(status));
X#endif DEBUG
X
X    if (status == SS$_EXQUOTA)			/* UCX - problem ?!? */
X	if (q->retry_counter++ < MAX_SEND_RETRIES)
X	{
X	    send_msg (q);
X	    return;
X    	} /*if*/
X
X    /* error, do cleanup */
X    if (q->msgbuf[q->msglen-1] == EOF_CHAR)
X	send_eof (q);				/* try sending EOF */
X    else
X	sendmsg_cleanup (q, status);
X} /* sendmsg_ast() */
X
X/*
X * Final cleanup after send_msg() unless message ends in EOF_CHAR
X */
X
Xstatic long
Xsendmsg_cleanup (Q_ENTRY * q, long status)
X{
X#ifdef DEBUG
X    if (! odd (status) && debug)
X	fprintf (stderr, "[%s]  sendmsg_cleanup: %s\n%s",
X			get_timestamp(), getmsgid (status), fmt_q_entry(q));
X#endif DEBUG
X    q->proc_state = (status == SS$_LINKDISCON) ? DONE : SEND_DONE;
X    SYS$SETEF (dispatch_ef);		/* wake scheduler */
X    return (status);
X} /* sendmsg_cleanup() */
X
X/*
X * Function for remote query/debugging
X *
X * This is a function invoked upon an UNDOCUMENTED (big) entry number to
X *  % lpq -l
X * and allows to get logfile, configuration and other debug items without
X * logging on to the VAX on which the server is running.
X *
X * The output is a dynamic string attached to q->msgbuf, with q->msglen
X * and q->offset set accordingly. The entry # supplied can be found in
X * q->qui->sel_tab[0].sel
X */
X
X/* "entry-numbers" to invoke the special services (almost random numbers) */
X#define QUERY_STAT      68910230		/* show statistics */
X#define QUERY_CONFIG    67305673		/* show config-file */
X#define QUERY_LOG       54635936		/* show logfile */
X#define QUERY_CONNS     73659310		/* dump connection queue */
X#define QUERY_ACCNT     64957041		/* show process accounting */
X#define QUERY_QUEUES    56963751		/* show VMS queues */
X#define QUERY_VERSION 	75549784		/* show version/baseline */
X
Xlong
Xdo_remote_query (Q_ENTRY * q)
X{
X    register char *p, *s, *t;
X    static char fn[VMS_STR];
X    q->msgoffs = 0;
X    switch (q->qui->sel_tab[0].sel)
X    {
X	case QUERY_ACCNT    :   /* get process accounting info */
X				p = s = format_proc_info();
X				if (s == NULL)
X				    return (JBC$_NOSUCHENT);
X				s += strlen(s);
X				*s++ = EOF_CHAR;	/* append EOF-char */
X				*s = '\0';
X				q->msgbuf = strdup (p);
X				q->msglen = strlen (q->msgbuf);
X				return (SS$_NORMAL);
X				break;
X
X      	case QUERY_CONNS    :	/* show connection queue */
X				p = s = fmt_q();
X				s += strlen(s);
X				*s++ = EOF_CHAR;	/* append EOF-char */
X				*s = '\0';
X				q->msgbuf = strdup (p);
X				q->msglen = strlen (q->msgbuf);
X				return (SS$_NORMAL);
X
X	case QUERY_STAT     : 	/* show statistics */
X				p = format_statistics (&statistics);
X				/* call again later *after* copying (static string!) */
X				t = format_queue_statistics ();
X				q->msgbuf = s = (char*)
X					malloc (2*strlen(p)+strlen(t)+80);
X				strcpy (s, p);
X				p = format_statistics (&total_statistics);
X				strcat (s, p);
X				strcat (s, t);
X				s += strlen(s);
X				*s++ = EOF_CHAR;	/* append EOF-char */
X				*s = '\0';
X				q->msglen = strlen (q->msgbuf);
X				return (SS$_NORMAL);
X
X	case QUERY_CONFIG   :	/* display config-file */
X				q->msgbuf = s = file_to_string (dlpd$config);
X				if (s == NULL)
X				    return (JBC$_NOSUCHENT);
X				s += strlen(s);
X				*s++ = EOF_CHAR;	/* append EOF-char */
X				*s = '\0';
X				q->msglen = strlen (q->msgbuf);
X				return (SS$_NORMAL);
X
X	case QUERY_LOG      :	/* display logfile */
X				if (isatty (fileno (stderr)))
X				    return (JBC$_NOSUCHENT);
X				if (fgetname (stderr, fn, 1) == NULL)
X				    return (JBC$_NOSUCHENT);
X				flush_logfile ();
X				q->msgbuf = s = file_to_string (fn);
X				if (s == NULL)
X				    return (JBC$_NOSUCHENT);
X				s += strlen(s);
X				*s++ = EOF_CHAR;	/* append EOF-char */
X				*s = '\0';
X				q->msglen = strlen (q->msgbuf);
X				return (SS$_NORMAL);
X
X	case QUERY_QUEUES   :   /* get list of VMS queues */
X				q->msgbuf = s = format_queue_tab ();
X				if (s == NULL)
X				    return (JBC$_NOSUCHENT);
X				s += strlen(s);
X				*s++ = EOF_CHAR;	/* append EOF-char */
X				*s = '\0';
X				q->msglen = strlen (q->msgbuf);
X				return (SS$_NORMAL);
X
X	case QUERY_VERSION  :	/* get version number */
X				q->msgbuf = s = malloc (strlen(baseline)+2);
X				if (s == NULL)
X				    return (JBC$_NOSUCHENT);
X				strcpy (s, baseline);
X				s += strlen(s);
X				*s++ = EOF_CHAR;	/* append EOF-char */
X				*s = '\0';
X				q->msglen = strlen (q->msgbuf);
X				return (SS$_NORMAL);
X
X       	default             :   /* unrecognized request */
X				return (JBC$_NOSUCHENT);
X    } /*switch*/
X} /* do_remote_query() */
X
X/*
X * Read a file into a (dynamically) allocated string
X *  (with a little space at the end)
X */
X
Xstatic char *
Xfile_to_string (char * fn)
X{
X    register FILE * fp;
X    register char *p, *q;
X    register long size, len, rem;
X    stat_t st;
X    long prvprv[2];				/* current privs */
X    static long sysprv[2]  = {PRV$M_SYSPRV, 0};
X
X    /* use SYSPRV to make file access easier */
X    SYS$SETPRV (1, sysprv, 0, prvprv);		/* enable privs */
X    prvprv[0] = sysprv[0] & ~prvprv[0];
X    /* use shared record mode access */
X    if ((fp = fopen (fn, "r", "ctx=rec", "shr=put")) == NULL)
X    {
X        SYS$SETPRV (0, prvprv, 1, NULL);	/* reset privs */
X	return (NULL);
X    } /*if*/
X    fstat (fileno(fp), &st);			/* get file-size */
X    SYS$SETPRV (0, prvprv, 1, NULL);		/* reset privs */
X    size = st.st_size + 260;			/* leave room for filename... */
X    p = q = (char*) malloc (size);		/* allocate memory */
X    if (p == NULL)
X    {
X	fprintf (stderr, "[%s]  file_to_string: malloc(%d) failed\n",
X						get_timestamp(), size);
X	fclose (fp);
X	return (NULL);
X    } /*if*/
X    fgetname (fp, p, 1);			/* get filename */
X    p   += strlen (p);
X    *p++ = '\n';
X    *p   = '\0';
X    len  = rem = size;
X    while ((len > 0) && (rem > 0) && (fgets (p, rem, fp) != NULL))
X    {
X	len = strlen (p);
X	p   += len;
X	rem -= len;
X    } /*if*/
X    fclose (fp);
X    return (q);
X} /* file_to_string() */
X
XPROC_INFO *
Xget_proc_info ()
X{
X    register long status;
X    int prcnam_len = 0, nodename_len = 0, username_len = 0;
X    static PROC_INFO pi;
X
X/* not (yet) in SYS$LIBRARY:JPIDEF.H */
X#ifndef JPI$_NODENAME
X#define JPI$_NODENAME 809
X#endif
X
X#ifndef JPI$_STS2
X#define JPI$_STS2 808
X#endif
X
X    ITEM_LIST_3 item_list[] =
X    {
X      sizeof(pi.prcnam)-1,  JPI$_PRCNAM,    pi.prcnam,         &prcnam_len,
X      sizeof(pi.username)-1,JPI$_USERNAME,  pi.username,       &username_len,
X      sizeof(pi.nodename)-1,JPI$_NODENAME,  pi.nodename,       &nodename_len,
X      sizeof(long),         JPI$_PID,       &pi.pid,           0,
X      sizeof(pi.authprv),   JPI$_AUTHPRIV,  &pi.authprv,       0,
X      sizeof(pi.curprv),    JPI$_CURPRIV,   &pi.curprv,        0,
X      sizeof(long),         JPI$_BIOLM,     &pi.biolm,         0,
X      sizeof(long),         JPI$_BIOCNT,    &pi.biocnt,        0,
X      sizeof(long),         JPI$_DIOLM,     &pi.diolm,         0,
X      sizeof(long),         JPI$_DIOCNT,    &pi.diocnt,        0,
X      sizeof(long),         JPI$_BYTLM,     &pi.bytlm,         0,
X      sizeof(long),         JPI$_BYTCNT,    &pi.bytcnt,        0,
X      sizeof(long),         JPI$_FILLM,     &pi.fillm,         0,
X      sizeof(long),         JPI$_FILCNT,    &pi.filcnt,        0,
X      sizeof(long),         JPI$_TQLM,      &pi.tqlm,          0,
X      sizeof(long),         JPI$_TQCNT,     &pi.tqcnt,         0,
X      sizeof(long),         JPI$_ENQLM,     &pi.enqlm,         0,
X      sizeof(long),         JPI$_ENQCNT,    &pi.enqcnt,        0,
X      sizeof(long),         JPI$_ASTLM,     &pi.astlm,         0,
X      sizeof(long),         JPI$_ASTCNT,    &pi.astcnt,        0,
X      sizeof(long),         JPI$_ASTACT,    &pi.astact,        0,
X      sizeof(long),         JPI$_ASTEN,     &pi.asten,         0,
X      sizeof(long),         JPI$_DIRIO,     &pi.dirio,         0,
X      sizeof(long),         JPI$_BUFIO,     &pi.bufio,         0,
X      sizeof(pi.cputim),    JPI$_CPUTIM,    &pi.cputim,	       0,
X      sizeof(pi.logintim),  JPI$_LOGINTIM,  &pi.logintim,      0,
X      sizeof(long),	    JPI$_PAGFILCNT, &pi.pagfilcnt,     0,
X      sizeof(long),	    JPI$_PGFLQUOTA, &pi.pgflquota,     0,
X      sizeof(long),	    JPI$_WSSIZE,    &pi.wssize,        0,
X      sizeof(long),	    JPI$_WSPEAK,    &pi.wspeak,        0,
X      sizeof(long),	    JPI$_WSAUTHEXT, &pi.wsauthext,     0,
X      sizeof(long),	    JPI$_VIRTPEAK,  &pi.virtpeak,      0,
X      0,                    0,              0,                 0
X    };
X
X    /* get information about current process */
X    status = SYS$GETJPIW (0, 0, 0, item_list, 0, 0, 0);
X
X    if (odd (status))
X    {
X     	pi.prcnam[prcnam_len] = '\0';
X     	pi.username[username_len] = '\0';
X     	pi.nodename[nodename_len] = '\0';
X	return (&pi);
X    } /*if*/
X    else
X	return (NULL);
X} /* get_proc_info() */
X
X
X/* function to get current process info to a static string */
Xchar *
Xformat_proc_info ()
X{
X    static char s[27*64];		/* text formatted into here */
X    static char t[24];			/* time string is put in here */
X    static $DESCRIPTOR (t_descr, t);
X    register char *p=s;
X    PROC_INFO * pi;
X    static long mult = 100000;		/* 10ms ---> 100ns */
X    long bintim[2];
X    short timlen;
X
X    if ((pi = get_proc_info ()) == NULL)
X	return (NULL);
X
X    sprintf (p, "Process \"%s\" on %s\n           (pid %08X) at %s\n",
X			pi->prcnam, pi->nodename, pi->pid, get_timestamp());
X    p += strlen (p);
X
X    SYS$ASCTIM (&timlen, &t_descr, &pi->logintim, 0);	/* convert to ASCII */
X    t[timlen] = '\0';
X    sprintf (p, "Login time                   %s\n", t);
X    p += strlen (p);
X    sprintf (p, "Username                     %s\n", pi->username);
X    p += strlen (p);
X
X    /* convert CPU time from 10ms ticks to std format */
X    bintim[0] = -pi->cputim;
X    bintim[1] = -1;
X    LIB$MULT_DELTA_TIME (&mult, &bintim);	/* 10ms ticks --> quadword */
X    SYS$ASCTIM (&timlen, &t_descr, &bintim, 0);	/* convert to ASCII */
X    t[timlen] = '\0';
X    sprintf (p, "CPU time                     %s\n", strtrm (t));
X    p += strlen (p);
X
X    sprintf (p, "Authorized Privileges        %08X %08X\n", pi->authprv[1], pi->authprv[0]);
X    p += strlen (p);
X    sprintf (p, "Current Privileges           %08X %08X\n", pi->curprv[1], pi->curprv[0]);
X    p += strlen (p);
X    sprintf (p, "Buffered I/O count/limit     %d/%d\n", pi->biocnt, pi->biolm);
X    p += strlen (p);
X    sprintf (p, "Direct I/O count/limit       %d/%d\n", pi->diocnt, pi->diolm);
X    p += strlen (p);
X    sprintf (p, "Byte count/limit             %d/%d\n", pi->bytcnt, pi->bytlm);
X    p += strlen (p);
X    sprintf (p, "File count/limit             %d/%d\n", pi->filcnt, pi->fillm);
X    p += strlen (p);
X    sprintf (p, "Timer queue count/limit      %d/%d\n", pi->tqcnt,  pi->tqlm);
X    p += strlen (p);
X    sprintf (p, "Enqueue count/limit          %d/%d\n", pi->enqcnt, pi->enqlm);
X    p += strlen (p);
X    sprintf (p, "AST count/limit              %d/%d\n", pi->astcnt, pi->astlm);
X    p += strlen (p);
X    sprintf (p, "Active/enabled ASTs          %d/%d\n", pi->astact, pi->asten);
X    p += strlen (p);
X    sprintf (p, "Buffered/direct I/O total    %d/%d\n", pi->dirio,  pi->bufio);
X    p += strlen (p);
X    sprintf (p, "Working set size/peak/ext    %d/%d/%d\n",
X				pi->wssize, pi->wspeak, pi->wsauthext);
X    p += strlen (p);
X    sprintf (p, "Peak virtual size            %d\n", pi->virtpeak);
X    p += strlen (p);
X    sprintf (p, "Page file count/quota        %d/%d\n",
X				pi->pagfilcnt, pi->pgflquota);
X
X    return (s);
X} /* format_proc_info() */
X
Xchar *
Xfmt_queue_flags (u_long qfl)		/* returns ptr to static string */
X{
X    static char s[12*20];		/* text is returned here */
X    register char *p = s;
X    register u_long m = 1;
X
X#define APP(s) { if (*p) strcat (p, ", "); strcat (p, s); }
X
X    *p = '\0';
X    while (m != 0)
X    {
X	switch (qfl & m)
X	{
X	    case QUI$M_QUEUE_FILE_BURST		: APP("FILE_BURST");	break;
X	    case QUI$M_QUEUE_FILE_BURST_ONE	: APP("FILE_BURST_ONE");break;
X	    case QUI$M_QUEUE_FILE_FLAG		: APP("FILE_FLAG");	break;
X	    case QUI$M_QUEUE_FILE_FLAG_ONE	: APP("FILE_FLAG_ONE");	break;
X	    case QUI$M_QUEUE_JOB_BURST 		: APP("JOB_BURST");	break;
X	    case QUI$M_QUEUE_JOB_FLAG	 	: APP("JOB_FLAG");	break;
X	    case QUI$M_QUEUE_GENERIC		: APP("GENERIC");	break;
X/*
X	    case QUI$M_QUEUE_GENERIC_SELECTION	: APP("GENERIC_SELECTION");break;
X*/
X	    case QUI$M_QUEUE_BATCH		: APP("BATCH");		break;
X	    case QUI$M_QUEUE_PRINTER		: APP("PRINTER");	break;
X	    case QUI$M_QUEUE_TERMINAL		: APP("TERMINAL");	break;
X	    default /* any others */		: 			break;
X	} /*switch*/
X	m <<= 1;
X    } /*while*/
X    return (s);
X} /* fmt_queue_flags() */
X
Xchar *
Xfmt_vms_queue_entry (QUEUE_TAB_ENTRY * qte)	/* returns ptr to static string */
X{
X    static char s[1024];
X    register char * p= s;
X    register TARGET_Q_ENTRY * t;
X
X    sprintf (s, "%s\n  %s\n", qte->queue, fmt_queue_flags (qte->queue_flags));
X    for (t=qte->targets[0]; t; t=t->next)
X    {
X	p += strlen(p);
X	sprintf (p, "  ---> %s\n", t->queue);
X    } /*for*/
X    strcat (p, "\n");
X    return (s);
X} /* fmt_vms_queue_entry() */
X
X
X
Xchar *
Xformat_queue_tab ()		/* returns ptr to *dynamic* string */
X{
X    register short n;
X    char * s = (char*) malloc (n_queue_tab_entries * 512);
X    register char * p;
X
X    if (s == NULL)
X	return (NULL);				/* malloc() failed */
X
X    for (n=0, p=s; n < n_queue_tab_entries; n++)
X    {
X	strcat (p, fmt_vms_queue_entry (&queue_tab[n]));
X	p += strlen(p);
X    } /*for*/
X
X    return (s);
X} /* format_queue_tab() */
END_OF_FILE
if test 41419 -ne `wc -c <'util.c'`; then
    echo shar: \"'util.c'\" unpacked with wrong size!
fi
# end of 'util.c'
fi
echo shar: End of archive 3 \(of 6\).
cp /dev/null ark3isdone
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
