/* SPOOLER.c - asynchronous file print program */ /* ** Date written: 04/23/86 ** Author: Tim Holloway ** Compuserve: 73026,2026 ** Bix: tholloway ** Fido node: 112/1 (Casa Mi Amiga). ** ** As featured in Ami Project Magazine. ** ** Version: 1.1 ** ** Copyright (C) 1986, by Tim Holloway. This program may be ** freely distributed for non-commercial use only. Use for commercial ** purposes without the express permission of the author is a violation ** of copyright law. ** ** Description: ** This program is the intermediary between user requests submitted ** via the SPOOL program, and the printers driven by copies of the ** PRTSPOOL program. Three queues are maintained: the logged-in user ** queue has an entry for each user (SPOOL or PRTSPOOL) logged into ** SPOOLER. The filename queue is where filenames passed from SPOOL ** are stored until they can be passed on to PRTSPOOLs. Finally, the ** waiting-printer queue is where requests for work from PRTSPOOL ** are held. ** ** This program illustrates message passing and waiting for messages. ** Each message is the name of a file. The named file will be printed, ** unless the name is SHUTDOWN, which causes SPOOLER to cease ** accepting login requests, and to respond to further messages with ** forced logouts. SPOOL shuts down completely when the login queue ** has at last been emptied. ** ** Change History: ** ** 11/19/86, TFH - added logic to FreeStruct SPOOL queue ** entries when they were removed from the SPOOL queue. How ** embarassing! Also, removed all printfs - does all talking via ** Intuition now. ** ** Usage: RUN SPOOLER */ #include #include #include #include #include "stdio.h" #include "spool.h" #define NOT ! /* A la RJ Mical */ #define USER_NAME_LENGTH 33 /* maximum length of a logged user name */ /* ** The following is an information hider - it insulates the workings ** of this program against changes in the DOS and from compiler ** dependencies. Also makes program more readable. */ #define on_port(mport) (1L << mport->mp_SigBit) /* A convenient allocator for structures: */ #define AllocStruct(structype, pool) (structype *) \ AllocMem (sizeof(structype), pool) /* And its converse... */ #define FreeStruct(area, structype) FreeMem(area, sizeof(structype)) /* Hide the details of test for an empty list */ #define EmptyList(list) (list.lh_Head->ln_Succ == NULL) #define streq(a,b) (strcmp(a,b) == 0) /* an old favorite */ /* ** Logged-in user queue element definition: */ typedef struct { struct Node lu_node; char lu_name[USER_NAME_LENGTH]; } logged_user; /* Prototypes. Lie slightly about function return types. */ extern logged_user *Find_Name(struct List *, TEXT *); extern SPOOLmsg *RemHead (struct List *), *GetMsg (struct MsgPort *); /* Global variables. */ BOOL spooler_running; spooler_closing_down; static void make_id (packet, str) /* create a unique login ID */ SPOOLmsg *packet; /* based on the sender's reply port address */ TEXT *str; { sprintf (str, "SPOOL User %lx", &packet->minfo.mn_ReplyPort); } static void error (msg) /* report error */ TEXT *msg; { Gripe ("SPOOLER:", msg, ""); } /************************************************************************* ** ** ** Log users in and out. Users are logged to ensure that nobody ** ** tries to send spool requests once the spooler starts closing ** ** down. ** ** ** *************************************************************************/ struct List login_list; /* anchor the list of logged-in users */ struct List file_queue; /* ditto the list of filenames to spool */ struct List waiting_printers; /* unemployed printer list */ static void login(packet) register SPOOLmsg *packet; { register logged_user *user; packet->log_status = LOG_OUT; /* assume the worst */ if (NOT spooler_closing_down) { /* printf ("Log in: "); */ user = AllocStruct (logged_user, MEMF_PUBLIC | MEMF_CLEAR); if (user == NULL) error ("Couldn't get memory to log in user"); else { user->lu_node.ln_Name = user->lu_name; user->lu_node.ln_Type = NT_UNKNOWN; user->lu_node.ln_Pri = 0; make_id (packet, user->lu_name); AddTail (&login_list, user); /* printf ("%s completed\n", user->lu_name); */ packet->log_status = LOGGED; } } } static void logout(packet) register SPOOLmsg *packet; { char user_id[USER_NAME_LENGTH]; register logged_user *user; make_id (packet, user_id); user = (logged_user *) FindName (&login_list, user_id); if (user == NULL) { error ("Logout failed - could not find user ID:"); error (user_id); } else { /* printf ("%s logged out.\n", user_id); */ Remove(user); /* ... from the logged-user list */ FreeStruct(user, logged_user); packet->log_status = LOG_OUT; if (EmptyList(login_list) && spooler_closing_down) spooler_running = FALSE; } } /************************************************************************* ** ** ** Spooler shutdown routines. ** ** ** *************************************************************************/ static void purge_queues() /* throw away any outstanding SPOOL requests */ { /* modified v1.1 */ SPOOLmsg *qp; while ( (qp = (SPOOLmsg *) RemHead(&file_queue)) != NULL) FreeStruct(qp, SPOOLmsg); } static void terminate_printers() /* tell the printers to get lost! */ { register SPOOLmsg *bail_out; while ( (bail_out = (SPOOLmsg *)RemHead(&waiting_printers)) != NULL ) { /* printf ("Force off PRT %lx\n", bail_out); */ logout(bail_out); /* remove from log */ bail_out->log_status = LOG_OUT; ReplyMsg(bail_out); } } /************************************************************************* ** ** ** Process inbound SPOOL requests. ** ** If somebody wants the request, pass it on, else make a copy ** ** of it, and queue the copy. ** ** ** *************************************************************************/ static void pass_request(packet) register SPOOLmsg *packet; { register SPOOLmsg *msg_requested; if ( (msg_requested= RemHead(&waiting_printers)) == NULL) { msg_requested = AllocStruct (SPOOLmsg, MEMF_PUBLIC); movmem (packet, msg_requested, sizeof(SPOOLmsg)); /* printf ("Queuing request for %s\n", packet->filename); */ AddTail (&file_queue, msg_requested); } else /* send the name to the PRTSPOOL's query */ { strcpy (msg_requested->filename, packet->filename); ReplyMsg (msg_requested); } } /************************************************************************* ** ** ** Process inbound SPOOL requests. ** ** ** *************************************************************************/ static void handle_input(packet) register SPOOLmsg *packet; { /* printf ("Inbound message from %lx\n", packet); */ switch (packet->log_status) { case LOG_IN: login (packet); break; case LOG_OUT: logout (packet); break; case LOGGED: if (spooler_closing_down) logout(packet); /* sorry! */ else if (streq(packet->filename, SHUTDOWN)) { spooler_closing_down = TRUE; purge_queues(); terminate_printers(); } else pass_request (packet); break; default: error ("SPL: Invalid log request"); } ReplyMsg (packet); /* return to owner! */ } /************************************************************************* ** ** ** Process outbound SPOOL requests. ** ** ** ** Notice that since the PRTSPOOL messages contain List nodes, ** ** they can be queued directly! ** ** ** *************************************************************************/ static void prt_request(packet) register SPOOLmsg *packet; { register SPOOLmsg *msg_requested; if ( (msg_requested= RemHead(&file_queue)) == NULL) { /* printf ("Queuing PRT request from %lx.\n", packet); */ AddTail (&waiting_printers, packet); } else { /* printf ("dequeueing and outputting %s\n", msg_requested->filename); */ strcpy (packet->filename, msg_requested->filename); FreeStruct (msg_requested, SPOOLmsg); /* V1.1 */ ReplyMsg (packet); } } /************************************************************************* ** ** ** Process PRTSPOOL requests. ** ** ** *************************************************************************/ static void handle_output(packet) register SPOOLmsg *packet; { /* printf ("PRTSPOOL message from %lx\n", packet); */ switch (packet->log_status) { case LOG_IN: login (packet); break; case LOG_OUT: logout (packet); break; case LOGGED: if (spooler_closing_down) logout(packet); /* sorry! */ else { prt_request (packet); return; /* reply done in prt_request */ } break; default: error ("PRT: Invalid log request"); } ReplyMsg (packet); /* return to owner! */ } /************************************************************************* ** ** ** Gentlemen, start your SPOOLers. ** ** ** *************************************************************************/ struct Library *IntuitionBase, *OpenLibrary(); void main (argc, argv) int argc; char *argv[]; { struct MsgPort *FindPort(), *CreatePort(), *inport, *outport; SPOOLmsg *packet; struct Process *myprocess, *FindTask(); if ( (IntuitionBase = OpenLibrary("intuition.library", 0)) == NULL) { Alert (AT_Recovery + AG_OpenLib + AO_Intuition, NULL); exit(20); } /* it doesn't hurt to run multiple SPOOLERS at once - but why bother? Only one will get the messages! */ if(FindPort(SPOOL_ME) != NULL) { error ("SPOOLER is already active!"); goto abort; } if ((inport = CreatePort(SPOOL_ME,0)) == NULL) { error ("Unable to create spool message port\n"); goto abort; } if ((outport = CreatePort(GIMME_A_FILE,0)) == NULL) { error ("Unable to create spool message port\n"); DeletePort (inport); goto abort; } NewList (&file_queue); NewList (&login_list); NewList (&waiting_printers); /* raise priority - this program is low-overhead (mostly Waiting) */ myprocess = FindTask(NULL); (void) SetTaskPri (myprocess, SPOOLER_PRIORITY); /* printf ("input message port for %s is at %lx\n", SPOOL_ME, inport); */ for (spooler_running = TRUE; spooler_running ; ) { /* printf ("Wait for port...\n"); */ Wait (on_port(inport) | on_port(outport)); /* '|', NOT '||' ! */ while ( (packet = GetMsg (inport)) != NULL) handle_input(packet); while ( (packet = GetMsg (outport)) != NULL) handle_output(packet); } /* printf ("Spooling has been terminated\n"); */ DeletePort (inport); DeletePort (outport); abort: CloseLibrary(IntuitionBase); }