From a06cc218bfa18943a46e051d5bbf463e1ddc0b6e Mon Sep 17 00:00:00 2001 From: defanor Date: Sat, 29 Apr 2017 04:36:01 +0300 Subject: Initial commit --- std2fifo.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 std2fifo.c (limited to 'std2fifo.c') diff --git a/std2fifo.c b/std2fifo.c new file mode 100644 index 0000000..a3848e9 --- /dev/null +++ b/std2fifo.c @@ -0,0 +1,222 @@ +/* + std2fifo, a std{in,out} <-> //{in,out} proxy + + This is free and unencumbered software released into the public + domain. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_BUF_SIZE 4096 +#define DIR_MODE S_IRWXU | S_IRWXG | S_IWGRP | S_IRGRP +#define FIFO_IN_MODE S_IRUSR | S_IWUSR | S_IWGRP +#define FIFO_OUT_MODE S_IRUSR | S_IWUSR | S_IRGRP + +#define max(x,y) ((x) > (y) ? (x) : (y)) + + +int chdir_err (const char *dir) +{ + if (chdir(dir)) { + syslog(LOG_ERR, "Can't change directory to %s: %s", dir, strerror(errno)); + return -1; + } + return 0; +} + +int fifo_open (const char *path, + mode_t mode, + int flag) +{ + if (mkfifo(path, mode) && errno != EEXIST) { + syslog(LOG_ERR, "Failed to create FIFO %s: %s", path, strerror(errno)); + return -1; + } + return open(path, flag); +} + +int write_all (int fd, + const char *buffer, + ssize_t count, + const char *err) +{ + ssize_t written, ret; + for (written = 0; written < count; written += ret) { + ret = write (fd, buffer + written, count - written); + if (! ret) { + syslog(LOG_ERR, "write() has returned 0"); + return -1; + } else if (ret < 0) { + if (err != NULL) + syslog(LOG_WARNING, "Failed to write to %s: %s", err, strerror(errno)); + return -1; + } + } + return 0; +} + +int run (const char *dir, + const char *val, + int continuous) +{ + int in = -1, out = -1; + fd_set rfds; + int select_val, max_fd; + ssize_t len; + static char buffer[MAX_BUF_SIZE + 1]; + + if (chdir_err(dir)) + return -1; + + /* Create the directory if it doesn't exist */ + if (mkdir(val, DIR_MODE) && errno != EEXIST) { + syslog(LOG_ERR, "Failed to create directory %s: %s", val, strerror(errno)); + return -1; + } + + if (chdir_err(val)) + return -1; + + in = fifo_open("in", FIFO_IN_MODE, O_RDONLY | O_NONBLOCK); + if (in == -1) + return -1; + + for (;;) { + FD_ZERO(&rfds); + FD_SET(in, &rfds); + FD_SET(STDIN_FILENO, &rfds); + max_fd = max(STDIN_FILENO, in); + + select_val = select(max_fd + 1, &rfds, NULL, NULL, NULL); + if (select_val == -1) { + /* error */ + syslog(LOG_ERR, "select() failure: %s", strerror(errno)); + break; + } else { + /* stdin to FIFO */ + if (FD_ISSET(STDIN_FILENO, &rfds)) { + len = read(STDIN_FILENO, buffer, MAX_BUF_SIZE); + if (len < 0) { + /* Error: quit */ + syslog(LOG_ERR, "Failed to read from stdin: %s", strerror(errno)); + break; + } else if (len == 0) { + /* EOF: quit, but without error */ + close(in); + return 0; + } + /* Open, write, close */ + /* Only open once in the continuous streams mode */ + if ((continuous && out == -1) || ! continuous) { + out = fifo_open("out", FIFO_OUT_MODE, O_WRONLY); + if (out == -1) { + syslog(LOG_ERR, "Failed to open 'out': %s", strerror(errno)); + break; + } + } + if (write_all(out, buffer, len, "FIFO")) + break; + /* Do not close in the continuous streams mode */ + if (! continuous) + close(out); + } + /* FIFO to stdout */ + if (FD_ISSET(in, &rfds)) { + len = read(in, buffer, MAX_BUF_SIZE); + if (len < 0) { + /* Error: quit */ + syslog(LOG_ERR, "Failed to read from FIFO: %s", strerror(errno)); + break; + } else if (len == 0) { + /* EOF: reopen or quit, unless in continuous streams mode */ + close(in); + if (continuous) + break; + in = fifo_open("in", FIFO_IN_MODE, O_RDONLY | O_NONBLOCK); + if (in == -1) { + syslog(LOG_ERR, "Failed to reopen 'in': %s", strerror(errno)); + break; + } + } else if (write_all(STDOUT_FILENO, buffer, len, "stdout")) + break; + } + } + } + + /* It's an error if we've got here */ + if (in >= 0) + close(in); + if (out >= 0) + close(out); + return -1; +} + +void print_help (const char *name) +{ + printf("Usage: %s [option ...] \n", name); + puts("Options:"); + puts(" -v an environment variable name"); + puts(" -c continuous streams"); + puts(" -i syslog ident to use"); + puts(" -e print messages into stderr, in addition to syslog"); + puts(" -h print this help message and exit"); +} + +int main (int argc, + char **argv) +{ + int c; + char *ident = "std2fifo"; + int syslog_options = 0, continuous = 0; + char *var = "SHA256", *val, *dir; + int ret; + struct sigaction sigact; + + while ((c = getopt (argc, argv, "v:ci:eh")) != -1) + switch (c) + { + case 'v': var = optarg; break; + case 'c': continuous = 1; break; + case 'i': ident = optarg; break; + case 'e': syslog_options |= LOG_PERROR; break; + case 'h': print_help(argv[0]); return 0; + default: print_help(argv[0]); return 1; + } + + if (argc <= optind) { + print_help(argv[0]); + return 1; + } + dir = argv[optind]; + val = getenv(var); + if (! val) { + print_help(argv[0]); + return 1; + } + + /* Prepare */ + openlog(ident, syslog_options, LOG_USER); + + /* Ignore SIGPIPE */ + sigact.sa_handler = SIG_IGN; + sigact.sa_flags = 0; + sigaction(SIGPIPE, &sigact, NULL); + + /* Run */ + ret = run(dir, val, continuous); + + /* Done */ + closelog(); + return ret; +} -- cgit v1.2.3