summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElrond <elrond@samba.org>2000-03-31 18:13:56 +0000
committerElrond <elrond@samba.org>2000-03-31 18:13:56 +0000
commit54c1d574a88cafbc029cf5aa5ebbd6f27054724c (patch)
tree65c3baaf93fbe8ae2d8795713244a977b4a1f1b1
parent29bfe52a01dacd1c1400724d4f688d1f951cd10e (diff)
downloadsamba-54c1d574a88cafbc029cf5aa5ebbd6f27054724c.tar.gz
merged sys_popen(), sys_pclose() from HEAD.
haven't run make proto, we don't need them currently, so any future make proto will catch them.
-rw-r--r--source/lib/system.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/source/lib/system.c b/source/lib/system.c
index 802a5a89127..4d968f0e986 100644
--- a/source/lib/system.c
+++ b/source/lib/system.c
@@ -635,3 +635,265 @@ struct passwd *sys_getpwuid(uid_t uid)
{
return copy_passwd_struct(getpwuid(uid));
}
+
+/**************************************************************************
+ Extract a command into an arg list. Uses a static pstring for storage.
+ Caller frees returned arg list (which contains pointers into the static pstring).
+****************************************************************************/
+
+static char **extract_args(const char *command)
+{
+ static pstring trunc_cmd;
+ char *ptr;
+ int argcl;
+ char **argl = NULL;
+ int i;
+
+ pstrcpy(trunc_cmd, command);
+
+ if(!(ptr = strtok(trunc_cmd, " \t"))) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * Count the args.
+ */
+
+ for( argcl = 1; ptr; ptr = strtok(NULL, " \t"))
+ argcl++;
+
+ if((argl = (char **)malloc((argcl + 1) * sizeof(char *))) == NULL)
+ return NULL;
+
+ /*
+ * Now do the extraction.
+ */
+
+ pstrcpy(trunc_cmd, command);
+
+ ptr = strtok(trunc_cmd, " \t");
+ i = 0;
+ argl[i++] = ptr;
+
+ while((ptr = strtok(NULL, " \t")) != NULL)
+ argl[i++] = ptr;
+
+ argl[i++] = NULL;
+ return argl;
+}
+
+/**************************************************************************
+ Wrapper for popen. Safer as it doesn't search a path.
+ Modified from the glibc sources.
+****************************************************************************/
+
+typedef struct _popen_list
+{
+ FILE *fp;
+ pid_t child_pid;
+ struct _popen_list *next;
+} popen_list;
+
+static popen_list *popen_chain;
+
+FILE *sys_popen(const char *command, const char *mode, BOOL paranoid)
+{
+ int parent_end, child_end;
+ int pipe_fds[2];
+ popen_list *entry = NULL;
+ char **argl = NULL;
+
+ if (pipe(pipe_fds) < 0)
+ return NULL;
+
+ if (mode[0] == 'r' && mode[1] == '\0') {
+ parent_end = pipe_fds[0];
+ child_end = pipe_fds[1];
+ } else if (mode[0] == 'w' && mode[1] == '\0') {
+ parent_end = pipe_fds[1];
+ child_end = pipe_fds[0];
+ } else {
+ errno = EINVAL;
+ goto err_exit;
+ }
+
+ if (!*command) {
+ errno = EINVAL;
+ goto err_exit;
+ }
+
+ if((entry = (popen_list *)malloc(sizeof(popen_list))) == NULL)
+ goto err_exit;
+
+ /*
+ * Extract the command and args into a NULL terminated array.
+ */
+
+ if(!(argl = extract_args(command)))
+ goto err_exit;
+
+ if(paranoid) {
+ /*
+ * Do some basic paranioa checks. Do a stat on the parent
+ * directory and ensure it's not world writable. Do a stat
+ * on the file itself and ensure it's owned by root and not
+ * world writable. Note this does *not* prevent symlink races,
+ * but is a generic "don't let the admin screw themselves"
+ * check.
+ */
+
+ SMB_STRUCT_STAT st;
+ pstring dir_name;
+ char *ptr = strrchr(argl[0], '/');
+
+ if(sys_stat(argl[0], &st) != 0)
+ goto err_exit;
+
+ if((st.st_uid != (uid_t)0) || (st.st_mode & S_IWOTH)) {
+ errno = EACCES;
+ goto err_exit;
+ }
+
+ if(!ptr) {
+ /*
+ * No '/' in name - use current directory.
+ */
+ pstrcpy(dir_name, ".");
+ } else {
+
+ /*
+ * Copy into a pstring and do the checks
+ * again (in case we were length tuncated).
+ */
+
+ pstrcpy(dir_name, argl[0]);
+ ptr = strrchr(dir_name, '/');
+ if(!ptr) {
+ errno = EINVAL;
+ goto err_exit;
+ }
+ if(strcmp(dir_name, "/") != 0)
+ *ptr = '\0';
+ if(!dir_name[0])
+ pstrcpy(dir_name, ".");
+ }
+
+ if(sys_stat(argl[0], &st) != 0)
+ goto err_exit;
+
+ if(!S_ISDIR(st.st_mode) || (st.st_mode & S_IWOTH)) {
+ errno = EACCES;
+ goto err_exit;
+ }
+ }
+
+ entry->child_pid = fork();
+
+ if (entry->child_pid == -1) {
+
+ /*
+ * Error !
+ */
+
+ goto err_exit;
+ }
+
+ if (entry->child_pid == 0) {
+
+ /*
+ * Child !
+ */
+
+ int child_std_end = (mode[0] == 'r') ? STDOUT_FILENO : STDIN_FILENO;
+ popen_list *p;
+
+ close(parent_end);
+ if (child_end != child_std_end) {
+ dup2 (child_end, child_std_end);
+ close (child_end);
+ }
+
+ /*
+ * POSIX.2: "popen() shall ensure that any streams from previous
+ * popen() calls that remain open in the parent process are closed
+ * in the new child process."
+ */
+
+ for (p = popen_chain; p; p = p->next)
+ close(fileno(p->fp));
+
+ execv(argl[0], argl);
+ _exit (127);
+ }
+
+ /*
+ * Parent.
+ */
+
+ close (child_end);
+ free((char *)argl);
+
+ /*
+ * Create the FILE * representing this fd.
+ */
+ entry->fp = fdopen(parent_end, mode);
+
+ /* Link into popen_chain. */
+ entry->next = popen_chain;
+ popen_chain = entry;
+
+ return entry->fp;
+
+err_exit:
+
+ if(entry)
+ free((char *)entry);
+ if(argl)
+ free((char *)argl);
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return NULL;
+}
+
+/**************************************************************************
+ Wrapper for pclose. Modified from the glibc sources.
+****************************************************************************/
+
+int sys_pclose( FILE *fp)
+{
+ int wstatus;
+ popen_list **ptr = &popen_chain;
+ popen_list *entry = NULL;
+ pid_t wait_pid;
+ int status = -1;
+
+ /* Unlink from popen_chain. */
+ for ( ; *ptr != NULL; ptr = &(*ptr)->next) {
+ if ((*ptr)->fp == fp) {
+ entry = *ptr;
+ *ptr = (*ptr)->next;
+ status = 0;
+ break;
+ }
+ }
+
+ if (status < 0 || close(fileno(entry->fp)) < 0)
+ return -1;
+
+ /*
+ * As Samba is catching and eating child process
+ * exits we don't really care about the child exit
+ * code, a -1 with errno = ECHILD will do fine for us.
+ */
+
+ do {
+ wait_pid = sys_waitpid (entry->child_pid, &wstatus, 0);
+ } while (wait_pid == -1 && errno == EINTR);
+
+ free((char *)entry);
+
+ if (wait_pid == -1)
+ return -1;
+ return wstatus;
+}