insomnia

A frontend for the hii IRC client

git clone https://git.8pit.net/insomnia.git

  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 (WIFEXITED(wstatus) && status == EXIT_SUCCESS)
 25			status = WEXITSTATUS(wstatus);
 26	}
 27
 28	if (status != EXIT_SUCCESS)
 29		exit(status);
 30}
 31
 32static void
 33sethandler(void)
 34{
 35	struct sigaction act;
 36
 37	act.sa_flags = SA_RESTART;
 38	act.sa_handler = sigchld;
 39	if (sigemptyset(&act.sa_mask) == -1)
 40		err(EXIT_FAILURE, "sigemptyset failed");
 41	if (sigaction(SIGCHLD, &act, NULL))
 42		err(EXIT_FAILURE, "sigaction failed");
 43}
 44
 45/* thread-safe version of basename(dirname(fp)) */
 46static ssize_t
 47servername(char **dest, char *fp)
 48{
 49	char *p, *last, *prev;
 50
 51	last = prev = NULL;
 52	p = &fp[strlen(fp)];
 53
 54	while (p-- != fp) {
 55		if (*p != '/')
 56			continue;
 57
 58		if (!last) {
 59			last = p;
 60		} else if (!prev) {
 61			prev = p;
 62			break;
 63		}
 64	}
 65
 66	if (!last || !prev)
 67		return -1;
 68
 69	*dest = ++prev;
 70	return last - prev;
 71}
 72
 73static void
 74readlines(char *fp, FILE *stream)
 75{
 76	size_t llen, nlen;
 77	char *nickstart, *line, *name;
 78
 79	line = NULL;
 80	llen = 0;
 81
 82	name = NULL;
 83	if ((nlen = servername(&name, fp)) <= 0)
 84		return;
 85
 86	while (getline(&line, &llen, stream) != -1) {
 87		if (!(nickstart = strchr(line, '[')))
 88			continue;
 89		if ((errno = pthread_mutex_lock(&mtx)))
 90			err(EXIT_FAILURE, "pthread_mutex_lock failed");
 91
 92		printf("%.*s[%.*s %s", (int)(nickstart - line), line,
 93			(int)nlen, name, nickstart + 1);
 94
 95		fflush(stdout);
 96		if ((errno = pthread_mutex_unlock(&mtx)))
 97			err(EXIT_FAILURE, "pthread_mutex_unlock failed");
 98	}
 99
100	if (ferror(stream))
101		errx(EXIT_FAILURE, "ferror failed");
102	free(line);
103}
104
105static void*
106tail(void *arg)
107{
108	FILE *stream;
109	int p[2];
110	char *fp;
111
112	fp = arg;
113	if (pipe(p))
114		err(EXIT_FAILURE, "pipe failed");
115
116	switch (fork()) {
117	case 0:
118		close(p[0]); /* close unused read-end */
119		close(STDOUT_FILENO);
120		dup(p[1]);
121		close(p[1]);
122
123		execlp("tail", "tail", "-f", fp, (char*)NULL);
124		err(EXIT_FAILURE, "execlp failed");
125	case -1:
126		err(EXIT_FAILURE, "fork failed");
127	default:
128		close(p[1]); /* close unused write-end */
129		if (!(stream = fdopen(p[0], "r")))
130		       err(EXIT_FAILURE, "fdopen failed");
131
132		readlines(fp, stream);
133		if (fclose(stream) == EOF)
134			err(EXIT_FAILURE, "fclose failed");
135	}
136
137	return NULL;
138}
139
140int
141main(int argc, char **argv)
142{
143	int i, nthrs;
144	pthread_t *thrs;
145
146#ifdef __OpenBSD__
147	if (pledge("stdio proc exec", NULL) == -1)
148		err(EXIT_FAILURE, "pledge failed");
149#endif
150
151	if (argc <= 1) {
152		fprintf(stderr, "USAGE: %s FILE...\n", argv[0]);
153		return EXIT_FAILURE;
154	}
155
156	/* Setup SIGCHLD handler */
157	sethandler();
158
159	nthrs = argc - 1;
160	if (!(thrs = malloc(sizeof(pthread_t) * nthrs)))
161		err(EXIT_FAILURE, "malloc failed");
162
163	for (i = 0; i < nthrs; i++)
164		if ((errno = pthread_create(&thrs[i], NULL, tail, argv[i + 1])))
165			err(EXIT_FAILURE, "pthread_create failed");
166	for (i = 0; i < nthrs; i++)
167		if ((errno = pthread_join(thrs[i], NULL)))
168			err(EXIT_FAILURE, "pthread_join failed");
169
170	return EXIT_SUCCESS;
171}