1 /*-
2  * Copyright (c) 2000-2003 Andrey Simonenko
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 
29 #ifndef lint
30 static const char rcsid[] ATTR_UNUSED =
31   "@(#)$Id: ipa_cmd.c,v 1.3.2.1 2011/11/15 18:12:29 simon Exp $";
32 #endif /* !lint */
33 
34 #include <sys/types.h>
35 #include <sys/time.h>
36 #include <sys/wait.h>
37 
38 #include <errno.h>
39 #include <grp.h>
40 #include <limits.h>
41 #include <pwd.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 
48 #ifdef HAVE_PATHS_H
49 # include <paths.h>
50 #endif
51 #ifndef _PATH_BSHELL
52 # ifdef __GNUC__
53 #  warning "defining _PATH_BSHELL to \"/bin/sh\""
54 # endif
55 # define _PATH_BSHELL "/bin/sh"
56 #endif
57 
58 #include "ipa_mod.h"
59 
60 #include "queue.h"
61 
62 #include "dlapi.h"
63 #include "confcommon.h"
64 #include "memfunc.h"
65 #include "parser.h"
66 
67 #include "ipa_ac.h"
68 #include "ipa_db.h"
69 #include "ipa_ctl.h"
70 #include "ipa_cmd.h"
71 #include "ipa_time.h"
72 
73 #include "ipa_conf.h"
74 #include "ipa_log.h"
75 #include "ipa_main.h"
76 #include "ipa_rules.h"
77 #include "ipa_autorules.h"
78 
79 #ifndef WCOREDUMP
80 # define WCOREDUMP(x) (0)
81 #endif
82 
83 #if defined(HAVE_CLOSEFROM) && \
84 	(STDIN_FILENO > 2 || STDOUT_FILENO > 2 || STDERR_FILENO > 2)
85 # undef HAVE_CLOSEFROM
86 #endif
87 
88 /*
89  * run_cmd() and run_cmd_cons() return -1 only if they cannot fork()
90  * a new process or if they get an error from waitpid(), if any error
91  * occurred in a child forked from these functions, then such error
92  * is invisible for run_cmds*() functions.
93  */
94 
95 /*
96  * If run_cmds() function returns -1, then we should terminate ipa(8).
97  */
98 
99 /*
100  * If return code from a child forked in run_cmdl_bg() or
101  * in run_cmdl_fg() is non-zero, then we should terminate ipa(8).
102  */
103 
104 /* Mzone for all struct cmd{}. */
105 ipa_mzone	*cmd_mzone;
106 
107 /* Memory for commands. */
108 ipa_mem_type	*m_cmd;
109 
110 /* shell_path parameter and its default value. */
111 char		*shell_path = NULL;
112 char		*shell_path_default = _PATH_BSHELL;
113 
114 /* shell_arg1 parameter and its default value. */
115 char		*shell_arg1 = NULL;
116 char		*shell_arg1_default = "-c";
117 
118 /* Section names for RC_STARTUP and RC_SHUTDOWN. */
119 const char *const rc_sect_name[] = { "startup", "shutdown" };
120 
121 signed char	global_debug_exec;	/* global { debug_exec } */
122 
123 struct cmds	cmds_global[2];		/* Global commands. */
124 
125 #ifdef WITH_ANY_LIMITS
126 struct wpid_hash wpid_hash[WPID_HASH_BUCKETS];
127 #endif
128 
129 static void	outerr(const char *, ...) ATTR_FORMAT(printf, 1, 2);
130 static void	outerrx(const char *, ...) ATTR_FORMAT(printf, 1, 2);
131 static void	outwarn(const char *, ...) ATTR_FORMAT(printf, 1, 2);
132 static void	run_cmd_child(const struct cmd *) ATTR_NORETURN;
133 static void	run_cmd_child_cons(const struct cmd *) ATTR_NORETURN;
134 static void	run_cmdl_child(const struct cmd_list *, int, const char *,
135 		    va_list) ATTR_FORMAT(printf, 3, 0) ATTR_NORETURN;
136 
137 /*
138  * Set supplementary groups for process, gid is a GID from getpwnam(),
139  * NGROUPS_MAX is defined in SUSv3.
140  */
141 int
setsuppgids(const char * user,gid_t gid)142 setsuppgids(const char *user, gid_t gid)
143 {
144 	gid_t gids[NGROUPS_MAX + 1];
145 	const struct group *grp;
146 	char *const *gr_user;
147 	int i, ngids;
148 
149 	gids[0] = gid;
150 	ngids = 1;
151 
152 	errno = 0;
153 	while ((grp = getgrent()) != NULL) {
154 		for (i = 0; i < ngids; ++i)
155 			if (grp->gr_gid == gids[i])
156 				goto next;
157 		for (gr_user = grp->gr_mem; *gr_user != NULL; ++gr_user) {
158 			if (strcmp(user, *gr_user) == 0) {
159 				if (ngids < NGROUPS_MAX + 1) {
160 					gids[ngids++] = grp->gr_gid;
161 					break;
162 				}
163 			}
164 		}
165 next:		;
166 	}
167 	if (errno != 0) {
168 		xlogmsgx(IPA_LOG_ERR, "setsuppgids: getgrent: %s",
169 		    strerror(errno));
170 		return (-1);
171 	}
172 	if (setgroups(ngids, gids) < 0) {
173 		xlogmsgx(IPA_LOG_ERR, "setsuppgids: setgroups: %s",
174 		    strerror(errno));
175 		return (-1);
176 	}
177 	return (0);
178 }
179 
180 static void
run_cmd_child(const struct cmd * cmd)181 run_cmd_child(const struct cmd *cmd)
182 {
183 	const struct passwd *pwd;
184 
185 	if (cmd->user != NULL) {
186 		errno = 0;
187 		pwd = getpwnam(cmd->user);
188 		if (pwd == NULL) {
189 			int errno_save;
190 
191 			errno_save = errno;
192 			open_log();
193 			if (errno_save != 0) {
194 				errno = errno_save;
195 				logmsg(IPA_LOG_ERR, "run_cmd_child: "
196 				    "getpwnam(%s)", cmd->user);
197 			} else
198 				logmsgx(IPA_LOG_ERR, "run_cmd_child: cannot "
199 				    "find user \"%s\"", cmd->user);
200 			goto failed;
201 		}
202 		if (setgid(pwd->pw_gid) < 0) {
203 			open_log();
204 			logmsg(IPA_LOG_ERR, "run_cmd_child: setgid(%lu)",
205 			    (unsigned long)pwd->pw_gid);
206 			goto failed;
207 		}
208 		if (setsuppgids(cmd->user, pwd->pw_gid) < 0) {
209 			open_log();
210 			logmsgx(IPA_LOG_ERR, "run_cmd_child: cannot set all "
211 			    "groups for user %s", cmd->user);
212 			goto failed;
213 		}
214 		if (setuid(pwd->pw_uid) < 0) {
215 			open_log();
216 			logmsg(IPA_LOG_ERR, "run_cmd_child: setuid(%lu)",
217 			    (unsigned long)pwd->pw_uid);
218 			goto failed;
219 		}
220 		/* Just, to be sure that descriptors are closed. */
221 		endpwent();
222 		endgrent();
223 	}
224 
225 	execl(shell_path, shell_path, shell_arg1, cmd->str, (char *)NULL);
226 	open_log();
227 	logmsg(IPA_LOG_ERR, "run_cmd_child: execl(\"%s\", \"%s\", \"%s\", %s)",
228 	    shell_path, shell_path, shell_arg1, parser_stringify(cmd->str));
229 failed:
230 	close_log();
231 	_exit(127);
232 }
233 
234 /*
235  * Run command changing user if needed, this function is
236  * called with disabled log system.  Return -1 if some error
237  * occurs and do not close log system in this case.
238  */
239 static int
run_cmd(const struct cmd * cmd)240 run_cmd(const struct cmd *cmd)
241 {
242 	pid_t pid;
243 	int status;
244 
245 	pid = fork();
246 	if (pid == (pid_t)-1) {
247 		open_log();
248 		logmsg(IPA_LOG_ERR, "run_cmd: fork");
249 		return (-1);
250 	}
251 
252 	if (pid == 0) {
253 		/* Child. */
254 		run_cmd_child(cmd);
255 		/* NOTREACHED */
256 	}
257 
258 	/* Parent. */
259 	while (waitpid(pid, &status, 0) == (pid_t)-1) {
260 		open_log();
261 		if (errno != EINTR) {
262 			logmsg(IPA_LOG_ERR, "run_cmd: waitpid(%ld)", (long)pid);
263 			return (-1);
264 		}
265 		logmsg(IPA_LOG_WARNING, "run_cmd: waitpid(%ld)", (long)pid);
266 		close_log();
267 	}
268 
269 	if (WIFEXITED(status)) {
270 		if (WEXITSTATUS(status) != 0) {
271 			open_log();
272 			logmsgx(IPA_LOG_WARNING, "run_cmd: exit status of "
273 			    "child %ld (%s) is %d%s", (long)pid,
274 			    parser_stringify(cmd->str), WEXITSTATUS(status),
275 			    WEXITSTATUS(status) == 127 ?
276 			    ", execution of the shell probably failed" : "");
277 			close_log();
278 		}
279 	} else {
280 		open_log();
281 		if (WIFSIGNALED(status))
282 			logmsgx(IPA_LOG_ERR, "run_cmd: abnormal termination "
283 			    "of child %ld (%s), signal %d%s", (long)pid,
284 			    parser_stringify(cmd->str), WTERMSIG(status),
285 			    WCOREDUMP(status) ? " (core file generated)" : "");
286 		else
287 			logmsgx(IPA_LOG_ERR, "run_cmd: termination of child "
288 			    "%ld (%s), unknown status", (long)pid,
289 			    parser_stringify(cmd->str));
290 		close_log();
291 	}
292 
293 	return (0);
294 }
295 
296 #ifndef HAVE_CLOSEFROM
297 static void
close_my_fd(int nofile_max)298 close_my_fd(int nofile_max)
299 {
300 	int fd;
301 
302 # if STDIN_FILENO < 3 && STDOUT_FILENO < 3 && STDERR_FILENO < 3
303 	for (fd = 3; fd < nofile_max; ++fd)
304 		(void)close(fd);
305 # else
306 	for (fd = 0; fd < nofile_max; ++fd)
307 		switch (fd) {
308 		case STDIN_FILENO:
309 		case STDOUT_FILENO:
310 		case STDERR_FILENO:
311 			break;
312 		default:
313 			(void)close(fd);
314 		}
315 # endif
316 }
317 #endif /* !HAVE_CLOSEFROM */
318 
319 static void
log_failed_cmd(const struct cmd * cmd,const char * format,va_list ap)320 log_failed_cmd(const struct cmd *cmd, const char *format, va_list ap)
321 {
322 	char *buf;
323 
324 	mem_vasprintf(m_tmp, &buf, format, ap);
325 	logmsgx(IPA_LOG_ERR, "execution of command %s from section %s failed",
326 	    parser_stringify(cmd->str), buf != NULL ? buf : "(no memory)");
327 	mem_free(buf, m_tmp);
328 }
329 
330 static void
run_cmdl_child(const struct cmd_list * cmd_list,int debug_exec,const char * format,va_list ap)331 run_cmdl_child(const struct cmd_list *cmd_list, int debug_exec,
332     const char *format, va_list ap)
333 {
334 	const struct cmd *cmd;
335 #ifndef HAVE_CLOSEFROM
336 	long val;
337 #endif
338 
339 	/* Close all descriptors, except stdin, stdout and stderr. */
340 #ifdef HAVE_CLOSEFROM
341 	close_log();
342 	(void)closefrom(3);
343 #else
344 	/*
345 	 * Since a module can call setrlimit(), it is necessary
346 	 * to call sysconf(_SC_OPEN_MAX).
347 	 */
348 	errno = 0;
349 	val = sysconf(_SC_OPEN_MAX);
350 	if (val < 0) {
351 		if (errno != 0)
352 			logmsg(IPA_LOG_ERR, "run_cmdl_child: "
353 			    "sysconf(_SC_OPEN_MAX)");
354 		else
355 			logmsgx(IPA_LOG_ERR, "run_cmdl_child: infinite limit "
356 			    "for _SC_OPEN_MAX is not supported");
357 		goto failed;
358 	}
359 # if LONG_MAX > INT_MAX
360 	if (val > INT_MAX) {
361 		logmsgx(IPA_LOG_ERR, "run_cmdl_child: too big value %ld for "
362 		    "'int' type from sysconf(_SC_OPEN_MAX)", val);
363 		goto failed;
364 	}
365 # endif
366 	close_log();
367 	close_my_fd((int)val);
368 #endif /* HAVE_CLOSEFROM */
369 
370 	STAILQ_FOREACH(cmd, cmd_list, link) {
371 		if (debug_exec > 1) {
372 			open_log();
373 			logdbg("exec %s %s",
374 			    cmd->user == NULL ? "" : cmd->user,
375 			    parser_stringify(cmd->str));
376 			close_log();
377 		}
378 		if (run_cmd(cmd) < 0) {
379 			/* Log was opened in exec_cmd(). */
380 			logbt("run_cmdl_child");
381 			log_failed_cmd(cmd, format, ap);
382 			goto failed;
383 		}
384 	}
385 	_exit(EXIT_SUCCESS);
386 
387 failed:
388 	close_log();
389 	_exit(EXIT_FAILURE);
390 }
391 
392 /*
393  * Run commands list in background and return PID of a child
394  * (internal version).
395  */
396 static pid_t
run_cmdl(const struct cmd_list * cmd_list,int debug_exec,const char * format,va_list ap)397 run_cmdl(const struct cmd_list *cmd_list, int debug_exec,
398     const char *format, va_list ap)
399 {
400 	pid_t pid;
401 
402 	pid = fork();
403 	if (pid == (pid_t)-1) {
404 		logmsg(IPA_LOG_ERR, "run_cmdl: fork");
405 		return ((pid_t)-1);
406 	}
407 
408 	if (pid == 0) {
409 		/* Child. */
410 		run_cmdl_child(cmd_list, debug_exec, format, ap);
411 		/* NOTREACHED */
412 	}
413 
414 	/* Parent. */
415 	return (pid);
416 
417 }
418 
419 /*
420  * Run commands list in background and wait.
421  */
422 static int
run_cmdl_fg(const struct cmd_list * cmd_list,int debug_exec,const char * format,va_list ap)423 run_cmdl_fg(const struct cmd_list *cmd_list, int debug_exec,
424     const char *format, va_list ap)
425 {
426 	pid_t pid;
427 	int status;
428 
429 	pid = run_cmdl(cmd_list, debug_exec, format, ap);
430 	if (pid == (pid_t)-1) {
431 		logbt("run_cmdl_fg");
432 		return (-1);
433 	}
434 
435 	while (waitpid(pid, &status, 0) == (pid_t)-1) {
436 		if (errno != EINTR) {
437 			logmsg(IPA_LOG_ERR, "run_cmdl_fg: waitpid(%ld)",
438 			    (long)pid);
439 			return (-1);
440 		}
441 		logmsg(IPA_LOG_WARNING, "run_cmdl_fg: waitpid(%ld)",
442 		    (long)pid);
443 	}
444 	log_stdall();
445 	if (WIFEXITED(status)) {
446 		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
447 			logmsgx(IPA_LOG_WARNING, "run_cmdl_fg: exit status of "
448 			    "child %ld is %d",(long)pid, WEXITSTATUS(status));
449 			return (-1);
450 		}
451 	} else {
452 		if (WIFSIGNALED(status))
453 			logmsgx(IPA_LOG_ERR, "run_cmdl_fg: abnormal "
454 			    "termination of child %ld, signal %d%s", (long)pid,
455 			    WTERMSIG(status), WCOREDUMP(status) ?
456 			    " (core file generated)" : "");
457 		else
458 			logmsgx(IPA_LOG_ERR, "run_cmdl_fg: termination of "
459 			    "child %ld, unknown status", (long)pid);
460 		return (-1);
461 	}
462 
463 	return (0);
464 }
465 
466 /*
467  * Run commands list in background and return PID of a child.
468  */
469 static pid_t
run_cmdl_bg(const struct cmd_list * cmd_list,int debug_exec,const char * format,va_list ap)470 run_cmdl_bg(const struct cmd_list *cmd_list, int debug_exec,
471     const char *format, va_list ap)
472 {
473 	pid_t pid;
474 
475 	pid = run_cmdl(cmd_list, debug_exec, format, ap);
476 	if (pid == (pid_t)-1)
477 		logbt("run_cmdl_bg");
478 	return (pid);
479 }
480 
481 #ifdef WITH_ANY_LIMITS
482 
483 #define WPID_ARG_ATTR
484 
485 /*
486  * Initialize lists of wpid_hash;
487  */
488 void
wpid_hash_init(void)489 wpid_hash_init(void)
490 {
491 	struct wpid_hash *h;
492 
493 	for (h = wpid_hash; h < wpid_hash + WPID_HASH_BUCKETS; ++h)
494 		LIST_INIT(h);
495 }
496 
497 /*
498  * Return non-zero if all lists in wpid_hash are empty,
499  * otherwise return zero.
500  */
501 int
wpid_hash_is_empty(void)502 wpid_hash_is_empty(void)
503 {
504 	const struct wpid_hash *h;
505 
506 	for (h = wpid_hash; h < wpid_hash + WPID_HASH_BUCKETS; ++h)
507 		if (!LIST_EMPTY(h))
508 			return (0);
509 	return (1);
510 }
511 
512 /*
513  * Remove wpid structure from the hash, wpid is there
514  * if its pid field is not equal to zero.
515  */
516 void
wpid_hash_rem(struct wpid * wpid)517 wpid_hash_rem(struct wpid *wpid)
518 {
519 	if (wpid->pid != 0) {
520 		wpid->pid = 0;
521 		LIST_REMOVE(wpid, link);
522 	}
523 }
524 
525 /*
526  * Quick version of wpid_hash_rem().
527  */
528 # define wpid_hash_rem_quick(w)	\
529 	LIST_REMOVE((w), link)
530 
531 /*
532  * Add wpid to wpid_hash.
533  */
534 # define wpid_hash_add(w) \
535 	LIST_INSERT_HEAD(&wpid_hash[wpid_hash_bucket((w)->pid)], wpid, link)
536 
537 /*
538  * Lookup wpid for the given PID.
539  */
540 struct wpid *
wpid_lookup_pid(pid_t pid)541 wpid_lookup_pid(pid_t pid)
542 {
543 	struct wpid_hash *h;
544 	struct wpid *wpid;
545 
546 	h = &wpid_hash[wpid_hash_bucket(pid)];
547 	LIST_FOREACH(wpid, h, link)
548 		if (wpid->pid == pid)
549 			break;
550 	return (wpid);
551 }
552 
553 /*
554  * Log message about previous wpid.
555  */
556 static void
log_prev_wpid(const struct wpid * wpid,const char * format,va_list ap)557 log_prev_wpid(const struct wpid *wpid, const char *format, va_list ap)
558 {
559 	char *buf;
560 
561 	mem_vasprintf(m_tmp, &buf, format, ap);
562 	logmsgx(IPA_LOG_WARNING, "previously run process with PID %ld "
563 	    "for %s has not exited yet", (long)wpid->pid,
564 	    buf != NULL ? buf : "(no memory)");
565 	mem_free(buf, m_tmp);
566 }
567 
568 void
log_term_wpid(const struct wpid * wpid)569 log_term_wpid(const struct wpid *wpid)
570 {
571 	const struct rule *rule;
572 	long pid;
573 
574 	pid = (long)wpid->pid;
575 	switch (wpid->type) {
576 # ifdef WITH_LIMITS
577 	case WPID_TYPE_LIMIT:
578 		{
579 			const struct limit *limit;
580 
581 			limit = wpid->u.limit;
582 			rule = limit->rule;
583 			logdbg("rule %s, limit %s: child %ld exited",
584 			    rule->name, limit->name, pid);
585 		}
586 		break;
587 # endif
588 # ifdef WITH_SUBLIMITS
589 	case WPID_TYPE_SUBLIMIT:
590 		{
591 			const struct sublimit *sublimit;
592 			const struct limit *limit;
593 
594 			sublimit = wpid->u.sublimit;
595 			limit = sublimit->limit;
596 			rule = limit->rule;
597 			logdbg("rule %s, limit %s, sublimit %s: "
598 			    "child %ld exited", rule->name, limit->name,
599 			    sublimit->name, pid);
600 		}
601 		break;
602 # endif
603 # ifdef WITH_THRESHOLDS
604 	case WPID_TYPE_THRESHOLD:
605 		{
606 			const struct threshold *threshold;
607 
608 			threshold = wpid->u.threshold;
609 			rule = threshold->rule;
610 			logdbg("rule %s, threshold %s: child %ld exited",
611 			    rule->name, threshold->name, pid);
612 		}
613 		break;
614 # endif
615 	}
616 }
617 
618 #else
619 
620 # define WPID_ARG_ATTR ATTR_UNUSED
621 
622 #endif /* WITH_ANY_LIMITS */
623 
624 /*
625  * Log debug message about running commands.
626  */
627 static void
log_run_cmds(const struct cmds * cmds,const char * format,va_list ap)628 log_run_cmds(const struct cmds *cmds, const char *format, va_list ap)
629 {
630 	char *buf;
631 
632 	mem_vasprintf(m_tmp, &buf, format, ap);
633 	logdbg("running %s (%ssync)", buf != NULL ? buf : "(no memory)",
634 	    cmds->sync ? "" : "a");
635 	mem_free(buf, m_tmp);
636 }
637 
638 /*
639  * Log debug message about failed commands.
640  */
641 static void
log_failed_cmds(const struct cmds * cmds,const char * format,va_list ap)642 log_failed_cmds(const struct cmds *cmds, const char *format, va_list ap)
643 {
644 	char *buf;
645 
646 	mem_vasprintf(m_tmp, &buf, format, ap);
647 	logmsgx(IPA_LOG_ERR, "failed %s (%ssync)",
648 	    buf != NULL ? buf : "(no memory)", cmds->sync ? "" : "a");
649 	mem_free(buf, m_tmp);
650 }
651 
652 /*
653  * Run commands from cmds, rule and format with
654  * rest arguments are used for debugging.
655  */
656 int
run_cmds(const struct rule * rule,struct wpid * wpid WPID_ARG_ATTR,const struct cmds * cmds,const char * format,...)657 run_cmds(const struct rule *rule, struct wpid *wpid WPID_ARG_ATTR,
658     const struct cmds *cmds, const char *format, ...)
659 {
660 	const struct cmd_list *cmd_list;
661 	va_list ap;
662 	pid_t pid;
663 	int debug_exec;
664 
665 	va_start(ap, format);
666 	debug_exec = rule->debug_exec;
667 	if (debug_exec)
668 		log_run_cmds(cmds, format, ap);
669 
670 #ifdef WITH_RULES
671 	if (!STAILQ_EMPTY(&cmds->ictl_list))
672 		if (run_ictl_list(&cmds->ictl_list) < 0)
673 			goto failed;
674 #endif
675 
676 	cmd_list = &cmds->cmd_list;
677 	if (!STAILQ_EMPTY(cmd_list)) {
678 		if (cmds->sync) {
679 			if (run_cmdl_fg(cmd_list, debug_exec, format, ap) < 0)
680 				goto failed;
681 		} else {
682 #ifdef WITH_ANY_LIMITS
683 			if (wpid != NULL && wpid->pid != 0) {
684 				log_prev_wpid(wpid, format, ap);
685 				wpid_hash_rem_quick(wpid);
686 			}
687 #endif
688 			pid = run_cmdl_bg(cmd_list, debug_exec, format, ap);
689 			if (pid == (pid_t)-1)
690 				goto failed;
691 #ifdef WITH_ANY_LIMITS
692 			if (wpid != NULL) {
693 				wpid->pid = pid;
694 				wpid_hash_add(wpid);
695 			}
696 #endif
697 		}
698 	}
699 
700 	va_end(ap);
701 	return (0);
702 
703 failed:
704 	logbt("run_cmds");
705 	log_failed_cmds(cmds, format, ap);
706 	va_end(ap);
707 	return (-1);
708 }
709 
710 /*
711  * Output a message to stderr and an error message if errno != 0.
712  */
713 static void
outerr(const char * format,...)714 outerr(const char *format, ...)
715 {
716 	va_list ap;
717 	int errno_save;
718 
719 	errno_save = errno;
720 	va_start(ap, format);
721 	vlogmsgx_stderr(IPA_LOG_ERR, format, ap);
722 	va_end(ap);
723 	if (errno_save != 0)
724 		fprintf(stderr, "Error: %s.\n", strerror(errno_save));
725 }
726 
727 /*
728  * Output a message to stderr.
729  */
730 static void
outerrx(const char * format,...)731 outerrx(const char *format, ...)
732 {
733 	va_list ap;
734 
735 	va_start(ap, format);
736 	vlogmsgx_stderr(IPA_LOG_ERR, format, ap);
737 	va_end(ap);
738 }
739 
740 /*
741  * Output a warning message to stderr.
742  */
743 static void
outwarn(const char * format,...)744 outwarn(const char *format, ...)
745 {
746 	va_list ap;
747 
748 	va_start(ap, format);
749 	vlogmsgx_stderr(IPA_LOG_WARNING, format, ap);
750 	va_end(ap);
751 }
752 
753 static void
run_cmd_child_cons(const struct cmd * cmd)754 run_cmd_child_cons(const struct cmd *cmd)
755 {
756 	const struct passwd *pwd;
757 
758 	if (cmd->user != NULL) {
759 		errno = 0;
760 		pwd = getpwnam(cmd->user);
761 		if (pwd == NULL) {
762 			if (errno != 0)
763 				outerr("run_cmd_child_cons: getpwnam(%s)",
764 				    cmd->user);
765 			else
766 				outerrx("run_cmd_child_cons: cannot find "
767 				    "user \"%s\"", cmd->user);
768 			goto failed;
769 		}
770 		if (setgid(pwd->pw_gid) < 0) {
771 			outerr("run_cmd_child_cons: setgid(%lu)",
772 			    (unsigned long)pwd->pw_gid);
773 			goto failed;
774 		}
775 		if (setsuppgids(cmd->user, pwd->pw_gid) < 0) {
776 			outerrx("run_cmd_child_cons: cannot set all groups "
777 			    "for user %s", cmd->user);
778 			goto failed;
779 		}
780 		if (setuid(pwd->pw_uid) < 0) {
781 			outerr("run_cmd_child_cons: setuid(%lu)",
782 			    (unsigned long)pwd->pw_uid);
783 			goto failed;
784 		}
785 		/* Just, to be sure that descriptors are closed. */
786 		endpwent();
787 		endgrent();
788 	}
789 
790 	execl(shell_path, shell_path, shell_arg1, cmd->str, (char *)NULL);
791 	outerr("run_cmd_child_cons: execl(\"%s\", \"%s\", \"%s\", %s)",
792 	    shell_path, shell_path, shell_arg1, parser_stringify(cmd->str));
793 failed:
794 	_exit(127);
795 }
796 
797 /*
798  * Run a command in foreground.  Return -1 if some error occurs.
799  */
800 static int
run_cmd_cons(const struct cmd * cmd)801 run_cmd_cons(const struct cmd *cmd)
802 {
803 	pid_t pid;
804 	int status;
805 
806 	pid = fork();
807 	if (pid == (pid_t)-1) {
808 		outerr("run_cmd_cons: fork");
809 		return (-1);
810 	}
811 
812 	if (pid == 0) {
813 		/* Child. */
814 		run_cmd_child_cons(cmd);
815 		/* NOTREACHED */
816 	}
817 
818 	/* Parent. */
819 	while (waitpid(pid, &status, 0) == (pid_t)-1)
820 		if (errno != EINTR) {
821 			outerr("run_cmd_cons: waitpid");
822 			return (-1);
823 		}
824 
825 	if (WIFEXITED(status)) {
826 		if (WEXITSTATUS(status) != 0)
827 			outwarn("run_cmd_cons: exit status of child is %d%s",
828 			    WEXITSTATUS(status), WEXITSTATUS(status) == 127 ?
829 			    ", execution of the shell probably failed" : "");
830 	} else if (WIFSIGNALED(status))
831 		outwarn("run_cmd_cons: abnormal termination of child, signal "
832 		    "%d%s", WTERMSIG(status), WCOREDUMP(status) ?
833 		    " (core file generated)" : "");
834 	else
835 		outwarn("run_cmd_cons: termination of child, unknown status");
836 
837 	return (0);
838 }
839 
840 /*
841  * Run commands from cmd_list in foreground, ignoring if
842  * some command cannot be run, user does not exist, etc.
843  */
844 int
run_cmds_cons(const struct cmds * cmds,const char * message,...)845 run_cmds_cons(const struct cmds *cmds, const char *message, ...)
846 {
847 	const struct cmd_list *cmd_list;
848 	const struct cmd *cmd;
849 	va_list ap;
850 #ifndef HAVE_CLOSEFROM
851 	long val;
852 #endif
853 
854 	printf("Running commands from ");
855 	va_start(ap, message);
856 	vprintf(message, ap);
857 	va_end(ap);
858 	printf(" section:\n");
859 
860 #ifdef WITH_RULES
861 	if (!STAILQ_EMPTY(&cmds->ictl_list))
862 		printf("- skipping all \"ictl\" commands\n");
863 #endif
864 
865 	cmd_list = &cmds->cmd_list;
866 	if (STAILQ_EMPTY(cmd_list)) {
867 		printf("- nothing to run\n");
868 		return (0);
869 	}
870 
871 	/* Close all descriptors, except stdin, stdout and stderr. */
872 #ifdef HAVE_CLOSEFROM
873 	(void)closefrom(3);
874 #else
875 	errno = 0;
876 	val = sysconf(_SC_OPEN_MAX);
877 	if (val < 0) {
878 		if (errno != 0)
879 			outerr("run_cmds_cons: sysconf(_SC_OPEN_MAX)");
880 		else
881 			outerrx("run_cmds_cons: infinite limit for "
882 			    "_SC_OPEN_MAX is not supported");
883 		return (-1);
884 	}
885 # if LONG_MAX > INT_MAX
886 	if (val > INT_MAX) {
887 		outerrx("run_cmds_cons: too big value %ld for 'int' type "
888 		    "from sysconf(_SC_OPEN_MAX)", val);
889 		return (-1);
890 	}
891 # endif
892 	close_my_fd((int)val);
893 #endif /* HAVE_CLOSEFROM */
894 
895 	STAILQ_FOREACH(cmd, cmd_list, link) {
896 		printf("* exec ");
897 		if (cmd->user != NULL)
898 			printf("%s ", cmd->user);
899 		print_string(cmd->str);
900 		printf("\n");
901 		fflush(stdout);
902 		if (run_cmd_cons(cmd) < 0) {
903 			outerrx("stop running commands");
904 			return (-1);
905 		}
906 	}
907 
908 	return (0);
909 }
910 
911 #ifdef WITH_SUBLIMITS
912 static int
run_cmds_sublimit(const struct rule * rule,const struct limit * limit,struct sublimit * sublimit,unsigned int x)913 run_cmds_sublimit(const struct rule *rule, const struct limit *limit,
914     struct sublimit *sublimit, unsigned int x)
915 {
916 	const char *rule_name, *limit_name, *sublimit_name, *sect_name;
917 	const struct cmds *cmds;
918 	struct cmds_limit *cmds_limit;
919 
920 	rule_name = rule->name;
921 	limit_name = limit->name;
922 	sublimit_name = sublimit->name;
923 	sect_name = rc_sect_name[x];
924 	cmds_limit = &sublimit->rc[x];
925 	cmds = &cmds_limit->cmds;
926 	if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL, cmds,
927 	    "rule %s { limit %s { sublimit %s { %s {}}}}",
928 	    rule_name, limit_name, sublimit_name, sect_name) < 0)
929 		goto failed;
930 	if (SUBLIMIT_IS_REACHED(sublimit)) {
931 		cmds = &cmds_limit->cmds_reached;
932 		if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL, cmds,
933 		    "rule %s { limit %s { sublimit %s { %s { "
934 		    "if_reached{}}}}}", rule_name, limit_name,
935 		    sublimit_name, sect_name) < 0)
936 			goto failed;
937 	} else {
938 		cmds = &cmds_limit->cmds_not_reached;
939 		if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL, cmds,
940 		    "rule %s { limit %s { sublimit %s { %s { "
941 		    "if_not_reached{}}}}}", rule_name, limit_name,
942 		    sublimit_name, sect_name) < 0)
943 			goto failed;
944 	}
945 	cmds_limit_free(cmds_limit);
946 	return (0);
947 
948 failed:
949 	logbt("run_cmds_sublimit");
950 	return (-1);
951 }
952 #endif
953 
954 #ifdef WITH_LIMITS
955 static int
run_cmds_limit(const struct rule * rule,struct limit * limit,unsigned int x)956 run_cmds_limit(const struct rule *rule, struct limit *limit, unsigned int x)
957 {
958 	const struct cmds *cmds;
959 	const char *rule_name, *limit_name, *sect_name;
960 	struct cmds_limit *cmds_limit;
961 #ifdef WITH_SUBLIMITS
962 	struct sublimit	*sublimit;
963 #endif
964 
965 	rule_name = rule->name;
966 	limit_name = limit->name;
967 	sect_name = rc_sect_name[x];
968 	cmds_limit = &limit->rc[x];
969 	cmds = &cmds_limit->cmds;
970 	if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL, cmds,
971 	    "rule %s { limit %s { %s {}}}", rule_name, limit_name,
972 	    sect_name) < 0)
973 		goto failed;
974 	if (LIMIT_IS_REACHED(limit)) {
975 		cmds = &cmds_limit->cmds_reached;
976 		if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL,
977 		    cmds, "rule %s { limit %s { %s { if_reached{}}}}",
978 		    rule_name, limit_name, sect_name) < 0)
979 			goto failed;
980 		if (ac_limit_event(rule, limit, x == RC_STARTUP ?
981 		    IPA_LIMIT_EVENT_STARTUP_IF_REACHED :
982 		    IPA_LIMIT_EVENT_SHUTDOWN_IF_REACHED) < 0)
983 			goto failed;
984 	} else {
985 		cmds = &cmds_limit->cmds_not_reached;
986 		if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL,
987 		    cmds, "rule %s { limit %s { %s { if_not_reached{}}}}",
988 		    rule_name, limit_name, sect_name) < 0)
989 			goto failed;
990 		if (ac_limit_event(rule, limit, x == RC_STARTUP ?
991 		    IPA_LIMIT_EVENT_STARTUP_IF_NOT_REACHED :
992 		    IPA_LIMIT_EVENT_SHUTDOWN_IF_NOT_REACHED) < 0)
993 			goto failed;
994 	}
995 	cmds_limit_free(cmds_limit);
996 # ifdef WITH_SUBLIMITS
997 	STAILQ_FOREACH(sublimit, &limit->sublimits, link)
998 		if (run_cmds_sublimit(rule, limit, sublimit, x) < 0)
999 			goto failed;
1000 # endif
1001 	return (0);
1002 
1003 failed:
1004 	logbt("run_cmds_limit");
1005 	return (-1);
1006 }
1007 #endif /* WITH_LIMITS */
1008 
1009 #ifdef WITH_THRESHOLDS
1010 static int
run_cmds_threshold(const struct rule * rule,struct threshold * threshold,unsigned int x)1011 run_cmds_threshold(const struct rule *rule, struct threshold *threshold,
1012     unsigned int x)
1013 {
1014 	struct cmds *cmds;
1015 
1016 	cmds = &threshold->rc[x];
1017 	if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL, cmds,
1018 	    "rule %s { threshold %s { %s {}}}", rule->name, threshold->name,
1019 	    rc_sect_name[x]) < 0) {
1020 		logbt("run_cmds_threshold");
1021 		return (-1);
1022 	}
1023 	cmds_free(cmds);
1024 	return (0);
1025 }
1026 #endif
1027 
1028 /*
1029  * Run startup or shutdown commands for a rule, its limits,
1030  * sublimits and thresholds.
1031  */
1032 int
run_cmds_rule(struct rule * rule,unsigned int x)1033 run_cmds_rule(struct rule *rule, unsigned int x)
1034 {
1035 	const char *rule_name, *sect_name;
1036 	struct cmds *cmds;
1037 	struct cmds_rule *cmds_rule;
1038 #ifdef WITH_THRESHOLDS
1039 	struct threshold *threshold;
1040 #endif
1041 #ifdef WITH_LIMITS
1042 	struct limit *limit;
1043 	char any_reached, all_reached;
1044 #endif
1045 
1046 	rule_name = rule->name;
1047 	sect_name = rc_sect_name[x];
1048 	cmds_rule = &rule->rc[x];
1049 	cmds = &cmds_rule->cmds;
1050 	if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL, cmds,
1051 	    "rule %s { %s {}}", rule_name, sect_name) < 0)
1052 		goto failed;
1053 
1054 #ifdef WITH_LIMITS
1055 	any_reached = 0;
1056 	all_reached = 1;
1057 	STAILQ_FOREACH(limit, &rule->limits, link)
1058 		if (LIMIT_IS_REACHED(limit))
1059 			any_reached = 1;
1060 		else
1061 			all_reached = 0;
1062 	if (any_reached) {
1063 		cmds = &cmds_rule->cmds_any_reached;
1064 		if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL, cmds,
1065 		    "rule %s { %s { if_any_reached {}}}", rule_name,
1066 		    sect_name) < 0)
1067 			goto failed;
1068 	} else {
1069 		cmds = &cmds_rule->cmds_any_not_reached;
1070 		if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL, cmds,
1071 		    "rule %s { %s { if_any_not_reached {}}}", rule_name,
1072 		    sect_name) < 0)
1073 			goto failed;
1074 	}
1075 	if (all_reached) {
1076 		cmds = &cmds_rule->cmds_all_reached;
1077 		if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL, cmds,
1078 		    "rule %s { %s { if_all_reached {}}}", rule_name,
1079 		    sect_name) < 0)
1080 			goto failed;
1081 	} else {
1082 		cmds = &cmds_rule->cmds_all_not_reached;
1083 		if (cmds->has_cmd && run_cmds(rule, (struct wpid *)NULL, cmds,
1084 		    "rule %s { %s { if_all_not_reached {}}}", rule_name,
1085 		    sect_name) < 0)
1086 			goto failed;
1087 	}
1088 	STAILQ_FOREACH(limit, &rule->limits, link)
1089 		if (run_cmds_limit(rule, limit, x) < 0)
1090 			goto failed;
1091 #endif /* WITH_LIMITS */
1092 
1093 #ifdef WITH_THRESHOLDS
1094 	STAILQ_FOREACH(threshold, &rule->thresholds, link)
1095 		if (run_cmds_threshold(rule, threshold, x) < 0)
1096 			goto failed;
1097 #endif
1098 
1099 	cmds_rule_free(cmds_rule);
1100 	return (0);
1101 
1102 failed:
1103 	logbt("run_cmds_rule");
1104 	return (-1);
1105 }
1106 
1107 /*
1108  * Run startup or shutdown commands for everything.
1109  */
1110 int
run_all_cmds(unsigned int x)1111 run_all_cmds(unsigned int x)
1112 {
1113 	struct rule *rule;
1114 
1115 	logmsgx(IPA_LOG_INFO, "running %s commands...", rc_sect_name[x]);
1116 
1117 	if (cmds_global[x].has_cmd) {
1118 		struct rule rule_arg;
1119 
1120 		rule_arg.debug_exec = global_debug_exec;
1121 		if (run_cmds(&rule_arg, (struct wpid *)NULL, &cmds_global[x],
1122 		    "%s {}", rc_sect_name[x]) < 0)
1123 			goto failed;
1124 		cmds_free(&cmds_global[x]);
1125 	}
1126 
1127 	TAILQ_FOREACH(rule, &rules_list, list)
1128 		if (run_cmds_rule(rule, x) < 0)
1129 			goto failed;
1130 
1131 	return (0);
1132 
1133 failed:
1134 	logbt("run_all_cmds");
1135 	logmsgx(IPA_LOG_ERR, "stop running commands");
1136 	return (-1);
1137 }
1138 
1139 /*
1140  * Release memory used by commands list.
1141  */
1142 void
cmds_free(struct cmds * cmds)1143 cmds_free(struct cmds *cmds)
1144 {
1145 	struct cmd *cmd, *cmd_next;
1146 	struct cmd_list *cmd_list;
1147 	unsigned int mask;
1148 
1149 #ifdef WITH_RULES
1150 	if (!STAILQ_EMPTY(&cmds->ictl_list))
1151 		free_ictl_list(&cmds->ictl_list);
1152 #endif
1153 	cmd_list = &cmds->cmd_list;
1154 	STAILQ_FOREACH_SAFE(cmd, cmd_list, link, cmd_next) {
1155 		mask = cmd->free_mask;
1156 		if (mask & CMD_FREE_STR)
1157 			mem_free(cmd->str, m_cmd);
1158 		if (mask & CMD_FREE_USER)
1159 			mem_free(cmd->user, m_cmd);
1160 		mzone_free(cmd_mzone, cmd);
1161 	}
1162 	STAILQ_INIT(cmd_list);
1163 }
1164 
1165 /*
1166  * Set sync_exec to -1, this means that it is undefined and
1167  * initialize heads of actual lists of commands.
1168  */
1169 void
cmds_init(struct cmds * cmds)1170 cmds_init(struct cmds *cmds)
1171 {
1172 	cmds->sect_set = cmds->has_cmd = 0;
1173 	cmds->sync = -1;
1174 #ifdef WITH_RULES
1175 	STAILQ_INIT(&cmds->ictl_list);
1176 #endif
1177 	STAILQ_INIT(&cmds->cmd_list);
1178 }
1179 
1180 /*
1181  * Initialize lists of commands in struct cmds_rule{}.
1182  */
1183 void
cmds_rule_init(struct cmds_rule * cmds_rule)1184 cmds_rule_init(struct cmds_rule *cmds_rule)
1185 {
1186 	cmds_init(&cmds_rule->cmds);
1187 #ifdef WITH_LIMITS
1188 	cmds_init(&cmds_rule->cmds_all_reached);
1189 	cmds_init(&cmds_rule->cmds_any_reached);
1190 	cmds_init(&cmds_rule->cmds_all_not_reached);
1191 	cmds_init(&cmds_rule->cmds_any_not_reached);
1192 #endif
1193 }
1194 
1195 void
cmds_rule_set_sync(struct cmds_rule * cmds_rule)1196 cmds_rule_set_sync(struct cmds_rule *cmds_rule)
1197 {
1198 #ifdef WITH_LIMITS
1199 	signed char sync_exec;
1200 #endif
1201 
1202 	if (cmds_rule->cmds.sync < 0)
1203 		cmds_rule->cmds.sync = 1;
1204 #ifdef WITH_LIMITS
1205 	sync_exec = cmds_rule->cmds.sync;
1206 	if (cmds_rule->cmds_all_reached.sync < 0)
1207 		cmds_rule->cmds_all_reached.sync = sync_exec;
1208 	if (cmds_rule->cmds_any_reached.sync < 0)
1209 		cmds_rule->cmds_any_reached.sync = sync_exec;
1210 	if (cmds_rule->cmds_all_not_reached.sync < 0)
1211 		cmds_rule->cmds_all_not_reached.sync = sync_exec;
1212 	if (cmds_rule->cmds_any_not_reached.sync < 0)
1213 		cmds_rule->cmds_any_not_reached.sync = sync_exec;
1214 #endif
1215 }
1216 
1217 /*
1218  * Release memory used by struct cmds_rule{}.
1219  */
1220 void
cmds_rule_free(struct cmds_rule * cmds_rule)1221 cmds_rule_free(struct cmds_rule *cmds_rule)
1222 {
1223 	cmds_free(&cmds_rule->cmds);
1224 #ifdef WITH_LIMITS
1225 	cmds_free(&cmds_rule->cmds_any_reached);
1226 	cmds_free(&cmds_rule->cmds_all_reached);
1227 	cmds_free(&cmds_rule->cmds_any_not_reached);
1228 	cmds_free(&cmds_rule->cmds_all_not_reached);
1229 #endif
1230 }
1231 
1232 #ifdef WITH_LIMITS
1233 /*
1234  * Initialize lists of commands in struct cmds_limit{}.
1235  */
1236 void
cmds_limit_init(struct cmds_limit * cmds_limit)1237 cmds_limit_init(struct cmds_limit *cmds_limit)
1238 {
1239 	cmds_init(&cmds_limit->cmds);
1240 	cmds_init(&cmds_limit->cmds_reached);
1241 	cmds_init(&cmds_limit->cmds_not_reached);
1242 }
1243 
1244 void
cmds_limit_set_sync(struct cmds_limit * cmds_limit)1245 cmds_limit_set_sync(struct cmds_limit *cmds_limit)
1246 {
1247 	signed char sync_exec;
1248 
1249 	if (cmds_limit->cmds.sync < 0)
1250 		cmds_limit->cmds.sync = 1;
1251 	sync_exec = cmds_limit->cmds.sync;
1252 	if (cmds_limit->cmds_reached.sync < 0)
1253 		cmds_limit->cmds_reached.sync = sync_exec;
1254 	if (cmds_limit->cmds_not_reached.sync < 0)
1255 		cmds_limit->cmds_not_reached.sync = sync_exec;
1256 }
1257 
1258 /*
1259  * Release memory used by struct cmds_limit{}.
1260  */
1261 void
cmds_limit_free(struct cmds_limit * cmds_limit)1262 cmds_limit_free(struct cmds_limit *cmds_limit)
1263 {
1264 	cmds_free(&cmds_limit->cmds);
1265 	cmds_free(&cmds_limit->cmds_reached);
1266 	cmds_free(&cmds_limit->cmds_not_reached);
1267 }
1268 #endif /* WITH_LIMITS */
1269 
1270 static void
cmd_substitute(char * dst,const char * src,const char * rule_name)1271 cmd_substitute(char *dst, const char *src, const char *rule_name)
1272 {
1273 	const char *ptr;
1274 
1275 	/*
1276 	 * Command string substitution algorithm:
1277 	 *   %% -> %
1278 	 *   %rule% -> rule_name
1279 	 */
1280 	for (; *src != '\0'; ++src)
1281 		if (*src == '%') {
1282 			if (*++src == '%') {
1283 				/* %% */
1284 				*dst++ = '%';
1285 			} else {
1286 				/* %rule% */
1287 				for (ptr = rule_name; *ptr != '\0';)
1288 					*dst++ = *ptr++;
1289 				src += sizeof("rule") - 1;
1290 			}
1291 		} else
1292 			*dst++ = *src;
1293 	*dst = '\0';
1294 }
1295 
1296 /*
1297  * Copy command list substituting "%..%" substrings.
1298  */
1299 int
cmds_copy(const struct rule * rule,struct cmds * cmds_dst,const struct cmds * cmds_src)1300 cmds_copy(const struct rule *rule, struct cmds *cmds_dst,
1301     const struct cmds *cmds_src)
1302 {
1303 	const struct cmd_list *cmd_list_src;
1304 	const struct cmd *cmd_src;
1305 	const char *rule_name;
1306 	struct cmd *cmd_dst;
1307 	struct cmd_list *cmd_list_dst;
1308 
1309 	if (!cmds_src->has_cmd)
1310 		return (0);
1311 
1312 	rule_name = rule->name;
1313 
1314 	cmds_dst->sect_set = cmds_dst->has_cmd = 1;
1315 	cmds_dst->sync = cmds_src->sync;
1316 
1317 	cmd_list_dst = &cmds_dst->cmd_list;
1318 	cmd_list_src = &cmds_src->cmd_list;
1319 
1320 	/* Copy struct cmd{} from cmd_list_src to cmd_list_dst one by one. */
1321 	STAILQ_FOREACH(cmd_src, cmd_list_src, link) {
1322 		cmd_dst = mzone_alloc(cmd_mzone);
1323 		if (cmd_dst == NULL) {
1324 			xlogmsgx(IPA_LOG_ERR, "cmds_copy: mzone_alloc failed");
1325 			return (-1);
1326 		}
1327 		STAILQ_INSERT_TAIL(cmd_list_dst, cmd_dst, link);
1328 		if (cmd_src->subst_per_cent == 0 && cmd_src->subst_rule == 0) {
1329 			cmd_dst->str = cmd_src->str;
1330 			cmd_dst->free_mask = 0;
1331 		} else {
1332 			cmd_dst->str = mem_malloc(cmd_src->str_size -
1333 			    cmd_src->subst_per_cent +
1334 			    (strlen(rule_name) - 6) * cmd_src->subst_rule,
1335 			    m_cmd);
1336 			if (cmd_dst->str == NULL) {
1337 				xlogmsgx(IPA_LOG_ERR, "cmds_copy: "
1338 				    "mem_malloc failed");
1339 				return (-1);
1340 			}
1341 			cmd_substitute(cmd_dst->str, cmd_src->str, rule_name);
1342 			cmd_dst->free_mask = CMD_FREE_STR;
1343 		}
1344 		/* Do not set CMD_FREE_USER, since we share user pointer. */
1345 		cmd_dst->user = cmd_src->user;
1346 	}
1347 	return (0);
1348 }
1349 
1350 /*
1351  * Copy rule's command list substituting "%...%" substrings.
1352  */
1353 int
cmds_rule_copy(const struct rule * rule,struct cmds_rule * cmds_rule_dst,const struct cmds_rule * cmds_rule_src)1354 cmds_rule_copy(const struct rule *rule, struct cmds_rule *cmds_rule_dst,
1355     const struct cmds_rule *cmds_rule_src)
1356 {
1357 	int rv;
1358 
1359 	rv = cmds_copy(rule, &cmds_rule_dst->cmds, &cmds_rule_src->cmds);
1360 #ifdef WITH_LIMITS
1361 	rv +=
1362 	    cmds_copy(rule, &cmds_rule_dst->cmds_all_reached,
1363 		&cmds_rule_src->cmds_all_reached) +
1364 	    cmds_copy(rule, &cmds_rule_dst->cmds_all_not_reached,
1365 		&cmds_rule_src->cmds_all_not_reached) +
1366 	    cmds_copy(rule, &cmds_rule_dst->cmds_any_reached,
1367 		&cmds_rule_src->cmds_any_reached) +
1368 	    cmds_copy(rule, &cmds_rule_dst->cmds_any_not_reached,
1369 		&cmds_rule_src->cmds_any_not_reached);
1370 #endif
1371 	if (rv < 0) {
1372 		xlogmsgx(IPA_LOG_ERR, "rule %s: cmds_rule_copy: "
1373 		    "cmds_copy failed", rule->name);
1374 		return (-1);
1375 	}
1376 	return (0);
1377 }
1378 
1379 #ifdef WITH_LIMITS
1380 /*
1381  * Copy limit's command list substituting "%...%" substrings.
1382  */
1383 int
cmds_limit_copy(const struct rule * rule,struct cmds_limit * cmds_limit_dst,const struct cmds_limit * cmds_limit_src)1384 cmds_limit_copy(const struct rule *rule, struct cmds_limit *cmds_limit_dst,
1385     const struct cmds_limit *cmds_limit_src)
1386 {
1387 	int rv;
1388 
1389 	rv =
1390 	    cmds_copy(rule, &cmds_limit_dst->cmds,
1391 		&cmds_limit_src->cmds) +
1392 	    cmds_copy(rule, &cmds_limit_dst->cmds_reached,
1393 		&cmds_limit_src->cmds_reached) +
1394 	    cmds_copy(rule, &cmds_limit_dst->cmds_not_reached,
1395 		&cmds_limit_src->cmds_not_reached);
1396 	if (rv < 0) {
1397 		xlogmsgx(IPA_LOG_ERR, "rule %s: cmds_limit_copy: "
1398 		    "cmds_copy failed", rule->name);
1399 		return (-1);
1400 	}
1401 	return (0);
1402 }
1403 #endif /* WITH_LIMITS */
1404