|
Packit Service |
51e54d |
/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
|
|
Packit Service |
51e54d |
* Free Software Foundation, Inc.
|
|
Packit Service |
51e54d |
* Copyright (C) 2003-2017 Colin Watson.
|
|
Packit Service |
51e54d |
* Written for groff by James Clark (jjc@jclark.com)
|
|
Packit Service |
51e54d |
* Heavily adapted and extended for man-db by Colin Watson.
|
|
Packit Service |
51e54d |
*
|
|
Packit Service |
51e54d |
* This file is part of libpipeline.
|
|
Packit Service |
51e54d |
*
|
|
Packit Service |
51e54d |
* libpipeline is free software; you can redistribute it and/or modify
|
|
Packit Service |
51e54d |
* it under the terms of the GNU General Public License as published by
|
|
Packit Service |
51e54d |
* the Free Software Foundation; either version 2 of the License, or (at
|
|
Packit Service |
51e54d |
* your option) any later version.
|
|
Packit Service |
51e54d |
*
|
|
Packit Service |
51e54d |
* libpipeline is distributed in the hope that it will be useful, but
|
|
Packit Service |
51e54d |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
51e54d |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Service |
51e54d |
* General Public License for more details.
|
|
Packit Service |
51e54d |
*
|
|
Packit Service |
51e54d |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
51e54d |
* along with libpipeline; if not, write to the Free Software
|
|
Packit Service |
51e54d |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
|
Packit Service |
51e54d |
* USA.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#ifdef HAVE_CONFIG_H
|
|
Packit Service |
51e54d |
# include "config.h"
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#include <stdio.h>
|
|
Packit Service |
51e54d |
#include <stdlib.h>
|
|
Packit Service |
51e54d |
#include <signal.h>
|
|
Packit Service |
51e54d |
#include <errno.h>
|
|
Packit Service |
51e54d |
#include <sys/select.h>
|
|
Packit Service |
51e54d |
#include <sys/time.h>
|
|
Packit Service |
51e54d |
#include <sys/types.h>
|
|
Packit Service |
51e54d |
#include <fcntl.h>
|
|
Packit Service |
51e54d |
#include <unistd.h>
|
|
Packit Service |
51e54d |
#include <stdarg.h>
|
|
Packit Service |
51e54d |
#include <assert.h>
|
|
Packit Service |
51e54d |
#include <string.h>
|
|
Packit Service |
51e54d |
#include <sys/wait.h>
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#include "dirname.h"
|
|
Packit Service |
51e54d |
#include "full-write.h"
|
|
Packit Service |
51e54d |
#include "safe-read.h"
|
|
Packit Service |
51e54d |
#include "safe-write.h"
|
|
Packit Service |
51e54d |
#include "xalloc.h"
|
|
Packit Service |
51e54d |
#include "xstrndup.h"
|
|
Packit Service |
51e54d |
#include "xvasprintf.h"
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#include "pipeline-private.h"
|
|
Packit Service |
51e54d |
#include "error.h"
|
|
Packit Service |
51e54d |
#include "pipeline.h"
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#ifdef USE_SOCKETPAIR_PIPE
|
|
Packit Service |
51e54d |
# include <netdb.h>
|
|
Packit Service |
51e54d |
# include <netinet/in.h>
|
|
Packit Service |
51e54d |
# include <sys/socket.h>
|
|
Packit Service |
51e54d |
# ifdef CORRECT_SOCKETPAIR_MODE
|
|
Packit Service |
51e54d |
# include <sys/stat.h>
|
|
Packit Service |
51e54d |
# endif
|
|
Packit Service |
51e54d |
# ifndef SHUT_RD
|
|
Packit Service |
51e54d |
# define SHUT_RD 0
|
|
Packit Service |
51e54d |
# endif
|
|
Packit Service |
51e54d |
# ifndef SHUT_WR
|
|
Packit Service |
51e54d |
# define SHUT_WR 1
|
|
Packit Service |
51e54d |
# endif
|
|
Packit Service |
51e54d |
# ifdef CORRECT_SOCKETPAIR_MODE
|
|
Packit Service |
51e54d |
# define pipe(p) (((socketpair(AF_UNIX,SOCK_STREAM,0,p) < 0) || \
|
|
Packit Service |
51e54d |
(shutdown((p)[1],SHUT_RD) < 0) || (fchmod((p)[1],S_IWUSR) < 0) || \
|
|
Packit Service |
51e54d |
(shutdown((p)[0],SHUT_WR) < 0) || (fchmod((p)[0],S_IRUSR) < 0)) ? -1 : 0)
|
|
Packit Service |
51e54d |
# else
|
|
Packit Service |
51e54d |
# define pipe(p) (((socketpair(AF_UNIX,SOCK_STREAM,0,p) < 0) || \
|
|
Packit Service |
51e54d |
(shutdown((p)[1],SHUT_RD) < 0) || (shutdown((p)[0],SHUT_WR) < 0)) ? -1 : 0)
|
|
Packit Service |
51e54d |
# endif
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#if defined(HAVE_SETENV) && !defined(HAVE_CLEARENV)
|
|
Packit Service |
51e54d |
int clearenv (void)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
/* According to:
|
|
Packit Service |
51e54d |
* http://hg.dovecot.org/dovecot-2.0/file/74d9f61e224d/src/lib/env-util.c#l56
|
|
Packit Service |
51e54d |
* simply setting "environ = NULL" crashes some systems. Creating a
|
|
Packit Service |
51e54d |
* new environment consisting of just a terminator is indeed
|
|
Packit Service |
51e54d |
* probably the best we can do.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
environ = XCALLOC (1, char *);
|
|
Packit Service |
51e54d |
return 0;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* ---------------------------------------------------------------------- */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Functions to build individual commands. */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipecmd_new (const char *name)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipecmd *cmd = XMALLOC (pipecmd);
|
|
Packit Service |
51e54d |
struct pipecmd_process *cmdp;
|
|
Packit Service |
51e54d |
char *name_base;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->tag = PIPECMD_PROCESS;
|
|
Packit Service |
51e54d |
cmd->name = xstrdup (name);
|
|
Packit Service |
51e54d |
cmd->nice = 0;
|
|
Packit Service |
51e54d |
cmd->discard_err = 0;
|
|
Packit Service |
51e54d |
cmd->cwd_fd = -1;
|
|
Packit Service |
51e54d |
cmd->cwd = NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->nenv = 0;
|
|
Packit Service |
51e54d |
cmd->env_max = 4;
|
|
Packit Service |
51e54d |
cmd->env = xnmalloc (cmd->env_max, sizeof *cmd->env);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->pre_exec_func = NULL;
|
|
Packit Service |
51e54d |
cmd->pre_exec_free_func = NULL;
|
|
Packit Service |
51e54d |
cmd->pre_exec_data = NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmdp = &cmd->u.process;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmdp->argc = 0;
|
|
Packit Service |
51e54d |
cmdp->argv_max = 4;
|
|
Packit Service |
51e54d |
cmdp->argv = xnmalloc (cmdp->argv_max, sizeof *cmdp->argv);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* argv[0] is the basename of the command name. */
|
|
Packit Service |
51e54d |
name_base = base_name (name);
|
|
Packit Service |
51e54d |
pipecmd_arg (cmd, name_base);
|
|
Packit Service |
51e54d |
free (name_base);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return cmd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipecmd_new_argv (const char *name, va_list argv)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipecmd *cmd = pipecmd_new (name);
|
|
Packit Service |
51e54d |
pipecmd_argv (cmd, argv);
|
|
Packit Service |
51e54d |
return cmd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipecmd_new_args (const char *name, ...)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
va_list argv;
|
|
Packit Service |
51e54d |
pipecmd *cmd;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
va_start (argv, name);
|
|
Packit Service |
51e54d |
cmd = pipecmd_new_argv (name, argv);
|
|
Packit Service |
51e54d |
va_end (argv);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return cmd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* As suggested in the header file, this function (for pipecmd_new_argstr()
|
|
Packit Service |
51e54d |
* and pipecmd_argstr()) is really a wart. If we didn't have to worry about
|
|
Packit Service |
51e54d |
* old configuration files then it wouldn't be necessary. Worse, the
|
|
Packit Service |
51e54d |
* definition for tr in man_db.conf currently contains single-quoting, and
|
|
Packit Service |
51e54d |
* people probably took that as a licence to do similar things, so we're
|
|
Packit Service |
51e54d |
* obliged to worry about quoting as well!
|
|
Packit Service |
51e54d |
*
|
|
Packit Service |
51e54d |
* However, we can mitigate this; shell quoting alone is safe though
|
|
Packit Service |
51e54d |
* sometimes confusing, but it's other shell constructs that tend to cause
|
|
Packit Service |
51e54d |
* real security holes. Therefore, rather than punting to 'sh -c' or
|
|
Packit Service |
51e54d |
* whatever, we parse a safe subset manually. Environment variables are not
|
|
Packit Service |
51e54d |
* currently handled because of tricky word splitting issues, but in
|
|
Packit Service |
51e54d |
* principle they could be if there's demand for it.
|
|
Packit Service |
51e54d |
*
|
|
Packit Service |
51e54d |
* TODO: Support setting environment variables.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
static char *argstr_get_word (const char **argstr)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
char *out = NULL;
|
|
Packit Service |
51e54d |
const char *litstart = *argstr;
|
|
Packit Service |
51e54d |
enum { NONE, SINGLE, DOUBLE } quotemode = NONE;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
while (**argstr) {
|
|
Packit Service |
51e54d |
char backslashed[2];
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* If it's just a literal character, go round again. */
|
|
Packit Service |
51e54d |
if ((quotemode == NONE && !strchr (" \t'\"\\", **argstr)) ||
|
|
Packit Service |
51e54d |
/* nothing is special in '; terminated by ' */
|
|
Packit Service |
51e54d |
(quotemode == SINGLE && **argstr != '\'') ||
|
|
Packit Service |
51e54d |
/* \ is special in "; terminated by " */
|
|
Packit Service |
51e54d |
(quotemode == DOUBLE && !strchr ("\"\\", **argstr))) {
|
|
Packit Service |
51e54d |
++*argstr;
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Within "", \ is only special when followed by $, `, ", or
|
|
Packit Service |
51e54d |
* \ (or <newline> in a real shell, but we don't do that).
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
if (quotemode == DOUBLE && **argstr == '\\' &&
|
|
Packit Service |
51e54d |
!strchr ("$`\"\\", *(*argstr + 1))) {
|
|
Packit Service |
51e54d |
++*argstr;
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Copy any accumulated literal characters. */
|
|
Packit Service |
51e54d |
if (litstart < *argstr) {
|
|
Packit Service |
51e54d |
char *tmp = xstrndup (litstart, *argstr - litstart);
|
|
Packit Service |
51e54d |
out = appendstr (out, tmp, NULL);
|
|
Packit Service |
51e54d |
free (tmp);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
switch (**argstr) {
|
|
Packit Service |
51e54d |
case ' ':
|
|
Packit Service |
51e54d |
case '\t':
|
|
Packit Service |
51e54d |
/* End of word; skip over extra whitespace. */
|
|
Packit Service |
51e54d |
while (*++*argstr)
|
|
Packit Service |
51e54d |
if (!strchr (" \t", **argstr))
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
return out;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case '\'':
|
|
Packit Service |
51e54d |
if (quotemode != NONE)
|
|
Packit Service |
51e54d |
quotemode = NONE;
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
quotemode = SINGLE;
|
|
Packit Service |
51e54d |
litstart = ++*argstr;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case '"':
|
|
Packit Service |
51e54d |
if (quotemode != NONE)
|
|
Packit Service |
51e54d |
quotemode = NONE;
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
quotemode = DOUBLE;
|
|
Packit Service |
51e54d |
litstart = ++*argstr;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case '\\':
|
|
Packit Service |
51e54d |
backslashed[0] = *++*argstr;
|
|
Packit Service |
51e54d |
if (!backslashed[0]) {
|
|
Packit Service |
51e54d |
/* Unterminated quoting; give up. */
|
|
Packit Service |
51e54d |
free (out);
|
|
Packit Service |
51e54d |
return NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
backslashed[1] = '\0';
|
|
Packit Service |
51e54d |
out = appendstr (out, backslashed, NULL);
|
|
Packit Service |
51e54d |
litstart = ++*argstr;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
default:
|
|
Packit Service |
51e54d |
assert (!"unexpected state parsing argstr");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (quotemode != NONE) {
|
|
Packit Service |
51e54d |
/* Unterminated quoting; give up. */
|
|
Packit Service |
51e54d |
free (out);
|
|
Packit Service |
51e54d |
return NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Copy any accumulated literal characters. */
|
|
Packit Service |
51e54d |
if (litstart < *argstr) {
|
|
Packit Service |
51e54d |
char *tmp = xstrndup (litstart, *argstr - litstart);
|
|
Packit Service |
51e54d |
out = appendstr (out, tmp, NULL);
|
|
Packit Service |
51e54d |
free (tmp);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return out;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipecmd_new_argstr (const char *argstr)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipecmd *cmd;
|
|
Packit Service |
51e54d |
char *arg;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
arg = argstr_get_word (&argstr);
|
|
Packit Service |
51e54d |
if (!arg)
|
|
Packit Service |
51e54d |
error (FATAL, 0,
|
|
Packit Service |
51e54d |
"badly formed configuration directive: '%s'",
|
|
Packit Service |
51e54d |
argstr);
|
|
Packit Service |
51e54d |
if (!strcmp (arg, "exec")) {
|
|
Packit Service |
51e54d |
/* Some old configuration files have "exec command" rather
|
|
Packit Service |
51e54d |
* than "command"; this worked fine when being evaluated by
|
|
Packit Service |
51e54d |
* a shell, but since exec is a shell builtin it doesn't
|
|
Packit Service |
51e54d |
* work when being executed directly. To work around this,
|
|
Packit Service |
51e54d |
* we just drop "exec" if it appears at the start of argstr.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
free (arg);
|
|
Packit Service |
51e54d |
arg = argstr_get_word (&argstr);
|
|
Packit Service |
51e54d |
if (!arg)
|
|
Packit Service |
51e54d |
error (FATAL, 0,
|
|
Packit Service |
51e54d |
"badly formed configuration directive: '%s'",
|
|
Packit Service |
51e54d |
argstr);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
cmd = pipecmd_new (arg);
|
|
Packit Service |
51e54d |
free (arg);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
while ((arg = argstr_get_word (&argstr))) {
|
|
Packit Service |
51e54d |
pipecmd_arg (cmd, arg);
|
|
Packit Service |
51e54d |
free (arg);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return cmd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipecmd_new_function (const char *name,
|
|
Packit Service |
51e54d |
pipecmd_function_type *func,
|
|
Packit Service |
51e54d |
pipecmd_function_free_type *free_func,
|
|
Packit Service |
51e54d |
void *data)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipecmd *cmd = XMALLOC (pipecmd);
|
|
Packit Service |
51e54d |
struct pipecmd_function *cmdf;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->tag = PIPECMD_FUNCTION;
|
|
Packit Service |
51e54d |
cmd->name = xstrdup (name);
|
|
Packit Service |
51e54d |
cmd->nice = 0;
|
|
Packit Service |
51e54d |
cmd->discard_err = 0;
|
|
Packit Service |
51e54d |
cmd->cwd_fd = -1;
|
|
Packit Service |
51e54d |
cmd->cwd = NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->nenv = 0;
|
|
Packit Service |
51e54d |
cmd->env_max = 4;
|
|
Packit Service |
51e54d |
cmd->env = xnmalloc (cmd->env_max, sizeof *cmd->env);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->pre_exec_func = NULL;
|
|
Packit Service |
51e54d |
cmd->pre_exec_free_func = NULL;
|
|
Packit Service |
51e54d |
cmd->pre_exec_data = NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmdf = &cmd->u.function;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmdf->func = func;
|
|
Packit Service |
51e54d |
cmdf->free_func = free_func;
|
|
Packit Service |
51e54d |
cmdf->data = data;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return cmd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipecmd_new_sequencev (const char *name, va_list cmdv)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipecmd *cmd = XMALLOC (pipecmd);
|
|
Packit Service |
51e54d |
struct pipecmd_sequence *cmds;
|
|
Packit Service |
51e54d |
pipecmd *child;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->tag = PIPECMD_SEQUENCE;
|
|
Packit Service |
51e54d |
cmd->name = xstrdup (name);
|
|
Packit Service |
51e54d |
cmd->nice = 0;
|
|
Packit Service |
51e54d |
cmd->discard_err = 0;
|
|
Packit Service |
51e54d |
cmd->cwd_fd = -1;
|
|
Packit Service |
51e54d |
cmd->cwd = NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->nenv = 0;
|
|
Packit Service |
51e54d |
cmd->env_max = 4;
|
|
Packit Service |
51e54d |
cmd->env = xnmalloc (cmd->env_max, sizeof *cmd->env);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->pre_exec_func = NULL;
|
|
Packit Service |
51e54d |
cmd->pre_exec_free_func = NULL;
|
|
Packit Service |
51e54d |
cmd->pre_exec_data = NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmds = &cmd->u.sequence;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmds->ncommands = 0;
|
|
Packit Service |
51e54d |
cmds->commands_max = 4;
|
|
Packit Service |
51e54d |
cmds->commands = xnmalloc (cmds->commands_max, sizeof *cmds->commands);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
child = va_arg (cmdv, pipecmd *);
|
|
Packit Service |
51e54d |
while (child) {
|
|
Packit Service |
51e54d |
pipecmd_sequence_command (cmd, child);
|
|
Packit Service |
51e54d |
child = va_arg (cmdv, pipecmd *);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return cmd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipecmd_new_sequence (const char *name, ...)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
va_list cmdv;
|
|
Packit Service |
51e54d |
pipecmd *cmd;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
va_start (cmdv, name);
|
|
Packit Service |
51e54d |
cmd = pipecmd_new_sequencev (name, cmdv);
|
|
Packit Service |
51e54d |
va_end (cmdv);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return cmd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
static void passthrough (void *data PIPELINE_ATTR_UNUSED)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
for (;;) {
|
|
Packit Service |
51e54d |
char buffer[4096];
|
|
Packit Service |
51e54d |
int r = safe_read (STDIN_FILENO, buffer, 4096);
|
|
Packit Service |
51e54d |
if (r <= 0)
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
if (full_write (STDOUT_FILENO, buffer,
|
|
Packit Service |
51e54d |
(size_t) r) < (size_t) r)
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipecmd_new_passthrough (void)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
return pipecmd_new_function ("cat", &passthrough, NULL, NULL);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipecmd_dup (pipecmd *cmd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipecmd *newcmd = XMALLOC (pipecmd);
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
newcmd->tag = cmd->tag;
|
|
Packit Service |
51e54d |
newcmd->name = xstrdup (cmd->name);
|
|
Packit Service |
51e54d |
newcmd->nice = cmd->nice;
|
|
Packit Service |
51e54d |
newcmd->discard_err = cmd->discard_err;
|
|
Packit Service |
51e54d |
newcmd->cwd_fd = cmd->cwd_fd;
|
|
Packit Service |
51e54d |
newcmd->cwd = cmd->cwd ? xstrdup (cmd->cwd) : NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
newcmd->nenv = cmd->nenv;
|
|
Packit Service |
51e54d |
newcmd->env_max = cmd->env_max;
|
|
Packit Service |
51e54d |
assert (newcmd->nenv <= newcmd->env_max);
|
|
Packit Service |
51e54d |
newcmd->env = xmalloc (newcmd->env_max * sizeof *newcmd->env);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
newcmd->pre_exec_func = cmd->pre_exec_func;
|
|
Packit Service |
51e54d |
newcmd->pre_exec_free_func = cmd->pre_exec_free_func;
|
|
Packit Service |
51e54d |
newcmd->pre_exec_data = cmd->pre_exec_data;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < cmd->nenv; ++i) {
|
|
Packit Service |
51e54d |
newcmd->env[i].name =
|
|
Packit Service |
51e54d |
cmd->env[i].name ? xstrdup (cmd->env[i].name) : NULL;
|
|
Packit Service |
51e54d |
newcmd->env[i].value =
|
|
Packit Service |
51e54d |
cmd->env[i].value ? xstrdup (cmd->env[i].value) : NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
switch (newcmd->tag) {
|
|
Packit Service |
51e54d |
case PIPECMD_PROCESS: {
|
|
Packit Service |
51e54d |
struct pipecmd_process *cmdp = &cmd->u.process;
|
|
Packit Service |
51e54d |
struct pipecmd_process *newcmdp = &newcmd->u.process;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
newcmdp->argc = cmdp->argc;
|
|
Packit Service |
51e54d |
newcmdp->argv_max = cmdp->argv_max;
|
|
Packit Service |
51e54d |
assert (newcmdp->argc < newcmdp->argv_max);
|
|
Packit Service |
51e54d |
newcmdp->argv = xmalloc
|
|
Packit Service |
51e54d |
(newcmdp->argv_max * sizeof *newcmdp->argv);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < cmdp->argc; ++i)
|
|
Packit Service |
51e54d |
newcmdp->argv[i] = xstrdup (cmdp->argv[i]);
|
|
Packit Service |
51e54d |
newcmdp->argv[cmdp->argc] = NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case PIPECMD_FUNCTION: {
|
|
Packit Service |
51e54d |
struct pipecmd_function *cmdf = &cmd->u.function;
|
|
Packit Service |
51e54d |
struct pipecmd_function *newcmdf = &newcmd->u.function;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
newcmdf->func = cmdf->func;
|
|
Packit Service |
51e54d |
newcmdf->free_func = cmdf->free_func;
|
|
Packit Service |
51e54d |
newcmdf->data = cmdf->data;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case PIPECMD_SEQUENCE: {
|
|
Packit Service |
51e54d |
struct pipecmd_sequence *cmds = &cmd->u.sequence;
|
|
Packit Service |
51e54d |
struct pipecmd_sequence *newcmds = &newcmd->u.sequence;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
newcmds->ncommands = cmds->ncommands;
|
|
Packit Service |
51e54d |
newcmds->commands_max = cmds->commands_max;
|
|
Packit Service |
51e54d |
assert (newcmds->ncommands <= newcmds->commands_max);
|
|
Packit Service |
51e54d |
newcmds->commands = xmalloc
|
|
Packit Service |
51e54d |
(newcmds->commands_max *
|
|
Packit Service |
51e54d |
sizeof *newcmds->commands);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < cmds->ncommands; ++i)
|
|
Packit Service |
51e54d |
newcmds->commands[i] =
|
|
Packit Service |
51e54d |
pipecmd_dup (cmds->commands[i]);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return newcmd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_arg (pipecmd *cmd, const char *arg)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
struct pipecmd_process *cmdp;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (cmd->tag == PIPECMD_PROCESS);
|
|
Packit Service |
51e54d |
cmdp = &cmd->u.process;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (cmdp->argc + 1 >= cmdp->argv_max) {
|
|
Packit Service |
51e54d |
cmdp->argv_max *= 2;
|
|
Packit Service |
51e54d |
cmdp->argv = xrealloc (cmdp->argv,
|
|
Packit Service |
51e54d |
cmdp->argv_max * sizeof *cmdp->argv);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmdp->argv[cmdp->argc++] = xstrdup (arg);
|
|
Packit Service |
51e54d |
assert (cmdp->argc < cmdp->argv_max);
|
|
Packit Service |
51e54d |
cmdp->argv[cmdp->argc] = NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_argf (pipecmd *cmd, const char *format, ...)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
va_list argv;
|
|
Packit Service |
51e54d |
char *arg;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
va_start (argv, format);
|
|
Packit Service |
51e54d |
arg = xvasprintf (format, argv);
|
|
Packit Service |
51e54d |
pipecmd_arg (cmd, arg);
|
|
Packit Service |
51e54d |
free (arg);
|
|
Packit Service |
51e54d |
va_end (argv);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_argv (pipecmd *cmd, va_list argv)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
const char *arg = va_arg (argv, const char *);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (cmd->tag == PIPECMD_PROCESS);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
while (arg) {
|
|
Packit Service |
51e54d |
pipecmd_arg (cmd, arg);
|
|
Packit Service |
51e54d |
arg = va_arg (argv, const char *);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_args (pipecmd *cmd, ...)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
va_list argv;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (cmd->tag == PIPECMD_PROCESS);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
va_start (argv, cmd);
|
|
Packit Service |
51e54d |
pipecmd_argv (cmd, argv);
|
|
Packit Service |
51e54d |
va_end (argv);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_argstr (pipecmd *cmd, const char *argstr)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
char *arg;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (cmd->tag == PIPECMD_PROCESS);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
while ((arg = argstr_get_word (&argstr))) {
|
|
Packit Service |
51e54d |
pipecmd_arg (cmd, arg);
|
|
Packit Service |
51e54d |
free (arg);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int pipecmd_get_nargs (pipecmd *cmd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
struct pipecmd_process *cmdp;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (cmd->tag == PIPECMD_PROCESS);
|
|
Packit Service |
51e54d |
cmdp = &cmd->u.process;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return cmdp->argc;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_nice (pipecmd *cmd, int value)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
cmd->nice = value;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_discard_err (pipecmd *cmd, int discard_err)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
cmd->discard_err = discard_err;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_chdir (pipecmd *cmd, const char *directory)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
free (cmd->cwd);
|
|
Packit Service |
51e54d |
cmd->cwd = xstrdup (directory);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_fchdir (pipecmd *cmd, int directory_fd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
cmd->cwd_fd = directory_fd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_setenv (pipecmd *cmd, const char *name, const char *value)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (cmd->nenv >= cmd->env_max) {
|
|
Packit Service |
51e54d |
cmd->env_max *= 2;
|
|
Packit Service |
51e54d |
cmd->env = xrealloc (cmd->env,
|
|
Packit Service |
51e54d |
cmd->env_max * sizeof *cmd->env);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->env[cmd->nenv].name = xstrdup (name);
|
|
Packit Service |
51e54d |
cmd->env[cmd->nenv].value = xstrdup (value);
|
|
Packit Service |
51e54d |
++cmd->nenv;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_unsetenv (pipecmd *cmd, const char *name)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (cmd->nenv >= cmd->env_max) {
|
|
Packit Service |
51e54d |
cmd->env_max *= 2;
|
|
Packit Service |
51e54d |
cmd->env = xrealloc (cmd->env,
|
|
Packit Service |
51e54d |
cmd->env_max * sizeof *cmd->env);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->env[cmd->nenv].name = xstrdup (name);
|
|
Packit Service |
51e54d |
cmd->env[cmd->nenv].value = NULL;
|
|
Packit Service |
51e54d |
++cmd->nenv;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_clearenv (pipecmd *cmd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (cmd->nenv >= cmd->env_max) {
|
|
Packit Service |
51e54d |
cmd->env_max *= 2;
|
|
Packit Service |
51e54d |
cmd->env = xrealloc (cmd->env,
|
|
Packit Service |
51e54d |
cmd->env_max * sizeof *cmd->env);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd->env[cmd->nenv].name = NULL;
|
|
Packit Service |
51e54d |
cmd->env[cmd->nenv].value = NULL;
|
|
Packit Service |
51e54d |
++cmd->nenv;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_pre_exec (pipecmd *cmd,
|
|
Packit Service |
51e54d |
pipecmd_function_type *func,
|
|
Packit Service |
51e54d |
pipecmd_function_free_type *free_func,
|
|
Packit Service |
51e54d |
void *data)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
cmd->pre_exec_func = func;
|
|
Packit Service |
51e54d |
cmd->pre_exec_free_func = free_func;
|
|
Packit Service |
51e54d |
cmd->pre_exec_data = data;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_sequence_command (pipecmd *cmd, pipecmd *child)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
struct pipecmd_sequence *cmds;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (cmd->tag == PIPECMD_SEQUENCE);
|
|
Packit Service |
51e54d |
cmds = &cmd->u.sequence;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (cmds->ncommands >= cmds->commands_max) {
|
|
Packit Service |
51e54d |
cmds->commands_max *= 2;
|
|
Packit Service |
51e54d |
cmds->commands = xrealloc
|
|
Packit Service |
51e54d |
(cmds->commands,
|
|
Packit Service |
51e54d |
cmds->commands_max * sizeof *cmds->commands);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmds->commands[cmds->ncommands++] = child;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_dump (pipecmd *cmd, FILE *stream)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (cmd->cwd_fd >= 0)
|
|
Packit Service |
51e54d |
fprintf (stream, "(cd <fd %d> && ", cmd->cwd_fd);
|
|
Packit Service |
51e54d |
else if (cmd->cwd)
|
|
Packit Service |
51e54d |
fprintf (stream, "(cd %s && ", cmd->cwd);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < cmd->nenv; ++i) {
|
|
Packit Service |
51e54d |
if (cmd->env[i].name)
|
|
Packit Service |
51e54d |
fprintf (stream, "%s=%s ",
|
|
Packit Service |
51e54d |
cmd->env[i].name,
|
|
Packit Service |
51e54d |
cmd->env[i].value ? cmd->env[i].value
|
|
Packit Service |
51e54d |
: "<unset>");
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
fputs ("env -i ", stream);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
switch (cmd->tag) {
|
|
Packit Service |
51e54d |
case PIPECMD_PROCESS: {
|
|
Packit Service |
51e54d |
struct pipecmd_process *cmdp = &cmd->u.process;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
fputs (cmd->name, stream);
|
|
Packit Service |
51e54d |
for (i = 1; i < cmdp->argc; ++i) {
|
|
Packit Service |
51e54d |
/* TODO: escape_shell()? */
|
|
Packit Service |
51e54d |
putc (' ', stream);
|
|
Packit Service |
51e54d |
fputs (cmdp->argv[i], stream);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case PIPECMD_FUNCTION:
|
|
Packit Service |
51e54d |
fputs (cmd->name, stream);
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case PIPECMD_SEQUENCE: {
|
|
Packit Service |
51e54d |
struct pipecmd_sequence *cmds = &cmd->u.sequence;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
putc ('(', stream);
|
|
Packit Service |
51e54d |
for (i = 0; i < cmds->ncommands; ++i) {
|
|
Packit Service |
51e54d |
pipecmd_dump (cmds->commands[i], stream);
|
|
Packit Service |
51e54d |
if (i < cmds->ncommands - 1)
|
|
Packit Service |
51e54d |
fputs (" && ", stream);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
putc (')', stream);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (cmd->cwd_fd >= 0 || cmd->cwd)
|
|
Packit Service |
51e54d |
putc (')', stream);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
char *pipecmd_tostring (pipecmd *cmd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
char *out = NULL;
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (cmd->cwd_fd >= 0) {
|
|
Packit Service |
51e54d |
char *cwd_fd_str = xasprintf ("%d", cmd->cwd_fd);
|
|
Packit Service |
51e54d |
out = appendstr (out, "(cd <fd ", cwd_fd_str, "> && ", NULL);
|
|
Packit Service |
51e54d |
free (cwd_fd_str);
|
|
Packit Service |
51e54d |
} else if (cmd->cwd)
|
|
Packit Service |
51e54d |
out = appendstr (out, "(cd ", cmd->cwd, " && ", NULL);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < cmd->nenv; ++i) {
|
|
Packit Service |
51e54d |
if (cmd->env[i].name)
|
|
Packit Service |
51e54d |
out = appendstr (out, cmd->env[i].name, "=",
|
|
Packit Service |
51e54d |
cmd->env[i].value ? cmd->env[i].value
|
|
Packit Service |
51e54d |
: "<unset>",
|
|
Packit Service |
51e54d |
" ", NULL);
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
out = appendstr (out, "env -i ", NULL);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
switch (cmd->tag) {
|
|
Packit Service |
51e54d |
case PIPECMD_PROCESS: {
|
|
Packit Service |
51e54d |
struct pipecmd_process *cmdp = &cmd->u.process;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
out = appendstr (out, cmd->name, NULL);
|
|
Packit Service |
51e54d |
for (i = 1; i < cmdp->argc; ++i)
|
|
Packit Service |
51e54d |
/* TODO: escape_shell()? */
|
|
Packit Service |
51e54d |
out = appendstr (out, " ", cmdp->argv[i],
|
|
Packit Service |
51e54d |
NULL);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case PIPECMD_FUNCTION:
|
|
Packit Service |
51e54d |
out = appendstr (out, cmd->name, NULL);
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case PIPECMD_SEQUENCE: {
|
|
Packit Service |
51e54d |
struct pipecmd_sequence *cmds = &cmd->u.sequence;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
out = appendstr (out, "(", NULL);
|
|
Packit Service |
51e54d |
for (i = 0; i < cmds->ncommands; ++i) {
|
|
Packit Service |
51e54d |
char *subout = pipecmd_tostring
|
|
Packit Service |
51e54d |
(cmds->commands[i]);
|
|
Packit Service |
51e54d |
out = appendstr (out, subout, NULL);
|
|
Packit Service |
51e54d |
free (subout);
|
|
Packit Service |
51e54d |
if (i < cmds->ncommands - 1)
|
|
Packit Service |
51e54d |
out = appendstr (out, " && ", NULL);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
out = appendstr (out, ")", NULL);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (cmd->cwd_fd >= 0 || cmd->cwd)
|
|
Packit Service |
51e54d |
out = appendstr (out, ")", NULL);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return out;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Children exit with this status if execvp fails. */
|
|
Packit Service |
51e54d |
#define EXEC_FAILED_EXIT_STATUS 0xff
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* When called internally during pipeline execution, this is called in the
|
|
Packit Service |
51e54d |
* forked child process, with file descriptors already set up.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
void pipecmd_exec (pipecmd *cmd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (cmd->nice)
|
|
Packit Service |
51e54d |
if (nice (cmd->nice) < 0)
|
|
Packit Service |
51e54d |
/* Don't worry too much. */
|
|
Packit Service |
51e54d |
debug ("nice failed: %s\n", strerror (errno));
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (cmd->discard_err) {
|
|
Packit Service |
51e54d |
int devnull = open ("/dev/null", O_WRONLY);
|
|
Packit Service |
51e54d |
if (devnull != -1) {
|
|
Packit Service |
51e54d |
dup2 (devnull, 2);
|
|
Packit Service |
51e54d |
close (devnull);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (cmd->cwd_fd >= 0) {
|
|
Packit Service |
51e54d |
if (fchdir (cmd->cwd_fd) < 0)
|
|
Packit Service |
51e54d |
error (EXEC_FAILED_EXIT_STATUS, errno,
|
|
Packit Service |
51e54d |
"can't change directory to fd %d", cmd->cwd_fd);
|
|
Packit Service |
51e54d |
} else if (cmd->cwd) {
|
|
Packit Service |
51e54d |
if (chdir (cmd->cwd) < 0)
|
|
Packit Service |
51e54d |
error (EXEC_FAILED_EXIT_STATUS, errno,
|
|
Packit Service |
51e54d |
"can't change directory to '%s'", cmd->cwd);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < cmd->nenv; ++i) {
|
|
Packit Service |
51e54d |
if (cmd->env[i].name) {
|
|
Packit Service |
51e54d |
if (cmd->env[i].value)
|
|
Packit Service |
51e54d |
setenv (cmd->env[i].name,
|
|
Packit Service |
51e54d |
cmd->env[i].value, 1);
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
unsetenv (cmd->env[i].name);
|
|
Packit Service |
51e54d |
} else
|
|
Packit Service |
51e54d |
clearenv ();
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
switch (cmd->tag) {
|
|
Packit Service |
51e54d |
case PIPECMD_PROCESS: {
|
|
Packit Service |
51e54d |
struct pipecmd_process *cmdp = &cmd->u.process;
|
|
Packit Service |
51e54d |
if (cmd->pre_exec_func)
|
|
Packit Service |
51e54d |
cmd->pre_exec_func (cmd->pre_exec_data);
|
|
Packit Service |
51e54d |
execvp (cmd->name, cmdp->argv);
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* TODO: ideally, could there be a facility
|
|
Packit Service |
51e54d |
* to execute non-blocking functions without
|
|
Packit Service |
51e54d |
* needing to fork?
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
case PIPECMD_FUNCTION: {
|
|
Packit Service |
51e54d |
struct pipecmd_function *cmdf = &cmd->u.function;
|
|
Packit Service |
51e54d |
if (cmd->pre_exec_func)
|
|
Packit Service |
51e54d |
cmd->pre_exec_func (cmd->pre_exec_data);
|
|
Packit Service |
51e54d |
cmdf->func (cmdf->data);
|
|
Packit Service |
51e54d |
/* pacify valgrind et al */
|
|
Packit Service |
51e54d |
if (cmdf->free_func)
|
|
Packit Service |
51e54d |
cmdf->free_func (cmdf->data);
|
|
Packit Service |
51e54d |
if (cmd->pre_exec_free_func)
|
|
Packit Service |
51e54d |
cmd->pre_exec_free_func (cmd->pre_exec_data);
|
|
Packit Service |
51e54d |
exit (0);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case PIPECMD_SEQUENCE: {
|
|
Packit Service |
51e54d |
struct pipecmd_sequence *cmds = &cmd->u.sequence;
|
|
Packit Service |
51e54d |
struct sigaction sa;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Flush all pending output so that subprocesses
|
|
Packit Service |
51e54d |
* don't inherit it.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
fflush (NULL);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* pipeline_start will have blocked SIGCHLD. We like
|
|
Packit Service |
51e54d |
* it that way. Lose the parent's signal handler,
|
|
Packit Service |
51e54d |
* though.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
memset (&sa, 0, sizeof sa);
|
|
Packit Service |
51e54d |
sa.sa_handler = SIG_DFL;
|
|
Packit Service |
51e54d |
sigemptyset (&sa.sa_mask);
|
|
Packit Service |
51e54d |
sa.sa_flags = 0;
|
|
Packit Service |
51e54d |
if (sigaction (SIGCHLD, &sa, NULL) == -1)
|
|
Packit Service |
51e54d |
error (FATAL, errno,
|
|
Packit Service |
51e54d |
"can't install SIGCHLD handler");
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < cmds->ncommands; ++i) {
|
|
Packit Service |
51e54d |
pipecmd *child = cmds->commands[i];
|
|
Packit Service |
51e54d |
pid_t pid = fork ();
|
|
Packit Service |
51e54d |
int status;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (pid < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "fork failed");
|
|
Packit Service |
51e54d |
if (pid == 0)
|
|
Packit Service |
51e54d |
pipecmd_exec (child);
|
|
Packit Service |
51e54d |
debug ("Started \"%s\", pid %d\n",
|
|
Packit Service |
51e54d |
child->name, pid);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
while (waitpid (pid, &status, 0) < 0) {
|
|
Packit Service |
51e54d |
if (errno == EINTR)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
error (FATAL, errno, "waitpid failed");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
debug (" \"%s\" (%d) -> %d\n",
|
|
Packit Service |
51e54d |
child->name, pid, status);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (WIFSIGNALED (status)) {
|
|
Packit Service |
51e54d |
int sig = WTERMSIG (status);
|
|
Packit Service |
51e54d |
#ifdef SIGPIPE
|
|
Packit Service |
51e54d |
if (sig == SIGPIPE)
|
|
Packit Service |
51e54d |
status = 0;
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
#endif /* SIGPIPE */
|
|
Packit Service |
51e54d |
if (getenv ("PIPELINE_QUIET"))
|
|
Packit Service |
51e54d |
;
|
|
Packit Service |
51e54d |
else if (WCOREDUMP (status))
|
|
Packit Service |
51e54d |
error (0, 0,
|
|
Packit Service |
51e54d |
"%s: %s (core dumped)",
|
|
Packit Service |
51e54d |
child->name,
|
|
Packit Service |
51e54d |
strsignal (sig));
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
error (0, 0, "%s: %s",
|
|
Packit Service |
51e54d |
child->name,
|
|
Packit Service |
51e54d |
strsignal (sig));
|
|
Packit Service |
51e54d |
} else if (!WIFEXITED (status))
|
|
Packit Service |
51e54d |
error (0, 0, "unexpected status %d",
|
|
Packit Service |
51e54d |
status);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (child->tag == PIPECMD_FUNCTION) {
|
|
Packit Service |
51e54d |
struct pipecmd_function *cmdf =
|
|
Packit Service |
51e54d |
&child->u.function;
|
|
Packit Service |
51e54d |
if (cmdf->free_func)
|
|
Packit Service |
51e54d |
(*cmdf->free_func)
|
|
Packit Service |
51e54d |
(cmdf->data);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (WIFSIGNALED (status)) {
|
|
Packit Service |
51e54d |
raise (WTERMSIG (status));
|
|
Packit Service |
51e54d |
exit (1); /* just to make sure */
|
|
Packit Service |
51e54d |
} else if (status && WIFEXITED (status))
|
|
Packit Service |
51e54d |
exit (WEXITSTATUS (status));
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
exit (0);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
error (EXEC_FAILED_EXIT_STATUS, errno, "can't execute %s", cmd->name);
|
|
Packit Service |
51e54d |
/* Never called, but gcc doesn't realise that error with non-zero
|
|
Packit Service |
51e54d |
* status always exits.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
exit (EXEC_FAILED_EXIT_STATUS);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipecmd_free (pipecmd *cmd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (!cmd)
|
|
Packit Service |
51e54d |
return;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
free (cmd->name);
|
|
Packit Service |
51e54d |
free (cmd->cwd);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < cmd->nenv; ++i) {
|
|
Packit Service |
51e54d |
free (cmd->env[i].name);
|
|
Packit Service |
51e54d |
free (cmd->env[i].value);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
free (cmd->env);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
switch (cmd->tag) {
|
|
Packit Service |
51e54d |
case PIPECMD_PROCESS: {
|
|
Packit Service |
51e54d |
struct pipecmd_process *cmdp = &cmd->u.process;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < cmdp->argc; ++i)
|
|
Packit Service |
51e54d |
free (cmdp->argv[i]);
|
|
Packit Service |
51e54d |
free (cmdp->argv);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case PIPECMD_FUNCTION:
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
case PIPECMD_SEQUENCE: {
|
|
Packit Service |
51e54d |
struct pipecmd_sequence *cmds = &cmd->u.sequence;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < cmds->ncommands; ++i)
|
|
Packit Service |
51e54d |
pipecmd_free (cmds->commands[i]);
|
|
Packit Service |
51e54d |
free (cmds->commands);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
free (cmd);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* ---------------------------------------------------------------------- */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Functions to build pipelines. */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipeline *pipeline_new (void)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipeline *p = XMALLOC (pipeline);
|
|
Packit Service |
51e54d |
p->ncommands = 0;
|
|
Packit Service |
51e54d |
p->commands_max = 4;
|
|
Packit Service |
51e54d |
p->commands = xnmalloc (p->commands_max, sizeof *p->commands);
|
|
Packit Service |
51e54d |
p->pids = NULL;
|
|
Packit Service |
51e54d |
p->statuses = NULL;
|
|
Packit Service |
51e54d |
p->redirect_in = p->redirect_out = REDIRECT_NONE;
|
|
Packit Service |
51e54d |
p->want_in = p->want_out = 0;
|
|
Packit Service |
51e54d |
p->want_infile = p->want_outfile = NULL;
|
|
Packit Service |
51e54d |
p->infd = p->outfd = -1;
|
|
Packit Service |
51e54d |
p->infile = p->outfile = NULL;
|
|
Packit Service |
51e54d |
p->source = NULL;
|
|
Packit Service |
51e54d |
p->buffer = NULL;
|
|
Packit Service |
51e54d |
p->buflen = p->bufmax = 0;
|
|
Packit Service |
51e54d |
p->line_cache = NULL;
|
|
Packit Service |
51e54d |
p->peek_offset = 0;
|
|
Packit Service |
51e54d |
p->ignore_signals = 0;
|
|
Packit Service |
51e54d |
return p;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipeline *pipeline_new_commandv (pipecmd *cmd1, va_list cmdv)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipeline *p = pipeline_new ();
|
|
Packit Service |
51e54d |
pipeline_command (p, cmd1);
|
|
Packit Service |
51e54d |
pipeline_commandv (p, cmdv);
|
|
Packit Service |
51e54d |
return p;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipeline *pipeline_new_commands (pipecmd *cmd1, ...)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
va_list cmdv;
|
|
Packit Service |
51e54d |
pipeline *p;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
va_start (cmdv, cmd1);
|
|
Packit Service |
51e54d |
p = pipeline_new_commandv (cmd1, cmdv);
|
|
Packit Service |
51e54d |
va_end (cmdv);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return p;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipeline *pipeline_new_command_argv (const char *name, va_list argv)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipeline *p;
|
|
Packit Service |
51e54d |
pipecmd *cmd;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
p = pipeline_new ();
|
|
Packit Service |
51e54d |
cmd = pipecmd_new_argv (name, argv);
|
|
Packit Service |
51e54d |
pipeline_command (p, cmd);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return p;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipeline *pipeline_new_command_args (const char *name, ...)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
va_list argv;
|
|
Packit Service |
51e54d |
pipeline *p;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
va_start (argv, name);
|
|
Packit Service |
51e54d |
p = pipeline_new_command_argv (name, argv);
|
|
Packit Service |
51e54d |
va_end (argv);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return p;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipeline *pipeline_join (pipeline *p1, pipeline *p2)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipeline *p = XMALLOC (pipeline);
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (!p1->pids);
|
|
Packit Service |
51e54d |
assert (!p2->pids);
|
|
Packit Service |
51e54d |
assert (!p1->statuses);
|
|
Packit Service |
51e54d |
assert (!p2->statuses);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
p->ncommands = p1->ncommands + p2->ncommands;
|
|
Packit Service |
51e54d |
p->commands_max = p1->ncommands + p2->ncommands;
|
|
Packit Service |
51e54d |
p->commands = xnmalloc (p->commands_max, sizeof *p->commands);
|
|
Packit Service |
51e54d |
p->pids = NULL;
|
|
Packit Service |
51e54d |
p->statuses = NULL;
|
|
Packit Service |
51e54d |
p->redirect_in = p1->redirect_in;
|
|
Packit Service |
51e54d |
p->want_in = p1->want_in;
|
|
Packit Service |
51e54d |
p->want_infile = p1->want_infile ? xstrdup (p1->want_infile) : NULL;
|
|
Packit Service |
51e54d |
p->redirect_out = p2->redirect_out;
|
|
Packit Service |
51e54d |
p->want_out = p2->want_out;
|
|
Packit Service |
51e54d |
p->want_outfile = p2->want_outfile ? xstrdup (p2->want_outfile) : NULL;
|
|
Packit Service |
51e54d |
p->infd = p1->infd;
|
|
Packit Service |
51e54d |
p->outfd = p2->outfd;
|
|
Packit Service |
51e54d |
p->infile = p1->infile;
|
|
Packit Service |
51e54d |
p->outfile = p2->outfile;
|
|
Packit Service |
51e54d |
p->source = NULL;
|
|
Packit Service |
51e54d |
p->buffer = NULL;
|
|
Packit Service |
51e54d |
p->buflen = p->bufmax = 0;
|
|
Packit Service |
51e54d |
p->line_cache = NULL;
|
|
Packit Service |
51e54d |
p->peek_offset = 0;
|
|
Packit Service |
51e54d |
p->ignore_signals = (p1->ignore_signals || p2->ignore_signals);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < p1->ncommands; ++i)
|
|
Packit Service |
51e54d |
p->commands[i] = pipecmd_dup (p1->commands[i]);
|
|
Packit Service |
51e54d |
for (i = 0; i < p2->ncommands; ++i)
|
|
Packit Service |
51e54d |
p->commands[p1->ncommands + i] = pipecmd_dup (p2->commands[i]);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return p;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_connect (pipeline *source, pipeline *sink, ...)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
va_list argv;
|
|
Packit Service |
51e54d |
pipeline *arg;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* We must be in control of output from the source pipeline. If the
|
|
Packit Service |
51e54d |
* source isn't started, we can force this.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
if (!source->pids)
|
|
Packit Service |
51e54d |
pipeline_want_out (source, -1);
|
|
Packit Service |
51e54d |
assert (source->redirect_out == REDIRECT_FD);
|
|
Packit Service |
51e54d |
assert (source->want_out < 0);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
va_start (argv, sink);
|
|
Packit Service |
51e54d |
for (arg = sink; arg; arg = va_arg (argv, pipeline *)) {
|
|
Packit Service |
51e54d |
assert (!arg->pids); /* not started */
|
|
Packit Service |
51e54d |
arg->source = source;
|
|
Packit Service |
51e54d |
pipeline_want_in (arg, -1);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Zero-command sinks should represent data being passed
|
|
Packit Service |
51e54d |
* straight through from the input to the output.
|
|
Packit Service |
51e54d |
* Unfortunately pipeline_start and pipeline_pump don't
|
|
Packit Service |
51e54d |
* handle this very well between them; a zero-command
|
|
Packit Service |
51e54d |
* pipeline has the write end of its input pipe wrongly
|
|
Packit Service |
51e54d |
* stashed in outfd and then pipeline_pump can't handle it
|
|
Packit Service |
51e54d |
* because it has nowhere to send output. Until this is
|
|
Packit Service |
51e54d |
* fixed, this kludge is necessary.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
if (arg->ncommands == 0)
|
|
Packit Service |
51e54d |
pipeline_command (arg, pipecmd_new_passthrough ());
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
va_end (argv);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_command (pipeline *p, pipecmd *cmd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (p->ncommands >= p->commands_max) {
|
|
Packit Service |
51e54d |
p->commands_max *= 2;
|
|
Packit Service |
51e54d |
p->commands = xrealloc (p->commands,
|
|
Packit Service |
51e54d |
p->commands_max * sizeof *p->commands);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
p->commands[p->ncommands++] = cmd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_command_argv (pipeline *p, const char *name, va_list argv)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipecmd *cmd;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
cmd = pipecmd_new_argv (name, argv);
|
|
Packit Service |
51e54d |
pipeline_command (p, cmd);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_command_args (pipeline *p, const char *name, ...)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
va_list argv;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
va_start (argv, name);
|
|
Packit Service |
51e54d |
pipeline_command_argv (p, name, argv);
|
|
Packit Service |
51e54d |
va_end (argv);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_command_argstr (pipeline *p, const char *argstr)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipeline_command (p, pipecmd_new_argstr (argstr));
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_commandv (pipeline *p, va_list cmdv)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipecmd *cmd = va_arg (cmdv, pipecmd *);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
while (cmd) {
|
|
Packit Service |
51e54d |
pipeline_command (p, cmd);
|
|
Packit Service |
51e54d |
cmd = va_arg (cmdv, pipecmd *);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_commands (pipeline *p, ...)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
va_list cmdv;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
va_start (cmdv, p);
|
|
Packit Service |
51e54d |
pipeline_commandv (p, cmdv);
|
|
Packit Service |
51e54d |
va_end (cmdv);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int pipeline_get_ncommands (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
return p->ncommands;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipeline_get_command (pipeline *p, int n)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (n < 0 || n >= p->ncommands)
|
|
Packit Service |
51e54d |
return NULL;
|
|
Packit Service |
51e54d |
return p->commands[n];
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd *pipeline_set_command (pipeline *p, int n, pipecmd *cmd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pipecmd *prev;
|
|
Packit Service |
51e54d |
if (n < 0 || n >= p->ncommands)
|
|
Packit Service |
51e54d |
return NULL;
|
|
Packit Service |
51e54d |
prev = p->commands[n];
|
|
Packit Service |
51e54d |
p->commands[n] = cmd;
|
|
Packit Service |
51e54d |
return prev;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pid_t pipeline_get_pid (pipeline *p, int n)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
assert (p->pids); /* pipeline started */
|
|
Packit Service |
51e54d |
if (n < 0 || n >= p->ncommands)
|
|
Packit Service |
51e54d |
return -1;
|
|
Packit Service |
51e54d |
return p->pids[n];
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_want_in (pipeline *p, int fd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
p->redirect_in = REDIRECT_FD;
|
|
Packit Service |
51e54d |
p->want_in = fd;
|
|
Packit Service |
51e54d |
p->want_infile = NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_want_out (pipeline *p, int fd)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
p->redirect_out = REDIRECT_FD;
|
|
Packit Service |
51e54d |
p->want_out = fd;
|
|
Packit Service |
51e54d |
p->want_outfile = NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_want_infile (pipeline *p, const char *file)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
p->redirect_in = (file != NULL) ? REDIRECT_FILE_NAME : REDIRECT_NONE;
|
|
Packit Service |
51e54d |
p->want_in = 0;
|
|
Packit Service |
51e54d |
p->want_infile = file ? xstrdup (file) : NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_want_outfile (pipeline *p, const char *file)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
p->redirect_out = (file != NULL) ? REDIRECT_FILE_NAME : REDIRECT_NONE;
|
|
Packit Service |
51e54d |
p->want_out = 0;
|
|
Packit Service |
51e54d |
p->want_outfile = file ? xstrdup (file) : NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_ignore_signals (pipeline *p, int ignore_signals)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
p->ignore_signals = ignore_signals;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
FILE *pipeline_get_infile (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
assert (p->pids); /* pipeline started */
|
|
Packit Service |
51e54d |
assert (p->statuses);
|
|
Packit Service |
51e54d |
if (p->infile)
|
|
Packit Service |
51e54d |
return p->infile;
|
|
Packit Service |
51e54d |
else if (p->infd == -1) {
|
|
Packit Service |
51e54d |
error (0, 0, "pipeline input not open");
|
|
Packit Service |
51e54d |
return NULL;
|
|
Packit Service |
51e54d |
} else
|
|
Packit Service |
51e54d |
return p->infile = fdopen (p->infd, "w");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
FILE *pipeline_get_outfile (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
assert (p->pids); /* pipeline started */
|
|
Packit Service |
51e54d |
assert (p->statuses);
|
|
Packit Service |
51e54d |
if (p->outfile)
|
|
Packit Service |
51e54d |
return p->outfile;
|
|
Packit Service |
51e54d |
else if (p->outfd == -1) {
|
|
Packit Service |
51e54d |
error (0, 0, "pipeline output not open");
|
|
Packit Service |
51e54d |
return NULL;
|
|
Packit Service |
51e54d |
} else
|
|
Packit Service |
51e54d |
return p->outfile = fdopen (p->outfd, "r");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_dump (pipeline *p, FILE *stream)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < p->ncommands; ++i) {
|
|
Packit Service |
51e54d |
pipecmd_dump (p->commands[i], stream);
|
|
Packit Service |
51e54d |
if (i < p->ncommands - 1)
|
|
Packit Service |
51e54d |
fputs (" | ", stream);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
fprintf (stream, " [input: {%d, %s}, output: {%d, %s}]\n",
|
|
Packit Service |
51e54d |
p->want_in, p->want_infile ? p->want_infile : "NULL",
|
|
Packit Service |
51e54d |
p->want_out, p->want_outfile ? p->want_outfile : "NULL");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
char *pipeline_tostring (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
char *out = NULL;
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < p->ncommands; ++i) {
|
|
Packit Service |
51e54d |
char *cmdout = pipecmd_tostring (p->commands[i]);
|
|
Packit Service |
51e54d |
out = appendstr (out, cmdout, NULL);
|
|
Packit Service |
51e54d |
free (cmdout);
|
|
Packit Service |
51e54d |
if (i < p->ncommands - 1)
|
|
Packit Service |
51e54d |
out = appendstr (out, " | ", NULL);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return out;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_free (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (!p)
|
|
Packit Service |
51e54d |
return;
|
|
Packit Service |
51e54d |
if (p->pids)
|
|
Packit Service |
51e54d |
pipeline_wait (p);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < p->ncommands; ++i)
|
|
Packit Service |
51e54d |
pipecmd_free (p->commands[i]);
|
|
Packit Service |
51e54d |
free (p->commands);
|
|
Packit Service |
51e54d |
free (p->pids);
|
|
Packit Service |
51e54d |
free (p->statuses);
|
|
Packit Service |
51e54d |
free (p->want_infile);
|
|
Packit Service |
51e54d |
free (p->want_outfile);
|
|
Packit Service |
51e54d |
free (p->buffer);
|
|
Packit Service |
51e54d |
free (p->line_cache);
|
|
Packit Service |
51e54d |
free (p);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* ---------------------------------------------------------------------- */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Functions to run pipelines and handle signals. */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
static pipeline **active_pipelines = NULL;
|
|
Packit Service |
51e54d |
static int n_active_pipelines = 0, max_active_pipelines = 0;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
static int sigchld = 0;
|
|
Packit Service |
51e54d |
static int queue_sigchld = 0;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
static int reap_children (int block)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pid_t pid;
|
|
Packit Service |
51e54d |
int status;
|
|
Packit Service |
51e54d |
int collected = 0;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
do {
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (sigchld) {
|
|
Packit Service |
51e54d |
/* Deal with a SIGCHLD delivery. */
|
|
Packit Service |
51e54d |
pid = waitpid (-1, &status, WNOHANG);
|
|
Packit Service |
51e54d |
--sigchld;
|
|
Packit Service |
51e54d |
} else
|
|
Packit Service |
51e54d |
pid = waitpid (-1, &status, block ? 0 : WNOHANG);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (pid < 0 && errno == EINTR) {
|
|
Packit Service |
51e54d |
/* Try again. */
|
|
Packit Service |
51e54d |
pid = 0;
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (pid <= 0)
|
|
Packit Service |
51e54d |
/* We've run out of children to reap. */
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
++collected;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Deliver the command status if possible. */
|
|
Packit Service |
51e54d |
for (i = 0; i < n_active_pipelines; ++i) {
|
|
Packit Service |
51e54d |
pipeline *p = active_pipelines[i];
|
|
Packit Service |
51e54d |
int j;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (!p || !p->pids || !p->statuses)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (j = 0; j < p->ncommands; ++j) {
|
|
Packit Service |
51e54d |
if (p->pids[j] == pid) {
|
|
Packit Service |
51e54d |
p->statuses[j] = status;
|
|
Packit Service |
51e54d |
i = n_active_pipelines;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
} while ((sigchld || block == 0) && pid >= 0);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (collected)
|
|
Packit Service |
51e54d |
return collected;
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
return -1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
static void pipeline_sigchld (int signum)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
/* really an assert, but that's not async-signal-safe */
|
|
Packit Service |
51e54d |
if (signum == SIGCHLD) {
|
|
Packit Service |
51e54d |
++sigchld;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (!queue_sigchld) {
|
|
Packit Service |
51e54d |
int save_errno = errno;
|
|
Packit Service |
51e54d |
reap_children (0);
|
|
Packit Service |
51e54d |
errno = save_errno;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
static void pipeline_install_sigchld (void)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
struct sigaction act;
|
|
Packit Service |
51e54d |
static int installed = 0;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (installed)
|
|
Packit Service |
51e54d |
return;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
memset (&act, 0, sizeof act);
|
|
Packit Service |
51e54d |
act.sa_handler = &pipeline_sigchld;
|
|
Packit Service |
51e54d |
sigemptyset (&act.sa_mask);
|
|
Packit Service |
51e54d |
sigaddset (&act.sa_mask, SIGINT);
|
|
Packit Service |
51e54d |
sigaddset (&act.sa_mask, SIGTERM);
|
|
Packit Service |
51e54d |
sigaddset (&act.sa_mask, SIGHUP);
|
|
Packit Service |
51e54d |
sigaddset (&act.sa_mask, SIGCHLD);
|
|
Packit Service |
51e54d |
act.sa_flags = 0;
|
|
Packit Service |
51e54d |
#ifdef SA_NOCLDSTOP
|
|
Packit Service |
51e54d |
act.sa_flags |= SA_NOCLDSTOP;
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
#ifdef SA_RESTART
|
|
Packit Service |
51e54d |
act.sa_flags |= SA_RESTART;
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
if (sigaction (SIGCHLD, &act, NULL) == -1)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "can't install SIGCHLD handler");
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
installed = 1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
static pipeline_post_fork_fn *post_fork = NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_install_post_fork (pipeline_post_fork_fn *fn)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
post_fork = fn;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
static int ignored_signals = 0;
|
|
Packit Service |
51e54d |
static struct sigaction osa_sigint, osa_sigquit;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_start (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
int i, j;
|
|
Packit Service |
51e54d |
int last_input = -1;
|
|
Packit Service |
51e54d |
int infd[2];
|
|
Packit Service |
51e54d |
sigset_t set, oset;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Make sure our SIGCHLD handler is installed. */
|
|
Packit Service |
51e54d |
pipeline_install_sigchld ();
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (!p->pids); /* pipeline not started already */
|
|
Packit Service |
51e54d |
assert (!p->statuses);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
init_debug ();
|
|
Packit Service |
51e54d |
if (debug_level) {
|
|
Packit Service |
51e54d |
debug ("Starting pipeline: ");
|
|
Packit Service |
51e54d |
pipeline_dump (p, stderr);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Flush all pending output so that subprocesses don't inherit it. */
|
|
Packit Service |
51e54d |
fflush (NULL);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->ignore_signals && !ignored_signals++) {
|
|
Packit Service |
51e54d |
struct sigaction sa;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Ignore SIGINT and SIGQUIT while subprocesses are running,
|
|
Packit Service |
51e54d |
* just like system().
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
memset (&sa, 0, sizeof sa);
|
|
Packit Service |
51e54d |
sa.sa_handler = SIG_IGN;
|
|
Packit Service |
51e54d |
sigemptyset (&sa.sa_mask);
|
|
Packit Service |
51e54d |
sa.sa_flags = 0;
|
|
Packit Service |
51e54d |
if (sigaction (SIGINT, &sa, &osa_sigint) < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "Couldn't ignore SIGINT");
|
|
Packit Service |
51e54d |
if (sigaction (SIGQUIT, &sa, &osa_sigquit) < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "Couldn't ignore SIGQUIT");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Add to the table of active pipelines, so that signal handlers
|
|
Packit Service |
51e54d |
* know what to do with exit statuses. Block SIGCHLD so that we can
|
|
Packit Service |
51e54d |
* do this safely.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
sigemptyset (&set);
|
|
Packit Service |
51e54d |
sigaddset (&set, SIGCHLD);
|
|
Packit Service |
51e54d |
sigemptyset (&oset);
|
|
Packit Service |
51e54d |
while (sigprocmask (SIG_BLOCK, &set, &oset) == -1 && errno == EINTR)
|
|
Packit Service |
51e54d |
;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Grow the table if necessary. */
|
|
Packit Service |
51e54d |
if (n_active_pipelines >= max_active_pipelines) {
|
|
Packit Service |
51e54d |
int filled = max_active_pipelines;
|
|
Packit Service |
51e54d |
if (max_active_pipelines)
|
|
Packit Service |
51e54d |
max_active_pipelines *= 2;
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
max_active_pipelines = 4;
|
|
Packit Service |
51e54d |
/* reduces to xmalloc (...) if active_pipelines == NULL */
|
|
Packit Service |
51e54d |
active_pipelines = xrealloc
|
|
Packit Service |
51e54d |
(active_pipelines,
|
|
Packit Service |
51e54d |
max_active_pipelines * sizeof *active_pipelines);
|
|
Packit Service |
51e54d |
memset (active_pipelines + filled, 0,
|
|
Packit Service |
51e54d |
(max_active_pipelines - filled) *
|
|
Packit Service |
51e54d |
sizeof *active_pipelines);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < max_active_pipelines; ++i)
|
|
Packit Service |
51e54d |
if (!active_pipelines[i]) {
|
|
Packit Service |
51e54d |
active_pipelines[i] = p;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
assert (i < max_active_pipelines);
|
|
Packit Service |
51e54d |
++n_active_pipelines;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
p->pids = xcalloc (p->ncommands, sizeof *p->pids);
|
|
Packit Service |
51e54d |
p->statuses = xcalloc (p->ncommands, sizeof *p->statuses);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Unblock SIGCHLD. */
|
|
Packit Service |
51e54d |
while (sigprocmask (SIG_SETMASK, &oset, NULL) == -1 && errno == EINTR)
|
|
Packit Service |
51e54d |
;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->redirect_in == REDIRECT_FD && p->want_in < 0) {
|
|
Packit Service |
51e54d |
if (pipe (infd) < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "pipe failed");
|
|
Packit Service |
51e54d |
last_input = infd[0];
|
|
Packit Service |
51e54d |
p->infd = infd[1];
|
|
Packit Service |
51e54d |
} else if (p->redirect_in == REDIRECT_FD)
|
|
Packit Service |
51e54d |
last_input = p->want_in;
|
|
Packit Service |
51e54d |
else if (p->redirect_in == REDIRECT_FILE_NAME) {
|
|
Packit Service |
51e54d |
assert (p->want_infile);
|
|
Packit Service |
51e54d |
last_input = open (p->want_infile, O_RDONLY);
|
|
Packit Service |
51e54d |
if (last_input < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "can't open %s", p->want_infile);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < p->ncommands; i++) {
|
|
Packit Service |
51e54d |
int pdes[2];
|
|
Packit Service |
51e54d |
pid_t pid;
|
|
Packit Service |
51e54d |
int output_read = -1, output_write = -1;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (i != p->ncommands - 1 ||
|
|
Packit Service |
51e54d |
(p->redirect_out == REDIRECT_FD && p->want_out < 0)) {
|
|
Packit Service |
51e54d |
if (pipe (pdes) < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "pipe failed");
|
|
Packit Service |
51e54d |
if (i == p->ncommands - 1)
|
|
Packit Service |
51e54d |
p->outfd = pdes[0];
|
|
Packit Service |
51e54d |
output_read = pdes[0];
|
|
Packit Service |
51e54d |
output_write = pdes[1];
|
|
Packit Service |
51e54d |
} else {
|
|
Packit Service |
51e54d |
if (p->redirect_out == REDIRECT_FD)
|
|
Packit Service |
51e54d |
output_write = p->want_out;
|
|
Packit Service |
51e54d |
else if (p->redirect_out == REDIRECT_FILE_NAME) {
|
|
Packit Service |
51e54d |
assert (p->want_outfile);
|
|
Packit Service |
51e54d |
output_write = open (p->want_outfile,
|
|
Packit Service |
51e54d |
O_WRONLY | O_CREAT |
|
|
Packit Service |
51e54d |
O_TRUNC, 0666);
|
|
Packit Service |
51e54d |
if (output_write < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "can't open %s",
|
|
Packit Service |
51e54d |
p->want_outfile);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Block SIGCHLD so that the signal handler doesn't collect
|
|
Packit Service |
51e54d |
* the exit status before we've filled in the pids array.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
sigemptyset (&set);
|
|
Packit Service |
51e54d |
sigaddset (&set, SIGCHLD);
|
|
Packit Service |
51e54d |
sigemptyset (&oset);
|
|
Packit Service |
51e54d |
while (sigprocmask (SIG_BLOCK, &set, &oset) == -1 &&
|
|
Packit Service |
51e54d |
errno == EINTR)
|
|
Packit Service |
51e54d |
;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pid = fork ();
|
|
Packit Service |
51e54d |
if (pid < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "fork failed");
|
|
Packit Service |
51e54d |
if (pid == 0) {
|
|
Packit Service |
51e54d |
/* child */
|
|
Packit Service |
51e54d |
if (post_fork)
|
|
Packit Service |
51e54d |
post_fork ();
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* input, reading side */
|
|
Packit Service |
51e54d |
if (last_input != -1) {
|
|
Packit Service |
51e54d |
if (dup2 (last_input, 0) < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "dup2 failed");
|
|
Packit Service |
51e54d |
if (close (last_input) < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "close failed");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* output, writing side */
|
|
Packit Service |
51e54d |
if (output_write != -1) {
|
|
Packit Service |
51e54d |
if (dup2 (output_write, 1) < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "dup2 failed");
|
|
Packit Service |
51e54d |
if (close (output_write) < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "close failed");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* output, reading side */
|
|
Packit Service |
51e54d |
if (output_read != -1)
|
|
Packit Service |
51e54d |
if (close (output_read))
|
|
Packit Service |
51e54d |
error (FATAL, errno, "close failed");
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* input from first command, writing side; must close
|
|
Packit Service |
51e54d |
* it in every child because it has to be created
|
|
Packit Service |
51e54d |
* before forking anything
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
if (p->infd != -1)
|
|
Packit Service |
51e54d |
if (close (p->infd))
|
|
Packit Service |
51e54d |
error (FATAL, errno, "close failed");
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* inputs and outputs from other active pipelines */
|
|
Packit Service |
51e54d |
for (j = 0; j < n_active_pipelines; ++j) {
|
|
Packit Service |
51e54d |
pipeline *active = active_pipelines[j];
|
|
Packit Service |
51e54d |
if (!active || active == p)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
/* ignore failures */
|
|
Packit Service |
51e54d |
if (active->infd != -1)
|
|
Packit Service |
51e54d |
close (active->infd);
|
|
Packit Service |
51e54d |
if (active->outfd != -1)
|
|
Packit Service |
51e54d |
close (active->outfd);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Restore signals. */
|
|
Packit Service |
51e54d |
if (p->ignore_signals) {
|
|
Packit Service |
51e54d |
sigaction (SIGINT, &osa_sigint, NULL);
|
|
Packit Service |
51e54d |
sigaction (SIGQUIT, &osa_sigquit, NULL);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipecmd_exec (p->commands[i]);
|
|
Packit Service |
51e54d |
/* never returns */
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* in the parent */
|
|
Packit Service |
51e54d |
if (last_input != -1) {
|
|
Packit Service |
51e54d |
if (close (last_input) < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "close failed");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
if (output_write != -1) {
|
|
Packit Service |
51e54d |
if (close (output_write) < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "close failed");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
if (output_read != -1)
|
|
Packit Service |
51e54d |
last_input = output_read;
|
|
Packit Service |
51e54d |
p->pids[i] = pid;
|
|
Packit Service |
51e54d |
p->statuses[i] = -1;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Unblock SIGCHLD. */
|
|
Packit Service |
51e54d |
while (sigprocmask (SIG_SETMASK, &oset, NULL) == -1 &&
|
|
Packit Service |
51e54d |
errno == EINTR)
|
|
Packit Service |
51e54d |
;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
debug ("Started \"%s\", pid %d\n", p->commands[i]->name, pid);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->ncommands == 0)
|
|
Packit Service |
51e54d |
p->outfd = last_input;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int pipeline_wait_all (pipeline *p, int **statuses, int *n_statuses)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
int ret = 0;
|
|
Packit Service |
51e54d |
int proc_count = p->ncommands;
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
int raise_signal = 0;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
init_debug ();
|
|
Packit Service |
51e54d |
if (debug_level) {
|
|
Packit Service |
51e54d |
debug ("Waiting for pipeline: ");
|
|
Packit Service |
51e54d |
pipeline_dump (p, stderr);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (p->pids); /* pipeline started */
|
|
Packit Service |
51e54d |
assert (p->statuses);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->infile) {
|
|
Packit Service |
51e54d |
if (fclose (p->infile))
|
|
Packit Service |
51e54d |
error (0, errno,
|
|
Packit Service |
51e54d |
"closing pipeline input stream failed");
|
|
Packit Service |
51e54d |
p->infile = NULL;
|
|
Packit Service |
51e54d |
p->infd = -1;
|
|
Packit Service |
51e54d |
} else if (p->infd != -1) {
|
|
Packit Service |
51e54d |
if (close (p->infd))
|
|
Packit Service |
51e54d |
error (0, errno, "closing pipeline input failed");
|
|
Packit Service |
51e54d |
p->infd = -1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->outfile) {
|
|
Packit Service |
51e54d |
if (fclose (p->outfile)) {
|
|
Packit Service |
51e54d |
error (0, errno,
|
|
Packit Service |
51e54d |
"closing pipeline output stream failed");
|
|
Packit Service |
51e54d |
ret = 127;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
p->outfile = NULL;
|
|
Packit Service |
51e54d |
p->outfd = -1;
|
|
Packit Service |
51e54d |
} else if (p->outfd != -1) {
|
|
Packit Service |
51e54d |
if (close (p->outfd)) {
|
|
Packit Service |
51e54d |
error (0, errno, "closing pipeline output failed");
|
|
Packit Service |
51e54d |
ret = 127;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
p->outfd = -1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Tell the SIGCHLD handler not to get in our way. */
|
|
Packit Service |
51e54d |
queue_sigchld = 1;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
while (proc_count > 0) {
|
|
Packit Service |
51e54d |
int r;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
debug ("Active processes (%d):\n", proc_count);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Check for any statuses already collected by SIGCHLD
|
|
Packit Service |
51e54d |
* handlers or the previous iteration before calling
|
|
Packit Service |
51e54d |
* reap_children() again.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
for (i = 0; i < p->ncommands; ++i) {
|
|
Packit Service |
51e54d |
int status;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->pids[i] == -1)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
debug (" \"%s\" (%d) -> %d\n",
|
|
Packit Service |
51e54d |
p->commands[i]->name, p->pids[i],
|
|
Packit Service |
51e54d |
p->statuses[i]);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->statuses[i] == -1)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
status = p->statuses[i];
|
|
Packit Service |
51e54d |
p->pids[i] = -1;
|
|
Packit Service |
51e54d |
--proc_count;
|
|
Packit Service |
51e54d |
if (WIFSIGNALED (status)) {
|
|
Packit Service |
51e54d |
int sig = WTERMSIG (status);
|
|
Packit Service |
51e54d |
#ifdef SIGPIPE
|
|
Packit Service |
51e54d |
if (sig == SIGPIPE)
|
|
Packit Service |
51e54d |
status = 0;
|
|
Packit Service |
51e54d |
else {
|
|
Packit Service |
51e54d |
#endif /* SIGPIPE */
|
|
Packit Service |
51e54d |
/* signals currently blocked,
|
|
Packit Service |
51e54d |
* re-raise later
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
if (sig == SIGINT || sig == SIGQUIT)
|
|
Packit Service |
51e54d |
raise_signal = sig;
|
|
Packit Service |
51e54d |
else if (getenv ("PIPELINE_QUIET"))
|
|
Packit Service |
51e54d |
;
|
|
Packit Service |
51e54d |
else if (WCOREDUMP (status))
|
|
Packit Service |
51e54d |
error (0, 0,
|
|
Packit Service |
51e54d |
"%s: %s (core dumped)",
|
|
Packit Service |
51e54d |
p->commands[i]->name,
|
|
Packit Service |
51e54d |
strsignal (sig));
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
error (0, 0, "%s: %s",
|
|
Packit Service |
51e54d |
p->commands[i]->name,
|
|
Packit Service |
51e54d |
strsignal (sig));
|
|
Packit Service |
51e54d |
#ifdef SIGPIPE
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
#endif /* SIGPIPE */
|
|
Packit Service |
51e54d |
} else if (!WIFEXITED (status))
|
|
Packit Service |
51e54d |
error (0, 0, "unexpected status %d",
|
|
Packit Service |
51e54d |
status);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->commands[i]->tag == PIPECMD_FUNCTION) {
|
|
Packit Service |
51e54d |
struct pipecmd_function *cmdf =
|
|
Packit Service |
51e54d |
&p->commands[i]->u.function;
|
|
Packit Service |
51e54d |
if (cmdf->free_func)
|
|
Packit Service |
51e54d |
(*cmdf->free_func) (cmdf->data);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (i == p->ncommands - 1) {
|
|
Packit Service |
51e54d |
if (WIFSIGNALED (status))
|
|
Packit Service |
51e54d |
ret = 128 + WTERMSIG (status);
|
|
Packit Service |
51e54d |
else if (WEXITSTATUS (status))
|
|
Packit Service |
51e54d |
ret = WEXITSTATUS (status);
|
|
Packit Service |
51e54d |
} else if (!ret &&
|
|
Packit Service |
51e54d |
(WIFSIGNALED (status) ||
|
|
Packit Service |
51e54d |
WEXITSTATUS (status)))
|
|
Packit Service |
51e54d |
ret = 127;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (proc_count >= 0);
|
|
Packit Service |
51e54d |
if (proc_count == 0)
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
errno = 0;
|
|
Packit Service |
51e54d |
r = reap_children (1);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (r == -1 && errno == ECHILD)
|
|
Packit Service |
51e54d |
/* Eh? The pipeline was allegedly still running, so
|
|
Packit Service |
51e54d |
* we shouldn't have got ECHILD.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
error (FATAL, errno, "waitpid failed");
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
queue_sigchld = 0;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < n_active_pipelines; ++i)
|
|
Packit Service |
51e54d |
if (active_pipelines[i] == p)
|
|
Packit Service |
51e54d |
active_pipelines[i] = NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* It isn't normally safe to compactify active_pipelines as we go,
|
|
Packit Service |
51e54d |
* because it's used by a signal handler. However, if it's entirely
|
|
Packit Service |
51e54d |
* empty, we can safely clean it up now. This prevents the table
|
|
Packit Service |
51e54d |
* growing without bound, not to mention pacifying valgrind.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
for (i = 0; i < n_active_pipelines; ++i)
|
|
Packit Service |
51e54d |
if (active_pipelines[i])
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
if (i == n_active_pipelines) {
|
|
Packit Service |
51e54d |
n_active_pipelines = 0;
|
|
Packit Service |
51e54d |
max_active_pipelines = 0;
|
|
Packit Service |
51e54d |
free (active_pipelines);
|
|
Packit Service |
51e54d |
active_pipelines = NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (statuses && n_statuses) {
|
|
Packit Service |
51e54d |
*statuses = xnmalloc (p->ncommands, sizeof **statuses);
|
|
Packit Service |
51e54d |
*n_statuses = p->ncommands;
|
|
Packit Service |
51e54d |
for (i = 0; i < p->ncommands; ++i)
|
|
Packit Service |
51e54d |
(*statuses)[i] = p->statuses[i];
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
free (p->pids);
|
|
Packit Service |
51e54d |
p->pids = NULL;
|
|
Packit Service |
51e54d |
free (p->statuses);
|
|
Packit Service |
51e54d |
p->statuses = NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->ignore_signals && !--ignored_signals) {
|
|
Packit Service |
51e54d |
/* Restore signals. */
|
|
Packit Service |
51e54d |
sigaction (SIGINT, &osa_sigint, NULL);
|
|
Packit Service |
51e54d |
sigaction (SIGQUIT, &osa_sigquit, NULL);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (raise_signal)
|
|
Packit Service |
51e54d |
raise (raise_signal);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return ret;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int pipeline_wait (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
return pipeline_wait_all (p, NULL, NULL);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int pipeline_run (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
int status;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
pipeline_start (p);
|
|
Packit Service |
51e54d |
status = pipeline_wait (p);
|
|
Packit Service |
51e54d |
pipeline_free (p);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return status;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_pump (pipeline *p, ...)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
va_list argv;
|
|
Packit Service |
51e54d |
int argc, i, j;
|
|
Packit Service |
51e54d |
pipeline *arg, **pieces;
|
|
Packit Service |
51e54d |
size_t *pos;
|
|
Packit Service |
51e54d |
int *known_source, *blocking_in, *blocking_out,
|
|
Packit Service |
51e54d |
*dying_source, *waiting, *write_error;
|
|
Packit Service |
51e54d |
struct sigaction sa, osa_sigpipe;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Count pipelines and allocate space for arrays. */
|
|
Packit Service |
51e54d |
va_start (argv, p);
|
|
Packit Service |
51e54d |
argc = 0;
|
|
Packit Service |
51e54d |
for (arg = p; arg; arg = va_arg (argv, pipeline *))
|
|
Packit Service |
51e54d |
++argc;
|
|
Packit Service |
51e54d |
va_end (argv);
|
|
Packit Service |
51e54d |
pieces = xnmalloc (argc, sizeof *pieces);
|
|
Packit Service |
51e54d |
pos = xnmalloc (argc, sizeof *pos);
|
|
Packit Service |
51e54d |
known_source = xcalloc (argc, sizeof *known_source);
|
|
Packit Service |
51e54d |
blocking_in = xcalloc (argc, sizeof *blocking_in);
|
|
Packit Service |
51e54d |
blocking_out = xcalloc (argc, sizeof *blocking_out);
|
|
Packit Service |
51e54d |
dying_source = xcalloc (argc, sizeof *dying_source);
|
|
Packit Service |
51e54d |
waiting = xcalloc (argc, sizeof *waiting);
|
|
Packit Service |
51e54d |
write_error = xcalloc (argc, sizeof *write_error);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Set up arrays of pipelines and their read positions. Start all
|
|
Packit Service |
51e54d |
* pipelines if necessary.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
va_start (argv, p);
|
|
Packit Service |
51e54d |
for (arg = p, i = 0; i < argc; arg = va_arg (argv, pipeline *), ++i) {
|
|
Packit Service |
51e54d |
pieces[i] = arg;
|
|
Packit Service |
51e54d |
pos[i] = 0;
|
|
Packit Service |
51e54d |
if (!pieces[i]->pids)
|
|
Packit Service |
51e54d |
pipeline_start (pieces[i]);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
assert (arg == NULL);
|
|
Packit Service |
51e54d |
va_end (argv);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* All source pipelines must be supplied as arguments. */
|
|
Packit Service |
51e54d |
for (i = 0; i < argc; ++i) {
|
|
Packit Service |
51e54d |
int found = 0;
|
|
Packit Service |
51e54d |
if (!pieces[i]->source)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
for (j = 0; j < argc; ++j) {
|
|
Packit Service |
51e54d |
if (pieces[i]->source == pieces[j]) {
|
|
Packit Service |
51e54d |
known_source[j] = found = 1;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
assert (found);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < argc; ++i) {
|
|
Packit Service |
51e54d |
int flags;
|
|
Packit Service |
51e54d |
if (pieces[i]->infd != -1) {
|
|
Packit Service |
51e54d |
flags = fcntl (pieces[i]->infd, F_GETFL);
|
|
Packit Service |
51e54d |
if (!(flags & O_NONBLOCK)) {
|
|
Packit Service |
51e54d |
blocking_in[i] = 1;
|
|
Packit Service |
51e54d |
fcntl (pieces[i]->infd, F_SETFL,
|
|
Packit Service |
51e54d |
flags | O_NONBLOCK);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
if (pieces[i]->outfd != -1) {
|
|
Packit Service |
51e54d |
flags = fcntl (pieces[i]->outfd, F_GETFL);
|
|
Packit Service |
51e54d |
if (!(flags & O_NONBLOCK)) {
|
|
Packit Service |
51e54d |
blocking_out[i] = 1;
|
|
Packit Service |
51e54d |
fcntl (pieces[i]->outfd, F_SETFL,
|
|
Packit Service |
51e54d |
flags | O_NONBLOCK);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#ifdef SIGPIPE
|
|
Packit Service |
51e54d |
memset (&sa, 0, sizeof sa);
|
|
Packit Service |
51e54d |
sa.sa_handler = SIG_IGN;
|
|
Packit Service |
51e54d |
sigemptyset (&sa.sa_mask);
|
|
Packit Service |
51e54d |
sa.sa_flags = 0;
|
|
Packit Service |
51e54d |
sigaction (SIGPIPE, &sa, &osa_sigpipe);
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#ifdef SA_RESTART
|
|
Packit Service |
51e54d |
/* We rely on getting EINTR from select. */
|
|
Packit Service |
51e54d |
sigaction (SIGCHLD, NULL, &sa);
|
|
Packit Service |
51e54d |
sa.sa_flags &= ~SA_RESTART;
|
|
Packit Service |
51e54d |
sigaction (SIGCHLD, &sa, NULL);
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (;;) {
|
|
Packit Service |
51e54d |
fd_set rfds, wfds;
|
|
Packit Service |
51e54d |
int maxfd = -1;
|
|
Packit Service |
51e54d |
int ret;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* If a source dies and all data from it has been written to
|
|
Packit Service |
51e54d |
* all sinks, close the writing end of the pipe to each of
|
|
Packit Service |
51e54d |
* its sinks.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
for (i = 0; i < argc; ++i) {
|
|
Packit Service |
51e54d |
if (!known_source[i] || pieces[i]->outfd != -1 ||
|
|
Packit Service |
51e54d |
pipeline_peek_size (pieces[i]))
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
for (j = 0; j < argc; ++j) {
|
|
Packit Service |
51e54d |
if (pieces[j]->source == pieces[i] &&
|
|
Packit Service |
51e54d |
pieces[j]->infd != -1) {
|
|
Packit Service |
51e54d |
if (close (pieces[j]->infd))
|
|
Packit Service |
51e54d |
error (0, errno,
|
|
Packit Service |
51e54d |
"closing pipeline "
|
|
Packit Service |
51e54d |
"input failed");
|
|
Packit Service |
51e54d |
pieces[j]->infd = -1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* If all sinks on a source have died, close the reading end
|
|
Packit Service |
51e54d |
* of the pipe from that source.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
for (i = 0; i < argc; ++i) {
|
|
Packit Service |
51e54d |
int got_sink = 0;
|
|
Packit Service |
51e54d |
if (!known_source[i] || pieces[i]->outfd == -1)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
for (j = 0; j < argc; ++j) {
|
|
Packit Service |
51e54d |
if (pieces[j]->source == pieces[i] &&
|
|
Packit Service |
51e54d |
pieces[j]->infd != -1) {
|
|
Packit Service |
51e54d |
got_sink = 1;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
if (got_sink)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
if (close (pieces[i]->outfd))
|
|
Packit Service |
51e54d |
error (0, errno,
|
|
Packit Service |
51e54d |
"closing pipeline output failed");
|
|
Packit Service |
51e54d |
pieces[i]->outfd = -1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
FD_ZERO (&rfds);
|
|
Packit Service |
51e54d |
FD_ZERO (&wfds);
|
|
Packit Service |
51e54d |
for (i = 0; i < argc; ++i) {
|
|
Packit Service |
51e54d |
/* Input to sink pipeline. */
|
|
Packit Service |
51e54d |
if (pieces[i]->source && pieces[i]->infd != -1 &&
|
|
Packit Service |
51e54d |
!waiting[i]) {
|
|
Packit Service |
51e54d |
FD_SET (pieces[i]->infd, &wfds);
|
|
Packit Service |
51e54d |
if (pieces[i]->infd > maxfd)
|
|
Packit Service |
51e54d |
maxfd = pieces[i]->infd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
/* Output from source pipeline. */
|
|
Packit Service |
51e54d |
if (known_source[i] && pieces[i]->outfd != -1) {
|
|
Packit Service |
51e54d |
FD_SET (pieces[i]->outfd, &rfds);
|
|
Packit Service |
51e54d |
if (pieces[i]->outfd > maxfd)
|
|
Packit Service |
51e54d |
maxfd = pieces[i]->outfd;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
if (maxfd == -1)
|
|
Packit Service |
51e54d |
break; /* nothing meaningful left to do */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
ret = select (maxfd + 1, &rfds, &wfds, NULL, NULL);
|
|
Packit Service |
51e54d |
if (ret < 0 && errno == EINTR) {
|
|
Packit Service |
51e54d |
/* Did a source or sink pipeline die? */
|
|
Packit Service |
51e54d |
for (i = 0; i < argc; ++i) {
|
|
Packit Service |
51e54d |
if (pieces[i]->ncommands == 0)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
if (known_source[i] && !dying_source[i] &&
|
|
Packit Service |
51e54d |
pieces[i]->outfd != -1) {
|
|
Packit Service |
51e54d |
int last = pieces[i]->ncommands - 1;
|
|
Packit Service |
51e54d |
assert (pieces[i]->statuses);
|
|
Packit Service |
51e54d |
if (pieces[i]->statuses[last] != -1) {
|
|
Packit Service |
51e54d |
debug ("source pipeline %d "
|
|
Packit Service |
51e54d |
"died\n", i);
|
|
Packit Service |
51e54d |
dying_source[i] = 1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
if (pieces[i]->source &&
|
|
Packit Service |
51e54d |
pieces[i]->infd != -1) {
|
|
Packit Service |
51e54d |
assert (pieces[i]->statuses);
|
|
Packit Service |
51e54d |
if (pieces[i]->statuses[0] != -1) {
|
|
Packit Service |
51e54d |
debug ("sink pipeline %d "
|
|
Packit Service |
51e54d |
"died\n", i);
|
|
Packit Service |
51e54d |
close (pieces[i]->infd);
|
|
Packit Service |
51e54d |
pieces[i]->infd = -1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
} else if (ret < 0)
|
|
Packit Service |
51e54d |
error (FATAL, errno, "select");
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Read a block of data from each available source pipeline. */
|
|
Packit Service |
51e54d |
for (i = 0; i < argc; ++i) {
|
|
Packit Service |
51e54d |
size_t peek_size, len;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (!known_source[i] || pieces[i]->outfd == -1)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
if (!FD_ISSET (pieces[i]->outfd, &rfds))
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
peek_size = pipeline_peek_size (pieces[i]);
|
|
Packit Service |
51e54d |
len = peek_size + 4096;
|
|
Packit Service |
51e54d |
if (!pipeline_peek (pieces[i], &len) ||
|
|
Packit Service |
51e54d |
len == peek_size) {
|
|
Packit Service |
51e54d |
/* Error or end-of-file; skip this pipeline
|
|
Packit Service |
51e54d |
* from now on.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
debug ("source pipeline %d returned error "
|
|
Packit Service |
51e54d |
"or EOF\n", i);
|
|
Packit Service |
51e54d |
close (pieces[i]->outfd);
|
|
Packit Service |
51e54d |
pieces[i]->outfd = -1;
|
|
Packit Service |
51e54d |
} else
|
|
Packit Service |
51e54d |
/* This is rather a large hammer. Whenever
|
|
Packit Service |
51e54d |
* any data is read from any source
|
|
Packit Service |
51e54d |
* pipeline, we go through and retry all
|
|
Packit Service |
51e54d |
* sink pipelines, even if they aren't
|
|
Packit Service |
51e54d |
* receiving data from the source in
|
|
Packit Service |
51e54d |
* question. This probably results in a few
|
|
Packit Service |
51e54d |
* more passes around the select() loop, but
|
|
Packit Service |
51e54d |
* it eliminates some annoyingly fiddly
|
|
Packit Service |
51e54d |
* bookkeeping.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
memset (waiting, 0, argc * sizeof *waiting);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Write as much data as we can to each available sink
|
|
Packit Service |
51e54d |
* pipeline.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
for (i = 0; i < argc; ++i) {
|
|
Packit Service |
51e54d |
const char *block;
|
|
Packit Service |
51e54d |
size_t peek_size;
|
|
Packit Service |
51e54d |
ssize_t w;
|
|
Packit Service |
51e54d |
size_t minpos;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (!pieces[i]->source || pieces[i]->infd == -1)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
if (!FD_ISSET (pieces[i]->infd, &wfds))
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
peek_size = pipeline_peek_size (pieces[i]->source);
|
|
Packit Service |
51e54d |
if (peek_size <= pos[i]) {
|
|
Packit Service |
51e54d |
/* Disable reading until data is read from a
|
|
Packit Service |
51e54d |
* source fd or a child process exits, so
|
|
Packit Service |
51e54d |
* that we neither spin nor block if the
|
|
Packit Service |
51e54d |
* source is slow.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
waiting[i] = 1;
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* peek a block from the source */
|
|
Packit Service |
51e54d |
block = pipeline_peek (pieces[i]->source, &peek_size);
|
|
Packit Service |
51e54d |
/* should all already be in the peek cache */
|
|
Packit Service |
51e54d |
assert (block);
|
|
Packit Service |
51e54d |
assert (peek_size);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* write as much of it as will fit to the sink */
|
|
Packit Service |
51e54d |
for (;;) {
|
|
Packit Service |
51e54d |
w = safe_write (pieces[i]->infd,
|
|
Packit Service |
51e54d |
block + pos[i],
|
|
Packit Service |
51e54d |
peek_size - pos[i]);
|
|
Packit Service |
51e54d |
if (w >= 0)
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
if (errno == EAGAIN) {
|
|
Packit Service |
51e54d |
w = 0;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
/* It may be useful for other processes to
|
|
Packit Service |
51e54d |
* continue even though this one fails, so
|
|
Packit Service |
51e54d |
* don't FATAL yet.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
if (errno != EPIPE)
|
|
Packit Service |
51e54d |
write_error[i] = errno;
|
|
Packit Service |
51e54d |
close (pieces[i]->infd);
|
|
Packit Service |
51e54d |
pieces[i]->infd = -1;
|
|
Packit Service |
51e54d |
goto next_sink;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
pos[i] += w;
|
|
Packit Service |
51e54d |
minpos = pos[i];
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* check other sinks on the same source, and update
|
|
Packit Service |
51e54d |
* the source's read position if earlier data is no
|
|
Packit Service |
51e54d |
* longer needed by any sink
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
for (j = 0; j < argc; ++j) {
|
|
Packit Service |
51e54d |
if (pieces[i]->source != pieces[j]->source ||
|
|
Packit Service |
51e54d |
pieces[j]->infd == -1)
|
|
Packit Service |
51e54d |
continue;
|
|
Packit Service |
51e54d |
if (pos[j] < minpos)
|
|
Packit Service |
51e54d |
minpos = pos[j];
|
|
Packit Service |
51e54d |
/* If the source is dead and all data has
|
|
Packit Service |
51e54d |
* been written to this sink, close the
|
|
Packit Service |
51e54d |
* writing end of the pipe to the sink.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
if (pieces[j]->source->outfd == -1 &&
|
|
Packit Service |
51e54d |
pos[j] >= peek_size) {
|
|
Packit Service |
51e54d |
close (pieces[j]->infd);
|
|
Packit Service |
51e54d |
pieces[j]->infd = -1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* If some data has been written to all sinks,
|
|
Packit Service |
51e54d |
* discard it from the source's peek cache.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
pipeline_peek_skip (pieces[i]->source, minpos);
|
|
Packit Service |
51e54d |
for (j = 0; j < argc; ++j) {
|
|
Packit Service |
51e54d |
if (pieces[i]->source == pieces[j]->source)
|
|
Packit Service |
51e54d |
pos[j] -= minpos;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
next_sink: ;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#ifdef SA_RESTART
|
|
Packit Service |
51e54d |
sigaction (SIGCHLD, NULL, &sa);
|
|
Packit Service |
51e54d |
sa.sa_flags |= SA_RESTART;
|
|
Packit Service |
51e54d |
sigaction (SIGCHLD, &sa, NULL);
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#ifdef SIGPIPE
|
|
Packit Service |
51e54d |
sigaction (SIGPIPE, &osa_sigpipe, NULL);
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < argc; ++i) {
|
|
Packit Service |
51e54d |
int flags;
|
|
Packit Service |
51e54d |
if (blocking_in[i] && pieces[i]->infd != -1) {
|
|
Packit Service |
51e54d |
flags = fcntl (pieces[i]->infd, F_GETFL);
|
|
Packit Service |
51e54d |
fcntl (pieces[i]->infd, F_SETFL, flags & ~O_NONBLOCK);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
if (blocking_out[i] && pieces[i]->outfd != -1) {
|
|
Packit Service |
51e54d |
flags = fcntl (pieces[i]->outfd, F_GETFL);
|
|
Packit Service |
51e54d |
fcntl (pieces[i]->outfd, F_SETFL, flags & ~O_NONBLOCK);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; i < argc; ++i) {
|
|
Packit Service |
51e54d |
if (write_error[i])
|
|
Packit Service |
51e54d |
error (FATAL, write_error[i], "write to sink %d", i);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
free (write_error);
|
|
Packit Service |
51e54d |
free (waiting);
|
|
Packit Service |
51e54d |
free (dying_source);
|
|
Packit Service |
51e54d |
free (blocking_out);
|
|
Packit Service |
51e54d |
free (blocking_in);
|
|
Packit Service |
51e54d |
free (known_source);
|
|
Packit Service |
51e54d |
free (pieces);
|
|
Packit Service |
51e54d |
free (pos);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* ---------------------------------------------------------------------- */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Functions to read output from pipelines. */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
static const char *get_block (pipeline *p, size_t *len, int peek)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
size_t readstart = 0, retstart = 0;
|
|
Packit Service |
51e54d |
size_t space = p->bufmax;
|
|
Packit Service |
51e54d |
size_t toread = *len;
|
|
Packit Service |
51e54d |
ssize_t r;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->buffer && p->peek_offset) {
|
|
Packit Service |
51e54d |
if (p->peek_offset >= toread) {
|
|
Packit Service |
51e54d |
/* We've got the whole thing in the peek cache; just
|
|
Packit Service |
51e54d |
* return it.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
const char *buffer;
|
|
Packit Service |
51e54d |
assert (p->peek_offset <= p->buflen);
|
|
Packit Service |
51e54d |
buffer = p->buffer + p->buflen - p->peek_offset;
|
|
Packit Service |
51e54d |
if (!peek)
|
|
Packit Service |
51e54d |
p->peek_offset -= toread;
|
|
Packit Service |
51e54d |
return buffer;
|
|
Packit Service |
51e54d |
} else {
|
|
Packit Service |
51e54d |
readstart = p->buflen;
|
|
Packit Service |
51e54d |
retstart = p->buflen - p->peek_offset;
|
|
Packit Service |
51e54d |
space -= p->buflen;
|
|
Packit Service |
51e54d |
toread -= p->peek_offset;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (toread > space) {
|
|
Packit Service |
51e54d |
if (p->buffer)
|
|
Packit Service |
51e54d |
p->bufmax = readstart + toread;
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
p->bufmax = toread;
|
|
Packit Service |
51e54d |
p->buffer = xrealloc (p->buffer, p->bufmax + 1);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (!peek)
|
|
Packit Service |
51e54d |
p->peek_offset = 0;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
assert (p->outfd != -1);
|
|
Packit Service |
51e54d |
r = safe_read (p->outfd, p->buffer + readstart, toread);
|
|
Packit Service |
51e54d |
if (r == -1)
|
|
Packit Service |
51e54d |
return NULL;
|
|
Packit Service |
51e54d |
p->buflen = readstart + r;
|
|
Packit Service |
51e54d |
if (peek)
|
|
Packit Service |
51e54d |
p->peek_offset += r;
|
|
Packit Service |
51e54d |
*len -= (toread - r);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return p->buffer + retstart;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
const char *pipeline_read (pipeline *p, size_t *len)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
return get_block (p, len, 0);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
const char *pipeline_peek (pipeline *p, size_t *len)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
return get_block (p, len, 1);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
size_t pipeline_peek_size (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (!p->buffer)
|
|
Packit Service |
51e54d |
return 0;
|
|
Packit Service |
51e54d |
return p->peek_offset;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
void pipeline_peek_skip (pipeline *p, size_t len)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (len > 0) {
|
|
Packit Service |
51e54d |
assert (p->buffer);
|
|
Packit Service |
51e54d |
assert (len <= p->peek_offset);
|
|
Packit Service |
51e54d |
p->peek_offset -= len;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* readline and peekline repeatedly peek larger and larger buffers until
|
|
Packit Service |
51e54d |
* they find a newline or they fail. readline then adjusts the peek offset.
|
|
Packit Service |
51e54d |
*/
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
static const char *get_line (pipeline *p, size_t *outlen)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
const size_t block = 4096;
|
|
Packit Service |
51e54d |
const char *buffer = NULL, *end = NULL;
|
|
Packit Service |
51e54d |
int i;
|
|
Packit Service |
51e54d |
size_t previous_len = 0;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (p->line_cache) {
|
|
Packit Service |
51e54d |
free (p->line_cache);
|
|
Packit Service |
51e54d |
p->line_cache = NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (outlen)
|
|
Packit Service |
51e54d |
*outlen = 0;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (i = 0; ; ++i) {
|
|
Packit Service |
51e54d |
size_t len = block * (i + 1);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
buffer = get_block (p, &len, 1);
|
|
Packit Service |
51e54d |
if (!buffer || len == 0)
|
|
Packit Service |
51e54d |
return NULL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (len == previous_len)
|
|
Packit Service |
51e54d |
/* end of file, no newline found */
|
|
Packit Service |
51e54d |
end = buffer + len - 1;
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
end = memchr (buffer + previous_len, '\n',
|
|
Packit Service |
51e54d |
len - previous_len);
|
|
Packit Service |
51e54d |
if (end)
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
previous_len = len;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (end) {
|
|
Packit Service |
51e54d |
p->line_cache = xstrndup (buffer, end - buffer + 1);
|
|
Packit Service |
51e54d |
if (outlen)
|
|
Packit Service |
51e54d |
*outlen = end - buffer + 1;
|
|
Packit Service |
51e54d |
return p->line_cache;
|
|
Packit Service |
51e54d |
} else
|
|
Packit Service |
51e54d |
return NULL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
const char *pipeline_readline (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
size_t buflen;
|
|
Packit Service |
51e54d |
const char *buffer = get_line (p, &buflen);
|
|
Packit Service |
51e54d |
if (buffer)
|
|
Packit Service |
51e54d |
p->peek_offset -= buflen;
|
|
Packit Service |
51e54d |
return buffer;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
const char *pipeline_peekline (pipeline *p)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
return get_line (p, NULL);
|
|
Packit Service |
51e54d |
}
|