static int run_server(struct conf *conf, const char *conffile, int *rfd, const char *oldport, const char *oldstatusport) { int ret=0; SSL_CTX *ctx=NULL; int found_normal_child=0; if(!(ctx=ssl_initialise_ctx(conf))) { logp("error initialising ssl ctx\n"); return 1; } if((ssl_load_dh_params(ctx, conf))) { logp("error loading dh params\n"); return 1; } if(!oldport || strcmp(oldport, conf->port)) { close_fd(rfd); if((*rfd=init_listen_socket(conf->port, 1))<0) return 1; } if(conf->status_port && (!oldstatusport || strcmp(oldstatusport, conf->status_port))) { close_fd(&sfd); if((sfd=init_listen_socket(conf->status_port, 0))<0) return 1; } while(!hupreload) { int mfd=-1; fd_set fsr; fd_set fsw; fd_set fse; struct timeval tval; if(sigchld) { chld_check_for_exiting(); sigchld=0; } FD_ZERO(&fsr); FD_ZERO(&fse); tval.tv_sec=1; tval.tv_usec=0; add_fd_to_sets(*rfd, &fsr, NULL, &fse, &mfd); if(sfd>=0) add_fd_to_sets(sfd, &fsr, NULL, &fse, &mfd); // Add read fds of normal children. found_normal_child=chld_add_fd_to_normal_sets(conf, &fsr, &fse, &mfd); // Leave if we had a SIGUSR1 and there are no children // running. if(gentleshutdown) { if(!gentleshutdown_logged) { logp("got SIGUSR2 gentle reload signal\n"); logp("will shut down once children have exited\n"); gentleshutdown_logged++; } else if(!found_normal_child) { logp("all children have exited - shutting down\n"); break; } } if(select(mfd+1, &fsr, NULL, &fse, &tval)<0) { if(errno!=EAGAIN && errno!=EINTR) { logp("select error in normal part of %s: %s\n", __func__, strerror(errno)); ret=1; break; } } if(FD_ISSET(*rfd, &fse)) { // Happens when a client exits. //logp("error on listening socket.\n"); if(!conf->forking) { gentleshutdown++; break; } continue; } if((sfd>=0 && FD_ISSET(sfd, &fse))) { // Happens when a client exits. //logp("error on status socket.\n"); if(!conf->forking) { gentleshutdown++; break; } continue; } if(FD_ISSET(*rfd, &fsr)) { // A normal client is incoming. if(process_incoming_client(*rfd, conf, ctx, conffile, 0 /* not a status client */)) { ret=1; break; } if(!conf->forking) { gentleshutdown++; break; } } if(sfd>=0 && FD_ISSET(sfd, &fsr)) { // A status client is incoming. //printf("status client?\n"); if(process_incoming_client(sfd, conf, ctx, conffile, 1 /* a status client */)) { ret=1; break; } if(!conf->forking) { gentleshutdown++; break; } } if(chld_fd_isset_normal(conf, &fsr, &fse)) { ret=1; break; } // Have a separate select for writing to status server children mfd=-1; FD_ZERO(&fsw); FD_ZERO(&fse); if(!chld_add_fd_to_status_sets(conf, &fsw, &fse, &mfd)) { // Did not find any status server children. // No need to do the select. continue; } // Do not hang around - doing the status stuff is a lower // priority thing than dealing with normal clients. tval.tv_sec=0; tval.tv_usec=500; //printf("try status server\n"); if(select(mfd+1, NULL, &fsw, &fse, &tval)<0) { if(errno!=EAGAIN && errno!=EINTR) { logp("select error in status part of %s: %s\n", __func__, strerror(errno)); ret=1; break; } } if(chld_fd_isset_status(conf, &fsw, &fse)) { ret=1; break; } } if(hupreload) logp("got SIGHUP reload signal\n"); ssl_destroy_ctx(ctx); return ret; }
static int run_server(struct conf **confs, const char *conffile, int *rfds, int *sfds) { int i=0; int ret=-1; SSL_CTX *ctx=NULL; int found_normal_child=0; struct asfd *asfd=NULL; struct asfd *scfd=NULL; struct async *mainas=NULL; const char *port=get_string(confs[OPT_PORT]); const char *address=get_string(confs[OPT_ADDRESS]); const char *status_port=get_string(confs[OPT_STATUS_PORT]); const char *status_address=get_string(confs[OPT_STATUS_ADDRESS]); if(!(ctx=ssl_initialise_ctx(confs))) { logp("error initialising ssl ctx\n"); goto end; } if((ssl_load_dh_params(ctx, confs))) { logp("error loading dh params\n"); goto end; } if(init_listen_socket(address, port, rfds) || init_listen_socket(status_address, status_port, sfds)) goto end; if(!(mainas=async_alloc()) || mainas->init(mainas, 0)) goto end; for(i=0; i<LISTEN_SOCKETS && rfds[i]!=-1; i++) { struct asfd *newfd; if(!(newfd=setup_asfd(mainas, "main server socket", &rfds[i]))) goto end; newfd->fdtype=ASFD_FD_SERVER_LISTEN_MAIN; } for(i=0; i<LISTEN_SOCKETS && sfds[i]!=-1; i++) { struct asfd *newfd; if(!(newfd=setup_asfd(mainas, "main server status socket", &sfds[i]))) goto end; newfd->fdtype=ASFD_FD_SERVER_LISTEN_STATUS; } while(!hupreload) { switch(mainas->read_write(mainas)) { case 0: for(asfd=mainas->asfd; asfd; asfd=asfd->next) { if(asfd->new_client) { // Incoming client. asfd->new_client=0; if(process_incoming_client(asfd, ctx, conffile, confs)) goto end; if(!get_int(confs[OPT_FORK])) { gentleshutdown++; ret=1; goto end; } continue; } } break; default: int removed=0; // Maybe one of the fds had a problem. // Find and remove it and carry on if possible. for(asfd=mainas->asfd; asfd; ) { struct asfd *a; if(!asfd->want_to_remove) { asfd=asfd->next; continue; } mainas->asfd_remove(mainas, asfd); logp("%s: disconnected fd %d\n", asfd->desc, asfd->fd); a=asfd->next; asfd_free(&asfd); asfd=a; removed++; } if(removed) break; // If we got here, there was no fd to remove. // It is a fatal error. goto end; } for(asfd=mainas->asfd; asfd; asfd=asfd->next) { if(asfd->fdtype!=ASFD_FD_SERVER_PIPE_READ || !asfd->rbuf->buf) continue; // One of the child processes is giving us information. // Try to append it to any of the status child pipes. for(scfd=mainas->asfd; scfd; scfd=scfd->next) { if(scfd->fdtype!=ASFD_FD_SERVER_PIPE_WRITE) continue; switch(scfd->append_all_to_write_buffer(scfd, asfd->rbuf)) { case APPEND_OK: case APPEND_BLOCKED: break; default: goto end; } } // Free the information, even if we did not manage // to append it. That should be OK, more will be along // soon. iobuf_free_content(asfd->rbuf); } chld_check_for_exiting(mainas); // Leave if we had a SIGUSR1 and there are no children running. if(gentleshutdown) { if(!gentleshutdown_logged) { logp("got SIGUSR2 gentle reload signal\n"); logp("will shut down once children have exited\n"); gentleshutdown_logged++; } // FIX THIS: // found_normal_child=chld_add_fd_to_normal_sets(confs, &fsr, &fse, &mfd); else if(!found_normal_child) { logp("all children have exited - shutting down\n"); break; } } } if(hupreload) logp("got SIGHUP reload signal\n"); ret=0; end: async_asfd_free_all(&mainas); if(ctx) ssl_destroy_ctx(ctx); return ret; }
static int process_incoming_client(int rfd, struct conf *conf, SSL_CTX *ctx, const char *conffile, int is_status_server) { int cfd=-1; pid_t childpid; int pipe_rfd[2]; int pipe_wfd[2]; socklen_t client_length=0; struct sockaddr_in client_name; client_length=sizeof(client_name); if((cfd=accept(rfd, (struct sockaddr *) &client_name, &client_length))==-1) { // Look out, accept will get interrupted by SIGCHLDs. if(errno==EINTR) return 0; logp("accept failed on %d: %s\n", rfd, strerror(errno)); return -1; } reuseaddr(cfd); chld_check_for_exiting(); if(!conf->forking) { if(is_status_server) return run_status_server(&rfd, &cfd, -1, conffile); else return run_child(&rfd, &cfd, ctx, conffile, conf->forking); } if(chld_add_incoming(conf, is_status_server)) { logp("Closing new connection.\n"); close_fd(&cfd); return 0; } if(pipe(pipe_rfd)<0 || pipe(pipe_wfd)<0) { logp("pipe failed: %s", strerror(errno)); close_fd(&cfd); return -1; } /* fork off our new process to handle this request */ switch((childpid=fork())) { case -1: logp("fork failed: %s\n", strerror(errno)); return -1; case 0: { int ret; // child struct sigaction sa; // Set SIGCHLD back to default, so that I // can get sensible returns from waitpid. memset(&sa, 0, sizeof(sa)); sa.sa_handler=SIG_DFL; sigaction(SIGCHLD, &sa, NULL); close(pipe_rfd[0]); // close read end close(pipe_wfd[1]); // close write end conf_free_content(conf); set_blocking(pipe_rfd[1]); status_wfd=pipe_rfd[1]; if(is_status_server) ret=run_status_server(&rfd, &cfd, pipe_wfd[0], conffile); else ret=run_child(&rfd, &cfd, ctx, conffile, conf->forking); close(pipe_rfd[1]); close(pipe_wfd[0]); exit(ret); } default: // parent close(pipe_rfd[1]); // close write end close(pipe_wfd[0]); // close read end // keep a note of the child pid. if(is_status_server) logp("forked status server child pid %d\n", childpid); else logp("forked child pid %d\n", childpid); chld_forked(childpid, pipe_rfd[0], pipe_wfd[1], is_status_server); close_fd(&cfd); return 0; } }