cScm Configuration Daemon

cScm – is a tool to convert SCM configuration files into binary format and store its in shared memory for reading by cSvn-ui and cGit-ui CGI scripts

12 Commits   0 Branches   1 Tag
     5         kx 
     5         kx #ifdef HAVE_CONFIG_H
     5         kx #include <config.h>
     5         kx #endif
     5         kx 
     5         kx #include <stdlib.h>
     5         kx #include <stdio.h>
     5         kx #include <sys/sysinfo.h>
     5         kx #include <sys/types.h>
     5         kx #include <stdint.h>
     5         kx #include <dirent.h>
     5         kx #include <sys/stat.h> /* chmod(2)    */
     5         kx #include <fcntl.h>
     5         kx #include <limits.h>
     5         kx #include <string.h>   /* strdup(3)   */
     5         kx #include <libgen.h>   /* basename(3) */
     5         kx #include <ctype.h>    /* tolower(3)  */
     5         kx #include <errno.h>
     5         kx #include <time.h>
     5         kx #include <sys/time.h>
     5         kx #include <pwd.h>
     5         kx #include <grp.h>
     5         kx #include <stdarg.h>
     5         kx #include <unistd.h>
     5         kx 
     5         kx #include <sys/inotify.h>
     5         kx 
     5         kx #include <locale.h>
     5         kx #include <wchar.h>
     5         kx #include <wctype.h>
     5         kx 
     5         kx #include <sys/wait.h>
     5         kx 
     5         kx #include <sys/resource.h>
     5         kx 
     5         kx #include <signal.h>
     5         kx #if !defined SIGCHLD && defined SIGCLD
     5         kx # define SIGCHLD SIGCLD
     5         kx #endif
     5         kx 
     5         kx #define _GNU_SOURCE
     5         kx #include <getopt.h>
     5         kx 
     5         kx 
     5         kx #include <daemon.h>
     5         kx #include <msglog.h>
     5         kx #include <error.h>
     5         kx #include <utf8ing.h>
     5         kx #include <symtab.h>
     5         kx #include <parse.h>
     5         kx #include <lex.h>
     5         kx #include <bconf.h>
     5         kx 
     5         kx #include <defs.h>
     5         kx 
     5         kx 
     5         kx /*********************
     5         kx   Default File Names:
     5         kx  */
     5         kx const char *CONFIG_FILE = CSVN_CONFIG;
     5         kx const char *BCF_FILE    = CSVN_HOME_DIR "/" CSVN_PROGRAM ".bcf";
     5         kx const char *LOG_FILE    = CSCM_LOG_DIR  "/" CSVN_PROGRAM "d.log";
     5         kx const char *PID_FILE    = CSCM_PID_DIR  "/" CSVN_PROGRAM "d.pid";
     5         kx const char *SHM_BCF     = CSVN_SHM_BCF;
     5         kx 
     5         kx 
     5         kx char *program = PROGRAM_DAEMON;
     5         kx int   exit_status = EXIT_SUCCESS; /* errors counter */
     5         kx 
     5         kx FILE *config = NULL;
     5         kx char *config_fname = NULL;
     5         kx 
     5         kx char *log_fname = NULL;
     5         kx char *pid_fname = NULL;
     5         kx 
     5         kx static sigset_t  blockmask;
     5         kx 
     5         kx static int  run_as_daemon = 0;
     5         kx static int  test_config_file = 0;
     5         kx static int  config_inotify = 0;
     5         kx 
     5         kx 
     5         kx static void free_resources( void );
     5         kx 
     5         kx 
     5         kx /***********************
     5         kx   Inotify declarations:
     5         kx  */
     5         kx #define IN_BUFFER_SIZE  (7 * (sizeof(struct inotify_event) + NAME_MAX + 1))
     5         kx 
     5         kx static int inotify_fd = 0, wd = 0;
     5         kx static struct inotify_event *event = NULL;
     5         kx static char buf[IN_BUFFER_SIZE] __attribute__ ((aligned(8)));
     5         kx 
     5         kx static int check_event( struct inotify_event *e )
     5         kx {
     5         kx   if( e->mask & IN_CLOSE_WRITE ) return 1;
     5         kx   return 0;
     5         kx }
     5         kx 
     5         kx static void init_config_inotify( void )
     5         kx {
     5         kx   inotify_fd = inotify_init(); /* Create inotify instance */
     5         kx   if( inotify_fd == -1 )
     5         kx   {
     5         kx     ERROR( "Cannot initialize inotify for file: %s", config_fname );
     5         kx     LOG( "Stop cScm Configuration Daemon." );
     5         kx     free_resources();
     5         kx     exit( 1 );
     5         kx   }
     5         kx 
     5         kx   wd = inotify_add_watch( inotify_fd, config_fname, IN_CLOSE_WRITE );
     5         kx   if( wd == -1 )
     5         kx   {
     5         kx     ERROR( "Cannot add inotify watch for file: %s", config_fname );
     5         kx     LOG( "Stop cScm Configuration Daemon." );
     5         kx     free_resources();
     5         kx     exit( 1 );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void fini_config_inotify( void )
     5         kx {
     5         kx   if( wd > 0 )         { (void)inotify_rm_watch( inotify_fd, wd ); wd = 0; }
     5         kx   if( inotify_fd > 0 ) { (void)close( inotify_fd ); inotify_fd = 0;        }
     5         kx }
     5         kx 
     5         kx 
     5         kx static void free_resources( void )
     5         kx {
     5         kx   fini_config_inotify();
     5         kx 
     5         kx   if( log_fname )
     5         kx   {
     5         kx     if( errlog ) { fclose( errlog ); errlog = NULL; }
     5         kx     free( log_fname ); log_fname = NULL;
     5         kx   }
     5         kx   if( config_fname )
     5         kx   {
     5         kx     if( config ) { fclose( config ); config = NULL; }
     5         kx     free( config_fname ); config_fname = NULL;
     5         kx   }
     5         kx   bcf_shm_free();
     5         kx   if( bcf_fname )
     5         kx   {
     5         kx     if( bcf ) { fclose( bcf ); bcf = NULL; }
     5         kx     (void)unlink( (const char *)bcf_fname );
     5         kx     free( bcf_fname ); bcf_fname = NULL;
     5         kx   }
     5         kx   if( pid_fname )
     5         kx   {
     5         kx     (void)unlink( (const char *)pid_fname );
     5         kx     free( pid_fname ); pid_fname = NULL;
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx void usage()
     5         kx {
     5         kx   free_resources();
     5         kx 
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Usage: %s [options]\n", program );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Start cScm Configuration Daemon to read config file.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Options:\n" );
     5         kx   fprintf( stdout, "  -h,--help                     Display this information.\n" );
     5         kx   fprintf( stdout, "  -v,--version                  Display the version of %s utility.\n", program );
     5         kx   fprintf( stdout, "  -d,--daemonize                Run in background as a daemon.\n" );
     5         kx   fprintf( stdout, "  -i,--inotify                  Notify about configuration changes.\n" );
     5         kx   fprintf( stdout, "  -b,--bcf=<BCF_FILE>           Binary config file. Default: %s.\n", BCF_FILE );
     5         kx   fprintf( stdout, "  -c,--config=<CONFIG_FILE>     Config file. Default: %s.\n", CONFIG_FILE );
     5         kx   fprintf( stdout, "  -l,--log=<LOG_FILE>           Log file. Default: %s.\n", LOG_FILE );
     5         kx   fprintf( stdout, "  -p,--pid=<PID_FILE>           PID file. Default: %s.\n", PID_FILE );
     5         kx   fprintf( stdout, "  -s,--scm=<SCM>                SCM 'svn' or 'git'. Default: 'svn'.\n" );
     5         kx   fprintf( stdout, "  -t,--test                     Test config file and exit.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx 
     5         kx   exit( EXIT_FAILURE );
     5         kx }
     5         kx 
     5         kx void to_lowercase( char *s )
     5         kx {
     5         kx   char *p = s;
     5         kx   while( p && *p ) { int c = *p; *p = tolower( c ); ++p; }
     5         kx }
     5         kx 
     5         kx void to_uppercase( char *s )
     5         kx {
     5         kx   char *p = s;
     5         kx   while( p && *p ) { int c = *p; *p = toupper( c ); ++p; }
     5         kx }
     5         kx 
     5         kx void version()
     5         kx {
     5         kx   char *upper = NULL;
     5         kx 
     5         kx   upper = (char *)alloca( strlen( program ) + 1 );
     5         kx 
     5         kx   strcpy( (char *)upper, (const char *)program );
     5         kx   to_uppercase( upper );
     5         kx 
     5         kx   fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION );
     5         kx 
     5         kx   fprintf( stdout, "Copyright (C) 2022 Andrey V.Kosteltsev.\n" );
     5         kx   fprintf( stdout, "This is free software.   There is NO warranty; not even\n" );
     5         kx   fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx 
     5         kx   free_resources();
     5         kx   exit( EXIT_SUCCESS );
     5         kx }
     5         kx 
     5         kx void get_args( int argc, char *argv[] )
     5         kx {
     5         kx   const char* short_options = "hvdic:l:p:s:t";
     5         kx 
     5         kx   const struct option long_options[] =
     5         kx   {
     5         kx     { "help",               no_argument,       NULL, 'h' },
     5         kx     { "version",            no_argument,       NULL, 'v' },
     5         kx     { "daemonize",          no_argument,       NULL, 'd' },
     5         kx     { "inotify",            no_argument,       NULL, 'i' },
     5         kx     { "bcf",                required_argument, NULL, 'b' },
     5         kx     { "config",             required_argument, NULL, 'c' },
     5         kx     { "log",                required_argument, NULL, 'l' },
     5         kx     { "pid",                required_argument, NULL, 'p' },
     5         kx     { "scm",                required_argument, NULL, 's' },
     5         kx     { "test",               no_argument,       NULL, 't' },
     5         kx     { NULL,                 0,                 NULL,  0  }
     5         kx   };
     5         kx 
     5         kx   int ret;
     5         kx   int option_index = 0;
     5         kx 
     5         kx   while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 )
     5         kx   {
     5         kx     switch( ret )
     5         kx     {
     5         kx       case 'h':
     5         kx       {
     5         kx         usage();
     5         kx         break;
     5         kx       }
     5         kx       case 'v':
     5         kx       {
     5         kx         version();
     5         kx         break;
     5         kx       }
     5         kx       case 'd':
     5         kx       {
     5         kx         run_as_daemon = 1;
     5         kx         break;
     5         kx       }
     5         kx       case 'i':
     5         kx       {
     5         kx         config_inotify = 1;
     5         kx         break;
     5         kx       }
     5         kx       case 't':
     5         kx       {
     5         kx         test_config_file = 1;
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 'b':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           bcf_fname = strdup( optarg );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 'c':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           config_fname = strdup( optarg );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 'l':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           log_fname = strdup( optarg );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 'p':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           pid_fname = strdup( optarg );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 's':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           if( *optarg && !strncmp( optarg, "git", 3 ) )
     5         kx           {
     5         kx             CONFIG_FILE = CGIT_CONFIG;
     5         kx                BCF_FILE = CGIT_HOME_DIR "/" CGIT_PROGRAM ".bcf";
     5         kx                LOG_FILE = CSCM_LOG_DIR  "/" CGIT_PROGRAM "d.log";
     5         kx                PID_FILE = CSCM_PID_DIR  "/" CGIT_PROGRAM "d.pid";
     5         kx                 SHM_BCF = CGIT_SHM_BCF;
     5         kx           }
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case '?': default:
     5         kx       {
     5         kx         usage();
     5         kx         break;
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx static int is_directory( const char *path )
     5         kx {
     5         kx   struct stat st;
     5         kx   return ( !stat( path, &st ) && S_ISDIR(st.st_mode) );
     5         kx }
     5         kx 
     5         kx static void logpid( void )
     5         kx {
     5         kx   FILE *fp;
     5         kx 
     5         kx   if( !pid_fname )
     5         kx   {
     5         kx     /* allocate memory for PID file name */
     5         kx     pid_fname = (char *)malloc( strlen( PID_FILE ) + 1 );
     5         kx     if( !pid_fname ) { FATAL_ERROR( "Cannot allocate memory for PID file name: %s", PID_FILE ); }
     5         kx     (void)strcpy( pid_fname, PID_FILE );
     5         kx   }
     5         kx 
     5         kx   /* Create CSCM_PID_DIR if not exists: */
     5         kx   {
     5         kx     char  *pid_dir = NULL, *dir = strdup( pid_fname );
     5         kx 
     5         kx     pid_dir = dirname( dir );
     5         kx 
     5         kx     if( !is_directory( (const char *)pid_dir ) )
     5         kx     {
     5         kx       /* Create if not exists */
     5         kx       if( 0 != mkdir( (const char *)pid_dir, 0755 ) )
     5         kx       {
     5         kx         FATAL_ERROR( "Cannot create directory for PID file: %s", PID_FILE );
     5         kx       }
     5         kx     }
     5         kx     free( dir );
     5         kx   }
     5         kx 
     5         kx   if( (fp = fopen( pid_fname, "w" )) != NULL )
     5         kx   {
     5         kx     fprintf( fp, "%u\n", getpid() );
     5         kx     (void)fclose( fp );
     5         kx   }
     5         kx }
     5         kx 
     5         kx void init_logs( void )
     5         kx {
     5         kx   /* print to stderr until the errlog file is open */
     5         kx   errlog = stderr;
     5         kx 
     5         kx   if( !log_fname )
     5         kx   {
     5         kx     /* allocate memory for LOG file name */
     5         kx     log_fname = (char *)malloc( strlen( LOG_FILE ) + 1 );
     5         kx     if( !log_fname ) { FATAL_ERROR( "Cannot open log file: %s", LOG_FILE ); }
     5         kx     (void)strcpy( log_fname, LOG_FILE );
     5         kx   }
     5         kx 
     5         kx   /* Create CSCM_LOG_DIR if not exists: */
     5         kx   {
     5         kx     char  *log_dir = NULL, *dir = strdup( log_fname );
     5         kx 
     5         kx     log_dir = dirname( dir );
     5         kx 
     5         kx     if( !is_directory( (const char *)log_dir ) )
     5         kx     {
     5         kx       /* Create if not exists */
     5         kx       if( 0 != mkdir( (const char *)log_dir, 0755 ) )
     5         kx       {
     5         kx         FATAL_ERROR( "Cannot create directory for log file: %s", LOG_FILE );
     5         kx       }
     5         kx     }
     5         kx     free( dir );
     5         kx   }
     5         kx 
     5         kx   /* open LOG file */
     5         kx   errlog = fopen( (const char *)log_fname, "a" );
     5         kx   if( !errlog ) { errlog = stderr; FATAL_ERROR( "Cannot open log file: %s", log_fname ); }
     5         kx }
     5         kx 
     5         kx void open_config_file( void )
     5         kx {
     5         kx   if( !config_fname )
     5         kx   {
     5         kx     /* allocate memory for CONFIG file name */
     5         kx     config_fname = (char *)malloc( strlen( CONFIG_FILE ) + 1 );
     5         kx     if( !config_fname )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot open config file: %s", CONFIG_FILE );
     5         kx       if( log_fname ) { fclose( errlog ); free( log_fname ); }
     5         kx     }
     5         kx     (void)strcpy( config_fname, CONFIG_FILE );
     5         kx   }
     5         kx 
     5         kx   /* open CONFIG file */
     5         kx   config = fopen( (const char *)config_fname, "r" );
     5         kx   if( !config )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot open config file: %s", config_fname );
     5         kx     if( log_fname ) { fclose( errlog ); free( log_fname ); }
     5         kx   }
     5         kx 
     5         kx   if( !bcf_fname )
     5         kx   {
     5         kx     /* allocate memory for BCF file name */
     5         kx     bcf_fname = (char *)malloc( strlen( BCF_FILE ) + 1 );
     5         kx     if( !bcf_fname )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot allocate memory gor BCF file name: %s", BCF_FILE );
     5         kx       if( log_fname ) { fclose( errlog ); free( log_fname ); }
     5         kx     }
     5         kx     (void)strcpy( bcf_fname, BCF_FILE );
     5         kx   }
     5         kx }
     5         kx 
     5         kx void close_config_file( void )
     5         kx {
     5         kx   if( config ) { fclose( config ); config = NULL; }
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx void parse_config_file( void )
     5         kx {
     5         kx   int ret;
     5         kx 
     5         kx   /***********************************
     5         kx     Blick signals until parser works:
     5         kx    */
     5         kx   (void)sigprocmask( SIG_BLOCK, (const sigset_t *)&blockmask, (sigset_t *)NULL );
     5         kx 
     5         kx   open_config_file();
     5         kx   init_symtab();
     5         kx   init_lex();
     5         kx 
     5         kx   if( !(ret = yyparse()) )
     5         kx   {
     5         kx     reverse_symlist( (SYMBOL **)&symlist );
     5         kx     remove_consts( (SYMBOL **)&symlist );
     5         kx     (void)write_binary_config();
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     bcf_shm_free();
     5         kx     if( bcf_fname )
     5         kx     {
     5         kx       if( bcf ) { fclose( bcf ); bcf = NULL; }
     5         kx       (void)unlink( (const char *)bcf_fname );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   fini_lex();
     5         kx   fini_symtab();
     5         kx   close_config_file();
     5         kx 
     5         kx   (void)sigprocmask( SIG_UNBLOCK, (const sigset_t *)&blockmask, (sigset_t *)NULL );
     5         kx 
     5         kx   if( test_config_file )
     5         kx   {
     5         kx     if( ret )
     5         kx     {
     5         kx       fprintf( stdout, "%s: %s: Config file is not correct. See: %s\n", program, config_fname, log_fname );
     5         kx       LOG( "Stop cScm Configuration Daemon." );
     5         kx       free_resources();
     5         kx       exit( 1 );
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "%s: %s: Config file is correct.\n", program, config_fname );
     5         kx       LOG( "Stop cScm Configuration Daemon." );
     5         kx       free_resources();
     5         kx       exit( 0 );
     5         kx     }
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx void fatal_error_actions( void )
     5         kx {
     5         kx   free_resources();
     5         kx }
     5         kx 
     5         kx void sigint( int signum )
     5         kx {
     5         kx   (void)signum;
     5         kx 
     5         kx   LOG( "received SIGINT: free resources at exit" );
     5         kx   LOG( "Stop cScm Configuration Daemon." );
     5         kx 
     5         kx   free_resources();
     5         kx   exit( 0 );
     5         kx }
     5         kx 
     5         kx void sigusr( int signum )
     5         kx {
     5         kx   if( signum == SIGUSR1 )
     5         kx   {
     5         kx     LOG( "signal USR1 has been received" );
     5         kx   }
     5         kx   else if( signum == SIGUSR2 )
     5         kx   {
     5         kx     LOG( "signal USR2 has been received" );
     5         kx   }
     5         kx }
     5         kx 
     5         kx void sighup( int signum )
     5         kx {
     5         kx   (void)signum;
     5         kx 
     5         kx   LOG( "received SIGHUP: parse config file: %s", config_fname );
     5         kx 
     5         kx   parse_config_file();
     5         kx }
     5         kx 
     5         kx static void set_signal_handlers()
     5         kx {
     5         kx   struct sigaction  sa;
     5         kx   sigset_t          set;
     5         kx 
     5         kx   memset( &sa, 0, sizeof( sa ) );
     5         kx   sa.sa_handler = sighup;          /* HUP: read config file */
     5         kx   sa.sa_flags = SA_RESTART;
     5         kx   sigemptyset( &set );
     5         kx   sigaddset( &set, SIGHUP );
     5         kx   sa.sa_mask = set;
     5         kx   sigaction( SIGHUP, &sa, NULL );
     5         kx 
     5         kx   memset( &sa, 0, sizeof( sa ) );
     5         kx   sa.sa_handler = sigusr;          /* USR1, USR2 */
     5         kx   sa.sa_flags = SA_RESTART;
     5         kx   sigemptyset( &set );
     5         kx   sigaddset( &set, SIGUSR1 );
     5         kx   sigaddset( &set, SIGUSR2 );
     5         kx   sa.sa_mask = set;
     5         kx   sigaction( SIGUSR1, &sa, NULL );
     5         kx   sigaction( SIGUSR2, &sa, NULL );
     5         kx 
     5         kx   memset( &sa, 0, sizeof( sa ) );
     5         kx   sa.sa_handler = sigint;          /* TERM, INT */
     5         kx   sa.sa_flags = SA_RESTART;
     5         kx   sigemptyset( &set );
     5         kx   sigaddset( &set, SIGTERM );
     5         kx   sigaddset( &set, SIGINT );
     5         kx   sa.sa_mask = set;
     5         kx   sigaction( SIGTERM, &sa, NULL );
     5         kx   sigaction( SIGINT, &sa,  NULL );
     5         kx 
     5         kx   memset( &sa, 0, sizeof( sa ) );  /* ignore SIGPIPE */
     5         kx   sa.sa_handler = SIG_IGN;
     5         kx   sa.sa_flags = 0;
     5         kx   sigaction( SIGPIPE, &sa, NULL );
     5         kx 
     5         kx   /* на случай блокировки сигналов с помощью sigprocmask(): */
     5         kx   sigemptyset( &blockmask );
     5         kx   sigaddset( &blockmask, SIGHUP );
     5         kx   sigaddset( &blockmask, SIGUSR1 );
     5         kx   sigaddset( &blockmask, SIGUSR2 );
     5         kx   sigaddset( &blockmask, SIGTERM );
     5         kx   sigaddset( &blockmask, SIGINT );
     5         kx 
     5         kx   /* System V fork+wait does not work if SIGCHLD is ignored */
     5         kx   signal( SIGCHLD, SIG_DFL );
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx int main( int argc, char *argv[] )
     5         kx {
     5         kx   gid_t  gid;
     5         kx 
     5         kx   set_signal_handlers();
     5         kx 
     5         kx   gid = getgid();
     5         kx   setgroups( 1, &gid );
     5         kx 
     5         kx   fatal_error_hook = fatal_error_actions;
     5         kx 
     5         kx   program = basename( argv[0] );
     5         kx   get_args( argc, argv );
     5         kx 
     5         kx   if( getppid() != 1 )
     5         kx   {
     5         kx     if( run_as_daemon ) daemon( 1, 0 );
     5         kx   }
     5         kx 
     5         kx   init_logs();
     5         kx   logpid();
     5         kx 
     5         kx   LOG( "Start cScm Configuration Daemon..." );
     5         kx 
     5         kx   parse_config_file();
     5         kx 
     5         kx   if( config_inotify ) init_config_inotify();
     5         kx 
     5         kx   for( ;; )
     5         kx   {
     5         kx     int    max_sd;
     5         kx     struct timeval timeout;
     5         kx     fd_set listen_set;
     5         kx 
     5         kx     /*************************************
     5         kx       Waiting for events from inotify_fd:
     5         kx      */
     5         kx     max_sd = inotify_fd + 1;
     5         kx     FD_ZERO( &listen_set );
     5         kx     FD_SET( inotify_fd, &listen_set );
     5         kx 
     5         kx     do
     5         kx     {
     5         kx       int rc;
     5         kx 
     5         kx       /*********************************************
     5         kx         Initialize the timeval struct to 5 seconds:
     5         kx        */
     5         kx       timeout.tv_sec  = 5;
     5         kx       timeout.tv_usec = 0;
     5         kx 
     5         kx       rc = select( max_sd, &listen_set, NULL, NULL, &timeout );
     5         kx 
     5         kx       /*****************************************
     5         kx         Check to see if the select call failed:
     5         kx        */
     5         kx       if( rc < 0 && errno != EINTR )
     5         kx       {
     5         kx         WARNING( "%s: inotify select() failed", config_fname );
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       /************************************************
     5         kx         Check to see if the 5 second time out expired:
     5         kx        */
     5         kx       if( rc == 0 )
     5         kx       {
     5         kx         /* Here we can output some log info. */
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       if( FD_ISSET( inotify_fd, &listen_set ) )
     5         kx       {
     5         kx         ssize_t  n;
     5         kx         char    *p;
     5         kx 
     5         kx         n = read( inotify_fd, buf, IN_BUFFER_SIZE );
     5         kx         if( n == 0 )
     5         kx         {
     5         kx           ERROR( "%s: read() from inotify file descriptor returned '0'", config_fname );
     5         kx           LOG( "Stop cScm Configuration Daemon." );
     5         kx           free_resources();
     5         kx           exit( 1 );
     5         kx         }
     5         kx         if( n == -1 )
     5         kx         {
     5         kx           ERROR( "%s: read() from inotify file descriptor returned '-1'", config_fname );
     5         kx           LOG( "Stop cScm Configuration Daemon." );
     5         kx           free_resources();
     5         kx           exit( 1 );
     5         kx         }
     5         kx 
     5         kx         for( p = buf; p < buf + n; )
     5         kx         {
     5         kx           event = (struct inotify_event *)p;
     5         kx           /************************************************************
     5         kx             в принципе, нам хватает одного события и, если мы получили
     5         kx             нужное событие и перечитали config file, то здесь мы можем
     5         kx             выйти из цикла чтения событий с помощью break
     5         kx            */
     5         kx           if( check_event( event ) )
     5         kx           {
     5         kx             LOG( "Config file '%s' has been changed. Read new config content...", config_fname );
     5         kx             parse_config_file();
     5         kx             break;
     5         kx           }
     5         kx           p += sizeof(struct inotify_event) + event->len;
     5         kx         }
     5         kx 
     5         kx       } /* End if( FD_ISSET() ) */
     5         kx 
     5         kx     } while( 1 );
     5         kx 
     5         kx     /*
     5         kx       Здесь мы можем выполнить действия, которые необходимы
     5         kx       в том случае, если в течение 5-и секунд мы не получили
     5         kx       ни одного сигнала об изменении дескриптора inotify_fd.
     5         kx      */
     5         kx     {
     5         kx       sleep( 5 );
     5         kx     }
     5         kx 
     5         kx   } /* End of waiting for( ;; ) */
     5         kx 
     5         kx 
     5         kx   LOG( "Stop cScm Configuration Daemon." );
     5         kx   free_resources();
     5         kx 
     5         kx   return 0;
     5         kx }