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 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 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)) != 0) { 303 return EINVAL; 304 } 305 306 attr->flags = flags; 307 308 return 0; 309 } 310 311 312 int 313 posix_spawnattr_getpgroup(const posix_spawnattr_t *_attr, pid_t *pgroup) 314 { 315 struct _posix_spawnattr *attr; 316 317 if (_attr == NULL || (attr = *_attr) == NULL || pgroup == NULL) 318 return EINVAL; 319 320 *pgroup = attr->pgroup; 321 322 return 0; 323 } 324 325 326 int 327 posix_spawnattr_setpgroup(posix_spawnattr_t *_attr, pid_t pgroup) 328 { 329 struct _posix_spawnattr *attr; 330 331 if (_attr == NULL || (attr = *_attr) == NULL) 332 return EINVAL; 333 334 attr->pgroup = pgroup; 335 336 return 0; 337 } 338 339 340 int 341 posix_spawnattr_getsigdefault(const posix_spawnattr_t *_attr, sigset_t *sigdefault) 342 { 343 struct _posix_spawnattr *attr; 344 345 if (_attr == NULL || (attr = *_attr) == NULL || sigdefault == NULL) 346 return EINVAL; 347 348 memcpy(sigdefault, &attr->sigdefault, sizeof(sigset_t)); 349 350 return 0; 351 } 352 353 354 int 355 posix_spawnattr_setsigdefault(posix_spawnattr_t *_attr, 356 const sigset_t *sigdefault) 357 { 358 struct _posix_spawnattr *attr; 359 360 if (_attr == NULL || (attr = *_attr) == NULL || sigdefault == NULL) 361 return EINVAL; 362 363 memcpy(&attr->sigdefault, sigdefault, sizeof(sigset_t)); 364 365 return 0; 366 } 367 368 369 int 370 posix_spawnattr_getsigmask(const posix_spawnattr_t *_attr, sigset_t *sigmask) 371 { 372 struct _posix_spawnattr *attr; 373 374 if (_attr == NULL || (attr = *_attr) == NULL || sigmask == NULL) 375 return EINVAL; 376 377 memcpy(sigmask, &attr->sigmask, sizeof(sigset_t)); 378 379 return 0; 380 } 381 382 383 int 384 posix_spawnattr_setsigmask(posix_spawnattr_t *_attr, const sigset_t *sigmask) 385 { 386 struct _posix_spawnattr *attr; 387 388 if (_attr == NULL || (attr = *_attr) == NULL || sigmask == NULL) 389 return EINVAL; 390 391 memcpy(&attr->sigmask, sigmask, sizeof(sigset_t)); 392 393 return 0; 394 } 395 396 397 static int 398 process_spawnattr(const posix_spawnattr_t *_attr) 399 { 400 if (_attr == NULL) 401 return 0; 402 403 struct _posix_spawnattr *attr = *_attr; 404 if (attr == NULL) 405 return EINVAL; 406 407 if ((attr->flags & POSIX_SPAWN_SETSIGMASK) != 0) 408 sigprocmask(SIG_SETMASK, &attr->sigmask, NULL); 409 410 if ((attr->flags & POSIX_SPAWN_SETSIGDEF) != 0) { 411 struct sigaction action; 412 action.sa_handler = SIG_DFL; 413 action.sa_flags = 0; 414 action.sa_userdata = NULL; 415 sigemptyset(&action.sa_mask); 416 for (int i = 1; i <= MAX_SIGNAL_NUMBER; i++) { 417 if (sigismember(&attr->sigdefault, i) == 1 418 && sigaction(i, &action, NULL) != 0) { 419 return errno; 420 } 421 } 422 } 423 424 if ((attr->flags & POSIX_SPAWN_RESETIDS) != 0) { 425 if (setegid(getgid()) != 0) 426 return errno; 427 if (seteuid(getuid()) != 0) 428 return errno; 429 } 430 431 if ((attr->flags & POSIX_SPAWN_SETSID) != 0) { 432 if (setsid() != 0) 433 return errno; 434 } 435 436 if ((attr->flags & POSIX_SPAWN_SETPGROUP) != 0) { 437 if (setpgid(0, attr->pgroup) != 0) 438 return errno; 439 } 440 441 return 0; 442 } 443 444 445 static int 446 process_file_actions(const posix_spawn_file_actions_t *_actions, int *errfd) 447 { 448 if (_actions == NULL) 449 return 0; 450 451 struct _posix_spawn_file_actions* actions = *_actions; 452 if (actions == NULL) 453 return EINVAL; 454 455 for (int i = 0; i < actions->count; i++) { 456 struct _file_action *action = &actions->actions[i]; 457 458 if (action->fd == *errfd) { 459 int newfd = dup(action->fd); 460 if (newfd == -1) 461 return errno; 462 close(action->fd); 463 fcntl(newfd, F_SETFD, FD_CLOEXEC); 464 *errfd = newfd; 465 } 466 467 if (action->type == file_action_close) { 468 if (close(action->fd) != 0) 469 return errno; 470 } else if (action->type == file_action_open) { 471 int fd = open(action->action.open_action.path, 472 action->action.open_action.oflag, 473 action->action.open_action.mode); 474 if (fd == -1) 475 return errno; 476 if (fd != action->fd) { 477 if (dup2(fd, action->fd) == -1) 478 return errno; 479 if (close(fd) != 0) 480 return errno; 481 } 482 } else if (action->type == file_action_dup2) { 483 if (dup2(action->action.dup2_action.srcfd, action->fd) == -1) 484 return errno; 485 } else if (action->type == file_action_chdir) { 486 if (chdir(action->action.chdir_action.path) == -1) 487 return errno; 488 } else if (action->type == file_action_fchdir) { 489 if (fchdir(action->fd) == -1) 490 return errno; 491 } 492 } 493 494 return 0; 495 } 496 497 498 static int 499 spawn_using_fork(pid_t *_pid, const char *path, 500 const posix_spawn_file_actions_t *actions, 501 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], 502 bool envpath) 503 { 504 int err = 0; 505 int fds[2]; 506 pid_t pid; 507 508 if (pipe(fds) != 0) 509 return errno; 510 if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) != 0 511 || fcntl(fds[1], F_SETFD, FD_CLOEXEC) != 0) { 512 goto fail; 513 } 514 pid = vfork(); 515 if (pid == -1) 516 goto fail; 517 518 if (pid != 0) { 519 // parent 520 if (_pid != NULL) 521 *_pid = pid; 522 close(fds[1]); 523 read(fds[0], &err, sizeof(err)); 524 if (err != 0) 525 waitpid(pid, NULL, WNOHANG); 526 close(fds[0]); 527 return err; 528 } 529 530 // child 531 close(fds[0]); 532 533 err = process_spawnattr(attrp); 534 if (err == 0) 535 err = process_file_actions(actions, &fds[1]); 536 if (err != 0) 537 goto fail_child; 538 539 if (envpath) 540 execvpe(path, argv, envp != NULL ? envp : environ); 541 else 542 execve(path, argv, envp != NULL ? envp : environ); 543 544 err = errno; 545 546 fail_child: 547 write(fds[1], &err, sizeof(err)); 548 close(fds[1]); 549 _exit(127); 550 551 fail: 552 err = errno; 553 close(fds[0]); 554 close(fds[1]); 555 return err; 556 } 557 558 559 static int 560 spawn_using_load_image(pid_t *_pid, const char *_path, 561 char *const argv[], char *const envp[], bool envpath) 562 { 563 const char* path; 564 // if envpath is specified but the path contains '/', don't search PATH 565 if (!envpath || strchr(_path, '/') != NULL) { 566 path = _path; 567 } else { 568 char* buffer = (char*)alloca(B_PATH_NAME_LENGTH); 569 status_t status = __look_up_in_path(_path, buffer); 570 if (status != B_OK) 571 return status; 572 path = buffer; 573 } 574 575 // count arguments 576 int32 argCount = 0; 577 while (argv[argCount] != NULL) 578 argCount++; 579 580 thread_id thread = __load_image_at_path(path, argCount, (const char**)argv, 581 (const char**)(envp != NULL ? envp : environ)); 582 if (thread < 0) 583 return thread; 584 585 *_pid = thread; 586 return resume_thread(thread); 587 } 588 589 590 static int 591 do_posix_spawn(pid_t *_pid, const char *path, 592 const posix_spawn_file_actions_t *actions, 593 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], 594 bool envpath) 595 { 596 if ((actions == NULL || (*actions)->count == 0) 597 && (attrp == NULL || (*attrp)->flags == 0)) { 598 return spawn_using_load_image(_pid, path, argv, envp, envpath); 599 } else { 600 return spawn_using_fork(_pid, path, actions, attrp, argv, envp, 601 envpath); 602 } 603 } 604 605 606 int 607 posix_spawn(pid_t *pid, const char *path, 608 const posix_spawn_file_actions_t *file_actions, 609 const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) 610 { 611 return do_posix_spawn(pid, path, file_actions, attrp, argv, envp, false); 612 } 613 614 615 int 616 posix_spawnp(pid_t *pid, const char *file, 617 const posix_spawn_file_actions_t *file_actions, 618 const posix_spawnattr_t *attrp, char *const argv[], 619 char *const envp[]) 620 { 621 return do_posix_spawn(pid, file, file_actions, attrp, argv, envp, true); 622 } 623 624