/* Preforking lib. * * The source code may be redistributed, as long it remains intact, and * with this License included. You may modify or use this source code in * anyway you see fit, but credit must be given where applicable. * * This software comes with NO warranty, and the author is NOT liable for * any damages which may come from the use of this software. * * You may not use the name of the author to promote this software. * ------------------------------------------------------------------------- * :) made changes to the orginal library so that this can run fine on linux * changes for locking so that it works because when i tried to * run without changes causes me a segmentaion fault * :) made a true preforking with dynamic forking library depending on the * load creates process on high load, kills process when the load is * less there by ensuring that resource are not misused * :) created an editable section which is a better for configuration * to having them hardcoded * * ganesh kumar godavari * gkgodava@archie.uccs.edu */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" struct child { unsigned char status; int sockd; pid_t child_pid; unsigned int served ; struct child *next; }; struct load { unsigned int child_num; unsigned int busy; }load; struct load *scoreboard; #define LOG_OPT LOG_PID #define LOG_FACILITY LOG_DAEMON #define LOG_LEVEL LOG_ERR #define BUSY 1 #define READY 2 #define BUFFSIZE 512 static pid_t wait_dead(void); static void wait_for_all_dead(void); static int fork_child(int num); static void child_main(int sockd); static void child_died(int i); static void kill_all(void); static void add_child_to_list(int sockd,pid_t child_pid); static unsigned char delete_child_from_list(pid_t child_pid); static void init_lock(void); static void remove_lock_file(void); static sigset_t block_signals(void); static void setup_signal_handlers(void); static void shutdown_server(int i); static int send_socket(int sockd, char *mesg, int size); static int recv_socket(int sockd, char *buff, int size); static void check_children(int i); static int release_lock(void); static int get_lock(void); static void file_open(char *); static void child_info(struct child*); static void display_info(void); int sockRead(int sockfd,char *buf,size_t n); static unsigned int child_num = 0; static unsigned int max = 0; static unsigned int min = 0; static unsigned int initial = 0; static unsigned int cycle = 0; static unsigned int busy = 0; static unsigned int spare = 0; static int nMutexFD; /* for file locking stuff */ char* shm_addr; /* address of shared memory segment */ int shm_id; /* ID of the shared memory segment */ struct shmid_ds shm_desc; static int (*child_func)(int); static char *logstring; static char buff[BUFFSIZE]; static int opened_lock = 0; static struct child *children = NULL; static int main_sockd; static sigset_t mask; static pid_t parent_pid; struct sockaddr_in peer; #if 0 int child_function(int sockd) { unsigned char message; syslog(LOG_ERR,"Accepted: %s",inet_ntoa(peer.sin_addr)); sleep(5); while(1) { if(recv(sockd,&message,1,0x0) < 0) break; send(sockd,&message,1,0x0); } close(sockd); return 0; } static void preforkServer(u_short port) { int sockd, one = 1; struct sockaddr_in mysocket; struct linger li; bzero((char *)&mysocket,sizeof(mysocket)); mysocket.sin_port = htons(port); mysocket.sin_addr.s_addr = INADDR_ANY; mysocket.sin_family = AF_INET; if((sockd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) { fprintf(stderr,"socket creation error socket\n"); exit(-1); } /* ** we will resuse port */ setsockopt(sockd,SOL_SOCKET,SO_REUSEADDR,(char *) &one, sizeof(int)); /* ** we will set keep alive. read Steven, Network Prog. 2nd edition, p186 */ one=1; setsockopt(sockd,SOL_SOCKET,SO_KEEPALIVE,(char *) &one, sizeof(int)); li.l_onoff=1; li.l_linger=30; setsockopt(sockd,SOL_SOCKET,SO_LINGER,(char *) &li, sizeof(struct linger)); if(bind(sockd,(struct sockaddr *)&mysocket,sizeof(mysocket)) < 0) { fprintf(stderr,"unable to bind\n"); exit( -1); } listen(sockd,511); { int len = sizeof(mysocket); if(getsockname(sockd,(struct sockaddr *)&mysocket,&len) < 0) { perror("getsockname"); exit(-1); } printf("bound to %d\n",ntohs(mysocket.sin_port)); fflush(stdout); } init_daemon("SSL_PROXY",(int) StartServers,(int) MaxClients,(int) MaxRequestsPerChild,(int) MaxSpareServers,(int) MinSpareServers,child_function,(int)sockd); exit(0); } #endif static pid_t wait_dead(void) { int status; return(wait3(&status,WNOHANG,NULL)); } static void wait_for_all_dead(void) { int status; while(child_num--) { wait(&status); } return; } static int fork_child(int num) { int sv[2]; pid_t child_pid; int wanted = num; while(num--) { if(socketpair(AF_UNIX,SOCK_STREAM,0,sv) < 0) { fprintf(stderr,"socketpair: %s",strerror(errno)); shutdown_server(0); } fcntl(sv[0],F_SETFL,O_NONBLOCK); fcntl(sv[1],F_SETFL,O_NONBLOCK); child_pid = fork(); if(child_pid == -1) { #ifdef EAGAIN if(errno == EAGAIN) { close(sv[0]); close(sv[1]); return (wanted - num); } else { fprintf(stderr,"fatal fork: %s",strerror(errno)); shutdown_server(0); } #else fprintf(stderr,"fatal fork: %s",strerror(errno)); shutdown_server(0); #endif } else if(child_pid == 0) { struct sigaction childsig; sigemptyset(&childsig.sa_mask); childsig.sa_flags = 0; childsig.sa_handler = SIG_DFL; close(sv[0]); sigprocmask(SIG_SETMASK,&mask,NULL); sigaction(SIGTERM,&childsig,NULL); #ifdef SIGCHLD #define CHILD SIGCHLD #elif SIGCLD #define CHILD SIGCLD #endif sigaction(CHILD,&childsig,NULL); #undef CHILD child_main(sv[1]); } else { close(sv[1]); add_child_to_list(sv[0],child_pid); child_num++; } } return wanted; } static void child_main(int sockd) { int length = sizeof(struct sockaddr_in); char message; int sockd2; while(cycle--) { message = READY; if(send_socket(sockd,(char *)&message,sizeof(message))) _exit(-1); kill(parent_pid,SIGUSR1); if(get_lock() < 0) { fprintf(stderr,"Could'nt obtain lock: %s",strerror(errno)); _exit(-1); } if((sockd2 = accept(main_sockd,(struct sockaddr *)&peer,&length)) < 0) { fprintf(stderr,"accept: %s",strerror(errno)); _exit(-1); } /* ** reducing the number of processes which we we donot have much load */ if ((scoreboard->child_num - scoreboard->busy) > spare ) { #if 0 (void) fprintf(stderr, "the child_num is %d and busy %d (child_num-busy) > spate :: %d > %d \n",scoreboard->child_num,scoreboard->busy,(scoreboard->child_num-scoreboard->busy),spare); #endif cycle = 0; } if(release_lock() < 0) { fprintf(stderr,"Could'nt release lock: %s",strerror(errno)); _exit(-1); } message = BUSY; if(send_socket(sockd,(char *)&message,sizeof(message)) < 0) _exit(-1); kill(parent_pid,SIGUSR1); child_func(sockd2); close(sockd2); } printf ("killing child with id %d\n",getpid()); _exit(0); } void init_daemon(char *logname, int num,int max_children,int num_cycle,int perfork,int min_children, int (*func)(int),int sockd) { int retval; main_sockd = sockd; logstring = logname; initial = num; max = max_children; min = min_children; cycle = num_cycle; spare = perfork; child_func = func; parent_pid = getpid(); mask = block_signals(); setup_signal_handlers(); init_lock(); while(1) { if(child_num < initial) { retval = fork_child(initial - child_num); } #if 0 (void) fprintf(stderr,"child number %d busy %d \n",child_num,busy); display_info(); #endif if(busy >= (child_num-min)) { if(max > busy) { if((max - child_num) > perfork) fork_child(initial); else fork_child((max - child_num)); } } if(get_lock() < 0) { fprintf(stderr,"Could'nt obtain lock: %s",strerror(errno)); _exit(-1); } scoreboard->busy = busy; scoreboard->child_num = child_num; #if 0 #ifdef PREFORK_SSL if (scoreboard->busy >= child_num) shutdown_server(0); #endif #endif if(release_lock() < 0) { fprintf(stderr,"Could'nt release lock: %s",strerror(errno)); _exit(-1); } sigsuspend(&mask); } } static void child_died(int i) { pid_t child_pid; while((child_pid = wait_dead()) > 0) { child_num--; if(delete_child_from_list(child_pid) == BUSY) busy--; } return; } static void kill_all(void) { struct child *ptr; for(ptr = children;ptr;ptr = ptr->next) { kill(ptr->child_pid,SIGTERM); } wait_for_all_dead(); return; } static void add_child_to_list(int sockd,pid_t child_pid) { struct child *ptr; if(children == NULL) { if((children = malloc(sizeof(struct child))) == NULL) { fprintf(stderr,"malloc: %s",strerror(errno)); shutdown_server(0); } ptr = children; ptr->next = NULL; } else { if((ptr = malloc(sizeof(struct child))) == NULL) { fprintf(stderr,"malloc: %s",strerror(errno)); shutdown_server(0); } ptr->next = children; children = ptr; } ptr->sockd = sockd; ptr->child_pid = child_pid; ptr->status = READY; ptr->served = 0; return; } static void child_info(struct child * child) { (void) fprintf(stderr,"child pid %d status is %d served %d \n", child->child_pid, child->status, child->served); } static void display_info(void) { struct child * ptr; for(ptr=children;ptr;ptr=ptr->next) (void) fprintf(stderr,"child pid %d status is %d served %d \n", ptr->child_pid,ptr->status,ptr->served); } static unsigned char delete_child_from_list(pid_t child_pid) { struct child *ptr, *last; char status; if(children->child_pid == child_pid) { #if 0 (void) fprintf(stderr,"call delete loop 1 %d\n ",child_pid); display_info(); #endif ptr = children->next; close(children->sockd); status = children->status; free(children); children = ptr; } else { for(ptr = children;ptr->next->child_pid != child_pid;ptr = ptr->next); last = ptr->next; ptr->next = ptr->next->next; close(last->sockd); status = last->status; free(last); } return status; } /* Attempt to create a unique file to use as a locking file. */ static void init_lock(void) { (void) sprintf(buff,"%s",PREFORK_LOCK); (void) file_open(buff); opened_lock = 1; (void) fprintf(stderr," init_lock succesfull"); return; } static void remove_lock_file(void) { unlink(buff); return; } static sigset_t block_signals(void) { sigset_t mask; sigset_t old; sigfillset(&mask); sigdelset(&mask,SIGKILL); sigdelset(&mask,SIGSTOP); sigdelset(&mask,SIGUSR1); sigprocmask(SIG_SETMASK,&mask,&old); return old; } static void setup_signal_handlers(void) { struct sigaction mysig; sigfillset(&mysig.sa_mask); sigdelset(&mysig.sa_mask,SIGSTOP); sigdelset(&mysig.sa_mask,SIGKILL); mysig.sa_flags = 0; mysig.sa_handler = shutdown_server; if(sigaction(SIGTERM,&mysig,NULL) < 0) { fprintf(stderr,"sigaction: %s",strerror(errno)); shutdown_server(0); } mysig.sa_handler = check_children; if(sigaction(SIGUSR1,&mysig,NULL) < 0) { fprintf(stderr,"sigaction: %s",strerror(errno)); shutdown_server(0); } #ifdef SA_NOCLDSTOP mysig.sa_flags = SA_NOCLDSTOP; #endif mysig.sa_handler = child_died; #ifdef SIGCHLD #define CHILD SIGCHLD #elif SIGCLD #define CHILD SIGCLD #endif if(sigaction(CHILD,&mysig,NULL) < 0) { fprintf(stderr,"sigaction: %s",strerror(errno)); shutdown_server(0); } #undef CHILD return; } static void shutdown_server(int i) { kill_all(); if(opened_lock) remove_lock_file(); exit(0); } static int send_socket(int sockd,char *mesg,int size) { fd_set sd; int retval; FD_ZERO(&sd); FD_SET(sockd,&sd); retval = select((sockd+1),NULL,&sd,NULL,NULL); if(retval < 0) return -1; retry: if(send(sockd,mesg,size,0x0) < 0) { #ifdef ENOSR if(errno == ENOSR) goto retry; #elif ENOMEM if(errno == ENOMEM) goto retry; #endif if(errno == EAGAIN) goto retry; fprintf(stderr,"send: %s",strerror(errno)); return -1; } return 0; } static int recv_socket(int sockd,char *buff,int size) { int nbytes = 0; while(nbytes < size) { if(recv(sockd,(buff+nbytes),1,0x0) < 0) { if(errno == EAGAIN) return (nbytes-1); else return -1; } nbytes++; } return nbytes; } static void check_children(int i) { fd_set rd; struct child *ptr; int retval, max = 0; struct timeval timeout = { 0, 0 }; char message; FD_ZERO(&rd); for(ptr = children;ptr;ptr = ptr->next) { FD_SET(ptr->sockd,&rd); if(max < ptr->sockd) { max = (ptr->sockd +1); } } retval = select(max,&rd,NULL,NULL,&timeout); ptr = children; while(retval--) { for(;ptr;ptr = ptr->next) { if(FD_ISSET(ptr->sockd,&rd)) { FD_CLR(ptr->sockd,&rd); if(recv_socket(ptr->sockd,&message,sizeof(message)) < 0) { /* Bogus message the child could of died. * Just close it and continue. */ close(ptr->sockd); break; } if((message == READY) && (ptr->status == BUSY)) { busy--; } else if((message == BUSY) && (ptr->status == READY)) { busy++; } ptr->status = message; if(message == READY) ptr->served = ptr->served + 1; break; } } } return; } void file_open(char *file) { if((nMutexFD =(int) open(file,O_RDWR|O_CREAT,0600 )) < 0) { (void) fprintf(stderr,"child could not open SSLMutex lockFile %s error is %d\n",file, errno); exit(1); } return; } static int get_lock(void) { int rc = -1; struct flock lock_it; lock_it.l_whence = SEEK_SET; /* from current point */ lock_it.l_start = 0; /* -"- */ lock_it.l_len = 0; /* until end of file */ lock_it.l_type = F_WRLCK; /* set exclusive/write lock */ lock_it.l_pid = 0; /* pid not actually interesting */ while (((rc = fcntl(nMutexFD, F_SETLKW, &lock_it)) < 0) && (errno == EINTR) ) ; (void) fcntl(nMutexFD,F_GETLK,&lock_it); if (lock_it.l_type != F_UNLCK) return 0; else return 1; } static int release_lock(void) { int rc = -1; struct flock unlock_it; unlock_it.l_whence = SEEK_SET; /* from current point */ unlock_it.l_start = 0; /* -"- */ unlock_it.l_len = 0; /* until end of file */ unlock_it.l_type = F_UNLCK; /* unlock */ unlock_it.l_pid = 0; /* pid not actually interesting */ while (((rc = fcntl(nMutexFD, F_SETLKW, &unlock_it)) < 0) && (errno == EINTR) ) ; (void) fcntl(nMutexFD,F_GETLK,&unlock_it); if(unlock_it.l_type == F_UNLCK) return 1; else return 0; } #if 0 int main(int argc,char *argv[]) { int sockd; struct sockaddr_in mysocket; shm_id = shmget(IPC_PRIVATE, sizeof(struct load), IPC_CREAT | IPC_EXCL | 0600); if (shm_id == -1) { perror("main: shmget: "); exit(1); } /* attach the shared memory segment to our process's address space. */ shm_addr = shmat(shm_id, NULL, 0); if (!shm_addr) /* operation failed. */ { perror("main: shmat: "); exit(1); } scoreboard = (struct load *) shm_addr; bzero((char *)&mysocket,sizeof(mysocket)); mysocket.sin_port = htons(SERVER_PORT); mysocket.sin_addr.s_addr = INADDR_ANY; mysocket.sin_family = AF_INET; if((sockd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) { perror("socket"); exit(-1); } if(bind(sockd,(struct sockaddr *)&mysocket,sizeof(mysocket)) < 0) { perror("bind"); exit(-1); } listen(sockd,511); { int len = sizeof(mysocket); if(getsockname(sockd,(struct sockaddr *)&mysocket,&len) < 0) { perror("getsockname"); exit(-1); } printf("bound to %d\n",ntohs(mysocket.sin_port)); fflush(stdout); } init_daemon("SSLPROXY",StartServers,MaxClients,MaxRequestsPerChild,MaxSpareServers,MinSpareServers,child_function,sockd); exit(0); } #endif