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>
9
10#include <sys/wait.h>
11#include <sys/types.h>
12
13static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
14
15static void
16sigchld(int num)
17{
18 int status, wstatus;
19
20 (void)num;
21
22 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 }
28
29 if (status != EXIT_SUCCESS)
30 exit(status);
31}
32
33static void
34sethandler(void)
35{
36 struct sigaction act;
37
38 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}
45
46/* thread-safe version of basename(dirname(fp)) */
47static ssize_t
48servername(char **dest, char *fp)
49{
50 char *p, *last, *prev;
51
52 last = prev = NULL;
53 p = &fp[strlen(fp)];
54
55 while (p-- != fp) {
56 if (*p != '/')
57 continue;
58
59 if (!last) {
60 last = p;
61 } else if (!prev) {
62 prev = p;
63 break;
64 }
65 }
66
67 if (!last || !prev)
68 return -1;
69
70 *dest = ++prev;
71 return last - prev;
72}
73
74static void
75readlines(char *fp, FILE *stream)
76{
77 size_t llen;
78 ssize_t nlen;
79 char *nickstart, *line, *name;
80
81 line = NULL;
82 llen = 0;
83
84 name = NULL;
85 if ((nlen = servername(&name, fp)) <= 0)
86 return;
87
88 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");
93
94 printf("%.*s[%.*s %s", (int)(nickstart - line), line,
95 (int)nlen, name, nickstart + 1);
96
97 fflush(stdout);
98 if ((errno = pthread_mutex_unlock(&mtx)))
99 err(EXIT_FAILURE, "pthread_mutex_unlock failed");
100 }
101
102 if (ferror(stream))
103 errx(EXIT_FAILURE, "ferror failed");
104 free(line);
105}
106
107static void*
108tail(void *arg)
109{
110 FILE *stream;
111 int p[2];
112 char *fp;
113
114 fp = arg;
115 if (pipe(p))
116 err(EXIT_FAILURE, "pipe failed");
117
118 switch (fork()) {
119 case 0:
120 close(p[0]); /* close unused read-end */
121 close(STDOUT_FILENO);
122 dup2(p[1], STDOUT_FILENO);
123 close(p[1]);
124
125 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");
133
134 readlines(fp, stream);
135 if (fclose(stream) == EOF)
136 err(EXIT_FAILURE, "fclose failed");
137 }
138
139 return NULL;
140}
141
142int
143main(int argc, char **argv)
144{
145 int i, nthrs;
146 pthread_t *thrs;
147
148#ifdef __OpenBSD__
149 if (pledge("stdio proc exec", NULL) == -1)
150 err(EXIT_FAILURE, "pledge failed");
151#endif
152
153 if (argc <= 1) {
154 fprintf(stderr, "USAGE: %s FILE...\n", argv[0]);
155 return EXIT_FAILURE;
156 }
157
158 /* Setup SIGCHLD handler */
159 sethandler();
160
161 nthrs = argc - 1;
162 if (!(thrs = malloc(sizeof(pthread_t) * nthrs)))
163 err(EXIT_FAILURE, "malloc failed");
164
165 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");
171
172 return EXIT_SUCCESS;
173}