diff --git a/master.c b/master.c index 05f15089..ba4f63e0 100644 --- a/master.c +++ b/master.c @@ -226,6 +226,12 @@ void master_loop(char **argv, char **environ) { if (uwsgi.has_emperor) { event_queue_add_fd_read(uwsgi.master_queue, uwsgi.emperor_fd); } + + if (uwsgi.zerg_server) { + uwsgi.zerg_server_fd = bind_to_unix(uwsgi.zerg_server, uwsgi.listen_queue, 0, 0); + event_queue_add_fd_read(uwsgi.master_queue, uwsgi.zerg_server_fd); + uwsgi_log("*** Zerg server enabled on %s ***\n", uwsgi.zerg_server); + } #ifdef UWSGI_UDP if (uwsgi.udp_socket) { udp_fd = bind_to_udp(uwsgi.udp_socket, 0, 0); @@ -632,6 +638,56 @@ void master_loop(char **argv, char **environ) { } } + if (uwsgi.zerg_server) { + if (interesting_fd == uwsgi.zerg_server_fd) { + struct sockaddr_un zsun; + socklen_t zsun_len = sizeof(struct sockaddr_un); + int zerg_client = accept(uwsgi.zerg_server_fd, (struct sockaddr *) &zsun, &zsun_len); + if (zerg_client < 0) { + uwsgi_error("zerg: accept()"); + continue; + } + + struct msghdr zerg_msg; + void *zerg_msg_control = uwsgi_malloc(CMSG_SPACE (sizeof (int) * uwsgi_count_sockets(uwsgi.sockets))); + struct iovec zerg_iov; + struct cmsghdr *cmsg; + + zerg_iov.iov_base = "uwsgi-zerg"; + zerg_iov.iov_len = 10; + + zerg_msg.msg_name = NULL; + zerg_msg.msg_namelen = 0; + zerg_msg.msg_iov = &zerg_iov; + zerg_msg.msg_iovlen = 1; + zerg_msg.msg_flags = 0; + zerg_msg.msg_control = zerg_msg_control; + zerg_msg.msg_controllen = CMSG_SPACE (sizeof (int) * uwsgi_count_sockets(uwsgi.sockets)); + + cmsg = CMSG_FIRSTHDR (&zerg_msg); + cmsg->cmsg_len = CMSG_LEN (sizeof (int) * uwsgi_count_sockets(uwsgi.sockets)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + struct uwsgi_socket *uwsgi_sock = uwsgi.sockets; + unsigned char *zerg_fd_ptr = CMSG_DATA(cmsg); + while(uwsgi_sock) { + memcpy(zerg_fd_ptr, &uwsgi_sock->fd, sizeof(int)); + zerg_fd_ptr += sizeof(int); + uwsgi_sock = uwsgi_sock->next; + } + + if (sendmsg(zerg_client, &zerg_msg, 0) < 0) { + uwsgi_error("sendmsg()"); + } + + + close(zerg_client); + + free(zerg_msg_control); + } + } + if (uwsgi.has_emperor) { if (interesting_fd == uwsgi.emperor_fd) { char byte; diff --git a/socket.c b/socket.c index 7a3c3dad..939fa75f 100644 --- a/socket.c +++ b/socket.c @@ -495,6 +495,17 @@ int bind_to_tcp(char *socket_name, int listen_queue, char *tcp_port) { uwsgi_nuclear_blast(); } + if (uwsgi.reuse_port) { +#ifdef SO_REUSEPORT + if (setsockopt(serverfd, SOL_SOCKET, SO_REUSEPORT, (const void *) &reuse_port, sizeof(int)) < 0) { + uwsgi_error("setsockopt()"); + uwsgi_nuclear_blast(); + } +#else + uwsgi_log("!!! your system does not support SO_REUSEPORT !!!\n"); +#endif + } + if (!uwsgi.no_defer_accept) { #ifdef __linux__ @@ -628,6 +639,17 @@ int timed_connect(struct pollfd *fdpoll, const struct sockaddr *addr, int addr_s } +int uwsgi_count_sockets(struct uwsgi_socket *uwsgi_sock) { + + int count = 0; + while(uwsgi_sock) { + count++; + uwsgi_sock = uwsgi_sock->next; + } + + return count; +} + int uwsgi_get_socket_num(struct uwsgi_socket *uwsgi_sock) { int count = 0; @@ -726,6 +748,15 @@ void uwsgi_add_socket_from_fd(struct uwsgi_socket *uwsgi_sock, int fd) { } if (gsa.sa->sa_family == AF_UNIX) { if (usa.sa_un.sun_path[0] == 0) abstract = 1; + // is it a zerg ? + if (uwsgi_sock->name == NULL) { + uwsgi_sock->fd = fd; + uwsgi_sock->family = AF_UNIX; + uwsgi_sock->bound = 1; + uwsgi_sock->name = uwsgi_concat2(usa.sa_un.sun_path+abstract, ""); + uwsgi_log("uwsgi zerg socket %d attached to UNIX address %s fd %d\n", uwsgi_get_socket_num(uwsgi_sock), usa.sa_un.sun_path+abstract, uwsgi_sock->fd); + return; + } if (!strcmp(usa.sa_un.sun_path+abstract, uwsgi_sock->name+abstract)) { uwsgi_sock->fd = fd; uwsgi_sock->family = AF_UNIX; @@ -751,6 +782,17 @@ void uwsgi_add_socket_from_fd(struct uwsgi_socket *uwsgi_sock, int fd) { else { computed_addr = uwsgi_concat3(ipv4a, ":", computed_port); } + + // is it a zerg ? + if (uwsgi_sock->name == NULL) { + uwsgi_sock->fd = fd; + uwsgi_sock->family = AF_INET; + uwsgi_sock->bound = 1; + uwsgi_sock->name = uwsgi_concat2(computed_addr, ""); + uwsgi_log("uwsgi zerg socket %d attached to INET address %s fd %d\n", uwsgi_get_socket_num(uwsgi_sock), computed_addr, uwsgi_sock->fd); + free(computed_addr); + return; + } char *asterisk = strchr(uwsgi_sock->name, '*'); int match = 1; if (asterisk) { diff --git a/utils.c b/utils.c index 9d84f8e8..18e1def2 100644 --- a/utils.c +++ b/utils.c @@ -2336,3 +2336,73 @@ int uwsgi_run_command(char *command) { exit(1); } +int *uwsgi_attach_fd(int fd, int count, char *code, size_t code_len) { + + struct msghdr msg; + ssize_t len; + char *id = NULL; + + struct iovec iov; + struct cmsghdr *cmsg; + int *ret; + int i; + + void *msg_control = uwsgi_malloc(CMSG_SPACE(sizeof(int) * count)); + + memset( msg_control, 0, CMSG_SPACE(sizeof(int) * count)); + + if (code && code_len > 0) { + id = uwsgi_malloc(code_len); + memset(id, 0, code_len); + } + + iov.iov_base = id; + iov.iov_len = code_len; + memset(&msg, 0, sizeof(msg)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = msg_control; + msg.msg_controllen = CMSG_SPACE(sizeof(int) * count); + msg.msg_flags = 0; + + len = recvmsg(fd, &msg, 0); + if (len <= 0) { + uwsgi_error("recvmsg()"); + return NULL; + } + + if (code && code_len > 0) { + if (strcmp(id, code)) { + return NULL; + } + } + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg) return NULL; + + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { + return NULL; + } + + ret = uwsgi_malloc(sizeof(int) * (count + 1)); + for(i=0;icmsg_len - ((char *)CMSG_DATA(cmsg)- (char *)cmsg)) > (sizeof(int) * (count + 1))) { + uwsgi_log("not enough space for sockets data, consider increasing it\n"); + return NULL; + } + + memcpy(ret, CMSG_DATA(cmsg), cmsg->cmsg_len - ((char *)CMSG_DATA(cmsg)- (char *)cmsg)); + + free(msg_control); + if (code && code_len > 0) { + free(id); + } + + return ret; +} diff --git a/uwsgi.c b/uwsgi.c index b68829bd..7cb491af 100644 --- a/uwsgi.c +++ b/uwsgi.c @@ -208,6 +208,9 @@ static struct option long_base_options[] = { {"namespace-net", required_argument, 0, LONG_ARGS_LINUX_NS_NET}, {"ns-net", required_argument, 0, LONG_ARGS_LINUX_NS_NET}, #endif + {"reuse-port", no_argument, &uwsgi.reuse_port, 1}, + {"zerg", required_argument, 0, LONG_ARGS_ZERG}, + {"zerg-server", required_argument, 0, LONG_ARGS_ZERG_SERVER}, {"cron", required_argument, 0, LONG_ARGS_CRON}, {"loop", required_argument, 0, LONG_ARGS_LOOP}, {"worker-exec", required_argument, 0, LONG_ARGS_WORKER_EXEC}, @@ -1603,11 +1606,26 @@ int uwsgi_start(void *v_argv) { if (!uwsgi.no_server) { //check for inherited sockets - if (uwsgi.is_a_reload) { + if (uwsgi.is_a_reload || uwsgi.zerg) { + + if (uwsgi.zerg) { + int zerg_fd; + i = 0; + for(;;) { + zerg_fd = uwsgi.zerg[i]; + if (zerg_fd == -1) { + break; + } + uwsgi_sock = uwsgi_new_socket(NULL); + uwsgi_add_socket_from_fd(uwsgi_sock, zerg_fd); + i++; + } + } + uwsgi_sock = uwsgi.sockets; while (uwsgi_sock) { //a bit overengineering - if (uwsgi_sock->name[0] != 0) { + if (uwsgi_sock->name[0] != 0 && !uwsgi_sock->bound) { for (j = 3; j < sysconf(_SC_OPEN_MAX); j++) { uwsgi_add_socket_from_fd(uwsgi_sock, j); } @@ -2421,6 +2439,7 @@ static int manage_base_opt(int i, char *optarg) { struct uwsgi_static_map *usm, *old_usm; struct uwsgi_config_template *uct, *old_uct; struct uwsgi_cron *uc, *old_uc; + int zerg_fd; switch (i) { @@ -2949,6 +2968,23 @@ static int manage_base_opt(int i, char *optarg) { case 's': uwsgi_new_socket(generate_socket_name(optarg)); return 1; + case LONG_ARGS_ZERG: + zerg_fd = uwsgi_connect(optarg, 30, 0); + if (zerg_fd < 0) { + uwsgi_log("--- unable to connect to zerg server ---\n"); + exit(1); + } + uwsgi.zerg = uwsgi_attach_fd(zerg_fd, 8, "uwsgi-zerg", 11); + if (uwsgi.zerg == NULL) { + uwsgi_log("--- invalid data received from zerg-server ---\n"); + exit(1); + } + close(zerg_fd); + return 1; + case LONG_ARGS_ZERG_SERVER: + uwsgi.zerg_server = optarg; + uwsgi.master_process = 1; + return 1; case LONG_ARGS_SHARED_SOCKET: uwsgi_new_shared_socket(generate_socket_name(optarg)); return 1; diff --git a/uwsgi.h b/uwsgi.h index ecd25973..0a7b9349 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -445,6 +445,8 @@ struct uwsgi_opt { #define LONG_ARGS_VASSALS_START_HOOK 17121 #define LONG_ARGS_VASSALS_STOP_HOOK 17122 #define LONG_ARGS_CRON 17123 +#define LONG_ARGS_ZERG 17124 +#define LONG_ARGS_ZERG_SERVER 17125 #define UWSGI_OK 0 @@ -863,6 +865,8 @@ struct uwsgi_server { time_t current_time; uint64_t master_cycles; + int reuse_port; + int lazy; int cheap; int idle; @@ -903,6 +907,10 @@ struct uwsgi_server { char *remap_modifier; + int *zerg; + char *zerg_server; + int zerg_server_fd; + char *chroot; gid_t gid; uid_t uid; @@ -2018,3 +2026,7 @@ void uwsgi_manage_signal_cron(time_t); int uwsgi_run_command(char *); void uwsgi_manage_command_cron(time_t); + +int *uwsgi_attach_fd(int, int, char *, size_t); + +int uwsgi_count_sockets(struct uwsgi_socket *);