rizin/librz/socket/run.c

1326 lines
30 KiB
C

// SPDX-FileCopyrightText: 2022 deroad <wargio@libero.it>
// SPDX-FileCopyrightText: 2014-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only
/* this helper api is here because it depends on rz_util and rz_socket */
/* we should find a better place for it. rz_io? */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <rz_socket.h>
#include <rz_util.h>
#include <rz_lib.h>
#include <rz_cons.h>
#include <sys/stat.h>
#include <sys/types.h>
#if __APPLE__ && HAVE_FORK
#include <spawn.h>
#include <sys/wait.h>
#include <mach/exception_types.h>
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/mach_traps.h>
#include <mach/task.h>
#include <mach/task_info.h>
#include <mach/thread_act.h>
#include <mach/thread_info.h>
#include <mach/vm_map.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#endif
#if HAVE_OPENPTY && HAVE_FORKPTY && HAVE_LOGIN_TTY
#if defined(__APPLE__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <util.h>
#elif defined(__FreeBSD__) || defined(__DragonFly__)
#include <libutil.h>
#else
#include <pty.h>
#include <utmp.h>
#endif
#endif
#if __UNIX__
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <grp.h>
#include <errno.h>
#if defined(__sun)
#include <sys/filio.h>
#endif /* __sun */
#endif /* __UNIX__ */
#ifdef _MSC_VER
#include <direct.h> // to compile chdir in msvc windows
#include <process.h> // to compile execv in msvc windows
#define pid_t int
#endif
#if HAVE_OPENPTY && HAVE_FORKPTY && HAVE_LOGIN_TTY
static int (*dyn_openpty)(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp) = NULL;
static int (*dyn_login_tty)(int fd) = NULL;
static id_t (*dyn_forkpty)(int *amaster, char *name, struct termios *termp, struct winsize *winp) = NULL;
static void dyn_init(void) {
if (!dyn_openpty) {
dyn_openpty = rz_sys_dlsym(NULL, "openpty");
}
if (!dyn_login_tty) {
dyn_openpty = rz_sys_dlsym(NULL, "login_tty");
}
if (!dyn_forkpty) {
dyn_openpty = rz_sys_dlsym(NULL, "forkpty");
}
}
#endif
RZ_API RzRunProfile *rz_run_new(const char *str) {
RzRunProfile *p = RZ_NEW0(RzRunProfile);
if (p) {
rz_run_reset(p);
if (str) {
rz_run_parsefile(p, str);
}
}
return p;
}
RZ_API void rz_run_reset(RzRunProfile *p) {
rz_return_if_fail(p);
memset(p, 0, sizeof(RzRunProfile));
p->_aslr = -1;
}
RZ_API bool rz_run_parse(RzRunProfile *pf, const char *profile) {
rz_return_val_if_fail(pf && profile, false);
char *p, *o, *str = rz_str_dup(profile);
if (!str) {
return false;
}
rz_str_replace_char(str, '\r', 0);
p = str;
while (p) {
if ((o = strchr(p, '\n'))) {
*o++ = 0;
}
rz_run_parseline(pf, p);
p = o;
}
free(str);
return true;
}
RZ_API void rz_run_free(RzRunProfile *r) {
if (r) {
free(r->_system);
free(r->_program);
free(r->_runlib);
free(r->_runlib_fcn);
free(r->_stdio);
free(r->_stdin);
free(r->_stdout);
free(r->_stderr);
free(r->_chgdir);
free(r->_chroot);
free(r->_libpath);
free(r->_preload);
free(r->_input);
free(r);
}
}
#if __UNIX__
static void set_limit(int n, int a, ut64 b) {
if (n) {
struct rlimit cl = { b, b };
setrlimit(RLIMIT_CORE, &cl);
} else {
struct rlimit cl = { 0, 0 };
setrlimit(a, &cl);
}
}
#endif
static char *resolve_value(const char *src, size_t *result_len) {
size_t src_len = strlen(src);
size_t copy_len = 0;
if (src_len < 1) {
return NULL;
}
char *copy = NULL;
char delimiter = 0;
if (src[0] == '\'' || src[0] == '"') {
delimiter = src[0];
if (src_len < 2 || src[src_len - 1] != delimiter) {
// the quoted string must have an end
RZ_LOG_ERROR("rz-run: missing `%c` at the end of the value `%s`\n", delimiter, src);
return NULL;
}
copy = rz_str_ndup(src + 1, src_len - 2);
src_len -= 2;
if (delimiter == '\'') {
// pass single-quoted string unaltered
copy_len = src_len;
goto end_resolve;
}
} else {
copy = rz_str_dup(src);
}
copy_len = src_len = rz_str_unescape(copy);
if (src_len < 1) {
goto end_resolve;
}
const char *end = copy + src_len;
switch (*copy) {
case '@': {
char *at_end = NULL;
size_t at_size = strtol(copy + 1, &at_end, 10);
if (!at_size || *at_end != '@') {
RZ_LOG_ERROR("rz-run: invalid @<num>@ in `%s`\n", copy);
free(copy);
return NULL;
}
at_end++;
size_t suffix_len = end - at_end;
if (suffix_len < 1) {
RZ_LOG_ERROR("rz-run: invalid string after @<num>@ in `%s`\n", copy);
free(copy);
return NULL;
}
char *buf = malloc(at_size + 1);
if (buf) {
buf[at_size] = 0;
for (size_t i = 0; i < at_size; i++) {
buf[i] = at_end[i % suffix_len];
}
}
free(copy);
copy = buf;
copy_len = at_size;
break;
}
case '`': {
char *backtick_end = strchr(src + 1, '`');
if (!backtick_end) {
RZ_LOG_ERROR("rz-run: missing `\n");
free(copy);
return NULL;
}
size_t msg_len = backtick_end - (copy + 1);
if (msg_len < 1) {
free(copy);
return rz_str_dup("");
}
*backtick_end = 0;
char *tmp = rz_sys_cmd_str(copy + 1, NULL, NULL);
free(copy);
rz_str_trim_tail(tmp);
copy = tmp;
copy_len = strlen(copy);
break;
}
case '!': {
char *tmp = rz_sys_cmd_str(copy + 1, NULL, NULL);
free(copy);
rz_str_trim_tail(tmp);
copy = tmp;
copy_len = strlen(copy);
break;
}
case ':': {
if (copy[1] != '!') {
src_len--;
memmove(copy, copy + 1, src_len);
copy[src_len] = 0;
} else {
if (copy + 2 >= end) {
RZ_LOG_ERROR("rz-run: missing strin after `!` in `%s`\n", copy);
free(copy);
return NULL;
}
char *tmp = rz_sys_cmd_str(copy + 2, NULL, NULL);
free(copy);
rz_str_trim_tail(tmp);
copy = tmp;
src_len = strlen(copy);
}
if (src_len < 2 || (src_len & 1)) {
RZ_LOG_ERROR("rz-run: invalid hexpair string `%s`\n", copy);
free(copy);
return NULL;
}
ut8 *buf = calloc(1, src_len);
int len = rz_hex_str2bin(copy, buf);
if (len < 1) {
RZ_LOG_ERROR("rz-run: invalid hexpair string `%s`\n", copy);
free(copy);
return NULL;
}
free(copy);
copy = (char *)buf;
copy_len = src_len >> 1;
break;
}
default:
break;
}
end_resolve:
if (result_len) {
*result_len = copy_len;
}
return copy;
}
static bool parse_bool(const char *value) {
return !rz_str_casecmp(value, "yes") ||
!rz_str_casecmp(value, "on") ||
!rz_str_casecmp(value, "true") ||
!rz_str_casecmp(value, "1");
}
#if __APPLE__
#else
#if HAVE_OPENPTY && HAVE_FORKPTY && HAVE_LOGIN_TTY
static void restore_saved_fd(int saved, bool restore, int fd) {
if (saved == -1) {
return;
}
if (restore) {
dup2(saved, fd);
}
close(saved);
}
#endif
static int handle_redirection_proc(const char *cmd, bool in, bool out, bool err) {
#if HAVE_OPENPTY && HAVE_FORKPTY && HAVE_LOGIN_TTY
if (!dyn_forkpty) {
// No forkpty api found, maybe we should fallback to just fork without any pty allocated
return -1;
}
// use PTY to redirect I/O because pipes can be problematic in
// case of interactive programs.
int saved_stdin = dup(STDIN_FILENO);
if (saved_stdin == -1) {
return -1;
}
int saved_stdout = dup(STDOUT_FILENO);
if (saved_stdout == -1) {
close(saved_stdin);
return -1;
}
int fdm, pid = dyn_forkpty(&fdm, NULL, NULL, NULL);
if (pid == -1) {
close(saved_stdin);
close(saved_stdout);
return -1;
}
const char *tn = ttyname(fdm);
if (!tn) {
close(saved_stdin);
close(saved_stdout);
return -1;
}
int fds = open(tn, O_RDWR);
if (fds == -1) {
close(saved_stdin);
close(saved_stdout);
return -1;
}
if (pid == 0) {
close(fdm);
// child process
if (in) {
dup2(fds, STDIN_FILENO);
}
if (out) {
dup2(fds, STDOUT_FILENO);
}
// child - program to run
// necessary because otherwise you can read the same thing you
// wrote on fdm.
struct termios t;
tcgetattr(fds, &t);
cfmakeraw(&t);
tcsetattr(fds, TCSANOW, &t);
int code = rz_sys_system(cmd);
restore_saved_fd(saved_stdin, in, STDIN_FILENO);
restore_saved_fd(saved_stdout, out, STDOUT_FILENO);
exit(code);
} else {
close(fds);
if (in) {
dup2(fdm, STDIN_FILENO);
}
if (out) {
dup2(fdm, STDOUT_FILENO);
}
// parent process
int status;
waitpid(pid, &status, 0);
}
// parent
close(saved_stdin);
close(saved_stdout);
return 0;
#else
#ifdef _MSC_VER
#pragma message("TODO: handle_redirection_proc: Not implemented for this platform")
#else
#warning handle_redirection_proc : unimplemented for this platform
#endif
return -1;
#endif
}
#endif
static int handle_redirection(const char *cmd, bool in, bool out, bool err) {
#if __APPLE__
// XXX handle this in other layer since things changes a little bit
// this seems like a really good place to refactor stuff
return 0;
#else
if (!cmd || !*cmd) {
return 0;
}
if (cmd[0] == '"') {
#if __UNIX__
if (in) {
int pipes[2];
if (rz_sys_pipe(pipes, true) != -1) {
size_t cmdl = strlen(cmd) - 2;
if (write(pipes[1], cmd + 1, cmdl) != cmdl) {
RZ_LOG_ERROR("rz-run: cannot write to the pipe\n");
close(0);
return 1;
}
if (write(pipes[1], "\n", 1) != 1) {
RZ_LOG_ERROR("rz-run: cannot write to the pipe\n");
close(0);
return 1;
}
while ((dup2(pipes[0], STDIN_FILENO) == -1) && (errno == EINTR)) {
}
rz_sys_pipe_close(pipes[0]);
rz_sys_pipe_close(pipes[1]);
} else {
RZ_LOG_ERROR("rz-run: cannot create pipe\n");
}
}
#else
#ifdef _MSC_VER
#pragma message("string redirection handle not yet done")
#else
#warning quoted string redirection handle not yet done
#endif
#endif
} else if (cmd[0] == '!') {
// redirection to a process
return handle_redirection_proc(cmd + 1, in, out, err);
} else {
// redirection to a file
int f, flag = 0, mode = 0;
flag |= in ? O_RDONLY : 0;
flag |= out ? O_WRONLY | O_CREAT : 0;
flag |= err ? O_WRONLY | O_CREAT : 0;
#ifdef __WINDOWS__
mode = _S_IREAD | _S_IWRITE;
#else
mode = S_IRUSR | S_IWUSR;
#endif
f = open(cmd, flag, mode);
if (f < 0) {
RZ_LOG_ERROR("rz-run: cannot open: %s\n", cmd);
return 1;
}
#define DUP(x) \
{ \
close(x); \
dup2(f, x); \
}
if (in) {
DUP(0);
}
if (out) {
DUP(1);
}
if (err) {
DUP(2);
}
close(f);
}
return 0;
#endif
}
RZ_API bool rz_run_parsefile(RzRunProfile *p, const char *b) {
rz_return_val_if_fail(p && b, false);
char *s = rz_file_slurp(b, NULL);
if (s) {
bool ret = rz_run_parse(p, s);
free(s);
return ret;
}
return 0;
}
static bool parse_key_value(const char *input, char **key, char **value) {
char *s_key = NULL, *s_value = NULL;
size_t length = input ? strlen(input) : 0;
if (length < 1 || *input == '#') {
return false;
}
const char *end = input + length;
const char *equal = strchr(input, '=');
if (!equal || equal + 1 >= end) {
return false;
}
s_key = rz_str_ndup(input, equal - input);
if (equal[1] == '$') {
s_value = rz_sys_getenv(equal + 1);
} else {
s_value = rz_str_dup(equal + 1);
}
if (!s_key || !s_value) {
free(s_key);
free(s_value);
return false;
}
*key = s_key;
*value = s_value;
return true;
}
RZ_API bool rz_run_parseline(RzRunProfile *p, const char *b) {
if (RZ_STR_ISEMPTY(b)) {
return true;
} else if (!strcmp(b, "clearenv")) {
rz_sys_clearenv();
return true;
}
char *key = NULL, *value = NULL;
if (!parse_key_value(b, &key, &value)) {
return false;
}
if (!strcmp(key, "program")) {
p->_args[0] = rz_str_dup(value);
p->_program = rz_str_dup(value);
} else if (!strcmp(key, "daemon")) {
p->_daemon = true;
} else if (!strcmp(key, "system")) {
p->_system = rz_str_dup(value);
} else if (!strcmp(key, "runlib")) {
p->_runlib = rz_str_dup(value);
} else if (!strcmp(key, "runlib.fcn")) {
p->_runlib_fcn = rz_str_dup(value);
} else if (!strcmp(key, "aslr")) {
p->_aslr = parse_bool(value);
} else if (!strcmp(key, "pid")) {
p->_pid = parse_bool(value);
} else if (!strcmp(key, "pidfile")) {
p->_pidfile = rz_str_dup(value);
} else if (!strcmp(key, "connect")) {
p->_connect = rz_str_dup(value);
} else if (!strcmp(key, "listen")) {
p->_listen = rz_str_dup(value);
} else if (!strcmp(key, "pty")) {
p->_pty = parse_bool(value);
} else if (!strcmp(key, "stdio")) {
if (value[0] == '!') {
p->_stdio = rz_str_dup(value);
} else {
p->_stdout = rz_str_dup(value);
p->_stderr = rz_str_dup(value);
p->_stdin = rz_str_dup(value);
}
} else if (!strcmp(key, "stdout")) {
p->_stdout = rz_str_dup(value);
} else if (!strcmp(key, "stdin")) {
p->_stdin = rz_str_dup(value);
} else if (!strcmp(key, "stderr")) {
p->_stderr = rz_str_dup(value);
} else if (!strcmp(key, "input")) {
p->_input = rz_str_dup(value);
} else if (!strcmp(key, "chdir")) {
p->_chgdir = rz_str_dup(value);
} else if (!strcmp(key, "core")) {
p->_docore = parse_bool(value);
} else if (!strcmp(key, "fork")) {
p->_dofork = parse_bool(value);
} else if (!strcmp(key, "sleep")) {
p->_rzsleep = atoi(value);
} else if (!strcmp(key, "maxstack")) {
p->_maxstack = atoi(value);
} else if (!strcmp(key, "maxproc")) {
p->_maxproc = atoi(value);
} else if (!strcmp(key, "maxfd")) {
p->_maxfd = atoi(value);
} else if (!strcmp(key, "bits")) {
p->_bits = atoi(value);
} else if (!strcmp(key, "chroot")) {
p->_chroot = rz_str_dup(value);
} else if (!strcmp(key, "libpath")) {
p->_libpath = rz_str_dup(value);
} else if (!strcmp(key, "preload")) {
p->_preload = rz_str_dup(value);
} else if (!strcmp(key, "rzpreload")) {
p->_rzpreload = parse_bool(value);
} else if (!strcmp(key, "setuid")) {
p->_setuid = rz_str_dup(value);
} else if (!strcmp(key, "seteuid")) {
p->_seteuid = rz_str_dup(value);
} else if (!strcmp(key, "setgid")) {
p->_setgid = rz_str_dup(value);
} else if (!strcmp(key, "setegid")) {
p->_setegid = rz_str_dup(value);
} else if (!strcmp(key, "nice")) {
p->_nice = atoi(value);
} else if (!strcmp(key, "timeout")) {
p->_timeout = atoi(value);
} else if (!strcmp(key, "timeoutsig")) {
p->_timeout_sig = rz_signal_from_string(value);
} else if (!memcmp(b, "arg", 3)) {
int n = atoi(b + 3);
if (n >= 0 && n < RZ_RUN_PROFILE_NARGS) {
p->_args[n] = resolve_value(value, NULL);
p->_argc++;
} else {
RZ_LOG_ERROR("rz-run: out of bounds args index: %d\n", n);
}
} else if (!strcmp(key, "envfile")) {
char *p, buf[1024];
size_t len;
FILE *fd = rz_sys_fopen(value, "r");
if (!fd) {
RZ_LOG_ERROR("rz-run: cannot open '%s'\n", value);
free(key);
free(value);
return false;
}
for (;;) {
if (!fgets(buf, sizeof(buf), fd)) {
break;
}
if (feof(fd)) {
break;
}
p = strchr(buf, '=');
if (p) {
*p++ = 0;
len = strlen(p);
if (len > 0 && p[len - 1] == '\n') {
p[len - 1] = 0;
}
if (len > 1 && p[len - 2] == '\r') {
p[len - 2] = 0;
}
rz_sys_setenv(buf, p);
}
}
fclose(fd);
} else if (!strcmp(key, "unsetenv")) {
rz_sys_setenv(value, NULL);
} else if (!strcmp(key, "setenv")) {
char *key2 = NULL, *value2 = NULL;
if (!parse_key_value(value, &key2, &value2)) {
free(key);
free(value);
return false;
}
char *V = resolve_value(value2, NULL);
if (V) {
rz_sys_setenv(key2, V);
free(V);
}
free(key2);
free(value2);
}
free(key);
free(value);
return true;
}
#if HAVE_OPENPTY && HAVE_FORKPTY && HAVE_LOGIN_TTY
static int fd_forward(int in_fd, int out_fd, char **buff) {
int size = 0;
if (ioctl(in_fd, FIONREAD, &size) == -1) {
perror("ioctl");
return -1;
}
if (!size) { // child process exited or socket is closed
return -1;
}
char *new_buff = realloc(*buff, size);
if (!new_buff) {
RZ_LOG_ERROR("rz-run: Failed to allocate buffer for redirection");
return -1;
}
*buff = new_buff;
if (read(in_fd, *buff, size) != size) {
perror("read");
return -1;
}
if (write(out_fd, *buff, size) != size) {
perror("write");
return -1;
}
return 0;
}
#endif
static int redirect_socket_to_stdio(RzSocket *sock) {
close(0);
close(1);
close(2);
dup2(sock->fd, 0);
dup2(sock->fd, 1);
dup2(sock->fd, 2);
return 0;
}
#if __WINDOWS__
static void *exit_process(void *user) {
int timeout = (int)(void *)user;
rz_sys_sleep(timeout);
RZ_LOG_DEBUG("rz_run: Interrupted by timeout\n");
exit(0);
return NULL;
}
#endif
static int redirect_socket_to_pty(RzSocket *sock) {
#if HAVE_OPENPTY && HAVE_FORKPTY && HAVE_LOGIN_TTY
// directly duplicating the fds using dup2() creates problems
// in case of interactive applications
int fdm, fds;
if (!dyn_openpty || (dyn_openpty && dyn_openpty(&fdm, &fds, NULL, NULL, NULL) == -1)) {
perror("opening pty");
return -1;
}
pid_t child_pid = rz_sys_fork();
if (child_pid == -1) {
RZ_LOG_ERROR("rz-run: cannot fork\n");
close(fdm);
close(fds);
return -1;
}
if (child_pid == 0) {
// child process
close(fds);
char *buff = NULL;
int sockfd = sock->fd;
int max_fd = fdm > sockfd ? fdm : sockfd;
while (true) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fdm, &readfds);
FD_SET(sockfd, &readfds);
if (select(max_fd + 1, &readfds, NULL, NULL, NULL) == -1) {
perror("select error");
break;
}
if (FD_ISSET(fdm, &readfds)) {
if (fd_forward(fdm, sockfd, &buff) != 0) {
break;
}
}
if (FD_ISSET(sockfd, &readfds)) {
if (fd_forward(sockfd, fdm, &buff) != 0) {
break;
}
}
}
free(buff);
close(fdm);
rz_socket_free(sock);
exit(0);
}
// parent
rz_socket_close_fd(sock);
if (dyn_login_tty) {
dyn_login_tty(fds);
}
close(fdm);
// disable the echo on slave stdin
struct termios t;
tcgetattr(0, &t);
cfmakeraw(&t);
tcsetattr(0, TCSANOW, &t);
return 0;
#else
// Fallback to socket to I/O redirection
return redirect_socket_to_stdio(sock);
#endif
}
RZ_API int rz_run_config_env(RzRunProfile *p) {
int ret;
#if HAVE_OPENPTY && HAVE_FORKPTY && HAVE_LOGIN_TTY
dyn_init();
#endif
if (!p->_program && !p->_system && !p->_runlib) {
RZ_LOG_ERROR("rz-run: No program, system or runlib rule defined\n");
return 1;
}
// when IO is redirected to a process, handle them together
if (handle_redirection(p->_stdio, true, true, false) != 0) {
return 1;
}
if (handle_redirection(p->_stdin, true, false, false) != 0) {
return 1;
}
if (handle_redirection(p->_stdout, false, true, false) != 0) {
return 1;
}
if (handle_redirection(p->_stderr, false, false, true) != 0) {
return 1;
}
if (p->_aslr != -1) {
rz_sys_aslr(p->_aslr);
}
#if __UNIX__
set_limit(p->_docore, RLIMIT_CORE, RLIM_INFINITY);
if (p->_maxfd) {
set_limit(p->_maxfd, RLIMIT_NOFILE, p->_maxfd);
}
#ifdef RLIMIT_NPROC
if (p->_maxproc) {
set_limit(p->_maxproc, RLIMIT_NPROC, p->_maxproc);
}
#endif
if (p->_maxstack) {
set_limit(p->_maxstack, RLIMIT_STACK, p->_maxstack);
}
#else
if (p->_docore || p->_maxfd || p->_maxproc || p->_maxstack)
RZ_LOG_WARN("rz-run: setrlimits not supported for this platform\n");
#endif
if (p->_connect) {
char *q = strchr(p->_connect, ':');
if (q) {
RzSocket *fd = rz_socket_new(0);
*q = 0;
if (!rz_socket_connect_tcp(fd, p->_connect, q + 1, 30)) {
RZ_LOG_ERROR("rz-run: cannot connect\n");
return 1;
}
if (p->_pty) {
if (redirect_socket_to_pty(fd) != 0) {
RZ_LOG_ERROR("rz-run: socket redirection failed\n");
rz_socket_free(fd);
return 1;
}
} else {
redirect_socket_to_stdio(fd);
}
} else {
RZ_LOG_ERROR("rz-run: Invalid format for connect. missing ':'\n");
return 1;
}
}
if (p->_listen) {
RzSocket *child, *fd = rz_socket_new(0);
bool is_child = false;
if (!rz_socket_listen(fd, p->_listen, NULL)) {
RZ_LOG_ERROR("rz-run: cannot listen\n");
rz_socket_free(fd);
return 1;
}
while (true) {
child = rz_socket_accept(fd);
if (child) {
is_child = true;
if (p->_dofork && !p->_dodebug) {
pid_t child_pid = rz_sys_fork();
if (child_pid == -1) {
RZ_LOG_ERROR("rz-run: cannot fork\n");
rz_socket_free(child);
rz_socket_free(fd);
return 1;
} else if (child_pid != 0) {
// parent code
is_child = false;
}
}
if (is_child) {
rz_socket_close_fd(fd);
RZ_LOG_ERROR("rz-run: connected\n");
if (p->_pty) {
if (redirect_socket_to_pty(child) != 0) {
RZ_LOG_ERROR("rz-run: socket redirection failed\n");
rz_socket_free(child);
rz_socket_free(fd);
return 1;
}
} else {
redirect_socket_to_stdio(child);
}
break;
} else {
rz_socket_close_fd(child);
}
}
}
if (!is_child) {
rz_socket_free(child);
}
rz_socket_free(fd);
}
if (p->_rzsleep != 0) {
rz_sys_sleep(p->_rzsleep);
}
#if __UNIX__
if (p->_chroot) {
if (chdir(p->_chroot) == -1) {
RZ_LOG_ERROR("rz-run: cannot chdir to chroot in %s\n", p->_chroot);
return 1;
} else {
if (chroot(".") == -1) {
RZ_LOG_ERROR("rz-run: cannot chroot to %s\n", p->_chroot);
return 1;
} else {
// Silenting pedantic meson flags...
if (chdir("/") == -1) {
RZ_LOG_ERROR("rz-run: cannot chdir to /\n");
return 1;
}
if (p->_chgdir) {
if (chdir(p->_chgdir) == -1) {
RZ_LOG_ERROR("rz-run: cannot chdir after chroot to %s\n", p->_chgdir);
return 1;
}
}
}
}
} else if (p->_chgdir) {
if (chdir(p->_chgdir) == -1) {
RZ_LOG_ERROR("rz-run: cannot chdir after chroot to %s\n", p->_chgdir);
return 1;
}
}
#else
if (p->_chgdir) {
ret = chdir(p->_chgdir);
if (ret < 0) {
return 1;
}
}
if (p->_chroot) {
ret = chdir(p->_chroot);
if (ret < 0) {
return 1;
}
}
#endif
#if __UNIX__
if (p->_setuid) {
ret = setgroups(0, NULL);
if (ret < 0) {
return 1;
}
ret = setuid(atoi(p->_setuid));
if (ret < 0) {
return 1;
}
}
if (p->_seteuid) {
ret = seteuid(atoi(p->_seteuid));
if (ret < 0) {
return 1;
}
}
if (p->_setgid) {
ret = setgid(atoi(p->_setgid));
if (ret < 0) {
return 1;
}
}
if (p->_input) {
int f2[2];
if (rz_sys_pipe(f2, true) != -1) {
close(0);
dup2(f2[0], 0);
} else {
RZ_LOG_ERROR("rz-run: cannot create pipe\n");
return 1;
}
size_t inpl = 0;
char *inp = resolve_value(p->_input, &inpl);
if (inp) {
if (write(f2[1], inp, inpl) != inpl) {
RZ_LOG_ERROR("rz-run: cannot write to the pipe\n");
}
rz_sys_pipe_close(f2[1]);
free(inp);
} else {
RZ_LOG_ERROR("rz-run: Invalid input\n");
}
}
#endif
if (p->_rzpreload) {
if (p->_preload) {
RZ_LOG_WARN("rz-run: only one library can be opened at a time\n");
free(p->_preload);
}
RzPath *sys_path = rz_path_new();
char *libdir = rz_path_libdir(sys_path);
rz_path_free(sys_path);
p->_preload = rz_file_path_join(libdir, "librz." RZ_LIB_EXT);
free(libdir);
}
if (p->_libpath) {
#if __WINDOWS__
RZ_LOG_ERROR("rz-run: libpath unsupported for this platform\n");
#elif __HAIKU__
char *orig = rz_sys_getenv("LIBRARY_PATH");
char *newlib = rz_str_newf("%s:%s", p->_libpath, orig);
rz_sys_setenv("LIBRARY_PATH", newlib);
free(newlib);
free(orig);
#elif __APPLE__
rz_sys_setenv("DYLD_LIBRARY_PATH", p->_libpath);
#else
rz_sys_setenv("LD_LIBRARY_PATH", p->_libpath);
#endif
}
if (p->_preload) {
#if __APPLE__
// 10.6
#ifndef __MAC_10_7
rz_sys_setenv("DYLD_PRELOAD", p->_preload);
#endif
rz_sys_setenv("DYLD_INSERT_LIBRARIES", p->_preload);
// 10.8
rz_sys_setenv("DYLD_FORCE_FLAT_NAMESPACE", "1");
#else
rz_sys_setenv("LD_PRELOAD", p->_preload);
#endif
}
if (p->_timeout) {
#if __UNIX__
int mypid = getpid();
if (!rz_sys_fork()) {
int use_signal = p->_timeout_sig;
if (use_signal < 1) {
use_signal = SIGKILL;
}
sleep(p->_timeout);
if (!kill(mypid, 0)) {
// eprintf ("\nrz_run: Interrupted by timeout\n");
}
kill(mypid, use_signal);
exit(0);
}
#else
if (p->_timeout_sig < 1 || p->_timeout_sig == 9) {
rz_th_new(exit_process, (void *)p->_timeout);
} else {
RZ_LOG_ERROR("rz-run: timeout with signal not supported for this platform\n");
}
#endif
}
return 0;
}
// NOTE: return value is like in unix return code (0 = ok, 1 = not ok)
RZ_API int rz_run_start(RzRunProfile *p) {
if (p->_execve) {
exit(rz_sys_execv(p->_program, (char *const *)p->_args));
}
#if __APPLE__ && HAVE_FORK
posix_spawnattr_t attr = { 0 };
pid_t pid = -1;
int ret;
posix_spawnattr_init(&attr);
if (p->_args[0]) {
char **envp = rz_sys_get_environ();
short spflags = POSIX_SPAWN_SETEXEC;
// https://opensource.apple.com/source/gdb/gdb-2831/src/gdb/macosx/macosx-nat-inferior.c.auto.html
if (p->_aslr != -1 && p->_aslr) {
#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
spflags |= _POSIX_SPAWN_DISABLE_ASLR;
#undef _POSIX_SPAWN_DISABLE_ASLR
}
(void)posix_spawnattr_setflags(&attr, spflags);
if (p->_bits) {
size_t copied = 1;
cpu_type_t cpu;
#if __i386__ || __x86_64__
cpu = CPU_TYPE_I386;
if (p->_bits == 64) {
cpu |= CPU_ARCH_ABI64;
}
#else
cpu = CPU_TYPE_ANY;
#endif
posix_spawnattr_setbinpref_np(
&attr, 1, &cpu, &copied);
}
ret = posix_spawnp(&pid, p->_args[0], NULL, &attr, p->_args, envp);
if (ret) {
RZ_LOG_ERROR("rz-run: posix_spawnp: %s\n", strerror(ret));
}
exit(ret);
}
#endif
if (p->_system) {
if (p->_pid) {
RZ_LOG_ERROR("rz-run: PID: Cannot determine pid with 'system' directive. Use 'program'.\n");
}
if (p->_daemon) {
#if __WINDOWS__
// eprintf ("PID: Cannot determine pid with 'system' directive. Use 'program'.\n");
#else
pid_t child = rz_sys_fork();
if (child == -1) {
perror("fork");
exit(1);
}
if (child) {
if (p->_pidfile) {
char pidstr[32];
snprintf(pidstr, sizeof(pidstr), "%d\n", child);
rz_file_dump(p->_pidfile,
(const ut8 *)pidstr,
strlen(pidstr), 0);
}
exit(0);
}
setsid();
if (p->_timeout) {
#if __UNIX__
int mypid = getpid();
if (!rz_sys_fork()) {
int use_signal = p->_timeout_sig;
if (use_signal < 1) {
use_signal = SIGKILL;
}
sleep(p->_timeout);
if (!kill(mypid, 0)) {
// eprintf ("\nrz_run: Interrupted by timeout\n");
}
kill(mypid, use_signal);
exit(0);
}
#else
RZ_LOG_ERROR("rz-run: timeout not supported for this platform\n");
#endif
}
#endif
#if __UNIX__
close(0);
close(1);
char *bin_sh = rz_file_binsh();
int ret = rz_sys_execl(bin_sh, "sh", "-c", p->_system, NULL);
free(bin_sh);
exit(ret);
#else
exit(rz_sys_system(p->_system));
#endif
} else {
if (p->_pidfile) {
RZ_LOG_WARN("pidfile doesnt work with 'system'.\n");
}
exit(rz_sys_system(p->_system));
}
}
if (p->_program) {
if (!rz_file_exists(p->_program)) {
char *progpath = rz_file_path(p->_program);
if (progpath && *progpath) {
free(p->_program);
p->_program = progpath;
} else {
free(progpath);
RZ_LOG_ERROR("rz-run: %s: file not found\n", p->_program);
return 1;
}
}
#if __UNIX__
// XXX HACK close all non-tty fds
{
int i;
for (i = 3; i < 1024; i++) {
close(i);
}
}
// TODO: use posix_spawn
if (p->_setgid) {
int ret = setgid(atoi(p->_setgid));
if (ret < 0) {
return 1;
}
}
if (p->_pid) {
RZ_LOG_WARN("rz-run: PID: %d\n", getpid());
}
if (p->_pidfile) {
char pidstr[32];
snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
rz_file_dump(p->_pidfile,
(const ut8 *)pidstr,
strlen(pidstr), 0);
}
#endif
if (p->_nice) {
#if HAVE_NICE
if (nice(p->_nice) == -1) {
return 1;
}
#else
RZ_LOG_ERROR("rz-run: nice not supported for this platform\n");
#endif
}
if (p->_daemon) {
#if __WINDOWS__
RZ_LOG_ERROR("rz-run: PID: Cannot determine pid with 'system' directive. Use 'program'.\n");
#else
pid_t child = rz_sys_fork();
if (child == -1) {
perror("fork");
exit(1);
}
if (child) {
if (p->_pidfile) {
char pidstr[32];
snprintf(pidstr, sizeof(pidstr), "%d\n", child);
rz_file_dump(p->_pidfile,
(const ut8 *)pidstr,
strlen(pidstr), 0);
exit(0);
}
}
setsid();
exit(rz_sys_execv(p->_program, (char *const *)p->_args));
#endif
}
exit(rz_sys_execv(p->_program, (char *const *)p->_args));
}
if (p->_runlib) {
if (!p->_runlib_fcn) {
RZ_LOG_ERROR("rz-run: no function specified. Please set runlib.fcn\n");
return 1;
}
void *addr = rz_sys_dlopen(p->_runlib);
if (!addr) {
RZ_LOG_ERROR("rz-run: could not load the library '%s'\n", p->_runlib);
return 1;
}
void (*fcn)(void) = rz_sys_dlsym(addr, p->_runlib_fcn);
if (!fcn) {
RZ_LOG_ERROR("rz-run: could not find the function '%s'\n", p->_runlib_fcn);
return 1;
}
switch (p->_argc) {
case 0:
fcn();
break;
case 1:
rz_run_call1(fcn, p->_args[1]);
break;
case 2:
rz_run_call2(fcn, p->_args[1], p->_args[2]);
break;
case 3:
rz_run_call3(fcn, p->_args[1], p->_args[2], p->_args[3]);
break;
case 4:
rz_run_call4(fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4]);
break;
case 5:
rz_run_call5(fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4],
p->_args[5]);
break;
case 6:
rz_run_call6(fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4],
p->_args[5], p->_args[6]);
break;
case 7:
rz_run_call7(fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4],
p->_args[5], p->_args[6], p->_args[7]);
break;
case 8:
rz_run_call8(fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4],
p->_args[5], p->_args[6], p->_args[7], p->_args[8]);
break;
case 9:
rz_run_call9(fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4],
p->_args[5], p->_args[6], p->_args[7], p->_args[8], p->_args[9]);
break;
case 10:
rz_run_call10(fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4],
p->_args[5], p->_args[6], p->_args[7], p->_args[8], p->_args[9], p->_args[10]);
break;
default:
RZ_LOG_ERROR("rz-run: too many arguments.\n");
return 1;
}
rz_sys_dlclose(addr);
}
return 0;
}
RZ_API char *rz_run_get_environ_profile(char **env) {
if (!env) {
return NULL;
}
RzStrBuf *sb = rz_strbuf_new(NULL);
while (*env) {
char *k = rz_str_dup(*env);
char *v = strchr(k, '=');
if (v) {
*v++ = 0;
RzStrEscOptions opt = { 0 };
opt.show_asciidot = false;
opt.esc_bslash = true;
opt.keep_printable = true;
v = rz_str_escape_8bit(v, true, &opt);
if (v) {
rz_strbuf_appendf(sb, "setenv=%s='%s'\n", k, v);
free(v);
}
}
free(k);
env++;
}
return rz_strbuf_drain(sb);
}