switch logtap to use fifo unstead of af_unix

This commit is contained in:
Daniel Barlow
2025-10-08 19:02:53 +01:00
parent af6e41db7a
commit 71aed767f2
2 changed files with 120 additions and 95 deletions

View File

@@ -278,10 +278,7 @@ in
eat = longrun {
name = "log-shipping-pipe-eat";
run = ''
fdmove -c 12 1 \
${pkgs.s6}/bin/s6-ipcserver ${cfg.shipping.socket} \
fdmove -c 1 12 \
cat
cat ${cfg.shipping.socket}
'';
producer-for = spew.name;
};

View File

@@ -1,15 +1,13 @@
#include <poll.h>
#include <sys/timerfd.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <errno.h> // for errno
#include <fcntl.h> // for fcntl, O_NONBLOCK, open, F_GETFL
#include <poll.h> // for POLLERR, POLLHUP, POLLIN
#include <signal.h> // for signal, SIGPIPE, SIG_IGN
#include <stdarg.h> // for va_end, va_list, va_start
#include <stdio.h> // for fprintf, stderr, vfprintf
#include <stdlib.h> // for malloc, exit
#include <string.h> // for strlen, strcat, strcpy, strerror
#include <sys/stat.h> // for stat, mkfifo, S_IFIFO
#include <unistd.h> // for write, STDOUT_FILENO, read
#define PROGRAM_NAME "logtap"
@@ -17,43 +15,48 @@
#include <error.h>
#else
#include <stdarg.h>
static void error(int status, int errnum, const char * fmt, ...) {
static void error(int status, int errnum, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, PROGRAM_NAME ": ");
vfprintf(stderr, fmt, ap);
if(errnum) fprintf(stderr, ": %s", strerror(errnum));
if (errnum)
fprintf(stderr, ": %s", strerror(errnum));
fprintf(stderr, "\n");
if(status) exit(status);
va_end(ap);
if (status)
exit(status);
}
#endif
int open_shipper_fifo(char* pathname)
{
int fd = -1;
struct stat statbuf;
int open_shipper_socket(char *pathname) {
int fd;
static int fail_count = 0;
struct sockaddr_un sa = {
.sun_family = AF_LOCAL
if (stat(pathname, &statbuf)) {
switch (errno) {
case ENOENT:
if(mkfifo(pathname, 0700)) {
error(1, errno, "mkfifo %s failed", pathname);
}
break;
default:
error(1, errno, "stat %s failed", pathname);
break;
}
} else {
if (!(statbuf.st_mode & S_IFIFO)) {
error(1, errno, "%s exists already and is not a fifo", pathname);
}
}
if (fd < 0) {
fd = open(pathname, O_NONBLOCK | O_RDWR, 0);
if (fd < 0)
error(1, errno, "failed to open fifo %s", pathname);
};
strncpy(sa.sun_path, pathname, sizeof(sa.sun_path) - 1);
fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if(fd >= 0) {
if(connect(fd, (struct sockaddr *) &sa, sizeof sa)) {
if((fail_count % 30) == 0) {
printf(PROGRAM_NAME ": cannot connect socket \"%s\": %s\n",
pathname,
strerror(errno));
}
fail_count++;
close(fd);
return -1;
}
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
return fd;
}
@@ -63,35 +66,70 @@ struct pollfd fds[] = {
{ .fd = -1, .events = POLLERR },
};
int is_connected(void) {
return (fds[2].fd >= 0);
#define FIFO_STATE_GOOD (-1)
#define FIFO_STATE_TIMEOUT_EXPIRED (0)
#define FIFO_STATE_TIMEOUT_MAX (50) /* ? probably going to depend on log volume */
char *start_cookie, *stop_cookie;
static int fifo_state = FIFO_STATE_TIMEOUT_EXPIRED;
int write_fifo(int fd, char* buf, int count)
{
int written_bytes = 0;
if (fifo_state == FIFO_STATE_GOOD) {
written_bytes = write(fd, buf, count);
if (written_bytes == -1) {
fifo_state = FIFO_STATE_TIMEOUT_MAX;
write(1, stop_cookie, strlen(stop_cookie));
}
int main(int argc, char * argv[]) {
char * buf = malloc(8192);
int out_bytes = 0;
int tee_bytes = 0;
if(argc != 3) {
error(1, 0, "usage: " PROGRAM_NAME " /path/to/socket cookie-text");
} else if (fifo_state > 0) {
fifo_state--;
} else if (fifo_state == FIFO_STATE_TIMEOUT_EXPIRED) {
written_bytes = write(fd, buf, count);
if (written_bytes >= 0) {
fifo_state = FIFO_STATE_GOOD;
write(1, start_cookie, strlen(start_cookie));
} else {
fifo_state = FIFO_STATE_TIMEOUT_MAX;
/* don't log again, we're in this state because it was bad
already, and it's still bad */
}
char * socket_pathname = argv[1];
char * cookie = argv[2];
char * start_cookie = malloc(strlen(cookie) + 8);
char * stop_cookie = malloc(strlen(cookie) + 7);
if(strlen(socket_pathname) > 108) {
error(1, 0, "socket pathname \"%s\" is too long, max 108 bytes",
socket_pathname);
} else {
error(1, 0, "impossible(sic) fifo state %d", fifo_state);
};
strcpy(start_cookie, cookie); strcat(start_cookie, " START\n");
strcpy(stop_cookie, cookie); strcat(stop_cookie, " STOP\n");
/* if the fifo can't be written, pretend to caller that we wrote
everything so that it doesn't back up. the backfill process
will write these entries later when the shipper is online
again */
return (fifo_state == FIFO_STATE_GOOD) ? written_bytes : count;
}
#define WRITE_LITERAL(fd, c) write(fd, c, sizeof c)
int main(int argc, char* argv[])
{
char* buf = malloc(8192);
int out_bytes = 0;
int fifo_bytes = 0;
if (argc != 3) {
error(1, 0, "usage: " PROGRAM_NAME " /path/to/fifo cookie-text");
}
char* fifo_pathname = argv[1];
char* cookie = argv[2];
start_cookie = malloc(strlen(cookie) + 8);
stop_cookie = malloc(strlen(cookie) + 7);
strcpy(start_cookie, cookie);
strcat(start_cookie, " START\n");
strcpy(stop_cookie, cookie);
strcat(stop_cookie, " STOP\n");
signal(SIGPIPE, SIG_IGN);
fds[2].fd = open_shipper_fifo(fifo_pathname);
int flags = fcntl(STDOUT_FILENO, F_GETFL);
fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK);
@@ -99,44 +137,34 @@ int main(int argc, char * argv[]) {
while (!quitting) {
int nfds = poll(fds, 3, 2000);
if (nfds > 0) {
if((fds[0].revents & (POLLIN|POLLHUP)) &&
(out_bytes == 0) &&
(tee_bytes == 0)) {
if ((fds[0].revents & (POLLIN | POLLHUP)) && (out_bytes == 0) && (fifo_bytes == 0)) {
out_bytes = read(fds[0].fd, buf, 8192);
if (out_bytes == 0) {
quitting = 1;
buf = PROGRAM_NAME " detected eof of file on stdin, exiting\n";
out_bytes = strlen(buf);
WRITE_LITERAL(1, PROGRAM_NAME " detected eof of file on stdin, exiting\n");
};
if(is_connected()) tee_bytes = out_bytes;
fifo_bytes = out_bytes;
};
if (fds[1].revents & (POLLERR | POLLHUP)) {
exit(1); // can't even log an error if the logging stream fails
};
if (fds[2].revents & (POLLERR | POLLHUP)) {
error(1, 0, "error or hangup writing to log fifo (revents=%d)", fds[2].revents);
};
if (out_bytes) {
out_bytes -= write(fds[1].fd, buf, out_bytes);
};
if(fds[1].revents & (POLLERR|POLLHUP)) {
exit(1); // can't even log an error if the logging stream fails
};
if(is_connected()) {
if(tee_bytes) {
tee_bytes -= write(fds[2].fd, buf, tee_bytes);
};
if(fds[2].revents & (POLLERR|POLLHUP)) {
close(fds[2].fd);
fds[2].fd = -1;
(void) write(1, stop_cookie, strlen(stop_cookie));
};
if (fifo_bytes) {
fifo_bytes -= write_fifo(fds[2].fd, buf, fifo_bytes);
};
} else {
if(! is_connected()) {
fds[2].fd = open_shipper_socket(argv[1]);
if(is_connected()) {
/* write cookie to stdout so that the backfill
* process knows we are now logging realtime
/* poll timed out, may as well try and see if the shipper
* is alive again
*/
write(fds[1].fd, start_cookie, strlen(start_cookie));
}
}
if (fifo_state > FIFO_STATE_TIMEOUT_EXPIRED)
fifo_state = FIFO_STATE_TIMEOUT_EXPIRED;
};
};
}