/* This file is part of mailfromd. -*- c -*-
Copyright (C) 2006, 2007, 2008 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include
#include
#include
#define NSTREAMS 1024
struct io_stream {
char *name;
mu_locker_t lock;
int fd[2];
pid_t pid;
char *buf;
size_t bufsize;
};
#define IFD(s) ((s).fd[0])
#define OFD(s) ((s).fd[1] == -1 ? (s).fd[0] : (s).fd[1])
static void
flush_stream(struct io_stream *str)
{
/*FIXME*/
}
static void
close_stream(struct io_stream *str)
{
if (OFD(*str) == -1)
return;
flush_stream(str);
close(OFD(*str));
if (IFD(*str) != -1)
close(IFD(*str));
if (str->pid) {
int status;
waitpid(str->pid, &status, 0);
}
str->fd[0] = -1;
str->fd[1] = -1;
str->pid = 0;
}
static int
read_stream_line(struct io_stream *str)
{
int fd = IFD(*str);
size_t i = 0;
int rc;
for (i = 0; ; i++) {
if (str->bufsize == i) {
if (str->bufsize == 0)
str->bufsize = 16;
str->buf = x2nrealloc(str->buf, &str->bufsize,
sizeof str->buf[1]);
}
rc = read(fd, str->buf + i, 1);
if (rc == -1)
return -1;
if (str->buf[i] == '\n') {
str->buf[i] = 0;
break;
}
}
return 0;
}
#define REDIRECT_STDIN_P(f) ((f) & (O_WRONLY|O_RDWR))
#define REDIRECT_STDOUT_P(f) ((f) == O_RDONLY || ((f) & (O_RDONLY|O_RDWR)))
static int
open_program_stream(struct io_stream *str, const char *cmdline, int flags)
{
int rightp[2], leftp[2];
int i;
int rc = 0;
pid_t pid;
int argc;
char **argv;
if (REDIRECT_STDIN_P(flags))
pipe(leftp);
if (REDIRECT_STDOUT_P(flags))
pipe(rightp);
switch (pid = fork()) {
/* The child branch. */
case 0:
/* attach the pipes */
/* Right-end */
if (REDIRECT_STDOUT_P(flags)) {
if (rightp[1] != 1) {
close(1);
dup2(rightp[1], 1);
}
close(rightp[0]);
}
/* Left-end */
if (REDIRECT_STDIN_P(flags)) {
if (leftp[0] != 0) {
close(0);
dup2(leftp[0], 0);
}
close(leftp[1]);
}
/* FIXME: Error output */
#if 0
if (errfile) {
i = open (errfile, O_CREAT|O_WRONLY|O_APPEND, 0644);
if (i > 0 && i != 2) {
dup2(i, 2);
close(i);
}
}
#endif
/* Close unneded descripitors */
for (i = getmaxfd(); i > 2; i--)
close(i);
debug1(10, "running %s", cmdline);
rc = mu_argcv_get(cmdline, "", NULL, &argc, &argv);
if (rc) {
mu_error(_("Cannot parse command line %s: %s"),
cmdline, mu_strerror(rc));
exit(127);
}
execvp(argv[0], argv);
mu_error(_("Cannot run %s: %s"),
cmdline, mu_strerror(rc));
exit(127);
/********************/
/* Parent branches: */
case -1:
/* Fork has failed */
/* Restore things */
rc = errno;
if (REDIRECT_STDOUT_P(flags)) {
close(rightp[0]);
close(rightp[1]);
}
if (REDIRECT_STDIN_P(flags)) {
close(leftp[0]);
close(leftp[1]);
}
break;
default:
str->pid = pid;
if (REDIRECT_STDOUT_P(flags)) {
str->fd[0] = rightp[0];
close(rightp[1]);
} else
str->fd[0] = -1;
if (REDIRECT_STDIN_P(flags)) {
str->fd[1] = leftp[1];
close(leftp[0]);
} else
str->fd[1] = -1;
}
return rc;
}
static int
open_file_stream(struct io_stream *str, const char *file, int flags)
{
str->fd[0] = open(file, flags, 0644); /* FIXME: mode? */
if (str->fd[0] == -1)
return errno;
return 0;
}
static void *
alloc_streams()
{
struct io_stream *p, *stab = xcalloc(NSTREAMS, sizeof *stab);
for (p = stab; p < stab + NSTREAMS; p++)
p->fd[0] = p->fd[1] = -1;
return stab;
}
static void
destroy_streams(void *data)
{
struct io_stream *stab = data;
struct io_stream *p;
for (p = stab; p < stab + NSTREAMS; p++) {
close_stream(p);
free(p->buf);
}
free(stab);
}
MF_DECLARE_DATA(IO, alloc_streams, destroy_streams)
MF_DEFUN(open, NUMBER, STRING name)
{
int i, rc;
int flags = 0;
int (*opf)(struct io_stream *, const char *, int) = open_file_stream;
struct io_stream *iotab = MF_GET_DATA;
for (i = 0; i < NSTREAMS; i++) {
if (iotab[i].fd[0] == -1)
break;
}
MF_ASSERT(i < NSTREAMS,
mfe_failure,
_("No more files available"));
debug1(10, "opening stream %s", name);
iotab[i].name = name;
if (*name == '>') {
flags |= O_RDWR|O_CREAT;
name++;
if (*name == '>') {
flags |= O_APPEND;
name++;
} else
flags |= O_TRUNC;
} else if (*name == '|') {
opf = open_program_stream;
flags = O_WRONLY;
name++;
if (*name == '&') {
flags = O_RDWR;
name++;
}
} else
flags = O_RDWR;
for (;*name && isspace(*name); name++)
;
rc = opf(&iotab[i], name, flags);
MF_ASSERT(rc == 0,
mfe_failure,
_("Cannot open stream %s: %s"), name,
mu_strerror(rc));
debug2(10, "open(%s) = %d", name, i);
MF_RETURN(i);
}
END
MF_DEFUN(close, VOID, NUMBER fd)
{
struct io_stream *iotab = MF_GET_DATA;
MF_ASSERT(fd >= 0 && fd < NSTREAMS,
mfe_range,
_("Invalid file descriptor"));
close_stream(&iotab[fd]);
}
END
MF_DEFUN(write, VOID, NUMBER fd, STRING str, OPTIONAL, NUMBER n)
{
struct io_stream *iotab = MF_GET_DATA;
int rc;
debug2(10, "writing %s to %d", str, fd);
MF_ASSERT(fd >= 0 && fd < NSTREAMS && OFD(iotab[fd]),
mfe_range,
_("Invalid file descriptor"));
rc = write(OFD(iotab[fd]), str, MF_OPTVAL(n, strlen (str)));
MF_ASSERT(n == rc,
mfe_io,
_("Write error on %s: %s"),
iotab[fd].name, mu_strerror(errno));
}
END
MF_DEFUN(getline, STRING, NUMBER fd)
{
struct io_stream *iotab = MF_GET_DATA;
int rc;
MF_ASSERT(fd >= 0 && fd < NSTREAMS && IFD(iotab[fd]),
mfe_range,
_("Invalid file descriptor"));
rc = read_stream_line(&iotab[fd]);
MF_ASSERT(rc == 0,
mfe_io,
_("Read error on %s: %s"),
iotab[fd].name, mu_strerror(errno));
MF_RETURN_STRING(iotab[fd].buf);
}
END
MF_INIT