"Fossies" - the Fresh Open Source Software Archive

Member "evolution-brutus-1.2.35/log/main.c" (18 Dec 2008, 14280 Bytes) of archive /linux/misc/old/evolution-brutus-1.2.35.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "main.c" see the Fossies "Dox" file reference documentation.

    1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
    2 
    3 /*
    4  *    Implementation file for a Brutus log daemon.
    5  *    Copyright (C) 2007 OMC Denmark ApS.
    6  *
    7  *    This program is free software; you can redistribute it and/or modify
    8  *    it under the terms of the GNU General Public License as published by
    9  *    the Free Software Foundation; either version 2 of the License, or
   10  *    (at your option) any later version.
   11  *
   12  *    This program is distributed in the hope that it will be useful,
   13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
   14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   15  *    GNU General Public License for more details.
   16  *
   17  *    You should have received a copy of the GNU General Public License
   18  *    along with this program; if not, write to the Free Software
   19  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
   20  *    MA 02111-1307 USA
   21  */
   22 
   23 #ifdef HAVE_CONFIG_H
   24 #include <config.h>
   25 #endif
   26 
   27 #include <sys/types.h>
   28 #include <sys/wait.h>
   29 #include <sys/file.h>
   30 #include <stdlib.h>
   31 #include <errno.h>
   32 #include <unistd.h>
   33 #include <signal.h>
   34 #include <locale.h>
   35 #include <gcrypt.h>
   36 #include <glib.h>
   37 #include <glib/gstdio.h>
   38 #include "brutus-logd.h"
   39 
   40 #ifndef ORBIT2_EX
   41 #define ORBIT2_EX(_ev_) ((NULL != _ev_) && ((_ev_)->_major != CORBA_NO_EXCEPTION))
   42 #endif
   43 
   44 static GMutex *log_mutex = NULL;
   45 #include "brutus-log_impl.c"
   46 
   47 static const char *prog_name = NULL;
   48 static CORBA_ORB global_orb = CORBA_OBJECT_NIL;
   49 static int global_lock_fd = -1;
   50 static int ref_file_fd = -1;
   51 
   52 // shutdown global ORB
   53 static void
   54 orb_shutdown(int sig)
   55 {
   56     if (CORBA_OBJECT_NIL != global_orb) {
   57         CORBA_Environment ev[1];
   58 
   59         CORBA_exception_init(ev);
   60         CORBA_ORB_shutdown(global_orb, TRUE, ev);
   61         CORBA_exception_free(ev);
   62     }
   63 }
   64 
   65 static CORBA_ORB
   66 create_orb(CORBA_Environment *ev)
   67 {
   68     CORBA_ORB orb = CORBA_OBJECT_NIL;
   69     int init_argc = 2;
   70     char **init_argv = (char**)malloc(sizeof(char*) * (init_argc));
   71     if (!init_argv)
   72         return CORBA_OBJECT_NIL;
   73 
   74     init_argv[0] = "DUMMY_ARGV0";
   75 
   76     // Explicitly force ORBit2 to be local only
   77     init_argv[1] = "--ORBLocalOnly=1";
   78 
   79     // initialize the ORB
   80     orb = CORBA_ORB_init(&init_argc, init_argv, "brutus-log_orb_orbit-io-thread", ev);
   81     if (ORBIT2_EX(ev))
   82         orb = CORBA_OBJECT_NIL;
   83 
   84     free(init_argv);
   85 
   86     return orb;
   87 }
   88 
   89 static int
   90 object_ref_to_file(CORBA_ORB orb,
   91            CORBA_Object object,
   92            gchar *filename,
   93            CORBA_Environment *ev)
   94 {
   95     gboolean retv = FALSE;
   96     CORBA_char *objref = NULL;
   97     int file_fd = -1;
   98     FILE *file = NULL;
   99     size_t c = 0;
  100 
  101     file_fd = g_open(filename, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR);
  102     if (-1 == file_fd)
  103         return -1;
  104 
  105     file = fdopen(file_fd, "w");
  106     if (!file)
  107         goto out;
  108 
  109     objref = CORBA_ORB_object_to_string(orb, object, ev);
  110     if (ORBIT2_EX(ev) || !objref)
  111         goto out;
  112 
  113     // must work even if (sizeof(char) != sizeof(CORBA_char))
  114     c = fwrite((const void*)objref, sizeof(CORBA_char), corba_strlen(objref)+1, file);
  115     if (c == (1 + corba_strlen(objref))) {
  116         retv = TRUE;
  117         fsync(file_fd);
  118     }
  119 
  120 out:
  121     if (objref)
  122         CORBA_free(objref);
  123 
  124     if (file) {
  125         if (fclose(file))
  126             retv = FALSE;
  127     } else {
  128         if (close(file_fd))
  129             retv = FALSE;
  130     }
  131     if (!retv)
  132         return -1;
  133 
  134     return g_open(filename, O_RDWR | O_SYNC, S_IRUSR | S_IWUSR);
  135 }
  136 
  137 typedef enum {
  138     EXIT_DAEMON = 0, /* we are the daemon                  */
  139     EXIT_OK = 1,     /* caller must exit with EXIT_SUCCESS */
  140     EXIT_ERROR = 2,  /* caller must exit with EXIT_FAILURE */
  141 } daemon_exit_t;
  142 
  143 static daemon_exit_t
  144 become_daemon(void)
  145 {
  146     struct sigaction sig_act;
  147     struct rlimit rl;
  148     unsigned int n;
  149     pid_t pid = -1;
  150     int fd0;
  151     int fd1;
  152     int fd2;
  153 
  154     /*
  155      * A process that has terminated but has not yet been waited for is a zombie.
  156      * On the other hand, if the parent dies first, init (process 1) inherits the
  157      * child and becomes its parent.
  158      *
  159      * (*) Citation from <http://www.win.tue.nl/~aeb/linux/lk/lk-10.html>
  160      */
  161 
  162     /* fork off the parent process to create the session daemon */
  163     pid = fork();
  164     switch (pid) {
  165     case -1 :
  166         return EXIT_ERROR;
  167     case 0 :    
  168         /* We are the intermediate child.
  169          * Now fork once more and try to ensure that this intermediate child
  170          * exits before the final child so that the final child is adopted
  171          * by init and does not become a zombie. This is racy as the final
  172          * child concievably could terminate (and become a zombie) before
  173          * this child exits. Knock on wood...
  174          */
  175 
  176         if ((setsid()) < 0)
  177             return EXIT_ERROR;
  178 
  179         sig_act.sa_handler = SIG_IGN;
  180         sigemptyset(&sig_act.sa_mask);
  181         sig_act.sa_flags = 0;
  182         if (sigaction(SIGHUP, &sig_act, NULL))
  183             return EXIT_ERROR;
  184 
  185         pid = fork();
  186         switch (pid) {
  187         case -1 :
  188             return EXIT_ERROR;
  189         case 0 :
  190             break;
  191         default :
  192             return EXIT_OK;
  193         }
  194 
  195         /*
  196          * (0 == pid) we are the final child
  197          */
  198 
  199         /* sleep for 1 second to give the parent plenty of time to exit */
  200         g_usleep(1000000);
  201 
  202         break;
  203     default :
  204         /* wait for intermediate child */
  205         waitpid(pid, NULL, 0);
  206 
  207         return EXIT_OK;
  208     }
  209 
  210     /*
  211      * We are now effectively the daemon and must continue
  212      * to prep the daemon process for operation
  213      */
  214 
  215     // change the working directory
  216     if ((chdir("/")) < 0)
  217         return EXIT_ERROR;
  218 
  219     // close any and all open file descriptors
  220     if (getrlimit(RLIMIT_NOFILE, &rl))
  221         return EXIT_ERROR;
  222     if (RLIM_INFINITY == rl.rlim_max)
  223         rl.rlim_max = 1024;
  224     for (n = 0; n < rl.rlim_max; n++) {
  225         if (close(n) && (EBADF != errno))
  226             return EXIT_ERROR;
  227     }
  228 
  229     // attach file descriptors 0, 1 and 2 to /dev/null
  230     fd0 = open("/dev/null", O_RDWR);
  231     fd1 = dup2(fd0, 1);
  232     fd2 = dup2(fd0, 2);
  233     if (0 != fd0)
  234         return EXIT_ERROR;
  235 
  236     return EXIT_DAEMON;
  237 }
  238 
  239 static int 
  240 lock_fd(const int fd)
  241 {
  242     struct flock lock = {
  243         .l_type = F_WRLCK,
  244         .l_start = 0,
  245         .l_whence = SEEK_SET,
  246         .l_len = 0,
  247     };
  248 
  249     if (-1 == fd)
  250         return -1;
  251 
  252     return fcntl(fd, F_SETLK, &lock);
  253 }
  254 
  255 static int
  256 unlock_fd(const int fd)
  257 {
  258     struct flock lock = {
  259         .l_type = F_UNLCK,
  260         .l_start = 0,
  261         .l_whence = SEEK_SET,
  262         .l_len = 0,
  263     };
  264 
  265     if (-1 == fd)
  266         return -1;
  267 
  268     return fcntl(fd, F_SETLK, &lock);
  269 }
  270 
  271 /*
  272  * Will attempt to lock the PID file and write the
  273  * pid into it. This function will return FALSE for
  274  * all errors. Callee is responsible for closing *fd 
  275  * if (*fd != -1).
  276  */
  277 static gboolean
  278 get_process_lock(int *fd)
  279 {
  280     char buf[32] = { '\0' };
  281     int p = 0;
  282     ssize_t w = 0;
  283     char *file_path = NULL;
  284 
  285     *fd = -1;
  286 
  287     file_path = g_strconcat(getenv("HOME"), "/", BRUTUS_LOGD_PID_FILE_NAME, NULL);
  288     if (!file_path)
  289         return FALSE;
  290 
  291     *fd = g_open(file_path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
  292     g_free(file_path);
  293     if (-1 == *fd)
  294         return FALSE;
  295 
  296     // lock it
  297     if (lock_fd(*fd))
  298         return FALSE;
  299 
  300     // we have the lock
  301     if (ftruncate(*fd, 0))
  302         return FALSE;
  303 
  304     /* write pid */
  305     p = snprintf(buf, sizeof(buf), "%d", getpid());
  306     w = write(*fd, buf, strlen(buf) + sizeof(char));
  307     p += sizeof(char);
  308     if (p != (int)w)
  309         return FALSE;
  310 
  311     if (fdatasync(*fd))
  312         return FALSE;
  313 
  314     return TRUE;
  315 }
  316 
  317 /*
  318  * This handler will only be called after the signal handler
  319  * is installed. The signal handler is not installed until
  320  * the lock on th epid file is in place.
  321  */
  322 static void
  323 restart(int sig)
  324 {
  325     unlock_fd(ref_file_fd); // unsafe in signal handler
  326     close(ref_file_fd);
  327     ref_file_fd = -1;
  328 
  329     unlock_fd(global_lock_fd); // unsafe in signal handler
  330     close(global_lock_fd);
  331     global_lock_fd = -1;
  332 
  333     if (-1 == system(prog_name)) // unsafe in signal handler
  334         ;
  335     orb_shutdown(sig); // unsafe in signal handler
  336 }
  337 
  338 static int
  339 open_fifo(void)
  340 {
  341     int fd = -1;
  342     char *fifo_file_path = NULL;
  343 
  344     fifo_file_path = g_strconcat(getenv("HOME"), "/", BRUTUS_LOGD_FIFO_FILE_NAME, NULL);
  345     if (!fifo_file_path)
  346         goto out;
  347 
  348     if (mknod(fifo_file_path, S_IFIFO | S_IRUSR | S_IWUSR, 0) && (EEXIST != errno))
  349         goto out;
  350 
  351     fd = g_open(fifo_file_path, O_RDWR, S_IRUSR | S_IWUSR);
  352 out:
  353     if (fifo_file_path)
  354         g_free(fifo_file_path);
  355 
  356     return fd;
  357 }
  358 
  359 /*
  360  * Will read from the fifo and write to the log file.
  361  * The written data is not guaranteed to be null-terminated.
  362  */
  363 static gpointer
  364 fifo_thread(gpointer data)
  365 {
  366     int fd = *((int*)data);
  367     int log = open_log();
  368     ssize_t num = 0;
  369     char buf[BRUTUS_LOGD_FIFO_MAX_BUF];
  370 
  371     if (0 >= fd)
  372         return NULL;
  373 
  374     do {    
  375         memset((void*)buf, '\0', BRUTUS_LOGD_FIFO_MAX_BUF);
  376 
  377         num = read(fd, (void*)buf, BRUTUS_LOGD_FIFO_MAX_BUF);
  378         if (-1 == num)
  379             break;
  380 
  381         while (!g_mutex_trylock(log_mutex))
  382             g_usleep(10);   
  383 
  384         if (-1 == write(log, (const void*)buf, num))
  385             ; // to avoid compiler warning
  386         fdatasync(log);
  387         
  388         g_mutex_unlock(log_mutex);
  389     } while (num > 0);
  390 
  391     if (-1 != fd)
  392         close(fd);
  393 
  394     return NULL;
  395 }
  396 
  397 static void
  398 truncate_log(void)
  399 {
  400     const char *log_file_name = NULL;
  401     char *log_file_path = NULL;
  402 
  403     while (!g_mutex_trylock(log_mutex))
  404         g_usleep(10);   
  405 
  406     log_file_name = getenv("BRUTUS_LOG_FILE_NAME");
  407     if (!log_file_name)
  408         log_file_name = BRUTUS_LOG_FILE;
  409     log_file_path = g_strconcat(getenv("HOME"), "/", BRUTUS_LOGD_DIR_NAME, "/", log_file_name, NULL);
  410     if (!log_file_path)
  411         goto out;
  412 
  413     if (-1 == truncate(log_file_path, 0))
  414         ;
  415     g_free(log_file_path);
  416 out:
  417     g_mutex_unlock(log_mutex);
  418 }
  419 
  420 int
  421 main(int argc, char *argv[])
  422 {
  423     PortableServer_POA root_poa = CORBA_OBJECT_NIL;
  424     BRUTUS_BrutusLog log_obj = CORBA_OBJECT_NIL;
  425     CORBA_Environment ev[1];
  426     gboolean daemon = TRUE;
  427     int retv = EXIT_FAILURE;
  428     gchar *ref_file_path = NULL;
  429     sigset_t mask;
  430     struct sigaction sig_act;
  431     daemon_exit_t dstat = EXIT_DAEMON;
  432     int log_fifo = -1;
  433 
  434     /* clear the file mode mask */
  435     umask(0);
  436 
  437     if (1 < argc)
  438         daemon = FALSE;
  439 
  440     /* restore signals */
  441     sigfillset(&mask);
  442     pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
  443 
  444     /* for restart */
  445     prog_name = argv[0];
  446 
  447     /* create application directory */
  448     {
  449         gchar *dir_path = NULL;
  450         int retv = 1;
  451 
  452         dir_path = g_strconcat(getenv("HOME"), "/", BRUTUS_LOGD_DIR_NAME, NULL);
  453         if (!dir_path)
  454             exit(EXIT_FAILURE);
  455 
  456         retv = g_mkdir_with_parents(dir_path, 0700);
  457         g_free(dir_path);
  458         if (retv)
  459             exit(EXIT_FAILURE);
  460     }
  461 
  462     if (daemon)
  463         dstat = become_daemon();
  464     switch (dstat) {
  465     case EXIT_DAEMON :
  466         break;
  467     case EXIT_OK :
  468         exit(EXIT_SUCCESS);
  469     case EXIT_ERROR :
  470     default :
  471         exit(EXIT_FAILURE);
  472     }
  473 
  474     /*
  475      * We are now the would-be log daemon
  476      */
  477 
  478     // take the lock and write the pid
  479     if (!get_process_lock(&global_lock_fd)) {
  480         if (-1 != global_lock_fd) {
  481             close(global_lock_fd);
  482             global_lock_fd = -1;
  483         }
  484         exit(EXIT_SUCCESS);
  485     }
  486 
  487     /*
  488      * We are now ensured that we are the only log daemon in the air
  489      * unless HOME is on NFS...
  490      *
  491      * All exits must be taken by "goto out;".
  492      */
  493 
  494     // make sure that the thread system is initialized
  495     if (!g_thread_supported()) 
  496         g_thread_init(NULL);
  497 
  498     CORBA_exception_init(ev);
  499 
  500     log_mutex = g_mutex_new();
  501     if (!log_mutex)
  502         goto out;
  503 
  504     truncate_log();
  505 
  506     /*
  507      * Initialize and activate FIFO logging
  508      */
  509     log_fifo = open_fifo();
  510     if (-1 != log_fifo) { // launch reader thread
  511         g_thread_create(fifo_thread,
  512                 (gpointer)&log_fifo,
  513                 FALSE,
  514                 NULL);
  515     } else
  516         goto out;
  517 
  518     /*
  519      * initialize CORBA
  520      */
  521 
  522     global_orb = create_orb(ev);
  523     if (ORBIT2_EX(ev))
  524         goto out;
  525     {
  526         PortableServer_POAManager poa_manager = CORBA_OBJECT_NIL;
  527 
  528         root_poa = (PortableServer_POA) CORBA_ORB_resolve_initial_references(global_orb, "RootPOA", ev);
  529         if (ORBIT2_EX(ev))
  530             goto out;
  531 
  532         poa_manager = PortableServer_POA__get_the_POAManager(root_poa, ev);
  533         if (ORBIT2_EX(ev))
  534             goto out;
  535 
  536         PortableServer_POAManager_activate(poa_manager, ev);
  537         if (ORBIT2_EX(ev))
  538             goto out;
  539 
  540         CORBA_Object_release((CORBA_Object)poa_manager, ev);
  541         if (ORBIT2_EX(ev))
  542             goto out;
  543     }
  544 
  545     /* create log servant */
  546     log_obj = impl_BRUTUS_BrutusLog__create(root_poa, ev);
  547     if (ORBIT2_EX(ev))
  548         goto out;
  549     if (CORBA_OBJECT_NIL == log_obj)
  550         goto out;
  551 
  552     /* write ior to file */
  553     ref_file_path = g_strconcat(getenv("HOME"), "/", BRUTUS_LOGD_REF_FILE_NAME, NULL);
  554     if (!ref_file_path)
  555         goto out;
  556     ref_file_fd = object_ref_to_file(global_orb, log_obj, ref_file_path, ev);
  557     if (-1 == ref_file_fd)
  558         goto out;
  559 
  560     /*
  561      * handle signals
  562      */
  563 
  564     sig_act.sa_handler = orb_shutdown;
  565     sigfillset(&sig_act.sa_mask); // ignore as many signals as possible in the handler!
  566     sig_act.sa_flags = SA_RESETHAND;
  567     sigaction(SIGUSR1, &sig_act, NULL);
  568 
  569     /* restart the daemon if SIGUSR2 is caught */
  570     if (daemon)
  571         sig_act.sa_handler = restart;
  572     else
  573         sig_act.sa_handler = orb_shutdown;
  574     sigfillset(&sig_act.sa_mask);
  575     sig_act.sa_flags = SA_RESETHAND;
  576     sigaction(SIGUSR2, &sig_act, NULL);
  577 
  578     sig_act.sa_handler = orb_shutdown;
  579     sigfillset(&sig_act.sa_mask);
  580     sig_act.sa_flags = SA_RESETHAND;
  581     sigaction(SIGTERM, &sig_act, NULL);
  582 
  583     sig_act.sa_handler = orb_shutdown;
  584     sigfillset(&sig_act.sa_mask);
  585     sig_act.sa_flags = SA_RESETHAND;
  586     sigaction(SIGINT, &sig_act, NULL);
  587 
  588     /* loop until killed */
  589     lock_fd(ref_file_fd);
  590     CORBA_ORB_run(global_orb, ev);
  591     unlock_fd(ref_file_fd);
  592 
  593     /* cleanup */
  594     {
  595         PortableServer_ObjectId *objid = NULL;
  596 
  597         objid = PortableServer_POA_reference_to_id(root_poa, (CORBA_Object)log_obj, ev);
  598         if (ORBIT2_EX(ev)) {
  599             if (objid)
  600                 CORBA_free(objid);
  601             goto out;
  602         }
  603 
  604         PortableServer_POA_deactivate_object(root_poa, objid, ev);
  605         CORBA_free(objid);
  606         if (ORBIT2_EX(ev))
  607             goto out;
  608 
  609         CORBA_Object_release((CORBA_Object)log_obj, ev);
  610         if (ORBIT2_EX(ev))
  611             goto out;
  612 
  613         PortableServer_POA_destroy(root_poa, TRUE, FALSE, ev);
  614         if (ORBIT2_EX(ev))
  615             goto out;
  616 
  617         CORBA_Object_release((CORBA_Object)root_poa, ev);
  618         if (ORBIT2_EX(ev))
  619             goto out;
  620 
  621         /* ORB: tear down the ORB */
  622         if (CORBA_OBJECT_NIL != global_orb) {
  623             CORBA_ORB_destroy(global_orb, ev);
  624             if (ORBIT2_EX(ev))
  625                 goto out;
  626 
  627             CORBA_Object_release((CORBA_Object)global_orb, ev);
  628             if (ORBIT2_EX(ev))
  629                 goto out;
  630             global_orb = CORBA_OBJECT_NIL;
  631         }
  632     }
  633     retv = EXIT_SUCCESS;
  634 
  635 out:
  636     /*
  637      * We only ever end up here if we succefully took
  638      * the lock and became the daemon
  639      */
  640 
  641     CORBA_exception_free(ev);
  642 
  643     /*
  644      * There is a small race where the daemon could catch a signal just after
  645      * installing signal handlers but before placing the lock. Releasing the
  646      * lock unnecessarily should not harm us though.
  647      */
  648     if (-1 != ref_file_fd)
  649         close(ref_file_fd);
  650 
  651     if (-1 != log_fifo)
  652         close(log_fifo);
  653 
  654     if (ref_file_path) {
  655         if (-1 == truncate(ref_file_path, 0))
  656             ;
  657         g_free(ref_file_path);
  658     }
  659 
  660     if (-1 != global_lock_fd) {
  661         unlock_fd(global_lock_fd);
  662         close(global_lock_fd);
  663     }
  664 
  665     if (log_mutex)
  666         g_mutex_free(log_mutex);
  667 
  668     exit(retv);
  669 }