summaryrefslogtreecommitdiff
path: root/src/spawn-fcgi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/spawn-fcgi.c')
-rw-r--r--src/spawn-fcgi.c258
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() {