dgaudet     97/09/12 13:13:26

  Modified:    htdocs/manual new_features_1_3.html
               src      CHANGES PORTING
               src/main conf.h http_log.c http_log.h http_main.c
                        http_main.h
               src/modules/standard mod_log_config.c
               src/os/win32 os.h
  Log:
    *) API: New register_other_child() API (see http_main.h) which allows
       modules to register children with the parent for maintenance.  It
       is disabled by defining NO_OTHER_CHILD.  [Dean Gaudet]
  
    *) API: New piped_log API (see http_log.h) which implements piped logs,
       and will use register_other_child to implement reliable piped logs
       when it is available.  The reliable piped logs part can be disabled
       by defining NO_RELIABLE_PIPED_LOGS.  At the moment reliable piped
       logs is only available on Unix. [Dean Gaudet]
  
  Revision  Changes    Path
  1.21      +11 -0     apachen/htdocs/manual/new_features_1_3.html
  
  Index: new_features_1_3.html
  ===================================================================
  RCS file: /export/home/cvs/apachen/htdocs/manual/new_features_1_3.html,v
  retrieving revision 1.20
  retrieving revision 1.21
  diff -u -r1.20 -r1.21
  --- new_features_1_3.html     1997/08/28 15:24:05     1.20
  +++ new_features_1_3.html     1997/09/12 20:13:01     1.21
  @@ -258,6 +258,17 @@
       identifier is available in the environment variable
       <code>UNIQUE_ID</code>.
   
  +<li><strong>Reliable Piped Logs</strong><br>
  +    On almost all Unix architectures Apache now implements "reliable"
  +    piped logs in <a href="mod/mod_log_config.html">mod_log_config</a>.
  +    Where reliable means that if the logging child dies for whatever
  +    reason, Apache will recover and respawn it without having to restart
  +    the entire server.  Furthermore if the logging child becomes "stuck"
  +    and isn't reading its pipe frequently enough Apache will also restart
  +    it.  This opens up more opportunities for log rotation, hit filtering,
  +    real-time splitting of multiple vhosts into separate logs, and
  +    asynchronous DNS resolving on the fly.
  +
   </ul>
   
   <!--#include virtual="footer.html" -->
  
  
  
  1.436     +11 -1     apachen/src/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/CHANGES,v
  retrieving revision 1.435
  retrieving revision 1.436
  diff -u -r1.435 -r1.436
  --- CHANGES   1997/09/12 19:53:10     1.435
  +++ CHANGES   1997/09/12 20:13:03     1.436
  @@ -1,6 +1,16 @@
   Changes with Apache 1.3b1
   
  -  *) set_last_modified() broken into set_last_modified(), set_etag(), and
  +  *) API: New register_other_child() API (see http_main.h) which allows
  +     modules to register children with the parent for maintenance.  It
  +     is disabled by defining NO_OTHER_CHILD.  [Dean Gaudet]
  +
  +  *) API: New piped_log API (see http_log.h) which implements piped logs,
  +     and will use register_other_child to implement reliable piped logs
  +     when it is available.  The reliable piped logs part can be disabled
  +     by defining NO_RELIABLE_PIPED_LOGS.  At the moment reliable piped
  +     logs is only available on Unix. [Dean Gaudet]
  +
  +  *) API: set_last_modified() broken into set_last_modified(), set_etag(), 
and
        meets_conditions().  This allows conditional HTTP selection to be
        handled separately from the storing of the header fields, and provides
        the ability for CGIs to set their own ETags for conditional checking.
  
  
  
  1.12      +10 -0     apachen/src/PORTING
  
  Index: PORTING
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/PORTING,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- PORTING   1997/09/01 21:47:46     1.11
  +++ PORTING   1997/09/12 20:13:05     1.12
  @@ -256,6 +256,16 @@
          an int *len on some architectures and a size_t *len on others.
          If left undefined apache will default it to int.
   
  +      NO_OTHER_CHILD:
  +       Do not implement the register_other_child API, usually because
  +       certain system calls aren't available.
  +
  +      NO_RELIABLE_PIPED_LOGS:
  +       Do not use reliable piped logs, which happen to also require
  +       the register_other_child API.  The reliable piped log code
  +       requires another child spawning interface which hasn't been
  +       generalised yet.
  +
   -----------
   Conclusion:
   -----------
  
  
  
  1.136     +8 -0      apachen/src/main/conf.h
  
  Index: conf.h
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/main/conf.h,v
  retrieving revision 1.135
  retrieving revision 1.136
  diff -u -r1.135 -r1.136
  --- conf.h    1997/09/12 08:09:09     1.135
  +++ conf.h    1997/09/12 20:13:07     1.136
  @@ -445,6 +445,7 @@
   #undef NO_KILLPG
   #define NO_SETSID
   #define NEED_WAITPID
  +#define NO_OTHER_CHILD
   #define STDIN_FILENO 0
   #define STDOUT_FILENO 1
   #define STDERR_FILENO 2
  @@ -541,6 +542,7 @@
   #include <sys/time.h>     
   #define MAXSOCKETS 4096
   #define HAVE_MMAP
  +#define NO_RELIABLE_PIPED_LOGS
       
   #elif defined(__MACHTEN__)
   typedef int rlim_t;
  @@ -595,6 +597,7 @@
   #define NO_SETSID
   #define NO_USE_SIGACTION
   #define NEED_WAITPID
  +#define NO_OTHER_CHILD
   #define HAVE_SYSLOG
   #include <sys/time.h>
   #include <stdlib.h>
  @@ -632,6 +635,7 @@
   /* All windows stuff is now in os/win32/os.h */
   
   #else
  +/* Unknown system - Edit these to match */
   #ifdef BSD
   #define HAVE_GMTOFF
   #else
  @@ -839,6 +843,10 @@
   /* so that we can use inline on some critical functions */
   #if !defined(__GNUC__)
   #define inline
  +#endif
  +
  +#ifdef NO_OTHER_CHILD
  +#define NO_RELIABLE_PIPED_LOGS
   #endif
   
   /* Finding offsets of elements within structures.
  
  
  
  1.33      +198 -0    apachen/src/main/http_log.c
  
  Index: http_log.c
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/main/http_log.c,v
  retrieving revision 1.32
  retrieving revision 1.33
  diff -u -r1.32 -r1.33
  --- http_log.c        1997/09/10 02:35:37     1.32
  +++ http_log.c        1997/09/12 20:13:08     1.33
  @@ -63,6 +63,7 @@
   #include "http_config.h"
   #include "http_core.h"
   #include "http_log.h"
  +#include "http_main.h"
   
   #include <stdarg.h>
   
  @@ -352,3 +353,200 @@
       exit(1);
   #endif
   }
  +
  +/* piped log support */
  +
  +#ifndef NO_RELIABLE_PIPED_LOGS
  +/* forward declaration */
  +static void piped_log_maintenance (int reason, void *data, int status);
  +
  +static int piped_log_spawn (piped_log *pl)
  +{
  +    int pid;
  +
  +    block_alarms();
  +    pid = fork();
  +    if (pid == 0) {
  +     /* XXX: this needs porting to OS2 and WIN32 */
  +     /* XXX: need to check what open fds the logger is actually passed,
  +      * XXX: and CGIs for that matter ... cleanup_for_exec *should*
  +      * XXX: close all the relevant stuff, but hey, it could be broken. */
  +     /* we're now in the child */
  +     close (STDIN_FILENO);
  +     dup2 (pl->fds[0], STDIN_FILENO);
  +
  +     cleanup_for_exec ();
  +     signal (SIGCHLD, SIG_DFL);      /* for HPUX */
  +     signal (SIGHUP, SIG_IGN);
  +     execl (SHELL_PATH, SHELL_PATH, "-c", pl->program, NULL);
  +     fprintf (stderr,
  +         "piped_log_spawn: unable to exec %s -c '%s': %s\n",
  +         SHELL_PATH, pl->program, strerror (errno));
  +     exit (1);
  +    }
  +    if (pid == -1) {
  +     fprintf (stderr,
  +         "piped_log_spawn: unable to fork(): %s\n", strerror (errno));
  +     unblock_alarms ();
  +     return -1;
  +    }
  +    unblock_alarms();
  +    pl->pid = pid;
  +    register_other_child (pid, piped_log_maintenance, pl, pl->fds[1]);
  +    return 0;
  +}
  +
  +
  +static void piped_log_maintenance (int reason, void *data, int status)
  +{
  +    piped_log *pl = data;
  +    int pid;
  +
  +    switch (reason) {
  +    case OC_REASON_DEATH:
  +    case OC_REASON_LOST:
  +     pl->pid = -1;
  +     unregister_other_child (pl);
  +     if (pl->program == NULL) {
  +         /* during a restart */
  +         break;
  +     }
  +     if (piped_log_spawn (pl) == -1) {
  +         /* what can we do?  This could be the error log we're having
  +          * problems opening up... */
  +         fprintf (stderr,
  +             "piped_log_maintenance: unable to respawn '%s': %s\n",
  +             pl->program, strerror (errno));
  +     }
  +     break;
  +    
  +    case OC_REASON_UNWRITABLE:
  +     if (pl->pid != -1) {
  +         kill (pl->pid, SIGTERM);
  +     }
  +     break;
  +    
  +    case OC_REASON_RESTART:
  +     pl->program = NULL;
  +     if (pl->pid != -1) {
  +         kill (pl->pid, SIGTERM);
  +     }
  +     break;
  +
  +    case OC_REASON_UNREGISTER:
  +     break;
  +    }
  +}
  +
  +
  +static void piped_log_cleanup (void *data)
  +{
  +    piped_log *pl = data;
  +
  +    if (pl->pid != -1) {
  +     kill (pl->pid, SIGTERM);
  +    }
  +    unregister_other_child (pl);
  +    close (pl->fds[0]);
  +    close (pl->fds[1]);
  +}
  +
  +
  +static void piped_log_cleanup_for_exec (void *data)
  +{
  +    piped_log *pl = data;
  +
  +    close (pl->fds[0]);
  +    close (pl->fds[1]);
  +}
  +
  +
  +API_EXPORT(piped_log *) open_piped_log (pool *p, const char *program)
  +{
  +    piped_log *pl;
  +    int pid;
  +
  +    pl = palloc (p, sizeof (*pl));
  +    pl->p = p;
  +    pl->program = pstrdup (p, program);
  +    pl->pid = -1;
  +    block_alarms ();
  +    if (pipe (pl->fds) == -1) {
  +     int save_errno = errno;
  +     unblock_alarms();
  +     errno = save_errno;
  +     return NULL;
  +    }
  +    register_cleanup (p, pl, piped_log_cleanup, piped_log_cleanup_for_exec);
  +    if (piped_log_spawn (pl) == -1) {
  +     int save_errno = errno;
  +     kill_cleanup (p, pl, piped_log_cleanup);
  +     close (pl->fds[0]);
  +     close (pl->fds[1]);
  +     unblock_alarms ();
  +     errno = save_errno;
  +     return NULL;
  +    }
  +    unblock_alarms ();
  +    return pl;
  +}
  +
  +API_EXPORT(void) close_piped_log (piped_log *pl)
  +{
  +    block_alarms ();
  +    piped_log_cleanup (pl);
  +    kill_cleanup (pl->p, pl, piped_log_cleanup);
  +    unblock_alarms ();
  +}
  +
  +#else
  +static int piped_log_child (void *cmd)
  +{
  +    /* Child process code for 'TransferLog "|..."';
  +     * may want a common framework for this, since I expect it will
  +     * be common for other foo-loggers to want this sort of thing...
  +     */
  +    int child_pid = 1;
  +
  +    cleanup_for_exec();
  +#ifdef SIGHUP
  +    signal (SIGHUP, SIG_IGN);
  +#endif
  +#if defined(WIN32)
  +    child_pid = spawnl (_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char 
*)cmd, NULL);
  +    return(child_pid);
  +#elif defined(__EMX__)
  +    /* For OS/2 we need to use a '/' */
  +    execl (SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
  +#else
  +    execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
  +#endif
  +    perror ("exec");
  +    fprintf (stderr, "Exec of shell for logging failed!!!\n");
  +    return(child_pid);
  +}
  +
  +
  +API_EXPORT(piped_log *) open_piped_log (pool *p, const char *program)
  +{
  +    piped_log *pl;
  +    FILE *dummy;
  +    
  +    if (!spawn_child (p, piped_log_child, (void *)program,
  +             kill_after_timeout, &dummy, NULL)) {
  +     perror ("spawn_child");
  +     fprintf (stderr, "Couldn't fork child for piped log process\n");
  +     exit (1);
  +    }
  +    pl = palloc (p, sizeof (*pl));
  +    pl->p = p;
  +    pl->write_f = dummy;
  +    return pl;
  +}
  +
  +
  +API_EXPORT(void) close_piped_log (piped_log *pl)
  +{
  +    pfclose (pl->p, pl->write_f);
  +}
  +#endif
  
  
  
  1.13      +20 -0     apachen/src/main/http_log.h
  
  Index: http_log.h
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/main/http_log.h,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- http_log.h        1997/08/27 05:45:37     1.12
  +++ http_log.h        1997/09/12 20:13:08     1.13
  @@ -79,3 +79,23 @@
   API_EXPORT(void) log_reason(const char *reason, const char *fname,
                            request_rec *r);
   
  +typedef struct piped_log {
  +    pool *p;
  +#ifndef NO_RELIABLE_PIPED_LOGS
  +    char *program;
  +    int pid;
  +    int fds[2];
  +#else
  +    FILE *write_f;
  +#endif
  +} piped_log;
  +
  +API_EXPORT(piped_log *) open_piped_log (pool *p, const char *program);
  +API_EXPORT(void) close_piped_log (piped_log *);
  +#ifndef NO_RELIABLE_PIPED_LOGS
  +#define piped_log_read_fd(pl)        ((pl)->fds[0])
  +#define piped_log_write_fd(pl)       ((pl)->fds[1])
  +#else
  +#define piped_log_read_fd(pl)        (-1)
  +#define piped_log_write_fd(pl)       (fileno((pl)->write_f))
  +#endif
  
  
  
  1.218     +231 -71   apachen/src/main/http_main.c
  
  Index: http_main.c
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/main/http_main.c,v
  retrieving revision 1.217
  retrieving revision 1.218
  diff -u -r1.217 -r1.218
  --- http_main.c       1997/09/11 19:32:43     1.217
  +++ http_main.c       1997/09/12 20:13:10     1.218
  @@ -234,6 +234,19 @@
   
   int one_process = 0;
   
  +#ifndef NO_OTHER_CHILD
  +/* used to maintain list of children which aren't part of the scoreboard */
  +typedef struct other_child_rec other_child_rec;
  +struct other_child_rec {
  +    other_child_rec *next;
  +    int pid;
  +    void (*maintenance)(int, void *, int);
  +    void *data;
  +    int write_fd;
  +};
  +static other_child_rec *other_children;
  +#endif
  +
   pool *pconf;                 /* Pool for config stuff */
   pool *ptrans;                        /* Pool for per-transaction stuff */
   
  @@ -1043,6 +1056,107 @@
   #endif /* ndef NO_LINGCLOSE */
   
   /*****************************************************************
  + * dealing with other children
  + */
  +
  +#ifndef NO_OTHER_CHILD
  +void register_other_child (int pid,
  +    void (*maintenance)(int reason, void *, int status),
  +    void *data, int write_fd)
  +{
  +    other_child_rec *ocr;
  +
  +    ocr = palloc (pconf, sizeof (*ocr));
  +    ocr->pid = pid;
  +    ocr->maintenance = maintenance;
  +    ocr->data = data;
  +    ocr->write_fd = write_fd;
  +    ocr->next = other_children;
  +    other_children = ocr;
  +}
  +
  +/* note that since this can be called by a maintenance function while we're
  + * scanning the other_children list, all scanners should protect themself
  + * by loading ocr->next before calling any maintenance function.
  + */
  +void unregister_other_child (void *data)
  +{
  +    other_child_rec **pocr, *nocr;
  +
  +    for (pocr = &other_children; *pocr; pocr = &(*pocr)->next) {
  +     if ((*pocr)->data == data) {
  +         nocr = (*pocr)->next;
  +         (*(*pocr)->maintenance)(OC_REASON_UNREGISTER, (*pocr)->data, -1);
  +         *pocr = nocr;
  +         /* XXX: um, well we've just wasted some space in pconf ? */
  +         return;
  +     }
  +    }
  +}
  +
  +/* test to ensure that the write_fds are all still writable, otherwise
  + * invoke the maintenance functions as appropriate */
  +static void probe_writable_fds (void)
  +{
  +    fd_set writable_fds;
  +    int fd_max;
  +    other_child_rec *ocr, *nocr;
  +    struct timeval tv;
  +    int rc;
  +
  +    if (other_children == NULL) return;
  +
  +    fd_max = 0;
  +    FD_ZERO (&writable_fds);
  +    for (ocr = other_children; ocr; ocr = ocr->next) {
  +     if (ocr->write_fd == -1) continue;
  +     FD_SET (ocr->write_fd, &writable_fds);
  +     if (ocr->write_fd > fd_max) {
  +         fd_max = ocr->write_fd;
  +     }
  +    }
  +    if (fd_max == 0) return;
  +
  +    do {
  +     tv.tv_sec = 0;
  +     tv.tv_usec = 0;
  +     rc = ap_select (fd_max + 1, NULL, &writable_fds, NULL, &tv);
  +    } while (rc == -1 && errno == EINTR);
  +
  +    if (rc == -1) {
  +     /* XXX: uhh this could be really bad, we could have a bad file
  +      * descriptor due to a bug in one of the maintenance routines */
  +     log_unixerr ("probe_writable_fds", "select", "could not probe
  +         writable fds", server_conf);
  +     return;
  +    }
  +    if (rc == 0) return;
  +
  +    for (ocr = other_children; ocr; ocr = nocr) {
  +     nocr = ocr->next;
  +     if (ocr->write_fd == -1) continue;
  +     if (FD_ISSET (ocr->write_fd, &writable_fds)) continue;
  +     (*ocr->maintenance)(OC_REASON_UNWRITABLE, ocr->data, -1);
  +    }
  +}
  +
  +/* possibly reap an other_child, return 0 if yes, -1 if not */
  +static int reap_other_child (int pid, int status)
  +{
  +    other_child_rec *ocr, *nocr;
  +
  +    for (ocr = other_children; ocr; ocr = nocr) {
  +     nocr = ocr->next;
  +     if (ocr->pid != pid) continue;
  +     ocr->pid = -1;
  +     (*ocr->maintenance)(OC_REASON_DEATH, ocr->data, status);
  +     return 0;
  +    }
  +    return -1;
  +}
  +#endif
  +
  +/*****************************************************************
    *
    * Dealing with the scoreboard... a lot of these variables are global
    * only to avoid getting clobbered by the longjmp() that happens when
  @@ -1605,74 +1719,104 @@
   {
   #ifndef MULTITHREAD
       int i, status;
  +    long int waittime = 4096; /* in usecs */
  +    struct timeval tv;
  +    int waitret, tries;
  +    int not_dead_yet;
  +#ifndef NO_OTHER_CHILD
  +    other_child_rec *ocr, *nocr;
  +#endif
   
       sync_scoreboard_image();
  -    for (i = 0; i < max_daemons_limit; ++i) {
  -     int pid = scoreboard_image->servers[i].x.pid;
   
  -     if (pid != my_pid && pid != 0) { 
  -         int waitret = 0,
  -             tries = start_tries;
  -
  -         while (waitret == 0 && tries <= 4) {
  -             long int waittime = 4096; /* in usecs */
  -             struct timeval tv;
  -         
  -             /* don't want to hold up progress any more than 
  -              * necessary, so keep checking to see if the child
  -              * has exited with an exponential backoff.
  -              * Currently set for a maximum wait of a bit over
  -              * four seconds.
  -              */
  -             while (((waitret = waitpid(pid, &status, WNOHANG)) == 0) &&
  -                      waittime < 3000000) {
  -                    tv.tv_sec = waittime / 1000000;
  -                    tv.tv_usec = waittime % 1000000;
  -                    waittime = waittime * 2;
  -                    ap_select(0, NULL, NULL, NULL, &tv);
  -             }
  -             if (waitret == 0) {
  -                 switch (tries) {
  -                 case 1:
  -                     /* perhaps it missed the SIGHUP, lets try again */
  -                     aplog_error(APLOG_MARK, APLOG_ERR, server_conf,
  -                                 "child process %d did not exit, sending 
another SIGHUP",
  -                                 pid);
  -                     kill(pid, SIGHUP);
  -                     break;
  -                 case 2:
  -                     /* ok, now it's being annoying */
  -                     aplog_error(APLOG_MARK, APLOG_ERR, server_conf,
  -                                 "child process %d still did not exit, 
sending a SIGTERM",
  -                                 pid);
  -                     kill(pid, SIGTERM);
  -                     break;
  -                 case 3:
  -                     /* die child scum */
  -                     aplog_error(APLOG_MARK, APLOG_ERR, server_conf,
  -                                 "child process %d still did not exit, 
sending a SIGKILL",
  -                                 pid);
  -                     kill(pid, SIGKILL);
  -                     break;
  -                 case 4:
  -                     /* gave it our best shot, but alas...  If this really 
  -                      * is a child we are trying to kill and it really hasn't
  -                      * exited, we will likely fail to bind to the port
  -                      * after the restart.
  -                      */
  -                     aplog_error(APLOG_MARK, APLOG_ERR, server_conf,
  -                                 "could not make child process %d exit, 
attempting to continue anyway",
  -                                 pid);
  -                     break;
  -                 }
  -             }
  -             tries++;
  +    tries = 0;
  +    for(tries = start_tries; tries < 4; ++tries) {
  +     /* don't want to hold up progress any more than 
  +     * necessary, but we need to allow children a few moments to exit.
  +     * delay with an exponential backoff.
  +     * Currently set for a maximum wait of a bit over
  +     * four seconds.
  +     */
  +     tv.tv_sec = waittime / 1000000;
  +     tv.tv_usec = waittime % 1000000;
  +     waittime = waittime * 2;
  +     ap_select(0, NULL, NULL, NULL, &tv);
  +
  +     /* now see who is done */
  +     not_dead_yet = 0;
  +     for (i = 0; i < max_daemons_limit; ++i) {
  +         int pid = scoreboard_image->servers[i].x.pid;
  +
  +         if (pid == my_pid || pid == 0) continue;
  +
  +         waitret = waitpid (pid, &status, WNOHANG);
  +         if (waitret == pid || waitret == -1) {
  +             scoreboard_image->servers[i].x.pid = 0;
  +             continue;
  +         }
  +         ++not_dead_yet;
  +         switch (tries) {
  +         case 1:
  +             /* perhaps it missed the SIGHUP, lets try again */
  +             aplog_error(APLOG_MARK, APLOG_ERR, server_conf,
  +                 "child process %d did not exit, sending another SIGHUP",
  +                 pid);
  +             kill(pid, SIGHUP);
  +             break;
  +         case 2:
  +             /* ok, now it's being annoying */
  +             aplog_error(APLOG_MARK, APLOG_ERR, server_conf,
  +                 "child process %d still did not exit, sending a SIGTERM",
  +                 pid);
  +             kill(pid, SIGTERM);
  +             break;
  +         case 3:
  +             /* die child scum */
  +             aplog_error(APLOG_MARK, APLOG_ERR, server_conf,
  +                 "child process %d still did not exit, sending a SIGKILL",
  +                 pid);
  +             kill(pid, SIGKILL);
  +             break;
  +         case 4:
  +             /* gave it our best shot, but alas...  If this really 
  +                 * is a child we are trying to kill and it really hasn't
  +                 * exited, we will likely fail to bind to the port
  +                 * after the restart.
  +                 */
  +             aplog_error(APLOG_MARK, APLOG_ERR, server_conf,
  +                 "could not make child process %d exit, "
  +                 "attempting to continue anyway", pid);
  +             break;
  +         }
  +     }
  +#ifndef NO_OTHER_CHILD
  +     for (ocr = other_children; ocr; ocr = nocr) {
  +         nocr = ocr->next;
  +         if (ocr->pid == -1) continue;
  +
  +         waitret = waitpid (ocr->pid, &status, WNOHANG);
  +         if (waitret == ocr->pid) {
  +             ocr->pid = -1;
  +             (*ocr->maintenance)(OC_REASON_DEATH, ocr->data, status);
  +         } else if (waitret == 0) {
  +             (*ocr->maintenance)(OC_REASON_RESTART, ocr->data, -1);
  +             ++not_dead_yet;
  +         } else if (waitret == -1) {
  +             /* uh what the heck? they didn't call unregister? */
  +             ocr->pid = -1;
  +             (*ocr->maintenance)(OC_REASON_LOST, ocr->data, -1);
            }
        }
  +#endif
  +     if (!not_dead_yet) {
  +         /* nothing left to wait for */
  +         break;
  +     }
       }
   #endif /* ndef MULTITHREAD */
   }
   
  +
   #if defined(BROKEN_WAIT) || defined(NEED_WAITPID)
   /*
   Some systems appear to fail to deliver dead children to wait() at times.
  @@ -1687,9 +1831,9 @@
   
       for (n = 0; n < max_daemons_limit; ++n) {
        if (scoreboard_image->servers[n].status != SERVER_DEAD
  -             && waitpid (scoreboard_image->servers[n].x.pid, &status, 
WNOHANG)
  -                 == -1
  -             && errno == ECHILD) {
  +         && waitpid (scoreboard_image->servers[n].x.pid, &status, WNOHANG)
  +             == -1
  +         && errno == ECHILD) {
            sync_scoreboard_image ();
            update_child_status (n, SERVER_DEAD, NULL);
            ret = 1;
  @@ -1703,7 +1847,13 @@
    * a while...
    */
   
  -static int wait_or_timeout (void)
  +/* number of calls to wait_or_timeout between writable probes */
  +#ifndef INTERVAL_OF_WRITABLE_PROBES
  +#define INTERVAL_OF_WRITABLE_PROBES 10
  +#endif
  +static int wait_or_timeout_counter;
  +
  +static int wait_or_timeout (int *status)
   {
   #ifdef WIN32
   #define MAXWAITOBJ MAXIMUM_WAIT_OBJECTS
  @@ -1740,21 +1890,22 @@
   
   #else /* WIN32 */
       struct timeval tv;
  -#ifndef NEED_WAITPID
       int ret;
   
  -    ret = waitpid (-1, NULL, WNOHANG);
  +    ++wait_or_timeout_counter;
  +    if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
  +     wait_or_timeout_counter = 0;
  +#ifndef NO_OTHER_CHILD
  +     probe_writable_fds();
  +#endif
  +    }
  +    ret = waitpid (-1, status, WNOHANG);
       if (ret == -1 && errno == EINTR) {
        return -1;
       }
       if (ret > 0) {
        return ret;
       }
  -#else
  -    if (reap_children ()) {
  -     return -1;
  -    }
  -#endif
       tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000;
       tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000;
       ap_select(0, NULL, NULL, NULL, &tv);
  @@ -2009,6 +2160,10 @@
       return (suexec_enabled);
   }
   
  +/*****************************************************************
  + * Connection structures and accounting...
  + */
  +
   /* This hashing function is designed to get good distribution in the cases
    * where the server is handling entire "networks" of servers.  i.e. a
    * whack of /24s.  This is probably the most common configuration for
  @@ -3183,7 +3338,8 @@
   
        while (!restart_pending && !shutdown_pending) {
            int child_slot;
  -         int pid = wait_or_timeout();
  +         int status;
  +         int pid = wait_or_timeout (&status);
   
            /* XXX: if it takes longer than 1 second for all our children
             * to start up and get into IDLE state then we may spawn an
  @@ -3207,6 +3363,10 @@
                        /* don't perform idle maintenance yet */
                        continue;
                    }
  +#ifndef NO_OTHER_CHILD
  +             } else if (reap_other_child (pid, status) == 0) {
  +                 /* handled */
  +#endif
                } else if (is_graceful) {
                    /* Great, we've probably just lost a slot in the
                     * scoreboard.  Somehow we don't know about this
  
  
  
  1.18      +40 -0     apachen/src/main/http_main.h
  
  Index: http_main.h
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/main/http_main.h,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- http_main.h       1997/09/10 17:43:23     1.17
  +++ http_main.h       1997/09/12 20:13:19     1.18
  @@ -97,3 +97,43 @@
   void time_process_request (int child_num, int status);
   unsigned int set_callback_and_alarm(void (*fn)(int), int x);
   int check_alarm(void);
  +
  +#ifndef NO_OTHER_CHILD
  +/*
  + * register an other_child -- a child which the main loop keeps track of
  + * and knows it is different than the rest of the scoreboard.
  + *
  + * pid is the pid of the child.
  + *
  + * maintenance is a function that is invoked with a reason, the data
  + * pointer passed here, and when appropriate a status result from waitpid().
  + *
  + * write_fd is an fd that is probed for writing by select() if it is ever
  + * unwritable, then maintenance is invoked with reason OC_REASON_UNWRITABLE.
  + * This is useful for log pipe children, to know when they've blocked.  To
  + * disable this feature, use -1 for write_fd.
  + */
  +API_EXPORT(void) register_other_child (int pid,
  +    void (*maintenance)(int reason, void *data, int status), void *data,
  +    int write_fd);
  +#define OC_REASON_DEATH              0       /* child has died, caller must 
call
  +                                         * unregister still */
  +#define OC_REASON_UNWRITABLE 1       /* write_fd is unwritable */
  +#define OC_REASON_RESTART    2       /* a restart is occuring, perform
  +                                      * any necessary cleanup (including
  +                                      * sending a special signal to child)
  +                                      */
  +#define OC_REASON_UNREGISTER 3       /* unregister has been called, do
  +                                      * whatever is necessary (including
  +                                      * kill the child) */
  +#define OC_REASON_LOST               4       /* somehow the child exited 
without
  +                                      * us knowing ... buggy os? */
  +
  +/*
  + * unregister an other_child.  Note that the data pointer is used here, and
  + * is assumed to be unique per other_child.  This is because the pid and
  + * write_fd are possibly killed off separately.
  + */
  +API_EXPORT(void) unregister_other_child (void *data);
  +
  +#endif
  
  
  
  1.38      +7 -35     apachen/src/modules/standard/mod_log_config.c
  
  Index: mod_log_config.c
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/modules/standard/mod_log_config.c,v
  retrieving revision 1.37
  retrieving revision 1.38
  diff -u -r1.37 -r1.38
  --- mod_log_config.c  1997/09/12 14:50:53     1.37
  +++ mod_log_config.c  1997/09/12 20:13:24     1.38
  @@ -164,6 +164,7 @@
   #include "httpd.h"
   #include "http_config.h"
   #include "http_core.h" /* For REMOTE_NAME */
  +#include "http_log.h"
   
   module MODULE_VAR_EXPORT config_log_module;
   
  @@ -717,32 +718,6 @@
   { NULL }
   };
   
  -static int config_log_child (void *cmd)
  -{
  -    /* Child process code for 'TransferLog "|..."';
  -     * may want a common framework for this, since I expect it will
  -     * be common for other foo-loggers to want this sort of thing...
  -     */
  -    int child_pid = 1;
  -
  -    cleanup_for_exec();
  -#ifdef SIGHUP
  -    signal (SIGHUP, SIG_IGN);
  -#endif
  -#if defined(WIN32)
  -    child_pid = spawnl (_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char 
*)cmd, NULL);
  -    return(child_pid);
  -#elif defined(__EMX__)
  -    /* For OS/2 we need to use a '/' */
  -    execl (SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
  -#else
  -    execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
  -#endif
  -    perror ("exec");
  -    fprintf (stderr, "Exec of shell for logging failed!!!\n");
  -    return(child_pid);
  -}
  -
   static config_log_state *open_config_log (server_rec *s, pool *p,
                                   config_log_state *cls,
                                   array_header *default_format) {
  @@ -753,16 +728,13 @@
       }
   
       if (*cls->fname == '|') {
  -        FILE *dummy;
  -        
  -        if (!spawn_child (p, config_log_child, (void *)(cls->fname+1),
  -                    kill_after_timeout, &dummy, NULL)) {
  -         perror ("spawn_child");
  -            fprintf (stderr, "Couldn't fork child for TransferLog 
process\n");
  -            exit (1);
  -        }
  +     piped_log *pl;
   
  -        cls->log_fd = fileno (dummy);
  +     pl = open_piped_log (p, cls->fname + 1);
  +     if (pl == NULL) {
  +         exit (1);
  +     }
  +     cls->log_fd = piped_log_write_fd (pl);
       }
       else {
           char *fname = server_root_relative (p, cls->fname);
  
  
  
  1.3       +3 -1      apachen/src/os/win32/os.h
  
  Index: os.h
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/os/win32/os.h,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- os.h      1997/09/12 09:06:43     1.2
  +++ os.h      1997/09/12 20:13:25     1.3
  @@ -67,7 +67,9 @@
   /* Seems Windows is not a subgenius */
   #define NO_SLACK
   #include <stddef.h>
  -/* Unknown system - Edit these to match */
  +
  +#define NO_OTHER_CHILD
  +#define NO_RELIABLE_PIPED_LOGS
   
   
   API_EXPORT(int)os_is_path_absolute(char *f);
  
  
  

Reply via email to