C中的分叉线程正在互相编写stdout

时间:2016-06-13 12:20:10

标签: c multithreading

我写了一个小守护进程。

这是守护进程的流程,通常是:

  1. 获取变量

  2. 从数据库中获取满足查询参数的所有行(可能是MySQL或Oracle)(在这种情况下获取具有此当前时间的所有行)。

  3. 如果找到任何行,则为每行运行一个Perl脚本(使用execv)。

  4. 这个守护进程运行良好,但问题是当我从查询中返回两行或更多行时,它们会启动,但Perl脚本输出是混合的。它们应该独立运行而不会相互干扰。

    我做错了吗?

    这是内存泄漏吗?

    这是我的代码:

    /*
     ============================================================================
     Name        : main.c
     Description : Daemon for scheduler
     ============================================================================
     */
    
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <unistd.h>
    #include <syslog.h>
    #include <string.h>
    #include <strings.h>
    #include <regex.h>
    #include <time.h>
    
    #ifdef MYSQL_CODE
    #include <mysql.h>
    #endif
    
    #ifdef ORACLE_CODE
    #include <ocilib.h>
    #endif
    
    #define DAEMON_NAME "schedulerd"
    
    void start_job(char *automation_user,char *automation_path,char *automation_log_path, char *id, char *site, char *db_type,char *db_host,char *db_user,char *db_password,char *db_schemata){
    
        pid_t pid;
        pid = fork();
    
        if (pid < 0) { exit(EXIT_FAILURE); }
    
        //We got a good pid, Continue to the next result
        if (pid > 0) {
            return;
        }
    
        char *file_format = "scheduler";
        char *seperator = "_";
        char *postfix = "_XXXXXX";
        char *extension = ".log";
    
        //get Time
        time_t now;
        struct tm *now_tm;
        char hour[2],min[2],sec[2];
    
        now = time(NULL);
        now_tm = localtime(&now);
        sprintf(hour, "%d", now_tm->tm_hour);
        sprintf(min, "%d", now_tm->tm_min);
        sprintf(sec, "%d", now_tm->tm_sec);
    
        char *str  = (char *)automation_log_path;
        strcat(str,(char *)file_format);
        strcat(str,seperator);
        strcat(str,hour);
        strcat(str,seperator);
        strcat(str,min);
        strcat(str,seperator);
        strcat(str,sec);
        strcat(str,postfix);
    
        // buffer to hold the temporary file name
        char nameBuff[128];
    
        int filedes = -1;
    
        // memset the buffers to 0
        memset(nameBuff,0,sizeof(nameBuff));
    
        // Copy the relevant information in the buffers
        strncpy(nameBuff,str,128);
    
        // Create the temporary file, this function will replace the 'X's
        filedes = mkstemp(nameBuff);
    
        if(filedes<1)
        {
            syslog (LOG_NOTICE, "Creation of temp file [%s] failed with error [%s]\n",nameBuff,strerror(errno));
        }else{
            mode_t perm = 0644;
            fchmod(filedes, perm); //Change permission to the file so everyone can read
    
            close(filedes); // Close created file
            //Rename file
            int ret;
            char newname[128];
            sprintf(newname, "%s%s", nameBuff,extension);
            ret = rename(nameBuff, newname);
            if(ret != 0) {
                syslog (LOG_NOTICE, "Renaming  of temp file %s to %s failed \n",nameBuff,newname);
                exit(EXIT_FAILURE);
            }
    
            char statement[256];
            sprintf(statement, "UPDATE scheduler_list SET log_file='%s' WHERE id='%s'", newname,id);
            syslog (LOG_NOTICE,"Adding to DB : %s\n", statement);
    
            char command[2048];
            sprintf(command, "cd %s ; ./runner.pl -site %s -log %s -scheduler_id %s -command \\\"./run_me.pl\\\"", automation_path,site,newname,id);
            //sprintf(command, "cd /net/10.7.5.50/opt/trunk/ ; ./runner.pl -site %s -log %s -scheduler_id %s -command \\\"./run_me.pl\\\"",site,newname,id);
    
            if (strcasestr(db_type,"mysql")){/* mysql */
    #ifdef MYSQL_CODE
                MYSQL *conn;
                //mysql_free_result(res); // Free mysql
                conn = mysql_init(NULL);
                /* Connect to database */
                if (!mysql_real_connect(conn, db_host,db_user, db_password, db_schemata, 0, NULL, 0)) {
                    syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
                    exit(EXIT_FAILURE);
                }
    
                if (mysql_query(conn,statement)) {
                    syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
                    exit(EXIT_FAILURE);
                }
    #endif
            }else{
    #ifdef ORACLE_CODE
                OCI_Connection* cn;
                OCI_Statement* st;
                OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT);
                char query_command[128];
                sprintf(query_command, "%s:1521/%s", db_host,db_schemata);
                cn = OCI_ConnectionCreate(query_command, db_user, db_password, OCI_SESSION_DEFAULT);
                st = OCI_StatementCreate(cn);
                OCI_Prepare(st, statement);
                OCI_Execute(st);
                OCI_Commit(cn);
                OCI_Cleanup();
    #endif
            }
    
            char *args[] = {"sudo", "-u", automation_user, "bash","-c",command,NULL};
    
            FILE *log_file_h = fopen(newname, "w");
            if (log_file_h == NULL)
            {
                syslog (LOG_NOTICE,"Error opening file %s !\n",newname);
                exit(EXIT_FAILURE);
            }
    
            syslog (LOG_NOTICE,"Starting scheduler job %s , command : %s",id, command);
            fclose(log_file_h);
    
            execv("/usr/bin/sudo",args);
            syslog (LOG_NOTICE,"Failed to start job %s ",id);
            perror("error");
        }
        exit(EXIT_FAILURE);
    }
    
    void process(char *automation_user,char *automation_path, char *automation_log_path ,char *db_type,char *db_host,char *db_user,char *db_password,char *db_schemata){
    
        if (strcasestr(db_type,"mysql")){/* mysql */
    #ifdef MYSQL_CODE
            MYSQL *conn;
            MYSQL_RES *res;
            MYSQL_ROW row;
            conn = mysql_init(NULL);
            /* Connect to database */
            if (!mysql_real_connect(conn, db_host,db_user, db_password, db_schemata, 0, NULL, 0)) {
                syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
                return;
            }
            /* send SQL query */
            if (mysql_query(conn, "SELECT id,site from scheduler_list where start_date = DATE_FORMAT(now(),'%Y-%m-%d %k:%i:00') AND id != father_id AND run='yes'")) {
                syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
                return;
            }
            res = mysql_use_result(conn);
            /* output table name */
            while ((row = mysql_fetch_row(res)) != NULL){
                char *id = malloc(strlen(row[0]) +1);
                strcpy(id,row[0]);
                char *site = malloc(strlen(row[1]) +1);
                strcpy(site,row[1]);
    
                start_job(automation_user,automation_path,automation_log_path,id,site,db_type, db_host,db_user,db_password,db_schemata);
            }
            /* close connection */
            mysql_free_result(res);
            mysql_close(conn);
    #endif
        }else{/* oracle */
    #ifdef ORACLE_CODE
            OCI_Connection* cn;
            OCI_Statement* st;
            OCI_Resultset* rs;
            OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT);
            char query_command[128];
            sprintf(query_command, "%s:1521/%s", db_host,db_schemata);
            cn = OCI_ConnectionCreate(query_command, db_user, db_password, OCI_SESSION_DEFAULT);
            st = OCI_StatementCreate(cn);
    
            OCI_ExecuteStmt(st, "SELECT id,site from scheduler_list where to_char(start_date, 'yyyy-mm-dd hh24:mi')  =  to_char(SYSDATE, 'yyyy-mm-dd hh24:mi')  AND id != father_id AND run='yes'");
    
            rs = OCI_GetResultset(st);
            while (OCI_FetchNext(rs)){
    
                char *id = malloc(strlen(OCI_GetString(rs, 1)) +1);
                strcpy(id,OCI_GetString(rs,1));
                char *site = malloc(strlen(OCI_GetString(rs,2)) +1);
                strcpy(site,OCI_GetString(rs,2));
                start_job(automation_user,automation_path,automation_log_path,id,site,db_type, db_host,db_user,db_password,db_schemata);
            }
            OCI_Cleanup();
    #endif
        }
    
    
    }
    
    char * set_conf_param (char *line, int addSlash){
        char *param =  malloc(strlen(line) + 2 + addSlash);
        strcpy(param,line);
        param = strchr(line,'=');
        param = param+1; //Remove '='
        strtok(param, "\n"); //remove /n
        if (addSlash == 1){
            int len = strlen(param);
            param[len] = '/';
            param[len+1] = '\0';
        }
        return strdup(param);
    }
    
    int main(int argc, char *argv[]) {
            FILE * fp;
            char * line = NULL;
            size_t len = 0;
            int found_db = 0;
            ssize_t read;
            pid_t pid, sid;
            char *automation_user=NULL,*automation_log_path=NULL ,*db_type=NULL, *db_host=NULL , *db_user=NULL, *db_password=NULL, *db_schemata=NULL;
            char *automation_path = getenv("AUTOMATION_PATH");
            //char *automation_path = "/net/10.7.5.50/opt/trunk/";
    
            char *automation_user_search = "automation_user=";
            char *automation_log_path_search = "automation_log=";
            char *db_type_search = "type=";
            char *db_host_search = "host=";
            char *db_user_search = "user=";
            char *db_password_search = "password=";
            char *db_schemata_search = "schemata=";
            const char comment = '#';
            /* Change the working directory to the root directory */
            /* or another appropriated directory */
            chdir(automation_path);
    
            //Set our Logging Mask and open the Log
            setlogmask(LOG_UPTO(LOG_NOTICE));
            openlog(DAEMON_NAME, LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
    
            syslog(LOG_NOTICE, "Entering Daemon");
    
            //Read framework.conf
            fp = fopen("framework.conf", "r");
            if (fp == NULL){
                syslog (LOG_NOTICE,"Failed to open framework.conf");
                exit(1);
            }
    
            //Read framework.conf
            fp = fopen("framework.conf", "r");
            if (fp == NULL){
                syslog (LOG_NOTICE,"Failed to open framework.conf");
                exit(1);
            }
            while ((read = getline(&line, &len, fp)) != -1) {
    
                //If line commented
                if (strchr(line,comment) != NULL){
                    continue;
                }
    
                if (strstr(line,automation_user_search) != NULL){
                    automation_user = set_conf_param(line,0);
                }
                else if (strstr(line,automation_log_path_search) != NULL){
                    automation_log_path = set_conf_param(line,1);
                }
    
                else if (db_type!=NULL && strcasestr(line,db_type) != NULL){
                    found_db = 1;
                }
                else if (strstr(line,db_type_search) != NULL){
                    db_type = set_conf_param(line,0);
                }
                else if (found_db && db_host==NULL && strstr(line,db_host_search) != NULL){
                    db_host = set_conf_param(line,0);
                }
                else if (found_db && db_user==NULL && strstr(line,db_user_search) != NULL){
                    db_user = set_conf_param(line,0);
                }
                else if (found_db && db_password==NULL && strstr(line,db_password_search) != NULL){
                    db_password = set_conf_param(line,0);
                }
                else if (found_db && db_schemata==NULL && strstr(line,db_schemata_search) != NULL){
                    db_schemata = set_conf_param(line,0);
                }
            }
    
            fclose(fp);
            if (line)
                free(line);
    
            if (automation_user==NULL){
                automation_user = "root";
            }
    
            //Fork the Parent Process
            pid = fork();
    
            if (pid < 0) { exit(EXIT_FAILURE); }
    
            //We got a good pid, Close the Parent Process
            if (pid > 0) { exit(EXIT_SUCCESS); }
    
            //Change File Mask
            umask(0);
    
            //Create a new Signature Id for our child
            sid = setsid();
            if (sid < 0) { exit(EXIT_FAILURE); }
    
            //Close Standard File Descriptors
            close(STDIN_FILENO);
            close(STDOUT_FILENO);
            close(STDERR_FILENO);
    
            //----------------
            //Main Process
            //----------------
            while(1){
                process(automation_user,automation_path,automation_log_path,db_type,db_host,db_user,db_password,db_schemata);    //Run our Process
                sleep(60);    //Sleep for 60 seconds
            }
    
            //Close the log
            closelog ();
    
            exit(EXIT_FAILURE);
    }
    

2 个答案:

答案 0 :(得分:7)

  1. 您似乎没有任何努力撰写minimal, complete verifiable example

    这只是整个程序的转储,包括您未提供的配置文件的依赖关系,以及您未显示其架构的数据库。这些都与您的问题无关。

    至少你编辑了公司名称,但作为参考,它仍然可以在编辑历史中看到。

  2.   

    C中的线程正在互相写stdout

    我在你的程序中看不到任何线程,你也没有分叉线程 - 你有什么是子进程

  3.   

    ......正在互相写stdout

    好吧,stdout是从父级继承的,所以所有的孩子(以及他们的 sudo / bash / perl /无论孩子们)都写到同一个地方。写入本身被锁定,但可以交错。

    如果您希望他们的输出不是交错的,请让他们写入不同的地方,并找出如何在以后显示/组合它们。

    每个子进程临时文件是一种流行的选择,但请注意,父进程必须跟踪子进程完成情况,以便知道何时打印然后删除每个文件。此外,您可能应该从终端分离子进程,关闭stdin,并考虑如何处理stderr。

  4.   

    这是内存泄漏吗?

    没有

答案 1 :(得分:0)

我想(在快速一瞥之后)问题出现在

execv("/usr/bin/sudo",args);

您应该确保只有一个实例正在运行。 这可以通过相互排斥来完成。 一个很好的描述是:    http://www.thegeekstuff.com/2012/05/c-mutex-examples/

简而言之:

// create a global mutex variable
pthread_mutex_t lock;

// initialise it in main
pthread_mutex_init(&lock, NULL);

// enclose your critical path
pthread_mutex_lock(&lock);
execv("/usr/bin/sudo",args);
pthread_mutex_unlock(&lock);

希望它有助于