Files
2015-09-10 11:27:43 +02:00

4620 lines
101 KiB
C
Executable File

#include <uwsgi.h>
extern struct uwsgi_server uwsgi;
#ifdef __BIG_ENDIAN__
uint16_t uwsgi_swap16(uint16_t x) {
return (uint16_t) ((x & 0xff) << 8 | (x & 0xff00) >> 8);
}
uint32_t uwsgi_swap32(uint32_t x) {
x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0x00FF00FF);
return (x >> 16) | (x << 16);
}
// thanks to ffmpeg project for this idea :P
uint64_t uwsgi_swap64(uint64_t x) {
union {
uint64_t ll;
uint32_t l[2];
} w, r;
w.ll = x;
r.l[0] = uwsgi_swap32(w.l[1]);
r.l[1] = uwsgi_swap32(w.l[0]);
return r.ll;
}
#endif
// check if a string is a valid hex number
int check_hex(char *str, int len) {
int i;
for (i = 0; i < len; i++) {
if ((str[i] < '0' && str[i] > '9') && (str[i] < 'a' && str[i] > 'f') && (str[i] < 'A' && str[i] > 'F')
) {
return 0;
}
}
return 1;
}
// increase worker harakiri
void inc_harakiri(struct wsgi_request *wsgi_req, int sec) {
if (uwsgi.master_process) {
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].harakiri += sec;
}
else {
alarm(uwsgi.harakiri_options.workers + sec);
}
}
// set worker harakiri
void set_harakiri(struct wsgi_request *wsgi_req, int sec) {
if (!wsgi_req) return;
if (sec == 0) {
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].harakiri = 0;
}
else {
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].harakiri = uwsgi_now() + sec;
}
if (!uwsgi.master_process) {
alarm(sec);
}
}
// set user harakiri
void set_user_harakiri(struct wsgi_request *wsgi_req, int sec) {
if (!uwsgi.master_process) {
uwsgi_log("!!! unable to set user harakiri without the master process !!!\n");
return;
}
// a 0 seconds value, reset the timer
if (sec == 0) {
if (uwsgi.muleid > 0) {
uwsgi.mules[uwsgi.muleid - 1].user_harakiri = 0;
}
else if (uwsgi.i_am_a_spooler) {
struct uwsgi_spooler *uspool = uwsgi.i_am_a_spooler;
uspool->user_harakiri = 0;
}
else if (wsgi_req) {
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].user_harakiri = 0;
}
}
else {
if (uwsgi.muleid > 0) {
uwsgi.mules[uwsgi.muleid - 1].user_harakiri = uwsgi_now() + sec;
}
else if (uwsgi.i_am_a_spooler) {
struct uwsgi_spooler *uspool = uwsgi.i_am_a_spooler;
uspool->user_harakiri = uwsgi_now() + sec;
}
else if (wsgi_req) {
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].user_harakiri = uwsgi_now() + sec;
}
}
}
// set mule harakiri
void set_mule_harakiri(int sec) {
if (sec == 0) {
uwsgi.mules[uwsgi.muleid - 1].harakiri = 0;
}
else {
uwsgi.mules[uwsgi.muleid - 1].harakiri = uwsgi_now() + sec;
}
if (!uwsgi.master_process) {
alarm(sec);
}
}
// set spooler harakiri
void set_spooler_harakiri(int sec) {
if (sec == 0) {
uwsgi.i_am_a_spooler->harakiri = 0;
}
else {
uwsgi.i_am_a_spooler->harakiri = uwsgi_now() + sec;
}
if (!uwsgi.master_process) {
alarm(sec);
}
}
// daemonize to the specified logfile
void daemonize(char *logfile) {
pid_t pid;
// do not daemonize under emperor
if (uwsgi.has_emperor) {
logto(logfile);
return;
}
pid = fork();
if (pid < 0) {
uwsgi_error("fork()");
exit(1);
}
if (pid != 0) {
_exit(0);
}
if (setsid() < 0) {
uwsgi_error("setsid()");
exit(1);
}
/* refork... */
pid = fork();
if (pid < 0) {
uwsgi_error("fork()");
exit(1);
}
if (pid != 0) {
_exit(0);
}
if (!uwsgi.do_not_change_umask) {
umask(0);
}
/*if (chdir("/") != 0) {
uwsgi_error("chdir()");
exit(1);
} */
uwsgi_remap_fd(0, "/dev/null");
logto(logfile);
}
// get current working directory
char *uwsgi_get_cwd() {
// set this to static to avoid useless reallocations in stats mode
static size_t newsize = 256;
char *cwd = uwsgi_malloc(newsize);
if (getcwd(cwd, newsize) == NULL && errno == ERANGE) {
newsize += 256;
uwsgi_log("need a bigger buffer (%lu bytes) for getcwd(). doing reallocation.\n", (unsigned long) newsize);
free(cwd);
cwd = uwsgi_malloc(newsize);
if (getcwd(cwd, newsize) == NULL) {
uwsgi_error("getcwd()");
exit(1);
}
}
return cwd;
}
#ifdef __linux__
void uwsgi_set_cgroup() {
char *cgroup_taskfile;
FILE *cgroup;
char *cgroup_opt;
struct uwsgi_string_list *usl, *uslo;
if (!uwsgi.cgroup)
return;
if (getuid())
return;
usl = uwsgi.cgroup;
while (usl) {
int mode = strtol(uwsgi.cgroup_dir_mode, 0, 8);
if (mkdir(usl->value, mode)) {
if (errno != EEXIST) {
uwsgi_error("uwsgi_set_cgroup()/mkdir()");
exit(1);
}
if (chmod(usl->value, mode)) {
uwsgi_error("uwsgi_set_cgroup()/chmod()");
exit(1);
}
uwsgi_log("using Linux cgroup %s with mode %o\n", usl->value, mode);
}
else {
uwsgi_log("created Linux cgroup %s with mode %o\n", usl->value, mode);
}
cgroup_taskfile = uwsgi_concat2(usl->value, "/tasks");
cgroup = fopen(cgroup_taskfile, "w");
if (!cgroup) {
uwsgi_error_open(cgroup_taskfile);
exit(1);
}
if (fprintf(cgroup, "%d\n", (int) getpid()) <= 0 || ferror(cgroup) || fclose(cgroup)) {
uwsgi_error("could not set cgroup");
exit(1);
}
uwsgi_log("assigned process %d to cgroup %s\n", (int) getpid(), cgroup_taskfile);
free(cgroup_taskfile);
uslo = uwsgi.cgroup_opt;
while (uslo) {
cgroup_opt = strchr(uslo->value, '=');
if (!cgroup_opt) {
cgroup_opt = strchr(uslo->value, ':');
if (!cgroup_opt) {
uwsgi_log("invalid cgroup-opt syntax\n");
exit(1);
}
}
cgroup_opt[0] = 0;
cgroup_opt++;
cgroup_taskfile = uwsgi_concat3(usl->value, "/", uslo->value);
cgroup = fopen(cgroup_taskfile, "w");
if (cgroup) {
if (fprintf(cgroup, "%s\n", cgroup_opt) <= 0 || ferror(cgroup) || fclose(cgroup)) {
uwsgi_log("could not set cgroup option %s to %s\n", uslo->value, cgroup_opt);
exit(1);
}
uwsgi_log("set %s to %s\n", cgroup_opt, cgroup_taskfile);
}
free(cgroup_taskfile);
cgroup_opt[-1] = '=';
uslo = uslo->next;
}
usl = usl->next;
}
}
#endif
#ifdef UWSGI_CAP
void uwsgi_apply_cap(cap_value_t * cap, int caps_count) {
cap_value_t minimal_cap_values[] = { CAP_SYS_CHROOT, CAP_SETUID, CAP_SETGID, CAP_SETPCAP };
cap_t caps = cap_init();
if (!caps) {
uwsgi_error("cap_init()");
exit(1);
}
cap_clear(caps);
cap_set_flag(caps, CAP_EFFECTIVE, 4, minimal_cap_values, CAP_SET);
cap_set_flag(caps, CAP_PERMITTED, 4, minimal_cap_values, CAP_SET);
cap_set_flag(caps, CAP_PERMITTED, caps_count, cap, CAP_SET);
cap_set_flag(caps, CAP_INHERITABLE, caps_count, cap, CAP_SET);
if (cap_set_proc(caps) < 0) {
uwsgi_error("cap_set_proc()");
exit(1);
}
cap_free(caps);
#ifdef __linux__
#ifdef SECBIT_KEEP_CAPS
if (prctl(SECBIT_KEEP_CAPS, 1, 0, 0, 0) < 0) {
uwsgi_error("prctl()");
exit(1);
}
#else
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
uwsgi_error("prctl()");
exit(1);
}
#endif
#endif
}
#endif
// drop privileges (as root)
/*
here we manage jails/namespaces too
it is a pretty huge function... refactory is needed
*/
void uwsgi_as_root() {
if (getuid() > 0)
goto nonroot;
#ifndef __RUMP__
if (!uwsgi.master_as_root && !uwsgi.uidname) {
uwsgi_log_initial("uWSGI running as root, you can use --uid/--gid/--chroot options\n");
}
#endif
int in_jail = 0;
#if defined(__linux__) && !defined(OBSOLETE_LINUX_KERNEL)
if (uwsgi.unshare && !uwsgi.reloads) {
if (unshare(uwsgi.unshare)) {
uwsgi_error("unshare()");
exit(1);
}
else {
uwsgi_log("[linux-namespace] applied unshare() mask: %d\n", uwsgi.unshare);
}
#ifdef CLONE_NEWUSER
if (uwsgi.unshare & CLONE_NEWUSER) {
if (setuid(0)) {
uwsgi_error("uwsgi_as_root()/setuid(0)");
exit(1);
}
}
#endif
in_jail = 1;
}
#endif
#ifdef UWSGI_CAP
if (uwsgi.cap && uwsgi.cap_count > 0 && !uwsgi.reloads) {
uwsgi_apply_cap(uwsgi.cap, uwsgi.cap_count);
}
#endif
#if defined(__FreeBSD__) || defined(__GNU_kFreeBSD__)
if (uwsgi.jail && !uwsgi.reloads) {
struct jail ujail;
char *jarg = uwsgi_str(uwsgi.jail);
char *j_hostname = NULL;
char *j_name = NULL;
char *space = strchr(jarg, ' ');
if (space) {
*space = 0;
j_hostname = space + 1;
space = strchr(j_hostname, ' ');
if (space) {
*space = 0;
j_name = space + 1;
}
}
ujail.version = JAIL_API_VERSION;
ujail.path = jarg;
ujail.hostname = j_hostname ? j_hostname : "";
ujail.jailname = j_name;
ujail.ip4s = 0;
ujail.ip6s = 0;
struct uwsgi_string_list *usl = NULL;
uwsgi_foreach(usl, uwsgi.jail_ip4) {
ujail.ip4s++;
}
struct in_addr *saddr = uwsgi_calloc(sizeof(struct in_addr) * ujail.ip4s);
int i = 0;
uwsgi_foreach(usl, uwsgi.jail_ip4) {
if (!inet_pton(AF_INET, usl->value, &saddr[i].s_addr)) {
uwsgi_error("jail()/inet_pton()");
exit(1);
}
i++;
}
ujail.ip4 = saddr;
#ifdef AF_INET6
uwsgi_foreach(usl, uwsgi.jail_ip6) {
ujail.ip6s++;
}
struct in6_addr *saddr6 = uwsgi_calloc(sizeof(struct in6_addr) * ujail.ip6s);
i = 0;
uwsgi_foreach(usl, uwsgi.jail_ip6) {
if (!inet_pton(AF_INET6, usl->value, &saddr6[i].s6_addr)) {
uwsgi_error("jail()/inet_pton()");
exit(1);
}
i++;
}
ujail.ip6 = saddr6;
#endif
int jail_id = jail(&ujail);
if (jail_id < 0) {
uwsgi_error("jail()");
exit(1);
}
if (uwsgi.jidfile) {
if (uwsgi_write_intfile(uwsgi.jidfile, jail_id)) {
uwsgi_log("unable to write jidfile\n");
exit(1);
}
}
uwsgi_log("--- running in FreeBSD jail %d ---\n", jail_id);
in_jail = 1;
}
#ifdef UWSGI_HAS_FREEBSD_LIBJAIL
if (uwsgi.jail_attach && !uwsgi.reloads) {
struct jailparam jparam;
uwsgi_log("attaching to FreeBSD jail %s ...\n", uwsgi.jail_attach);
if (!is_a_number(uwsgi.jail_attach)) {
if (jailparam_init(&jparam, "name")) {
uwsgi_error("jailparam_init()");
exit(1);
}
}
else {
if (jailparam_init(&jparam, "jid")) {
uwsgi_error("jailparam_init()");
exit(1);
}
}
jailparam_import(&jparam, uwsgi.jail_attach);
int jail_id = jailparam_set(&jparam, 1, JAIL_UPDATE | JAIL_ATTACH);
if (jail_id < 0) {
uwsgi_error("jailparam_set()");
exit(1);
}
jailparam_free(&jparam, 1);
uwsgi_log("--- running in FreeBSD jail %d ---\n", jail_id);
in_jail = 1;
}
if (uwsgi.jail2 && !uwsgi.reloads) {
struct uwsgi_string_list *usl = NULL;
unsigned nparams = 0;
uwsgi_foreach(usl, uwsgi.jail2) {
nparams++;
}
struct jailparam *params = uwsgi_malloc(sizeof(struct jailparam) * nparams);
int i = 0;
uwsgi_foreach(usl, uwsgi.jail2) {
uwsgi_log("FreeBSD libjail applying %s\n", usl->value);
char *equal = strchr(usl->value, '=');
if (equal) {
*equal = 0;
}
if (jailparam_init(&params[i], usl->value)) {
uwsgi_error("jailparam_init()");
exit(1);
}
if (equal) {
jailparam_import(&params[i], equal + 1);
*equal = '=';
}
else {
jailparam_import(&params[i], "1");
}
i++;
}
int jail_id = jailparam_set(params, nparams, JAIL_CREATE | JAIL_ATTACH);
if (jail_id < 0) {
uwsgi_error("jailparam_set()");
exit(1);
}
jailparam_free(params, nparams);
if (uwsgi.jidfile) {
if (uwsgi_write_intfile(uwsgi.jidfile, jail_id)) {
uwsgi_log("unable to write jidfile\n");
exit(1);
}
}
uwsgi_log("--- running in FreeBSD jail %d ---\n", jail_id);
in_jail = 1;
}
#endif
#endif
if (in_jail || uwsgi.jailed) {
uwsgi_hooks_run(uwsgi.hook_post_jail, "post-jail", 1);
struct uwsgi_string_list *usl = NULL;
uwsgi_foreach(usl, uwsgi.mount_post_jail) {
uwsgi_log("mounting \"%s\" (post-jail)...\n", usl->value);
if (uwsgi_mount_hook(usl->value)) {
exit(1);
}
}
uwsgi_foreach(usl, uwsgi.umount_post_jail) {
uwsgi_log("un-mounting \"%s\" (post-jail)...\n", usl->value);
if (uwsgi_umount_hook(usl->value)) {
exit(1);
}
}
uwsgi_foreach(usl, uwsgi.exec_post_jail) {
uwsgi_log("running \"%s\" (post-jail)...\n", usl->value);
int ret = uwsgi_run_command_and_wait(NULL, usl->value);
if (ret != 0) {
uwsgi_log("command \"%s\" exited with non-zero code: %d\n", usl->value, ret);
exit(1);
}
}
uwsgi_foreach(usl, uwsgi.call_post_jail) {
if (uwsgi_call_symbol(usl->value)) {
uwsgi_log("unable to call function \"%s\"\n", usl->value);
exit(1);
}
}
if (uwsgi.refork_post_jail) {
uwsgi_log("re-fork()ing...\n");
pid_t pid = fork();
if (pid < 0) {
uwsgi_error("fork()");
exit(1);
}
if (pid > 0) {
// block all signals
sigset_t smask;
sigfillset(&smask);
sigprocmask(SIG_BLOCK, &smask, NULL);
int status;
if (waitpid(pid, &status, 0) < 0) {
uwsgi_error("waitpid()");
}
_exit(0);
}
}
int i;
for (i = 0; i < uwsgi.gp_cnt; i++) {
if (uwsgi.gp[i]->post_jail) {
uwsgi.gp[i]->post_jail();
}
}
}
if (uwsgi.chroot && !uwsgi.reloads) {
if (!uwsgi.master_as_root)
uwsgi_log("chroot() to %s\n", uwsgi.chroot);
if (chroot(uwsgi.chroot)) {
uwsgi_error("chroot()");
exit(1);
}
#ifdef __linux__
if (uwsgi.logging_options.memory_report) {
uwsgi_log("*** Warning, on linux system you have to bind-mount the /proc fs in your chroot to get memory debug/report.\n");
}
#endif
}
#ifdef __linux__
if (uwsgi.pivot_root && !uwsgi.reloads) {
char *arg = uwsgi_str(uwsgi.pivot_root);
char *space = strchr(arg, ' ');
if (!space) {
uwsgi_log("invalid pivot_root syntax, new_root and put_old must be separated by a space\n");
exit(1);
}
*space = 0;
#if defined(MS_REC) && defined(MS_PRIVATE)
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) {
uwsgi_error("mount()");
exit(1);
}
#endif
if (chdir(arg)) {
uwsgi_error("pivot_root()/chdir()");
exit(1);
}
space += 1 + strlen(arg);
if (space[0] == '/')
space++;
if (pivot_root(".", space)) {
uwsgi_error("pivot_root()");
exit(1);
}
if (uwsgi.logging_options.memory_report) {
uwsgi_log("*** Warning, on linux system you have to bind-mount the /proc fs in your chroot to get memory debug/report.\n");
}
free(arg);
if (chdir("/")) {
uwsgi_error("chdir()");
exit(1);
}
}
#endif
#if defined(__linux__) && !defined(OBSOLETE_LINUX_KERNEL)
if (uwsgi.unshare2 && !uwsgi.reloads) {
if (unshare(uwsgi.unshare2)) {
uwsgi_error("unshare()");
exit(1);
}
else {
uwsgi_log("[linux-namespace] applied unshare() mask: %d\n", uwsgi.unshare2);
}
#ifdef CLONE_NEWUSER
if (uwsgi.unshare2 & CLONE_NEWUSER) {
if (setuid(0)) {
uwsgi_error("uwsgi_as_root()/setuid(0)");
exit(1);
}
}
#endif
in_jail = 1;
}
#endif
if (uwsgi.refork_as_root) {
uwsgi_log("re-fork()ing...\n");
pid_t pid = fork();
if (pid < 0) {
uwsgi_error("fork()");
exit(1);
}
if (pid > 0) {
// block all signals
sigset_t smask;
sigfillset(&smask);
sigprocmask(SIG_BLOCK, &smask, NULL);
int status;
if (waitpid(pid, &status, 0) < 0) {
uwsgi_error("waitpid()");
}
_exit(0);
}
}
struct uwsgi_string_list *usl;
uwsgi_foreach(usl, uwsgi.wait_for_interface) {
if (!uwsgi.wait_for_interface_timeout) {
uwsgi.wait_for_interface_timeout = 60;
}
uwsgi_log("waiting for interface %s (max %d seconds) ...\n", usl->value, uwsgi.wait_for_interface_timeout);
int counter = 0;
for (;;) {
if (counter > uwsgi.wait_for_interface_timeout) {
uwsgi_log("interface %s unavailable after %d seconds\n", usl->value, counter);
exit(1);
}
unsigned int index = if_nametoindex(usl->value);
if (index > 0) {
uwsgi_log("interface %s found with index %u\n", usl->value, index);
break;
}
else {
sleep(1);
counter++;
}
}
}
uwsgi_foreach(usl, uwsgi.wait_for_fs) {
if (uwsgi_wait_for_fs(usl->value, 0)) exit(1);
}
uwsgi_foreach(usl, uwsgi.wait_for_file) {
if (uwsgi_wait_for_fs(usl->value, 1)) exit(1);
}
uwsgi_foreach(usl, uwsgi.wait_for_dir) {
if (uwsgi_wait_for_fs(usl->value, 2)) exit(1);
}
uwsgi_foreach(usl, uwsgi.wait_for_mountpoint) {
if (uwsgi_wait_for_mountpoint(usl->value)) exit(1);
}
uwsgi_hooks_run(uwsgi.hook_as_root, "as root", 1);
uwsgi_foreach(usl, uwsgi.mount_as_root) {
uwsgi_log("mounting \"%s\" (as root)...\n", usl->value);
if (uwsgi_mount_hook(usl->value)) {
exit(1);
}
}
uwsgi_foreach(usl, uwsgi.umount_as_root) {
uwsgi_log("un-mounting \"%s\" (as root)...\n", usl->value);
if (uwsgi_umount_hook(usl->value)) {
exit(1);
}
}
// now run the scripts needed by root
uwsgi_foreach(usl, uwsgi.exec_as_root) {
uwsgi_log("running \"%s\" (as root)...\n", usl->value);
int ret = uwsgi_run_command_and_wait(NULL, usl->value);
if (ret != 0) {
uwsgi_log("command \"%s\" exited with non-zero code: %d\n", usl->value, ret);
exit(1);
}
}
uwsgi_foreach(usl, uwsgi.call_as_root) {
if (uwsgi_call_symbol(usl->value)) {
uwsgi_log("unable to call function \"%s\"\n", usl->value);
}
}
if (uwsgi.gidname) {
struct group *ugroup = getgrnam(uwsgi.gidname);
if (ugroup) {
uwsgi.gid = ugroup->gr_gid;
}
else {
uwsgi_log("group %s not found.\n", uwsgi.gidname);
exit(1);
}
}
if (uwsgi.uidname) {
struct passwd *upasswd = getpwnam(uwsgi.uidname);
if (upasswd) {
uwsgi.uid = upasswd->pw_uid;
}
else {
uwsgi_log("user %s not found.\n", uwsgi.uidname);
exit(1);
}
}
if (uwsgi.logfile_chown) {
int log_fd = 2;
if (uwsgi.log_master && uwsgi.original_log_fd > -1) {
log_fd = uwsgi.original_log_fd;
}
if (fchown(log_fd, uwsgi.uid, uwsgi.gid)) {
uwsgi_error("fchown()");
exit(1);
}
}
// fix ipcsem owner
if (uwsgi.lock_ops.lock_init == uwsgi_lock_ipcsem_init) {
struct uwsgi_lock_item *uli = uwsgi.registered_locks;
while (uli) {
union semun {
int val;
struct semid_ds *buf;
ushort *array;
} semu;
struct semid_ds sds;
memset(&sds, 0, sizeof(sds));
semu.buf = &sds;
int semid = 0;
memcpy(&semid, uli->lock_ptr, sizeof(int));
if (semctl(semid, 0, IPC_STAT, semu)) {
uwsgi_error("semctl()");
exit(1);
}
semu.buf->sem_perm.uid = uwsgi.uid;
semu.buf->sem_perm.gid = uwsgi.gid;
if (semctl(semid, 0, IPC_SET, semu)) {
uwsgi_error("semctl()");
exit(1);
}
uli = uli->next;
}
}
// ok try to call some special hook before finally dropping privileges
int i;
for (i = 0; i < uwsgi.gp_cnt; i++) {
if (uwsgi.gp[i]->before_privileges_drop) {
uwsgi.gp[i]->before_privileges_drop();
}
}
if (uwsgi.gid) {
if (!uwsgi.master_as_root)
uwsgi_log("setgid() to %d\n", uwsgi.gid);
if (setgid(uwsgi.gid)) {
uwsgi_error("setgid()");
exit(1);
}
if (uwsgi.no_initgroups || !uwsgi.uid) {
if (setgroups(0, NULL)) {
uwsgi_error("setgroups()");
exit(1);
}
}
else {
char *uidname = uwsgi.uidname;
if (!uidname) {
struct passwd *pw = getpwuid(uwsgi.uid);
if (pw)
uidname = pw->pw_name;
}
if (!uidname)
uidname = uwsgi_num2str(uwsgi.uid);
if (initgroups(uidname, uwsgi.gid)) {
uwsgi_error("setgroups()");
exit(1);
}
}
struct uwsgi_string_list *usl;
size_t ags = 0;
uwsgi_foreach(usl, uwsgi.additional_gids) ags++;
if (ags > 0) {
gid_t *ags_list = uwsgi_calloc(sizeof(gid_t) * ags);
size_t g_pos = 0;
uwsgi_foreach(usl, uwsgi.additional_gids) {
ags_list[g_pos] = atoi(usl->value);
if (!ags_list[g_pos]) {
struct group *g = getgrnam(usl->value);
if (g) {
ags_list[g_pos] = g->gr_gid;
}
else {
uwsgi_log("unable to find group %s\n", usl->value);
exit(1);
}
}
g_pos++;
}
if (setgroups(ags, ags_list)) {
uwsgi_error("setgroups()");
exit(1);
}
}
int additional_groups = getgroups(0, NULL);
if (additional_groups > 0) {
gid_t *gids = uwsgi_calloc(sizeof(gid_t) * additional_groups);
if (getgroups(additional_groups, gids) > 0) {
int i;
for (i = 0; i < additional_groups; i++) {
if (gids[i] == uwsgi.gid)
continue;
struct group *gr = getgrgid(gids[i]);
if (gr) {
uwsgi_log("set additional group %d (%s)\n", gids[i], gr->gr_name);
}
else {
uwsgi_log("set additional group %d\n", gids[i]);
}
}
}
free(gids);
}
}
if (uwsgi.uid) {
if (!uwsgi.master_as_root)
uwsgi_log("setuid() to %d\n", uwsgi.uid);
if (setuid(uwsgi.uid)) {
uwsgi_error("setuid()");
exit(1);
}
}
#ifndef __RUMP__
if (!getuid()) {
uwsgi_log_initial("*** WARNING: you are running uWSGI as root !!! (use the --uid flag) *** \n");
}
#endif
#ifdef UWSGI_CAP
if (uwsgi.cap && uwsgi.cap_count > 0 && !uwsgi.reloads) {
cap_t caps = cap_init();
if (!caps) {
uwsgi_error("cap_init()");
exit(1);
}
cap_clear(caps);
cap_set_flag(caps, CAP_EFFECTIVE, uwsgi.cap_count, uwsgi.cap, CAP_SET);
cap_set_flag(caps, CAP_PERMITTED, uwsgi.cap_count, uwsgi.cap, CAP_SET);
cap_set_flag(caps, CAP_INHERITABLE, uwsgi.cap_count, uwsgi.cap, CAP_SET);
if (cap_set_proc(caps) < 0) {
uwsgi_error("cap_set_proc()");
exit(1);
}
cap_free(caps);
}
#endif
if (uwsgi.refork) {
uwsgi_log("re-fork()ing...\n");
pid_t pid = fork();
if (pid < 0) {
uwsgi_error("fork()");
exit(1);
}
if (pid > 0) {
// block all signals
sigset_t smask;
sigfillset(&smask);
sigprocmask(SIG_BLOCK, &smask, NULL);
int status;
if (waitpid(pid, &status, 0) < 0) {
uwsgi_error("waitpid()");
}
_exit(0);
}
}
uwsgi_hooks_run(uwsgi.hook_as_user, "as user", 1);
// now run the scripts needed by the user
uwsgi_foreach(usl, uwsgi.exec_as_user) {
uwsgi_log("running \"%s\" (as uid: %d gid: %d) ...\n", usl->value, (int) getuid(), (int) getgid());
int ret = uwsgi_run_command_and_wait(NULL, usl->value);
if (ret != 0) {
uwsgi_log("command \"%s\" exited with non-zero code: %d\n", usl->value, ret);
exit(1);
}
}
uwsgi_foreach(usl, uwsgi.call_as_user) {
if (uwsgi_call_symbol(usl->value)) {
uwsgi_log("unable to call function \"%s\"\n", usl->value);
exit(1);
}
}
// we could now patch the binary
if (uwsgi.unprivileged_binary_patch) {
uwsgi.argv[0] = uwsgi.unprivileged_binary_patch;
execvp(uwsgi.unprivileged_binary_patch, uwsgi.argv);
uwsgi_error("execvp()");
exit(1);
}
if (uwsgi.unprivileged_binary_patch_arg) {
uwsgi_exec_command_with_args(uwsgi.unprivileged_binary_patch_arg);
}
return;
nonroot:
if (uwsgi.chroot && !uwsgi.is_a_reload) {
uwsgi_log("cannot chroot() as non-root user\n");
exit(1);
}
if (uwsgi.gid && getgid() != uwsgi.gid) {
uwsgi_log("cannot setgid() as non-root user\n");
exit(1);
}
if (uwsgi.uid && getuid() != uwsgi.uid) {
uwsgi_log("cannot setuid() as non-root user\n");
exit(1);
}
}
static void close_and_free_request(struct wsgi_request *wsgi_req) {
// close the connection with the client
if (!wsgi_req->fd_closed) {
// NOTE, if we close the socket before receiving eventually sent data, socket layer will send a RST
wsgi_req->socket->proto_close(wsgi_req);
}
if (wsgi_req->post_file) {
fclose(wsgi_req->post_file);
}
if (wsgi_req->post_read_buf) {
free(wsgi_req->post_read_buf);
}
if (wsgi_req->post_readline_buf) {
free(wsgi_req->post_readline_buf);
}
if (wsgi_req->proto_parser_buf) {
free(wsgi_req->proto_parser_buf);
}
}
// destroy a request
void uwsgi_destroy_request(struct wsgi_request *wsgi_req) {
close_and_free_request(wsgi_req);
int foo;
if (uwsgi.threads > 1) {
// now the thread can die...
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &foo);
}
// reset for avoiding following requests to fail on non-uwsgi protocols
// thanks Marko Tiikkaja for catching it
wsgi_req->uh->_pktsize = 0;
// some plugins expected async_id to be defined before setup
int tmp_id = wsgi_req->async_id;
memset(wsgi_req, 0, sizeof(struct wsgi_request));
wsgi_req->async_id = tmp_id;
}
// finalize/close/free a request
void uwsgi_close_request(struct wsgi_request *wsgi_req) {
int waitpid_status;
int tmp_id;
uint64_t tmp_rt, rss = 0, vsz = 0;
// apply transformations
if (wsgi_req->transformations) {
if (uwsgi_apply_final_transformations(wsgi_req) == 0) {
if (wsgi_req->transformed_chunk && wsgi_req->transformed_chunk_len > 0) {
uwsgi_response_write_body_do(wsgi_req, wsgi_req->transformed_chunk, wsgi_req->transformed_chunk_len);
}
}
uwsgi_free_transformations(wsgi_req);
}
// check if headers should be sent
if (wsgi_req->headers) {
if (!wsgi_req->headers_sent && !wsgi_req->headers_size && !wsgi_req->response_size) {
uwsgi_response_write_headers_do(wsgi_req);
}
uwsgi_buffer_destroy(wsgi_req->headers);
}
uint64_t end_of_request = uwsgi_micros();
wsgi_req->end_of_request = end_of_request;
if (!wsgi_req->do_not_account_avg_rt) {
tmp_rt = wsgi_req->end_of_request - wsgi_req->start_of_request;
uwsgi.workers[uwsgi.mywid].running_time += tmp_rt;
uwsgi.workers[uwsgi.mywid].avg_response_time = (uwsgi.workers[uwsgi.mywid].avg_response_time + tmp_rt) / 2;
}
// get memory usage
if (uwsgi.logging_options.memory_report == 1 || uwsgi.force_get_memusage) {
get_memusage(&rss, &vsz);
uwsgi.workers[uwsgi.mywid].vsz_size = vsz;
uwsgi.workers[uwsgi.mywid].rss_size = rss;
}
if (!wsgi_req->do_not_account) {
uwsgi.workers[0].requests++;
uwsgi.workers[uwsgi.mywid].requests++;
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].requests++;
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].write_errors += wsgi_req->write_errors;
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].read_errors += wsgi_req->read_errors;
// this is used for MAX_REQUESTS
uwsgi.workers[uwsgi.mywid].delta_requests++;
}
#ifdef UWSGI_ROUTING
// apply final routes after accounting
uwsgi_apply_final_routes(wsgi_req);
#endif
// close socket and free parsers-allocated memory
close_and_free_request(wsgi_req);
// after_request hook
if (!wsgi_req->is_raw && uwsgi.p[wsgi_req->uh->modifier1]->after_request)
uwsgi.p[wsgi_req->uh->modifier1]->after_request(wsgi_req);
// after_request custom hooks
struct uwsgi_string_list *usl = NULL;
uwsgi_foreach(usl, uwsgi.after_request_hooks) {
void (*func) (struct wsgi_request *) = (void (*)(struct wsgi_request *)) usl->custom_ptr;
func(wsgi_req);
}
if (uwsgi.threads > 1) {
// now the thread can die...
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &tmp_id);
}
// leave harakiri mode
if (uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].harakiri > 0) {
set_harakiri(wsgi_req, 0);
}
// leave user harakiri mode
if (uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].user_harakiri > 0) {
set_user_harakiri(wsgi_req, 0);
}
if (!wsgi_req->do_not_account) {
// this is racy in multithread mode
if (wsgi_req->response_size > 0) {
uwsgi.workers[uwsgi.mywid].tx += wsgi_req->response_size;
}
if (wsgi_req->headers_size > 0) {
uwsgi.workers[uwsgi.mywid].tx += wsgi_req->headers_size;
}
}
// defunct process reaper
if (uwsgi.reaper == 1) {
while (waitpid(WAIT_ANY, &waitpid_status, WNOHANG) > 0);
}
// free logvars
struct uwsgi_logvar *lv = wsgi_req->logvars;
while (lv) {
struct uwsgi_logvar *ptr = lv;
lv = lv->next;
free(ptr);
}
// free additional headers
struct uwsgi_string_list *ah = wsgi_req->additional_headers;
while (ah) {
struct uwsgi_string_list *ptr = ah;
ah = ah->next;
free(ptr->value);
free(ptr);
}
// free remove headers
ah = wsgi_req->remove_headers;
while (ah) {
struct uwsgi_string_list *ptr = ah;
ah = ah->next;
free(ptr->value);
free(ptr);
}
// free chunked input
if (wsgi_req->chunked_input_buf) {
uwsgi_buffer_destroy(wsgi_req->chunked_input_buf);
}
// free websocket engine
if (wsgi_req->websocket_buf) {
uwsgi_buffer_destroy(wsgi_req->websocket_buf);
}
if (wsgi_req->websocket_send_buf) {
uwsgi_buffer_destroy(wsgi_req->websocket_send_buf);
}
// reset request
wsgi_req->uh->_pktsize = 0;
tmp_id = wsgi_req->async_id;
memset(wsgi_req, 0, sizeof(struct wsgi_request));
// some plugins expected async_id to be defined before setup
wsgi_req->async_id = tmp_id;
// yes, this is pretty useless but we cannot ensure all of the plugin have the same behaviour
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 0;
if (uwsgi.max_requests > 0 && uwsgi.workers[uwsgi.mywid].delta_requests >= (uwsgi.max_requests + ((uwsgi.mywid-1) * uwsgi.max_requests_delta))
&& (end_of_request - (uwsgi.workers[uwsgi.mywid].last_spawn * 1000000) >= uwsgi.min_worker_lifetime * 1000000)) {
goodbye_cruel_world("max requests reached (%llu >= %llu)",
(unsigned long long) uwsgi.workers[uwsgi.mywid].delta_requests,
(unsigned long long) (uwsgi.max_requests + ((uwsgi.mywid-1) * uwsgi.max_requests_delta))
);
}
if (uwsgi.reload_on_as && (rlim_t) vsz >= uwsgi.reload_on_as && (end_of_request - (uwsgi.workers[uwsgi.mywid].last_spawn * 1000000) >= uwsgi.min_worker_lifetime * 1000000)) {
goodbye_cruel_world("reload-on-as limit reached (%llu >= %llu)",
(unsigned long long) (rlim_t) vsz,
(unsigned long long) uwsgi.reload_on_as
);
}
if (uwsgi.reload_on_rss && (rlim_t) rss >= uwsgi.reload_on_rss && (end_of_request - (uwsgi.workers[uwsgi.mywid].last_spawn * 1000000) >= uwsgi.min_worker_lifetime * 1000000)) {
goodbye_cruel_world("reload-on-rss limit reached (%llu >= %llu)",
(unsigned long long) (rlim_t) rss,
(unsigned long long) uwsgi.reload_on_rss
);
}
// after the first request, if i am a vassal, signal Emperor about my loyalty
if (uwsgi.has_emperor && !uwsgi.loyal) {
uwsgi_log("announcing my loyalty to the Emperor...\n");
char byte = 17;
if (write(uwsgi.emperor_fd, &byte, 1) != 1) {
uwsgi_error("write()");
}
uwsgi.loyal = 1;
}
#ifdef __linux__
#ifdef MADV_MERGEABLE
// run the ksm mapper
if (uwsgi.linux_ksm > 0 && (uwsgi.workers[uwsgi.mywid].requests % uwsgi.linux_ksm) == 0) {
uwsgi_linux_ksm_map();
}
#endif
#endif
}
#ifdef __linux__
#ifdef MADV_MERGEABLE
void uwsgi_linux_ksm_map(void) {
int dirty = 0;
size_t i;
unsigned long long start = 0, end = 0;
int errors = 0;
int lines = 0;
int fd = open("/proc/self/maps", O_RDONLY);
if (fd < 0) {
uwsgi_error_open("[uwsgi-KSM] /proc/self/maps");
return;
}
// allocate memory if not available;
if (uwsgi.ksm_mappings_current == NULL) {
if (!uwsgi.ksm_buffer_size)
uwsgi.ksm_buffer_size = 32768;
uwsgi.ksm_mappings_current = uwsgi_malloc(uwsgi.ksm_buffer_size);
uwsgi.ksm_mappings_current_size = 0;
}
if (uwsgi.ksm_mappings_last == NULL) {
if (!uwsgi.ksm_buffer_size)
uwsgi.ksm_buffer_size = 32768;
uwsgi.ksm_mappings_last = uwsgi_malloc(uwsgi.ksm_buffer_size);
uwsgi.ksm_mappings_last_size = 0;
}
uwsgi.ksm_mappings_current_size = read(fd, uwsgi.ksm_mappings_current, uwsgi.ksm_buffer_size);
close(fd);
if (uwsgi.ksm_mappings_current_size <= 0) {
uwsgi_log("[uwsgi-KSM] unable to read /proc/self/maps data\n");
return;
}
// we now have areas
if (uwsgi.ksm_mappings_last_size == 0 || uwsgi.ksm_mappings_current_size != uwsgi.ksm_mappings_last_size) {
dirty = 1;
}
else {
if (memcmp(uwsgi.ksm_mappings_current, uwsgi.ksm_mappings_last, uwsgi.ksm_mappings_current_size) != 0) {
dirty = 1;
}
}
// it is dirty, swap addresses and parse it
if (dirty) {
char *tmp = uwsgi.ksm_mappings_last;
uwsgi.ksm_mappings_last = uwsgi.ksm_mappings_current;
uwsgi.ksm_mappings_current = tmp;
size_t tmp_size = uwsgi.ksm_mappings_last_size;
uwsgi.ksm_mappings_last_size = uwsgi.ksm_mappings_current_size;
uwsgi.ksm_mappings_current_size = tmp_size;
// scan each line and call madvise on it
char *ptr = uwsgi.ksm_mappings_last;
for (i = 0; i < uwsgi.ksm_mappings_last_size; i++) {
if (uwsgi.ksm_mappings_last[i] == '\n') {
lines++;
uwsgi.ksm_mappings_last[i] = 0;
if (sscanf(ptr, "%llx-%llx %*s", &start, &end) == 2) {
if (madvise((void *) (long) start, (size_t) (end - start), MADV_MERGEABLE)) {
errors++;
}
}
uwsgi.ksm_mappings_last[i] = '\n';
ptr = uwsgi.ksm_mappings_last + i + 1;
}
}
if (errors >= lines) {
uwsgi_error("[uwsgi-KSM] unable to share pages");
}
}
}
#endif
#endif
#ifdef __linux__
long uwsgi_num_from_file(char *filename, int quiet) {
char buf[16];
ssize_t len;
int fd = open(filename, O_RDONLY);
if (fd < 0) {
if (!quiet)
uwsgi_error_open(filename);
return -1L;
}
len = read(fd, buf, sizeof(buf));
if (len == 0) {
if (!quiet)
uwsgi_log("read error %s\n", filename);
close(fd);
return -1L;
}
close(fd);
return strtol(buf, (char **) NULL, 10);
}
#endif
// setup for a new request
void wsgi_req_setup(struct wsgi_request *wsgi_req, int async_id, struct uwsgi_socket *uwsgi_sock) {
wsgi_req->app_id = -1;
wsgi_req->async_id = async_id;
wsgi_req->sendfile_fd = -1;
wsgi_req->hvec = uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].hvec;
// skip the first 4 bytes;
wsgi_req->uh = (struct uwsgi_header *) uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].buffer;
wsgi_req->buffer = uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].buffer + 4;
if (uwsgi.post_buffering > 0) {
wsgi_req->post_buffering_buf = uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].post_buf;
}
if (uwsgi_sock) {
wsgi_req->socket = uwsgi_sock;
}
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 0;
// now check for suspend request
if (uwsgi.workers[uwsgi.mywid].suspended == 1) {
uwsgi_log_verbose("*** worker %d suspended ***\n", uwsgi.mywid);
cycle:
// wait for some signal (normally SIGTSTP) or 10 seconds (as fallback)
(void) poll(NULL, 0, 10 * 1000);
if (uwsgi.workers[uwsgi.mywid].suspended == 1)
goto cycle;
uwsgi_log_verbose("*** worker %d resumed ***\n", uwsgi.mywid);
}
}
int wsgi_req_async_recv(struct wsgi_request *wsgi_req) {
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 1;
wsgi_req->start_of_request = uwsgi_micros();
wsgi_req->start_of_request_in_sec = wsgi_req->start_of_request / 1000000;
if (!wsgi_req->do_not_add_to_async_queue) {
if (event_queue_add_fd_read(uwsgi.async_queue, wsgi_req->fd) < 0)
return -1;
async_add_timeout(wsgi_req, uwsgi.socket_timeout);
uwsgi.async_proto_fd_table[wsgi_req->fd] = wsgi_req;
}
// enter harakiri mode
if (uwsgi.harakiri_options.workers > 0) {
set_harakiri(wsgi_req, uwsgi.harakiri_options.workers);
}
return 0;
}
// receive a new request
int wsgi_req_recv(int queue, struct wsgi_request *wsgi_req) {
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 1;
wsgi_req->start_of_request = uwsgi_micros();
wsgi_req->start_of_request_in_sec = wsgi_req->start_of_request / 1000000;
// edge triggered sockets get the whole request during accept() phase
if (!wsgi_req->socket->edge_trigger) {
for (;;) {
int ret = wsgi_req->socket->proto(wsgi_req);
if (ret == UWSGI_OK)
break;
if (ret == UWSGI_AGAIN) {
ret = uwsgi_wait_read_req(wsgi_req);
if (ret <= 0)
return -1;
continue;
}
return -1;
}
}
// enter harakiri mode
if (uwsgi.harakiri_options.workers > 0) {
set_harakiri(wsgi_req, uwsgi.harakiri_options.workers);
}
#ifdef UWSGI_ROUTING
if (uwsgi_apply_routes(wsgi_req) == UWSGI_ROUTE_BREAK)
return 0;
#endif
wsgi_req->async_status = uwsgi.p[wsgi_req->uh->modifier1]->request(wsgi_req);
return 0;
}
void uwsgi_post_accept(struct wsgi_request *wsgi_req) {
// set close on exec (if not a new socket)
if (!wsgi_req->socket->edge_trigger && uwsgi.close_on_exec) {
if (fcntl(wsgi_req->fd, F_SETFD, FD_CLOEXEC) < 0) {
uwsgi_error("fcntl()");
}
}
// enable TCP_NODELAY ?
if (uwsgi.tcp_nodelay) {
uwsgi_tcp_nodelay(wsgi_req->fd);
}
}
// accept a new request
int wsgi_req_simple_accept(struct wsgi_request *wsgi_req, int fd) {
wsgi_req->fd = wsgi_req->socket->proto_accept(wsgi_req, fd);
if (wsgi_req->fd < 0) {
return -1;
}
uwsgi_post_accept(wsgi_req);
return 0;
}
// send heartbeat to the emperor
void uwsgi_heartbeat() {
if (!uwsgi.has_emperor)
return;
time_t now = uwsgi_now();
if (uwsgi.next_heartbeat <= now) {
char byte = 26;
if (write(uwsgi.emperor_fd, &byte, 1) != 1) {
uwsgi_error("write()");
}
uwsgi.next_heartbeat = now + uwsgi.heartbeat;
}
}
// accept a request
int wsgi_req_accept(int queue, struct wsgi_request *wsgi_req) {
int ret;
int interesting_fd = -1;
struct uwsgi_socket *uwsgi_sock = uwsgi.sockets;
int timeout = -1;
thunder_lock;
// heartbeat
// in multithreaded mode we are now locked
if (uwsgi.has_emperor && uwsgi.heartbeat) {
time_t now = uwsgi_now();
// overengineering ... (reduce skew problems)
timeout = uwsgi.heartbeat;
if (!uwsgi.next_heartbeat) {
uwsgi.next_heartbeat = now;
}
if (uwsgi.next_heartbeat >= now) {
timeout = uwsgi.next_heartbeat - now;
}
}
// need edge trigger ?
if (uwsgi.is_et) {
while (uwsgi_sock) {
if (uwsgi_sock->retry && uwsgi_sock->retry[wsgi_req->async_id]) {
timeout = 0;
break;
}
uwsgi_sock = uwsgi_sock->next;
}
// reset pointer
uwsgi_sock = uwsgi.sockets;
}
ret = event_queue_wait(queue, timeout, &interesting_fd);
if (ret < 0) {
thunder_unlock;
return -1;
}
// check for heartbeat
if (uwsgi.has_emperor && uwsgi.heartbeat) {
uwsgi_heartbeat();
// no need to continue if timed-out
if (ret == 0) {
thunder_unlock;
return -1;
}
}
// kill the thread after the request completion
if (uwsgi.threads > 1)
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &ret);
if (uwsgi.signal_socket > -1 && (interesting_fd == uwsgi.signal_socket || interesting_fd == uwsgi.my_signal_socket)) {
thunder_unlock;
uwsgi_receive_signal(wsgi_req, interesting_fd, "worker", uwsgi.mywid);
if (uwsgi.threads > 1)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ret);
return -1;
}
while (uwsgi_sock) {
if (interesting_fd == uwsgi_sock->fd || (uwsgi_sock->retry && uwsgi_sock->retry[wsgi_req->async_id]) || (uwsgi_sock->fd_threads && interesting_fd == uwsgi_sock->fd_threads[wsgi_req->async_id])) {
wsgi_req->socket = uwsgi_sock;
wsgi_req->fd = wsgi_req->socket->proto_accept(wsgi_req, interesting_fd);
thunder_unlock;
if (wsgi_req->fd < 0) {
if (uwsgi.threads > 1)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ret);
return -1;
}
if (!uwsgi_sock->edge_trigger) {
uwsgi_post_accept(wsgi_req);
}
return 0;
}
uwsgi_sock = uwsgi_sock->next;
}
thunder_unlock;
if (uwsgi.threads > 1)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ret);
return -1;
}
// translate a OS env to a uWSGI option
void env_to_arg(char *src, char *dst) {
int i;
int val = 0;
for (i = 0; i < (int) strlen(src); i++) {
if (src[i] == '=') {
val = 1;
}
if (val) {
dst[i] = src[i];
}
else {
dst[i] = tolower((int) src[i]);
if (dst[i] == '_') {
dst[i] = '-';
}
}
}
dst[strlen(src)] = 0;
}
// parse OS envs
void parse_sys_envs(char **envs) {
char **uenvs = envs;
char *earg, *eq_pos;
while (*uenvs) {
if (!strncmp(*uenvs, "UWSGI_", 6) && strncmp(*uenvs, "UWSGI_RELOADS=", 14) && strncmp(*uenvs, "UWSGI_VASSALS_DIR=", 18) && strncmp(*uenvs, "UWSGI_EMPEROR_FD=", 17) && strncmp(*uenvs, "UWSGI_BROODLORD_NUM=", 20) && strncmp(*uenvs, "UWSGI_EMPEROR_FD_CONFIG=", 24) && strncmp(*uenvs, "UWSGI_EMPEROR_PROXY=", 20) && strncmp(*uenvs, "UWSGI_JAIL_PID=", 15) && strncmp(*uenvs, "UWSGI_ORIGINAL_PROC_NAME=", 25)) {
earg = uwsgi_malloc(strlen(*uenvs + 6) + 1);
env_to_arg(*uenvs + 6, earg);
eq_pos = strchr(earg, '=');
if (!eq_pos) {
break;
}
eq_pos[0] = 0;
add_exported_option(earg, eq_pos + 1, 0);
}
uenvs++;
}
}
// get the application id
int uwsgi_get_app_id(struct wsgi_request *wsgi_req, char *key, uint16_t key_len, int modifier1) {
int i;
struct stat st;
int found;
char *app_name = key;
uint16_t app_name_len = key_len;
if (app_name_len == 0 && wsgi_req) {
app_name = wsgi_req->appid;
app_name_len = wsgi_req->appid_len;
if (app_name_len == 0) {
if (!uwsgi.ignore_script_name) {
app_name = wsgi_req->script_name;
app_name_len = wsgi_req->script_name_len;
}
if (uwsgi.vhost) {
char *vhost_name = uwsgi_concat3n(wsgi_req->host, wsgi_req->host_len, "|", 1, wsgi_req->script_name, wsgi_req->script_name_len);
app_name_len = wsgi_req->host_len + 1 + wsgi_req->script_name_len;
app_name = uwsgi_req_append(wsgi_req, "UWSGI_APPID", 11, vhost_name, app_name_len);
free(vhost_name);
if (!app_name) {
uwsgi_log("unable to add UWSGI_APPID to the uwsgi buffer, consider increasing it\n");
return -1;
}
#ifdef UWSGI_DEBUG
uwsgi_debug("VirtualHost KEY=%.*s\n", app_name_len, app_name);
#endif
}
wsgi_req->appid = app_name;
wsgi_req->appid_len = app_name_len;
}
}
for (i = 0; i < uwsgi_apps_cnt; i++) {
// reset check
found = 0;
#ifdef UWSGI_DEBUG
uwsgi_log("searching for %.*s in %.*s %p\n", app_name_len, app_name, uwsgi_apps[i].mountpoint_len, uwsgi_apps[i].mountpoint, uwsgi_apps[i].callable);
#endif
if (!uwsgi_apps[i].callable) {
continue;
}
if (!uwsgi_strncmp(uwsgi_apps[i].mountpoint, uwsgi_apps[i].mountpoint_len, app_name, app_name_len)) {
found = 1;
}
if (found) {
if (uwsgi_apps[i].touch_reload[0]) {
if (!stat(uwsgi_apps[i].touch_reload, &st)) {
if (st.st_mtime != uwsgi_apps[i].touch_reload_mtime) {
// serve the new request and reload
uwsgi.workers[uwsgi.mywid].manage_next_request = 0;
if (uwsgi.threads > 1) {
uwsgi.workers[uwsgi.mywid].destroy = 1;
}
#ifdef UWSGI_DEBUG
uwsgi_log("mtime %d %d\n", st.st_mtime, uwsgi_apps[i].touch_reload_mtime);
#endif
}
}
}
if (modifier1 == -1)
return i;
if (modifier1 == uwsgi_apps[i].modifier1)
return i;
}
}
return -1;
}
char *uwsgi_substitute(char *src, char *what, char *with) {
int count = 0;
if (!with)
return src;
size_t len = strlen(src);
size_t wlen = strlen(what);
size_t with_len = strlen(with);
char *p = strstr(src, what);
if (!p) {
return src;
}
while (p) {
count++;
p = strstr(p + wlen, what);
}
len += (count * with_len) + 1;
char *dst = uwsgi_calloc(len);
char *ptr = src;
p = strstr(ptr, what);
while (p) {
strncat(dst, ptr, (p - ptr));
strncat(dst, with, with_len);
ptr = p + wlen;
p = strstr(ptr, what);
}
strncat(dst, ptr, strlen(ptr));
return dst;
}
int uwsgi_is_file(char *filename) {
struct stat st;
if (stat(filename, &st)) {
return 0;
}
if (S_ISREG(st.st_mode))
return 1;
return 0;
}
int uwsgi_is_file2(char *filename, struct stat *st) {
if (stat(filename, st)) {
return 0;
}
if (S_ISREG(st->st_mode))
return 1;
return 0;
}
int uwsgi_is_dir(char *filename) {
struct stat st;
if (stat(filename, &st)) {
return 0;
}
if (S_ISDIR(st.st_mode))
return 1;
return 0;
}
int uwsgi_is_link(char *filename) {
struct stat st;
if (lstat(filename, &st)) {
return 0;
}
if (S_ISLNK(st.st_mode))
return 1;
return 0;
}
void *uwsgi_malloc(size_t size) {
char *ptr = malloc(size);
if (ptr == NULL) {
uwsgi_error("malloc()");
uwsgi_log("!!! tried memory allocation of %llu bytes !!!\n", (unsigned long long) size);
uwsgi_backtrace(uwsgi.backtrace_depth);
exit(1);
}
return ptr;
}
void *uwsgi_calloc(size_t size) {
// thanks Mathieu Dupuy for pointing out that calloc is faster
// than malloc + memset
char *ptr = calloc(1, size);
if (ptr == NULL) {
uwsgi_error("calloc()");
uwsgi_log("!!! tried memory allocation of %llu bytes !!!\n", (unsigned long long) size);
uwsgi_backtrace(uwsgi.backtrace_depth);
exit(1);
}
return ptr;
}
char *uwsgi_resolve_ip(char *domain) {
struct hostent *he;
he = gethostbyname(domain);
if (!he || !*he->h_addr_list || (he->h_addrtype != AF_INET
#ifdef AF_INET6
&& he->h_addrtype != AF_INET6
#endif
)) {
return NULL;
}
return inet_ntoa(*(struct in_addr *) he->h_addr_list[0]);
}
int uwsgi_file_exists(char *filename) {
// TODO check for http url or stdin
return !access(filename, R_OK);
}
int uwsgi_file_executable(char *filename) {
// TODO check for http url or stdin
return !access(filename, R_OK | X_OK);
}
char *magic_sub(char *buffer, size_t len, size_t * size, char *magic_table[]) {
size_t i;
size_t magic_len = 0;
char *magic_buf = uwsgi_malloc(len);
char *magic_ptr = magic_buf;
char *old_magic_buf;
for (i = 0; i < len; i++) {
if (buffer[i] == '%' && (i + 1) < len && magic_table[(unsigned char) buffer[i + 1]]) {
old_magic_buf = magic_buf;
magic_buf = uwsgi_concat3n(old_magic_buf, magic_len, magic_table[(unsigned char) buffer[i + 1]], strlen(magic_table[(unsigned char) buffer[i + 1]]), buffer + i + 2, len - i - 2);
free(old_magic_buf);
magic_len += strlen(magic_table[(unsigned char) buffer[i + 1]]);
magic_ptr = magic_buf + magic_len;
i++;
}
else {
*magic_ptr = buffer[i];
magic_ptr++;
magic_len++;
}
}
*size = magic_len;
return magic_buf;
}
void init_magic_table(char *magic_table[]) {
int i;
for (i = 0; i <= 0xff; i++) {
magic_table[i] = "";
}
magic_table['%'] = "%";
magic_table['('] = "%(";
}
char *uwsgi_num2str(int num) {
char *str = uwsgi_malloc(11);
snprintf(str, 11, "%d", num);
return str;
}
char *uwsgi_64bit2str(int64_t num) {
char *str = uwsgi_malloc(sizeof(MAX64_STR) + 1);
snprintf(str, sizeof(MAX64_STR) + 1, "%lld", (long long) num);
return str;
}
int uwsgi_num2str2(int num, char *ptr) {
return snprintf(ptr, 11, "%d", num);
}
int uwsgi_num2str2n(int num, char *ptr, int size) {
return snprintf(ptr, size, "%d", num);
}
int uwsgi_long2str2n(unsigned long long num, char *ptr, int size) {
int ret = snprintf(ptr, size, "%llu", num);
if (ret <= 0 || ret > size)
return 0;
return ret;
}
int is_unix(char *socket_name, int len) {
return !memchr(socket_name, ':', len);
}
int is_a_number(char *what) {
int i;
for (i = 0; i < (int) strlen(what); i++) {
if (!isdigit((int) what[i]))
return 0;
}
return 1;
}
void uwsgi_unix_signal(int signum, void (*func) (int)) {
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = func;
sigemptyset(&sa.sa_mask);
if (sigaction(signum, &sa, NULL) < 0) {
uwsgi_error("sigaction()");
}
}
int uwsgi_list_has_num(char *list, int num) {
char *list2 = uwsgi_concat2(list, "");
char *p, *ctx = NULL;
uwsgi_foreach_token(list2, ",", p, ctx) {
if (atoi(p) == num) {
free(list2);
return 1;
}
}
free(list2);
return 0;
}
int uwsgi_list_has_str(char *list, char *str) {
char *list2 = uwsgi_str(list);
char *p, *ctx = NULL;
uwsgi_foreach_token(list2, " ", p, ctx) {
if (!strcasecmp(p, str)) {
free(list2);
return 1;
}
}
free(list2);
return 0;
}
static char hex2num(char *str) {
char val = 0;
val <<= 4;
if (str[0] >= '0' && str[0] <= '9') {
val += str[0] & 0x0F;
}
else if (str[0] >= 'A' && str[0] <= 'F') {
val += (str[0] & 0x0F) + 9;
}
else if (str[0] >= 'a' && str[0] <= 'f') {
val += (str[0] & 0x0F) + 9;
}
else {
return 0;
}
val <<= 4;
if (str[1] >= '0' && str[1] <= '9') {
val += str[1] & 0x0F;
}
else if (str[1] >= 'A' && str[1] <= 'F') {
val += (str[1] & 0x0F) + 9;
}
else if (str[1] >= 'a' && str[1] <= 'f') {
val += (str[1] & 0x0F) + 9;
}
else {
return 0;
}
return val;
}
int uwsgi_str2_num(char *str) {
int num = 0;
num = 10 * (str[0] - 48);
num += str[1] - 48;
return num;
}
int uwsgi_str3_num(char *str) {
int num = 0;
num = 100 * (str[0] - 48);
num += 10 * (str[1] - 48);
num += str[2] - 48;
return num;
}
int uwsgi_str4_num(char *str) {
int num = 0;
num = 1000 * (str[0] - 48);
num += 100 * (str[1] - 48);
num += 10 * (str[2] - 48);
num += str[3] - 48;
return num;
}
size_t uwsgi_str_num(char *str, int len) {
int i;
size_t num = 0;
uint64_t delta = pow(10, len);
for (i = 0; i < len; i++) {
delta = delta / 10;
num += delta * (str[i] - 48);
}
return num;
}
char *uwsgi_split3(char *buf, size_t len, char sep, char **part1, size_t * part1_len, char **part2, size_t * part2_len, char **part3, size_t * part3_len) {
size_t i;
int status = 0;
*part1 = NULL;
*part2 = NULL;
*part3 = NULL;
for (i = 0; i < len; i++) {
if (buf[i] == sep) {
// get part1
if (status == 0) {
*part1 = buf;
*part1_len = i;
status = 1;
}
// get part2
else if (status == 1) {
*part2 = *part1 + *part1_len + 1;
*part2_len = (buf + i) - *part2;
break;
}
}
}
if (*part1 && *part2) {
if (*part2 + *part2_len + 1 > buf + len) {
return NULL;
}
*part3 = *part2 + *part2_len + 1;
*part3_len = (buf + len) - *part3;
return buf + len;
}
return NULL;
}
char *uwsgi_split4(char *buf, size_t len, char sep, char **part1, size_t * part1_len, char **part2, size_t * part2_len, char **part3, size_t * part3_len, char **part4, size_t * part4_len) {
size_t i;
int status = 0;
*part1 = NULL;
*part2 = NULL;
*part3 = NULL;
*part4 = NULL;
for (i = 0; i < len; i++) {
if (buf[i] == sep) {
// get part1
if (status == 0) {
*part1 = buf;
*part1_len = i;
status = 1;
}
// get part2
else if (status == 1) {
*part2 = *part1 + *part1_len + 1;
*part2_len = (buf + i) - *part2;
status = 2;
}
// get part3
else if (status == 2) {
*part3 = *part2 + *part2_len + 1;
*part3_len = (buf + i) - *part3;
break;
}
}
}
if (*part1 && *part2 && *part3) {
if (*part3 + *part3_len + 1 > buf + len) {
return NULL;
}
*part4 = *part3 + *part3_len + 1;
*part4_len = (buf + len) - *part4;
return buf + len;
}
return NULL;
}
char *uwsgi_netstring(char *buf, size_t len, char **netstring, size_t * netstring_len) {
char *ptr = buf;
char *watermark = buf + len;
*netstring_len = 0;
while (ptr < watermark) {
// end of string size ?
if (*ptr == ':') {
*netstring_len = uwsgi_str_num(buf, ptr - buf);
if (ptr + *netstring_len + 2 > watermark) {
return NULL;
}
*netstring = ptr + 1;
return ptr + *netstring_len + 2;
}
ptr++;
}
return NULL;
}
struct uwsgi_dyn_dict *uwsgi_dyn_dict_new(struct uwsgi_dyn_dict **dd, char *key, int keylen, char *val, int vallen) {
struct uwsgi_dyn_dict *uwsgi_dd = *dd, *old_dd;
if (!uwsgi_dd) {
*dd = uwsgi_malloc(sizeof(struct uwsgi_dyn_dict));
uwsgi_dd = *dd;
uwsgi_dd->prev = NULL;
}
else {
while (uwsgi_dd) {
old_dd = uwsgi_dd;
uwsgi_dd = uwsgi_dd->next;
}
uwsgi_dd = uwsgi_malloc(sizeof(struct uwsgi_dyn_dict));
old_dd->next = uwsgi_dd;
uwsgi_dd->prev = old_dd;
}
uwsgi_dd->key = key;
uwsgi_dd->keylen = keylen;
uwsgi_dd->value = val;
uwsgi_dd->vallen = vallen;
uwsgi_dd->hits = 0;
uwsgi_dd->status = 0;
uwsgi_dd->next = NULL;
return uwsgi_dd;
}
void uwsgi_dyn_dict_del(struct uwsgi_dyn_dict *item) {
struct uwsgi_dyn_dict *prev = item->prev;
struct uwsgi_dyn_dict *next = item->next;
if (prev) {
prev->next = next;
}
if (next) {
next->prev = prev;
}
free(item);
}
void uwsgi_dyn_dict_free(struct uwsgi_dyn_dict **dd) {
struct uwsgi_dyn_dict *attr = *dd;
while(attr) {
struct uwsgi_dyn_dict *tmp = attr;
attr = attr->next;
if (tmp->value) free(tmp->value);
free(tmp);
}
*dd = NULL;
}
void *uwsgi_malloc_shared(size_t size) {
void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
if (addr == MAP_FAILED) {
uwsgi_log("unable to allocate %llu bytes (%lluMB)\n", (unsigned long long) size, (unsigned long long) (size / (1024 * 1024)));
uwsgi_error("mmap()");
exit(1);
}
return addr;
}
void *uwsgi_calloc_shared(size_t size) {
void *ptr = uwsgi_malloc_shared(size);
// NOTE by Mathieu Dupuy:
// OSes guarantee mmap MAP_ANON memory area to be zero-filled (see man pages)
// we should trust it, but history has taught us it is better to be paranoid.
// Lucky enough this function is called ony in startup phases, so performance
// tips/tricks are irrelevant (So, le'ts call memset...)
memset(ptr, 0, size);
return ptr;
}
struct uwsgi_string_list *uwsgi_string_new_list(struct uwsgi_string_list **list, char *value) {
struct uwsgi_string_list *uwsgi_string = *list, *old_uwsgi_string;
if (!uwsgi_string) {
*list = uwsgi_malloc(sizeof(struct uwsgi_string_list));
uwsgi_string = *list;
}
else {
while (uwsgi_string) {
old_uwsgi_string = uwsgi_string;
uwsgi_string = uwsgi_string->next;
}
uwsgi_string = uwsgi_malloc(sizeof(struct uwsgi_string_list));
old_uwsgi_string->next = uwsgi_string;
}
uwsgi_string->value = value;
uwsgi_string->len = 0;
if (value) {
uwsgi_string->len = strlen(value);
}
uwsgi_string->next = NULL;
uwsgi_string->custom = 0;
uwsgi_string->custom2 = 0;
uwsgi_string->custom_ptr = NULL;
return uwsgi_string;
}
#ifdef UWSGI_PCRE
struct uwsgi_regexp_list *uwsgi_regexp_custom_new_list(struct uwsgi_regexp_list **list, char *value, char *custom) {
struct uwsgi_regexp_list *url = *list, *old_url;
if (!url) {
*list = uwsgi_malloc(sizeof(struct uwsgi_regexp_list));
url = *list;
}
else {
while (url) {
old_url = url;
url = url->next;
}
url = uwsgi_malloc(sizeof(struct uwsgi_regexp_list));
old_url->next = url;
}
if (uwsgi_regexp_build(value, &url->pattern, &url->pattern_extra)) {
exit(1);
}
url->next = NULL;
url->custom = 0;
url->custom_ptr = NULL;
url->custom_str = custom;
return url;
}
#endif
char *uwsgi_string_get_list(struct uwsgi_string_list **list, int pos, size_t * len) {
struct uwsgi_string_list *uwsgi_string = *list;
int counter = 0;
while (uwsgi_string) {
if (counter == pos) {
*len = uwsgi_string->len;
return uwsgi_string->value;
}
uwsgi_string = uwsgi_string->next;
counter++;
}
*len = 0;
return NULL;
}
void uwsgi_string_del_list(struct uwsgi_string_list **list, struct uwsgi_string_list *item) {
struct uwsgi_string_list *uwsgi_string = *list, *old_uwsgi_string = NULL;
while (uwsgi_string) {
if (uwsgi_string == item) {
// parent instance ?
if (old_uwsgi_string == NULL) {
*list = uwsgi_string->next;
}
else {
old_uwsgi_string->next = uwsgi_string->next;
}
free(uwsgi_string);
return;
}
old_uwsgi_string = uwsgi_string;
uwsgi_string = uwsgi_string->next;
}
}
void uwsgi_sig_pause() {
sigset_t mask;
sigemptyset(&mask);
sigsuspend(&mask);
}
char *uwsgi_binsh() {
struct uwsgi_string_list *usl = NULL;
uwsgi_foreach(usl, uwsgi.binsh) {
if (uwsgi_file_executable(usl->value)) {
return usl->value;
}
}
return "/bin/sh";
}
void uwsgi_exec_command_with_args(char *cmdline) {
char *argv[4];
argv[0] = uwsgi_binsh();
argv[1] = "-c";
argv[2] = cmdline;
argv[3] = NULL;
execvp(argv[0], argv);
uwsgi_error("execvp()");
exit(1);
}
static int uwsgi_run_command_do(char *command, char *arg) {
char *argv[4];
#ifdef __linux__
if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0)) {
uwsgi_error("prctl()");
}
#endif
if (command == NULL) {
argv[0] = uwsgi_binsh();
argv[1] = "-c";
argv[2] = arg;
argv[3] = NULL;
execvp(argv[0], argv);
}
else {
argv[0] = command;
argv[1] = arg;
argv[2] = NULL;
execvp(command, argv);
}
uwsgi_error("execvp()");
//never here
exit(1);
}
int uwsgi_run_command_and_wait(char *command, char *arg) {
int waitpid_status = 0;
pid_t pid = fork();
if (pid < 0) {
return -1;
}
if (pid > 0) {
if (waitpid(pid, &waitpid_status, 0) < 0) {
uwsgi_error("uwsgi_run_command_and_wait()/waitpid()");
return -1;
}
return WEXITSTATUS(waitpid_status);
}
return uwsgi_run_command_do(command, arg);
}
int uwsgi_run_command_putenv_and_wait(char *command, char *arg, char **envs, unsigned int nenvs) {
int waitpid_status = 0;
pid_t pid = fork();
if (pid < 0) {
return -1;
}
if (pid > 0) {
if (waitpid(pid, &waitpid_status, 0) < 0) {
uwsgi_error("uwsgi_run_command_and_wait()/waitpid()");
return -1;
}
return WEXITSTATUS(waitpid_status);
}
unsigned int i;
for (i = 0; i < nenvs; i++) {
if (putenv(envs[i])) {
uwsgi_error("uwsgi_run_command_putenv_and_wait()/putenv()");
exit(1);
}
}
return uwsgi_run_command_do(command, arg);
}
pid_t uwsgi_run_command(char *command, int *stdin_fd, int stdout_fd) {
char *argv[4];
int waitpid_status = 0;
pid_t pid = fork();
if (pid < 0) {
return -1;
}
if (pid > 0) {
if (stdin_fd && stdin_fd[0] > -1) {
close(stdin_fd[0]);
}
if (stdout_fd > -1) {
close(stdout_fd);
}
if (waitpid(pid, &waitpid_status, WNOHANG) < 0) {
uwsgi_error("waitpid()");
return -1;
}
return pid;
}
uwsgi_close_all_sockets();
//uwsgi_close_all_fds();
int i;
for (i = 3; i < (int) uwsgi.max_fd; i++) {
if (stdin_fd) {
if (i == stdin_fd[0] || i == stdin_fd[1]) {
continue;
}
}
if (stdout_fd > -1) {
if (i == stdout_fd) {
continue;
}
}
#ifdef __APPLE__
fcntl(i, F_SETFD, FD_CLOEXEC);
#else
close(i);
#endif
}
if (stdin_fd) {
close(stdin_fd[1]);
}
else {
if (!uwsgi_valid_fd(0)) {
int in_fd = open("/dev/null", O_RDONLY);
if (in_fd < 0) {
uwsgi_error_open("/dev/null");
}
else {
if (in_fd != 0) {
if (dup2(in_fd, 0) < 0) {
uwsgi_error("dup2()");
}
}
}
}
}
if (stdout_fd > -1 && stdout_fd != 1) {
if (dup2(stdout_fd, 1) < 0) {
uwsgi_error("dup2()");
exit(1);
}
}
if (stdin_fd && stdin_fd[0] > -1 && stdin_fd[0] != 0) {
if (dup2(stdin_fd[0], 0) < 0) {
uwsgi_error("dup2()");
exit(1);
}
}
if (setsid() < 0) {
uwsgi_error("setsid()");
exit(1);
}
argv[0] = uwsgi_binsh();
argv[1] = "-c";
argv[2] = command;
argv[3] = NULL;
execvp(uwsgi_binsh(), argv);
uwsgi_error("execvp()");
//never here
exit(1);
}
int uwsgi_endswith(char *str1, char *str2) {
size_t i;
size_t str1len = strlen(str1);
size_t str2len = strlen(str2);
char *ptr;
if (str2len > str1len)
return 0;
ptr = (str1 + str1len) - str2len;
for (i = 0; i < str2len; i++) {
if (*ptr != str2[i])
return 0;
ptr++;
}
return 1;
}
void uwsgi_chown(char *filename, char *owner) {
uid_t new_uid = -1;
uid_t new_gid = -1;
struct group *new_group = NULL;
struct passwd *new_user = NULL;
char *colon = strchr(owner, ':');
if (colon) {
colon[0] = 0;
}
if (is_a_number(owner)) {
new_uid = atoi(owner);
}
else {
new_user = getpwnam(owner);
if (!new_user) {
uwsgi_log("unable to find user %s\n", owner);
exit(1);
}
new_uid = new_user->pw_uid;
}
if (colon) {
colon[0] = ':';
if (is_a_number(colon + 1)) {
new_gid = atoi(colon + 1);
}
else {
new_group = getgrnam(colon + 1);
if (!new_group) {
uwsgi_log("unable to find group %s\n", colon + 1);
exit(1);
}
new_gid = new_group->gr_gid;
}
}
if (chown(filename, new_uid, new_gid)) {
uwsgi_error("chown()");
exit(1);
}
}
char *uwsgi_get_binary_path(char *argvzero) {
#if defined(__linux__) || defined(__CYGWIN__)
char *buf = uwsgi_calloc(PATH_MAX + 1);
ssize_t len = readlink("/proc/self/exe", buf, PATH_MAX);
if (len > 0) {
return buf;
}
free(buf);
#elif defined(_WIN32)
char *buf = uwsgi_calloc(PATH_MAX + 1);
if (GetModuleFileName(NULL, buf, PATH_MAX) > 0) {
return buf;
}
free(buf);
#elif defined(__NetBSD__)
char *buf = uwsgi_calloc(PATH_MAX + 1);
ssize_t len = readlink("/proc/curproc/exe", buf, PATH_MAX);
if (len > 0) {
return buf;
}
if (realpath(argvzero, buf)) {
return buf;
}
free(buf);
#elif defined(__APPLE__)
char *buf = uwsgi_malloc(uwsgi.page_size);
uint32_t len = uwsgi.page_size;
if (_NSGetExecutablePath(buf, &len) == 0) {
// return only absolute path
#ifndef OLD_REALPATH
char *newbuf = realpath(buf, NULL);
if (newbuf) {
free(buf);
return newbuf;
}
#endif
}
free(buf);
#elif defined(__sun__)
// do not free this value !!!
char *buf = (char *) getexecname();
if (buf) {
// return only absolute path
if (buf[0] == '/') {
return buf;
}
char *newbuf = uwsgi_malloc(PATH_MAX + 1);
if (realpath(buf, newbuf)) {
return newbuf;
}
}
#elif defined(__FreeBSD__) || defined(__GNU_kFreeBSD__)
char *buf = uwsgi_malloc(uwsgi.page_size);
size_t len = uwsgi.page_size;
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
if (sysctl(mib, 4, buf, &len, NULL, 0) == 0) {
return buf;
}
free(buf);
#endif
return argvzero;
}
char *uwsgi_get_line(char *ptr, char *watermark, int *size) {
char *p = ptr;
int count = 0;
while (p < watermark) {
if (*p == '\n') {
*size = count;
return ptr + count;
}
count++;
p++;
}
return NULL;
}
void uwsgi_build_mime_dict(char *filename) {
size_t size = 0;
char *buf = uwsgi_open_and_read(filename, &size, 1, NULL);
char *watermark = buf + size;
int linesize = 0;
char *line = buf;
int i;
int type_size = 0;
int ext_start = 0;
int found;
int entries = 0;
uwsgi_log("building mime-types dictionary from file %s...", filename);
while (uwsgi_get_line(line, watermark, &linesize) != NULL) {
found = 0;
if (isalnum((int) line[0])) {
// get the type size
for (i = 0; i < linesize; i++) {
if (isblank((int) line[i])) {
type_size = i;
found = 1;
break;
}
}
if (!found) {
line += linesize + 1;
continue;
}
found = 0;
for (i = type_size; i < linesize; i++) {
if (!isblank((int) line[i])) {
ext_start = i;
found = 1;
break;
}
}
if (!found) {
line += linesize + 1;
continue;
}
char *current = line + ext_start;
int ext_size = 0;
for (i = ext_start; i < linesize; i++) {
if (isblank((int) line[i])) {
#ifdef UWSGI_DEBUG
uwsgi_log("%.*s %.*s\n", ext_size, current, type_size, line);
#endif
uwsgi_dyn_dict_new(&uwsgi.mimetypes, current, ext_size, line, type_size);
entries++;
ext_size = 0;
current = NULL;
continue;
}
else if (current == NULL) {
current = line + i;
}
ext_size++;
}
if (current && ext_size > 1) {
#ifdef UWSGI_DEBUG
uwsgi_log("%.*s %.*s\n", ext_size, current, type_size, line);
#endif
uwsgi_dyn_dict_new(&uwsgi.mimetypes, current, ext_size, line, type_size);
entries++;
}
}
line += linesize + 1;
}
uwsgi_log("%d entry found\n", entries);
}
#ifdef __linux__
struct uwsgi_unshare_id {
char *name;
int value;
};
static struct uwsgi_unshare_id uwsgi_unshare_list[] = {
#ifdef CLONE_FILES
{"files", CLONE_FILES},
#endif
#ifdef CLONE_NEWIPC
{"ipc", CLONE_NEWIPC},
#endif
#ifdef CLONE_NEWNET
{"net", CLONE_NEWNET},
#endif
#ifdef CLONE_IO
{"io", CLONE_IO},
#endif
#ifdef CLONE_PARENT
{"parent", CLONE_PARENT},
#endif
#ifdef CLONE_NEWPID
{"pid", CLONE_NEWPID},
#endif
#ifdef CLONE_NEWNS
{"ns", CLONE_NEWNS},
{"fs", CLONE_NEWNS},
{"mount", CLONE_NEWNS},
{"mnt", CLONE_NEWNS},
#endif
#ifdef CLONE_SYSVSEM
{"sysvsem", CLONE_SYSVSEM},
#endif
#ifdef CLONE_NEWUTS
{"uts", CLONE_NEWUTS},
#endif
#ifdef CLONE_NEWUSER
{"user", CLONE_NEWUSER},
#endif
{NULL, -1}
};
static int uwsgi_get_unshare_id(char *name) {
struct uwsgi_unshare_id *uui = uwsgi_unshare_list;
while (uui->name) {
if (!strcmp(uui->name, name))
return uui->value;
uui++;
}
return -1;
}
void uwsgi_build_unshare(char *what, int *mask) {
char *list = uwsgi_str(what);
char *p, *ctx = NULL;
uwsgi_foreach_token(list, ",", p, ctx) {
int u_id = uwsgi_get_unshare_id(p);
if (u_id != -1) {
*mask |= u_id;
}
else {
uwsgi_log("unknown namespace subsystem: %s\n", p);
exit(1);
}
}
free(list);
}
#endif
#ifdef UWSGI_CAP
struct uwsgi_cap {
char *name;
cap_value_t value;
};
static struct uwsgi_cap uwsgi_cap_list[] = {
{"chown", CAP_CHOWN},
{"dac_override", CAP_DAC_OVERRIDE},
{"dac_read_search", CAP_DAC_READ_SEARCH},
{"fowner", CAP_FOWNER},
{"fsetid", CAP_FSETID},
{"kill", CAP_KILL},
{"setgid", CAP_SETGID},
{"setuid", CAP_SETUID},
{"setpcap", CAP_SETPCAP},
{"linux_immutable", CAP_LINUX_IMMUTABLE},
{"net_bind_service", CAP_NET_BIND_SERVICE},
{"net_broadcast", CAP_NET_BROADCAST},
{"net_admin", CAP_NET_ADMIN},
{"net_raw", CAP_NET_RAW},
{"ipc_lock", CAP_IPC_LOCK},
{"ipc_owner", CAP_IPC_OWNER},
{"sys_module", CAP_SYS_MODULE},
{"sys_rawio", CAP_SYS_RAWIO},
{"sys_chroot", CAP_SYS_CHROOT},
{"sys_ptrace", CAP_SYS_PTRACE},
{"sys_pacct", CAP_SYS_PACCT},
{"sys_admin", CAP_SYS_ADMIN},
{"sys_boot", CAP_SYS_BOOT},
{"sys_nice", CAP_SYS_NICE},
{"sys_resource", CAP_SYS_RESOURCE},
{"sys_time", CAP_SYS_TIME},
{"sys_tty_config", CAP_SYS_TTY_CONFIG},
{"mknod", CAP_MKNOD},
#ifdef CAP_LEASE
{"lease", CAP_LEASE},
#endif
#ifdef CAP_AUDIT_WRITE
{"audit_write", CAP_AUDIT_WRITE},
#endif
#ifdef CAP_AUDIT_CONTROL
{"audit_control", CAP_AUDIT_CONTROL},
#endif
#ifdef CAP_SETFCAP
{"setfcap", CAP_SETFCAP},
#endif
#ifdef CAP_MAC_OVERRIDE
{"mac_override", CAP_MAC_OVERRIDE},
#endif
#ifdef CAP_MAC_ADMIN
{"mac_admin", CAP_MAC_ADMIN},
#endif
#ifdef CAP_SYSLOG
{"syslog", CAP_SYSLOG},
#endif
#ifdef CAP_WAKE_ALARM
{"wake_alarm", CAP_WAKE_ALARM},
#endif
{NULL, -1}
};
static int uwsgi_get_cap_id(char *name) {
struct uwsgi_cap *ucl = uwsgi_cap_list;
while (ucl->name) {
if (!strcmp(ucl->name, name))
return ucl->value;
ucl++;
}
return -1;
}
int uwsgi_build_cap(char *what, cap_value_t ** cap) {
int cap_id;
char *caps = uwsgi_str(what);
int pos = 0;
int count = 0;
char *p, *ctx = NULL;
uwsgi_foreach_token(caps, ",", p, ctx) {
if (is_a_number(p)) {
count++;
}
else {
cap_id = uwsgi_get_cap_id(p);
if (cap_id != -1) {
count++;
}
else {
uwsgi_log("[security] unknown capability: %s\n", p);
}
}
}
free(caps);
*cap = uwsgi_malloc(sizeof(cap_value_t) * count);
caps = uwsgi_str(what);
ctx = NULL;
uwsgi_foreach_token(caps, ",", p, ctx) {
if (is_a_number(p)) {
cap_id = atoi(p);
}
else {
cap_id = uwsgi_get_cap_id(p);
}
if (cap_id != -1) {
(*cap)[pos] = cap_id;
uwsgi_log("setting capability %s [%d]\n", p, cap_id);
pos++;
}
else {
uwsgi_log("[security] unknown capability: %s\n", p);
}
}
free(caps);
return count;
}
#endif
void uwsgi_apply_config_pass(char symbol, char *(*hook) (char *)) {
int i, j;
for (i = 0; i < uwsgi.exported_opts_cnt; i++) {
int has_symbol = 0;
int depth = 0;
char *magic_key = NULL;
char *magic_val = NULL;
if (uwsgi.exported_opts[i]->value && !uwsgi.exported_opts[i]->configured) {
for (j = 0; j < (int) strlen(uwsgi.exported_opts[i]->value); j++) {
if (uwsgi.exported_opts[i]->value[j] == symbol) {
has_symbol = 1;
}
else if (uwsgi.exported_opts[i]->value[j] == '(' && has_symbol == 1) {
has_symbol = 2;
depth = 0;
magic_key = uwsgi.exported_opts[i]->value + j + 1;
}
else if (has_symbol > 1) {
if (uwsgi.exported_opts[i]->value[j] == '(') {
has_symbol++;
depth++;
}
else if (uwsgi.exported_opts[i]->value[j] == ')') {
if (depth > 0) {
has_symbol++;
depth--;
continue;
}
if (has_symbol <= 2) {
magic_key = NULL;
has_symbol = 0;
continue;
}
#ifdef UWSGI_DEBUG
uwsgi_log("need to interpret the %.*s tag\n", has_symbol - 2, magic_key);
#endif
char *tmp_magic_key = uwsgi_concat2n(magic_key, has_symbol - 2, "", 0);
magic_val = hook(tmp_magic_key);
free(tmp_magic_key);
if (!magic_val) {
magic_key = NULL;
has_symbol = 0;
continue;
}
uwsgi.exported_opts[i]->value = uwsgi_concat4n(uwsgi.exported_opts[i]->value, (magic_key - 2) - uwsgi.exported_opts[i]->value, magic_val, strlen(magic_val), magic_key + (has_symbol - 1), strlen(magic_key + (has_symbol - 1)), "", 0);
#ifdef UWSGI_DEBUG
uwsgi_log("computed new value = %s\n", uwsgi.exported_opts[i]->value);
#endif
magic_key = NULL;
has_symbol = 0;
j = 0;
}
else {
has_symbol++;
}
}
else {
has_symbol = 0;
}
}
}
}
}
void uwsgi_set_processname(char *name) {
#if defined(__linux__) || defined(__sun__)
size_t amount = 0;
// prepare for strncat
*uwsgi.orig_argv[0] = 0;
if (uwsgi.procname_prefix) {
amount += strlen(uwsgi.procname_prefix);
if ((int) amount > uwsgi.max_procname - 1)
return;
strncat(uwsgi.orig_argv[0], uwsgi.procname_prefix, uwsgi.max_procname - (amount + 1));
}
amount += strlen(name);
if ((int) amount > uwsgi.max_procname - 1)
return;
strncat(uwsgi.orig_argv[0], name, (uwsgi.max_procname - amount + 1));
if (uwsgi.procname_append) {
amount += strlen(uwsgi.procname_append);
if ((int) amount > uwsgi.max_procname - 1)
return;
strncat(uwsgi.orig_argv[0], uwsgi.procname_append, uwsgi.max_procname - (amount + 1));
}
// fill with spaces...
memset(uwsgi.orig_argv[0] + amount + 1, ' ', uwsgi.max_procname - (amount));
// end with \0
memset(uwsgi.orig_argv[0] + amount + 1 + (uwsgi.max_procname - (amount)), '\0', 1);
#elif defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) || defined(__NetBSD__)
if (uwsgi.procname_prefix) {
if (!uwsgi.procname_append) {
setproctitle("-%s%s", uwsgi.procname_prefix, name);
}
else {
setproctitle("-%s%s%s", uwsgi.procname_prefix, name, uwsgi.procname_append);
}
}
else if (uwsgi.procname_append) {
if (!uwsgi.procname_prefix) {
setproctitle("-%s%s", name, uwsgi.procname_append);
}
else {
setproctitle("-%s%s%s", uwsgi.procname_prefix, name, uwsgi.procname_append);
}
}
else {
setproctitle("-%s", name);
}
#endif
}
// this is a wrapper for fork restoring original argv
pid_t uwsgi_fork(char *name) {
pid_t pid = fork();
if (pid == 0) {
#ifndef __CYGWIN__
if (uwsgi.never_swap) {
if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
uwsgi_error("mlockall()");
}
}
#endif
#if defined(__linux__) || defined(__sun__)
int i;
for (i = 0; i < uwsgi.argc; i++) {
// stop fixing original argv if the new one is bigger
if (!uwsgi.orig_argv[i]) break;
strcpy(uwsgi.orig_argv[i], uwsgi.argv[i]);
}
#endif
if (uwsgi.auto_procname && name) {
if (uwsgi.procname) {
uwsgi_set_processname(uwsgi.procname);
}
else {
uwsgi_set_processname(name);
}
}
}
return pid;
}
void escape_shell_arg(char *src, size_t len, char *dst) {
size_t i;
char *ptr = dst;
for (i = 0; i < len; i++) {
if (strchr("&;`'\"|*?~<>^()[]{}$\\\n", src[i])) {
*ptr++ = '\\';
}
*ptr++ = src[i];
}
*ptr++ = 0;
}
void escape_json(char *src, size_t len, char *dst) {
size_t i;
char *ptr = dst;
for (i = 0; i < len; i++) {
if (src[i] == '\t') {
*ptr++ = '\\';
*ptr++ = 't';
}
else if (src[i] == '\n') {
*ptr++ = '\\';
*ptr++ = 'n';
}
else if (src[i] == '\r') {
*ptr++ = '\\';
*ptr++ = 'r';
}
else if (src[i] == '"') {
*ptr++ = '\\';
*ptr++ = '"';
}
else if (src[i] == '\\') {
*ptr++ = '\\';
*ptr++ = '\\';
}
else {
*ptr++ = src[i];
}
}
*ptr++ = 0;
}
/*
build PATH_INFO from raw_uri
it manages:
percent encoding
dot_segments removal
stop at the first #
*/
void http_url_decode(char *buf, uint16_t * len, char *dst) {
enum {
zero = 0,
percent1,
percent2,
slash,
dot,
dotdot
} status;
uint16_t i, current_new_len, new_len = 0;
char value[2];
char *ptr = dst;
value[0] = '0';
value[1] = '0';
status = zero;
int no_slash = 0;
if (*len > 0 && buf[0] != '/') {
status = slash;
no_slash = 1;
}
for (i = 0; i < *len; i++) {
char c = buf[i];
if (c == '#')
break;
switch (status) {
case zero:
if (c == '%') {
status = percent1;
break;
}
if (c == '/') {
status = slash;
break;
}
*ptr++ = c;
new_len++;
break;
case percent1:
if (c == '%') {
*ptr++ = '%';
new_len++;
status = zero;
break;
}
value[0] = c;
status = percent2;
break;
case percent2:
value[1] = c;
*ptr++ = hex2num(value);
new_len++;
status = zero;
break;
case slash:
if (c == '.') {
status = dot;
break;
}
// we could be at the first round (in non slash)
if (i > 0 || !no_slash) {
*ptr++ = '/';
new_len++;
}
if (c == '%') {
status = percent1;
break;
}
if (c == '/') {
status = slash;
break;
}
*ptr++ = c;
new_len++;
status = zero;
break;
case dot:
if (c == '.') {
status = dotdot;
break;
}
if (c == '/') {
status = slash;
break;
}
if (i > 1) {
*ptr++ = '/';
new_len++;
}
*ptr++ = '.';
new_len++;
if (c == '%') {
status = percent1;
break;
}
*ptr++ = c;
new_len++;
status = zero;
break;
case dotdot:
// here we need to remove a segment
if (c == '/') {
current_new_len = new_len;
while (current_new_len) {
current_new_len--;
ptr--;
if (dst[current_new_len] == '/') {
break;
}
}
new_len = current_new_len;
status = slash;
break;
}
if (i > 2) {
*ptr++ = '/';
new_len++;
}
*ptr++ = '.';
new_len++;
*ptr++ = '.';
new_len++;
if (c == '%') {
status = percent1;
break;
}
*ptr++ = c;
new_len++;
status = zero;
break;
// over engineering
default:
*ptr++ = c;
new_len++;
break;
}
}
switch (status) {
case slash:
case dot:
*ptr++ = '/';
new_len++;
break;
case dotdot:
current_new_len = new_len;
while (current_new_len) {
if (dst[current_new_len - 1] == '/') {
break;
}
current_new_len--;
}
new_len = current_new_len;
break;
default:
break;
}
*len = new_len;
}
/*
we scan the table in reverse, as updated values are at the end
*/
char *uwsgi_get_var(struct wsgi_request *wsgi_req, char *key, uint16_t keylen, uint16_t * len) {
int i;
for (i = wsgi_req->var_cnt - 1; i > 0; i -= 2) {
if (!uwsgi_strncmp(key, keylen, wsgi_req->hvec[i - 1].iov_base, wsgi_req->hvec[i - 1].iov_len)) {
*len = wsgi_req->hvec[i].iov_len;
return wsgi_req->hvec[i].iov_base;
}
}
return NULL;
}
struct uwsgi_app *uwsgi_add_app(int id, uint8_t modifier1, char *mountpoint, int mountpoint_len, void *interpreter, void *callable) {
if (id > uwsgi.max_apps) {
uwsgi_log("FATAL ERROR: you cannot load more than %d apps in a worker\n", uwsgi.max_apps);
exit(1);
}
struct uwsgi_app *wi = &uwsgi_apps[id];
memset(wi, 0, sizeof(struct uwsgi_app));
wi->modifier1 = modifier1;
wi->mountpoint_len = mountpoint_len < 0xff ? mountpoint_len : (0xff - 1);
strncpy(wi->mountpoint, mountpoint, wi->mountpoint_len);
wi->interpreter = interpreter;
wi->callable = callable;
uwsgi_apps_cnt++;
// check if we need to emulate fork() COW
int i;
if (uwsgi.mywid == 0) {
for (i = 1; i <= uwsgi.numproc; i++) {
memcpy(&uwsgi.workers[i].apps[id], &uwsgi.workers[0].apps[id], sizeof(struct uwsgi_app));
uwsgi.workers[i].apps_cnt = uwsgi_apps_cnt;
}
}
if (!uwsgi.no_default_app) {
if ((mountpoint_len == 0 || (mountpoint_len == 1 && mountpoint[0] == '/')) && uwsgi.default_app == -1) {
uwsgi.default_app = id;
}
}
return wi;
}
char *uwsgi_check_touches(struct uwsgi_string_list *touch_list) {
// touch->value - file path
// touch->custom - file timestamp
// touch->custom2 - 0 if file exists, 1 if it does not exists
struct uwsgi_string_list *touch = touch_list;
while (touch) {
struct stat tr_st;
if (stat(touch->value, &tr_st)) {
if (touch->custom && !touch->custom2) {
#ifdef UWSGI_DEBUG
uwsgi_log("[uwsgi-check-touches] File %s was removed\n", touch->value);
#endif
touch->custom2 = 1;
return touch->custom_ptr ? touch->custom_ptr : touch->value;
}
else if (!touch->custom && !touch->custom2) {
uwsgi_log("unable to stat() %s, events will be triggered as soon as the file is created\n", touch->value);
touch->custom2 = 1;
}
touch->custom = 0;
}
else {
if (!touch->custom && touch->custom2) {
#ifdef UWSGI_DEBUG
uwsgi_log("[uwsgi-check-touches] File was created: %s\n", touch->value);
#endif
touch->custom = (uint64_t) tr_st.st_mtime;
touch->custom2 = 0;
return touch->custom_ptr ? touch->custom_ptr : touch->value;
}
else if (touch->custom && (uint64_t) tr_st.st_mtime > touch->custom) {
#ifdef UWSGI_DEBUG
uwsgi_log("[uwsgi-check-touches] modification detected on %s: %llu -> %llu\n", touch->value, (unsigned long long) touch->custom, (unsigned long long) tr_st.st_mtime);
#endif
touch->custom = (uint64_t) tr_st.st_mtime;
return touch->custom_ptr ? touch->custom_ptr : touch->value;
}
touch->custom = (uint64_t) tr_st.st_mtime;
}
touch = touch->next;
}
return NULL;
}
char *uwsgi_chomp(char *str) {
ssize_t slen = (ssize_t) strlen(str), i;
if (!slen)
return str;
slen--;
for (i = slen; i >= 0; i--) {
if (str[i] == '\r' || str[i] == '\n') {
str[i] = 0;
}
else {
return str;
}
}
return str;
}
char *uwsgi_chomp2(char *str) {
ssize_t slen = (ssize_t) strlen(str), i;
if (!slen)
return str;
slen--;
for (i = slen; i >= 0; i--) {
if (str[i] == '\r' || str[i] == '\n' || str[i] == '\t' || str[i] == ' ') {
str[i] = 0;
}
else {
return str;
}
}
return str;
}
int uwsgi_tmpfd() {
int fd = -1;
char *tmpdir = getenv("TMPDIR");
if (!tmpdir) {
tmpdir = "/tmp";
}
#ifdef O_TMPFILE
fd = open(tmpdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
if (fd >= 0) {
return fd;
}
// fallback to old style
#endif
char *template = uwsgi_concat2(tmpdir, "/uwsgiXXXXXX");
fd = mkstemp(template);
unlink(template);
free(template);
return fd;
}
FILE *uwsgi_tmpfile() {
int fd = uwsgi_tmpfd();
if (fd < 0)
return NULL;
return fdopen(fd, "w+");
}
int uwsgi_file_to_string_list(char *filename, struct uwsgi_string_list **list) {
char line[1024];
FILE *fh = fopen(filename, "r");
if (fh) {
while (fgets(line, 1024, fh)) {
uwsgi_string_new_list(list, uwsgi_chomp(uwsgi_str(line)));
}
fclose(fh);
return 1;
}
uwsgi_error_open(filename);
return 0;
}
void uwsgi_setup_post_buffering() {
if (!uwsgi.post_buffering_bufsize)
uwsgi.post_buffering_bufsize = 8192;
if (uwsgi.post_buffering_bufsize < uwsgi.post_buffering) {
uwsgi.post_buffering_bufsize = uwsgi.post_buffering;
uwsgi_log("setting request body buffering size to %lu bytes\n", (unsigned long) uwsgi.post_buffering_bufsize);
}
}
void uwsgi_emulate_cow_for_apps(int id) {
int i;
// check if we need to emulate fork() COW
if (uwsgi.mywid == 0) {
for (i = 1; i <= uwsgi.numproc; i++) {
memcpy(&uwsgi.workers[i].apps[id], &uwsgi.workers[0].apps[id], sizeof(struct uwsgi_app));
uwsgi.workers[i].apps_cnt = uwsgi_apps_cnt;
}
}
}
int uwsgi_write_intfile(char *filename, int n) {
FILE *pidfile = fopen(filename, "w");
if (!pidfile) {
uwsgi_error_open(filename);
exit(1);
}
if (fprintf(pidfile, "%d\n", n) <= 0 || ferror(pidfile)) {
fclose(pidfile);
return -1;
}
if (fclose(pidfile)) {
return -1;
}
return 0;
}
void uwsgi_write_pidfile(char *pidfile_name) {
uwsgi_log("writing pidfile to %s\n", pidfile_name);
if (uwsgi_write_intfile(pidfile_name, (int) getpid())) {
uwsgi_log("could not write pidfile.\n");
}
}
void uwsgi_write_pidfile_explicit(char *pidfile_name, pid_t pid) {
uwsgi_log("writing pidfile to %s\n", pidfile_name);
if (uwsgi_write_intfile(pidfile_name, (int) pid)) {
uwsgi_log("could not write pidfile.\n");
}
}
char *uwsgi_expand_path(char *dir, int dir_len, char *ptr) {
char src[PATH_MAX + 1];
memcpy(src, dir, dir_len);
src[dir_len] = 0;
char *dst = ptr;
if (!dst)
dst = uwsgi_malloc(PATH_MAX + 1);
if (!realpath(src, dst)) {
uwsgi_error_realpath(src);
if (!ptr)
free(dst);
return NULL;
}
return dst;
}
void uwsgi_set_cpu_affinity() {
char buf[4096];
int ret;
int pos = 0;
if (uwsgi.cpu_affinity) {
int base_cpu = (uwsgi.mywid - 1) * uwsgi.cpu_affinity;
if (base_cpu >= uwsgi.cpus) {
base_cpu = base_cpu % uwsgi.cpus;
}
ret = snprintf(buf, 4096, "mapping worker %d to CPUs:", uwsgi.mywid);
if (ret < 25 || ret >= 4096) {
uwsgi_log("unable to initialize cpu affinity !!!\n");
exit(1);
}
pos += ret;
#if defined(__linux__) || defined(__GNU_kFreeBSD__)
cpu_set_t cpuset;
#elif defined(__FreeBSD__)
cpuset_t cpuset;
#endif
#if defined(__linux__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__)
CPU_ZERO(&cpuset);
int i;
for (i = 0; i < uwsgi.cpu_affinity; i++) {
if (base_cpu >= uwsgi.cpus)
base_cpu = 0;
CPU_SET(base_cpu, &cpuset);
ret = snprintf(buf + pos, 4096 - pos, " %d", base_cpu);
if (ret < 2 || ret >= 4096) {
uwsgi_log("unable to initialize cpu affinity !!!\n");
exit(1);
}
pos += ret;
base_cpu++;
}
#endif
#if defined(__linux__) || defined(__GNU_kFreeBSD__)
if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset)) {
uwsgi_error("sched_setaffinity()");
}
#elif defined(__FreeBSD__)
if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cpuset), &cpuset)) {
uwsgi_error("cpuset_setaffinity");
}
#endif
uwsgi_log("%s\n", buf);
}
}
#ifdef UWSGI_ELF
#if defined(__linux__)
#include <elf.h>
#endif
char *uwsgi_elf_section(char *filename, char *s, size_t * len) {
struct stat st;
char *output = NULL;
int fd = open(filename, O_RDONLY);
if (fd < 0) {
uwsgi_error_open(filename);
return NULL;
}
if (fstat(fd, &st)) {
uwsgi_error("stat()");
close(fd);
return NULL;
}
if (st.st_size < EI_NIDENT) {
uwsgi_log("invalid elf file: %s\n", filename);
close(fd);
return NULL;
}
char *addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) {
uwsgi_error("mmap()");
close(fd);
return NULL;
}
if (addr[0] != ELFMAG0)
goto clear;
if (addr[1] != ELFMAG1)
goto clear;
if (addr[2] != ELFMAG2)
goto clear;
if (addr[3] != ELFMAG3)
goto clear;
if (addr[4] == ELFCLASS32) {
// elf header
Elf32_Ehdr *elfh = (Elf32_Ehdr *) addr;
// first section
Elf32_Shdr *sections = ((Elf32_Shdr *) (addr + elfh->e_shoff));
// number of sections
int ns = elfh->e_shnum;
// the names table
Elf32_Shdr *table = &sections[elfh->e_shstrndx];
// string table session pointer
char *names = addr + table->sh_offset;
Elf32_Shdr *ss = NULL;
int i;
for (i = 0; i < ns; i++) {
char *name = names + sections[i].sh_name;
if (!strcmp(name, s)) {
ss = &sections[i];
break;
}
}
if (ss) {
*len = ss->sh_size;
output = uwsgi_concat2n(addr + ss->sh_offset, ss->sh_size, "", 0);
}
}
else if (addr[4] == ELFCLASS64) {
// elf header
Elf64_Ehdr *elfh = (Elf64_Ehdr *) addr;
// first section
Elf64_Shdr *sections = ((Elf64_Shdr *) (addr + elfh->e_shoff));
// number of sections
int ns = elfh->e_shnum;
// the names table
Elf64_Shdr *table = &sections[elfh->e_shstrndx];
// string table session pointer
char *names = addr + table->sh_offset;
Elf64_Shdr *ss = NULL;
int i;
for (i = 0; i < ns; i++) {
char *name = names + sections[i].sh_name;
if (!strcmp(name, s)) {
ss = &sections[i];
break;
}
}
if (ss) {
*len = ss->sh_size;
output = uwsgi_concat2n(addr + ss->sh_offset, ss->sh_size, "", 0);
}
}
clear:
close(fd);
munmap(addr, st.st_size);
return output;
}
#endif
static void *uwsgi_thread_run(void *arg) {
struct uwsgi_thread *ut = (struct uwsgi_thread *) arg;
// block all signals
sigset_t smask;
sigfillset(&smask);
pthread_sigmask(SIG_BLOCK, &smask, NULL);
ut->queue = event_queue_init();
event_queue_add_fd_read(ut->queue, ut->pipe[1]);
ut->func(ut);
return NULL;
}
struct uwsgi_thread *uwsgi_thread_new_with_data(void (*func) (struct uwsgi_thread *), void *data) {
struct uwsgi_thread *ut = uwsgi_calloc(sizeof(struct uwsgi_thread));
#if defined(SOCK_SEQPACKET) && defined(__linux__)
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, ut->pipe)) {
#else
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, ut->pipe)) {
#endif
free(ut);
return NULL;
}
uwsgi_socket_nb(ut->pipe[0]);
uwsgi_socket_nb(ut->pipe[1]);
ut->func = func;
ut->data = data;
pthread_attr_init(&ut->tattr);
pthread_attr_setdetachstate(&ut->tattr, PTHREAD_CREATE_DETACHED);
// 512K should be enough...
pthread_attr_setstacksize(&ut->tattr, 512 * 1024);
if (pthread_create(&ut->tid, &ut->tattr, uwsgi_thread_run, ut)) {
uwsgi_error("pthread_create()");
goto error;
}
return ut;
error:
close(ut->pipe[0]);
close(ut->pipe[1]);
free(ut);
return NULL;
}
struct uwsgi_thread *uwsgi_thread_new(void (*func) (struct uwsgi_thread *)) {
return uwsgi_thread_new_with_data(func, NULL);
}
int uwsgi_kvlist_parse(char *src, size_t len, char list_separator, char kv_separator, ...) {
size_t i;
va_list ap;
struct uwsgi_string_list *itemlist = NULL;
char *buf = uwsgi_calloc(len + 1);
// ok let's start splitting the string
int escaped = 0;
char *base = buf;
char *ptr = buf;
for (i = 0; i < len; i++) {
if (src[i] == list_separator && !escaped) {
*ptr++ = 0;
uwsgi_string_new_list(&itemlist, base);
base = ptr;
}
else if (src[i] == '\\' && !escaped) {
escaped = 1;
}
else if (escaped) {
*ptr++ = src[i];
escaped = 0;
}
else {
*ptr++ = src[i];
}
}
if (ptr > base) {
uwsgi_string_new_list(&itemlist, base);
}
struct uwsgi_string_list *usl = itemlist;
while (usl) {
len = strlen(usl->value);
char *item_buf = uwsgi_calloc(len + 1);
base = item_buf;
ptr = item_buf;
escaped = 0;
for (i = 0; i < len; i++) {
if (usl->value[i] == kv_separator && !escaped) {
*ptr++ = 0;
va_start(ap, kv_separator);
for (;;) {
char *p = va_arg(ap, char *);
if (!p)
break;
char **pp = va_arg(ap, char **);
if (!pp)
break;
if (!strcmp(p, base)) {
*pp = uwsgi_str(usl->value + i + 1);
}
}
va_end(ap);
break;
}
else if (usl->value[i] == '\\' && !escaped) {
escaped = 1;
}
else if (escaped) {
escaped = 0;
}
else {
*ptr++ = usl->value[i];
}
}
free(item_buf);
usl = usl->next;
}
// destroy the list (no need to destroy the value as it is a pointer to buf)
usl = itemlist;
while (usl) {
struct uwsgi_string_list *tmp_usl = usl;
usl = usl->next;
free(tmp_usl);
}
free(buf);
return 0;
}
int uwsgi_send_http_stats(int fd) {
char buf[4096];
int ret = uwsgi_waitfd(fd, uwsgi.socket_timeout);
if (ret <= 0)
return -1;
if (read(fd, buf, 4096) <= 0)
return -1;
struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size);
if (!ub)
return -1;
if (uwsgi_buffer_append(ub, "HTTP/1.0 200 OK\r\n", 17))
goto error;
if (uwsgi_buffer_append(ub, "Connection: close\r\n", 19))
goto error;
if (uwsgi_buffer_append(ub, "Access-Control-Allow-Origin: *\r\n", 32))
goto error;
if (uwsgi_buffer_append(ub, "Content-Type: application/json\r\n", 32))
goto error;
if (uwsgi_buffer_append(ub, "\r\n", 2))
goto error;
if (uwsgi_buffer_send(ub, fd))
goto error;
uwsgi_buffer_destroy(ub);
return 0;
error:
uwsgi_buffer_destroy(ub);
return -1;
}
int uwsgi_call_symbol(char *symbol) {
void (*func) (void) = dlsym(RTLD_DEFAULT, symbol);
if (!func)
return -1;
func();
return 0;
}
int uwsgi_plugin_modifier1(char *plugin) {
int ret = -1;
char *symbol_name = uwsgi_concat2(plugin, "_plugin");
struct uwsgi_plugin *up = dlsym(RTLD_DEFAULT, symbol_name);
if (!up)
goto end;
ret = up->modifier1;
end:
free(symbol_name);
return ret;
}
char *uwsgi_strip(char *src) {
char *dst = src;
size_t len = strlen(src);
int i;
for (i = 0; i < (ssize_t) len; i++) {
if (src[i] == ' ' || src[i] == '\t') {
dst++;
}
}
len -= (dst - src);
for (i = len; i >= 0; i--) {
if (dst[i] == ' ' || dst[i] == '\t') {
dst[i] = 0;
}
else {
break;
}
}
return dst;
}
void uwsgi_uuid(char *buf) {
#ifdef UWSGI_UUID
uuid_t uuid_value;
uuid_generate(uuid_value);
uuid_unparse(uuid_value, buf);
#else
int i, r[11];
if (!uwsgi_file_exists("/dev/urandom"))
goto fallback;
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
goto fallback;
for (i = 0; i < 11; i++) {
if (read(fd, &r[i], 4) != 4) {
close(fd);
goto fallback;
}
}
close(fd);
goto done;
fallback:
for (i = 0; i < 11; i++) {
r[i] = rand();
}
done:
snprintf(buf, 37, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10]);
#endif
}
int uwsgi_uuid_cmp(char *x, char *y) {
int i;
for (i = 0; i < 36; i++) {
if (x[i] != y[i]) {
if (x[i] > y[i]) {
return 1;
}
return 0;
}
}
return 0;
}
void uwsgi_additional_header_add(struct wsgi_request *wsgi_req, char *hh, uint16_t hh_len) {
// will be freed on request's end
char *header = uwsgi_concat2n(hh, hh_len, "", 0);
uwsgi_string_new_list(&wsgi_req->additional_headers, header);
}
void uwsgi_remove_header(struct wsgi_request *wsgi_req, char *hh, uint16_t hh_len) {
char *header = uwsgi_concat2n(hh, hh_len, "", 0);
uwsgi_string_new_list(&wsgi_req->remove_headers, header);
}
// based on nginx implementation
static uint8_t b64_table64[] = {
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,
77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77,
77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77
};
static char b64_table64_2[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *uwsgi_base64_decode(char *buf, size_t len, size_t * d_len) {
// find the real size and check for invalid values
size_t i;
for (i = 0; i < len; i++) {
if (buf[i] == '=')
break;
// check for invalid content
if (b64_table64[(uint8_t) buf[i]] == 77) {
return NULL;
}
}
// check for invalid size
if (i % 4 == 1)
return NULL;
// compute the new size
*d_len = (((len + 3) / 4) * 3);
char *dst = uwsgi_malloc(*d_len + 1);
char *ptr = dst;
uint8_t *src = (uint8_t *) buf;
while (i > 3) {
*ptr++ = (char) (b64_table64[src[0]] << 2 | b64_table64[src[1]] >> 4);
*ptr++ = (char) (b64_table64[src[1]] << 4 | b64_table64[src[2]] >> 2);
*ptr++ = (char) (b64_table64[src[2]] << 6 | b64_table64[src[3]]);
src += 4;
i -= 4;
}
if (i > 1) {
*ptr++ = (char) (b64_table64[src[0]] << 2 | b64_table64[src[1]] >> 4);
}
if (i > 2) {
*ptr++ = (char) (b64_table64[src[1]] << 4 | b64_table64[src[2]] >> 2);
}
*d_len = (ptr - dst);
*ptr++ = 0;
return dst;
}
char *uwsgi_base64_encode(char *buf, size_t len, size_t * d_len) {
*d_len = ((len * 4) / 3) + 5;
uint8_t *src = (uint8_t *) buf;
char *dst = uwsgi_malloc(*d_len);
char *ptr = dst;
while (len >= 3) {
*ptr++ = b64_table64_2[src[0] >> 2];
*ptr++ = b64_table64_2[((src[0] << 4) & 0x30) | (src[1] >> 4)];
*ptr++ = b64_table64_2[((src[1] << 2) & 0x3C) | (src[2] >> 6)];
*ptr++ = b64_table64_2[src[2] & 0x3F];
src += 3;
len -= 3;
}
if (len > 0) {
*ptr++ = b64_table64_2[src[0] >> 2];
uint8_t tmp = (src[0] << 4) & 0x30;
if (len > 1)
tmp |= src[1] >> 4;
*ptr++ = b64_table64_2[tmp];
if (len < 2) {
*ptr++ = '=';
}
else {
*ptr++ = b64_table64_2[(src[1] << 2) & 0x3C];
}
*ptr++ = '=';
}
*ptr = 0;
*d_len = ((char *) ptr - dst);
return dst;
}
uint16_t uwsgi_be16(char *buf) {
uint16_t *src = (uint16_t *) buf;
uint16_t ret = 0;
uint8_t *ptr = (uint8_t *) & ret;
ptr[0] = (uint8_t) ((*src >> 8) & 0xff);
ptr[1] = (uint8_t) (*src & 0xff);
return ret;
}
uint32_t uwsgi_be32(char *buf) {
uint32_t *src = (uint32_t *) buf;
uint32_t ret = 0;
uint8_t *ptr = (uint8_t *) & ret;
ptr[0] = (uint8_t) ((*src >> 24) & 0xff);
ptr[1] = (uint8_t) ((*src >> 16) & 0xff);
ptr[2] = (uint8_t) ((*src >> 8) & 0xff);
ptr[3] = (uint8_t) (*src & 0xff);
return ret;
}
uint64_t uwsgi_be64(char *buf) {
uint64_t *src = (uint64_t *) buf;
uint64_t ret = 0;
uint8_t *ptr = (uint8_t *) & ret;
ptr[0] = (uint8_t) ((*src >> 56) & 0xff);
ptr[1] = (uint8_t) ((*src >> 48) & 0xff);
ptr[2] = (uint8_t) ((*src >> 40) & 0xff);
ptr[3] = (uint8_t) ((*src >> 32) & 0xff);
ptr[4] = (uint8_t) ((*src >> 24) & 0xff);
ptr[5] = (uint8_t) ((*src >> 16) & 0xff);
ptr[6] = (uint8_t) ((*src >> 8) & 0xff);
ptr[7] = (uint8_t) (*src & 0xff);
return ret;
}
char *uwsgi_get_header(struct wsgi_request *wsgi_req, char *hh, uint16_t len, uint16_t * rlen) {
char *key = uwsgi_malloc(len + 6);
uint16_t key_len = len;
char *ptr = key;
*rlen = 0;
if (uwsgi_strncmp(hh, len, "Content-Length", 14) && uwsgi_strncmp(hh, len, "Content-Type", 12)) {
memcpy(ptr, "HTTP_", 5);
ptr += 5;
key_len += 5;
}
uint16_t i;
for (i = 0; i < len; i++) {
if (hh[i] == '-') {
*ptr++ = '_';
}
else {
*ptr++ = toupper((int) hh[i]);
}
}
char *value = uwsgi_get_var(wsgi_req, key, key_len, rlen);
free(key);
return value;
}
static char *uwsgi_hex_table[] = {
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
"B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
"C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
"E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF",
};
char *uwsgi_str_to_hex(char *src, size_t slen) {
char *dst = uwsgi_malloc(slen * 2);
char *ptr = dst;
size_t i;
for (i = 0; i < slen; i++) {
uint8_t pos = (uint8_t) src[i];
memcpy(ptr, uwsgi_hex_table[pos], 2);
ptr += 2;
}
return dst;
}
// dst has to be 3 times buf size (USE IT ONLY FOR PATH_INFO !!!)
void http_url_encode(char *buf, uint16_t * len, char *dst) {
uint16_t i;
char *ptr = dst;
for (i = 0; i < *len; i++) {
if ((buf[i] >= 'A' && buf[i] <= 'Z') || (buf[i] >= 'a' && buf[i] <= 'z') || (buf[i] >= '0' && buf[i] <= '9') || buf[i] == '-' || buf[i] == '_' || buf[i] == '.' || buf[i] == '~' || buf[i] == '/') {
*ptr++ = buf[i];
}
else {
char *h = uwsgi_hex_table[(int) buf[i]];
*ptr++ = '%';
*ptr++ = h[0];
*ptr++ = h[1];
}
}
*len = ptr - dst;
}
void uwsgi_takeover() {
if (uwsgi.i_am_a_spooler) {
uwsgi_spooler_run();
}
else if (uwsgi.muleid) {
uwsgi_mule_run();
}
else {
uwsgi_worker_run();
}
}
// create a message pipe
void create_msg_pipe(int *fd, int bufsize) {
#if defined(SOCK_SEQPACKET) && defined(__linux__)
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fd)) {
#else
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fd)) {
#endif
uwsgi_error("create_msg_pipe()/socketpair()");
exit(1);
}
uwsgi_socket_nb(fd[0]);
uwsgi_socket_nb(fd[1]);
if (bufsize) {
if (setsockopt(fd[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(int))) {
uwsgi_error("create_msg_pipe()/setsockopt()");
}
if (setsockopt(fd[0], SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(int))) {
uwsgi_error("create_msg_pipe()/setsockopt()");
}
if (setsockopt(fd[1], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(int))) {
uwsgi_error("create_msg_pipe()/setsockopt()");
}
if (setsockopt(fd[1], SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(int))) {
uwsgi_error("create_msg_pipe()/setsockopt()");
}
}
}
char *uwsgi_binary_path() {
return uwsgi.binary_path ? uwsgi.binary_path : "uwsgi";
}
void uwsgi_envdir(char *edir) {
DIR *d = opendir(edir);
if (!d) {
uwsgi_error("[uwsgi-envdir] opendir()");
exit(1);
}
struct dirent *de;
while ((de = readdir(d)) != NULL) {
// skip hidden files
if (de->d_name[0] == '.')
continue;
struct stat st;
char *filename = uwsgi_concat3(edir, "/", de->d_name);
if (stat(filename, &st)) {
uwsgi_log("[uwsgi-envdir] error stating %s\n", filename);
uwsgi_error("[uwsgi-envdir] stat()");
exit(1);
}
if (!S_ISREG(st.st_mode)) {
free(filename);
continue;
}
// unsetenv
if (st.st_size == 0) {
#ifdef UNSETENV_VOID
unsetenv(de->d_name);
#else
if (unsetenv(de->d_name)) {
uwsgi_log("[uwsgi-envdir] unable to unset %s\n", de->d_name);
uwsgi_error("[uwsgi-envdir] unsetenv");
exit(1);
}
#endif
free(filename);
continue;
}
// read the content of the file
size_t size = 0;
char *content = uwsgi_open_and_read(filename, &size, 1, NULL);
if (!content) {
uwsgi_log("[uwsgi-envdir] unable to open %s\n", filename);
uwsgi_error_open(filename);
exit(1);
}
free(filename);
// HACK, envdir states we only need to strip the end of the string ....
uwsgi_chomp2(content);
// ... and substitute 0 with \n
size_t slen = strlen(content);
size_t i;
for (i = 0; i < slen; i++) {
if (content[i] == 0) {
content[i] = '\n';
}
}
if (setenv(de->d_name, content, 1)) {
uwsgi_log("[uwsgi-envdir] unable to set %s\n", de->d_name);
uwsgi_error("[uwsgi-envdir] setenv");
exit(1);
}
free(content);
}
closedir(d);
}
void uwsgi_envdirs(struct uwsgi_string_list *envdirs) {
struct uwsgi_string_list *usl = envdirs;
while (usl) {
uwsgi_envdir(usl->value);
usl = usl->next;
}
}
void uwsgi_opt_envdir(char *opt, char *value, void *foobar) {
uwsgi_envdir(value);
}
void uwsgi_exit(int status) {
uwsgi.last_exit_code = status;
// disable macro expansion
(exit) (status);
}
int uwsgi_base128(struct uwsgi_buffer *ub, uint64_t l, int first) {
if (l > 127) {
if (uwsgi_base128(ub, l / 128, 0))
return -1;
}
l %= 128;
if (first) {
if (uwsgi_buffer_u8(ub, (uint8_t) l))
return -1;
}
else {
if (uwsgi_buffer_u8(ub, 0x80 | (uint8_t) l))
return -1;
}
return 0;
}
#ifdef __linux__
void uwsgi_setns(char *path) {
int (*u_setns) (int, int) = (int (*)(int, int)) dlsym(RTLD_DEFAULT, "setns");
if (!u_setns) {
uwsgi_log("your system misses setns() syscall !!!\n");
exit(1);
}
// cound be overwritten
int count = 64;
uwsgi_log("joining namespaces from %s ...\n", path);
for (;;) {
int ns_fd = uwsgi_connect(path, 30, 0);
if (ns_fd < 0) {
uwsgi_error("uwsgi_setns()/uwsgi_connect()");
sleep(1);
continue;
}
int *fds = uwsgi_attach_fd(ns_fd, &count, "uwsgi-setns", 11);
if (fds && count > 0) {
int i;
for (i = 0; i < count; i++) {
if (fds[i] > -1) {
if (u_setns(fds[i], 0) < 0) {
uwsgi_error("uwsgi_setns()/setns()");
exit(1);
}
close(fds[i]);
}
}
free(fds);
close(ns_fd);
break;
}
if (fds)
free(fds);
close(ns_fd);
sleep(1);
}
}
#endif
mode_t uwsgi_mode_t(char *value, int *error) {
mode_t mode = 0;
*error = 0;
if (strlen(value) < 3) {
*error = 1;
return mode;
}
if (strlen(value) == 3) {
mode = (mode << 3) + (value[0] - '0');
mode = (mode << 3) + (value[1] - '0');
mode = (mode << 3) + (value[2] - '0');
}
else {
mode = (mode << 3) + (value[1] - '0');
mode = (mode << 3) + (value[2] - '0');
mode = (mode << 3) + (value[3] - '0');
}
return mode;
}
int uwsgi_wait_for_socket(char *socket_name) {
if (!uwsgi.wait_for_socket_timeout) {
uwsgi.wait_for_socket_timeout = 60;
}
uwsgi_log("waiting for %s (max %d seconds) ...\n", socket_name, uwsgi.wait_for_socket_timeout);
int counter = 0;
for (;;) {
if (counter > uwsgi.wait_for_socket_timeout) {
uwsgi_log("%s unavailable after %d seconds\n", socket_name, counter);
return -1;
}
// wait for 1 second to respect uwsgi.wait_for_fs_timeout
int fd = uwsgi_connect(socket_name, 1, 0);
if (fd < 0) goto retry;
close(fd);
uwsgi_log_verbose("%s ready\n", socket_name);
return 0;
retry:
sleep(1);
counter++;
}
return -1;
}
int uwsgi_wait_for_mountpoint(char *mountpoint) {
if (!uwsgi.wait_for_fs_timeout) {
uwsgi.wait_for_fs_timeout = 60;
}
uwsgi_log("waiting for %s (max %d seconds) ...\n", mountpoint, uwsgi.wait_for_fs_timeout);
int counter = 0;
for (;;) {
if (counter > uwsgi.wait_for_fs_timeout) {
uwsgi_log("%s unavailable after %d seconds\n", mountpoint, counter);
return -1;
}
struct stat st0;
struct stat st1;
if (stat(mountpoint, &st0)) goto retry;
if (!S_ISDIR(st0.st_mode)) goto retry;
char *relative = uwsgi_concat2(mountpoint, "/../");
if (stat(relative, &st1)) {
free(relative);
goto retry;
}
free(relative);
// useless :P
if (!S_ISDIR(st1.st_mode)) goto retry;
if (st0.st_dev == st1.st_dev) goto retry;
uwsgi_log_verbose("%s mounted\n", mountpoint);
return 0;
retry:
sleep(1);
counter++;
}
return -1;
}
// type -> 1 file, 2 dir, 0 both
int uwsgi_wait_for_fs(char *filename, int type) {
if (!uwsgi.wait_for_fs_timeout) {
uwsgi.wait_for_fs_timeout = 60;
}
uwsgi_log("waiting for %s (max %d seconds) ...\n", filename, uwsgi.wait_for_fs_timeout);
int counter = 0;
for (;;) {
if (counter > uwsgi.wait_for_fs_timeout) {
uwsgi_log("%s unavailable after %d seconds\n", filename, counter);
return -1;
}
struct stat st;
if (stat(filename, &st)) goto retry;
if (type == 1 && !S_ISREG(st.st_mode)) goto retry;
if (type == 2 && !S_ISDIR(st.st_mode)) goto retry;
uwsgi_log_verbose("%s found\n", filename);
return 0;
retry:
sleep(1);
counter++;
}
return -1;
}
#if !defined(_GNU_SOURCE) && !defined(__UCLIBC__)
int uwsgi_versionsort(const struct dirent **da, const struct dirent **db) {
const char *a = (*da)->d_name;
const char *b = (*db)->d_name;
long la, lb;
char *endptr;
// Check if a and b are valid numbers.
la = strtol(a, &endptr, 10);
if (strcmp(endptr, "\0") || endptr == a) {
a = NULL;
}
lb = strtol(b, &endptr, 10);
if (strcmp(endptr, "\0") || endptr == b) {
b = NULL;
}
if (a && b) {
return (la < lb ? -1 : la > lb);
} else if (a) {
return -1;
} else if (b) {
return 1;
} else {
return strcmp((*da)->d_name, (*db)->d_name);
}
}
#endif