示例#1
0
文件: onion.c 项目: arunsirrpi/onion
/**
 * @short Internal accept of just one request. 
 * 
 * It might be called straight from listen, or from the epoller.
 * 
 * @returns 0 if ok, <0 if error.
 */
static int onion_accept_request(onion *o){
  struct sockaddr_storage cli_addr;
  socklen_t cli_len;
  int clientfd=onion_accept(o, &cli_addr, &cli_len);
  
  if (clientfd<0)
    return -1;
  
	if (o->flags&O_POLL){
		onion_request *req=onion_connection_start(o, clientfd, &cli_addr, cli_len);
		if (!req){
      shutdown(clientfd,SHUT_RDWR); // Socket must be destroyed.
      close(clientfd);
			return 0;
		}
		onion_poller_slot *slot=onion_poller_slot_new(clientfd, (void*)onion_connection_read, req);
		onion_poller_slot_set_shutdown(slot, (void*)onion_connection_shutdown, req);
		onion_poller_slot_set_timeout(slot, o->timeout);
		
		onion_poller_add(o->poller, slot);
	}
	else{
		onion_process_request(o, clientfd, &cli_addr, cli_len);
	}
	return 0;
}
示例#2
0
文件: poller.c 项目: Nov11/onion
/**
 * @short Returns a poller object that helps polling on sockets and files
 * @memberof onion_poller_t
 *
 * This poller is implemented through epoll, but other implementations are possible
 *
 */
onion_poller *onion_poller_new(int n){
	onion_poller *p=onion_low_malloc(sizeof(onion_poller));
	p->fd=epoll_create1(EPOLL_CLOEXEC);
	if (p->fd < 0){
		ONION_ERROR("Error creating the poller. %s", strerror(errno));
		onion_low_free(p);
		return NULL;
	}
	p->eventfd=eventfd(0,EFD_CLOEXEC | EFD_NONBLOCK);
#if EFD_CLOEXEC == 0
  fcntl(p->eventfd,F_SETFD,FD_CLOEXEC);
#endif
	p->head=NULL;
	p->n=0;
  p->stop=0;

#ifdef HAVE_PTHREADS
  ONION_DEBUG("Init thread stuff for poll. Eventfd at %d", p->eventfd);
  p->npollers=0;
  pthread_mutexattr_t attr;
  pthread_mutexattr_init(&attr);
  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  pthread_mutex_init(&p->mutex, &attr);
  pthread_mutexattr_destroy(&attr);
#endif

  onion_poller_slot *ev=onion_poller_slot_new(p->eventfd,onion_poller_stop_helper,p);
  onion_poller_add(p,ev);

	return p;
}
示例#3
0
/**
 * @short Called when a new connection appears on the listenfd
 * @memberof onion_listen_point_t
 * 
 * When the new connection appears, creates the request and adds it to the pollers.
 * 
 * It returns always 1 as any <0 would detach from the poller and close the listen point, 
 * and not accepting a request does not mean the connection point is corrupted. If a 
 * connection point may become corrupted should be the connection point itself who detaches 
 * from the poller.
 * 
 * @param op The listen point from where the request must be built
 * @returns 1 always. 
 */
int onion_listen_point_accept(onion_listen_point *op){
	onion_request *req=onion_request_new(op);
	if (req){
		if (req->connection.fd>0){
			onion_poller_slot *slot=onion_poller_slot_new(req->connection.fd, (void*)onion_listen_point_read_ready, req);
			if (!slot)
				return 1;
			onion_poller_slot_set_timeout(slot, req->connection.listen_point->server->timeout);
			onion_poller_slot_set_shutdown(slot, (void*)onion_request_free, req);
			onion_poller_add(req->connection.listen_point->server->poller, slot);
			return 1;
		}
		if (req->connection.fd<0)
			ONION_ERROR("Error creating connection");
		// else, no need for fd... no use case yet.
	}

	return 1;
}
示例#4
0
/**
 * @short Called when a new connection appears on the listenfd
 * @memberof onion_listen_point_t
 * 
 * When the new connection appears, creates the request and adds it to the pollers.
 * 
 * It returns always 1 as any <0 would detach from the poller and close the listen point, 
 * and not accepting a request does not mean the connection point is corrupted. If a 
 * connection point may become corrupted should be the connection point itself who detaches 
 * from the poller.
 * 
 * @param op The listen point from where the request must be built
 * @returns 1 always. The poller needs one to keep listening for connections.
 */
int onion_listen_point_accept(onion_listen_point *op){
	onion_request *req=onion_request_new(op);
	if (req){
		if (req->connection.fd>0){
			onion_poller_slot *slot=onion_poller_slot_new(req->connection.fd, (void*)onion_listen_point_read_ready, req);
			if (!slot)
				return 1;
			onion_poller_slot_set_timeout(slot, req->connection.listen_point->server->timeout);
			onion_poller_slot_set_shutdown(slot, (void*)onion_request_free, req);
			onion_poller_add(req->connection.listen_point->server->poller, slot);
			return 1;
		}
		// No fd. This could mean error, or not fd based. Normally error would not return a req.
		onion_request_free(req);
		ONION_ERROR("Error creating connection");
		return 1;
	}

	return 1;
}
示例#5
0
文件: onion.c 项目: KokaKiwi/onion
/**
 * @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;
}
示例#6
0
文件: onion.c 项目: arunsirrpi/onion
/**
 * @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;
}
示例#7
0
/// Creates a new oterm
process *oterm_new(oterm_data *data, oterm_session *session, const char *username, char impersonate){
	process *oterm=malloc(sizeof(process));

	const char *command_name;
	int i;
	for (i=strlen(data->exec_command);i>=0;i--)
		if (data->exec_command[i]=='/')
			break;
	command_name=&data->exec_command[i+1];

  
  /// Get the UUID, linux nicely gives it.
  {
    int fd=open("/proc/sys/kernel/random/uuid", O_RDONLY);
    if (fd>=0){
      int r=read(fd, oterm->uuid, sizeof(oterm->uuid)-1);
      close(fd);
      if (r!=sizeof(oterm->uuid)-1) // So we will use the pseudo random generator.
        fd=-1;
    }
    if (fd<0){
      const char random_chars[]="0123456789abcdef-";
      for (i=0;i<sizeof(oterm->uuid)-1;i++){
        oterm->uuid[i]=random_chars[rand()%sizeof(random_chars)];
      }
    }
    oterm->uuid[sizeof(oterm->uuid)-1]=0;
    ONION_DEBUG("New UUID for this terminal is %s", oterm->uuid);
  }
  
	oterm->buffer=calloc(1, BUFFER_SIZE);
	oterm->buffer_pos=0;
	pthread_mutex_init(&oterm->mutex, NULL);
	pthread_cond_init(&oterm->dataReady, NULL);
	
	ONION_DEBUG("Creating new terminal, exec %s (%s)", data->exec_command, command_name);
	
	oterm->pid=forkpty(&oterm->fd, NULL, NULL, NULL);
	if ( oterm->pid== 0 ){ // on child
		// Copy env vars.
		char **envs=malloc(sizeof(char*)*(1+ONION_CLEARENV_COUNT+ONION_EXTRAENV_COUNT));
		int i,j=0;
		for (i=0;i<ONION_CLEARENV_COUNT;i++){
			const char *env=onion_clearenvs[i];
			const char *val=getenv(env);
			if (val){
				int l=strlen(env)+1+strlen(val)+1;
				envs[j]=malloc(l);
				sprintf(envs[j],"%s=%s",env,val);
				j++;
			}
		}
		for (i=0;i<ONION_EXTRAENV_COUNT;i++){
			envs[j]=strdup(onion_extraenvs[i]);
			j++;
		}
		envs[j]=NULL;

		// Change personality to that user
		if (impersonate){
			struct passwd *pw;
			pw=getpwnam(username);
			int error;
			if (!pw){
				ONION_ERROR("Cant find user to drop priviledges: %s", username);
				exit(1);
			}
			else{
				error=setgid(pw->pw_gid);
				error|=setuid(pw->pw_uid);
			}
			if (error){
				ONION_ERROR("Cant set the uid/gid for user %s", username);
				exit(1);
			}
		}
		for (i=3;i<256;i++) // Force close file descriptors. Dirty but it works.
			close(i);

		int ok=execle(data->exec_command, command_name, NULL, envs);
		fprintf(stderr,"%s:%d Could not exec shell: %d\n",__FILE__,__LINE__,ok);
		perror("");
		exit(1);
	}
	oterm->title=strdup(data->exec_command);
	ONION_DEBUG("Default title is %s", oterm->title);
	oterm->next=NULL;
	// I set myself at end
	pthread_mutex_lock( &session->head_mutex );
	if (!session->head)
		session->head=oterm;
	else{
		process *next=session->head;
		while (next->next) next=next->next;
		next->next=oterm;
	}
  onion_poller_slot *sl=onion_poller_slot_new(oterm->fd, (void*)oterm_data_ready, oterm);
  onion_poller_add(onion_get_poller(data->onion), sl);
  pthread_mutex_unlock( &session->head_mutex );
	
  onion_dict_add(data->processes, oterm->uuid, oterm, 0);
	
	return oterm;
}