the upcoming 2.0 version
https://redmine.lighttpd.net/projects/lighttpd2
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
4.1 KiB
192 lines
4.1 KiB
|
|
#include <lighttpd/angel_base.h> |
|
|
|
#include <grp.h> |
|
|
|
static void read_pipe(liServer *srv, liErrorPipe *epipe, gboolean flush) { |
|
const ssize_t max_read = 1024; |
|
ssize_t r, toread; |
|
GString *buf; |
|
int count = 10; |
|
|
|
if (-1 == epipe->fds[0]) return; |
|
|
|
for (;;) { |
|
if (ioctl(epipe->fds[0], FIONREAD, &toread) || toread == 0) { |
|
toread = 256; |
|
} else { |
|
if (toread > max_read) toread = max_read; |
|
} |
|
|
|
buf = g_string_sized_new(toread); |
|
g_string_set_size(buf, toread); |
|
|
|
r = read(epipe->fds[0], buf->str, toread); |
|
if (r < 0) { |
|
g_string_free(buf, TRUE); |
|
switch (errno) { |
|
case EINTR: continue; |
|
case EAGAIN: |
|
#if EWOULDBLOCK != EAGAIN |
|
case EWOULDBLOCK: |
|
#endif |
|
return; /* come back later */ |
|
case ECONNRESET: |
|
goto close_epipe; |
|
default: |
|
ERROR(srv, "read error: %s", g_strerror(errno)); |
|
goto close_epipe; |
|
} |
|
} else if (r == 0) { /* EOF */ |
|
g_string_free(buf, TRUE); |
|
goto close_epipe; |
|
} |
|
|
|
g_string_set_size(buf, r); |
|
epipe->cb(srv, epipe, buf); |
|
g_string_free(buf, TRUE); |
|
|
|
if (!flush) break; |
|
|
|
if (--count <= 0) { |
|
buf = g_string_new("error while trying to flush error-pipe: didn't see EOF. closing"); |
|
epipe->cb(srv, epipe, buf); |
|
g_string_free(buf, TRUE); |
|
return; |
|
} |
|
} |
|
|
|
return; |
|
|
|
close_epipe: |
|
ev_io_stop(srv->loop, &epipe->fd_watcher); |
|
close(epipe->fds[0]); |
|
epipe->fds[0] = -1; |
|
} |
|
|
|
static void error_pipe_cb(struct ev_loop *loop, ev_io *w, int revents) { |
|
liErrorPipe *epipe = w->data; |
|
UNUSED(loop); |
|
UNUSED(revents); |
|
|
|
read_pipe(epipe->srv, epipe, FALSE); |
|
} |
|
|
|
liErrorPipe* li_error_pipe_new(liServer *srv, liErrorPipeCB cb, gpointer ctx) { |
|
liErrorPipe *epipe; |
|
int fds[2]; |
|
|
|
if (-1 == pipe(fds)) { |
|
ERROR(srv, "Couldn't create pipe: %s", g_strerror(errno)); |
|
return NULL; |
|
} |
|
|
|
epipe = g_slice_new0(liErrorPipe); |
|
epipe->srv = srv; |
|
epipe->cb = cb; |
|
epipe->ctx = ctx; |
|
ev_io_init(&epipe->fd_watcher, error_pipe_cb, fds[0], EV_READ); |
|
epipe->fd_watcher.data = epipe; |
|
epipe->fds[0] = fds[0]; |
|
epipe->fds[1] = fds[1]; |
|
|
|
li_fd_init(fds[0]); |
|
|
|
return epipe; |
|
} |
|
|
|
void li_error_pipe_free(liErrorPipe *epipe) { |
|
liServer *srv = epipe->srv; |
|
|
|
ev_io_stop(srv->loop, &epipe->fd_watcher); |
|
li_error_pipe_flush(epipe); |
|
if (-1 != epipe->fds[0]) close(epipe->fds[0]); |
|
if (-1 != epipe->fds[1]) close(epipe->fds[1]); |
|
|
|
g_slice_free(liErrorPipe, epipe); |
|
} |
|
|
|
/** closes out-fd */ |
|
void li_error_pipe_activate(liErrorPipe *epipe) { |
|
liServer *srv = epipe->srv; |
|
|
|
if (-1 != epipe->fds[1]) close(epipe->fds[1]); |
|
ev_io_start(srv->loop, &epipe->fd_watcher); |
|
} |
|
|
|
/** closes in-fd, moves out-fd to dest_fd */ |
|
void li_error_pipe_use(liErrorPipe *epipe, int dest_fd) { |
|
if (-1 != epipe->fds[0]) { |
|
close(epipe->fds[0]); |
|
epipe->fds[0] = -1; |
|
} |
|
if (epipe->fds[1] != dest_fd) { |
|
dup2(epipe->fds[1], dest_fd); |
|
close(epipe->fds[1]); |
|
epipe->fds[1] = dest_fd; |
|
} |
|
} |
|
|
|
void li_error_pipe_flush(liErrorPipe *epipe) { |
|
read_pipe(epipe->srv, epipe, TRUE); |
|
} |
|
|
|
static void proc_epipe_cb(liServer *srv, liErrorPipe *epipe, GString *msg) { |
|
liProc *proc = epipe->ctx; |
|
|
|
BACKEND_LINES(srv, msg->str, "%s[%i]: ", proc->appname, proc->child_pid); |
|
} |
|
|
|
liProc* li_proc_new(liServer *srv, gchar **args, gchar **env, uid_t uid, gid_t gid, gchar *username, liProcSetupCB cb, gpointer ctx) { |
|
liProc *proc; |
|
pid_t pid; |
|
|
|
proc = g_slice_new0(liProc); |
|
proc->srv = srv; |
|
proc->child_pid = -1; |
|
proc->epipe = li_error_pipe_new(srv, proc_epipe_cb, proc); |
|
proc->appname = g_strdup(li_remove_path(args[0])); |
|
|
|
switch (pid = fork()) { |
|
case 0: |
|
li_error_pipe_use(proc->epipe, STDERR_FILENO); |
|
|
|
setsid(); |
|
|
|
if (gid != (gid_t) -1) { |
|
setgid(gid); |
|
setgroups(0, NULL); |
|
if (username) initgroups(username, gid); |
|
} |
|
|
|
if (cb) cb(ctx); |
|
|
|
if (uid != (uid_t) -1) { |
|
setuid(uid); |
|
} |
|
|
|
if (NULL == env) env = environ; |
|
|
|
execve(args[0], args, env); |
|
g_printerr("exec('%s') failed: %s\n", args[0], g_strerror(errno)); |
|
exit(-1); |
|
|
|
break; |
|
case -1: |
|
ERROR(srv, "fork failed: %s", g_strerror(errno)); |
|
li_proc_free(proc); |
|
return NULL; |
|
default: |
|
proc->child_pid = pid; |
|
li_error_pipe_activate(proc->epipe); |
|
break; |
|
} |
|
|
|
return proc; |
|
} |
|
|
|
void li_proc_free(liProc *proc) { |
|
li_error_pipe_free(proc->epipe); |
|
g_free(proc->appname); |
|
g_slice_free(liProc, proc); |
|
}
|
|
|