mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-23 20:03:31 +01:00
Delete old process_handle_t code.
This patch removes the old process_handle_t code. Everything should by now be using the process_t interface. See: https://bugs.torproject.org/28179
This commit is contained in:
parent
289ed0849d
commit
f7d13425fc
2
.gitignore
vendored
2
.gitignore
vendored
@ -239,7 +239,6 @@ uptime-*.json
|
||||
/src/test/test
|
||||
/src/test/test-slow
|
||||
/src/test/test-bt-cl
|
||||
/src/test/test-child
|
||||
/src/test/test-process
|
||||
/src/test/test-memwipe
|
||||
/src/test/test-ntor-cl
|
||||
@ -250,7 +249,6 @@ uptime-*.json
|
||||
/src/test/test.exe
|
||||
/src/test/test-slow.exe
|
||||
/src/test/test-bt-cl.exe
|
||||
/src/test/test-child.exe
|
||||
/src/test/test-process.exe
|
||||
/src/test/test-ntor-cl.exe
|
||||
/src/test/test-hs-ntor-cl.exe
|
||||
|
@ -913,22 +913,6 @@ handle_proxy_line(const char *line, managed_proxy_t *mp)
|
||||
} else if (!strcmpstart(line, PROTO_LOG)) {
|
||||
parse_log_line(line);
|
||||
return;
|
||||
} else if (!strcmpstart(line, SPAWN_ERROR_MESSAGE)) {
|
||||
/* managed proxy launch failed: parse error message to learn why. */
|
||||
int retval, child_state, saved_errno;
|
||||
retval = tor_sscanf(line, SPAWN_ERROR_MESSAGE "%x/%x",
|
||||
&child_state, &saved_errno);
|
||||
if (retval == 2) {
|
||||
log_warn(LD_GENERAL,
|
||||
"Could not launch managed proxy executable at '%s' ('%s').",
|
||||
mp->argv[0], strerror(saved_errno));
|
||||
} else { /* failed to parse error message */
|
||||
log_warn(LD_GENERAL,"Could not launch managed proxy executable at '%s'.",
|
||||
mp->argv[0]);
|
||||
}
|
||||
|
||||
mp->conf_state = PT_PROTO_FAILED_LAUNCH;
|
||||
return;
|
||||
}
|
||||
|
||||
log_notice(LD_GENERAL, "Unknown line received by managed proxy (%s).", line);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,124 +11,10 @@
|
||||
#ifndef TOR_SUBPROCESS_H
|
||||
#define TOR_SUBPROCESS_H
|
||||
|
||||
#include "lib/cc/torint.h"
|
||||
#include "lib/testsupport/testsupport.h"
|
||||
#include <stddef.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
struct smartlist_t;
|
||||
|
||||
void tor_disable_spawning_background_processes(void);
|
||||
|
||||
typedef struct process_handle_t process_handle_t;
|
||||
struct process_environment_t;
|
||||
int tor_spawn_background(const char *const filename, const char **argv,
|
||||
struct process_environment_t *env,
|
||||
process_handle_t **process_handle_out);
|
||||
|
||||
#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
|
||||
|
||||
/** Status of an I/O stream. */
|
||||
enum stream_status {
|
||||
IO_STREAM_OKAY,
|
||||
IO_STREAM_EAGAIN,
|
||||
IO_STREAM_TERM,
|
||||
IO_STREAM_CLOSED
|
||||
};
|
||||
|
||||
const char *stream_status_to_string(enum stream_status stream_status);
|
||||
|
||||
enum stream_status get_string_from_pipe(int fd, char *buf, size_t count);
|
||||
|
||||
/* Values of process_handle_t.status. */
|
||||
#define PROCESS_STATUS_NOTRUNNING 0
|
||||
#define PROCESS_STATUS_RUNNING 1
|
||||
#define PROCESS_STATUS_ERROR -1
|
||||
|
||||
#ifdef SUBPROCESS_PRIVATE
|
||||
struct waitpid_callback_t;
|
||||
|
||||
/** Structure to represent the state of a process with which Tor is
|
||||
* communicating. The contents of this structure are private to util.c */
|
||||
struct process_handle_t {
|
||||
/** One of the PROCESS_STATUS_* values */
|
||||
int status;
|
||||
#ifdef _WIN32
|
||||
HANDLE stdin_pipe;
|
||||
HANDLE stdout_pipe;
|
||||
HANDLE stderr_pipe;
|
||||
PROCESS_INFORMATION pid;
|
||||
#else /* !(defined(_WIN32)) */
|
||||
int stdin_pipe;
|
||||
int stdout_pipe;
|
||||
int stderr_pipe;
|
||||
pid_t pid;
|
||||
/** If the process has not given us a SIGCHLD yet, this has the
|
||||
* waitpid_callback_t that gets invoked once it has. Otherwise this
|
||||
* contains NULL. */
|
||||
struct waitpid_callback_t *waitpid_cb;
|
||||
/** The exit status reported by waitpid. */
|
||||
int waitpid_exit_status;
|
||||
#endif /* defined(_WIN32) */
|
||||
};
|
||||
#endif /* defined(SUBPROCESS_PRIVATE) */
|
||||
|
||||
/* Return values of tor_get_exit_code() */
|
||||
#define PROCESS_EXIT_RUNNING 1
|
||||
#define PROCESS_EXIT_EXITED 0
|
||||
#define PROCESS_EXIT_ERROR -1
|
||||
int tor_get_exit_code(process_handle_t *process_handle,
|
||||
int block, int *exit_code);
|
||||
int tor_split_lines(struct smartlist_t *sl, char *buf, int len);
|
||||
#ifdef _WIN32
|
||||
ssize_t tor_read_all_handle(HANDLE h, char *buf, size_t count,
|
||||
const process_handle_t *process);
|
||||
#else
|
||||
ssize_t tor_read_all_handle(int fd, char *buf, size_t count,
|
||||
const process_handle_t *process,
|
||||
int *eof);
|
||||
#endif /* defined(_WIN32) */
|
||||
ssize_t tor_read_all_from_process_stdout(
|
||||
const process_handle_t *process_handle, char *buf, size_t count);
|
||||
ssize_t tor_read_all_from_process_stderr(
|
||||
const process_handle_t *process_handle, char *buf, size_t count);
|
||||
char *tor_join_win_cmdline(const char *argv[]);
|
||||
|
||||
int tor_process_get_pid(process_handle_t *process_handle);
|
||||
#ifdef _WIN32
|
||||
HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle);
|
||||
#else
|
||||
int tor_process_get_stdout_pipe(process_handle_t *process_handle);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
MOCK_DECL(struct smartlist_t *, tor_get_lines_from_handle,(HANDLE *handle,
|
||||
enum stream_status *stream_status));
|
||||
#else
|
||||
MOCK_DECL(struct smartlist_t *, tor_get_lines_from_handle,(int fd,
|
||||
enum stream_status *stream_status));
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
int tor_terminate_process(process_handle_t *process_handle);
|
||||
|
||||
MOCK_DECL(void, tor_process_handle_destroy,(process_handle_t *process_handle,
|
||||
int also_terminate_process));
|
||||
|
||||
#ifdef SUBPROCESS_PRIVATE
|
||||
/* Prototypes for private functions only used by util.c (and unit tests) */
|
||||
|
||||
#ifndef _WIN32
|
||||
STATIC int format_helper_exit_status(unsigned char child_state,
|
||||
int saved_errno, char *hex_errno);
|
||||
|
||||
/* Space for hex values of child state, a slash, saved_errno (with
|
||||
leading minus) and newline (no null) */
|
||||
#define HEX_ERRNO_SIZE (sizeof(char) * 2 + 1 + \
|
||||
1 + sizeof(int) * 2 + 1)
|
||||
#endif /* !defined(_WIN32) */
|
||||
|
||||
#endif /* defined(SUBPROCESS_PRIVATE) */
|
||||
char *tor_join_win_cmdline(const char *argv[]);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
all: test.exe test-child.exe bench.exe
|
||||
all: test.exe bench.exe
|
||||
|
||||
CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common /I ..\or \
|
||||
/I ..\ext
|
||||
@ -30,8 +30,5 @@ test.exe: $(TEST_OBJECTS)
|
||||
bench.exe: bench.obj
|
||||
$(CC) $(CFLAGS) bench.obj $(LIBS) ..\common\*.lib /Fe$@
|
||||
|
||||
test-child.exe: test-child.obj
|
||||
$(CC) $(CFLAGS) test-child.obj /Fe$@
|
||||
|
||||
clean:
|
||||
del *.obj *.lib test.exe bench.exe test-child.exe
|
||||
del *.obj *.lib test.exe bench.exe
|
||||
|
@ -65,7 +65,6 @@ noinst_PROGRAMS+= \
|
||||
src/test/test \
|
||||
src/test/test-slow \
|
||||
src/test/test-memwipe \
|
||||
src/test/test-child \
|
||||
src/test/test-process \
|
||||
src/test/test_workqueue \
|
||||
src/test/test-switch-id \
|
||||
@ -204,7 +203,6 @@ src_test_test_slow_SOURCES += \
|
||||
src/test/test_slow.c \
|
||||
src/test/test_crypto_slow.c \
|
||||
src/test/test_process_slow.c \
|
||||
src/test/test_util_slow.c \
|
||||
src/test/testing_common.c \
|
||||
src/test/testing_rsakeys.c \
|
||||
src/ext/tinytest.c
|
||||
|
@ -1,61 +0,0 @@
|
||||
/* Copyright (c) 2011-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#include "orconfig.h"
|
||||
#include <stdio.h>
|
||||
#ifdef _WIN32
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif /* defined(_WIN32) */
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SLEEP(sec) Sleep((sec)*1000)
|
||||
#else
|
||||
#define SLEEP(sec) sleep(sec)
|
||||
#endif
|
||||
|
||||
/** Trivial test program which prints out its command line arguments so we can
|
||||
* check if tor_spawn_background() works */
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
int delay = 1;
|
||||
int fast = 0;
|
||||
|
||||
if (argc > 1) {
|
||||
if (!strcmp(argv[1], "--hang")) {
|
||||
delay = 60;
|
||||
} else if (!strcmp(argv[1], "--fast")) {
|
||||
fast = 1;
|
||||
delay = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stdout, "OUT\n");
|
||||
fprintf(stderr, "ERR\n");
|
||||
for (i = 1; i < argc; i++)
|
||||
fprintf(stdout, "%s\n", argv[i]);
|
||||
if (!fast)
|
||||
fprintf(stdout, "SLEEPING\n");
|
||||
/* We need to flush stdout so that test_util_spawn_background_partial_read()
|
||||
succeed. Otherwise ReadFile() will get the entire output in one */
|
||||
// XXX: Can we make stdio flush on newline?
|
||||
fflush(stdout);
|
||||
if (!fast)
|
||||
SLEEP(1);
|
||||
fprintf(stdout, "DONE\n");
|
||||
fflush(stdout);
|
||||
if (fast)
|
||||
return 0;
|
||||
|
||||
while (--delay) {
|
||||
SLEEP(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -271,7 +271,6 @@ extern struct testcase_t x509_tests[];
|
||||
|
||||
extern struct testcase_t slow_crypto_tests[];
|
||||
extern struct testcase_t slow_process_tests[];
|
||||
extern struct testcase_t slow_util_tests[];
|
||||
|
||||
extern struct testgroup_t testgroups[];
|
||||
|
||||
|
@ -117,22 +117,27 @@ test_sigsafe_err(void *arg)
|
||||
content = read_file_to_str(fn, 0, NULL);
|
||||
|
||||
tt_ptr_op(content, OP_NE, NULL);
|
||||
tor_split_lines(lines, content, (int)strlen(content));
|
||||
smartlist_split_string(lines, content, "\n", 0, 0);
|
||||
tt_int_op(smartlist_len(lines), OP_GE, 5);
|
||||
|
||||
if (strstr(smartlist_get(lines, 0), "opening new log file"))
|
||||
if (strstr(smartlist_get(lines, 0), "opening new log file")) {
|
||||
void *item = smartlist_get(lines, 0);
|
||||
smartlist_del_keeporder(lines, 0);
|
||||
tor_free(item);
|
||||
}
|
||||
|
||||
tt_assert(strstr(smartlist_get(lines, 0), "Say, this isn't too cool"));
|
||||
/* Next line is blank. */
|
||||
tt_assert(!strcmpstart(smartlist_get(lines, 1), "=============="));
|
||||
tt_assert(!strcmpstart(smartlist_get(lines, 2), "Minimal."));
|
||||
/* Next line is blank. */
|
||||
tt_assert(!strcmpstart(smartlist_get(lines, 3), "=============="));
|
||||
tt_str_op(smartlist_get(lines, 4), OP_EQ,
|
||||
tt_str_op(smartlist_get(lines, 1), OP_EQ, "");
|
||||
tt_assert(!strcmpstart(smartlist_get(lines, 2), "=============="));
|
||||
tt_assert(!strcmpstart(smartlist_get(lines, 3), "Minimal."));
|
||||
tt_str_op(smartlist_get(lines, 4), OP_EQ, "");
|
||||
tt_assert(!strcmpstart(smartlist_get(lines, 5), "=============="));
|
||||
tt_str_op(smartlist_get(lines, 6), OP_EQ,
|
||||
"Testing any attempt to manually log from a signal.");
|
||||
|
||||
done:
|
||||
tor_free(content);
|
||||
SMARTLIST_FOREACH(lines, char *, x, tor_free(x));
|
||||
smartlist_free(lines);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
struct testgroup_t testgroups[] = {
|
||||
{ "slow/crypto/", slow_crypto_tests },
|
||||
{ "slow/process/", slow_process_tests },
|
||||
{ "slow/util/", slow_util_tests },
|
||||
END_OF_GROUPS
|
||||
};
|
||||
|
||||
|
@ -4301,204 +4301,6 @@ test_util_load_win_lib(void *ptr)
|
||||
}
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
#ifndef _WIN32
|
||||
static void
|
||||
clear_hex_errno(char *hex_errno)
|
||||
{
|
||||
memset(hex_errno, '\0', HEX_ERRNO_SIZE + 1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_exit_status(void *ptr)
|
||||
{
|
||||
/* Leave an extra byte for a \0 so we can do string comparison */
|
||||
char hex_errno[HEX_ERRNO_SIZE + 1];
|
||||
int n;
|
||||
|
||||
(void)ptr;
|
||||
|
||||
clear_hex_errno(hex_errno);
|
||||
tt_str_op("",OP_EQ, hex_errno);
|
||||
|
||||
clear_hex_errno(hex_errno);
|
||||
n = format_helper_exit_status(0, 0, hex_errno);
|
||||
tt_str_op("0/0\n",OP_EQ, hex_errno);
|
||||
tt_int_op(n,OP_EQ, strlen(hex_errno));
|
||||
|
||||
#if SIZEOF_INT == 4
|
||||
|
||||
clear_hex_errno(hex_errno);
|
||||
n = format_helper_exit_status(0, 0x7FFFFFFF, hex_errno);
|
||||
tt_str_op("0/7FFFFFFF\n",OP_EQ, hex_errno);
|
||||
tt_int_op(n,OP_EQ, strlen(hex_errno));
|
||||
|
||||
clear_hex_errno(hex_errno);
|
||||
n = format_helper_exit_status(0xFF, -0x80000000, hex_errno);
|
||||
tt_str_op("FF/-80000000\n",OP_EQ, hex_errno);
|
||||
tt_int_op(n,OP_EQ, strlen(hex_errno));
|
||||
tt_int_op(n,OP_EQ, HEX_ERRNO_SIZE);
|
||||
|
||||
#elif SIZEOF_INT == 8
|
||||
|
||||
clear_hex_errno(hex_errno);
|
||||
n = format_helper_exit_status(0, 0x7FFFFFFFFFFFFFFF, hex_errno);
|
||||
tt_str_op("0/7FFFFFFFFFFFFFFF\n",OP_EQ, hex_errno);
|
||||
tt_int_op(n,OP_EQ, strlen(hex_errno));
|
||||
|
||||
clear_hex_errno(hex_errno);
|
||||
n = format_helper_exit_status(0xFF, -0x8000000000000000, hex_errno);
|
||||
tt_str_op("FF/-8000000000000000\n",OP_EQ, hex_errno);
|
||||
tt_int_op(n,OP_EQ, strlen(hex_errno));
|
||||
tt_int_op(n,OP_EQ, HEX_ERRNO_SIZE);
|
||||
|
||||
#endif /* SIZEOF_INT == 4 || ... */
|
||||
|
||||
clear_hex_errno(hex_errno);
|
||||
n = format_helper_exit_status(0x7F, 0, hex_errno);
|
||||
tt_str_op("7F/0\n",OP_EQ, hex_errno);
|
||||
tt_int_op(n,OP_EQ, strlen(hex_errno));
|
||||
|
||||
clear_hex_errno(hex_errno);
|
||||
n = format_helper_exit_status(0x08, -0x242, hex_errno);
|
||||
tt_str_op("8/-242\n",OP_EQ, hex_errno);
|
||||
tt_int_op(n,OP_EQ, strlen(hex_errno));
|
||||
|
||||
clear_hex_errno(hex_errno);
|
||||
tt_str_op("",OP_EQ, hex_errno);
|
||||
|
||||
done:
|
||||
;
|
||||
}
|
||||
#endif /* !defined(_WIN32) */
|
||||
|
||||
#ifndef _WIN32
|
||||
static void
|
||||
test_util_string_from_pipe(void *ptr)
|
||||
{
|
||||
int test_pipe[2] = {-1, -1};
|
||||
int retval = 0;
|
||||
enum stream_status status = IO_STREAM_TERM;
|
||||
ssize_t retlen;
|
||||
char buf[4] = { 0 };
|
||||
|
||||
(void)ptr;
|
||||
|
||||
errno = 0;
|
||||
|
||||
/* Set up a pipe to test on */
|
||||
retval = pipe(test_pipe);
|
||||
tt_int_op(retval, OP_EQ, 0);
|
||||
|
||||
/* Send in a string. */
|
||||
retlen = write(test_pipe[1], "ABC", 3);
|
||||
tt_int_op(retlen, OP_EQ, 3);
|
||||
|
||||
status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
|
||||
tt_int_op(errno, OP_EQ, 0);
|
||||
tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
|
||||
tt_str_op(buf, OP_EQ, "ABC");
|
||||
errno = 0;
|
||||
|
||||
/* Send in a string that contains a nul. */
|
||||
retlen = write(test_pipe[1], "AB\0", 3);
|
||||
tt_int_op(retlen, OP_EQ, 3);
|
||||
|
||||
status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
|
||||
tt_int_op(errno, OP_EQ, 0);
|
||||
tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
|
||||
tt_str_op(buf, OP_EQ, "AB");
|
||||
errno = 0;
|
||||
|
||||
/* Send in a string that contains a nul only. */
|
||||
retlen = write(test_pipe[1], "\0", 1);
|
||||
tt_int_op(retlen, OP_EQ, 1);
|
||||
|
||||
status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
|
||||
tt_int_op(errno, OP_EQ, 0);
|
||||
tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
|
||||
tt_str_op(buf, OP_EQ, "");
|
||||
errno = 0;
|
||||
|
||||
/* Send in a string that contains a trailing newline. */
|
||||
retlen = write(test_pipe[1], "AB\n", 3);
|
||||
tt_int_op(retlen, OP_EQ, 3);
|
||||
|
||||
status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
|
||||
tt_int_op(errno, OP_EQ, 0);
|
||||
tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
|
||||
tt_str_op(buf, OP_EQ, "AB");
|
||||
errno = 0;
|
||||
|
||||
/* Send in a string that contains a newline only. */
|
||||
retlen = write(test_pipe[1], "\n", 1);
|
||||
tt_int_op(retlen, OP_EQ, 1);
|
||||
|
||||
status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
|
||||
tt_int_op(errno, OP_EQ, 0);
|
||||
tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
|
||||
tt_str_op(buf, OP_EQ, "");
|
||||
errno = 0;
|
||||
|
||||
/* Send in a string and check that we nul terminate return values. */
|
||||
retlen = write(test_pipe[1], "AAA", 3);
|
||||
tt_int_op(retlen, OP_EQ, 3);
|
||||
|
||||
status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
|
||||
tt_int_op(errno, OP_EQ, 0);
|
||||
tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
|
||||
tt_str_op(buf, OP_EQ, "AAA");
|
||||
tt_mem_op(buf, OP_EQ, "AAA\0", sizeof(buf));
|
||||
errno = 0;
|
||||
|
||||
retlen = write(test_pipe[1], "B", 1);
|
||||
tt_int_op(retlen, OP_EQ, 1);
|
||||
|
||||
memset(buf, '\xff', sizeof(buf));
|
||||
status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
|
||||
tt_int_op(errno, OP_EQ, 0);
|
||||
tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
|
||||
tt_str_op(buf, OP_EQ, "B");
|
||||
tt_mem_op(buf, OP_EQ, "B\0\xff\xff", sizeof(buf));
|
||||
errno = 0;
|
||||
|
||||
/* Send in multiple lines. */
|
||||
retlen = write(test_pipe[1], "A\nB", 3);
|
||||
tt_int_op(retlen, OP_EQ, 3);
|
||||
|
||||
status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
|
||||
tt_int_op(errno, OP_EQ, 0);
|
||||
tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
|
||||
tt_str_op(buf, OP_EQ, "A\nB");
|
||||
errno = 0;
|
||||
|
||||
/* Send in a line and close */
|
||||
retlen = write(test_pipe[1], "AB", 2);
|
||||
tt_int_op(retlen, OP_EQ, 2);
|
||||
retval = close(test_pipe[1]);
|
||||
tt_int_op(retval, OP_EQ, 0);
|
||||
test_pipe[1] = -1;
|
||||
|
||||
status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
|
||||
tt_int_op(errno, OP_EQ, 0);
|
||||
tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
|
||||
tt_str_op(buf, OP_EQ, "AB");
|
||||
errno = 0;
|
||||
|
||||
/* Check for EOF */
|
||||
status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
|
||||
tt_int_op(errno, OP_EQ, 0);
|
||||
tt_int_op(status, OP_EQ, IO_STREAM_CLOSED);
|
||||
errno = 0;
|
||||
|
||||
done:
|
||||
if (test_pipe[0] != -1)
|
||||
close(test_pipe[0]);
|
||||
if (test_pipe[1] != -1)
|
||||
close(test_pipe[1]);
|
||||
}
|
||||
|
||||
#endif /* !defined(_WIN32) */
|
||||
|
||||
/**
|
||||
* Test for format_hex_number_sigsafe()
|
||||
*/
|
||||
@ -4651,67 +4453,6 @@ struct split_lines_test_t {
|
||||
const char *split_line[MAX_SPLIT_LINE_COUNT]; // Split lines
|
||||
};
|
||||
|
||||
/**
|
||||
* Test that we properly split a buffer into lines
|
||||
*/
|
||||
static void
|
||||
test_util_split_lines(void *ptr)
|
||||
{
|
||||
/* Test cases. orig_line of last test case must be NULL.
|
||||
* The last element of split_line[i] must be NULL. */
|
||||
struct split_lines_test_t tests[] = {
|
||||
{"", 0, {NULL}},
|
||||
{"foo", 3, {"foo", NULL}},
|
||||
{"\n\rfoo\n\rbar\r\n", 12, {"foo", "bar", NULL}},
|
||||
{"fo o\r\nb\tar", 10, {"fo o", "b.ar", NULL}},
|
||||
{"\x0f""f\0o\0\n\x01""b\0r\0\r", 12, {".f.o.", ".b.r.", NULL}},
|
||||
{"line 1\r\nline 2", 14, {"line 1", "line 2", NULL}},
|
||||
{"line 1\r\n\r\nline 2", 16, {"line 1", "line 2", NULL}},
|
||||
{"line 1\r\n\r\r\r\nline 2", 18, {"line 1", "line 2", NULL}},
|
||||
{"line 1\r\n\n\n\n\rline 2", 18, {"line 1", "line 2", NULL}},
|
||||
{"line 1\r\n\r\t\r\nline 3", 18, {"line 1", ".", "line 3", NULL}},
|
||||
{"\n\t\r\t\nline 3", 11, {".", ".", "line 3", NULL}},
|
||||
{NULL, 0, { NULL }}
|
||||
};
|
||||
|
||||
int i, j;
|
||||
char *orig_line=NULL;
|
||||
smartlist_t *sl=NULL;
|
||||
|
||||
(void)ptr;
|
||||
|
||||
for (i=0; tests[i].orig_line; i++) {
|
||||
sl = smartlist_new();
|
||||
/* Allocate space for string and trailing NULL */
|
||||
orig_line = tor_memdup(tests[i].orig_line, tests[i].orig_length + 1);
|
||||
tor_split_lines(sl, orig_line, tests[i].orig_length);
|
||||
|
||||
j = 0;
|
||||
log_info(LD_GENERAL, "Splitting test %d of length %d",
|
||||
i, tests[i].orig_length);
|
||||
SMARTLIST_FOREACH_BEGIN(sl, const char *, line) {
|
||||
/* Check we have not got too many lines */
|
||||
tt_int_op(MAX_SPLIT_LINE_COUNT, OP_GT, j);
|
||||
/* Check that there actually should be a line here */
|
||||
tt_ptr_op(tests[i].split_line[j], OP_NE, NULL);
|
||||
log_info(LD_GENERAL, "Line %d of test %d, should be <%s>",
|
||||
j, i, tests[i].split_line[j]);
|
||||
/* Check that the line is as expected */
|
||||
tt_str_op(line,OP_EQ, tests[i].split_line[j]);
|
||||
j++;
|
||||
} SMARTLIST_FOREACH_END(line);
|
||||
/* Check that we didn't miss some lines */
|
||||
tt_ptr_op(NULL,OP_EQ, tests[i].split_line[j]);
|
||||
tor_free(orig_line);
|
||||
smartlist_free(sl);
|
||||
sl = NULL;
|
||||
}
|
||||
|
||||
done:
|
||||
tor_free(orig_line);
|
||||
smartlist_free(sl);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_di_ops(void *arg)
|
||||
{
|
||||
@ -6483,12 +6224,9 @@ struct testcase_t util_tests[] = {
|
||||
UTIL_TEST(nowrap_math, 0),
|
||||
UTIL_TEST(num_cpus, 0),
|
||||
UTIL_TEST_WIN_ONLY(load_win_lib, 0),
|
||||
UTIL_TEST_NO_WIN(exit_status, 0),
|
||||
UTIL_TEST_NO_WIN(string_from_pipe, 0),
|
||||
UTIL_TEST(format_hex_number, 0),
|
||||
UTIL_TEST(format_dec_number, 0),
|
||||
UTIL_TEST(join_win_cmdline, 0),
|
||||
UTIL_TEST(split_lines, 0),
|
||||
UTIL_TEST(n_bits_set, 0),
|
||||
UTIL_TEST(eat_whitespace, 0),
|
||||
UTIL_TEST(sl_new_from_text_lines, 0),
|
||||
|
@ -1,396 +0,0 @@
|
||||
/* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#include "orconfig.h"
|
||||
#define UTIL_PRIVATE
|
||||
#define SUBPROCESS_PRIVATE
|
||||
#include "lib/crypt_ops/crypto_cipher.h"
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/process/subprocess.h"
|
||||
#include "lib/process/waitpid.h"
|
||||
#include "lib/string/printf.h"
|
||||
#include "lib/time/compat_time.h"
|
||||
#include "test/test.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef BUILDDIR
|
||||
#define BUILDDIR "."
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define notify_pending_waitpid_callbacks() STMT_NIL
|
||||
#define TEST_CHILD "test-child.exe"
|
||||
#define EOL "\r\n"
|
||||
#else
|
||||
#define TEST_CHILD (BUILDDIR "/src/test/test-child")
|
||||
#define EOL "\n"
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
#ifdef _WIN32
|
||||
/* I've assumed Windows doesn't have the gap between fork and exec
|
||||
* that causes the race condition on unix-like platforms */
|
||||
#define MATCH_PROCESS_STATUS(s1,s2) ((s1) == (s2))
|
||||
|
||||
#else /* !(defined(_WIN32)) */
|
||||
/* work around a race condition of the timing of SIGCHLD handler updates
|
||||
* to the process_handle's fields, and checks of those fields
|
||||
*
|
||||
* TODO: Once we can signal failure to exec, change PROCESS_STATUS_RUNNING to
|
||||
* PROCESS_STATUS_ERROR (and similarly with *_OR_NOTRUNNING) */
|
||||
#define PROCESS_STATUS_RUNNING_OR_NOTRUNNING (PROCESS_STATUS_RUNNING+1)
|
||||
#define IS_RUNNING_OR_NOTRUNNING(s) \
|
||||
((s) == PROCESS_STATUS_RUNNING || (s) == PROCESS_STATUS_NOTRUNNING)
|
||||
/* well, this is ugly */
|
||||
#define MATCH_PROCESS_STATUS(s1,s2) \
|
||||
( (s1) == (s2) \
|
||||
||((s1) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \
|
||||
&& IS_RUNNING_OR_NOTRUNNING(s2)) \
|
||||
||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \
|
||||
&& IS_RUNNING_OR_NOTRUNNING(s1)))
|
||||
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
/** Helper function for testing tor_spawn_background */
|
||||
static void
|
||||
run_util_spawn_background(const char *argv[], const char *expected_out,
|
||||
const char *expected_err, int expected_exit,
|
||||
int expected_status)
|
||||
{
|
||||
int retval, exit_code;
|
||||
ssize_t pos;
|
||||
process_handle_t *process_handle=NULL;
|
||||
char stdout_buf[100], stderr_buf[100];
|
||||
int status;
|
||||
|
||||
/* Start the program */
|
||||
#ifdef _WIN32
|
||||
status = tor_spawn_background(NULL, argv, NULL, &process_handle);
|
||||
#else
|
||||
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
|
||||
#endif
|
||||
|
||||
notify_pending_waitpid_callbacks();
|
||||
|
||||
/* the race condition doesn't affect status,
|
||||
* because status isn't updated by the SIGCHLD handler,
|
||||
* but we still need to handle PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
|
||||
tt_assert(MATCH_PROCESS_STATUS(expected_status, status));
|
||||
if (status == PROCESS_STATUS_ERROR) {
|
||||
tt_ptr_op(process_handle, OP_EQ, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
tt_ptr_op(process_handle, OP_NE, NULL);
|
||||
|
||||
/* When a spawned process forks, fails, then exits very quickly,
|
||||
* (this typically occurs when exec fails)
|
||||
* there is a race condition between the SIGCHLD handler
|
||||
* updating the process_handle's fields, and this test
|
||||
* checking the process status in those fields.
|
||||
* The SIGCHLD update can occur before or after the code below executes.
|
||||
* This causes intermittent failures in spawn_background_fail(),
|
||||
* typically when the machine is under load.
|
||||
* We use PROCESS_STATUS_RUNNING_OR_NOTRUNNING to avoid this issue. */
|
||||
|
||||
/* the race condition affects the change in
|
||||
* process_handle->status from RUNNING to NOTRUNNING */
|
||||
tt_assert(MATCH_PROCESS_STATUS(expected_status, process_handle->status));
|
||||
|
||||
#ifndef _WIN32
|
||||
notify_pending_waitpid_callbacks();
|
||||
/* the race condition affects the change in
|
||||
* process_handle->waitpid_cb to NULL,
|
||||
* so we skip the check if expected_status is ambiguous,
|
||||
* that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
|
||||
tt_assert(process_handle->waitpid_cb != NULL
|
||||
|| expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING);
|
||||
#endif /* !defined(_WIN32) */
|
||||
|
||||
#ifdef _WIN32
|
||||
tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
|
||||
tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
|
||||
tt_assert(process_handle->stdin_pipe != INVALID_HANDLE_VALUE);
|
||||
#else
|
||||
tt_assert(process_handle->stdout_pipe >= 0);
|
||||
tt_assert(process_handle->stderr_pipe >= 0);
|
||||
tt_assert(process_handle->stdin_pipe >= 0);
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
/* Check stdout */
|
||||
pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
|
||||
sizeof(stdout_buf) - 1);
|
||||
tt_assert(pos >= 0);
|
||||
stdout_buf[pos] = '\0';
|
||||
tt_int_op(strlen(expected_out),OP_EQ, pos);
|
||||
tt_str_op(expected_out,OP_EQ, stdout_buf);
|
||||
|
||||
notify_pending_waitpid_callbacks();
|
||||
|
||||
/* Check it terminated correctly */
|
||||
retval = tor_get_exit_code(process_handle, 1, &exit_code);
|
||||
tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
|
||||
tt_int_op(expected_exit,OP_EQ, exit_code);
|
||||
// TODO: Make test-child exit with something other than 0
|
||||
|
||||
#ifndef _WIN32
|
||||
notify_pending_waitpid_callbacks();
|
||||
tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
|
||||
#endif
|
||||
|
||||
/* Check stderr */
|
||||
pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
|
||||
sizeof(stderr_buf) - 1);
|
||||
tt_assert(pos >= 0);
|
||||
stderr_buf[pos] = '\0';
|
||||
tt_str_op(expected_err,OP_EQ, stderr_buf);
|
||||
tt_int_op(strlen(expected_err),OP_EQ, pos);
|
||||
|
||||
notify_pending_waitpid_callbacks();
|
||||
|
||||
done:
|
||||
if (process_handle)
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
}
|
||||
|
||||
/** Check that we can launch a process and read the output */
|
||||
static void
|
||||
test_util_spawn_background_ok(void *ptr)
|
||||
{
|
||||
const char *argv[] = {TEST_CHILD, "--test", NULL};
|
||||
const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL;
|
||||
const char *expected_err = "ERR"EOL;
|
||||
|
||||
(void)ptr;
|
||||
|
||||
run_util_spawn_background(argv, expected_out, expected_err, 0,
|
||||
PROCESS_STATUS_RUNNING);
|
||||
}
|
||||
|
||||
/** Check that failing to find the executable works as expected */
|
||||
static void
|
||||
test_util_spawn_background_fail(void *ptr)
|
||||
{
|
||||
const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
|
||||
const char *expected_err = "";
|
||||
char expected_out[1024];
|
||||
char code[32];
|
||||
#ifdef _WIN32
|
||||
const int expected_status = PROCESS_STATUS_ERROR;
|
||||
#else
|
||||
/* TODO: Once we can signal failure to exec, set this to be
|
||||
* PROCESS_STATUS_RUNNING_OR_ERROR */
|
||||
const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING;
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
memset(expected_out, 0xf0, sizeof(expected_out));
|
||||
memset(code, 0xf0, sizeof(code));
|
||||
|
||||
(void)ptr;
|
||||
|
||||
tor_snprintf(code, sizeof(code), "%x/%x",
|
||||
9 /* CHILD_STATE_FAILEXEC */ , ENOENT);
|
||||
tor_snprintf(expected_out, sizeof(expected_out),
|
||||
"ERR: Failed to spawn background process - code %s\n", code);
|
||||
|
||||
run_util_spawn_background(argv, expected_out, expected_err, 255,
|
||||
expected_status);
|
||||
}
|
||||
|
||||
/** Test that reading from a handle returns a partial read rather than
|
||||
* blocking */
|
||||
static void
|
||||
test_util_spawn_background_partial_read_impl(int exit_early)
|
||||
{
|
||||
const int expected_exit = 0;
|
||||
const int expected_status = PROCESS_STATUS_RUNNING;
|
||||
|
||||
int retval, exit_code;
|
||||
ssize_t pos = -1;
|
||||
process_handle_t *process_handle=NULL;
|
||||
int status;
|
||||
char stdout_buf[100], stderr_buf[100];
|
||||
|
||||
const char *argv[] = {TEST_CHILD, "--test", NULL};
|
||||
const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL,
|
||||
"DONE" EOL,
|
||||
NULL };
|
||||
const char *expected_err = "ERR" EOL;
|
||||
|
||||
#ifndef _WIN32
|
||||
int eof = 0;
|
||||
#endif
|
||||
int expected_out_ctr;
|
||||
|
||||
if (exit_early) {
|
||||
argv[1] = "--hang";
|
||||
expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL;
|
||||
}
|
||||
|
||||
/* Start the program */
|
||||
#ifdef _WIN32
|
||||
status = tor_spawn_background(NULL, argv, NULL, &process_handle);
|
||||
#else
|
||||
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
|
||||
#endif
|
||||
tt_int_op(expected_status,OP_EQ, status);
|
||||
tt_assert(process_handle);
|
||||
tt_int_op(expected_status,OP_EQ, process_handle->status);
|
||||
|
||||
/* Check stdout */
|
||||
for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) {
|
||||
#ifdef _WIN32
|
||||
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
|
||||
sizeof(stdout_buf) - 1, NULL);
|
||||
#else
|
||||
/* Check that we didn't read the end of file last time */
|
||||
tt_assert(!eof);
|
||||
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
|
||||
sizeof(stdout_buf) - 1, NULL, &eof);
|
||||
#endif /* defined(_WIN32) */
|
||||
log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
|
||||
|
||||
/* We would have blocked, keep on trying */
|
||||
if (0 == pos)
|
||||
continue;
|
||||
|
||||
tt_assert(pos > 0);
|
||||
stdout_buf[pos] = '\0';
|
||||
tt_str_op(expected_out[expected_out_ctr],OP_EQ, stdout_buf);
|
||||
tt_int_op(strlen(expected_out[expected_out_ctr]),OP_EQ, pos);
|
||||
expected_out_ctr++;
|
||||
}
|
||||
|
||||
if (exit_early) {
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
process_handle = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* The process should have exited without writing more */
|
||||
#ifdef _WIN32
|
||||
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
|
||||
sizeof(stdout_buf) - 1,
|
||||
process_handle);
|
||||
tt_int_op(0,OP_EQ, pos);
|
||||
#else /* !(defined(_WIN32)) */
|
||||
if (!eof) {
|
||||
/* We should have got all the data, but maybe not the EOF flag */
|
||||
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
|
||||
sizeof(stdout_buf) - 1,
|
||||
process_handle, &eof);
|
||||
tt_int_op(0,OP_EQ, pos);
|
||||
tt_assert(eof);
|
||||
}
|
||||
/* Otherwise, we got the EOF on the last read */
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
/* Check it terminated correctly */
|
||||
retval = tor_get_exit_code(process_handle, 1, &exit_code);
|
||||
tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
|
||||
tt_int_op(expected_exit,OP_EQ, exit_code);
|
||||
|
||||
// TODO: Make test-child exit with something other than 0
|
||||
|
||||
/* Check stderr */
|
||||
pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
|
||||
sizeof(stderr_buf) - 1);
|
||||
tt_assert(pos >= 0);
|
||||
stderr_buf[pos] = '\0';
|
||||
tt_str_op(expected_err,OP_EQ, stderr_buf);
|
||||
tt_int_op(strlen(expected_err),OP_EQ, pos);
|
||||
|
||||
done:
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_spawn_background_partial_read(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
test_util_spawn_background_partial_read_impl(0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_spawn_background_exit_early(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
test_util_spawn_background_partial_read_impl(1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_spawn_background_waitpid_notify(void *arg)
|
||||
{
|
||||
int retval, exit_code;
|
||||
process_handle_t *process_handle=NULL;
|
||||
int status;
|
||||
int ms_timer;
|
||||
|
||||
const char *argv[] = {TEST_CHILD, "--fast", NULL};
|
||||
|
||||
(void) arg;
|
||||
|
||||
#ifdef _WIN32
|
||||
status = tor_spawn_background(NULL, argv, NULL, &process_handle);
|
||||
#else
|
||||
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
|
||||
#endif
|
||||
|
||||
tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
|
||||
tt_ptr_op(process_handle, OP_NE, NULL);
|
||||
|
||||
/* We're not going to look at the stdout/stderr output this time. Instead,
|
||||
* we're testing whether notify_pending_waitpid_calbacks() can report the
|
||||
* process exit (on unix) and/or whether tor_get_exit_code() can notice it
|
||||
* (on windows) */
|
||||
|
||||
#ifndef _WIN32
|
||||
ms_timer = 30*1000;
|
||||
tt_ptr_op(process_handle->waitpid_cb, OP_NE, NULL);
|
||||
while (process_handle->waitpid_cb && ms_timer > 0) {
|
||||
tor_sleep_msec(100);
|
||||
ms_timer -= 100;
|
||||
notify_pending_waitpid_callbacks();
|
||||
}
|
||||
tt_int_op(ms_timer, OP_GT, 0);
|
||||
tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
|
||||
#endif /* !defined(_WIN32) */
|
||||
|
||||
ms_timer = 30*1000;
|
||||
while (((retval = tor_get_exit_code(process_handle, 0, &exit_code))
|
||||
== PROCESS_EXIT_RUNNING) && ms_timer > 0) {
|
||||
tor_sleep_msec(100);
|
||||
ms_timer -= 100;
|
||||
}
|
||||
tt_int_op(ms_timer, OP_GT, 0);
|
||||
|
||||
tt_int_op(retval, OP_EQ, PROCESS_EXIT_EXITED);
|
||||
|
||||
done:
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
}
|
||||
|
||||
#undef TEST_CHILD
|
||||
#undef EOL
|
||||
|
||||
#undef MATCH_PROCESS_STATUS
|
||||
|
||||
#ifndef _WIN32
|
||||
#undef PROCESS_STATUS_RUNNING_OR_NOTRUNNING
|
||||
#undef IS_RUNNING_OR_NOTRUNNING
|
||||
#endif
|
||||
|
||||
#define UTIL_TEST(name, flags) \
|
||||
{ #name, test_util_ ## name, flags, NULL, NULL }
|
||||
|
||||
struct testcase_t slow_util_tests[] = {
|
||||
UTIL_TEST(spawn_background_ok, 0),
|
||||
UTIL_TEST(spawn_background_fail, 0),
|
||||
UTIL_TEST(spawn_background_partial_read, 0),
|
||||
UTIL_TEST(spawn_background_exit_early, 0),
|
||||
UTIL_TEST(spawn_background_waitpid_notify, 0),
|
||||
END_OF_TESTCASES
|
||||
};
|
Loading…
Reference in New Issue
Block a user