nxt_process.c (341:5e1a9c009968) nxt_process.c (343:9fa845db60fb)
1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7#include <nxt_main.h>
8#include <nxt_main_process.h>
9
10
11static void nxt_process_start(nxt_task_t *task, nxt_process_t *process);
12static nxt_int_t nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc);
13
14
15/* A cached process pid. */
16nxt_pid_t nxt_pid;
17
18/* An original parent process pid. */
19nxt_pid_t nxt_ppid;
20
21
22nxt_pid_t
23nxt_process_create(nxt_task_t *task, nxt_process_t *process)
24{
25 nxt_pid_t pid;
26 nxt_process_t *p;
27 nxt_runtime_t *rt;
28
29 rt = task->thread->runtime;
30
31 pid = fork();
32
33 switch (pid) {
34
35 case -1:
36 nxt_log(task, NXT_LOG_CRIT, "fork() failed while creating \"%s\" %E",
37 process->init->name, nxt_errno);
38 break;
39
40 case 0:
41 /* A child. */
42 nxt_pid = getpid();
43
44 /* Clean inherited cached thread tid. */
45 task->thread->tid = 0;
46
47 process->pid = nxt_pid;
48
49 rt->types = 0;
50
51 nxt_port_reset_next_id();
52
53 nxt_event_engine_thread_adopt(task->thread->engine);
54
55 /* Remove not ready processes */
56 nxt_runtime_process_each(rt, p) {
57
58 if (!p->ready) {
59 nxt_debug(task, "remove not ready process %PI", p->pid);
60
1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7#include <nxt_main.h>
8#include <nxt_main_process.h>
9
10
11static void nxt_process_start(nxt_task_t *task, nxt_process_t *process);
12static nxt_int_t nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc);
13
14
15/* A cached process pid. */
16nxt_pid_t nxt_pid;
17
18/* An original parent process pid. */
19nxt_pid_t nxt_ppid;
20
21
22nxt_pid_t
23nxt_process_create(nxt_task_t *task, nxt_process_t *process)
24{
25 nxt_pid_t pid;
26 nxt_process_t *p;
27 nxt_runtime_t *rt;
28
29 rt = task->thread->runtime;
30
31 pid = fork();
32
33 switch (pid) {
34
35 case -1:
36 nxt_log(task, NXT_LOG_CRIT, "fork() failed while creating \"%s\" %E",
37 process->init->name, nxt_errno);
38 break;
39
40 case 0:
41 /* A child. */
42 nxt_pid = getpid();
43
44 /* Clean inherited cached thread tid. */
45 task->thread->tid = 0;
46
47 process->pid = nxt_pid;
48
49 rt->types = 0;
50
51 nxt_port_reset_next_id();
52
53 nxt_event_engine_thread_adopt(task->thread->engine);
54
55 /* Remove not ready processes */
56 nxt_runtime_process_each(rt, p) {
57
58 if (!p->ready) {
59 nxt_debug(task, "remove not ready process %PI", p->pid);
60
61 nxt_runtime_process_remove(rt, p);
61 nxt_runtime_process_remove(task, p);
62
63 } else {
64 nxt_port_mmaps_destroy(p->incoming, 0);
65 nxt_port_mmaps_destroy(p->outgoing, 0);
66 }
67
68 } nxt_runtime_process_loop;
69
62
63 } else {
64 nxt_port_mmaps_destroy(p->incoming, 0);
65 nxt_port_mmaps_destroy(p->outgoing, 0);
66 }
67
68 } nxt_runtime_process_loop;
69
70 nxt_runtime_process_add(rt, process);
70 nxt_runtime_process_add(task, process);
71
72 nxt_process_start(task, process);
73
74 process->ready = 1;
75
76 break;
77
78 default:
79 /* A parent. */
80 nxt_debug(task, "fork(\"%s\"): %PI", process->init->name, pid);
81
82 process->pid = pid;
83
71
72 nxt_process_start(task, process);
73
74 process->ready = 1;
75
76 break;
77
78 default:
79 /* A parent. */
80 nxt_debug(task, "fork(\"%s\"): %PI", process->init->name, pid);
81
82 process->pid = pid;
83
84 nxt_runtime_process_add(rt, process);
84 nxt_runtime_process_add(task, process);
85
86 break;
87 }
88
89 return pid;
90}
91
92
93static void
94nxt_process_start(nxt_task_t *task, nxt_process_t *process)
95{
96 nxt_int_t ret;
97 nxt_port_t *port, *main_port;
98 nxt_thread_t *thread;
99 nxt_runtime_t *rt;
100 nxt_process_init_t *init;
101 nxt_event_engine_t *engine;
102 const nxt_event_interface_t *interface;
103
104 init = process->init;
105
106 nxt_log(task, NXT_LOG_INFO, "%s started", init->name);
107
108 nxt_process_title(task, "unit: %s", init->name);
109
110 thread = task->thread;
111
112 nxt_random_init(&thread->random);
113
114 if (init->user_cred != NULL && getuid() == 0) {
115 /* Super-user. */
116
117 ret = nxt_user_cred_set(task, init->user_cred);
118 if (ret != NXT_OK) {
119 goto fail;
120 }
121 }
122
123 rt = thread->runtime;
124
125 rt->types |= (1U << init->type);
126
127 engine = thread->engine;
128
129 /* Update inherited main process event engine and signals processing. */
130 engine->signals->sigev = init->signals;
131
132 interface = nxt_service_get(rt->services, "engine", rt->engine);
133 if (nxt_slow_path(interface == NULL)) {
134 goto fail;
135 }
136
137 if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) {
138 goto fail;
139 }
140
141 ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads,
142 60000 * 1000000LL);
143 if (nxt_slow_path(ret != NXT_OK)) {
144 goto fail;
145 }
146
147 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
148
149 nxt_port_read_close(main_port);
150 nxt_port_write_enable(task, main_port);
151
152 port = nxt_process_port_first(process);
153
154 nxt_port_write_close(port);
155
156 ret = init->start(task, init->data);
157
158 if (nxt_slow_path(ret != NXT_OK)) {
159 goto fail;
160 }
161
162 nxt_port_enable(task, port, init->port_handlers);
163
164 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY,
165 -1, init->stream, 0, NULL);
166
167 if (nxt_slow_path(ret != NXT_OK)) {
168 nxt_log(task, NXT_LOG_ERR, "failed to send READY message to main");
169
170 goto fail;
171 }
172
173 return;
174
175fail:
176
177 exit(1);
178}
179
180
181#if (NXT_HAVE_POSIX_SPAWN)
182
183/*
184 * Linux glibc 2.2 posix_spawn() is implemented via fork()/execve().
185 * Linux glibc 2.4 posix_spawn() without file actions and spawn
186 * attributes uses vfork()/execve().
187 *
188 * On FreeBSD 8.0 posix_spawn() is implemented via vfork()/execve().
189 *
190 * Solaris 10:
191 * In the Solaris 10 OS, posix_spawn() is currently implemented using
192 * private-to-libc vfork(), execve(), and exit() functions. They are
193 * identical to regular vfork(), execve(), and exit() in functionality,
194 * but they are not exported from libc and therefore don't cause the
195 * deadlock-in-the-dynamic-linker problem that any multithreaded code
196 * outside of libc that calls vfork() can cause.
197 *
198 * On MacOSX 10.5 (Leoprad) and NetBSD 6.0 posix_spawn() is implemented
199 * as syscall.
200 */
201
202nxt_pid_t
203nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
204{
205 nxt_pid_t pid;
206
207 nxt_debug(task, "posix_spawn(\"%s\")", name);
208
209 if (posix_spawn(&pid, name, NULL, NULL, argv, envp) != 0) {
210 nxt_log(task, NXT_LOG_CRIT, "posix_spawn(\"%s\") failed %E",
211 name, nxt_errno);
212 return -1;
213 }
214
215 return pid;
216}
217
218#else
219
220nxt_pid_t
221nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
222{
223 nxt_pid_t pid;
224
225 /*
226 * vfork() is better than fork() because:
227 * it is faster several times;
228 * its execution time does not depend on private memory mapping size;
229 * it has lesser chances to fail due to the ENOMEM error.
230 */
231
232 pid = vfork();
233
234 switch (pid) {
235
236 case -1:
237 nxt_log(task, NXT_LOG_CRIT, "vfork() failed while executing \"%s\" %E",
238 name, nxt_errno);
239 break;
240
241 case 0:
242 /* A child. */
243 nxt_debug(task, "execve(\"%s\")", name);
244
245 (void) execve(name, argv, envp);
246
247 nxt_log(task, NXT_LOG_CRIT, "execve(\"%s\") failed %E",
248 name, nxt_errno);
249
250 exit(1);
251 nxt_unreachable();
252 break;
253
254 default:
255 /* A parent. */
256 nxt_debug(task, "vfork(): %PI", pid);
257 break;
258 }
259
260 return pid;
261}
262
263#endif
264
265
266nxt_int_t
267nxt_process_daemon(nxt_task_t *task)
268{
269 nxt_fd_t fd;
270 nxt_pid_t pid;
271 const char *msg;
272
273 /*
274 * fork() followed by a parent process's exit() detaches a child process
275 * from an init script or terminal shell process which has started the
276 * parent process and allows the child process to run in background.
277 */
278
279 pid = fork();
280
281 switch (pid) {
282
283 case -1:
284 msg = "fork() failed %E";
285 goto fail;
286
287 case 0:
288 /* A child. */
289 break;
290
291 default:
292 /* A parent. */
293 nxt_debug(task, "fork(): %PI", pid);
294 exit(0);
295 nxt_unreachable();
296 }
297
298 nxt_pid = getpid();
299
300 /* Clean inherited cached thread tid. */
301 task->thread->tid = 0;
302
303 nxt_debug(task, "daemon");
304
305 /* Detach from controlling terminal. */
306
307 if (setsid() == -1) {
308 nxt_log(task, NXT_LOG_CRIT, "setsid() failed %E", nxt_errno);
309 return NXT_ERROR;
310 }
311
312 /*
313 * Reset file mode creation mask: any access
314 * rights can be set on file creation.
315 */
316 umask(0);
317
318 /* Redirect STDIN and STDOUT to the "/dev/null". */
319
320 fd = open("/dev/null", O_RDWR);
321 if (fd == -1) {
322 msg = "open(\"/dev/null\") failed %E";
323 goto fail;
324 }
325
326 if (dup2(fd, STDIN_FILENO) == -1) {
327 msg = "dup2(\"/dev/null\", STDIN) failed %E";
328 goto fail;
329 }
330
331 if (dup2(fd, STDOUT_FILENO) == -1) {
332 msg = "dup2(\"/dev/null\", STDOUT) failed %E";
333 goto fail;
334 }
335
336 if (fd > STDERR_FILENO) {
337 nxt_fd_close(fd);
338 }
339
340 return NXT_OK;
341
342fail:
343
344 nxt_log(task, NXT_LOG_CRIT, msg, nxt_errno);
345
346 return NXT_ERROR;
347}
348
349
350void
351nxt_nanosleep(nxt_nsec_t ns)
352{
353 struct timespec ts;
354
355 ts.tv_sec = ns / 1000000000;
356 ts.tv_nsec = ns % 1000000000;
357
358 (void) nanosleep(&ts, NULL);
359}
360
361
362nxt_int_t
363nxt_user_cred_get(nxt_task_t *task, nxt_user_cred_t *uc, const char *group)
364{
365 struct group *grp;
366 struct passwd *pwd;
367
368 nxt_errno = 0;
369
370 pwd = getpwnam(uc->user);
371
372 if (nxt_slow_path(pwd == NULL)) {
373
374 if (nxt_errno == 0) {
375 nxt_log(task, NXT_LOG_CRIT,
376 "getpwnam(\"%s\") failed, user \"%s\" not found",
377 uc->user, uc->user);
378 } else {
379 nxt_log(task, NXT_LOG_CRIT, "getpwnam(\"%s\") failed %E",
380 uc->user, nxt_errno);
381 }
382
383 return NXT_ERROR;
384 }
385
386 uc->uid = pwd->pw_uid;
387 uc->base_gid = pwd->pw_gid;
388
389 if (group != NULL && group[0] != '\0') {
390 nxt_errno = 0;
391
392 grp = getgrnam(group);
393
394 if (nxt_slow_path(grp == NULL)) {
395
396 if (nxt_errno == 0) {
397 nxt_log(task, NXT_LOG_CRIT,
398 "getgrnam(\"%s\") failed, group \"%s\" not found",
399 group, group);
400 } else {
401 nxt_log(task, NXT_LOG_CRIT, "getgrnam(\"%s\") failed %E",
402 group, nxt_errno);
403 }
404
405 return NXT_ERROR;
406 }
407
408 uc->base_gid = grp->gr_gid;
409 }
410
411 if (getuid() == 0) {
412 return nxt_user_groups_get(task, uc);
413 }
414
415 return NXT_OK;
416}
417
418
419/*
420 * nxt_user_groups_get() stores an array of groups IDs which should be
421 * set by the initgroups() function for a given user. The initgroups()
422 * may block a just forked worker process for some time if LDAP or NDIS+
423 * is used, so nxt_user_groups_get() allows to get worker user groups in
424 * main process. In a nutshell the initgroups() calls getgrouplist()
425 * followed by setgroups(). However Solaris lacks the getgrouplist().
426 * Besides getgrouplist() does not allow to query the exact number of
427 * groups while NGROUPS_MAX can be quite large (e.g. 65536 on Linux).
428 * So nxt_user_groups_get() emulates getgrouplist(): at first the function
429 * saves the super-user groups IDs, then calls initgroups() and saves the
430 * specified user groups IDs, and then restores the super-user groups IDs.
431 * This works at least on Linux, FreeBSD, and Solaris, but does not work
432 * on MacOSX, getgroups(2):
433 *
434 * To provide compatibility with applications that use getgroups() in
435 * environments where users may be in more than {NGROUPS_MAX} groups,
436 * a variant of getgroups(), obtained when compiling with either the
437 * macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can
438 * be used that is not limited to {NGROUPS_MAX} groups. However, this
439 * variant only returns the user's default group access list and not
440 * the group list modified by a call to setgroups(2).
441 *
442 * For such cases initgroups() is used in worker process as fallback.
443 */
444
445static nxt_int_t
446nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
447{
448 int nsaved, ngroups;
449 nxt_int_t ret;
450 nxt_gid_t *saved;
451
452 nsaved = getgroups(0, NULL);
453
454 if (nsaved == -1) {
455 nxt_log(task, NXT_LOG_CRIT, "getgroups(0, NULL) failed %E", nxt_errno);
456 return NXT_ERROR;
457 }
458
459 nxt_debug(task, "getgroups(0, NULL): %d", nsaved);
460
461 if (nsaved > NGROUPS_MAX) {
462 /* MacOSX case. */
463 return NXT_OK;
464 }
465
466 saved = nxt_malloc(nsaved * sizeof(nxt_gid_t));
467
468 if (saved == NULL) {
469 return NXT_ERROR;
470 }
471
472 ret = NXT_ERROR;
473
474 nsaved = getgroups(nsaved, saved);
475
476 if (nsaved == -1) {
477 nxt_log(task, NXT_LOG_CRIT, "getgroups(%d) failed %E",
478 nsaved, nxt_errno);
479 goto fail;
480 }
481
482 nxt_debug(task, "getgroups(): %d", nsaved);
483
484 if (initgroups(uc->user, uc->base_gid) != 0) {
485 nxt_log(task, NXT_LOG_CRIT, "initgroups(%s, %d) failed",
486 uc->user, uc->base_gid);
487 goto restore;
488 }
489
490 ngroups = getgroups(0, NULL);
491
492 if (ngroups == -1) {
493 nxt_log(task, NXT_LOG_CRIT, "getgroups(0, NULL) failed %E", nxt_errno);
494 goto restore;
495 }
496
497 nxt_debug(task, "getgroups(0, NULL): %d", ngroups);
498
499 uc->gids = nxt_malloc(ngroups * sizeof(nxt_gid_t));
500
501 if (uc->gids == NULL) {
502 goto restore;
503 }
504
505 ngroups = getgroups(ngroups, uc->gids);
506
507 if (ngroups == -1) {
508 nxt_log(task, NXT_LOG_CRIT, "getgroups(%d) failed %E",
509 ngroups, nxt_errno);
510 goto restore;
511 }
512
513 uc->ngroups = ngroups;
514
515#if (NXT_DEBUG)
516 {
517 u_char *p, *end;
518 nxt_uint_t i;
519 u_char msg[NXT_MAX_ERROR_STR];
520
521 p = msg;
522 end = msg + NXT_MAX_ERROR_STR;
523
524 for (i = 0; i < uc->ngroups; i++) {
525 p = nxt_sprintf(p, end, "%uL:", (uint64_t) uc->gids[i]);
526 }
527
528 nxt_debug(task, "user \"%s\" cred: uid:%uL base gid:%uL, gids:%*s",
529 uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid,
530 p - msg, msg);
531 }
532#endif
533
534 ret = NXT_OK;
535
536restore:
537
538 if (setgroups(nsaved, saved) != 0) {
539 nxt_log(task, NXT_LOG_CRIT, "setgroups(%d) failed %E",
540 nsaved, nxt_errno);
541 ret = NXT_ERROR;
542 }
543
544fail:
545
546 nxt_free(saved);
547
548 return ret;
549}
550
551
552nxt_int_t
553nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
554{
555 nxt_debug(task, "user cred set: \"%s\" uid:%uL base gid:%uL",
556 uc->user, (uint64_t) uc->uid, uc->base_gid);
557
558 if (setgid(uc->base_gid) != 0) {
559 nxt_log(task, NXT_LOG_CRIT, "setgid(%d) failed %E",
560 uc->base_gid, nxt_errno);
561 return NXT_ERROR;
562 }
563
564 if (uc->gids != NULL) {
565 if (setgroups(uc->ngroups, uc->gids) != 0) {
566 nxt_log(task, NXT_LOG_CRIT, "setgroups(%i) failed %E",
567 uc->ngroups, nxt_errno);
568 return NXT_ERROR;
569 }
570
571 } else {
572 /* MacOSX fallback. */
573 if (initgroups(uc->user, uc->base_gid) != 0) {
574 nxt_log(task, NXT_LOG_CRIT, "initgroups(%s, %d) failed",
575 uc->user, uc->base_gid);
576 return NXT_ERROR;
577 }
578 }
579
580 if (setuid(uc->uid) != 0) {
581 nxt_log(task, NXT_LOG_CRIT, "setuid(%d) failed %E", uc->uid, nxt_errno);
582 return NXT_ERROR;
583 }
584
585 return NXT_OK;
586}
587
588
589static void
590nxt_process_port_mp_cleanup(nxt_task_t *task, void *obj, void *data)
591{
85
86 break;
87 }
88
89 return pid;
90}
91
92
93static void
94nxt_process_start(nxt_task_t *task, nxt_process_t *process)
95{
96 nxt_int_t ret;
97 nxt_port_t *port, *main_port;
98 nxt_thread_t *thread;
99 nxt_runtime_t *rt;
100 nxt_process_init_t *init;
101 nxt_event_engine_t *engine;
102 const nxt_event_interface_t *interface;
103
104 init = process->init;
105
106 nxt_log(task, NXT_LOG_INFO, "%s started", init->name);
107
108 nxt_process_title(task, "unit: %s", init->name);
109
110 thread = task->thread;
111
112 nxt_random_init(&thread->random);
113
114 if (init->user_cred != NULL && getuid() == 0) {
115 /* Super-user. */
116
117 ret = nxt_user_cred_set(task, init->user_cred);
118 if (ret != NXT_OK) {
119 goto fail;
120 }
121 }
122
123 rt = thread->runtime;
124
125 rt->types |= (1U << init->type);
126
127 engine = thread->engine;
128
129 /* Update inherited main process event engine and signals processing. */
130 engine->signals->sigev = init->signals;
131
132 interface = nxt_service_get(rt->services, "engine", rt->engine);
133 if (nxt_slow_path(interface == NULL)) {
134 goto fail;
135 }
136
137 if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) {
138 goto fail;
139 }
140
141 ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads,
142 60000 * 1000000LL);
143 if (nxt_slow_path(ret != NXT_OK)) {
144 goto fail;
145 }
146
147 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
148
149 nxt_port_read_close(main_port);
150 nxt_port_write_enable(task, main_port);
151
152 port = nxt_process_port_first(process);
153
154 nxt_port_write_close(port);
155
156 ret = init->start(task, init->data);
157
158 if (nxt_slow_path(ret != NXT_OK)) {
159 goto fail;
160 }
161
162 nxt_port_enable(task, port, init->port_handlers);
163
164 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY,
165 -1, init->stream, 0, NULL);
166
167 if (nxt_slow_path(ret != NXT_OK)) {
168 nxt_log(task, NXT_LOG_ERR, "failed to send READY message to main");
169
170 goto fail;
171 }
172
173 return;
174
175fail:
176
177 exit(1);
178}
179
180
181#if (NXT_HAVE_POSIX_SPAWN)
182
183/*
184 * Linux glibc 2.2 posix_spawn() is implemented via fork()/execve().
185 * Linux glibc 2.4 posix_spawn() without file actions and spawn
186 * attributes uses vfork()/execve().
187 *
188 * On FreeBSD 8.0 posix_spawn() is implemented via vfork()/execve().
189 *
190 * Solaris 10:
191 * In the Solaris 10 OS, posix_spawn() is currently implemented using
192 * private-to-libc vfork(), execve(), and exit() functions. They are
193 * identical to regular vfork(), execve(), and exit() in functionality,
194 * but they are not exported from libc and therefore don't cause the
195 * deadlock-in-the-dynamic-linker problem that any multithreaded code
196 * outside of libc that calls vfork() can cause.
197 *
198 * On MacOSX 10.5 (Leoprad) and NetBSD 6.0 posix_spawn() is implemented
199 * as syscall.
200 */
201
202nxt_pid_t
203nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
204{
205 nxt_pid_t pid;
206
207 nxt_debug(task, "posix_spawn(\"%s\")", name);
208
209 if (posix_spawn(&pid, name, NULL, NULL, argv, envp) != 0) {
210 nxt_log(task, NXT_LOG_CRIT, "posix_spawn(\"%s\") failed %E",
211 name, nxt_errno);
212 return -1;
213 }
214
215 return pid;
216}
217
218#else
219
220nxt_pid_t
221nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
222{
223 nxt_pid_t pid;
224
225 /*
226 * vfork() is better than fork() because:
227 * it is faster several times;
228 * its execution time does not depend on private memory mapping size;
229 * it has lesser chances to fail due to the ENOMEM error.
230 */
231
232 pid = vfork();
233
234 switch (pid) {
235
236 case -1:
237 nxt_log(task, NXT_LOG_CRIT, "vfork() failed while executing \"%s\" %E",
238 name, nxt_errno);
239 break;
240
241 case 0:
242 /* A child. */
243 nxt_debug(task, "execve(\"%s\")", name);
244
245 (void) execve(name, argv, envp);
246
247 nxt_log(task, NXT_LOG_CRIT, "execve(\"%s\") failed %E",
248 name, nxt_errno);
249
250 exit(1);
251 nxt_unreachable();
252 break;
253
254 default:
255 /* A parent. */
256 nxt_debug(task, "vfork(): %PI", pid);
257 break;
258 }
259
260 return pid;
261}
262
263#endif
264
265
266nxt_int_t
267nxt_process_daemon(nxt_task_t *task)
268{
269 nxt_fd_t fd;
270 nxt_pid_t pid;
271 const char *msg;
272
273 /*
274 * fork() followed by a parent process's exit() detaches a child process
275 * from an init script or terminal shell process which has started the
276 * parent process and allows the child process to run in background.
277 */
278
279 pid = fork();
280
281 switch (pid) {
282
283 case -1:
284 msg = "fork() failed %E";
285 goto fail;
286
287 case 0:
288 /* A child. */
289 break;
290
291 default:
292 /* A parent. */
293 nxt_debug(task, "fork(): %PI", pid);
294 exit(0);
295 nxt_unreachable();
296 }
297
298 nxt_pid = getpid();
299
300 /* Clean inherited cached thread tid. */
301 task->thread->tid = 0;
302
303 nxt_debug(task, "daemon");
304
305 /* Detach from controlling terminal. */
306
307 if (setsid() == -1) {
308 nxt_log(task, NXT_LOG_CRIT, "setsid() failed %E", nxt_errno);
309 return NXT_ERROR;
310 }
311
312 /*
313 * Reset file mode creation mask: any access
314 * rights can be set on file creation.
315 */
316 umask(0);
317
318 /* Redirect STDIN and STDOUT to the "/dev/null". */
319
320 fd = open("/dev/null", O_RDWR);
321 if (fd == -1) {
322 msg = "open(\"/dev/null\") failed %E";
323 goto fail;
324 }
325
326 if (dup2(fd, STDIN_FILENO) == -1) {
327 msg = "dup2(\"/dev/null\", STDIN) failed %E";
328 goto fail;
329 }
330
331 if (dup2(fd, STDOUT_FILENO) == -1) {
332 msg = "dup2(\"/dev/null\", STDOUT) failed %E";
333 goto fail;
334 }
335
336 if (fd > STDERR_FILENO) {
337 nxt_fd_close(fd);
338 }
339
340 return NXT_OK;
341
342fail:
343
344 nxt_log(task, NXT_LOG_CRIT, msg, nxt_errno);
345
346 return NXT_ERROR;
347}
348
349
350void
351nxt_nanosleep(nxt_nsec_t ns)
352{
353 struct timespec ts;
354
355 ts.tv_sec = ns / 1000000000;
356 ts.tv_nsec = ns % 1000000000;
357
358 (void) nanosleep(&ts, NULL);
359}
360
361
362nxt_int_t
363nxt_user_cred_get(nxt_task_t *task, nxt_user_cred_t *uc, const char *group)
364{
365 struct group *grp;
366 struct passwd *pwd;
367
368 nxt_errno = 0;
369
370 pwd = getpwnam(uc->user);
371
372 if (nxt_slow_path(pwd == NULL)) {
373
374 if (nxt_errno == 0) {
375 nxt_log(task, NXT_LOG_CRIT,
376 "getpwnam(\"%s\") failed, user \"%s\" not found",
377 uc->user, uc->user);
378 } else {
379 nxt_log(task, NXT_LOG_CRIT, "getpwnam(\"%s\") failed %E",
380 uc->user, nxt_errno);
381 }
382
383 return NXT_ERROR;
384 }
385
386 uc->uid = pwd->pw_uid;
387 uc->base_gid = pwd->pw_gid;
388
389 if (group != NULL && group[0] != '\0') {
390 nxt_errno = 0;
391
392 grp = getgrnam(group);
393
394 if (nxt_slow_path(grp == NULL)) {
395
396 if (nxt_errno == 0) {
397 nxt_log(task, NXT_LOG_CRIT,
398 "getgrnam(\"%s\") failed, group \"%s\" not found",
399 group, group);
400 } else {
401 nxt_log(task, NXT_LOG_CRIT, "getgrnam(\"%s\") failed %E",
402 group, nxt_errno);
403 }
404
405 return NXT_ERROR;
406 }
407
408 uc->base_gid = grp->gr_gid;
409 }
410
411 if (getuid() == 0) {
412 return nxt_user_groups_get(task, uc);
413 }
414
415 return NXT_OK;
416}
417
418
419/*
420 * nxt_user_groups_get() stores an array of groups IDs which should be
421 * set by the initgroups() function for a given user. The initgroups()
422 * may block a just forked worker process for some time if LDAP or NDIS+
423 * is used, so nxt_user_groups_get() allows to get worker user groups in
424 * main process. In a nutshell the initgroups() calls getgrouplist()
425 * followed by setgroups(). However Solaris lacks the getgrouplist().
426 * Besides getgrouplist() does not allow to query the exact number of
427 * groups while NGROUPS_MAX can be quite large (e.g. 65536 on Linux).
428 * So nxt_user_groups_get() emulates getgrouplist(): at first the function
429 * saves the super-user groups IDs, then calls initgroups() and saves the
430 * specified user groups IDs, and then restores the super-user groups IDs.
431 * This works at least on Linux, FreeBSD, and Solaris, but does not work
432 * on MacOSX, getgroups(2):
433 *
434 * To provide compatibility with applications that use getgroups() in
435 * environments where users may be in more than {NGROUPS_MAX} groups,
436 * a variant of getgroups(), obtained when compiling with either the
437 * macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can
438 * be used that is not limited to {NGROUPS_MAX} groups. However, this
439 * variant only returns the user's default group access list and not
440 * the group list modified by a call to setgroups(2).
441 *
442 * For such cases initgroups() is used in worker process as fallback.
443 */
444
445static nxt_int_t
446nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
447{
448 int nsaved, ngroups;
449 nxt_int_t ret;
450 nxt_gid_t *saved;
451
452 nsaved = getgroups(0, NULL);
453
454 if (nsaved == -1) {
455 nxt_log(task, NXT_LOG_CRIT, "getgroups(0, NULL) failed %E", nxt_errno);
456 return NXT_ERROR;
457 }
458
459 nxt_debug(task, "getgroups(0, NULL): %d", nsaved);
460
461 if (nsaved > NGROUPS_MAX) {
462 /* MacOSX case. */
463 return NXT_OK;
464 }
465
466 saved = nxt_malloc(nsaved * sizeof(nxt_gid_t));
467
468 if (saved == NULL) {
469 return NXT_ERROR;
470 }
471
472 ret = NXT_ERROR;
473
474 nsaved = getgroups(nsaved, saved);
475
476 if (nsaved == -1) {
477 nxt_log(task, NXT_LOG_CRIT, "getgroups(%d) failed %E",
478 nsaved, nxt_errno);
479 goto fail;
480 }
481
482 nxt_debug(task, "getgroups(): %d", nsaved);
483
484 if (initgroups(uc->user, uc->base_gid) != 0) {
485 nxt_log(task, NXT_LOG_CRIT, "initgroups(%s, %d) failed",
486 uc->user, uc->base_gid);
487 goto restore;
488 }
489
490 ngroups = getgroups(0, NULL);
491
492 if (ngroups == -1) {
493 nxt_log(task, NXT_LOG_CRIT, "getgroups(0, NULL) failed %E", nxt_errno);
494 goto restore;
495 }
496
497 nxt_debug(task, "getgroups(0, NULL): %d", ngroups);
498
499 uc->gids = nxt_malloc(ngroups * sizeof(nxt_gid_t));
500
501 if (uc->gids == NULL) {
502 goto restore;
503 }
504
505 ngroups = getgroups(ngroups, uc->gids);
506
507 if (ngroups == -1) {
508 nxt_log(task, NXT_LOG_CRIT, "getgroups(%d) failed %E",
509 ngroups, nxt_errno);
510 goto restore;
511 }
512
513 uc->ngroups = ngroups;
514
515#if (NXT_DEBUG)
516 {
517 u_char *p, *end;
518 nxt_uint_t i;
519 u_char msg[NXT_MAX_ERROR_STR];
520
521 p = msg;
522 end = msg + NXT_MAX_ERROR_STR;
523
524 for (i = 0; i < uc->ngroups; i++) {
525 p = nxt_sprintf(p, end, "%uL:", (uint64_t) uc->gids[i]);
526 }
527
528 nxt_debug(task, "user \"%s\" cred: uid:%uL base gid:%uL, gids:%*s",
529 uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid,
530 p - msg, msg);
531 }
532#endif
533
534 ret = NXT_OK;
535
536restore:
537
538 if (setgroups(nsaved, saved) != 0) {
539 nxt_log(task, NXT_LOG_CRIT, "setgroups(%d) failed %E",
540 nsaved, nxt_errno);
541 ret = NXT_ERROR;
542 }
543
544fail:
545
546 nxt_free(saved);
547
548 return ret;
549}
550
551
552nxt_int_t
553nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
554{
555 nxt_debug(task, "user cred set: \"%s\" uid:%uL base gid:%uL",
556 uc->user, (uint64_t) uc->uid, uc->base_gid);
557
558 if (setgid(uc->base_gid) != 0) {
559 nxt_log(task, NXT_LOG_CRIT, "setgid(%d) failed %E",
560 uc->base_gid, nxt_errno);
561 return NXT_ERROR;
562 }
563
564 if (uc->gids != NULL) {
565 if (setgroups(uc->ngroups, uc->gids) != 0) {
566 nxt_log(task, NXT_LOG_CRIT, "setgroups(%i) failed %E",
567 uc->ngroups, nxt_errno);
568 return NXT_ERROR;
569 }
570
571 } else {
572 /* MacOSX fallback. */
573 if (initgroups(uc->user, uc->base_gid) != 0) {
574 nxt_log(task, NXT_LOG_CRIT, "initgroups(%s, %d) failed",
575 uc->user, uc->base_gid);
576 return NXT_ERROR;
577 }
578 }
579
580 if (setuid(uc->uid) != 0) {
581 nxt_log(task, NXT_LOG_CRIT, "setuid(%d) failed %E", uc->uid, nxt_errno);
582 return NXT_ERROR;
583 }
584
585 return NXT_OK;
586}
587
588
589static void
590nxt_process_port_mp_cleanup(nxt_task_t *task, void *obj, void *data)
591{
592 nxt_runtime_t *rt;
593 nxt_process_t *process;
594
595 process = obj;
592 nxt_process_t *process;
593
594 process = obj;
596 rt = data;
597
598 process->port_cleanups--;
599
600 if (process->port_cleanups == 0) {
595
596 process->port_cleanups--;
597
598 if (process->port_cleanups == 0) {
601 nxt_runtime_process_remove(rt, process);
599 nxt_runtime_process_remove(task, process);
602 }
603}
604
605void
606nxt_process_port_add(nxt_task_t *task, nxt_process_t *process, nxt_port_t *port)
607{
608 port->process = process;
609 nxt_queue_insert_tail(&process->ports, &port->link);
610
611 nxt_mp_cleanup(port->mem_pool, nxt_process_port_mp_cleanup, task, process,
600 }
601}
602
603void
604nxt_process_port_add(nxt_task_t *task, nxt_process_t *process, nxt_port_t *port)
605{
606 port->process = process;
607 nxt_queue_insert_tail(&process->ports, &port->link);
608
609 nxt_mp_cleanup(port->mem_pool, nxt_process_port_mp_cleanup, task, process,
612 task->thread->runtime);
610 NULL);
613 process->port_cleanups++;
614}
615
616
617void
618nxt_process_connected_port_add(nxt_process_t *process, nxt_port_t *port)
619{
620 nxt_thread_mutex_lock(&process->cp_mutex);
621
622 nxt_port_hash_add(&process->connected_ports, port);
623
624 nxt_thread_mutex_unlock(&process->cp_mutex);
625}
626
627void
628nxt_process_connected_port_remove(nxt_process_t *process, nxt_port_t *port)
629{
630 nxt_thread_mutex_lock(&process->cp_mutex);
631
632 nxt_port_hash_remove(&process->connected_ports, port);
633
634 nxt_thread_mutex_unlock(&process->cp_mutex);
635}
636
637nxt_port_t *
638nxt_process_connected_port_find(nxt_process_t *process, nxt_pid_t pid,
639 nxt_port_id_t port_id)
640{
641 nxt_port_t *res;
642
643 nxt_thread_mutex_lock(&process->cp_mutex);
644
645 res = nxt_port_hash_find(&process->connected_ports, pid, port_id);
646
647 nxt_thread_mutex_unlock(&process->cp_mutex);
648
649 return res;
650}
611 process->port_cleanups++;
612}
613
614
615void
616nxt_process_connected_port_add(nxt_process_t *process, nxt_port_t *port)
617{
618 nxt_thread_mutex_lock(&process->cp_mutex);
619
620 nxt_port_hash_add(&process->connected_ports, port);
621
622 nxt_thread_mutex_unlock(&process->cp_mutex);
623}
624
625void
626nxt_process_connected_port_remove(nxt_process_t *process, nxt_port_t *port)
627{
628 nxt_thread_mutex_lock(&process->cp_mutex);
629
630 nxt_port_hash_remove(&process->connected_ports, port);
631
632 nxt_thread_mutex_unlock(&process->cp_mutex);
633}
634
635nxt_port_t *
636nxt_process_connected_port_find(nxt_process_t *process, nxt_pid_t pid,
637 nxt_port_id_t port_id)
638{
639 nxt_port_t *res;
640
641 nxt_thread_mutex_lock(&process->cp_mutex);
642
643 res = nxt_port_hash_find(&process->connected_ports, pid, port_id);
644
645 nxt_thread_mutex_unlock(&process->cp_mutex);
646
647 return res;
648}