refifo

FIFO writter that starts over for new readers
git clone git://git.vgx.fr/refifo
Log | Files | Refs

refifo.c (4032B)


      1 #include <assert.h>
      2 #include <errno.h>
      3 #include <fcntl.h>
      4 #include <limits.h>
      5 #include <poll.h>
      6 #include <signal.h>
      7 #include <stdbool.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <sys/stat.h>
     11 #include <sys/types.h>
     12 #include <unistd.h>
     13 
     14 #define NAME "refifo"
     15 
     16 struct
     17 {
     18   char *data;
     19   size_t size;
     20   size_t read;
     21   size_t end;
     22   bool closed;
     23 } buffer;
     24 
     25 char *fifo_path;
     26 
     27 static int
     28 errexit(const char *msg)
     29 {
     30   perror(msg);
     31   exit(1);
     32 }
     33 
     34 static void
     35 config_output()
     36 {
     37   int ret = mkfifo(fifo_path, 0666);
     38 
     39   if (ret == -1) {
     40     if (errno != EEXIST) {
     41       errexit("mkfifo");
     42     } else {
     43       errno = 0;
     44     }
     45   }
     46 
     47   void (*sigret)(int) = signal(SIGPIPE, SIG_IGN);
     48   if (sigret == SIG_ERR)
     49     errexit("signal");
     50 
     51   int fd = open(fifo_path, O_WRONLY);
     52   if (fd == -1)
     53     errexit("open");
     54 
     55   ret = dup2(fd, 1);
     56   if (ret == -1)
     57     errexit("dup2");
     58 
     59   ret = close(fd);
     60   if (ret == -1)
     61     errexit("close");
     62 }
     63 
     64 static void
     65 config_input(void)
     66 {
     67   int flags = fcntl(0, F_GETFD);
     68   if (flags < 0)
     69     errexit("fcntl(0, F_GETFD)");
     70 
     71   int ret = fcntl(0, F_SETFD, flags | O_NONBLOCK);
     72   if (ret == -1)
     73     errexit("fcntl(0, F_SETFD, ...)");
     74 }
     75 
     76 static void
     77 grow_buffer(size_t size)
     78 {
     79   if (!size)
     80     size = PIPE_BUF;
     81 
     82   buffer.size += size;
     83   buffer.data = realloc(buffer.data, buffer.size);
     84 
     85   if (!buffer.data)
     86     errexit("realloc");
     87 }
     88 
     89 static void
     90 write_file(void)
     91 {
     92   char path[] = NAME ".tmp.XXXXXX";
     93   int fd = mkstemp(path);
     94   if (fd == -1)
     95     errexit("mkstemp");
     96 
     97   ssize_t ret = 0;
     98   size_t wrote = 0;
     99   while (wrote != buffer.end) {
    100     ret = write(fd, buffer.data + wrote, buffer.end - wrote);
    101     if (ret == -1) {
    102       perror("write(TMPFILE)");
    103       goto closeexit;
    104     } else {
    105       wrote += ret;
    106     }
    107   }
    108 
    109   ret = close(fd);
    110   if (ret == -1) {
    111     perror("close(TMPFILE)");
    112     goto unlinkexit;
    113   }
    114 
    115   ret = rename(path, fifo_path);
    116   if (ret == -1) {
    117     perror("rename");
    118     goto unlinkexit;
    119   }
    120 
    121   return;
    122 
    123 closeexit:
    124   ret = close(fd);
    125   if (ret == -1)
    126     perror("close(TMPFILE)");
    127 
    128 unlinkexit:
    129   ret = unlink(path);
    130   if (ret == -1)
    131     perror("unlink(TMPFILE)");
    132 
    133   exit(1);
    134 }
    135 
    136 static int
    137 read_data(void)
    138 {
    139   struct pollfd fds[] = {
    140     { .fd = 0, .events = POLLIN },
    141     { .fd = 1, .events = 0 },
    142   };
    143 
    144   int ret = poll(fds, 2, -1);
    145 
    146   if (ret == -1)
    147     errexit("poll(IN)");
    148 
    149   if (fds[1].revents)
    150     return -1;
    151 
    152   if (fds[0].revents & POLLIN) {
    153 
    154     if (buffer.end == buffer.size)
    155       grow_buffer(0);
    156 
    157     ret = read(0, buffer.data + buffer.end, buffer.size - buffer.end);
    158 
    159     if (ret == -1) {
    160       errexit("read");
    161     }
    162 
    163     if (ret == 0) {
    164       write_file();
    165       buffer.closed = true;
    166       return 0;
    167     }
    168     buffer.end += ret;
    169     return ret;
    170   }
    171 
    172   assert(fds[0].revents & (POLLHUP | POLLERR));
    173 
    174   write_file();
    175   buffer.closed = true;
    176   return 0;
    177 }
    178 
    179 static void
    180 io_loop(void)
    181 {
    182   while (true) {
    183     struct pollfd writefd = {
    184       .fd = 1,
    185       .events = POLLOUT,
    186     };
    187 
    188     int ret = poll(&writefd, 1, -1);
    189     if (ret == -1)
    190       errexit("poll(OUT)");
    191 
    192     if (writefd.revents & (POLLHUP | POLLERR)) {
    193       if (buffer.closed)
    194         return;
    195       buffer.read = 0;
    196     }
    197     if (writefd.revents & POLLOUT) {
    198       if (buffer.read == buffer.end) {
    199         if (buffer.closed) {
    200           return;
    201         } else {
    202           ret = read_data();
    203           if (ret == -1) {
    204             buffer.read = 0;
    205             continue;
    206           }
    207           if (ret == 0)
    208             return;
    209         }
    210       }
    211       ssize_t wrote =
    212         write(1, buffer.data + buffer.read, buffer.end - buffer.read);
    213       if (wrote == -1) {
    214         if (errno == EPIPE) {
    215           errno = 0;
    216           if (buffer.closed)
    217             return;
    218           buffer.read = 0;
    219         } else {
    220           errexit("write(FIFO)");
    221         }
    222       } else {
    223         buffer.read += wrote;
    224       }
    225     }
    226   }
    227 }
    228 
    229 int
    230 main(int argc, char *argv[])
    231 {
    232   if (argc != 2) {
    233     fprintf(stderr, "Usage: %s FIFO\n", argv[0]);
    234     return 1;
    235   }
    236 
    237   fifo_path = argv[1];
    238 
    239   config_output();
    240   config_input();
    241   io_loop();
    242 
    243   return 0;
    244 }