| // Copyright 2015 The Bazel Authors. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #define _GNU_SOURCE | |
| #include <unistd.h> | |
| #include <sys/stat.h> | |
| #include <sys/time.h> | |
| #include <sys/types.h> | |
| #include <sys/wait.h> | |
| #include <errno.h> | |
| #include <signal.h> | |
| #include <math.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <fcntl.h> | |
| #include "process-tools.h" | |
| int SwitchToEuid() { | |
| int uid = getuid(); | |
| int euid = geteuid(); | |
| if (uid != euid) { | |
| CHECK_CALL(setreuid(euid, euid)); | |
| } | |
| return euid; | |
| } | |
| int SwitchToEgid() { | |
| int gid = getgid(); | |
| int egid = getegid(); | |
| if (gid != egid) { | |
| CHECK_CALL(setregid(egid, egid)); | |
| } | |
| return egid; | |
| } | |
| void Redirect(const char *target_path, int fd, const char *name) { | |
| if (target_path != NULL && strcmp(target_path, "-") != 0) { | |
| int fd_out; | |
| const int flags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND; | |
| CHECK_CALL(fd_out = open(target_path, flags, 0666)); | |
| CHECK_CALL(dup2(fd_out, fd)); | |
| CHECK_CALL(close(fd_out)); | |
| } | |
| } | |
| void RedirectStdout(const char *stdout_path) { | |
| Redirect(stdout_path, STDOUT_FILENO, "stdout"); | |
| } | |
| void RedirectStderr(const char *stderr_path) { | |
| Redirect(stderr_path, STDERR_FILENO, "stderr"); | |
| } | |
| void KillEverything(int pgrp, bool gracefully, double graceful_kill_delay) { | |
| if (gracefully) { | |
| kill(-pgrp, SIGTERM); | |
| // Round up fractional seconds in this polling implementation. | |
| int kill_delay = (int)(ceil(graceful_kill_delay)); | |
| // If the process is still alive, give it some time to die gracefully. | |
| while (kill_delay-- > 0 && kill(-pgrp, 0) == 0) { | |
| sleep(1); | |
| } | |
| } | |
| kill(-pgrp, SIGKILL); | |
| } | |
| void HandleSignal(int sig, void (*handler)(int)) { | |
| struct sigaction sa = {.sa_handler = handler}; | |
| CHECK_CALL(sigemptyset(&sa.sa_mask)); | |
| CHECK_CALL(sigaction(sig, &sa, NULL)); | |
| } | |
| void UnHandle(int sig) { | |
| switch (sig) { | |
| case SIGSTOP: | |
| case SIGKILL: | |
| // These signals can't be handled, so they'll always have a valid default | |
| // handler. In fact, even trying to install SIG_DFL again will result in | |
| // EINVAL, so we'll just not do anything for these. | |
| return; | |
| default: | |
| HandleSignal(sig, SIG_DFL); | |
| } | |
| } | |
| void ClearSignalMask() { | |
| // Use an empty signal mask for the process. | |
| sigset_t empty_sset; | |
| CHECK_CALL(sigemptyset(&empty_sset)); | |
| CHECK_CALL(sigprocmask(SIG_SETMASK, &empty_sset, NULL)); | |
| // Set the default signal handler for all signals. | |
| for (int i = 1; i < NSIG; ++i) { | |
| if (i == SIGKILL || i == SIGSTOP) { | |
| continue; | |
| } | |
| struct sigaction sa = {.sa_handler = SIG_DFL}; | |
| CHECK_CALL(sigemptyset(&sa.sa_mask)); | |
| // Ignore possible errors, because we might not be allowed to set the | |
| // handler for certain signals, but we still want to try. | |
| sigaction(i, &sa, NULL); | |
| } | |
| } | |
| void SetTimeout(double timeout_secs) { | |
| if (timeout_secs <= 0) { | |
| return; | |
| } | |
| double int_val, fraction_val; | |
| fraction_val = modf(timeout_secs, &int_val); | |
| struct itimerval timer; | |
| timer.it_interval.tv_sec = 0; | |
| timer.it_interval.tv_usec = 0; | |
| timer.it_value.tv_sec = (long)int_val, | |
| timer.it_value.tv_usec = (long)(fraction_val * 1e6); | |
| CHECK_CALL(setitimer(ITIMER_REAL, &timer, NULL)); | |
| } | |
| int WaitChild(pid_t pid, const char *name) { | |
| int err, status; | |
| do { | |
| err = waitpid(pid, &status, 0); | |
| } while (err == -1 && errno == EINTR); | |
| if (err == -1) { | |
| DIE("wait on %s (pid %d) failed\n", name, pid); | |
| } | |
| return status; | |
| } |