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 }