Return-Path: Delivered-To: new-httpd-archive@hyperreal.org Received: (qmail 24449 invoked by uid 6000); 26 Mar 1998 05:36:59 -0000 Received: (qmail 24442 invoked from network); 26 Mar 1998 05:36:57 -0000 Received: from twinlark.arctic.org (204.62.130.91) by taz.hyperreal.org with SMTP; 26 Mar 1998 05:36:57 -0000 Received: (qmail 11413 invoked by uid 500); 26 Mar 1998 05:50:29 -0000 Date: Wed, 25 Mar 1998 21:50:29 -0800 (PST) From: Dean Gaudet To: new-httpd@apache.org Subject: arctic.patch Message-ID: X-Comment: Visit http://www.arctic.org/~dgaudet/legal for information regarding copyright and disclaimer. MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Sender: new-httpd-owner@apache.org Precedence: bulk Reply-To: new-httpd@apache.org Hey I keep saying I'm always running HEAD... but the truth is I've always got a few mods. Is there anything here that folks think I should clean up and commit? This contains: - the -p command line switch so that you can pass pre-opened sockets to the server (so it doesn't need to run as root at all) - listenwrap, which can be setuid root and spawn the server with a -p option - SymLinksIfGroupMatch, which I find absolutely necessary for systems that have groups maintaining a web site... although I've stopped using it in favour of mod_allowdev. - gif89-expires-hack - some extra debugging log messages - the piped log child is spawned in its own process group, chdir()d to server_root... this is so that you can use server_root relative paths in the piped log command - USE_MMAP_FILES is disabled. I did this last week for a very specific reason... I'll post more later. Dean Index: include/http_core.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/http_core.h,v retrieving revision 1.39 diff -u -r1.39 http_core.h --- http_core.h 1998/03/17 19:38:40 1.39 +++ http_core.h 1998/03/26 05:30:41 @@ -75,6 +75,7 @@ #define OPT_INCNOEXEC 32 #define OPT_SYM_OWNER 64 #define OPT_MULTI 128 +#define OPT_SYM_GROUP 256 #define OPT_ALL (OPT_INDEXES|OPT_INCLUDES|OPT_SYM_LINKS|OPT_EXECCGI) /* options for get_remote_host() */ @@ -136,7 +137,7 @@ /* Per-directory configuration */ -typedef unsigned char allow_options_t; +typedef unsigned short allow_options_t; typedef unsigned char overrides_t; typedef struct { Index: main/buff.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/buff.c,v retrieving revision 1.66 diff -u -r1.66 buff.c --- buff.c 1998/03/25 02:57:22 1.66 +++ buff.c 1998/03/26 05:30:41 @@ -926,7 +926,9 @@ * chunk before proceeding onto anything else. This routine either writes * nbytes and returns 0 or returns -1 indicating a failure. * - * This is *seriously broken* if used on a non-blocking fd. It will poll. + * XXX: When used on a non-blocking fd it may block until it can write. + * (This is somewhat easier to do than it is to remember all the appropriate + * state and return to the caller.) * * Deals with calling doerror and setting bytes_sent. */ @@ -944,6 +946,19 @@ doerror(fb, B_WR); return -1; } + if (errno == EAGAIN) { + /* hack hack: it's non-blocking, block until it's ready for + * another write. + */ + fd_set fds; + int rv; + + do { + FD_ZERO(&fds); + FD_SET(fb->fd, &fds); + rv = ap_select(fb->fd + 1, NULL, &fds, NULL, NULL); + } while (rv == -1 && errno == EINTR); + } } else { nbyte -= i; @@ -1034,7 +1049,6 @@ * an interim solution pending a complete rewrite of all this stuff in * 2.0, using something like sfio stacked disciplines or BSD's funopen(). * - * Can be used on non-blocking descriptors, but only if they're not chunked. * Deals with doerror() and bytes_sent. */ static int bcwrite(BUFF *fb, const void *buf, int nbyte) Index: main/http_core.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/http_core.c,v retrieving revision 1.175 diff -u -r1.175 http_core.c --- http_core.c 1998/03/17 19:38:42 1.175 +++ http_core.c 1998/03/26 05:30:41 @@ -66,6 +66,8 @@ #include "scoreboard.h" #include "fnmatch.h" +#undef USE_MMAP_FILES + #ifdef USE_MMAP_FILES #include @@ -820,6 +822,8 @@ opt = OPT_SYM_LINKS; else if(!strcasecmp(w,"SymLinksIfOwnerMatch")) opt = OPT_SYM_OWNER; + else if(!strcasecmp(w,"SymLinksIfGroupMatch")) + opt = OPT_SYM_GROUP; else if(!strcasecmp(w,"execCGI")) opt = OPT_EXECCGI; else if (!strcasecmp(w,"MultiViews")) @@ -1995,7 +1999,9 @@ if (r->proxyreq) return HTTP_FORBIDDEN; if ((r->uri[0] != '/') && strcmp(r->uri, "*")) { aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server, - "Invalid URI in request %s", r->the_request); + "(%s) Invalid URI in request %s", + get_remote_host(r->connection, NULL, REMOTE_NOLOOKUP), + r->the_request); return BAD_REQUEST; } @@ -2115,6 +2121,31 @@ if (d->content_md5 & 1) { table_setn(r->headers_out, "Content-MD5", ap_md5digest(r->pool, f)); } + /* Shameless hack: if the file to be sent is image/gif and + * gif89-expires-hack is set then remove the Expires header. This is + * required because of a bug in Mozilla versions up through at least 4 + * which causes it to re-request the animation every loop. You + * probably want this in your config: + * + * BrowserMatch Mozilla/[0-4]\. gif89-expires-hack + * + * -djg + */ + if (r->content_type + && strcasecmp (r->content_type, "image/gif") == 0 + && r->finfo.st_size > 6 + && table_get (r->subprocess_env, "gif89-expires-hack")) { +#define GIF_HEADER_LEN (6) + char gif_header[ GIF_HEADER_LEN ]; + + if (fread (gif_header, 1, GIF_HEADER_LEN, f) == GIF_HEADER_LEN + && memcmp (gif_header, "GIF89a", GIF_HEADER_LEN) == 0) { + table_unset (r->headers_out, "Expires"); + table_unset (r->headers_out, "Cache-Control"); + } + fseek (f, 0L, SEEK_SET); +#undef GIF_HEADER_LEN + } rangestatus = set_byterange(r); #ifdef CHARSET_EBCDIC @@ -2159,6 +2190,15 @@ MD5Update(&context, (void *)mm, r->finfo.st_size); table_setn(r->headers_out, "Content-MD5", ap_md5contextTo64(r->pool, &context)); + } + /* Shameless gif89-expires-hack, see above */ + if (r->content_type + && strcasecmp (r->content_type, "image/gif") == 0 + && table_get (r->subprocess_env, "gif89-expires-hack") + && r->finfo.st_size > 6 + && memcmp (mm, "GIF89a", 6) == 0) { + table_unset (r->headers_out, "Expires"); + table_unset (r->headers_out, "Cache-Control"); } rangestatus = set_byterange(r); Index: main/http_log.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/http_log.c,v retrieving revision 1.49 diff -u -r1.49 http_log.c --- http_log.c 1998/03/17 07:54:19 1.49 +++ http_log.c 1998/03/26 05:30:42 @@ -65,6 +65,7 @@ #include "http_core.h" #include "http_log.h" #include "http_main.h" +#include "http_conf_globals.h" #include @@ -385,15 +386,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()); @@ -452,6 +453,7 @@ static int piped_log_spawn (piped_log *pl) { int pid; + int pgrp; block_alarms(); pid = fork(); @@ -462,6 +464,36 @@ * XXX: close all the relevant stuff, but hey, it could be broken. */ RAISE_SIGSTOP(PIPED_LOG_SPAWN); /* 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); @@ -491,6 +523,9 @@ { piped_log *pl = data; + aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL, + "piped_log_maintenance reason = %d, status = %d", reason, + status); switch (reason) { case OC_REASON_DEATH: case OC_REASON_LOST: Index: main/http_main.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/http_main.c,v retrieving revision 1.311 diff -u -r1.311 http_main.c --- http_main.c 1998/03/25 10:07:50 1.311 +++ http_main.c 1998/03/26 05:30:42 @@ -258,6 +258,7 @@ */ listen_rec *listeners; static listen_rec *head_listener; +static listen_rec *permanent_listeners; char server_root[MAX_STRING_LEN]; char server_confname[MAX_STRING_LEN]; @@ -2753,6 +2754,9 @@ { listen_rec *lr; + if (permanent_listeners) { + return; + } ap_assert(old_listeners == NULL); if (listeners == NULL) { return; @@ -2808,6 +2812,20 @@ listen_rec *lr; int fd; + if (permanent_listeners) { + lr = permanent_listeners; + listeners = lr; + head_listener = lr; + do { + if (lr->next == NULL) { + /* first time through, create the loop */ + lr->next = permanent_listeners; + return; + } + lr = lr->next; + } while (lr != permanent_listeners); + return; + } listenmaxfd = -1; FD_ZERO(&listenfds); lr = listeners; @@ -3635,6 +3653,9 @@ * shut down gracefully, in case it happened to pick up a request * while we were counting */ + aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "server has %d children, %d are idle, killing one", + total_non_dead, idle_count); kill(scoreboard_image->parent[to_kill].pid, SIGUSR1); idle_spawn_rate = 1; } @@ -3906,6 +3927,58 @@ void STANDALONE_MAIN(int argc, char **argv); #endif /* STANDALONE_MAIN */ +static void pre_opened_socket(const char *arg) +{ + int fd; + struct sockaddr_in local_addr; + listen_rec *lr; + NET_SIZE_T clen; + + fd = atoi(arg); + + clen = sizeof(local_addr); + if (getsockname(fd, (struct sockaddr *)&local_addr, &clen) == -1) { + aplog_error(APLOG_MARK, APLOG_ERR, NULL, + "error parsing arg -p '%s', skipping", arg); + return; + } + + if (local_addr.sin_family != AF_INET) { + aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, + "arg -p %d is not an AF_INET socket, skipping", fd); + return; + } + +#ifdef LINUX + /* work around linux 2.0.x bug */ + if (local_addr.sin_addr.s_addr == htonl(0x7f000001)) { + local_addr.sin_addr.s_addr = 0; + } +#endif + + /* We don't want these descriptors to be passed down to the + * grandchildren. Since we're not going to manage them in pools + * we have to set the close-on-exec flag. + */ + if (fcntl(fd, F_SETFD, 1)) { + aplog_error(APLOG_MARK, APLOG_ERR, NULL, + "arg -p %d, can't fcntl(%d, F_SETFD, 1), skipping", fd, fd); + return; + } + + lr = malloc(sizeof(listen_rec)); + if (lr == NULL) { + aplog_error(APLOG_MARK, APLOG_CRIT, NULL, + "out of memory allocating permanent_listener"); + exit(1); + } + lr->local_addr = local_addr; + lr->fd = fd; + lr->used = 0; + lr->next = permanent_listeners; + permanent_listeners = lr; +} + extern char *optarg; extern int optind; @@ -3924,9 +3997,9 @@ setup_prelinked_modules(); #ifdef DEBUG_SIGSTOP - while ((c = getopt(argc, argv, "C:c:Xd:f:vVhlZ:")) != -1) { + while ((c = getopt(argc, argv, "C:c:Xd:f:vVhlZ:p:")) != -1) { #else - while ((c = getopt(argc, argv, "C:c:Xd:f:vVhl")) != -1) { + while ((c = getopt(argc, argv, "C:c:Xd:f:vVhlp:")) != -1) { #endif char **new; switch (c) { @@ -3957,6 +4030,9 @@ case 'l': show_modules(); exit(0); + case 'p': + pre_opened_socket(optarg); + break; case 'X': ++one_process; /* Weird debugging mode. */ break; Index: main/http_request.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/http_request.c,v retrieving revision 1.113 diff -u -r1.113 http_request.c --- http_request.c 1998/03/17 07:54:22 1.113 +++ http_request.c 1998/03/26 05:30:43 @@ -153,13 +153,19 @@ /* OK, it's a symlink. May still be OK with OPT_SYM_OWNER */ - if (!(opts & OPT_SYM_OWNER)) + if (!(opts & (OPT_SYM_OWNER|OPT_SYM_GROUP))) return HTTP_FORBIDDEN; if (stat(d, &fi) < 0) return HTTP_FORBIDDEN; - return (fi.st_uid == lfi.st_uid) ? OK : HTTP_FORBIDDEN; + if ((opts & OPT_SYM_OWNER) && fi.st_uid == lfi.st_uid) + return OK; + + if ((opts & OPT_SYM_GROUP) && fi.st_gid == lfi.st_gid) + return OK; + + return HTTP_FORBIDDEN; #endif } Index: modules/standard/mod_rewrite.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/standard/mod_rewrite.c,v retrieving revision 1.93 diff -u -r1.93 mod_rewrite.c --- mod_rewrite.c 1998/03/13 19:20:42 1.93 +++ mod_rewrite.c 1998/03/26 05:30:45 @@ -1247,7 +1247,7 @@ * only do something under runtime if the engine is really enabled, * for this directory, else return immediately! */ - if (!(allow_options(r) & (OPT_SYM_LINKS | OPT_SYM_OWNER))) { + if (!(allow_options(r) & (OPT_SYM_LINKS | OPT_SYM_OWNER | OPT_SYM_GROUP))) { /* FollowSymLinks is mandatory! */ aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server, "Options FollowSymLinks or SymLinksIfOwnerMatch is off " Index: support/.cvsignore =================================================================== RCS file: /export/home/cvs/apache-1.3/src/support/.cvsignore,v retrieving revision 1.5 diff -u -r1.5 .cvsignore --- .cvsignore 1998/03/17 13:59:01 1.5 +++ .cvsignore 1998/03/26 05:30:47 @@ -1,3 +1,4 @@ +listenwrap Makefile rotatelogs htpasswd Index: support/Makefile.tmpl =================================================================== RCS file: /export/home/cvs/apache-1.3/src/support/Makefile.tmpl,v retrieving revision 1.12 diff -u -r1.12 Makefile.tmpl --- Makefile.tmpl 1998/03/17 15:42:42 1.12 +++ Makefile.tmpl 1998/03/26 05:30:47 @@ -25,6 +25,9 @@ logresolve: logresolve.o $(CC) $(INCLUDES) $(CFLAGS) logresolve.o -o logresolve $(LDFLAGS) $(LIBS) +listenwrap: listenwrap.o + $(CC) $(INCLUDES) $(CFLAGS) listenwrap.o -o listenwrap $(LDFLAGS) $(LIBS) + ab: ab.o $(CC) $(INCLUDES) $(CFLAGS) ab.o -o ab $(LDFLAGS) $(LIBS) Index: support/listenwrap.c =================================================================== RCS file: listenwrap.c diff -N listenwrap.c --- /dev/null Wed Mar 25 21:30:00 1998 +++ listenwrap.c Wed Mar 25 21:30:47 1998 @@ -0,0 +1,148 @@ +#include "httpd.h" + +#define PORT 80 +#define SEND_BUFFER_SIZE 16384 +#define LISTEN_BACKLOG 511 +#define HTTP_USER 511 +#define HTTP_GROUP 511 +#define HTTPD_PATH "/home/www/bin/httpd" + +/* stolen from http_main.c ... should be turned into a lib function I guess */ + +#if defined(TCP_NODELAY) && !defined(MPE) +static void sock_disable_nagle(int s) +{ + /* The Nagle algorithm says that we should delay sending partial + * packets in hopes of getting more data. We don't want to do + * this; we are not telnet. There are bad interactions between + * persistent connections and Nagle's algorithm that have very severe + * performance penalties. (Failing to disable Nagle is not much of a + * problem with simple HTTP.) + * + * In spite of these problems, failure here is not a shooting offense. + */ + int just_say_no = 1; + + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no, + sizeof(int)) < 0) { + perror("setsockopt(TCP_NODELAY)"); + } +} + +#else +#define sock_disable_nagle(s) /* NOOP */ +#endif + +#ifndef MAX_SECS_TO_LINGER +#define MAX_SECS_TO_LINGER 30 +#endif + +#ifdef USE_SO_LINGER +#define NO_LINGCLOSE /* The two lingering options are exclusive */ + +static void sock_enable_linger(int s) +{ + struct linger li; + + li.l_onoff = 1; + li.l_linger = MAX_SECS_TO_LINGER; + + if (setsockopt(s, SOL_SOCKET, SO_LINGER, + (char *) &li, sizeof(struct linger)) < 0) { + perror("setsockopt(SO_LINGER)"); + /* not a fatal error */ + } +} + +#else +#define sock_enable_linger(s) /* NOOP */ +#endif /* USE_SO_LINGER */ + +static int make_sock(const struct sockaddr_in *local_addr) +{ + int s; + int one = 1; + + if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + perror("socket"); + exit(1); + } + + if (bind(s, (struct sockaddr *) local_addr, + sizeof(struct sockaddr_in)) == -1) { + perror("bind"); + exit(1); + } + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, + sizeof(int)) < 0) { + perror("setsockopt(SO_REUSEADDR)"); + exit(1); + } + one = 1; + if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, + sizeof(int)) < 0) { + perror("setsockopt(SO_KEEPALIVE"); + exit(1); + } + + sock_disable_nagle(s); + sock_enable_linger(s); + + one = SEND_BUFFER_SIZE; + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, + (char *) &one, sizeof(int)) < 0) { + perror("setsockopt(SO_SNDBUF)"); + /* not a fatal error */ + } + + if (listen(s, LISTEN_BACKLOG) == -1) { + perror("listen"); + exit(1); + } + + return s; +} + +void main(int argc, char **argv) +{ + struct sockaddr_in local_addr; + int s; + char **new_argv; + char *new_env[] = { + "SHELL=/bin/sh", + "PATH=/usr/local/bin:/usr/bin:/bin", + NULL + }; + char buf[512]; + + local_addr.sin_family = AF_INET; + local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + local_addr.sin_port = htons(PORT); + + s = make_sock(&local_addr); + + new_argv = malloc(sizeof(char *) * (argc + 3)); + new_argv[0] = HTTPD_PATH; + new_argv[1] = "-p"; + sprintf(buf, "%d", s); + new_argv[2] = buf; + memcpy(new_argv + 3, argv + 1, sizeof(char *) * (argc - 1)); + new_argv[argc+2] = NULL; + + if (setgroups(0, NULL) == -1) { + perror("setgroups"); + exit(1); + } + if (setgid(HTTP_GROUP) == -1) { + perror("setgid"); + exit(1); + } + if (setuid(HTTP_USER) == -1) { + perror("setuid"); + exit(1); + } + execve(HTTPD_PATH, new_argv, new_env); + perror("execve"); + exit(1); +}