1 /* Copyright (C) 2009 Intel Corporation
2    Author: Andi Kleen
3    Manage trigger commands running as separate processes.
4 
5    mcelog is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public
7    License as published by the Free Software Foundation; version
8    2.
9 
10    mcelog is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14 
15    You should find a copy of v2 of the GNU General Public License somewhere
16    on your Linux system; if not, write to the Free Software Foundation,
17    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18 #define _GNU_SOURCE 1
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <signal.h>
23 #include <stdbool.h>
24 #include <string.h>
25 #include <sys/wait.h>
26 #include "trigger.h"
27 #include "eventloop.h"
28 #include "list.h"
29 #include "mcelog.h"
30 #include "memutil.h"
31 #include "config.h"
32 
33 struct child {
34 	struct list_head nd;
35 	pid_t child;
36 	const char *name;
37 };
38 
39 static LIST_HEAD(childlist);
40 static int num_children;
41 static int children_max = 4;
42 static char *trigger_dir;
43 
44 static void finish_child(pid_t child, int status);
45 
mcelog_fork(const char * name)46 pid_t mcelog_fork(const char *name)
47 {
48 	pid_t child;
49 	struct child *c;
50 
51 	child = fork();
52 	if (child <= 0)
53 		return child;
54 
55 	num_children++;
56 	c = xalloc(sizeof(struct child));
57 	c->name = name;
58 	c->child = child;
59 	list_add_tail(&c->nd, &childlist);
60 	return child;
61 }
62 
63 // note: trigger must be allocated, e.g. from config
run_trigger(char * trigger,char * argv[],char ** env,bool sync,const char * reporter)64 void run_trigger(char *trigger, char *argv[], char **env, bool sync, const char* reporter)
65 {
66 	pid_t child;
67 
68 	char *fallback_argv[] = {
69 		trigger,
70 		NULL,
71 	};
72 
73 	if (!argv)
74 		argv = fallback_argv;
75 
76 	Lprintf("Running trigger `%s' (reporter: %s)\n", trigger, reporter);
77 	if (children_max > 0 && num_children >= children_max) {
78 		Eprintf("Too many trigger children running already\n");
79 		return;
80 	}
81 
82 	child = mcelog_fork(trigger);
83 	if (child < 0) {
84 		SYSERRprintf("Cannot create process for trigger");
85 		return;
86 	}
87 	if (child == 0) {
88 		if (trigger_dir && chdir(trigger_dir) == -1)
89 			SYSERRprintf("Cannot chdir(%s) for trigger", trigger_dir);
90 		else
91 			execve(trigger, argv, env);
92 		_exit(127);
93 	}
94 }
95 
96 /* Clean up child on SIGCHLD */
finish_child(pid_t child,int status)97 static void finish_child(pid_t child, int status)
98 {
99 	struct child *c, *tmpc;
100 
101 	list_for_each_entry_safe (c, tmpc, &childlist, nd) {
102 		if (c->child == child) {
103 			if (WIFEXITED(status) && WEXITSTATUS(status)) {
104 				Eprintf("Trigger `%s' exited with status %d\n",
105 					c->name, WEXITSTATUS(status));
106 			} else if (WIFSIGNALED(status)) {
107 				Eprintf("Trigger `%s' died with signal %s\n",
108 					c->name, strsignal(WTERMSIG(status)));
109 			}
110 			list_del(&c->nd);
111 			free(c);
112 			num_children--;
113 			return;
114 		}
115 	}
116 	abort();
117 }
118 
119 /* Runs only directly after ppoll */
child_handler(int sig,siginfo_t * si,void * ctx)120 static void child_handler(int sig, siginfo_t *si, void *ctx)
121 {
122 	int status;
123 	pid_t pid;
124 
125 	if (waitpid(si->si_pid, &status, WNOHANG) < 0) {
126 		SYSERRprintf("Cannot collect child %d", si->si_pid);
127 		return;
128 	}
129 	finish_child(si->si_pid, status);
130 
131 	/* Check other child(ren)'s status to avoid zombie process */
132 	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
133 		finish_child(pid, status);
134 	}
135 }
136 
trigger_setup(void)137 void trigger_setup(void)
138 {
139 	char *s;
140 	struct sigaction sa = {
141 		.sa_sigaction = child_handler,
142 		.sa_flags = SA_SIGINFO|SA_NOCLDSTOP|SA_RESTART,
143 	};
144 	sigaction(SIGCHLD, &sa, NULL);
145 	event_signal(SIGCHLD);
146 
147 	config_number("trigger", "children-max", "%d", &children_max);
148 
149 	s = config_string("trigger", "directory");
150 	if (s) {
151 		if (access(s, R_OK|X_OK) < 0)
152 			SYSERRprintf("Cannot access trigger directory `%s'", s);
153 		trigger_dir = s;
154 	}
155 }
156 
trigger_wait(void)157 void trigger_wait(void)
158 {
159 	int status;
160 	int pid;
161 
162 	while ((pid = waitpid((pid_t)-1, &status, 0)) > 0)
163 		finish_child(pid, status);
164 }
165 
trigger_check(char * s)166 int trigger_check(char *s)
167 {
168 	char *name;
169 	int rc;
170 
171 	if (trigger_dir)
172 		xasprintf(&name, "%s/%s", trigger_dir, s);
173 	else
174 		name = s;
175 
176 	rc = access(name, R_OK|X_OK);
177 
178 	if (trigger_dir)
179 		free(name);
180 
181 	return rc;
182 }
183