//#define DEBUG //-------------------------------------------------------------------------- // C R O N / 2 A UN*X CRON clone for OS/2 // Copyright (C) 1993-1994 Bob Hood // // Author: Bob Hood // Date : 04.03.93 // Notes : This is a CRON clone for OS/2. It uses a CRON-compatible // launch file with some OS/2-isms for controlling processes. // This is the command-line version. // // See the documentation for more information. // //-------------------------------------------------------------------------- #define EXTERN #include "cron2.h" #undef EXTERN #include #include #include // for thread functions #include // for access() #include #include #include #ifdef TCPIP #include #include #include #include #endif char *days[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; void display_queue(void); void process_file(void); PROCESS * parse_line(char *,short); void check_launch(void); void launch_app(PROCESS *); void crondie(void); void free_memory(void); PROCESS * killProc(PROCESS *theProc); void log(PSZ format,...); void flushlog(void); void clean_AT(void); static short total_OS2,total_VDM,total_PM; static short inAT,total_AT; static DATETIME dt; static char *comspec; static char logname[80]; static char cron2bakfile[80]; char cron2datfile[80]; int c2errno; struct stat startstat,newstat; // Items from the TCP/IP server module #ifdef TCPIP static unsigned short use_tcpip; extern void cron2serv(PVOID); extern void process_request(int); extern void servdie(void); #endif // Items from the AT module extern void cron2at(PVOID); extern int open_pipe(void); extern void atdie(void); //-------------------------------------------------------------------------- void main(int argc,char *argv[]) { short x; int atthread; int last_minute; unsigned long Drive,driveMap,dhold; char buf1[300]; #ifdef TCPIP int tcpthread; char strport[20]; struct sockaddr_in serv_addr; #endif printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); printf("³ C R O N / 2 ³\n"); printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); printf("³ A UN*X CRON clone for OS/2 2.x ³\n"); #ifdef TCPIP printf("³ with client/server networking ³\n"); printf("³ ³\n"); printf("³ version 1.4n ³\n"); #else printf("³ ³\n"); printf("³ version 1.4 ³\n"); #endif printf("³ ³\n"); printf("³ Copyright (C) 1993 Bob Hood ³\n"); printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\n\n"); DosQCurDisk(&Drive,&driveMap); dhold = (unsigned long)sizeof(buf1); DosQCurDir(Drive,buf1,&dhold); c2errno = NO_PROBLEM; stdbyHead = NULL; thisStdby = NULL; *logname = 0; sprintf(cron2datfile,"%c:\\%s\\CRON2.DAT",Drive + 64, buf1); sprintf(cron2bakfile,"%c:\\%s\\CRON2.BAK",Drive + 64, buf1); #ifdef TCPIP use_tcpip = FALSE; use_server = FALSE; tcpip_port = SERV_PORT_ADDR; serv_security = 0; // preset for "-n" or "-n0" #endif if(argc > 1) { x = 1; while(x < argc) { if(argv[x][0] == '-') { switch(argv[x][1]) { #ifdef TCPIP case 'n': // activate TCP/IP stuff if(use_server) // the '-s' option has already { // been specified! printf("WARNING: You have already specified the '-s' option! Ignoring\n"); break; } use_tcpip = (sock_init() == 0); if(!use_tcpip) printf("WARNING: TCP/IP protocol not available! Ignoring\n"); else { if(argv[x][2] == '\0') break; // see if server-also has been specified if(argv[x][2] == 's') use_server = CRON2_SERVER_ALSO; if(argv[x][3] == '\0') break; // a security level has been specified if(argv[x][3] == '1') ++serv_security; } break; case 's': // activate the server thread if(use_tcpip) // the 'n' option has already { // been specified! printf("WARNING: You have already specified the '-n' option! Ignoring\n"); log("WARNING: You have already specified the '-n' option! Ignoring\n"); break; } use_server = CRON2_SERVER_ONLY; use_tcpip = (sock_init() == 0); if(sock_init() != 0) { printf("FATAL: TCP/IP protocol not available! Cannot initiate server!\n"); log("FATAL: TCP/IP protocol not available! Cannot initiate server!\n"); DosExit(1,1); } if(argv[x][2] == '\0') break; // a security level has been specified if(argv[x][2] == '1') ++serv_security; break; case 'p': // port to use for TCP/IP tcpip_port = atoi(&argv[x][2]); break; #endif case 'l': sprintf(logname,"%c:\\%s\\CRON2.LOG",Drive + 64, buf1); // if(argv[x][2] != '\0') // strcpy(logname,&argv[x][2]); DosGetDateTime(&dt); log("=============================================\nCRON/2 STARTED %s %2d.%02d.%02d @ %02d:%02d\n", days[dt.weekday], dt.month,dt.day,dt.year, dt.hours,dt.minutes); break; default: printf("ERROR: Invalid command line switch: %s\n",argv[x]); #ifdef TCPIP printf(" Usage: CRON2 [[-n[s[0/1]]] [-s[0/1]] [-p]] [-l[logfile]]\n\n"); #else printf(" Usage: CRON2 [-l[logfile]]\n\n"); #endif DosExit(1,2); } } else { printf("ERROR: Invalid command line switch: %s\n",argv[x]); #ifdef TCPIP printf(" Usage: CRON2 [[-n[s[0/1]]] [-s[0/1]] [-p]] [-l[logfile]]\n\n"); #else printf(" Usage: CRON2 [-l[logfile]]\n\n"); #endif DosExit(1,2); } ++x; } } comspec = getenv("COMSPEC"); total_PM = 0; total_OS2 = 0; total_VDM = 0; DosExitList(1,(PFNEXITLIST)crondie); process_file(); if(total_PM == 0 && total_OS2 == 0 && total_VDM == 0) { #ifdef TCPIP if(use_server && serv_security == 1) { printf("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); log("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); DosExit(1,2); } #else printf("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); log("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); DosExit(1,2); #endif } stat(cron2datfile,&startstat); display_queue(); DosGetDateTime(&dt); printf("%s %2d.%02d.%02d %02d:%02d\r",days[dt.weekday], dt.month, dt.day, dt.year, dt.hours, dt.minutes); fflush(stdout); #ifdef TCPIP // initialize the TCP/IP global error bucket... // // o if this value is greater than 0, then a process needs to be // started as a result of a remote request // // o if this value is less than 0, then an error occurred in the // server thread, and it is now dead tcpip_error = 0; if(use_server) { // add a TCP/IP shut-down function to the exit list... DosExitList(1,(PFNEXITLIST)servdie); if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { printf("FATAL: TCP/IP socket() call failed!\n"); log("FATAL: TCP/IP socket() call failed!\n"); DosExit(1,1); } bzero((char *)&serv_addr,sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(tcpip_port); if(bind(sockfd,&serv_addr,sizeof(serv_addr)) < 0) { printf("FATAL: TCP/IP bind() call failed!\n"); log("FATAL: TCP/IP bind() call failed!\n"); DosExit(1,1); } listen(sockfd,5); if(use_server == CRON2_SERVER_ONLY) // we are running only as a server (no launching is initiated // locally). Run the cron2serv() function only... cron2serv(NULL); // we never reach here! else // start a thread for the TCP/IP server process... tcpthread = _beginthread(cron2serv,NULL,16384,NULL); } #endif // start the thread to deal with the AT pipe... at_error = 0; atthread = _beginthread(cron2at,NULL,16384,NULL); DosExitList(1,(PFNEXITLIST)atdie); last_minute = 0; while(TRUE) { DosGetDateTime(&dt); if(last_minute != dt.minutes) { last_minute = dt.minutes; printf("%s %2d.%02d.%02d %02d:%02d\r",days[dt.weekday], dt.month, dt.day, dt.year, dt.hours, dt.minutes); fflush(stdout); if(stdbyHead) flushlog(); stat(cron2datfile,&newstat); if(startstat.st_mtime != newstat.st_mtime) { printf("Reprocessing CRON2.DAT... \n"); log("Reprocessing CRON2.DAT...\n"); free_memory(); total_PM = 0; total_OS2 = 0; total_VDM = 0; process_file(); if(total_PM == 0 && total_OS2 == 0 && total_VDM == 0) { #ifdef TCPIP if(use_server && serv_security == 1) { printf("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); log("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); DosExit(1,2); } #else printf("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); log("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); DosExit(1,2); #endif } stat(cron2datfile,&startstat); display_queue(); } DosSleep(500L); check_launch(); } DosSleep(5000L); #ifdef TCPIP if(use_server) { // I need a semaphore to manage contention of the `tcpip_error' // variable. I'll put it in someday when I have more time (Ha!) if(tcpip_error == 0) continue; if(tcpip_error < 0) // an error { use_server = FALSE; // shut off any further checks continue; } // at this point, a remote has requested that a process // be started. the `remoteProc' pointer contains a valid // process entry, so we need to launch it and reset for the // next request... launch_app(remoteProc); remoteProc = killProc(remoteProc); tcpip_error = 0; } #endif if(at_error < 0) { // the AT pipe has failed for some reason, notify the // user printf("\nWARNING: The AT pipe thread has failed: "); log("\nWARNING: The AT pipe thread has failed: "); switch(at_error) { case PIPE_OPEN_FAILED: printf("OPEN FAILED\n"); log("OPEN FAILED\n"); break; case PIPE_BROKE: printf("PIPE BROKE\n"); log("PIPE BROKE\n"); break; case PIPE_CONNECT_FAILED: printf("PIPE CONNECT FAILED\n"); log("PIPE CONNECT FAILED\n"); break; case PIPE_READ_FAILED: printf("PIPE READ FAILED\n"); log("PIPE READ FAILED\n"); break; case PIPE_DISCONNECT_FAILED: printf("PIPE DISCONNECT FAILED\n"); log("PIPE DISCONNECT FAILED\n"); break; case PIPE_WRITE_FAILED: printf("PIPE WRITE FAILED\n"); log("PIPE WRITE FAILED\n"); break; default: printf("UNKNOWN ERROR: code=%d\n",at_error); log("UNKNOWN ERROR: code=%d\n",at_error); break; } at_error = 0; } } } //-------------------------------------------------------------------------- void display_queue(void) { printf("ÚÄ QUEUE ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); #ifdef TCPIP if(!use_server) { #endif if(total_PM == 0) printf("³ no "); else printf("³ %3d ",total_PM); printf("Presentation Manager process%s ³\n",total_PM == 1 ? " " : "es"); if(total_OS2 == 0) printf("³ no "); else printf("³ %3d ",total_OS2); printf("OS/2 process%s ³\n",total_OS2 == 1 ? " " : "es"); if(total_VDM == 0) printf("³ no "); else printf("³ %3d ",total_VDM); printf("VDM process%s ³\n",total_VDM == 1 ? " " : "es"); #ifdef TCPIP } if(use_tcpip || use_server) printf("³ ³\n"); printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\n\n"); if(use_server == CRON2_SERVER_ONLY) printf("ÄÄ SERVER ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\n"); else if(use_server == CRON2_SERVER_ALSO) printf("ÄÄ LAUNCH/SERVER ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\n"); else printf("ÄÄ LAUNCH ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\n"); #else printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\n\n"); printf("ÄÄ LAUNCH ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\n"); #endif } //-------------------------------------------------------------------------- void process_file(void) { FILE *file; char buf[300]; char comment[80]; char *p; short line,x,y; PROCESS *thisProc,*newProc; PROCESS *thisAT; procHead = NULL; atHead = NULL; inAT = FALSE; if((file = fopen(cron2datfile,"r")) == NULL) { #ifdef TCPIP if(use_server == CRON2_SERVER_ONLY) return; #endif printf("FATAL: Cannot access CRON2.DAT file\n"); log("FATAL: Cannot access CRON2.DAT file\n"); DosExit(1,1); } line = 0; while(fgets(buf,299,file) != NULL) { ++line; if(*buf == '#') { *comment = 0; if(*(buf + 1) == '$') { p = buf + 2; while(*p == ' ' || *p == '\t') ++p; strncpy(comment,p,79); } continue; // comment line } *(strchr(buf,'\n')) = 0; if(*buf == 0) { *comment = 0; continue; // blank line } if(*buf == '@') { *comment = 0; inAT = TRUE; continue; } if((newProc = parse_line(buf,line)) == NULL) { *comment = 0; if(c2errno == BAD_CRON2_ENTRY) continue; // not a fatal error fclose(file); DosExit(1,2); } if(inAT) { if(atHead == NULL) atHead = newProc; else thisAT->next = newProc; thisAT = newProc; thisAT->next = NULL; thisAT->remove = FALSE; *thisAT->comment = 0; if(*comment) { strcpy(thisAT->comment,comment); *comment = 0; } } else { if(procHead == NULL) procHead = newProc; else thisProc->next = newProc; thisProc = newProc; thisProc->next = NULL; *thisProc->comment = 0; if(*comment) { strcpy(thisProc->comment,comment); *comment = 0; } } } fclose(file); #ifdef DEBUG { short ndx; // dump the data for verification thisProc = procHead; while(thisProc) { if(thisProc->minutes[0] == -1) printf("*"); else { for(ndx = 0;ndx < 60;ndx++) { printf("%d",thisProc->minutes[ndx]); if(thisProc->minutes[ndx+1] != -1) printf(","); else break; } } printf(" "); if(thisProc->hours[0] == -1) printf("*"); else { for(ndx = 0;ndx < 24;ndx++) { printf("%d",thisProc->hours[ndx]); if(thisProc->hours[ndx+1] != -1) printf(","); else break; } } printf(" "); if(thisProc->days[0] == -1) printf("*"); else { for(ndx = 0;ndx < 31;ndx++) { printf("%d",thisProc->days[ndx]); if(thisProc->days[ndx+1] != -1) printf(","); else break; } } printf(" "); if(thisProc->months[0] == -1) printf("*"); else { for(ndx = 0;ndx < 12;ndx++) { printf("%d",thisProc->months[ndx]); if(thisProc->months[ndx+1] != -1) printf(","); else break; } } printf(" "); if(thisProc->dow[0] == -1) printf("*"); else { for(ndx = 0;ndx < 7;ndx++) { printf("%d",thisProc->dow[ndx]); if(thisProc->dow[ndx+1] != -1) printf(","); else break; } } printf(" "); switch(thisProc->type) { case CRON_TYPE_OS2: printf("OS2, "); break; case CRON_TYPE_VDM: printf("VDM, "); break; case CRON_TYPE_PM: printf("PM, "); break; } #ifdef TCPIP printf("%s, (%c) (%s) {%s}%s %s\n",thisProc->priority ? "BG":"FG", thisProc->drive + 'A', thisProc->path, thisProc->hostname, thisProc->exec, thisProc->opts); #else printf("%s, (%c) (%s) %s %s\n",thisProc->priority ? "BG":"FG", thisProc->drive + 'A', thisProc->path, thisProc->exec, thisProc->opts); #endif thisProc = thisProc->next; } } #endif } //-------------------------------------------------------------------------- PROCESS *parse_line(char *line,short ln) { char exe[256]; char opts[100]; char buf[100]; char from[5],to[5]; char *token,*subtoken,*p; char *hold; char drive; short w,x,y,z,ndx; PROCESS *theProc; if((theProc = (PROCESS *)malloc(sizeof(PROCESS))) == NULL) { log("Memory allocation failure in parse_line()\n"); c2errno = BAD_MEMORY; return(NULL); } theProc->origline = (char *)malloc(strlen(line) + 1); strcpy(theProc->origline,line); theProc->as_child = FALSE; x = 0; token = strtok(line," "); while(token) { ++x; if(inAT) { // AT line format: // 06.24.93 13:00 OS2 BG FULL- c:\uucp_os2.cmd switch(x) { case 1: strcpy(theProc->date,token); break; case 2: strcpy(theProc->time,token); break; case 3: if(stricmp(token,"VDM") == 0) theProc->type = CRON_TYPE_VDM; else if(stricmp(token,"OS2") == 0) theProc->type = CRON_TYPE_OS2; else if(stricmp(token,"PM") == 0) theProc->type = CRON_TYPE_PM; else { printf("ERROR: Invalid process type (%s) on line %d of CRON2.DAT (AT section)\n",token,ln); log("ERROR: Invalid process type (%s) on line %d of CRON2.DAT (AT section)\n",token,ln); c2errno = BAD_CRON2_ENTRY; free(theProc->origline); free(theProc); return(NULL); } break; case 4: if(stricmp(token,"FG") == 0) theProc->priority = CRON_PRIOR_FG; else if(stricmp(token,"BG") == 0) theProc->priority = CRON_PRIOR_BG; else { printf("ERROR: Invalid process priority (%s) on line %d of CRON2.DAT (AT section)\n",token,ln); log("ERROR: Invalid process priority (%s) on line %d of CRON2.DAT (AT section)\n",token,ln); c2errno = BAD_CRON2_ENTRY; free(theProc->origline); free(theProc); return(NULL); } break; case 5: theProc->minimized = FALSE; if(token[4] == '-') { theProc->minimized = TRUE; token[4] = '\0'; } if(stricmp(token,"WIND") == 0) theProc->visual = CRON_VISUAL_WIND; else if(stricmp(token,"FULL") == 0) theProc->visual = CRON_VISUAL_FULL; else { printf("ERROR: Invalid process visual (%s) on line %d of CRON2.DAT (AT section)\n",token,ln); log("ERROR: Invalid process visual (%s) on line %d of CRON2.DAT (AT section)\n",token,ln); c2errno = BAD_CRON2_ENTRY; free(theProc->origline); free(theProc); return(NULL); } break; case 6: #ifdef TCPIP theProc->hostname[0] = '\0'; #endif // because we're using strtok(), we will need to // piece together any remaining items into a single // string for parsing... strncpy(exe,token,255); *opts = 0; while((token = strtok(NULL," ")) != NULL) { strcat(opts,token); strcat(opts," "); } if(*opts) opts[strlen(opts)] = '\0'; // delete trailing space strcpy(theProc->opts,opts); theProc->drive = -1; subtoken = exe; if(*(subtoken + 1) == ':') { drive = *subtoken; if(islower(drive)) drive = _toupper(drive); theProc->drive = drive - 'A'; subtoken += 2; // skip drive designator } theProc->path[0] = '\0'; if(*subtoken == '\\') { // there's a path provided. We need to strip it // from the executable's name, and store it // make a quick check to see if there are any more // back-slashes in the string... ++subtoken; y = 1; theProc->path[0] = '\\'; while(strchr(subtoken,'\\') != NULL) { // copy up to the next back-slash while(*subtoken != '\\') { theProc->path[y] = *subtoken++; ++y; } theProc->path[y] = *subtoken; ++y; ++subtoken; } theProc->path[y] = '\0'; x = strlen(theProc->path); if(x > 1 && theProc->path[x-1] == '\\') theProc->path[x-1] = '\0'; } // what remains on subtoken is the executable name strcpy(theProc->exec,subtoken); // is this an OS/2 .CMD file? (need to explicity // launch CMD.EXE to run it or we will generate the // "bad EXE format" error) if((hold = strchr(subtoken,'.')) != NULL) { ++hold; theProc->cmdfile = (stricmp(hold,"cmd") == 0); } // make sure that this application actually exists if(access(exe,0)) { theProc->drive = -1; *theProc->path = 0; theProc->cmdfile = TRUE; } break; } token = strtok(NULL," "); continue; } switch(x) { case 1: // minutes for(ndx = 0;ndx < 60;ndx++) theProc->minutes[ndx] = -1; if(*token == '*') // every minute break; if(strchr(token,',') == NULL && strchr(token,'-') == NULL) { theProc->minutes[0] = atoi(token); break; } else { ndx = 0; subtoken = token; while(TRUE) { from[0] = '\0'; to[0] = '\0'; p = (char *)from; while(isdigit(*subtoken)) { *p = *subtoken; ++subtoken; ++p; } *p = 0; if(*subtoken == '-') { ++subtoken; p = (char *)to; while(isdigit(*subtoken)) { *p = *subtoken; ++subtoken; ++p; } *p = 0; } if(to[0] == '\0') { // no range operator... theProc->minutes[ndx] = atoi(from); ++ndx; } else { y = atoi(from); z = atoi(to); for(w = y;w <= z;w++) { theProc->minutes[ndx] = w; ++ndx; } } if(*subtoken != ',') break; ++subtoken; } token = subtoken; } break; case 2: // hours for(ndx = 0;ndx < 24;ndx++) theProc->hours[ndx] = -1; if(*token == '*') // every hour break; if(strchr(token,',') == NULL && strchr(token,'-') == NULL) { theProc->hours[0] = atoi(token); break; } else { ndx = 0; subtoken = token; while(TRUE) { from[0] = '\0'; to[0] = '\0'; p = (char *)from; while(isdigit(*subtoken)) { *p = *subtoken; ++subtoken; ++p; } *p = 0; if(*subtoken == '-') { ++subtoken; p = (char *)to; while(isdigit(*subtoken)) { *p = *subtoken; ++subtoken; ++p; } *p = 0; } if(to[0] == '\0') { // no range operator... theProc->hours[ndx] = atoi(from); ++ndx; } else { y = atoi(from); z = atoi(to); for(w = y;w <= z;w++) { theProc->hours[ndx] = w; ++ndx; } } if(*subtoken != ',') break; ++subtoken; } token = subtoken; } break; case 3: // days for(ndx = 0;ndx < 31;ndx++) theProc->days[ndx] = -1; if(*token == '*') // every day break; if(strchr(token,',') == NULL && strchr(token,'-') == NULL) { theProc->days[0] = atoi(token); break; } else { ndx = 0; subtoken = token; while(TRUE) { from[0] = '\0'; to[0] = '\0'; p = (char *)from; while(isdigit(*subtoken)) { *p = *subtoken; ++subtoken; ++p; } *p = 0; if(*subtoken == '-') { ++subtoken; p = (char *)to; while(isdigit(*subtoken)) { *p = *subtoken; ++subtoken; ++p; } *p = 0; } if(to[0] == '\0') { // no range operator... theProc->days[ndx] = atoi(from); ++ndx; } else { y = atoi(from); z = atoi(to); for(w = y;w <= z;w++) { theProc->days[ndx] = w; ++ndx; } } if(*subtoken != ',') break; ++subtoken; } token = subtoken; } break; case 4: // months for(ndx = 0;ndx < 12;ndx++) theProc->months[ndx] = -1; if(*token == '*') // every month break; if(strchr(token,',') == NULL && strchr(token,'-') == NULL) { theProc->months[0] = atoi(token); break; } else { ndx = 0; subtoken = token; while(TRUE) { from[0] = '\0'; to[0] = '\0'; p = (char *)from; while(isdigit(*subtoken)) { *p = *subtoken; ++subtoken; ++p; } *p = 0; if(*subtoken == '-') { ++subtoken; p = (char *)to; while(isdigit(*subtoken)) { *p = *subtoken; ++subtoken; ++p; } *p = 0; } if(to[0] == '\0') { // no range operator... theProc->months[ndx] = atoi(from); ++ndx; } else { y = atoi(from); z = atoi(to); for(w = y;w <= z;w++) { theProc->months[ndx] = w; ++ndx; } } if(*subtoken != ',') break; ++subtoken; } token = subtoken; } break; case 5: // dow's for(ndx = 0;ndx < 7;ndx++) theProc->dow[ndx] = -1; if(*token == '*') // every day-of-the-week break; if(strchr(token,',') == NULL && strchr(token,'-') == NULL) { theProc->dow[0] = atoi(token); break; } else { ndx = 0; subtoken = token; while(TRUE) { from[0] = '\0'; to[0] = '\0'; p = (char *)from; while(isdigit(*subtoken)) { *p = *subtoken; ++subtoken; ++p; } *p = 0; if(*subtoken == '-') { ++subtoken; p = (char *)to; while(isdigit(*subtoken)) { *p = *subtoken; ++subtoken; ++p; } *p = 0; } if(to[0] == '\0') { // no range operator... theProc->dow[ndx] = atoi(from); ++ndx; } else { y = atoi(from); z = atoi(to); for(w = y;w <= z;w++) { theProc->dow[ndx] = w; ++ndx; } } if(*subtoken != ',') break; ++subtoken; } token = subtoken; } break; case 6: // process type (VDM, OS2, or PM) if(stricmp(token,"VDM") == 0) { theProc->type = CRON_TYPE_VDM; ++total_VDM; } else if(stricmp(token,"OS2") == 0) { theProc->type = CRON_TYPE_OS2; ++total_OS2; } else if(stricmp(token,"PM") == 0) { theProc->type = CRON_TYPE_PM; ++total_PM; } else { printf("ERROR: Invalid process type (%s) on line %d of CRON2.DAT\n",token,ln); log("ERROR: Invalid process type (%s) on line %d of CRON2.DAT\n",token,ln); c2errno = BAD_CRON2_ENTRY; free(theProc->origline); free(theProc); return(NULL); } break; case 7: // priority (FG or BG) if(stricmp(token,"FG") == 0) theProc->priority = CRON_PRIOR_FG; else if(stricmp(token,"BG") == 0) theProc->priority = CRON_PRIOR_BG; else { printf("ERROR: Invalid process priority (%s) on line %d of CRON2.DAT\n",token,ln); log("ERROR: Invalid process priority (%s) on line %d of CRON2.DAT\n",token,ln); switch(theProc->type) { case CRON_TYPE_PM: --total_PM; break; case CRON_TYPE_OS2: --total_OS2; break; case CRON_TYPE_VDM: --total_VDM; break; } c2errno = BAD_CRON2_ENTRY; free(theProc->origline); free(theProc); return(NULL); } break; case 8: // visual (WIND or FULL, optionally minimized [-]) theProc->minimized = FALSE; if(token[4] == '-') { theProc->minimized = TRUE; token[4] = '\0'; } if(stricmp(token,"WIND") == 0) theProc->visual = CRON_VISUAL_WIND; else if(stricmp(token,"FULL") == 0) theProc->visual = CRON_VISUAL_FULL; else { printf("ERROR: Invalid process visual (%s) on line %d of CRON2.DAT\n",token,ln); log("ERROR: Invalid process visual (%s) on line %d of CRON2.DAT\n",token,ln); switch(theProc->type) { case CRON_TYPE_PM: --total_PM; break; case CRON_TYPE_OS2: --total_OS2; break; case CRON_TYPE_VDM: --total_VDM; break; } c2errno = BAD_CRON2_ENTRY; free(theProc->origline); free(theProc); return(NULL); } break; case 9: // line to execute #ifdef TCPIP // see if there is a hostname attached to this // line (denoted by braces surrounding the host name) theProc->hostname[0] = '\0'; if(*token == '{') { // suck out the host name... ++token; subtoken = theProc->hostname; x = 0; while(*token != '}') { *subtoken++ = *token++; ++x; if(x == (HOSTNAMELEN - 1)) { while(*token != '}') ++token; break; } } *subtoken = 0; ++token; // skip last brace } if(theProc->hostname[0] && !use_tcpip) { // we have not initialized TCP/IP, so host names are // superfluous theProc->hostname[0] = '\0'; printf("WARNING: hostname found without TCP/IP initialization! Ignoring\n"); log("WARNING: hostname found without TCP/IP initialization! Ignoring\n"); } #endif // because we're using strtok(), we will need to // piece together any remaining items into a single // string for parsing... strncpy(exe,token,255); *opts = 0; while((token = strtok(NULL," ")) != NULL) { strcat(opts,token); strcat(opts," "); } if(*opts) opts[strlen(opts)] = '\0'; // delete trailing space strcpy(theProc->opts,opts); theProc->drive = -1; subtoken = exe; if(*(subtoken + 1) == ':') { drive = *subtoken; if(islower(drive)) drive = _toupper(drive); theProc->drive = drive - 'A'; subtoken += 2; // skip drive designator } theProc->path[0] = '\0'; if(*subtoken == '\\') { // there's a path provided. We need to strip it // from the executable's name, and store it // make a quick check to see if there are any more // back-slashes in the string... ++subtoken; y = 1; theProc->path[0] = '\\'; while(strchr(subtoken,'\\') != NULL) { // copy up to the next back-slash while(*subtoken != '\\') { theProc->path[y] = *subtoken++; ++y; } theProc->path[y] = *subtoken; ++y; ++subtoken; } theProc->path[y] = '\0'; x = strlen(theProc->path); if(x > 1 && theProc->path[x-1] == '\\') theProc->path[x-1] = '\0'; } // what remains on subtoken is the executable name strcpy(theProc->exec,subtoken); // is this an OS/2 .CMD file? (need to explicity // launch CMD.EXE to run it or we will generate the // "bad EXE format" error) if((hold = strchr(subtoken,'.')) != NULL) { ++hold; theProc->cmdfile = (stricmp(hold,"cmd") == 0); } // make sure that this application actually exists if(access(exe,0)) { theProc->drive = -1; *theProc->path = 0; theProc->cmdfile = TRUE; } break; } token = strtok(NULL," "); } return(theProc); } //-------------------------------------------------------------------------- void check_launch(void) { PROCESS *thisProc,*thisAT; short ndx; short launched_AT; char date[10],time[7]; printf("%s %2d.%02d.%02d %02d:%02d\r",days[dt.weekday], dt.month, dt.day, dt.year, dt.hours, dt.minutes); fflush(stdout); // look for launchable processes thisProc = procHead; while(thisProc) { // walk through each minute if(thisProc->minutes[0] != -1) // -1 means match any (we fall through) { for(ndx = 0;ndx < 60;ndx++) { if(thisProc->minutes[ndx] == dt.minutes || thisProc->minutes[ndx] == -1) break; } if(thisProc->minutes[ndx] == -1) // we didn't match { thisProc = thisProc->next; continue; // loop back up top } } // walk through each hour if(thisProc->hours[0] != -1) { for(ndx = 0;ndx < 24;ndx++) { if(thisProc->hours[ndx] == dt.hours || thisProc->hours[ndx] == -1) break; } if(thisProc->hours[ndx] == -1) // we didn't match { thisProc = thisProc->next; continue; // loop back up top } } // walk through each day if(thisProc->days[0] != -1) { for(ndx = 0;ndx < 31;ndx++) { if(thisProc->days[ndx] == dt.day || thisProc->days[ndx] == -1) break; } if(thisProc->days[ndx] == -1) // we didn't match { thisProc = thisProc->next; continue; // loop back up top } } // walk through each month if(thisProc->months[0] != -1) { for(ndx = 0;ndx < 31;ndx++) { if(thisProc->months[ndx] == dt.month || thisProc->months[ndx] == -1) break; } if(thisProc->months[ndx] == -1) // we didn't match { thisProc = thisProc->next; continue; // loop back up top } } // walk through each dow if(thisProc->dow[0] != -1) { for(ndx = 0;ndx < 7;ndx++) { if(thisProc->dow[ndx] == (unsigned short)dt.weekday || thisProc->dow[ndx] == -1) break; } if(thisProc->dow[ndx] == -1) // we didn't match { thisProc = thisProc->next; continue; // loop back up top } } // EVERYTHING MATCHED! Launch this puppy! printf("Launching %s @ %02d:%02d", thisProc->exec, dt.hours, dt.minutes); if(*thisProc->comment) log("\n# %s",thisProc->comment); log("Launching %s %2d.%02d.%02d @ %02d:%02d", thisProc->exec, dt.month,dt.day,dt.year, dt.hours,dt.minutes); #ifdef TCPIP if(*thisProc->hostname) { printf(" on host \"%s\"",thisProc->hostname); log(" on host \"%s\"",thisProc->hostname); } #endif printf("..."); log("..."); fflush(stdout); launch_app(thisProc); // ...and skip to the next entry thisProc = thisProc->next; // don't be a CPU hog - sleep for half a second between launches DosSleep(500L); } // look for launchable AT processes if(atHead) { sprintf(date,"%02d.%02d.%02d",dt.month,dt.day,dt.year - 1900); sprintf(time,"%02d:%02d",dt.hours,dt.minutes); launched_AT = FALSE; thisProc = atHead; while(thisProc) { if(strcmp(thisProc->time,time) != 0) { thisProc = thisProc->next; continue; // loop back up top } if(strcmp(thisProc->date,date) != 0) { thisProc = thisProc->next; continue; // loop back up top } // EVERYTHING MATCHED! Launch this puppy! printf("Launching AT command %s",thisProc->exec); log("Launching AT command %s",thisProc->exec); printf("..."); log("..."); fflush(stdout); launch_app(thisProc); // strip this AT entry from the CRON2.DAT file thisProc->remove = launched_AT = TRUE; // ...and skip to the next entry thisProc = thisProc->next; // don't be a CPU hog - sleep for half a second between launches DosSleep(500L); } if(launched_AT) clean_AT(); } DosGetDateTime(&dt); printf("%s %2d.%02d.%02d %02d:%02d\r",days[dt.weekday], dt.month, dt.day, dt.year, dt.hours, dt.minutes); fflush(stdout); } //-------------------------------------------------------------------------- void launch_app(PROCESS *theProc) { unsigned long newSID,hold; PID newPID; unsigned rc; unsigned long oldDrive; unsigned long driveMap; char buf1[300]; char exec[256],options[256]; STARTDATA sdata; #ifdef TCPIP if(*theProc->hostname) { // we need to send this process to a remote host... // first, resolve the host name... struct sockaddr_in serv_addr; struct hostent *host; short sockfd; // Fill in the structure "serv_addr" with the address of the // server that we want to connect with. bzero((char *)&serv_addr,sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(tcpip_port); if((serv_addr.sin_addr.s_addr = inet_addr(theProc->hostname)) == INADDR_NONE) { // host's name is not in dotted-decimal, look it up if((host = gethostbyname(theProc->hostname)) == NULL) { // we can't grok this host printf("ERROR: unable to resolve hostname \"%s\"!\n",theProc->hostname); log("ERROR: unable to resolve hostname \"%s\"!\n",theProc->hostname); return; } serv_addr.sin_family = host->h_addrtype; bcopy(host->h_addr,(caddr_t)&serv_addr.sin_addr,host->h_length); } // Open a TCP socket if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { printf("ERROR: unable to open socket to server \"%s\"!\n",theProc->hostname); log("ERROR: unable to open socket to server \"%s\"!\n",theProc->hostname); return; } // Connect to the server if(connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0) { printf("ERROR: unable to connect to server \"%s\"!\n",theProc->hostname); log("ERROR: unable to connect to server \"%s\"!\n",theProc->hostname); return; } sprintf(buf1,"%02d",strlen(theProc->origline)); rc = send(sockfd,buf1,2,0); if(rc != 2) { printf("ERROR: error in send() call to server \"%s\": %d\n",theProc->hostname,rc); log("ERROR: error in send() call to server \"%s\": %d\n",theProc->hostname,rc); return; } rc = send(sockfd,theProc->origline,strlen(theProc->origline),0); if(rc != strlen(theProc->origline)) { printf("ERROR: error in send() call to server \"%s\": %d\n",theProc->hostname,rc); log("ERROR: error in send() call to server \"%s\": %d\n",theProc->hostname,rc); return; } soclose(sockfd); return; } #endif // set the drive and path before we launch... DosQCurDisk(&oldDrive,&driveMap); hold = (unsigned long)sizeof(buf1); DosQCurDir(oldDrive,buf1,&hold); if(theProc->drive != -1) { rc = DosSelectDisk(theProc->drive + 1); if(rc) { printf("ERROR: DosSelectDisk failed: %d (disk: %c)\n",rc,theProc->drive + 1); log("ERROR: DosSelectDisk failed: %d (disk: %c)\n",rc,theProc->drive + 1); return; } } if(*theProc->path) { rc = DosSetCurrentDir(theProc->path); if(rc) { DosSelectDisk(oldDrive); printf("ERROR: DosChdir failed: %d (path: %s)\n",rc,theProc->path); log("ERROR: DosChdir failed: %d (path: %s)\n",rc,theProc->path); return; } } if(theProc->cmdfile) { strcpy(exec,comspec); strcpy(options," /c "); // this works with both CMD and 4OS2-32 strcat(options,theProc->exec); strcat(options," "); strcat(options,theProc->opts); } else { strcpy(exec,theProc->exec); strcpy(options,theProc->opts); } sdata.Length = 50; if(theProc->as_child) sdata.Related = SSF_RELATED_CHILD; else sdata.Related = SSF_RELATED_INDEPENDENT; sdata.FgBg = theProc->priority ? SSF_FGBG_BACK : SSF_FGBG_FORE; if(theProc->minimized) sdata.FgBg = SSF_FGBG_BACK; sdata.TraceOpt = SSF_TRACEOPT_NONE; sdata.PgmTitle = NULL; // use program name as title sdata.PgmName = exec; sdata.PgmInputs = options; sdata.TermQ = NULL; // no queue for termination notification sdata.Environment = NULL; // pass a copy of the parent's environment sdata.InheritOpt = SSF_INHERTOPT_PARENT; switch(theProc->type) { case CRON_TYPE_OS2: sdata.SessionType = SSF_TYPE_FULLSCREEN; break; case CRON_TYPE_VDM: sdata.SessionType = SSF_TYPE_VDM; break; case CRON_TYPE_PM: sdata.SessionType = SSF_TYPE_PM; break; } sdata.IconFile = NULL; sdata.PgmHandle = 0; sdata.PgmControl = 0; switch(theProc->visual) { case CRON_VISUAL_WIND: if(sdata.SessionType == SSF_TYPE_FULLSCREEN) // OS/2 sdata.SessionType = SSF_TYPE_WINDOWABLEVIO; else if(sdata.SessionType == SSF_TYPE_VDM) sdata.SessionType = SSF_TYPE_WINDOWEDVDM; // an SSF_TYPE_PM type is already windowed... break; case CRON_VISUAL_FULL: if(sdata.SessionType == SSF_TYPE_PM) sdata.PgmControl = SSF_CONTROL_MAXIMIZE; break; } if(theProc->minimized) sdata.PgmControl = SSF_CONTROL_MINIMIZE; sdata.InitXPos = 0; sdata.InitYPos = 0; sdata.InitXSize = 0; sdata.InitYSize = 0; rc = DosStartSession(&sdata,&newSID,&newPID); // reset the drive and path... DosSelectDisk(oldDrive); DosSetCurrentDir(buf1); if(rc) { printf("ERROR: DosStartSession failed: %d\n",rc); log("ERROR: DosStartSession failed: %d\n",rc); } else { // save these for the pending termination... theProc->pid = newPID; theProc->sid = newSID; printf("done!\n"); log("done!\n"); if(*theProc->comment) log("\n"); } } //-------------------------------------------------------------------------- // Clean-up routines for leaving CRON2 void crondie(void) { printf("Cleaning up CRON/2..."); log("Cleaning up CRON/2..."); fflush(stdout); free_memory(); clean_AT(); printf("done!\n"); log("done!\n"); } //-------------------------------------------------------------------------- void free_memory(void) { PROCESS *nextProc; while(procHead) { nextProc = procHead->next; killProc(procHead); procHead = nextProc; } procHead = NULL; while(atHead) { nextProc = atHead->next; killProc(atHead); atHead = nextProc; } atHead = NULL; } //-------------------------------------------------------------------------- PROCESS * killProc(PROCESS *theProc) { free(theProc->origline); free(theProc); return(NULL); } //-------------------------------------------------------------------------- void log(PSZ format, ...) { va_list pArg; char buf[300]; LONG lCount; FILE *logfile; MSGSTANDBY *newStdby; if(!*logname) return; // logging is not active... logfile = fopen(logname,"a"); va_start(pArg, format); lCount = vsprintf(buf, format, pArg); va_end(pArg); if(logfile != NULL) { if(stdbyHead) { // dump the pending messages first while(stdbyHead) { fprintf(logfile,"%s",stdbyHead->message); thisStdby = stdbyHead->next; free(stdbyHead); stdbyHead = thisStdby; } stdbyHead = NULL; } fprintf(logfile,"%s",buf); fclose(logfile); } else { // the log file is probably open by // another process right now, so // put the entry on the standby list newStdby = (MSGSTANDBY *) malloc(sizeof(MSGSTANDBY)); if(newStdby == NULL) return; if(stdbyHead == NULL) stdbyHead = newStdby; else thisStdby->next = newStdby; thisStdby = newStdby; thisStdby->next = NULL; strncpy(thisStdby->message,buf,80); } } //-------------------------------------------------------------------------- void flushlog(void) { FILE *logfile; if(!*logname) return; if((logfile = fopen(logname,"a")) != NULL) { // dump the pending messages while(stdbyHead) { fprintf(logfile,"%s",stdbyHead->message); thisStdby = stdbyHead->next; free(stdbyHead); stdbyHead = thisStdby; } stdbyHead = NULL; fclose(logfile); } } //-------------------------------------------------------------------------- void clean_AT(void) { char line[300]; FILE *cron2; FILE *cron2bak; PROCESS *thisAT; inAT = FALSE; if((cron2 = fopen(cron2datfile,"r")) == NULL) return; if((cron2bak = fopen(cron2bakfile,"w")) == NULL) { fclose(cron2); return; } while(fgets(line,299,cron2) != NULL) { *(strchr(line,'\n')) = 0; if(*line == '@') { inAT = TRUE; fprintf(cron2bak,"%s\n",line); continue; } if(!inAT || *line == '#') { fprintf(cron2bak,"%s\n",line); continue; } thisAT = atHead; while(thisAT) { if(thisAT->remove && strcmp(line,thisAT->origline) == 0) { *line = 0; break; } thisAT = thisAT->next; } if(*line) fprintf(cron2bak,"%s\n",line); } fclose(cron2); fclose(cron2bak); unlink(cron2datfile); rename(cron2bakfile,cron2datfile); free_memory(); total_PM = 0; total_OS2 = 0; total_VDM = 0; process_file(); if(total_PM == 0 && total_OS2 == 0 && total_VDM == 0) { #ifdef TCPIP if(use_server && serv_security == 1) { printf("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); log("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); DosExit(1,2); } #else printf("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); log("FATAL: Nothing to do! No processes configured in CRON2.DAT!\n\n"); DosExit(1,2); #endif } stat(cron2datfile,&startstat); }