meillo@0: /* This a snippet I found in sourceforge. I just changed the identing
meillo@29:    style to my own and deleted the main function. -- oku
meillo@29:    The functions destroy_argv() and create_argv() were added by oku.
meillo@29: */
meillo@0: 
meillo@0: #include <errno.h>
meillo@0: #include <stdio.h>
meillo@0: #include <stdlib.h>
meillo@0: #include <unistd.h>
meillo@0: #include <string.h>
meillo@0: #include <sys/types.h>
meillo@15: #include <sysexits.h>
meillo@0: 
meillo@0: #include "peopen.h"
meillo@0: #include "masqmail.h"
meillo@0: 
meillo@10: static void
meillo@10: destroy_argv(char **arr)
meillo@0: {
meillo@10: 	char *p = arr[0];
meillo@10: 	int i = 0;
meillo@0: 
meillo@10: 	while (p) {
meillo@10: 		free(p);
meillo@10: 		p = arr[i++];
meillo@10: 	}
meillo@10: 	free(arr);
meillo@0: }
meillo@0: 
meillo@10: static char**
meillo@10: create_argv(const char *cmd, int count)
meillo@0: {
meillo@10: 	char buf[strlen(cmd) + 1];
meillo@10: 	char **arr, *q;
meillo@10: 	const char *p;
meillo@10: 	int i = 0;
meillo@0: 
meillo@27: 	arr = (char **) g_malloc(sizeof(char *) * count);
meillo@0: 
meillo@10: 	p = cmd;
meillo@10: 	while (*p && i < (count - 1)) {
meillo@10: 		while (*p && isspace(*p))
meillo@10: 			p++;
meillo@10: 		q = buf;
meillo@10: 		while (*p && !isspace(*p))
meillo@10: 			*q++ = *p++;
meillo@15: 		*q = '\0';
meillo@10: 		arr[i++] = strdup(buf);
meillo@10: 		while (*p && isspace(*p))
meillo@10: 			p++;
meillo@10: 	}
meillo@10: 	arr[i] = NULL;
meillo@10: 
meillo@10: 	return arr;
meillo@0: }
meillo@0: 
meillo@10: FILE*
meillo@10: peidopen(const char *command, const char *type, char *const envp[], int *ret_pid, uid_t uid, gid_t gid)
meillo@0: {
meillo@10: 	enum { Read, Write } mode;
meillo@10: 	int pipe_fd[2];
meillo@10: 	pid_t pid;
meillo@0: 
meillo@10: 	if (command == NULL || type == NULL) {
meillo@10: 		errno = EINVAL;
meillo@10: 		return NULL;
meillo@10: 	}
meillo@0: 
meillo@10: 	if (strcmp(type, "r")) {
meillo@10: 		if (strcmp(type, "w")) {
meillo@10: 			errno = EINVAL;
meillo@10: 			return NULL;
meillo@10: 		} else
meillo@10: 			mode = Write;
meillo@10: 	} else
meillo@10: 		mode = Read;
meillo@0: 
meillo@10: 	if (pipe(pipe_fd) == -1)
meillo@10: 		return NULL;
meillo@0: 
meillo@10: 	switch (pid = fork()) {
meillo@10: 	case 0:  /* child thread */
meillo@0: 
meillo@10: 		{
meillo@10: 			int i, max_fd = sysconf(_SC_OPEN_MAX);
meillo@10: 
meillo@10: 			if (max_fd <= 0)
meillo@10: 				max_fd = 64;
meillo@10: 			for (i = 0; i < max_fd; i++)
meillo@10: 				if ((i != pipe_fd[0]) && (i != pipe_fd[1]))
meillo@10: 					close(i);
meillo@10: 		}
meillo@10: 		if (close(pipe_fd[mode == Read ? 0 : 1]) != -1 &&
meillo@10: 			dup2(pipe_fd[mode == Read ? 1 : 0],
meillo@10: 				 mode == Read ? STDOUT_FILENO : STDIN_FILENO) != -1) {
meillo@207: 			/*      char *argv [] = { "/bin/sh", "-c", (char*) command, NULL }; */
meillo@10: 			char **argv = create_argv(command, 10);
meillo@10: 			int ret;
meillo@10: 
meillo@10: 			if (uid != (uid_t) - 1) {
meillo@10: 				if ((ret = seteuid(0)) != 0) {
meillo@10: 					exit(EX_NOPERM);
meillo@10: 				}
meillo@10: 			}
meillo@10: 			if (gid != (gid_t) - 1) {
meillo@10: 				if ((ret = setgid(gid)) != 0) {
meillo@10: 					exit(EX_NOPERM);
meillo@10: 				}
meillo@10: 			}
meillo@10: 			if (uid != (uid_t) - 1) {
meillo@10: 				if ((ret = setuid(uid)) != 0) {
meillo@10: 					exit(EX_NOPERM);
meillo@10: 				}
meillo@10: 			}
meillo@10: 			execve(*argv, argv, envp);
meillo@10: 		}
meillo@10: 
meillo@10: 		_exit(errno);
meillo@10: 
meillo@10: 	default:  /* parent thread */
meillo@10: 		*ret_pid = pid;
meillo@10: 		close(pipe_fd[mode == Read ? 1 : 0]);
meillo@10: 		return fdopen(pipe_fd[mode == Read ? 0 : 1], type);
meillo@10: 
meillo@10: 	case -1:
meillo@10: 		close(pipe_fd[0]);
meillo@10: 		close(pipe_fd[1]);
meillo@10: 		return NULL;
meillo@0: 	}
meillo@0: }
meillo@0: 
meillo@10: FILE*
meillo@10: peopen(const char *command, const char *type, char *const envp[], int *ret_pid)
meillo@0: {
meillo@10: 	return peidopen(command, type, envp, ret_pid, -1, -1);
meillo@0: }