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