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