/*-
* 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 <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <unistd.h>
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
#include <sys/wait.h>
#include <sys/time.h>
#ifdef HAVE_PATHS_H
# include <paths.h>
#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 */
syntax highlighted by Code2HTML, v. 0.9.1