hooks.c (211884) | hooks.c (211885) |
---|---|
1/*- 2 * Copyright (c) 2010 The FreeBSD Foundation | 1/*- 2 * Copyright (c) 2010 The FreeBSD Foundation |
3 * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> |
|
3 * All rights reserved. 4 * 5 * This software was developed by Pawel Jakub Dawidek under sponsorship from 6 * the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: --- 12 unchanged lines hidden (view full) --- 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> | 4 * All rights reserved. 5 * 6 * This software was developed by Pawel Jakub Dawidek under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: --- 12 unchanged lines hidden (view full) --- 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> |
31__FBSDID("$FreeBSD: head/sbin/hastd/hooks.c 211884 2010-08-27 14:35:39Z pjd $"); | 32__FBSDID("$FreeBSD: head/sbin/hastd/hooks.c 211885 2010-08-27 14:38:12Z pjd $"); |
32 33#include <sys/types.h> | 33 34#include <sys/types.h> |
35#include <sys/sysctl.h> |
|
34#include <sys/wait.h> 35 36#include <assert.h> | 36#include <sys/wait.h> 37 38#include <assert.h> |
39#include <errno.h> |
|
37#include <fcntl.h> | 40#include <fcntl.h> |
41#include <libgen.h> 42#include <paths.h> 43#include <signal.h> 44#include <stdbool.h> 45#include <stdint.h> |
|
38#include <stdio.h> 39#include <stdlib.h> | 46#include <stdio.h> 47#include <stdlib.h> |
40#include <unistd.h> | |
41#include <string.h> 42#include <syslog.h> | 48#include <string.h> 49#include <syslog.h> |
43#include <libgen.h> 44#include <paths.h> | 50#include <unistd.h> |
45 46#include <pjdlog.h> 47 48#include "hooks.h" | 51 52#include <pjdlog.h> 53 54#include "hooks.h" |
55#include "synch.h" |
|
49 | 56 |
57/* Report processes that are running for too long not often than this value. */ 58#define REPORT_INTERVAL 60 59 60/* Are we initialized? */ 61static bool hooks_initialized = false; 62 63/* 64 * Keep all processes we forked on a global queue, so we can report nicely 65 * when they finish or report that they are running for a long time. 66 */ 67#define HOOKPROC_MAGIC_ALLOCATED 0x80090ca 68#define HOOKPROC_MAGIC_ONLIST 0x80090c0 69struct hookproc { 70 /* Magic. */ 71 int hp_magic; 72 /* PID of a forked child. */ 73 pid_t hp_pid; 74 /* When process were forked? */ 75 time_t hp_birthtime; 76 /* When we logged previous reported? */ 77 time_t hp_lastreport; 78 /* Path to executable and all the arguments we passed. */ 79 char hp_comm[PATH_MAX]; 80 TAILQ_ENTRY(hookproc) hp_next; 81}; 82static TAILQ_HEAD(, hookproc) hookprocs; 83static pthread_mutex_t hookprocs_lock; 84 |
|
50static void 51descriptors(void) 52{ 53 long maxfd; 54 int fd; 55 56 /* 57 * Close all descriptors. --- 45 unchanged lines hidden (view full) --- 103 pjdlog_errno(LOG_WARNING, 104 "Unable to duplicate descriptor for stderr"); 105 } 106 if (fd != STDOUT_FILENO && fd != STDERR_FILENO) 107 close(fd); 108 } 109} 110 | 85static void 86descriptors(void) 87{ 88 long maxfd; 89 int fd; 90 91 /* 92 * Close all descriptors. --- 45 unchanged lines hidden (view full) --- 138 pjdlog_errno(LOG_WARNING, 139 "Unable to duplicate descriptor for stderr"); 140 } 141 if (fd != STDOUT_FILENO && fd != STDERR_FILENO) 142 close(fd); 143 } 144} 145 |
111int | 146void 147hook_init(void) 148{ 149 150 mtx_init(&hookprocs_lock); 151 TAILQ_INIT(&hookprocs); 152 hooks_initialized = true; 153} 154 155static struct hookproc * 156hook_alloc(const char *path, char **args) 157{ 158 struct hookproc *hp; 159 unsigned int ii; 160 161 hp = malloc(sizeof(*hp)); 162 if (hp == NULL) { 163 pjdlog_error("Unable to allocate %zu bytes of memory for a hook.", 164 sizeof(*hp)); 165 return (NULL); 166 } 167 168 hp->hp_pid = 0; 169 hp->hp_birthtime = hp->hp_lastreport = time(NULL); 170 (void)strlcpy(hp->hp_comm, path, sizeof(hp->hp_comm)); 171 /* We start at 2nd argument as we don't want to have exec name twice. */ 172 for (ii = 1; args[ii] != NULL; ii++) { 173 (void)strlcat(hp->hp_comm, " ", sizeof(hp->hp_comm)); 174 (void)strlcat(hp->hp_comm, args[ii], sizeof(hp->hp_comm)); 175 } 176 if (strlen(hp->hp_comm) >= sizeof(hp->hp_comm) - 1) { 177 pjdlog_error("Exec path too long, correct configuration file."); 178 free(hp); 179 return (NULL); 180 } 181 hp->hp_magic = HOOKPROC_MAGIC_ALLOCATED; 182 return (hp); 183} 184 185static void 186hook_add(struct hookproc *hp, pid_t pid) 187{ 188 189 assert(hp->hp_magic == HOOKPROC_MAGIC_ALLOCATED); 190 assert(hp->hp_pid == 0); 191 192 hp->hp_pid = pid; 193 mtx_lock(&hookprocs_lock); 194 hp->hp_magic = HOOKPROC_MAGIC_ONLIST; 195 TAILQ_INSERT_TAIL(&hookprocs, hp, hp_next); 196 mtx_unlock(&hookprocs_lock); 197} 198 199static void 200hook_remove(struct hookproc *hp) 201{ 202 203 assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST); 204 assert(hp->hp_pid > 0); 205 assert(mtx_owned(&hookprocs_lock)); 206 207 TAILQ_REMOVE(&hookprocs, hp, hp_next); 208 hp->hp_magic = HOOKPROC_MAGIC_ALLOCATED; 209} 210 211static void 212hook_free(struct hookproc *hp) 213{ 214 215 assert(hp->hp_magic == HOOKPROC_MAGIC_ALLOCATED); 216 assert(hp->hp_pid > 0); 217 218 hp->hp_magic = 0; 219 free(hp); 220} 221 222static struct hookproc * 223hook_find(pid_t pid) 224{ 225 struct hookproc *hp; 226 227 assert(mtx_owned(&hookprocs_lock)); 228 229 TAILQ_FOREACH(hp, &hookprocs, hp_next) { 230 assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST); 231 assert(hp->hp_pid > 0); 232 233 if (hp->hp_pid == pid) 234 break; 235 } 236 237 return (hp); 238} 239 240void 241hook_check(bool sigchld) 242{ 243 struct hookproc *hp, *hp2; 244 int status; 245 time_t now; 246 pid_t pid; 247 248 assert(hooks_initialized); 249 250 /* 251 * If SIGCHLD was received, garbage collect finished processes. 252 */ 253 while (sigchld && (pid = wait3(&status, WNOHANG, NULL)) > 0) { 254 mtx_lock(&hookprocs_lock); 255 hp = hook_find(pid); 256 if (hp == NULL) { 257 mtx_unlock(&hookprocs_lock); 258 pjdlog_warning("Unknown process pid=%u", pid); 259 continue; 260 } 261 hook_remove(hp); 262 mtx_unlock(&hookprocs_lock); 263 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 264 pjdlog_debug(1, "Hook exited gracefully (pid=%u, cmd=[%s]).", 265 pid, hp->hp_comm); 266 } else if (WIFSIGNALED(status)) { 267 pjdlog_error("Hook was killed (pid=%u, signal=%d, cmd=[%s]).", 268 pid, WTERMSIG(status), hp->hp_comm); 269 } else { 270 pjdlog_error("Hook exited ungracefully (pid=%u, exitcode=%d, cmd=[%s]).", 271 pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1, 272 hp->hp_comm); 273 } 274 hook_free(hp); 275 } 276 277 /* 278 * Report about processes that are running for a long time. 279 */ 280 now = time(NULL); 281 mtx_lock(&hookprocs_lock); 282 TAILQ_FOREACH_SAFE(hp, &hookprocs, hp_next, hp2) { 283 assert(hp->hp_magic == HOOKPROC_MAGIC_ONLIST); 284 assert(hp->hp_pid > 0); 285 286 /* 287 * If process doesn't exists we somehow missed it. 288 * Not much can be done expect for logging this situation. 289 */ 290 if (kill(hp->hp_pid, 0) == -1 && errno == ESRCH) { 291 pjdlog_warning("Hook disappeared (pid=%u, cmd=[%s]).", 292 hp->hp_pid, hp->hp_comm); 293 hook_remove(hp); 294 hook_free(hp); 295 continue; 296 } 297 298 /* 299 * Skip proccesses younger than 1 minute. 300 */ 301 if (now - hp->hp_lastreport < REPORT_INTERVAL) 302 continue; 303 304 /* 305 * Hook is running for too long, report it. 306 */ 307 pjdlog_warning("Hook is running for %ju seconds (pid=%u, cmd=[%s]).", 308 (uintmax_t)(now - hp->hp_birthtime), hp->hp_pid, 309 hp->hp_comm); 310 hp->hp_lastreport = now; 311 } 312 mtx_unlock(&hookprocs_lock); 313} 314 315void |
112hook_exec(const char *path, ...) 113{ 114 va_list ap; | 316hook_exec(const char *path, ...) 317{ 318 va_list ap; |
115 int ret; | |
116 117 va_start(ap, path); | 319 320 va_start(ap, path); |
118 ret = hook_execv(path, ap); | 321 hook_execv(path, ap); |
119 va_end(ap); | 322 va_end(ap); |
120 return (ret); | |
121} 122 | 323} 324 |
123int | 325void |
124hook_execv(const char *path, va_list ap) 125{ | 326hook_execv(const char *path, va_list ap) 327{ |
328 struct hookproc *hp; |
|
126 char *args[64]; 127 unsigned int ii; | 329 char *args[64]; 330 unsigned int ii; |
128 pid_t pid, wpid; 129 int status; | 331 pid_t pid; |
130 | 332 |
333 assert(hooks_initialized); 334 |
|
131 if (path == NULL || path[0] == '\0') | 335 if (path == NULL || path[0] == '\0') |
132 return (0); | 336 return; |
133 134 memset(args, 0, sizeof(args)); 135 args[0] = basename(path); 136 for (ii = 1; ii < sizeof(args) / sizeof(args[0]); ii++) { 137 args[ii] = va_arg(ap, char *); 138 if (args[ii] == NULL) 139 break; 140 } 141 assert(ii < sizeof(args) / sizeof(args[0])); 142 | 337 338 memset(args, 0, sizeof(args)); 339 args[0] = basename(path); 340 for (ii = 1; ii < sizeof(args) / sizeof(args[0]); ii++) { 341 args[ii] = va_arg(ap, char *); 342 if (args[ii] == NULL) 343 break; 344 } 345 assert(ii < sizeof(args) / sizeof(args[0])); 346 |
347 hp = hook_alloc(path, args); 348 if (hp == NULL) 349 return; 350 |
|
143 pid = fork(); 144 switch (pid) { 145 case -1: /* Error. */ | 351 pid = fork(); 352 switch (pid) { 353 case -1: /* Error. */ |
146 pjdlog_errno(LOG_ERR, "Unable to fork %s", path); 147 return (-1); | 354 pjdlog_errno(LOG_ERR, "Unable to fork to execute %s", path); 355 return; |
148 case 0: /* Child. */ 149 descriptors(); 150 execv(path, args); 151 pjdlog_errno(LOG_ERR, "Unable to execute %s", path); 152 exit(EX_SOFTWARE); 153 default: /* Parent. */ | 356 case 0: /* Child. */ 357 descriptors(); 358 execv(path, args); 359 pjdlog_errno(LOG_ERR, "Unable to execute %s", path); 360 exit(EX_SOFTWARE); 361 default: /* Parent. */ |
362 hook_add(hp, pid); |
|
154 break; 155 } | 363 break; 364 } |
156 157 wpid = waitpid(pid, &status, 0); 158 assert(wpid == pid); 159 160 return (WEXITSTATUS(status)); | |
161} | 365} |