From 8b8b57769e42c7fdb447fa9553aedb91ed728576 Mon Sep 17 00:00:00 2001 From: Unbit Date: Sat, 3 May 2014 10:17:27 +0200 Subject: [PATCH] first prototype of a fork server --- core/fork_server.c | 69 ++++++++++++++++++++++++++++++++++++++ core/init.c | 2 ++ core/uwsgi.c | 11 ++++++ plugins/psgi/psgi.h | 2 ++ plugins/psgi/psgi_loader.c | 6 ++++ plugins/psgi/psgi_plugin.c | 10 ++++++ uwsgi.h | 4 +++ uwsgiconfig.py | 2 +- 8 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 core/fork_server.c diff --git a/core/fork_server.c b/core/fork_server.c new file mode 100644 index 00000000..4dd4137f --- /dev/null +++ b/core/fork_server.c @@ -0,0 +1,69 @@ +#include + +extern struct uwsgi_server uwsgi; + +void uwsgi_fork_server(char *socket) { + int fd = bind_to_unix(socket, uwsgi.listen_queue, uwsgi.chmod_socket, uwsgi.abstract_socket); + if (fd < 0) exit(1); + + if (uwsgi_socket_passcred(fd)) exit(1); + + for(;;) { + struct sockaddr_un client_src; + socklen_t client_src_len = 0; + int client_fd = accept(fd, (struct sockaddr *) &client_src, &client_src_len); + if (client_fd < 0) { + uwsgi_error("uwsgi_fork_server()/accept()"); + continue; + } + char buf[4096]; + pid_t ppid = -1; + uid_t uid = -1; + gid_t gid = -1; + ssize_t len = uwsgi_recv_cred2(client_fd, buf, 4096, &ppid, &uid, &gid); + uwsgi_log("RET = %d %d %d %d\n", len, ppid, uid, gid); + + pid_t pid = fork(); + if (pid < 0) { + uwsgi_error("uwsgi_fork_server()/fork()"); + goto end; + } + else if (pid > 0) { + goto end; + } + else { + // reparent the process +#ifdef __linux__ + if (prctl(PR_SET_CHILD_SUBREAPER, ppid, 0, 0, 0)) { + uwsgi_error("uwsgi_fork_server()/fork()"); + exit(1); + } +#endif + // now fork again and kill + pid_t new_pid = fork(); + if (new_pid < 0) { + uwsgi_error("uwsgi_fork_server()/fork()"); + exit(1); + } + else if (new_pid > 0) { + exit(0); + } + else { + uwsgi_log("double fork() and reparenting successfull (new pid: %d)\n", getpid()); + uwsgi.argc = 3; + uwsgi.argv = uwsgi_malloc(sizeof(char *) * (uwsgi.argc+1)); + uwsgi.argv[0] = uwsgi.binary_path; + uwsgi.argv[1] = uwsgi_str("--socket"); + uwsgi.argv[2] = uwsgi_str(":1717"); + uwsgi.argv[3] = NULL; + // this is the only step required to have a consistent environment + uwsgi.fork_socket = NULL; + return; + } + } + +end: + close(client_fd); + + } +} diff --git a/core/init.c b/core/init.c index c4ce7147..587a7666 100644 --- a/core/init.c +++ b/core/init.c @@ -252,6 +252,8 @@ void uwsgi_commandline_config() { int i; uwsgi.option_index = -1; + // required in case we want to call getopt_long from the beginning + optind = 0; char *optname; while ((i = getopt_long(uwsgi.argc, uwsgi.argv, uwsgi.short_options, uwsgi.long_options, &uwsgi.option_index)) != -1) { diff --git a/core/uwsgi.c b/core/uwsgi.c index b1145ebb..c660c3dd 100644 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -356,6 +356,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"setns-skip", required_argument, 0, "skip the specified entry when sending setns file descriptors", uwsgi_opt_add_string_list, &uwsgi.setns_socket_skip, 0}, {"setns", required_argument, 0, "join a namespace created by an external uWSGI instance", uwsgi_opt_set_str, &uwsgi.setns, 0}, {"setns-preopen", no_argument, 0, "open /proc/self/ns as soon as possible and cache fds", uwsgi_opt_true, &uwsgi.setns_preopen, 0}, + {"fork-socket", required_argument, 0, "suspend the execution after early initialization and fork() at every unix socket connection", uwsgi_opt_set_str, &uwsgi.fork_socket, 0}, #endif {"jailed", no_argument, 0, "mark the instance as jailed (force the execution of post_jail hooks)", uwsgi_opt_true, &uwsgi.jailed, 0}, #if defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) @@ -2131,6 +2132,8 @@ void uwsgi_setup(int argc, char *argv[], char *envp[]) { struct group *gr = getgrgid(getgid()); uwsgi.magic_table['G'] = gr ? gr->gr_name : uwsgi.magic_table['g']; +configure: + // you can embed a ini file in the uWSGi binary with default options #ifdef UWSGI_EMBED_CONFIG uwsgi_ini_config("", uwsgi.magic_table); @@ -2155,6 +2158,14 @@ void uwsgi_setup(int argc, char *argv[], char *envp[]) { // ok, the options dictionary is available, lets manage it uwsgi_configure(); + // stop the execution until a connection arrives on the fork socket + if (uwsgi.fork_socket) { + uwsgi_log_verbose("waiting for fork-socket connections...\n"); + uwsgi_fork_server(uwsgi.fork_socket); + // if we are here a new process has been spawned + goto configure; + } + // fixup cwd if (uwsgi.force_cwd) uwsgi.cwd = uwsgi.force_cwd; diff --git a/plugins/psgi/psgi.h b/plugins/psgi/psgi.h index 92e6b588..e7c386b9 100644 --- a/plugins/psgi/psgi.h +++ b/plugins/psgi/psgi.h @@ -67,6 +67,8 @@ struct uwsgi_perl { CV *spooler; int no_plack; + + void *early_psgi_callable; }; void init_perl_embedded_module(void); diff --git a/plugins/psgi/psgi_loader.c b/plugins/psgi/psgi_loader.c index c798408e..5e0255df 100644 --- a/plugins/psgi/psgi_loader.c +++ b/plugins/psgi/psgi_loader.c @@ -453,6 +453,12 @@ int init_psgi_app(struct wsgi_request *wsgi_req, char *app, uint16_t app_len, Pe PERL_SET_CONTEXT(interpreters[0]); } + // is it an early loading ? + if (!uwsgi.workers) { + uperl.early_psgi_callable = callables[0]; + return 0; + } + if (uwsgi_apps_cnt >= uwsgi.max_apps) { uwsgi_log("ERROR: you cannot load more than %d apps in a worker\n", uwsgi.max_apps); goto clear; diff --git a/plugins/psgi/psgi_plugin.c b/plugins/psgi/psgi_plugin.c index a764cb2c..a177d709 100644 --- a/plugins/psgi/psgi_plugin.c +++ b/plugins/psgi/psgi_plugin.c @@ -26,6 +26,12 @@ static void uwsgi_opt_plshell(char *opt, char *value, void *foobar) { } } +int uwsgi_perl_init(void); +static void uwsgi_opt_early_psgi(char *opt, char *value, void *foobar) { + uwsgi_perl_init(); + init_psgi_app(NULL, value, strlen(value), uperl.main); +} + struct uwsgi_option uwsgi_perl_options[] = { {"psgi", required_argument, 0, "load a psgi app", uwsgi_opt_set_str, &uperl.psgi, 0}, @@ -46,6 +52,7 @@ struct uwsgi_option uwsgi_perl_options[] = { {"plshell-oneshot", no_argument, 0, "run a perl interactive shell (one shot)", uwsgi_opt_plshell, NULL, 0}, {"perl-no-plack", no_argument, 0, "force the use of do instead of Plack::Util::load_psgi", uwsgi_opt_true, &uperl.no_plack, 0}, + {"early-psgi", required_argument, 0, "load a psgi app soon after perl initialization", uwsgi_opt_early_psgi, NULL, UWSGI_OPT_IMMEDIATE}, {0, 0, 0, 0, 0, 0, 0}, }; @@ -437,6 +444,9 @@ int uwsgi_perl_init(){ int argc; int i; + // the perl interpreter could be already initialized + if (uperl.main) return 0; + uperl.embedding[0] = ""; uperl.embedding[1] = "-e"; uperl.embedding[2] = "0"; diff --git a/uwsgi.h b/uwsgi.h index 0dc855b6..6924b02d 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -2709,6 +2709,8 @@ struct uwsgi_server { int mule_reload_mercy; int alarm_cheap; + + char *fork_socket; }; struct uwsgi_rpc { @@ -4761,6 +4763,8 @@ mode_t uwsgi_mode_t(char *, int *); int uwsgi_notify_socket_manage(int); int uwsgi_notify_msg(char *, char *, size_t); +void uwsgi_fork_server(char *); + #ifdef __cplusplus } #endif diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 78a464d1..b6796db1 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -602,7 +602,7 @@ class uConf(object): 'core/setup_utils', 'core/clock', 'core/init', 'core/buffer', 'core/reader', 'core/writer', 'core/alarm', 'core/cron', 'core/hooks', 'core/plugins', 'core/lock', 'core/cache', 'core/daemons', 'core/errors', 'core/hash', 'core/master_events', 'core/chunked', 'core/queue', 'core/event', 'core/signal', 'core/strings', 'core/progress', 'core/timebomb', 'core/ini', 'core/fsmon', 'core/mount', - 'core/metrics', 'core/plugins_builder', 'core/sharedarea', + 'core/metrics', 'core/plugins_builder', 'core/sharedarea', 'core/fork_server', 'core/rpc', 'core/gateway', 'core/loop', 'core/cookie', 'core/querystring', 'core/rb_timers', 'core/transformations', 'core/uwsgi'] # add protocols self.gcc_list.append('proto/base')