Пример #1
0
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);
}
Пример #2
0
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;
}
Пример #3
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;
}
Пример #4
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;
}
Пример #5
0
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;
}
Пример #6
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;
}
Пример #7
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;
}
Пример #8
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);
}
Пример #9
0
/*! \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;
}
Пример #10
0
/*! \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;
}
Пример #11
0
/*! \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;
}
Пример #12
0
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;
}