liminix

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

logtap.c (4031B)


      1 #include <poll.h>
      2 #include <sys/timerfd.h>
      3 #include <time.h>
      4 #include <stdio.h>
      5 #include <string.h>
      6 #include <stdlib.h>
      7 #include <unistd.h>
      8 #include <fcntl.h>
      9 #include <signal.h>
     10 #include <sys/socket.h>
     11 #include <sys/un.h>
     12 #include <errno.h>
     13 
     14 #define PROGRAM_NAME "logtap"
     15 
     16 #ifdef _GNU_SOURCE
     17 #include <error.h>
     18 #else
     19 #include <stdarg.h>
     20 static void error(int status, int errnum, const char * fmt, ...) {
     21     va_list ap;
     22     va_start(ap, fmt);
     23 
     24     fprintf(stderr, PROGRAM_NAME  ": ");
     25     vfprintf(stderr, fmt, ap);
     26     if(errnum) fprintf(stderr, ": %s", strerror(errnum));
     27     fprintf(stderr, "\n");
     28     if(status) exit(status);
     29 }
     30 #endif
     31 
     32 
     33 int open_shipper_socket(char *pathname) {
     34     int fd;
     35     static int fail_count = 0;
     36 
     37     struct sockaddr_un sa = {
     38         .sun_family = AF_LOCAL
     39     };
     40     strncpy(sa.sun_path, pathname, sizeof(sa.sun_path) - 1);
     41 
     42     fd = socket(AF_LOCAL, SOCK_STREAM, 0);
     43     if(fd >= 0) {
     44         if(connect(fd, (struct sockaddr *) &sa, sizeof sa)) {
     45             if((fail_count % 30) == 0) {
     46                 printf(PROGRAM_NAME ": cannot connect socket \"%s\": %s\n",
     47                        pathname,
     48                        strerror(errno));
     49 	    }
     50             fail_count++;
     51             close(fd);
     52             return -1;
     53         }
     54         int flags = fcntl(fd, F_GETFL);
     55         fcntl(fd, F_SETFL, flags | O_NONBLOCK);
     56     }
     57     return fd;
     58 }
     59 
     60 struct pollfd fds[] = {
     61     { .fd = 0, .events = POLLIN },
     62     { .fd = 1, .events = POLLERR },
     63     { .fd = -1, .events = POLLERR },
     64 };
     65 
     66 int is_connected(void) {
     67     return (fds[2].fd >= 0);
     68 }
     69 
     70 
     71 int main(int argc, char * argv[]) {
     72 
     73     char * buf = malloc(8192);
     74     int out_bytes = 0;
     75     int tee_bytes = 0;
     76 
     77     if(argc != 3) {
     78         error(1, 0, "usage: " PROGRAM_NAME " /path/to/socket cookie-text");
     79     }
     80     char * socket_pathname = argv[1];
     81     char * cookie = argv[2];
     82     char * start_cookie = malloc(strlen(cookie) + 8);
     83     char * stop_cookie = malloc(strlen(cookie) + 7);
     84 
     85     if(strlen(socket_pathname) > 108) {
     86         error(1, 0, "socket pathname \"%s\" is too long, max 108 bytes",
     87               socket_pathname);
     88     };
     89 
     90     strcpy(start_cookie, cookie); strcat(start_cookie, " START\n");
     91     strcpy(stop_cookie, cookie); strcat(stop_cookie, " STOP\n");
     92 
     93     signal(SIGPIPE, SIG_IGN);
     94 
     95     int flags = fcntl(STDOUT_FILENO, F_GETFL);
     96     fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK);
     97 
     98     int quitting = 0;
     99     while(! quitting) {
    100         int nfds = poll(fds, 3, 2000);
    101         if(nfds > 0) {
    102             if((fds[0].revents & (POLLIN|POLLHUP)) &&
    103                (out_bytes == 0) &&
    104                (tee_bytes == 0)) {
    105                 out_bytes = read(fds[0].fd, buf, 8192);
    106                 if(out_bytes == 0) {
    107                     quitting = 1;
    108                     buf = PROGRAM_NAME " detected eof of file on stdin, exiting\n";
    109                     out_bytes = strlen(buf);
    110                 };
    111                 if(is_connected()) tee_bytes = out_bytes;
    112             };
    113 
    114             if(out_bytes) {
    115                 out_bytes -= write(fds[1].fd, buf, out_bytes);
    116             };
    117             if(fds[1].revents & (POLLERR|POLLHUP)) {
    118                 exit(1); // can't even log an error if the logging stream fails
    119             };
    120             if(is_connected()) {
    121                 if(tee_bytes) {
    122                     tee_bytes -= write(fds[2].fd, buf, tee_bytes);
    123                 };
    124                 if(fds[2].revents & (POLLERR|POLLHUP)) {
    125                     close(fds[2].fd);
    126                     fds[2].fd = -1;
    127                     (void) write(1, stop_cookie, strlen(stop_cookie));
    128                 };
    129             };
    130         } else {
    131             if(! is_connected()) {
    132                 fds[2].fd = open_shipper_socket(argv[1]);
    133                 if(is_connected()) {
    134                     /* write cookie to stdout so that the backfill
    135                      * process knows we are now logging realtime
    136                      */
    137                     write(fds[1].fd, start_cookie, strlen(start_cookie));
    138                 }
    139             }
    140         };
    141     };
    142 }