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 }