home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: SysTools / SysTools.zip / pmcron03.zip / process.c < prev    next >
C/C++ Source or Header  |  1996-05-09  |  19KB  |  371 lines

  1. /* Copyright (c) 1995 Florian Große-Coosmann, RCS section at the eof         */
  2. /* This module includes functions starting a job and copying its output      */
  3. /* to our output file.                                                       */
  4. #define INCL_NOPM
  5. #define INCL_NOCOMMON
  6. #define INCL_DOSSEMAPHORES
  7. #define INCL_DOSFILEMGR
  8. #define INCL_DOSPROCESS
  9. #include <os2.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <time.h>
  14. #include <process.h>
  15. #include <fcntl.h>
  16. #include <share.h>
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include <io.h>
  20. #include "server.h"
  21.  
  22. typedef struct _PROC {                  /* one process slot                  */
  23.    struct _PROC  *next;
  24.    int            pid;                  /* process ID, the following values  */
  25.                                         /* are reserved:                     */
  26.                                         /* 0 = never started                 */
  27.                                         /* -1 = can't start, PROC may been   */
  28.                                         /*      deleted.                     */
  29.    int            rc;                   /* return code                       */
  30.    int            reason;               /* reason of the termination (signal */
  31.                                         /* number) the following value is    */
  32.                                         /* reserverd: -1 = never started     */
  33.    char          *tmpfilename;          /* file name of stdout and stderr of */
  34. } PROC;                                 /* the newly started process         */
  35.  
  36. static PROC first = {NULL,0,0,0,NULL};
  37.  
  38. /*****************************************************************************/
  39. /*  function name : EndProcess                                               */
  40. /*                                                                           */
  41. /*  arguments     : process id of a terminated job, reason of the            */
  42. /*                  termination (0 or trap number), return code              */
  43. /*                                                                           */
  44. /*  description   : this is a function for the main thread, which wants to   */
  45. /*                  indicate the termination of a job. The cleanup is done   */
  46. /*                  by the cron thread later.                                */
  47. /*                                                                           */
  48. /*  note          : this has to be called while running in a signal          */
  49. /*                  procedure, because we are NOT blocking the access to     */
  50. /*                  the job list!                                            */
  51. /*****************************************************************************/
  52. void EndProcess(int pid,int reason,int rc)
  53. {
  54.    PROC *run = first.next;
  55.    while (run != NULL) {
  56.       if (pid == run->pid) {            /* process found                     */
  57.          run->rc = rc;
  58.          run->reason = reason;
  59.          return;
  60.       }
  61.       run = run->next;
  62.    }
  63. }
  64.  
  65. /*****************************************************************************/
  66. /*  function name : NewSlot                                                  */
  67. /*                                                                           */
  68. /*  return value  : a newly allocated and initialized process slot, NULL     */
  69. /*                  in case of insufficent memory                            */
  70. /*                                                                           */
  71. /*  description   : creates a new process slot and initialized it to the     */
  72. /*                  default value. It is appended to the job list.           */
  73. /*                                                                           */
  74. /*  note          : the blocking of the job list must be done by the         */
  75. /*                  calling routine.                                         */
  76. /*****************************************************************************/
  77. static PROC *NewSlot(void)
  78. {
  79.    PROC *run,*last;
  80.    last = &first;
  81.    run = first.next;
  82.    while (run != NULL) {                /* search for the insertion place    */
  83.       last = run;
  84.       run = run->next;
  85.    }
  86.    if ((run = malloc(sizeof(PROC))) == NULL)
  87.       return(NULL);
  88.    memset(run,0,sizeof(PROC));
  89.    run->reason = -1;                    /* never started                     */
  90.    last->next = run;                    /* insert into the list              */
  91.    return(run);
  92. }
  93.  
  94. /*****************************************************************************/
  95. /*  function name : NewTmpFile                                               */
  96. /*                                                                           */
  97. /*  arguments     : buffer for a handle                                      */
  98. /*                                                                           */
  99. /*  return value  : allocated file name of a temporary file, NULL in case    */
  100. /*                  of insufficent memory or other errors.                   */
  101. /*                                                                           */
  102. /*  description   : creates a new temporary file and returns the filename.   */
  103. /*                  The handle of the file is returned in the argument       */
  104. /*                  buffer.                                                  */
  105. /*****************************************************************************/
  106. static char *NewTmpFile(int *handle)
  107. {
  108.    char *buf,*ptr;
  109.    unsigned i;
  110.    if ((buf = malloc(strlen(DefIOPath) + 2 + 11)) == NULL)  /* delim+term. 0+*/
  111.       return(NULL);                     /* length of filename component      */
  112.    strcpy(buf,DefIOPath);               /* create the filename base          */
  113.    if (DefIOPath[0] != '\0')
  114.       if (strchr(Delims,DefIOPath[strlen(DefIOPath) - 1]) == NULL)
  115.          strcat(buf,"\\");
  116.    ptr = buf + strlen(buf);             /* where to append Cron????.tmp      */
  117.    for (i = 0;i <= 0xFFFF;i++) {
  118.       sprintf(ptr,"Cron%04X.tmp",i);
  119.       if ((*handle = sopen(buf,
  120.                            O_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_SYNC,
  121.                            SH_DENYWR,
  122.                            S_IREAD | S_IWRITE)) != -1)
  123.          return(buf);
  124.    }
  125.                                         /* can't allocate a tempfile, cleanup*/
  126.    free(buf);
  127.    return(NULL);
  128. }
  129.  
  130. /*****************************************************************************/
  131. /*  function name : WaitDelete                                               */
  132. /*                                                                           */
  133. /*  arguments     : filename to delete                                       */
  134. /*                                                                           */
  135. /*  description   : delete the given file. If an error occurs we assume a    */
  136. /*                  race condition and wait approximately 2 time slices,     */
  137. /*                  then we try again once more. If the error persists we    */
  138. /*                  write an error message to the output.                    */
  139. /*****************************************************************************/
  140. static void WaitDelete(char *s)
  141. {
  142.    ULONG os2rc;
  143.    if (DosDelete(s) == 0)
  144.       return;
  145.    DosSleep(64);                        /* 2 * TimeSlice                     */
  146.    if ((os2rc = DosDelete(s)) != 0)
  147.       Message(Get(IDS_TempfileNotDeleted),
  148.               s,os2rc);
  149. }
  150.  
  151. /*****************************************************************************/
  152. /*  function name : CopyTemp                                                 */
  153. /*                                                                           */
  154. /*  arguments     : filename of the output file of a job, process id,        */
  155. /*                  return code und reason of the termination of the job     */
  156. /*                                                                           */
  157. /*  description   : writes a message that the job has been stoped and        */
  158. /*                  copies the contents of the job output file to our        */
  159. /*                  output file.                                             */
  160. /*****************************************************************************/
  161. static void CopyTemp(char *fn,int pid,int rc,int reason)
  162. {
  163.    int chunk,done,done2;
  164.    int handle;
  165.    long len;
  166.    int neednl = 1;                      /* newline needed at eof?            */
  167.    static char notbuf[128];             /* minimal emergency buffer          */
  168.    static const char *Msg;
  169.    char *buf;
  170.  
  171.    if (out == NULL) {                    /* ignore output?                    */
  172.       WaitDelete(fn);
  173.       return;
  174.    }
  175.    if (reason == 0)
  176.       Msg = Get(IDS_JobEndsNormal);
  177.    else {
  178.       Msg = Get(IDS_JobEndsDueSignal);
  179.       rc = reason;
  180.    }
  181.    if ((handle = sopen(fn,O_RDONLY | O_BINARY | O_NOINHERIT,SH_DENYNO)) == -1){
  182.       Message(Msg,pid,rc,Get(IDS_TempfileInaccessable));
  183.       return;
  184.    }
  185.    len = filelength(handle);
  186.    if (len == 0) {                      /* file empty?                       */
  187.       Message(Msg,pid,rc,Get(IDS_TempfileEmpty));
  188.       close(handle);
  189.       WaitDelete(fn);
  190.       return;
  191.    }
  192.    if ((buf = malloc(0x4000)) == NULL) {  /* get a temorary buffer           */
  193.       buf = notbuf;                     /* no memory, use the emergency buf  */
  194.       chunk = 128;
  195.    } else
  196.       chunk = 0x4000;
  197.  
  198.    BlockOutput();                       /* don't allow any access to the     */
  199.                                         /* output file, we work on it        */
  200.    if (filelength(fileno(out)) >= Threshold) /* need a fresh output file?    */
  201.       NextOutputFile(0);
  202.    fputs(GetTimeString(),out);          /* normal Message processing         */
  203.    fprintf(out,Msg,pid,rc,Get(IDS_TempfileFollows));
  204.    fflush(out);                         /* flush any buffers                 */
  205.    lseek(fileno(out),0l,SEEK_END);      /* seek to end of file and change    */
  206.    setmode(fileno(out),O_BINARY);       /* the writing mode to binary since  */
  207.                                         /* the job output file is in binary  */
  208.                                         /* mode too. Don't use the slow      */
  209.                                         /* text mode.                        */
  210.    while (len > 0) {                    /* copy until eof                    */
  211.       if ((done = read(handle,buf,chunk)) <= 0)
  212.          break;
  213.       if ((done2 = write(fileno(out),buf,done)) <= 0)
  214.          break;
  215.       if ((done2 != done) || (done == 0))
  216.          break;
  217.       len -= done;
  218.       if (len <= 0)                     /* that's all? Check if last char    */
  219.          if (buf[done - 1] == '\n')     /* was a newline. In this case we    */
  220.             neednl = 0;                 /* don't have to do it ourself.      */
  221.    }
  222.    if (neednl)                          /* append newline?                   */
  223.       write(fileno(out),"\r\n",2);
  224.    setmode(fileno(out),O_TEXT);         /* back to standard text mode        */
  225.    fseek(out,0l,SEEK_END);              /* seek to eof, update internal FILE */
  226.                                         /* buffers                           */
  227.    UnBlockOutput();                     /* end of copy                       */
  228.    if (buf != notbuf)                   /* is there an allocated buffer?     */
  229.       free(buf);                        /* free it!                          */
  230.    close(handle);                       /* cleanup                           */
  231.    WaitDelete(fn);
  232. }
  233.  
  234. /*****************************************************************************/
  235. /*  function name : StartProcess                                             */
  236. /*                                                                           */
  237. /*  arguments     : list entry of a cron job                                 */
  238. /*                                                                           */
  239. /*  description   : starts the job and appends job infos to the running      */
  240. /*                  process list (process slot). All errors were process     */
  241. /*                  locally.                                                 */
  242. /*                  We use CMD (getenv(COMSPEC)) to start any job. Every     */
  243. /*                  job gets a freshly created temporary output file.        */
  244. /*                                                                           */
  245. /*  note          : the blocking of the job list must be done by the         */
  246. /*                  calling routine.                                         */
  247. /*****************************************************************************/
  248. void StartProcess(LIST_ENTRY *job)
  249. {
  250.    PROC *proc;
  251.    int tmpfilehandle;
  252.    static char *cmd = NULL;
  253.    if (cmd == NULL)                     /* never determined?                 */
  254.       if ((cmd = getenv("COMSPEC")) == NULL)
  255.          FatalError(Get(IDS_NoComSpec),(HWND) 0);
  256.    if ((proc = NewSlot()) == NULL) {    /* can't create a job slot?          */
  257.       Message(Get(IDS_NotEnoughMemory));
  258.       return;
  259.    }
  260.    if ((proc->tmpfilename = NewTmpFile(&tmpfilehandle)) == NULL) {
  261.       Message(Get(IDS_NoMemOrNoTempfile));
  262.       proc->pid = -1;                   /* don't delete the job slot         */
  263.       proc->reason = 0;                 /* immediately, we may run into race */
  264.       return;                           /* conditions due some signals to    */
  265.                                         /* the main thread. The deletion is  */
  266.    }                                    /* done by ReapChildren              */
  267.  
  268.    if (tmpfilehandle != 1)              /* reassign stdout and stderr of the */
  269.       dup2(tmpfilehandle,1);            /* newly created job to the          */
  270.    if (tmpfilehandle != 2)              /* temprary file                     */
  271.       dup2(tmpfilehandle,2);
  272.    if ((tmpfilehandle != 1) && (tmpfilehandle != 2))  /* close the tempfile  */
  273.       close(tmpfilehandle);             /* thus the only owner is the new    */
  274.                                         /* job.                              */
  275.                                         /* Now, we start the job, we let it  */
  276.                                         /* run in the background of the      */
  277.                                         /* current process. This is either   */
  278.                                         /* detach or unvisible. The user     */
  279.                                         /* (Crontabs!) must support a "start"*/
  280.                                         /* command to show it in a visible   */
  281.                                         /* session except in case of a PM    */
  282.                                         /* program with crond started as a   */
  283.                                         /* PM program, too.                  */
  284.  
  285.    if ((proc->pid = spawnl(P_NOWAIT,    /* P_SESSION has some problems, yet  */
  286.                            cmd,
  287.                            cmd,
  288.                            "/C",
  289.                            job->StartCmd,
  290.                            NULL)) == -1) {
  291.       proc->reason = 0;                 /* proc->pid is -1, the mess is      */
  292.       Message(Get(IDS_CantStartJob),    /* cleaned up by the next call of    */
  293.              job->StartCmd,strerror(errno)); /* ReapChildren                 */
  294.    } else                               /* job started normally              */
  295.       Message(Get(IDS_JobStarted),
  296.               job->StartCmd,proc->pid);
  297.    dup2(0,1);                           /* stdout and stderr back to the     */
  298.    dup2(0,2);                           /* "nul" device                      */
  299. }
  300.  
  301. /*****************************************************************************/
  302. /*  function name : ReapChildren                                             */
  303. /*                                                                           */
  304. /*  return value  : number of still outstanding jobs                         */
  305. /*                                                                           */
  306. /*  description   : looks for terminated jobs in the job list, copies        */
  307. /*                  their output to our output files und frees all           */
  308. /*                  allocated resources belonging to the children.           */
  309. /*****************************************************************************/
  310. int ReapChildren(void)
  311. {
  312.    PROC *run,*last,*del;
  313.    int pid,retval = 0;
  314.    BlockProcess();                      /* block the job list                */
  315.    last = &first;
  316.    run = last->next;
  317.    while (run != NULL) {                /* check every job slot              */
  318.       if ((run->pid != 0) && (run->reason != -1)) {   /* job has started?    */
  319.          if ((pid = run->pid) != -1) {  /* job stated normally?              */
  320.             run->pid = -1;              /* job has ended, copy its output    */
  321.             CopyTemp(run->tmpfilename,pid,run->rc,run->reason);
  322.          }
  323.          /* else: error while spawning the process */
  324.          del = run;
  325.          last->next = run->next;        /* cut off the list                  */
  326.          run = run->next;               /* set run to the next candidat      */
  327.          if (del->tmpfilename != NULL)  /* tempfile available?               */
  328.             free(del->tmpfilename);
  329.          free(del);                     /* free the job slot                 */
  330.          continue;
  331.       }
  332.                                         /* job is running:                   */
  333.       last = run;
  334.       run = run->next;
  335.       retval++;
  336.    }
  337.    UnBlockProcess();                    /* allow the access of the job list  */
  338.                                         /* by other threads                  */
  339.    return(retval);
  340. }
  341.  
  342. /*****************************************************************************/
  343. /*  function name : ShowStillRunnings                                        */
  344. /*                                                                           */
  345. /*  description   : displays all running jobs with their output file. This   */
  346. /*                  function is called once at the end of the cron thread.   */
  347. /*****************************************************************************/
  348. void ShowStillRunnings(void)
  349. {
  350.    PROC *run = first.next;
  351.    while (run != NULL) {                /* no need to block the list         */
  352.       if ((run->pid != 0) && (run->pid != -1))
  353.          Message(Get(IDS_JobYetActive),
  354.                  run->pid,
  355.                  run->tmpfilename);
  356.       run = run->next;
  357.    }
  358. }
  359.  
  360. /* RCS depending informations
  361.  *
  362.  * $Name: Version121 $
  363.  *
  364.  * $Log: process.c $
  365.  * Revision 1.1  1995/02/03 10:42:46  Florian
  366.  * Initial revision
  367.  *
  368.  *
  369.  */
  370. static char rcsid[] = "@(#)$Id: process.c 1.1 1995/02/03 10:42:46 Florian Rel $";
  371.