afpfs-ng-mac/lib/loop.c

273 lines
4.6 KiB
C

/*
* loop.c
*
* Copyright (C) 2007 Alex deVries
*
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <utime.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
#include "afpfs-ng/afp.h"
#include "afpfs-ng/dsi.h"
#include "afpfs-ng/utils.h"
#define SIGNAL_TO_USE SIGUSR2
static unsigned char exit_program=0;
static pthread_t ending_thread;
static pthread_t main_thread = (pthread_t)NULL;
static int loop_started=0;
static pthread_cond_t loop_started_condition;
static pthread_mutex_t loop_started_mutex;
void trigger_exit(void)
{
exit_program=1;
}
void termination_handler(int signum)
{
switch (signum) {
case SIGINT:
case SIGTERM:
trigger_exit();
break;
default:
break;
}
signal(SIGNAL_TO_USE, termination_handler);
}
#define max(a,b) (((a)>(b)) ? (a) : (b))
static fd_set rds;
static int max_fd=0;
static void add_fd(int fd)
{
FD_SET(fd,&rds);
if ((fd+1) > max_fd) max_fd=fd+1;
}
static void rm_fd(int fd)
{
int i;
FD_CLR(fd,&rds);
for (i=max_fd;i>=0;i--)
if (FD_ISSET(i,&rds)) {
max_fd=i;
break;
}
max_fd++;
}
void signal_main_thread(void)
{
if (main_thread)
pthread_kill(main_thread,SIGNAL_TO_USE);
}
static int ending=0;
void * just_end_it_now(void * ignore)
{
if (ending) return NULL;
ending=1;
if (libafpclient->forced_ending_hook)
libafpclient->forced_ending_hook();
exit_program=2;
signal_main_thread();
return NULL;
}
/*This is a hack to handle a problem where the first pthread_kill doesnt' work*/
static unsigned char firsttime=0;
void add_fd_and_signal(int fd)
{
add_fd(fd);
signal_main_thread();
if (!firsttime) {
firsttime=1;
signal_main_thread();
}
}
void rm_fd_and_signal(int fd)
{
rm_fd(fd);
signal_main_thread();
}
void loop_disconnect(struct afp_server *s)
{
if (s->connect_state!=SERVER_STATE_CONNECTED)
return;
rm_fd_and_signal(s->fd);
/* Handle disconnect */
close(s->fd);
s->connect_state=SERVER_STATE_DISCONNECTED;
s->need_resume=1;
}
static int process_server_fds(fd_set * set, int max_fd, int ** onfd)
{
struct afp_server * s;
int ret;
s = get_server_base();
for (;s;s=s->next) {
if (s->next==s) printf("Danger, recursive loop\n");
if (FD_ISSET(s->fd,set)) {
ret=dsi_recv(s);
*onfd=&s->fd;
if (ret==-1) {
loop_disconnect(s);
return -1;
}
return 1;
}
}
return 0;
}
static void deal_with_server_signals(fd_set *set, int * max_fd)
{
if (exit_program==1) {
pthread_create(&ending_thread,NULL,just_end_it_now,NULL);
}
}
void afp_wait_for_started_loop(void)
{
if (loop_started) return;
pthread_cond_wait(&loop_started_condition,&loop_started_mutex);
}
static void * afp_main_quick_startup_thread(void * other)
{
afp_main_loop(-1);
return NULL;
}
int afp_main_quick_startup(pthread_t * thread)
{
pthread_t loop_thread;
pthread_create(&loop_thread,NULL,afp_main_quick_startup_thread,NULL);
if (thread)
memcpy(thread,&loop_thread,sizeof(pthread_t));
return 0;
}
int afp_main_loop(int command_fd) {
fd_set ords, oeds;
struct timespec tv;
int ret;
int fderrors=0;
sigset_t sigmask, orig_sigmask;
main_thread=pthread_self();
FD_ZERO(&rds);
if (command_fd>=0)
add_fd(command_fd);
sigemptyset(&sigmask);
sigaddset(&sigmask,SIGNAL_TO_USE);
sigprocmask(SIG_BLOCK,&sigmask,&orig_sigmask);
signal(SIGNAL_TO_USE,termination_handler);
signal(SIGTERM,termination_handler);
signal(SIGINT,termination_handler);
while(1) {
ords=rds;
oeds=rds;
if (loop_started) {
tv.tv_sec=30;
tv.tv_nsec=0;
} else {
tv.tv_sec=0;
tv.tv_nsec=0;
}
ret=pselect(max_fd,&ords,NULL,&oeds,&tv,&orig_sigmask);
if (exit_program==2) break;
if (exit_program==1) {
pthread_create(&ending_thread,NULL,just_end_it_now,NULL);
}
if (ret<0) {
switch(errno) {
case EINTR:
deal_with_server_signals(&rds,&max_fd);
break;
case EBADF:
if (fderrors > 100) {
log_for_client(NULL,AFPFSD,LOG_ERR,
"Too many fd errors, exiting\n");
break;
}
fderrors++;
continue;
}
continue;
}
fderrors=0;
if (ret==0) {
/* Timeout */
if (loop_started==0) {
loop_started=1;
pthread_cond_signal(&loop_started_condition);
if (libafpclient->loop_started)
libafpclient->loop_started();
}
} else {
int * onfd;
fderrors=0;
switch (process_server_fds(&ords,max_fd,&onfd)) {
case -1:
continue;
case 1:
continue;
}
if (libafpclient->scan_extra_fds) {
if (libafpclient->scan_extra_fds(
command_fd,&ords,&max_fd)>0)
continue;
}
}
}
return -1;
}