1    	/*
2    	 * Copyright 2012 Red Hat, Inc.
3    	 *
4    	 * This copyrighted material is made available to anyone wishing to use,
5    	 * modify, copy, or redistribute it subject to the terms and conditions
6    	 * of the GNU General Public License v2 or (at your option) any later version.
7    	 */
8    	
9    	#include <inttypes.h>
10   	#include <unistd.h>
11   	#include <stdio.h>
12   	#include <stdlib.h>
13   	#include <stdint.h>
14   	#include <stddef.h>
15   	#include <poll.h>
16   	#include <fcntl.h>
17   	#include <string.h>
18   	#include <errno.h>
19   	#include <limits.h>
20   	#include <time.h>
21   	#include <stdarg.h>
22   	#include <signal.h>
23   	#include <sys/time.h>
24   	#include <sys/types.h>
25   	#include <sys/wait.h>
26   	#include <sys/prctl.h>
27   	#include <grp.h>
28   	
29   	#include "dlm_daemon.h"
30   	
31   	
32   	static int _log_stderr;
33   	
34   	#define log_helper(fmt, args...) \
35   	do { \
36   		if (_log_stderr) \
37   			fprintf(stderr, "%llu " fmt "\n", (unsigned long long)monotime(), ##args); \
38   	} while (0)
39   	
40   	
41   	/*
42   	 * Restrict the commands that can be run.
43   	 */
44   	
45   	#define CMD_ID_LVCHANGE_REFRESH 1
46   	#define CMD_ID_LVS 2
47   	
48   	static int _get_cmd_id(char **av, int av_count)
49   	{
50   		if ((av_count >= 3) &&
51   		    !strcmp(av[0], "lvm") &&
52   		    !strcmp(av[1], "lvchange") &&
53   		    !strcmp(av[2], "--refresh")) {
54   			return CMD_ID_LVCHANGE_REFRESH;
55   		}
56   	
57   		if ((av_count >= 2) &&
58   		    !strcmp(av[0], "lvm") &&
59   		    !strcmp(av[1], "lvs")) {
60   			return CMD_ID_LVS;
61   		}
62   	
63   		return 0;
64   	}
65   	
66   	/*
67   	 * Keep track of running pids mainly because when the process
68   	 * exits we get the pid, and need to look up the uuid from the
69   	 * pid to return the uuid/pid/result back to the main daemon.
70   	 */
71   	
72   	#define MAX_RUNNING 32
73   	
74   	struct running {
75   		char uuid[RUN_UUID_LEN];
76   		int pid;
77   		int cmd_id;
78   	};
79   	
80   	static struct running running_cmds[MAX_RUNNING];
81   	static int running_count;
82   	
83   	static int _save_running_cmd(char *uuid, int pid, int cmd_id)
84   	{
85   		int i;
86   	
87   		for (i = 0; i < MAX_RUNNING; i++) {
88   			if (!running_cmds[i].pid) {
89   				running_cmds[i].pid = pid;
90   				running_cmds[i].cmd_id = cmd_id;
91   				memcpy(running_cmds[i].uuid, uuid, RUN_UUID_LEN);
92   				running_count++;
93   				return 0;
94   			}
95   		}
96   		log_helper("too many running commands");
97   		return -1;
98   	}
99   	
100  	static struct running *_get_running_cmd(int pid)
101  	{
102  		int i;
103  	
104  		for (i = 0; i < MAX_RUNNING; i++) {
105  			if (running_cmds[i].pid == pid)
106  				return &running_cmds[i];
107  		}
108  		return NULL;
109  	}
110  	
111  	static struct running *_get_running_uuid(char *uuid)
112  	{
113  		int i;
114  	
(1) Event cond_true: Condition "i < 32", taking true branch.
115  		for (i = 0; i < MAX_RUNNING; i++) {
(2) Event string_null: Passing unterminated string "uuid" to "strcmp", which expects a null-terminated string.
116  			if (!strcmp(running_cmds[i].uuid, uuid))
117  				return &running_cmds[i];
118  		}
119  		return NULL;
120  	}
121  	
122  	static void _clear_running_cmd(struct running *running)
123  	{
124  		running_count--;
125  		running->pid = 0;
126  		running->cmd_id = 0;
127  		memset(running->uuid, 0, RUN_UUID_LEN);
128  	}
129  	
130  	/* runs in child process that was forked by helper */
131  	
132  	static void exec_command(char *cmd_str, int out_fd)
133  	{
134  		char cmd_buf[16];
135  		char arg[ONE_ARG_LEN];
136  		char *av[MAX_AV_COUNT + 1]; /* +1 for NULL */
137  		int av_count = 0;
138  		int i, rv, arg_len, cmd_len, cmd_id;
139  	
140  		for (i = 0; i < MAX_AV_COUNT + 1; i++)
141  			av[i] = NULL;
142  	
143  		if (!cmd_str[0])
144  			return;
145  	
146  		/* this should already be done, but make sure */
147  		cmd_str[RUN_COMMAND_LEN - 1] = '\0';
148  	
149  		memset(&arg, 0, sizeof(arg));
150  		arg_len = 0;
151  		cmd_len = strlen(cmd_str);
152  	
153  		for (i = 0; i < cmd_len; i++) {
154  			if (!cmd_str[i])
155  				break;
156  	
157  			if (av_count == MAX_AV_COUNT)
158  				break;
159  	
160  			if (cmd_str[i] == '\\') {
161  				if (i == (cmd_len - 1))
162  					break;
163  				i++;
164  	
165  				if (cmd_str[i] == '\\') {
166  					arg[arg_len++] = cmd_str[i];
167  					continue;
168  				}
169  				if (isspace(cmd_str[i])) {
170  					arg[arg_len++] = cmd_str[i];
171  					continue;
172  				} else {
173  					break;
174  				}
175  			}
176  	
177  			if (isalnum(cmd_str[i]) || ispunct(cmd_str[i])) {
178  				arg[arg_len++] = cmd_str[i];
179  			} else if (isspace(cmd_str[i])) {
180  				if (arg_len)
181  					av[av_count++] = strdup(arg);
182  	
183  				memset(arg, 0, sizeof(arg));
184  				arg_len = 0;
185  			} else {
186  				break;
187  			}
188  		}
189  	
190  		if ((av_count < MAX_AV_COUNT) && arg_len) {
191  			av[av_count++] = strdup(arg);
192  		}
193  	
194  		/*
195  		for (i = 0; i < MAX_AV_COUNT + 1; i++) {
196  			if (!av[i])
197  				break;
198  	
199  			log_helper("command av[%d] \"%s\"", i, av[i]);
200  		}
201  		*/
202  	
203  		cmd_id = _get_cmd_id(av, av_count);
204  	
205  		/* tell the parent the command we have identified to run */
206  		memset(cmd_buf, 0, sizeof(cmd_buf));
207  		snprintf(cmd_buf, sizeof(cmd_buf), "cmd_id %d", cmd_id);
208  		rv = write(out_fd, cmd_buf, sizeof(cmd_buf));
209  		if (rv < 0)
210  			log_helper("write cmd_buf from child error %d", rv);
211  		close(out_fd);
212  	
213  		/* if we return before exec, the child does exit(1) (failure) */
214  		if (!cmd_id)
215  			return;
216  	
217  		execvp(av[0], av);
218  	}
219  	
220  	static int read_request(int fd, struct run_request *req)
221  	{
222  		int rv;
223  	 retry:
(1) Event string_null_source: Function "read" does not terminate string "req". [Note: The source code implementation of the function has been overridden by a builtin model.]
224  		rv = read(fd, req, sizeof(struct run_request));
(2) Event cond_false: Condition "rv == -1", taking false branch.
225  		if (rv == -1 && errno == EINTR)
(3) Event if_end: End of if statement.
226  			goto retry;
227  	
(4) Event cond_false: Condition "rv != 1160UL /* sizeof (struct run_request) */", taking false branch.
228  		if (rv != sizeof(struct run_request))
(5) Event if_end: End of if statement.
229  			return -1;
230  		return 0;
231  	}
232  	
233  	static int send_status(int fd)
234  	{
235  		struct run_reply rep;
236  		int rv;
237  	
238  		memset(&rep, 0, sizeof(rep));
239  	
240  		rv = write(fd, &rep, sizeof(rep));
241  	
242  		if (rv == sizeof(rep))
243  			return 0;
244  		return -1;
245  	}
246  	
247  	static int send_result(struct running *running, int fd, int pid, int result)
248  	{
249  		struct run_reply rep;
250  		int rv;
251  	
252  		memset(&rep, 0, sizeof(rep));
253  	
254  		rep.header.type = DLM_MSG_RUN_REPLY;
255  	
256  		memcpy(rep.uuid, running->uuid, RUN_UUID_LEN);
257  		rep.info.local_pid = pid;
258  		rep.info.local_result = result;
259  	
260  		rv = write(fd, &rep, sizeof(rep));
261  	
262  		if (rv == sizeof(rep))
263  			return 0;
264  		return -1;
265  	}
266  	
267  	#define HELPER_STATUS_INTERVAL 30
268  	#define STANDARD_TIMEOUT_MS (HELPER_STATUS_INTERVAL*1000)
269  	#define RECOVERY_TIMEOUT_MS 1000
270  	
271  	/* run by the child helper process forked by dlm_controld in setup_helper */
272  	
273  	int run_helper(int in_fd, int out_fd, int log_stderr)
274  	{
275  		struct pollfd pollfd;
276  		struct run_request req;
277  		struct running *running;
278  		struct dlm_header *hd = (struct dlm_header *)&req;
279  		char cmd_buf[16];
280  		siginfo_t info;
281  		unsigned int fork_count = 0;
282  		unsigned int done_count = 0;
283  		time_t now, last_send, last_good = 0;
284  		int timeout = STANDARD_TIMEOUT_MS;
285  		int rv, pid, cmd_id;
286  	
287  		_log_stderr = log_stderr;
288  	
289  		rv = setgroups(0, NULL);
(1) Event cond_true: Condition "rv < 0", taking true branch.
290  		if (rv < 0)
(2) Event cond_true: Condition "_log_stderr", taking true branch.
291  			log_helper("error clearing helper groups errno %i", errno);
292  	
293  		memset(&pollfd, 0, sizeof(pollfd));
294  		pollfd.fd = in_fd;
295  		pollfd.events = POLLIN;
296  	
297  		now = monotime();
298  		last_send = now;
299  		rv = send_status(out_fd);
(3) Event cond_true: Condition "!rv", taking true branch.
300  		if (!rv)
301  			last_good = now;
302  	
303  		openlog("dlm_controld", LOG_CONS | LOG_PID, LOG_LOCAL4);
304  	
305  		while (1) {
306  			rv = poll(&pollfd, 1, timeout);
(4) Event cond_true: Condition "rv == -1", taking true branch.
(5) Event cond_true: Condition "*__errno_location() == 4", taking true branch.
(8) Event cond_false: Condition "rv == -1", taking false branch.
307  			if (rv == -1 && errno == EINTR)
(6) Event continue: Continuing loop.
(9) Event if_end: End of if statement.
308  				continue;
309  	
(10) Event cond_false: Condition "rv < 0", taking false branch.
310  			if (rv < 0)
(11) Event if_end: End of if statement.
311  				exit(0);
312  	
313  			now = monotime();
314  	
(12) Event cond_true: Condition "now - last_good >= 30", taking true branch.
(13) Event cond_true: Condition "now - last_send >= 2", taking true branch.
315  			if (now - last_good >= HELPER_STATUS_INTERVAL &&
316  			    now - last_send >= 2) {
317  				last_send = now;
318  				rv = send_status(out_fd);
(14) Event cond_true: Condition "!rv", taking true branch.
319  				if (!rv)
320  					last_good = now;
321  			}
322  	
323  			memset(&req, 0, sizeof(req));
324  	
(15) Event cond_true: Condition "pollfd.revents & 1", taking true branch.
325  			if (pollfd.revents & POLLIN) {
(16) Event string_null_source: Function "read_request" does not terminate string "req". [details]
Also see events: [string_null]
326  				rv = read_request(in_fd, &req);
(17) Event cond_false: Condition "rv", taking false branch.
327  				if (rv)
(18) Event if_end: End of if statement.
328  					continue;
329  	
(19) Event cond_false: Condition "hd->type == DLM_MSG_RUN_REQUEST", taking false branch.
330  				if (hd->type == DLM_MSG_RUN_REQUEST) {
331  					int cmd_pipe[2];
332  	
333  					if (running_count >= MAX_RUNNING) {
334  						log_helper("too many running commands");
335  						exit(1);
336  					}
337  	
338  					/*
339  					 * Child writes cmd_buf to cmd_pipe, parent reads
340  					 * cmd_buf from cmd_pipe.  cmd_buf contains the
341  					 * string "cmd_id <num>" where <num> is CMD_ID_NUM
342  					 * identifying the command being run by the child.
343  					 */
344  	
345  					if (pipe(cmd_pipe))
346  						exit(1);
347  	
348  					pid = fork();
349  					if (!pid) {
350  						close(cmd_pipe[0]);
351  						exec_command(req.command, cmd_pipe[1]);
352  						exit(1);
353  					}
354  	
355  					close(cmd_pipe[1]);
356  	
357  					memset(cmd_buf, 0, sizeof(cmd_buf));
358  					cmd_id = 0;
359  	
360  					rv = read(cmd_pipe[0], cmd_buf, sizeof(cmd_buf));
361  					if (rv < 0)
362  						log_helper("helper read child cmd_id error %d", rv);
363  	
364  					close(cmd_pipe[0]);
365  	
366  					sscanf(cmd_buf, "cmd_id %d", &cmd_id);
367  	
368  					_save_running_cmd(req.uuid, pid, cmd_id);
369  	
370  					fork_count++;
371  	
372  					log_helper("helper run %s pid %d cmd_id %d running %d fork_count %d done_count %d %s",
373  						   req.uuid, pid, cmd_id, running_count, fork_count, done_count, req.command);
374  	
(20) Event else_branch: Reached else branch.
(21) Event cond_true: Condition "hd->type == DLM_MSG_RUN_CANCEL", taking true branch.
375  				} else if (hd->type == DLM_MSG_RUN_CANCEL) {
376  	
377  					/* TODO: should we also send kill to the pid? */
378  	
(22) Event string_null: Passing unterminated string "req.uuid" to "_get_running_uuid", which expects a null-terminated string. [details]
Also see events: [string_null_source]
379  					if (!(running = _get_running_uuid(req.uuid)))
380  						log_helper("no running cmd for cancel uuid");
381  					else {
382  						log_helper("cancel running cmd uuid %s pid %d", running->uuid, running->pid);
383  						_clear_running_cmd(running);
384  					}
385  				}
386  			}
387  	
388  			if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL))
389  				exit(0);
390  	
391  			/* collect child exits until no more children exist (ECHILD)
392  			   or none are ready (WNOHANG) */
393  	
394  			while (1) {
395  				memset(&info, 0, sizeof(info));
396  	
397  				rv = waitid(P_ALL, 0, &info, WEXITED | WNOHANG);
398  	
399  				if ((rv < 0) && (errno == ECHILD)) {
400  					/*
401  					log_helper("helper no children exist fork_count %d done_count %d", fork_count, done_count);
402  					*/
403  					timeout = STANDARD_TIMEOUT_MS;
404  				}
405  	
406  				else if (!rv && !info.si_pid) {
407  					log_helper("helper no children ready fork_count %d done_count %d", fork_count, done_count);
408  					timeout = RECOVERY_TIMEOUT_MS;
409  				}
410  	
411  				else if (!rv && info.si_pid) {
412  					done_count++;
413  	
414  					if (!(running = _get_running_cmd(info.si_pid))) {
415  						log_helper("running cmd for pid %d result %d not found",
416  							   info.si_pid, info.si_status);
417  						continue;
418  					} else {
419  						log_helper("running cmd for pid %d result %d done",
420  							   info.si_pid, info.si_status);
421  					}
422  	
423  					if (info.si_status) {
424  						syslog(LOG_ERR, "%llu run error %s id %d pid %d status %d code %d",
425  						       (unsigned long long)monotime(),
426  						       running->uuid, running->cmd_id, running->pid,
427  						       info.si_status, info.si_code);
428  					}
429  	
430  					send_result(running, out_fd, info.si_pid, info.si_status);
431  	
432  					_clear_running_cmd(running);
433  					continue;
434  				}
435  	
436  				else {
437  					log_helper("helper waitid rv %d errno %d fork_count %d done_count %d",
438  						  rv, errno, fork_count, done_count);
439  				}
440  	
441  				break;
442  			}
(7) Event loop: Looping back.
443  		}
444  	
445  		closelog();
446  		return 0;
447  	}
448