void tcp_worker_proc( int unix_sock, int max_fd ) { /* init reactor for TCP worker */ tcpmain_sock=unix_sock; /* init com. socket */ if ( init_worker_reactor( "TCP_worker", max_fd, RCT_PRIO_MAX)<0 ) { goto error; } /* start watching for the timer jobs */ if (reactor_add_reader( timer_fd_out, F_TIMER_JOB, RCT_PRIO_TIMER,NULL)<0){ LM_CRIT("failed to add timer pipe_out to reactor\n"); goto error; } /* add the unix socket */ if (reactor_add_reader( tcpmain_sock, F_TCPMAIN, RCT_PRIO_PROC, NULL)<0) { LM_CRIT("failed to add socket to the fd list\n"); goto error; } /* main loop */ reactor_main_loop( TCP_CHILD_SELECT_TIMEOUT, error, tcp_receive_timeout()); error: destroy_worker_reactor(); LM_CRIT("exiting..."); exit(-1); }
int async_fd_resume(int *fd, void *param) { async_ctx *ctx = (async_ctx *)param; int ret; async_status = ASYNC_DONE; /* assume default status as done */ /* call the resume function in order to read and handle data */ ret = ((async_resume_fd*)ctx->resume_f)( *fd, ctx->resume_param ); if (async_status==ASYNC_CONTINUE) { /* leave the fd into the reactor*/ return 0; } else if (async_status==ASYNC_CHANGE_FD) { if (ret<0) { LM_ERR("ASYNC_CHANGE_FD: given file descriptor shall be " "positive!\n"); return 0; } else if (ret>0 && ret==*fd) { /*trying to add the same fd; shall continue*/ LM_CRIT("You are trying to replace the old fd with the same fd!" "Will act as in ASYNC_CONTINUE!\n"); return 0; } /* remove the old fd from the reactor */ reactor_del_reader( *fd, -1, IO_FD_CLOSING); *fd=ret; /* insert the new fd inside the reactor */ if (reactor_add_reader(*fd,F_FD_ASYNC,RCT_PRIO_ASYNC,(void*)ctx)<0 ) { LM_ERR("failed to add async FD to reactor -> act in sync mode\n"); do { async_status = ASYNC_DONE; ret = ((async_resume_fd*)ctx->resume_f)(*fd,ctx->resume_param); if (async_status == ASYNC_CHANGE_FD) *fd=ret; } while(async_status==ASYNC_CONTINUE||async_status==ASYNC_CHANGE_FD); goto done; } else { /* successfully changed fd */ return 0; } } /* remove from reactor, we are done */ reactor_del_reader( *fd, -1, IO_FD_CLOSING); done: if (async_status == ASYNC_DONE_CLOSE_FD) close(*fd); return 0; }
int register_async_fd(int fd, async_resume_fd *f, void *resume_param) { async_ctx *ctx = NULL; if ( (ctx=shm_malloc(sizeof(async_ctx)))==NULL) { LM_ERR("failed to allocate new async_ctx\n"); return -1; } ctx->resume_f = f; ctx->resume_param = resume_param; /* place the FD + resume function (as param) into reactor */ if (reactor_add_reader( fd, F_FD_ASYNC, RCT_PRIO_ASYNC, (void*)ctx)<0 ) { LM_ERR("failed to add async FD to reactor\n"); shm_free(ctx); return -1; } return 0; }
int async_script_launch(struct sip_msg *msg, struct action* a, int report_route) { struct sip_msg req; async_launch_ctx *ctx; int fd; /* run the function (the action) and get back from it the FD, * resume function and param */ if ( a->type!=AMODULE_T || a->elem[0].type!=ACMD_ST || a->elem[0].u.data==NULL ) { LM_CRIT("BUG - invalid action for async I/O - it must be" " a MODULE_T ACMD_ST \n"); return -1; } if ( (ctx=shm_malloc(sizeof(async_launch_ctx)))==NULL) { LM_ERR("failed to allocate new ctx, forcing sync mode\n"); return -1; } async_status = ASYNC_NO_IO; /*assume defauly status "no IO done" */ return_code = ((acmd_export_t*)(a->elem[0].u.data))->function(msg, (async_ctx*)ctx, (char*)a->elem[1].u.data, (char*)a->elem[2].u.data, (char*)a->elem[3].u.data, (char*)a->elem[4].u.data, (char*)a->elem[5].u.data, (char*)a->elem[6].u.data ); /* what to do now ? */ if (async_status>=0) { /* async I/O was successfully launched */ fd = async_status; } else if (async_status==ASYNC_NO_FD) { /* async was successfully launched but without a FD resume * in this case, we need to push the async ctx back to the * function, so it can trigger the resume later, by itself */ } else if (async_status==ASYNC_NO_IO) { /* no IO, so simply continue with the script */ shm_free(ctx); return 1; } else if (async_status==ASYNC_SYNC) { /* IO already done in SYNC'ed way */ goto report; } else if (async_status==ASYNC_CHANGE_FD) { LM_ERR("Incorrect ASYNC_CHANGE_FD status usage!" "You should use this status only from the" "resume function in case something went wrong" "and you have other alternatives!\n"); shm_free(ctx); return -1; } else { /* generic error, go for resume route, report it to script */ shm_free(ctx); return -1; } /* ctx is to be used from this point further */ ctx->report_route = report_route; if (async_status!=ASYNC_NO_FD) { LM_DBG("placing launch job into reactor\n"); /* place the FD + resume function (as param) into reactor */ if (reactor_add_reader(fd,F_LAUNCH_ASYNC,RCT_PRIO_ASYNC,(void*)ctx)<0){ LM_ERR("failed to add async FD to reactor -> act in sync mode\n"); goto sync; } } /* done, return to the script */ return 1; sync: /* run the resume function */ LM_DBG("running launch job in sync mode\n"); do { async_status = ASYNC_DONE; return_code = ((async_resume_module*)(ctx->async.resume_f)) ( fd, msg, ctx->async.resume_param ); if (async_status == ASYNC_CHANGE_FD) fd = return_code; } while(async_status==ASYNC_CONTINUE||async_status==ASYNC_CHANGE_FD); /* the IO completed, so report now */ report: shm_free(ctx); if (report_route==-1) return 1; /* run the report route inline */ init_dummy_request( req ); set_route_type( REQUEST_ROUTE ); run_top_route( rlist[report_route].a, &req); /* remove all added AVP */ reset_avps( ); return 1; }
int async_launch_resume(int *fd, void *param) { struct sip_msg req; async_launch_ctx *ctx = (async_launch_ctx *)param; LM_DBG("resume for a launch job\n"); init_dummy_request( req ); async_status = ASYNC_DONE; /* assume default status as done */ /* call the resume function in order to read and handle data */ return_code = ((async_resume_module*)(ctx->async.resume_f)) ( *fd, &req, ctx->async.resume_param ); if (async_status==ASYNC_CONTINUE) { /* do not run the report route, leave the fd into the reactor*/ goto restore; } else if (async_status==ASYNC_DONE_NO_IO) { /* don't do any change on the fd, since the module handled everything*/ goto run_route; } else if (async_status==ASYNC_CHANGE_FD) { if (return_code<0) { LM_ERR("ASYNC_CHANGE_FD: given file descriptor must be " "positive!\n"); goto restore; } else if (return_code>0 && return_code==*fd) { /*trying to add the same fd; shall continue*/ LM_CRIT("You are trying to replace the old fd with the same fd!" "Will act as in ASYNC_CONTINUE!\n"); goto restore; } /* remove the old fd from the reactor */ reactor_del_reader( *fd, -1, IO_FD_CLOSING); *fd=return_code; /* insert the new fd inside the reactor */ if (reactor_add_reader( *fd, F_LAUNCH_ASYNC, RCT_PRIO_ASYNC, (void*)ctx)<0 ) { LM_ERR("failed to add async FD to reactor -> act in sync mode\n"); do { async_status = ASYNC_DONE; return_code = ((async_resume_module*)(ctx->async.resume_f)) ( *fd, &req, ctx->async.resume_param ); if (async_status == ASYNC_CHANGE_FD) *fd=return_code; } while(async_status==ASYNC_CONTINUE||async_status==ASYNC_CHANGE_FD); goto run_route; } else { /* successfully changed fd */ goto restore; } } /* remove from reactor, we are done */ reactor_del_reader( *fd, -1, IO_FD_CLOSING); run_route: if (async_status == ASYNC_DONE_CLOSE_FD) close(*fd); if (ctx->report_route!=-1) { LM_DBG("runinng report route for a launch job\n"); set_route_type( REQUEST_ROUTE ); run_top_route( rlist[ctx->report_route].a, &req); /* remove all added AVP */ reset_avps( ); } /* no need for the context anymore */ shm_free(ctx); LM_DBG("done with a launch job\n"); restore: /* clean whatever extra structures were added by script functions */ free_sip_msg(&req); return 0; }
/* function triggered from reactor in order to continue the processing */ int t_resume_async(int *fd, void *param) { static struct sip_msg faked_req; static struct ua_client uac; async_ctx *ctx = (async_ctx *)param; struct cell *backup_t; struct cell *backup_cancelled_t; struct cell *backup_e2eack_t; struct usr_avp **backup_list; struct socket_info* backup_si; struct cell *t= ctx->t; int route; LM_DBG("resuming on fd %d, transaction %p \n",*fd, t); if (current_processing_ctx) { LM_CRIT("BUG - a context already set!\n"); abort(); } /* prepare for resume route */ uac.br_flags = getb0flags( t->uas.request ) ; uac.uri = *GET_RURI( t->uas.request ); if (!fake_req( &faked_req /* the fake msg to be built*/, t->uas.request, /* the template msg saved in transaction */ &t->uas, /*the UAS side of the transaction*/ &uac, /* the fake UAC */ 1 /* copy dst_uri too */) ) { LM_ERR("fake_req failed\n"); return 0; } /* enviroment setting */ current_processing_ctx = ctx->msg_ctx; backup_t = get_t(); backup_e2eack_t = get_e2eack_t(); backup_cancelled_t = get_cancelled_t(); /* fake transaction */ set_t( t ); set_cancelled_t(ctx->cancelled_t); set_e2eack_t(ctx->e2eack_t); reset_kr(); set_kr(ctx->kr); /* make available the avp list from transaction */ backup_list = set_avp_list( &t->user_avps ); /* set default send address to the saved value */ backup_si = bind_address; bind_address = t->uac[0].request.dst.send_sock; async_status = ASYNC_DONE; /* assume default status as done */ /* call the resume function in order to read and handle data */ return_code = ctx->resume_f( *fd, &faked_req, ctx->resume_param ); if (async_status==ASYNC_CONTINUE) { /* do not run the resume route */ goto restore; } else if (async_status==ASYNC_CHANGE_FD) { if (return_code<0) { LM_ERR("ASYNC_CHANGE_FD: given file descriptor shall be positive!\n"); goto restore; } else if (return_code > 0 && return_code == *fd) { /*trying to add the same fd; shall continue*/ LM_CRIT("You are trying to replace the old fd with the same fd!" "Will act as in ASYNC_CONTINUE!\n"); goto restore; } /* remove the old fd from the reactor */ reactor_del_reader( *fd, -1, IO_FD_CLOSING); *fd=return_code; /* insert the new fd inside the reactor */ if (reactor_add_reader( *fd, F_SCRIPT_ASYNC, RCT_PRIO_ASYNC, (void*)ctx)<0 ) { LM_ERR("failed to add async FD to reactor -> act in sync mode\n"); do { return_code = ctx->resume_f( *fd, &faked_req, ctx->resume_param ); if (async_status == ASYNC_CHANGE_FD) *fd=return_code; } while(async_status==ASYNC_CONTINUE||async_status==ASYNC_CHANGE_FD); goto route; } /* changed fd; now restore old state */ goto restore; } /* remove from reactor, we are done */ reactor_del_reader( *fd, -1, IO_FD_CLOSING); route: if (async_status == ASYNC_DONE_CLOSE_FD) close(*fd); /* run the resume_route (some type as the original one) */ swap_route_type(route, ctx->route_type); run_resume_route( ctx->resume_route, &faked_req); set_route_type(route); /* no need for the context anymore */ shm_free(ctx); /* free also the processing ctx if still set * NOTE: it may become null if inside the run_resume_route * another async jump was made (and context attached again * to transaction) */ if (current_processing_ctx) { context_destroy(CONTEXT_GLOBAL, current_processing_ctx); pkg_free(current_processing_ctx); } restore: /* restore original environment */ set_t(backup_t); set_cancelled_t(backup_cancelled_t); set_e2eack_t(backup_e2eack_t); /* restore original avp list */ set_avp_list( backup_list ); bind_address = backup_si; free_faked_req( &faked_req, t); current_processing_ctx = NULL; return 0; }
int t_handle_async(struct sip_msg *msg, struct action* a , int resume_route) { async_ctx *ctx = NULL; async_resume_module *ctx_f; void *ctx_p; struct cell *t; int r; int fd; /* create transaction and save everything into transaction */ t=get_t(); if ( t==0 || t==T_UNDEFINED ) { /* create transaction */ r = t_newtran( msg , 1 /*full uas clone*/ ); if (r==0) { /* retransmission -> no follow up; we return a negative * code to indicate do_action that the top route is * is completed (there no resume route to follow) */ return -1; } else if (r<0) { LM_ERR("could not create a new transaction\n"); goto failure; } t=get_t(); } else { /* update the cloned UAS (from transaction) * with data from current msg */ if (t->uas.request) update_cloned_msg_from_msg( t->uas.request, msg); } /* run the function (the action) and get back from it the FD, * resume function and param */ if ( a->type!=AMODULE_T || a->elem[0].type!=ACMD_ST || a->elem[0].u.data==NULL ) { LM_CRIT("BUG - invalid action for async I/O - it must be" " a MODULE_T ACMD_ST \n"); goto failure; } async_status = ASYNC_NO_IO; /*assume defauly status "no IO done" */ return_code = ((acmd_export_t*)(a->elem[0].u.data))->function(msg, &ctx_f, &ctx_p, (char*)a->elem[1].u.data, (char*)a->elem[2].u.data, (char*)a->elem[3].u.data, (char*)a->elem[4].u.data, (char*)a->elem[5].u.data, (char*)a->elem[6].u.data ); /* what to do now ? */ if (async_status>=0) { /* async I/O was successfully launched */ fd = async_status; if (msg->REQ_METHOD==METHOD_ACK || /* ^^^ end2end ACK, there is no actual transaction here */ t->uas.request==NULL /* ^^^ local requests do not support async in local route */ ) { goto sync; } } else if (async_status==ASYNC_NO_IO) { /* no IO, so simply go for resume route */ goto resume; } else if (async_status==ASYNC_SYNC) { /* IO already done in SYNC'ed way */ goto resume; } else if (async_status==ASYNC_CHANGE_FD) { LM_ERR("Incorrect ASYNC_CHANGE_FD status usage!" "You should use this status only from the" "resume function in case something went wrong" "and you have other alternatives!\n"); /*FIXME should we go to resume or exit?it's quite an invalid scenario */ goto resume; } else { /* generic error, go for resume route */ goto resume; } /* do we have a reactor in this process, to handle this asyn I/O ? */ if ( 0/*reactor_exists()*/ ) { /* no reactor, so we directly call the resume function which will block waiting for data */ goto sync; } if ( (ctx=shm_malloc(sizeof(async_ctx)))==NULL) { LM_ERR("failed to allocate new ctx\n"); goto sync; } ctx->resume_f = ctx_f; ctx->resume_param = ctx_p; ctx->resume_route = resume_route; ctx->route_type = route_type; ctx->msg_ctx = current_processing_ctx; ctx->t = t; ctx->kr = get_kr(); ctx->cancelled_t = get_cancelled_t(); ctx->e2eack_t = get_e2eack_t(); current_processing_ctx = NULL; set_t(T_UNDEFINED); reset_cancelled_t(); reset_e2eack_t(); /* place the FD + resume function (as param) into reactor */ if (reactor_add_reader( fd, F_SCRIPT_ASYNC, RCT_PRIO_ASYNC, (void*)ctx)<0 ) { LM_ERR("failed to add async FD to reactor -> act in sync mode\n"); goto sync; } /* done, break the script */ return 0; sync: if (ctx) { /* * the context was moved in reactor, but the reactor could not * fullfil the request - we have to restore the environment -- razvanc */ current_processing_ctx = ctx->msg_ctx; set_t(t); set_cancelled_t(ctx->cancelled_t); set_e2eack_t(ctx->e2eack_t); shm_free(ctx); } /* run the resume function */ do { return_code = ctx_f( fd, msg, ctx_p ); if (async_status == ASYNC_CHANGE_FD) fd = return_code; } while(async_status==ASYNC_CONTINUE||async_status==ASYNC_CHANGE_FD); /* run the resume route in sync mode */ run_resume_route( resume_route, msg); /* break original script */ return 0; failure: /* execute here the resume route with failure indication */ return_code = -1; resume: /* run the resume route */ run_resume_route( resume_route, msg); /* the triggering route is terminated and whole script ended */ return 0; }
static void tcp_main_server(void) { static unsigned int last_sec = 0; int flags; struct socket_info* si; int n; /* we run in a separate, dedicated process, with its own reactor * (reactors are per process) */ if (init_worker_reactor("TCP_main", RCT_PRIO_MAX)<0) goto error; /* now start watching all the fds*/ /* add all the sockets we listens on for connections */ for( n=PROTO_FIRST ; n<PROTO_LAST ; n++ ) if ( is_tcp_based_proto(n) ) for( si=protos[n].listeners ; si ; si=si->next ) { if ( (si->socket!=-1) && reactor_add_reader( si->socket, F_TCP_LISTENER, RCT_PRIO_NET, si)<0 ) { LM_ERR("failed to add listen socket to reactor\n"); goto error; } } /* add all the unix sockets used for communcation with other opensips * processes (get fd, new connection a.s.o) */ for (n=1; n<counted_processes; n++) { /* skip myslef (as process) and -1 socks (disabled) (we can't have 0, we never close it!) */ if (n!=process_no && pt[n].unix_sock>0) if (reactor_add_reader( pt[n].unix_sock, F_TCP_WORKER, RCT_PRIO_PROC, &pt[n])<0){ LM_ERR("failed to add process %d (%s) unix socket " "to the fd list\n", n, pt[n].desc); goto error; } } /* add all the unix sokets used for communication with the tcp childs */ for (n=0; n<tcp_children_no; n++) { /*we can't have 0, we never close it!*/ if (tcp_children[n].unix_sock>0) { /* make socket non-blocking */ flags=fcntl(tcp_children[n].unix_sock, F_GETFL); if (flags==-1){ LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno)); goto error; } if (fcntl(tcp_children[n].unix_sock,F_SETFL,flags|O_NONBLOCK)==-1){ LM_ERR("set non-blocking failed: (%d) %s\n", errno, strerror(errno)); goto error; } /* add socket for listening */ if (reactor_add_reader( tcp_children[n].unix_sock, F_TCP_TCPWORKER, RCT_PRIO_PROC, &tcp_children[n])<0) { LM_ERR("failed to add tcp child %d unix socket to " "the fd list\n", n); goto error; } } } /* main loop (requires "handle_io()" implementation) */ reactor_main_loop( TCP_MAIN_SELECT_TIMEOUT, error, tcpconn_lifetime(last_sec, 0) ); error: destroy_worker_reactor(); LM_CRIT("exiting..."); exit(-1); }
/*! \brief handles io from a "generic" ser process (get fd or new_fd from a tcp_send) * * \param p - pointer in the ser processes array (pt[]), to the entry for * which an io event was detected * \param fd_i - fd index in the fd_array (useful for optimizing * io_watch_deletes) * \return handle_* return convention: * - -1 on error reading from the fd, * - 0 on EAGAIN or when no more io events are queued * (receive buffer empty), * - >0 on successfull reads from the fd (the receive buffer might * be non-empty). */ inline static int handle_worker(struct process_table* p, int fd_i) { struct tcp_connection* tcpconn; long response[2]; int cmd; int bytes; int ret; int fd; ret=-1; if (p->unix_sock<=0){ /* (we can't have a fd==0, 0 is never closed )*/ LM_CRIT("fd %d for %d (pid %d)\n", p->unix_sock, (int)(p-&pt[0]), p->pid); goto error; } /* get all bytes and the fd (if transmitted) * (this is a SOCK_STREAM so read is not atomic) */ bytes=receive_fd(p->unix_sock, response, sizeof(response), &fd, MSG_DONTWAIT); if (bytes<(int)sizeof(response)){ /* too few bytes read */ if (bytes==0){ /* EOF -> bad, child has died */ LM_DBG("dead child %d, pid %d" " (shutting down?)\n", (int)(p-&pt[0]), p->pid); /* don't listen on it any more */ reactor_del_reader( p->unix_sock, fd_i, 0/*flags*/); goto error; /* child dead => no further io events from it */ }else if (bytes<0){ /* EAGAIN is ok if we try to empty the buffer * e.g: SIGIO_RT overflow mode or EPOLL ET */ if ((errno!=EAGAIN) && (errno!=EWOULDBLOCK)){ LM_CRIT("read from child %d (pid %d): %s [%d]\n", (int)(p-&pt[0]), p->pid, strerror(errno), errno); ret=-1; }else{ ret=0; } /* try to ignore ? */ goto end; }else{ /* should never happen */ LM_CRIT("too few bytes received (%d)\n", bytes ); ret=0; /* something was read so there is no error; otoh if receive_fd returned less then requested => the receive buffer is empty => no more io queued on this fd */ goto end; } } ret=1; /* something was received, there might be more queued */ LM_DBG("read response= %lx, %ld, fd %d from %d (%d)\n", response[0], response[1], fd, (int)(p-&pt[0]), p->pid); cmd=response[1]; tcpconn=(struct tcp_connection*)response[0]; if (tcpconn==0){ LM_CRIT("null tcpconn pointer received from child %d (pid %d)" "%lx, %lx\n", (int)(p-&pt[0]), p->pid, response[0], response[1]) ; goto end; } switch(cmd){ case CONN_ERROR: if (!(tcpconn->flags & F_CONN_REMOVED) && (tcpconn->s!=-1)){ reactor_del_all( tcpconn->s, -1, IO_FD_CLOSING); tcpconn->flags|=F_CONN_REMOVED; } tcpconn_destroy(tcpconn); /* will close also the fd */ break; case CONN_GET_FD: /* send the requested FD */ /* WARNING: take care of setting refcnt properly to * avoid race condition */ if (send_fd(p->unix_sock, &tcpconn, sizeof(tcpconn), tcpconn->s)<=0){ LM_ERR("send_fd failed\n"); } break; case CONN_NEW: /* update the fd in the requested tcpconn*/ /* WARNING: take care of setting refcnt properly to * avoid race condition */ if (fd==-1){ LM_CRIT(" cmd CONN_NEW: no fd received\n"); break; } tcpconn->s=fd; /* add tcpconn to the list*/ tcpconn_add(tcpconn); reactor_add_reader( tcpconn->s, F_TCPCONN, RCT_PRIO_NET, tcpconn); tcpconn->flags&=~F_CONN_REMOVED; break; case ASYNC_CONNECT: /* connection is not yet linked to hash = not yet * available to the outside world */ if (fd==-1){ LM_CRIT(" cmd CONN_NEW: no fd received\n"); break; } tcpconn->s=fd; /* add tcpconn to the list*/ tcpconn_add(tcpconn); /* FIXME - now we have lifetime==default_lifetime - should we * set a shorter one when waiting for a connect ??? */ /* only maintain the socket in the IO_WATCH_WRITE watcher * while we have stuff to write - otherwise we're going to get * useless events */ reactor_add_writer( tcpconn->s, F_TCPCONN, RCT_PRIO_NET, tcpconn); tcpconn->flags&=~F_CONN_REMOVED; break; case ASYNC_WRITE: if (tcpconn->state==S_CONN_BAD){ tcpconn->lifetime=0; break; } /* must be after the de-ref*/ reactor_add_writer( tcpconn->s, F_TCPCONN, RCT_PRIO_NET, tcpconn); tcpconn->flags&=~F_CONN_REMOVED; break; default: LM_CRIT("unknown cmd %d\n", cmd); } end: return ret; error: return -1; }
/*! \brief handles io from a tcp worker process * \param tcp_c - pointer in the tcp_children array, to the entry for * which an io event was detected * \param fd_i - fd index in the fd_array (useful for optimizing * io_watch_deletes) * \return handle_* return convention: -1 on error, 0 on EAGAIN (no more * io events queued), >0 on success. success/error refer only to * the reads from the fd. */ inline static int handle_tcp_worker(struct tcp_child* tcp_c, int fd_i) { struct tcp_connection* tcpconn; long response[2]; int cmd; int bytes; if (tcp_c->unix_sock<=0){ /* (we can't have a fd==0, 0 is never closed )*/ LM_CRIT("fd %d for %d (pid %d, ser no %d)\n", tcp_c->unix_sock, (int)(tcp_c-&tcp_children[0]), tcp_c->pid, tcp_c->proc_no); goto error; } /* read until sizeof(response) * (this is a SOCK_STREAM so read is not atomic) */ bytes=recv_all(tcp_c->unix_sock, response, sizeof(response), MSG_DONTWAIT); if (bytes<(int)sizeof(response)){ if (bytes==0){ /* EOF -> bad, child has died */ LM_DBG("dead tcp worker %d (pid %d, no %d)" " (shutting down?)\n", (int)(tcp_c-&tcp_children[0]), tcp_c->pid, tcp_c->proc_no ); /* don't listen on it any more */ reactor_del_reader( tcp_c->unix_sock, fd_i, 0/*flags*/); /* eof. so no more io here, it's ok to return error */ goto error; }else if (bytes<0){ /* EAGAIN is ok if we try to empty the buffer * e.g.: SIGIO_RT overflow mode or EPOLL ET */ if ((errno!=EAGAIN) && (errno!=EWOULDBLOCK)){ LM_CRIT("read from tcp worker %ld (pid %d, no %d) %s [%d]\n", (long)(tcp_c-&tcp_children[0]), tcp_c->pid, tcp_c->proc_no, strerror(errno), errno ); }else{ bytes=0; } /* try to ignore ? */ goto end; }else{ /* should never happen */ LM_CRIT("too few bytes received (%d)\n", bytes ); bytes=0; /* something was read so there is no error; otoh if receive_fd returned less then requested => the receive buffer is empty => no more io queued on this fd */ goto end; } } LM_DBG("reader response= %lx, %ld from %d \n", response[0], response[1], (int)(tcp_c-&tcp_children[0])); cmd=response[1]; tcpconn=(struct tcp_connection*)response[0]; if (tcpconn==0){ /* should never happen */ LM_CRIT("null tcpconn pointer received from tcp child %d (pid %d):" "%lx, %lx\n", (int)(tcp_c-&tcp_children[0]), tcp_c->pid, response[0], response[1]) ; goto end; } switch(cmd){ case CONN_RELEASE: tcp_c->busy--; if (tcpconn->state==S_CONN_BAD){ tcpconn_destroy(tcpconn); break; } tcpconn_put(tcpconn); /* must be after the de-ref*/ reactor_add_reader( tcpconn->s, F_TCPCONN, RCT_PRIO_NET, tcpconn); tcpconn->flags&=~F_CONN_REMOVED; break; case ASYNC_WRITE: tcp_c->busy--; if (tcpconn->state==S_CONN_BAD){ tcpconn_destroy(tcpconn); break; } tcpconn_put(tcpconn); /* must be after the de-ref*/ reactor_add_writer( tcpconn->s, F_TCPCONN, RCT_PRIO_NET, tcpconn); tcpconn->flags&=~F_CONN_REMOVED; break; case CONN_ERROR: case CONN_DESTROY: case CONN_EOF: /* WARNING: this will auto-dec. refcnt! */ tcp_c->busy--; /* main doesn't listen on it => we don't have to delete it if (tcpconn->s!=-1) io_watch_del(&io_h, tcpconn->s, -1, IO_FD_CLOSING); */ tcpconn_destroy(tcpconn); /* closes also the fd */ break; default: LM_CRIT("unknown cmd %d from tcp reader %d\n", cmd, (int)(tcp_c-&tcp_children[0])); } end: return bytes; error: return -1; }
/*! \brief * handle io routine, based on the fd_map type * (it will be called from reactor_main_loop ) * params: fm - pointer to a fd hash entry * idx - index in the fd_array (or -1 if not known) * return: -1 on error, or when we are not interested any more on reads * from this fd (e.g.: we are closing it ) * 0 on EAGAIN or when by some other way it is known that no more * io events are queued on the fd (the receive buffer is empty). * Usefull to detect when there are no more io events queued for * sigio_rt, epoll_et, kqueue. * >0 on successfull read from the fd (when there might be more io * queued -- the receive buffer might still be non-empty) */ inline static int handle_io(struct fd_map* fm, int idx,int event_type) { int ret=0; int n; struct tcp_connection* con; int s,rw; long resp; long response[2]; switch(fm->type){ case F_TIMER_JOB: handle_timer_job(); break; case F_SCRIPT_ASYNC: async_resume_f( fm->fd, fm->data); return 0; case F_TCPMAIN: again: ret=n=receive_fd(fm->fd, response, sizeof(response), &s, 0); if (n<0){ if (errno == EWOULDBLOCK || errno == EAGAIN){ ret=0; break; }else if (errno == EINTR) goto again; else{ LM_CRIT("read_fd: %s \n", strerror(errno)); abort(); /* big error*/ } } if (n==0){ LM_WARN("0 bytes read\n"); break; } con = (struct tcp_connection *)response[0]; rw = (int)response[1]; if (con==0){ LM_CRIT("null pointer\n"); break; } if (s==-1) { LM_BUG("read_fd:no fd read\n"); /* FIXME? */ return -1; } if (con==tcp_conn_lst){ LM_CRIT("duplicate" " connection received: %p, id %d, fd %d, refcnt %d" " state %d (n=%d)\n", con, con->id, con->fd, con->refcnt, con->state, n); tcpconn_release(con, CONN_ERROR,0); break; /* try to recover */ } LM_DBG("We have received conn %p with rw %d on fd %d\n",con,rw,s); if (rw & IO_WATCH_READ) { /* 0 attempts so far for this SIP MSG */ con->msg_attempts = 0; /* must be before reactor_add, as the add might catch some * already existing events => might call handle_io and * handle_io might decide to del. the new connection => * must be in the list */ tcpconn_listadd(tcp_conn_lst, con, c_next, c_prev); con->timeout = con->lifetime; if (reactor_add_reader( s, F_TCPCONN, RCT_PRIO_NET, con )<0) { LM_CRIT("failed to add new socket to the fd list\n"); tcpconn_listrm(tcp_conn_lst, con, c_next, c_prev); goto con_error; } /* mark that the connection is currently in our process future writes to this con won't have to acquire FD */ con->proc_id = process_no; /* save FD which is valid in context of this TCP worker */ con->fd=s; } else if (rw & IO_WATCH_WRITE) { LM_DBG("Received con for async write %p ref = %d\n",con,con->refcnt); lock_get(&con->write_lock); resp = protos[con->type].net.write( (void*)con, s ); lock_release(&con->write_lock); if (resp<0) { ret=-1; /* some error occured */ con->state=S_CONN_BAD; tcpconn_release(con, CONN_ERROR,1); break; } else if (resp==1) { tcpconn_release(con, ASYNC_WRITE,1); } else { tcpconn_release(con, CONN_RELEASE,1); } ret = 0; /* we always close the socket received for writing */ close(s); } break; case F_TCPCONN: if (event_type & IO_WATCH_READ) { con=(struct tcp_connection*)fm->data; resp = protos[con->type].net.read( (void*)con, &ret ); if (resp<0) { ret=-1; /* some error occured */ con->state=S_CONN_BAD; reactor_del_all( con->fd, idx, IO_FD_CLOSING ); tcpconn_listrm(tcp_conn_lst, con, c_next, c_prev); con->proc_id = -1; if (con->fd!=-1) { close(con->fd); con->fd = -1; } tcpconn_release(con, CONN_ERROR,0); } else if (con->state==S_CONN_EOF) { reactor_del_all( con->fd, idx, IO_FD_CLOSING ); tcpconn_listrm(tcp_conn_lst, con, c_next, c_prev); con->proc_id = -1; if (con->fd!=-1) { close(con->fd); con->fd = -1; } tcpconn_release(con, CONN_EOF,0); } else { //tcpconn_release(con, CONN_RELEASE); /* keep the connection for now */ break; } } break; case F_NONE: LM_CRIT("empty fd map %p: " "{%d, %d, %p}\n", fm, fm->fd, fm->type, fm->data); goto error; default: LM_CRIT("uknown fd type %d\n", fm->type); goto error; } return ret; con_error: con->state=S_CONN_BAD; tcpconn_release(con, CONN_ERROR,0); return ret; error: return -1; }
int t_handle_async(struct sip_msg *msg, struct action* a , int resume_route) { async_ctx *ctx; async_resume_module *ctx_f; void *ctx_p; struct cell *t; int r; int fd; /* create transaction and save everything into transaction */ t=get_t(); if ( t==0 || t==T_UNDEFINED ) { /* create transaction */ r = t_newtran( msg , 1 /*full uas clone*/ ); if (r==0) { /* retransmission -> break the script, no follow up */ return 0; } else if (r<0) { LM_ERR("could not create a new transaction\n"); goto failure; } t=get_t(); } else { /* update the cloned UAS (from transaction) * with data from current msg */ update_cloned_msg_from_msg( t->uas.request, msg); } /* run the function (the action) and get back from it the FD, * resume function and param */ if ( a->type!=AMODULE_T || a->elem[0].type!=ACMD_ST || a->elem[0].u.data==NULL ) { LM_CRIT("BUG - invalid action for async I/O - it must be" " a MODULE_T ACMD_ST \n"); goto failure; } async_status = ASYNC_NO_IO; /*assume defauly status "no IO done" */ return_code = ((acmd_export_t*)(a->elem[0].u.data))->function(msg, &ctx_f, &ctx_p, (char*)a->elem[1].u.data, (char*)a->elem[2].u.data, (char*)a->elem[3].u.data, (char*)a->elem[4].u.data, (char*)a->elem[5].u.data, (char*)a->elem[6].u.data ); /* what to do now ? */ if (async_status>=0) { /* async I/O was succesfully launched */ fd = async_status; } else if (async_status==ASYNC_NO_IO) { /* no IO, so simply go for resume route */ goto resume; } else if (async_status==ASYNC_SYNC) { /* IO already done in SYNC'ed way */ goto resume; } else { /* generic error, go for resume route */ goto resume; } /* do we have a reactor in this process, to handle this asyn I/O ? */ if ( 0/*reactor_exists()*/ ) { /* no reactor, so we directly call the resume function which will block waiting for data */ goto sync; } if ( (ctx=shm_malloc(sizeof(async_ctx)))==NULL) { LM_ERR("failed to allocate new ctx\n"); goto sync; } ctx->resume_f = ctx_f; ctx->resume_param = ctx_p; ctx->resume_route = resume_route; ctx->route_type = route_type; ctx->msg_ctx = current_processing_ctx; ctx->t = t; ctx->kr = get_kr(); current_processing_ctx = NULL; set_t(T_UNDEFINED); /* place the FD + resume function (as param) into reactor */ if (reactor_add_reader( fd, F_SCRIPT_ASYNC, RCT_PRIO_ASYNC, (void*)ctx)<0 ) { LM_ERR("failed to add async FD to reactor -> act in sync mode\n"); shm_free(ctx); goto sync; } /* done, break the script */ return 0; sync: /* run the resume function */ do { return_code = ctx_f( fd, msg, ctx_p ); } while(async_status!=ASYNC_CONTINUE); /* run the resume route in sync mode */ run_resume_route( resume_route, msg); /* break original script */ return 0; failure: /* execute here the resume route with failure indication */ return_code = -1; resume: /* run the resume route */ run_resume_route( resume_route, msg); /* the triggering route is terminated and whole script ended */ return 0; }