Exemplo n.º 1
0
static void* onion_poller_poll_start (void* data)
{
  onion_poller* poller = data;
  pthread_mutex_lock (&count_mtx);
  poller_counter++;
#ifdef __DEBUG__
#ifdef __USE_GNU 
  long cnt = poller_counter;
#endif /*__USE_GNU */
#endif /*__DEBUG__*/
  pthread_mutex_unlock (&count_mtx);
#ifdef __DEBUG__
#ifdef __USE_GNU 
  /* GNU systems such as Linux with GNU glibc have the non-standard pthread_setname_np */
  {
    char thrname[16];
    memset (thrname, 0, sizeof(thrname));
    snprintf (thrname, sizeof(thrname), "onpoller%ld", cnt);
    pthread_setname_np (pthread_self(), thrname);
  }
#endif /*__USE_GNU */
#endif /*__DEBUG__*/
  onion_poller_poll(poller);
  return NULL;
}
Exemplo n.º 2
0
/**
 * @short Performs the listening with the given mode
 * @memberof onion_t
 *
 * This is the main loop for the onion server.
 * 
 * It initiates the listening on all the selected ports and addresses.
 *
 * @returns !=0 if there is any error. It returns actualy errno from the network operations. See socket for more information.
 */
int onion_listen(onion *o){
#ifdef HAVE_PTHREADS
	if (!(o->flags&O_DETACHED) && (o->flags&O_DETACH_LISTEN)){ // Must detach and return
		o->flags|=O_DETACHED;
		pthread_create(&o->listen_thread,NULL, (void*)onion_listen, o);
		return 0;
	}
#endif
	
	if (!o->listen_points){
		onion_add_listen_point(o,NULL,NULL,onion_http_new());
		ONION_DEBUG("Created default HTTP listen port");
	}

	/// Start listening
	size_t successful_listened_points=0;
	onion_listen_point **lp=o->listen_points;
	while (*lp){
		int listen_result=onion_listen_point_listen(*lp);
		if (!listen_result) {
			successful_listened_points++;
		}
		lp++;
	}
	if (!successful_listened_points){
		ONION_ERROR("There are no available listen points");
		return 1;
	}

	
	if (o->flags&O_ONE){
		onion_listen_point **listen_points=o->listen_points;
		if (listen_points[1]!=NULL){
			ONION_WARNING("Trying to use non-poll and non-thread mode with several listen points. Only the first will be listened");
		}
		onion_listen_point *op=listen_points[0];
		do{
			onion_request *req=onion_request_new(op);
			if (!req)
				continue;
			ONION_DEBUG("Accepted request %p", req);
			onion_request_set_no_keep_alive(req);
			int ret;
			do{
				ret=req->connection.listen_point->read_ready(req);
			}while(ret>=0);
			ONION_DEBUG("End of request %p", req);
			onion_request_free(req);
			//req->connection.listen_point->close(req);
		}while(((o->flags&O_ONE_LOOP) == O_ONE_LOOP) && op->listenfd>0);
	}
	else{
		onion_listen_point **listen_points=o->listen_points;
		while (*listen_points){
			onion_listen_point *p=*listen_points;
			ONION_DEBUG("Adding listen point fd %d to poller", p->listenfd);
			onion_poller_slot *slot=onion_poller_slot_new(p->listenfd, (void*)onion_listen_point_accept, p);
			onion_poller_slot_set_type(slot, O_POLL_ALL);
			onion_poller_add(o->poller, slot);
			listen_points++;
		}

#ifdef HAVE_PTHREADS
		ONION_DEBUG("Start polling / listening %p, %p, %p", o->listen_points, *o->listen_points, *(o->listen_points+1));
		if (o->flags&O_THREADED){
			o->threads=malloc(sizeof(pthread_t)*(o->nthreads-1));
			int i;
			for (i=0;i<o->nthreads-1;i++){
				pthread_create(&o->threads[i],NULL,(void*)onion_poller_poll, o->poller);
			}
			
			// Here is where it waits.. but eventually it will exit at onion_listen_stop
			onion_poller_poll(o->poller);
			ONION_DEBUG("Closing onion_listen");
			
			for (i=0;i<o->nthreads-1;i++){
				pthread_join(o->threads[i],NULL);
			}
		}
		else
#endif
			onion_poller_poll(o->poller);

		listen_points=o->listen_points;
		while (*listen_points){
			onion_listen_point *p=*listen_points;
			if (p->listenfd>0){
				ONION_DEBUG("Removing %d from poller", p->listenfd);
				onion_poller_remove(o->poller, p->listenfd);
			}
			listen_points++;
		}
	}
	return 0;
}
Exemplo n.º 3
0
/**
 * @short Performs the listening with the given mode
 * @memberof onion_t
 *
 * This is the main loop for the onion server.
 *
 * @returns !=0 if there is any error. It returns actualy errno from the network operations. See socket for more information.
 */
int onion_listen(onion *o){
#ifdef HAVE_PTHREADS
	if (o->flags&O_DETACH_LISTEN && !(o->flags&O_DETACHED)){ // On first call it sets the variable, and then call again, this time detached.
		o->flags|=O_DETACHED;
		pthread_attr_t attr;
		pthread_attr_init(&attr);
		pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); // It do not need to pthread_join. No leak here.
		pthread_t listen_thread;
		pthread_create(&listen_thread, &attr,(void*(*)(void*)) onion_listen, o);
		pthread_attr_destroy(&attr);
		return 0;
	}
#endif
	
	int sockfd=0;
#ifdef HAVE_SYSTEMD
	if (o->flags&O_SYSTEMD){
		int n=sd_listen_fds(0);
		ONION_DEBUG("Checking if have systemd sockets: %d",n);
		if (n>0){ // If 0, normal startup. Else use the first LISTEN_FDS.
			ONION_DEBUG("Using systemd sockets");
			if (n>1){
				ONION_WARNING("Get more than one systemd socket descriptor. Using only the first.");
			}
			sockfd=SD_LISTEN_FDS_START+0;
		}
	}
#endif
	if (sockfd==0){
		struct addrinfo hints;
		struct addrinfo *result, *rp;
		
		memset(&hints,0, sizeof(struct addrinfo));
		hints.ai_canonname=NULL;
		hints.ai_addr=NULL;
		hints.ai_next=NULL;
		hints.ai_socktype=SOCK_STREAM;
		hints.ai_family=AF_UNSPEC;
		hints.ai_flags=AI_PASSIVE|AI_NUMERICSERV;
		
		if (getaddrinfo(o->hostname, o->port, &hints, &result) !=0 ){
			ONION_ERROR("Error getting local address and port: %s", strerror(errno));
			return errno;
		}
		
		int optval=1;
		for(rp=result;rp!=NULL;rp=rp->ai_next){
			sockfd=socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
			if(SOCK_CLOEXEC == 0) { // Good compiler know how to cut this out
				int flags=fcntl(sockfd, F_GETFD);
				if (flags==-1){
					ONION_ERROR("Retrieving flags from listen socket");
				}
				flags|=FD_CLOEXEC;
				if (fcntl(sockfd, F_SETFD, flags)==-1){
					ONION_ERROR("Setting O_CLOEXEC to listen socket");
				}
			}
			if (sockfd<0) // not valid
				continue;
			if (setsockopt(sockfd,SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) ) < 0){
				ONION_ERROR("Could not set socket options: %s",strerror(errno));
			}
			if (bind(sockfd, rp->ai_addr, rp->ai_addrlen) == 0)
				break; // Success
			close(sockfd);
		}
		if (rp==NULL){
			ONION_ERROR("Could not find any suitable address to bind to.");
			return errno;
		}

#ifdef __DEBUG__
		char address[64];
		getnameinfo(rp->ai_addr, rp->ai_addrlen, address, 32,
								&address[32], 32, NI_NUMERICHOST | NI_NUMERICSERV);
		ONION_DEBUG("Listening to %s:%s",address,&address[32]);
#endif
		freeaddrinfo(result);
		listen(sockfd,5); // queue of only 5.
	}
	o->listenfd=sockfd;
	// Drops priviledges as it has binded.
	if (o->username){
		struct passwd *pw;
		pw=getpwnam(o->username);
		int error;
		if (!pw){
			ONION_ERROR("Cant find user to drop priviledges: %s", o->username);
			return errno;
		}
		else{
			error=initgroups(o->username, pw->pw_gid);
			error|=setgid(pw->pw_gid);
			error|=setuid(pw->pw_uid);
		}
		if (error){
			ONION_ERROR("Cant set the uid/gid for user %s", o->username);
			return errno;
		}
	}

	if (o->flags&O_POLL){
#ifdef HAVE_PTHREADS
		o->poller=onion_poller_new(o->max_threads+1);
#else
		o->poller=onion_poller_new(8);
#endif
		onion_poller_add(o->poller, onion_poller_slot_new(o->listenfd, (void*)onion_accept_request, o));
		// O_POLL && O_THREADED == O_POOL. Create several threads to poll.
#ifdef HAVE_PTHREADS
		if (o->flags&O_THREADED){
			ONION_WARNING("Pool mode is experimental. %d threads.", o->max_threads);
			pthread_t *thread=(pthread_t*)malloc(sizeof(pthread_t)*(o->max_threads-1));
			int i;
			for(i=0;i<o->max_threads-1;i++){
				pthread_create(&thread[i], NULL, onion_poller_adaptor, o);
			}
			onion_poller_poll(o->poller);
			ONION_DEBUG("Stopped poll");
			for(i=0;i<o->max_threads-1;i++){
				//pthread_cancel(thread[i]); // Cancel is WRONG! It left sometimes mutex without unlock, wich made deadlocks. For example.
				pthread_join(thread[i], NULL);
			}
			free(thread);
		}
		else
#endif
		{
			ONION_WARNING("Poller mode is experimental.");
			onion_poller_poll(o->poller);
		}
	}
	else if (o->flags&O_ONE){
		if ((o->flags&O_ONE_LOOP) == O_ONE_LOOP){
			while(o->listenfd>0){ // Loop while listening
				onion_accept_request(o);
			}
		}
		else{
      ONION_DEBUG("Listening just one connection");
			onion_accept_request(o);
		}
	}
#ifdef HAVE_PTHREADS
	else if (o->flags&O_THREADS_ENABLED){
		pthread_attr_t attr;
		pthread_attr_init(&attr);
		pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); // It do not need to pthread_join. No leak here.
		
		while(1){
      struct sockaddr_storage cli_addr;
      socklen_t cli_len;
      int clientfd=onion_accept(o, &cli_addr, &cli_len);
      
      if (clientfd<0)
        return errno;
			// If more than allowed processes, it waits here blocking socket, as it should be.
			// __DEBUG__
#if 0 
			int nt;
			sem_getvalue(&o->thread_count, &nt); 
			ONION_DEBUG("%d threads working, %d max threads", o->max_threads-nt, o->max_threads);
#endif
			sem_wait(&o->thread_count); 

			// Has to be malloc'd. If not it wil be overwritten on next petition. The threads frees it
			onion_request_thread_data *data=malloc(sizeof(onion_request_thread_data)); 
			data->o=o;
			data->clientfd=clientfd;
			data->client_addr=cli_addr;
      data->client_len=cli_len;
			
			pthread_create(&data->thread_handle, &attr, onion_request_thread, data);
		}
		pthread_attr_destroy(&attr);
	}
#endif
	close(o->listenfd);
	return 0;
}
Exemplo n.º 4
0
/// Simple adaptor to call from pool threads the poller.
static __attribute__((unused)) void *onion_poller_adaptor(void *o){
	onion_poller_poll(((onion*)o)->poller);
	ONION_DEBUG("Stopped poll");
	return NULL;
}