/*- * Copyright (c) 2000-2003 Andrey Simonenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #ifndef lint static const char rcsid[] ATTR_UNUSED = "@(#)$Id: ipa_cmd.c,v 1.2.2.6 2007/05/11 16:29:59 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H # include #endif #ifndef _PATH_BSHELL # ifdef __GNUC__ # warning "defining _PATH_BSHELL to \"/bin/sh\"" # endif # define _PATH_BSHELL "/bin/sh" #endif #include "ipa_mod.h" #include "dlapi.h" #include "confcommon.h" #include "memfunc.h" #include "parser.h" #include "ipa_ac.h" #include "ipa_db.h" #include "ipa_cmd.h" #include "ipa_ctl.h" #include "ipa_time.h" #include "ipa_conf.h" #include "ipa_log.h" #include "ipa_main.h" #include "ipa_rules.h" #include "ipa_autorules.h" #ifndef WCOREDUMP # define WCOREDUMP(x) (0) #endif #if defined(HAVE_CLOSEFROM) && (STDIN_FILENO > 2 || STDOUT_FILENO > 2 || STDERR_FILENO > 2) # undef HAVE_CLOSEFROM #endif /* * exec_cmd() and exec_cmd_cons() return -1 only if they cannot fork() * a new process or if they get an error from waitpid(), if any error * occurred in a child forked from these functions, then such error * is invisible for exec_cmd_list*() functions. */ /* * If exec_cmd_list() function return -1, then we should * terminate ipa(8). */ /* * If return code from a child forked in exec_cmd_list_bg() or * in exec_cmd_list_fd() is non-zero then we should terminate ipa(8). */ ipa_mzone *cmd_mzone; /* Mzone for all struct cmd{}. */ ipa_mem_type *m_cmd; /* Memory for commands. */ char *shell_path = NULL; /* shell_path parameter. */ char *shell_path_default = _PATH_BSHELL; char *shell_arg1 = NULL; /* shell_arg1 parameter. */ char *shell_arg1_default = "-c"; int global_debug_exec; /* global { debug_exec } */ static int debug_exec; /* Local modified debug_exec parameter. */ struct cmd_list cmds_startup; /* Standalone startup {} */ struct cmd_list cmds_shutdown; /* Standalone shutdown {} */ #ifdef WITH_ANY_LIMITS struct wpid_hash wpid_hash[WPID_HASH_BUCKETS]; #endif const char *const sync_exec_msg[] = { "async", "sync" }; static void printf_error(const char *, ...) ATTR_FORMAT(printf, 1, 2); static void printf_errorx(const char *, ...) ATTR_FORMAT(printf, 1, 2); static void printf_warning(const char *, ...) ATTR_FORMAT(printf, 1, 2); /* * Set supplementary groups for process, gid is a GID from getpwnam(), * NGROUPS_MAX is defined in SUSv3. */ int setsuppgids(const char *user, gid_t gid) { int i, ngids = 1; gid_t gids[NGROUPS_MAX + 1]; char **gr_user; const struct group *grp; gids[0] = gid; errno = 0; while ( (grp = getgrent()) != NULL) { for (i = 0; i < ngids; ++i) if (grp->gr_gid == gids[i]) goto next; for (gr_user = grp->gr_mem; *gr_user != NULL; ++gr_user) { if (strcmp(user, *gr_user) == 0) { if (ngids < NGROUPS_MAX + 1) { gids[ngids++] = grp->gr_gid; break; } } } next: ; } if (errno != 0) { xlogmsgx(IPA_LOG_ERR, "setsuppgids: getgrent: %s", strerror(errno)); return -1; } if (setgroups(ngids, gids) < 0) { xlogmsgx(IPA_LOG_ERR, "setsuppgids: setgroups: %s", strerror(errno)); return -1; } return 0; } /* * Run command changing permissions if needed, this function is * called with disabled log system. Return -1 if some error occurs * and do not close log system in this case. */ static int exec_cmd(const struct cmd *cmd) { int status; pid_t childpid; if ( (childpid = fork()) == (pid_t)-1) { open_log(); logmsg(IPA_LOG_ERR, "exec_cmd: fork"); return -1; } if (childpid == 0) { /* Child. */ const struct passwd *pwd; if (cmd->user != NULL) { errno = 0; if ( (pwd = getpwnam(cmd->user)) == NULL) { int errno_save; errno_save = errno; open_log(); if (errno_save != 0) { errno = errno_save; logmsg(IPA_LOG_ERR, "exec_cmd: getpwnam(%s)", cmd->user); } else logmsgx(IPA_LOG_ERR, "exec_cmd: cannot find user \"%s\"", cmd->user); goto failed; } if (setgid(pwd->pw_gid) < 0) { open_log(); logmsg(IPA_LOG_ERR, "exec_cmd: setgid(%lu)", (u_long)pwd->pw_gid); goto failed; } if (setsuppgids(cmd->user, pwd->pw_gid) < 0) { open_log(); logmsgx(IPA_LOG_ERR, "exec_cmd: cannot set all groups for user %s", cmd->user); goto failed; } if (setuid(pwd->pw_uid) < 0) { open_log(); logmsg(IPA_LOG_ERR, "exec_cmd: setuid(%lu)", (u_long)pwd->pw_uid); goto failed; } /* Just, to be sure that descriptors are closed. */ endpwent(); endgrent(); } execl(shell_path, shell_path, shell_arg1, cmd->str, (char *)NULL); open_log(); logmsg(IPA_LOG_ERR, "exec_cmd: execl(\"%s\", \"%s\", \"%s\", %s) failed", shell_path, shell_path, shell_arg1, parser_buf_to_string(cmd->str)); failed: close_log(); _exit(127); } /* Parent. */ while (waitpid(childpid, &status, 0) == (pid_t)-1) { open_log(); if (errno != EINTR) { logmsg(IPA_LOG_ERR, "exec_cmd: waitpid(%ld)", (long)childpid); return -1; } logmsg(IPA_LOG_WARNING, "exec_cmd: waitpid(%ld)", (long)childpid); close_log(); } if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { open_log(); logmsgx(IPA_LOG_WARNING, "exec_cmd: exit status of child %ld (%s) is %d%s", (long)childpid, parser_buf_to_string(cmd->str), WEXITSTATUS(status), WEXITSTATUS(status) == 127 ? ", execution of the shell probably failed" : ""); close_log(); } } else { open_log(); if (WIFSIGNALED(status)) logmsgx(IPA_LOG_ERR, "exec_cmd: abnormal termination of child %ld (%s), signal %d%s", (long)childpid, parser_buf_to_string(cmd->str), WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : ""); else logmsgx(IPA_LOG_ERR, "exec_cmd: termination of child %ld (%s), unknown status", (long)childpid, parser_buf_to_string(cmd->str)); close_log(); } return 0; } #ifndef HAVE_CLOSEFROM static void close_my_fd(int nofile_max) { int fd; # if STDIN_FILENO < 3 && STDOUT_FILENO < 3 && STDERR_FILENO < 3 for (fd = 3; fd < nofile_max; ++fd) (void)close(fd); # else for (fd = 0; fd < nofile_max; ++fd) switch (fd) { case STDIN_FILENO: case STDOUT_FILENO: case STDERR_FILENO: break; default: (void)close(fd); } # endif } #endif /* !HAVE_CLOSEFROM */ /* * Run commands list in background and return PID of a child * (internal version). */ static pid_t exec_cmd_list_internal(const struct cmd_list *cmd_list, const char *format, va_list ap) { pid_t childpid; if ( (childpid = fork()) == (pid_t)-1) { logmsg(IPA_LOG_ERR, "exec_cmd_list_internal: fork"); return (pid_t)-1; } if (childpid == 0) { /* Child. */ const struct cmd *cmd; #ifndef HAVE_CLOSEFROM long val; #endif /* Close all descriptors, except stdin, stdout and stderr. */ #ifdef HAVE_CLOSEFROM close_log(); (void)closefrom(3); #else /* * Since a module can call setrlimit(), it is necessary * to call sysconf(_SC_OPEN_MAX) again. */ errno = 0; val = sysconf(_SC_OPEN_MAX); if (val < 0) { if (errno != 0) logmsg(IPA_LOG_ERR, "fd_init: sysconf(_SC_OPEN_MAX)"); else logmsgx(IPA_LOG_ERR, "fd_init: infinite limit for _SC_OPEN_MAX is not supported"); goto failed; } # if SIZEOF_LONG > SIZEOF_INT if (val > INT_MAX) { logmsgx(IPA_LOG_ERR, "fd_init: too big value %ld for int type from sysconf(_SC_OPEN_MAX)", val); goto failed; } # endif close_log(); close_my_fd((int)val); #endif /* HAVE_CLOSEFROM */ STAILQ_FOREACH(cmd, &cmd_list->list, link) { if (debug_exec > 1) { open_log(); if (cmd->user == NULL) logmsgx(IPA_LOG_INFO, "run command: exec %s", parser_buf_to_string(cmd->str)); else logmsgx(IPA_LOG_INFO, "run command: exec %s %s", cmd->user, parser_buf_to_string(cmd->str)); close_log(); } if (exec_cmd(cmd) < 0) { char *buf; /* Log was opened in exec_cmd(). */ mem_vasprintf(m_tmp, &buf, format, ap); logmsgx(IPA_LOG_ERR, "exec_cmd_list_internal: execution of command %s from section %s failed", parser_buf_to_string(cmd->str), buf != NULL ? buf : "(mem_vasprintf failed)"); goto failed; } } _exit(0); failed: close_log(); _exit(1); } /* Parent. */ return childpid; } /* * Run commands list in background and wait. */ static int exec_cmd_list_fg(const struct cmd_list *cmd_list, const char *format, va_list ap) { int status; pid_t childpid; childpid = exec_cmd_list_internal(cmd_list, format, ap); if (childpid == (pid_t)-1) { logmsgx(IPA_LOG_ERR, "exec_cmd_list_fg: exec_cmd_list_internal failed"); return -1; } while (waitpid(childpid, &status, 0) == (pid_t)-1) { if (errno != EINTR) { logmsg(IPA_LOG_ERR, "exec_cmd_list_fg: waitpid(%ld)", (long)childpid); return -1; } logmsgx(IPA_LOG_WARNING, "exec_cmd_list_fg: waitpid(%ld)", (long)childpid); } log_stdall(); if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { logmsgx(IPA_LOG_WARNING, "exec_cmd_list_fg: exit status of child %ld, which run commands, is %d", (long)childpid, WEXITSTATUS(status)); return -1; } } else { if (WIFSIGNALED(status)) logmsgx(IPA_LOG_ERR, "exec_cmd_list_fg: abnormal termination of child %ld, which run commands, signal %d%s", (long)childpid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : ""); else logmsgx(IPA_LOG_ERR, "exec_cmd_list_fg: termination of child %ld, which run commands, unknown status", (long)childpid); return -1; } return 0; } /* * Run commands list in background and return PID of a child. */ static pid_t exec_cmd_list_bg(const struct cmd_list *cmd_list, const char *format, va_list ap) { pid_t childpid; childpid = exec_cmd_list_internal(cmd_list, format, ap); if (childpid == (pid_t)-1) logmsgx(IPA_LOG_ERR, "exec_cmd_list_bg: exec_cmd_list_internal failed"); return childpid; } #ifdef WITH_ANY_LIMITS #define WPID_ARG_ATTR void init_wpid_hash(void) { struct wpid_hash *h; for (h = wpid_hash; h < wpid_hash + WPID_HASH_BUCKETS; ++h) LIST_INIT(h); } int wpid_hash_is_empty(void) { const struct wpid_hash *h; for (h = wpid_hash; h < wpid_hash + WPID_HASH_BUCKETS; ++h) if (!LIST_EMPTY(h)) return 0; return 1; } /* * Remove wpid structure from the hash, wpid is there * if its pid field is not equal to zero. */ void rem_wpid_from_hash(struct wpid *wpid) { if (wpid->pid != 0) { wpid->pid = 0; LIST_REMOVE(wpid, hlink); } } # define add_wpid_to_hash(w) \ LIST_INSERT_HEAD(&wpid_hash[get_wpid_bucket((w)->pid)], wpid, hlink) #else # define WPID_ARG_ATTR ATTR_UNUSED #endif /* WITH_ANY_LIMITS */ /* * Run commands from cmd_list, rule and format with * rest arguments are used for debugging. */ int exec_cmd_list(const struct rule *rule, struct wpid *wpid WPID_ARG_ATTR, const struct cmd_list *cmd_list, const char *format, ...) { int error = 0; pid_t pid; va_list ap; debug_exec = rule->debug_exec; va_start(ap, format); if (cmd_list->sync_exec) { if (exec_cmd_list_fg(cmd_list, format, ap) < 0) error = -1; #ifdef WITH_ANY_LIMITS if (wpid != NULL) rem_wpid_from_hash(wpid); #endif } else { if ( (pid = exec_cmd_list_bg(cmd_list, format, ap)) == (pid_t)-1) { error = -1; #ifdef WITH_ANY_LIMITS if (wpid != NULL) rem_wpid_from_hash(wpid); #endif } else { #ifdef WITH_ANY_LIMITS if (wpid != NULL) { if (wpid->pid == 0) { wpid->pid = pid; add_wpid_to_hash(wpid); } else wpid->pid = pid; } #endif } } va_end(ap); return error; } /* * Output a message to stderr and an error message if errno != 0. */ static void printf_error(const char *format, ...) { int errno_save = errno; va_list ap; va_start(ap, format); vlogmsgx_stderr(IPA_LOG_ERR, format, ap); va_end(ap); if (errno_save != 0) fprintf(stderr, "Error: %s.\n", strerror(errno_save)); } /* * Output a message to stderr. */ static void printf_errorx(const char *format, ...) { va_list ap; va_start(ap, format); vlogmsgx_stderr(IPA_LOG_ERR, format, ap); va_end(ap); } /* * Output a warning message to stderr. */ static void printf_warning(const char *format, ...) { va_list ap; va_start(ap, format); vlogmsgx_stderr(IPA_LOG_WARNING, format, ap); va_end(ap); } /* * Run a command in foreground. Return -1 if some error occurs. */ static int exec_cmd_cons(const struct cmd *cmd) { int status; pid_t childpid; if ( (childpid = fork()) == (pid_t)-1) { printf_error("exec_cmd_cons: fork"); return -1; } if (childpid == 0) { /* Child. */ const struct passwd *pwd; if (cmd->user != NULL) { errno = 0; if ( (pwd = getpwnam(cmd->user)) == NULL) { if (errno != 0) printf_error("exec_cmd_cons: getpwnam(%s)", cmd->user); else printf_error("exec_cmd_cons: cannot find user \"%s\"", cmd->user); goto failed; } if (setgid(pwd->pw_gid) < 0) { printf_error("exec_cmd_cons: setgid(%lu)", (u_long)pwd->pw_gid); goto failed; } if (setsuppgids(cmd->user, pwd->pw_gid) < 0) { errno = 0; printf_error("exec_cmd_cons: cannot set all groups for user %s", cmd->user); goto failed; } if (setuid(pwd->pw_uid) < 0) { printf_error("exec_cmd_cons: setuid(%lu)", (u_long)pwd->pw_uid); goto failed; } /* Just, to be sure that descriptors are closed. */ endpwent(); endgrent(); } execl(shell_path, shell_path, shell_arg1, cmd->str, (char *)NULL); printf_error("execl(\"%s\", \"%s\", \"%s\", %s)", shell_path, shell_path, shell_arg1, parser_buf_to_string(cmd->str)); failed: printf_errorx("child could not run this command"); _exit(127); } /* Parent. */ while (waitpid(childpid, &status, 0) == (pid_t)-1) if (errno != EINTR) { printf_error("exec_cmd_cons: waitpid"); return -1; } if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) printf_warning("exec_cmd_cons: exit status of child is %d%s", WEXITSTATUS(status), WEXITSTATUS(status) == 127 ? ", execution of the shell probably failed" : ""); } else if (WIFSIGNALED(status)) printf_warning("exec_cmd_cons: abnormal termination of child, signal %d%s", WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : ""); else printf_warning("exec_cmd_cons: termination of child, unknown status"); return 0; } /* * Run commands from cmd_list in foreground, ignoring if * some command can't be run, user does not exist, etc. */ int exec_cmd_list_cons(const struct cmd_list *cmd_list, const char *message, ...) { const struct cmd *cmd; va_list ap; #ifndef HAVE_CLOSEFROM long val; #endif printf("Running commands from "); va_start(ap, message); vprintf(message, ap); va_end(ap); printf(" section:\n"); if (STAILQ_EMPTY(&cmd_list->list)) { printf("- nothing to run\n"); return 0; } /* Close all descriptors, except stdin, stdout and stderr. */ #ifdef HAVE_CLOSEFROM (void)closefrom(3); #else errno = 0; val = sysconf(_SC_OPEN_MAX); if (val < 0) { if (errno != 0) printf_error("exec_cmd_list_cons: sysconf(_SC_OPEN_MAX)"); else printf_errorx("exec_cmd_list_cons: infinite limit for _SC_OPEN_MAX is not supported"); return -1; } # if SIZEOF_LONG > SIZEOF_INT if (val > INT_MAX) { printf_errorx("exec_cmd_list_cons: too big value %ld for int type from sysconf(_SC_OPEN_MAX)", val); return -1; } # endif close_my_fd((int)val); #endif /* HAVE_CLOSEFROM */ STAILQ_FOREACH(cmd, &cmd_list->list, link) { printf("* exec "); if (cmd->user != NULL) printf("%s ", cmd->user); print_string(cmd->str); printf("\n"); fflush(stdout); if (exec_cmd_cons(cmd) < 0) { printf_errorx("stop running commands"); return -1; } } return 0; } /* * Run commands from startup (x == 0) or from shutdown (x == 1) * sections. */ int run_cmds(int x) { struct rule dummy_rule; dummy_rule.debug_exec = global_debug_exec; if (x == RC_STARTUP) { logmsgx(IPA_LOG_INFO, "running startup commands..."); if (!STAILQ_EMPTY(&cmds_startup.list)) { if (global_debug_exec) logmsgx(IPA_LOG_INFO, "global startup commands: run commands (%s)", sync_exec_msg[cmds_startup.sync_exec]); if (exec_cmd_list(&dummy_rule, (struct wpid *)NULL, &cmds_startup, "startup {}") == (pid_t)-1) goto failed; free_cmd_list(&cmds_startup); } } else { logmsgx(IPA_LOG_INFO, "running shutdown commands..."); if (!STAILQ_EMPTY(&cmds_shutdown.list)) { if (global_debug_exec) logmsgx(IPA_LOG_INFO, "global shutdown commands: run commands (%s)", sync_exec_msg[cmds_shutdown.sync_exec]); if (exec_cmd_list(&dummy_rule, (struct wpid *)NULL, &cmds_shutdown, "shutdown {}") == (pid_t)-1) goto failed; } } if (run_rules_cmds(x) < 0) goto failed; return 0; failed: logmsgx(IPA_LOG_ERR, "stop running commands"); return -1; } /* * Release memory used by commands list. */ void free_cmd_list(struct cmd_list *cmd_list) { u_int free_mask; struct cmd *cmd, *cmd_next; for (cmd = STAILQ_FIRST(&cmd_list->list); cmd != NULL; cmd = cmd_next) { cmd_next = STAILQ_NEXT(cmd, link); free_mask = cmd->free_mask; if (free_mask & CMD_FREE_STR) mem_free(cmd->str, m_cmd); if (free_mask & CMD_FREE_USER) mem_free(cmd->user, m_cmd); mzone_free(cmd_mzone, cmd); } STAILQ_INIT(&cmd_list->list); } /* * Set sync_exec to -1, this means that it is undefined and * init head of actual list of commands. */ void init_cmd_list(struct cmd_list *cmd_list) { cmd_list->sync_exec = -1; STAILQ_INIT(&cmd_list->list); } /* * Init lists of commands in struct cmds_rule{}. */ void init_cmds_rule(struct cmds_rule *ptr) { ptr->set = 0; init_cmd_list(&ptr->cmdl); #ifdef WITH_LIMITS init_cmd_list(&ptr->cmdl_if_all_reached); init_cmd_list(&ptr->cmdl_if_any_reached); init_cmd_list(&ptr->cmdl_if_all_not_reached); init_cmd_list(&ptr->cmdl_if_any_not_reached); #endif } void set_sync_exec_cmds_rule(struct cmds_rule *ptr) { #ifdef WITH_LIMITS int sync_exec; #endif if (ptr->cmdl.sync_exec < 0) ptr->cmdl.sync_exec = 1; #ifdef WITH_LIMITS sync_exec = ptr->cmdl.sync_exec; if (ptr->cmdl_if_all_reached.sync_exec < 0) ptr->cmdl_if_all_reached.sync_exec = sync_exec; if (ptr->cmdl_if_any_reached.sync_exec < 0) ptr->cmdl_if_any_reached.sync_exec = sync_exec; if (ptr->cmdl_if_all_not_reached.sync_exec < 0) ptr->cmdl_if_all_not_reached.sync_exec = sync_exec; if (ptr->cmdl_if_any_not_reached.sync_exec < 0) ptr->cmdl_if_any_not_reached.sync_exec = sync_exec; #endif } /* * Release memory used by struct cmds_rule{}. */ void free_cmds_rule(struct cmds_rule *ptr) { free_cmd_list(&ptr->cmdl); #ifdef WITH_LIMITS free_cmd_list(&ptr->cmdl_if_any_reached); free_cmd_list(&ptr->cmdl_if_all_reached); free_cmd_list(&ptr->cmdl_if_any_not_reached); free_cmd_list(&ptr->cmdl_if_all_not_reached); #endif } #ifdef WITH_LIMITS /* * Init lists of commands in struct cmds_limit{}. */ void init_cmds_limit(struct cmds_limit *ptr) { ptr->set = 0; init_cmd_list(&ptr->cmdl); init_cmd_list(&ptr->cmdl_if_reached); init_cmd_list(&ptr->cmdl_if_not_reached); } void set_sync_exec_cmds_limit(struct cmds_limit *ptr) { int sync_exec; if (ptr->cmdl.sync_exec < 0) ptr->cmdl.sync_exec = 1; sync_exec = ptr->cmdl.sync_exec; if (ptr->cmdl_if_reached.sync_exec < 0) ptr->cmdl_if_reached.sync_exec = sync_exec; if (ptr->cmdl_if_not_reached.sync_exec < 0) ptr->cmdl_if_not_reached.sync_exec = sync_exec; } /* * Release memory used by struct cmds_limit{}. */ void free_cmds_limit(struct cmds_limit *ptr) { free_cmd_list(&ptr->cmdl); free_cmd_list(&ptr->cmdl_if_reached); free_cmd_list(&ptr->cmdl_if_not_reached); } #endif /* WITH_LIMITS */ /* * Copy command list substituting "%..%" substrings. */ int copy_cmd_list(const struct rule *rule, struct cmd_list *list_dst, const struct cmd_list *list_src) { char *dst; const char *src, *ptr; const char *rule_name; struct cmd *cmd_dst; const struct cmd *cmd_src; rule_name = rule->rule_name; list_dst->sync_exec = list_src->sync_exec; /* Copy struct cmd{} from list_src to list_dst one by one. */ STAILQ_FOREACH(cmd_src, &list_src->list, link) { if ( (cmd_dst = mzone_alloc(cmd_mzone)) == NULL) { xlogmsgx(IPA_LOG_ERR, "copy_cmd_list: mzone_alloc failed"); return -1; } STAILQ_INSERT_TAIL(&list_dst->list, cmd_dst, link); if (cmd_src->subst_per_cent == 0 && cmd_src->subst_rule == 0) { cmd_dst->str = cmd_src->str; cmd_dst->free_mask = 0; } else { if ( (cmd_dst->str = mem_malloc(cmd_src->str_size - 1 * cmd_src->subst_per_cent + (strlen(rule_name) - 6) * cmd_src->subst_rule, m_cmd)) == NULL) { xlogmsgx(IPA_LOG_ERR, "copy_cmd_list: mem_malloc failed"); return -1; } /* * Command string substitution algorithm: * %% -> % (that's why "- 1 * cmd_src->subst_per_cent" above) * %rule% -> rule_name ("6" above, because strlen("%rule%") == 6) */ for (src = cmd_src->str, dst = cmd_dst->str; *src != '\0'; ++src) if (*src == '%') { if (*++src == '%') { /* %% */ *dst++ = '%'; } else { /* %rule% */ for (ptr = rule_name; *ptr != '\0';) *dst++ = *ptr++; src += 4; /* 4 == strlen("rule") */ } } else *dst++ = *src; *dst = '\0'; cmd_dst->free_mask = CMD_FREE_STR; } /* Don't set CMD_FREE_USER, since we share user pointer. */ cmd_dst->user = cmd_src->user; } return 0; } /* * Copy rule's command list substituting "%...%" substrings. */ int copy_cmds_rule(const struct rule *rule, struct cmds_rule *list_dst, const struct cmds_rule *list_src) { if (copy_cmd_list(rule, &list_dst->cmdl, &list_src->cmdl) < 0 #ifdef WITH_LIMITS || copy_cmd_list(rule, &list_dst->cmdl_if_all_reached, &list_src->cmdl_if_all_reached) < 0 || copy_cmd_list(rule, &list_dst->cmdl_if_all_not_reached, &list_src->cmdl_if_all_not_reached) < 0 || copy_cmd_list(rule, &list_dst->cmdl_if_any_reached, &list_src->cmdl_if_any_reached) < 0 || copy_cmd_list(rule, &list_dst->cmdl_if_any_not_reached, &list_src->cmdl_if_any_not_reached) < 0 #endif ) { xlogmsgx(IPA_LOG_ERR, "rule %s: copy_cmds_rule: copy_cmd_list failed", rule->rule_name); return -1; } return 0; } #ifdef WITH_LIMITS /* * Copy limit's command list substituting "%...%" substrings. */ int copy_cmds_limit(const struct rule *rule, struct cmds_limit *list_dst, const struct cmds_limit *list_src) { if (copy_cmd_list(rule, &list_dst->cmdl, &list_src->cmdl) < 0 || copy_cmd_list(rule, &list_dst->cmdl_if_reached, &list_src->cmdl_if_reached) < 0 || copy_cmd_list(rule, &list_dst->cmdl_if_not_reached, &list_src->cmdl_if_not_reached) < 0) { xlogmsgx(IPA_LOG_ERR, "rule %s: copy_cmds_limit: copy_cmd_list failed", rule->rule_name); return -1; } return 0; } #endif /* WITH_LIMITS */