Deleted Added
full compact
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}