|
@@ -2883,17 +2883,34 @@ load_windows_system_library(const TCHAR *library_name)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-/** Format child_state and saved_errno as a hex string placed in hex_errno.
|
|
|
- * Called between fork and _exit, so must be signal-handler safe */
|
|
|
+/** Format <b>child_state</b> and <b>saved_errno</b> as a hex string placed in
|
|
|
+ * <b>hex_errno</b>. Called between fork and _exit, so must be signal-handler
|
|
|
+ * safe.
|
|
|
+ *
|
|
|
+ * <b>hex_errno</b> must have at least HEX_ERRNO_SIZE bytes available.
|
|
|
+ *
|
|
|
+ * The format of <b>hex_errno</b> is: "CHILD_STATE/ERRNO\n", left-padded
|
|
|
+ * with spaces. Note that there is no trailing \0. CHILD_STATE indicates where
|
|
|
+ * in the processs of starting the child process did the failure occur (see
|
|
|
+ * CHILD_STATE_* macros for definition), and SAVED_ERRNO is the value of
|
|
|
+ * errno when the failure occurred.
|
|
|
+ */
|
|
|
+
|
|
|
void
|
|
|
format_helper_exit_status(unsigned char child_state, int saved_errno,
|
|
|
char *hex_errno)
|
|
|
{
|
|
|
- /* Convert errno to be unsigned for hex conversion */
|
|
|
unsigned int unsigned_errno;
|
|
|
char *cur;
|
|
|
+ size_t i;
|
|
|
|
|
|
- /* If errno is negative, negate it */
|
|
|
+ /* Fill hex_errno with spaces, and a trailing newline (memset may
|
|
|
+ not be signal handler safe, so we can't use it) */
|
|
|
+ for (i = 0; i < (HEX_ERRNO_SIZE - 1); i++)
|
|
|
+ hex_errno[i] = ' ';
|
|
|
+ hex_errno[HEX_ERRNO_SIZE - 1] = '\n';
|
|
|
+
|
|
|
+ /* Convert errno to be unsigned for hex conversion */
|
|
|
if (saved_errno < 0) {
|
|
|
unsigned_errno = (unsigned int) -saved_errno;
|
|
|
} else {
|
|
@@ -2903,17 +2920,26 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
|
|
|
/* Convert errno to hex (start before \n) */
|
|
|
cur = hex_errno + HEX_ERRNO_SIZE - 2;
|
|
|
|
|
|
+ /* Check for overflow on first iteration of the loop */
|
|
|
+ if (cur < hex_errno)
|
|
|
+ return;
|
|
|
+
|
|
|
do {
|
|
|
*cur-- = "0123456789ABCDEF"[unsigned_errno % 16];
|
|
|
unsigned_errno /= 16;
|
|
|
} while (unsigned_errno != 0 && cur >= hex_errno);
|
|
|
|
|
|
- /* Add on the minus side if errno was negative */
|
|
|
- if (saved_errno < 0)
|
|
|
+ /* Prepend the minus sign if errno was negative */
|
|
|
+ if (saved_errno < 0 && cur >= hex_errno)
|
|
|
*cur-- = '-';
|
|
|
|
|
|
/* Leave a gap */
|
|
|
- *cur-- = '/';
|
|
|
+ if (cur >= hex_errno)
|
|
|
+ *cur-- = '/';
|
|
|
+
|
|
|
+ /* Check for overflow on first iteration of the loop */
|
|
|
+ if (cur < hex_errno)
|
|
|
+ return;
|
|
|
|
|
|
/* Convert child_state to hex */
|
|
|
do {
|
|
@@ -2938,13 +2964,20 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
|
|
|
|
|
|
#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
|
|
|
|
|
|
-/** Start a program in the background. If <b>filename</b> contains a '/', then
|
|
|
- * it will be treated as an absolute or relative path. Otherwise the system
|
|
|
- * path will be searched for <b>filename</b>. Returns pid on success, otherwise
|
|
|
- * returns -1.
|
|
|
- * Some parts of this code are based on the POSIX subprocess module from Python
|
|
|
+/** Start a program in the background. If <b>filename</b> contains a '/',
|
|
|
+ * then it will be treated as an absolute or relative path. Otherwise the
|
|
|
+ * system path will be searched for <b>filename</b>. The strings in
|
|
|
+ * <b>argv</b> will be passed as the command line arguments of the child
|
|
|
+ * program (following convention, argv[0] should normally be the filename of
|
|
|
+ * the executable). The last element of argv must be NULL. If the child
|
|
|
+ * program is launched, the PID will be returned and <b>stdout_read</b> and
|
|
|
+ * <b>stdout_err</b> will be set to file descriptors from which the stdout
|
|
|
+ * and stderr, respectively, output of the child program can be read, and the
|
|
|
+ * stdin of the child process shall be set to /dev/null. Otherwise returns
|
|
|
+ * -1. Some parts of this code are based on the POSIX subprocess module from
|
|
|
+ * Python.
|
|
|
*/
|
|
|
-static int
|
|
|
+int
|
|
|
tor_spawn_background(const char *const filename, int *stdout_read,
|
|
|
int *stderr_read, const char **argv)
|
|
|
{
|
|
@@ -2974,16 +3007,12 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|
|
and we are not allowed to use unsafe functions between fork and exec */
|
|
|
error_message_length = strlen(error_message);
|
|
|
|
|
|
- /* Fill hex_errno with spaces, and a trailing newline */
|
|
|
- memset(hex_errno, ' ', sizeof(hex_errno) - 1);
|
|
|
- hex_errno[sizeof(hex_errno) - 1] = '\n';
|
|
|
-
|
|
|
child_state = CHILD_STATE_PIPE;
|
|
|
|
|
|
/* Set up pipe for redirecting stdout and stderr of child */
|
|
|
retval = pipe(stdout_pipe);
|
|
|
if (-1 == retval) {
|
|
|
- log_err(LD_GENERAL,
|
|
|
+ log_warn(LD_GENERAL,
|
|
|
"Failed to set up pipe for stdout communication with child process: %s",
|
|
|
strerror(errno));
|
|
|
return -1;
|
|
@@ -2991,7 +3020,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|
|
|
|
|
retval = pipe(stderr_pipe);
|
|
|
if (-1 == retval) {
|
|
|
- log_err(LD_GENERAL,
|
|
|
+ log_warn(LD_GENERAL,
|
|
|
"Failed to set up pipe for stderr communication with child process: %s",
|
|
|
strerror(errno));
|
|
|
return -1;
|
|
@@ -3043,7 +3072,8 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|
|
child_state = CHILD_STATE_CLOSEFD;
|
|
|
|
|
|
/* Close all other fds, including the read end of the pipe */
|
|
|
- /* TODO: use closefrom if available */
|
|
|
+ /* XXX: use closefrom if available, or better still set FD_CLOEXEC
|
|
|
+ on all of Tor's open files */
|
|
|
for (fd = STDERR_FILENO + 1; fd < max_fd; fd++)
|
|
|
close(fd);
|
|
|
|
|
@@ -3059,7 +3089,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|
|
child_state = CHILD_STATE_FAILEXEC;
|
|
|
|
|
|
error:
|
|
|
- /* TODO: are we leaking fds from the pipe? */
|
|
|
+ /* XXX: are we leaking fds from the pipe? */
|
|
|
|
|
|
format_helper_exit_status(child_state, errno, hex_errno);
|
|
|
|
|
@@ -3075,7 +3105,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|
|
/* In parent */
|
|
|
|
|
|
if (-1 == pid) {
|
|
|
- log_err(LD_GENERAL, "Failed to fork child process: %s", strerror(errno));
|
|
|
+ log_warn(LD_GENERAL, "Failed to fork child process: %s", strerror(errno));
|
|
|
close(stdout_pipe[0]);
|
|
|
close(stdout_pipe[1]);
|
|
|
close(stderr_pipe[0]);
|
|
@@ -3088,7 +3118,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|
|
retval = close(stdout_pipe[1]);
|
|
|
|
|
|
if (-1 == retval) {
|
|
|
- log_err(LD_GENERAL,
|
|
|
+ log_warn(LD_GENERAL,
|
|
|
"Failed to close write end of stdout pipe in parent process: %s",
|
|
|
strerror(errno));
|
|
|
/* Do not return -1, because the child is running, so the parent
|
|
@@ -3099,7 +3129,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|
|
retval = close(stderr_pipe[1]);
|
|
|
|
|
|
if (-1 == retval) {
|
|
|
- log_err(LD_GENERAL,
|
|
|
+ log_warn(LD_GENERAL,
|
|
|
"Failed to close write end of stderr pipe in parent process: %s",
|
|
|
strerror(errno));
|
|
|
/* Do not return -1, because the child is running, so the parent
|
|
@@ -3152,26 +3182,27 @@ log_from_pipe(FILE *stream, int severity, const char *executable,
|
|
|
} else {
|
|
|
/* No newline; check whether we overflowed the buffer */
|
|
|
if (!feof(stream))
|
|
|
- log_err(LD_GENERAL,
|
|
|
+ log_warn(LD_GENERAL,
|
|
|
"Line from port forwarding helper was truncated: %s", buf);
|
|
|
/* TODO: What to do with this error? */
|
|
|
}
|
|
|
|
|
|
/* Check if buf starts with SPAWN_ERROR_MESSAGE */
|
|
|
- if (strstr(buf, SPAWN_ERROR_MESSAGE) == buf) {
|
|
|
+ if (strcmpstart(buf, SPAWN_ERROR_MESSAGE) == 0) {
|
|
|
/* Parse error message */
|
|
|
int retval, child_state, saved_errno;
|
|
|
- retval = sscanf(buf, SPAWN_ERROR_MESSAGE "%d/%d",
|
|
|
- &child_state, &saved_errno);
|
|
|
+ retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x",
|
|
|
+ &child_state, &saved_errno);
|
|
|
if (retval == 2) {
|
|
|
- log_err(LD_GENERAL,
|
|
|
+ log_warn(LD_GENERAL,
|
|
|
"Failed to start child process \"%s\" in state %d: %s",
|
|
|
executable, child_state, strerror(saved_errno));
|
|
|
if (child_status)
|
|
|
*child_status = 1;
|
|
|
} else {
|
|
|
- /* Failed to parse message from child process, log it as error */
|
|
|
- log_err(LD_GENERAL,
|
|
|
+ /* Failed to parse message from child process, log it as a
|
|
|
+ warning */
|
|
|
+ log_warn(LD_GENERAL,
|
|
|
"Unexpected message from port forwarding helper \"%s\": %s",
|
|
|
executable, buf);
|
|
|
}
|
|
@@ -3237,7 +3268,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
|
|
|
|
|
|
child_pid = tor_spawn_background(filename, &fd_out, &fd_err, argv);
|
|
|
if (child_pid < 0) {
|
|
|
- log_err(LD_GENERAL, "Failed to start port forwarding helper %s",
|
|
|
+ log_warn(LD_GENERAL, "Failed to start port forwarding helper %s",
|
|
|
filename);
|
|
|
child_pid = -1;
|
|
|
return;
|
|
@@ -3258,7 +3289,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
|
|
|
/* Read from stdout/stderr and log result */
|
|
|
retval = 0;
|
|
|
stdout_status = log_from_pipe(stdout_read, LOG_INFO, filename, &retval);
|
|
|
- stderr_status = log_from_pipe(stderr_read, LOG_ERR, filename, &retval);
|
|
|
+ stderr_status = log_from_pipe(stderr_read, LOG_WARN, filename, &retval);
|
|
|
if (retval) {
|
|
|
/* There was a problem in the child process */
|
|
|
time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL;
|
|
@@ -3280,7 +3311,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
|
|
|
if (1 == retval) {
|
|
|
log_info(LD_GENERAL, "Port forwarding helper terminated");
|
|
|
} else {
|
|
|
- log_err(LD_GENERAL, "Failed to read from port forwarding helper");
|
|
|
+ log_warn(LD_GENERAL, "Failed to read from port forwarding helper");
|
|
|
}
|
|
|
|
|
|
/* TODO: The child might not actually be finished (maybe it failed or
|