#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; }