diff options
Diffstat (limited to 'src/spawn-fcgi.c')
-rw-r--r-- | src/spawn-fcgi.c | 258 |
1 files changed, 139 insertions, 119 deletions
diff --git a/src/spawn-fcgi.c b/src/spawn-fcgi.c index 8237e79..60e02bd 100644 --- a/src/spawn-fcgi.c +++ b/src/spawn-fcgi.c @@ -37,7 +37,7 @@ typedef int socklen_t; #endif #ifdef HAVE_SYS_UN_H -int fcgi_spawn_connection(char *appPath, char *addr, unsigned short port, const char *unixsocket, int child_count, int pid_fd, int nofork) { +int fcgi_spawn_connection(char *appPath, char **appArgv, char *addr, unsigned short port, const char *unixsocket, int child_count, int pid_fd, int nofork) { int fcgi_fd; int socket_type, status; struct timeval tv = { 0, 100 * 1000 }; @@ -48,6 +48,9 @@ int fcgi_spawn_connection(char *appPath, char *addr, unsigned short port, const socklen_t servlen; + pid_t child; + int val; + if (child_count < 2) { child_count = 5; } @@ -71,6 +74,25 @@ int fcgi_spawn_connection(char *appPath, char *addr, unsigned short port, const #endif socket_type = AF_UNIX; fcgi_addr = (struct sockaddr *) &fcgi_addr_un; + + /* check if some backend is listening on the socket + * as if we delete the socket-file and rebind there will be no "socket already in use" error + */ + if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { + fprintf(stderr, "%s.%d\n", + __FILE__, __LINE__); + return -1; + } + + if (-1 != connect(fcgi_fd, fcgi_addr, servlen)) { + fprintf(stderr, "%s.%d: socket is already used, can't spawn\n", + __FILE__, __LINE__); + return -1; + } + + /* cleanup previous socket if it exists */ + unlink(unixsocket); + close(fcgi_fd); } else { fcgi_addr_in.sin_family = AF_INET; if (addr != NULL) { @@ -85,144 +107,128 @@ int fcgi_spawn_connection(char *appPath, char *addr, unsigned short port, const fcgi_addr = (struct sockaddr *) &fcgi_addr_in; } + /* open socket */ if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); return -1; } - if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) { - /* server is not up, spawn in */ - pid_t child; - int val; + val = 1; + if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { + fprintf(stderr, "%s.%d\n", + __FILE__, __LINE__); + return -1; + } - if (unixsocket) unlink(unixsocket); + /* create socket */ + if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) { + fprintf(stderr, "%s.%d: bind failed: %s\n", + __FILE__, __LINE__, + strerror(errno)); + return -1; + } - close(fcgi_fd); + if (-1 == listen(fcgi_fd, 1024)) { + fprintf(stderr, "%s.%d: fd = -1\n", + __FILE__, __LINE__); + return -1; + } - /* reopen socket */ - if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { - fprintf(stderr, "%s.%d\n", - __FILE__, __LINE__); - return -1; - } + if (!nofork) { + child = fork(); + } else { + child = 0; + } - val = 1; - if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { - fprintf(stderr, "%s.%d\n", - __FILE__, __LINE__); - return -1; - } + switch (child) { + case 0: { + char cgi_childs[64]; - /* create socket */ - if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) { - fprintf(stderr, "%s.%d: bind failed: %s\n", - __FILE__, __LINE__, - strerror(errno)); - return -1; - } + int i = 0; - if (-1 == listen(fcgi_fd, 1024)) { - fprintf(stderr, "%s.%d: fd = -1\n", - __FILE__, __LINE__); - return -1; - } + /* is safe as we limit to 256 childs */ + sprintf(cgi_childs, "PHP_FCGI_CHILDREN=%d", child_count); - if (!nofork) { - child = fork(); - } else { - child = 0; + if(fcgi_fd != FCGI_LISTENSOCK_FILENO) { + close(FCGI_LISTENSOCK_FILENO); + dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO); + close(fcgi_fd); } - switch (child) { - case 0: { - char cgi_childs[64]; - char *b; - - int i = 0; - - /* is save as we limit to 256 childs */ - sprintf(cgi_childs, "PHP_FCGI_CHILDREN=%d", child_count); + /* we don't need the client socket */ + for (i = 3; i < 256; i++) { + close(i); + } - if(fcgi_fd != FCGI_LISTENSOCK_FILENO) { - close(FCGI_LISTENSOCK_FILENO); - dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO); - close(fcgi_fd); - } + /* create environment */ - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - close(i); - } + putenv(cgi_childs); - /* create environment */ + /* fork and replace shell */ + if (appArgv) { + execv(appArgv[0], appArgv); - putenv(cgi_childs); - - /* fork and replace shell */ - b = malloc(strlen("exec ") + strlen(appPath) + 1); + } else { + char *b = malloc(strlen("exec ") + strlen(appPath) + 1); strcpy(b, "exec "); strcat(b, appPath); /* exec the cgi */ execl("/bin/sh", "sh", "-c", b, (char *)NULL); + } - exit(errno); + exit(errno); + + break; + } + case -1: + /* error */ + break; + default: + /* father */ + + /* wait */ + select(0, NULL, NULL, NULL, &tv); + + switch (waitpid(child, &status, WNOHANG)) { + case 0: + fprintf(stderr, "%s.%d: child spawned successfully: PID: %d\n", + __FILE__, __LINE__, + child); + + /* write pid file */ + if (pid_fd != -1) { + /* assume a 32bit pid_t */ + char pidbuf[12]; + + snprintf(pidbuf, sizeof(pidbuf) - 1, "%d", child); + + write(pid_fd, pidbuf, strlen(pidbuf)); + close(pid_fd); + pid_fd = -1; + } break; - } case -1: - /* error */ break; default: - /* father */ - - /* wait */ - select(0, NULL, NULL, NULL, &tv); - - switch (waitpid(child, &status, WNOHANG)) { - case 0: - fprintf(stderr, "%s.%d: child spawned successfully: PID: %d\n", + if (WIFEXITED(status)) { + fprintf(stderr, "%s.%d: child exited with: %d, %s\n", + __FILE__, __LINE__, + WEXITSTATUS(status), strerror(WEXITSTATUS(status))); + } else if (WIFSIGNALED(status)) { + fprintf(stderr, "%s.%d: child signaled: %d\n", + __FILE__, __LINE__, + WTERMSIG(status)); + } else { + fprintf(stderr, "%s.%d: child died somehow: %d\n", __FILE__, __LINE__, - child); - - /* write pid file */ - if (pid_fd != -1) { - /* assume a 32bit pid_t */ - char pidbuf[12]; - - snprintf(pidbuf, sizeof(pidbuf) - 1, "%d", child); - - write(pid_fd, pidbuf, strlen(pidbuf)); - close(pid_fd); - pid_fd = -1; - } - - break; - case -1: - break; - default: - if (WIFEXITED(status)) { - fprintf(stderr, "%s.%d: child exited with: %d, %s\n", - __FILE__, __LINE__, - WEXITSTATUS(status), strerror(WEXITSTATUS(status))); - } else if (WIFSIGNALED(status)) { - fprintf(stderr, "%s.%d: child signaled: %d\n", - __FILE__, __LINE__, - WTERMSIG(status)); - } else { - fprintf(stderr, "%s.%d: child died somehow: %d\n", - __FILE__, __LINE__, - status); - } + status); } - - break; } - } else { - fprintf(stderr, "%s.%d: socket is already used, can't spawn\n", - __FILE__, __LINE__); - return -1; + + break; } close(fcgi_fd); @@ -239,9 +245,12 @@ void show_version () { } void show_help () { - char *b = "spawn-fcgi" "-" PACKAGE_VERSION \ -" - spawns fastcgi processes\n" \ -"usage:\n" \ + char *b = \ +"Usage: spawn-fcgi [options] -- <fcgiapp> [fcgi app arguments]\n" \ +"\n" \ +"spawn-fcgi v" PACKAGE_VERSION " - spawns fastcgi processes\n" \ +"\n" \ +"Options:\n" \ " -f <fcgiapp> filename of the fcgi-application\n" \ " -a <addr> bind to ip address\n" \ " -p <port> bind to tcp-port\n" \ @@ -264,6 +273,7 @@ int main(int argc, char **argv) { char *fcgi_app = NULL, *changeroot = NULL, *username = NULL, *groupname = NULL, *unixsocket = NULL, *pid_file = NULL, *addr = NULL; + char **fcgi_app_argv = { NULL }; unsigned short port = 0; int child_count = 5; int i_am_root, o; @@ -274,10 +284,10 @@ int main(int argc, char **argv) { i_am_root = (getuid() == 0); - while(-1 != (o = getopt(argc, argv, "c:f:g:hna:p:u:vC:s:P:"))) { + while(-1 != (o = getopt(argc, argv, "c:f:g:hna:p:u:vC:s:P:"))) { switch(o) { case 'f': fcgi_app = optarg; break; - case 'a': addr = optarg;/* ip addr */ break; + case 'a': addr = optarg;/* ip addr */ break; case 'p': port = strtol(optarg, NULL, 10);/* port */ break; case 'C': child_count = strtol(optarg, NULL, 10);/* */ break; case 's': unixsocket = optarg; /* unix-domain socket */ break; @@ -294,7 +304,11 @@ int main(int argc, char **argv) { } } - if (fcgi_app == NULL || (port == 0 && unixsocket == NULL)) { + if (optind < argc) { + fcgi_app_argv = &argv[optind]; + } + + if ((fcgi_app == NULL && fcgi_app_argv == NULL) || (port == 0 && unixsocket == NULL)) { show_help(); return -1; } @@ -404,6 +418,18 @@ int main(int argc, char **argv) { } } + /* + * Change group before chroot, when we have access + * to /etc/group + */ + if (groupname) { + setgid(grp->gr_gid); + setgroups(0, NULL); + if (username) { + initgroups(username, grp->gr_gid); + } + } + if (changeroot) { if (-1 == chroot(changeroot)) { fprintf(stderr, "%s.%d: %s %s\n", @@ -420,18 +446,12 @@ int main(int argc, char **argv) { } /* drop root privs */ - if (groupname) { - setgid(grp->gr_gid); - } if (username) { - if (groupname) { - initgroups(username, grp->gr_gid); - } setuid(pwd->pw_uid); } } - return fcgi_spawn_connection(fcgi_app, addr, port, unixsocket, child_count, pid_fd, nofork); + return fcgi_spawn_connection(fcgi_app, fcgi_app_argv, addr, port, unixsocket, child_count, pid_fd, nofork); } #else int main() { |