"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "cf-runagent/cf-runagent.c" between
cfengine-3.15.3.tar.gz and cfengine-3.15.4.tar.gz

About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.

cf-runagent.c  (cfengine-3.15.3):cf-runagent.c  (cfengine-3.15.4)
skipping to change at line 53 skipping to change at line 53
#include <audit.h> #include <audit.h>
#include <man.h> #include <man.h>
#include <connection_info.h> #include <connection_info.h>
#include <addr_lib.h> #include <addr_lib.h>
#include <loading.h> #include <loading.h>
#include <expand.h> /* ProtocolVersionParse */ #include <expand.h> /* ProtocolVersionParse */
#include <hash.h> #include <hash.h>
#include <string_lib.h> #include <string_lib.h>
#include <cleanup.h> #include <cleanup.h>
#define CF_RA_EXIT_CODE_OTHER_ERR 101
typedef enum typedef enum
{ {
RUNAGENT_CONTROL_HOSTS, RUNAGENT_CONTROL_HOSTS,
RUNAGENT_CONTROL_PORT_NUMBER, RUNAGENT_CONTROL_PORT_NUMBER,
RUNAGENT_CONTROL_FORCE_IPV4, RUNAGENT_CONTROL_FORCE_IPV4,
RUNAGENT_CONTROL_TRUSTKEY, RUNAGENT_CONTROL_TRUSTKEY,
RUNAGENT_CONTROL_ENCRYPT, RUNAGENT_CONTROL_ENCRYPT,
RUNAGENT_CONTROL_BACKGROUND, RUNAGENT_CONTROL_BACKGROUND,
RUNAGENT_CONTROL_MAX_CHILD, RUNAGENT_CONTROL_MAX_CHILD,
RUNAGENT_CONTROL_OUTPUT_TO_FILE, RUNAGENT_CONTROL_OUTPUT_TO_FILE,
RUNAGENT_CONTROL_OUTPUT_DIRECTORY, RUNAGENT_CONTROL_OUTPUT_DIRECTORY,
RUNAGENT_CONTROL_TIMEOUT, RUNAGENT_CONTROL_TIMEOUT,
RUNAGENT_CONTROL_NONE RUNAGENT_CONTROL_NONE
} RunagentControl; } RunagentControl;
static void ThisAgentInit(void); static void ThisAgentInit(void);
static GenericAgentConfig *CheckOpts(int argc, char **argv); static GenericAgentConfig *CheckOpts(int argc, char **argv);
static void KeepControlPromises(EvalContext *ctx, const Policy *policy); static void KeepControlPromises(EvalContext *ctx, const Policy *policy);
static bool HailServer(const EvalContext *ctx, const GenericAgentConfig *config, char *host); static int HailServer(const EvalContext *ctx, const GenericAgentConfig *config, char *host);
static void SendClassData(AgentConnection *conn); static void SendClassData(AgentConnection *conn);
static void HailExec(AgentConnection *conn, char *peer); static int HailExec(AgentConnection *conn, char *peer);
static FILE *NewStream(char *name); static FILE *NewStream(char *name);
/*******************************************************************/ /*******************************************************************/
/* Command line options */ /* Command line options */
/*******************************************************************/ /*******************************************************************/
static const char *const CF_RUNAGENT_SHORT_DESCRIPTION = static const char *const CF_RUNAGENT_SHORT_DESCRIPTION =
"activate cf-agent on a remote host"; "activate cf-agent on a remote host";
static const char *const CF_RUNAGENT_MANPAGE_LONG_DESCRIPTION = static const char *const CF_RUNAGENT_MANPAGE_LONG_DESCRIPTION =
skipping to change at line 161 skipping to change at line 163
int MAXCHILD = 50; /* GLOBAL_P GLOBAL_A */ int MAXCHILD = 50; /* GLOBAL_P GLOBAL_A */
const Rlist *HOSTLIST = NULL; /* GLOBAL_P GLOBAL_A */ const Rlist *HOSTLIST = NULL; /* GLOBAL_P GLOBAL_A */
char SENDCLASSES[CF_MAXVARSIZE] = ""; /* GLOBAL_A */ char SENDCLASSES[CF_MAXVARSIZE] = ""; /* GLOBAL_A */
char DEFINECLASSES[CF_MAXVARSIZE] = ""; /* GLOBAL_A */ char DEFINECLASSES[CF_MAXVARSIZE] = ""; /* GLOBAL_A */
char REMOTEBUNDLES[CF_MAXVARSIZE] = ""; char REMOTEBUNDLES[CF_MAXVARSIZE] = "";
/*****************************************************************************/ /*****************************************************************************/
/**
* @param is_exit_code whether #remote_exit_status is a exit code directly
* (#true) or a status from wait() (#false)
*/
static inline void UpdateExitCode(int *exit_code, int remote_exit_status, bool o
ne_host, bool is_exit_code)
{
assert(exit_code != NULL);
if (one_host)
{
if (is_exit_code)
{
*exit_code = remote_exit_status;
return;
}
if (WIFEXITED(remote_exit_status))
{
*exit_code = WEXITSTATUS(remote_exit_status);
return;
}
*exit_code = CF_RA_EXIT_CODE_OTHER_ERR;
return;
}
/* Other error should always take priority, otherwise, count failed remote
* agent runs. */
if ((*exit_code < CF_RA_EXIT_CODE_OTHER_ERR) &&
(!WIFEXITED(remote_exit_status) || (WEXITSTATUS(remote_exit_status) != E
XIT_SUCCESS)))
{
*exit_code = MIN(*exit_code + 1, 100);
}
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
#if !defined(__MINGW32__) #if !defined(__MINGW32__)
int count = 0; int count = 0;
int status; int status;
int pid; int pid;
#endif #endif
GenericAgentConfig *config = CheckOpts(argc, argv); GenericAgentConfig *config = CheckOpts(argc, argv);
EvalContext *ctx = EvalContextNew(); EvalContext *ctx = EvalContextNew();
GenericAgentConfigApply(ctx, config); GenericAgentConfigApply(ctx, config);
GenericAgentDiscoverContext(ctx, config); const char *program_invocation_name = argv[0];
const char *last_dir_sep = strrchr(program_invocation_name, FILE_SEPARATOR);
const char *program_name = (last_dir_sep != NULL ? last_dir_sep + 1 : progra
m_invocation_name);
GenericAgentDiscoverContext(ctx, config, program_name);
Policy *policy = LoadPolicy(ctx, config); Policy *policy = LoadPolicy(ctx, config);
GenericAgentPostLoadInit(ctx); GenericAgentPostLoadInit(ctx);
ThisAgentInit(); ThisAgentInit();
KeepControlPromises(ctx, policy); // Set RUNATTR using copy KeepControlPromises(ctx, policy); // Set RUNATTR using copy
/* Exit codes:
* - exit code from the remote agent run if only 1 host specified
* - number of failed remote agent runs up to 100 otherwise
* - >100 in case of other errors */
int exit_code = 0;
if (BACKGROUND && INTERACTIVE) if (BACKGROUND && INTERACTIVE)
{ {
Log(LOG_LEVEL_ERR, "You cannot specify background mode and interactive m ode together"); Log(LOG_LEVEL_ERR, "You cannot specify background mode and interactive m ode together");
DoCleanupAndExit(EXIT_FAILURE); DoCleanupAndExit(CF_RA_EXIT_CODE_OTHER_ERR);
} }
/* HvB */ /* HvB */
const bool one_host = (HOSTLIST != NULL) && (HOSTLIST->next == NULL);
if (HOSTLIST) if (HOSTLIST)
{ {
const Rlist *rp = HOSTLIST; const Rlist *rp = HOSTLIST;
while (rp != NULL) while (rp != NULL)
{ {
#ifdef __MINGW32__ #ifdef __MINGW32__
if (BACKGROUND) if (BACKGROUND)
{ {
Log(LOG_LEVEL_VERBOSE, Log(LOG_LEVEL_VERBOSE,
"Windows does not support starting processes in the backgrou nd - starting in foreground"); "Windows does not support starting processes in the backgrou nd - starting in foreground");
BACKGROUND = false; BACKGROUND = false;
} }
#else #else
if (BACKGROUND) /* parallel */ if (BACKGROUND) /* parallel */
{ {
if (count < MAXCHILD) if (count < MAXCHILD)
{ {
if (fork() == 0) /* child process */ if (fork() == 0) /* child process */
{ {
HailServer(ctx, config, RlistScalarValue(rp)); int remote_exit_code = HailServer(ctx, config, RlistScal
DoCleanupAndExit(EXIT_SUCCESS); arValue(rp));
DoCleanupAndExit(remote_exit_code > 0 ? remote_exit_cod
e : CF_RA_EXIT_CODE_OTHER_ERR);
} }
else /* parent process */ else /* parent process */
{ {
rp = rp->next; rp = rp->next;
count++; count++;
} }
} }
else else
{ {
pid = wait(&status); pid = wait(&status);
Log(LOG_LEVEL_DEBUG, "child = %d, child number = %d", pid, c ount); Log(LOG_LEVEL_DEBUG, "child = %d, child number = %d", pid, c ount);
count--; count--;
UpdateExitCode(&exit_code, status, one_host, false);
} }
} }
else /* serial */ else /* serial */
#endif /* __MINGW32__ */ #endif /* __MINGW32__ */
{ {
HailServer(ctx, config, RlistScalarValue(rp)); int remote_exit_code = HailServer(ctx, config, RlistScalarValue(
rp));
UpdateExitCode(&exit_code, remote_exit_code, one_host, true);
rp = rp->next; rp = rp->next;
} }
} /* end while */ } /* end while */
} /* end if HOSTLIST */ } /* end if HOSTLIST */
#ifndef __MINGW32__ #ifndef __MINGW32__
if (BACKGROUND) if (BACKGROUND)
{ {
Log(LOG_LEVEL_NOTICE, "Waiting for child processes to finish"); Log(LOG_LEVEL_NOTICE, "Waiting for child processes to finish");
while (count > 0) while (count > 0)
{ {
pid = wait(&status); pid = wait(&status);
Log(LOG_LEVEL_VERBOSE, "Child %d ended, number %d", pid, count); Log(LOG_LEVEL_VERBOSE, "Child %d ended, number %d", pid, count);
count--; count--;
UpdateExitCode(&exit_code, status, one_host, false);
} }
} }
#endif #endif
PolicyDestroy(policy); PolicyDestroy(policy);
GenericAgentFinalize(ctx, config); GenericAgentFinalize(ctx, config);
CallCleanupFunctions(); CallCleanupFunctions();
return 0; return exit_code;
} }
/*******************************************************************/ /*******************************************************************/
static GenericAgentConfig *CheckOpts(int argc, char **argv) static GenericAgentConfig *CheckOpts(int argc, char **argv)
{ {
extern char *optarg; extern char *optarg;
int c; int c;
GenericAgentConfig *config = GenericAgentConfigNewDefault(AGENT_TYPE_RUNAGEN T, GetTTYInteractive()); GenericAgentConfig *config = GenericAgentConfigNewDefault(AGENT_TYPE_RUNAGEN T, GetTTYInteractive());
skipping to change at line 450 skipping to change at line 500
/*******************************************************************/ /*******************************************************************/
static void ThisAgentInit(void) static void ThisAgentInit(void)
{ {
umask(077); umask(077);
} }
/********************************************************************/ /********************************************************************/
static bool HailServer(const EvalContext *ctx, const GenericAgentConfig *config, char *host) static int HailServer(const EvalContext *ctx, const GenericAgentConfig *config, char *host)
{ {
assert(host != NULL); assert(host != NULL);
AgentConnection *conn; AgentConnection *conn;
char hostkey[CF_HOSTKEY_STRING_SIZE], user[CF_SMALLBUF]; char hostkey[CF_HOSTKEY_STRING_SIZE], user[CF_SMALLBUF];
bool gotkey; bool gotkey;
char reply[8]; char reply[8];
bool trustkey = false; bool trustkey = false;
char *hostname, *port; char *hostname, *port;
ParseHostPort(host, &hostname, &port); ParseHostPort(host, &hostname, &port);
if (hostname == NULL) if (hostname == NULL)
{ {
Log(LOG_LEVEL_INFO, "No remote hosts were specified to connect to"); Log(LOG_LEVEL_INFO, "No remote hosts were specified to connect to");
return false; return -1;
} }
if (port == NULL) if (port == NULL)
{ {
port = "5308"; port = "5308";
} }
char ipaddr[CF_MAX_IP_LEN]; char ipaddr[CF_MAX_IP_LEN];
if (Hostname2IPString(ipaddr, hostname, sizeof(ipaddr)) == -1) if (Hostname2IPString(ipaddr, hostname, sizeof(ipaddr)) == -1)
{ {
Log(LOG_LEVEL_ERR, Log(LOG_LEVEL_ERR,
"HailServer: ERROR, could not resolve '%s'", hostname); "HailServer: ERROR, could not resolve '%s'", hostname);
return false; return -1;
} }
Address2Hostkey(hostkey, sizeof(hostkey), ipaddr); Address2Hostkey(hostkey, sizeof(hostkey), ipaddr);
GetCurrentUserName(user, sizeof(user)); GetCurrentUserName(user, sizeof(user));
if (INTERACTIVE) if (INTERACTIVE)
{ {
Log(LOG_LEVEL_VERBOSE, "Using interactive key trust..."); Log(LOG_LEVEL_VERBOSE, "Using interactive key trust...");
gotkey = HavePublicKey(user, ipaddr, hostkey) != NULL; gotkey = HavePublicKey(user, ipaddr, hostkey) != NULL;
skipping to change at line 558 skipping to change at line 608
.protocol_version = config->protocol_version, .protocol_version = config->protocol_version,
.trust_server = trustkey, .trust_server = trustkey,
.off_the_record = false .off_the_record = false
}; };
int err = 0; int err = 0;
conn = ServerConnection(hostname, port, CONNTIMEOUT, connflags, &err); conn = ServerConnection(hostname, port, CONNTIMEOUT, connflags, &err);
if (conn == NULL) if (conn == NULL)
{ {
Log(LOG_LEVEL_ERR, "Failed to connect to host: %s", hostname); Log(LOG_LEVEL_ERR, "Failed to connect to host: %s", hostname);
return false; return -1;
} }
/* Send EXEC command. */ /* Send EXEC command. */
HailExec(conn, hostname); return HailExec(conn, hostname);
return true;
} }
/********************************************************************/ /********************************************************************/
/* Level 2 */ /* Level 2 */
/********************************************************************/ /********************************************************************/
static void KeepControlPromises(EvalContext *ctx, const Policy *policy) static void KeepControlPromises(EvalContext *ctx, const Policy *policy)
{ {
Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_RUNAGENT); Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_RUNAGENT);
if (constraints) if (constraints)
skipping to change at line 706 skipping to change at line 754
if (SendTransaction(conn->conn_info, CFD_TERMINATOR, 0, CF_DONE) == -1) if (SendTransaction(conn->conn_info, CFD_TERMINATOR, 0, CF_DONE) == -1)
{ {
Log(LOG_LEVEL_ERR, "Transaction failed. (send: %s)", GetErrorStr()); Log(LOG_LEVEL_ERR, "Transaction failed. (send: %s)", GetErrorStr());
return; return;
} }
} }
/********************************************************************/ /********************************************************************/
static void HailExec(AgentConnection *conn, char *peer) static int HailExec(AgentConnection *conn, char *peer)
{ {
char sendbuf[CF_BUFSIZE - CF_INBAND_OFFSET] = "EXEC"; char sendbuf[CF_BUFSIZE - CF_INBAND_OFFSET] = "EXEC";
size_t sendbuf_len = strlen(sendbuf); size_t sendbuf_len = strlen(sendbuf);
if (!NULL_OR_EMPTY(DEFINECLASSES)) if (!NULL_OR_EMPTY(DEFINECLASSES))
{ {
StrCat(sendbuf, sizeof(sendbuf), &sendbuf_len, " -D", 0); StrCat(sendbuf, sizeof(sendbuf), &sendbuf_len, " -D", 0);
StrCat(sendbuf, sizeof(sendbuf), &sendbuf_len, DEFINECLASSES, 0); StrCat(sendbuf, sizeof(sendbuf), &sendbuf_len, DEFINECLASSES, 0);
} }
if (!NULL_OR_EMPTY(REMOTEBUNDLES)) if (!NULL_OR_EMPTY(REMOTEBUNDLES))
{ {
StrCat(sendbuf, sizeof(sendbuf), &sendbuf_len, " -b ", 0); StrCat(sendbuf, sizeof(sendbuf), &sendbuf_len, " -b ", 0);
StrCat(sendbuf, sizeof(sendbuf), &sendbuf_len, REMOTEBUNDLES, 0); StrCat(sendbuf, sizeof(sendbuf), &sendbuf_len, REMOTEBUNDLES, 0);
} }
if (sendbuf_len >= sizeof(sendbuf)) if (sendbuf_len >= sizeof(sendbuf))
{ {
Log(LOG_LEVEL_ERR, "Command longer than maximum transaction packet"); Log(LOG_LEVEL_ERR, "Command longer than maximum transaction packet");
DisconnectServer(conn); DisconnectServer(conn);
return; return -1;
} }
if (SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE) == -1) if (SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE) == -1)
{ {
Log(LOG_LEVEL_ERR, "Transmission rejected. (send: %s)", GetErrorStr()); Log(LOG_LEVEL_ERR, "Transmission rejected. (send: %s)", GetErrorStr());
DisconnectServer(conn); DisconnectServer(conn);
return; return -1;
} }
/* TODO we are sending class data right after EXEC, when the server might /* TODO we are sending class data right after EXEC, when the server might
* have already rejected us with BAD reply. So this class data with the * have already rejected us with BAD reply. So this class data with the
* CFD_TERMINATOR will be interpreted by the server as a new, bogus * CFD_TERMINATOR will be interpreted by the server as a new, bogus
* protocol command, and the server will complain. */ * protocol command, and the server will complain. */
SendClassData(conn); SendClassData(conn);
char recvbuffer[CF_BUFSIZE]; char recvbuffer[CF_BUFSIZE];
FILE *fp = NewStream(peer); FILE *fp = NewStream(peer);
int exit_code = -1;
while (true) while (true)
{ {
memset(recvbuffer, 0, sizeof(recvbuffer)); memset(recvbuffer, 0, sizeof(recvbuffer));
if (ReceiveTransaction(conn->conn_info, recvbuffer, NULL) == -1) if (ReceiveTransaction(conn->conn_info, recvbuffer, NULL) == -1)
{ {
break; break;
} }
if (strncmp(recvbuffer, CFD_TERMINATOR, strlen(CFD_TERMINATOR)) == 0) if (strncmp(recvbuffer, CFD_TERMINATOR, strlen(CFD_TERMINATOR)) == 0)
{ {
skipping to change at line 771 skipping to change at line 820
{ {
fprintf(fp, "%s> !! %s\n", ipaddr, recvbuffer + 4); fprintf(fp, "%s> !! %s\n", ipaddr, recvbuffer + 4);
} }
/* cf-serverd >= 3.7 quotes command output with "> ". */ /* cf-serverd >= 3.7 quotes command output with "> ". */
else if (strncmp(recvbuffer, "> ", 2) == 0) else if (strncmp(recvbuffer, "> ", 2) == 0)
{ {
fprintf(fp, "%s> -> %s", ipaddr, &recvbuffer[2]); fprintf(fp, "%s> -> %s", ipaddr, &recvbuffer[2]);
} }
else else
{ {
/* '(exit code: N)' is a special line, not prefixed with '>' (so not
* part of output) and sent last by new cf-serverd (3.18.0+) */
if (StringStartsWith(recvbuffer, "(exit code:"))
{
/* Should never show up twice. */
assert(exit_code == -1);
int scanned = sscanf(recvbuffer, "(exit code: %d)", &exit_code);
if (scanned != 1)
{
Log(LOG_LEVEL_ERR, "Failed to parse exit code from '%s'", re
cvbuffer);
}
}
fprintf(fp, "%s> %s", ipaddr, recvbuffer); fprintf(fp, "%s> %s", ipaddr, recvbuffer);
} }
if (recv_len > 0 && recvbuffer[recv_len - 1] != '\n') if (recv_len > 0 && recvbuffer[recv_len - 1] != '\n')
{ {
/* We'll be printing double newlines here with new cf-serverd /* We'll be printing double newlines here with new cf-serverd
* versions, so check for already trailing newlines. */ * versions, so check for already trailing newlines. */
/* TODO deprecate this path in a couple of versions. cf-serverd is /* TODO deprecate this path in a couple of versions. cf-serverd is
* supposed to munch the newlines so we must always append one. */ * supposed to munch the newlines so we must always append one. */
fputc('\n', fp); fputc('\n', fp);
} }
} }
if (fp != stdout) if (fp != stdout)
{ {
fclose(fp); fclose(fp);
} }
DisconnectServer(conn); DisconnectServer(conn);
return exit_code;
} }
/********************************************************************/ /********************************************************************/
/* Level */ /* Level */
/********************************************************************/ /********************************************************************/
static FILE *NewStream(char *name) static FILE *NewStream(char *name)
{ {
char filename[CF_BUFSIZE]; char filename[CF_BUFSIZE];
 End of changes. 25 change blocks. 
19 lines changed or deleted 88 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)