hooks.c revision 213429
1204076Spjd/*-
2204076Spjd * Copyright (c) 2010 The FreeBSD Foundation
3211885Spjd * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
4204076Spjd * All rights reserved.
5204076Spjd *
6204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
7204076Spjd * the FreeBSD Foundation.
8204076Spjd *
9204076Spjd * Redistribution and use in source and binary forms, with or without
10204076Spjd * modification, are permitted provided that the following conditions
11204076Spjd * are met:
12204076Spjd * 1. Redistributions of source code must retain the above copyright
13204076Spjd *    notice, this list of conditions and the following disclaimer.
14204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
15204076Spjd *    notice, this list of conditions and the following disclaimer in the
16204076Spjd *    documentation and/or other materials provided with the distribution.
17204076Spjd *
18204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28204076Spjd * SUCH DAMAGE.
29204076Spjd */
30204076Spjd
31204076Spjd#include <sys/cdefs.h>
32204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/hooks.c 213429 2010-10-04 21:43:06Z pjd $");
33204076Spjd
34204076Spjd#include <sys/types.h>
35211885Spjd#include <sys/sysctl.h>
36204076Spjd#include <sys/wait.h>
37204076Spjd
38204076Spjd#include <assert.h>
39211885Spjd#include <errno.h>
40204076Spjd#include <fcntl.h>
41211885Spjd#include <libgen.h>
42211885Spjd#include <paths.h>
43211885Spjd#include <signal.h>
44211885Spjd#include <stdbool.h>
45211885Spjd#include <stdint.h>
46204076Spjd#include <stdio.h>
47204076Spjd#include <stdlib.h>
48204076Spjd#include <string.h>
49204076Spjd#include <syslog.h>
50211885Spjd#include <unistd.h>
51204076Spjd
52204076Spjd#include <pjdlog.h>
53204076Spjd
54204076Spjd#include "hooks.h"
55211885Spjd#include "synch.h"
56204076Spjd
57211885Spjd/* Report processes that are running for too long not often than this value. */
58211885Spjd#define	REPORT_INTERVAL	60
59211885Spjd
60211885Spjd/* Are we initialized? */
61211885Spjdstatic bool hooks_initialized = false;
62211885Spjd
63211885Spjd/*
64211885Spjd * Keep all processes we forked on a global queue, so we can report nicely
65211885Spjd * when they finish or report that they are running for a long time.
66211885Spjd */
67211885Spjd#define	HOOKPROC_MAGIC_ALLOCATED	0x80090ca
68211885Spjd#define	HOOKPROC_MAGIC_ONLIST		0x80090c0
69211885Spjdstruct hookproc {
70211885Spjd	/* Magic. */
71211885Spjd	int	hp_magic;
72211885Spjd	/* PID of a forked child. */
73211885Spjd	pid_t	hp_pid;
74211885Spjd	/* When process were forked? */
75211885Spjd	time_t	hp_birthtime;
76211885Spjd	/* When we logged previous reported? */
77211885Spjd	time_t	hp_lastreport;
78211885Spjd	/* Path to executable and all the arguments we passed. */
79211885Spjd	char	hp_comm[PATH_MAX];
80211885Spjd	TAILQ_ENTRY(hookproc) hp_next;
81211885Spjd};
82211885Spjdstatic TAILQ_HEAD(, hookproc) hookprocs;
83211885Spjdstatic pthread_mutex_t hookprocs_lock;
84211885Spjd
85211976Spjdstatic void hook_remove(struct hookproc *hp);
86211976Spjdstatic void hook_free(struct hookproc *hp);
87211976Spjd
88204076Spjdstatic void
89204076Spjddescriptors(void)
90204076Spjd{
91204076Spjd	long maxfd;
92204076Spjd	int fd;
93204076Spjd
94204076Spjd	/*
95204076Spjd	 * Close all descriptors.
96204076Spjd	 */
97204076Spjd	maxfd = sysconf(_SC_OPEN_MAX);
98204076Spjd	if (maxfd < 0) {
99204076Spjd		pjdlog_errno(LOG_WARNING, "sysconf(_SC_OPEN_MAX) failed");
100204076Spjd		maxfd = 1024;
101204076Spjd	}
102211884Spjd	for (fd = 0; fd <= maxfd; fd++) {
103211884Spjd		switch (fd) {
104211884Spjd		case STDIN_FILENO:
105211884Spjd		case STDOUT_FILENO:
106211884Spjd		case STDERR_FILENO:
107211884Spjd			if (pjdlog_mode_get() == PJDLOG_MODE_STD)
108211884Spjd				break;
109211884Spjd			/* FALLTHROUGH */
110211884Spjd		default:
111211884Spjd			close(fd);
112211884Spjd			break;
113211884Spjd		}
114211884Spjd	}
115211884Spjd	if (pjdlog_mode_get() == PJDLOG_MODE_STD)
116211884Spjd		return;
117204076Spjd	/*
118204076Spjd	 * Redirect stdin, stdout and stderr to /dev/null.
119204076Spjd	 */
120204076Spjd	fd = open(_PATH_DEVNULL, O_RDONLY);
121204076Spjd	if (fd < 0) {
122204076Spjd		pjdlog_errno(LOG_WARNING, "Unable to open %s for reading",
123204076Spjd		    _PATH_DEVNULL);
124204076Spjd	} else if (fd != STDIN_FILENO) {
125204076Spjd		if (dup2(fd, STDIN_FILENO) < 0) {
126204076Spjd			pjdlog_errno(LOG_WARNING,
127204076Spjd			    "Unable to duplicate descriptor for stdin");
128204076Spjd		}
129204076Spjd		close(fd);
130204076Spjd	}
131204076Spjd	fd = open(_PATH_DEVNULL, O_WRONLY);
132204076Spjd	if (fd < 0) {
133204076Spjd		pjdlog_errno(LOG_WARNING, "Unable to open %s for writing",
134204076Spjd		    _PATH_DEVNULL);
135204076Spjd	} else {
136204076Spjd		if (fd != STDOUT_FILENO && dup2(fd, STDOUT_FILENO) < 0) {
137204076Spjd			pjdlog_errno(LOG_WARNING,
138204076Spjd			    "Unable to duplicate descriptor for stdout");
139204076Spjd		}
140204076Spjd		if (fd != STDERR_FILENO && dup2(fd, STDERR_FILENO) < 0) {
141204076Spjd			pjdlog_errno(LOG_WARNING,
142204076Spjd			    "Unable to duplicate descriptor for stderr");
143204076Spjd		}
144204076Spjd		if (fd != STDOUT_FILENO && fd != STDERR_FILENO)
145204076Spjd			close(fd);
146204076Spjd	}
147204076Spjd}
148204076Spjd
149211885Spjdvoid
150211885Spjdhook_init(void)
151211885Spjd{
152211885Spjd
153211976Spjd	assert(!hooks_initialized);
154211976Spjd
155211885Spjd	mtx_init(&hookprocs_lock);
156211885Spjd	TAILQ_INIT(&hookprocs);
157211885Spjd	hooks_initialized = true;
158211885Spjd}
159211885Spjd
160211976Spjdvoid
161211976Spjdhook_fini(void)
162211976Spjd{
163211976Spjd	struct hookproc *hp;
164211976Spjd
165211976Spjd	assert(hooks_initialized);
166211976Spjd
167211976Spjd	mtx_lock(&hookprocs_lock);
168211976Spjd	while ((hp = TAILQ_FIRST(&hookprocs)) != NULL) {
169211976Spjd		assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
170211976Spjd		assert(hp->hp_pid > 0);
171211976Spjd
172211976Spjd		hook_remove(hp);
173211976Spjd		hook_free(hp);
174211976Spjd	}
175211976Spjd	mtx_unlock(&hookprocs_lock);
176211976Spjd
177211976Spjd	mtx_destroy(&hookprocs_lock);
178211976Spjd	TAILQ_INIT(&hookprocs);
179211976Spjd	hooks_initialized = false;
180211976Spjd}
181211976Spjd
182211885Spjdstatic struct hookproc *
183211885Spjdhook_alloc(const char *path, char **args)
184211885Spjd{
185211885Spjd	struct hookproc *hp;
186211885Spjd	unsigned int ii;
187211885Spjd
188211885Spjd	hp = malloc(sizeof(*hp));
189211885Spjd	if (hp == NULL) {
190211885Spjd		pjdlog_error("Unable to allocate %zu bytes of memory for a hook.",
191211885Spjd		    sizeof(*hp));
192211885Spjd		return (NULL);
193211885Spjd	}
194211885Spjd
195211885Spjd	hp->hp_pid = 0;
196211885Spjd	hp->hp_birthtime = hp->hp_lastreport = time(NULL);
197211885Spjd	(void)strlcpy(hp->hp_comm, path, sizeof(hp->hp_comm));
198211885Spjd	/* We start at 2nd argument as we don't want to have exec name twice. */
199211885Spjd	for (ii = 1; args[ii] != NULL; ii++) {
200211885Spjd		(void)strlcat(hp->hp_comm, " ", sizeof(hp->hp_comm));
201211885Spjd		(void)strlcat(hp->hp_comm, args[ii], sizeof(hp->hp_comm));
202211885Spjd	}
203211885Spjd	if (strlen(hp->hp_comm) >= sizeof(hp->hp_comm) - 1) {
204211885Spjd		pjdlog_error("Exec path too long, correct configuration file.");
205211885Spjd		free(hp);
206211885Spjd		return (NULL);
207211885Spjd	}
208211885Spjd	hp->hp_magic = HOOKPROC_MAGIC_ALLOCATED;
209211885Spjd	return (hp);
210211885Spjd}
211211885Spjd
212211885Spjdstatic void
213211885Spjdhook_add(struct hookproc *hp, pid_t pid)
214211885Spjd{
215211885Spjd
216211885Spjd	assert(hp->hp_magic == HOOKPROC_MAGIC_ALLOCATED);
217211885Spjd	assert(hp->hp_pid == 0);
218211885Spjd
219211885Spjd	hp->hp_pid = pid;
220211885Spjd	mtx_lock(&hookprocs_lock);
221211885Spjd	hp->hp_magic = HOOKPROC_MAGIC_ONLIST;
222211885Spjd	TAILQ_INSERT_TAIL(&hookprocs, hp, hp_next);
223211885Spjd	mtx_unlock(&hookprocs_lock);
224211885Spjd}
225211885Spjd
226211885Spjdstatic void
227211885Spjdhook_remove(struct hookproc *hp)
228211885Spjd{
229211885Spjd
230211885Spjd	assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
231211885Spjd	assert(hp->hp_pid > 0);
232211885Spjd	assert(mtx_owned(&hookprocs_lock));
233211885Spjd
234211885Spjd	TAILQ_REMOVE(&hookprocs, hp, hp_next);
235211885Spjd	hp->hp_magic = HOOKPROC_MAGIC_ALLOCATED;
236211885Spjd}
237211885Spjd
238211885Spjdstatic void
239211885Spjdhook_free(struct hookproc *hp)
240211885Spjd{
241211885Spjd
242211885Spjd	assert(hp->hp_magic == HOOKPROC_MAGIC_ALLOCATED);
243211885Spjd	assert(hp->hp_pid > 0);
244211885Spjd
245211885Spjd	hp->hp_magic = 0;
246211885Spjd	free(hp);
247211885Spjd}
248211885Spjd
249211885Spjdstatic struct hookproc *
250211885Spjdhook_find(pid_t pid)
251211885Spjd{
252211885Spjd	struct hookproc *hp;
253211885Spjd
254211885Spjd	assert(mtx_owned(&hookprocs_lock));
255211885Spjd
256211885Spjd	TAILQ_FOREACH(hp, &hookprocs, hp_next) {
257211885Spjd		assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
258211885Spjd		assert(hp->hp_pid > 0);
259211885Spjd
260211885Spjd		if (hp->hp_pid == pid)
261211885Spjd			break;
262211885Spjd	}
263211885Spjd
264211885Spjd	return (hp);
265211885Spjd}
266211885Spjd
267211885Spjdvoid
268211976Spjdhook_check_one(pid_t pid, int status)
269211976Spjd{
270211976Spjd	struct hookproc *hp;
271211976Spjd
272211976Spjd	mtx_lock(&hookprocs_lock);
273211976Spjd	hp = hook_find(pid);
274211976Spjd	if (hp == NULL) {
275211976Spjd		mtx_unlock(&hookprocs_lock);
276211976Spjd		pjdlog_debug(1, "Unknown process pid=%u", pid);
277211976Spjd		return;
278211976Spjd	}
279211976Spjd	hook_remove(hp);
280211976Spjd	mtx_unlock(&hookprocs_lock);
281211976Spjd	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
282211976Spjd		pjdlog_debug(1, "Hook exited gracefully (pid=%u, cmd=[%s]).",
283211976Spjd		    pid, hp->hp_comm);
284211976Spjd	} else if (WIFSIGNALED(status)) {
285211976Spjd		pjdlog_error("Hook was killed (pid=%u, signal=%d, cmd=[%s]).",
286211976Spjd		    pid, WTERMSIG(status), hp->hp_comm);
287211976Spjd	} else {
288211976Spjd		pjdlog_error("Hook exited ungracefully (pid=%u, exitcode=%d, cmd=[%s]).",
289211976Spjd		    pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1,
290211976Spjd		    hp->hp_comm);
291211976Spjd	}
292211976Spjd	hook_free(hp);
293211976Spjd}
294211976Spjd
295211976Spjdvoid
296213429Spjdhook_check(void)
297211885Spjd{
298211885Spjd	struct hookproc *hp, *hp2;
299211885Spjd	time_t now;
300211885Spjd
301211885Spjd	assert(hooks_initialized);
302211885Spjd
303211885Spjd	/*
304211885Spjd	 * Report about processes that are running for a long time.
305211885Spjd	 */
306211885Spjd	now = time(NULL);
307211885Spjd	mtx_lock(&hookprocs_lock);
308211885Spjd	TAILQ_FOREACH_SAFE(hp, &hookprocs, hp_next, hp2) {
309211885Spjd		assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
310211885Spjd		assert(hp->hp_pid > 0);
311211885Spjd
312211885Spjd		/*
313211885Spjd		 * If process doesn't exists we somehow missed it.
314211885Spjd		 * Not much can be done expect for logging this situation.
315211885Spjd		 */
316211885Spjd		if (kill(hp->hp_pid, 0) == -1 && errno == ESRCH) {
317211885Spjd			pjdlog_warning("Hook disappeared (pid=%u, cmd=[%s]).",
318211885Spjd			    hp->hp_pid, hp->hp_comm);
319211885Spjd			hook_remove(hp);
320211885Spjd			hook_free(hp);
321211885Spjd			continue;
322211885Spjd		}
323211885Spjd
324211885Spjd		/*
325211885Spjd		 * Skip proccesses younger than 1 minute.
326211885Spjd		 */
327211885Spjd		if (now - hp->hp_lastreport < REPORT_INTERVAL)
328211885Spjd			continue;
329211885Spjd
330211885Spjd		/*
331211885Spjd		 * Hook is running for too long, report it.
332211885Spjd		 */
333211885Spjd		pjdlog_warning("Hook is running for %ju seconds (pid=%u, cmd=[%s]).",
334211885Spjd		    (uintmax_t)(now - hp->hp_birthtime), hp->hp_pid,
335211885Spjd		    hp->hp_comm);
336211885Spjd		hp->hp_lastreport = now;
337211885Spjd	}
338211885Spjd	mtx_unlock(&hookprocs_lock);
339211885Spjd}
340211885Spjd
341211885Spjdvoid
342204076Spjdhook_exec(const char *path, ...)
343204076Spjd{
344204076Spjd	va_list ap;
345204076Spjd
346204076Spjd	va_start(ap, path);
347211885Spjd	hook_execv(path, ap);
348204076Spjd	va_end(ap);
349204076Spjd}
350204076Spjd
351211885Spjdvoid
352204076Spjdhook_execv(const char *path, va_list ap)
353204076Spjd{
354211885Spjd	struct hookproc *hp;
355204076Spjd	char *args[64];
356204076Spjd	unsigned int ii;
357211885Spjd	pid_t pid;
358204076Spjd
359211885Spjd	assert(hooks_initialized);
360211885Spjd
361204076Spjd	if (path == NULL || path[0] == '\0')
362211885Spjd		return;
363204076Spjd
364204076Spjd	memset(args, 0, sizeof(args));
365204076Spjd	args[0] = basename(path);
366204076Spjd	for (ii = 1; ii < sizeof(args) / sizeof(args[0]); ii++) {
367204076Spjd		args[ii] = va_arg(ap, char *);
368204076Spjd		if (args[ii] == NULL)
369204076Spjd			break;
370204076Spjd	}
371204076Spjd	assert(ii < sizeof(args) / sizeof(args[0]));
372204076Spjd
373211885Spjd	hp = hook_alloc(path, args);
374211885Spjd	if (hp == NULL)
375211885Spjd		return;
376211885Spjd
377204076Spjd	pid = fork();
378204076Spjd	switch (pid) {
379204076Spjd	case -1:	/* Error. */
380211885Spjd		pjdlog_errno(LOG_ERR, "Unable to fork to execute %s", path);
381213183Spjd		hook_free(hp);
382211885Spjd		return;
383204076Spjd	case 0:		/* Child. */
384204076Spjd		descriptors();
385204076Spjd		execv(path, args);
386204076Spjd		pjdlog_errno(LOG_ERR, "Unable to execute %s", path);
387204076Spjd		exit(EX_SOFTWARE);
388204076Spjd	default:	/* Parent. */
389211885Spjd		hook_add(hp, pid);
390204076Spjd		break;
391204076Spjd	}
392204076Spjd}
393