hooks.c revision 219816
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 219816 2011-03-21 08:37:50Z 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"
55219816Spjd#include "subr.h"
56211885Spjd#include "synch.h"
57204076Spjd
58211885Spjd/* Report processes that are running for too long not often than this value. */
59211885Spjd#define	REPORT_INTERVAL	60
60211885Spjd
61211885Spjd/* Are we initialized? */
62211885Spjdstatic bool hooks_initialized = false;
63211885Spjd
64211885Spjd/*
65211885Spjd * Keep all processes we forked on a global queue, so we can report nicely
66211885Spjd * when they finish or report that they are running for a long time.
67211885Spjd */
68211885Spjd#define	HOOKPROC_MAGIC_ALLOCATED	0x80090ca
69211885Spjd#define	HOOKPROC_MAGIC_ONLIST		0x80090c0
70211885Spjdstruct hookproc {
71211885Spjd	/* Magic. */
72211885Spjd	int	hp_magic;
73211885Spjd	/* PID of a forked child. */
74211885Spjd	pid_t	hp_pid;
75211885Spjd	/* When process were forked? */
76211885Spjd	time_t	hp_birthtime;
77211885Spjd	/* When we logged previous reported? */
78211885Spjd	time_t	hp_lastreport;
79211885Spjd	/* Path to executable and all the arguments we passed. */
80211885Spjd	char	hp_comm[PATH_MAX];
81211885Spjd	TAILQ_ENTRY(hookproc) hp_next;
82211885Spjd};
83211885Spjdstatic TAILQ_HEAD(, hookproc) hookprocs;
84211885Spjdstatic pthread_mutex_t hookprocs_lock;
85211885Spjd
86211976Spjdstatic void hook_remove(struct hookproc *hp);
87211976Spjdstatic void hook_free(struct hookproc *hp);
88211976Spjd
89204076Spjdstatic void
90204076Spjddescriptors(void)
91204076Spjd{
92204076Spjd	int fd;
93204076Spjd
94204076Spjd	/*
95214119Spjd	 * Close all (or almost all) descriptors.
96204076Spjd	 */
97214119Spjd	if (pjdlog_mode_get() == PJDLOG_MODE_STD) {
98214119Spjd		closefrom(MAX(MAX(STDIN_FILENO, STDOUT_FILENO),
99214119Spjd		    STDERR_FILENO) + 1);
100214119Spjd		return;
101204076Spjd	}
102214119Spjd
103214119Spjd	closefrom(0);
104214119Spjd
105204076Spjd	/*
106204076Spjd	 * Redirect stdin, stdout and stderr to /dev/null.
107204076Spjd	 */
108204076Spjd	fd = open(_PATH_DEVNULL, O_RDONLY);
109204076Spjd	if (fd < 0) {
110204076Spjd		pjdlog_errno(LOG_WARNING, "Unable to open %s for reading",
111204076Spjd		    _PATH_DEVNULL);
112204076Spjd	} else if (fd != STDIN_FILENO) {
113204076Spjd		if (dup2(fd, STDIN_FILENO) < 0) {
114204076Spjd			pjdlog_errno(LOG_WARNING,
115204076Spjd			    "Unable to duplicate descriptor for stdin");
116204076Spjd		}
117204076Spjd		close(fd);
118204076Spjd	}
119204076Spjd	fd = open(_PATH_DEVNULL, O_WRONLY);
120204076Spjd	if (fd < 0) {
121204076Spjd		pjdlog_errno(LOG_WARNING, "Unable to open %s for writing",
122204076Spjd		    _PATH_DEVNULL);
123204076Spjd	} else {
124204076Spjd		if (fd != STDOUT_FILENO && dup2(fd, STDOUT_FILENO) < 0) {
125204076Spjd			pjdlog_errno(LOG_WARNING,
126204076Spjd			    "Unable to duplicate descriptor for stdout");
127204076Spjd		}
128204076Spjd		if (fd != STDERR_FILENO && dup2(fd, STDERR_FILENO) < 0) {
129204076Spjd			pjdlog_errno(LOG_WARNING,
130204076Spjd			    "Unable to duplicate descriptor for stderr");
131204076Spjd		}
132204076Spjd		if (fd != STDOUT_FILENO && fd != STDERR_FILENO)
133204076Spjd			close(fd);
134204076Spjd	}
135204076Spjd}
136204076Spjd
137211885Spjdvoid
138211885Spjdhook_init(void)
139211885Spjd{
140211885Spjd
141211976Spjd	assert(!hooks_initialized);
142211976Spjd
143211885Spjd	mtx_init(&hookprocs_lock);
144211885Spjd	TAILQ_INIT(&hookprocs);
145211885Spjd	hooks_initialized = true;
146211885Spjd}
147211885Spjd
148211976Spjdvoid
149211976Spjdhook_fini(void)
150211976Spjd{
151211976Spjd	struct hookproc *hp;
152211976Spjd
153211976Spjd	assert(hooks_initialized);
154211976Spjd
155211976Spjd	mtx_lock(&hookprocs_lock);
156211976Spjd	while ((hp = TAILQ_FIRST(&hookprocs)) != NULL) {
157211976Spjd		assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
158211976Spjd		assert(hp->hp_pid > 0);
159211976Spjd
160211976Spjd		hook_remove(hp);
161211976Spjd		hook_free(hp);
162211976Spjd	}
163211976Spjd	mtx_unlock(&hookprocs_lock);
164211976Spjd
165211976Spjd	mtx_destroy(&hookprocs_lock);
166211976Spjd	TAILQ_INIT(&hookprocs);
167211976Spjd	hooks_initialized = false;
168211976Spjd}
169211976Spjd
170211885Spjdstatic struct hookproc *
171211885Spjdhook_alloc(const char *path, char **args)
172211885Spjd{
173211885Spjd	struct hookproc *hp;
174211885Spjd	unsigned int ii;
175211885Spjd
176211885Spjd	hp = malloc(sizeof(*hp));
177211885Spjd	if (hp == NULL) {
178211885Spjd		pjdlog_error("Unable to allocate %zu bytes of memory for a hook.",
179211885Spjd		    sizeof(*hp));
180211885Spjd		return (NULL);
181211885Spjd	}
182211885Spjd
183211885Spjd	hp->hp_pid = 0;
184211885Spjd	hp->hp_birthtime = hp->hp_lastreport = time(NULL);
185211885Spjd	(void)strlcpy(hp->hp_comm, path, sizeof(hp->hp_comm));
186211885Spjd	/* We start at 2nd argument as we don't want to have exec name twice. */
187211885Spjd	for (ii = 1; args[ii] != NULL; ii++) {
188219816Spjd		(void)snprlcat(hp->hp_comm, sizeof(hp->hp_comm), " %s",
189219816Spjd		    args[ii]);
190211885Spjd	}
191211885Spjd	if (strlen(hp->hp_comm) >= sizeof(hp->hp_comm) - 1) {
192211885Spjd		pjdlog_error("Exec path too long, correct configuration file.");
193211885Spjd		free(hp);
194211885Spjd		return (NULL);
195211885Spjd	}
196211885Spjd	hp->hp_magic = HOOKPROC_MAGIC_ALLOCATED;
197211885Spjd	return (hp);
198211885Spjd}
199211885Spjd
200211885Spjdstatic void
201211885Spjdhook_add(struct hookproc *hp, pid_t pid)
202211885Spjd{
203211885Spjd
204211885Spjd	assert(hp->hp_magic == HOOKPROC_MAGIC_ALLOCATED);
205211885Spjd	assert(hp->hp_pid == 0);
206211885Spjd
207211885Spjd	hp->hp_pid = pid;
208211885Spjd	mtx_lock(&hookprocs_lock);
209211885Spjd	hp->hp_magic = HOOKPROC_MAGIC_ONLIST;
210211885Spjd	TAILQ_INSERT_TAIL(&hookprocs, hp, hp_next);
211211885Spjd	mtx_unlock(&hookprocs_lock);
212211885Spjd}
213211885Spjd
214211885Spjdstatic void
215211885Spjdhook_remove(struct hookproc *hp)
216211885Spjd{
217211885Spjd
218211885Spjd	assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
219211885Spjd	assert(hp->hp_pid > 0);
220211885Spjd	assert(mtx_owned(&hookprocs_lock));
221211885Spjd
222211885Spjd	TAILQ_REMOVE(&hookprocs, hp, hp_next);
223211885Spjd	hp->hp_magic = HOOKPROC_MAGIC_ALLOCATED;
224211885Spjd}
225211885Spjd
226211885Spjdstatic void
227211885Spjdhook_free(struct hookproc *hp)
228211885Spjd{
229211885Spjd
230211885Spjd	assert(hp->hp_magic == HOOKPROC_MAGIC_ALLOCATED);
231211885Spjd	assert(hp->hp_pid > 0);
232211885Spjd
233211885Spjd	hp->hp_magic = 0;
234211885Spjd	free(hp);
235211885Spjd}
236211885Spjd
237211885Spjdstatic struct hookproc *
238211885Spjdhook_find(pid_t pid)
239211885Spjd{
240211885Spjd	struct hookproc *hp;
241211885Spjd
242211885Spjd	assert(mtx_owned(&hookprocs_lock));
243211885Spjd
244211885Spjd	TAILQ_FOREACH(hp, &hookprocs, hp_next) {
245211885Spjd		assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
246211885Spjd		assert(hp->hp_pid > 0);
247211885Spjd
248211885Spjd		if (hp->hp_pid == pid)
249211885Spjd			break;
250211885Spjd	}
251211885Spjd
252211885Spjd	return (hp);
253211885Spjd}
254211885Spjd
255211885Spjdvoid
256211976Spjdhook_check_one(pid_t pid, int status)
257211976Spjd{
258211976Spjd	struct hookproc *hp;
259211976Spjd
260211976Spjd	mtx_lock(&hookprocs_lock);
261211976Spjd	hp = hook_find(pid);
262211976Spjd	if (hp == NULL) {
263211976Spjd		mtx_unlock(&hookprocs_lock);
264211976Spjd		pjdlog_debug(1, "Unknown process pid=%u", pid);
265211976Spjd		return;
266211976Spjd	}
267211976Spjd	hook_remove(hp);
268211976Spjd	mtx_unlock(&hookprocs_lock);
269211976Spjd	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
270211976Spjd		pjdlog_debug(1, "Hook exited gracefully (pid=%u, cmd=[%s]).",
271211976Spjd		    pid, hp->hp_comm);
272211976Spjd	} else if (WIFSIGNALED(status)) {
273211976Spjd		pjdlog_error("Hook was killed (pid=%u, signal=%d, cmd=[%s]).",
274211976Spjd		    pid, WTERMSIG(status), hp->hp_comm);
275211976Spjd	} else {
276211976Spjd		pjdlog_error("Hook exited ungracefully (pid=%u, exitcode=%d, cmd=[%s]).",
277211976Spjd		    pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1,
278211976Spjd		    hp->hp_comm);
279211976Spjd	}
280211976Spjd	hook_free(hp);
281211976Spjd}
282211976Spjd
283211976Spjdvoid
284213429Spjdhook_check(void)
285211885Spjd{
286211885Spjd	struct hookproc *hp, *hp2;
287211885Spjd	time_t now;
288211885Spjd
289211885Spjd	assert(hooks_initialized);
290211885Spjd
291211885Spjd	/*
292211885Spjd	 * Report about processes that are running for a long time.
293211885Spjd	 */
294211885Spjd	now = time(NULL);
295211885Spjd	mtx_lock(&hookprocs_lock);
296211885Spjd	TAILQ_FOREACH_SAFE(hp, &hookprocs, hp_next, hp2) {
297211885Spjd		assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
298211885Spjd		assert(hp->hp_pid > 0);
299211885Spjd
300211885Spjd		/*
301211885Spjd		 * If process doesn't exists we somehow missed it.
302211885Spjd		 * Not much can be done expect for logging this situation.
303211885Spjd		 */
304211885Spjd		if (kill(hp->hp_pid, 0) == -1 && errno == ESRCH) {
305211885Spjd			pjdlog_warning("Hook disappeared (pid=%u, cmd=[%s]).",
306211885Spjd			    hp->hp_pid, hp->hp_comm);
307211885Spjd			hook_remove(hp);
308211885Spjd			hook_free(hp);
309211885Spjd			continue;
310211885Spjd		}
311211885Spjd
312211885Spjd		/*
313211885Spjd		 * Skip proccesses younger than 1 minute.
314211885Spjd		 */
315211885Spjd		if (now - hp->hp_lastreport < REPORT_INTERVAL)
316211885Spjd			continue;
317211885Spjd
318211885Spjd		/*
319211885Spjd		 * Hook is running for too long, report it.
320211885Spjd		 */
321211885Spjd		pjdlog_warning("Hook is running for %ju seconds (pid=%u, cmd=[%s]).",
322211885Spjd		    (uintmax_t)(now - hp->hp_birthtime), hp->hp_pid,
323211885Spjd		    hp->hp_comm);
324211885Spjd		hp->hp_lastreport = now;
325211885Spjd	}
326211885Spjd	mtx_unlock(&hookprocs_lock);
327211885Spjd}
328211885Spjd
329211885Spjdvoid
330204076Spjdhook_exec(const char *path, ...)
331204076Spjd{
332204076Spjd	va_list ap;
333204076Spjd
334204076Spjd	va_start(ap, path);
335211885Spjd	hook_execv(path, ap);
336204076Spjd	va_end(ap);
337204076Spjd}
338204076Spjd
339211885Spjdvoid
340204076Spjdhook_execv(const char *path, va_list ap)
341204076Spjd{
342211885Spjd	struct hookproc *hp;
343204076Spjd	char *args[64];
344204076Spjd	unsigned int ii;
345213938Spjd	sigset_t mask;
346211885Spjd	pid_t pid;
347204076Spjd
348211885Spjd	assert(hooks_initialized);
349211885Spjd
350204076Spjd	if (path == NULL || path[0] == '\0')
351211885Spjd		return;
352204076Spjd
353204076Spjd	memset(args, 0, sizeof(args));
354204076Spjd	args[0] = basename(path);
355204076Spjd	for (ii = 1; ii < sizeof(args) / sizeof(args[0]); ii++) {
356204076Spjd		args[ii] = va_arg(ap, char *);
357204076Spjd		if (args[ii] == NULL)
358204076Spjd			break;
359204076Spjd	}
360204076Spjd	assert(ii < sizeof(args) / sizeof(args[0]));
361204076Spjd
362211885Spjd	hp = hook_alloc(path, args);
363211885Spjd	if (hp == NULL)
364211885Spjd		return;
365211885Spjd
366204076Spjd	pid = fork();
367204076Spjd	switch (pid) {
368204076Spjd	case -1:	/* Error. */
369211885Spjd		pjdlog_errno(LOG_ERR, "Unable to fork to execute %s", path);
370213183Spjd		hook_free(hp);
371211885Spjd		return;
372204076Spjd	case 0:		/* Child. */
373204076Spjd		descriptors();
374213938Spjd		PJDLOG_VERIFY(sigemptyset(&mask) == 0);
375213938Spjd		PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
376217308Spjd		/*
377217312Spjd		 * Dummy handler set for SIGCHLD in the parent will be restored
378217312Spjd		 * to SIG_IGN on execv(3) below, so there is no need to do
379217312Spjd		 * anything with it.
380217308Spjd		 */
381204076Spjd		execv(path, args);
382204076Spjd		pjdlog_errno(LOG_ERR, "Unable to execute %s", path);
383204076Spjd		exit(EX_SOFTWARE);
384204076Spjd	default:	/* Parent. */
385211885Spjd		hook_add(hp, pid);
386204076Spjd		break;
387204076Spjd	}
388204076Spjd}
389