Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

fo_server.c

Go to the documentation of this file.
00001 
00008 /* {{{ Initial comments */
00009 /*
00010  * $LastChangedDate: 2004-02-13 09:48:42 +0100 (Fri, 13 Feb 2004) $
00011  * $LastChangedRevision: 46 $
00012  * $LastChangedBy: ckruse $
00013  *
00014  */
00015 /* }}} */
00016 
00017 /* {{{ Includes */
00018 #include "config.h"
00019 #include "defines.h"
00020 
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <time.h>
00024 #include <pthread.h>
00025 #include <gdome.h>
00026 #include <errno.h>
00027 #include <string.h>
00028 
00029 #include <sys/stat.h>
00030 
00031 /* socket includes */
00032 #include <sys/types.h>
00033 #include <sys/socket.h>
00034 #include <netdb.h>
00035 #include <unistd.h>
00036 #include <sys/un.h>
00037 
00038 #include <sys/wait.h>
00039 #include <signal.h>
00040 #include <fcntl.h>
00041 
00042 #ifdef CF_SHARED_MEM
00043 #include <sys/ipc.h>
00044 #include <sys/shm.h>
00045 #include <sys/sem.h>
00046 #endif
00047 
00048 #ifdef CF_SHARED_MEM
00049 #include "semaphores.h"
00050 #endif
00051 
00052 #include "cf_pthread.h"
00053 
00054 #include "hashlib.h"
00055 #include "utils.h"
00056 #include "configparser.h"
00057 #include "readline.h"
00058 #include "fo_server.h"
00059 #include "initfinish.h"
00060 #include "serverlib.h"
00061 #include "readline.h"
00062 #include "archiver.h"
00063 
00064 /* }}} */
00065 
00066 /* definition of the global variables */
00067 int    RUN; 
00068 t_head head; 
00070 /* {{{ flsh */
00076 void flsh(int n) {
00077   int status;
00078 
00079   cf_log(LOG_STD,__FILE__,__LINE__,"flushing file handles\n");
00080 
00081   if((status = pthread_mutex_lock(&head.log_lock)) != 0) {
00082     fprintf(stderr,"pthread_mutex_lock: %s\n",strerror(status));
00083     return;
00084   }
00085 
00086   if(head.std) {
00087     fflush(head.std);
00088   }
00089   if(head.err) {
00090     fflush(head.err);
00091   }
00092 
00093   pthread_mutex_unlock(&head.log_lock);
00094 }
00095 /* }}} */
00096 
00097 /* {{{ terminate */
00098 void terminate(int n) {
00099   cf_log(LOG_STD,__FILE__,__LINE__,"got signal SIG%d, going down\n",n);
00100   RUN = 0;
00101 }
00102 /* }}} */
00103 
00104 /* {{{ archiver_and_writer */
00110 void *archiver_and_writer(void *arg) {
00111   t_name_value *v = cfg_get_value(&fo_server_conf,"RunArchiver");
00112   int val = atoi(v->values[0]);
00113 #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
00114   struct sched_param param;
00115 
00116   /* the archiver has a priority lower than the server and two priorities lower than the workers */
00117   memset(&param,0,sizeof(struct sched_param));
00118   param.sched_priority = (sched_get_priority_min(SCHEDULING) + sched_get_priority_max(SCHEDULING)) / 2;
00119   pthread_setschedparam(pthread_self(),SCHEDULING,&param);
00120 #endif
00121 
00122   if(!v) {
00123     cf_log(LOG_ERR,__FILE__,__LINE__,"FATAL ERROR: could not get archiver intervall!\n");
00124     RUN = 0;
00125     return NULL;
00126   }
00127 
00128   while(RUN) {
00129     sleep(val); /* sleep breaks when we get SIGTERM */
00130 
00131     if(RUN) {
00132       /* run archiver and write to disk... */
00133       cf_log(LOG_STD,__FILE__,__LINE__,"running archiver...\n");
00134       cf_run_archiver_and_write_to_disk();
00135     }
00136   }
00137 
00138   return NULL;
00139 }
00140 /* }}} */
00141 
00142 /* {{{ setup_server_infos */
00147 void setup_server_infos(void) {
00148   FILE *fpid;
00149   t_name_value *pidfile;
00150   pid_t pid;
00151   u_char buff[50];
00152 
00153   /*
00154    * First, lets get the path to the pid file
00155    */
00156   pidfile = cfg_get_value(&fo_server_conf,"PIDFile");
00157   if(!pidfile) {
00158     fprintf(stderr,"I need a pid file!\n");
00159     exit(-1);
00160   }
00161 
00162   /*
00163    * then, try to open it
00164    */
00165   fpid = fopen(pidfile->values[0],"r");
00166   if(fpid) {
00167     /*
00168      * after that, try to read the pid
00169      */
00170     if(!fread(buff,1,50,fpid)) {
00171       fprintf(stderr,"I could not read the pid file!\n");
00172       exit(-1);
00173     }
00174 
00175     /*
00176      * then, check if the pid is a valid pid
00177      */
00178     pid = atoi(buff);
00179     if(kill(pid,0) != -1) {
00180       fprintf(stderr,"Server seems to run already at pid %d\n",pid);
00181       exit(-1);
00182     }
00183     else {
00184       /*
00185        * if the error is a permission problem, there may be a server
00186        * running. So go sure and exit
00187        */
00188       if(errno == EPERM) {
00189         fprintf(stderr,"Server seems to run already at pid %d\n",pid);
00190         exit(-1);
00191       }
00192     }
00193 
00194     /*
00195      * ok, everything finished. Close file
00196      */
00197     fclose(fpid);
00198   }
00199   else {
00200     /*
00201      * pid file cannot be found. Lets be on the secure site and exit
00202      */
00203     if(errno != ENOENT) {
00204       fprintf(stderr,"error opening pid file: %s",strerror(errno));
00205       exit(-1);
00206     }
00207   }
00208 
00209   /*
00210    * Now, open the file writeable and try to write the
00211    * pid of this process into the file
00212    */
00213   fpid = fopen(pidfile->values[0],"w");
00214   if(fpid) {
00215     pid = getpid();
00216 
00217     fprintf(fpid,"%d",pid);
00218     fclose(fpid);
00219   }
00220   else {
00221     /*
00222      * if the pid file cannot be opened for writing, die
00223      */
00224     fprintf(stderr,"could not open pid file '%s'!\n",pidfile->values[0]);
00225     exit(-1);
00226   }
00227 }
00228 /* }}} */
00229 
00230 #ifdef CF_SHARED_MEM
00231 /* {{{ shared memory functions */
00232 
00237 int create_shm_sem(t_name_value *shm) {
00238   union semun smn;
00239   unsigned short x = 0;
00240 
00241   if((head.shm_sem = semget(atoi(shm->values[2]),1,S_IRWXU|S_IRWXG|S_IRWXO|IPC_CREAT)) == -1) {
00242     cf_log(LOG_ERR,__FILE__,__LINE__,"semget: %s\n",strerror(errno));
00243     return -1;
00244   }
00245 
00246   smn.array = &x;
00247   if(semctl(head.shm_sem,0,SETALL,smn) == -1) {
00248     cf_log(LOG_ERR,__FILE__,__LINE__,"semctl: %s\n",strerror(errno));
00249     return -1;
00250   }
00251 
00252   return 0;
00253 }
00254 
00255 /* }}} */
00256 #endif
00257 
00258 
00266 int main(int argc,char *argv[]) {
00267   int sockfd,size,status,connfd,ret = 0,i;
00268   u_char *fname;
00269   t_array *fnames;
00270   struct sockaddr_un *addr = fo_alloc(NULL,1,sizeof(struct sockaddr_un),FO_ALLOC_CALLOC);
00271   t_configfile default_conf,server_conf;
00272   pthread_t thr;
00273   t_name_value *pidfile;
00274   fd_set rfds;
00275   struct timeval timeout;
00276   pthread_attr_t attr;
00277 
00278   size_t hi;
00279   t_handler_config *handler;
00280   t_server_init_filter fkt;
00281 
00282   static const u_char *wanted[] = {
00283     "fo_default", "fo_server"
00284   };
00285 
00286   #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
00287   struct sched_param param;
00288   #endif
00289 
00290   #ifdef CF_SHARED_MEM
00291   t_name_value *shm;
00292   #endif
00293 
00294   /* {{{ Initialization */
00295 
00296   #ifndef DEBUG
00297   pid_t pid;
00298 
00299   /* we daemonize... */
00300   pid = fork();
00301   switch(pid) {
00302   case -1:
00303     perror("fork");
00304     exit(-1);
00305 
00306   case 0:
00307     if(setsid() == -1) {
00308       cf_log(LOG_ERR,__FILE__,__LINE__,"setsid: %s\n",strerror(errno));
00309       exit(-1);
00310     }
00311 
00312    break;
00313 
00314   default:
00315     printf("server forked. It's pid is: %d\n",pid);
00316     exit(0);
00317   }
00318   #endif
00319 
00320   init_modules();
00321 
00322   /* initialisation of the head-variable */
00323   str_init(&head.cache_invisible);
00324   str_init(&head.cache_visible);
00325 
00326   head.date_visible      = 0;
00327   head.date_invisible    = 0;
00328 
00329   head.thread            = NULL;
00330 
00331   head.std               = NULL;
00332   head.err               = NULL;
00333 
00334   head.locked            = 0;
00335 
00336   head.clients.workers   = 0;
00337   head.clients.clientnum = 0;
00338   head.clients.down      = 0;
00339   head.clients.clients   = NULL;
00340   head.clients.last      = NULL;
00341 
00342   head.servers           = NULL;
00343 
00344   head.protocol_handlers = NULL;
00345 
00346   head.threads           = NULL;
00347 
00348   #ifdef CF_SHARED_MEM
00349   head.shm_ids[0]        = -1;
00350   head.shm_ids[1]        = -1;
00351 
00352   head.shm_ptrs[0]       = NULL;
00353   head.shm_ptrs[1]       = NULL;
00354 
00355   head.shm_sem           = -1;
00356   #endif
00357 
00358   RUN                    = 1;
00359 
00360   cf_rwlock_init("head.lock",&head.lock);
00361   cf_rwlock_init("head.threads_lock",&head.threads_lock);
00362 
00363   pthread_mutex_init(&head.log_lock,NULL);
00364 
00365   cf_mutex_init("head.clients.cond_lock",&head.clients.cond_lock);
00366   cf_mutex_init("head.clients.lock",&head.clients.lock);
00367   cf_mutex_init("head.server_lock",&head.server_lock);
00368 
00369   pthread_cond_init(&head.clients.cond,NULL);
00370 
00371   /* signal handlers */
00372   signal(SIGPIPE,SIG_IGN);
00373   signal(SIGINT,flsh);
00374   signal(SIGHUP,flsh);
00375   signal(SIGTERM,terminate);
00376 
00377   /* }}} */
00378 
00379   /* {{{ Configuration */
00380   /* initialization finished, get config files */
00381   if((fnames = get_conf_file(wanted,2)) == NULL) {
00382     return EXIT_FAILURE;
00383   }
00384 
00385   /* init the configuration file structure */
00386   fname = *((u_char **)array_element_at(fnames,0));
00387   cfg_init_file(&default_conf,fname);
00388   cfg_register_options(&default_conf,default_options);
00389   free(fname);
00390 
00391   fname = *((u_char **)array_element_at(fnames,1));
00392   cfg_init_file(&server_conf,fname);
00393   cfg_register_options(&server_conf,fo_server_options);
00394   free(fname);
00395 
00396   array_destroy(fnames);
00397   free(fnames);
00398 
00399   /* init configuration structures and parse config */
00400   if(read_config(&default_conf,NULL) != 0) {
00401     cfg_cleanup_file(&default_conf);
00402     cfg_cleanup_file(&server_conf);
00403 
00404     fprintf(stderr,"configfile error!\n");
00405     return EXIT_FAILURE;
00406   }
00407 
00408   if(read_config(&server_conf,NULL) != 0) {
00409     cfg_cleanup_file(&default_conf);
00410     cfg_cleanup_file(&server_conf);
00411 
00412     fprintf(stderr,"configfile error!\n");
00413     return EXIT_FAILURE;
00414   }
00415 
00416   /* }}} */
00417 
00418   /* {{{ more initialization */
00419   /*
00420    * let's go sure that no other server is running at this configuration
00421    */
00422   setup_server_infos();
00423   pidfile = cfg_get_value(&fo_server_conf,"PIDFile");
00424 
00425   #ifdef CF_SHARED_MEM
00426   shm      = cfg_get_value(&fo_default_conf,"SharedMemIds");
00427 
00428   if(create_shm_sem(shm) != 0) return EXIT_FAILURE;
00429 
00430   cf_mutex_init("head.shm_lock",&head.shm_lock);
00431   #endif
00432 
00433   /*
00434    * now we don't longer need stdout, stdin and stderr
00435    */
00436   #ifndef DEBUG
00437   close(fileno(stdin));
00438   close(fileno(stdout));
00439   close(fileno(stderr));
00440   #endif
00441 
00442   #ifdef sun
00443   /*
00444    * On Solaris 2.5, on a uniprocessor machine threads run not
00445    * asynchronously by default. So we increase the thread concurrency
00446    * level that threads can run asynchronous. In fact, in concurrency
00447    * level six, six threads can run "simultanously".
00448    */
00449   thr_setconcurrency(6);
00450   #endif
00451 
00452   pthread_attr_init(&attr);
00453 
00454   /*
00455    * on very high traffic, the server does accept more and more
00456    * connections, but does not serve these connection in an
00457    * acceptable time. So we experience a little bit with thread
00458    * scheduling...
00459    */
00460   #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
00461   memset(&param,0,sizeof(struct sched_param));
00462 
00463   param.sched_priority = (sched_get_priority_min(SCHEDULING) + sched_get_priority_max(SCHEDULING)) / 2;
00464 
00465   pthread_setschedparam(pthread_self(),SCHEDULING,&param);
00466 
00467   param.sched_priority++;
00468   pthread_attr_setschedparam(&attr,&param);
00469   pthread_attr_setinheritsched(&attr,PTHREAD_INHERIT_SCHED);
00470   #endif
00471 
00472   /* create the forum tree */
00473   make_forumtree(&fo_default_conf,&head);
00474 
00475   /* and now create the socket */
00476   sockfd = cf_set_us_up_the_socket(addr);
00477   size   = sizeof(addr);
00478 
00479   cf_push_server(sockfd,(struct sockaddr *)addr,size,cf_handle_request);
00480 
00481   /* generate the cache */
00482   cf_generate_cache(NULL);
00483 
00484   /*
00485    * the archiver will run in a defined rythm. archiver_and_write will do this, and
00486    * it has its own thread.
00487    */
00488   if((status = pthread_create(&thr,&attr,archiver_and_writer,NULL)) != 0) {
00489     cf_log(LOG_ERR,__FILE__,__LINE__,"pthread_create: %s",strerror(status));
00490     RUN = 0;
00491   }
00492   pthread_detach(thr);
00493 
00494   /* run initialization plugins */
00495   if(Modules[INIT_HANDLER].elements) {
00496     ret = FLT_OK;
00497 
00498     for(hi=0;hi<Modules[INIT_HANDLER].elements && (ret == FLT_DECLINE || ret == FLT_OK);hi++) {
00499       handler = array_element_at(&Modules[INIT_HANDLER],hi);
00500       fkt     = (t_server_init_filter)handler->func;
00501       ret     = fkt(sockfd);
00502     }
00503   }
00504 
00505   /* }}} */
00506 
00507   if(ret != FLT_EXIT) {
00508     t_server *srv;
00509 
00510     CF_RW_WR(&head.lock);
00511 
00512     /* set up the workers... */
00513     for(i=0;i<INITIAL_WORKERS_NUM;i++) {
00514       if((status = pthread_create(&head.workers[i],&attr,cf_worker,NULL)) != 0) {
00515         cf_log(LOG_ERR,__FILE__,__LINE__,"pthread_create: %s\n",strerror(status));
00516         RUN = 0;
00517         break;
00518       }
00519 
00520       cf_log(LOG_STD,__FILE__,__LINE__,"created worker %d\n",i);
00521       head.clients.workers++;
00522     }
00523 
00524     CF_RW_UN(&head.lock);
00525 
00526 
00527     /* and now, enter the main loop */
00528     cf_log(LOG_STD,__FILE__,__LINE__,"Read config, parsed xml, set up the socket and generated cache. Now listening...\n");
00529 
00530     /* run the main loop */
00531     while(RUN) {
00532       /* set the fdset */
00533       FD_ZERO(&rfds);
00534 
00535       /* fill the fdset with the server sockets */
00536       CF_LM(&head.server_lock);
00537 
00538       for(srv = head.servers;srv;srv = srv->next) {
00539         if(sockfd < srv->sock) sockfd = srv->sock;
00540         FD_SET(srv->sock,&rfds);
00541       }
00542 
00543       CF_UM(&head.server_lock);
00544 
00545       /*
00546        * since linux developers had the silly idea to modify
00547        * the timeout struct of select(), we have to re-initialize
00548        * it in each loop
00549        */
00550       memset(&timeout,0,sizeof(struct timeval));
00551 
00552       /* check every 10 seconds if we shall exit */
00553       timeout.tv_sec = 10;
00554 
00555       /* wait for connections */
00556       ret = select(sockfd+1,&rfds,NULL,NULL,&timeout);
00557 
00558       /* connection or timeout? */
00559       if(ret > 0) {
00560         /* get the connection */
00561         CF_LM(&head.server_lock);
00562 
00563         for(srv=head.servers;srv;srv=srv->next) {
00564           if(FD_ISSET(srv->sock,&rfds)) {
00565             size   = srv->size;
00566             connfd = accept(srv->sock,srv->addr,&size);
00567 
00568             /* accept-error? */
00569             if(connfd <= 0) {
00570               cf_log(LOG_ERR,__FILE__,__LINE__,"accept: %s\n",strerror(errno));
00571               continue;
00572             }
00573 
00574             cf_push_client(connfd,srv->worker);
00575           }
00576         }
00577 
00578         CF_UM(&head.server_lock);
00579       }
00580     }
00581   }
00582 
00583   /* close server sockets */
00584   cf_log(LOG_STD,__FILE__,__LINE__,"closing server sockets...\n");
00585   CF_LM(&head.server_lock);
00586 
00587   if(1) {
00588     t_server *srv,*srv1;
00589 
00590     for(srv=head.servers;srv;srv=srv1) {
00591       close(srv->sock);
00592       srv1 = srv->next;
00593       free(srv);
00594     }
00595   }
00596 
00597   CF_UM(&head.server_lock);
00598 
00599   free(addr);
00600 
00601   /* run cleanup code of the modules (if neccessary) */
00602   cf_log(LOG_STD,__FILE__,__LINE__,"cleaning up modules...\n");
00603   cleanup_modules(Modules);
00604 
00605   /* write threads to disk */
00606   cf_log(LOG_STD,__FILE__,__LINE__,"running archiver and writing threads to disk...\n");
00607   cf_run_archiver_and_write_to_disk();
00608 
00609   /* cleanup code */
00610   cf_log(LOG_STD,__FILE__,__LINE__,"running cleanup code...\n");
00611   cleanup_forumtree();
00612 
00613   #ifdef CF_SHARED_MEM
00614   cf_log(LOG_STD,__FILE__,__LINE__,"destroying shared memory and semaphores...\n");
00615 
00616   if(head.shm_sem >= 0)
00617     if(semctl(head.shm_sem,0,IPC_RMID,NULL) == -1) cf_log(LOG_ERR,__FILE__,__LINE__,"semctl: %s\n",strerror(errno));
00618 
00619   for(i=0;i<2;i++) {
00620     if(head.shm_ids[i] >= 0) {
00621       if(head.shm_ptrs[i])
00622         if(shmdt(head.shm_ptrs[i]) < 0) cf_log(LOG_ERR,__FILE__,__LINE__,"shmdt: %s\n",strerror(errno));
00623 
00624       if(shmctl(head.shm_ids[i],IPC_RMID,0) < 0) cf_log(LOG_ERR,__FILE__,__LINE__,"shmctl: %s\n",strerror(errno));
00625     }
00626   }
00627   #endif
00628 
00629   /* destroy workers */
00630   CF_LM(&head.clients.lock);
00631 
00632   size              = head.clients.workers;
00633   head.clients.down = 1;
00634   CF_UM(&head.clients.lock);
00635 
00636   pthread_cond_broadcast(&head.clients.cond);
00637 
00638   for(i=0;i<size;i++) {
00639     pthread_join(head.workers[i],NULL);
00640   }
00641 
00642   /*
00643    * destroy waiting connections
00644    */
00645   if(head.clients.clients) {
00646     t_client *cli,*cli1;
00647 
00648     CF_LM(&head.clients.lock);
00649 
00650     for(cli=head.clients.clients;cli;cli=cli1) {
00651       writen(cli->sock,"506 Server is going down\n",25);
00652       close(cli->sock);
00653       cli1 = cli->next;
00654       free(cli);
00655     }
00656 
00657     CF_UM(&head.clients.lock);
00658   }
00659 
00660   if(head.threads) cf_hash_destroy(head.threads);
00661   if(head.protocol_handlers) cf_hash_destroy(head.protocol_handlers);
00662 
00663   /* close logfiles */
00664   cf_log(LOG_STD,__FILE__,__LINE__,"closing logfiles... bye!\n");
00665   pthread_mutex_lock(&head.log_lock);
00666   if(head.err) {
00667     fclose(head.err);
00668   }
00669   if(head.std) {
00670     fclose(head.std);
00671   }
00672   pthread_mutex_unlock(&head.log_lock);
00673 
00674   cf_rwlock_destroy(&head.lock);
00675   cf_rwlock_destroy(&head.threads_lock);
00676 
00677   pthread_mutex_destroy(&head.log_lock);
00678 
00679   cf_mutex_destroy(&head.clients.cond_lock);
00680   cf_mutex_destroy(&head.clients.lock);
00681   cf_mutex_destroy(&head.server_lock);
00682 
00683   pthread_cond_destroy(&head.clients.cond);
00684 
00685   /* the pid file is not longer needed */
00686   unlink(pidfile->values[0]);
00687 
00688   /* also the config */
00689   cfg_cleanup(&fo_default_conf);
00690   cfg_cleanup(&fo_server_conf);
00691 
00692   cfg_cleanup_file(&default_conf);
00693   cfg_cleanup_file(&server_conf);
00694 
00695   return EXIT_SUCCESS;
00696 }
00697 
00698 /* eof */

Generated on Sun Apr 25 16:37:39 2004 for Classic Forum by doxygen 1.3.5