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 (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}