/* Small library of useful utilities based on the dietlib by fefe Copyright (C) 2003, 2008 Andreas Franz Borchert -------------------------------------------------------------------- This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* =head1 NAME pconnect -- create a pipeline to a given command =head1 SYNOPSIS #include enum {PIPE_READ = 0, PIPE_WRITE = 1}; typedef struct pipe_end { int fd; pid_t pid; int wstat; } pipe_end; int pconnect(const char* path, char* const* argv, int mode, pipe_end* pipe_con); int pconnect2(const char* path, char* const* argv, int mode, int fd, pipe_end* pipe_con); int phangup(pipe_end* pipe_end); =head1 DESCRIPTION I provides a safe alternative to I by avoiding the invocation of a shell to interpret a command line. Instead of a command string, a path for the command and an argument list is passed to I that are passed directly to L in the forked-off process. The I which has to be either I or I selects the end of the pipe that is left to the invoking process. Pipelines created by I are to be closed by I which waits for the forked-off process to terminate and returns its status in the I field. I works similar to I but connects in the spawned off process I to the remaining standard input or output file descriptor not connected to the pipeline. This allows pipelines to be chained. =head1 DIAGNOSTICS All functions return 1 on success and 0 otherwise. =head1 EXAMPLE The following example retrieves the output of ``ps -fu login'' where F is some given login name: char ps_path[] = "/usr/bin/ps"; strlist argv = {0}; strlist_push(&argv, ps_path); strlist_push(&argv, "-fu"); strlist_push(&argv, login); strlist_push0(&argv); pipe_end pipe; int ok = pconnect(ps_path, argv.list, PIPE_READ, &pipe); strlist_free(&argv); if (!ok) return 0; stralloc ps_output = {0}; ssize_t nbytes; char buf[512]; while ((nbytes = read(pipe.fd, buf, sizeof buf)) > 0) { stralloc_catb(&ps_output, buf, nbytes); } phangup(&pipe); =head1 BUGS The close-on-exec flag is set for all pipe file descriptors left for the invoking process. However, the file descriptors are unfortunately not closed automatically if a forked-off process does not exec() to another image. =head1 AUTHOR Andreas F. Borchert =cut */ #include #include #include #include #include #include /* * create a pipeline to the given command; * mode should be either PIPE_READ or PIPE_WRITE; * return a filled pipe_end structure and 1 on success * and 0 in case of failures */ int pconnect(const char* path, char* const* argv, int mode, pipe_end* pipe_con) { return pconnect2(path, argv, mode, mode, pipe_con); } /* * like pconnect() but connect fd to the standard input * or output file descriptor that is not connected to the pipe */ int pconnect2(const char* path, char* const* argv, int mode, int fd, pipe_end* pipe_con) { int pipefds[2]; if (pipe(pipefds) < 0) return 0; int myside = mode; int otherside = 1 - mode; fflush(0); pid_t child = fork(); if (child < 0) { close(pipefds[0]); close(pipefds[1]); return 0; } if (child == 0) { close(pipefds[myside]); dup2(pipefds[otherside], otherside); close(pipefds[otherside]); if (fd != myside) { dup2(fd, myside); close(fd); } execvp(path, argv); exit(255); } close(pipefds[otherside]); int flags = fcntl(pipefds[myside], F_GETFD); flags |= FD_CLOEXEC; fcntl(pipefds[myside], F_SETFD, flags); pipe_con->pid = child; pipe_con->fd = pipefds[myside]; pipe_con->wstat = 0; return 1; } /* * close pipeline and wait for the forked-off process to exit; * the wait status is returned in wstat (if non-null); * 1 is returned if successful, 0 otherwise */ int phangup(pipe_end* pipe) { if (close(pipe->fd) < 0) return 0; if (waitpid(pipe->pid, &pipe->wstat, 0) < 0) return 0; return 1; }