Esempio n. 1
0
void LocalExec(const ExecConfig *config)
{
    FILE *pp;
    char line[CF_BUFSIZE], line_escaped[sizeof(line) * 2], filename[CF_BUFSIZE], *sp;
    char cmd[CF_BUFSIZE], esc_command[CF_BUFSIZE];
    int print, count = 0;
    void *thread_name;
    time_t starttime = time(NULL);
    char starttime_str[64];
    FILE *fp;
    char canonified_fq_name[CF_BUFSIZE];

    thread_name = ThreadUniqueName();

    cf_strtimestamp_local(starttime, starttime_str);

    CfOut(cf_verbose, "", "------------------------------------------------------------------\n\n");
    CfOut(cf_verbose, "", "  LocalExec(%sscheduled) at %s\n", config->scheduled_run ? "" : "not ", starttime_str);
    CfOut(cf_verbose, "", "------------------------------------------------------------------\n");

/* Need to make sure we have LD_LIBRARY_PATH here or children will die  */

    if (strlen(config->exec_command) > 0)
    {
        strncpy(cmd, config->exec_command, CF_BUFSIZE - 1);

        if (!strstr(cmd, "-Dfrom_cfexecd"))
        {
            strcat(cmd, " -Dfrom_cfexecd");
        }
    }
    else
    {
        ConstructFailsafeCommand(config->scheduled_run, cmd);
    }

    strncpy(esc_command, MapName(cmd), CF_BUFSIZE - 1);

    snprintf(line, CF_BUFSIZE - 1, "_%jd_%s", (intmax_t) starttime, CanonifyName(cf_ctime(&starttime)));

    strlcpy(canonified_fq_name, config->fq_name, CF_BUFSIZE);
    CanonifyNameInPlace(canonified_fq_name);

    snprintf(filename, CF_BUFSIZE - 1, "%s/outputs/cf_%s_%s_%p", CFWORKDIR, canonified_fq_name, line, thread_name);
    MapName(filename);

/* What if no more processes? Could sacrifice and exec() - but we need a sentinel */

    if ((fp = fopen(filename, "w")) == NULL)
    {
        CfOut(cf_error, "fopen", "!! Couldn't open \"%s\" - aborting exec\n", filename);
        return;
    }

#if !defined(__MINGW32__)
/*
 * Don't inherit this file descriptor on fork/exec
 */

    if (fileno(fp) != -1)
    {
        fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
    }
#endif

    CfOut(cf_verbose, "", " -> Command => %s\n", cmd);

    if ((pp = cf_popen_sh(esc_command, "r")) == NULL)
    {
        CfOut(cf_error, "cf_popen", "!! Couldn't open pipe to command \"%s\"\n", cmd);
        fclose(fp);
        return;
    }

    CfOut(cf_verbose, "", " -> Command is executing...%s\n", esc_command);

    while (!feof(pp))
    {
        if(!IsReadReady(fileno(pp), (config->agent_expireafter * SECONDS_PER_MINUTE)))
        {
            char errmsg[CF_MAXVARSIZE];
            snprintf(errmsg, sizeof(errmsg), "cf-execd: !! Timeout waiting for output from agent (agent_expireafter=%d) - terminating it",
                     config->agent_expireafter);

            CfOut(cf_error, "", "%s", errmsg);
            fprintf(fp, "%s\n", errmsg);
            count++;

            pid_t pid_agent;

            if(PipeToPid(&pid_agent, pp))
            {
                ProcessSignalTerminate(pid_agent);
            }
            else
            {
                CfOut(cf_error, "", "!! Could not get PID of agent");
            }

            break;
        }

        {
            ssize_t num_read = CfReadLine(line, CF_BUFSIZE, pp);
            if (num_read == -1)
            {
                FatalError("Cannot continue on CfReadLine error");
            }
            else if (num_read == 0)
            {
                break;
            }
        }

        if(!CfReadLine(line, CF_BUFSIZE, pp))
        {
            break;
        }

        if (ferror(pp))
        {
            fflush(pp);
            break;
        }

        print = false;

        for (sp = line; *sp != '\0'; sp++)
        {
            if (!isspace((int) *sp))
            {
                print = true;
                break;
            }
        }

        if (print)
        {
            // we must escape print format chars (%) from output

            ReplaceStr(line, line_escaped, sizeof(line_escaped), "%", "%%");

            fprintf(fp, "%s\n", line_escaped);
            count++;

            /* If we can't send mail, log to syslog */

            if (strlen(config->mail_to_address) == 0)
            {
                strncat(line_escaped, "\n", sizeof(line_escaped) - 1 - strlen(line_escaped));
                if ((strchr(line_escaped, '\n')) == NULL)
                {
                    line_escaped[sizeof(line_escaped) - 2] = '\n';
                }

                CfOut(cf_inform, "", "%s", line_escaped);
            }

            line[0] = '\0';
            line_escaped[0] = '\0';
        }
    }

    cf_pclose(pp);
    CfDebug("Closing fp\n");
    fclose(fp);

    CfOut(cf_verbose, "", " -> Command is complete\n");

    if (count)
    {
        CfOut(cf_verbose, "", " -> Mailing result\n");
        MailResult(config, filename);
    }
    else
    {
        CfOut(cf_verbose, "", " -> No output\n");
        unlink(filename);
    }
}
Esempio n. 2
0
static bool IsReadReady(int fd, int timeout_sec)
{
    fd_set  rset;
    FD_ZERO(&rset);
    FD_SET(fd, &rset);

    struct timeval tv = {
        .tv_sec = timeout_sec,
        .tv_usec = 0,
    };

    int ret = select(fd + 1, &rset, NULL, NULL, &tv);

    if(ret < 0)
    {
        Log(LOG_LEVEL_ERR, "IsReadReady: Failed checking for data. (select: %s)", GetErrorStr());
        return false;
    }

    if(FD_ISSET(fd, &rset))
    {
        return true;
    }

    if(ret == 0)  // timeout
    {
        return false;
    }

    // can we get here?
    Log(LOG_LEVEL_ERR, "IsReadReady: Unknown outcome (ret > 0 but our only fd is not set). (select: %s)", GetErrorStr());

    return false;
}
#if defined(__hpux) && defined(__GNUC__)
#pragma GCC diagnostic warning "-Wstrict-aliasing"
#endif

#endif  /* __MINGW32__ */

void LocalExec(const ExecConfig *config)
{
    time_t starttime = time(NULL);

    void *thread_name = ThreadUniqueName();

    {
        char starttime_str[64];
        cf_strtimestamp_local(starttime, starttime_str);

        if (LEGACY_OUTPUT)
        {
            Log(LOG_LEVEL_VERBOSE, "------------------------------------------------------------------");
            Log(LOG_LEVEL_VERBOSE, "  LocalExec(%sscheduled) at %s", config->scheduled_run ? "" : "not ", starttime_str);
            Log(LOG_LEVEL_VERBOSE, "------------------------------------------------------------------");
        }
        else
        {
            Log(LOG_LEVEL_VERBOSE, "LocalExec(%sscheduled) at %s", config->scheduled_run ? "" : "not ", starttime_str);
        }
    }

/* Need to make sure we have LD_LIBRARY_PATH here or children will die  */

    char cmd[CF_BUFSIZE];
    if (strlen(config->exec_command) > 0)
    {
        strncpy(cmd, config->exec_command, CF_BUFSIZE - 1);

        if (!strstr(cmd, "-Dfrom_cfexecd"))
        {
            strcat(cmd, " -Dfrom_cfexecd");
        }
    }
    else
    {
        ConstructFailsafeCommand(config->scheduled_run, cmd);
    }

    char esc_command[CF_BUFSIZE];
    strncpy(esc_command, MapName(cmd), CF_BUFSIZE - 1);

    char line[CF_BUFSIZE];
    snprintf(line, CF_BUFSIZE - 1, "_%jd_%s", (intmax_t) starttime, CanonifyName(ctime(&starttime)));

    char filename[CF_BUFSIZE];
    {
        char canonified_fq_name[CF_BUFSIZE];

        strlcpy(canonified_fq_name, config->fq_name, CF_BUFSIZE);
        CanonifyNameInPlace(canonified_fq_name);


        snprintf(filename, CF_BUFSIZE - 1, "%s/outputs/cf_%s_%s_%p", CFWORKDIR, canonified_fq_name, line, thread_name);
        MapName(filename);
    }


/* What if no more processes? Could sacrifice and exec() - but we need a sentinel */

    FILE *fp = fopen(filename, "w");
    if (!fp)
    {
        Log(LOG_LEVEL_ERR, "Couldn't open '%s' - aborting exec. (fopen: %s)", filename, GetErrorStr());
        return;
    }

#if !defined(__MINGW32__)
/*
 * Don't inherit this file descriptor on fork/exec
 */

    if (fileno(fp) != -1)
    {
        fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
    }
#endif

    Log(LOG_LEVEL_VERBOSE, "Command => %s", cmd);

    FILE *pp = cf_popen_sh(esc_command, "r");
    if (!pp)
    {
        Log(LOG_LEVEL_ERR, "Couldn't open pipe to command '%s'. (cf_popen: %s)", cmd, GetErrorStr());
        fclose(fp);
        return;
    }

    Log(LOG_LEVEL_VERBOSE, "Command is executing...%s", esc_command);

    int count = 0;
    for (;;)
    {
        if(!IsReadReady(fileno(pp), (config->agent_expireafter * SECONDS_PER_MINUTE)))
        {
            char errmsg[CF_MAXVARSIZE];
            snprintf(errmsg, sizeof(errmsg), "cf-execd: !! Timeout waiting for output from agent (agent_expireafter=%d) - terminating it",
                     config->agent_expireafter);

            Log(LOG_LEVEL_ERR, "%s", errmsg);
            fprintf(fp, "%s\n", errmsg);
            count++;

            pid_t pid_agent;

            if(PipeToPid(&pid_agent, pp))
            {
                ProcessSignalTerminate(pid_agent);
            }
            else
            {
                Log(LOG_LEVEL_ERR, "Could not get PID of agent");
            }

            break;
        }

        ssize_t res = CfReadLine(line, CF_BUFSIZE, pp);

        if (res == 0)
        {
            break;
        }

        if (res == -1)
        {
            Log(LOG_LEVEL_ERR, "Unable to read output from command '%s'. (cfread: %s)", cmd, GetErrorStr());
            cf_pclose(pp);
            return;
        }

        bool print = false;

        for (const char *sp = line; *sp != '\0'; sp++)
        {
            if (!isspace((int) *sp))
            {
                print = true;
                break;
            }
        }

        if (print)
        {
            char line_escaped[sizeof(line) * 2];

            // we must escape print format chars (%) from output

            ReplaceStr(line, line_escaped, sizeof(line_escaped), "%", "%%");

            fprintf(fp, "%s\n", line_escaped);
            count++;

            /* If we can't send mail, log to syslog */

            if (strlen(config->mail_to_address) == 0)
            {
                strncat(line_escaped, "\n", sizeof(line_escaped) - 1 - strlen(line_escaped));
                if ((strchr(line_escaped, '\n')) == NULL)
                {
                    line_escaped[sizeof(line_escaped) - 2] = '\n';
                }

                Log(LOG_LEVEL_INFO, "%s", line_escaped);
            }

            line[0] = '\0';
            line_escaped[0] = '\0';
        }
    }

    cf_pclose(pp);
    Log(LOG_LEVEL_DEBUG, "Closing fp");
    fclose(fp);

    Log(LOG_LEVEL_VERBOSE, "Command is complete");

    if (count)
    {
        Log(LOG_LEVEL_VERBOSE, "Mailing result");
        MailResult(config, filename);
    }
    else
    {
        Log(LOG_LEVEL_VERBOSE, "No output");
        unlink(filename);
    }
}