diff -Nurbd lighttpd-1.4.21.orig/doc/lighttpd.conf lighttpd-1.4.21/doc/lighttpd.conf --- lighttpd-1.4.21.orig/doc/lighttpd.conf 2008-09-23 21:03:06.000000000 +0200 +++ lighttpd-1.4.21/doc/lighttpd.conf 2009-02-19 12:51:59.000000000 +0100 @@ -140,6 +140,9 @@ ## bind to port (default: 80) #server.port = 81 +## if server.port = 0 the bound port is written to +#server.port-file = "/var/run/lighttpd.port" + ## bind to localhost (default: all interfaces) #server.bind = "127.0.0.1" diff -Nurbd lighttpd-1.4.21.orig/src/array.h lighttpd-1.4.21/src/array.h --- lighttpd-1.4.21.orig/src/array.h 2009-02-05 23:26:19.000000000 +0100 +++ lighttpd-1.4.21/src/array.h 2009-02-19 12:53:31.000000000 +0100 @@ -144,7 +144,8 @@ buffer *host; - unsigned short port; + /* we use port == -1 to determine an uninitialized port */ + int port; time_t disable_ts; int is_disabled; diff -Nurbd lighttpd-1.4.21.orig/src/base.h lighttpd-1.4.21/src/base.h --- lighttpd-1.4.21.orig/src/base.h 2009-02-05 22:35:46.000000000 +0100 +++ lighttpd-1.4.21/src/base.h 2009-02-19 13:12:45.000000000 +0100 @@ -84,6 +84,7 @@ typedef enum { T_CONFIG_UNSET, T_CONFIG_STRING, T_CONFIG_SHORT, + T_CONFIG_INT, T_CONFIG_BOOLEAN, T_CONFIG_ARRAY, T_CONFIG_LOCAL, @@ -462,7 +463,7 @@ } buffer_plugin; typedef struct { - unsigned short port; + int port; buffer *bindhost; buffer *errorlog_file; @@ -499,6 +500,7 @@ } stat_cache_engine; unsigned short enable_cores; unsigned short reject_expect_100_with_417; + buffer *port_file; /* stores the assigned port for port == 0 */ } server_config; typedef struct { @@ -629,6 +631,7 @@ uid_t uid; gid_t gid; + int port_file_fd; /* fd to write the port-number to if server.port == 0 */ } server; diff -Nurbd lighttpd-1.4.21.orig/src/configfile.c lighttpd-1.4.21/src/configfile.c --- lighttpd-1.4.21.orig/src/configfile.c 2009-02-05 22:35:46.000000000 +0100 +++ lighttpd-1.4.21/src/configfile.c 2009-02-19 13:21:31.000000000 +0100 @@ -35,7 +35,8 @@ { "server.chroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 3 */ { "server.username", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 4 */ { "server.groupname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 5 */ - { "server.port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 6 */ + /* type is INT to allow value 0 for random server port and -1 for default */ + { "server.port", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_SERVER }, /* 6 */ { "server.tag", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ { "server.use-ipv6", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ { "server.modules", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 9 */ @@ -96,6 +97,7 @@ { "etag.use-size", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 51 */ { "server.reject-expect-100-with-417", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 52 */ { "debug.log-timeouts", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 53 */ + { "server.port-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 54 */ { "server.host", "use server.bind instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.docroot", "use server.document-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.virtual-root", "load mod_simple_vhost and use simple-vhost.server-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, @@ -222,6 +224,8 @@ cv[50].destination = &(s->etag_use_mtime); cv[51].destination = &(s->etag_use_size); + cv[54].destination = srv->srvconf.port_file; + srv->config_storage[i] = s; if (0 != (ret = config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv))) { @@ -1222,7 +1226,8 @@ } } - if (srv->srvconf.port == 0) { + /* port == -1: means port is still unset (no configuration-value was set in configfile) -> use default */ + if (srv->srvconf.port == -1) { srv->srvconf.port = s->is_ssl ? 443 : 80; } diff -Nurbd lighttpd-1.4.21.orig/src/configfile-glue.c lighttpd-1.4.21/src/configfile-glue.c --- lighttpd-1.4.21.orig/src/configfile-glue.c 2009-02-05 23:26:19.000000000 +0100 +++ lighttpd-1.4.21/src/configfile-glue.c 2009-02-19 13:56:59.000000000 +0100 @@ -1,5 +1,6 @@ #include #include +#include #include "base.h" #include "buffer.h" @@ -116,6 +117,42 @@ return -1; } break; + case T_CONFIG_INT: + switch(du->type) { + case TYPE_INTEGER: { + data_integer *di = (data_integer *)du; + + *((int *)(cv[i].destination)) = di->value; + break; + } + case TYPE_STRING: { + data_string *ds = (data_string *)du; + + /* If the value came from an environment variable, then it is a + * data_string, although it may contain a number in ASCII + * decimal format. We try to interpret the string as a decimal + * short before giving up, in order to support setting numeric + * values with environment variables (eg, port number). + */ + if (ds->value->ptr && *ds->value->ptr) { + char *e; + long l = strtol(ds->value->ptr, &e, 11); + if (e != ds->value->ptr && !*e && l >= MININT && l <= MAXINT) { + *((int *)(cv[i].destination)) = l; + break; + + } + } + + log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected an integer:", cv[i].key, ds->value); + + return -1; + } + default: + log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected an integer, range ", MININT, " ... ", MAXINT); + return -1; + } + break; case T_CONFIG_BOOLEAN: if (du->type == TYPE_STRING) { data_string *ds = (data_string *)du; diff -Nurbd lighttpd-1.4.21.orig/src/network.c lighttpd-1.4.21/src/network.c --- lighttpd-1.4.21.orig/src/network.c 2008-07-30 21:34:23.000000000 +0200 +++ lighttpd-1.4.21/src/network.c 2009-02-19 14:11:41.000000000 +0100 @@ -8,6 +8,7 @@ #include #include #include +#include #include "network.h" #include "fdevent.h" @@ -72,6 +73,8 @@ buffer *b; int is_unix_domain_socket = 0; int fd; + char assigned_port_buf[7]; /* 5 chars for port + 1 for \n + 1 for \0 */ + unsigned short assigned_port = 0; #ifdef SO_ACCEPTFILTER struct accept_filter_arg afa; @@ -92,7 +95,10 @@ } #endif - srv_socket = calloc(1, sizeof(*srv_socket)); + if (NULL == (srv_socket = calloc(1, sizeof(*srv_socket)))) { + log_error_write(srv, __FILE__, __LINE__, "s", "can't allocate memory for srv_socket"); + return -1; + } srv_socket->fd = -1; srv_socket->srv_token = buffer_init(); @@ -127,7 +133,8 @@ if (host[0] == '/') { /* host is a unix-domain-socket */ is_unix_domain_socket = 1; - } else if (port == 0 || port > 65535) { + } else if (port > 65535) { + /* port == 0 selects a random port which is reported back after binding */ log_error_write(srv, __FILE__, __LINE__, "sd", "port out of range:", port); return -1; @@ -289,6 +296,7 @@ return -1; } + errno = 0; if (0 != bind(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len)) { switch(srv_socket->addr.plain.sa_family) { case AF_UNIX: @@ -304,6 +312,43 @@ } return -1; } + if ( 0 == port ) { + switch(srv_socket->addr.plain.sa_family) { + case AF_UNIX: + /* nothing to do here */ + break; + default: + /* get assigned port */ +#ifdef HAVE_IPV6 + if (s->use_ipv6) { + errno = 0; + if ( -1 == getsockname( srv_socket->fd, (struct sockaddr*) &(srv_socket->addr), &addr_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "can't get socket info:", + strerror(errno)); + return -1; + } + assigned_port = ntohs(srv_socket->addr.ipv6.sin6_port); + } +#endif + if (0 == assigned_port) { + errno = 0; + if ( -1 == getsockname( srv_socket->fd, (struct sockaddr*) &(srv_socket->addr), &addr_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "can't get socket info:", + strerror(errno)); + return -1; + } + assigned_port = ntohs(srv_socket->addr.ipv4.sin_port); + } + sprintf(assigned_port_buf, "%u\n", assigned_port); + if ( write(srv->port_file_fd, assigned_port_buf, strlen(assigned_port_buf)) <= 0 ) { + log_error_write(srv, __FILE__, __LINE__, "sd", "failed to write assigned port to file:", assigned_port ); + } + close(srv->port_file_fd); + srv->port_file_fd = -1; + } + } if (-1 == listen(srv_socket->fd, 128 * 8)) { log_error_write(srv, __FILE__, __LINE__, "ss", "listen failed: ", strerror(errno)); diff -Nurbd lighttpd-1.4.21.orig/src/server.c lighttpd-1.4.21/src/server.c --- lighttpd-1.4.21.orig/src/server.c 2009-02-05 22:35:46.000000000 +0100 +++ lighttpd-1.4.21/src/server.c 2009-02-19 14:22:08.000000000 +0100 @@ -177,6 +177,7 @@ CLEAN(srvconf.bindhost); CLEAN(srvconf.event_handler); CLEAN(srvconf.pid_file); + CLEAN(srvconf.port_file); CLEAN(tmp_chunk_len); #undef CLEAN @@ -250,6 +251,7 @@ CLEAN(srvconf.pid_file); CLEAN(srvconf.modules_dir); CLEAN(srvconf.network_backend); + CLEAN(srvconf.port_file); CLEAN(tmp_chunk_len); #undef CLEAN @@ -485,6 +487,7 @@ int o; int num_childs = 0; int pid_fd = -1, fd; + int port_fd = -1; size_t i; #ifdef HAVE_SIGACTION struct sigaction act; @@ -513,7 +516,8 @@ /* init structs done */ - srv->srvconf.port = 0; + /* port == -1: port is unset -> use default */ + srv->srvconf.port = -1; #ifdef HAVE_GETUID i_am_root = (getuid() == 0); #else @@ -646,6 +650,39 @@ } } + /* srv->srvconf.port should be set now if not panic */ + assert(srv->srvconf.port >= 0); + /* open port file BEFORE chroot but only if server.port == 0 */ + if ((0 == srv->srvconf.port) && srv->srvconf.port_file->used) { + if (-1 == (port_fd = open(srv->srvconf.port_file->ptr, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { + struct stat st; + if (errno != EEXIST) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "opening port-file failed:", srv->srvconf.port_file, strerror(errno)); + return -1; + } + + if (0 != stat(srv->srvconf.port_file->ptr, &st)) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "stating existing port-file failed:", + srv->srvconf.port_file, strerror(errno)); + } + + if (!S_ISREG(st.st_mode)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "port-file exists and isn't regular file:", srv->srvconf.port_file); + return -1; + } + + if (-1 == (port_fd = open(srv->srvconf.port_file->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "opening port-file failed:", srv->srvconf.port_file, strerror(errno)); + return -1; + } + } + srv->port_file_fd = port_fd; + } + if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* select limits itself * @@ -1345,6 +1382,18 @@ } } } + if (srv->srvconf.port_file->used && + srv->srvconf.changeroot->used == 0) { + if (0 != unlink(srv->srvconf.port_file->ptr)) { + if (errno != EACCES && errno != EPERM) { + log_error_write(srv, __FILE__, __LINE__, "sbds", + "unlink failed for:", + srv->srvconf.port_file, + errno, + strerror(errno)); + } + } + } } } @@ -1461,6 +1510,19 @@ } } } + if (srv->srvconf.port_file->used && + srv->srvconf.changeroot->used == 0 && + 0 == graceful_shutdown) { + if (0 != unlink(srv->srvconf.port_file->ptr)) { + if (errno != EACCES && errno != EPERM) { + log_error_write(srv, __FILE__, __LINE__, "sbds", + "unlink failed for:", + srv->srvconf.port_file, + errno, + strerror(errno)); + } + } + } #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd",