|
|
|
@ -0,0 +1,534 @@
|
|
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include <grp.h>
|
|
|
|
|
#include <pwd.h>
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
#define UNUSED(x) ((void)(x))
|
|
|
|
|
|
|
|
|
|
#define FCGI_LISTENSOCK_FILENO 0
|
|
|
|
|
|
|
|
|
|
#ifndef __GNUC__
|
|
|
|
|
# define __attribute__(x) /*NOTHING*/
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
spawn-fcgi - spawns fastcgi processes
|
|
|
|
|
|
|
|
|
|
The basic code was extracted from lighttpd (http://www.lighttpd.net/) and modified
|
|
|
|
|
by Stefan Buehler in 2008 (use glib2, keep fds open and change socket ownership).
|
|
|
|
|
|
|
|
|
|
COPYING from lighttpd:
|
|
|
|
|
Copyright (c) 2004, Jan Kneschke, incremental
|
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
|
|
- Redistributions of source code must retain the above copyright notice, this
|
|
|
|
|
list of conditions and the following disclaimer.
|
|
|
|
|
|
|
|
|
|
- Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
|
this list of conditions and the following disclaimer in the documentation
|
|
|
|
|
and/or other materials provided with the distribution.
|
|
|
|
|
|
|
|
|
|
- Neither the name of the 'incremental' nor the names of its contributors may
|
|
|
|
|
be used to endorse or promote products derived from this software without
|
|
|
|
|
specific prior written permission.
|
|
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
|
|
|
THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
gchar *fcgiapp, **args;
|
|
|
|
|
in_addr_t addr;
|
|
|
|
|
gint port;
|
|
|
|
|
gchar *unixsocket;
|
|
|
|
|
gint php_childs; /* set env var */
|
|
|
|
|
gint fork_childs;
|
|
|
|
|
gchar *pid_file;
|
|
|
|
|
gboolean no_fork;
|
|
|
|
|
gboolean show_version;
|
|
|
|
|
gboolean keep_fds, close_fds; /* keep/close STDIN/STDOUT */
|
|
|
|
|
|
|
|
|
|
gchar *chroot;
|
|
|
|
|
gchar *uid, *gid;
|
|
|
|
|
gchar *socketuid, *socketgid;
|
|
|
|
|
gint socketmode;
|
|
|
|
|
} options;
|
|
|
|
|
|
|
|
|
|
struct data {
|
|
|
|
|
socklen_t socklen;
|
|
|
|
|
struct sockaddr *sockaddr;
|
|
|
|
|
int socket;
|
|
|
|
|
|
|
|
|
|
gboolean i_am_root;
|
|
|
|
|
uid_t uid, socketuid;
|
|
|
|
|
gid_t gid, socketgid;
|
|
|
|
|
gchar *username;
|
|
|
|
|
|
|
|
|
|
int pid_fd;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static options opts = {
|
|
|
|
|
NULL, NULL,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
NULL,
|
|
|
|
|
5,
|
|
|
|
|
1,
|
|
|
|
|
NULL,
|
|
|
|
|
FALSE,
|
|
|
|
|
FALSE,
|
|
|
|
|
FALSE, FALSE,
|
|
|
|
|
|
|
|
|
|
NULL,
|
|
|
|
|
NULL, NULL,
|
|
|
|
|
NULL, NULL,
|
|
|
|
|
0600
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct data data;
|
|
|
|
|
|
|
|
|
|
/* only require an entry for name != NULL, otherwise a id as key is ok */
|
|
|
|
|
int readpwdent(gchar *key, uid_t *uid, gid_t *gid, gchar** name) {
|
|
|
|
|
struct passwd *pwd;
|
|
|
|
|
errno = 0;
|
|
|
|
|
*gid = (gid_t) -1;
|
|
|
|
|
if (NULL == (pwd = getpwnam(key)) && NULL == (pwd = getpwuid(atoi(key)))) {
|
|
|
|
|
if (name == NULL && 0 < (*uid = (uid_t) atoi(key))) return 0;
|
|
|
|
|
g_printerr("Couldn't find passwd entry for '%s': %s\n", key, g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
*uid = pwd->pw_uid;
|
|
|
|
|
*gid = pwd->pw_gid;
|
|
|
|
|
if (name) *name = g_strdup(pwd->pw_name);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int readgrpent(gchar *key, gid_t *gid) {
|
|
|
|
|
struct group *grp;
|
|
|
|
|
errno = 0;
|
|
|
|
|
if (NULL == (grp = getgrnam(key)) && NULL == (grp = getgrgid(atoi(key)))) {
|
|
|
|
|
if (0 < (*gid = (gid_t) atoi(key))) return 0;
|
|
|
|
|
g_printerr("Couldn't find group entry for '%s': %s\n", key, g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
*gid = grp->gr_gid;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int create_sockaddr() {
|
|
|
|
|
if (opts.addr != 0 && opts.port == 0) {
|
|
|
|
|
g_printerr("Specified address without port\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opts.port != 0 && opts.unixsocket != NULL) {
|
|
|
|
|
g_printerr("Either tcp:port or unix domain socket, not both\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opts.port == 0 && opts.unixsocket == NULL) {
|
|
|
|
|
g_printerr("Need either tcp:port or unix domain socket\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opts.port != 0) {
|
|
|
|
|
struct sockaddr_in *sin;
|
|
|
|
|
sin = g_malloc0(sizeof(struct sockaddr_in));
|
|
|
|
|
sin->sin_family = AF_INET;
|
|
|
|
|
if (opts.addr)
|
|
|
|
|
sin->sin_addr.s_addr = opts.addr;
|
|
|
|
|
else
|
|
|
|
|
sin->sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
|
sin->sin_port = htons(opts.port);
|
|
|
|
|
data.sockaddr = (struct sockaddr*) sin;
|
|
|
|
|
data.socklen = sizeof(struct sockaddr_in);
|
|
|
|
|
} else {
|
|
|
|
|
struct sockaddr_un *sun;
|
|
|
|
|
gsize slen = strlen(opts.unixsocket), len = 1 + slen + (gsize) (((struct sockaddr_un *) 0)->sun_path);
|
|
|
|
|
sun = (struct sockaddr_un*) g_malloc0(len);
|
|
|
|
|
sun->sun_family = AF_UNIX;
|
|
|
|
|
strcpy(sun->sun_path, opts.unixsocket);
|
|
|
|
|
data.sockaddr = (struct sockaddr*) sun;
|
|
|
|
|
data.socklen = len - 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int bind_socket() {
|
|
|
|
|
int s, val;
|
|
|
|
|
|
|
|
|
|
/* Check if socket is already open */
|
|
|
|
|
/* TODO: should this be skippable? */
|
|
|
|
|
if (-1 == (s = socket(data.sockaddr->sa_family, SOCK_STREAM, 0))) {
|
|
|
|
|
g_printerr("Couldn't open socket: %s\n", g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (0 == connect(s, data.sockaddr, data.socklen)) {
|
|
|
|
|
close(s);
|
|
|
|
|
g_printerr("Socket already in use, can't spawn\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
close(s);
|
|
|
|
|
|
|
|
|
|
if (opts.unixsocket) unlink(opts.unixsocket);
|
|
|
|
|
if (-1 == (data.socket = socket(data.sockaddr->sa_family, SOCK_STREAM, 0))) {
|
|
|
|
|
g_printerr("Couldn't open socket: %s\n", g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val = 1;
|
|
|
|
|
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
|
|
|
|
|
close(s);
|
|
|
|
|
g_printerr("Couldn't set SO_REUSEADDR: %s\n", g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (-1 == bind(s, data.sockaddr, data.socklen)) {
|
|
|
|
|
close(s);
|
|
|
|
|
g_printerr("Couldn't bind socket: %s\n", g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (-1 == listen(s, 1024)) {
|
|
|
|
|
close(s);
|
|
|
|
|
g_printerr("Couldn't listen on socket: %s\n", g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opts.unixsocket) {
|
|
|
|
|
data.socketuid = (uid_t) -1;
|
|
|
|
|
data.socketgid = (gid_t) -1;
|
|
|
|
|
if (opts.socketuid
|
|
|
|
|
&& 0 != (readpwdent(opts.socketuid, &data.socketuid, &data.socketgid, NULL))) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (opts.socketgid
|
|
|
|
|
&& 0 != (readgrpent(opts.socketgid, &data.socketgid))) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (-1 == chown(opts.unixsocket, data.socketuid, data.socketgid)) {
|
|
|
|
|
close(s);
|
|
|
|
|
g_printerr("Couldn't chown socket: %s\n", g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (-1 == chmod(opts.unixsocket, opts.socketmode)) {
|
|
|
|
|
close(s);
|
|
|
|
|
g_printerr("Couldn't chmod socket: %s\n", g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data.socket = s;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int open_pidfile() {
|
|
|
|
|
if (opts.pid_file) {
|
|
|
|
|
struct stat st;
|
|
|
|
|
if (0 == (data.pid_fd = open(opts.pid_file, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (errno != EEXIST) {
|
|
|
|
|
g_printerr("Opening pid-file '%s' failed: %s\n", opts.pid_file, g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (0 != stat(opts.pid_file, &st)) {
|
|
|
|
|
g_printerr("Stating pid-file '%s' failed: %s\n", opts.pid_file, g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!S_ISREG(st.st_mode)) {
|
|
|
|
|
g_printerr("pid-file exists and isn't regular file: '%s'\n", opts.pid_file);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (-1 == (data.pid_fd = open(opts.pid_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) {
|
|
|
|
|
g_printerr("Opening pid-file '%s' failed: %s\n", opts.pid_file, g_strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void prepare_env() {
|
|
|
|
|
GString *tmp;
|
|
|
|
|
if (-1 != opts.php_childs) {
|
|
|
|
|
tmp = g_string_sized_new(0);
|
|
|
|
|
g_string_printf(tmp, "PHP_FCGI_CHILDREN=%d", opts.php_childs);
|
|
|
|
|
putenv(tmp->str);
|
|
|
|
|
g_string_free(tmp, FALSE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int drop_priv() {
|
|
|
|
|
if (data.i_am_root) {
|
|
|
|
|
/* set user and group */
|
|
|
|
|
|
|
|
|
|
data.uid = (uid_t) -1;
|
|
|
|
|
data.gid = (gid_t) -1;
|
|
|
|
|
data.username = NULL;
|
|
|
|
|
|
|
|
|
|
if (opts.uid
|
|
|
|
|
&& 0 != (readpwdent(opts.uid, &data.uid, &data.gid, &data.username))) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (opts.gid
|
|
|
|
|
&& 0 != (readgrpent(opts.gid, &data.gid))) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* do the change before we do the chroot() */
|
|
|
|
|
if (data.gid != (gid_t) -1) {
|
|
|
|
|
if (0 != setgid(data.gid)) {
|
|
|
|
|
g_printerr("setgid failed: %s\n", strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (0 != setgroups(0, NULL)) {
|
|
|
|
|
g_printerr("setgroups failed: %s\n", strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.username
|
|
|
|
|
&& 0 != initgroups(data.username, data.gid)) {
|
|
|
|
|
g_printerr("initgroups failed: %s\n", strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opts.chroot) {
|
|
|
|
|
if (-1 == chroot(opts.chroot)) {
|
|
|
|
|
g_printerr("chroot failed: %s\n", strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (-1 == chdir("/")) {
|
|
|
|
|
g_printerr("chdir failed: %s\n", strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* drop root privs */
|
|
|
|
|
if (data.uid != (uid_t) -1
|
|
|
|
|
&& 0 != setuid(data.uid)) {
|
|
|
|
|
g_printerr("setuid failed: %s\n", strerror(errno));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void move2fd(int srcfd, int dstfd) {
|
|
|
|
|
if (srcfd != dstfd) {
|
|
|
|
|
close(dstfd);
|
|
|
|
|
dup2(srcfd, dstfd);
|
|
|
|
|
close(srcfd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void move2devnull(int fd) {
|
|
|
|
|
move2fd(open("/dev/null", O_RDWR), fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pid_t daemonize() {
|
|
|
|
|
pid_t child;
|
|
|
|
|
int status;
|
|
|
|
|
struct timeval tv = { 0, 100 * 1000 };
|
|
|
|
|
|
|
|
|
|
switch (child = fork()) {
|
|
|
|
|
case 0:
|
|
|
|
|
/* loose control terminal */
|
|
|
|
|
setsid();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
case -1:
|
|
|
|
|
g_printerr("Fork failed: %s\n", g_strerror(errno));
|
|
|
|
|
return (pid_t) -1;
|
|
|
|
|
default:
|
|
|
|
|
/* wait */
|
|
|
|
|
select(0, NULL, NULL, NULL, &tv);
|
|
|
|
|
|
|
|
|
|
waitforchild:
|
|
|
|
|
switch (waitpid(child, &status, WNOHANG)) {
|
|
|
|
|
case 0:
|
|
|
|
|
g_printerr("Child spawned successfully: PID: %d\n", child);
|
|
|
|
|
return child;
|
|
|
|
|
case -1:
|
|
|
|
|
if (EINTR == errno) goto waitforchild;
|
|
|
|
|
g_printerr("Unknown error: %s\n", g_strerror(errno));
|
|
|
|
|
return (pid_t) -1;
|
|
|
|
|
default:
|
|
|
|
|
if (WIFEXITED(status)) {
|
|
|
|
|
g_printerr("Child exited with: %d, %s\n", WEXITSTATUS(status), strerror(WEXITSTATUS(status)));
|
|
|
|
|
} else if (WIFSIGNALED(status)) {
|
|
|
|
|
g_printerr("Child signaled: %d\n", WTERMSIG(status));
|
|
|
|
|
} else {
|
|
|
|
|
g_printerr("Child died somehow: %d\n", status);
|
|
|
|
|
}
|
|
|
|
|
return (pid_t) -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void start() __attribute__((noreturn));
|
|
|
|
|
void start() {
|
|
|
|
|
int save_err_fileno = -1;
|
|
|
|
|
move2fd(data.socket, FCGI_LISTENSOCK_FILENO);
|
|
|
|
|
|
|
|
|
|
if (opts.close_fds) {
|
|
|
|
|
move2devnull(STDOUT_FILENO);
|
|
|
|
|
|
|
|
|
|
save_err_fileno = dup(STDERR_FILENO);
|
|
|
|
|
fcntl(save_err_fileno, F_SETFD, FD_CLOEXEC);
|
|
|
|
|
|
|
|
|
|
close(STDERR_FILENO);
|
|
|
|
|
dup2(STDOUT_FILENO, STDERR_FILENO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opts.args) {
|
|
|
|
|
execv(opts.args[0], opts.args);
|
|
|
|
|
} else {
|
|
|
|
|
GString *tmp = g_string_sized_new(0);
|
|
|
|
|
g_string_printf(tmp, "exec %s", opts.fcgiapp);
|
|
|
|
|
execl("/bin/sh", "sh", "-c", tmp->str, (char *)NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opts.close_fds) {
|
|
|
|
|
dup2(save_err_fileno, STDERR_FILENO);
|
|
|
|
|
}
|
|
|
|
|
g_printerr("Exec failed: %s\n", g_strerror(errno));
|
|
|
|
|
exit(errno);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean option_parse_address(const gchar *option_name, const gchar *value, gpointer d, GError **error) {
|
|
|
|
|
UNUSED(option_name);
|
|
|
|
|
UNUSED(d);
|
|
|
|
|
UNUSED(error);
|
|
|
|
|
|
|
|
|
|
opts.addr = inet_addr(value);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const GOptionEntry entries[] = {
|
|
|
|
|
{ "application", 'f', 0, G_OPTION_ARG_FILENAME, &opts.fcgiapp, "Filename of the fcgi-application", "fcgiapp" },
|
|
|
|
|
{ "address", 'a', 0, G_OPTION_ARG_CALLBACK, (gpointer) (intptr_t) &option_parse_address, "Bind to ip address", "addr" },
|
|
|
|
|
{ "port", 'p', 0, G_OPTION_ARG_INT, &opts.port, "Bind to tcp-port", "port" },
|
|
|
|
|
{ "socket", 's', 0, G_OPTION_ARG_FILENAME, &opts.unixsocket, "Bind to unix-domain socket", "path" },
|
|
|
|
|
{ "socket-uid", 'U', 0, G_OPTION_ARG_STRING, &opts.socketuid, "change unix-domain socket owner to user-id", "user" },
|
|
|
|
|
{ "socket-gid", 'G', 0, G_OPTION_ARG_STRING, &opts.socketgid, "change unix-domain socket group to group-id", "group" },
|
|
|
|
|
{ "socket-mode", 'M', 0, G_OPTION_ARG_INT, &opts.socketmode, "change unix-domain socket mode", "mode" },
|
|
|
|
|
{ "phpchilds", 'C', 0, G_OPTION_ARG_INT, &opts.php_childs, "(PHP only) Number of childs to spawn (default 5)", "childs" },
|
|
|
|
|
{ "childs", 'F', 0, G_OPTION_ARG_INT, &opts.fork_childs, "Number of childs to fork (default 1)", "childs" },
|
|
|
|
|
{ "pid", 'P', 0, G_OPTION_ARG_FILENAME, &opts.pid_file, "Name of PID-file for spawned process", "path" },
|
|
|
|
|
{ "no-daemon", 'n', 0, G_OPTION_ARG_NONE, &opts.no_fork, "Don't fork (for daemontools)", NULL },
|
|
|
|
|
{ "keep-fds", 0, 0, G_OPTION_ARG_NONE, &opts.keep_fds, "Keep stdout/stderr open (default for --no-daemon)", NULL },
|
|
|
|
|
{ "close-fds", 0, 0, G_OPTION_ARG_NONE, &opts.close_fds, "Close stdout/stderr (default it not --no-daemon)", NULL },
|
|
|
|
|
{ "version", 'v', 0, G_OPTION_ARG_NONE, &opts.show_version, "Show version", NULL },
|
|
|
|
|
{ "chroot", 'c', 0, G_OPTION_ARG_FILENAME, &opts.chroot, "(root only) chroot to directory", "dir" },
|
|
|
|
|
{ "uid", 'u', 0, G_OPTION_ARG_STRING, &opts.uid, "(root only) change to user-id", "user" },
|
|
|
|
|
{ "gid", 'g', 0, G_OPTION_ARG_STRING, &opts.gid, "(root only) change to group-id", "group" },
|
|
|
|
|
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &opts.args, "<fcgiapp> [fcgi app arguments]", NULL },
|
|
|
|
|
{ NULL, 0, 0, 0, NULL, NULL, NULL }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
|
GOptionContext *context;
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
int res;
|
|
|
|
|
GString *tmp = g_string_sized_new(0);
|
|
|
|
|
|
|
|
|
|
/* init */
|
|
|
|
|
data.socket = -1;
|
|
|
|
|
data.pid_fd = -1;
|
|
|
|
|
|
|
|
|
|
data.i_am_root = (getuid() == 0);
|
|
|
|
|
/* UID handling */
|
|
|
|
|
if (!data.i_am_root && (geteuid() == 0 || getegid() == 0)) {
|
|
|
|
|
/* we are setuid-root */
|
|
|
|
|
g_printerr("Are you nuts ? Don't apply a SUID bit to this binary\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
context = g_option_context_new("-- <fcgiapp> [fcgi app arguments]");
|
|
|
|
|
g_option_context_add_main_entries(context, entries, NULL);
|
|
|
|
|
g_option_context_set_summary(context, PACKAGE_NAME "-" PACKAGE_VERSION " - spawns fastcgi processes");
|
|
|
|
|
|
|
|
|
|
opts.close_fds = opts.no_fork ? opts.close_fds : !opts.keep_fds;
|
|
|
|
|
|
|
|
|
|
if (!g_option_context_parse (context, &argc, &argv, &error)) {
|
|
|
|
|
g_printerr("Option parsing failed: %s\n", error->message);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opts.show_version) {
|
|
|
|
|
g_printerr(PACKAGE_NAME "-" PACKAGE_VERSION " - spawns fastcgi processes\n");
|
|
|
|
|
g_printerr("Build: " PACKAGE_BUILD_DATE "\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check options */
|
|
|
|
|
if ((opts.fcgiapp && opts.args) || (!opts.fcgiapp && !opts.args)) {
|
|
|
|
|
g_printerr("Specify either a fcgi application with -f or the application with arguments after '--'\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (0 != (res = create_sockaddr())) return res;
|
|
|
|
|
if (0 != (res = bind_socket())) return res;
|
|
|
|
|
if (0 != (res = open_pidfile())) return res;
|
|
|
|
|
if (0 != (res = drop_priv())) return res;
|
|
|
|
|
|
|
|
|
|
prepare_env();
|
|
|
|
|
|
|
|
|
|
if (opts.no_fork) {
|
|
|
|
|
start();
|
|
|
|
|
} else {
|
|
|
|
|
gint i;
|
|
|
|
|
for (i = 0; i < opts.fork_childs; i++) {
|
|
|
|
|
pid_t child = daemonize();
|
|
|
|
|
if (child == (pid_t) -1) return -1;
|
|
|
|
|
if (child == 0) start();
|
|
|
|
|
/* write pid file */
|
|
|
|
|
if (data.pid_fd != -1) {
|
|
|
|
|
g_string_printf(tmp, "%d\n", child);
|
|
|
|
|
if (i == opts.fork_childs-1) {
|
|
|
|
|
/* avoid eol for the last one */
|
|
|
|
|
write(data.pid_fd, tmp->str, tmp->len-1);
|
|
|
|
|
} else {
|
|
|
|
|
write(data.pid_fd, tmp->str, tmp->len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_string_free(tmp, TRUE);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|