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