#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config_servers.h" #include "config_cmds.h" #include "darray.h" #include "buf.h" #include "mq_cmd.h" #include "mq_ntf.h" #include "tbl_qcmd.h" #include "util.h" #include "sock_conn.h" #include "irc_parse.h" #include "mmm.h" /* no defence programming, no error checking, no argument checking just PoC nothing else */ #define MQ_MSG_SIZE 8192 //HACK extern int __irc_buf_drain_io(irc_buf *ib); /* Wrap call back, to manage how going be executed callback. Manage input and output params for callback. INPUT(string)->OUTPUT(string) */ tble_cmd_resp* cllbk_wrapper( void *(*call)(void *), tble_cmd_param *param) { void *data_out = NULL; tble_cmd_resp *resp = NULL; char *data=NULL; if (call == NULL) { PERM(); return NULL; } if (param == NULL) { PERM(); return NULL; } //prepare response resp = tbl_cmd_resp_c(param); if (resp == NULL) { PERM(); } tbl_cmd_resp_print(resp); data = alloc_new_str(param->param); //call callback data_out = call(data); //set result to returned response if (data_out == NULL) { //response doesnt have any output if (resp) { //response dont have result resp->ret_code = TBL_RSP_NORESP; resp->resp = NULL; } } else { if (resp) { //set succesfull repsonse resp->ret_code = TBL_RSP_OK; resp->resp = alloc_new_str(data_out); } } //dangerouse place =P free(data_out); free(data); return resp; } /* return unique ID, with atomic counter should work in all cases */ _Atomic int _glbl_id=0; #define SEND_CMD_IN(MQ,ID,CMD,PARAM) \ send_mq_cmd(MQ,MQ_IN,ID,CMD,strlen(CMD),PARAM,strlen(PARAM)); #define SEND_CMD_OUT(MQ,ID,CMD,PARAM) \ send_mq_cmd(MQ,MQ_OUT,ID,CMD,strlen(CMD),PARAM,strlen(PARAM)); #define RECV_CMD_IN(MQ,CMD) \ recv_mq_cmd(MQ,MQ_IN,CMD); #define RECV_CMD_OUT(MQ,CMD) \ recv_mq_cmd(MQ,MQ_OUT,CMD); /* |--------|<--IN---|---------| | SERVER | | MANAGER | |--------|--OUT-->|---------| */ #define STACK_SIZE (1024*128) /* not supposed to be changed. */ typedef struct server_cfg { /* thread params */ int tid; void *stack; //atomic_int running; _Atomic int running; mq_ntf_mdt *mq; /* irc server config */ char *user; char *password; char *server; //should be changed to hostname? char **channels; char *port; int ssl; } server_cfg; /******************************************************************************* server thread *******************************************************************************/ /* server_cfg struct as input */ #define TH_STATE_INIT 0 #define TH_STATE_START 1 #define TH_STATE_LISTEN_IN 2 #define TH_STATE_LISTEN_OUT 3 #define TH_STATE_SEND_IN 4 #define TH_STATE_SEND_OUT 5 #define TH_STATE_TERMINATION 6 #define TH_STATE_EXIT 7 #define TH_CONN_BUF_SZ 1024 int th_start_client(void *data) { //int cmd_id = 1; int err; //mq_cmd *qcmd=NULL; //queue command int run; int mq_event; int byte_num; int fret=-1; server_cfg *cfg = data; mq_ntf_mdt *mq = cfg->mq; mq_cmd *recv_cmd = NULL; // for recieved cmd from mq struct mq_attr out_attr, *ptr_out_attr=&out_attr; struct mq_attr in_attr, *ptr_in_attr=&in_attr; char *out_buf = NULL; char *in_buf = NULL; //network creation var int cret = -1; //int conn=-1; irc_conn conn; char conn_buf[TH_CONN_BUF_SZ]; char cmd_buf[TH_CONN_BUF_SZ]; //irc parsting irc_buf *ib = NULL; irc_token *itok = NULL; char *irc_line = NULL; //table to match response/request tble_qcmd *qcmd = NULL; tbl_qcmd *qtbl = NULL; //create response table qtbl = tbl_qcmd_c(100); if (qtbl == NULL) { PERM(); } atomic_fetch_add(&cfg->running,1); printf("Start client\n"); printf("Server %d\n",cfg->tid); sleep(1); //PRINT("SERVER:%s PORT:%s USER:%s\n", cfg->server, cfg->port, cfg->user); /* //PNL(); //PRINT("SERVER:%s PORT:%s USER:%s\n", cfg->server, cfg->port, cfg->user); conn = irc_connect(cfg->server, cfg->port); //conn = irc_connect("irc.freenode.net", "6667"); //PNL(); if (conn < 0) { PNL(); printf("cant connect to server\n"); //well we dont whant to just exit from thread //lets put inside main thread CONNection checker //if no connection just send some commmand and create //some logic how to deal with it atomic_fetch_sub(&cfg->running,1); //return 0; }*/ memset(&conn, 0, sizeof(irc_conn)); fret = irc_open(cfg->server, cfg->port, &conn); if (fret != 0) { ERROR("Cant connect\n"); return -1; } //lets timeout after 150 sec, ping from server is 120 sec irc_read_timeout(&conn,150); //PNL(); //send some commands to irc to register nick snprintf(cmd_buf, TH_CONN_BUF_SZ, "USER %s 0 0 :%s\r\n", cfg->user, cfg->user); write(conn.conn_fd, cmd_buf, strlen(cmd_buf)); snprintf(cmd_buf, TH_CONN_BUF_SZ,"NICK %s \r\n", cfg->user); write(conn.conn_fd, cmd_buf, strlen(cmd_buf)); //prepare message queue mq = cfg->mq; if (mq_ntf_getattr(mq, MQ_OUT, &ptr_out_attr) == -1) { printf("Cant get attribute\n"); ENL(); } //PRINT("SERVER:%s PORT:%s USER:%s\n", cfg->server, cfg->port, cfg->user); out_buf = malloc(out_attr.mq_msgsize); memset(out_buf, 0, out_attr.mq_msgsize); if (out_buf == NULL) { ENL(); } //PRINT("SERVER:%s PORT:%s USER:%s\n", cfg->server, cfg->port, cfg->user); if (mq_ntf_getattr(mq, MQ_IN, &ptr_in_attr) == -1) { printf("Cant get attribute\n"); ENL(); } in_buf = malloc(in_attr.mq_msgsize); memset(in_buf, 0, in_attr.mq_msgsize); if (in_buf == NULL) { ENL(); } //PRINT("SERVER:%s PORT:%s USER:%s\n", cfg->server, cfg->port, cfg->user); //create irc parsing structures memset(conn_buf, 0, TH_CONN_BUF_SZ); memset(cmd_buf, 0, TH_CONN_BUF_SZ); ib = irc_buf_create(); if (ib == NULL) { PERM(); //return 0; } //send command wait for response printf("Start loop\n"); run = 1; while (run) { //Collect events from MQ printf("Client loop tick\n"); mq_event = mq_ntf_select(mq, MQ_IN); switch(mq_event) { case 0: //PRINT("EVENT 0\n"); break; case 1: //PRINT("MQ_EVENT IN\n"); //memset(in_buf, 0, in_attr.mq_msgsize); if ((byte_num = mq_ntf_read(mq, MQ_IN, in_buf, in_attr.mq_msgsize)) == -1) { printf("Cant read input message \n"); } else { in_buf[byte_num] = 0x0; PRINT("Recieve %s\n", in_buf); } break; default: printf("Unknown event type\n"); } //fast code to exit if QUIT command is recieved //for command in queue //recieve commands from queue if (mq_event == 1) { recv_cmd = mq_cmd_creates(in_buf, in_attr.mq_msgsize, -1); if (recv_cmd != NULL) { if (mq_cmd_o_cmp_cmd(recv_cmd,"QUIT") == 0) { printf("QUIT recieved lets quit main loop\n"); break; } else if (mq_cmd_o_cmp_cmd(recv_cmd,"PRIVMSG:") == 0) { //PRINT("Response\n") char *paramPtr; size_t paramSz; mq_cmd_param(recv_cmd, ¶mPtr, ¶mSz); printf("ID:%d Recieved response [%s]%zu\n",mq_cmd_id(recv_cmd), paramPtr, paramSz); //wrong tble_cmd_resp *match_resp = malloc(sizeof(tble_cmd_resp)); memset(match_resp, 0, sizeof(tble_cmd_resp)); if (match_resp != NULL) { match_resp->qid = mq_cmd_id(recv_cmd); //for now any type is ok, make time consistant, for this usage //match_cmd->type = QCMD_NONE match_resp->resp = alloc_new_str_s(paramPtr, paramSz); //check if command match int cmd_id = tbl_qcmd_resp(qtbl, match_resp); if (cmd_id > 0) { tble_qcmd *cmd = tbl_qcmd_match_id(qtbl, cmd_id); if (cmd != NULL) { //PRINT("Found ID in qcmd table %d\n", cmd_id); char *resp_user = token_new_str(cmd->tok,0); char *dest_user = token_new_str(cmd->tok,2); char *sep = strchr(resp_user,'!'); resp_user[sep-resp_user] = 0x0; memset(cmd_buf, 0, TH_CONN_BUF_SZ); if (dest_user[0] == '#') { //response for chan PRIVMSG //prepare response int fret = snprintf(cmd_buf, TH_CONN_BUF_SZ,":%s PRIVMSG %s :%s \r\n", cfg->user, dest_user, match_resp->resp); PRINT("%s\n",cmd_buf); write(conn.conn_fd, cmd_buf, fret); } else { //response for user PRIVMSG //prepare response int fret = snprintf(cmd_buf, TH_CONN_BUF_SZ,":%s PRIVMSG %s :%s \r\n", dest_user, resp_user, match_resp->resp); PRINT("%s\n",cmd_buf); write(conn.conn_fd, cmd_buf, fret); } free(dest_user); dest_user = NULL; free(resp_user); resp_user = NULL; //remove command by id //for now just remove command without checkings its state tbl_qcmd_del_by_id(qtbl, cmd_id); //no error check } } tbl_cmd_resp_free(match_resp); } //lets memleak here } else { PRINT("Unknown command\n"); } } mq_cmd_free(recv_cmd); recv_cmd = NULL; } //should be added proper command state check and so on tbl_qcmd_mng_states(qtbl); // This should be runed regulary //recieve irc commands and pass to MQ, save command to table //fix this by allowing drain just chunks, not whole buffer memset(conn_buf, 0, TH_CONN_BUF_SZ); PNL(); cret = 0; //if((cret = read(conn,conn_buf, 256))>0) if ((cret = irc_read(&conn, conn_buf, 256))>0) { PRINT(">>READ %d<<\n", cret); irc_buf_puts(ib, conn_buf, cret); while (irc_buf_ready(ib) == 1) { irc_line = irc_buf_line(ib); if (irc_line) { printf("PARSE>>%s<<\n",irc_line); if (memcmp(irc_line,"PING",4)==0) { int fret; const int pong_buf_sz = 128; char pong_buf[pong_buf_sz]; snprintf(pong_buf,pong_buf_sz,"PONG %s\n",cfg->user); PRINT("%s",pong_buf) fret = write(conn.conn_fd,pong_buf,strlen(pong_buf)); printf("OUT>>PONG %d\n",fret); } itok = irc_parse(irc_line,strlen(irc_line)); if (itok != NULL) { int j; for (j=0;jtk_list[j]->token); } if (token_cmp(itok,1,"NOTICE")==1) { } //something is sended to cbot if (token_cmp(itok,1,"PRIVMSG") == 1) { int fret,fret2; //char send_back[128]; //PNL(); memset(cmd_buf,0,128); char *uname = token_new_str(itok,0); char *dest_name = token_new_str(itok,2); char *msg = token_new_str(itok,3); //could send without first symbol pal char *sep = strchr(uname,'!'); uname[sep-uname] = 0x0; PNL() //if (token_cmp(itok,3,":join") == 1) if (strncmp(":join",msg,5)==0) { //lets check if one more argument is there { char *chan_name=NULL; int iter=0; char *sep_chan = strchr(msg,' ')+1; //small hack //check if channel in allowed list, if yes then continue iter=0; while (cfg->channels[iter]!=NULL) { chan_name = cfg->channels[iter]; if ((strncmp(chan_name,sep_chan,strlen(chan_name))==0) && (strlen(sep_chan)==strlen(chan_name))) { //lets join channel memset(cmd_buf, 0, TH_CONN_BUF_SZ); int fret = snprintf(cmd_buf, TH_CONN_BUF_SZ, ":%s JOIN %s\r\n", dest_name, sep_chan); write(conn.conn_fd, cmd_buf, fret); break; } iter++; if (cfg->channels[iter]==NULL) { PRINT("Cant find this chan in da list\n"); } } } } else { //just send to test that thos commands works mate //fret2=snprintf(cmd_buf,128,":%s PRIVMSG %s PONGER\r\n",cfg->user,uname); //printf("FORMATED [%s]",cmd_buf); //fret = write(conn,cmd_buf,fret2); //create cmd table command and add to table qcmd = tbl_qcmd_cmd_c(); if (qcmd != NULL) { qcmd->cid = uniq_id(); qcmd->state = QCMD_NONE; qcmd->cmd = alloc_new_str("PRIVMSG"); qcmd->param = alloc_new_str(msg); qcmd->timestamp = time(NULL); tbl_qcmd_add_tok(qcmd, itok); //HOW? add commands that are in execution list, dont process all messages tbl_qcmd_add(qtbl, qcmd); tbl_qcmd_print_tbl(qtbl,TBL_PF_QCMD_ID |TBL_PF_QCMD_CID |TBL_PF_QCMD_STATE |TBL_PF_QCMD_CMD |TBL_PF_QCMD_PARAM |TBL_PF_QCMD_TID |TBL_PF_QCMD_TIDX); } //create command and send to mq PRINT("MSG %s\n",msg); mq_cmd *privcmd = mq_cmd_create(qcmd->cid,"PRIVMSG",strlen("PRIVMSG"),msg,strlen(msg)); char *bufPtr; bufPtr = mq_cmd_buf(privcmd); PRINT("%s\n",bufPtr); PRINT("write %d bytes\n",mq_ntf_write(mq, MQ_OUT, bufPtr, strlen(bufPtr))); mq_cmd_free(privcmd); //for safety make null, as no one should use it anymore qcmd = NULL; } free(msg); free(uname); //printf("OUT<>!READ %d<<\n", cret); //hope server where disconnected //close(conn); if (irc_reconnect(&conn)==-1) { while (-1 == irc_reconnect(&conn)) { printf("Reconnect in 10 sec\n"); sleep(10); } sleep(1); } //send some commands to irc to register nick snprintf(cmd_buf, TH_CONN_BUF_SZ, "USER %s 0 0 :%s\r\n", cfg->user, cfg->user); write(conn.conn_fd, cmd_buf, strlen(cmd_buf)); snprintf(cmd_buf, TH_CONN_BUF_SZ,"NICK %s \r\n", cfg->user); write(conn.conn_fd, cmd_buf, strlen(cmd_buf)); } } else { perror("No conn"); PRINT(">>READ %d <<\n",cret); PRINT("CRET %d CONN %d LIBC %d\n", cret, conn.err, conn.liberr); sleep(1); if (conn.err == ERR_SC_TIMEOUT) { printf("TIMEOUT\n"); if (irc_reconnect(&conn)==-1) { while (-1 == irc_reconnect(&conn)) { printf("Reconnect in 10 sec\n"); sleep(10); } sleep(1); } //send some commands to irc to register nick snprintf(cmd_buf, TH_CONN_BUF_SZ, "USER %s 0 0 :%s\r\n", cfg->user, cfg->user); write(conn.conn_fd, cmd_buf, strlen(cmd_buf)); snprintf(cmd_buf, TH_CONN_BUF_SZ,"NICK %s \r\n", cfg->user); write(conn.conn_fd, cmd_buf, strlen(cmd_buf)); } } //send command over queue } printf("End client\n"); PNL(); atomic_fetch_sub( &cfg->running,1); return 0; } #define EVENT_HND_STACK_SIZE (1024*1024) typedef struct event_handler_cfg { /* thread params*/ void *stack; mq_ntf_mdt *mq_listen; int mq_num; _Atomic int running; } event_handler_cfg; /* server_cfg struct as input */ #define EH_STATE_INIT 0 #define EH_STATE_START 1 #define EH_STATE_LISTEN_IN 2 #define EH_STATE_LISTEN_OUT 3 #define EH_STATE_SEND_IN 4 #define EH_STATE_SEND_OUT 5 #define EH_STATE_TERMINATION 6 #define EH_STATE_EXIT 7 /******************************************************************************* Event thead. Recieve all events from server thread and pass them for execution. Return results from execution to server thread. *******************************************************************************/ /* Thread to reacieve messages and return them back */ int th_event_manager(void *data) { event_handler_cfg *cfg = data; atomic_fetch_add(&cfg->running,1); mq_ntf_mdt *mq=NULL,*mq_cur=NULL; int i_mq=0; //to iter trought mqueue char *out_buf = NULL; int run; int mq_event; //read any command mq_cmd *recv_cmd = NULL; // for recieved cmd from mq struct mq_attr out_attr, *ptr_out_attr=&out_attr; //execution and command table generation //tble_exec *ecmd = NULL; tbl_exec *etbl = NULL; tble_qcmd *qcmd = NULL; tbl_qcmd *qtbl = NULL; //create execution table etbl = tbl_exec_list_c(30); if (etbl == NULL) { PERM(); } //load predefined/precompiled command list { single_cmd_def *single_cmd = confgi_cmd_list; while ((single_cmd!=NULL)&&(single_cmd->name!=NULL)) { tble_exec *ecmd = NULL; ecmd = tbl_exec_c(); if (ecmd == NULL) { PERM(); } ecmd->id = uniq_id(); ecmd->name = alloc_new_str("local-executor"); ecmd->cmd = alloc_new_str(single_cmd->name); ecmd->callback = single_cmd->callback; if (-1 == tbl_exec_add(etbl, ecmd)) { PERM(); } single_cmd++; } } tbl_exec_print_tbl(etbl, TBL_PF_EXEC_ALL); //create command table qtbl = tbl_qcmd_c(30);//well 30 should be ok right? //config mq printf("Start event thread\n"); mq = cfg->mq_listen; //get mq attributes if (mq_ntf_getattr(mq, MQ_OUT, &ptr_out_attr) == -1) { printf("Cant get attribute\n"); } out_buf = malloc(out_attr.mq_msgsize); //maybe its not null printf("Start event loop\n"); run = 1; while(run) { //check if there is some message and save it to buffer /* run += 1; mq_event = mq_ntf_select(mq, MQ_OUT); switch(mq_event) { case 0: //PRINT("EVENT 0\n"); break; case 1: PRINT("MQ_EVENT OUT\n"); memset(out_buf, 0, out_attr.mq_msgsize); if (mq_ntf_read(mq, MQ_OUT, out_buf, out_attr.mq_msgsize) == -1) { printf("Cant read output message\n"); } else { out_buf[out_attr.mq_msgsize-1] = 0x0; printf("Recieve %s\n", out_buf); } break; default: printf("Unknown event type\n"); } */ run += 1; for (i_mq=0;i_mqmq_num;i_mq++) { mq = cfg->mq_listen; mq_event = mq_ntf_select(&mq[i_mq], MQ_OUT); mq_cur = &mq[i_mq]; if (mq_event == 1) { PRINT("MQ_EVENT OUT\n"); memset(out_buf, 0, out_attr.mq_msgsize); //will fail if some of queue have different buffer size param if (mq_ntf_read(&mq[i_mq], MQ_OUT, out_buf, out_attr.mq_msgsize) == -1) { printf("Cant read output message\n"); } else { out_buf[out_attr.mq_msgsize-1] = 0x0; printf("Recieve %s\n", out_buf); } break; } } //mq_cur have currently queue where message comes from, need to make some buffer that gona pass/save this info //if QUIT then quit the thread if (mq_event == 1) { recv_cmd = mq_cmd_creates(out_buf, out_attr.mq_msgsize, 1); //PNL(); if (recv_cmd != NULL) { char *recv_cmd_param=NULL; size_t recv_cmd_param_sz=-1; //get command fields mq_cmd_param(recv_cmd, &recv_cmd_param, &recv_cmd_param_sz); if (mq_cmd_o_cmp_cmd(recv_cmd,"QUIT") == 0) { printf("QUIT recieved lets quit main loop\n"); break; } else if (mq_cmd_o_cmp_cmd(recv_cmd,"PRIVMSG") == 0) { //check if command callback excists PRINT("recv_cmd_param %s\n", recv_cmd_param); if (recv_cmd_param!=NULL) { if (tbl_exec_in_s(etbl, recv_cmd_param+1)>=0) //there is ':' in front of command, check if ptr not null otherwise die { void *ret_msg = NULL; PRINT("CMD [%s]\n",recv_cmd_param); if (tbl_exec_run(etbl, recv_cmd_param+1, &ret_msg)>=0) { if (ret_msg!=NULL) { //prepare response message and send it it back mq_cmd *privcmd = mq_cmd_create( mq_cmd_id(recv_cmd), "PRIVMSG", strlen("PRIVMSG"), ret_msg, strlen(ret_msg) ); char *cmdBuf = mq_cmd_buf(privcmd); mq_ntf_write(mq_cur, MQ_IN, cmdBuf, strlen(cmdBuf)); mq_cmd_free(privcmd); } } else { PRINT("Command execution error\n"); } } else { PRINT("Command not found\n"); } } } else if (mq_cmd_o_cmp_cmd(recv_cmd,"CMD2") == 0) { printf("Hey dude it works second time\n"); } else { printf("Unknow command\n"); char *cmdPtr; size_t cmdSz; mq_cmd_cmd(recv_cmd, &cmdPtr, &cmdSz); char *cmdNew = alloc_new_str_s(cmdPtr,cmdSz); printf("Unknown cmd [%s]\n", cmdNew); FREE(cmdNew); } } mq_cmd_free(recv_cmd); } //applay to recieved command executor //other command pass to cmd/execution matching table //pass to exec handler //get response if command is immidieate otherwise wait for response in next execution } free(out_buf); PNL(); printf("End event thread\n"); atomic_fetch_sub( &cfg->running,1); return 0; } /******************************************************************************* Main code entry code. Init threads. Give basic params to them. And then become as a watch dog. *******************************************************************************/ int main(int argc, char **argv) { int i; int cnt_servers,cnt_running; server_cfg *cfg_list; event_handler_cfg *evhnd_cfg; mq_ntf_mdt *mq_array; /*set atomic variables to init value*/ atomic_store(&_glbl_id, 0); /* Load configuration */ cnt_servers = SIZEOF_SERVER_LIST; for (i=0;itid = i; srvc->stack = malloc(STACK_SIZE); //NULL will brake everything ;) if (srvc->stack == NULL) { ERROR("BLow"); } srvc->user = isrvc->user; srvc->password = isrvc->password; srvc->server = isrvc->server; srvc->channels = isrvc->channels; srvc->port = isrvc->port; srvc->ssl = isrvc->ssl; //atomic_init( &srvc->running, 1); atomic_store(&srvc->running, 0); /* initalise posix mq */ if (0 != mq_ntf_open(&mq_array[i], i)) { printf("Couldnt open mq_ntf_open\n"); } srvc->mq = &mq_array[i]; //try to drain mq mq_ntf_drain(&mq_array[i]); PRINT("SERVER:%s PORT:%s USER:%s\n", srvc->server, srvc->port, srvc->user); /* clone new proc */ clone(th_start_client, srvc->stack+STACK_SIZE, CLONE_VM|CLONE_FILES, (void *)srvc); } /* event handler thread */ evhnd_cfg = malloc(sizeof(event_handler_cfg)); memset(evhnd_cfg, 0, sizeof(event_handler_cfg)); evhnd_cfg->stack = malloc(EVENT_HND_STACK_SIZE); atomic_store(&evhnd_cfg->running, 0); evhnd_cfg->mq_num = cnt_servers; evhnd_cfg->mq_listen = mq_array; clone(th_event_manager, evhnd_cfg->stack+EVENT_HND_STACK_SIZE, CLONE_VM|CLONE_FILES, (void *)evhnd_cfg); //PNL(); /* wait a sec while all threads will start running */ sleep(1); /* run until all threads are up */ cnt_running = 1; while(cnt_running != 0) { //printf("Count\n"); /*count how many proceses is running there*/ int val; cnt_running = 0; for (i=0;i