hooks.c revision 213183
1142146Srwatson/*- 2142146Srwatson * Copyright (c) 2010 The FreeBSD Foundation 3142146Srwatson * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 4142146Srwatson * All rights reserved. 5142146Srwatson * 6142146Srwatson * This software was developed by Pawel Jakub Dawidek under sponsorship from 7142146Srwatson * the FreeBSD Foundation. 8142146Srwatson * 9142146Srwatson * Redistribution and use in source and binary forms, with or without 10142146Srwatson * modification, are permitted provided that the following conditions 11142146Srwatson * are met: 12142146Srwatson * 1. Redistributions of source code must retain the above copyright 13142146Srwatson * notice, this list of conditions and the following disclaimer. 14142146Srwatson * 2. Redistributions in binary form must reproduce the above copyright 15142146Srwatson * notice, this list of conditions and the following disclaimer in the 16142146Srwatson * documentation and/or other materials provided with the distribution. 17142146Srwatson * 18142146Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19142146Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20142146Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21142146Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22142146Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23142146Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24142146Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25142146Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26142146Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27142146Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28142146Srwatson * SUCH DAMAGE. 29142146Srwatson */ 30142146Srwatson 31142146Srwatson#include <sys/cdefs.h> 32142146Srwatson__FBSDID("$FreeBSD: head/sbin/hastd/hooks.c 213183 2010-09-26 10:39:01Z pjd $"); 33142146Srwatson 34142146Srwatson#include <sys/types.h> 35142146Srwatson#include <sys/sysctl.h> 36142146Srwatson#include <sys/wait.h> 37142146Srwatson 38142146Srwatson#include <assert.h> 39142146Srwatson#include <errno.h> 40142146Srwatson#include <fcntl.h> 41142146Srwatson#include <libgen.h> 42142146Srwatson#include <paths.h> 43142146Srwatson#include <signal.h> 44142146Srwatson#include <stdbool.h> 45142146Srwatson#include <stdint.h> 46142146Srwatson#include <stdio.h> 47142146Srwatson#include <stdlib.h> 48142146Srwatson#include <string.h> 49142146Srwatson#include <syslog.h> 50142146Srwatson#include <unistd.h> 51142146Srwatson 52142146Srwatson#include <pjdlog.h> 53142146Srwatson 54142146Srwatson#include "hooks.h" 55142146Srwatson#include "synch.h" 56142146Srwatson 57281399Sngie/* Report processes that are running for too long not often than this value. */ 58281399Sngie#define REPORT_INTERVAL 60 59142146Srwatson 60142146Srwatson/* Are we initialized? */ 61142146Srwatsonstatic bool hooks_initialized = false; 62142146Srwatson 63142146Srwatson/* 64142146Srwatson * Keep all processes we forked on a global queue, so we can report nicely 65142146Srwatson * when they finish or report that they are running for a long time. 66142146Srwatson */ 67142146Srwatson#define HOOKPROC_MAGIC_ALLOCATED 0x80090ca 68142146Srwatson#define HOOKPROC_MAGIC_ONLIST 0x80090c0 69142146Srwatsonstruct hookproc { 70142146Srwatson /* Magic. */ 71142146Srwatson int hp_magic; 72142146Srwatson /* PID of a forked child. */ 73142146Srwatson pid_t hp_pid; 74142146Srwatson /* When process were forked? */ 75142146Srwatson time_t hp_birthtime; 76142146Srwatson /* When we logged previous reported? */ 77142146Srwatson time_t hp_lastreport; 78142146Srwatson /* Path to executable and all the arguments we passed. */ 79142146Srwatson char hp_comm[PATH_MAX]; 80142146Srwatson TAILQ_ENTRY(hookproc) hp_next; 81142146Srwatson}; 82142146Srwatsonstatic TAILQ_HEAD(, hookproc) hookprocs; 83142146Srwatsonstatic pthread_mutex_t hookprocs_lock; 84142146Srwatson 85142146Srwatsonstatic void hook_remove(struct hookproc *hp); 86142146Srwatsonstatic void hook_free(struct hookproc *hp); 87142146Srwatson 88142146Srwatsonstatic void 89142146Srwatsondescriptors(void) 90142146Srwatson{ 91142146Srwatson long maxfd; 92142146Srwatson int fd; 93142146Srwatson 94142146Srwatson /* 95142146Srwatson * Close all descriptors. 96142146Srwatson */ 97142146Srwatson maxfd = sysconf(_SC_OPEN_MAX); 98142146Srwatson if (maxfd < 0) { 99142146Srwatson pjdlog_errno(LOG_WARNING, "sysconf(_SC_OPEN_MAX) failed"); 100142146Srwatson maxfd = 1024; 101142146Srwatson } 102142146Srwatson for (fd = 0; fd <= maxfd; fd++) { 103142146Srwatson switch (fd) { 104142146Srwatson case STDIN_FILENO: 105142146Srwatson case STDOUT_FILENO: 106142146Srwatson case STDERR_FILENO: 107142146Srwatson if (pjdlog_mode_get() == PJDLOG_MODE_STD) 108281399Sngie break; 109142146Srwatson /* FALLTHROUGH */ 110142146Srwatson default: 111142146Srwatson close(fd); 112142146Srwatson break; 113142146Srwatson } 114142146Srwatson } 115142146Srwatson if (pjdlog_mode_get() == PJDLOG_MODE_STD) 116142146Srwatson return; 117142146Srwatson /* 118142146Srwatson * Redirect stdin, stdout and stderr to /dev/null. 119142146Srwatson */ 120142146Srwatson fd = open(_PATH_DEVNULL, O_RDONLY); 121142146Srwatson if (fd < 0) { 122142146Srwatson pjdlog_errno(LOG_WARNING, "Unable to open %s for reading", 123142146Srwatson _PATH_DEVNULL); 124142146Srwatson } else if (fd != STDIN_FILENO) { 125142146Srwatson if (dup2(fd, STDIN_FILENO) < 0) { 126142146Srwatson pjdlog_errno(LOG_WARNING, 127142146Srwatson "Unable to duplicate descriptor for stdin"); 128142146Srwatson } 129142146Srwatson close(fd); 130142146Srwatson } 131142146Srwatson fd = open(_PATH_DEVNULL, O_WRONLY); 132142146Srwatson if (fd < 0) { 133142146Srwatson pjdlog_errno(LOG_WARNING, "Unable to open %s for writing", 134142146Srwatson _PATH_DEVNULL); 135142146Srwatson } else { 136142146Srwatson if (fd != STDOUT_FILENO && dup2(fd, STDOUT_FILENO) < 0) { 137142146Srwatson pjdlog_errno(LOG_WARNING, 138142146Srwatson "Unable to duplicate descriptor for stdout"); 139142146Srwatson } 140142146Srwatson if (fd != STDERR_FILENO && dup2(fd, STDERR_FILENO) < 0) { 141142146Srwatson pjdlog_errno(LOG_WARNING, 142142146Srwatson "Unable to duplicate descriptor for stderr"); 143142146Srwatson } 144142146Srwatson if (fd != STDOUT_FILENO && fd != STDERR_FILENO) 145142146Srwatson close(fd); 146142146Srwatson } 147142146Srwatson} 148142146Srwatson 149142146Srwatsonvoid 150142146Srwatsonhook_init(void) 151142146Srwatson{ 152142146Srwatson 153142146Srwatson assert(!hooks_initialized); 154142146Srwatson 155142146Srwatson mtx_init(&hookprocs_lock); 156142146Srwatson TAILQ_INIT(&hookprocs); 157142146Srwatson hooks_initialized = true; 158142146Srwatson} 159142146Srwatson 160142146Srwatsonvoid 161142146Srwatsonhook_fini(void) 162142146Srwatson{ 163142146Srwatson struct hookproc *hp; 164142146Srwatson 165142146Srwatson assert(hooks_initialized); 166142146Srwatson 167142146Srwatson mtx_lock(&hookprocs_lock); 168142146Srwatson while ((hp = TAILQ_FIRST(&hookprocs)) != NULL) { 169142146Srwatson assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST); 170142146Srwatson assert(hp->hp_pid > 0); 171142146Srwatson 172142146Srwatson hook_remove(hp); 173142146Srwatson hook_free(hp); 174142146Srwatson } 175142146Srwatson mtx_unlock(&hookprocs_lock); 176142146Srwatson 177142146Srwatson mtx_destroy(&hookprocs_lock); 178142146Srwatson TAILQ_INIT(&hookprocs); 179142146Srwatson hooks_initialized = false; 180142146Srwatson} 181142146Srwatson 182142146Srwatsonstatic struct hookproc * 183142146Srwatsonhook_alloc(const char *path, char **args) 184142146Srwatson{ 185142146Srwatson struct hookproc *hp; 186142146Srwatson unsigned int ii; 187142146Srwatson 188142146Srwatson hp = malloc(sizeof(*hp)); 189142146Srwatson if (hp == NULL) { 190142146Srwatson pjdlog_error("Unable to allocate %zu bytes of memory for a hook.", 191142146Srwatson sizeof(*hp)); 192142146Srwatson return (NULL); 193142146Srwatson } 194142146Srwatson 195142146Srwatson hp->hp_pid = 0; 196142146Srwatson hp->hp_birthtime = hp->hp_lastreport = time(NULL); 197142146Srwatson (void)strlcpy(hp->hp_comm, path, sizeof(hp->hp_comm)); 198142146Srwatson /* We start at 2nd argument as we don't want to have exec name twice. */ 199142146Srwatson for (ii = 1; args[ii] != NULL; ii++) { 200142146Srwatson (void)strlcat(hp->hp_comm, " ", sizeof(hp->hp_comm)); 201142146Srwatson (void)strlcat(hp->hp_comm, args[ii], sizeof(hp->hp_comm)); 202142146Srwatson } 203142146Srwatson if (strlen(hp->hp_comm) >= sizeof(hp->hp_comm) - 1) { 204142146Srwatson pjdlog_error("Exec path too long, correct configuration file."); 205142146Srwatson free(hp); 206142146Srwatson return (NULL); 207142146Srwatson } 208142146Srwatson hp->hp_magic = HOOKPROC_MAGIC_ALLOCATED; 209142146Srwatson return (hp); 210142146Srwatson} 211142146Srwatson 212142146Srwatsonstatic void 213142146Srwatsonhook_add(struct hookproc *hp, pid_t pid) 214142146Srwatson{ 215142146Srwatson 216142146Srwatson assert(hp->hp_magic == HOOKPROC_MAGIC_ALLOCATED); 217142146Srwatson assert(hp->hp_pid == 0); 218142146Srwatson 219281399Sngie hp->hp_pid = pid; 220142146Srwatson mtx_lock(&hookprocs_lock); 221142146Srwatson hp->hp_magic = HOOKPROC_MAGIC_ONLIST; 222142146Srwatson TAILQ_INSERT_TAIL(&hookprocs, hp, hp_next); 223142146Srwatson mtx_unlock(&hookprocs_lock); 224142146Srwatson} 225142146Srwatson 226142146Srwatsonstatic void 227142146Srwatsonhook_remove(struct hookproc *hp) 228142146Srwatson{ 229142146Srwatson 230142146Srwatson assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST); 231142146Srwatson assert(hp->hp_pid > 0); 232142146Srwatson assert(mtx_owned(&hookprocs_lock)); 233142146Srwatson 234142146Srwatson TAILQ_REMOVE(&hookprocs, hp, hp_next); 235142146Srwatson hp->hp_magic = HOOKPROC_MAGIC_ALLOCATED; 236142146Srwatson} 237142146Srwatson 238142146Srwatsonstatic void 239142146Srwatsonhook_free(struct hookproc *hp) 240142146Srwatson{ 241142146Srwatson 242142146Srwatson assert(hp->hp_magic == HOOKPROC_MAGIC_ALLOCATED); 243142146Srwatson assert(hp->hp_pid > 0); 244142146Srwatson 245142146Srwatson hp->hp_magic = 0; 246142146Srwatson free(hp); 247142146Srwatson} 248142146Srwatson 249142146Srwatsonstatic struct hookproc * 250142146Srwatsonhook_find(pid_t pid) 251142146Srwatson{ 252142146Srwatson struct hookproc *hp; 253142146Srwatson 254142146Srwatson assert(mtx_owned(&hookprocs_lock)); 255142146Srwatson 256142146Srwatson TAILQ_FOREACH(hp, &hookprocs, hp_next) { 257142146Srwatson assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST); 258142146Srwatson assert(hp->hp_pid > 0); 259142146Srwatson 260142146Srwatson if (hp->hp_pid == pid) 261142146Srwatson break; 262142146Srwatson } 263142146Srwatson 264142146Srwatson return (hp); 265142146Srwatson} 266142146Srwatson 267142146Srwatsonvoid 268142146Srwatsonhook_check_one(pid_t pid, int status) 269142146Srwatson{ 270142146Srwatson struct hookproc *hp; 271142146Srwatson 272142146Srwatson mtx_lock(&hookprocs_lock); 273142146Srwatson hp = hook_find(pid); 274142146Srwatson if (hp == NULL) { 275142146Srwatson mtx_unlock(&hookprocs_lock); 276142146Srwatson pjdlog_debug(1, "Unknown process pid=%u", pid); 277142146Srwatson return; 278142146Srwatson } 279142146Srwatson hook_remove(hp); 280142146Srwatson mtx_unlock(&hookprocs_lock); 281142146Srwatson if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 282142146Srwatson pjdlog_debug(1, "Hook exited gracefully (pid=%u, cmd=[%s]).", 283142146Srwatson pid, hp->hp_comm); 284142146Srwatson } else if (WIFSIGNALED(status)) { 285142146Srwatson pjdlog_error("Hook was killed (pid=%u, signal=%d, cmd=[%s]).", 286142146Srwatson pid, WTERMSIG(status), hp->hp_comm); 287142146Srwatson } else { 288142146Srwatson pjdlog_error("Hook exited ungracefully (pid=%u, exitcode=%d, cmd=[%s]).", 289142146Srwatson pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1, 290142146Srwatson hp->hp_comm); 291142146Srwatson } 292142146Srwatson hook_free(hp); 293142146Srwatson} 294142146Srwatson 295142146Srwatsonvoid 296142146Srwatsonhook_check(bool sigchld) 297142146Srwatson{ 298142146Srwatson struct hookproc *hp, *hp2; 299142146Srwatson int status; 300142146Srwatson time_t now; 301281399Sngie pid_t pid; 302142146Srwatson 303142146Srwatson assert(hooks_initialized); 304142146Srwatson 305142146Srwatson /* 306142146Srwatson * If SIGCHLD was received, garbage collect finished processes. 307142146Srwatson */ 308142146Srwatson if (sigchld) { 309142146Srwatson while ((pid = wait3(&status, WNOHANG, NULL)) > 0) 310142146Srwatson hook_check_one(pid, status); 311142146Srwatson } 312142146Srwatson 313142146Srwatson /* 314142146Srwatson * Report about processes that are running for a long time. 315142146Srwatson */ 316142146Srwatson now = time(NULL); 317142146Srwatson mtx_lock(&hookprocs_lock); 318142146Srwatson TAILQ_FOREACH_SAFE(hp, &hookprocs, hp_next, hp2) { 319 assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST); 320 assert(hp->hp_pid > 0); 321 322 /* 323 * If process doesn't exists we somehow missed it. 324 * Not much can be done expect for logging this situation. 325 */ 326 if (kill(hp->hp_pid, 0) == -1 && errno == ESRCH) { 327 pjdlog_warning("Hook disappeared (pid=%u, cmd=[%s]).", 328 hp->hp_pid, hp->hp_comm); 329 hook_remove(hp); 330 hook_free(hp); 331 continue; 332 } 333 334 /* 335 * Skip proccesses younger than 1 minute. 336 */ 337 if (now - hp->hp_lastreport < REPORT_INTERVAL) 338 continue; 339 340 /* 341 * Hook is running for too long, report it. 342 */ 343 pjdlog_warning("Hook is running for %ju seconds (pid=%u, cmd=[%s]).", 344 (uintmax_t)(now - hp->hp_birthtime), hp->hp_pid, 345 hp->hp_comm); 346 hp->hp_lastreport = now; 347 } 348 mtx_unlock(&hookprocs_lock); 349} 350 351void 352hook_exec(const char *path, ...) 353{ 354 va_list ap; 355 356 va_start(ap, path); 357 hook_execv(path, ap); 358 va_end(ap); 359} 360 361void 362hook_execv(const char *path, va_list ap) 363{ 364 struct hookproc *hp; 365 char *args[64]; 366 unsigned int ii; 367 pid_t pid; 368 369 assert(hooks_initialized); 370 371 if (path == NULL || path[0] == '\0') 372 return; 373 374 memset(args, 0, sizeof(args)); 375 args[0] = basename(path); 376 for (ii = 1; ii < sizeof(args) / sizeof(args[0]); ii++) { 377 args[ii] = va_arg(ap, char *); 378 if (args[ii] == NULL) 379 break; 380 } 381 assert(ii < sizeof(args) / sizeof(args[0])); 382 383 hp = hook_alloc(path, args); 384 if (hp == NULL) 385 return; 386 387 pid = fork(); 388 switch (pid) { 389 case -1: /* Error. */ 390 pjdlog_errno(LOG_ERR, "Unable to fork to execute %s", path); 391 hook_free(hp); 392 return; 393 case 0: /* Child. */ 394 descriptors(); 395 execv(path, args); 396 pjdlog_errno(LOG_ERR, "Unable to execute %s", path); 397 exit(EX_SOFTWARE); 398 default: /* Parent. */ 399 hook_add(hp, pid); 400 break; 401 } 402} 403