File: main.c | Size: 16,007 bytes | Download file | Back to directory listing | BWPOW's homepage
#include "scardbroker.h"
 
INFO info;
const char *types_pull[]={"users","cards",NULL};
const char *types_push[]={"import","payments","cctv",NULL};
 
pthread_mutex_t p_printf_mutex = PTHREAD_MUTEX_INITIALIZER;
 
void p_printf(const char *fmt, ...)
{
    char buf[4096],fname[PATH_MAX];
    struct tm *t;
    time_t theTime;
    FILE *s;
    va_list ap;
 
    if(info.logpath==NULL||info.logpath[0]=='\0') return;
 
    va_start(ap,fmt);
    vsnprintf(buf,sizeof(buf),fmt,ap);
    va_end(ap);
    buf[sizeof(buf)-1]='\0';
 
    pthread_mutex_lock(&p_printf_mutex);
 
    time(&theTime);
    t=localtime(&theTime);
 
    if(info.logpath[0]!='-') {
        sprintf(fname,"%s/scardbroker_%04d-%02d-%02d.log",info.logpath,t->tm_year+1900,t->tm_mon+1,t->tm_mday);
        s=fopen(fname,"a");
        if(s) {
            fprintf(s,"%04d-%02d-%02d %02d:%02d:%02d %s\n",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec,buf);
            fclose(s);
        }
    }
    else{
        printf("%s\n",buf);
    }
    pthread_mutex_unlock(&p_printf_mutex);
}
 
void main_exit_code(const char *text,const int rcode)
{
    if(strlen(text)>0){
        p_printf("Exit message: %s",text);
        fprintf(stderr,"%s\n",text);
    }
    p_printf("Application exit with errorcode %d",rcode);
    exit(rcode);
}
 
void main_exit_printf(const char *fmt, ...)
{
    char buf[4096];
    va_list ap;
    va_start(ap,fmt);
    vsnprintf(buf,sizeof(buf),fmt,ap);
    va_end(ap);
    main_exit_code(buf,1);
}
 
void main_exit(const char *text)
{
    main_exit_code(text,1);
}
 
int main_STR_INT_MAP_find(const STR_INT_MAP m[],const size_t num,const char *key)
{
    size_t i;
    for(i=0;i<num;++i){
        if(m[i].key==NULL) continue;
        if(strcmp(key,m[i].key)==0){
            return m[i].value;
        }
    }
    return -1;
}
 
int main_find_pokladna_ident(const char *key)
{
    return main_STR_INT_MAP_find(info.map_entry_to_pokladna,info.num_pokladna_idents,key);
}
 
int main_find_pokladna(const char *branch_id,const char *pokladna_id)
{
    int i;
    for(i=0;i<info.num_pokladna;++i){
        if(info.pokladna[i].branch_id==NULL) continue;
        if(info.pokladna[i].pokladna_id==NULL) continue;
        if(strcmp(info.pokladna[i].branch_id,branch_id)==0&&strcmp(info.pokladna[i].pokladna_id,pokladna_id)==0){
            return i;
        }
    }
    return -1;
}
 
int main_is_dir(const char *dname)
{
    struct stat st;
    if(stat(dname,&st)!=0) return 0;
    if(!S_ISDIR(st.st_mode)) return 0;
    return 1;
}
 
int main_is_file(const char *fname)
{
    struct stat st;
    if(stat(fname,&st)!=0) return 0;
    if(!S_ISREG(st.st_mode)) return 0;
    return 1;
}
 
int main_mkdir(const char *dname)
{
    if(main_is_dir(dname)) return 1;
    if(mkdir(dname,0755)!=0) return 0;
    if(main_is_dir(dname)) return 1;
    return 0;
}
 
static const unsigned char _b_hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
 
void main_bin2hex(unsigned char *buf,const size_t len)
{
    int i;
    for(i=(int)len-1;i>=0;--i){
        buf[(i*2)+1]=_b_hex[buf[i]&15];
        buf[(i*2)  ]=_b_hex[buf[i]>>4];
    }
    buf[len*2]='\0';
}
 
static unsigned char _main_hex2bin_c(const unsigned char *b,const size_t pos)
{
    unsigned char c=0;
    if(b[pos]>='0'&&b[pos]<='9') c|=(b[pos]-'0')<<4;
    if(b[pos]>='a'&&b[pos]<='f') c|=(b[pos]-'a'+10)<<4;
    if(b[pos]>='A'&&b[pos]<='F') c|=(b[pos]-'A'+10)<<4;
 
    if(b[pos+1]>='0'&&b[pos+1]<='9') c|=b[pos+1]-'0';
    if(b[pos+1]>='a'&&b[pos+1]<='f') c|=b[pos+1]-'a'+10;
    if(b[pos+1]>='A'&&b[pos+1]<='F') c|=b[pos+1]-'A'+10;
 
    return c;
}
 
void main_hex2bin(unsigned char *buf,const size_t len)
{
    int i;
    for(i=0;i<(int)len;++i){
        buf[i]=_main_hex2bin_c(buf,i*2);
    }
}
 
int main_read_config(const char *fname)
{
    FILE *s;
    char tbuf[4096],*val;
    ssize_t l;
    int i;
 
    int pokl_id=-1;
    char *branch_id=NULL;
    char *branch_name=NULL;
 
    s=fopen(fname,"r");
    if(s==NULL){
        p_printf("Could not open file '%s'",fname);
        return -1;
    }
 
    // default values
    info.is_debug=0;
    info.is_end=0;
    info.is_online=0;
    info.last_online_timestamp=time(0);
    info.is_to_upload=0;
    info.is_correct_time=0;
 
    info.datapath=NULL;
 
    info.broker_url=NULL;
    info.broker_client=NULL;
    info.broker_salt=NULL;
    info.broker_id=NULL;
 
    info.num_pokladna=0;
    info.num_pokladna_idents=0;
 
    for(i=0;i<MAX_POKLADNA;++i){
        info.pokladna[i].branch_id=NULL;
        info.pokladna[i].branch_name=NULL;
        info.pokladna[i].pokladna_id=NULL;
        info.pokladna[i].pokladna_salt=NULL;
        info.pokladna[i].hwid=NULL;
    }
 
    for(i=0;i<MAX_POKLADNA_IDENTS;++i){
        info.map_entry_to_pokladna[i].key=NULL;
        info.map_entry_to_pokladna[i].value=-1;
    }
 
    for(i=0;i<MAX_LISTFILE;++i){
        info.listfile[i].ftype=NULL;
        info.listfile[i].fname=NULL;
    }
    info.num_listfile=0;
 
    while(!feof(s)){
        if(fgets(tbuf,sizeof(tbuf),s)==NULL) break;
        tbuf[sizeof(tbuf)-1]='\0';
        l=strlen(tbuf);
        while(l>0&&(tbuf[l-1]=='\n'||tbuf[l-1]=='\r')) l--;
        tbuf[l]='\0';
 
        // skip this line, if it is empty or starts with # or ;
        if(l<=0||tbuf[0]=='#'||tbuf[0]==';') continue;
 
        // every config line should have NAME=VALUE
        // NAME and VALUE can contain white spaces, but won't be trimmed
 
        val=strchr(tbuf,'=');
        if(val==NULL) continue; //did not find =
        val[0]='\0';
        val++;
        if(strlen(val)<1) continue;
 
        //p_printf("CONFIG: '%s' = '%s'",tbuf,val);
 
        // broker identification
        if(strcmp(tbuf,"BROKER_URL")==0){
            if(info.broker_url!=NULL) free(info.broker_url);
            info.broker_url=strdup(val);
            if(info.broker_url==NULL) main_exit_printf("malloc error: %s",tbuf);
            continue;
        }
 
        if(strcmp(tbuf,"BROKER_CLIENT")==0){
            if(info.broker_client!=NULL) free(info.broker_client);
            info.broker_client=strdup(val);
            if(info.broker_client==NULL) main_exit_printf("malloc error: %s",tbuf);
            continue;
        }
 
        if(strcmp(tbuf,"BROKER_ID")==0){
            if(info.broker_id!=NULL) free(info.broker_id);
            info.broker_id=strdup(val);
            if(info.broker_id==NULL) main_exit_printf("malloc error: %s",tbuf);
            continue;
        }
 
        if(strcmp(tbuf,"BROKER_SALT")==0){
            if(info.broker_salt!=NULL) free(info.broker_salt);
            info.broker_salt=strdup(val);
            if(info.broker_salt==NULL) main_exit_printf("malloc error: %s",tbuf);
            continue;
        }
 
        // system variables
        if(strcmp(tbuf,"DATAPATH")==0){
            if(info.datapath!=NULL) free(info.datapath);
            info.datapath=strdup(val);
            if(info.datapath==NULL) main_exit_printf("malloc error: %s",tbuf);
            continue;
        }
 
        // branch
        if(strcmp(tbuf,"BRANCH_ID")==0){
            if(branch_id!=NULL) free(branch_id);
            branch_id=strdup(val);
            if(branch_id==NULL) main_exit_printf("malloc error: %s",tbuf);
            continue;
        }
 
        if(strcmp(tbuf,"BRANCH_NAME")==0){
            if(branch_name!=NULL) free(branch_name);
            branch_name=strdup(val);
            if(branch_name==NULL) main_exit_printf("malloc error: %s",tbuf);
            continue;
        }
 
        // pokladna
        if(strcmp(tbuf,"POKLADNA_ID")==0){
            info.num_pokladna++;
            if(info.num_pokladna>=MAX_POKLADNA){
                main_exit_printf("CONFIG ERROR: Maximum number (%d) of pokladna reached. To prevent error, I'm shutting down now",MAX_POKLADNA);
            }
            pokl_id=info.num_pokladna-1;
 
            if(branch_id==NULL) main_exit_printf("CONFIG ERROR: Can't creare pokladna '%s', because BRANCH_ID is not set",val);
            if(branch_name==NULL) main_exit_printf("CONFIG ERROR: Can't creare pokladna '%s', because BRANCH_NAME is not set",val);
 
            // check all previous pokladna for duplicity
            // we check only BRANCH_ID and POKLADNA_ID, those two together MUST be unique
            for(i=info.num_pokladna-2;i>=0;--i){
                if(info.pokladna[i].branch_id==NULL||info.pokladna[i].branch_name==NULL||info.pokladna[i].pokladna_id==NULL){
                    main_exit("MEMORY CORRUPTION: already set pokladna strings are NULL");
                }
                if(strcasecmp(info.pokladna[i].branch_id,branch_id)==0&&strcasecmp(info.pokladna[i].pokladna_id,val)==0){
                    main_exit_printf("CONFIG ERROR: Duplicated pokladna entry detected: '%s' -> '%s'",branch_id,val);
                }
            }
 
            if(info.pokladna[pokl_id].branch_id!=NULL||info.pokladna[pokl_id].branch_name!=NULL||info.pokladna[pokl_id].pokladna_id!=NULL||info.pokladna[pokl_id].pokladna_salt!=NULL||info.pokladna[pokl_id].hwid!=NULL){
                main_exit("MEMORY CORRUPTION: current pokladna strings are not NULL");
            }
 
            // now we know, that the latest entry is really unique, so let's add it to the list
            info.pokladna[pokl_id].branch_id=strdup(branch_id);
            info.pokladna[pokl_id].branch_name=strdup(branch_name);
            info.pokladna[pokl_id].pokladna_id=strdup(val);
 
            if(info.pokladna[pokl_id].branch_id==NULL||info.pokladna[pokl_id].branch_name==NULL||info.pokladna[pokl_id].pokladna_id==NULL){
                main_exit("malloc error: POKLADNA_ID");
            }
 
            p_printf("CONFIG INFO: Added new pokladna %d with '%s (%s)' -> '%s'",pokl_id,branch_id,branch_name,val);
            continue;
        }
 
        if(strcmp(tbuf,"POKLADNA_SALT")==0){
            if(pokl_id<0) main_exit("CONFIG ERROR: Can't set SALT, because no pokladna active");
            if(info.pokladna[pokl_id].pokladna_salt!=NULL) free(info.pokladna[pokl_id].pokladna_salt);
            info.pokladna[pokl_id].pokladna_salt=strdup(val);
            if(info.pokladna[pokl_id].pokladna_salt==NULL) main_exit_printf("malloc error: %s",tbuf);
            p_printf("CONFIG INFO: Added SALT to pokladna %d",pokl_id);
            continue;
        }
 
        if(strcmp(tbuf,"POKLADNA_ENTRY")==0){
            if(pokl_id<0) main_exit("CONFIG ERROR: Can't set ENTRY, because no pokladna active");
 
            if(main_find_pokladna_ident(val)!=-1){
                main_exit_printf("CONFIG ERROR: Duplicate POKLADNA_ENTRY '%s'",val);
            }
            if(info.num_pokladna_idents>=MAX_POKLADNA_IDENTS){
                main_exit_printf("CONFIG ERROR: Maximum number (%d) of POKLADNA_ENTRY reached. To prevent error, I'm shutting down now",MAX_POKLADNA_IDENTS);
            }
 
            info.map_entry_to_pokladna[info.num_pokladna_idents].key=strdup(val);
            info.map_entry_to_pokladna[info.num_pokladna_idents].value=pokl_id;
 
            if(info.map_entry_to_pokladna[info.num_pokladna_idents].key==NULL){
                main_exit("malloc error: POKLADNA_ENTRY");
            }
 
            p_printf("CONFIG INFO: Added ENTRY '%s' to pokladna %d",val,pokl_id);
 
            info.num_pokladna_idents++;
            continue;
        }
 
        p_printf("CONFIG: Ignoring unknown entry '%s'",tbuf);
    }
    fclose(s);
 
    if(info.num_pokladna<=0) main_exit("CONFIG ERROR: No pokladna set");
    for(i=0;i<info.num_pokladna;++i){
        if(info.pokladna[i].branch_id==NULL) main_exit_printf("CONFIG ERROR: Pokladna %d BRANCH_ID not set",i);
        if(info.pokladna[i].branch_name==NULL) main_exit_printf("CONFIG ERROR: Pokladna %d BRANCH_NAME not set",i);
        if(info.pokladna[i].pokladna_id==NULL) main_exit_printf("CONFIG ERROR: Pokladna %d POKLADNA_ID not set",i);
        if(info.pokladna[i].pokladna_salt==NULL) main_exit_printf("CONFIG ERROR: Pokladna %d POKLADNA_SALT not set",i);
    }
 
    if(info.broker_url==NULL) main_exit("CONFIG ERROR: BROKER_URL not set");
    if(info.broker_client==NULL) main_exit("CONFIG ERROR: BROKER_CLIENT not set");
    if(info.broker_id==NULL) main_exit("CONFIG ERROR: BROKER_ID not set");
    if(info.broker_salt==NULL) main_exit("CONFIG ERROR: BROKER_SALT not set");
 
    if(info.datapath==NULL) main_exit("CONFIG ERROR: DATAPATH not set");
    if(!main_is_dir(info.datapath)) main_exit("CONFIG ERROR: DATAPATH does not exists on FS, create it first");
 
    p_printf("CONFIG INFO: Total pokladna count = %d",info.num_pokladna);
    return 0;
}
 
void main_set_online(const uint8_t online,const char *name)
{
    if(online!=info.is_online){
        if(online){
            info.last_online_timestamp=0;
            p_printf("NETWORK STATUS: We are back online (in %s)",name);
        }
        else{
            info.last_online_timestamp=time(0);
            p_printf("NETWORK STATUS: We are now OFFLINE (in %s)",name);
        }
        info.is_online=online;
    }
}
 
void main_handler(int signal)
{
    p_printf("++++ Caught %s signal ++++",strsignal(signal));
    info.is_end=1;
}
 
int main_check_correct_time(void)
{
    struct tm *t;
    time_t theTime;
    time(&theTime);
    t=localtime(&theTime);
    if((t->tm_year+1900)>=2013){
        if(!info.is_correct_time){
            p_printf("Date and time seem to be set correctly now");
            info.is_correct_time=1;
        }
        return 1;
    }
 
    main_set_online(0,"main_check_correct_time");
    if(info.is_correct_time){
        p_printf("Date is WRONG! Year must be at least 2013.");
        info.is_correct_time=0;
    }
 
    return 0;
}
 
int main(void)
{
    int i,ticks=0;
    INFOCURL c;
 
    info.is_end=0;
    info.logpath=strdup("/var/log");
 
    p_printf("APP: START +++ (%s %s) +++++++++++++++++++++++++++++++++++++++++++++",APP_NAME,APP_VER);
    if(main_read_config("/etc/scardbroker.conf")!=0){
        main_exit("ERROR: main_read_config failed!");
    }
 
    signal(SIGINT,main_handler);
    signal(SIGTERM,main_handler);
    signal(SIGQUIT,main_handler);
 
    info.is_correct_time=-1;
    main_check_correct_time();
 
    if(http_global_open()!=0) main_exit("CURL failed to start");
    if(FCGX_Init()!=0) main_exit("FCGX failed to start");
 
    if(pthread_mutex_init(&info.mutex_fs,NULL)!=0) main_exit("Can't initalise mutex");
 
    for(i=0;i<info.num_pokladna;++i){
        if(pthread_mutex_init(&info.pokladna[i].mutex,NULL)!=0) main_exit("Can't initalise mutex");
        if(pthread_mutex_init(&info.pokladna[i].mutex_hwid,NULL)!=0) main_exit("Can't initalise mutex");
    }
 
    for(i=0;i<MAX_THREADS;++i){
        info.thr_fcgi[i].id=i;
        http_open(&info.thr_fcgi[i].c);
        if(pthread_create(&info.thr_fcgi[i].thr,NULL,thread_fastcgi,(void *)&info.thr_fcgi[i])!=0) main_exit("Can't create thread");
    }
 
    http_open(&c);
    info.backsync_type=0;
    info.backsync_pos=0;
 
    // this is the main loop
    // it is responsible for background synchronization of local storage with remote server
    while(!info.is_end){
        if(ticks<=0){
            ticks=broker_background_sync(&c);
        }
        sleep(1);
        ticks--;
    }
    http_close(&c);
    info.is_end=1;
 
    p_printf("ShutDownPending()");
    FCGX_ShutdownPending();
 
    signal(SIGINT,NULL);
    signal(SIGTERM,NULL);
    signal(SIGQUIT,NULL);
 
    pthread_mutex_lock(&info.mutex_fs);
    p_printf("FS locked, we can shutdown...");
    for(i=0;i<MAX_THREADS;++i){
        pthread_kill(info.thr_fcgi[i].thr,SIGINT);
    }
 
    for(i=0;i<MAX_THREADS;++i){
        (void) pthread_join(info.thr_fcgi[i].thr,NULL);
        http_close(&info.thr_fcgi[i].c);
    }
    pthread_mutex_unlock(&info.mutex_fs);
 
    for(i=0;i<info.num_pokladna;++i){
        pthread_mutex_destroy(&info.pokladna[i].mutex_hwid);
        pthread_mutex_destroy(&info.pokladna[i].mutex);
    }
 
    pthread_mutex_destroy(&info.mutex_fs);
 
    p_printf("APP: END ++++++++++++++++++++++++++++++++++++++++++++++++");
    http_global_close();
    main_exit_code("",0);
    return 0;
}