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{
|