home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d6xx / d656 / cybercron.lha / CyberCron / Source / CyberCron.c < prev    next >
C/C++ Source or Header  |  1992-05-15  |  49KB  |  2,044 lines

  1. /* CyberCron.c
  2.  
  3.    Copyright © 1992 by Christopher A. Wichura (caw@miroc.chi.il.us).
  4.    All rights reserved.
  5. */
  6.  
  7. struct RxsLib *RexxSysBase;
  8. unsigned long ARexxLibCount = 0;
  9.  
  10. /*
  11.  * here we have storage for the current crontab file, sendmail command
  12.  * and the name of our log file
  13.  */
  14. UBYTE CronTabName[256];
  15. UBYTE SendMailCmd[256];
  16. UBYTE LogFile[256];
  17.  
  18. /*
  19.  * these are used by the ParseEvent() routine when no priority or stack size
  20.  * is specified.
  21.  */
  22. ULONG DefaultStackSize = 4096;
  23. BYTE DefaultPriority = 0;
  24.  
  25. /*
  26.  * this array is used to keep track of which job numbers are in use and
  27.  * which ones are free.
  28.  */
  29. UBYTE Jobs[256];
  30.  
  31. /*
  32.  * this global is the list header for all cybernodes.  we tell who added the
  33.  * event (crontab or via a rexx command) by whether or not the CNB_CRONTAB
  34.  * bit is set in the cn_Flags field.
  35.  */
  36. struct List EventList;
  37.  
  38. #define ESC "\x1B["
  39. #define CYBERCRON ESC "1;33;42mCyberCron" ESC "0;32;40m"
  40.  
  41. #define ARG_TEMPLATE "CRONTAB/K,LOGFILE/K,SENDMAIL/K,DEFSTACK/K/N,DEFPRI/K/N,CRONPRI/K/N"
  42. enum CmdlineReadArgs {
  43.     ARG_CRONTAB,
  44.     ARG_LOGFILE,
  45.     ARG_SENDMAIL,
  46.     ARG_STACK,
  47.     ARG_PRI,
  48.     ARG_CPRI,
  49.     ARG_sizeof
  50. };
  51.  
  52. /* extern references to our version and revision numbers */
  53. extern ULONG __far Version;
  54. extern ULONG __far Revision;
  55. extern UBYTE __far VersionID[];
  56.  
  57. /* extern references to our help text and copyright */
  58. extern UBYTE __far arg_help[];
  59. extern UBYTE __far copyright[];
  60.  
  61. /* storage for the pointer to StdErr */
  62. BPTR StdErr = NULL;
  63.  
  64. /* our old task priority */
  65. WORD OldPriority = -1;
  66.  
  67. /* for our main ReadArgs call so we can free it later */
  68. struct RDArgs *ArgsPtr = NULL;
  69. STRPTR WBArgs = NULL;
  70.  
  71. /* stuff used in launching/destroying jobs */
  72. struct SignalSemaphore jobSema;
  73. ULONG NumSystemJobs = 0;
  74. ULONG NumARexxJobs = 0;
  75.  
  76. /* Semaphore to protect Log() being called under EndSystemJob() */
  77. struct SignalSemaphore logSema;
  78.  
  79. /* stuff for our timer port */
  80. struct MsgPort *TimerPort = NULL;
  81. struct timerequest TimerIO;
  82. BOOL TimerUP = FALSE;
  83. BOOL DoingTimeRequest = FALSE;
  84.  
  85. /* stuff for our notify request */
  86. struct NotifyRequest MyNotifyRequest;
  87. UBYTE NotifySignal = -1;
  88. BOOL NotifyUP = FALSE;
  89.  
  90. /* stuff for our rexx port */
  91. struct MsgPort *RexxPort = NULL;
  92.  
  93. /* global flags */
  94. BOOL BringerDown = FALSE;    /* trying to quit ? */
  95. BOOL Suspended = FALSE;        /* currently suspended ? */
  96.  
  97. /* storage for our old pr_WindowPtr */
  98. APTR OldWindowPtr;
  99.  
  100. /* specifies the maximum number of jobs for each of the queues */
  101. struct JobQueue jobQueue[27];
  102.  
  103. /* this is our main routine */
  104. int __regargs main(char *cmdptr, int cmdlen, struct WBStartup *WBMsg)
  105. {
  106.     struct RDArgs MyArgs;
  107.     char *ArgArray[ARG_sizeof];
  108.     UBYTE TextBuf[256];
  109.     ULONG NSignal, TSignal, RSignal;
  110.     ULONG signals;
  111.     ULONG numJobs;
  112.     int index;
  113.     BPTR lock;
  114.  
  115.     struct timeval tr_time;
  116.  
  117.     OldWindowPtr = ((struct Process *)FindTask(NULL))->pr_WindowPtr;
  118.     ((struct Process *)FindTask(NULL))->pr_WindowPtr = (APTR)-1;
  119.  
  120.     StdErr = ((struct Process *)FindTask(NULL))->pr_CES;
  121.     if (StdErr == NULL)
  122.         StdErr = Output();
  123.  
  124.     if (WBMsg) {
  125.         if (!(WBArgs = WBtoCLIargs(WBMsg)))
  126.             MyExit(5);
  127.     }
  128.  
  129.     NewList(&EventList);
  130.     InitSemaphore(&jobSema);
  131.     InitSemaphore(&logSema);
  132.  
  133.     /* do the stuff needed to call ReadArgs to parse the command line */
  134.     memset ((char *)&MyArgs, 0, sizeof(struct RDArgs));
  135.     memset(ArgArray, 0, sizeof(ArgArray));
  136.  
  137.     if (!(MyArgs.RDA_ExtHelp = (UBYTE *)AllocVec(strlen(arg_help) + strlen(copyright) + (2 * strlen(CYBERCRON)) + strlen(VersionID) + 1, MEMF_CLEAR))) {
  138.         ErrorMsg("Out of memory!\n");
  139.         MyExit(5);
  140.     }
  141.  
  142.     MySPrintf((char *)MyArgs.RDA_ExtHelp, arg_help, CYBERCRON, VersionID, copyright, CYBERCRON);
  143.  
  144.     if (WBArgs) {
  145.         MyArgs.RDA_Source.CS_Buffer = WBArgs;
  146.         MyArgs.RDA_Source.CS_Length = strlen(WBArgs);
  147.         MyArgs.RDA_Source.CS_CurChr = 0L;
  148.     }
  149.  
  150.     /* now call ReadArgs to parse the command line */
  151.     ArgsPtr = ReadArgs(ARG_TEMPLATE, (LONG *)&ArgArray, &MyArgs);
  152.  
  153.     /* free the memory we used for this ReadArgs() call */
  154.     FreeVec((char *)MyArgs.RDA_ExtHelp);
  155.     FreeVec(WBArgs);
  156.     WBArgs = NULL;
  157.  
  158.     if (!ArgsPtr) {
  159.         Fault(IoErr(), NULL, TextBuf, sizeof(TextBuf));
  160.         ErrorMsg("%s\n", TextBuf);
  161.         MyExit(5);
  162.     }
  163.  
  164.     if (ArgArray[ARG_CRONTAB])
  165.         if (strlen(ArgArray[ARG_CRONTAB]) + 1 > sizeof(CronTabName)) {
  166.             ErrorMsg("Crontab filename too long.\n");
  167.             MyExit(5);
  168.         } else
  169.             strcpy(CronTabName, ArgArray[ARG_CRONTAB]);
  170.     else
  171.         strcpy(CronTabName, "S:CronTab");
  172.  
  173.     if (ArgArray[ARG_LOGFILE]) {
  174.         if (strlen(ArgArray[ARG_LOGFILE]) + 1 > sizeof(LogFile)) {
  175.             ErrorMsg("Log filename too long.\n");
  176.             MyExit(5);
  177.         } else
  178.             strcpy(LogFile, ArgArray[ARG_LOGFILE]);
  179.     }
  180.  
  181.     if (ArgArray[ARG_SENDMAIL]) {
  182.         if (strlen(ArgArray[ARG_SENDMAIL]) + 1 > sizeof(SendMailCmd)) {
  183.             ErrorMsg("SendMail command too long.\n");
  184.             MyExit(5);
  185.         } else
  186.             strcpy(SendMailCmd, ArgArray[ARG_SENDMAIL]);
  187.     }
  188.  
  189.     if (ArgArray[ARG_STACK])
  190.         DefaultStackSize = *((LONG *)ArgArray[ARG_STACK]);
  191.     else {
  192.         /* if we have a cli attached to us then get the default
  193.            stack size out of it.  Otherwise leave it be.
  194.            WBtoCLIargs() will probably have set DefaultStackSize
  195.            for us already in such a case.  If not, the hard-coded
  196.            default of 4096 will be used */
  197.  
  198.         struct CommandLineInterface *cli;
  199.  
  200.         if (cli = Cli())
  201.             DefaultStackSize = sizeof(LONG) * cli->cli_DefaultStack;
  202.     }
  203.  
  204.     if (DefaultStackSize < 2048)
  205.         DefaultStackSize = 2048;
  206.  
  207.     if (ArgArray[ARG_PRI])
  208.         DefaultPriority = *((LONG *)ArgArray[ARG_PRI]) & 0xFF;
  209.  
  210.     if (ArgArray[ARG_CPRI])
  211.         OldPriority = SetTaskPri(FindTask(NULL), *((LONG *)ArgArray[ARG_CPRI]) & 0xFF);
  212.  
  213.     /* open up our ARexx port */
  214.     if (!(RexxPort = CreatePort("CYBERCRON", 0))) {
  215.         ErrorMsg("Couldn't create ARexx port.\n");
  216.         MyExit(5);
  217.     }
  218.  
  219.     RSignal = 1L << RexxPort->mp_SigBit;
  220.  
  221.     /* open up the timer */
  222.     if (!(TimerPort = CreatePort(NULL, 0))) {
  223.         ErrorMsg("Couldn't create timer port.\n");
  224.         MyExit(5);
  225.     }
  226.  
  227.     if (OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)&TimerIO, 0)) {
  228.         ErrorMsg("Couldn't open %s.\n", TIMERNAME);
  229.         MyExit(5);
  230.     }
  231.  
  232.     TimerIO.tr_node.io_Message.mn_ReplyPort = TimerPort;
  233.     TSignal = 1L << TimerPort->mp_SigBit;
  234.     TimerUP = TRUE;
  235.  
  236.     if ((NotifySignal = AllocSignal(-1)) == -1) {
  237.         ErrorMsg("Couldn't allocate notify signal.\n");
  238.         MyExit(5);
  239.     }
  240.  
  241.     NSignal = 1L << NotifySignal;
  242.  
  243.     memset((char *)&MyNotifyRequest, 0, sizeof(struct NotifyRequest));
  244.  
  245.     MyNotifyRequest.nr_Name = CronTabName;
  246.     MyNotifyRequest.nr_Flags = NRF_SEND_SIGNAL;
  247.     MyNotifyRequest.nr_stuff.nr_Signal.nr_Task = FindTask(NULL);
  248.     MyNotifyRequest.nr_stuff.nr_Signal.nr_SignalNum = NotifySignal;
  249.  
  250.     if (StartNotify(&MyNotifyRequest) == DOSFALSE) {
  251.         ErrorMsg("Couldn't start notification on crontab file.\n");
  252.         MyExit(5);
  253.     }
  254.     NotifyUP = TRUE;
  255.  
  256.     ReadCronTab();
  257.  
  258.     /* initialize jobQueue Maximums */
  259.     for (index = 0; index < 27; index++) {
  260.         jobQueue[index].jq_Max = 1;
  261.         jobQueue[index].jq_Cur = 0;
  262.     }
  263.  
  264.     /* set queue 0 to 0xFFFFFFFF (infinite) so that jobs with no queue
  265.        specified will use one with no limits */
  266.     jobQueue[0].jq_Max = 0xFFFFFFFF;
  267.  
  268.     /* print a banner to the world saying we've started */
  269.     if (!WBMsg) {
  270.         MySPrintf(TextBuf, "%s daemon v%s started.\n", CYBERCRON, VersionID);
  271.         PutStr(TextBuf);
  272.         PutStr(copyright);
  273.         Flush(Output());
  274.     }
  275.  
  276.     /* loop forever waiting for each minute and checking to see if we need
  277.        to do anything.  also look for break, notify, etc.  BringerDown
  278.        can be set by the ARexx SHUTDOWN command so check it as well.
  279.     */
  280.  
  281.     for (;BringerDown == FALSE;) {
  282.         TimerIO.tr_node.io_Command = TR_ADDREQUEST;
  283.         TimerIO.tr_time.tv_micro = 0;
  284.         GetSysTime(&tr_time);
  285.         TimerIO.tr_time.tv_secs = 60 - tr_time.tv_secs % 60;
  286.         SetSignal(0L, TSignal);
  287.         SendIO((struct IORequest *)&TimerIO);
  288.         DoingTimeRequest = TRUE;
  289.  
  290.         signals = Wait(TSignal | NSignal | RSignal | SIGBREAKF_CTRL_C);
  291.  
  292.         if (signals & TSignal) {
  293.             GetMsg(TimerPort);
  294.             DoingTimeRequest = FALSE;
  295.             if (Suspended == FALSE)
  296.                 ScanForJobs();
  297.         }
  298.  
  299.         if (signals & NSignal)
  300.             if (lock = Lock(CronTabName, ACCESS_READ)) {
  301.                 FreeEvents(TRUE);
  302.  
  303.                 /* not really an error, but ReadCronTab() will send
  304.                    messages along the lines of parse error in line #
  305.                    if they occur so we spit this out to let them know
  306.                    why they are getting these parse errors. */
  307.  
  308.                 ErrorMsg("Crontab file modified, re-reading.\n");
  309.                 Log("Crontab file modified, re-reading.\n");
  310.  
  311.                 ReadCronTab();
  312.                 UnLock(lock);
  313.             }
  314.  
  315.         if (signals & RSignal)
  316.             HandleRexxEvents();
  317.  
  318.         if (DoingTimeRequest) {
  319.             DoingTimeRequest = FALSE;
  320.  
  321.             Disable();
  322.             if (((struct Message *)&TimerIO)->mn_Node.ln_Type != NT_REPLYMSG)
  323.                 AbortIO((struct IORequest *)&TimerIO);
  324.             Enable();
  325.  
  326.             WaitIO((struct IORequest *)&TimerIO);
  327.         }
  328.  
  329.         if (signals & SIGBREAKF_CTRL_C)
  330.             break;
  331.     }
  332.  
  333.     BringerDown = TRUE;
  334.  
  335.     ObtainSemaphore(&jobSema);
  336.     numJobs = NumSystemJobs + NumARexxJobs;
  337.     ReleaseSemaphore(&jobSema);
  338.  
  339.     if (numJobs) {
  340.         ErrorMsg("Waiting for all outstanding jobs to terminate.\n");
  341.  
  342.         for (;numJobs;) {
  343.             TimerIO.tr_node.io_Command = TR_ADDREQUEST;
  344.             TimerIO.tr_time.tv_secs = 15;
  345.             TimerIO.tr_time.tv_micro = 0;
  346.             SetSignal(0L, TSignal);
  347.             SendIO((struct IORequest *)&TimerIO);
  348.             DoingTimeRequest = TRUE;
  349.  
  350.             signals = Wait(TSignal | RSignal);
  351.  
  352.             if (signals & TSignal) {
  353.                 GetMsg(TimerPort);
  354.                 DoingTimeRequest = FALSE;
  355.             }
  356.  
  357.             if (signals & RSignal)
  358.                 HandleRexxEvents();
  359.  
  360.             if (DoingTimeRequest) {
  361.                 DoingTimeRequest = FALSE;
  362.  
  363.                 Disable();
  364.                 if (((struct Message *)&TimerIO)->mn_Node.ln_Type != NT_REPLYMSG)
  365.                     AbortIO((struct IORequest *)&TimerIO);
  366.                 Enable();
  367.  
  368.                 WaitIO((struct IORequest *)&TimerIO);
  369.             }
  370.  
  371.             ObtainSemaphore(&jobSema);
  372.             numJobs = NumSystemJobs + NumARexxJobs;
  373.             ReleaseSemaphore(&jobSema);
  374.         }
  375.     }
  376.  
  377.     MyExit(0);
  378. }
  379.  
  380. /* loop through the event list looking for any jobs to start */
  381.  
  382. void ScanForJobs(void)
  383. {
  384.     struct CyberNode *cn, *tcn;
  385.  
  386.     UBYTE    DayOfWeek;
  387.     UWORD    Month;
  388.     ULONG    Day;
  389.     ULONG    Hour;
  390.     ULONG    Min;
  391.     BOOL    WasDelayed;
  392.  
  393.     SystemTime_t st;
  394.  
  395.     /* initilize the bit fields for us to do comparisons against */
  396.     GetSystemTime(&st);
  397.  
  398.     DayOfWeek = 1L << st.st_DOW;
  399.     Month = 1L << st.st_Month;
  400.     Day = 1L << st.st_Day;
  401.     Hour = 1L << st.st_Hour;
  402.     if (st.st_Min > 31)
  403.         Min = 1L << (st.st_Min - 32);
  404.     else
  405.         Min = 1L << st.st_Min;
  406.  
  407.     /* loop through the list looking for events to do */
  408.     for (cn = (struct CyberNode *)EventList.lh_Head; cn->cn_Node.ln_Succ;
  409.         cn = (struct CyberNode *)cn->cn_Node.ln_Succ) {
  410.             if (cn->cn_Flags & CNF_DELAYED)
  411.                 WasDelayed = TRUE;
  412.             else
  413.                 WasDelayed = FALSE;
  414.  
  415.             if (cn->cn_DayOfWeek & DayOfWeek || WasDelayed)
  416.                 if (cn->cn_Month & Month || WasDelayed)
  417.                     if (cn->cn_Day & Day || WasDelayed)
  418.                         if (cn->cn_Hour & Hour || WasDelayed)
  419.                             if ((st.st_Min > 31 && cn->cn_HiMin & Min) ||
  420.                                 (st.st_Min < 32 && cn->cn_LoMin & Min) || WasDelayed) {
  421.                                 ObtainSemaphore(&jobSema);
  422.                                 if (jobQueue[cn->cn_ObeyQueue].jq_Cur < jobQueue[cn->cn_ObeyQueue].jq_Max) {
  423.                                     if (cn->cn_Flags & CNF_REXX)
  424.                                         StartRexxJob(cn);
  425.                                     else
  426.                                         StartSystemJob(cn);
  427.  
  428.                                     cn->cn_Flags &= ~CNF_DELAYED;
  429.  
  430.                                     if (cn->cn_Flags & CNF_EXECONE) {
  431.                                         tcn = (struct CyberNode *)cn->cn_Node.ln_Pred;
  432.                                         Remove((struct Node *)cn);
  433.                                         FreeVec(cn);
  434.                                         cn = tcn;
  435.                                     }
  436.                                 } else
  437.                                     cn->cn_Flags |= CNF_DELAYED;
  438.  
  439.                                 ReleaseSemaphore(&jobSema);
  440.                             }
  441.     }
  442. }
  443.  
  444. void HandleRexxEvents(void)
  445. {
  446.     struct RexxMsg *msg;
  447.  
  448.     /* this is the table of ARexx commands that CyberCron knows.  the
  449.        format is as follows:
  450.  
  451.        1) a short that is the length of the command name
  452.        2) a pointer to the command name
  453.        3) a short to descibe the args to pass to the function.
  454.         value 0 = no args
  455.         value 1 = pointer to string after command name
  456.         value 2 = an integer
  457.         value 3 = pointer to the current ARexx message
  458.        4) a short to describe the return value from the function
  459.         value 0 = no returns, set rc to zero
  460.         value 1 = return an argstring
  461.         value 2 = return integer in rc
  462.         value 3 = return an argstring already in argstring format
  463.        5) a pointer to the function
  464.     */
  465.  
  466. #define NUMRXCMDS 17
  467.     static struct {short len; char *RxCmd; short args; short ret; APTR func;} CmdTbl[NUMRXCMDS] = {
  468.         {8,  "SHUTDOWN", 0, 0, (APTR)&rx_Shutdown},
  469.         {7,  "VERSION", 0, 1, (APTR)&rx_Version},
  470.         {7,  "SUSPEND", 0, 0, (APTR)&rx_Suspend},
  471.         {6,  "RESUME", 0, 0, (APTR)&rx_Resume},
  472.         {14, "NEW_EVENT_FILE", 1, 2, (APTR)&rx_NewEventFile},
  473.         {16, "CLOSE_EVENT_FILE", 0, 0, (APTR)&rx_CloseEventFile},
  474.         {9,  "ADD_EVENT", 1, 2, (APTR)&rx_AddEvent},
  475.         {11, "SHOW_STATUS", 0, 1, (APTR)&rx_ShowStatus},
  476.         {17, "PURGE_REXX_EVENTS", 0, 0, (APTR)&rx_PurgeRexxEvents},
  477.         {17, "DELETE_REXX_EVENT", 1, 2, (APTR)&rx_DeleteRexxEvent},
  478.         {12, "DELETE_EVENT", 1, 2, (APTR)&rx_DeleteEvent},
  479.         {11, "LIST_EVENTS", 0, 3, (APTR)&rx_ListEvents},
  480.         {10, "SHOW_EVENT", 1, 1, (APTR)&rx_ShowEvent},
  481.         {12, "NEW_LOG_FILE", 1, 2, (APTR)&rx_NewLogFile},
  482.         {14, "CLOSE_LOG_FILE", 0, 0, (APTR)&rx_CloseLogFile},
  483.         {13, "SET_QUEUE_MAX", 1, 2, (APTR)&rx_SetQueueMax},
  484.         {13, "GET_QUEUE_MAX", 1, 2, (APTR)&rx_GetQueueMax}
  485.     };
  486.  
  487.     /* if we can't get ahold of the rexx system library then we
  488.        spin emptying our message port by replying to everything.
  489.        shouldn't happen, but if some idiot tries sending us messages
  490.        when they don't have ARexx then its better safe than sorry */
  491.  
  492.     if (GetARexxLib() == FALSE) {
  493.         ErrorMsg("Couldn't handle events on ARexx port -- no %s available!\n", RXSNAME);
  494.         Log("Couldn't handle events on ARexx port -- no %s available!\n", RXSNAME);
  495.  
  496.         while ((msg = (struct RexxMsg *)GetMsg(RexxPort)))
  497.             ReplyMsg((struct Message *)msg);
  498.  
  499.         return;
  500.     }
  501.  
  502.     /* we've got the ARexx library so spin on our port looking for messages.
  503.        if its a reply then a command/string we launched has finished and ARexx
  504.        is returning its results to us.  Otherwise, it's a command we are to
  505.        execute so call DoMsg() to dispatch it */
  506.  
  507.     while ((msg = (struct RexxMsg *)GetMsg(RexxPort)))
  508.         if (msg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
  509.             if (!msg->rm_Args[2])
  510.                 Log("Job %ld ended.\n", (UWORD)msg->rm_Args[1]);
  511.  
  512.             Close(msg->rm_Stdout);
  513.             Close(msg->rm_Stdin);
  514.             FreeJobNum((UWORD)msg->rm_Args[1]);
  515.             ObtainSemaphore(&jobSema);
  516.             jobQueue[(int)msg->rm_Args[3]].jq_Cur--;
  517.             ReleaseSemaphore(&jobSema);
  518.             DeleteArgstring(msg->rm_Args[0]);
  519.             DeleteRexxMsg(msg);
  520.             NumARexxJobs--;
  521.         } else
  522.             DoMsg(msg, (APTR)&CmdTbl, NUMRXCMDS, BringerDown);
  523.  
  524.     FreeARexxLib();
  525. }
  526.  
  527. void rx_Shutdown(void)
  528. {
  529.     BringerDown = TRUE;
  530. }
  531.  
  532. STRPTR rx_Version(void)
  533. {
  534.     return VersionID;
  535. }
  536.  
  537. void rx_Suspend(void)
  538. {
  539.     Suspended = TRUE;
  540. }
  541.  
  542. void rx_Resume(void)
  543. {
  544.     Suspended = FALSE;
  545. }
  546.  
  547. int rx_NewEventFile(STRPTR name)
  548. {
  549.     BPTR lock;
  550.  
  551.     if (lock = Lock(name, ACCESS_READ)) {
  552.         if (strlen(name) + 1 > sizeof(CronTabName)) {
  553.             ErrorMsg("Crontab filename too long.\n");
  554.             UnLock(lock);
  555.             return RC_WARN;
  556.         }
  557.  
  558.         FreeEvents(TRUE);
  559.  
  560.         if (NotifyUP) {
  561.             EndNotify(&MyNotifyRequest);
  562.             NotifyUP = FALSE;
  563.         }
  564.  
  565.         strcpy(CronTabName, name);
  566.  
  567.         memset((char *)&MyNotifyRequest, 0, sizeof(struct NotifyRequest));
  568.  
  569.         MyNotifyRequest.nr_Name = CronTabName;
  570.         MyNotifyRequest.nr_Flags = NRF_SEND_SIGNAL;
  571.         MyNotifyRequest.nr_stuff.nr_Signal.nr_Task = FindTask(NULL);
  572.         MyNotifyRequest.nr_stuff.nr_Signal.nr_SignalNum = NotifySignal;
  573.  
  574.         if (StartNotify(&MyNotifyRequest) == DOSFALSE) {
  575.             ErrorMsg("Couldn't start notification on crontab file.\n");
  576.             UnLock(lock);
  577.             strcpy(CronTabName, "<None>");
  578.             return RC_ERROR;
  579.         }
  580.         NotifyUP = TRUE;
  581.  
  582.         /* again, not really an error */
  583.         ErrorMsg("New crontab file specified, reading.\n");
  584.         Log("New crontab file specified, reading.\n");
  585.  
  586.         ReadCronTab();
  587.         UnLock(lock);
  588.         return RC_OK;
  589.     }
  590.  
  591.     return RC_WARN;
  592. }
  593.  
  594.  
  595. void rx_CloseEventFile(void)
  596. {
  597.     FreeEvents(TRUE);
  598.     EndNotify(&MyNotifyRequest);
  599.     NotifyUP = FALSE;
  600.     strcpy(CronTabName, "<None>");
  601. }
  602.  
  603. int rx_NewLogFile(STRPTR name)
  604. {
  605.     if (strlen(name) + 1 > sizeof(LogFile)) {
  606.         ErrorMsg("Log filename too long.\n");
  607.         return RC_WARN;
  608.     }
  609.  
  610.     ObtainSemaphore(&logSema);
  611.     strcpy(LogFile, name);
  612.     ReleaseSemaphore(&logSema);
  613.  
  614.     return RC_OK;
  615. }
  616.  
  617. void rx_CloseLogFile(void)
  618. {
  619.     ObtainSemaphore(&logSema);
  620.     LogFile[0] = '\0';
  621.     ReleaseSemaphore(&logSema);
  622. }
  623.  
  624. STRPTR rx_ShowStatus(void)
  625. {
  626.     static UBYTE result[sizeof(CronTabName) + sizeof(LogFile)+ 16];
  627.  
  628.     MySPrintf(result, "%s %s %s", (Suspended ? "SUSPENDED" : "ACTIVE"), CronTabName, (LogFile[0] ? LogFile : "<None>"));
  629.  
  630.     return result;
  631. }
  632.  
  633. void rx_PurgeRexxEvents(void)
  634. {
  635.     FreeEvents(FALSE);
  636. }
  637.  
  638. int rx_AddEvent(STRPTR event)
  639. {
  640.     struct CyberNode *cn;
  641.  
  642.     if (cn = ParseEvent(event)) {
  643.         AddTail(&EventList, (struct Node *)cn);
  644.         return RC_OK;
  645.     } else
  646.         return RC_ERROR;
  647. }
  648.  
  649. int rx_DeleteRexxEvent(STRPTR name)
  650. {
  651.     struct CyberNode *cn;
  652.  
  653.     if ((cn = FindEvent(name)) && !(cn->cn_Flags & CNF_CRONTAB)) {
  654.         Remove((struct Node *)cn);
  655.         FreeVec(cn);
  656.         return RC_OK;
  657.     }
  658.  
  659.     return RC_ERROR;
  660. }
  661.  
  662. int rx_DeleteEvent(STRPTR name)
  663. {
  664.     struct CyberNode *cn;
  665.  
  666.     if (cn = FindEvent(name)) {
  667.         Remove((struct Node *)cn);
  668.         FreeVec(cn);
  669.         return RC_OK;
  670.     }
  671.  
  672.     return RC_ERROR;
  673. }
  674.  
  675. STRPTR rx_ListEvents(void)
  676. {
  677.     struct CyberNode *cn;
  678.     STRPTR    string, string2;
  679.     ULONG    num;
  680.  
  681.     num = 0;
  682.     for (cn = (struct CyberNode *)EventList.lh_Head; cn->cn_Node.ln_Succ;
  683.         cn = (struct CyberNode *)cn->cn_Node.ln_Succ)
  684.             num++;
  685.  
  686.     if (num == 0)
  687.         return CreateArgstring("<None>", 6);
  688.  
  689.     if (!(string = CreateArgstring(NULL, num * 11)))
  690.         return NULL;
  691.  
  692.     string2 = string;
  693.     for (cn = (struct CyberNode *)EventList.lh_Head; cn->cn_Node.ln_Succ;
  694.         cn = (struct CyberNode *)cn->cn_Node.ln_Succ) {
  695.             MySPrintf(string2, "0x%08lx ", cn);
  696.             string2 += 11;
  697.         }
  698.  
  699.     *--string2 = '\0';
  700.  
  701.     return string;
  702. }
  703.  
  704. UBYTE rxSE_Buf[1024 + 12];
  705.  
  706. STRPTR rx_ShowEvent(STRPTR name)
  707. {
  708.     struct CyberNode *cn;
  709.     STRPTR    ptr;
  710.     ULONG    Bits[2];
  711.  
  712.     if (!(cn = FindEvent(name)))
  713.         return NULL;
  714.  
  715.     MySPrintf(rxSE_Buf, "0x%08lx ", cn);
  716.     ptr = &rxSE_Buf[11];
  717.  
  718.     if (cn->cn_Name) {
  719.         MySPrintf(ptr, ":NAME %s ", cn->cn_Name);
  720.         ptr += strlen(ptr);
  721.     }
  722.  
  723.     Bits[0] = cn->cn_LoMin, Bits[1] = cn->cn_HiMin;
  724.     UnParseBits(Bits, ptr, 0, 59);
  725.     ptr += strlen(ptr);
  726.     *ptr++ = ' ';
  727.  
  728.     Bits[1] = 0;
  729.  
  730.     Bits[0] = cn->cn_Hour;
  731.     UnParseBits(Bits, ptr, 0, 23);
  732.     ptr += strlen(ptr);
  733.     *ptr++ = ' ';
  734.  
  735.     Bits[0] = cn->cn_Day;
  736.     UnParseBits(Bits, ptr, 1, 31);
  737.     ptr += strlen(ptr);
  738.     *ptr++ = ' ';
  739.  
  740.     Bits[0] = cn->cn_Month;
  741.     UnParseBits(Bits, ptr, 1, 12);
  742.     ptr += strlen(ptr);
  743.     *ptr++ = ' ';
  744.  
  745.     Bits[0] = cn->cn_DayOfWeek;
  746.     UnParseBits(Bits, ptr, 0, 6);
  747.     ptr += strlen(ptr);
  748.     *ptr++ = ' ';
  749.  
  750.     if (cn->cn_Flags & CNF_EXECONE) {
  751.         strcpy(ptr, ":EXECONCE ");
  752.         ptr += 10;
  753.     }
  754.  
  755.     if (cn->cn_Flags & CNF_NOLOG) {
  756.         strcpy(ptr, ":NOLOG ");
  757.         ptr += 7;
  758.     }
  759.  
  760.     if (cn->cn_Flags & CNF_REXX) {
  761.         strcpy(ptr, ":REXX ");
  762.         ptr += 6;
  763.     } else {
  764.         if (cn->cn_Stack != DefaultStackSize) {
  765.             MySPrintf(ptr, ":STACK %ld ", cn->cn_Stack);
  766.             ptr += strlen(ptr);
  767.         }
  768.  
  769.         if (cn->cn_Priority != DefaultPriority) {
  770.             MySPrintf(ptr, ":PRI %ld ", cn->cn_Priority);
  771.             ptr += strlen(ptr);
  772.         }
  773.  
  774.         if (cn->cn_CustomShell) {
  775.             MySPrintf(ptr, ":CUSTOMSH %s ", cn->cn_CustomShell);
  776.             ptr += strlen(ptr);
  777.         } else if (cn->cn_Flags & CNF_SYSSH) {
  778.             strcpy(ptr, ":SYSSH ");
  779.             ptr += 7;
  780.         }
  781.     }
  782.  
  783.     strcpy(ptr, cn->cn_Command);
  784.     ptr += strlen(ptr);
  785.     *ptr++ = ' ';
  786.  
  787.     if (cn->cn_ReDirIn) {
  788.         MySPrintf(ptr, "< %s ", cn->cn_ReDirIn);
  789.         ptr += strlen(ptr);
  790.     }
  791.  
  792.     if (cn->cn_SendToUser && SendMailCmd[0]) {
  793.         MySPrintf(ptr, ":MAILUSER %s ", cn->cn_SendToUser);
  794.         ptr += strlen(ptr);
  795.     } else {
  796.         if (cn->cn_ReDirOut) {
  797.             MySPrintf(ptr, "%s %s ", (cn->cn_Flags & CNF_OUTAPP ? ">>" : ">"), cn->cn_ReDirOut);
  798.             ptr += strlen(ptr);
  799.         }
  800.     }
  801.  
  802.     if (cn->cn_ReDirErr) {
  803.         MySPrintf(ptr, "2%s %s ", (cn->cn_Flags & CNF_ERRAPP ? ">>" : ">"), cn->cn_ReDirErr);
  804.         ptr += strlen(ptr);
  805.     }
  806.  
  807.     if (cn->cn_ObeyQueue) {
  808.         MySPrintf(ptr, ":OBEYQUEUE %lc ", cn->cn_ObeyQueue + 'a' - 1);
  809.         ptr += 13;
  810.     }
  811.  
  812.     *--ptr = '\0';
  813.  
  814.     return rxSE_Buf;
  815. }
  816.  
  817. int rx_SetQueueMax(STRPTR argline)
  818. {
  819.     int queueNum;
  820.  
  821.     if (isalpha(*argline)) {
  822.         queueNum = tolower(*argline) - 'a' + 1;
  823.  
  824.         argline++;
  825.  
  826.         while (isspace(*argline++))
  827.             ;
  828.  
  829.         if (*--argline) {
  830.             jobQueue[queueNum].jq_Max = atol(argline);
  831.             return RC_OK;
  832.         }
  833.     }
  834.  
  835.     return RC_ERROR;
  836. }
  837.  
  838. int rx_GetQueueMax(STRPTR argline)
  839. {
  840.     int queueNum;
  841.  
  842.     if (isalpha(*argline)) {
  843.         queueNum = tolower(*argline) - 'a' + 1;
  844.         return (int)jobQueue[queueNum].jq_Max;
  845.     } else
  846.         return -1;
  847. }
  848.  
  849. STRPTR WBtoCLIargs(struct WBStartup *WBMsg)
  850. {
  851.     UBYTE *Argline, *ArglineSave, *SourcePtr;
  852.     struct Library *IconBase;
  853.     UBYTE tempChar;
  854.     BOOL sawEqual;
  855.     int index;
  856.     ULONG size;
  857.     BPTR oldDir;
  858.     struct DiskObject *dob;
  859.  
  860.     if (!(IconBase = OpenLibrary("icon.library", 37)))
  861.         return NULL;
  862.  
  863.     oldDir = CurrentDir(WBMsg->sm_ArgList[0].wa_Lock);
  864.  
  865.     if (!(dob = GetDiskObjectNew(WBMsg->sm_ArgList[0].wa_Name))) {
  866.         CloseLibrary(IconBase);
  867.         CurrentDir(oldDir);
  868.         return NULL;
  869.     }
  870.  
  871.     if (dob->do_ToolTypes)
  872.         for (size = index = 0; dob->do_ToolTypes[index]; index++)
  873.             size += strlen(dob->do_ToolTypes[index]) + 1;
  874.     else
  875.         size = 0;
  876.  
  877.     if (Argline = AllocVec(size + 2, MEMF_CLEAR)) {
  878.         ArglineSave = Argline;
  879.  
  880.         if (dob->do_ToolTypes)
  881.             for (index = 0; dob->do_ToolTypes[index]; index++) {
  882.                 SourcePtr = dob->do_ToolTypes[index];
  883.                 sawEqual = 0;
  884.                 while (tempChar = *SourcePtr++) {
  885.                     if (tempChar == '=' && !sawEqual) {
  886.                         tempChar = ' ';
  887.                         sawEqual = 1;
  888.                     }
  889.                     *Argline++ = tempChar;
  890.                 }
  891.                 *Argline++ = ' ';
  892.             }
  893.  
  894.         *Argline++ = '\n';
  895.         *Argline = '\0';
  896.     } else
  897.         ArglineSave = NULL;
  898.  
  899.     if (dob->do_StackSize)
  900.         DefaultStackSize = dob->do_StackSize;
  901.  
  902.     FreeDiskObject(dob);
  903.     CloseLibrary(IconBase);
  904.     CurrentDir(oldDir);
  905.     return ArglineSave;
  906. }
  907.  
  908. void MyExit(int error)
  909. {
  910.     if (OldPriority != -1)
  911.         SetTaskPri(FindTask(NULL), OldPriority);
  912.  
  913.     if (ARexxLibCount)
  914.         CloseLibrary((struct Library *)RexxSysBase);
  915.  
  916.     FreeEvents(TRUE);
  917.     FreeEvents(FALSE);
  918.  
  919.     if (NotifyUP)
  920.         EndNotify(&MyNotifyRequest);
  921.  
  922.     if (NotifySignal != -1)
  923.         FreeSignal(NotifySignal);
  924.  
  925.     if (TimerUP) {
  926.         if (DoingTimeRequest) {
  927.             Disable();
  928.             if (((struct Message *)&TimerIO)->mn_Node.ln_Type != NT_REPLYMSG)
  929.                 AbortIO((struct IORequest *)&TimerIO);
  930.             Enable();
  931.  
  932.             WaitIO((struct IORequest *)&TimerIO);
  933.         }
  934.  
  935.         CloseDevice((struct IORequest *)&TimerIO);
  936.     }
  937.  
  938.     if (TimerPort)
  939.         DeletePort(TimerPort);
  940.  
  941.     if (RexxPort)
  942.         DeletePort(RexxPort);
  943.  
  944.     if (WBArgs)
  945.         FreeVec(WBArgs);
  946.  
  947.     if (ArgsPtr)
  948.         FreeArgs(ArgsPtr);
  949.  
  950.     ((struct Process *)FindTask(NULL))->pr_WindowPtr = OldWindowPtr;
  951.  
  952.     XCEXIT(error);
  953. }
  954.  
  955. /*
  956.  * this routine will read the crontab file, calling ParseEvent() to create
  957.  * CyberNodes, and then link them into the event list.
  958.  * 
  959.  */
  960.  
  961. UBYTE RCT_Buf[1024];
  962.  
  963. void ReadCronTab(void)
  964. {
  965.     BPTR fh;
  966.     struct CyberNode *cn;
  967.     ULONG line = 0;
  968.     LONG error;
  969.  
  970.     if (!(fh = Open(CronTabName, MODE_OLDFILE))) {
  971.         ErrorMsg("Couldn't open file \"%s\"\n", CronTabName);
  972.         Log("Couldn't open file \"%s\"\n", CronTabName);
  973.         return;
  974.     }
  975.  
  976.     while (FGets(fh, RCT_Buf, sizeof(RCT_Buf))) {
  977.         line++;
  978.  
  979.         if (RCT_Buf[0] == '#' || RCT_Buf[0] == '\n')
  980.             continue;
  981.  
  982.         if (cn = ParseEvent(RCT_Buf)) {
  983.             cn->cn_Flags |= CNF_CRONTAB;
  984.             AddTail(&EventList, (struct Node *) cn);
  985.         } else {
  986.             ErrorMsg("Error parsing line %ld in file \"%s\"\n", line, CronTabName);
  987.             Log("Error parsing line %ld in file \"%s\"\n", line, CronTabName);
  988.         }
  989.     }
  990.  
  991.     error = IoErr();
  992.  
  993.     if (error) {
  994.         Fault(error, NULL, RCT_Buf, sizeof(RCT_Buf) - 1);
  995.         ErrorMsg("I/O Error #%ld (%s) reading line %ld in file \"%s\"\n", error, RCT_Buf, line + 1, CronTabName);
  996.         Log("I/O Error #%ld (%s) reading line %ld in file \"%s\"\n", error, RCT_Buf, line + 1, CronTabName);
  997.     }
  998.  
  999.     Close(fh);
  1000. }
  1001.  
  1002. /*
  1003.  * this routine will parse an ASCII string and make a CyberNode out of it.
  1004.  * it returns NULL if there was an error during the parse.  otherwise it
  1005.  * returns a pointer to the node.
  1006.  * 
  1007.  * note that we do something really sneaky here.  we use the ReadArgs() routine
  1008.  * to do the initial parse!.  This means that the order in which items occur
  1009.  * in a crontab entry can be virtually anything the user desires!
  1010.  */
  1011. #define PE_TEMPLATE "Event/M,</K,>/K,>>/K,2>/K,2>>/K,:NAME/K,:STACK/K/N,:PRI/K/N,:CUSTOMSH/K,:MAILUSER/K,:OBEYQUEUE/K,:SYSSH/S,:REXX/S,:NOLOG/S,:EXECONCE/S"
  1012.  
  1013. enum ParseEventReadArgs {
  1014.     PEARG_EVENT,
  1015.     PEARG_REDIRIN,
  1016.     PEARG_REDIROUT,
  1017.     PEARG_REDIROUT2,
  1018.     PEARG_REDIRERR,
  1019.     PEARG_REDIRERR2,
  1020.     PEARG_REXXNAME,
  1021.     PEARG_STACK,
  1022.     PEARG_PRI,
  1023.     PEARG_CUSTOMSH,
  1024.     PEARG_MAILUSER,
  1025.     PEARG_OBEYQUEUE,
  1026.     PEARG_SYSSH,
  1027.     PEARG_REXX,
  1028.     PEARG_NOLOG,
  1029.     PEARG_EXECONE,
  1030.     PEARG_sizeof
  1031. };
  1032.  
  1033. enum Events {
  1034.     EVENT_MINUTE,
  1035.     EVENT_HOUR,
  1036.     EVENT_DAY,
  1037.     EVENT_MONTH,
  1038.     EVENT_DOW,
  1039.     EVENT_COMMAND,
  1040.     EVENT_ARGS
  1041. };
  1042.  
  1043. UBYTE PE_Buf[sizeof(RCT_Buf) + 4];
  1044.  
  1045. struct CyberNode *ParseEvent(STRPTR event)
  1046. {
  1047.     struct CyberNode *cn;
  1048.     struct RDArgs *PArgsPtr;
  1049.     struct RDArgs MyArgs;
  1050.     char *ArgArray[PEARG_sizeof];
  1051.  
  1052.     register char **EventArgs;
  1053.     register int index;
  1054.     ULONG size;
  1055.     ULONG Bits[2];
  1056.  
  1057.     /* set up the buffer for our ReadArgs() call.  We have to copy
  1058.        over the string and put a new line at the end of it because
  1059.        of a limitation of ReadArgs().  sigh. */
  1060.  
  1061.     {
  1062.         ULONG length;
  1063.  
  1064.         length = strlen(event);
  1065.         if (length + 2 > sizeof(PE_Buf))
  1066.             return (struct CyberNode *)NULL;
  1067.  
  1068.         CopyMem(event, PE_Buf, length);
  1069.  
  1070.         PE_Buf[length++] = '\n';
  1071.         PE_Buf[length] = '\0';
  1072.  
  1073.         memset ((char *)&MyArgs, 0, sizeof(struct RDArgs));
  1074.         MyArgs.RDA_Source.CS_Buffer = PE_Buf;
  1075.         MyArgs.RDA_Source.CS_Length = length;
  1076.         MyArgs.RDA_Source.CS_CurChr = 0L;
  1077.     }
  1078.  
  1079.     /*
  1080.      * here we walk through the event line to make sure it isnt all blank.
  1081.      */
  1082.  
  1083.     while (isspace(*event))
  1084.         event++;
  1085.  
  1086.     if (!*event)
  1087.         return (struct CyberNode *)NULL;
  1088.  
  1089.     memset(ArgArray, 0, sizeof(ArgArray));
  1090.  
  1091.     /* now we call ReadArgs() */
  1092.     PArgsPtr = ReadArgs(PE_TEMPLATE, (LONG *)&ArgArray, &MyArgs);
  1093.  
  1094.     if (!PArgsPtr)
  1095.         return (struct CyberNode *)NULL;
  1096.  
  1097.     /* if they specified a name to be known as via the rexx port, make
  1098.        sure it doesn't start with 0x because that's what we use to
  1099.        prefix a hex number for nodes with no name and we don't want
  1100.        the user fooling around with names we consider private. */
  1101.  
  1102.     if (ArgArray[PEARG_REXXNAME])
  1103.         if (ArgArray[PEARG_REXXNAME][0] == '0' && tolower(ArgArray[PEARG_REXXNAME][1]) == 'x') {
  1104.             FreeArgs(PArgsPtr);
  1105.             return (struct CyberNode *)NULL;
  1106.         }
  1107.  
  1108.  
  1109.     /*
  1110.      * ok, ReadArgs has parsed the event for us.  make sure that we have
  1111.      * at least 5 time specs and a command name.
  1112.      */
  1113.     EventArgs = (char **) ArgArray[PEARG_EVENT];
  1114.  
  1115.     for (index = EVENT_MINUTE; index < EVENT_COMMAND; index++, EventArgs++)
  1116.         if (!*EventArgs || !isdigit(*EventArgs[0]) && *EventArgs[0] != '*') {
  1117.             FreeArgs(PArgsPtr);
  1118.             return (struct CyberNode *)NULL;
  1119.         }
  1120.  
  1121.     /*
  1122.      * we have the five time spec strings.  now check to make sure we have a
  1123.      * command name.  we will also calculate its size as well as the size of
  1124.      * any args for the command while we are at it
  1125.      */
  1126.  
  1127.     if (!*EventArgs) {
  1128.         FreeArgs(PArgsPtr);
  1129.         return (struct CyberNode *)NULL;
  1130.     }
  1131.  
  1132.     size = strlen(*EventArgs++) + 1;
  1133.  
  1134.     while (*EventArgs)
  1135.         size += strlen(*EventArgs++) + 1;
  1136.  
  1137.     /*
  1138.      * now figure out the memory needed to store the other textual items for
  1139.      * this node
  1140.      */
  1141.  
  1142.     if (ArgArray[PEARG_REDIRIN])
  1143.         size += strlen(ArgArray[PEARG_REDIRIN]) + 1;
  1144.  
  1145.     if (ArgArray[PEARG_REDIROUT])
  1146.         size += strlen(ArgArray[PEARG_REDIROUT]) + 1;
  1147.     if (ArgArray[PEARG_REDIROUT2]) {
  1148.         size += strlen(ArgArray[PEARG_REDIROUT2]) + 1;
  1149.         if (ArgArray[PEARG_REDIROUT])
  1150.             size -= strlen(ArgArray[PEARG_REDIROUT]) + 1;
  1151.     }
  1152.  
  1153.     if (ArgArray[PEARG_REDIRERR])
  1154.         size += strlen(ArgArray[PEARG_REDIRERR]) + 1;
  1155.     if (ArgArray[PEARG_REDIRERR2]) {
  1156.         size += strlen(ArgArray[PEARG_REDIRERR2]) + 1;
  1157.         if (ArgArray[PEARG_REDIRERR])
  1158.             size -= strlen(ArgArray[PEARG_REDIRERR]) + 1;
  1159.     }
  1160.  
  1161.     if (ArgArray[PEARG_REXXNAME])
  1162.         size += strlen(ArgArray[PEARG_REXXNAME]) + 1;
  1163.     if (ArgArray[PEARG_CUSTOMSH])
  1164.         size += strlen(ArgArray[PEARG_CUSTOMSH]) + 1;
  1165.  
  1166.     if (ArgArray[PEARG_MAILUSER])
  1167.         size += strlen(ArgArray[PEARG_MAILUSER]) + 1;
  1168.  
  1169.     if (ArgArray[PEARG_OBEYQUEUE] && !isalpha(ArgArray[PEARG_OBEYQUEUE][0])) {
  1170.         FreeArgs(PArgsPtr);
  1171.         return (struct CyberNode *)NULL;
  1172.     }
  1173.  
  1174.     if (!(cn = (struct CyberNode *) AllocVec(size + sizeof(struct CyberNode) + 1, MEMF_CLEAR))) {
  1175.         FreeArgs(PArgsPtr);
  1176.         return (struct CyberNode *)NULL;
  1177.     }
  1178.  
  1179.     /*
  1180.      * now that we have got the memory for the CyberNode start filling it
  1181.      * in.  we start by testing the STACK and PRI fields of the arg list and
  1182.      * use Atol() to get their values if present.  We then test the REXX and
  1183.      * NOLOG flags and use them to set the cn_Flags element.
  1184.      */
  1185.  
  1186.     if (ArgArray[PEARG_STACK])
  1187.         cn->cn_Stack = *((LONG *)ArgArray[PEARG_STACK]);
  1188.     if (cn->cn_Stack < 2048)
  1189.         cn->cn_Stack = DefaultStackSize;
  1190.  
  1191.     if (ArgArray[PEARG_PRI])
  1192.         cn->cn_Priority = *((LONG *)ArgArray[PEARG_PRI]) & 0xFF;
  1193.     else
  1194.         cn->cn_Priority = DefaultPriority;
  1195.  
  1196.     if (ArgArray[PEARG_OBEYQUEUE])
  1197.         cn->cn_ObeyQueue = tolower(ArgArray[PEARG_OBEYQUEUE][0]) - 'a' + 1;
  1198.  
  1199.     if (ArgArray[PEARG_REXX])
  1200.         cn->cn_Flags |= CNF_REXX;
  1201.  
  1202.     if (ArgArray[PEARG_NOLOG])
  1203.         cn->cn_Flags |= CNF_NOLOG;
  1204.  
  1205.     if (ArgArray[PEARG_SYSSH])
  1206.         cn->cn_Flags |= CNF_SYSSH;
  1207.  
  1208.     if (ArgArray[PEARG_EXECONE])
  1209.         cn->cn_Flags |= CNF_EXECONE;
  1210.  
  1211.     /*
  1212.      * now prepare to copy the textual items over into memory behind the
  1213.      * CyberNode
  1214.      */
  1215.  
  1216.     event = (char *) cn + sizeof(struct CyberNode);
  1217.  
  1218.     if (ArgArray[PEARG_REXXNAME]) {
  1219.         cn->cn_Name = event;
  1220.         size = strlen(ArgArray[PEARG_REXXNAME]) + 1;
  1221.         CopyMem(ArgArray[PEARG_REXXNAME], event, size);
  1222.         event += size;
  1223.     }
  1224.     if (ArgArray[PEARG_REDIRIN]) {
  1225.         cn->cn_ReDirIn = event;
  1226.         size = strlen(ArgArray[PEARG_REDIRIN]) + 1;
  1227.         CopyMem(ArgArray[PEARG_REDIRIN], event, size);
  1228.         event += size;
  1229.     }
  1230.     if (ArgArray[PEARG_REDIROUT] && !ArgArray[PEARG_REDIROUT2]) {
  1231.         cn->cn_ReDirOut = event;
  1232.         size = strlen(ArgArray[PEARG_REDIROUT]) + 1;
  1233.         CopyMem(ArgArray[PEARG_REDIROUT], event, size);
  1234.         event += size;
  1235.     }
  1236.     if (ArgArray[PEARG_REDIROUT2]) {
  1237.         cn->cn_ReDirOut = event;
  1238.         size = strlen(ArgArray[PEARG_REDIROUT2]) + 1;
  1239.         CopyMem(ArgArray[PEARG_REDIROUT2], event, size);
  1240.         event += size;
  1241.         cn->cn_Flags |= CNF_OUTAPP;
  1242.     }
  1243.     if (ArgArray[PEARG_REDIRERR] && !ArgArray[PEARG_REDIRERR2]) {
  1244.         cn->cn_ReDirErr = event;
  1245.         size = strlen(ArgArray[PEARG_REDIRERR]) + 1;
  1246.         CopyMem(ArgArray[PEARG_REDIRERR], event, size);
  1247.         event += size;
  1248.     }
  1249.     if (ArgArray[PEARG_REDIRERR2]) {
  1250.         cn->cn_ReDirErr = event;
  1251.         size = strlen(ArgArray[PEARG_REDIRERR2]) + 1;
  1252.         CopyMem(ArgArray[PEARG_REDIRERR2], event, size);
  1253.         event += size;
  1254.         cn->cn_Flags |= CNF_ERRAPP;
  1255.     }
  1256.     if (ArgArray[PEARG_CUSTOMSH]) {
  1257.         cn->cn_CustomShell = event;
  1258.         size = strlen(ArgArray[PEARG_CUSTOMSH]) + 1;
  1259.         CopyMem(ArgArray[PEARG_CUSTOMSH], event, size);
  1260.         event += size;
  1261.     }
  1262.     if (ArgArray[PEARG_MAILUSER]) {
  1263.         cn->cn_SendToUser = event;
  1264.         size = strlen(ArgArray[PEARG_MAILUSER]) + 1;
  1265.         CopyMem(ArgArray[PEARG_MAILUSER], event, size);
  1266.         event += size;
  1267.     }
  1268.  
  1269.     EventArgs = (char **) ArgArray[PEARG_EVENT];
  1270.     cn->cn_Command = event;
  1271.     index = EVENT_COMMAND;
  1272.     size = strlen(EventArgs[index]);
  1273.     CopyMem(EventArgs[index++], event, size);
  1274.     event += size;
  1275.     *event++ = ' ';
  1276.  
  1277.     if (EventArgs[index])
  1278.         while (EventArgs[index]) {
  1279.             size = strlen(EventArgs[index]);
  1280.             CopyMem(EventArgs[index++], event, size);
  1281.             event += size;
  1282.             *event++ = ' ';
  1283.         }
  1284.  
  1285.     *--event = 0;
  1286.  
  1287.     /*
  1288.      * Now we need to convert the ASCII time values into bitmaps to store in
  1289.      * the node.  Note that we do not check to see if the time strings are
  1290.      * within range or not.  We simply logically AND away any invalid bits
  1291.      * and use whats left.
  1292.      */
  1293.  
  1294.     ParseBits(Bits, EventArgs[EVENT_MINUTE]);
  1295.     cn->cn_LoMin = Bits[0], cn->cn_HiMin = Bits[1] & 0xFFFFFFF;
  1296.  
  1297.     ParseBits(Bits, EventArgs[EVENT_HOUR]);
  1298.     cn->cn_Hour = Bits[0] & 0xFFFFFF;
  1299.  
  1300.     ParseBits(Bits, EventArgs[EVENT_DAY]);
  1301.     cn->cn_Day = Bits[0] & 0xFFFFFFFE;
  1302.  
  1303.     ParseBits(Bits, EventArgs[EVENT_MONTH]);
  1304.     cn->cn_Month = Bits[0] & 0x1FFE;
  1305.  
  1306.     ParseBits(Bits, EventArgs[EVENT_DOW]);
  1307.     cn->cn_DayOfWeek = Bits[0] & 0x7F;
  1308.  
  1309.     FreeArgs(PArgsPtr);
  1310.     return cn;
  1311. }
  1312.  
  1313. /*
  1314.  * this will take an ASCII time string and convert it into a bitmap for
  1315.  * storage in a CyberNode
  1316.  */
  1317. void ParseBits(ULONG *bits, STRPTR tstr)
  1318. {
  1319.     register char *ptr;
  1320.     int start, end;
  1321.     int save;
  1322.  
  1323.     if (*tstr == '*') {
  1324.         bits[0] = bits[1] = 0xFFFFFFFF;
  1325.         return;
  1326.     } else
  1327.         bits[0] = bits[1] = 0;
  1328.  
  1329.     for (;;) {
  1330.         ptr = tstr;
  1331.         while (isdigit(*ptr))
  1332.             ptr++;
  1333.  
  1334.         save = *ptr, *ptr = NULL;
  1335.         end = start = atol(tstr);
  1336.  
  1337.         if (save == '-') {
  1338.             tstr = ++ptr;
  1339.  
  1340.             while (isdigit(*ptr))
  1341.                 ptr++;
  1342.  
  1343.             save = *ptr, *ptr = NULL;
  1344.             end = atol(tstr);
  1345.         }
  1346.         if (start >= 0 && end >= start)
  1347.             while (start <= end) {
  1348.                 if (start >= 64)
  1349.                     break;
  1350.  
  1351.                 if (start < 32)
  1352.                     bits[0] |= 1L << start;
  1353.                 else
  1354.                     bits[1] |= 1L << (start - 32);
  1355.  
  1356.                 start++;
  1357.             }
  1358.  
  1359.         if (!save)
  1360.             break;
  1361.         else
  1362.             tstr = ptr + 1;
  1363.     }
  1364. }
  1365.  
  1366. /* convert a bit field back into an ASCII time string */
  1367. void UnParseBits(ULONG *bits, STRPTR ptr, int lowBit, int hiBit)
  1368. {
  1369.     STRPTR tptr;
  1370.     int curBit, startBit;
  1371.     BOOL isOn, lastOn;
  1372.  
  1373.     /* first check to see if everything is specified and return "*" if so */
  1374.     for (curBit = lowBit; curBit <= hiBit; curBit++)
  1375.         if ((curBit < 32 && !(bits[0] & 1L << curBit)) || (curBit > 31 && !(bits[1] & 1L << (curBit - 32))))
  1376.             break;
  1377.  
  1378.     if (curBit == hiBit + 1) {
  1379.         strcpy(ptr, "*");
  1380.         return;
  1381.     }
  1382.  
  1383.     /* it's not "*" so walk through and build things the hard way */
  1384.     tptr = ptr;
  1385.     *tptr = 0;
  1386.     lastOn = FALSE;
  1387.  
  1388.     for (curBit = lowBit; curBit < hiBit + 2; curBit++) {
  1389.         if ((curBit < 32 && (bits[0] & 1L << curBit)) || (curBit > 31 && (bits[1] & 1L << (curBit - 32))))
  1390.             isOn = TRUE;
  1391.         else
  1392.             isOn = FALSE;
  1393.  
  1394.         if (isOn & !lastOn) {
  1395.             MySPrintf(tptr, "%ld", curBit);
  1396.             startBit = curBit;
  1397.         }
  1398.  
  1399.         if (!isOn)
  1400.             if (lastOn && startBit != curBit - 1)
  1401.                 MySPrintf(tptr, "-%ld,", curBit - 1);
  1402.             else
  1403.                 if (tptr > ptr && *(tptr - 1) != ',')
  1404.                     strcpy(tptr, ",");
  1405.  
  1406.         tptr += strlen(tptr);
  1407.         lastOn = isOn;
  1408.     }
  1409.  
  1410.     if (tptr == ptr)
  1411.         strcpy(ptr, "*");    /* Uh oh.  Somehow we have a field with nothing specified.  Fill it in with a "*" */
  1412.  
  1413.     else
  1414.         *--tptr = '\0';
  1415.  
  1416.     return;
  1417. }
  1418.  
  1419. /* find a specific CyberNode by name */
  1420.  
  1421. struct CyberNode *FindEvent(STRPTR name)
  1422. {
  1423.     struct CyberNode *cn;
  1424.     struct CyberNode *eventAddr;
  1425.  
  1426.     if (!name || name[0] == '\0')
  1427.         return (struct CyberNode *)NULL;
  1428.  
  1429.     if (name[0] == '0' && tolower(name[1]) == 'x')
  1430.         stch_l(&name[2], (long *)&eventAddr);
  1431.     else
  1432.         eventAddr = 0;
  1433.  
  1434.     for (cn = (struct CyberNode *)EventList.lh_Head; cn->cn_Node.ln_Succ;
  1435.         cn = (struct CyberNode *)cn->cn_Node.ln_Succ)
  1436.             if (cn == eventAddr || (cn->cn_Name && stricmp(name, cn->cn_Name) == 0))
  1437.                 return cn;
  1438.  
  1439.     return (struct CyberNode *)NULL;
  1440. }
  1441.  
  1442. /* this routine will walk through the even list and free all the nodes in
  1443.    it of a given type.  If called with TRUE it will free crontab entries,
  1444.    otherwise it will free Rexx entries
  1445. */
  1446.  
  1447. void FreeEvents(BOOL DoCronTabEntries)
  1448. {
  1449.     register struct CyberNode *cn;
  1450.     register struct CyberNode *tcn;
  1451.     UBYTE Flags;
  1452.  
  1453.     for (cn = (struct CyberNode *)EventList.lh_Head; cn->cn_Node.ln_Succ;
  1454.         cn = (struct CyberNode *)cn->cn_Node.ln_Succ) {
  1455.             Flags = cn->cn_Flags & CNF_CRONTAB;
  1456.  
  1457.             if ((DoCronTabEntries && Flags) || (!DoCronTabEntries && !Flags)) {
  1458.                 Remove((struct Node *)cn);
  1459.                 tcn = (struct CyberNode *)cn->cn_Node.ln_Pred;
  1460.                 FreeVec(cn);
  1461.                 cn = tcn;
  1462.             }
  1463.     }
  1464. }
  1465.  
  1466. /* this allocates an output filehandle for us which output will be piped through to sendmail over */
  1467. UBYTE SSM_Buf[sizeof(SendMailCmd) * 2];
  1468. UBYTE SSM_Buf2[50];
  1469.  
  1470. BPTR SetupSendMail(STRPTR cmdName, STRPTR userName)
  1471. {
  1472.     BPTR pfho, pfhi, ofh;
  1473.     UBYTE pipeName[36];
  1474.  
  1475.     struct timeval tr_time;
  1476.  
  1477.     GetSysTime(&tr_time);
  1478.  
  1479.     MySPrintf(pipeName, "PIPE:CyberCron.%08lx.%08lx", tr_time.tv_secs, tr_time.tv_micro);
  1480.  
  1481.     MySPrintf(SSM_Buf2, "CyberCron daemon v%s", VersionID);
  1482.     MySPrintf(SSM_Buf, SendMailCmd, "cybercron", SSM_Buf2);
  1483.  
  1484.     ofh = Open("NIL:", MODE_NEWFILE);
  1485.     if (!ofh)
  1486.         return NULL;
  1487.  
  1488.     pfho = Open(pipeName, MODE_NEWFILE);
  1489.     if (!pfho) {
  1490.         Close(ofh);
  1491.         return NULL;
  1492.     }
  1493.  
  1494.     pfhi = Open(pipeName, MODE_OLDFILE);
  1495.     if (!pfhi) {
  1496.         Close(pfho);
  1497.         Close(ofh);
  1498.         return NULL;
  1499.     }
  1500.  
  1501.     if (SystemTags(SSM_Buf, SYS_Input, pfhi, SYS_Output, ofh, SYS_Asynch, TRUE,
  1502.         NP_StackSize, 25000, NP_CopyVars, TRUE, NP_Cli, TRUE, TAG_DONE) == -1) {
  1503.         Close(pfho);
  1504.         Close(pfhi);
  1505.         Close(ofh);
  1506.         return NULL;
  1507.     }
  1508.  
  1509.     FPrintf(pfho, "To: %s\nSubject: Output of \"%s\"\n\n", userName, cmdName);
  1510.     Flush(pfho);
  1511.  
  1512.     return pfho;
  1513. }
  1514.  
  1515. /* this routine will start up a job using System() */
  1516. void StartSystemJob(struct CyberNode *cn)
  1517. {
  1518.     BPTR    ifh, ofh /*, efh */;
  1519.     struct SystemECdata *ecdata;
  1520.  
  1521.     struct TagItem tlist[20];
  1522.     int tlistIdx = 0;
  1523.  
  1524.     if (!(ecdata = AllocVec(sizeof(struct SystemECdata), MEMF_CLEAR)))
  1525.         return;
  1526.  
  1527.     ecdata->jobNo = GetJobNum();
  1528.     if (!ecdata->jobNo) {
  1529.         Log("Job table full trying to start \"%s\"\n", cn->cn_Command);
  1530.         FreeVec(ecdata);
  1531.         return;
  1532.     }
  1533.  
  1534.     if (cn->cn_ReDirIn) {
  1535.         ifh = Open(cn->cn_ReDirIn, MODE_OLDFILE);
  1536.         if (!ifh) {
  1537.             Log("Couldn't open %s redirection for \"%s\"\n", "input", cn->cn_Command);
  1538.             FreeJobNum(ecdata->jobNo);
  1539.             FreeVec(ecdata);
  1540.             return;
  1541.         }
  1542.     } else {
  1543.         ifh = Open("NIL:", MODE_OLDFILE);
  1544.         if (!ifh) {
  1545.             Log("Couldn't open %s redirection for \"%s\"\n", "input", cn->cn_Command);
  1546.             FreeJobNum(ecdata->jobNo);
  1547.             FreeVec(ecdata);
  1548.             return;
  1549.         }
  1550.     }
  1551.  
  1552.     tlist[tlistIdx].ti_Tag = SYS_Input;
  1553.     tlist[tlistIdx++].ti_Data = (ULONG)ifh;
  1554.  
  1555.     if (cn->cn_SendToUser && SendMailCmd[0]) {
  1556.         ofh = SetupSendMail(cn->cn_Command, cn->cn_SendToUser);
  1557.         if (!ofh) {
  1558.             Log("Couldn't open %s redirection for \"%s\"\n", "output", cn->cn_Command);
  1559.             Close(ifh);
  1560.             FreeJobNum(ecdata->jobNo);
  1561.             FreeVec(ecdata);
  1562.             return;
  1563.         }
  1564.     } else {
  1565.         if (cn->cn_ReDirOut) {
  1566.             ofh = Open(cn->cn_ReDirOut, (cn->cn_Flags & CNF_OUTAPP ? MODE_READWRITE : MODE_NEWFILE));
  1567.             if (!ofh) {
  1568.                 Log("Couldn't open %s redirection for \"%s\"\n", "output", cn->cn_Command);
  1569.                 Close(ifh);
  1570.                 FreeJobNum(ecdata->jobNo);
  1571.                 FreeVec(ecdata);
  1572.                 return;
  1573.             }
  1574.         } else {
  1575.             ofh = Open("NIL:", MODE_NEWFILE);
  1576.             if (!ofh) {
  1577.                 Log("Couldn't open %s redirection for \"%s\"\n", "output", cn->cn_Command);
  1578.                 Close(ifh);
  1579.                 FreeJobNum(ecdata->jobNo);
  1580.                 FreeVec(ecdata);
  1581.                 return;
  1582.             }
  1583.         }
  1584.  
  1585.         if (cn->cn_Flags & CNF_OUTAPP)
  1586.             Seek(ofh, 0, OFFSET_END);
  1587.     }
  1588.  
  1589.     tlist[tlistIdx].ti_Tag = SYS_Output;
  1590.     tlist[tlistIdx++].ti_Data = (ULONG)ofh;
  1591.  
  1592.     tlist[tlistIdx].ti_Tag = SYS_Asynch;
  1593.     tlist[tlistIdx++].ti_Data = TRUE;
  1594.  
  1595. /*
  1596.     Sigh..  Randel tells me that StdErr is pretty much unofficially "not for use"
  1597.     under 2.0.  This is here for the day that it can be used.  All that should need
  1598.     to be done is to pull out the comments.  Oh well.
  1599.  
  1600.     Do not uncomment this code.  NP_Error and NP_ErrorClose are __IGNORED__ by the
  1601.     system right now so if you specify stderr redirection, it will open the file and never
  1602.     close it.
  1603.  
  1604.     if (cn->cn_ReDirErr) {
  1605.         efh = Open(cn->cn_ReDirErr, (cn->cn_Flags & CNF_ERRAPP ? MODE_READWRITE : MODE_NEWFILE));
  1606.         if (!efh) {
  1607.             Log("Couldn't open %s redirection for \"%s\"\n", "error", cn->cn_Command);
  1608.             Close(ofh);
  1609.             Close(ifh);
  1610.             FreeJobNum(ecdata->jobNo);
  1611.             FreeVec(ecdata);
  1612.             return;
  1613.         }
  1614.  
  1615.         if (cn->cn_Flags & CNF_ERRAPP)
  1616.             Seek(efh, 0, OFFSET_END);
  1617.  
  1618.         tlist[tlistIdx].ti_Tag = NP_Error;
  1619.         tlist[tlistIdx++].ti_Data = (ULONG)efh;
  1620.         tlist[tlistIdx].ti_Tag = NP_CloseError;
  1621.         tlist[tlistIdx++].ti_Data = TRUE;
  1622.     } else {
  1623.         tlist[tlistIdx].ti_Tag = NP_Error;
  1624.         tlist[tlistIdx++].ti_Data = (ULONG)StdErr;
  1625.         tlist[tlistIdx].ti_Tag = NP_CloseError;
  1626.         tlist[tlistIdx++].ti_Data = FALSE;
  1627.     }
  1628. */
  1629.     tlist[tlistIdx].ti_Tag = NP_StackSize;
  1630.     tlist[tlistIdx++].ti_Data = (ULONG)cn->cn_Stack;
  1631.  
  1632.     tlist[tlistIdx].ti_Tag = NP_Priority;
  1633.     tlist[tlistIdx++].ti_Data = (ULONG)cn->cn_Priority;
  1634.  
  1635.     if (cn->cn_CustomShell) {
  1636.         tlist[tlistIdx].ti_Tag = SYS_CustomShell;
  1637.         tlist[tlistIdx++].ti_Data = (ULONG)cn->cn_CustomShell;
  1638.     } else {
  1639.         tlist[tlistIdx].ti_Tag = SYS_UserShell;
  1640.         tlist[tlistIdx++].ti_Data = ((cn->cn_Flags & CNF_SYSSH) ? FALSE : TRUE);
  1641.     }
  1642.  
  1643.     tlist[tlistIdx].ti_Tag = NP_Cli;
  1644.     tlist[tlistIdx++].ti_Data = TRUE;
  1645.  
  1646.     tlist[tlistIdx].ti_Tag = NP_CopyVars;
  1647.     tlist[tlistIdx++].ti_Data = TRUE;
  1648.  
  1649.     tlist[tlistIdx].ti_Tag = NP_ExitCode;
  1650.     tlist[tlistIdx++].ti_Data = (ULONG)&EndSystemJob;
  1651.  
  1652.     tlist[tlistIdx].ti_Tag = NP_ExitData;
  1653.     tlist[tlistIdx++].ti_Data = (ULONG)ecdata;
  1654.  
  1655.     tlist[tlistIdx].ti_Tag = TAG_DONE;
  1656.     tlist[tlistIdx].ti_Data = 0;
  1657.  
  1658.     ecdata->queueNo = cn->cn_ObeyQueue;
  1659.     ecdata->flags = cn->cn_Flags & CNF_NOLOG;
  1660.  
  1661.     if (SystemTagList(cn->cn_Command, tlist) == -1) {
  1662.         Log("System() failed to start \"%s\"\n", cn->cn_Command);
  1663. /*
  1664.         See above for why this is currently commented out.
  1665.  
  1666.         if (cn->cn_ReDirErr)
  1667.             Close(efh);
  1668. */
  1669.         Close(ofh);
  1670.         Close(ifh);
  1671.         FreeJobNum(ecdata->jobNo);
  1672.         FreeVec(ecdata);
  1673.         return;
  1674.     }
  1675.  
  1676.     if (!(cn->cn_Flags & CNF_NOLOG))
  1677.         Log("Job %ld started:  \"%s\"\n", ecdata->jobNo, cn->cn_Command);
  1678.  
  1679.     ObtainSemaphore(&jobSema);
  1680.     NumSystemJobs++;
  1681.     jobQueue[cn->cn_ObeyQueue].jq_Cur++;
  1682.     ReleaseSemaphore(&jobSema);
  1683. }
  1684.  
  1685. int __saveds __asm EndSystemJob(register __d0 int rc, register __d1 struct SystemECdata *data)
  1686. {
  1687.     if (!data->flags)
  1688.         Log("Job %ld ended.\n", data->jobNo);
  1689.  
  1690.     FreeJobNum(data->jobNo);
  1691.  
  1692.     ObtainSemaphore(&jobSema);
  1693.     NumSystemJobs--;
  1694.     jobQueue[data->queueNo].jq_Cur--;
  1695.     ReleaseSemaphore(&jobSema);
  1696.  
  1697.     FreeVec(data);
  1698.  
  1699.     return rc;
  1700. }
  1701.  
  1702. void StartRexxJob(struct CyberNode *cn)
  1703. {
  1704.     struct RexxMsg *msg;
  1705.     struct MsgPort *rexxServerPort;
  1706.     STRPTR    command;
  1707.     UWORD    jobNo;
  1708.     BPTR    ifh, ofh;
  1709.  
  1710.     if (GetARexxLib() == NULL) {
  1711.         ErrorMsg("Couldn't start \"%s\" -- no %s available!\n", cn->cn_Command, RXSNAME);
  1712.         Log("Couldn't start \"%s\" -- no %s available!\n", cn->cn_Command, RXSNAME);
  1713.         return;
  1714.     }
  1715.  
  1716.     jobNo = GetJobNum();
  1717.     if (!jobNo) {
  1718.         Log("Job table full trying to start \"%s\"\n", cn->cn_Command);
  1719.         FreeARexxLib();
  1720.         return;
  1721.     }
  1722.  
  1723.     if (cn->cn_ReDirIn) {
  1724.         ifh = Open(cn->cn_ReDirIn, MODE_OLDFILE);
  1725.         if (!ifh) {
  1726.             Log("Couldn't open %s redirection for \"%s\"\n", "input", cn->cn_Command);
  1727.             FreeJobNum(jobNo);
  1728.             FreeARexxLib();
  1729.             return;
  1730.         }
  1731.     } else {
  1732.         ifh = Open("NIL:", MODE_OLDFILE);
  1733.         if (!ifh) {
  1734.             Log("Couldn't open %s redirection for \"%s\"\n", "input", cn->cn_Command);
  1735.             FreeJobNum(jobNo);
  1736.             FreeARexxLib();
  1737.             return;
  1738.         }
  1739.     }
  1740.  
  1741.     if (cn->cn_SendToUser && SendMailCmd[0]) {
  1742.         ofh = SetupSendMail(cn->cn_Command, cn->cn_SendToUser);
  1743.         if (!ofh) {
  1744.             Log("Couldn't open %s redirection for \"%s\"\n", "output", cn->cn_Command);
  1745.             Close(ifh);
  1746.             FreeJobNum(jobNo);
  1747.             FreeARexxLib();
  1748.             return;
  1749.         }
  1750.     } else {
  1751.         if (cn->cn_ReDirOut) {
  1752.             ofh = Open(cn->cn_ReDirOut, (cn->cn_Flags & CNF_OUTAPP ? MODE_READWRITE : MODE_NEWFILE));
  1753.             if (!ofh) {
  1754.                 Log("Couldn't open %s redirection for \"%s\"\n", "output", cn->cn_Command);
  1755.                 Close(ifh);
  1756.                 FreeJobNum(jobNo);
  1757.                 FreeARexxLib();
  1758.                 return;
  1759.             }
  1760.         } else {
  1761.             ofh = Open("NIL:", MODE_NEWFILE);
  1762.             if (!ofh) {
  1763.                 Log("Couldn't open %s redirection for \"%s\"\n", "output", cn->cn_Command);
  1764.                 Close(ifh);
  1765.                 FreeJobNum(jobNo);
  1766.                 FreeARexxLib();
  1767.                 return;
  1768.             }
  1769.         }
  1770.  
  1771.         if (cn->cn_Flags & CNF_OUTAPP)
  1772.             Seek(ofh, 0, OFFSET_END);
  1773.     }
  1774.  
  1775.     if (cn->cn_Command[0] == '`')
  1776.         command = &cn->cn_Command[1];
  1777.     else
  1778.         command = cn->cn_Command;
  1779.  
  1780.     if (!(msg = CreateRexxMsg(RexxPort, "rexx", RXSDIR))) {
  1781.         Log("Couldn't create %s to launch \"%s\"\n", "RexxMsg", cn->cn_Command);
  1782.         Close(ofh);
  1783.         Close(ifh);
  1784.         FreeJobNum(jobNo);
  1785.         FreeARexxLib();
  1786.         return;
  1787.     }
  1788.  
  1789.     if (!(msg->rm_Args[0] = CreateArgstring(command, strlen(command)))) {
  1790.         Log("Couldn't create %s to launch \"%s\"\n", "Argstring", cn->cn_Command);
  1791.         DeleteRexxMsg(msg);
  1792.         Close(ofh);
  1793.         Close(ifh);
  1794.         FreeJobNum(jobNo);
  1795.         FreeARexxLib();
  1796.         return;
  1797.     }
  1798.  
  1799.     msg->rm_Action = RXCOMM;
  1800.     if (command != cn->cn_Command)
  1801.         msg->rm_Action |= RXFF_STRING;
  1802.  
  1803.     msg->rm_Args[1] = (STRPTR)jobNo;
  1804.     msg->rm_Args[2] = (STRPTR)(cn->cn_Flags & CNF_NOLOG);
  1805.     msg->rm_Args[3] = (STRPTR)cn->cn_ObeyQueue;
  1806.  
  1807.     msg->rm_Stdin = ifh;
  1808.     msg->rm_Stdout = ofh;
  1809.  
  1810.     Forbid();
  1811.     if (rexxServerPort = FindPort(RXSDIR))
  1812.         PutMsg(rexxServerPort, (struct Message *)msg);
  1813.     Permit();
  1814.  
  1815.     if (rexxServerPort) {
  1816.         if (!msg->rm_Args[2])
  1817.             Log("Job %ld started:  \"%s\"\n", jobNo, cn->cn_Command);
  1818.         ObtainSemaphore(&jobSema);
  1819.         NumARexxJobs++;
  1820.         jobQueue[cn->cn_ObeyQueue].jq_Cur++;
  1821.         ReleaseSemaphore(&jobSema);
  1822.     } else {
  1823.         Log("Couldn't connect to %s host to launch \"%s\"\n", RXSDIR, cn->cn_Command);
  1824.         DeleteArgstring(msg->rm_Args[0]);
  1825.         DeleteRexxMsg(msg);
  1826.         Close(ofh);
  1827.         Close(ifh);
  1828.         FreeJobNum(jobNo);
  1829.     }
  1830.  
  1831.     FreeARexxLib();
  1832. }
  1833.  
  1834. /*
  1835.  * this routine will attempt to get a job number for us. it returns the job
  1836.  * number or 0 if no jobs are free.
  1837.  */
  1838. UWORD GetJobNum(void)
  1839. {
  1840.     register UWORD job;
  1841.     register int index;
  1842.     register UBYTE mask;
  1843.  
  1844.     ObtainSemaphore(&jobSema);
  1845.  
  1846.     for (job = 0; job < sizeof(Jobs) * sizeof(UBYTE); job++) {
  1847.         index = job / sizeof(UBYTE);
  1848.         mask = 1L << (job - index * sizeof(UBYTE));
  1849.  
  1850.         if (Jobs[index] & mask)
  1851.             continue;
  1852.  
  1853.         Jobs[index] |= mask;
  1854.         ReleaseSemaphore(&jobSema);
  1855.         return (UWORD) (job + 1);
  1856.     }
  1857.  
  1858.     ReleaseSemaphore(&jobSema);
  1859.     return (UWORD) 0;
  1860. }
  1861.  
  1862. /* this routine will free a job number previously allocated */
  1863. void FreeJobNum(UWORD job)
  1864. {
  1865.     register int index;
  1866.     register UBYTE mask;
  1867.  
  1868.     if (!job || job >= sizeof(Jobs) * sizeof(UBYTE))
  1869.         return;
  1870.  
  1871.     job--;
  1872.  
  1873.     index = job / sizeof(UBYTE);
  1874.     mask = 1L << (job - index * sizeof(UBYTE));
  1875.  
  1876.     ObtainSemaphore(&jobSema);
  1877.     Jobs[index] &= ~mask;
  1878.     ReleaseSemaphore(&jobSema);
  1879. }
  1880.  
  1881. void __stdargs Log(STRPTR fmt, ...)
  1882. {
  1883.     va_list args;
  1884.     BPTR loghandle;
  1885.     struct DateTime dat;
  1886.     UBYTE Date[LEN_DATSTRING + 1];
  1887.     UBYTE Time[LEN_DATSTRING + 1];
  1888.  
  1889.     DateStamp(&dat.dat_Stamp);
  1890.     dat.dat_Format = FORMAT_DOS;
  1891.     dat.dat_Flags = 0;
  1892.     dat.dat_StrDay = NULL;
  1893.     dat.dat_StrDate = Date;
  1894.     dat.dat_StrTime = Time;
  1895.  
  1896.     memset(Date, 0, LEN_DATSTRING + 1);
  1897.     memset(Time, 0, LEN_DATSTRING + 1);
  1898.     DateToStr(&dat);
  1899.  
  1900.     va_start(args, fmt);
  1901.     if (LogFile[0]) {
  1902.         ObtainSemaphore(&logSema);
  1903.  
  1904.         if (loghandle = Open(LogFile, MODE_READWRITE)) {
  1905.             Seek(loghandle, 0, OFFSET_END);
  1906.  
  1907.             FPrintf(loghandle, "(%s %s) ", Date, Time);
  1908.             VFPrintf(loghandle, fmt, (LONG *)args);
  1909.             Close(loghandle);
  1910.         }
  1911.  
  1912.         ReleaseSemaphore(&logSema);
  1913.     }
  1914.     va_end(args);
  1915. }
  1916.  
  1917. /*
  1918.  * this function will open the ARexx library for us.  it handles nested
  1919.  * calls to open the library such that we only call OpenLibrary() one.  each
  1920.  * time a rexx command is run, we call this routine to open the library and
  1921.  * when the RexxMsg comes back we call FreeARexxLib() to decrement the nest
  1922.  * count.
  1923.  */
  1924. BOOL GetARexxLib(void)
  1925. {
  1926.     if (ARexxLibCount) {
  1927.         ARexxLibCount++;
  1928.         return TRUE;
  1929.     }
  1930.  
  1931.     if (!(RexxSysBase = (struct RxsLib *) OpenLibrary(RXSNAME, 0)))
  1932.         return FALSE;
  1933.  
  1934.     ARexxLibCount = 1;
  1935.     return TRUE;
  1936. }
  1937.  
  1938. /*
  1939.  * this routine is the opposite of GetARexxLib().  it frees a nested open
  1940.  * count for the ARexx library.  if the count goes to zero then we call
  1941.  * CloseLibrary() to free the library for real.
  1942.  */
  1943. void FreeARexxLib(void)
  1944. {
  1945.     if (!--ARexxLibCount)
  1946.         CloseLibrary((struct Library *)RexxSysBase);
  1947. }
  1948.  
  1949. void __stdargs ErrorMsg(char *fmt, ...)
  1950. /* we use this function to send error messages to StdErr.  We prefix all
  1951.    messages with "CyberCron:" since the message might be dumped into a stream
  1952.    with other people sending output to it */
  1953. {
  1954.     va_list args;
  1955.  
  1956.     if (StdErr) {
  1957.         FWrite(StdErr, "CyberCron:  ", 1, 12);
  1958.  
  1959.         va_start(args, fmt);
  1960.         VFPrintf(StdErr, (STRPTR)fmt, (LONG *)args);
  1961.         Flush(StdErr);
  1962.         va_end(args);
  1963.     }
  1964. }
  1965.  
  1966. static void __regargs MySPrintfSupp(char);
  1967.  
  1968. void __stdargs MySPrintf(STRPTR buf, STRPTR fmt, ...)
  1969. {
  1970.     va_list args;
  1971.  
  1972.     va_start(args, fmt);
  1973.     RawDoFmt(fmt, (APTR)args, MySPrintfSupp, (APTR)buf)
  1974.     va_end(args);
  1975. }
  1976.  
  1977. /* this next bit is rather SAS specific and was snitched from Loren Rittle */
  1978. static void __regargs MySPrintfSupp(char Char)
  1979. {
  1980.     __emit(0x16C0);    /* MOVE.B D0,(A3)+ */
  1981. }
  1982.  
  1983. static UBYTE DayTable[2][12] = {
  1984.     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  1985.     {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
  1986. };
  1987.  
  1988. #define BASEYEAR (1970)
  1989. #define BASEDAY  (3)
  1990. #define LEAP (ScrapA % 4 == 0 && ScrapA % 100 != 0 || ScrapA % 400 == 0)
  1991.  
  1992. void GetSystemTime(SystemTime_t *st)
  1993. {
  1994.     register ULONG c;
  1995.     register ULONG ScrapA;
  1996.     register ULONG ScrapB;
  1997.     register ULONG secs;
  1998.     struct timeval tr_time;
  1999.  
  2000.     GetSysTime(&tr_time);
  2001.  
  2002.     st->st_tvsecs = secs = tr_time.tv_secs;
  2003.  
  2004.     c = (secs / SECSINDAY) - 2251;
  2005.  
  2006.     /* start of code grabbed from Thomas Rokicki */
  2007.     ScrapA = (4 * c + 3) / 1461;
  2008.     c -= 1461 * ScrapA / 4;
  2009.     ScrapA += 1984;
  2010.     ScrapB = (5 * c + 2) / 153;
  2011.     st->st_Day = c - (153 * ScrapB + 2) / 5 + 1;
  2012.     ScrapB += 3;
  2013.     if (ScrapB > 12) {
  2014.         ScrapA += 1;
  2015.         ScrapB -= 12;
  2016.     }
  2017.     /* end of code grabbed from Rokicki */
  2018.  
  2019.     st->st_Year = ScrapA;
  2020.     st->st_Month = ScrapB;
  2021.  
  2022.     c = secs % SECSINDAY;
  2023.     ScrapA = c / 3600;
  2024.     c -= ScrapA * 3600;
  2025.     ScrapB = c / 60;
  2026.     c -= ScrapB * 60;
  2027.  
  2028.     st->st_Hour = ScrapA;
  2029.     st->st_Min = ScrapB;
  2030.     st->st_Sec = c;
  2031.  
  2032.     /* now figure out the day of the week */
  2033.  
  2034.     c = BASEDAY;
  2035.  
  2036.     for (ScrapA = BASEYEAR; ScrapA < st->st_Year; ScrapA++)
  2037.         c += LEAP ? 366 : 365;
  2038.  
  2039.     for (ScrapB = 1; ScrapB < st->st_Month; ScrapB++)
  2040.         c += DayTable[LEAP][ScrapB - 1];
  2041.  
  2042.     st->st_DOW = (c + st->st_Day) % 7;
  2043. }
  2044.