xref: /freebsd-src/sbin/hastd/hooks.c (revision 32e86a82f54826f14ea381affa6674db3aa3b5ae)
132115b10SPawel Jakub Dawidek /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
432115b10SPawel Jakub Dawidek  * Copyright (c) 2010 The FreeBSD Foundation
51cdaf10cSPawel Jakub Dawidek  * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
632115b10SPawel Jakub Dawidek  * All rights reserved.
732115b10SPawel Jakub Dawidek  *
832115b10SPawel Jakub Dawidek  * This software was developed by Pawel Jakub Dawidek under sponsorship from
932115b10SPawel Jakub Dawidek  * the FreeBSD Foundation.
1032115b10SPawel Jakub Dawidek  *
1132115b10SPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
1232115b10SPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
1332115b10SPawel Jakub Dawidek  * are met:
1432115b10SPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
1532115b10SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
1632115b10SPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
1732115b10SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
1832115b10SPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
1932115b10SPawel Jakub Dawidek  *
2032115b10SPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
2132115b10SPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2232115b10SPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2332115b10SPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2432115b10SPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2532115b10SPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2632115b10SPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2732115b10SPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2832115b10SPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2932115b10SPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3032115b10SPawel Jakub Dawidek  * SUCH DAMAGE.
3132115b10SPawel Jakub Dawidek  */
3232115b10SPawel Jakub Dawidek 
3332115b10SPawel Jakub Dawidek #include <sys/types.h>
341cdaf10cSPawel Jakub Dawidek #include <sys/sysctl.h>
3532115b10SPawel Jakub Dawidek #include <sys/wait.h>
3632115b10SPawel Jakub Dawidek 
371cdaf10cSPawel Jakub Dawidek #include <errno.h>
3832115b10SPawel Jakub Dawidek #include <fcntl.h>
3932115b10SPawel Jakub Dawidek #include <paths.h>
401cdaf10cSPawel Jakub Dawidek #include <signal.h>
411cdaf10cSPawel Jakub Dawidek #include <stdbool.h>
421cdaf10cSPawel Jakub Dawidek #include <stdint.h>
431cdaf10cSPawel Jakub Dawidek #include <stdio.h>
441cdaf10cSPawel Jakub Dawidek #include <stdlib.h>
451cdaf10cSPawel Jakub Dawidek #include <string.h>
461cdaf10cSPawel Jakub Dawidek #include <syslog.h>
471cdaf10cSPawel Jakub Dawidek #include <unistd.h>
4832115b10SPawel Jakub Dawidek 
4932115b10SPawel Jakub Dawidek #include <pjdlog.h>
5032115b10SPawel Jakub Dawidek 
5132115b10SPawel Jakub Dawidek #include "hooks.h"
528a8763b7SPawel Jakub Dawidek #include "subr.h"
531cdaf10cSPawel Jakub Dawidek #include "synch.h"
541cdaf10cSPawel Jakub Dawidek 
551cdaf10cSPawel Jakub Dawidek /* Report processes that are running for too long not often than this value. */
561cdaf10cSPawel Jakub Dawidek #define	REPORT_INTERVAL	60
571cdaf10cSPawel Jakub Dawidek 
581cdaf10cSPawel Jakub Dawidek /* Are we initialized? */
591cdaf10cSPawel Jakub Dawidek static bool hooks_initialized = false;
601cdaf10cSPawel Jakub Dawidek 
611cdaf10cSPawel Jakub Dawidek /*
621cdaf10cSPawel Jakub Dawidek  * Keep all processes we forked on a global queue, so we can report nicely
631cdaf10cSPawel Jakub Dawidek  * when they finish or report that they are running for a long time.
641cdaf10cSPawel Jakub Dawidek  */
651cdaf10cSPawel Jakub Dawidek #define	HOOKPROC_MAGIC_ALLOCATED	0x80090ca
661cdaf10cSPawel Jakub Dawidek #define	HOOKPROC_MAGIC_ONLIST		0x80090c0
671cdaf10cSPawel Jakub Dawidek struct hookproc {
681cdaf10cSPawel Jakub Dawidek 	/* Magic. */
691cdaf10cSPawel Jakub Dawidek 	int	hp_magic;
701cdaf10cSPawel Jakub Dawidek 	/* PID of a forked child. */
711cdaf10cSPawel Jakub Dawidek 	pid_t	hp_pid;
721cdaf10cSPawel Jakub Dawidek 	/* When process were forked? */
731cdaf10cSPawel Jakub Dawidek 	time_t	hp_birthtime;
741cdaf10cSPawel Jakub Dawidek 	/* When we logged previous reported? */
751cdaf10cSPawel Jakub Dawidek 	time_t	hp_lastreport;
761cdaf10cSPawel Jakub Dawidek 	/* Path to executable and all the arguments we passed. */
771cdaf10cSPawel Jakub Dawidek 	char	hp_comm[PATH_MAX];
781cdaf10cSPawel Jakub Dawidek 	TAILQ_ENTRY(hookproc) hp_next;
791cdaf10cSPawel Jakub Dawidek };
801cdaf10cSPawel Jakub Dawidek static TAILQ_HEAD(, hookproc) hookprocs;
811cdaf10cSPawel Jakub Dawidek static pthread_mutex_t hookprocs_lock;
8232115b10SPawel Jakub Dawidek 
8325ec2e3eSPawel Jakub Dawidek static void hook_remove(struct hookproc *hp);
8425ec2e3eSPawel Jakub Dawidek static void hook_free(struct hookproc *hp);
8525ec2e3eSPawel Jakub Dawidek 
8632115b10SPawel Jakub Dawidek static void
descriptors(void)8732115b10SPawel Jakub Dawidek descriptors(void)
8832115b10SPawel Jakub Dawidek {
8932115b10SPawel Jakub Dawidek 	int fd;
9032115b10SPawel Jakub Dawidek 
9132115b10SPawel Jakub Dawidek 	/*
926c71649cSPawel Jakub Dawidek 	 * Close all (or almost all) descriptors.
9332115b10SPawel Jakub Dawidek 	 */
946c71649cSPawel Jakub Dawidek 	if (pjdlog_mode_get() == PJDLOG_MODE_STD) {
956c71649cSPawel Jakub Dawidek 		closefrom(MAX(MAX(STDIN_FILENO, STDOUT_FILENO),
966c71649cSPawel Jakub Dawidek 		    STDERR_FILENO) + 1);
97e64887c4SPawel Jakub Dawidek 		return;
986c71649cSPawel Jakub Dawidek 	}
996c71649cSPawel Jakub Dawidek 
1006c71649cSPawel Jakub Dawidek 	closefrom(0);
1016c71649cSPawel Jakub Dawidek 
10232115b10SPawel Jakub Dawidek 	/*
10332115b10SPawel Jakub Dawidek 	 * Redirect stdin, stdout and stderr to /dev/null.
10432115b10SPawel Jakub Dawidek 	 */
10532115b10SPawel Jakub Dawidek 	fd = open(_PATH_DEVNULL, O_RDONLY);
1062b1b224dSPawel Jakub Dawidek 	if (fd == -1) {
10732115b10SPawel Jakub Dawidek 		pjdlog_errno(LOG_WARNING, "Unable to open %s for reading",
10832115b10SPawel Jakub Dawidek 		    _PATH_DEVNULL);
10932115b10SPawel Jakub Dawidek 	} else if (fd != STDIN_FILENO) {
1102b1b224dSPawel Jakub Dawidek 		if (dup2(fd, STDIN_FILENO) == -1) {
11132115b10SPawel Jakub Dawidek 			pjdlog_errno(LOG_WARNING,
11232115b10SPawel Jakub Dawidek 			    "Unable to duplicate descriptor for stdin");
11332115b10SPawel Jakub Dawidek 		}
11432115b10SPawel Jakub Dawidek 		close(fd);
11532115b10SPawel Jakub Dawidek 	}
11632115b10SPawel Jakub Dawidek 	fd = open(_PATH_DEVNULL, O_WRONLY);
1172b1b224dSPawel Jakub Dawidek 	if (fd == -1) {
11832115b10SPawel Jakub Dawidek 		pjdlog_errno(LOG_WARNING, "Unable to open %s for writing",
11932115b10SPawel Jakub Dawidek 		    _PATH_DEVNULL);
12032115b10SPawel Jakub Dawidek 	} else {
1212b1b224dSPawel Jakub Dawidek 		if (fd != STDOUT_FILENO && dup2(fd, STDOUT_FILENO) == -1) {
12232115b10SPawel Jakub Dawidek 			pjdlog_errno(LOG_WARNING,
12332115b10SPawel Jakub Dawidek 			    "Unable to duplicate descriptor for stdout");
12432115b10SPawel Jakub Dawidek 		}
1252b1b224dSPawel Jakub Dawidek 		if (fd != STDERR_FILENO && dup2(fd, STDERR_FILENO) == -1) {
12632115b10SPawel Jakub Dawidek 			pjdlog_errno(LOG_WARNING,
12732115b10SPawel Jakub Dawidek 			    "Unable to duplicate descriptor for stderr");
12832115b10SPawel Jakub Dawidek 		}
12932115b10SPawel Jakub Dawidek 		if (fd != STDOUT_FILENO && fd != STDERR_FILENO)
13032115b10SPawel Jakub Dawidek 			close(fd);
13132115b10SPawel Jakub Dawidek 	}
13232115b10SPawel Jakub Dawidek }
13332115b10SPawel Jakub Dawidek 
1341cdaf10cSPawel Jakub Dawidek void
hook_init(void)1351cdaf10cSPawel Jakub Dawidek hook_init(void)
1361cdaf10cSPawel Jakub Dawidek {
1371cdaf10cSPawel Jakub Dawidek 
138adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(!hooks_initialized);
13925ec2e3eSPawel Jakub Dawidek 
1401cdaf10cSPawel Jakub Dawidek 	mtx_init(&hookprocs_lock);
1411cdaf10cSPawel Jakub Dawidek 	TAILQ_INIT(&hookprocs);
1421cdaf10cSPawel Jakub Dawidek 	hooks_initialized = true;
1431cdaf10cSPawel Jakub Dawidek }
1441cdaf10cSPawel Jakub Dawidek 
14525ec2e3eSPawel Jakub Dawidek void
hook_fini(void)14625ec2e3eSPawel Jakub Dawidek hook_fini(void)
14725ec2e3eSPawel Jakub Dawidek {
14825ec2e3eSPawel Jakub Dawidek 	struct hookproc *hp;
14925ec2e3eSPawel Jakub Dawidek 
150adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(hooks_initialized);
15125ec2e3eSPawel Jakub Dawidek 
15225ec2e3eSPawel Jakub Dawidek 	mtx_lock(&hookprocs_lock);
15325ec2e3eSPawel Jakub Dawidek 	while ((hp = TAILQ_FIRST(&hookprocs)) != NULL) {
154adf8002bSPawel Jakub Dawidek 		PJDLOG_ASSERT(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
155adf8002bSPawel Jakub Dawidek 		PJDLOG_ASSERT(hp->hp_pid > 0);
15625ec2e3eSPawel Jakub Dawidek 
15725ec2e3eSPawel Jakub Dawidek 		hook_remove(hp);
15825ec2e3eSPawel Jakub Dawidek 		hook_free(hp);
15925ec2e3eSPawel Jakub Dawidek 	}
16025ec2e3eSPawel Jakub Dawidek 	mtx_unlock(&hookprocs_lock);
16125ec2e3eSPawel Jakub Dawidek 
16225ec2e3eSPawel Jakub Dawidek 	mtx_destroy(&hookprocs_lock);
16325ec2e3eSPawel Jakub Dawidek 	TAILQ_INIT(&hookprocs);
16425ec2e3eSPawel Jakub Dawidek 	hooks_initialized = false;
16525ec2e3eSPawel Jakub Dawidek }
16625ec2e3eSPawel Jakub Dawidek 
1671cdaf10cSPawel Jakub Dawidek static struct hookproc *
hook_alloc(const char * path,char ** args)1681cdaf10cSPawel Jakub Dawidek hook_alloc(const char *path, char **args)
1691cdaf10cSPawel Jakub Dawidek {
1701cdaf10cSPawel Jakub Dawidek 	struct hookproc *hp;
1711cdaf10cSPawel Jakub Dawidek 	unsigned int ii;
1721cdaf10cSPawel Jakub Dawidek 
1731cdaf10cSPawel Jakub Dawidek 	hp = malloc(sizeof(*hp));
1741cdaf10cSPawel Jakub Dawidek 	if (hp == NULL) {
1751cdaf10cSPawel Jakub Dawidek 		pjdlog_error("Unable to allocate %zu bytes of memory for a hook.",
1761cdaf10cSPawel Jakub Dawidek 		    sizeof(*hp));
1771cdaf10cSPawel Jakub Dawidek 		return (NULL);
1781cdaf10cSPawel Jakub Dawidek 	}
1791cdaf10cSPawel Jakub Dawidek 
1801cdaf10cSPawel Jakub Dawidek 	hp->hp_pid = 0;
1811cdaf10cSPawel Jakub Dawidek 	hp->hp_birthtime = hp->hp_lastreport = time(NULL);
1821cdaf10cSPawel Jakub Dawidek 	(void)strlcpy(hp->hp_comm, path, sizeof(hp->hp_comm));
1831cdaf10cSPawel Jakub Dawidek 	/* We start at 2nd argument as we don't want to have exec name twice. */
1841cdaf10cSPawel Jakub Dawidek 	for (ii = 1; args[ii] != NULL; ii++) {
1858a8763b7SPawel Jakub Dawidek 		(void)snprlcat(hp->hp_comm, sizeof(hp->hp_comm), " %s",
1868a8763b7SPawel Jakub Dawidek 		    args[ii]);
1871cdaf10cSPawel Jakub Dawidek 	}
1881cdaf10cSPawel Jakub Dawidek 	if (strlen(hp->hp_comm) >= sizeof(hp->hp_comm) - 1) {
1891cdaf10cSPawel Jakub Dawidek 		pjdlog_error("Exec path too long, correct configuration file.");
1901cdaf10cSPawel Jakub Dawidek 		free(hp);
1911cdaf10cSPawel Jakub Dawidek 		return (NULL);
1921cdaf10cSPawel Jakub Dawidek 	}
1931cdaf10cSPawel Jakub Dawidek 	hp->hp_magic = HOOKPROC_MAGIC_ALLOCATED;
1941cdaf10cSPawel Jakub Dawidek 	return (hp);
1951cdaf10cSPawel Jakub Dawidek }
1961cdaf10cSPawel Jakub Dawidek 
1971cdaf10cSPawel Jakub Dawidek static void
hook_add(struct hookproc * hp,pid_t pid)1981cdaf10cSPawel Jakub Dawidek hook_add(struct hookproc *hp, pid_t pid)
1991cdaf10cSPawel Jakub Dawidek {
2001cdaf10cSPawel Jakub Dawidek 
201adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(hp->hp_magic == HOOKPROC_MAGIC_ALLOCATED);
202adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(hp->hp_pid == 0);
2031cdaf10cSPawel Jakub Dawidek 
2041cdaf10cSPawel Jakub Dawidek 	hp->hp_pid = pid;
2051cdaf10cSPawel Jakub Dawidek 	mtx_lock(&hookprocs_lock);
2061cdaf10cSPawel Jakub Dawidek 	hp->hp_magic = HOOKPROC_MAGIC_ONLIST;
2071cdaf10cSPawel Jakub Dawidek 	TAILQ_INSERT_TAIL(&hookprocs, hp, hp_next);
2081cdaf10cSPawel Jakub Dawidek 	mtx_unlock(&hookprocs_lock);
2091cdaf10cSPawel Jakub Dawidek }
2101cdaf10cSPawel Jakub Dawidek 
2111cdaf10cSPawel Jakub Dawidek static void
hook_remove(struct hookproc * hp)2121cdaf10cSPawel Jakub Dawidek hook_remove(struct hookproc *hp)
2131cdaf10cSPawel Jakub Dawidek {
2141cdaf10cSPawel Jakub Dawidek 
215adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
216adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(hp->hp_pid > 0);
217adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(mtx_owned(&hookprocs_lock));
2181cdaf10cSPawel Jakub Dawidek 
2191cdaf10cSPawel Jakub Dawidek 	TAILQ_REMOVE(&hookprocs, hp, hp_next);
2201cdaf10cSPawel Jakub Dawidek 	hp->hp_magic = HOOKPROC_MAGIC_ALLOCATED;
2211cdaf10cSPawel Jakub Dawidek }
2221cdaf10cSPawel Jakub Dawidek 
2231cdaf10cSPawel Jakub Dawidek static void
hook_free(struct hookproc * hp)2241cdaf10cSPawel Jakub Dawidek hook_free(struct hookproc *hp)
2251cdaf10cSPawel Jakub Dawidek {
2261cdaf10cSPawel Jakub Dawidek 
227adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(hp->hp_magic == HOOKPROC_MAGIC_ALLOCATED);
228adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(hp->hp_pid > 0);
2291cdaf10cSPawel Jakub Dawidek 
2301cdaf10cSPawel Jakub Dawidek 	hp->hp_magic = 0;
2311cdaf10cSPawel Jakub Dawidek 	free(hp);
2321cdaf10cSPawel Jakub Dawidek }
2331cdaf10cSPawel Jakub Dawidek 
2341cdaf10cSPawel Jakub Dawidek static struct hookproc *
hook_find(pid_t pid)2351cdaf10cSPawel Jakub Dawidek hook_find(pid_t pid)
2361cdaf10cSPawel Jakub Dawidek {
2371cdaf10cSPawel Jakub Dawidek 	struct hookproc *hp;
2381cdaf10cSPawel Jakub Dawidek 
239adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(mtx_owned(&hookprocs_lock));
2401cdaf10cSPawel Jakub Dawidek 
2411cdaf10cSPawel Jakub Dawidek 	TAILQ_FOREACH(hp, &hookprocs, hp_next) {
242adf8002bSPawel Jakub Dawidek 		PJDLOG_ASSERT(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
243adf8002bSPawel Jakub Dawidek 		PJDLOG_ASSERT(hp->hp_pid > 0);
2441cdaf10cSPawel Jakub Dawidek 
2451cdaf10cSPawel Jakub Dawidek 		if (hp->hp_pid == pid)
2461cdaf10cSPawel Jakub Dawidek 			break;
2471cdaf10cSPawel Jakub Dawidek 	}
2481cdaf10cSPawel Jakub Dawidek 
2491cdaf10cSPawel Jakub Dawidek 	return (hp);
2501cdaf10cSPawel Jakub Dawidek }
2511cdaf10cSPawel Jakub Dawidek 
2521cdaf10cSPawel Jakub Dawidek void
hook_check_one(pid_t pid,int status)25325ec2e3eSPawel Jakub Dawidek hook_check_one(pid_t pid, int status)
2541cdaf10cSPawel Jakub Dawidek {
25525ec2e3eSPawel Jakub Dawidek 	struct hookproc *hp;
2561cdaf10cSPawel Jakub Dawidek 
2571cdaf10cSPawel Jakub Dawidek 	mtx_lock(&hookprocs_lock);
2581cdaf10cSPawel Jakub Dawidek 	hp = hook_find(pid);
2591cdaf10cSPawel Jakub Dawidek 	if (hp == NULL) {
2601cdaf10cSPawel Jakub Dawidek 		mtx_unlock(&hookprocs_lock);
26125ec2e3eSPawel Jakub Dawidek 		pjdlog_debug(1, "Unknown process pid=%u", pid);
26225ec2e3eSPawel Jakub Dawidek 		return;
2631cdaf10cSPawel Jakub Dawidek 	}
2641cdaf10cSPawel Jakub Dawidek 	hook_remove(hp);
2651cdaf10cSPawel Jakub Dawidek 	mtx_unlock(&hookprocs_lock);
2661cdaf10cSPawel Jakub Dawidek 	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
2671cdaf10cSPawel Jakub Dawidek 		pjdlog_debug(1, "Hook exited gracefully (pid=%u, cmd=[%s]).",
2681cdaf10cSPawel Jakub Dawidek 		    pid, hp->hp_comm);
2691cdaf10cSPawel Jakub Dawidek 	} else if (WIFSIGNALED(status)) {
2701cdaf10cSPawel Jakub Dawidek 		pjdlog_error("Hook was killed (pid=%u, signal=%d, cmd=[%s]).",
2711cdaf10cSPawel Jakub Dawidek 		    pid, WTERMSIG(status), hp->hp_comm);
2721cdaf10cSPawel Jakub Dawidek 	} else {
2731cdaf10cSPawel Jakub Dawidek 		pjdlog_error("Hook exited ungracefully (pid=%u, exitcode=%d, cmd=[%s]).",
2741cdaf10cSPawel Jakub Dawidek 		    pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1,
2751cdaf10cSPawel Jakub Dawidek 		    hp->hp_comm);
2761cdaf10cSPawel Jakub Dawidek 	}
2771cdaf10cSPawel Jakub Dawidek 	hook_free(hp);
2781cdaf10cSPawel Jakub Dawidek }
2791cdaf10cSPawel Jakub Dawidek 
28025ec2e3eSPawel Jakub Dawidek void
hook_check(void)2815f24b330SPawel Jakub Dawidek hook_check(void)
28225ec2e3eSPawel Jakub Dawidek {
28325ec2e3eSPawel Jakub Dawidek 	struct hookproc *hp, *hp2;
28425ec2e3eSPawel Jakub Dawidek 	time_t now;
28525ec2e3eSPawel Jakub Dawidek 
286adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(hooks_initialized);
28725ec2e3eSPawel Jakub Dawidek 
2882b5ad0e0SPawel Jakub Dawidek 	pjdlog_debug(2, "Checking hooks.");
2891884f6bbSPawel Jakub Dawidek 
29025ec2e3eSPawel Jakub Dawidek 	/*
2911cdaf10cSPawel Jakub Dawidek 	 * Report about processes that are running for a long time.
2921cdaf10cSPawel Jakub Dawidek 	 */
2931cdaf10cSPawel Jakub Dawidek 	now = time(NULL);
2941cdaf10cSPawel Jakub Dawidek 	mtx_lock(&hookprocs_lock);
2951cdaf10cSPawel Jakub Dawidek 	TAILQ_FOREACH_SAFE(hp, &hookprocs, hp_next, hp2) {
296adf8002bSPawel Jakub Dawidek 		PJDLOG_ASSERT(hp->hp_magic == HOOKPROC_MAGIC_ONLIST);
297adf8002bSPawel Jakub Dawidek 		PJDLOG_ASSERT(hp->hp_pid > 0);
2981cdaf10cSPawel Jakub Dawidek 
2991cdaf10cSPawel Jakub Dawidek 		/*
3001cdaf10cSPawel Jakub Dawidek 		 * If process doesn't exists we somehow missed it.
3011cdaf10cSPawel Jakub Dawidek 		 * Not much can be done expect for logging this situation.
3021cdaf10cSPawel Jakub Dawidek 		 */
3031cdaf10cSPawel Jakub Dawidek 		if (kill(hp->hp_pid, 0) == -1 && errno == ESRCH) {
3041cdaf10cSPawel Jakub Dawidek 			pjdlog_warning("Hook disappeared (pid=%u, cmd=[%s]).",
3051cdaf10cSPawel Jakub Dawidek 			    hp->hp_pid, hp->hp_comm);
3061cdaf10cSPawel Jakub Dawidek 			hook_remove(hp);
3071cdaf10cSPawel Jakub Dawidek 			hook_free(hp);
3081cdaf10cSPawel Jakub Dawidek 			continue;
3091cdaf10cSPawel Jakub Dawidek 		}
3101cdaf10cSPawel Jakub Dawidek 
3111cdaf10cSPawel Jakub Dawidek 		/*
312b95a6ecfSGordon Bergling 		 * Skip processes younger than 1 minute.
3131cdaf10cSPawel Jakub Dawidek 		 */
3141cdaf10cSPawel Jakub Dawidek 		if (now - hp->hp_lastreport < REPORT_INTERVAL)
3151cdaf10cSPawel Jakub Dawidek 			continue;
3161cdaf10cSPawel Jakub Dawidek 
3171cdaf10cSPawel Jakub Dawidek 		/*
3181cdaf10cSPawel Jakub Dawidek 		 * Hook is running for too long, report it.
3191cdaf10cSPawel Jakub Dawidek 		 */
3201cdaf10cSPawel Jakub Dawidek 		pjdlog_warning("Hook is running for %ju seconds (pid=%u, cmd=[%s]).",
3211cdaf10cSPawel Jakub Dawidek 		    (uintmax_t)(now - hp->hp_birthtime), hp->hp_pid,
3221cdaf10cSPawel Jakub Dawidek 		    hp->hp_comm);
3231cdaf10cSPawel Jakub Dawidek 		hp->hp_lastreport = now;
3241cdaf10cSPawel Jakub Dawidek 	}
3251cdaf10cSPawel Jakub Dawidek 	mtx_unlock(&hookprocs_lock);
3261cdaf10cSPawel Jakub Dawidek }
3271cdaf10cSPawel Jakub Dawidek 
3281cdaf10cSPawel Jakub Dawidek void
hook_exec(const char * path,...)32932115b10SPawel Jakub Dawidek hook_exec(const char *path, ...)
33032115b10SPawel Jakub Dawidek {
33132115b10SPawel Jakub Dawidek 	va_list ap;
33232115b10SPawel Jakub Dawidek 
33332115b10SPawel Jakub Dawidek 	va_start(ap, path);
3341cdaf10cSPawel Jakub Dawidek 	hook_execv(path, ap);
33532115b10SPawel Jakub Dawidek 	va_end(ap);
33632115b10SPawel Jakub Dawidek }
33732115b10SPawel Jakub Dawidek 
3381cdaf10cSPawel Jakub Dawidek void
hook_execv(const char * path,va_list ap)33932115b10SPawel Jakub Dawidek hook_execv(const char *path, va_list ap)
34032115b10SPawel Jakub Dawidek {
3411cdaf10cSPawel Jakub Dawidek 	struct hookproc *hp;
34232115b10SPawel Jakub Dawidek 	char *args[64];
34332115b10SPawel Jakub Dawidek 	unsigned int ii;
344c0a124e6SPawel Jakub Dawidek 	sigset_t mask;
3451cdaf10cSPawel Jakub Dawidek 	pid_t pid;
3461cdaf10cSPawel Jakub Dawidek 
347adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(hooks_initialized);
34832115b10SPawel Jakub Dawidek 
34932115b10SPawel Jakub Dawidek 	if (path == NULL || path[0] == '\0')
3501cdaf10cSPawel Jakub Dawidek 		return;
35132115b10SPawel Jakub Dawidek 
35232115b10SPawel Jakub Dawidek 	memset(args, 0, sizeof(args));
353e33d251eSEd Schouten 	args[0] = __DECONST(char *, path);
35432115b10SPawel Jakub Dawidek 	for (ii = 1; ii < sizeof(args) / sizeof(args[0]); ii++) {
35532115b10SPawel Jakub Dawidek 		args[ii] = va_arg(ap, char *);
35632115b10SPawel Jakub Dawidek 		if (args[ii] == NULL)
35732115b10SPawel Jakub Dawidek 			break;
35832115b10SPawel Jakub Dawidek 	}
359adf8002bSPawel Jakub Dawidek 	PJDLOG_ASSERT(ii < sizeof(args) / sizeof(args[0]));
36032115b10SPawel Jakub Dawidek 
3611cdaf10cSPawel Jakub Dawidek 	hp = hook_alloc(path, args);
3621cdaf10cSPawel Jakub Dawidek 	if (hp == NULL)
3631cdaf10cSPawel Jakub Dawidek 		return;
3641cdaf10cSPawel Jakub Dawidek 
3651884f6bbSPawel Jakub Dawidek 	pjdlog_debug(1, "Executing hook: %s", hp->hp_comm);
3661884f6bbSPawel Jakub Dawidek 
36732115b10SPawel Jakub Dawidek 	pid = fork();
36832115b10SPawel Jakub Dawidek 	switch (pid) {
36932115b10SPawel Jakub Dawidek 	case -1:	/* Error. */
3701cdaf10cSPawel Jakub Dawidek 		pjdlog_errno(LOG_ERR, "Unable to fork to execute %s", path);
371b71de2e0SPawel Jakub Dawidek 		hook_free(hp);
3721cdaf10cSPawel Jakub Dawidek 		return;
37332115b10SPawel Jakub Dawidek 	case 0:		/* Child. */
37432115b10SPawel Jakub Dawidek 		descriptors();
375c0a124e6SPawel Jakub Dawidek 		PJDLOG_VERIFY(sigemptyset(&mask) == 0);
376c0a124e6SPawel Jakub Dawidek 		PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
377bcaa0b67SPawel Jakub Dawidek 		/*
378ac7b0b09SPawel Jakub Dawidek 		 * Dummy handler set for SIGCHLD in the parent will be restored
379ac7b0b09SPawel Jakub Dawidek 		 * to SIG_IGN on execv(3) below, so there is no need to do
380ac7b0b09SPawel Jakub Dawidek 		 * anything with it.
381bcaa0b67SPawel Jakub Dawidek 		 */
38232115b10SPawel Jakub Dawidek 		execv(path, args);
38332115b10SPawel Jakub Dawidek 		pjdlog_errno(LOG_ERR, "Unable to execute %s", path);
38432115b10SPawel Jakub Dawidek 		exit(EX_SOFTWARE);
38532115b10SPawel Jakub Dawidek 	default:	/* Parent. */
3861cdaf10cSPawel Jakub Dawidek 		hook_add(hp, pid);
38732115b10SPawel Jakub Dawidek 		break;
38832115b10SPawel Jakub Dawidek 	}
38932115b10SPawel Jakub Dawidek }
390