1 /* 2 * Copyright 2017-2019, Jérôme Duval, jerome.duval@gmail.com 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <spawn.h> 8 9 #include <errno.h> 10 #include <signal.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <unistd.h> 15 16 #include <libroot_private.h> 17 #include <signal_defs.h> 18 #include <syscalls.h> 19 20 21 enum action_type { 22 file_action_open, 23 file_action_close, 24 file_action_dup2, 25 file_action_chdir, 26 file_action_fchdir 27 }; 28 29 struct _file_action { 30 enum action_type type; 31 int fd; 32 union { 33 struct { 34 char* path; 35 int oflag; 36 mode_t mode; 37 } open_action; 38 struct { 39 int srcfd; 40 } dup2_action; 41 struct { 42 char* path; 43 } chdir_action; 44 } action; 45 }; 46 47 struct _posix_spawnattr { 48 short flags; 49 pid_t pgroup; 50 sigset_t sigdefault; 51 sigset_t sigmask; 52 }; 53 54 struct _posix_spawn_file_actions { 55 int size; 56 int count; 57 _file_action *actions; 58 }; 59 60 61 static int 62 posix_spawn_file_actions_extend(struct _posix_spawn_file_actions *actions) 63 { 64 int newsize = actions->size + 4; 65 void *newactions = realloc(actions->actions, 66 newsize * sizeof(struct _file_action)); 67 if (newactions == NULL) 68 return ENOMEM; 69 actions->actions = (struct _file_action*)newactions; 70 actions->size = newsize; 71 return 0; 72 } 73 74 75 int 76 posix_spawn_file_actions_init(posix_spawn_file_actions_t *_file_actions) 77 { 78 posix_spawn_file_actions_t actions = (posix_spawn_file_actions_t)malloc( 79 sizeof(struct _posix_spawn_file_actions)); 80 81 if (actions == NULL) 82 return ENOMEM; 83 84 memset(actions, 0, sizeof(*actions)); 85 *_file_actions = actions; 86 87 return 0; 88 } 89 90 91 int 92 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *_actions) 93 { 94 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL; 95 96 if (actions == NULL) 97 return EINVAL; 98 99 for (int i = 0; i < actions->count; i++) { 100 struct _file_action *action = &actions->actions[i]; 101 102 if (action->type == file_action_open) 103 free(action->action.open_action.path); 104 else if (action->type == file_action_chdir) 105 free(action->action.chdir_action.path); 106 } 107 108 free(actions); 109 110 return 0; 111 } 112 113 114 int 115 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *_actions, 116 int fildes, const char *path, int oflag, mode_t mode) 117 { 118 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL; 119 120 if (actions == NULL) 121 return EINVAL; 122 123 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX)) 124 return EBADF; 125 126 char* npath = strdup(path); 127 if (npath == NULL) 128 return ENOMEM; 129 if (actions->count == actions->size 130 && posix_spawn_file_actions_extend(actions) != 0) { 131 free(npath); 132 return ENOMEM; 133 } 134 135 struct _file_action *action = &actions->actions[actions->count]; 136 action->type = file_action_open; 137 action->fd = fildes; 138 action->action.open_action.path = npath; 139 action->action.open_action.oflag = oflag; 140 action->action.open_action.mode = mode; 141 actions->count++; 142 return 0; 143 } 144 145 146 int 147 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *_actions, 148 int fildes) 149 { 150 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL; 151 152 if (actions == NULL) 153 return EINVAL; 154 155 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX)) 156 return EBADF; 157 158 if (actions->count == actions->size 159 && posix_spawn_file_actions_extend(actions) != 0) { 160 return ENOMEM; 161 } 162 163 struct _file_action *action = &actions->actions[actions->count]; 164 action->type = file_action_close; 165 action->fd = fildes; 166 actions->count++; 167 return 0; 168 } 169 170 171 int 172 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *_actions, 173 int fildes, int newfildes) 174 { 175 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL; 176 177 if (actions == NULL) 178 return EINVAL; 179 180 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX)) 181 return EBADF; 182 183 if (actions->count == actions->size 184 && posix_spawn_file_actions_extend(actions) != 0) { 185 return ENOMEM; 186 } 187 188 struct _file_action *action = &actions->actions[actions->count]; 189 action->type = file_action_dup2; 190 action->fd = newfildes; 191 action->action.dup2_action.srcfd = fildes; 192 actions->count++; 193 return 0; 194 } 195 196 197 extern "C" int 198 posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *_actions, 199 const char *path) 200 { 201 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL; 202 203 if (actions == NULL) 204 return EINVAL; 205 206 char* npath = strdup(path); 207 if (npath == NULL) 208 return ENOMEM; 209 if (actions->count == actions->size 210 && posix_spawn_file_actions_extend(actions) != 0) { 211 free(npath); 212 return ENOMEM; 213 } 214 215 struct _file_action *action = &actions->actions[actions->count]; 216 action->type = file_action_chdir; 217 action->fd = -1; 218 action->action.chdir_action.path = npath; 219 actions->count++; 220 return 0; 221 } 222 223 224 extern "C" int 225 posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *_actions, 226 int fildes) 227 { 228 struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL; 229 230 if (actions == NULL) 231 return EINVAL; 232 233 if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX)) 234 return EBADF; 235 236 if (actions->count == actions->size 237 && posix_spawn_file_actions_extend(actions) != 0) { 238 return ENOMEM; 239 } 240 241 struct _file_action *action = &actions->actions[actions->count]; 242 action->type = file_action_fchdir; 243 action->fd = fildes; 244 actions->count++; 245 return 0; 246 } 247 248 249 int 250 posix_spawnattr_init(posix_spawnattr_t *_attr) 251 { 252 posix_spawnattr_t attr = (posix_spawnattr_t)malloc( 253 sizeof(struct _posix_spawnattr)); 254 255 if (attr == NULL) 256 return ENOMEM; 257 258 memset(attr, 0, sizeof(*attr)); 259 *_attr = attr; 260 261 return 0; 262 } 263 264 265 int 266 posix_spawnattr_destroy(posix_spawnattr_t *_attr) 267 { 268 struct _posix_spawnattr* attr = _attr != NULL ? *_attr : NULL; 269 270 if (attr == NULL) 271 return EINVAL; 272 273 free(attr); 274 275 return 0; 276 } 277 278 279 int 280 posix_spawnattr_getflags(const posix_spawnattr_t *_attr, short *flags) 281 { 282 struct _posix_spawnattr *attr; 283 284 if (_attr == NULL || (attr = *_attr) == NULL || flags == NULL) 285 return EINVAL; 286 287 *flags = attr->flags; 288 289 return 0; 290 } 291 292 293 int 294 posix_spawnattr_setflags(posix_spawnattr_t *_attr, short flags) 295 { 296 struct _posix_spawnattr *attr; 297 298 if (_attr == NULL || (attr = *_attr) == NULL) 299 return EINVAL; 300 301 if ((flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP 302 | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK 303 | POSIX_SPAWN_SETSID)) != 0) { 304 return EINVAL; 305 } 306 307 attr->flags = flags; 308 309 return 0; 310 } 311 312 313 int 314 posix_spawnattr_getpgroup(const posix_spawnattr_t *_attr, pid_t *pgroup) 315 { 316 struct _posix_spawnattr *attr; 317 318 if (_attr == NULL || (attr = *_attr) == NULL || pgroup == NULL) 319 return EINVAL; 320 321 *pgroup = attr->pgroup; 322 323 return 0; 324 } 325 326 327 int 328 posix_spawnattr_setpgroup(posix_spawnattr_t *_attr, pid_t pgroup) 329 { 330 struct _posix_spawnattr *attr; 331 332 if (_attr == NULL || (attr = *_attr) == NULL) 333 return EINVAL; 334 335 attr->pgroup = pgroup; 336 337 return 0; 338 } 339 340 341 int 342 posix_spawnattr_getsigdefault(const posix_spawnattr_t *_attr, sigset_t *sigdefault) 343 { 344 struct _posix_spawnattr *attr; 345 346 if (_attr == NULL || (attr = *_attr) == NULL || sigdefault == NULL) 347 return EINVAL; 348 349 memcpy(sigdefault, &attr->sigdefault, sizeof(sigset_t)); 350 351 return 0; 352 } 353 354 355 int 356 posix_spawnattr_setsigdefault(posix_spawnattr_t *_attr, 357 const sigset_t *sigdefault) 358 { 359 struct _posix_spawnattr *attr; 360 361 if (_attr == NULL || (attr = *_attr) == NULL || sigdefault == NULL) 362 return EINVAL; 363 364 memcpy(&attr->sigdefault, sigdefault, sizeof(sigset_t)); 365 366 return 0; 367 } 368 369 370 int 371 posix_spawnattr_getsigmask(const posix_spawnattr_t *_attr, sigset_t *sigmask) 372 { 373 struct _posix_spawnattr *attr; 374 375 if (_attr == NULL || (attr = *_attr) == NULL || sigmask == NULL) 376 return EINVAL; 377 378 memcpy(sigmask, &attr->sigmask, sizeof(sigset_t)); 379 380 return 0; 381 } 382 383 384 int 385 posix_spawnattr_setsigmask(posix_spawnattr_t *_attr, const sigset_t *sigmask) 386 { 387 struct _posix_spawnattr *attr; 388 389 if (_attr == NULL || (attr = *_attr) == NULL || sigmask == NULL) 390 return EINVAL; 391 392 memcpy(&attr->sigmask, sigmask, sizeof(sigset_t)); 393 394 return 0; 395 } 396 397 398 static int 399 process_spawnattr(const posix_spawnattr_t *_attr) 400 { 401 if (_attr == NULL) 402 return 0; 403 404 struct _posix_spawnattr *attr = *_attr; 405 if (attr == NULL) 406 return EINVAL; 407 408 if ((attr->flags & POSIX_SPAWN_SETSIGMASK) != 0) 409 sigprocmask(SIG_SETMASK, &attr->sigmask, NULL); 410 411 if ((attr->flags & POSIX_SPAWN_SETSIGDEF) != 0) { 412 struct sigaction action; 413 action.sa_handler = SIG_DFL; 414 action.sa_flags = 0; 415 action.sa_userdata = NULL; 416 sigemptyset(&action.sa_mask); 417 for (int i = 1; i <= MAX_SIGNAL_NUMBER; i++) { 418 if (sigismember(&attr->sigdefault, i) == 1 419 && sigaction(i, &action, NULL) != 0) { 420 return errno; 421 } 422 } 423 } 424 425 if ((attr->flags & POSIX_SPAWN_RESETIDS) != 0) { 426 if (setegid(getgid()) != 0) 427 return errno; 428 if (seteuid(getuid()) != 0) 429 return errno; 430 } 431 432 if ((attr->flags & POSIX_SPAWN_SETSID) != 0) { 433 if (setsid() != 0) 434 return errno; 435 } 436 437 if ((attr->flags & POSIX_SPAWN_SETPGROUP) != 0) { 438 if (setpgid(0, attr->pgroup) != 0) 439 return errno; 440 } 441 442 return 0; 443 } 444 445 446 static int 447 process_file_actions(const posix_spawn_file_actions_t *_actions, int *errfd) 448 { 449 if (_actions == NULL) 450 return 0; 451 452 struct _posix_spawn_file_actions* actions = *_actions; 453 if (actions == NULL) 454 return EINVAL; 455 456 for (int i = 0; i < actions->count; i++) { 457 struct _file_action *action = &actions->actions[i]; 458 459 if (action->fd == *errfd) { 460 int newfd = dup(action->fd); 461 if (newfd == -1) 462 return errno; 463 close(action->fd); 464 fcntl(newfd, F_SETFD, FD_CLOEXEC); 465 *errfd = newfd; 466 } 467 468 if (action->type == file_action_close) { 469 if (close(action->fd) != 0) 470 return errno; 471 } else if (action->type == file_action_open) { 472 int fd = open(action->action.open_action.path, 473 action->action.open_action.oflag, 474 action->action.open_action.mode); 475 if (fd == -1) 476 return errno; 477 if (fd != action->fd) { 478 if (dup2(fd, action->fd) == -1) 479 return errno; 480 if (close(fd) != 0) 481 return errno; 482 } 483 } else if (action->type == file_action_dup2) { 484 if (dup2(action->action.dup2_action.srcfd, action->fd) == -1) 485 return errno; 486 } else if (action->type == file_action_chdir) { 487 if (chdir(action->action.chdir_action.path) == -1) 488 return errno; 489 } else if (action->type == file_action_fchdir) { 490 if (fchdir(action->fd) == -1) 491 return errno; 492 } 493 } 494 495 return 0; 496 } 497 498 499 static int 500 spawn_using_fork(pid_t *_pid, const char *path, 501 const posix_spawn_file_actions_t *actions, 502 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], 503 bool envpath) 504 { 505 int err = 0; 506 int fds[2]; 507 pid_t pid; 508 509 if (pipe(fds) != 0) 510 return errno; 511 if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) != 0 512 || fcntl(fds[1], F_SETFD, FD_CLOEXEC) != 0) { 513 goto fail; 514 } 515 pid = vfork(); 516 if (pid == -1) 517 goto fail; 518 519 if (pid != 0) { 520 // parent 521 if (_pid != NULL) 522 *_pid = pid; 523 close(fds[1]); 524 read(fds[0], &err, sizeof(err)); 525 if (err != 0) 526 waitpid(pid, NULL, WNOHANG); 527 close(fds[0]); 528 return err; 529 } 530 531 // child 532 close(fds[0]); 533 534 err = process_spawnattr(attrp); 535 if (err == 0) 536 err = process_file_actions(actions, &fds[1]); 537 if (err != 0) 538 goto fail_child; 539 540 if (envpath) 541 execvpe(path, argv, envp != NULL ? envp : environ); 542 else 543 execve(path, argv, envp != NULL ? envp : environ); 544 545 err = errno; 546 547 fail_child: 548 write(fds[1], &err, sizeof(err)); 549 close(fds[1]); 550 _exit(127); 551 552 fail: 553 err = errno; 554 close(fds[0]); 555 close(fds[1]); 556 return err; 557 } 558 559 560 static int 561 spawn_using_load_image(pid_t *_pid, const char *_path, 562 char *const argv[], char *const envp[], bool envpath) 563 { 564 const char* path; 565 // if envpath is specified but the path contains '/', don't search PATH 566 if (!envpath || strchr(_path, '/') != NULL) { 567 path = _path; 568 } else { 569 char* buffer = (char*)alloca(B_PATH_NAME_LENGTH); 570 status_t status = __look_up_in_path(_path, buffer); 571 if (status != B_OK) 572 return status; 573 path = buffer; 574 } 575 576 // count arguments 577 int32 argCount = 0; 578 while (argv[argCount] != NULL) 579 argCount++; 580 581 thread_id thread = __load_image_at_path(path, argCount, (const char**)argv, 582 (const char**)(envp != NULL ? envp : environ)); 583 if (thread < 0) 584 return thread; 585 586 *_pid = thread; 587 return resume_thread(thread); 588 } 589 590 591 static int 592 do_posix_spawn(pid_t *_pid, const char *path, 593 const posix_spawn_file_actions_t *actions, 594 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], 595 bool envpath) 596 { 597 if ((actions == NULL || (*actions)->count == 0) 598 && (attrp == NULL || (*attrp)->flags == 0)) { 599 return spawn_using_load_image(_pid, path, argv, envp, envpath); 600 } else { 601 return spawn_using_fork(_pid, path, actions, attrp, argv, envp, 602 envpath); 603 } 604 } 605 606 607 int 608 posix_spawn(pid_t *pid, const char *path, 609 const posix_spawn_file_actions_t *file_actions, 610 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) 611 { 612 return do_posix_spawn(pid, path, file_actions, attrp, argv, envp, false); 613 } 614 615 616 int 617 posix_spawnp(pid_t *pid, const char *file, 618 const posix_spawn_file_actions_t *file_actions, 619 const posix_spawnattr_t *attrp, char *const argv[], 620 char *const envp[]) 621 { 622 return do_posix_spawn(pid, file, file_actions, attrp, argv, envp, true); 623 } 624 625 626 B_DEFINE_WEAK_ALIAS(posix_spawn_file_actions_addchdir_np, posix_spawn_file_actions_addchdir); 627 B_DEFINE_WEAK_ALIAS(posix_spawn_file_actions_addfchdir_np, posix_spawn_file_actions_addfchdir); 628