diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2021-04-09 07:19:03 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2021-04-09 07:19:03 -0300 |
commit | 0e752a6e215aee21dc73da097c3225495d54a5b6 (patch) | |
tree | b81be02cbf2f06aebf322ac4a5d014b44176bba5 /libotr/libgpg-error-1.42/src/spawn-posix.c | |
parent | 7754076c715285173311a1b6811ce377950e18a6 (diff) |
Add libotr/etc sources
Diffstat (limited to 'libotr/libgpg-error-1.42/src/spawn-posix.c')
-rw-r--r-- | libotr/libgpg-error-1.42/src/spawn-posix.c | 886 |
1 files changed, 886 insertions, 0 deletions
diff --git a/libotr/libgpg-error-1.42/src/spawn-posix.c b/libotr/libgpg-error-1.42/src/spawn-posix.c new file mode 100644 index 0000000..ac4c4ce --- /dev/null +++ b/libotr/libgpg-error-1.42/src/spawn-posix.c @@ -0,0 +1,886 @@ +/* exechelp.c - Fork and exec helpers for POSIX + * Copyright (C) 2004, 2007-2009, 2010 Free Software Foundation, Inc. + * Copyright (C) 2004, 2006-2012, 2014-2017 g10 Code GmbH + * + * This file is part of Libgpg-error. + * + * Libgpg-error is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgpg-error is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ + * + * This file was originally a part of GnuPG. + */ + +#include <config.h> + +#if defined(HAVE_W32_SYSTEM) || defined (HAVE_W32CE_SYSTEM) +#error This code is only used on POSIX +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif +#include <unistd.h> +#include <fcntl.h> + +#include <sys/wait.h> + +#ifdef HAVE_GETRLIMIT +#include <sys/time.h> +#include <sys/resource.h> +#endif /*HAVE_GETRLIMIT*/ + +#ifdef HAVE_STAT +# include <sys/stat.h> +#endif + +#if __linux__ +# include <sys/types.h> +# include <dirent.h> +#endif /*__linux__ */ + +#include "gpgrt-int.h" + + +static void +out_of_core (int line) +{ + _gpgrt_log_fatal ("malloc failed at line %d: %s\n", + line, _gpg_strerror (_gpg_err_code_from_syserror ())); + /*NOTREACHED*/ +} + + +/* Return the maximum number of currently allowed open file + * descriptors. Only useful on POSIX systems but returns a value on + * other systems too. */ +static int +get_max_fds (void) +{ + int max_fds = -1; +#ifdef HAVE_GETRLIMIT + struct rlimit rl; + + /* Under Linux we can figure out the highest used file descriptor by + * reading /proc/PID/fd. This is in the common cases much fast than + * for example doing 4096 close calls where almost all of them will + * fail. On a system with a limit of 4096 files and only 8 files + * open with the highest number being 10, we speedup close_all_fds + * from 125ms to 0.4ms including readdir. + * + * Another option would be to close the file descriptors as returned + * from reading that directory - however then we need to snapshot + * that list before starting to close them. */ +#ifdef __linux__ + { + DIR *dir = NULL; + struct dirent *dir_entry; + const char *s; + int x; + + /* FIXME: Check gpgme on how to do this right on Linux. */ + dir = opendir ("/proc/self/fd"); + if (dir) + { + while ((dir_entry = readdir (dir))) + { + s = dir_entry->d_name; + if ( *s < '0' || *s > '9') + continue; + x = atoi (s); + if (x > max_fds) + max_fds = x; + } + closedir (dir); + } + if (max_fds != -1) + return max_fds + 1; + } +#endif /* __linux__ */ + + +# ifdef RLIMIT_NOFILE + if (!getrlimit (RLIMIT_NOFILE, &rl)) + max_fds = rl.rlim_max; +# endif + +# ifdef RLIMIT_OFILE + if (max_fds == -1 && !getrlimit (RLIMIT_OFILE, &rl)) + max_fds = rl.rlim_max; + +# endif +#endif /*HAVE_GETRLIMIT*/ + +#ifdef _SC_OPEN_MAX + if (max_fds == -1) + { + long int scres = sysconf (_SC_OPEN_MAX); + if (scres >= 0) + max_fds = scres; + } +#endif + +#ifdef _POSIX_OPEN_MAX + if (max_fds == -1) + max_fds = _POSIX_OPEN_MAX; +#endif + +#ifdef OPEN_MAX + if (max_fds == -1) + max_fds = OPEN_MAX; +#endif + + if (max_fds == -1) + max_fds = 256; /* Arbitrary limit. */ + + /* AIX returns INT32_MAX instead of a proper value. We assume that + this is always an error and use an arbitrary limit. */ +#ifdef INT32_MAX + if (max_fds == INT32_MAX) + max_fds = 256; +#endif + + return max_fds; +} + + +/* Close all file descriptors starting with descriptor FIRST. If + * EXCEPT is not NULL, it is expected to be a list of file descriptors + * which shall not be closed. This list shall be sorted in ascending + * order with the end marked by -1. */ +static void +close_all_fds (int first, int *except) +{ + int max_fd = get_max_fds (); + int fd, i, except_start; + + if (except) + { + except_start = 0; + for (fd=first; fd < max_fd; fd++) + { + for (i=except_start; except[i] != -1; i++) + { + if (except[i] == fd) + { + /* If we found the descriptor in the exception list + we can start the next compare run at the next + index because the exception list is ordered. */ + except_start = i + 1; + break; + } + } + if (except[i] == -1) + close (fd); + } + } + else + { + for (fd=first; fd < max_fd; fd++) + close (fd); + } + + _gpg_err_set_errno (0); +} + + +/* Returns an array with all currently open file descriptors. The end + * of the array is marked by -1. The caller needs to release this + * array using the *standard free* and not with xfree. This allow the + * use of this function right at startup even before libgcrypt has + * been initialized. Returns NULL on error and sets ERRNO + * accordingly. + * + * FIXME: Needs to be adjusted for use here. + */ +#if 0 +int * +get_all_open_fds (void) +{ + int *array; + size_t narray; + int fd, max_fd, idx; +#ifndef HAVE_STAT + array = calloc (1, sizeof *array); + if (array) + array[0] = -1; +#else /*HAVE_STAT*/ + struct stat statbuf; + + max_fd = get_max_fds (); + narray = 32; /* If you change this change also t-exechelp.c. */ + array = calloc (narray, sizeof *array); + if (!array) + return NULL; + + /* Note: The list we return is ordered. */ + for (idx=0, fd=0; fd < max_fd; fd++) + if (!(fstat (fd, &statbuf) == -1 && errno == EBADF)) + { + if (idx+1 >= narray) + { + int *tmp; + + narray += (narray < 256)? 32:256; + tmp = realloc (array, narray * sizeof *array); + if (!tmp) + { + free (array); + return NULL; + } + array = tmp; + } + array[idx++] = fd; + } + array[idx] = -1; +#endif /*HAVE_STAT*/ + return array; +} +#endif /*0*/ + + +/* The exec core used right after the fork. This will never return. */ +static void +do_exec (const char *pgmname, const char *argv[], + int fd_in, int fd_out, int fd_err, + int *except, void (*preexec)(void) ) +{ + char **arg_list; + int i, j; + int fds[3]; + + fds[0] = fd_in; + fds[1] = fd_out; + fds[2] = fd_err; + + /* Create the command line argument array. */ + i = 0; + if (argv) + while (argv[i]) + i++; + arg_list = xtrycalloc (i+2, sizeof *arg_list); + if (!arg_list) + out_of_core (__LINE__); + arg_list[0] = strrchr (pgmname, '/'); + if (arg_list[0]) + arg_list[0]++; + else + { + arg_list[0] = xtrystrdup (pgmname); + if (!arg_list[0]) + out_of_core (__LINE__); + } + if (argv) + for (i=0,j=1; argv[i]; i++, j++) + arg_list[j] = (char*)argv[i]; + + /* Assign /dev/null to unused FDs. */ + for (i=0; i <= 2; i++) + { + if (fds[i] == -1 ) + { + fds[i] = open ("/dev/null", i? O_WRONLY : O_RDONLY); + if (fds[i] == -1) + _gpgrt_log_fatal ("failed to open '%s': %s\n", + "/dev/null", strerror (errno)); + } + } + + /* Connect the standard files. */ + for (i=0; i <= 2; i++) + { + if (fds[i] != i && dup2 (fds[i], i) == -1) + _gpgrt_log_fatal ("dup2 std%s failed: %s\n", + i==0?"in":i==1?"out":"err", strerror (errno)); + } + + /* Close all other files. */ + close_all_fds (3, except); + + if (preexec) + preexec (); + execv (pgmname, arg_list); + /* No way to print anything, as we have may have closed all streams. */ + _exit (127); +} + + +/* Helper for _gpgrt_make_pipe. */ +static gpg_err_code_t +do_create_pipe (int filedes[2]) +{ + gpg_error_t err = 0; + + _gpgrt_pre_syscall (); + if (pipe (filedes) == -1) + { + err = _gpg_err_code_from_syserror (); + filedes[0] = filedes[1] = -1; + } + _gpgrt_post_syscall (); + + return err; +} + + +/* Helper for _gpgrt_make_pipe. */ +static gpg_err_code_t +do_create_pipe_and_estream (int filedes[2], estream_t *r_fp, + int outbound, int nonblock) +{ + gpg_err_code_t err; + + _gpgrt_pre_syscall (); + if (pipe (filedes) == -1) + { + err = _gpg_err_code_from_syserror (); + _gpgrt_log_error (_("error creating a pipe: %s\n"), _gpg_strerror (err)); + filedes[0] = filedes[1] = -1; + *r_fp = NULL; + return err; + } + _gpgrt_post_syscall (); + + if (!outbound) + *r_fp = _gpgrt_fdopen (filedes[0], nonblock? "r,nonblock" : "r"); + else + *r_fp = _gpgrt_fdopen (filedes[1], nonblock? "w,nonblock" : "w"); + if (!*r_fp) + { + err = _gpg_err_code_from_syserror (); + _gpgrt_log_error (_("error creating a stream for a pipe: %s\n"), + _gpg_strerror (err)); + close (filedes[0]); + close (filedes[1]); + filedes[0] = filedes[1] = -1; + return err; + } + return 0; +} + + +/* Create a pipe. The DIRECTION parameter gives the type of the created pipe: + * DIRECTION < 0 := Inbound pipe: On Windows the write end is inheritable. + * DIRECTION > 0 := Outbound pipe: On Windows the read end is inheritable. + * If R_FP is NULL a standard pipe and no stream is created, DIRECTION + * should then be 0. */ +gpg_err_code_t +_gpgrt_make_pipe (int filedes[2], estream_t *r_fp, int direction, + int nonblock) +{ + if (r_fp && direction) + return do_create_pipe_and_estream (filedes, r_fp, + (direction > 0), nonblock); + else + return do_create_pipe (filedes); +} + + +/* Fork and exec the PGMNAME, see gpgrt-int.h for details. */ +gpg_err_code_t +_gpgrt_spawn_process (const char *pgmname, const char *argv[], + int *except, void (*preexec)(void), unsigned int flags, + estream_t *r_infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid) +{ + gpg_error_t err; + int inpipe[2] = {-1, -1}; + int outpipe[2] = {-1, -1}; + int errpipe[2] = {-1, -1}; + estream_t infp = NULL; + estream_t outfp = NULL; + estream_t errfp = NULL; + int nonblock = !!(flags & GPGRT_SPAWN_NONBLOCK); + + if (r_infp) + *r_infp = NULL; + if (r_outfp) + *r_outfp = NULL; + if (r_errfp) + *r_errfp = NULL; + *pid = (pid_t)(-1); /* Always required. */ + + if (r_infp) + { + err = _gpgrt_create_outbound_pipe (inpipe, &infp, nonblock); + if (err) + return err; + } + + if (r_outfp) + { + err = _gpgrt_create_inbound_pipe (outpipe, &outfp, nonblock); + if (err) + { + if (infp) + _gpgrt_fclose (infp); + else if (inpipe[1] != -1) + close (inpipe[1]); + if (inpipe[0] != -1) + close (inpipe[0]); + + return err; + } + } + + if (r_errfp) + { + err = _gpgrt_create_inbound_pipe (errpipe, &errfp, nonblock); + if (err) + { + if (infp) + _gpgrt_fclose (infp); + else if (inpipe[1] != -1) + close (inpipe[1]); + if (inpipe[0] != -1) + close (inpipe[0]); + + if (outfp) + _gpgrt_fclose (outfp); + else if (outpipe[0] != -1) + close (outpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + + return err; + } + } + + _gpgrt_pre_syscall (); + *pid = fork (); + _gpgrt_post_syscall (); + if (*pid == (pid_t)(-1)) + { + err = _gpg_err_code_from_syserror (); + _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (err)); + + if (infp) + _gpgrt_fclose (infp); + else if (inpipe[1] != -1) + close (inpipe[1]); + if (inpipe[0] != -1) + close (inpipe[0]); + + if (outfp) + _gpgrt_fclose (outfp); + else if (outpipe[0] != -1) + close (outpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + + if (errfp) + _gpgrt_fclose (errfp); + else if (errpipe[0] != -1) + close (errpipe[0]); + if (errpipe[1] != -1) + close (errpipe[1]); + return err; + } + + if (!*pid) + { + /* This is the child. */ + /* FIXME: Needs to be done by preexec: + gcry_control (GCRYCTL_TERM_SECMEM); */ + _gpgrt_fclose (infp); + _gpgrt_fclose (outfp); + _gpgrt_fclose (errfp); + do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1], + except, preexec); + /*NOTREACHED*/ + } + + /* This is the parent. */ + if (inpipe[0] != -1) + close (inpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + if (errpipe[1] != -1) + close (errpipe[1]); + + if (r_infp) + *r_infp = infp; + if (r_outfp) + *r_outfp = outfp; + if (r_errfp) + *r_errfp = errfp; + + return 0; +} + + +/* Fork and exec the PGMNAME using FDs, see gpgrt-int.h for details. */ +gpg_err_code_t +_gpgrt_spawn_process_fd (const char *pgmname, const char *argv[], + int infd, int outfd, int errfd, pid_t *pid) +{ + gpg_error_t err; + + _gpgrt_pre_syscall (); + *pid = fork (); + _gpgrt_post_syscall (); + if (*pid == (pid_t)(-1)) + { + err = _gpg_err_code_from_syserror (); + _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (err)); + return err; + } + + if (!*pid) + { + /* FIXME: We need to add a preexec so that a + gcry_control (GCRYCTL_TERM_SECMEM); + can be done. */ + /* Run child. */ + do_exec (pgmname, argv, infd, outfd, errfd, NULL, NULL); + /*NOTREACHED*/ + } + + return 0; +} + + +/* Waiting for child processes. + * + * waitpid(2) may return information about terminated children that we + * did not yet request, and there is no portable way to wait for a + * specific set of children. + * + * As a workaround, we store the results of children for later use. + * + * XXX: This assumes that PIDs are not reused too quickly. + * FIXME: This is not thread-safe. + */ + +struct terminated_child +{ + pid_t pid; + int exitcode; + struct terminated_child *next; +}; + +static struct terminated_child *terminated_children; + + +static gpg_err_code_t +store_result (pid_t pid, int exitcode) +{ + struct terminated_child *c; + + c = xtrymalloc (sizeof *c); + if (c == NULL) + return _gpg_err_code_from_syserror (); + + c->pid = pid; + c->exitcode = exitcode; + c->next = terminated_children; + terminated_children = c; + + return 0; +} + + +static int +get_result (pid_t pid, int *r_exitcode) +{ + struct terminated_child *c, **prevp; + + for (prevp = &terminated_children, c = terminated_children; + c; + prevp = &c->next, c = c->next) + if (c->pid == pid) + { + *prevp = c->next; + *r_exitcode = c->exitcode; + xfree (c); + return 1; + } + + return 0; +} + + +/* See gpgrt-int.h for a description. */ +gpg_err_code_t +_gpgrt_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode) +{ + gpg_err_code_t ec; + int i, status; + + if (r_exitcode) + *r_exitcode = -1; + + if (pid == (pid_t)(-1)) + return GPG_ERR_INV_VALUE; + + _gpgrt_pre_syscall (); + while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1) + && errno == EINTR); + _gpgrt_post_syscall (); + + if (i == (pid_t)(-1)) + { + ec = _gpg_err_code_from_syserror (); + _gpgrt_log_error (_("waiting for process %d to terminate failed: %s\n"), + (int)pid, _gpg_strerror (ec)); + } + else if (!i) + { + ec = GPG_ERR_TIMEOUT; /* Still running. */ + } + else if (WIFEXITED (status) && WEXITSTATUS (status) == 127) + { + /* FIXME: This is GnuPG specific. */ + _gpgrt_log_error (_("error running '%s': probably not installed\n"), + pgmname); + ec = GPG_ERR_CONFIGURATION; + } + else if (WIFEXITED (status) && WEXITSTATUS (status)) + { + if (!r_exitcode) + _gpgrt_log_error (_("error running '%s': exit status %d\n"), pgmname, + WEXITSTATUS (status)); + else + *r_exitcode = WEXITSTATUS (status); + ec = GPG_ERR_GENERAL; + } + else if (!WIFEXITED (status)) + { + _gpgrt_log_error (_("error running '%s': terminated\n"), pgmname); + ec = GPG_ERR_GENERAL; + } + else + { + if (r_exitcode) + *r_exitcode = 0; + ec = 0; + } + + return ec; +} + + +/* See gpgrt-int.h for a description. + * + * FIXME: What about using a poll like data structure for the pids and + * their exit codes? The whole thing is anyway problematic in a + * threaded processs because waitpid has no association between PIDS + * and threads. + */ +gpg_err_code_t +_gpgrt_wait_processes (const char **pgmnames, pid_t *pids, size_t count, + int hang, int *r_exitcodes) +{ + gpg_err_code_t ec = 0; + size_t i, left; + int *dummy = NULL; + + if (!r_exitcodes) + { + dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count); + if (!dummy) + return _gpg_err_code_from_syserror (); + } + + for (i = 0, left = count; i < count; i++) + { + int status = -1; + + if (pids[i] == (pid_t)(-1)) + return GPG_ERR_INV_VALUE; + + /* See if there was a previously stored result for this pid. */ + if (get_result (pids[i], &status)) + left -= 1; + + r_exitcodes[i] = status; + } + + while (left > 0) + { + pid_t pid; + int status; + + _gpgrt_pre_syscall (); + while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1) + && errno == EINTR); + _gpgrt_post_syscall (); + + if (pid == (pid_t)(-1)) + { + ec = _gpg_err_code_from_syserror (); + _gpgrt_log_error (_("waiting for processes to terminate" + " failed: %s\n"), _gpg_strerror (ec)); + break; + } + else if (!pid) + { + ec = GPG_ERR_TIMEOUT; /* Still running. */ + break; + } + else + { + for (i = 0; i < count; i++) + if (pid == pids[i]) + break; + + if (i == count) + { + /* No match, store this result. */ + ec = store_result (pid, status); + if (ec) + break; + continue; + } + + /* Process PIDS[i] died. */ + if (r_exitcodes[i] != (pid_t) -1) + { + _gpgrt_log_error ("PID %d was reused", (int)pid); + ec = GPG_ERR_GENERAL; + break; + } + + left -= 1; + r_exitcodes[i] = status; + } + } + + for (i = 0; i < count; i++) + { + if (r_exitcodes[i] == -1) + continue; + + if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127) + { + _gpgrt_log_error (_("error running '%s': probably not installed\n"), + pgmnames[i]); + ec = GPG_ERR_CONFIGURATION; + } + else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i])) + { + if (dummy) + _gpgrt_log_error (_("error running '%s': exit status %d\n"), + pgmnames[i], WEXITSTATUS (r_exitcodes[i])); + else + r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]); + ec = GPG_ERR_GENERAL; + } + else if (!WIFEXITED (r_exitcodes[i])) + { + _gpgrt_log_error (_("error running '%s': terminated\n"), pgmnames[i]); + ec = GPG_ERR_GENERAL; + } + } + + xfree (dummy); + return ec; +} + + +/* See gpgrt-int.h for a description. FIXME: We should add a prexec + * callback. */ +gpg_err_code_t +_gpgrt_spawn_process_detached (const char *pgmname, const char *argv[], + const char *envp[] ) +{ + gpg_err_code_t ec; + pid_t pid; + int i; + + /* FIXME: Is this GnuPG specific or should we keep it. */ + if (getuid() != geteuid()) + return GPG_ERR_BUG; + + if (access (pgmname, X_OK)) + return _gpg_err_code_from_syserror (); + + _gpgrt_pre_syscall (); + pid = fork (); + _gpgrt_post_syscall (); + if (pid == (pid_t)(-1)) + { + ec = _gpg_err_code_from_syserror (); + _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (ec)); + return ec; + } + + if (!pid) + { + pid_t pid2; + + /* gcry_control (GCRYCTL_TERM_SECMEM); */ + if (setsid() == -1 || chdir ("/")) + _exit (1); + + pid2 = fork (); /* Double fork to let init take over the new child. */ + if (pid2 == (pid_t)(-1)) + _exit (1); + if (pid2) + _exit (0); /* Let the parent exit immediately. */ + + for (i=0; envp && envp[i]; i++) + { + char *p = xtrystrdup (envp[i]); + if (!p) + out_of_core (__LINE__); + putenv (p); + } + + do_exec (pgmname, argv, -1, -1, -1, NULL, NULL); + /*NOTREACHED*/ + } + + _gpgrt_pre_syscall (); + if (waitpid (pid, NULL, 0) == -1) + { + _gpgrt_post_syscall (); + ec = _gpg_err_code_from_syserror (); + _gpgrt_log_error ("waitpid failed in gpgrt_spawn_process_detached: %s", + _gpg_strerror (ec)); + return ec; + } + else + _gpgrt_post_syscall (); + + return 0; +} + + +/* Kill a process; that is send an appropriate signal to the process. + * gnupg_wait_process must be called to actually remove the process + from the system. An invalid PID is ignored. */ +void +_gpgrt_kill_process (pid_t pid) +{ + if (pid != (pid_t)(-1)) + { + _gpgrt_pre_syscall (); + kill (pid, SIGTERM); + _gpgrt_post_syscall (); + } +} + + +void +_gpgrt_release_process (pid_t pid) +{ + (void)pid; +} |