ext/ruby/passenger_native_support.c in passenger-3.0.21 vs ext/ruby/passenger_native_support.c in passenger-3.9.1.beta

- old
+ new

@@ -1,8 +1,8 @@ /* * Phusion Passenger - http://www.modrails.com/ - * Copyright (c) 2010 Phusion + * Copyright (c) 2010, 2011, 2012 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,10 +31,11 @@ #include "rubysig.h" #endif #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> +#include <sys/wait.h> #include <sys/un.h> #include <sys/socket.h> #include <sys/uio.h> #include <sys/time.h> #include <sys/resource.h> @@ -45,16 +46,16 @@ #include <fcntl.h> #include <errno.h> #include <limits.h> #include <grp.h> #include <signal.h> +#include <pthread.h> #ifdef HAVE_ALLOCA_H #include <alloca.h> #endif #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define HAVE_KQUEUE - #include <pthread.h> #include <sys/event.h> #include <sys/time.h> #endif #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -82,217 +83,10 @@ #ifdef HAVE_KQUEUE static VALUE cFileSystemWatcher; #endif /* - * call-seq: send_fd(socket_fd, fd_to_send) - * - * Send a file descriptor over the given Unix socket. You do not have to call - * this function directly. A convenience wrapper is provided by IO#send_io. - * - * - +socket_fd+ (integer): The file descriptor of the socket. - * - +fd_to_send+ (integer): The file descriptor to send. - * - Raises +SystemCallError+ if something went wrong. - */ -static VALUE -send_fd(VALUE self, VALUE socket_fd, VALUE fd_to_send) { - struct msghdr msg; - struct iovec vec; - char dummy[1]; - #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) - struct { - struct cmsghdr header; - int fd; - } control_data; - #else - char control_data[CMSG_SPACE(sizeof(int))]; - #endif - struct cmsghdr *control_header; - int control_payload; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - - /* Linux and Solaris require msg_iov to be non-NULL. */ - dummy[0] = '\0'; - vec.iov_base = dummy; - vec.iov_len = sizeof(dummy); - msg.msg_iov = &vec; - msg.msg_iovlen = 1; - - msg.msg_control = (caddr_t) &control_data; - msg.msg_controllen = sizeof(control_data); - msg.msg_flags = 0; - - control_header = CMSG_FIRSTHDR(&msg); - control_header->cmsg_level = SOL_SOCKET; - control_header->cmsg_type = SCM_RIGHTS; - control_payload = NUM2INT(fd_to_send); - #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) - control_header->cmsg_len = sizeof(control_data); - control_data.fd = control_payload; - #else - control_header->cmsg_len = CMSG_LEN(sizeof(int)); - memcpy(CMSG_DATA(control_header), &control_payload, sizeof(int)); - #endif - - if (sendmsg(NUM2INT(socket_fd), &msg, 0) == -1) { - rb_sys_fail("sendmsg(2)"); - return Qnil; - } - - return Qnil; -} - -/* - * call-seq: recv_fd(socket_fd) - * - * Receive a file descriptor from the given Unix socket. Returns the received - * file descriptor as an integer. Raises +SystemCallError+ if something went - * wrong. - * - * You do not have call this method directly. A convenience wrapper is - * provided by IO#recv_io. - */ -static VALUE -recv_fd(VALUE self, VALUE socket_fd) { - struct msghdr msg; - struct iovec vec; - char dummy[1]; - #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) - // File descriptor passing macros (CMSG_*) seem to be broken - // on 64-bit MacOS X. This structure works around the problem. - struct { - struct cmsghdr header; - int fd; - } control_data; - #define EXPECTED_CMSG_LEN sizeof(control_data) - #else - char control_data[CMSG_SPACE(sizeof(int))]; - #define EXPECTED_CMSG_LEN CMSG_LEN(sizeof(int)) - #endif - struct cmsghdr *control_header; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - - dummy[0] = '\0'; - vec.iov_base = dummy; - vec.iov_len = sizeof(dummy); - msg.msg_iov = &vec; - msg.msg_iovlen = 1; - - msg.msg_control = (caddr_t) &control_data; - msg.msg_controllen = sizeof(control_data); - msg.msg_flags = 0; - - if (recvmsg(NUM2INT(socket_fd), &msg, 0) == -1) { - rb_sys_fail("Cannot read file descriptor with recvmsg()"); - return Qnil; - } - - control_header = CMSG_FIRSTHDR(&msg); - if (control_header == NULL) { - rb_raise(rb_eIOError, "No valid file descriptor received."); - return Qnil; - } - if (control_header->cmsg_len != EXPECTED_CMSG_LEN - || control_header->cmsg_level != SOL_SOCKET - || control_header->cmsg_type != SCM_RIGHTS) { - rb_raise(rb_eIOError, "No valid file descriptor received."); - return Qnil; - } - #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) - return INT2NUM(control_data.fd); - #else - return INT2NUM(*((int *) CMSG_DATA(control_header))); - #endif -} - -/* - * call-seq: create_unix_socket(filename, backlog) - * - * Create a SOCK_STREAM server Unix socket. Unlike Ruby's UNIXServer class, - * this function is also able to create Unix sockets on the abstract namespace - * by prepending the filename with a null byte. - * - * - +filename+ (string): The filename of the Unix socket to create. - * - +backlog+ (integer): The backlog to use for listening on the socket. - * - Returns: The file descriptor of the created Unix socket, as an integer. - * - Raises +SystemCallError+ if something went wrong. - */ -static VALUE -create_unix_socket(VALUE self, VALUE filename, VALUE backlog) { - int fd, ret; - struct sockaddr_un addr; - const char *filename_str; - long filename_length; - - filename_str = RSTRING_PTR(filename); - filename_length = RSTRING_LEN(filename); - - fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (fd == -1) { - rb_sys_fail("Cannot create a Unix socket"); - return Qnil; - } - - addr.sun_family = AF_UNIX; - memcpy(addr.sun_path, filename_str, - MIN((long) filename_length, (long) sizeof(addr.sun_path))); - addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; - - ret = bind(fd, (const struct sockaddr *) &addr, sizeof(addr)); - if (ret == -1) { - int e = errno; - close(fd); - errno = e; - rb_sys_fail("Cannot bind Unix socket"); - return Qnil; - } - - ret = listen(fd, NUM2INT(backlog)); - if (ret == -1) { - int e = errno; - close(fd); - errno = e; - rb_sys_fail("Cannot listen on Unix socket"); - return Qnil; - } - return INT2NUM(fd); -} - -/* - * call-seq: close_all_file_descriptors(exceptions) - * - * Close all file descriptors, except those given in the +exceptions+ array. - * For example, the following would close all file descriptors except standard - * input (0) and standard output (1). - * - * close_all_file_descriptors([0, 1]) - */ -static VALUE -close_all_file_descriptors(VALUE self, VALUE exceptions) { - long i, j; - - for (i = sysconf(_SC_OPEN_MAX) - 1; i >= 0; i--) { - int is_exception = 0; - #ifdef RB_RESERVED_FD_P - is_exception = rb_reserved_fd_p((int) i); - #endif - for (j = 0; j < RARRAY_LEN(exceptions) && !is_exception; j++) { - long fd = NUM2INT(rb_ary_entry(exceptions, j)); - is_exception = i == fd; - } - if (!is_exception) { - close((int) i); - } - } - return Qnil; -} - -/* * call-seq: disable_stdio_buffering * * Disables any kind of buffering on the C +stdout+ and +stderr+ variables, * so that +fprintf()+ on +stdout+ and +stderr+ have immediate effect. */ @@ -584,34 +378,11 @@ f_writev3(VALUE self, VALUE fd, VALUE components1, VALUE components2, VALUE components3) { VALUE array_of_components[3] = { components1, components2, components3 }; return f_generic_writev(fd, array_of_components, 3); } -/** - * Ruby's implementations of initgroups, setgid and setuid are broken various ways, - * sigh... - * Ruby's setgid and setuid can't handle negative UIDs and initgroups is just broken. - * Work around it by using our own implementation. - */ static VALUE -switch_user(VALUE self, VALUE username, VALUE uid, VALUE gid) { - uid_t the_uid = (uid_t) NUM2LL(uid); - gid_t the_gid = (gid_t) NUM2LL(gid); - - if (initgroups(RSTRING_PTR(username), the_gid) == -1) { - rb_sys_fail("initgroups"); - } - if (setgid(the_gid) == -1) { - rb_sys_fail("setgid"); - } - if (setuid(the_uid) == -1) { - rb_sys_fail("setuid"); - } - return Qnil; -} - -static VALUE process_times(VALUE self) { struct rusage usage; unsigned long long utime, stime; if (getrusage(RUSAGE_SELF, &usage) == -1) { @@ -621,10 +392,86 @@ utime = (unsigned long long) usage.ru_utime.tv_sec * 1000000 + usage.ru_utime.tv_usec; stime = (unsigned long long) usage.ru_stime.tv_sec * 1000000 + usage.ru_stime.tv_usec; return rb_struct_new(S_ProcessTimes, rb_ull2inum(utime), rb_ull2inum(stime)); } +static void * +detach_process_main(void *arg) { + pid_t pid = (pid_t) (long) arg; + int ret; + do { + ret = waitpid(pid, NULL, 0); + } while (ret == -1 && errno == EINTR); + return NULL; +} + +static VALUE +detach_process(VALUE self, VALUE pid) { + pthread_t thr; + pthread_attr_t attr; + size_t stack_size = 96 * 1024; + + unsigned long min_stack_size; + int stack_min_size_defined; + int round_stack_size; + + #ifdef PTHREAD_STACK_MIN + // PTHREAD_STACK_MIN may not be a constant macro so we need + // to evaluate it dynamically. + min_stack_size = PTHREAD_STACK_MIN; + stack_min_size_defined = 1; + #else + // Assume minimum stack size is 128 KB. + min_stack_size = 128 * 1024; + stack_min_size_defined = 0; + #endif + if (stack_size != 0 && stack_size < min_stack_size) { + stack_size = min_stack_size; + round_stack_size = !stack_min_size_defined; + } else { + round_stack_size = 1; + } + + if (round_stack_size) { + // Round stack size up to page boundary. + long page_size; + #if defined(_SC_PAGESIZE) + page_size = sysconf(_SC_PAGESIZE); + #elif defined(_SC_PAGE_SIZE) + page_size = sysconf(_SC_PAGE_SIZE); + #elif defined(PAGESIZE) + page_size = sysconf(PAGESIZE); + #elif defined(PAGE_SIZE) + page_size = sysconf(PAGE_SIZE); + #else + page_size = getpagesize(); + #endif + if (stack_size % page_size != 0) { + stack_size = stack_size - (stack_size % page_size) + page_size; + } + } + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, 1); + pthread_attr_setstacksize(&attr, stack_size); + pthread_create(&thr, &attr, detach_process_main, (void *) NUM2LONG(pid)); + pthread_attr_destroy(&attr); + return Qnil; +} + +/** + * Freeze the current process forever. On Ruby 1.9 this never unlocks the GIL. + * Useful for testing purposes. + */ +static VALUE +freeze_process(VALUE self) { + while (1) { + usleep(60 * 1000000); + } + return Qnil; +} + #if defined(HAVE_KQUEUE) || defined(IN_DOXYGEN) typedef struct { VALUE klass; VALUE filenames; VALUE termination_pipe; @@ -822,10 +669,11 @@ result = rb_protect(fs_watcher_init, (VALUE) watcher, &status); if (status) { fs_watcher_free(watcher); rb_jump_tag(status); + return Qnil; } else { return result; } } @@ -1008,20 +856,17 @@ */ mNativeSupport = rb_define_module_under(mPassenger, "NativeSupport"); S_ProcessTimes = rb_struct_define("ProcessTimes", "utime", "stime", NULL); - rb_define_singleton_method(mNativeSupport, "send_fd", send_fd, 2); - rb_define_singleton_method(mNativeSupport, "recv_fd", recv_fd, 1); - rb_define_singleton_method(mNativeSupport, "create_unix_socket", create_unix_socket, 2); - rb_define_singleton_method(mNativeSupport, "close_all_file_descriptors", close_all_file_descriptors, 1); rb_define_singleton_method(mNativeSupport, "disable_stdio_buffering", disable_stdio_buffering, 0); rb_define_singleton_method(mNativeSupport, "split_by_null_into_hash", split_by_null_into_hash, 1); rb_define_singleton_method(mNativeSupport, "writev", f_writev, 2); rb_define_singleton_method(mNativeSupport, "writev2", f_writev2, 3); rb_define_singleton_method(mNativeSupport, "writev3", f_writev3, 4); - rb_define_singleton_method(mNativeSupport, "switch_user", switch_user, 3); rb_define_singleton_method(mNativeSupport, "process_times", process_times, 0); + rb_define_singleton_method(mNativeSupport, "detach_process", detach_process, 1); + rb_define_singleton_method(mNativeSupport, "freeze_process", freeze_process, 0); #ifdef HAVE_KQUEUE cFileSystemWatcher = rb_define_class_under(mNativeSupport, "FileSystemWatcher", rb_cObject); rb_define_singleton_method(cFileSystemWatcher, "_new",