iface_t *iface_bind(int family, int port) { iface_t *iface; iface = iface_create(family); if(iface != NULL) { if(family == AF_INET) { ((struct sockaddr_in *) iface->addr)->sin_port = htons(port); } else if(family == AF_INET6) { ((struct sockaddr_in6 *) iface->addr)->sin6_port = htons(port); } iface->socket_fd = socket(family, SOCK_STREAM, 0); if(iface->socket_fd == -1) { iface_destroy(iface); iface = NULL; } else { if(family == AF_INET) { if(inet_pton(family, "localhost", &(((struct sockaddr_in *) iface->addr)->sin_addr)) == -1) { iface_destroy(iface); iface = NULL; } } else if(family == AF_INET6) { if(inet_pton(family, "localhost", &(((struct sockaddr_in6 *) iface->addr)->sin6_addr)) == -1) { iface_destroy(iface); iface = NULL; } } if(iface != NULL) { bind(iface->socket_fd, iface->addr, iface->addrlen); listen(iface->socket_fd, 5); } } } return iface; }
int main(int argc, char ** argv) { long templ; pthread_t tid; pid_t pid; char *config=NULL; iface_t *engine; struct if_engine *ifg; iface_t *ifptr,*ifptr2,*rptr; iface_t **tiptr; unsigned int i=1; int opt,err=0; void *ret; struct kopts *options=NULL; sigset_t set; struct iolists lists = { /* initialize io_mutex separately below */ .init_mutex = PTHREAD_MUTEX_INITIALIZER, .init_cond = PTHREAD_COND_INITIALIZER, .dead_cond = PTHREAD_COND_INITIALIZER, .initialized = NULL, .outputs = NULL, .inputs = NULL, .dead = NULL }; struct rlimit lim; int gotinputs=0; int rcvdsig; struct sigaction sa; pthread_mutex_init(&lists.io_mutex,NULL); /* command line argument processing */ while ((opt=getopt(argc,argv,"d:f:o:V")) != -1) { switch (opt) { case 'd': if ((((templ=strtol(optarg,NULL,0)) == 0) && (errno == EINVAL || errno == ERANGE )) || (templ < 0) || (templ > 9)) { logerr(errno,"Bad debug level %s: Must be 1-9",optarg); err++; } else debuglevel=templ; break; case 'o': if (cmdlineopt(&options,optarg) < 0) err++; break; case 'f': config=optarg; break; case 'V': printf("%s\n",VERSION); if (argc == 2) exit(0); else err++; break; default: err++; } } if (err) { fprintf(stderr, "Usage: %s [-V] | [ -f <config file>] [-o <option=value>]... [<interface specification> ...]\n",argv[0]); exit(1); } /* If a config file is specified by a commad line argument, read it. If * not, look for a default config file unless told not to using "-f-" on the * command line */ if ((config && (strcmp(config,"-"))) || (!config && (config = get_def_config()))) { DEBUG(1,"Using config file %s",config); if ((engine=parse_file(config)) == NULL) { fprintf(stderr,"Error parsing config file: %s\n",errno? strerror(errno):"Syntax Error"); exit(1); } } else { /* global options for engine configuration are also returned in config * file parsing. If we didn't do that, get default options here */ DEBUG(1,"Not using config file"); engine = get_default_global(); } proc_engine_options(engine,options); engine->lists = &lists; lists.engine=engine; for (tiptr=&engine->next;optind < argc;optind++) { if (!(ifptr=parse_arg(argv[optind]))) { fprintf(stderr,"Failed to parse interface specifier %s\n", argv[optind]); exit(1); } ifptr->next=(*tiptr); (*tiptr)=ifptr; tiptr=&ifptr->next; } /* We choose to go into the background here before interface initialzation * rather than later. Disadvantage: Errors don't get fed back on stderr. * Advantage: We can close all the file descriptors now rather than pulling * then from under erroneously specified stdin/stdout etc. */ ifg=(struct if_engine *)engine->info; if (ifg->flags & K_BACKGROUND) { if ((pid = fork()) < 0) { perror("fork failed"); exit(1); } else if (pid) exit(0); /* Continue here as child */ /* Really should close all file descriptors. Harder to do in OS * independent way. Just close the ones we know about for this cut * Check first if connected to a tty to allow redirection / piping in * background mode */ if (isatty(fileno(stdin))) { fclose(stdin); ifg->flags |= K_NOSTDIN; } if (isatty(fileno(stdout))) { fclose(stdout); ifg->flags |= K_NOSTDOUT; } if (isatty(fileno(stderr))) { fclose(stderr); ifg->flags |= K_NOSTDERR; } setsid(); (void) chdir("/"); umask(0); } /* log to stderr or syslog, as appropriate */ initlog((ifg->flags & K_NOSTDERR)?ifg->logto:-1); /* Lower max open files if necessary. We do this to ensure that ids for * all connections can be represented in IDMINORBITS. Actually we only * need to do that per server, so this is a bit of a hack and should be * corrected */ if (getrlimit(RLIMIT_NOFILE,&lim) < 0) logterm(errno,"Couldn't get resource limits"); if (lim.rlim_cur > 1<<IDMINORBITS) { logwarn("Lowering NOFILE from %u to %u",lim.rlim_cur,1<<IDMINORBITS); lim.rlim_cur=1<<IDMINORBITS; if(setrlimit(RLIMIT_NOFILE,&lim) < 0) logterm(errno,"Could not set file descriptor limit"); } /* our list of "real" interfaces starts after the first which is the * dummy "interface" specifying the multiplexing engine * walk the list, initialising the interfaces. Sometimes "BOTH" interfaces * are initialised to one IN and one OUT which then need to be linked back * into the list */ for (ifptr=engine->next,tiptr=&lists.initialized,i=0;ifptr;ifptr=ifptr2) { ifptr2 = ifptr->next; if (i == MAXINTERFACES) logterm(0,"Too many interfaces"); ifptr->id=++i<<IDMINORBITS; if (ifptr->name) { if (insertname(ifptr->name,ifptr->id) < 0) logterm(errno,"Failed to associate interface name and id"); } ifptr->lists = &lists; if ((rptr=(*iftypes[ifptr->type].init_func)(ifptr)) == NULL) { logerr(0,"Failed to initialize Interface %s",(ifptr->name)? ifptr->name:"(unnamed)"); if (!flag_test(ifptr,F_OPTIONAL)) { timetodie++; break; } /* Free all resources associated with interface * This is a bigger task than it looks. Before the "optional" * flag this was not an issue as we'd just be exiting after this * Now we need to clean up properly but not yet implemented. This * is a little memory leak with each failed init attempt */ free(ifptr); continue; } for (;ifptr;ifptr = ifptr->next) { /* This loop should be done once for IN or OUT interfaces twice for * interfaces where the initialisation routine has expanded them to an * IN/OUT pair. */ if (ifptr->direction == IN) ifptr->q=engine->q; if (ifptr->checksum <0) ifptr->checksum = engine->checksum; if (ifptr->strict <0) ifptr->strict = engine->strict; (*tiptr)=ifptr; tiptr=&ifptr->next; if (ifptr->next==ifptr2) ifptr->next=NULL; } } /* One more spin through the list now we've initialised the name to id * mapping so we can update references to "name" with an id */ for (ifptr=engine->next;ifptr;ifptr=ifptr->next) { if (ifptr->ofilter) if (name2id(ifptr->ofilter)) logterm(errno,"Name to interface translation failed"); } /* Create the key for thread local storage: in this case for a pointer to * the interface each thread is handling */ if (pthread_key_create(&ifkey,iface_destroy)) { logerr(errno,"Error creating key"); timetodie++; } if (timetodie) { for (ifptr=lists.initialized;ifptr;ifptr=ifptr2) { ifptr2=ifptr->next; iface_destroy(ifptr); } exit(1); } if (name2id(engine->ofilter)) logterm(errno,"Failed to translate interface names to IDs"); if (engine->options) free_options(engine->options); pthread_setspecific(ifkey,(void *)&lists); reaper=pthread_self(); sigemptyset(&set); sigemptyset(&sa.sa_mask); sa.sa_handler=terminate; sa.sa_flags=0; sigaction(SIGUSR1,&sa,NULL); sigaddset(&set,SIGUSR1); sigaddset(&set,SIGUSR2); sigaddset(&set,SIGALRM); sigaddset(&set,SIGTERM); sigaddset(&set,SIGINT); pthread_sigmask(SIG_BLOCK, &set, NULL); sigdelset(&set,SIGUSR1); signal(SIGPIPE,SIG_IGN); pthread_create(&tid,NULL,run_engine,(void *) engine); pthread_mutex_lock(&lists.io_mutex); for (ifptr=lists.initialized;ifptr;ifptr=ifptr->next) { /* Check we've got at least one input */ if ((ifptr->direction == IN ) || (ifptr->direction == BOTH)) gotinputs=1; /* Create a thread to run each interface */ pthread_create(&tid,NULL,(void *)start_interface,(void *) ifptr); } while (lists.initialized) pthread_cond_wait(&lists.init_cond,&lists.io_mutex); /* Have to wait until here to do something about no inputs to * avoid deadlock on io_mutex */ if (!gotinputs) { logerr(0,"No Inputs!"); pthread_mutex_lock(&engine->q->q_mutex); engine->q->active=0; pthread_cond_broadcast(&engine->q->freshmeat); pthread_mutex_unlock(&engine->q->q_mutex); timetodie++; } /* While there are remaining outputs, wait until something is added to the * dead list, reap everything on the dead list and check for outputs again * until all the outputs have been reaped * Note that when there are no more inputs, we set the * engine's queue inactive causing it to set all the outputs' queues * inactive and shutting them down. Thus the last input exiting also shuts * everything down */ while (lists.outputs || lists.inputs || lists.dead) { if (lists.dead == NULL && (timetodie <= 0)) { pthread_mutex_unlock(&lists.io_mutex); /* Here we're waiting for SIGTERM/SIGINT (user shutdown requests), * SIGUSR2 (notifications of termination from interface threads) * and (later) SIGALRM to notify of the grace period expiry */ (void) sigwait(&set,&rcvdsig); pthread_mutex_lock(&lists.io_mutex); } if ((timetodie > 0) || ( lists.outputs == NULL && (timetodie == 0)) || rcvdsig == SIGTERM || rcvdsig == SIGINT) { timetodie=-1; /* Once we've caught a user shutdown address we don't need to be * told twice */ signal(SIGTERM,SIG_IGN); signal(SIGINT,SIG_IGN); sigdelset(&set,SIGTERM); sigdelset(&set,SIGINT); for (ifptr=lists.inputs;ifptr;ifptr=ifptr->next) { pthread_kill(ifptr->tid,SIGUSR1); } for (ifptr=lists.outputs;ifptr;ifptr=ifptr->next) { if (ifptr->q == NULL) pthread_kill(ifptr->tid,SIGUSR1); } /* Set up the graceperiod alarm */ if (graceperiod) alarm(graceperiod); } if (rcvdsig == SIGALRM || graceperiod == 0) { sigdelset(&set,SIGALRM); /* Make sure we don't come back here with 0 graceperiod */ if (graceperiod == 0) graceperiod=1; for (ifptr=lists.outputs;ifptr;ifptr=ifptr->next) { if (ifptr->q) pthread_kill(ifptr->tid,SIGUSR1); } } for (ifptr=lists.dead;ifptr;ifptr=lists.dead) { lists.dead=ifptr->next; pthread_join(ifptr->tid,&ret); free(ifptr); } } /* For neatness... */ pthread_mutex_unlock(&lists.io_mutex); exit(0); }