Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 90 additions & 6 deletions frankenphp.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,23 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef ZEND_WIN32
#include <unistd.h>
#endif
#if defined(__linux__)
#include <sys/prctl.h>
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#include <unistd.h>
#elif defined(__FreeBSD__)
#include <pthread_np.h>
#include <sys/procctl.h>
#include <unistd.h>
#elif defined(__OpenBSD__)
#include <pthread_np.h>
#include <unistd.h>
#endif
#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
defined(__DragonFly__)
#define FRANKENPHP_KQUEUE_PARENT_DEATH 1
#include <sys/event.h>
#include <sys/types.h>
#include <unistd.h>
#endif

#include "_cgo_export.h"
Expand Down Expand Up @@ -95,10 +105,66 @@ __thread HashTable *sandboxed_env = NULL;

#ifndef PHP_WIN32
static bool is_forked_child = false;
static void frankenphp_fork_child(void) { is_forked_child = true; }
static pid_t fork_parent_pid = 0;

static void frankenphp_fork_prepare(void) { fork_parent_pid = getpid(); }

#if defined(FRANKENPHP_KQUEUE_PARENT_DEATH)
/* Watcher thread for platforms without a kernel parent-death signal.
* Blocks in kevent() until the parent exit, then force-kills this child. */
static void *frankenphp_parent_death_watcher(void *arg) {
pid_t ppid = (pid_t)(intptr_t)arg;
int kq = kqueue();
if (kq < 0) {
_exit(1);
}
struct kevent kev;
EV_SET(&kev, ppid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, NULL);
if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0) {
_exit(1);
}
struct kevent triggered;
if (kevent(kq, NULL, 0, &triggered, 1, NULL) > 0) {
_exit(1);
}
close(kq);
return NULL;
}
#endif

static void frankenphp_fork_child(void) {
is_forked_child = true;
#if defined(__linux__)
// if the parent process dies between fork() and this prctl()
if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) != 0 ||
getppid() != fork_parent_pid) {
_exit(1);
}
#elif defined(__FreeBSD__)
/* FreeBSD analogue of PR_SET_PDEATHSIG - except with
* parent process death, rather than parent thread death. */
int sig = SIGKILL;
if (procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sig) != 0 ||
getppid() != fork_parent_pid) {
_exit(1);
}
#elif defined(FRANKENPHP_KQUEUE_PARENT_DEATH)
/* No kernel parent-death signal available; spawn a watcher that
* blocks on EVFILT_PROC/NOTE_EXIT. */
if (getppid() != fork_parent_pid) {
_exit(1);
}
pthread_t watcher;
if (pthread_create(&watcher, NULL, frankenphp_parent_death_watcher,
(void *)(intptr_t)fork_parent_pid) != 0) {
_exit(1);
}
pthread_detach(watcher);
#endif
}

static void frankenphp_register_atfork(void) {
pthread_atfork(NULL, NULL, frankenphp_fork_child);
pthread_atfork(frankenphp_fork_prepare, NULL, frankenphp_fork_child);
}
#endif

Expand Down Expand Up @@ -922,6 +988,12 @@ static int frankenphp_startup(sapi_module_struct *sapi_module) {
static int frankenphp_deactivate(void) { return SUCCESS; }

static size_t frankenphp_ub_write(const char *str, size_t str_length) {
#ifndef PHP_WIN32
if (UNEXPECTED(is_forked_child)) {
return 0;
}
#endif

struct go_ub_write_return result =
go_ub_write(thread_index, (char *)str, str_length);

Expand All @@ -933,6 +1005,12 @@ static size_t frankenphp_ub_write(const char *str, size_t str_length) {
}

static int frankenphp_send_headers(sapi_headers_struct *sapi_headers) {
#ifndef PHP_WIN32
if (UNEXPECTED(is_forked_child)) {
return SAPI_HEADER_SEND_FAILED;
}
#endif

if (SG(request_info).no_headers == 1) {
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
Expand All @@ -958,6 +1036,12 @@ static int frankenphp_send_headers(sapi_headers_struct *sapi_headers) {
}

static void frankenphp_sapi_flush(void *server_context) {
#ifndef PHP_WIN32
if (UNEXPECTED(is_forked_child)) {
return;
}
#endif

sapi_send_headers();
if (go_sapi_flush(thread_index)) {
php_handle_aborted_connection();
Expand Down
Loading