You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Dean Gaudet <dg...@arctic.org> on 1997/11/03 22:50:59 UTC

aplogger

Ok, I admit there were gotchas in my cat and grep suggestions. 
Specifically, they don't deal gracefully with termination signals, a few
log entries may be lost on rotation.

Included, find aplogger.c, which doesn't have these problems.  It's only
the beginning of a solution -- because a full solution requires the logger
to be in its own process group.  Otherwise the killpg()s that apache does
will send signals to all the logging processes.  But at any rate ... 

Oh yeah, this should build on any POSIX system, when we have a libap it
would be easy to make it use our portability stuff.

Dean

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

static int deferred_exit;
static const char *progname;

#define BLOCK_SIZE	4096

static void sig_handler(int sig)
{
    deferred_exit = 1;
}

static void install_signal_handlers(void)
{
    struct sigaction sa;
    int i;

    static const int sigs[] = {
#ifdef SIGHUP
	SIGHUP,
#endif
#ifdef SIGINT
	SIGINT,
#endif
#ifdef SIGQUIT
	SIGQUIT,
#endif
#ifdef SIGPIPE
	SIGPIPE,
#endif
#ifdef SIGUSR1
	SIGUSR1,
#endif
#ifdef SIGTERM
	SIGTERM,
#endif
#ifdef SIGPWR
	SIGPWR,
#endif
	0
    };

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = sig_handler;
    for (i = 0; sigs[i]; ++i) {
	if (sigaction(sigs[i], &sa, NULL) < 0) {
	    fprintf(stderr,
		"%s: error installing signal handler for signal %d: %s\n",
		progname, sigs[i], strerror(errno));
	    exit(1);
	}
    }
}


static inline void safe_write(int f, const void *b, size_t l)
{
    int rc;

    while (l) {
        rc = write(f, b, l);
        if (rc == -1) {
            if (errno != EINTR) {
                fprintf(stderr, "%s: write error: %s\n", progname,
                    strerror(errno));
                exit(1);
            }
        }
        if (rc > 0) {
            l -= rc;
            b = (void *)((char *)b + rc);
        }
    }
}


void main(int argc, char **argv)
{
    char buf[BLOCK_SIZE];
    int rc;
    FILE *pid;

    progname = argv[0];
    if (argc != 2) {
	fprintf(stderr, "usage: %s pidfilename >> logfile\n", progname);
	exit(1);
    }

    install_signal_handlers();

    if ((pid = fopen(argv[1], "w")) == NULL) {
	fprintf(stderr, "%s: unable to open %s for writing: %s\n",
	    progname, argv[1], strerror(errno));
	exit(1);
    }
    fprintf(pid, "%u\n", getpid());
    fclose(pid);

    for(;;) {
	if (deferred_exit) {
	    exit(0);
	}
	rc = read(STDIN_FILENO, buf, sizeof(buf));
	if (rc <= 0) {
	    if (rc == -1 && errno != EINTR) {
		fprintf(stderr, "%s: error reading stdin: %s\n",
		    progname, strerror(errno));
		exit(1);
	    }
	    if (rc == 0) {
		exit(0);
	    }
	    /* otherwise it's an EINTR, and we'll catch it at the top of
	     * the loop
	     */
	} else {
	    safe_write(STDOUT_FILENO, buf, rc);
	}
    }
}




Re: aplogger

Posted by Dean Gaudet <dg...@arctic.org>.

On Mon, 3 Nov 1997, Marc Slemko wrote:

> You can argue until you are camel-blue in the fact that it is better, and
> that you should only need one of these for all your servers, and you can
> split it out in the logger process if you feel like it.  I agree with you. 
> But... you won't get people to buy that. 

This is exactly how people react to qmail the first time they deal with
its logging.  After a while most of them come to realise it's more
powerful, and the lack of the more "traditional" styles of logging (i.e.
monolithic, feature-laden programs) is not a lack at all.

I'm thinking of actually implementing my "new" security model for apache
and including all these utilities in a separate package.  I need a name
though.

Dean


Re: aplogger

Posted by Marc Slemko <ma...@worldgate.com>.
On Mon, 3 Nov 1997, Dean Gaudet wrote:

> Uh, yeah, ok once more, with the patch. 
> 
> Oh I neglected to mention, that the reason aplogger logs to stdout is that
> with the process group tweak, you can do things like:
> 
>     TransferLog "| bin/aplogger logs/logger.pid | grep -v foobar >>logs/access_log"

So you use two extra processes (erm... make that three, no?) for each log
file.

You can argue until you are camel-blue in the fact that it is better, and
that you should only need one of these for all your servers, and you can
split it out in the logger process if you feel like it.  I agree with you. 
But... you won't get people to buy that. 

I'm all for including a number of useful scripts or programs to use when
logging to a pipe.  Basic support for those that don't want to would be
cool too.  Patch if I get time...

Hmm.  "apache-contrib.tar.gz"?  For things that we want to have more than
current contrib (OTOH, fixing the current contrib archives would help a
lot...) but aren't wanting to put in the base distribution.  If only
someone had time... 

> 
> And sending TERM to aplogger is good enough to rotate, without
> losing hits.
> 
> Dean
> 
> On Mon, 3 Nov 1997, Dean Gaudet wrote:
> 
> > 
> > 
> > On Mon, 3 Nov 1997, Dean Gaudet wrote:
> > 
> > > Included, find aplogger.c, which doesn't have these problems.  It's only
> > > the beginning of a solution -- because a full solution requires the logger
> > > to be in its own process group.  Otherwise the killpg()s that apache does
> > > will send signals to all the logging processes.  But at any rate ... 
> > 
> > Included find a patch which gives the logger its own process group, and
> > also chdir(server_root) before spawning.  This allows server_root relative
> > pathnames to be used, such as: 
> > 
> >     TransferLog "| bin/aplogger logs/logger.pid >> logs/access_log"
> > 
> > I'm getting a "long lost child came home" while using this across a
> > USR1...  looks like a latent bug in my register_other_child api.  I'll fix
> > it later.  It seems to work well enough. 
> > 
> > arctic.org is now using this for logging.  I figure I should, since I'm
> > advocating it ;)
> > 
> > Dean
> 
> Index: main/http_log.c
> ===================================================================
> RCS file: /export/home/cvs/apachen/src/main/http_log.c,v
> retrieving revision 1.42
> diff -u -r1.42 http_log.c
> --- http_log.c	1997/10/27 19:09:43	1.42
> +++ http_log.c	1997/11/03 21:58:01
> @@ -65,6 +65,7 @@
>  #include "http_core.h"
>  #include "http_log.h"
>  #include "http_main.h"
> +#include "http_conf_globals.h"
>  
>  #include <stdarg.h>
>  
> @@ -338,15 +339,15 @@
>  }
>      
>  
> -void log_pid (pool *p, char *pid_fname)
> +void log_pid (pool *p, char *fname)
>  {
>      FILE *pid_file;
>  
> -    if (!pid_fname) return;
> -    pid_fname = server_root_relative (p, pid_fname);
> -    if(!(pid_file = fopen(pid_fname,"w"))) {
> +    if (!fname) return;
> +    fname = server_root_relative (p, fname);
> +    if(!(pid_file = fopen(fname,"w"))) {
>  	perror("fopen");
> -        fprintf(stderr,"httpd: could not log pid to file %s\n", pid_fname);
> +        fprintf(stderr,"httpd: could not log pid to file %s\n", fname);
>          exit(1);
>      }
>      fprintf(pid_file,"%ld\n",(long)getpid());
> @@ -405,6 +406,7 @@
>  static int piped_log_spawn (piped_log *pl)
>  {
>      int pid;
> +    int pgrp;
>  
>      block_alarms();
>      pid = fork();
> @@ -414,6 +416,36 @@
>  	 * 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 */
> +	/* cd ServerRoot so that it's easy to construct portable logger
> +	 * lines */
> +	if (chdir(server_root) == -1) {
> +	    aplog_error(APLOG_MARK, APLOG_CRIT, NULL,
> +		"logger: unable to chdir(%s)", server_root);
> +	    exit (1);
> +	}
> +#ifndef NO_SETSID
> +	if ((pgrp = setsid()) == -1) {
> +	    aplog_error(APLOG_MARK, APLOG_CRIT, NULL, "logger: setsid failed");
> +	    exit(1);
> +	}
> +#elif defined(NEXT) || defined(NEWSOS)
> +	if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) {
> +	    aplog_error(APLOG_MARK, APLOG_CRIT, NULL,
> +		"logger: setpgrp or getpgrp failed");
> +	    exit(1);
> +	}
> +#elif defined(__EMX__)
> +	/* OS/2 don't support process group IDs */
> +	pgrp = -getpid();
> +#elif defined(MPE)
> +	/* MPE uses negative pid for process group */
> +	pgrp = -getpid();
> +#else
> +	if ((pgrp = setpgrp(getpid(), 0)) == -1) {
> +	    aplog_error(APLOG_MARK, APLOG_CRIT, NULL, "logger: setpgrp failed");
> +	    exit(1);
> +	}
> +#endif
>  	close (STDIN_FILENO);
>  	dup2 (pl->fds[0], STDIN_FILENO);
>  
> 


Re: aplogger

Posted by Dean Gaudet <dg...@arctic.org>.
Uh, yeah, ok once more, with the patch. 

Oh I neglected to mention, that the reason aplogger logs to stdout is that
with the process group tweak, you can do things like:

    TransferLog "| bin/aplogger logs/logger.pid | grep -v foobar >>logs/access_log"

And sending TERM to aplogger is good enough to rotate, without
losing hits.

Dean

On Mon, 3 Nov 1997, Dean Gaudet wrote:

> 
> 
> On Mon, 3 Nov 1997, Dean Gaudet wrote:
> 
> > Included, find aplogger.c, which doesn't have these problems.  It's only
> > the beginning of a solution -- because a full solution requires the logger
> > to be in its own process group.  Otherwise the killpg()s that apache does
> > will send signals to all the logging processes.  But at any rate ... 
> 
> Included find a patch which gives the logger its own process group, and
> also chdir(server_root) before spawning.  This allows server_root relative
> pathnames to be used, such as: 
> 
>     TransferLog "| bin/aplogger logs/logger.pid >> logs/access_log"
> 
> I'm getting a "long lost child came home" while using this across a
> USR1...  looks like a latent bug in my register_other_child api.  I'll fix
> it later.  It seems to work well enough. 
> 
> arctic.org is now using this for logging.  I figure I should, since I'm
> advocating it ;)
> 
> Dean

Index: main/http_log.c
===================================================================
RCS file: /export/home/cvs/apachen/src/main/http_log.c,v
retrieving revision 1.42
diff -u -r1.42 http_log.c
--- http_log.c	1997/10/27 19:09:43	1.42
+++ http_log.c	1997/11/03 21:58:01
@@ -65,6 +65,7 @@
 #include "http_core.h"
 #include "http_log.h"
 #include "http_main.h"
+#include "http_conf_globals.h"
 
 #include <stdarg.h>
 
@@ -338,15 +339,15 @@
 }
     
 
-void log_pid (pool *p, char *pid_fname)
+void log_pid (pool *p, char *fname)
 {
     FILE *pid_file;
 
-    if (!pid_fname) return;
-    pid_fname = server_root_relative (p, pid_fname);
-    if(!(pid_file = fopen(pid_fname,"w"))) {
+    if (!fname) return;
+    fname = server_root_relative (p, fname);
+    if(!(pid_file = fopen(fname,"w"))) {
 	perror("fopen");
-        fprintf(stderr,"httpd: could not log pid to file %s\n", pid_fname);
+        fprintf(stderr,"httpd: could not log pid to file %s\n", fname);
         exit(1);
     }
     fprintf(pid_file,"%ld\n",(long)getpid());
@@ -405,6 +406,7 @@
 static int piped_log_spawn (piped_log *pl)
 {
     int pid;
+    int pgrp;
 
     block_alarms();
     pid = fork();
@@ -414,6 +416,36 @@
 	 * 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 */
+	/* cd ServerRoot so that it's easy to construct portable logger
+	 * lines */
+	if (chdir(server_root) == -1) {
+	    aplog_error(APLOG_MARK, APLOG_CRIT, NULL,
+		"logger: unable to chdir(%s)", server_root);
+	    exit (1);
+	}
+#ifndef NO_SETSID
+	if ((pgrp = setsid()) == -1) {
+	    aplog_error(APLOG_MARK, APLOG_CRIT, NULL, "logger: setsid failed");
+	    exit(1);
+	}
+#elif defined(NEXT) || defined(NEWSOS)
+	if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) {
+	    aplog_error(APLOG_MARK, APLOG_CRIT, NULL,
+		"logger: setpgrp or getpgrp failed");
+	    exit(1);
+	}
+#elif defined(__EMX__)
+	/* OS/2 don't support process group IDs */
+	pgrp = -getpid();
+#elif defined(MPE)
+	/* MPE uses negative pid for process group */
+	pgrp = -getpid();
+#else
+	if ((pgrp = setpgrp(getpid(), 0)) == -1) {
+	    aplog_error(APLOG_MARK, APLOG_CRIT, NULL, "logger: setpgrp failed");
+	    exit(1);
+	}
+#endif
 	close (STDIN_FILENO);
 	dup2 (pl->fds[0], STDIN_FILENO);
 


Re: aplogger

Posted by Dean Gaudet <dg...@arctic.org>.

On Mon, 3 Nov 1997, Dean Gaudet wrote:

> Included, find aplogger.c, which doesn't have these problems.  It's only
> the beginning of a solution -- because a full solution requires the logger
> to be in its own process group.  Otherwise the killpg()s that apache does
> will send signals to all the logging processes.  But at any rate ... 

Included find a patch which gives the logger its own process group, and
also chdir(server_root) before spawning.  This allows server_root relative
pathnames to be used, such as: 

    TransferLog "| bin/aplogger logs/logger.pid >> logs/access_log"

I'm getting a "long lost child came home" while using this across a
USR1...  looks like a latent bug in my register_other_child api.  I'll fix
it later.  It seems to work well enough. 

arctic.org is now using this for logging.  I figure I should, since I'm
advocating it ;)

Dean