static FILE *my_popen (const char *command, const char *mode); static FILE *make_pipe (int pipe_local, int pipe_remote, int handle, const char *command, const char *mode); static int do_spawnv (const char *command); static void restore (int org_handle, int org_private, int handle); /* The following is a reimplementation of 'popen()' that * works around problems the original kLIBC version has * in finding a command shell that it can use to invoke * 'spawnlp()'. Unless the user happens to have 'sh.exe' * in '/@unixroot/bin', spawnlp() will fail. * * Since the shell adds no value other than parsing the * commandline arguments into an array of character pointers, * it resolves the issue by eliminating the shell, parsing * the commandline itself, then invoking spawnv() to start * the new process directly. * * Portions of this code were taken from the kLIBC version * of 'popen()' (Copyright (c) 1992-1996 by Eberhard Mattes). * * Rich Walsh 2018-03-05 */ /* this is a modified version of the popen() found in popen.c */ static FILE* my_popen (const char *command, const char *mode) { int ph[2]; FILE *stream; int saved_errno; if (mode[0] != 'r' && mode[0] != 'w') { errno = EINVAL; return NULL; } if (pipe (ph) == -1 || fcntl (ph[0], F_SETFD, FD_CLOEXEC) == -1 || fcntl (ph[1], F_SETFD, FD_CLOEXEC) == -1) return NULL; if (mode[0] == 'r') stream = make_pipe (ph[0], ph[1], STDOUT_FILENO, command, mode); else stream = make_pipe (ph[1], ph[0], STDIN_FILENO, command, mode); if (stream == NULL) { saved_errno = errno; close (ph[0]); close (ph[1]); errno = saved_errno; debug_msg("make_pipe failed - errno= %d\n", errno); } return stream; } /* this is a modified version of the make_pipe() found in popen.c */ static FILE* make_pipe (int pipe_local, int pipe_remote, int handle, const char *command, const char *mode) { int i, org_handle, org_private; FILE *f; fcntl (pipe_local, F_SETFD, FD_CLOEXEC); org_private = fcntl (handle, F_GETFD, 0); if (org_private == -1) return NULL; org_handle = dup (handle); if (org_handle == -1) return NULL; fcntl (org_handle, F_SETFD, FD_CLOEXEC); i = dup2 (pipe_remote, handle); if (i != handle) { restore (org_handle, org_private, handle); errno = EBADF; return NULL; } if (close (pipe_remote) == -1) { restore (org_handle, org_private, handle); return NULL; } f = fdopen (pipe_local, mode); if (f == NULL) { restore (org_handle, org_private, handle); return NULL; } i = do_spawnv (command); if (i == -1) { perror("spawnv"); fclose (f); f = NULL; debug_msg ("spawnv failed\n"); } else f->_pid = i; restore (org_handle, org_private, handle); return f; } /* this is a newly-added function which parses the commandline * passed to my_popen(), then invokes spawnv() to start the * child process directly without using a shell. */ static int do_spawnv (const char *command) { #define MAX_ARGS 24 int ctr; char *ptr; char *argv[MAX_ARGS]; char cmd[512]; errno = 0; if (strlen (command) >= sizeof(cmd)) { errno = E2BIG; return -1; } strcpy (cmd, command); ctr = 0; ptr = cmd; while (*ptr == ' ') ptr++; argv[ctr++] = ptr; for (ptr = strchr (ptr, ' '); ctr < MAX_ARGS && ptr; ctr++, ptr = strchr (ptr, ' ')) { *ptr++ = 0; while (*ptr == ' ') ptr++; if (!*ptr) break; if (*ptr != '\"') { argv[ctr] = ptr; continue; } argv[ctr] = ++ptr; ptr = strchr (ptr, '\"'); if (!ptr) { errno = EINVAL; return -1; } *ptr++ = 0; } if (ctr >= MAX_ARGS) { errno = E2BIG; return -1; } argv[ctr] = 0; return spawnv (P_NOWAIT, argv[0], argv); } /* taken from popen.c as-is*/ static void restore (int org_handle, int org_private, int handle) { int saved_errno; saved_errno = errno; dup2 (org_handle, handle); close (org_handle); fcntl (handle, F_SETFD, org_private); errno = saved_errno; }