summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_process.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nxt_process.c')
-rw-r--r--src/nxt_process.c251
1 files changed, 189 insertions, 62 deletions
diff --git a/src/nxt_process.c b/src/nxt_process.c
index c4aef21c..638765a4 100644
--- a/src/nxt_process.c
+++ b/src/nxt_process.c
@@ -7,10 +7,16 @@
#include <nxt_main.h>
#include <nxt_main_process.h>
+#if (NXT_HAVE_CLONE)
+#include <nxt_clone.h>
+#endif
+
+#include <signal.h>
static void nxt_process_start(nxt_task_t *task, nxt_process_t *process);
static nxt_int_t nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc);
-
+static nxt_int_t nxt_process_worker_setup(nxt_task_t *task,
+ nxt_process_t *process, int parentfd);
/* A cached process pid. */
nxt_pid_t nxt_pid;
@@ -34,84 +40,217 @@ nxt_bool_t nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
{ 0, 0, 0, 1, 0 },
};
-nxt_pid_t
-nxt_process_create(nxt_task_t *task, nxt_process_t *process)
-{
- nxt_pid_t pid;
+
+static nxt_int_t
+nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd) {
+ pid_t rpid, pid;
+ ssize_t n;
+ nxt_int_t parent_status;
nxt_process_t *p;
nxt_runtime_t *rt;
+ nxt_process_init_t *init;
nxt_process_type_t ptype;
- rt = task->thread->runtime;
+ pid = getpid();
+ rpid = 0;
+ rt = task->thread->runtime;
+ init = process->init;
- pid = fork();
+ /* Setup the worker process. */
- switch (pid) {
+ n = read(parentfd, &rpid, sizeof(rpid));
+ if (nxt_slow_path(n == -1 || n != sizeof(rpid))) {
+ nxt_alert(task, "failed to read real pid");
+ return NXT_ERROR;
+ }
- case -1:
- nxt_alert(task, "fork() failed while creating \"%s\" %E",
- process->init->name, nxt_errno);
- break;
+ if (nxt_slow_path(rpid == 0)) {
+ nxt_alert(task, "failed to get real pid from parent");
+ return NXT_ERROR;
+ }
- case 0:
- /* A child. */
- nxt_pid = getpid();
+ nxt_pid = rpid;
+
+ /* Clean inherited cached thread tid. */
+ task->thread->tid = 0;
+
+ process->pid = nxt_pid;
+
+ if (nxt_pid != pid) {
+ nxt_debug(task, "app \"%s\" real pid %d", init->name, nxt_pid);
+ nxt_debug(task, "app \"%s\" isolated pid: %d", init->name, pid);
+ }
- /* Clean inherited cached thread tid. */
- task->thread->tid = 0;
+ n = read(parentfd, &parent_status, sizeof(parent_status));
+ if (nxt_slow_path(n == -1 || n != sizeof(parent_status))) {
+ nxt_alert(task, "failed to read parent status");
+ return NXT_ERROR;
+ }
- process->pid = nxt_pid;
+ if (nxt_slow_path(close(parentfd) == -1)) {
+ nxt_alert(task, "failed to close reader pipe fd");
+ return NXT_ERROR;
+ }
- ptype = process->init->type;
+ if (nxt_slow_path(parent_status != NXT_OK)) {
+ return parent_status;
+ }
- nxt_port_reset_next_id();
+ ptype = init->type;
- nxt_event_engine_thread_adopt(task->thread->engine);
+ nxt_port_reset_next_id();
- /* Remove not ready processes */
- nxt_runtime_process_each(rt, p) {
+ nxt_event_engine_thread_adopt(task->thread->engine);
- if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0) {
- nxt_debug(task, "remove not required process %PI", p->pid);
+ /* Remove not ready processes. */
+ nxt_runtime_process_each(rt, p) {
- nxt_process_close_ports(task, p);
+ if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0) {
+ nxt_debug(task, "remove not required process %PI", p->pid);
- continue;
- }
+ nxt_process_close_ports(task, p);
- if (!p->ready) {
- nxt_debug(task, "remove not ready process %PI", p->pid);
+ continue;
+ }
- nxt_process_close_ports(task, p);
+ if (!p->ready) {
+ nxt_debug(task, "remove not ready process %PI", p->pid);
- continue;
- }
+ nxt_process_close_ports(task, p);
- nxt_port_mmaps_destroy(&p->incoming, 0);
- nxt_port_mmaps_destroy(&p->outgoing, 0);
+ continue;
+ }
- } nxt_runtime_process_loop;
+ nxt_port_mmaps_destroy(&p->incoming, 0);
+ nxt_port_mmaps_destroy(&p->outgoing, 0);
- nxt_runtime_process_add(task, process);
+ } nxt_runtime_process_loop;
- nxt_process_start(task, process);
+ nxt_runtime_process_add(task, process);
- process->ready = 1;
+ nxt_process_start(task, process);
- break;
+ process->ready = 1;
- default:
- /* A parent. */
- nxt_debug(task, "fork(\"%s\"): %PI", process->init->name, pid);
+ return NXT_OK;
+}
- process->pid = pid;
- nxt_runtime_process_add(task, process);
+nxt_pid_t
+nxt_process_create(nxt_task_t *task, nxt_process_t *process)
+{
+ int pipefd[2];
+ nxt_int_t ret;
+ nxt_pid_t pid;
+ nxt_process_init_t *init;
- break;
+ if (nxt_slow_path(pipe(pipefd) == -1)) {
+ nxt_alert(task, "failed to create process pipe for passing rpid");
+ return -1;
+ }
+
+ init = process->init;
+
+#if (NXT_HAVE_CLONE)
+ pid = nxt_clone(SIGCHLD|init->isolation.clone.flags);
+#else
+ pid = fork();
+#endif
+
+ if (nxt_slow_path(pid < 0)) {
+#if (NXT_HAVE_CLONE)
+ nxt_alert(task, "clone() failed while creating \"%s\" %E",
+ init->name, nxt_errno);
+#else
+ nxt_alert(task, "fork() failed while creating \"%s\" %E",
+ init->name, nxt_errno);
+#endif
+
+ return pid;
+ }
+
+ if (pid == 0) {
+ /* Child. */
+
+ if (nxt_slow_path(close(pipefd[1]) == -1)) {
+ nxt_alert(task, "failed to close writer pipe fd");
+ return NXT_ERROR;
+ }
+
+ ret = nxt_process_worker_setup(task, process, pipefd[0]);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ exit(1);
+ }
+
+ /*
+ * Explicitly return 0 to notice the caller function this is the child.
+ * The caller must return to the event engine work queue loop.
+ */
+ return 0;
+ }
+
+ /* Parent. */
+
+ if (nxt_slow_path(close(pipefd[0]) != 0)) {
+ nxt_alert(task, "failed to close pipe: %E", nxt_errno);
+ }
+
+ /*
+ * At this point, the child process is blocked reading the
+ * pipe fd to get its real pid (rpid).
+ *
+ * If anything goes wrong now, we need to terminate the child
+ * process by sending a NXT_ERROR in the pipe.
+ */
+
+#if (NXT_HAVE_CLONE)
+ nxt_debug(task, "clone(\"%s\"): %PI", init->name, pid);
+#else
+ nxt_debug(task, "fork(\"%s\"): %PI", init->name, pid);
+#endif
+
+ if (nxt_slow_path(write(pipefd[1], &pid, sizeof(pid)) == -1)) {
+ nxt_alert(task, "failed to write real pid");
+ goto fail_cleanup;
+ }
+
+#if (NXT_HAVE_CLONE_NEWUSER)
+ if ((init->isolation.clone.flags & CLONE_NEWUSER) == CLONE_NEWUSER) {
+ ret = nxt_clone_proc_map(task, pid, &init->isolation.clone);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail_cleanup;
+ }
+ }
+#endif
+
+ ret = NXT_OK;
+
+ if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) {
+ nxt_alert(task, "failed to write status");
+ goto fail_cleanup;
}
+ process->pid = pid;
+
+ nxt_runtime_process_add(task, process);
+
return pid;
+
+fail_cleanup:
+
+ ret = NXT_ERROR;
+
+ if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) {
+ nxt_alert(task, "failed to write status");
+ }
+
+ if (nxt_slow_path(close(pipefd[1]) != 0)) {
+ nxt_alert(task, "failed to close pipe: %E", nxt_errno);
+ }
+
+ waitpid(pid, NULL, 0);
+
+ return -1;
}
@@ -133,22 +272,17 @@ nxt_process_start(nxt_task_t *task, nxt_process_t *process)
nxt_process_title(task, "unit: %s", init->name);
thread = task->thread;
+ rt = thread->runtime;
nxt_random_init(&thread->random);
- if (init->user_cred != NULL) {
- /*
- * Changing user credentials requires either root privileges
- * or CAP_SETUID and CAP_SETGID capabilities on Linux.
- */
+ if (rt->capabilities.setid && init->user_cred != NULL) {
ret = nxt_user_cred_set(task, init->user_cred);
if (ret != NXT_OK) {
goto fail;
}
}
- rt = thread->runtime;
-
rt->type = init->type;
engine = thread->engine;
@@ -592,15 +726,8 @@ nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid);
if (setgid(uc->base_gid) != 0) {
- if (nxt_errno == NXT_EPERM) {
- nxt_log(task, NXT_LOG_NOTICE, "setgid(%d) failed %E, ignored",
- uc->base_gid, nxt_errno);
- return NXT_OK;
-
- } else {
- nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
- return NXT_ERROR;
- }
+ nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
+ return NXT_ERROR;
}
if (uc->gids != NULL) {