int process_command_line(command_line* cmd_line) { int child_pid = fork(); if (child_pid == 0) { // use pipe's fd for piping if (cmd_line->piped) { dup3(cmd_line->pipe_filedes[0], FD_STDIN); dup3(cmd_line->target_command_line->pipe_filedes[1], FD_STDOUT); } usleep(DELAY_EXEC_IN_MICRO_SECOND); char **argv = build_argv(cmd_line); execvp(argv[0], argv); fprintf(stderr, "failed to execute command \'%s\': No such file or directory\n", argv[0]); exit(-1); } cmd_line->pid = child_pid; if (equal_str(cmd_line->action, "wait")) { pid_t pid; int status; while ((pid = waitpid(child_pid, &status, 0))) { if (pid == -1 && errno == EINTR) { continue; } break; } } return 0; }
static uint32_t execute_addr2line(char* buffer, uint32_t buffer_size, const char* image, void* addr) { ssize_t len; int pipefd[2]; uint32_t output_len; (void) pipe(pipefd); pid_t pid = fork(); if (pid == 0) { char buf[32]; int fd_stderr = open("/tmp/addr2line.err", O_CREAT | O_WRONLY, 0666); (void) close(pipefd[0]); # ifndef HAVE_DUP3 (void) dup2(pipefd[1], STDOUT_FILENO); (void) dup2(fd_stderr, STDERR_FILENO); # else (void) dup3(pipefd[1], STDOUT_FILENO, O_CLOEXEC); (void) dup3(fd_stderr, STDERR_FILENO, O_CLOEXEC); # endif // Invokes addr2line utility to determine the function name // and the line information from an address in the code segment (void) snprintf(buf, sizeof(buf), "%p", addr); (void) execlp("addr2line", "addr2line", buf, "-f", "-C", "-e", image, (char*)0); abort(); } (void) close(pipefd[1]); output_len = 0; loop: len = read(pipefd[0], buffer + output_len, buffer_size - output_len); if (len > 0) { output_len += len; goto loop; } buffer[output_len] = 0; (void) close(pipefd[0]); (void) waitpid(pid, 0, 0); return output_len; }
static int spawn_getent(const char *database, const char *key, pid_t *rpid) { int pipe_fds[2]; pid_t pid; assert(database); assert(key); assert(rpid); if (pipe2(pipe_fds, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to allocate pipe: %m"); pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork getent child: %m"); else if (pid == 0) { int nullfd; char *empty_env = NULL; if (dup3(pipe_fds[1], STDOUT_FILENO, 0) < 0) _exit(EXIT_FAILURE); if (pipe_fds[0] > 2) safe_close(pipe_fds[0]); if (pipe_fds[1] > 2) safe_close(pipe_fds[1]); nullfd = open("/dev/null", O_RDWR); if (nullfd < 0) _exit(EXIT_FAILURE); if (dup3(nullfd, STDIN_FILENO, 0) < 0) _exit(EXIT_FAILURE); if (dup3(nullfd, STDERR_FILENO, 0) < 0) _exit(EXIT_FAILURE); if (nullfd > 2) safe_close(nullfd); (void) reset_all_signal_handlers(); (void) reset_signal_mask(); close_all_fds(NULL, 0); execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env); execle("/bin/getent", "getent", database, key, NULL, &empty_env); _exit(EXIT_FAILURE); } pipe_fds[1] = safe_close(pipe_fds[1]); *rpid = pid; return pipe_fds[0]; }
int main(int argc, char *argv[]) { int r, fd = -1, saved_stderr = -1; log_parse_environment(); log_open(); r = parse_argv(argc, argv); if (r <= 0) goto finish; fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix); if (fd < 0) { log_error("Failed to create stream fd: %s", strerror(-fd)); r = fd; goto finish; } saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3); if (dup3(fd, STDOUT_FILENO, 0) < 0 || dup3(fd, STDERR_FILENO, 0) < 0) { log_error("Failed to duplicate fd: %m"); r = -errno; goto finish; } if (fd >= 3) close_nointr_nofail(fd); fd = -1; if (argc <= optind) execl("/bin/cat", "/bin/cat", NULL); else execvp(argv[optind], argv + optind); r = -errno; /* Let's try to restore a working stderr, so we can print the error message */ if (saved_stderr >= 0) dup3(saved_stderr, STDERR_FILENO, 0); log_error("Failed to execute process: %s", strerror(-r)); finish: if (fd >= 0) close_nointr_nofail(fd); if (saved_stderr >= 0) close_nointr_nofail(saved_stderr); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
static int run(int argc, char *argv[]) { _cleanup_close_ int fd = -1, saved_stderr = -1; int r; log_parse_environment(); log_open(); r = parse_argv(argc, argv); if (r <= 0) return r; fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix); if (fd < 0) return log_error_errno(fd, "Failed to create stream fd: %m"); saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3); r = rearrange_stdio(STDIN_FILENO, fd, fd); /* Invalidates fd on succcess + error! */ TAKE_FD(fd); if (r < 0) return log_error_errno(r, "Failed to rearrange stdout/stderr: %m"); if (argc <= optind) (void) execl("/bin/cat", "/bin/cat", NULL); else (void) execvp(argv[optind], argv + optind); r = -errno; /* Let's try to restore a working stderr, so we can print the error message */ if (saved_stderr >= 0) (void) dup3(saved_stderr, STDERR_FILENO, 0); return log_error_errno(r, "Failed to execute process: %m"); }
/*! Duplicate File Descriptor. * \return whatever \c dup2 or \c dup3 returns. * \todo Better to use \c freopen? */ inline int dupx(int oldfd, int newfd) { #ifdef _GNU_SOURCE int flags = 0; return dup3(oldfd, newfd, flags); #else return dup2(oldfd, newfd); #endif }
ATF_TC_BODY(dup3_max, tc) { struct rlimit res; (void)memset(&res, 0, sizeof(struct rlimit)); (void)getrlimit(RLIMIT_NOFILE, &res); errno = 0; ATF_REQUIRE_ERRNO(EBADF, dup3(STDERR_FILENO, res.rlim_cur + 1, O_CLOEXEC) == -1); }
int uv__dup2_cloexec(int oldfd, int newfd) { int r; #if defined(__FreeBSD__) && __FreeBSD__ >= 10 do r = dup3(oldfd, newfd, O_CLOEXEC); while (r == -1 && errno == EINTR); if (r == -1) return -errno; return r; #elif defined(__FreeBSD__) && defined(F_DUP2FD_CLOEXEC) do r = fcntl(oldfd, F_DUP2FD_CLOEXEC, newfd); while (r == -1 && errno == EINTR); if (r != -1) return r; if (errno != EINVAL) return -errno; /* Fall through. */ #elif defined(__linux__) static int no_dup3; if (!no_dup3) { do r = uv__dup3(oldfd, newfd, UV__O_CLOEXEC); while (r == -1 && (errno == EINTR || errno == EBUSY)); if (r != -1) return r; if (errno != ENOSYS) return -errno; /* Fall through. */ no_dup3 = 1; } #endif { int err; do r = dup2(oldfd, newfd); #if defined(__linux__) while (r == -1 && (errno == EINTR || errno == EBUSY)); #else while (r == -1 && errno == EINTR); #endif if (r == -1) return -errno; err = uv__cloexec(newfd, 1); if (err) { uv__close(newfd); return err; } return r; } }
STATIC int copyfd(int from, int to, int cloexec) { int newfd; if (cloexec && to > 2) newfd = dup3(from, to, O_CLOEXEC); else newfd = dup2(from, to); return newfd; }
int dup2(int oldFD, int newFD, bool closeOnExec) { int flags = 0; if (closeOnExec) { flags |= O_CLOEXEC; } int fd = dup3(oldFD, newFD, flags); if (fd == -1) { THROW_EXCEPTION(UtilsException, "dup3() failed", errno); } return fd; }
static void check_mode(bool _dup, bool _dup2, bool _dup3) { int mode[3] = { O_RDONLY, O_WRONLY, O_RDWR }; int perm[5] = { 0700, 0400, 0600, 0444, 0666 }; struct stat st, st1; int fd, fd1, fd2; size_t i, j; /* * Check that a duplicated descriptor * retains the mode of the original file. */ for (i = 0; i < __arraycount(mode); i++) { for (j = 0; j < __arraycount(perm); j++) { fd1 = open(path, mode[i] | O_CREAT, perm[j]); fd2 = open("/etc/passwd", O_RDONLY); ATF_REQUIRE(fd1 >= 0); ATF_REQUIRE(fd2 >= 0); if (_dup != false) fd = dup(fd1); else if (_dup2 != false) fd = dup2(fd1, fd2); else if (_dup3 != false) fd = dup3(fd1, fd2, O_CLOEXEC); else { fd = -1; } ATF_REQUIRE(fd >= 0); (void)memset(&st, 0, sizeof(struct stat)); (void)memset(&st1, 0, sizeof(struct stat)); ATF_REQUIRE(fstat(fd, &st) == 0); ATF_REQUIRE(fstat(fd1, &st1) == 0); if (st.st_mode != st1.st_mode) atf_tc_fail("invalid mode"); (void)close(fd); (void)close(fd1); (void)close(fd2); (void)unlink(path); } } }
int main() { dup(2); //staptest// dup (2) = NNNN dup(256); //staptest// dup (256) = -NNNN (EBADF) dup(-1); //staptest// dup (-1) = -NNNN (EBADF) dup2(3, 4); //staptest// [[[[dup2 (3, 4!!!!dup3 (3, 4, 0x0]]]]) = 4 dup2(255, 256); //staptest// [[[[dup2 (255, 256!!!!dup3 (255, 256, 0x0]]]]) = -NNNN (EBADF) /* weird corner case oldfd == newfd */ dup2(1, 1); //staptest// [[[[dup2 (1, 1!!!!fcntl (1, F_GETFL, 0x0]]]]) = 1 dup2(-1, 4); //staptest// [[[[dup2 (-1, 4!!!!dup3 (-1, 4, 0x0]]]]) = -NNNN (EBADF) dup2(3, -1); //staptest// [[[[dup2 (3, -1!!!!dup3 (3, -1, 0x0]]]]) = -NNNN (EBADF) #ifdef O_CLOEXEC dup3 (4, 5, O_CLOEXEC); //staptest// dup3 (4, 5, O_CLOEXEC) = 5 dup3 (256, 255, O_CLOEXEC); //staptest// dup3 (256, 255, O_CLOEXEC) = -NNNN (EBADF) dup3 (5, 6, 666); //staptest// dup3 (5, 6, O_[^ ]+|XXXX) = -NNNN (EINVAL) /* corner case not valid for dup3 */ dup3 (1, 1, O_CLOEXEC); //staptest// dup3 (1, 1, O_CLOEXEC) = -NNNN (EINVAL) dup3 (-1, 7, 0); //staptest// dup3 (-1, 7, 0x0) = -NNNN (EBADF) dup3 (3, -1, O_CLOEXEC); //staptest// dup3 (3, -1, O_CLOEXEC) = -NNNN (EBADF) dup3 (3, 7, -1); //staptest// dup3 (3, 7, O_[^ ]+|XXXX) = -NNNN (EINVAL) #endif return 0; }
int xdup3nc(int oldfd, int newfd, int flags) { int rc; do { rc = dup3(oldfd, newfd, flags); } while (rc < 0 && errno == EINTR); if (rc < 0) die_errno("dup3(%d,%d,0x%x)", oldfd, newfd, (unsigned) flags); return rc; }
INTERPOSE (dup3, int, int oldfd, int newfd, int flags) { int ret; CHECK_INTERPOSE (dup3); ret = dup3 (oldfd, newfd, flags); copy_our_fd (oldfd, ret); copy_our_af (oldfd, ret); return ret; }
int main(int argc, char* argv[]) { pid_t child; int status; int ret; int fd; int pipe_fds[2]; struct rlimit nofile; int fd_limit; if (argc == 2) { atomic_puts("EXIT-SUCCESS"); return 77; } /* Various spawning APIs try to close all open file descriptors before exec --- via direct close(), or by setting CLOEXEC. Check that those don't interfere with rr by closing RR_RESERVED_ROOT_DIR_FD or some other essential file descriptor. */ test_assert(0 == getrlimit(RLIMIT_NOFILE, &nofile)); if (nofile.rlim_cur == RLIM_INFINITY || nofile.rlim_cur > MAX_FDS) { fd_limit = MAX_FDS; } else { fd_limit = nofile.rlim_cur; } for (fd = STDERR_FILENO + 1; fd < fd_limit; ++fd) { ret = fcntl(fd, F_SETFD, FD_CLOEXEC); test_assert(ret == 0 || (ret == -1 && errno == EBADF)); ret = dup2(STDERR_FILENO, fd); test_assert(ret == fd || (ret == -1 && errno == EBADF)); ret = dup3(STDERR_FILENO, fd, O_CLOEXEC); test_assert(ret == fd || (ret == -1 && errno == EBADF)); ret = close(fd); test_assert(ret == 0 || (ret == -1 && errno == EBADF)); } /* Check that syscall buffering still works */ test_assert(0 == pipe(pipe_fds)); test_assert(1 == write(pipe_fds[1], "c", 1)); if (0 == (child = fork())) { execl(argv[0], argv[0], "step2", NULL); } test_assert(child == waitpid(child, &status, 0)); test_assert(WIFEXITED(status) && WEXITSTATUS(status) == 77); return 0; }
int uv__dup2_cloexec(int oldfd, int newfd) { int r; #if defined(__FreeBSD__) r = dup3(oldfd, newfd, O_CLOEXEC); if (r == -1) return -errno; return r; #elif defined(__linux__) static int no_dup3; if (!no_dup3) { do r = uv__dup3(oldfd, newfd, UV__O_CLOEXEC); while (r == -1 && errno == EBUSY); if (r != -1) return r; if (errno != ENOSYS) return -errno; /* Fall through. */ no_dup3 = 1; } #endif { int err; do r = dup2(oldfd, newfd); #if defined(__linux__) while (r == -1 && errno == EBUSY); #else while (0); /* Never retry. */ #endif if (r == -1) return -errno; err = uv__cloexec(newfd, 1); if (err) { uv__close(newfd); return err; } return r; } }
int main(int argc, char* argv[]) { int pipe_fds[2]; int fd; char ch; test_assert(0 == pipe(pipe_fds)); test_assert(10 == write(pipe_fds[1], buf, 10)); fd = dup(pipe_fds[0]); test_assert(fd >= 0); test_assert(fd != pipe_fds[0] && fd != pipe_fds[1]); test_assert(1 == read(fd, &ch, 1)); test_assert(ch == '0'); fd = dup2(pipe_fds[0], 0); test_assert(fd == 0); test_assert(1 == read(fd, &ch, 1)); test_assert(ch == '1'); fd = dup3(pipe_fds[0], 49, O_CLOEXEC); test_assert(fd == 49); test_assert(1 == read(fd, &ch, 1)); test_assert(ch == '2'); test_assert(FD_CLOEXEC == fcntl(fd, F_GETFD)); test_assert(fd == dup2(0, fd)); test_assert(0 == fcntl(fd, F_GETFD)); fd = fcntl(pipe_fds[0], F_DUPFD, 49); test_assert(fd == 50); test_assert(1 == read(fd, &ch, 1)); test_assert(ch == '3'); test_assert(0 == fcntl(fd, F_GETFD)); fd = fcntl(pipe_fds[0], F_DUPFD_CLOEXEC, 49); test_assert(fd == 51); test_assert(1 == read(fd, &ch, 1)); test_assert(ch == '4'); test_assert(FD_CLOEXEC == fcntl(fd, F_GETFD)); atomic_puts("EXIT-SUCCESS"); return 0; }
JNIEXPORT jint JNICALL xnio_native(dup2)(JNIEnv *env, jclass clazz, jint oldFD, jint newFD, jobject preserve) { if (dup3) { if (dup3(oldFD, newFD, O_CLOEXEC) < 0) { return -errno; } else { return 0; } } else { if (dup2(oldFD, newFD) < 0) { return -errno; } if (fcntl(newFD, F_SETFD, FD_CLOEXEC) == -1) { int code = errno; close(newFD); return -code; } return 0; } }
ATF_TC_BODY(dup3_err, tc) { int fd; fd = open("/etc/passwd", O_RDONLY); ATF_REQUIRE(fd >= 0); errno = 0; #if defined(__FreeBSD__) || defined(__linux__) /* * FreeBSD and linux return EINVAL, because... * * [EINVAL] The oldd argument is equal to the newd argument. */ ATF_REQUIRE(dup3(fd, fd, O_CLOEXEC) == -1); #else ATF_REQUIRE(dup3(fd, fd, O_CLOEXEC) != -1); #endif errno = 0; #if defined(__FreeBSD__) || defined(__linux__) ATF_REQUIRE_ERRNO(EINVAL, dup3(-1, -1, O_CLOEXEC) == -1); ATF_REQUIRE_ERRNO(EBADF, dup3(fd, -1, O_CLOEXEC) == -1); #else ATF_REQUIRE_ERRNO(EBADF, dup3(-1, -1, O_CLOEXEC) == -1); #endif errno = 0; ATF_REQUIRE_ERRNO(EBADF, dup3(fd, -1, O_CLOEXEC) == -1); errno = 0; ATF_REQUIRE_ERRNO(EBADF, dup3(-1, fd, O_CLOEXEC) == -1); errno = 0; ATF_REQUIRE_ERRNO(EINVAL, dup3(fd, 1, O_NOFOLLOW) == -1); (void)close(fd); }
int dup3 (int oldfd, int newfd, int flags) { #if HAVE_DUP3 # undef dup3 /* Try the system call first, if it exists. (We may be running with a glibc that has the function but with an older kernel that lacks it.) */ { /* Cache the information whether the system call really exists. */ static int have_dup3_really; /* 0 = unknown, 1 = yes, -1 = no */ if (have_dup3_really >= 0) { int result = dup3 (oldfd, newfd, flags); if (!(result < 0 && errno == ENOSYS)) { have_dup3_really = 1; #if REPLACE_FCHDIR if (0 <= result) result = _gl_register_dup (oldfd, newfd); #endif return result; } have_dup3_really = -1; } } #endif if (newfd < 0 || newfd >= getdtablesize () || fcntl (oldfd, F_GETFD) == -1) { errno = EBADF; return -1; } if (newfd == oldfd) { errno = EINVAL; return -1; } /* Check the supported flags. Note that O_NONBLOCK is not supported, because setting it on newfd would implicitly also set it on oldfd. */ if ((flags & ~(O_CLOEXEC | O_BINARY | O_TEXT)) != 0) { errno = EINVAL; return -1; } if (flags & O_CLOEXEC) { int result; close (newfd); result = fcntl (oldfd, F_DUPFD_CLOEXEC, newfd); if (newfd < result) { close (result); errno = EIO; result = -1; } if (result < 0) return -1; } else if (dup2 (oldfd, newfd) < 0) return -1; #if O_BINARY if (flags & O_BINARY) setmode (newfd, O_BINARY); else if (flags & O_TEXT) setmode (newfd, O_TEXT); #endif return newfd; }
static apr_status_t file_dup(apr_file_t **new_file, apr_file_t *old_file, apr_pool_t *p, int which_dup) { int rv; #ifdef HAVE_DUP3 int flags = 0; #endif if (which_dup == 2) { if ((*new_file) == NULL) { /* We can't dup2 unless we have a valid new_file */ return APR_EINVAL; } #ifdef HAVE_DUP3 if (!((*new_file)->flags & (APR_FOPEN_NOCLEANUP|APR_INHERIT))) flags |= O_CLOEXEC; rv = dup3(old_file->filedes, (*new_file)->filedes, flags); #else rv = dup2(old_file->filedes, (*new_file)->filedes); if (!((*new_file)->flags & (APR_FOPEN_NOCLEANUP|APR_INHERIT))) { int flags; if (rv == -1) return errno; if ((flags = fcntl((*new_file)->filedes, F_GETFD)) == -1) return errno; flags |= FD_CLOEXEC; if (fcntl((*new_file)->filedes, F_SETFD, flags) == -1) return errno; } #endif } else { rv = dup(old_file->filedes); } if (rv == -1) return errno; if (which_dup == 1) { (*new_file) = (apr_file_t *)apr_pcalloc(p, sizeof(apr_file_t)); (*new_file)->pool = p; (*new_file)->filedes = rv; } (*new_file)->fname = apr_pstrdup(p, old_file->fname); (*new_file)->buffered = old_file->buffered; /* If the existing socket in a dup2 is already buffered, we * have an existing and valid (hopefully) mutex, so we don't * want to create it again as we could leak! */ #if APR_HAS_THREADS if ((*new_file)->buffered && !(*new_file)->thlock && old_file->thlock) { apr_thread_mutex_create(&((*new_file)->thlock), APR_THREAD_MUTEX_DEFAULT, p); } #endif /* As above, only create the buffer if we haven't already * got one. */ if ((*new_file)->buffered && !(*new_file)->buffer) { (*new_file)->buffer = apr_palloc(p, old_file->bufsize); (*new_file)->bufsize = old_file->bufsize; } /* this is the way dup() works */ (*new_file)->blocking = old_file->blocking; /* make sure unget behavior is consistent */ (*new_file)->ungetchar = old_file->ungetchar; /* apr_file_dup2() retains the original cleanup, reflecting * the existing inherit and nocleanup flags. This means, * that apr_file_dup2() cannot be called against an apr_file_t * already closed with apr_file_close, because the expected * cleanup was already killed. */ if (which_dup == 2) { return APR_SUCCESS; } /* apr_file_dup() retains all old_file flags with the exceptions * of APR_INHERIT and APR_FOPEN_NOCLEANUP. * The user must call apr_file_inherit_set() on the dupped * apr_file_t when desired. */ (*new_file)->flags = old_file->flags & ~(APR_INHERIT | APR_FOPEN_NOCLEANUP); apr_pool_cleanup_register((*new_file)->pool, (void *)(*new_file), apr_unix_file_cleanup, apr_unix_child_file_cleanup); #ifndef WAITIO_USES_POLL /* Start out with no pollset. apr_wait_for_io_or_timeout() will * initialize the pollset if needed. */ (*new_file)->pollset = NULL; #endif return APR_SUCCESS; }
void nsexec(void) { int pipenum; // If we don't have init pipe, then just return to the go routine, // we'll only have init pipe for start or exec pipenum = get_init_pipe(); if (pipenum == -1) { return; } // Retrieve the netlink header struct nlmsghdr nl_msg_hdr; int len; if ((len = read(pipenum, &nl_msg_hdr, NLMSG_HDRLEN)) != NLMSG_HDRLEN) { pr_perror("Invalid netlink header length %d", len); exit(1); } if (nl_msg_hdr.nlmsg_type == NLMSG_ERROR) { pr_perror("Failed to read netlink message"); exit(1); } if (nl_msg_hdr.nlmsg_type != INIT_MSG) { pr_perror("Unexpected msg type %d", nl_msg_hdr.nlmsg_type); exit(1); } // Retrieve data int nl_total_size = NLMSG_PAYLOAD(&nl_msg_hdr, 0); char data[nl_total_size]; if ((len = read(pipenum, data, nl_total_size)) != nl_total_size) { pr_perror("Failed to read netlink payload, %d != %d", len, nl_total_size); exit(1); } jmp_buf env; int syncpipe[2] = {-1, -1}; struct nsenter_config config = process_nl_attributes(pipenum, data, nl_total_size); // required clone_flags to be passed if (config.cloneflags == -1) { pr_perror("Missing clone_flags"); exit(1); } // prepare sync pipe between parent and child. We need this to let the // child know that the parent has finished setting up if (pipe(syncpipe) != 0) { pr_perror("Failed to setup sync pipe between parent and child"); exit(1); } if (setjmp(env) == 1) { // Child uint8_t s = 0; int consolefd = config.consolefd; // close the writing side of pipe close(syncpipe[1]); // sync with parent if ((read(syncpipe[0], &s, 1) != 1) || (s != 1)) { pr_perror("Failed to read sync byte from parent"); exit(1); } if (setsid() == -1) { pr_perror("setsid failed"); exit(1); } if (setuid(0) == -1) { pr_perror("setuid failed"); exit(1); } if (setgid(0) == -1) { pr_perror("setgid failed"); exit(1); } if (setgroups(0, NULL) == -1) { pr_perror("setgroups failed"); exit(1); } if (consolefd != -1) { if (ioctl(consolefd, TIOCSCTTY, 0) == -1) { pr_perror("ioctl TIOCSCTTY failed"); exit(1); } if (dup3(consolefd, STDIN_FILENO, 0) != STDIN_FILENO) { pr_perror("Failed to dup stdin"); exit(1); } if (dup3(consolefd, STDOUT_FILENO, 0) != STDOUT_FILENO) { pr_perror("Failed to dup stdout"); exit(1); } if (dup3(consolefd, STDERR_FILENO, 0) != STDERR_FILENO) { pr_perror("Failed to dup stderr"); exit(1); } } // Finish executing, let the Go runtime take over. return; } // Parent start_child(pipenum, &env, syncpipe, &config); }
int dup2 (int fd, int fd2) { return dup3 (fd, fd2, 0); }
int VanillaProc::setupOOMEvent(const std::string &cgroup_string) { #if !(defined(HAVE_EVENTFD) && defined(HAVE_EXT_LIBCGROUP)) // Shut the compiler up. cgroup_string.size(); return 0; #else // Initialize the event descriptor int tmp_efd = eventfd(0, EFD_CLOEXEC); if (tmp_efd == -1) { dprintf(D_ALWAYS, "Unable to create new event FD for starter: %u %s\n", errno, strerror(errno)); return 1; } // Find the memcg location on disk void * handle = NULL; struct cgroup_mount_point mount_info; int ret = cgroup_get_controller_begin(&handle, &mount_info); std::stringstream oom_control; std::stringstream event_control; bool found_memcg = false; while (ret == 0) { if (strcmp(mount_info.name, MEMORY_CONTROLLER_STR) == 0) { found_memcg = true; oom_control << mount_info.path << "/"; event_control << mount_info.path << "/"; break; } cgroup_get_controller_next(&handle, &mount_info); } if (!found_memcg && (ret != ECGEOF)) { dprintf(D_ALWAYS, "Error while locating memcg controller for starter: %u %s\n", ret, cgroup_strerror(ret)); return 1; } cgroup_get_controller_end(&handle); if (found_memcg == false) { dprintf(D_ALWAYS, "Memcg is not available; OOM notification disabled for starter.\n"); return 1; } // Finish constructing the location of the control files oom_control << cgroup_string << "/memory.oom_control"; std::string oom_control_str = oom_control.str(); event_control << cgroup_string << "/cgroup.event_control"; std::string event_control_str = event_control.str(); // Open the oom_control and event control files TemporaryPrivSentry sentry(PRIV_ROOT); m_oom_fd = open(oom_control_str.c_str(), O_RDONLY | O_CLOEXEC); if (m_oom_fd == -1) { dprintf(D_ALWAYS, "Unable to open the OOM control file for starter: %u %s\n", errno, strerror(errno)); return 1; } int event_ctrl_fd = open(event_control_str.c_str(), O_WRONLY | O_CLOEXEC); if (event_ctrl_fd == -1) { dprintf(D_ALWAYS, "Unable to open event control for starter: %u %s\n", errno, strerror(errno)); return 1; } // Inform Linux we will be handling the OOM events for this container. int oom_fd2 = open(oom_control_str.c_str(), O_WRONLY | O_CLOEXEC); if (oom_fd2 == -1) { dprintf(D_ALWAYS, "Unable to open the OOM control file for writing for starter: %u %s\n", errno, strerror(errno)); close(event_ctrl_fd); return 1; } const char limits [] = "1"; ssize_t nwritten = full_write(oom_fd2, &limits, 1); if (nwritten < 0) { /* Newer kernels return EINVAL if you attempt to enable OOM management * on a cgroup where use_hierarchy is set to 1 and it is not the parent * cgroup. * * This is a common setup, so we log and move along. * * See also #4435. */ if (errno == EINVAL) { dprintf(D_FULLDEBUG, "Unable to setup OOM killer management because" " memory.use_hierarchy is enabled for this cgroup; consider" " disabling it for this host or set BASE_CGROUP=/. The hold" " message for an OOM event may not be reliably set.\n"); } else { dprintf(D_ALWAYS, "Failure when attempting to enable OOM killer " " management for this job (errno=%d, %s).\n", errno, strerror(errno)); close(event_ctrl_fd); close(oom_fd2); close(tmp_efd); return 1; } } close(oom_fd2); // Create the subscription string: std::stringstream sub_ss; sub_ss << tmp_efd << " " << m_oom_fd; std::string sub_str = sub_ss.str(); if ((nwritten = full_write(event_ctrl_fd, sub_str.c_str(), sub_str.size())) < 0) { dprintf(D_ALWAYS, "Unable to write into event control file for starter: %u %s\n", errno, strerror(errno)); close(event_ctrl_fd); close(tmp_efd); return 1; } close(event_ctrl_fd); // Fool DC into talking to the eventfd int pipes[2]; pipes[0] = -1; pipes[1] = -1; int fd_to_replace = -1; if (!daemonCore->Create_Pipe(pipes, true) || pipes[0] == -1) { dprintf(D_ALWAYS, "Unable to create a DC pipe\n"); close(tmp_efd); close(m_oom_fd); m_oom_fd = -1; return 1; } if (!daemonCore->Get_Pipe_FD(pipes[0], &fd_to_replace) || fd_to_replace == -1) { dprintf(D_ALWAYS, "Unable to lookup pipe's FD\n"); close(tmp_efd); close(m_oom_fd); m_oom_fd = -1; daemonCore->Close_Pipe(pipes[0]); daemonCore->Close_Pipe(pipes[1]); return 1; } dup3(tmp_efd, fd_to_replace, O_CLOEXEC); close(tmp_efd); m_oom_efd = pipes[0]; m_oom_efd2 = pipes[1]; // Inform DC we want to recieve notifications from this FD. if (-1 == daemonCore->Register_Pipe(pipes[0],"OOM event fd", static_cast<PipeHandlercpp>(&VanillaProc::outOfMemoryEvent),"OOM Event Handler",this,HANDLE_READ)) { dprintf(D_ALWAYS, "Failed to register OOM event FD pipe.\n"); daemonCore->Close_Pipe(pipes[0]); daemonCore->Close_Pipe(pipes[1]); m_oom_fd = -1; m_oom_efd = -1; m_oom_efd2 = -1; } dprintf(D_FULLDEBUG, "Subscribed the starter to OOM notification for this cgroup; jobs triggering an OOM will be put on hold.\n"); return 0; #endif }
int main(int __unused argc, char __unused *argv[]) { struct rlimit rlp; int orgfd, fd1, fd2, test = 0; orgfd = getafile(); printf("1..32\n"); /* If dup(2) ever work? */ if ((fd1 = dup(orgfd)) < 0) err(1, "dup"); printf("ok %d - dup(2) works\n", ++test); /* Set close-on-exec */ if (fcntl(fd1, F_SETFD, 1) != 0) err(1, "fcntl(F_SETFD)"); /* If dup2(2) ever work? */ if ((fd2 = dup2(fd1, fd1 + 1)) < 0) err(1, "dup2"); printf("ok %d - dup2(2) works\n", ++test); /* Do we get the right fd? */ ++test; if (fd2 != fd1 + 1) printf("no ok %d - dup2(2) didn't give us the right fd\n", test); else printf("ok %d - dup2(2) returned a correct fd\n", test); /* Was close-on-exec cleared? */ ++test; if (fcntl(fd2, F_GETFD) != 0) printf("not ok %d - dup2(2) didn't clear close-on-exec\n", test); else printf("ok %d - dup2(2) cleared close-on-exec\n", test); /* * Dup to itself. * * We're testing a small tweak in dup2 semantics. * Normally dup and dup2 will clear the close-on-exec * flag on the new fd (which appears to be an implementation * mistake from start and not some planned behavior). * In today's implementations of dup and dup2 we have to make * an effort to really clear that flag. But all tested * implementations of dup2 have another tweak. If we * dup2(old, new) when old == new, the syscall short-circuits * and returns early (because there is no need to do all the * work (and there is a risk for serious mistakes)). * So although the docs say that dup2 should "take 'old', * close 'new' perform a dup(2) of 'old' into 'new'" * the docs are not really followed because close-on-exec * is not cleared on 'new'. * * Since everyone has this bug, we pretend that this is * the way it is supposed to be and test here that it really * works that way. * * This is a fine example on where two separate implementation * fuckups take out each other and make the end-result the way * it was meant to be. */ if ((fd2 = dup2(fd1, fd1)) < 0) err(1, "dup2"); printf("ok %d - dup2(2) to itself works\n", ++test); /* Do we get the right fd? */ ++test; if (fd2 != fd1) printf("not ok %d - dup2(2) didn't give us the right fd\n", test); else printf("ok %d - dup2(2) to itself returned a correct fd\n", test); /* Was close-on-exec cleared? */ ++test; if (fcntl(fd2, F_GETFD) == 0) printf("not ok %d - dup2(2) cleared close-on-exec\n", test); else printf("ok %d - dup2(2) didn't clear close-on-exec\n", test); /* Does fcntl(F_DUPFD) work? */ if ((fd2 = fcntl(fd1, F_DUPFD, 10)) < 0) err(1, "fcntl(F_DUPFD)"); if (fd2 < 10) printf("not ok %d - fcntl(F_DUPFD) returned wrong fd %d\n", ++test, fd2); else printf("ok %d - fcntl(F_DUPFD) works\n", ++test); /* Was close-on-exec cleared? */ ++test; if (fcntl(fd2, F_GETFD) != 0) printf( "not ok %d - fcntl(F_DUPFD) didn't clear close-on-exec\n", test); else printf("ok %d - fcntl(F_DUPFD) cleared close-on-exec\n", test); ++test; if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) err(1, "getrlimit"); if ((fd2 = dup2(fd1, rlp.rlim_cur + 1)) >= 0) printf("not ok %d - dup2(2) bypassed NOFILE limit\n", test); else printf("ok %d - dup2(2) didn't bypass NOFILE limit\n", test); /* If fcntl(F_DUP2FD) ever work? */ if ((fd2 = fcntl(fd1, F_DUP2FD, fd1 + 1)) < 0) err(1, "fcntl(F_DUP2FD)"); printf("ok %d - fcntl(F_DUP2FD) works\n", ++test); /* Do we get the right fd? */ ++test; if (fd2 != fd1 + 1) printf( "no ok %d - fcntl(F_DUP2FD) didn't give us the right fd\n", test); else printf("ok %d - fcntl(F_DUP2FD) returned a correct fd\n", test); /* Was close-on-exec cleared? */ ++test; if (fcntl(fd2, F_GETFD) != 0) printf( "not ok %d - fcntl(F_DUP2FD) didn't clear close-on-exec\n", test); else printf("ok %d - fcntl(F_DUP2FD) cleared close-on-exec\n", test); /* Dup to itself */ if ((fd2 = fcntl(fd1, F_DUP2FD, fd1)) < 0) err(1, "fcntl(F_DUP2FD)"); printf("ok %d - fcntl(F_DUP2FD) to itself works\n", ++test); /* Do we get the right fd? */ ++test; if (fd2 != fd1) printf( "not ok %d - fcntl(F_DUP2FD) didn't give us the right fd\n", test); else printf( "ok %d - fcntl(F_DUP2FD) to itself returned a correct fd\n", test); /* Was close-on-exec cleared? */ ++test; if (fcntl(fd2, F_GETFD) == 0) printf("not ok %d - fcntl(F_DUP2FD) cleared close-on-exec\n", test); else printf("ok %d - fcntl(F_DUP2FD) didn't clear close-on-exec\n", test); ++test; if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) err(1, "getrlimit"); if ((fd2 = fcntl(fd1, F_DUP2FD, rlp.rlim_cur + 1)) >= 0) printf("not ok %d - fcntl(F_DUP2FD) bypassed NOFILE limit\n", test); else printf("ok %d - fcntl(F_DUP2FD) didn't bypass NOFILE limit\n", test); /* Does fcntl(F_DUPFD_CLOEXEC) work? */ if ((fd2 = fcntl(fd1, F_DUPFD_CLOEXEC, 10)) < 0) err(1, "fcntl(F_DUPFD_CLOEXEC)"); if (fd2 < 10) printf("not ok %d - fcntl(F_DUPFD_CLOEXEC) returned wrong fd %d\n", ++test, fd2); else printf("ok %d - fcntl(F_DUPFD_CLOEXEC) works\n", ++test); /* Was close-on-exec cleared? */ ++test; if (fcntl(fd2, F_GETFD) != 1) printf( "not ok %d - fcntl(F_DUPFD_CLOEXEC) didn't set close-on-exec\n", test); else printf("ok %d - fcntl(F_DUPFD_CLOEXEC) set close-on-exec\n", test); /* If fcntl(F_DUP2FD_CLOEXEC) ever work? */ if ((fd2 = fcntl(fd1, F_DUP2FD_CLOEXEC, fd1 + 1)) < 0) err(1, "fcntl(F_DUP2FD_CLOEXEC)"); printf("ok %d - fcntl(F_DUP2FD_CLOEXEC) works\n", ++test); /* Do we get the right fd? */ ++test; if (fd2 != fd1 + 1) printf( "no ok %d - fcntl(F_DUP2FD_CLOEXEC) didn't give us the right fd\n", test); else printf("ok %d - fcntl(F_DUP2FD_CLOEXEC) returned a correct fd\n", test); /* Was close-on-exec set? */ ++test; if (fcntl(fd2, F_GETFD) != FD_CLOEXEC) printf( "not ok %d - fcntl(F_DUP2FD_CLOEXEC) didn't set close-on-exec\n", test); else printf("ok %d - fcntl(F_DUP2FD_CLOEXEC) set close-on-exec\n", test); /* * It is unclear what F_DUP2FD_CLOEXEC should do when duplicating a * file descriptor onto itself. */ ++test; if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) err(1, "getrlimit"); if ((fd2 = fcntl(fd1, F_DUP2FD_CLOEXEC, rlp.rlim_cur + 1)) >= 0) printf("not ok %d - fcntl(F_DUP2FD_CLOEXEC) bypassed NOFILE limit\n", test); else printf("ok %d - fcntl(F_DUP2FD_CLOEXEC) didn't bypass NOFILE limit\n", test); /* Does dup3(O_CLOEXEC) ever work? */ if ((fd2 = dup3(fd1, fd1 + 1, O_CLOEXEC)) < 0) err(1, "dup3(O_CLOEXEC)"); printf("ok %d - dup3(O_CLOEXEC) works\n", ++test); /* Do we get the right fd? */ ++test; if (fd2 != fd1 + 1) printf( "no ok %d - dup3(O_CLOEXEC) didn't give us the right fd\n", test); else printf("ok %d - dup3(O_CLOEXEC) returned a correct fd\n", test); /* Was close-on-exec set? */ ++test; if (fcntl(fd2, F_GETFD) != FD_CLOEXEC) printf( "not ok %d - dup3(O_CLOEXEC) didn't set close-on-exec\n", test); else printf("ok %d - dup3(O_CLOEXEC) set close-on-exec\n", test); /* Does dup3(0) ever work? */ if ((fd2 = dup3(fd1, fd1 + 1, 0)) < 0) err(1, "dup3(0)"); printf("ok %d - dup3(0) works\n", ++test); /* Do we get the right fd? */ ++test; if (fd2 != fd1 + 1) printf( "no ok %d - dup3(0) didn't give us the right fd\n", test); else printf("ok %d - dup3(0) returned a correct fd\n", test); /* Was close-on-exec cleared? */ ++test; if (fcntl(fd2, F_GETFD) != 0) printf( "not ok %d - dup3(0) didn't clear close-on-exec\n", test); else printf("ok %d - dup3(0) cleared close-on-exec\n", test); /* dup3() does not allow duplicating to the same fd */ ++test; if (dup3(fd1, fd1, O_CLOEXEC) != -1) printf( "not ok %d - dup3(fd1, fd1, O_CLOEXEC) succeeded\n", test); else printf("ok %d - dup3(fd1, fd1, O_CLOEXEC) failed\n", test); ++test; if (dup3(fd1, fd1, 0) != -1) printf( "not ok %d - dup3(fd1, fd1, 0) succeeded\n", test); else printf("ok %d - dup3(fd1, fd1, 0) failed\n", test); ++test; if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) err(1, "getrlimit"); if ((fd2 = dup3(fd1, rlp.rlim_cur + 1, O_CLOEXEC)) >= 0) printf("not ok %d - dup3(O_CLOEXEC) bypassed NOFILE limit\n", test); else printf("ok %d - dup3(O_CLOEXEC) didn't bypass NOFILE limit\n", test); return (0); }
int VanillaProc::setupOOMEvent(const std::string &cgroup_string) { #if !(defined(HAVE_EVENTFD) && defined(HAVE_EXT_LIBCGROUP)) // Shut the compiler up. cgroup_string.size(); return 0; #else // Initialize the event descriptor m_oom_efd = eventfd(0, EFD_CLOEXEC); if (m_oom_efd == -1) { dprintf(D_ALWAYS, "Unable to create new event FD for starter: %u %s\n", errno, strerror(errno)); return 1; } // Find the memcg location on disk void * handle = NULL; struct cgroup_mount_point mount_info; int ret = cgroup_get_controller_begin(&handle, &mount_info); std::stringstream oom_control; std::stringstream event_control; bool found_memcg = false; while (ret == 0) { if (strcmp(mount_info.name, MEMORY_CONTROLLER_STR) == 0) { found_memcg = true; oom_control << mount_info.path << "/"; event_control << mount_info.path << "/"; break; } cgroup_get_controller_next(&handle, &mount_info); } if (!found_memcg && (ret != ECGEOF)) { dprintf(D_ALWAYS, "Error while locating memcg controller for starter: %u %s\n", ret, cgroup_strerror(ret)); return 1; } cgroup_get_controller_end(&handle); if (found_memcg == false) { dprintf(D_ALWAYS, "Memcg is not available; OOM notification disabled for starter.\n"); return 1; } // Finish constructing the location of the control files oom_control << cgroup_string << "/memory.oom_control"; std::string oom_control_str = oom_control.str(); event_control << cgroup_string << "/cgroup.event_control"; std::string event_control_str = event_control.str(); // Open the oom_control and event control files TemporaryPrivSentry sentry(PRIV_ROOT); m_oom_fd = open(oom_control_str.c_str(), O_RDONLY | O_CLOEXEC); if (m_oom_fd == -1) { dprintf(D_ALWAYS, "Unable to open the OOM control file for starter: %u %s\n", errno, strerror(errno)); return 1; } int event_ctrl_fd = open(event_control_str.c_str(), O_WRONLY | O_CLOEXEC); if (event_ctrl_fd == -1) { dprintf(D_ALWAYS, "Unable to open event control for starter: %u %s\n", errno, strerror(errno)); return 1; } // Inform Linux we will be handling the OOM events for this container. int oom_fd2 = open(oom_control_str.c_str(), O_WRONLY | O_CLOEXEC); if (oom_fd2 == -1) { dprintf(D_ALWAYS, "Unable to open the OOM control file for writing for starter: %u %s\n", errno, strerror(errno)); close(event_ctrl_fd); return 1; } const char limits [] = "1"; ssize_t nwritten = full_write(oom_fd2, &limits, 1); if (nwritten < 0) { dprintf(D_ALWAYS, "Unable to set OOM control to %s for starter: %u %s\n", limits, errno, strerror(errno)); close(event_ctrl_fd); close(oom_fd2); return 1; } close(oom_fd2); // Create the subscription string: std::stringstream sub_ss; sub_ss << m_oom_efd << " " << m_oom_fd; std::string sub_str = sub_ss.str(); if ((nwritten = full_write(event_ctrl_fd, sub_str.c_str(), sub_str.size())) < 0) { dprintf(D_ALWAYS, "Unable to write into event control file for starter: %u %s\n", errno, strerror(errno)); close(event_ctrl_fd); return 1; } close(event_ctrl_fd); // Fool DC into talking to the eventfd int pipes[2]; pipes[0] = -1; pipes[1] = -1; int fd_to_replace = -1; if (daemonCore->Create_Pipe(pipes, true) == -1 || pipes[0] == -1) { dprintf(D_ALWAYS, "Unable to create a DC pipe\n"); close(m_oom_efd); m_oom_efd = -1; close(m_oom_fd); m_oom_fd = -1; return 1; } if ( daemonCore->Get_Pipe_FD(pipes[0], &fd_to_replace) == -1 || fd_to_replace == -1) { dprintf(D_ALWAYS, "Unable to lookup pipe's FD\n"); close(m_oom_efd); m_oom_efd = -1; close(m_oom_fd); m_oom_fd = -1; daemonCore->Close_Pipe(pipes[0]); daemonCore->Close_Pipe(pipes[1]); return 1; } dup3(m_oom_efd, fd_to_replace, O_CLOEXEC); close(m_oom_efd); m_oom_efd = pipes[0]; // Inform DC we want to recieve notifications from this FD. daemonCore->Register_Pipe(pipes[0],"OOM event fd", static_cast<PipeHandlercpp>(&VanillaProc::outOfMemoryEvent),"OOM Event Handler",this,HANDLE_READ); return 0; #endif }