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 #if (NXT_HAVE_CLONE) 11 #include <nxt_clone.h> 12 #endif 13 14 #include <signal.h> 15 16 static void nxt_process_start(nxt_task_t *task, nxt_process_t *process); 17 static nxt_int_t nxt_user_groups_get(nxt_task_t *task, nxt_mp_t *mp, 18 nxt_user_cred_t *uc); 19 static nxt_int_t nxt_process_worker_setup(nxt_task_t *task, 20 nxt_process_t *process, int parentfd); 21 22 /* A cached process pid. */ 23 nxt_pid_t nxt_pid; 24 25 /* An original parent process pid. */ 26 nxt_pid_t nxt_ppid; 27 28 nxt_bool_t nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = { 29 { 1, 1, 1, 1, 1 }, 30 { 1, 0, 0, 0, 0 }, 31 { 1, 0, 0, 1, 0 }, 32 { 1, 0, 1, 0, 1 }, 33 { 1, 0, 0, 0, 0 }, 34 }; 35 36 nxt_bool_t nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = { 37 { 0, 0, 0, 0, 0 }, 38 { 0, 0, 0, 0, 0 }, 39 { 0, 0, 0, 1, 0 }, 40 { 0, 0, 1, 0, 1 }, 41 { 0, 0, 0, 1, 0 }, 42 }; 43 44 45 static nxt_int_t 46 nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd) 47 { 48 pid_t rpid, pid; 49 ssize_t n; 50 nxt_int_t parent_status; 51 nxt_process_t *p; 52 nxt_runtime_t *rt; 53 nxt_process_init_t *init; 54 nxt_process_type_t ptype; 55 56 pid = getpid(); 57 rpid = 0; 58 rt = task->thread->runtime; 59 init = process->init; 60 61 /* Setup the worker process. */ 62 63 n = read(parentfd, &rpid, sizeof(rpid)); 64 if (nxt_slow_path(n == -1 || n != sizeof(rpid))) { 65 nxt_alert(task, "failed to read real pid"); 66 return NXT_ERROR; 67 } 68 69 if (nxt_slow_path(rpid == 0)) { 70 nxt_alert(task, "failed to get real pid from parent"); 71 return NXT_ERROR; 72 } 73 74 nxt_pid = rpid; 75 76 /* Clean inherited cached thread tid. */ 77 task->thread->tid = 0; 78 79 process->pid = nxt_pid; 80 81 if (nxt_pid != pid) { 82 nxt_debug(task, "app \"%s\" real pid %d", init->name, nxt_pid); 83 nxt_debug(task, "app \"%s\" isolated pid: %d", init->name, pid); 84 } 85 86 n = read(parentfd, &parent_status, sizeof(parent_status)); 87 if (nxt_slow_path(n == -1 || n != sizeof(parent_status))) { 88 nxt_alert(task, "failed to read parent status"); 89 return NXT_ERROR; 90 } 91 92 if (nxt_slow_path(parent_status != NXT_OK)) { 93 return parent_status; 94 } 95 96 ptype = init->type; 97 98 nxt_port_reset_next_id(); 99 100 nxt_event_engine_thread_adopt(task->thread->engine); 101 102 /* Remove not ready processes. */ 103 nxt_runtime_process_each(rt, p) { 104 105 if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0) { 106 nxt_debug(task, "remove not required process %PI", p->pid); 107 108 nxt_process_close_ports(task, p); 109 110 continue; 111 } 112 113 if (!p->ready) { 114 nxt_debug(task, "remove not ready process %PI", p->pid); 115 116 nxt_process_close_ports(task, p); 117 118 continue; 119 } 120 121 nxt_port_mmaps_destroy(&p->incoming, 0); 122 nxt_port_mmaps_destroy(&p->outgoing, 0); 123 124 } nxt_runtime_process_loop; 125 126 nxt_runtime_process_add(task, process); 127 128 nxt_process_start(task, process); 129 130 process->ready = 1; 131 132 return NXT_OK; 133 } 134 135 136 nxt_pid_t 137 nxt_process_create(nxt_task_t *task, nxt_process_t *process) 138 { 139 int pipefd[2]; 140 nxt_int_t ret; 141 nxt_pid_t pid; 142 nxt_process_init_t *init; 143 144 if (nxt_slow_path(pipe(pipefd) == -1)) { 145 nxt_alert(task, "failed to create process pipe for passing rpid"); 146 return -1; 147 } 148 149 init = process->init; 150 151 #if (NXT_HAVE_CLONE) 152 pid = nxt_clone(SIGCHLD | init->isolation.clone.flags); 153 if (nxt_slow_path(pid < 0)) { 154 nxt_alert(task, "clone() failed while creating \"%s\" %E", 155 init->name, nxt_errno); 156 goto cleanup; 157 } 158 #else 159 pid = fork(); 160 if (nxt_slow_path(pid < 0)) { 161 nxt_alert(task, "fork() failed while creating \"%s\" %E", 162 init->name, nxt_errno); 163 goto cleanup; 164 } 165 #endif 166 167 if (pid == 0) { 168 /* Child. */ 169 170 if (nxt_slow_path(close(pipefd[1]) == -1)) { 171 nxt_alert(task, "failed to close writer pipe fd"); 172 } 173 174 ret = nxt_process_worker_setup(task, process, pipefd[0]); 175 if (nxt_slow_path(ret != NXT_OK)) { 176 exit(1); 177 } 178 179 if (nxt_slow_path(close(pipefd[0]) == -1)) { 180 nxt_alert(task, "failed to close writer pipe fd"); 181 } 182 183 /* 184 * Explicitly return 0 to notice the caller function this is the child. 185 * The caller must return to the event engine work queue loop. 186 */ 187 return 0; 188 } 189 190 /* Parent. */ 191 192 /* 193 * At this point, the child process is blocked reading the 194 * pipe fd to get its real pid (rpid). 195 * 196 * If anything goes wrong now, we need to terminate the child 197 * process by sending a NXT_ERROR in the pipe. 198 */ 199 200 #if (NXT_HAVE_CLONE) 201 nxt_debug(task, "clone(\"%s\"): %PI", init->name, pid); 202 #else 203 nxt_debug(task, "fork(\"%s\"): %PI", init->name, pid); 204 #endif 205 206 if (nxt_slow_path(write(pipefd[1], &pid, sizeof(pid)) == -1)) { 207 nxt_alert(task, "failed to write real pid"); 208 goto fail; 209 } 210 211 #if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER) 212 if ((init->isolation.clone.flags & CLONE_NEWUSER) == CLONE_NEWUSER) { 213 ret = nxt_clone_proc_map(task, pid, &init->isolation.clone); 214 if (nxt_slow_path(ret != NXT_OK)) { 215 goto fail; 216 } 217 } 218 #endif 219 220 ret = NXT_OK; 221 222 if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) { 223 nxt_alert(task, "failed to write status"); 224 goto fail; 225 } 226 227 process->pid = pid; 228 229 nxt_runtime_process_add(task, process); 230 231 goto cleanup; 232 233 fail: 234 235 ret = NXT_ERROR; 236 237 if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) { 238 nxt_alert(task, "failed to write status"); 239 } 240 241 waitpid(pid, NULL, 0); 242 243 pid = -1; 244 245 cleanup: 246 247 if (nxt_slow_path(close(pipefd[0]) != 0)) { 248 nxt_alert(task, "failed to close pipe: %E", nxt_errno); 249 } 250 251 if (nxt_slow_path(close(pipefd[1]) != 0)) { 252 nxt_alert(task, "failed to close pipe: %E", nxt_errno); 253 } 254 255 return pid; 256 } 257 258 259 static void 260 nxt_process_start(nxt_task_t *task, nxt_process_t *process) 261 { 262 nxt_int_t ret; 263 nxt_port_t *port, *main_port; 264 nxt_thread_t *thread; 265 nxt_runtime_t *rt; 266 nxt_process_init_t *init; 267 nxt_event_engine_t *engine; 268 const nxt_event_interface_t *interface; 269 270 init = process->init; 271 272 nxt_log(task, NXT_LOG_INFO, "%s started", init->name); 273 274 nxt_process_title(task, "unit: %s", init->name); 275 276 thread = task->thread; 277 rt = thread->runtime; 278 279 nxt_random_init(&thread->random); 280 281 if (rt->capabilities.setid && init->user_cred != NULL) { 282 ret = nxt_user_cred_set(task, init->user_cred); 283 if (ret != NXT_OK) { 284 goto fail; 285 } 286 } 287 288 rt->type = init->type; 289 290 engine = thread->engine; 291 292 /* Update inherited main process event engine and signals processing. */ 293 engine->signals->sigev = init->signals; 294 295 interface = nxt_service_get(rt->services, "engine", rt->engine); 296 if (nxt_slow_path(interface == NULL)) { 297 goto fail; 298 } 299 300 if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) { 301 goto fail; 302 } 303 304 ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads, 305 60000 * 1000000LL); 306 if (nxt_slow_path(ret != NXT_OK)) { 307 goto fail; 308 } 309 310 main_port = rt->port_by_type[NXT_PROCESS_MAIN]; 311 312 nxt_port_read_close(main_port); 313 nxt_port_write_enable(task, main_port); 314 315 port = nxt_process_port_first(process); 316 317 nxt_port_write_close(port); 318 319 ret = init->start(task, init->data); 320 321 if (nxt_slow_path(ret != NXT_OK)) { 322 goto fail; 323 } 324 325 nxt_port_enable(task, port, init->port_handlers); 326 327 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY, 328 -1, init->stream, 0, NULL); 329 330 if (nxt_slow_path(ret != NXT_OK)) { 331 nxt_log(task, NXT_LOG_ERR, "failed to send READY message to main"); 332 333 goto fail; 334 } 335 336 return; 337 338 fail: 339 340 exit(1); 341 } 342 343 344 #if (NXT_HAVE_POSIX_SPAWN) 345 346 /* 347 * Linux glibc 2.2 posix_spawn() is implemented via fork()/execve(). 348 * Linux glibc 2.4 posix_spawn() without file actions and spawn 349 * attributes uses vfork()/execve(). 350 * 351 * On FreeBSD 8.0 posix_spawn() is implemented via vfork()/execve(). 352 * 353 * Solaris 10: 354 * In the Solaris 10 OS, posix_spawn() is currently implemented using 355 * private-to-libc vfork(), execve(), and exit() functions. They are 356 * identical to regular vfork(), execve(), and exit() in functionality, 357 * but they are not exported from libc and therefore don't cause the 358 * deadlock-in-the-dynamic-linker problem that any multithreaded code 359 * outside of libc that calls vfork() can cause. 360 * 361 * On MacOSX 10.5 (Leoprad) and NetBSD 6.0 posix_spawn() is implemented 362 * as syscall. 363 */ 364 365 nxt_pid_t 366 nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp) 367 { 368 nxt_pid_t pid; 369 370 nxt_debug(task, "posix_spawn(\"%s\")", name); 371 372 if (posix_spawn(&pid, name, NULL, NULL, argv, envp) != 0) { 373 nxt_alert(task, "posix_spawn(\"%s\") failed %E", name, nxt_errno); 374 return -1; 375 } 376 377 return pid; 378 } 379 380 #else 381 382 nxt_pid_t 383 nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp) 384 { 385 nxt_pid_t pid; 386 387 /* 388 * vfork() is better than fork() because: 389 * it is faster several times; 390 * its execution time does not depend on private memory mapping size; 391 * it has lesser chances to fail due to the ENOMEM error. 392 */ 393 394 pid = vfork(); 395 396 switch (pid) { 397 398 case -1: 399 nxt_alert(task, "vfork() failed while executing \"%s\" %E", 400 name, nxt_errno); 401 break; 402 403 case 0: 404 /* A child. */ 405 nxt_debug(task, "execve(\"%s\")", name); 406 407 (void) execve(name, argv, envp); 408 409 nxt_alert(task, "execve(\"%s\") failed %E", name, nxt_errno); 410 411 exit(1); 412 nxt_unreachable(); 413 break; 414 415 default: 416 /* A parent. */ 417 nxt_debug(task, "vfork(): %PI", pid); 418 break; 419 } 420 421 return pid; 422 } 423 424 #endif 425 426 427 nxt_int_t 428 nxt_process_daemon(nxt_task_t *task) 429 { 430 nxt_fd_t fd; 431 nxt_pid_t pid; 432 const char *msg; 433 434 fd = -1; 435 436 /* 437 * fork() followed by a parent process's exit() detaches a child process 438 * from an init script or terminal shell process which has started the 439 * parent process and allows the child process to run in background. 440 */ 441 442 pid = fork(); 443 444 switch (pid) { 445 446 case -1: 447 msg = "fork() failed %E"; 448 goto fail; 449 450 case 0: 451 /* A child. */ 452 break; 453 454 default: 455 /* A parent. */ 456 nxt_debug(task, "fork(): %PI", pid); 457 exit(0); 458 nxt_unreachable(); 459 } 460 461 nxt_pid = getpid(); 462 463 /* Clean inherited cached thread tid. */ 464 task->thread->tid = 0; 465 466 nxt_debug(task, "daemon"); 467 468 /* Detach from controlling terminal. */ 469 470 if (setsid() == -1) { 471 nxt_alert(task, "setsid() failed %E", nxt_errno); 472 return NXT_ERROR; 473 } 474 475 /* 476 * Reset file mode creation mask: any access 477 * rights can be set on file creation. 478 */ 479 umask(0); 480 481 /* Redirect STDIN and STDOUT to the "/dev/null". */ 482 483 fd = open("/dev/null", O_RDWR); 484 if (fd == -1) { 485 msg = "open(\"/dev/null\") failed %E"; 486 goto fail; 487 } 488 489 if (dup2(fd, STDIN_FILENO) == -1) { 490 msg = "dup2(\"/dev/null\", STDIN) failed %E"; 491 goto fail; 492 } 493 494 if (dup2(fd, STDOUT_FILENO) == -1) { 495 msg = "dup2(\"/dev/null\", STDOUT) failed %E"; 496 goto fail; 497 } 498 499 if (fd > STDERR_FILENO) { 500 nxt_fd_close(fd); 501 } 502 503 return NXT_OK; 504 505 fail: 506 507 nxt_alert(task, msg, nxt_errno); 508 509 if (fd != -1) { 510 nxt_fd_close(fd); 511 } 512 513 return NXT_ERROR; 514 } 515 516 517 void 518 nxt_nanosleep(nxt_nsec_t ns) 519 { 520 struct timespec ts; 521 522 ts.tv_sec = ns / 1000000000; 523 ts.tv_nsec = ns % 1000000000; 524 525 (void) nanosleep(&ts, NULL); 526 } 527 528 529 nxt_int_t 530 nxt_user_cred_get(nxt_task_t *task, nxt_mp_t *mp, nxt_user_cred_t *uc, 531 const char *group) 532 { 533 struct group *grp; 534 struct passwd *pwd; 535 536 nxt_errno = 0; 537 538 pwd = getpwnam(uc->user); 539 540 if (nxt_slow_path(pwd == NULL)) { 541 542 if (nxt_errno == 0) { 543 nxt_alert(task, "getpwnam(\"%s\") failed, user \"%s\" not found", 544 uc->user, uc->user); 545 } else { 546 nxt_alert(task, "getpwnam(\"%s\") failed %E", uc->user, nxt_errno); 547 } 548 549 return NXT_ERROR; 550 } 551 552 uc->uid = pwd->pw_uid; 553 uc->base_gid = pwd->pw_gid; 554 555 if (group != NULL && group[0] != '\0') { 556 nxt_errno = 0; 557 558 grp = getgrnam(group); 559 560 if (nxt_slow_path(grp == NULL)) { 561 562 if (nxt_errno == 0) { 563 nxt_alert(task, 564 "getgrnam(\"%s\") failed, group \"%s\" not found", 565 group, group); 566 } else { 567 nxt_alert(task, "getgrnam(\"%s\") failed %E", group, nxt_errno); 568 } 569 570 return NXT_ERROR; 571 } 572 573 uc->base_gid = grp->gr_gid; 574 } 575 576 nxt_debug(task, "about to get \"%s\" groups (uid:%d, base gid:%d)", 577 uc->user, uc->uid, uc->base_gid); 578 579 if (nxt_user_groups_get(task, mp, uc) != NXT_OK) { 580 return NXT_ERROR; 581 } 582 583 #if (NXT_DEBUG) 584 { 585 u_char *p, *end; 586 nxt_uint_t i; 587 u_char msg[NXT_MAX_ERROR_STR]; 588 589 p = msg; 590 end = msg + NXT_MAX_ERROR_STR; 591 592 for (i = 0; i < uc->ngroups; i++) { 593 p = nxt_sprintf(p, end, "%d%c", uc->gids[i], 594 i+1 < uc->ngroups ? ',' : '\0'); 595 } 596 597 nxt_debug(task, "user \"%s\" has gids:%*s", uc->user, p - msg, msg); 598 } 599 #endif 600 601 return NXT_OK; 602 } 603 604 605 #if (NXT_HAVE_GETGROUPLIST && !NXT_MACOSX) 606 607 #define NXT_NGROUPS nxt_min(256, NGROUPS_MAX) 608 609 610 static nxt_int_t 611 nxt_user_groups_get(nxt_task_t *task, nxt_mp_t *mp, nxt_user_cred_t *uc) 612 { 613 int ngroups; 614 gid_t groups[NXT_NGROUPS]; 615 616 ngroups = NXT_NGROUPS; 617 618 if (getgrouplist(uc->user, uc->base_gid, groups, &ngroups) < 0) { 619 if (nxt_slow_path(ngroups <= NXT_NGROUPS)) { 620 nxt_alert(task, "getgrouplist(\"%s\", %d, ...) failed %E", uc->user, 621 uc->base_gid, nxt_errno); 622 623 return NXT_ERROR; 624 } 625 } 626 627 if (ngroups > NXT_NGROUPS) { 628 if (ngroups > NGROUPS_MAX) { 629 ngroups = NGROUPS_MAX; 630 } 631 632 uc->ngroups = ngroups; 633 634 uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(gid_t)); 635 if (nxt_slow_path(uc->gids == NULL)) { 636 return NXT_ERROR; 637 } 638 639 if (nxt_slow_path(getgrouplist(uc->user, uc->base_gid, uc->gids, 640 &ngroups) < 0)) { 641 642 nxt_alert(task, "getgrouplist(\"%s\", %d) failed %E", uc->user, 643 uc->base_gid, nxt_errno); 644 645 return NXT_ERROR; 646 } 647 648 return NXT_OK; 649 } 650 651 uc->ngroups = ngroups; 652 653 uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(gid_t)); 654 if (nxt_slow_path(uc->gids == NULL)) { 655 return NXT_ERROR; 656 } 657 658 nxt_memcpy(uc->gids, groups, ngroups * sizeof(gid_t)); 659 660 return NXT_OK; 661 } 662 663 664 #else 665 666 /* 667 * For operating systems that lack getgrouplist(3) or it's buggy (MacOS), 668 * nxt_user_groups_get() stores an array of groups IDs which should be 669 * set by the setgroups() function for a given user. The initgroups() 670 * may block a just forked worker process for some time if LDAP or NDIS+ 671 * is used, so nxt_user_groups_get() allows to get worker user groups in 672 * main process. In a nutshell the initgroups() calls getgrouplist() 673 * followed by setgroups(). However older Solaris lacks the getgrouplist(). 674 * Besides getgrouplist() does not allow to query the exact number of 675 * groups in some platforms, while NGROUPS_MAX can be quite large (e.g. 676 * 65536 on Linux). 677 * So nxt_user_groups_get() emulates getgrouplist(): at first the function 678 * saves the super-user groups IDs, then calls initgroups() and saves the 679 * specified user groups IDs, and then restores the super-user groups IDs. 680 * This works at least on Linux, FreeBSD, and Solaris, but does not work 681 * on MacOSX, getgroups(2): 682 * 683 * To provide compatibility with applications that use getgroups() in 684 * environments where users may be in more than {NGROUPS_MAX} groups, 685 * a variant of getgroups(), obtained when compiling with either the 686 * macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can 687 * be used that is not limited to {NGROUPS_MAX} groups. However, this 688 * variant only returns the user's default group access list and not 689 * the group list modified by a call to setgroups(2). 690 * 691 * For such cases initgroups() is used in worker process as fallback. 692 */ 693 694 static nxt_int_t 695 nxt_user_groups_get(nxt_task_t *task, nxt_mp_t *mp, nxt_user_cred_t *uc) 696 { 697 int nsaved, ngroups; 698 nxt_int_t ret; 699 nxt_gid_t *saved; 700 701 nsaved = getgroups(0, NULL); 702 703 if (nxt_slow_path(nsaved == -1)) { 704 nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno); 705 return NXT_ERROR; 706 } 707 708 nxt_debug(task, "getgroups(0, NULL): %d", nsaved); 709 710 if (nsaved > NGROUPS_MAX) { 711 /* MacOSX case. */ 712 713 uc->gids = NULL; 714 uc->ngroups = 0; 715 716 return NXT_OK; 717 } 718 719 saved = nxt_mp_alloc(mp, nsaved * sizeof(nxt_gid_t)); 720 721 if (nxt_slow_path(saved == NULL)) { 722 return NXT_ERROR; 723 } 724 725 ret = NXT_ERROR; 726 727 nsaved = getgroups(nsaved, saved); 728 729 if (nxt_slow_path(nsaved == -1)) { 730 nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno); 731 goto free; 732 } 733 734 nxt_debug(task, "getgroups(): %d", nsaved); 735 736 if (initgroups(uc->user, uc->base_gid) != 0) { 737 if (nxt_errno == NXT_EPERM) { 738 nxt_log(task, NXT_LOG_NOTICE, 739 "initgroups(%s, %d) failed %E, ignored", 740 uc->user, uc->base_gid, nxt_errno); 741 742 ret = NXT_OK; 743 744 goto free; 745 746 } else { 747 nxt_alert(task, "initgroups(%s, %d) failed %E", 748 uc->user, uc->base_gid, nxt_errno); 749 goto restore; 750 } 751 } 752 753 ngroups = getgroups(0, NULL); 754 755 if (nxt_slow_path(ngroups == -1)) { 756 nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno); 757 goto restore; 758 } 759 760 nxt_debug(task, "getgroups(0, NULL): %d", ngroups); 761 762 uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(nxt_gid_t)); 763 764 if (nxt_slow_path(uc->gids == NULL)) { 765 goto restore; 766 } 767 768 ngroups = getgroups(ngroups, uc->gids); 769 770 if (nxt_slow_path(ngroups == -1)) { 771 nxt_alert(task, "getgroups(%d) failed %E", ngroups, nxt_errno); 772 goto restore; 773 } 774 775 uc->ngroups = ngroups; 776 777 ret = NXT_OK; 778 779 restore: 780 781 if (nxt_slow_path(setgroups(nsaved, saved) != 0)) { 782 nxt_alert(task, "setgroups(%d) failed %E", nsaved, nxt_errno); 783 ret = NXT_ERROR; 784 } 785 786 free: 787 788 nxt_mp_free(mp, saved); 789 790 return ret; 791 } 792 793 794 #endif 795 796 797 nxt_int_t 798 nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc) 799 { 800 nxt_debug(task, "user cred set: \"%s\" uid:%d base gid:%d", 801 uc->user, uc->uid, uc->base_gid); 802 803 if (setgid(uc->base_gid) != 0) { 804 805 #if (NXT_HAVE_CLONE) 806 if (nxt_errno == EINVAL) { 807 nxt_log(task, NXT_LOG_ERR, "The gid %d isn't valid in the " 808 "application namespace.", uc->base_gid); 809 return NXT_ERROR; 810 } 811 #endif 812 813 nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno); 814 return NXT_ERROR; 815 } 816 817 if (uc->gids != NULL) { 818 if (setgroups(uc->ngroups, uc->gids) != 0) { 819 820 #if (NXT_HAVE_CLONE) 821 if (nxt_errno == EINVAL) { 822 nxt_log(task, NXT_LOG_ERR, "The user \"%s\" (uid: %d) has " 823 "supplementary group ids not valid in the application " 824 "namespace.", uc->user, uc->uid); 825 return NXT_ERROR; 826 } 827 #endif 828 829 nxt_alert(task, "setgroups(%i) failed %E", uc->ngroups, nxt_errno); 830 return NXT_ERROR; 831 } 832 833 } else { 834 /* MacOSX fallback. */ 835 if (initgroups(uc->user, uc->base_gid) != 0) { 836 nxt_alert(task, "initgroups(%s, %d) failed %E", 837 uc->user, uc->base_gid, nxt_errno); 838 return NXT_ERROR; 839 } 840 } 841 842 if (setuid(uc->uid) != 0) { 843 844 #if (NXT_HAVE_CLONE) 845 if (nxt_errno == EINVAL) { 846 nxt_log(task, NXT_LOG_ERR, "The uid %d (user \"%s\") isn't " 847 "valid in the application namespace.", uc->uid, uc->user); 848 return NXT_ERROR; 849 } 850 #endif 851 852 nxt_alert(task, "setuid(%d) failed %E", uc->uid, nxt_errno); 853 return NXT_ERROR; 854 } 855 856 return NXT_OK; 857 } 858 859 860 void 861 nxt_process_use(nxt_task_t *task, nxt_process_t *process, int i) 862 { 863 process->use_count += i; 864 865 if (process->use_count == 0) { 866 nxt_runtime_process_release(task->thread->runtime, process); 867 } 868 } 869 870 871 void 872 nxt_process_port_add(nxt_task_t *task, nxt_process_t *process, nxt_port_t *port) 873 { 874 nxt_assert(port->process == NULL); 875 876 port->process = process; 877 nxt_queue_insert_tail(&process->ports, &port->link); 878 879 nxt_process_use(task, process, 1); 880 } 881 882 883 nxt_process_type_t 884 nxt_process_type(nxt_process_t *process) 885 { 886 return nxt_queue_is_empty(&process->ports) ? 0 : 887 (nxt_process_port_first(process))->type; 888 } 889 890 891 void 892 nxt_process_close_ports(nxt_task_t *task, nxt_process_t *process) 893 { 894 nxt_port_t *port; 895 896 nxt_process_port_each(process, port) { 897 898 nxt_port_close(task, port); 899 900 nxt_runtime_port_remove(task, port); 901 902 } nxt_process_port_loop; 903 } 904 905 906 void 907 nxt_process_connected_port_add(nxt_process_t *process, nxt_port_t *port) 908 { 909 nxt_thread_mutex_lock(&process->cp_mutex); 910 911 nxt_port_hash_add(&process->connected_ports, port); 912 913 nxt_thread_mutex_unlock(&process->cp_mutex); 914 } 915 916 917 void 918 nxt_process_connected_port_remove(nxt_process_t *process, nxt_port_t *port) 919 { 920 nxt_thread_mutex_lock(&process->cp_mutex); 921 922 nxt_port_hash_remove(&process->connected_ports, port); 923 924 nxt_thread_mutex_unlock(&process->cp_mutex); 925 } 926 927 928 nxt_port_t * 929 nxt_process_connected_port_find(nxt_process_t *process, nxt_pid_t pid, 930 nxt_port_id_t port_id) 931 { 932 nxt_port_t *res; 933 934 nxt_thread_mutex_lock(&process->cp_mutex); 935 936 res = nxt_port_hash_find(&process->connected_ports, pid, port_id); 937 938 nxt_thread_mutex_unlock(&process->cp_mutex); 939 940 return res; 941 } 942