liminix

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

logtap.c (4628B)


      1 #include <errno.h>                   // for errno
      2 #include <fcntl.h>                   // for fcntl, O_NONBLOCK, open, F_GETFL
      3 #include <poll.h>                    // for POLLERR, POLLHUP, POLLIN
      4 #include <signal.h>                  // for signal, SIGPIPE, SIG_IGN
      5 #include <stdarg.h>                  // for va_end, va_list, va_start
      6 #include <stdio.h>                   // for fprintf, stderr, vfprintf
      7 #include <stdlib.h>                  // for malloc, exit
      8 #include <string.h>                  // for strlen, strcat, strcpy, strerror
      9 #include <sys/stat.h>                // for stat, mkfifo, S_IFIFO
     10 #include <unistd.h>                  // for write, STDOUT_FILENO, read
     11 
     12 #define PROGRAM_NAME "logtap"
     13 
     14 #ifdef _GNU_SOURCE
     15 #include <error.h>
     16 #else
     17 #include <stdarg.h>
     18 static void error(int status, int errnum, const char* fmt, ...)
     19 {
     20     va_list ap;
     21     va_start(ap, fmt);
     22 
     23     fprintf(stderr, PROGRAM_NAME ": ");
     24     vfprintf(stderr, fmt, ap);
     25     if (errnum)
     26         fprintf(stderr, ": %s", strerror(errnum));
     27     va_end(ap);
     28 
     29     fprintf(stderr, "\n");
     30     if (status)
     31         exit(status);
     32 }
     33 #endif
     34 
     35 #define FIFO_RETRY_TIMEOUT_MAX (50)
     36 
     37 struct pollfd fds[] = {
     38     { .fd = 0, .events = POLLIN },
     39     { .fd = 1, .events = POLLERR },
     40     { .fd = -1, .events = POLLERR },
     41 };
     42 
     43 #define START_SENTINEL "# LOG-SHIPPING-START\n"
     44 #define STOP_SENTINEL "# LOG-SHIPPING-STOP\n"
     45 
     46 static int fifo_connected(void)
     47 {
     48     return (fds[2].fd >= 0);
     49 }
     50 
     51 int fifo_retry_timeout = -1;
     52 
     53 int open_shipper_fifo(char* pathname)
     54 {
     55     struct stat statbuf;
     56     int fd;
     57 
     58     if (stat(pathname, &statbuf)) {
     59         switch (errno) {
     60         case ENOENT:
     61             if (mkfifo(pathname, 0700)) {
     62                 error(1, errno, "mkfifo %s failed", pathname);
     63             }
     64             break;
     65         default:
     66             error(1, errno, "stat %s failed", pathname);
     67             break;
     68         }
     69     } else {
     70         if (!(statbuf.st_mode & S_IFIFO)) {
     71             error(1, errno, "%s exists already and is not a fifo", pathname);
     72         }
     73     }
     74 
     75     fd = open(pathname, O_WRONLY | O_NONBLOCK, 0);
     76     if (fd >= 0) {
     77         fifo_retry_timeout = -1;
     78         /* write cookie to stdout so that the backfill process knows
     79          * we are now logging realtime
     80          */
     81         write(fds[1].fd, START_SENTINEL, sizeof START_SENTINEL);
     82     } else {
     83         fifo_retry_timeout = FIFO_RETRY_TIMEOUT_MAX;
     84     }
     85 
     86     return fd;
     87 }
     88 
     89 int main(int argc, char* argv[])
     90 {
     91 
     92     char* buf = malloc(8192);
     93     int out_bytes = 0;
     94     int tee_bytes = 0;
     95 
     96     if (argc != 3) {
     97         error(1, 0, "usage: " PROGRAM_NAME " /path/to/fifo");
     98     }
     99 
    100     signal(SIGPIPE, SIG_IGN);
    101 
    102     int flags = fcntl(STDOUT_FILENO, F_GETFL);
    103     fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK);
    104 
    105     fds[2].fd = open_shipper_fifo(argv[1]);
    106 
    107     int quitting = 0;
    108 
    109     while (!quitting) {
    110         int nfds = poll(fds, 3, 2000);
    111         if (nfds > 0) {
    112             if (fds[1].revents & (POLLERR | POLLHUP)) {
    113                 exit(1); // can't even log an error if the logging stream fails
    114             };
    115             if (fifo_connected() && (fds[2].revents & (POLLERR | POLLHUP))) {
    116                 close(fds[2].fd);
    117                 fds[2].fd = -1;
    118                 tee_bytes = 0;
    119                 /* FIXME: nonblocking write, if stdout is not ready
    120                  * we will lose the message */
    121                 (void) write(1, STOP_SENTINEL, sizeof STOP_SENTINEL);
    122                 fifo_retry_timeout = FIFO_RETRY_TIMEOUT_MAX;
    123             };
    124 
    125             if ((fds[0].revents & (POLLIN | POLLHUP)) && (out_bytes == 0) && (tee_bytes == 0)) {
    126                 out_bytes = read(fds[0].fd, buf, 8192);
    127                 if (out_bytes == 0) {
    128                     quitting = 1;
    129                     buf = PROGRAM_NAME " detected eof of file on stdin, exiting\n";
    130                     out_bytes = strlen(buf);
    131                 };
    132                 if (fifo_connected())
    133                     tee_bytes = out_bytes;
    134             };
    135 
    136             if (out_bytes) {
    137                 out_bytes -= write(fds[1].fd, buf, out_bytes);
    138             };
    139             if (tee_bytes) {
    140                 int written = write(fds[2].fd, buf, tee_bytes);
    141                 if (written >= 0)
    142                     tee_bytes -= written;
    143             };
    144             if (out_bytes == 0 && fifo_retry_timeout > 0) {
    145                 fifo_retry_timeout--;
    146                 if (fifo_retry_timeout == 0) {
    147                     fds[2].fd = open_shipper_fifo(argv[1]);
    148                 };
    149             };
    150         } else {
    151             if (!fifo_connected()) {
    152                 fds[2].fd = open_shipper_fifo(argv[1]);
    153             }
    154         };
    155     };
    156 }