;/* Execute me to Compile and link ShellTimerDaemon.c shelltimer start verbose ; For debugging, use the following command: ; lc -. -O -ms -b1 -cfistq -j73 -v -Lit -d3 -isrc:memlib -dMWDEBUG=1 ShellTimerDaemon.c ; For production, use the following command: lc -. -O -ms -b1 -cfistq -j73 -Lit ShellTimerDaemon.c echo "Time to compile and link: " NOLINE shelltimer stop quit */ /*** **** SHELLTIMERDAEMON.C **** **** Creation: John Lindwall **** 19 Jan 1992 **** **** Description: The timer server for the ShellTimer system. **** This daemon accepts requests from the client program, ShellTimer. **** It maintains a dynamic list of timer sessions. **** **** When the client requests that a timer session be started, the **** daemon records the shell PID of the client and the current time **** in an ST_Node data structure. Later client requests can query **** the value of the timer, cancel the timer session, and stop the **** the timer. The daemon can also be commanded to terminate. **** Elapsed timer values are computed by the daemon and sent back to **** client. **** **** The AmigaDOS Break command can also be used to terminate the **** daemon, by sending it a Control-C. **** **** As written, this program requires AmigaDOS 2.04. It could **** be modified to run under 1.3, but I don't have the inclination. **** 2.0 features used include the AllocVec() call (love it!). **** **** See ShellTimer.doc for more information. **** **** The ShellTimer system (which includes the programs **** ShellTimerDaemon and ShellTimer) is released into the Public **** Domain by the author, John Lindwall. I ask that the archive **** be kept intact with the docs and the source code. Bug reports **** should be sent to me at johnl@crash.cts.com. **** **** Note: The code is not commented ... sorry. **** **** Overhauls: ***/ #include #include #include #include #include #include #include #include #include #include #include #include #include "ShellTimer.h" #define APPNAME "ShellTimerDaemon" #define ADOS2_VERSION 37 void CleanExit(char *, int); void SetupTimerDevice(void); void SetupMessagePort(void); void ProcessMessages(void); void StartTimer(struct ST_Message *msg); void StopTimer(struct ST_Message *msg); void QueryTimer(struct ST_Message *msg); void CancelTimer(struct ST_Message *msg); void SetupRequestList(void); void DestroyRequestList(void); BOOL InsertRequest(int requestID, struct timeval *startTime); struct ST_Node *RemoveRequest(int requestID); BOOL InitMsgEndDiffTime(struct ST_Message *msg, struct ST_Node *request, struct timeval *currentTime); void ToggleTimer(struct ST_Message *msg); void Print(char *s); struct ST_Node { struct Node node; int requestID; struct timeval startTime; }; struct List requestList; struct Library *TimerBase; struct timerequest *TimerIO; struct timeval *time; struct MsgPort *publicPort; UBYTE versionTag[] = "\0$VER: " APPNAME " 1.0 (03.02.92) by John Lindwall\n"; /* Disable SAS/C Control-C/D checking */ int CXBRK(void) { return(0); } int chkabort(void) { return(0); } void main(int argc, char *argv[]) { extern struct DOSLibrary *DOSBase; if( DOSBase->dl_lib.lib_Version < ADOS2_VERSION ) { CleanExit("AmigaDOS 2.0 required!\n", -20); } Forbid(); if( FindPort(ST_PORT) ) { Permit(); CleanExit("Daemon already loaded!\n", -5); } Permit(); if( argc != 0 ) { Print(&versionTag[7]); } #ifdef MWDEBUG MWInit(NULL, 0, "CON:0/0/639/199/MemLib"); #endif SetupTimerDevice(); SetupRequestList(); SetupMessagePort(); ProcessMessages(); CleanExit("Exiting.\n", 0); } void SetupRequestList(void) { NewList(&requestList); } void DestroyRequestList(void) { struct ST_Node *node; while( (node = (struct ST_Node *) RemHead(&requestList)) != NULL ) { FreeVec(node); } } BOOL InsertRequest(int requestID, struct timeval *startTime) { struct ST_Node *node; if( (node = AllocVec(sizeof(struct ST_Node), MEMF_ANY)) == NULL ) { return(FALSE); } node->requestID = requestID; node->startTime.tv_secs = startTime->tv_secs; node->startTime.tv_micro = startTime->tv_micro; Enqueue(&requestList, (struct Node *)node); return(TRUE); } struct ST_Node * RemoveRequest(int requestID) { struct ST_Node *foundNode; BOOL match; static struct ST_Node copyOfFoundNode; if( IsListEmpty(&requestList) ) { return(NULL); } match = FALSE; foundNode = (struct ST_Node *) requestList.lh_Head; do { if( requestID == foundNode->requestID ) { match = TRUE; break; } foundNode = (struct ST_Node *) foundNode->node.ln_Succ; } while( ! match && foundNode->node.ln_Succ != NULL ); if( match ) { Remove((struct Node *)foundNode); CopyMem(foundNode, ©OfFoundNode, sizeof(struct ST_Node)); FreeVec(foundNode); return(©OfFoundNode); } else { return(NULL); } } void ProcessMessages(void) { ULONG sigRecvd; BOOL done = FALSE; struct ST_Message *msg; while( ! done ) { sigRecvd = Wait(SIGBREAKF_CTRL_C | 1L << publicPort->mp_SigBit); if( sigRecvd & SIGBREAKF_CTRL_C ) { done = TRUE; } else { msg = (struct ST_Message *) GetMsg(publicPort); #ifdef DEBUG printf("STD: I got a message! : %ld\n", msg->code); fflush(stdout); #endif switch( msg->code ) { case ST_START: StartTimer(msg); break; case ST_STOP: StopTimer(msg); break; case ST_CANCEL: CancelTimer(msg); break; case ST_TOGGLE: ToggleTimer(msg); break; case ST_QUERY: QueryTimer(msg); break; case ST_QUIT: done = TRUE; break; } ReplyMsg((struct Message *)msg); } } } void StartTimer(struct ST_Message *msg) { struct timeval currentTime; GetSysTime(¤tTime); RemoveRequest(msg->requestID); if( InsertRequest(msg->requestID, ¤tTime) ) { msg->startTime.tv_secs = currentTime.tv_secs; msg->startTime.tv_micro = currentTime.tv_micro; } else { msg->code = ST_ERR; } } void StopTimer(struct ST_Message *msg) { struct ST_Node *request; struct timeval currentTime; GetSysTime(¤tTime); if( (request = RemoveRequest(msg->requestID)) != NULL ) { if( ! InitMsgEndDiffTime(msg, request, ¤tTime) ) { msg->code = ST_TIMEWARP; } } else { msg->code = ST_ERR_NO_PENDING; } } void QueryTimer(struct ST_Message *msg) { struct ST_Node *request; struct timeval currentTime, trueStartTime; GetSysTime(¤tTime); if( (request = RemoveRequest(msg->requestID)) != NULL ) { trueStartTime.tv_secs = request->startTime.tv_secs; trueStartTime.tv_micro = request->startTime.tv_micro; if( ! InitMsgEndDiffTime(msg, request, ¤tTime) ) { msg->code = ST_TIMEWARP; return; } } else { msg->code = ST_ERR_NO_PENDING; return; } if( ! InsertRequest(msg->requestID, &trueStartTime) ) { msg->code = ST_ERR; } } BOOL InitMsgEndDiffTime(struct ST_Message *msg, struct ST_Node *request, struct timeval *currentTime) { if( CmpTime(currentTime, &(request->startTime)) != -1 ) { return(FALSE); } msg->endTime.tv_secs = currentTime->tv_secs; msg->endTime.tv_micro = currentTime->tv_micro; SubTime(currentTime, &(request->startTime)); msg->diffTime.tv_secs = currentTime->tv_secs; msg->diffTime.tv_micro = currentTime->tv_micro; return(TRUE); } void CancelTimer(struct ST_Message *msg) { struct ST_Node *request; if( (request = RemoveRequest(msg->requestID)) == NULL ) { msg->code = ST_ERR; } } void ToggleTimer(struct ST_Message *msg) { struct ST_Node *request; struct timeval currentTime; GetSysTime(¤tTime); if( (request = RemoveRequest(msg->requestID)) != NULL ) { if( ! InitMsgEndDiffTime(msg, request, ¤tTime) ) { msg->code = ST_TIMEWARP; } else { msg->code = ST_STOP; } } else { if( InsertRequest(msg->requestID, ¤tTime) ) { msg->startTime.tv_secs = currentTime.tv_secs; msg->startTime.tv_micro = currentTime.tv_micro; msg->code = ST_START; } else { msg->code = ST_ERR; } } } void SetupMessagePort(void) { if( (publicPort = CreatePort(ST_PORT, 0)) == NULL ) { CleanExit("Can't open public message port\n", -3); } } void SetupTimerDevice(void) { LONG error; TimerIO = (struct timerequest *) AllocVec(sizeof(struct timerequest), MEMF_PUBLIC | MEMF_CLEAR); time = (struct timeval *) AllocVec(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR); if( TimerIO == NULL || time == NULL ) { CleanExit("Out of memory\n", -1); } if( error = OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *) TimerIO, 0L) ) { CleanExit("Can't open timer device\n", -2); } TimerBase = (struct Library *) TimerIO->tr_node.io_Device; } void CleanExit(char *string, int returnCode) { struct ST_Message *msg; if( string != NULL ) { Print(APPNAME ": "); Print(string); } DestroyRequestList(); if( publicPort ) { while( msg = (struct ST_Message *) GetMsg(publicPort) ) { msg->code = ST_ERR; ReplyMsg((struct Message *)msg); } DeletePort(publicPort); } if( TimerBase ) { CloseDevice((struct IORequest *) TimerIO); } if( TimerIO ) { FreeVec(TimerIO); } if( time ) { FreeVec(time); } #ifdef MWDEBUG MWTerm(); #endif exit(returnCode); } void Print(char *string) { Write(Output(), string, strlen(string)); }