1#include <err.h>2#include <errno.h>3#include <pthread.h>4#include <signal.h>5#include <stdio.h>6#include <stdlib.h>7#include <string.h>8#include <unistd.h>910#include <sys/wait.h>11#include <sys/types.h>1213static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;1415static void16sigchld(int num)17{18 int status, wstatus;1920 (void)num;2122 status = EXIT_SUCCESS;23 while (waitpid((pid_t)-1, &wstatus, WNOHANG) > 0) {24 if (status != EXIT_SUCCESS)25 continue;26 status = (WIFEXITED(wstatus)) ? WEXITSTATUS(wstatus) : EXIT_FAILURE;27 }2829 if (status != EXIT_SUCCESS)30 exit(status);31}3233static void34sethandler(void)35{36 struct sigaction act;3738 act.sa_flags = SA_RESTART;39 act.sa_handler = sigchld;40 if (sigemptyset(&act.sa_mask) == -1)41 err(EXIT_FAILURE, "sigemptyset failed");42 if (sigaction(SIGCHLD, &act, NULL))43 err(EXIT_FAILURE, "sigaction failed");44}4546/* thread-safe version of basename(dirname(fp)) */47static ssize_t48servername(char **dest, char *fp)49{50 char *p, *last, *prev;5152 last = prev = NULL;53 p = &fp[strlen(fp)];5455 while (p-- != fp) {56 if (*p != '/')57 continue;5859 if (!last) {60 last = p;61 } else if (!prev) {62 prev = p;63 break;64 }65 }6667 if (!last || !prev)68 return -1;6970 *dest = ++prev;71 return last - prev;72}7374static void75readlines(char *fp, FILE *stream)76{77 size_t llen;78 ssize_t nlen;79 char *nickstart, *line, *name;8081 line = NULL;82 llen = 0;8384 name = NULL;85 if ((nlen = servername(&name, fp)) <= 0)86 return;8788 while (getline(&line, &llen, stream) != -1) {89 if (!(nickstart = strchr(line, '[')))90 continue;91 if ((errno = pthread_mutex_lock(&mtx)))92 err(EXIT_FAILURE, "pthread_mutex_lock failed");9394 printf("%.*s[%.*s %s", (int)(nickstart - line), line,95 (int)nlen, name, nickstart + 1);9697 fflush(stdout);98 if ((errno = pthread_mutex_unlock(&mtx)))99 err(EXIT_FAILURE, "pthread_mutex_unlock failed");100 }101102 if (ferror(stream))103 errx(EXIT_FAILURE, "ferror failed");104 free(line);105}106107static void*108tail(void *arg)109{110 FILE *stream;111 int p[2];112 char *fp;113114 fp = arg;115 if (pipe(p))116 err(EXIT_FAILURE, "pipe failed");117118 switch (fork()) {119 case 0:120 close(p[0]); /* close unused read-end */121 if (dup2(p[1], STDOUT_FILENO) == -1)122 err(EXIT_FAILURE, "dup2 failed");123 close(p[1]);124125 execlp("tail", "tail", "-f", fp, (char*)NULL);126 err(EXIT_FAILURE, "execlp failed");127 case -1:128 err(EXIT_FAILURE, "fork failed");129 default:130 close(p[1]); /* close unused write-end */131 if (!(stream = fdopen(p[0], "r")))132 err(EXIT_FAILURE, "fdopen failed");133134 readlines(fp, stream);135 if (fclose(stream) == EOF)136 err(EXIT_FAILURE, "fclose failed");137 }138139 return NULL;140}141142int143main(int argc, char **argv)144{145 int i, nthrs;146 pthread_t *thrs;147148#ifdef __OpenBSD__149 if (pledge("stdio proc exec", NULL) == -1)150 err(EXIT_FAILURE, "pledge failed");151#endif152153 if (argc <= 1) {154 fprintf(stderr, "USAGE: %s FILE...\n", argv[0]);155 return EXIT_FAILURE;156 }157158 /* Setup SIGCHLD handler */159 sethandler();160161 nthrs = argc - 1;162 if (!(thrs = malloc(sizeof(pthread_t) * nthrs)))163 err(EXIT_FAILURE, "malloc failed");164165 for (i = 0; i < nthrs; i++)166 if ((errno = pthread_create(&thrs[i], NULL, tail, argv[i + 1])))167 err(EXIT_FAILURE, "pthread_create failed");168 for (i = 0; i < nthrs; i++)169 if ((errno = pthread_join(thrs[i], NULL)))170 err(EXIT_FAILURE, "pthread_join failed");171172 return EXIT_SUCCESS;173}