/* This file is part of Mailfromd. -*- c -*- Copyright (C) 2006, 2007, 2008, 2009 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 && c_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")); if (!MF_DEFINED(n)) n = strlen (str); rc = write(OFD(iotab[fd]), str, n); 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