[comp.sources.unix] v11i025: 3Bnet utilities and printer spooler, Part01/02

rs@uunet.UU.NET (Rich Salz) (08/28/87)

Submitted-by: hayes%wizard@sdcsvax.ucsd.edu (James Hayes)
Posting-number: Volume 11, Issue 25
Archive-name: 3bnet/Part01

This package provides a set of tools, documentation on security, and a
network printer spooler for a set of machines running 3Bnet.
These have only been tested on a 3b2/310 running SVR2.0x and I don't make
any guarantees otherwise.  (Besides the guarantee that it is really bad
code... Commented, but bad.)

As usual, complain if something doesn't work... I'll try to help as much
as possible, but I can't circumvent acts of god.

Jim Hayes, University of California at San Diego.

BITNET: hayes%sdcsvax@WISCVM.BITNET
ARPA:	hayes@sdcsvax.ucsd.edu
UUCP:   {pick one close to berkeley}!sdcsvax!hayes

------------------SNIPPITY-------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
 
echo x - Makefile
sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//'
#
#
CFLAGS = -g
#
all:	spool 

spool:	spool.o spool.defs
	cc spool.o -o spool

clean:	
	rm *.o core
@//E*O*F Makefile//
chmod u=rw,g=r,o=r Makefile
 
echo x - spool.c
sed 's/^@//' > "spool.c" <<'@//E*O*F spool.c//'
#include <stdio.h>
#include <sys/utsname.h>  		/* System naming fetching library */
#include <string.h>			/* Until Jim can learn strings... */
#include <signal.h>
#include "spool.h"			/* spool.c #defines. */

/* **************************************************************
print:
  spool -pprinter_name -b[banner] {files}

query:
  spool -pprinter_name -q {<--query.}

list available printer:
  spool -l

(A -d may be added to turn debugging on.)

Author:  James Hayes.  (hayes@sdcsvax.ucsd.edu)
Log: 
        Version 1.5 (Added option for banner.)		13-Feb-86 
	Version 1.6 (Fixed things/cleaned up the code.)	19-Feb-86
	Version 2.0 (Frozen Revision)			03-Mar-86

**************************************************************** */

   char *printer_name[20],	/* The printer name known to eecs70 students.*/
        *printer_node[20],	/* The node name of the receiving printer. */
	*restriction[2];	/* The restriction flag. */

    int validate_printer(),     /* Verify printer exists. */
	list_printers(),	/* List available printers */
	printer_status(),	/* Request remote printer status */
	timeout(),		/* Wait for file to arrive. */
	student,		/* Non-zero if "student" logged in */
	debug;			/* Non-zero if debugging enabled. */

/* ********************************************************************** */ 

main(argc,argv)
int argc;
char *argv[];

{
   FILE *infd, *outfd, *fopen();

   extern int errno,		/* What else? */
  	optind,			/* For use with getopt (2) */
	opterr,    		/* Ditto */
	debug,			/* debug flag */
        student,		/* Non zero if student running program. */
	list_printers(),
	validate_printer();


   extern char *optarg,		/* Getopt option target string */
	*printer_name[20],	/* The printer name known to ee70 students.*/  
        *printer_node[20],	/* The node name of the receiving printer. */
	*restriction[2];	/* The restriction flag. */

   char *mktemp(),		/* MaKe a TeMPorary unique file name */
	*outname,		/* The temporary name itself. */
	*template[150],		/* The template used to create the temp name.*/
	*remote_command[255],   /* The remote command holder to the remote machine */
	*requested_printer, 	/* The printer requested by the student.*/
	*banner,		/* The truncated (10chars) banner name. */
	*login_name[20];	/* Login name of calling user. */
  	 
   int in_char,			/* Current stream input character */
       current_opt,		/* Pointer the next argv[] option. getopt(2) */
       error,			/* Non zero if command line errors found.*/
       prflag,			/* Non zero if a printer was specified */
       list_available,		/* Non zero if a printer list was asked for */
       query,			/* Non zero if printer status requested	*/
       banflag,			/* Non zero if a banner was specified */
       exec_status,		/* Exit status of the system call 'system()' */
       i; 

   struct utsname name;		/* Structure for the uname() system call */

   list_available=		/* Initialize everything. */
   debug=
   query=
   error=
   prflag=
   student=
   banflag=0;   

   uname(&name);					/* Get the system name. */
   sprintf(template,DEFAULT_DIR,name.sysname);		/* Make the temporary file templaye. */

   cuserid(login_name);					/* Get the login name of the user */
   if ( (strcmp(login_name,"student"))==0 ) student++;  /* If student, set the student flag */

   if ( (strcmp(argv[3],"@"))==0 ) return_state(argv[1],argv[2]);   /* Invoke the return mode of spool ? */

   /* Loop through all possible options and set flags according to what
      is chosen.  If an erronious command line option is choosen, then
      error is set to non-zero and the spooling stops.   */

   while  ((current_opt=getopt(argc,argv,"p:b:sld")) !=EOF)
	     switch (current_opt)
	      {
	       case 'p':		/* Printer? */
			prflag++;
			requested_printer=optarg;
			break;
 	       case 'b':		/* Banner name? */
			banflag++;
			banner=optarg;
			break;
	       case 's': 		/* Query printer? */
			query++;
			break;
	       case 'd':		/* Debug flag? */
			debug++;
			break;
	       case 'l':		/* List printers? */
			list_available++;
			break;
	       case '?':		/* Bad option? */
			error++;
			break;
	      }


DEBUG Template for making files %s\n",template);
DEBUG Student UID=%d, student flag=%d\n",getuid(),student);

      if ( (query) && (prflag))						/* Got -s and a printer name? */
	  if ( (validate_printer(requested_printer))==0 )  		/* Valid name? */
	    {
	      fprintf(stderr,"spool: printer %s does not exist.\n", requested_printer);
	      fprintf(stderr, "       To see supported printers, use: spool -l\n"); 
              exit(1);
	    }
	  else								/* Everything O.K... Do query. */
	    {
	      query_request(printer_node);
	      exit(0);
 	    }

      if (query)	/*Since -s is a strange option, be polite. */
	{
	  fprintf(stderr,"spool: Please request a printer\n");
	  exit(2);
	}

      if (list_available) 		/* List available printers?  */
	{
	  DEBUG Listing printers: \n");
	  list_printers();
	  exit(0);
        }

      /********************************************************************
       * Bad flag combinations, ie. Banner and no printer, printer and no
       * banner, no banner and printer, are not allowed.
       ********************************************************************
       */

      if ((prflag==NULL) && (banflag)) error++;
      if ((banflag==NULL) && (prflag)) error++;
      if ((banflag==NULL) && (prflag==NULL)) error++;

      if (error==0)			/* No errors? Check printer name. */

	  if ( (validate_printer(requested_printer))==0 )
	    {
	     fprintf(stderr, "spool: Incorrect printer choice\n");
	     error++;
	    }
      if (error)
       {
	 fprintf(stderr,USAGE);		/* If there was an error, print the */
	 list_printers();		/* command usage, and list the */
	 exit(3);			/* Available printers. */
       }

DEBUG Your printer: node: %s Res: %s Name: %s\n\n", printer_node,restriction, printer_name);

       i=optind;  			/* Save pointer to beginning of file names in the argv. */

/*******************************************************************************************************
 * Scan through each name specified to see if it is readable...  (As usual, complain if not, and exit.)
 *******************************************************************************************************
 */

	 for (;optind<argc;optind++)
	   if (access(argv[i],4) !=NULL)   			/* Check for read permissions */
	     {
	       fprintf(stderr,"spool: error opening %s for transmission.\n", argv[optind]);
	       exit(4);
	     }

	infd=stdin;			/* Default input is stdin. */
	outname=mktemp(template);  	/* Form unique filename. */
  	outfd=fopen(outname,"w"); 	/* Open it for writing. */
	if (outfd==NULL)		/* Error? */
 	  {
	    fprintf(stderr,"spool: panic: error opening tempfile %s\n",outname);
	    exit(5);	
	  }

	optind=i;	/* Restore saved argv pointer */

	do   		/* Copy each file into the transmission file. */
	  {
	      if (optind < argc)
		   {
		     infd=fopen(argv[i],"r");   /* Open for reading */
		     if (infd==NULL)
		      {
			fprintf(stderr,"spool: File %s was deleted, spool attempt aborted.\n",argv[i]);
			exit(6);
		      }
		   }
           while ( (in_char=getc(infd)) !=EOF) putc(in_char,outfd); 
           if (infd!=stdin) fclose(infd);    			/* Don't try and close stdin. */
	  } while (++i < argc);		     			/* Until all files are done. */

      fclose(outfd); 		        			/* Ready to transmit. */

      errno=0;  /* Just in case... */

      sprintf(banner,"%.10s",banner);   			/* Truncate the banner. */

      if ( (strcmp(name.sysname,printer_node))==0)		/* Are we on the same node we are trying to send to? */
        sprintf(remote_command,LOCAL_LP,LOCAL_LP_ARGS);         /* If so, use the local command. */
      else 
        sprintf(remote_command,COMMAND,ARGUMENTS);  		/* Otherwise, use the network command. */

      DEBUG Remote command:\n%s\n",remote_command);

      if (debug==0)
      {
        exec_status=system(remote_command); 			/* Execute the command. */

        if (exec_status == ERR) 				/* Was there a problem? */
	  {
	   fprintf(stderr,"spool: can't execute transmit program, spool attempt aborted with\n");
	   fprintf(stderr,"       system() erorr %d.\n",errno);
	   unlink(outname);
	   exit(7);
	  }
       }

DEBUG Unlinking work file %s.\n",outname);

	if (unlink(outname) != NULL)
	  {
	    fprintf(stderr,"panic: spool could not delete the temporary workfile %s. File(s) did transmit.\n",outname);
	    exit(8);
	  }

  if ( (strcmp(name.sysname,printer_node))!=0)
  list_response(name.sysname);						/* Wait for status file to come back. & printit. */

DEBUG EXIT.\n");
}

/****************************************************************************
 * validate (requested_printer);
 *
 *        return code:  0=not found.
 *			non-zero, found.
 *
 *       If found, the node_name of the printer is returned in printer_node,
 *	 along with it's restriction in restriction.
 ****************************************************************************
 */

validate_printer(requested_printer)
char *requested_printer;
{
  FILE *infd;

extern char *printer_name[20],	/* The printer name known to ee70 students.*/  
        *printer_node[20],	/* The node name of the receiving printer. */
	*restriction[2];	/* The restriction flag. */

extern int student;

  int	in_char,		/* Current stream input char. */
	found,			/* found requested printer in SYS_PRINTERS file if non-zero. */
	scan_OK;		/* Result of fscanf. -1=EOF. */
	
  found=0;
  if ((infd=fopen(SYS_PRINTERS,"r"))==NULL)
  {
    printf(stderr,"spool: Network printer database could not be located.\n");
    exit(10);
  }
  else
  {
   scan_OK=0;
   do
   {
     in_char=getc(infd);			/* Get first character */
     if (in_char==EOL) 				/* Blank line? */
      {
       do in_char=getc(infd) ;  		/* Skip any extra newlines. */
       while (in_char==EOL);
      }
     if (in_char==SCRATCH)      		/* Was it a beginning of a comment? */
	 do in_char=getc(infd) ;
	 while ( (in_char!=EOL) && (in_char!=EOF) ) ;
			 
     if (in_char==EOF) scan_OK=-1;  		/* End of file? God forbid! */

     if ((scan_OK==-1) || (in_char==SCRATCH) || (in_char==EOL)) ;
     else
       {
	ungetc(in_char,infd);	/* Put the first character back. */
	scan_OK=fscanf(infd,"%s%s%s",printer_name,restriction, printer_node);

	   if ( (strcmp(printer_name,requested_printer))==0 ) /* Found? */
	     {
	         scan_OK=EOF;       		/* Found? YES!, so artificially end the file. */
		 found++;
		 DEBUG Found printer name %s, restriction=%s.\n",printer_name, restriction);
		 DEBUG Student flag=%d\n",student);

	         if ( ((strcmp(restriction,"R"))==0)  && (student>0) )
	           {
		     fprintf(stderr, "spool: You are not allowed to use printer %s.\n", requested_printer);
		     exit(13);
	           }
	     }
       }
   } while (scan_OK!=EOF);		/* Keep going until EOF */
     fclose(infd);
  }
   return found;
}

/****************************************************************************
 * List available printers from SYS_PRINTERS.
 * (and print them out of course!)
 ****************************************************************************
 */

list_printers()
{
  FILE *infd;

extern char *printer_name[20],	/* The printer name known to ee70 students.*/  
        *printer_node[20],	/* The node name of the receiving printer. */
	*restriction[2];	/* The restriction flag. */

extern int debug;

int 	in_char,
	scan_OK;

  if ((infd=fopen(SYS_PRINTERS,"r"))==NULL)
  {
    printf(stderr,"spool: Network printer database could not be located.\n");
    exit(10);
  }
  else
  {
    fprintf(stdout,"\n   %-15s| %-30s\n","Printer Name:", "Restriction: (A=Anyone, R=Restricted)");
    fprintf(stdout,"   ---------------+--------------------------------------\n");
    scan_OK=0;
    do
     {
       in_char=getc(infd);  /* See above for the comments for this mess. */
       if (in_char==EOL) 
          do in_char=getc(infd); while (in_char==EOL);

       if (in_char==SCRATCH)
	 do in_char=getc(infd); while ( (in_char!=EOL) && (in_char!=EOF) );
			 
       if (in_char==EOF) scan_OK=-1;
       if ((scan_OK==-1) || (in_char==SCRATCH) || (in_char==EOL)) ;
       else
        {
	  ungetc(in_char,infd);
	  scan_OK=fscanf(infd,"%s%s%s",printer_name,restriction, printer_node);
	  fprintf(stdout,"   %-15s|%2s\n",printer_name,restriction);
        }
   } while (scan_OK!=EOF) ;
  }
   fclose(infd);
   fprintf(stdout,"\n");
   return;
}

/****************************************************************************
 * List response sent by remote system.  If it hasn't arrived yet, wait
 * until it does.  (Up to 40 seconds.)
 ****************************************************************************
 */
list_response(system_name)			/* Name of this system. (Used to build correct file name.) */
char *system_name;
{
   FILE *infd;
   int in_char;
   char *filename[30];

   sprintf(filename,STATUS_TEMPLATE,system_name);
   wait_file(TIMEOUT,filename);			/* Wait for TIMEOUT seconds for file 'filename' to arrive. */

   if ( (infd=fopen(filename,"r"))==NULL )
      {
	fprintf(stderr,"spool: Status file \'%s\' is missing. Hmm.\n", system_name);
	exit(11);
      }
    else
      {
	fprintf(stdout,"\n");
        while ( (in_char=getc(infd)) !=EOF) putc(in_char,stdout); 	/* List the file. */
	fprintf(stdout,"\n");
	close(infd);
	if ( (unlink(filename)) !=NULL )
	    fprintf(stderr,"Can't remove status file %s. Tell proctor.\n", filename);
	exit(8);
      }
}

/****************************************************************************
 * query_request(printer_node);
 * Line printer query request for printer 'printer_node'.
 ****************************************************************************
 */

query_request(printer_node)
char *printer_node;
{
   extern int errno,
	debug;

   int exec_status,
       in_char; 

   char *mktemp(),
	*outname,
	*template[150],
	*remote_command[255];  

   struct utsname name;		/* System name fetching structure */

   uname(&name);	 	/* Fetch our system name. */

   errno=0;

   if ( (strcmp(name.sysname,printer_node))==0)       /* Requesting from same machine as printer? */
     sprintf(remote_command,LOCAL_LPSTAT);	      /* Issue local lpstat command. */
   else
     sprintf(remote_command,QUERY_COMMAND,QUERY_ARGUMENTS);   /* Otherwise, use remote command. */

   DEBUG Remote command: %s\n",remote_command);

  if (debug==0)
   {
   exec_status=system(remote_command);

      if (exec_status == ERR)
	  {
	   fprintf(stderr,"spool: Can't execute request program, request attempt aborted with\n");
	   fprintf(stderr,"       system() error %d.\n",errno);
	   exit(9);
	  }
   }
   if ( (strcmp(name.sysname,printer_node))!=0)		/* If requesting from remote machine, wait for results. */
   list_response(name.sysname);
}

/****************************************************************************
 * wait_file(seconds,filename);
 * wait 'seconds' for file 'filename'
 * NOTE:  THE FILE BEING WAITED FOR IS UNLINKED, JUST IN CASE AN OLDER 
 *        VERSION OF IT MIGHT BE AROUND.    YOU HAVE BEEN WARNED....
 * 
 * If no file arrives after 'seconds', print error and exit(12) 
 ****************************************************************************
 */
wait_file(seconds,filename)
int seconds;
char *filename;
{
   DEBUG Filename: %s\n",filename);
   unlink(filename);			/* Just in case a leftover exists. */

   signal(SIGALRM,timeout); 		/* Set the alarm to point to the time out routine. */
   alarm(seconds);			/* Allow seconds timeout. */
   fprintf(stdout,"One moment please, your request is being processed.");
   fflush(stdout);
   
   do 
     {
       fprintf(stdout,".");		/* Print little dots every few seconds to let the user know what's goin on. */
       fflush(stdout);
       sleep(2);
     }
      while ((access(filename,4))!=NULL );    /* Keep doing it until the time runs out, or the file arrives. */

  signal(SIGALRM, SIG_IGN);
}

timeout()
{
  signal(SIGALRM, SIG_IGN);
  printf("\nSorry, the remote machine is not answering. Please make sure the\n");
  printf("printer/remote machine is operating properly.\n");
  exit(12);
}

/****************************************************************************
 * This is the return server for the spool program. It is invoked with
 * spool machine_making_request request_type @
 ****************************************************************************
 */
return_state(sysname,command)
char *sysname,
     *command;
{
 int exec_status; 
 char *remote_command[255];

 printf("Command: %s, Sysname: %s\n",command,sysname);

  if (strcmp(command,"S")==0)					/* Status request */
	sprintf(remote_command,STAT_COMMAND,STAT_ARGUMENTS);

  if (strcmp(command,"R")==0)					/* Return lp command status */
	sprintf(remote_command,LP_COMMAND,LP_ARGUMENTS);

  printf("Remote command: %s\n",remote_command);

  exec_status=system(remote_command);

  exit(0);
}

@//E*O*F spool.c//
chmod u=rw,g=r,o=r spool.c
 
echo x - spool.doc.ms
sed 's/^@//' > "spool.doc.ms" <<'@//E*O*F spool.doc.ms//'
@.ST
@.nr VS 18
@.ds LH "EECS 70
@.ds RH %
@.ds CH "Project Report
@.ds CF
@.sp 2i
@.LG
@.ce
\fBReport of activities\fI
@.sp
@.ce
\fBPrinter Spooling Over 3bnet\fR
@.sp 2
@.ce
@.SM
@.nr VS 15
James A. Hayes
@.br
@.ce
University of California,
@.br
@.ce
San Diego
@.br
@.sp 3
@.ce
@.LG
@.I ABSTRACT
@.sp
@.QP
@.LG
@.nr VS 18
The goal of the
@.I spool
implementation is to allow students to send information to printers
not physically
connected to their machine.  This design replaces the current method of
having to physically login to the printer's host.  This document
will provide an outline of the 
@.I spool
implementation.
@.sp 3
@.nr VS 14
@.LP
March, 1986
@.br
@.nr PS 12
@.bp
@.SH
Introduction
@.PP
While the concept of a remote printer spooling system is easy to
understand, the actual implementation is at best, complicated.  If you have
not already done so, please read the
@.I spool
manual page which will explain the \fIspool\fR command syntax.
This document will try to explain the
@.I spool
implementation in three stages.  An explanation of network
security, a description of the
@.I nisend
command,  and finally,
a nuts-and-bolts description of the spooling software.
@.sp
@.SH
1. Network Security
@.PP
The AT&T 3B2 computer is capable of running an Ethernet network 
communications
system.  While the software provided with the Ethernet hardware is complete, 
a major problem arises when trying to use it in an educational environment.
The network commands are not protected from subversive
application.  That is
to say, it is dangerous when
@.I anyone
is given permission to use the network
commands.  This means that a student on machine A can
@.I copy
or knowingly
@.I damage
homework files from a student on machine B.
@.PP
The means for securing
the crucial network command (\fInisend\fR)
is borne from built-in UNIX\(dg
@.FS
\(dg.
UNIX is a trademark of AT&T Bell Laboratories.
@.FE
execution privileges.
@.PP
The network command
@.I nisend
is owned by
@.B bin
and is a member of the newly
created group
\fBnisecure\fR.  It has  only the owner and group execute
permissions set.  This will not allow anyone but
@.B root
or a member of the
group
@.B nisecure
to execute the
@.I nisend
program.  The print spooler (\fIspool\fR) is
also a member of the group
@.B nisecure
and sets its group identification (sgid) on
execution.  The execute permissions for
@.I spool
allow it to be run by anyone.
@.PP
Since the group ID of
@.I spool
is the same as \fInisend\fR,
@.I spool
is allowed to call
\fInisend\fR.  So far, 
@.I spool
is the only program that is allowed to call \fInisend\fR.
@.PP
The access permissions (as seen by \fIls\fR)
look like this:
@.KS
@.sp
@.ce
/usr/bin/nisend /usr/bin/spool
@.TS
center;
l l l l l l.
-rwxr-s---	bin	nisecure	xx/xx/86	12:00	nisend
-rwxr-xr-x	root	nisecure	xx/xx/86	12:00	spool
@.TE
@.KE
@.sp
@.SH
2. The `nisend' Program
@.PP
The
@.I nisend
program is provided by AT&T as a front-end file transfer and
remote command execution system to be used by programs at the
shell and applications
level.  It is complete, but does have a few problems that are worth
mentioning.
@.PP
@.I Nisend
sends mail to its users after every request, transfer, command
execution, error and hiccup.  The default file access mode is set to 
600 (owner read/write).  The directory where the file will be saved (once
transmitted) is not the same directory it was taken from.  The default
receiving directory is ~/rje/\fIname\fR.  The
local file to be transmitted must
be the last item on the command line.  The one line \*Q3bnet job xxxxyyy
submitted\*U is always printed.
@.PP
The  above problems may be circumvented by command line options.  To
stop mail, use the
@.I undocumented
-e option.  To set
the access permissions of
the remote files, use the -a\fImode\fR option.  To specify
what directory the
file is to be saved in, use the -f\fIpath\fR option.  The
\*Q3bnet...\*U message may be silenced by the -s option.
@.PP
The spool program uses a standard form of the
@.I nisend
command that avoids all the above problems. It looks like this:
@.LP
@.in +.5i
@.ti -.5i
@.na
nisend -s -e -d\fIremote_machine\fR -a0666
-f/usr/tmp/\fIremote_name\fR
-!\*Q\fIremote_command\fR\*U /usr/tmp/\fIlocal_file\fR
@.ad b
@.in -.5i
@.sp
@.SH
3. The `spool' Program, an Overview
@.PP
The
@.I spool
program is not a server, although it performs the function of
a server because it is executed on the sending machine as well
as the receiving machine when a request is submitted.
@.PP
The sending machine is responsible for collecting the files, sending
them, submitting them for printing, and waiting for confirmation from the
@.I spool
program on the receiving end.  The 
@.I spool
program on the receiving end
takes the confirmation from
@.I lp
or status from
@.I lpstat
and returns it to the
waiting machine on the sending end.
@.sp
@.SH
3.1. Spooling a File for Remote Printing
@.sp
\fRCOMMAND: spool -p room10 -bJones foo.c
@.sp
ACTIONS:
@.PP
Validate the command options and verify that the specified file(s) and
printer (via \fI/etc/sysprint\fR) exist.
Read the file(s) (via stdin or file name(s)
on command line), concatenate them, and put them in a temporary file
/usr/tmp/sp.\fInode\fR.\fIXXXXX\fR, where
@.I node
is the
@.B 3bnet
node name of the user's
machine making the request, and
@.I XXXXX
is the process ID of the
@.I spool
program.  The above actions insure that the temporary file will have a
unique name on the remote machine when it is arrives there.
@.PP
Send the file to the remote system, execute
@.I lp
on the
remote system, store the standard output and standard error of
@.I lp
in another temporary file /usr/tmp/\fInode\fR.stat,
send it back to the user, display it
and exit.
@.KS
@.LP
For example: (See also \fIFigure 1\fR)
@.IP \fBUSER:\fR 10
$ pr *.s | spool -pX -bSharon  (executed on machine A, for printer X
on machine B, with PID=5427.)
@.sp
@.KE
@.LP
@.B SPOOL:
(SENDING END)
@.IP 1.
Collect standard input (or specified files) and save in
/usr/tmp/sp.A.a5427
@.IP 2.
Execute
@.I nisend
(via the
@.I system()
call).
@.QP
@.na
/usr/bin/nisend -s -e -dB -a0666 -f/usr/tmp/sp.A.a5427 -!\*Q\fBSee
remote command below\fR\*U /usr/tmp/sp.A.a5427
@.ad b
@.IP 3.
Remove /usr/tmp/sp.A.a5427
@.IP 4.
Wait for the response /usr/tmp/A.stat to arrive, (contains the output
of the
@.I lp
command.) give it 40 seconds to show up, and if it doesn't
arrive after that, give up and tell the user what happened. If it
does arrive in the allotted time, print it out.
@.IP 5.
Remove /usr/tmp/A.stat (If it arrived from the remote machine.)
@.sp
@.LP
\fBRemotely executed command (broken down into steps):\fR
[Please note that the
@.I lp
command is executed on the printing system by
@.I spool
on the requesting system and not by
@.I spool
on the printing system.  This saves about 3 seconds that would
normally be wasted waiting for
@.I spool
to fork() and exec(), then waiting for
@.I lp
to echo the request ID.  Now, the result of
@.I lp
can be sent back the moment it is available.  This is quirk of the
@.I nisend
command.]
@.sp
@.in +.5i
@.IP A.
submit the job to lp and capture its output:
@.QP
@.na
usr/bin/lp -tSharon -c /usr/tmp/sp.A.a5427 > /usr/tmp/A.stat 2>&1
@.ad b
@.IP B.
Remove the temporary file:
rm /usr/tmp/sp.A.a5427
@.IP C.
Return the captured output of
@.I lp
to the calling spool by executing
spool on the receiving end:
@.in +.5i
/usr/bin/spool A R @  (A=Name to send back to, R=\fIlp\fR report,
@@=Receiver mode.)
@.in -.5i
@.IP D.
Remove the captured output of lp and return.
@.QP
rm /usr/tmp/A.stat
@.in -.5i
@.sp 1
@.SH
3.2. Requesting a Status Report on the Remote Printer
@.sp
\fRCOMMAND: spool -s -p room10
@.sp
ACTIONS:
@.PP
Validate the command options and verify that the specified printer (via
\fI/etc/sysprint\fR) exists.
@.PP
Execute the
@.I lpstat
program on the remote system, store the standard output
and standard error of
@.I lpstat
in a temporary file /usr/tmp/\fInode\fR.stat, send it back to
the user, display it and exit. (\fINode\fR is the requesting
@.B 3bnet
node name.)
@.KS
@.LP
For example: (see also \fIFigure 2\fR)
@.IP \fBUSER:\fR 10
$ spool -s -pX (executed on machine A, for printer X on machine B)
@.br
@.KE
@.LP
@.B SPOOL:
(SENDING END)
@.IP 1.
Execute
@.I nisend
(via the
@.I system()
call):
@.QP
@.na
/usr/bin/nisend -s -e -dB -a0666
-!\*Q\fBSee remote command below\fR\*U
@.ad b
@.IP 2.
Wait for the response /usr/tmp/A.stat to arrive, (contains the output of
the
@.I lpstat
command.) give it 40 seconds to show up, and if it
doesn't arrive after that, give up and tell the user what
happened. If it does arrive in the allotted time, print it out.
@.IP 3.
Remove /usr/tmp/A.stat (If it arrived from the remote machine.)
@.sp
@.LP
\fBRemotely executed command (broken down into steps):\fR
@.in +.5i
@.IP A.
Fire-up
@.I spool
on the receiving end. There it will execute
@.I lpstat
and return the output back to the user:
@.in +.5i
/usr/bin/spool A S @  (A=Machine name to send back to, S=lp status
report, @=Receiver mode.)
@.in -.5i
@.IP B.
Remove the captured output of
@.I lpstat
and exit.
@.in +.5i
rm /usr/tmp/A.stat
@.in -1i
@.sp 2
@.SH
Conclusion/Comments
@.PP
It is not the most efficient, but it works.  Possible future
improvements might involve an actual spool daemon.  This would make
handshaking more reliable, instead of using 
@.I nisend
which is very slow and very sensitive to system load.
@.sp
@//E*O*F spool.doc.ms//
chmod u=rwx,g=r,o= spool.doc.ms
 
echo x - spool.h
sed 's/^@//' > "spool.h" <<'@//E*O*F spool.h//'
#define SYS_PRINTERS "/etc/sysprint"    /* Location of printer info. */
#define DEFAULT_DIR "/usr/tmp/%sXXXXXX"      /* Place for temp files,
	where %s and XXXXXX will be replaced by the machine name and
	process I.D. */
#define TIMEOUT 50			/* Seconds to wait for response */
/* ***************************************************************************
COMMAND is what is sent to the remote system.  Various arguments in the
command (%s strings) are specified in the ARGUMENTS portion.  The arguments
are declared in the order they appear in the command.
************************************************************************** */

#define COMMAND "/usr/bin/nisend -e -d%s -a0666 -s -c -f%s -!\042\
/usr/bin/lp -c -t%s %s>/usr/tmp/%s.stat 2>&1; rm %s;\
/usr/local/spool %s R @; rm /usr/tmp/%s.stat\042 %s"


#define ARGUMENTS printer_node,outname,banner,outname,name.sysname,outname,\
	name.sysname,name.sysname,outname

/* **************************************************************************
QUERY_COMMAND is what gets sent to the remote printer to get its status
sent back. QUERY_ARGUEMENTS are stuffed into the QUERY_COMMAND.
************************************************************************** */
#define QUERY_COMMAND "/usr/bin/nisend -e -s -d%s -!\
\042/usr/local/spool %s S @; rm /usr/tmp/%s.stat\042"

#define QUERY_ARGUMENTS printer_node,name.sysname,name.sysname

#define USAGE "print:  spool -p {printer} -b {banner}\n\
status: spool -p {printer} -s\n\
list supported network printers: spool -l\n"

#define STAT_COMMAND "lpstat -a -r -u -p>/usr/tmp/%s.stat;\
/usr/bin/nisend -c -e -d%s -a0666 -f/usr/tmp/%s.stat \
/usr/tmp/%s.stat"

#define STAT_ARGUMENTS sysname,sysname,sysname,sysname

#define LP_COMMAND "/usr/bin/nisend -s -e -c -d%s -a0666 -f /usr/tmp/%s.stat \
/usr/tmp/%s.stat"

#define LP_ARGUMENTS sysname,sysname,sysname 

/* We use these commands in the case that the person spooling to a remote
	printer, isn't.  (Meaning: When they are on the same machine as
	the printer, and no network activity needs to take place.
/*
#define LOCAL_LP "/usr/bin/lp -c -t%s %s"
#define LOCAL_LP_ARGS banner,outname

#define LOCAL_LPSTAT "/usr/bin/lpstat -a -r -u -p"
#define STATUS_TEMPLATE "/usr/tmp/%s.stat"

#define ERR -1
#define DEBUG if (debug) fprintf(stderr,"Debug>
#define EOL 10
#define SCRATCH 35

@//E*O*F spool.h//
chmod u=rw,g=r,o=r spool.h
 
echo x - spool.man
sed 's/^@//' > "spool.man" <<'@//E*O*F spool.man//'
@.TH SPOOL 1 Local
@.AT 5 2.0
@.SH NAME
spool - spool files to remote printer.
@.sp
@.SH SYNOPSIS
@.PP
spool -s 
@.RI -p printer
@.br
spool
@.RI -p printer
@.RI -b banner
[files]
@.br
spool -l
@.br
spool
@.I node_name
R|S @
@.B (see below)
@.sp
@.SH DESCRIPTION
@.PP
@.I Spool
allows the printing of files on a remote machine.
After sending the request,
@.I spool
waits for (and displays) confirmation of the printing or status request.
@.PP            
The command line options:
@.TP
@.B -p
This option is used to specify the remote
printer name for the printing or status request. (Required)
@.TP
@.B -s
Requests a queue and status report from the remote printer.
This must be used in conjunction with the
@.B -p
flag.
(See above.)
@.TP
@.B -l
Lists the printers that can be reached from the network, along with
information regarding restricted use.
@.TP
@.B -b
The
@.BI -b banner
option is required for all printing
requests.
@.I Banner
will appear on the printout in large letters 
below the login name of the requestor.
There is a ten character maximum, with no spaces.
@.TP
@.B -d
Turns on a myriad of sometimes useful debugging
information, but does not actually execute the
remote commands.
@.PP
The last
@.I spool
command invokes the ``return'' mode of the
@.I spool
program.  When a
@.I spool
request is submitted, the
@.I spool
program on the remote printer is invoked with the
name of the calling machine, an R for ``return 
@.I spool
request
number'' or an S for ``return
@.I lp
queue status'' and then an
@@ sign.  The @ sign is introduced to make it very unlikely
for a user to type ``spool Mach7 R @'' from the console. (See
reference below.)
@.br
@.sp
@.SH EXAMPLES
pr foo.s | spool -pgumby -bJohn
@.br
@.PP
@.in +.5i
Formats the file foo.s and feeds it to
@.I spool
for printing on the remote printer
@.B gumby.
The name on the printout will be John.
@.in -.5i
@.br
@.sp
spool -ppokey -bJones hexdump.c
@.PP
@.in +.5i
Prints the file hexdump.c on the remote printer 
@.B pokey
with the name Jones on the printout.
@.in -.5i
@.br
@.sp
spool -s -psluggo
@.PP
@.in +.5i
Requests status information about printer \fBsluggo\fR.  This
command will list the printer status, pending and
currently printing files, and other tidbits of
knowledge.
@.in -.5i
@.br
@.sp
spool -l
@.PP
@.in +.5i
Lists the reachable printers physically connected to
the network.  This list is taken from 
@.B ``etc/sysprint.''
[See \fBsysprint(7)\fR]
@.in -.5i
@.br
@.sp
@.SH NOTES
@.PP
The '-' options may be specified in any order.
Options may be combined, and spaces between options
and arguments are allowed.  For example:
\fIspool -s -p room10\fR is the same as \fIspool -sproom10\fR.
[See \fBgetopt(3C)\fR]
@.PP
Restricted use of printers is determined by login name through
\fIcuserid\fR. Currently, the
`student' login is not allowed to use restricted printers.
@.br
@.SH FILES
@.br
@.B /etc/sysprint
@.SH DIAGNOSTICS
@.br
Exit codes and messages:
@.sp
@.TP
1
Printer xxxx does not exist
@.TP
2
Please request a printer
@.TP
3
General usage error
@.TP
4
Error opening xxxx for transmission
@.TP
5
Panic: Error opening temporary file
@.TP
6
File disappeared
@.TP
7
Can't execute transmit program. (Nisend failed)
@.TP
8
Panic: Could not delete temporary file
@.TP
9
Can't execute request program. (Nisend failed)
@.TP
10
Network printer database is missing.
@.TP
11
Status file is missing. Hmm.
@.TP
12
Sorry, remote machine is not answering...
@.TP
13
Not allowed to use printer xxxx
@.br
@.sp
@.SH REFERENCE
@.LP
``\fBProject Report: Printer Spooling Over 3bnet\fR'' by James A.
Hayes, March, 1986.
@.SH SEE ALSO
@.LP
\fIsysprint(7)\fR, \fIgetopt(3C)\fR, \fIcuserid(2)\fR.
@.br
@//E*O*F spool.man//
chmod u=rwx,g=r,o= spool.man
 
echo x - sysprint
sed 's/^@//' > "sysprint" <<'@//E*O*F sysprint//'
# system printer database.
#
#  Name   Restriction   Node   
    5414	A	3bp5414
    5426	A	3bp5426
#
# done
#
@//E*O*F sysprint//
chmod u=rw,g=r,o= sysprint
 
-- 

Rich $alz
Cronus Project, BBN Labs			rsalz@bbn.com
Moderator, comp.sources.unix			sources@uunet.??

2.s