Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

file 154 lines (146 sloc) 3.571 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
/*
* Copyright (c) 2011 Yelp, Inc.
*
* Available under the ISC License. For details, see
* LICENSE
*/

#define _XOPEN_SOURCE 600

#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifdef HAS_SIGNALFD
#include <sys/signalfd.h>
#endif
#include <unistd.h>

#define NUM_EVENTS 16

static int pid = 0;
static bool running = true;

#ifndef HAS_SIGNALFD
/* Handler for SIGCHLD */
void handler(int signal __attribute__((unused)))
{
    running = false;
}
#endif

int add_watch(int epfd, int fd, uint32_t events)
{
    struct epoll_event e;
    e.events = events;
    e.data.fd = fd;
    return epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &e);
}

int main(int argc, char** argv)
{
    int err, ep;
    int status;
#ifdef HAS_SIGNALFD
    int chldfd;
#endif
    sigset_t sigset;
    struct epoll_event events[NUM_EVENTS];

    if (argc < 2) {
        fprintf(stderr, "Usage: eppipe command\n"
                "Runs `command` under eppipe\n");
        return 2;
    }
    // Set up a signalfd to handle the SIGCHLD
    if (sigemptyset(&sigset)) {
        perror("sigemptyset");
        goto kill;
    }
    if (sigaddset(&sigset, SIGCHLD)) {
        perror("sigaddset");
        goto kill;
    }
#ifdef HAS_SIGNALFD
    if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0) {
        perror ("sigprocmask");
        goto kill;
    }
    if ((chldfd = signalfd(-1, &sigset, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
        perror("signalfd");
        goto kill;
    }
#else
    signal(SIGCHLD, handler);
#endif
    pid = fork();
    if (pid == 0) {
        err = execvp(argv[1], argv + 1);
        if (err) {
            fprintf(stderr, "Tried to exec '%s'\n", argv[1]);
            perror("execlp");
            return EXIT_FAILURE;
        }
    } else if (pid < 0) {
        perror("fork");
        return EXIT_FAILURE;
    }
    if ((ep = epoll_create(3)) < 0) {
        perror("epoll_create");
        goto kill;
    }
    if (add_watch(ep, STDOUT_FILENO, EPOLLIN|EPOLLHUP) < 0) {
        perror("add_watch stdout");
        goto kill;
    }
#ifdef HAS_SIGNALFD
    if (add_watch(ep, chldfd, EPOLLIN|EPOLLHUP)) {
        perror("add_watch signalfd");
        goto kill;
    }
#endif
    while(running) {
        int n_events, i;
#ifdef HAS_SIGNALFD
        if ((n_events = epoll_wait(ep, events, NUM_EVENTS, 10000)) < 0) {
#else
        if ((n_events = epoll_pwait(ep, events, NUM_EVENTS, 500, &sigset)) < 0) {
#endif
            if (errno == EINTR) {
                continue;
            }
            perror("epoll_wait");
            goto kill;
        }
        for (i = 0; i < n_events; ++i) {
            if (events[i].data.fd == STDOUT_FILENO) {
                char buf[1];
                ssize_t read_b = 0;
                do {
                    read_b = read(STDOUT_FILENO, buf, 1);
                } while (read_b > 0);
                if (read_b == 0) {
                    kill(pid, SIGTERM);
                    running = false;
                    break;
                }
            }
#ifdef HAS_SIGNALFD
            else if (events[i].data.fd == chldfd) {
                running = false;
            }
#endif
            else {
                fprintf(stderr, "Activity on unrecognized fd!\n");
                goto kill;
            }
        }
    }
wait:
    fflush(stdout);
    fflush(stderr);
    waitpid(pid, &status, 0);
    return WEXITSTATUS(status);
kill:
    if (pid > 0) {
        kill(pid, SIGTERM);
    }
    goto wait;
}
Something went wrong with that request. Please try again.