From 12c7b1c5658602269da2f5b75835ec0f5fab8890 Mon Sep 17 00:00:00 2001 From: kx Date: Fri, 24 Mar 2023 02:53:04 +0300 Subject: Version 0.1.4 --- cscmd/Makefile.am | 63 +++++ cscmd/README.in | 5 + cscmd/bconf.c | 477 +++++++++++++++++++++++++++++++ cscmd/bconf.h | 25 ++ cscmd/cscmd.8.in | 66 +++++ cscmd/daemon.c | 39 +++ cscmd/daemon.h | 7 + cscmd/error.c | 92 ++++++ cscmd/error.h | 25 ++ cscmd/lex.c | 815 +++++++++++++++++++++++++++++++++++++++++++++++++++++ cscmd/lex.h | 26 ++ cscmd/logrotate.in | 8 + cscmd/main.c | 737 ++++++++++++++++++++++++++++++++++++++++++++++++ cscmd/main.h | 21 ++ cscmd/msglog.c | 70 +++++ cscmd/msglog.h | 98 +++++++ cscmd/parse.y | 107 +++++++ cscmd/rc.cscmd.in | 96 +++++++ cscmd/symtab.c | 471 +++++++++++++++++++++++++++++++ cscmd/symtab.h | 67 +++++ cscmd/utf8ing.c | 121 ++++++++ cscmd/utf8ing.h | 22 ++ cscmd/xalloc.c | 36 +++ cscmd/xalloc.h | 19 ++ 24 files changed, 3513 insertions(+) create mode 100644 cscmd/Makefile.am create mode 100644 cscmd/README.in create mode 100644 cscmd/bconf.c create mode 100644 cscmd/bconf.h create mode 100644 cscmd/cscmd.8.in create mode 100644 cscmd/daemon.c create mode 100644 cscmd/daemon.h create mode 100644 cscmd/error.c create mode 100644 cscmd/error.h create mode 100644 cscmd/lex.c create mode 100644 cscmd/lex.h create mode 100644 cscmd/logrotate.in create mode 100644 cscmd/main.c create mode 100644 cscmd/main.h create mode 100644 cscmd/msglog.c create mode 100644 cscmd/msglog.h create mode 100644 cscmd/parse.y create mode 100644 cscmd/rc.cscmd.in create mode 100644 cscmd/symtab.c create mode 100644 cscmd/symtab.h create mode 100644 cscmd/utf8ing.c create mode 100644 cscmd/utf8ing.h create mode 100644 cscmd/xalloc.c create mode 100644 cscmd/xalloc.h (limited to 'cscmd') diff --git a/cscmd/Makefile.am b/cscmd/Makefile.am new file mode 100644 index 0000000..6fdd496 --- /dev/null +++ b/cscmd/Makefile.am @@ -0,0 +1,63 @@ + +AM_CPPFLAGS = -I@top_srcdir@ -DYYERROR_VERBOSE=1 + +sbin_PROGRAMS = cscmd + +cscmd_SOURCES = bconf.c daemon.c error.c lex.c main.c msglog.c symtab.c utf8ing.c xalloc.c + +noinst_HEADERS = bconf.h daemon.h error.h lex.h main.h msglog.h symtab.h utf8ing.h xalloc.h + +control_DATA = rc.cgitd rc.csvnd +logrotate_DATA = cgit csvn + +csvndhome_DATA = README.csvn +cgitdhome_DATA = README.cgit + +man8_MANS = cscmd.8 +notrans_nodist_man8_MANS = cscmd.8 + +nodist_cscmd_SOURCES = parse.c parse.h rc.csvnd csvn rc.cgitd cgit +BUILT_SOURCES = parse.c parse.h rc.csvnd csvn rc.cgitd cgit + +parse.c: parse.y + @BISON@ -lvy --defines=parse.h -o $@ $^ + +README.csvn: README.in + cat $^ | sed "s,\@CSCM_PROGRAM\@,${CSVN_PROGRAM},g" | \ + sed "s,\@CSCM_PROGRAM_NAME\@,${CSVN_PROGRAM_NAME},g" > $@ + +README.cgit: README.in + cat $^ | sed "s,\@CSCM_PROGRAM\@,${CGIT_PROGRAM},g" | \ + sed "s,\@CSCM_PROGRAM_NAME\@,${CGIT_PROGRAM_NAME},g" > $@ + +rc.csvnd: rc.cscmd.in + cat $^ | sed "s,\@sbindir\@,${sbindir},g" | \ + sed "s,\@CSCM_NAME\@,${CSVN_NAME},g" | \ + sed "s,\@CSCM_CONFIG\@,${CSVN_CONFIG},g" | \ + sed "s,\@CSCM_HOME_PATH\@,${CSCM_HOME_PATH},g" | \ + sed "s,\@CSCM_PID_DIR\@,${CSCM_PID_DIR},g" | \ + sed "s,\@CSCM_LOG_DIR\@,${CSCM_LOG_DIR},g" | \ + sed "s,\@CSCM_PROGRAM\@,${CSVN_PROGRAM},g" | \ + sed "s,\@CSCM_PROGRAM_NAME\@,${CSVN_PROGRAM_NAME},g" | \ + sed "s,\@PROGRAM_DAEMON\@,${PROGRAM_DAEMON},g" > $@ + +rc.cgitd: rc.cscmd.in + cat $^ | sed "s,\@sbindir\@,${sbindir},g" | \ + sed "s,\@CSCM_NAME\@,${CGIT_NAME},g" | \ + sed "s,\@CSCM_CONFIG\@,${CGIT_CONFIG},g" | \ + sed "s,\@CSCM_HOME_PATH\@,${CSCM_HOME_PATH},g" | \ + sed "s,\@CSCM_PID_DIR\@,${CSCM_PID_DIR},g" | \ + sed "s,\@CSCM_LOG_DIR\@,${CSCM_LOG_DIR},g" | \ + sed "s,\@CSCM_PROGRAM\@,${CGIT_PROGRAM},g" | \ + sed "s,\@CSCM_PROGRAM_NAME\@,${CGIT_PROGRAM_NAME},g" | \ + sed "s,\@PROGRAM_DAEMON\@,${PROGRAM_DAEMON},g" > $@ + +csvn: logrotate.in + cat $^ | sed "s,\@CSCM_LOG_DIR\@,${CSCM_LOG_DIR},g" | \ + sed "s,\@CSCM_PROGRAM\@,${CSVN_PROGRAM},g" > $@ + +cgit: logrotate.in + cat $^ | sed "s,\@CSCM_LOG_DIR\@,${CSCM_LOG_DIR},g" | \ + sed "s,\@CSCM_PROGRAM\@,${CGIT_PROGRAM},g" > $@ + +CLEANFILES = parse.c parse.h parse.output README.csvn rc.csvnd csvn README.cgit rc.cgitd cgit cscmd.8 diff --git a/cscmd/README.in b/cscmd/README.in new file mode 100644 index 0000000..185a415 --- /dev/null +++ b/cscmd/README.in @@ -0,0 +1,5 @@ + +@CSCM_PROGRAM_NAME@ Daemon HOME directory: +========================== + +@CSCM_PROGRAM@.bcf - is a binary config file created by @CSCM_PROGRAM_NAME@ Daemon diff --git a/cscmd/bconf.c b/cscmd/bconf.c new file mode 100644 index 0000000..8576805 --- /dev/null +++ b/cscmd/bconf.c @@ -0,0 +1,477 @@ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include +#include +#include +#include +#include /* strdup(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +extern const char *SHM_BCF; + +FILE *bcf = NULL; +char *bcf_fname = NULL; + +static void *bcf_shm_address = NULL; +static int bcf_shm_fd = -1; + +static int snum, rnum, dnum, global_dnum, global_rnum, indent; + +static unsigned char *ftab, *stab = NULL; +static Bcf32_Off stabsz = 0; + +static Bcf32_Off shoff; /* section header table’s file offset in bytes */ +static Bcf32_Off rhoff; /* repository header table’s file offset in bytes */ +static Bcf32_Off dtoff; /* data entries table’s file offset in bytes */ +static Bcf32_Off stoff; /* string table’s file offset in bytes */ + +static Bcf32_fhdr *fhdr; +static Bcf32_shdr *shdr; +static Bcf32_rhdr *rhdr; +static Bcf32_dntr *dntr; + + +void bcf_shm_free( void ) +{ + if( bcf_shm_address ) + { + struct stat st; + + if( !fstat( bcf_shm_fd, (struct stat *)&st ) ) + { + (void)munmap( bcf_shm_address, (size_t)st.st_size ); + bcf_shm_address = NULL; + bcf_shm_fd = shm_unlink( SHM_BCF ); + } + } + bcf_shm_address = NULL; + bcf_shm_fd = -1; +} + +/************************************************ + Функции создания BCF файла по таблице symlist: + */ + +static Bcf32_Off extend_strtab( const char *val ) +{ + Bcf32_Off off = 1; + Bcf32_Off len = 0; + unsigned char *dest = NULL; + + if( !stab ) + { + /************************************* + The first string in strtab is equal + to "" for empty strings. + */ + stabsz = (Bcf32_Off)(strlen( val ) + 2); + stab = (unsigned char *)xmalloc( (size_t)stabsz ); + (void)strncpy( (char *)&stab[1], val, stabsz - 1 ); + return off; + } + else + { + off = stabsz; + len = (Bcf32_Off)(strlen( val ) + 1); + stabsz += len; + + stab = (unsigned char *)xrealloc( (void *)stab, (size_t)stabsz ); + dest = &stab[off]; + (void)strncpy( (char *)dest, val, len ); + return off; + } +} + +static void count_symbols( int *snum, int *rnum, int *dnum, int *gdts, int *grps, SYMBOL *list ) +{ + SYMBOL *head = list; + + if( !head ) return; + + while( head ) + { + /************************************ + count symbols( head ): + */ + switch( head->type ) + { + case STRING: + if( indent == 0 ) *gdts += 1; + *dnum += 1; + break; + case PATH: + if( indent == 0 ) *gdts += 1; + *dnum += 1; + break; + case NUMERICAL: + if( indent == 0 ) *gdts += 1; + *dnum += 1; + break; + + case SECTION: + *snum += 1; + break; + case REPO: + if( indent == 0 ) *grps += 1; + *rnum += 1; + break; + + default: + break; + } + + if( head->list ) { indent += 1; count_symbols( snum, rnum, dnum, gdts, grps, head->list ); } + /* + End of count symbols( head ). + ************************************/ + + head = head->next; + } +} + +static void write_global_data( SYMBOL *list ) +{ + SYMBOL *head = list; + + if( !head ) return; + + while( head ) + { + /************************************ + global symbols( head ): + */ + switch( head->type ) + { + case STRING: + dntr->d_name = extend_strtab( (const char *)head->name ); + dntr->d_type = DT_STRING; + dntr->_v.d_valptr = extend_strtab( (const char *)head->u.string ); + dtoff += (Bcf32_Off)sizeof( Bcf32_dntr ); + ++dntr; + break; + case PATH: + dntr->d_name = extend_strtab( (const char *)head->name ); + dntr->d_type = DT_PATH; + dntr->_v.d_valptr = extend_strtab( (const char *)head->u.path ); + dtoff += (Bcf32_Off)sizeof( Bcf32_dntr ); + ++dntr; + break; + case NUMERICAL: + dntr->d_name = extend_strtab( (const char *)head->name ); + dntr->d_type = DT_NUMERICAL; + dntr->_v.d_value = head->u.value; + dtoff += (Bcf32_Off)sizeof( Bcf32_dntr ); + ++dntr; + break; + + default: + break; + } + /* + End of global symbols( head ). + ************************************/ + + head = head->next; + } +} + +static Bcf32_Half write_repo_data( SYMBOL *list ) +{ + Bcf32_Half cntr = 0; + SYMBOL *head = list; + + if( !head ) return cntr; + + while( head ) + { + /************************************ + symbols( head ): + */ + switch( head->type ) + { + case STRING: + dntr->d_name = extend_strtab( (const char *)head->name ); + dntr->d_type = DT_STRING; + dntr->_v.d_valptr = extend_strtab( (const char *)head->u.string ); + dtoff += (Bcf32_Off)sizeof( Bcf32_dntr ); + ++dntr; + ++cntr; + break; + case PATH: + dntr->d_name = extend_strtab( (const char *)head->name ); + dntr->d_type = DT_PATH; + dntr->_v.d_valptr = extend_strtab( (const char *)head->u.path ); + dtoff += (Bcf32_Off)sizeof( Bcf32_dntr ); + ++dntr; + ++cntr; + break; + case NUMERICAL: + dntr->d_name = extend_strtab( (const char *)head->name ); + dntr->d_type = DT_NUMERICAL; + dntr->_v.d_value = head->u.value; + dtoff += (Bcf32_Off)sizeof( Bcf32_dntr ); + ++dntr; + ++cntr; + break; + + default: + break; + } + /* + End of symbols( head ). + ************************************/ + + head = head->next; + } + + return cntr; +} + +static void write_global_repos( SYMBOL *list ) +{ + SYMBOL *head = list; + + if( !head ) return; + + while( head ) + { + /************************************ + global symbols( head ): + */ + if( head->type == REPO ) + { + rhdr->r_rhdr = extend_strtab( (const char *)head->u.path ); + rhdr->r_rdata = dtoff; + rhdr->r_dnum = write_repo_data( head->list ); + + rhoff += (Bcf32_Off)sizeof( Bcf32_rhdr ); + ++rhdr; + } + /* + End of global symbols( head ). + ************************************/ + + head = head->next; + } +} + + +static Bcf32_Half write_repos( SYMBOL *list ) +{ + Bcf32_Half cntr = 0; + SYMBOL *head = list; + + if( !head ) return cntr; + + while( head ) + { + /************************************ + symbols( head ): + */ + if( head->type == REPO ) + { + rhdr->r_rhdr = extend_strtab( (const char *)head->u.path ); + rhdr->r_rdata = dtoff; + rhdr->r_dnum = write_repo_data( head->list ); + + rhoff += (Bcf32_Off)sizeof( Bcf32_rhdr ); + ++rhdr; + ++cntr; + } + /* + End of symbols( head ). + ************************************/ + + head = head->next; + } + + return cntr; +} + +static void write_sections( SYMBOL *list ) +{ + SYMBOL *head = list; + + if( !head ) return; + + while( head ) + { + /************************************ + global symbols( head ): + */ + if( head->type == SECTION ) + { + (void)strncpy( (char *)shdr->s_name, SMAG_REPOS, (size_t)SI_NIDENT ); + shdr->s_type = ST_REPOS; + shdr->s_shdr = extend_strtab( (const char *)head->u.string ); + shdr->s_sdata = rhoff; + shdr->s_dnum = write_repos( head->list );; + + shoff += (Bcf32_Off)sizeof( Bcf32_shdr ); + ++shdr; + } + /* + End of global symbols( head ). + ************************************/ + + head = head->next; + } +} + + +int write_binary_config( void ) +{ + int ret = 0; + + ftab = NULL; + stab = NULL; + + snum = 0, rnum = 0, dnum = 0, global_dnum = 0, global_rnum = 0, indent = 0, stabsz = 0; + fhdr = NULL, shdr = NULL, rhdr = NULL, dntr = NULL; + shoff = 0, rhoff = 0, dtoff = 0, stoff = 0; + + count_symbols( &snum, &rnum, &dnum, &global_dnum, &global_rnum, symlist ); + + if( global_dnum ) snum += 1; /* add .global section for global variables */ + if( global_rnum ) snum += 1; /* add noname .repos section for global repositories */ + + shoff = (Bcf32_Off)sizeof( Bcf32_fhdr ); + rhoff = (Bcf32_Off)(shoff + snum * sizeof( Bcf32_shdr )); + dtoff = (Bcf32_Off)(rhoff + rnum * sizeof( Bcf32_rhdr )); + stoff = (Bcf32_Off)(dtoff + dnum * sizeof( Bcf32_dntr )); + + ftab = (unsigned char *)xmalloc( (size_t)stoff ); + + /****************** + Fill File Header + */ + fhdr = (Bcf32_fhdr *)ftab; + + (void)strncpy( (char *)fhdr->b_ident, BCFMAG, (size_t)SZBCFMAG ); + fhdr->b_ident[BI_CLASS] = BCF_CLASS_32; +#if __BYTE_ORDER == __LITTLE_ENDIAN + fhdr->b_ident[BI_DATA] = BCF_DATA_LSB; +#else + fhdr->b_ident[BI_DATA] = BCF_DATA_MSB; +#endif + fhdr->b_ident[BI_VERSION] = BV_CURRENT; + fhdr->b_ident[BI_PAD] = BCF_PAD; + + fhdr->b_hsize = (Bcf32_Half)sizeof( Bcf32_fhdr ); + fhdr->b_shoff = (Bcf32_Off)shoff; + fhdr->b_shentsize = (Bcf32_Half)sizeof( Bcf32_shdr ); + fhdr->b_shnum = (Bcf32_Half)snum; + fhdr->b_rhoff = (Bcf32_Off)rhoff; + fhdr->b_rhentsize = (Bcf32_Half)sizeof( Bcf32_rhdr ); + fhdr->b_rhnum = (Bcf32_Half)rnum; + fhdr->b_dtoff = (Bcf32_Off)dtoff; + fhdr->b_dtentsize = (Bcf32_Half)sizeof( Bcf32_dntr ); + fhdr->b_dtnum = (Bcf32_Half)dnum; + fhdr->b_stoff = (Bcf32_Off)stoff; + + shdr = (Bcf32_shdr *)&ftab[shoff]; + rhdr = (Bcf32_rhdr *)&ftab[rhoff]; + dntr = (Bcf32_dntr *)&ftab[dtoff]; + + if( global_dnum ) + { + (void)strncpy( (char *)shdr->s_name, SMAG_GLOBAL, (size_t)SI_NIDENT ); + shdr->s_type = ST_GLOBAL; + shdr->s_shdr = 0; /* Global section is always a first noname .global section */ + shdr->s_sdata = fhdr->b_dtoff; + shdr->s_dnum = (Bcf32_Half)global_dnum; + + write_global_data( symlist ); + + shoff += (Bcf32_Off)sizeof( Bcf32_shdr ); + ++shdr; + } + + if( global_rnum ) + { + (void)strncpy( (char *)shdr->s_name, SMAG_REPOS, (size_t)SI_NIDENT ); + shdr->s_type = ST_REPOS; + shdr->s_shdr = 0; /* Global repos plased in the second noname .repos section */ + shdr->s_sdata = fhdr->b_rhoff; + shdr->s_dnum = (Bcf32_Half)global_rnum; + + write_global_repos( symlist ); + + shoff += (Bcf32_Off)sizeof( Bcf32_shdr ); + ++shdr; + } + + write_sections( symlist ); + + /********************** + Whole BCF file size: + */ + fhdr->b_fsize = (Bcf32_Word)( stoff + stabsz ); + + bcf_shm_free(); + + bcf_shm_fd = shm_open( SHM_BCF, O_CREAT | O_TRUNC | O_RDWR, 0644 ); + if( bcf_shm_fd != -1 ) + { + (void)ftruncate( bcf_shm_fd, (size_t)fhdr->b_fsize ); + bcf_shm_address = mmap( NULL, (size_t)fhdr->b_fsize, PROT_WRITE, MAP_SHARED, bcf_shm_fd, 0 ); + if( bcf_shm_address != MAP_FAILED ) + { + memcpy( bcf_shm_address, (const void *)ftab, (size_t)stoff ); + memcpy( bcf_shm_address + (size_t)stoff, (const void *)stab, (size_t)stabsz ); + } + } + + if( bcf_fname ) + { + bcf = fopen( (const char *)bcf_fname, "w" ); + if( !bcf ) { FATAL_ERROR( "Cannot open BCF file: %s", bcf_fname ); } + } + + (void)fwrite( (void *)ftab, (size_t)stoff, 1, bcf ); + (void)fwrite( (void *)stab, (size_t)stabsz, 1, bcf ); + + if( bcf_fname ) + { + if( bcf ) { fclose( bcf ); bcf = NULL; } /* Do not free bcf_fname[] */ + } + + if( ftab ) { free( ftab ); ftab = NULL; } + if( stab ) { free( stab ); stab = NULL; } + + shoff = 0, rhoff = 0, dtoff = 0, stoff = 0; + fhdr = NULL, shdr = NULL, rhdr = NULL, dntr = NULL; + snum = 0, rnum = 0, dnum = 0, global_dnum = 0, global_rnum = 0, indent = 0, stabsz = 0; + + ret = 1; /* success */ + + return ret; +} diff --git a/cscmd/bconf.h b/cscmd/bconf.h new file mode 100644 index 0000000..cd8b49d --- /dev/null +++ b/cscmd/bconf.h @@ -0,0 +1,25 @@ + +#ifndef __BCONF_H +#define __BCONF_H + +#include + + +extern FILE *bcf; +extern char *bcf_fname; + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern int write_binary_config( void ); +extern void bcf_shm_free( void ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __BCONF_H */ diff --git a/cscmd/cscmd.8.in b/cscmd/cscmd.8.in new file mode 100644 index 0000000..f9fb862 --- /dev/null +++ b/cscmd/cscmd.8.in @@ -0,0 +1,66 @@ +.\" +.TH "CSCMD" 8 "2022-02-19" "cScm Configuration Daemon" "cscmd" + +.SH "NAME" +\fBcscmd\fR \- cScm Configurations Daemon + +.SH "SYNOPSIS" +.PP +\fB\fBcscmd\fR [\fBOPTIONS\fR]\fR \fB\-\-scm\fR=[\fIsvn\fR|\fIgit\fR] + +.SH "SUMMARY" +\fBcscmd\fR should be run at boot time by \fI@sysconfdir@/rc.d/rc.csvnd\fR or \fI@sysconfdir@/rc.d/rc.cgitd\fR. +This daemon read the config file \fI@sysconfdir@/csvn-ui.rc\fR or \fI@sysconfdir@/cgit-ui.rc\fR (depends on \fB\-\-scm\fR option) +and convert it to binary (see \fI@includedir@/cscm/bcf.h\fR) form for cSvn or cGit CGI scripts. + + +.SH "OPTIONS" + +.TP +\fB-h\fR,\fB--help\fR +Display help information. + +.TP +\fB-v\fR,\fB--version\fR +Display the version of \fBcscm\fR daemon. + +.TP +\fB-d\fR,\fB--daemonize\fR +Run in background as a daemon. + +.TP +\fB-i\fR,\fB--inotify\fR +Notify about configuration changes. If this option is set then \fBcscmd\fR daemon selects changes made +in \fI@sysconfdir@/csvn-ui.rc\fR or \fI@sysconfdir@/cgit-ui.rc\fR config file and reread configuration when changes is done. +Without this option rereading configuration file can be done by sending \fB-HUP\fR to the \fBcscmd\fR daemon. + +.TP +\fB-b\fR,\fB--bcf\fR=\fB\fR +Binary config file (depends on \fB\-\-scm\fR option). Default: \fI@CSCM_HOME_PATH@/csvn/csvn.bcf\fR. + +.TP +\fB-c\fR,\fB--config\fR=\fB\fR +Config file (depends on \fB\-\-scm\fR option). Default: \fI@sysconfdir@/csvn-ui.rc\fR. + +.TP +\fB-l\fR,\fB--log\fR=\fB\fR +Log file (depends on \fB\-\-scm\fR option). Default: \fI@CSCM_LOG_DIR@/csvnd.log\fR. + +.TP +\fB-p\fR,\fB--pid\fR=\fB\fR +Log file (depends on \fB\-\-scm\fR option). Default: \fI@CSCM_PID_DIR@/csvnd.pid\fR. + +.TP +\fB-s\fR,\fB--scm\fR=\fB[svn|git]\fR +SCM engine name: \fIsvn\fR or \fIgit\fR. Default: \fIsvn\fR. + +.TP +\fB-t\fR,\fB--test\fR +Test the config file and exit. + + +.SH "SEE ALSO" +.BR csvn-ui.rc(5), +.BR cgit-ui.rc(5) + + diff --git a/cscmd/daemon.c b/cscmd/daemon.c new file mode 100644 index 0000000..80ad4ed --- /dev/null +++ b/cscmd/daemon.c @@ -0,0 +1,39 @@ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +int daemon( int nochdir, int noclose ) +{ + int fd; + + switch( fork() ) + { + case -1: + return( -1 ); + case 0: + break; + default: + _exit( 0 ); /* direct use kernel exit */ + } + + if( setsid() == -1 ) return( -1 ); + if( !nochdir ) chdir( "/" ); + if( noclose ) return( 0 ); + + fd = open( _PATH_DEVNULL, O_RDWR, 0 ); + if( fd != -1 ) + { + dup2( fd, STDIN_FILENO ); + dup2( fd, STDOUT_FILENO ); + dup2( fd, STDERR_FILENO ); + if( fd > 2 ) close( fd ); + } + return( 0 ); +} diff --git a/cscmd/daemon.h b/cscmd/daemon.h new file mode 100644 index 0000000..83dd044 --- /dev/null +++ b/cscmd/daemon.h @@ -0,0 +1,7 @@ + +#ifndef __DAEMON_H__ +#define __DAEMON_H__ + +extern int daemon( int, int ); + +#endif /* __DAEMON_H__ */ diff --git a/cscmd/error.c b/cscmd/error.c new file mode 100644 index 0000000..ec469c9 --- /dev/null +++ b/cscmd/error.c @@ -0,0 +1,92 @@ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + + +extern char *config_fname; + +int errors = 0; +int warnings = 0; + + +void error( char *fmt, ... ) +{ + va_list arg_ptr; + char buf[MAX_ERROR_MSG_SIZE]; + char msg[MAX_ERROR_MSG_SIZE]; + char *format = "%s:%d:%d: %s"; + + va_start( arg_ptr, fmt ); + + vsnprintf( msg, MAX_ERROR_MSG_SIZE, (const void *)fmt, arg_ptr ); + + va_end( arg_ptr ); /* Reset variable arguments. */ + + snprintf( buf, MAX_ERROR_MSG_SIZE, format, config_fname, lineno, colno, msg ); + + ERROR( "%s", buf ); + + ++errors; +} + +void warning( char *fmt, ... ) +{ + va_list arg_ptr; + char buf[MAX_ERROR_MSG_SIZE]; + char msg[MAX_ERROR_MSG_SIZE]; + char *format = "%s:%d:%d: %s"; + + va_start( arg_ptr, fmt ); + + vsnprintf( msg, MAX_ERROR_MSG_SIZE, (const void *)fmt, arg_ptr ); + + va_end( arg_ptr ); /* Reset variable arguments. */ + + snprintf( buf, MAX_ERROR_MSG_SIZE, format, config_fname, lineno, colno, msg ); + + WARNING( "%s", buf ); + + ++warnings; +} + +void no_space( void ) +{ + char buf[MAX_ERROR_MSG_SIZE]; + char *format = "%s: Cannot allocate memory"; + + snprintf( buf, MAX_ERROR_MSG_SIZE, format, config_fname ); + + FATAL_ERROR( "%s", buf ); + + ++errors; +} + +void unterminated_comment( void ) +{ + char buf[MAX_ERROR_MSG_SIZE]; + char *format = "%s:%d:%d: Unterminated comment"; + + snprintf( buf, MAX_ERROR_MSG_SIZE, format, config_fname, lineno, colno ); + + ERROR( "%s", buf ); + + ++errors; +} diff --git a/cscmd/error.h b/cscmd/error.h new file mode 100644 index 0000000..866ec2a --- /dev/null +++ b/cscmd/error.h @@ -0,0 +1,25 @@ + +#ifndef __ERROR_H +#define __ERROR_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_ERROR_MSG_SIZE PATH_MAX + +extern int errors; +extern int warnings; + +extern void error( char *fmt, ... ); +extern void warning( char *fmt, ... ); +extern void no_space( void ); +extern void unterminated_comment( void ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __ERROR_H */ diff --git a/cscmd/lex.c b/cscmd/lex.c new file mode 100644 index 0000000..318e074 --- /dev/null +++ b/cscmd/lex.c @@ -0,0 +1,815 @@ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCRE2_CODE_UNIT_WIDTH 32 +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + + +int lineno = 0; +int colno = 0; + +static int maxtoken; +static wchar_t *token_buffer; + +static int max8token; +static utf8_t *token_utf8_buffer; + +int indent_level = 0; /* Number of '{' minus number of '}'. */ + +static int end_of_file = 0; +static int nextchar = -1; + +static char *locale; + +#define GETC(c) ({ wint_t ret; ++colno; ret = fgetwc( config ); ret; }) +#define UNGETC(c) ({ wint_t ret; --colno; ret = ungetwc( c, config ); ret; }) + + +static wchar_t *extend_token_buffer( wchar_t *p ) +{ + int offset = p - token_buffer; + maxtoken = maxtoken * 2 + 10; + token_buffer = (wchar_t *)xrealloc( token_buffer, (maxtoken + 2)*sizeof(wchar_t) ); + + return( token_buffer + offset ); +} + +static utf8_t *extend_token_utf8_buffer( utf8_t *p ) +{ + int offset = p - token_utf8_buffer; + max8token = max8token * 2 + 10; + token_utf8_buffer = (utf8_t *)xrealloc( token_utf8_buffer, (max8token + 2)*6 ); + + return( token_utf8_buffer + offset ); +} + + +void yyerror( char const *s ) +{ + error( "%s", s ); +} + + +void init_lex( void ) +{ + locale = setlocale( LC_ALL, "en_US.utf8" ); + + lineno = 0; + colno = 0; + + nextchar = -1; + maxtoken = 40; + max8token = 40; + + indent_level = 0; + end_of_file = 0; + + token_buffer = (wchar_t *)xmalloc( maxtoken * sizeof(wchar_t) + 2 ); + token_utf8_buffer = (utf8_t *)xmalloc( max8token * 6 + 2 ); +} + +void fini_lex( void ) +{ + locale = setlocale( LC_ALL, locale ); + + if( token_buffer ) { free( token_buffer ); token_buffer = NULL; } + if( token_utf8_buffer ) { free( token_utf8_buffer ); token_utf8_buffer = NULL; } + + indent_level = 0; + end_of_file = 0; + + max8token = 0; + maxtoken = 0; + nextchar = -1; + + lineno = 0; + colno = 0; +} + +static wint_t check_newline( void ) +{ + wint_t c; + + ++lineno; + colno = 0; /* считает GETC()/UNGETC(); здесь надо только обнулить */ + + /***************************************** + Read first nonwhite char on the line. + *****************************************/ + c = GETC(); + while( c == ' ' || c == '\t' ) c = GETC(); + + if( c == '#' ) goto skipline; + else return( c ); + + /* skip the rest of this line */ +skipline: + + while( c != '\n' && c != WEOF ) + c = GETC(); + + return( c ); +} + +static wint_t skip_comment( int c ) +{ + if( c == '*' ) + { +do1: + do + { + c = GETC(); + if( c == '\n' ) { ++lineno; colno = 0; } + + } while( c != '*' && c != WEOF ); + + if( c == WEOF ) + { + unterminated_comment(); + return( WEOF ); + } + + c = GETC(); + + if( c == '/' ) + { + c = GETC(); + if( c == '\n' ) c = check_newline(); + return( c ); + } + else + { + UNGETC( c ); + goto do1; + } + } + else if( c == '/' || c == '#' ) + { + do + { + c = GETC(); + + } while( c != '\n' && c != WEOF ); + + if( c == WEOF ) + { + unterminated_comment(); + return( WEOF ); + } + else c = check_newline(); + + return( c ); + } + + return( c ); + +} /* End skip_commemnt() */ + +static wint_t skip_white_space( wint_t c ) +{ + for( ;; ) + { + switch( c ) + { + case '\n': + c = check_newline(); + break; + + case '#': + c = skip_comment( c ); + return( skip_white_space( c ) ); + break; + + case '/': + c = GETC(); + if( c == '/' || c == '*' ) + { + c = skip_comment( c ); + return( skip_white_space( c ) ); + } + else + { + UNGETC( c ); + return( '/' ); + } + break; + + case ' ': + case '\t': + case '\f': + case '\v': + case '\b': + case '\r': + c = GETC(); + break; + case '\\': + c = GETC(); + if( c == '\n' ) { ++lineno; colno = 0; } + else + { + warning( "%s", "Stray '\\' in program" ); + } + c = GETC(); + break; + default: + return( c ); + + } /* End switch( c ) */ + + } /* End for( ;; ) */ + +} /* End skip_white_space() */ + +static wint_t readescape( int *ignore_ptr ) +/* + read escape sequence, returning a char, or store 1 in *ignore_ptr + if it is backslash-newline + */ +{ + wint_t c = GETC(); + wint_t code; + unsigned count; + unsigned firstdig = 0; + int nonull; + + switch( c ) + { + case 'x': + code = 0; + count = 0; + nonull = 0; + while( 1 ) + { + c = GETC(); + if( !(c >= 'a' && c <= 'f') && + !(c >= 'A' && c <= 'F') && + !(c >= '0' && c <= '9') ) + { + UNGETC( c ); + break; + } + code *= 16; + if( c >= 'a' && c <= 'f' ) code += c - 'a' + 10; + if( c >= 'A' && c <= 'F' ) code += c - 'A' + 10; + if( c >= '0' && c <= '9' ) code += c - '0'; + if( code != 0 || count != 0 ) + { + if( count == 0 ) firstdig = code; + count++; + } + nonull = 1; + + } /* End while( 1 ) */ + + if( !nonull ) + { + error( "%s", "\\x used with no following hex digits" ); + } + else if( count == 0 ) + /* Digits are all 0's. Ok. */ + ; + else if( (count - 1) * 4 >= 32 || /* 32 == bits per INT */ + (count > 1 && ((1 << (32 - (count-1) * 4)) <= firstdig ))) + { + warning( "%s", "Hex escape out of range" ); + } + return( code ); + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': + code = 0; + count = 0; + while( (c <= '7') && (c >= '0') && (count++ < 6) ) + { + code = (code * 8) + (c - '0'); + c = GETC(); + } + UNGETC( c ); + return( code ); + + case '\\': case '\'': case '"': + return( c ); + + case '\n': + lineno++; colno = 0; + *ignore_ptr = 1; + return( 0 ); + + case 'n': + return( '\n' ); + + case 't': + return( '\t' ); + + case 'r': + return( '\r' ); + + case 'f': + return( '\f' ); + + case 'b': + return( '\b' ); + + case 'a': + return( '\a' ); + + case 'v': + return( '\v' ); + } + + return( c ); + +} /* End of readescape() */ + + +int html_symbol_name( wchar_t *str ) +{ + int rc = 0, error = 0; + PCRE2_SIZE offset = 0; + wchar_t pattern[] = L"^(&[#A-Za-z0-9]*;)"; + + pcre2_match_data *match; + + pcre2_code *regexp = pcre2_compile( (PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, &error, &offset, NULL ); + if( regexp == NULL ) + { + return 0; /* PCRE compilation failed */ + } + + match = pcre2_match_data_create_from_pattern( regexp, NULL ); + + rc = pcre2_match( regexp, (PCRE2_SPTR)str, (int)wcslen(str), 0, 0, match, NULL ); + if( rc < 0 ) + { + /* not match */ + pcre2_match_data_free( match ); + pcre2_code_free( regexp ); + return 0; + } + else + { + /* match */ + pcre2_match_data_free( match ); + pcre2_code_free( regexp ); + return 1; + } +} + + +int yylex( void ) +{ + wint_t c; + wchar_t *p; + int value; + + if( nextchar >= 0 ) + c = nextchar, nextchar = -1; + else + c = GETC(); + + while( 1 ) + { + switch( c ) + { + case ' ': + case '\t': + case '\f': + case '\v': + case '\b': + c = skip_white_space( c ); + break; + + case '\r': + case '\n': + case '/': + case '#': + case '\\': + c = skip_white_space( c ); + + default: + goto found_nonwhite; + + } /* End switch( c ) */ +found_nonwhite: + + token_buffer[0] = c; + token_buffer[1] = 0; + + switch( c ) + { + case WEOF: + end_of_file = 1; + token_buffer[0] = 0; + value = 0; + goto done; + break; + + case '$': /* dollar in identifier */ + if( 1 ) goto letter; + return '$'; + + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': + case '_': + + /* RUSSIAN */ + case L'А': case L'Б': case L'В': case L'Г': case L'Д': + case L'Е': case L'Ё': case L'Ж': case L'З': case L'И': + case L'Й': case L'К': case L'Л': case L'М': case L'Н': + case L'О': case L'П': case L'Р': case L'С': case L'Т': + case L'У': case L'Ф': case L'Х': case L'Ц': case L'Ч': + case L'Ш': case L'Щ': case L'Ъ': case L'Ы': case L'Ь': + case L'Э': case L'Ю': case L'Я': + + case L'а': case L'б': case L'в': case L'г': case L'д': + case L'е': case L'ё': case L'ж': case L'з': case L'и': + case L'й': case L'к': case L'л': case L'м': case L'н': + case L'о': case L'п': case L'р': case L'с': case L'т': + case L'у': case L'ф': case L'х': case L'ц': case L'ч': + case L'ш': case L'щ': case L'ъ': case L'ы': case L'ь': + case L'э': case L'ю': case L'я': + +letter: + p = token_buffer; + while( iswalnum( c ) || c == '_' || c == '$' || c == '@' || c == '-' || c == '.' || c == ':' ) + { + if( p >= token_buffer + maxtoken ) + { + p = extend_token_buffer( p ); + extend_token_utf8_buffer( token_utf8_buffer ); + } + + *p++ = c; + c = GETC(); + } + *p = 0; + nextchar = c; + value = VARIABLE; + + (void)copy_ucs4_to_utf8( (utf8_t *)token_utf8_buffer, (const ucs4_t *)token_buffer ); + + /********************* + install into symtab + *********************/ + { + if( !strcmp( "section", (const char *)token_utf8_buffer ) ) + { + value = SECTION; + yylval.sym = install( NULL, SECTION, NULL ); + } + else if( !strcmp( "repo", (const char *)token_utf8_buffer ) ) + { + value = REPO; + yylval.sym = install( NULL, REPO, NULL ); + } + else + { + SYMBOL *sp = NULL; + + if( (sp = lookup( (const char *)token_utf8_buffer )) == (SYMBOL *)0 ) + sp = install( (const char *)token_utf8_buffer, VARIABLE, 0 ); + + /****************************************************************** + Если переменная уже в таблице, то мы предполагаем, что она имеет + тип равный одному из допустимых: NUMERICAL, STRING, или PATH. + ******************************************************************/ + if( sp->type != VARIABLE ) + { + switch( sp->type ) + { + case NUMERICAL: + case STRING: + case PATH: + value = sp->type; + break; + default: + /* error */ + break; + } + } + yylval.sym = sp; + } + } + + token_buffer[0] = 0; + token_utf8_buffer[0] = 0; + goto done; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int constant = 0; +/* integer: */ + p = token_buffer; + while( iswdigit( c ) ) + { + if( p >= token_buffer + maxtoken ) + { + p = extend_token_buffer( p ); + extend_token_utf8_buffer( token_utf8_buffer ); + } + + *p++ = c; + c = GETC(); + } + *p = 0; + nextchar = c; + value = NUMERICAL; + + (void)copy_ucs4_to_utf8( (utf8_t *)token_utf8_buffer, (const ucs4_t *)token_buffer ); + + /********************* + install into symtab + *********************/ + { + (void)swscanf( (const wchar_t *)token_buffer, L"%d", &constant ); + yylval.sym = install( NULL, NUMERICAL, constant ); + } + + token_buffer[0] = 0; + token_utf8_buffer[0] = 0; + goto done; + break; + } + + case '\'': +/* path_constant: */ + { + int num_chars = 0; + unsigned int width = 8; /* to allow non asscii in path set width = 16 */ + + while( 1 ) + { +tryagain: + c = GETC(); + + if( c == '\'' || c == WEOF ) break; + if( c == '\\' ) + { + int ignore = 0; + c = readescape( &ignore ); + if( ignore ) goto tryagain; + if( (unsigned)c >= (1 << width) ) + { + warning( "%s", "Escape sequence out of range" ); + } + } + else if( c == '\n' ) { lineno++; colno = 0; } + + num_chars++; + if( num_chars > maxtoken - 4 ) + { + extend_token_buffer( token_buffer ); + extend_token_utf8_buffer( token_utf8_buffer ); + } + + token_buffer[num_chars] = c; + + } /* End while( 1 ) */ + + token_buffer[num_chars + 1] = '\''; + token_buffer[num_chars + 2] = 0; + + if( c != '\'' ) + { + error( "%s", "Malformated path constant" ); + } + else if( num_chars == 0 ) + { + error( "%s", "Empty path constant" ); + } + + /* build path: */ + { + wchar_t *s, *string = NULL; + wchar_t *p = &token_buffer[0]; + + while( *p ) + { + if( *p == '\n' || *p == '\t' ) *p = ' '; + ++p; + } + + string = (wchar_t *)malloc( maxtoken * 4 + 10 ); + + p = &token_buffer[1]; + s = &string[0]; + + while( *p == ' ' ) ++p; + + while( *p ) + { + if( *p != ' ' ) + *s++ = *p++; + else + ++p; + } + --s; *s = 0; + while( *(s-1) == ' ' ) --s; + *s = 0; + + (void)copy_ucs4_to_utf8( (utf8_t *)token_utf8_buffer, (const ucs4_t *)string ); + + free( string ); + } + + /********************* + install into symtab + *********************/ + { + yylval.sym = install( NULL, PATH, (char *)token_utf8_buffer ); + } + + token_buffer[0] = 0; + token_utf8_buffer[0] = 0; + value = PATH; + goto done; + } + + case '"': +/* string_constant: */ + { + c = GETC(); + p = token_buffer + 1; + + while( c != '"' && c >= 0 ) + { + if( c == '\\' ) + { + int ignore = 0; + c = readescape( &ignore ); + if( ignore ) goto skipnewline; + } + else if( c == '\n' ) lineno++; + + if( p == token_buffer + maxtoken ) + { + p = extend_token_buffer( p ); + extend_token_utf8_buffer( token_utf8_buffer ); + } + *p++ = c; + +skipnewline: + c = GETC(); + + } /* End while( " ) */ + + *p = 0; + + if( c < 0 ) + { + error( "%s", "Unterminated string constant" ); + } + + + *p++ = '"'; + *p = 0; + + /* build string: */ + { + wchar_t *s, *string = NULL; + wchar_t *p = &token_buffer[0]; + + while( *p ) + { + if( *p == '\n' || *p == '\t' ) *p = ' '; + ++p; + } + + string = (wchar_t *)malloc( maxtoken * 4 + 10 ); + + p = &token_buffer[1]; + s = &string[0]; + + while( *p == ' ' ) ++p; + + while( *p ) + { + if( *p != ' ' ) + { + switch( *p ) + { + case '&': + /************************************************ + Skip HTML symbol names such as  ,... etc.: + */ + if( ! html_symbol_name( p ) ) + { + *s++ = '&'; *s++ = 'a'; *s++ = 'm'; *s++ = 'p'; *s++ = ';'; ++p; + } + else + { + *s++ = *p++; + } + break; + + case '<': + *s++ = '&'; *s++ = 'l'; *s++ = 't'; *s++ = ';'; ++p; + break; + + case '>': + *s++ = '&'; *s++ = 'g'; *s++ = 't'; *s++ = ';'; ++p; + break; + + default: + *s++ = *p++; + break; + } + } + else + { + /* skip multiple spaces */ + if( *(p+1) != ' ' ) + *s++ = *p++; + else + ++p; + } + } + --s; *s = 0; + while( *(s-1) == ' ' ) --s; + *s = 0; + + (void)copy_ucs4_to_utf8( (utf8_t *)token_utf8_buffer, (const ucs4_t *)string ); + + free( string ); + } + + /********************* + install into symtab + *********************/ + { + yylval.sym = install( NULL, STRING, (char *)token_utf8_buffer ); + } + + token_buffer[0] = 0; + token_utf8_buffer[0] = 0; + value = STRING; + goto done; + } + + case 0: + value = 1; + goto done; + break; + + case '{': + indent_level++; + value = c; + goto done; + break; + + case '}': + indent_level--; + value = c; + goto done; + break; + + default: + value = c; + goto done; + break; + + } /* End switch( c ) */ + + } /* End while( 1 ) */ + +done: + + return( value ); +} diff --git a/cscmd/lex.h b/cscmd/lex.h new file mode 100644 index 0000000..5c686e2 --- /dev/null +++ b/cscmd/lex.h @@ -0,0 +1,26 @@ + +#ifndef __LEX_H +#define __LEX_H + + +extern int lineno; +extern int colno; + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern void init_lex( void ); +extern void fini_lex( void ); + +extern int yylex( void ); +extern void yyerror( char const *s ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __LEX_H */ diff --git a/cscmd/logrotate.in b/cscmd/logrotate.in new file mode 100644 index 0000000..4d36ac5 --- /dev/null +++ b/cscmd/logrotate.in @@ -0,0 +1,8 @@ + +@CSCM_LOG_DIR@/@CSCM_PROGRAM@d.log { + rotate 7 + size=5M + compress + notifempty + missingok +} diff --git a/cscmd/main.c b/cscmd/main.c new file mode 100644 index 0000000..9058046 --- /dev/null +++ b/cscmd/main.c @@ -0,0 +1,737 @@ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include /* chmod(2) */ +#include +#include +#include /* strdup(3) */ +#include /* basename(3) */ +#include /* tolower(3) */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +# define SIGCHLD SIGCLD +#endif + +#define _GNU_SOURCE +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/********************* + Default File Names: + */ +const char *CONFIG_FILE = CSVN_CONFIG; +const char *BCF_FILE = CSVN_HOME_DIR "/" CSVN_PROGRAM ".bcf"; +const char *LOG_FILE = CSCM_LOG_DIR "/" CSVN_PROGRAM "d.log"; +const char *PID_FILE = CSCM_PID_DIR "/" CSVN_PROGRAM "d.pid"; +const char *SHM_BCF = CSVN_SHM_BCF; + + +char *program = PROGRAM_DAEMON; +int exit_status = EXIT_SUCCESS; /* errors counter */ + +FILE *config = NULL; +char *config_fname = NULL; + +char *log_fname = NULL; +char *pid_fname = NULL; + +static sigset_t blockmask; + +static int run_as_daemon = 0; +static int test_config_file = 0; +static int config_inotify = 0; + + +static void free_resources( void ); + + +/*********************** + Inotify declarations: + */ +#define IN_BUFFER_SIZE (7 * (sizeof(struct inotify_event) + NAME_MAX + 1)) + +static int inotify_fd = 0, wd = 0; +static struct inotify_event *event = NULL; +static char buf[IN_BUFFER_SIZE] __attribute__ ((aligned(8))); + +static int check_event( struct inotify_event *e ) +{ + if( e->mask & IN_CLOSE_WRITE ) return 1; + return 0; +} + +static void init_config_inotify( void ) +{ + inotify_fd = inotify_init(); /* Create inotify instance */ + if( inotify_fd == -1 ) + { + ERROR( "Cannot initialize inotify for file: %s", config_fname ); + LOG( "Stop cScm Configuration Daemon." ); + free_resources(); + exit( 1 ); + } + + wd = inotify_add_watch( inotify_fd, config_fname, IN_CLOSE_WRITE ); + if( wd == -1 ) + { + ERROR( "Cannot add inotify watch for file: %s", config_fname ); + LOG( "Stop cScm Configuration Daemon." ); + free_resources(); + exit( 1 ); + } +} + +static void fini_config_inotify( void ) +{ + if( wd > 0 ) { (void)inotify_rm_watch( inotify_fd, wd ); wd = 0; } + if( inotify_fd > 0 ) { (void)close( inotify_fd ); inotify_fd = 0; } +} + + +static void free_resources( void ) +{ + fini_config_inotify(); + + if( log_fname ) + { + if( errlog ) { fclose( errlog ); errlog = NULL; } + free( log_fname ); log_fname = NULL; + } + if( config_fname ) + { + if( config ) { fclose( config ); config = NULL; } + free( config_fname ); config_fname = NULL; + } + bcf_shm_free(); + if( bcf_fname ) + { + if( bcf ) { fclose( bcf ); bcf = NULL; } + (void)unlink( (const char *)bcf_fname ); + free( bcf_fname ); bcf_fname = NULL; + } + if( pid_fname ) + { + (void)unlink( (const char *)pid_fname ); + free( pid_fname ); pid_fname = NULL; + } +} + + +void usage() +{ + free_resources(); + + fprintf( stdout, "\n" ); + fprintf( stdout, "Usage: %s [options]\n", program ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Start cScm Configuration Daemon to read config file.\n" ); + fprintf( stdout, "\n" ); + fprintf( stdout, "Options:\n" ); + fprintf( stdout, " -h,--help Display this information.\n" ); + fprintf( stdout, " -v,--version Display the version of %s utility.\n", program ); + fprintf( stdout, " -d,--daemonize Run in background as a daemon.\n" ); + fprintf( stdout, " -i,--inotify Notify about configuration changes.\n" ); + fprintf( stdout, " -b,--bcf= Binary config file. Default: %s.\n", BCF_FILE ); + fprintf( stdout, " -c,--config= Config file. Default: %s.\n", CONFIG_FILE ); + fprintf( stdout, " -l,--log= Log file. Default: %s.\n", LOG_FILE ); + fprintf( stdout, " -p,--pid= PID file. Default: %s.\n", PID_FILE ); + fprintf( stdout, " -s,--scm= SCM 'svn' or 'git'. Default: 'svn'.\n" ); + fprintf( stdout, " -t,--test Test config file and exit.\n" ); + fprintf( stdout, "\n" ); + + exit( EXIT_FAILURE ); +} + +void to_lowercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = tolower( c ); ++p; } +} + +void to_uppercase( char *s ) +{ + char *p = s; + while( p && *p ) { int c = *p; *p = toupper( c ); ++p; } +} + +void version() +{ + char *upper = NULL; + + upper = (char *)alloca( strlen( program ) + 1 ); + + strcpy( (char *)upper, (const char *)program ); + to_uppercase( upper ); + + fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION ); + + fprintf( stdout, "Copyright (C) 2022 Andrey V.Kosteltsev.\n" ); + fprintf( stdout, "This is free software. There is NO warranty; not even\n" ); + fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ); + fprintf( stdout, "\n" ); + + free_resources(); + exit( EXIT_SUCCESS ); +} + +void get_args( int argc, char *argv[] ) +{ + const char* short_options = "hvdic:l:p:s:t"; + + const struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "daemonize", no_argument, NULL, 'd' }, + { "inotify", no_argument, NULL, 'i' }, + { "bcf", required_argument, NULL, 'b' }, + { "config", required_argument, NULL, 'c' }, + { "log", required_argument, NULL, 'l' }, + { "pid", required_argument, NULL, 'p' }, + { "scm", required_argument, NULL, 's' }, + { "test", no_argument, NULL, 't' }, + { NULL, 0, NULL, 0 } + }; + + int ret; + int option_index = 0; + + while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 ) + { + switch( ret ) + { + case 'h': + { + usage(); + break; + } + case 'v': + { + version(); + break; + } + case 'd': + { + run_as_daemon = 1; + break; + } + case 'i': + { + config_inotify = 1; + break; + } + case 't': + { + test_config_file = 1; + break; + } + + case 'b': + { + if( optarg != NULL ) + { + bcf_fname = strdup( optarg ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case 'c': + { + if( optarg != NULL ) + { + config_fname = strdup( optarg ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case 'l': + { + if( optarg != NULL ) + { + log_fname = strdup( optarg ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case 'p': + { + if( optarg != NULL ) + { + pid_fname = strdup( optarg ); + } + else + /* option is present but without value */ + usage(); + break; + } + + case 's': + { + if( optarg != NULL ) + { + if( *optarg && !strncmp( optarg, "git", 3 ) ) + { + CONFIG_FILE = CGIT_CONFIG; + BCF_FILE = CGIT_HOME_DIR "/" CGIT_PROGRAM ".bcf"; + LOG_FILE = CSCM_LOG_DIR "/" CGIT_PROGRAM "d.log"; + PID_FILE = CSCM_PID_DIR "/" CGIT_PROGRAM "d.pid"; + SHM_BCF = CGIT_SHM_BCF; + } + } + else + /* option is present but without value */ + usage(); + break; + } + + case '?': default: + { + usage(); + break; + } + } + } +} + + +static int is_directory( const char *path ) +{ + struct stat st; + return ( !stat( path, &st ) && S_ISDIR(st.st_mode) ); +} + +static void logpid( void ) +{ + FILE *fp; + + if( !pid_fname ) + { + /* allocate memory for PID file name */ + pid_fname = (char *)malloc( strlen( PID_FILE ) + 1 ); + if( !pid_fname ) { FATAL_ERROR( "Cannot allocate memory for PID file name: %s", PID_FILE ); } + (void)strcpy( pid_fname, PID_FILE ); + } + + /* Create CSCM_PID_DIR if not exists: */ + { + char *pid_dir = NULL, *dir = strdup( pid_fname ); + + pid_dir = dirname( dir ); + + if( !is_directory( (const char *)pid_dir ) ) + { + /* Create if not exists */ + if( 0 != mkdir( (const char *)pid_dir, 0755 ) ) + { + FATAL_ERROR( "Cannot create directory for PID file: %s", PID_FILE ); + } + } + free( dir ); + } + + if( (fp = fopen( pid_fname, "w" )) != NULL ) + { + fprintf( fp, "%u\n", getpid() ); + (void)fclose( fp ); + } +} + +void init_logs( void ) +{ + /* print to stderr until the errlog file is open */ + errlog = stderr; + + if( !log_fname ) + { + /* allocate memory for LOG file name */ + log_fname = (char *)malloc( strlen( LOG_FILE ) + 1 ); + if( !log_fname ) { FATAL_ERROR( "Cannot open log file: %s", LOG_FILE ); } + (void)strcpy( log_fname, LOG_FILE ); + } + + /* Create CSCM_LOG_DIR if not exists: */ + { + char *log_dir = NULL, *dir = strdup( log_fname ); + + log_dir = dirname( dir ); + + if( !is_directory( (const char *)log_dir ) ) + { + /* Create if not exists */ + if( 0 != mkdir( (const char *)log_dir, 0755 ) ) + { + FATAL_ERROR( "Cannot create directory for log file: %s", LOG_FILE ); + } + } + free( dir ); + } + + /* open LOG file */ + errlog = fopen( (const char *)log_fname, "a" ); + if( !errlog ) { errlog = stderr; FATAL_ERROR( "Cannot open log file: %s", log_fname ); } +} + +void open_config_file( void ) +{ + if( !config_fname ) + { + /* allocate memory for CONFIG file name */ + config_fname = (char *)malloc( strlen( CONFIG_FILE ) + 1 ); + if( !config_fname ) + { + FATAL_ERROR( "Cannot open config file: %s", CONFIG_FILE ); + if( log_fname ) { fclose( errlog ); free( log_fname ); } + } + (void)strcpy( config_fname, CONFIG_FILE ); + } + + /* open CONFIG file */ + config = fopen( (const char *)config_fname, "r" ); + if( !config ) + { + FATAL_ERROR( "Cannot open config file: %s", config_fname ); + if( log_fname ) { fclose( errlog ); free( log_fname ); } + } + + if( !bcf_fname ) + { + /* allocate memory for BCF file name */ + bcf_fname = (char *)malloc( strlen( BCF_FILE ) + 1 ); + if( !bcf_fname ) + { + FATAL_ERROR( "Cannot allocate memory gor BCF file name: %s", BCF_FILE ); + if( log_fname ) { fclose( errlog ); free( log_fname ); } + } + (void)strcpy( bcf_fname, BCF_FILE ); + } +} + +void close_config_file( void ) +{ + if( config ) { fclose( config ); config = NULL; } +} + + + +void parse_config_file( void ) +{ + int ret; + + /*********************************** + Blick signals until parser works: + */ + (void)sigprocmask( SIG_BLOCK, (const sigset_t *)&blockmask, (sigset_t *)NULL ); + + open_config_file(); + init_symtab(); + init_lex(); + + if( !(ret = yyparse()) ) + { + reverse_symlist( (SYMBOL **)&symlist ); + remove_consts( (SYMBOL **)&symlist ); + (void)write_binary_config(); + } + else + { + bcf_shm_free(); + if( bcf_fname ) + { + if( bcf ) { fclose( bcf ); bcf = NULL; } + (void)unlink( (const char *)bcf_fname ); + } + } + + fini_lex(); + fini_symtab(); + close_config_file(); + + (void)sigprocmask( SIG_UNBLOCK, (const sigset_t *)&blockmask, (sigset_t *)NULL ); + + if( test_config_file ) + { + if( ret ) + { + fprintf( stdout, "%s: %s: Config file is not correct. See: %s\n", program, config_fname, log_fname ); + LOG( "Stop cScm Configuration Daemon." ); + free_resources(); + exit( 1 ); + } + else + { + fprintf( stdout, "%s: %s: Config file is correct.\n", program, config_fname ); + LOG( "Stop cScm Configuration Daemon." ); + free_resources(); + exit( 0 ); + } + } +} + + + +void fatal_error_actions( void ) +{ + free_resources(); +} + +void sigint( int signum ) +{ + (void)signum; + + LOG( "received SIGINT: free resources at exit" ); + LOG( "Stop cScm Configuration Daemon." ); + + free_resources(); + exit( 0 ); +} + +void sigusr( int signum ) +{ + if( signum == SIGUSR1 ) + { + LOG( "signal USR1 has been received" ); + } + else if( signum == SIGUSR2 ) + { + LOG( "signal USR2 has been received" ); + } +} + +void sighup( int signum ) +{ + (void)signum; + + LOG( "received SIGHUP: parse config file: %s", config_fname ); + + parse_config_file(); +} + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sighup; /* HUP: read config file */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGHUP ); + sa.sa_mask = set; + sigaction( SIGHUP, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigusr; /* USR1, USR2 */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGUSR1 ); + sigaddset( &set, SIGUSR2 ); + sa.sa_mask = set; + sigaction( SIGUSR1, &sa, NULL ); + sigaction( SIGUSR2, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + memset( &sa, 0, sizeof( sa ) ); /* ignore SIGPIPE */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigaction( SIGPIPE, &sa, NULL ); + + /* на случай блокировки сигналов с помощью sigprocmask(): */ + sigemptyset( &blockmask ); + sigaddset( &blockmask, SIGHUP ); + sigaddset( &blockmask, SIGUSR1 ); + sigaddset( &blockmask, SIGUSR2 ); + sigaddset( &blockmask, SIGTERM ); + sigaddset( &blockmask, SIGINT ); + + /* System V fork+wait does not work if SIGCHLD is ignored */ + signal( SIGCHLD, SIG_DFL ); +} + + + +int main( int argc, char *argv[] ) +{ + gid_t gid; + + set_signal_handlers(); + + gid = getgid(); + setgroups( 1, &gid ); + + fatal_error_hook = fatal_error_actions; + + program = basename( argv[0] ); + get_args( argc, argv ); + + if( getppid() != 1 ) + { + if( run_as_daemon ) daemon( 1, 0 ); + } + + init_logs(); + logpid(); + + LOG( "Start cScm Configuration Daemon..." ); + + parse_config_file(); + + if( config_inotify ) init_config_inotify(); + + for( ;; ) + { + int max_sd; + struct timeval timeout; + fd_set listen_set; + + /************************************* + Waiting for events from inotify_fd: + */ + max_sd = inotify_fd + 1; + FD_ZERO( &listen_set ); + FD_SET( inotify_fd, &listen_set ); + + do + { + int rc; + + /********************************************* + Initialize the timeval struct to 5 seconds: + */ + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + rc = select( max_sd, &listen_set, NULL, NULL, &timeout ); + + /***************************************** + Check to see if the select call failed: + */ + if( rc < 0 && errno != EINTR ) + { + WARNING( "%s: inotify select() failed", config_fname ); + break; + } + + /************************************************ + Check to see if the 5 second time out expired: + */ + if( rc == 0 ) + { + /* Here we can output some log info. */ + break; + } + + if( FD_ISSET( inotify_fd, &listen_set ) ) + { + ssize_t n; + char *p; + + n = read( inotify_fd, buf, IN_BUFFER_SIZE ); + if( n == 0 ) + { + ERROR( "%s: read() from inotify file descriptor returned '0'", config_fname ); + LOG( "Stop cScm Configuration Daemon." ); + free_resources(); + exit( 1 ); + } + if( n == -1 ) + { + ERROR( "%s: read() from inotify file descriptor returned '-1'", config_fname ); + LOG( "Stop cScm Configuration Daemon." ); + free_resources(); + exit( 1 ); + } + + for( p = buf; p < buf + n; ) + { + event = (struct inotify_event *)p; + /************************************************************ + в принципе, нам хватает одного события и, если мы получили + нужное событие и перечитали config file, то здесь мы можем + выйти из цикла чтения событий с помощью break + */ + if( check_event( event ) ) + { + LOG( "Config file '%s' has been changed. Read new config content...", config_fname ); + parse_config_file(); + break; + } + p += sizeof(struct inotify_event) + event->len; + } + + } /* End if( FD_ISSET() ) */ + + } while( 1 ); + + /* + Здесь мы можем выполнить действия, которые необходимы + в том случае, если в течение 5-и секунд мы не получили + ни одного сигнала об изменении дескриптора inotify_fd. + */ + { + sleep( 5 ); + } + + } /* End of waiting for( ;; ) */ + + + LOG( "Stop cScm Configuration Daemon." ); + free_resources(); + + return 0; +} diff --git a/cscmd/main.h b/cscmd/main.h new file mode 100644 index 0000000..75404a4 --- /dev/null +++ b/cscmd/main.h @@ -0,0 +1,21 @@ + +#ifndef __MAIN_H +#define __MAIN_H + + +extern FILE *config; + + +#ifdef __cplusplus +extern "C" { +#endif + + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __MAIN_H */ diff --git a/cscmd/msglog.c b/cscmd/msglog.c new file mode 100644 index 0000000..2c4a821 --- /dev/null +++ b/cscmd/msglog.c @@ -0,0 +1,70 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +FILE *errlog; + +void (*fatal_error_hook)( void ); + +void logmsg( FILE *logfile, enum _msg_type type, char *format, ... ) +{ + va_list argp; + + if( ! format ) return; + + { + time_t t = time( NULL ); + struct tm tm = *localtime(&t); + + fprintf( logfile, "[%04d-%02d-%02d %02d:%02d:%02d]: ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec ); + } + + switch( type ) + { + case MSG_FATAL: fprintf( logfile, "%s: FATAL: ", program ); break; + case MSG_ERROR: fprintf( logfile, "%s: ERROR: ", program ); break; + case MSG_WARNING: fprintf( logfile, "%s: WARNING: ", program ); break; + case MSG_NOTICE: fprintf( logfile, "%s: NOTE: ", program ); break; + case MSG_INFO: fprintf( logfile, "%s: INFO: ", program ); break; + case MSG_DEBUG: fprintf( logfile, "%s: DEBUG: ", program ); break; + case MSG_LOG: + fprintf( logfile, "%s: ", program ); + break; + default: + fprintf( logfile, "%s: ", program ); + break; + } + va_start( argp, format ); + vfprintf( errlog, format, argp ); + fprintf( errlog, "\n" ); + (void)fflush( errlog ); +} diff --git a/cscmd/msglog.h b/cscmd/msglog.h new file mode 100644 index 0000000..fc256f0 --- /dev/null +++ b/cscmd/msglog.h @@ -0,0 +1,98 @@ + +/********************************************************************** + + Copyright 2019 Andrey V.Kosteltsev + + Licensed under the Radix.pro License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://radix.pro/licenses/LICENSE-1.0-en_US.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + **********************************************************************/ + +#ifndef _MSG_LOG_H_ +#define _MSG_LOG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern FILE *errlog; + +extern void (*fatal_error_hook)( void ); + +extern char *program; +extern int exit_status; + +enum _msg_type +{ + MSG_FATAL = 0, + MSG_ERROR, + MSG_WARNING, + MSG_NOTICE, + MSG_INFO, + MSG_DEBUG, + + MSG_LOG +}; + +#define FATAL_ERROR( ... ) \ + do \ + { \ + logmsg( errlog, MSG_FATAL, __VA_ARGS__ ); \ + if( fatal_error_hook) fatal_error_hook(); \ + exit( EXIT_FAILURE ); \ + } while (0) + +#define ERROR( ... ) \ + do \ + { \ + logmsg( errlog, MSG_ERROR, __VA_ARGS__ ); \ + ++exit_status; \ + } while (0) + +#define WARNING( ... ) \ + do \ + { \ + logmsg( errlog, MSG_WARNING, __VA_ARGS__ ); \ + } while (0) + +#define NOTICE( ... ) \ + do \ + { \ + logmsg( errlog, MSG_NOTICE, __VA_ARGS__ ); \ + } while (0) + +#define INFO( ... ) \ + do \ + { \ + logmsg( errlog, MSG_INFO, __VA_ARGS__ ); \ + } while (0) + +#define DEBUG( ... ) \ + do \ + { \ + logmsg( errlog, MSG_DEBUG, __VA_ARGS__ ); \ + } while (0) + +#define LOG( ... ) \ + do \ + { \ + logmsg( errlog, MSG_LOG, __VA_ARGS__ ); \ + } while (0) + + +extern void logmsg( FILE *logfile, enum _msg_type type, char *format, ... ); + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif + +#endif /* _MSG_LOG_H_ */ diff --git a/cscmd/parse.y b/cscmd/parse.y new file mode 100644 index 0000000..cdb2d5b --- /dev/null +++ b/cscmd/parse.y @@ -0,0 +1,107 @@ + +%{ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +%} + + +%union +{ + SYMBOL *sym; +} + +%token VARIABLE 501 SECTION 502 REPO 503 +%token NUMERICAL 510 STRING 511 PATH 512 +%right '=' +%left UNARYMINUS +/************************************************************ + Following tokens declared only for verbose error messaging + to prevent "$undefined" values of unexpected symbols: + */ +%token '!' '"' '#' '$' '%' '&' '\'' '(' ')' '*' '/' '+' '-' +%token '.' ',' ':' '<' '>' '?' '@' '[' '\\' ']' '^' '`' + +%start list + +%% +list: /* nothing */ + | list ';' + | list repo + | list section + | list assign ';' + | list error ';' { return 1; } + ; + +assign: VARIABLE '=' NUMERICAL { (void)assign_value( $1, $3 ); } + | VARIABLE '=' '+' NUMERICAL { (void)assign_value( $1, $4 ); } + | VARIABLE '=' '-' NUMERICAL %prec UNARYMINUS { $4->u.value = -$4->u.value; (void)assign_value( $1, $4 ); } + | VARIABLE '=' STRING { (void)assign_value( $1, $3 ); } + | VARIABLE '=' PATH { (void)assign_value( $1, $3 ); } + | NUMERICAL '=' NUMERICAL { (void)assign_value( $1, $3 ); } + | STRING '=' STRING { (void)assign_value( $1, $3 ); } + | PATH '=' PATH { (void)assign_value( $1, $3 ); } + ; + +alist: /* nothing */ + | alist ';' + | alist assign ';' + ; + +repo: REPO PATH '{' + { + if( lookup_repo( $2->u.path ) ) + { + error( "Repository '%s' is already defined", $2->u.path ); + return 1; + } + (void)assign_value( $1, $2 ); push_symlist( (SYMBOL **)&($1->list) ); + } + alist + '}' { pop_symlist(); } + ; + +rlist: /* nothing */ + | rlist repo + ; + +section: + SECTION STRING '{' + { + if( lookup_section( $2->u.string ) ) + { + error( "Section '%s' is already defined", $2->u.string ); + return 1; + } + (void)assign_value( $1, $2 ); push_symlist( (SYMBOL **)&($1->list) ); + } + rlist + '}' { pop_symlist(); } + ; + +%% + diff --git a/cscmd/rc.cscmd.in b/cscmd/rc.cscmd.in new file mode 100644 index 0000000..e3297e1 --- /dev/null +++ b/cscmd/rc.cscmd.in @@ -0,0 +1,96 @@ +#!/bin/sh +# +# /etc/rc.d/rc.@CSCM_PROGRAM@d - @CSCM_PROGRAM_NAME@ daemon control script. +# + +BIN=@sbindir@/@PROGRAM_DAEMON@ +CONF=@CSCM_CONFIG@ +BCF=@CSCM_HOME_PATH@/@CSCM_PROGRAM@/@CSCM_PROGRAM@.bcf +PID=@CSCM_PID_DIR@/@CSCM_PROGRAM@d.pid +LOG=@CSCM_LOG_DIR@/@CSCM_PROGRAM@d.log + +INOTIFY=--inotify + +cscmd_start() { + # Sanity checks. + if [ ! -r $CONF ]; then + echo "$CONF does not appear to exist. Abort." + exit 1 + fi + + if [ -s $PID ]; then + echo "@CSCM_PROGRAM_NAME@ daemon appears to already be running?" + exit 1 + fi + + echo "Starting @CSCM_PROGRAM_NAME@ server daemon..." + if [ -x $BIN ]; then + $BIN --daemonize $INOTIFY --scm=@CSCM_NAME@ --pid=$PID --log=$LOG --bcf=$BCF --config=$CONF + fi +} + +cscmd_test_conf() { + echo "Checking configuration for correct syntax and then" + echo "trying to open files referenced in configuration..." + echo "" + if [ -s $PID ] ; then + echo "@PROGRAM_DAEMON@: $CONF: Config file is correct." + else + $BIN --test --scm=@CSCM_NAME@ --pid=$PID --log=$LOG --bcf=$BCF --config=$CONF + fi +} + +cscmd_status() { + if [ -s $PID ] ; then + echo "@CSCM_PROGRAM_NAME@ daemon is running as PID: $(cat $PID)" + else + echo "@CSCM_PROGRAM_NAME@ daemon is stopped." + fi +} + +cscmd_stop() { + echo "Shutdown @CSCM_PROGRAM_NAME@ daemon gracefully..." + if [ -s $PID ] ; then + kill -TERM $(cat $PID) + else + echo "@CSCM_PROGRAM_NAME@ daemon appears to already be stopped." + fi +} + +cscmd_reload() { + echo "Reloading @CSCM_PROGRAM_NAME@ daemon configuration..." + if [ -s $PID ] ; then + kill -HUP $(cat $PID) + else + echo "@CSCM_PROGRAM_NAME@ daemon is not running." + fi +} + +cscmd_restart() { + cscmd_stop + sleep 3 + cscmd_start +} + +case "$1" in + check) + cscmd_test_conf + ;; + reload) + cscmd_reload + ;; + restart) + cscmd_restart + ;; + start) + cscmd_start + ;; + stop) + cscmd_stop + ;; + status) + cscmd_status + ;; + *) + echo "usage: `basename $0` {check|reload|restart|start|stop|status}" +esac diff --git a/cscmd/symtab.c b/cscmd/symtab.c new file mode 100644 index 0000000..62899c1 --- /dev/null +++ b/cscmd/symtab.c @@ -0,0 +1,471 @@ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +SYMBOL *symlist = NULL; + +static SYMTAB *symtab = NULL; + +static int constants_counter = 0; +static int sections_counter = 0; +static int repos_counter = 0; + + +static SYMBOL *free_const( SYMBOL *sp ) +{ + SYMBOL *next = NULL; + + if( !sp ) return next; + + next = sp->next; + + free( sp->name ); + + switch( sp->type ) + { + case STRING: + if( sp->u.string ) free( sp->u.string ); + break; + case PATH: + if( sp->u.string ) free( sp->u.path ); + break; + + case NUMERICAL: + default: + break; + } + + free( sp ); + + return next; +} + +static void free_symlist( SYMBOL *sp ); + +static SYMBOL *free_symbol( SYMBOL *sp ) +{ + SYMBOL *next = NULL; + + if( !sp ) return next; + + if( sp->list ) (void)free_symlist( sp->list ); + + next = sp->next; + + free( sp->name ); + + switch( sp->type ) + { + case SECTION: + case STRING: + if( sp->u.string ) free( sp->u.string ); + break; + case REPO: + case PATH: + if( sp->u.string ) free( sp->u.path ); + break; + + case VARIABLE: + case NUMERICAL: + default: + break; + } + + free( sp ); + + return next; +} + +static void free_symlist( SYMBOL *sp ) +{ + SYMBOL *next = NULL; + + if( !sp ) return; + + next = free_symbol( sp ); + while( next ) + { + next = free_symbol( next ); + } +} + +/****************************************** + Initialize the stak of symlist pointers: + */ +void init_symtab( void ) +{ + SYMTAB *sa = (SYMTAB *)xmalloc( sizeof( SYMTAB ) ); + + symtab = NULL; + symlist = NULL; + + constants_counter = 0; + sections_counter = 0; + repos_counter = 0; + + sa->symlist = (SYMBOL **)&symlist; + sa->next = symtab; + symtab = sa; +} + + +/******************************************* + Push the address of symlist to the stack: + */ +void push_symlist( SYMBOL **head ) +{ + if( head ) + { + SYMTAB *sa = (SYMTAB *)xmalloc( sizeof( SYMTAB ) ); + + sa->symlist = head; + sa->next = symtab; + symtab = sa; + } +} + +/******************************************** + Pop the address of symlist from the stack: + */ +void pop_symlist( void ) +{ + if( symtab && symtab->next ) + { + SYMTAB *sa = symtab; + symtab = symtab->next; + free( sa ); + } +} + +/************************************ + Free the stak of symlist pointers: + */ +void fini_symtab( void ) +{ + if( !symtab ) return; + + while( symtab ) + { + SYMTAB *sa = symtab; + symtab = symtab->next; + free( sa ); + } + + constants_counter = 0; + sections_counter = 0; + repos_counter = 0; + + symtab = NULL; + free_symlist( symlist ); /* free main symlist */ + symlist = NULL; +} + + +/****************************** + Reverse symlist recursively: + */ +void reverse_symlist( SYMBOL **head ) +{ + SYMBOL *prev = NULL, *curr = *head, *next; + + while( curr ) + { + if( curr->list ) reverse_symlist( (SYMBOL **)&(curr->list) ); + + next = curr->next; + curr->next = prev; + prev = curr; + curr = next; + } + + *head = prev; +} + +/****************************************************** + Remove temporary constants from symlist recursively: + */ +void remove_consts( SYMBOL **head ) +{ + SYMBOL *tmp = NULL; + + while( *head ) + { + tmp = *head; + if( !strncmp( tmp->name, "__const.", 8 ) ) + { + *head = tmp->next; + (void)free_const( tmp ); + } + else + { + head = &tmp->next; + if( tmp->list ) remove_consts( (SYMBOL **)&(tmp->list) ); + } + } +} + + +SYMBOL *assign_value( SYMBOL *dest, SYMBOL *src ) +{ + SYMBOL *ret = NULL; + + if( !dest || !src ) return ret; + + if( dest->type == VARIABLE ) /* always not initialized */ + { + dest->type = src->type; + dest->u.value = 0; + + switch( src->type ) + { + case NUMERICAL: + dest->u.value = src->u.value; + break; + case STRING: + dest->u.string = strdup( (const char *)src->u.string ); + break; + case PATH: + dest->u.path = strdup( (const char *)src->u.path ); + break; + default: + /* error */ + break; + } + } + else if( dest->type == STRING || dest->type == SECTION ) + { + switch( src->type ) + { + case STRING: + if( src->u.string ) + { + if( dest->u.string ) free( dest->u.string ); + dest->u.string = strdup( (const char *)src->u.string ); + } + else + { + if( dest->u.string ) free( dest->u.string ); + dest->u.string = NULL; + } + break; + default: + /* error */ + break; + } + } + else if( dest->type == PATH || dest->type == REPO ) + { + switch( src->type ) + { + case PATH: + if( src->u.path ) + { + if( dest->u.path ) free( dest->u.path ); + dest->u.path = strdup( (const char *)src->u.path ); + } + else + { + if( dest->u.path ) free( dest->u.path ); + dest->u.path = NULL; + } + break; + default: + /* error */ + break; + } + } + else if( dest->type == src->type ) + { + switch( src->type ) + { + case NUMERICAL: + dest->u.value = src->u.value; + break; + case STRING: + if( dest->u.string ) free( dest->u.string ); + dest->u.string = strdup( (const char *)src->u.string ); + break; + case PATH: + if( dest->u.path ) free( dest->u.path ); + dest->u.path = strdup( (const char *)src->u.path ); + break; + default: + /* error */ + break; + } + } + else + { + /* error */ + } + + return dest; +} + + +SYMBOL *install( const char *s, int type, ... ) +{ + SYMBOL *sp = NULL; + char name[80] = "__undef"; + + if( !symtab ) return sp; + + va_list argp; + + if( ! type ) return( sp ); + + sp = (SYMBOL *)xmalloc( sizeof( SYMBOL ) ); + + switch( type ) + { + case NUMERICAL: + case STRING: + case PATH: + sprintf( (char *)&name[0], "__const.%d", constants_counter++ ); + sp->name = strdup( (const char *)&name[0] ); + break; + case REPO: + sprintf( (char *)&name[0], "__repo.%d", repos_counter++ ); + sp->name = strdup( (const char *)&name[0] ); + break; + case SECTION: + sprintf( (char *)&name[0], "__section.%d", sections_counter++ ); + sp->name = strdup( (const char *)&name[0] ); + break; + default: + if( !s ) + sp->name = strdup( (const char *)&name[0] ); + else + sp->name = strdup( s ); + break; + } + sp->type = type; + + va_start( argp, type ); + + switch( type ) + { + case SECTION: + case STRING: + { + char *string = (char *)va_arg( argp, char * ); + if( string ) sp->u.string = strdup( (const char *)string ); + break; + } + case REPO: + case PATH: + { + char *path = (char *)va_arg( argp, char * ); + if( path ) sp->u.path = strdup( (const char *)path ); + break; + } + + case VARIABLE: + case NUMERICAL: + default: + sp->u.value = (int)va_arg( argp, int ); + break; + } + + sp->next = *(symtab->symlist); /* alloc in begin of list */ + *(symtab->symlist) = sp; + + return( sp ); +} + +/*********************************** + Find variable in current symlist: + */ +SYMBOL *lookup( const char *s ) +{ + SYMBOL *sp; + + for( sp = *(symtab->symlist); sp != (SYMBOL *)0; sp = sp->next ) + { + if( strcmp( sp->name, s ) == 0 ) return( sp ); + } + + return( 0 ); /* запись не найдена */ +} + +/********************************* + Find section in global symlist: + */ +SYMBOL *lookup_section( const char *s ) +{ + SYMBOL *sp; + + for( sp = symlist; sp != (SYMBOL *)0; sp = sp->next ) + { + if( sp->type == SECTION && sp->u.string && strcmp( sp->u.string, s ) == 0 ) return( sp ); + } + + return( 0 ); /* запись не найдена */ +} + +/******************************** + Find repo globally in symlist: + */ +#if 0 +SYMBOL *lookup_repo_global( const char *s ) +{ + SYMBOL *sp; + + for( sp = symlist; sp != (SYMBOL *)0; sp = sp->next ) + { + /*************************** + lookup in global section: + */ + if( sp->type == REPO && sp->u.path && strcmp( sp->u.path, s ) == 0 ) return( sp ); + + /************************* + lookup in each section: + */ + if( sp->type == SECTION && sp->list ) + { + SYMBOL *rp; + for( rp = sp->list; rp != (SYMBOL *)0; rp = rp->next ) + { + if( rp->type == REPO && rp->u.path && strcmp( rp->u.path, s ) == 0 ) return( rp ); + } + } + } + + return( 0 ); /* запись не найдена */ +} +#endif + +/******************************* + Find repo in current symlist: + */ +SYMBOL *lookup_repo( const char *s ) +{ + SYMBOL *sp; + + for( sp = *(symtab->symlist); sp != (SYMBOL *)0; sp = sp->next ) + { + if( sp->type == REPO && sp->u.path && strcmp( sp->u.path, s ) == 0 ) return( sp ); + } + + return( 0 ); /* запись не найдена */ +} diff --git a/cscmd/symtab.h b/cscmd/symtab.h new file mode 100644 index 0000000..5019569 --- /dev/null +++ b/cscmd/symtab.h @@ -0,0 +1,67 @@ + +#ifndef __SYMTAB_H +#define __SYMTAB_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************** + SYMBOL is a node of symlist: + */ +typedef struct symbol SYMBOL; +struct symbol +{ + char *name; /* Variable name */ + int type; /* VARIABLE, SECTION, REPO, NUMERICAL, STRING, PATH */ + union + { + int value; /* for NUMERICAL */ + char *string; /* for STRING */ + char *path; /* for PATH */ + } u; + + struct symbol *list; /* The list of variables. Used for SECTION and REPO */ + + struct symbol *next; /* Next Symbol */ +}; + +/********************************************** + SYMTAB is an entry of the stack of symlists: + */ +typedef struct symtab SYMTAB; +struct symtab +{ + SYMBOL **symlist; + struct symtab *next; /* Next Entry */ +}; + + +extern SYMBOL *symlist; + +extern void init_symtab( void ); +extern void push_symlist( SYMBOL **head ); +extern void pop_symlist( void ); +extern void fini_symtab( void ); + +extern void reverse_symlist( SYMBOL **head ); +extern void remove_consts( SYMBOL **head ); + +//debug +extern void print_symlist( int indent, SYMBOL *head ); + +extern SYMBOL *install( const char *s, int type, ... ); +extern SYMBOL *lookup( const char *s ); +extern SYMBOL *lookup_section( const char *s ); +extern SYMBOL *lookup_repo( const char *s ); + +extern SYMBOL *assign_value( SYMBOL *dest, SYMBOL *src ); + + + +#ifdef __cplusplus +} +#endif + +#endif /* __SYMTAB_H */ diff --git a/cscmd/utf8ing.c b/cscmd/utf8ing.c new file mode 100644 index 0000000..1d67c79 --- /dev/null +++ b/cscmd/utf8ing.c @@ -0,0 +1,121 @@ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +static const ucs4_t replacement_char = 0xfffd; +static const ucs4_t maximum_ucs4 = 0x7fffffff; + +static const int half_shift = 10; +static const ucs4_t half_base = 0x0010000; + +static const ucs4_t surrogate_high_start = 0xd800; +static const ucs4_t surrogate_high_end = 0xdbff; +static const ucs4_t surrogate_low_start = 0xdc00; +static const ucs4_t surrogate_low_end = 0xdfff; + +static utf8_t +first_byte_mark[7] = { 0x00, 0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc }; + + +/*************************************************************** + static copy_ucs4_to_utf8() + + Переводит строку символов UCS4( src ) в UTF8( dest ). + + Возвращаемое значение: + Количество байт, реально записанное в DEST. + + NOTE: + Выход за пределы памяти, выделенной под указатель DEST + не контролируются. + Подразумевается, что строка SRC имеет null-терминатор. + ***************************************************************/ +int copy_ucs4_to_utf8( utf8_t *dest, const ucs4_t *src ) +{ + utf8_t target[7]; + utf8_t *ptr; + int count = 0; + + while( *src ) + { + ucs4_t c; + int bytes_to_write = 0; + const ucs4_t byte_mask = 0xbf; + const ucs4_t byte_mark = 0x80; + + c = *src++; + + if( c >= surrogate_high_start && + c <= surrogate_high_end && *src ) + { + ucs4_t c2 = *src; + + if( c2 >= surrogate_low_start && + c2 <= surrogate_low_end ) + { + c = ((c - surrogate_high_start) << half_shift) + + (c2 - surrogate_low_start) + half_base; + ++src; + } + } + + if( c < 0x80 ) bytes_to_write = 1; + else if( c < 0x800 ) bytes_to_write = 2; + else if( c < 0x10000 ) bytes_to_write = 3; + else if( c < 0x200000 ) bytes_to_write = 4; + else if( c < 0x4000000 ) bytes_to_write = 5; + else if( c <= maximum_ucs4 ) bytes_to_write = 6; + else + { + bytes_to_write = 2; c = replacement_char; + } + + ptr = &target[0] + bytes_to_write; + + switch( bytes_to_write ) + { + case 6: + *--ptr = (c | byte_mark) & byte_mask; c >>= 6; + case 5: + *--ptr = (c | byte_mark) & byte_mask; c >>= 6; + case 4: + *--ptr = (c | byte_mark) & byte_mask; c >>= 6; + case 3: + *--ptr = (c | byte_mark) & byte_mask; c >>= 6; + case 2: + *--ptr = (c | byte_mark) & byte_mask; c >>= 6; + case 1: + *--ptr = c | first_byte_mark[bytes_to_write]; + } + + ptr = &target[0]; + + while( bytes_to_write > 0 ) + { + *dest++ = *ptr++; /* write byte */ + --bytes_to_write; + ++count; + } + + } /* End while( *src ) */ + + *dest = (utf8_t)0; /* null terminator */ + + return( count ); + +} /* End of static copy_ucs4_to_utf8() */ diff --git a/cscmd/utf8ing.h b/cscmd/utf8ing.h new file mode 100644 index 0000000..d96cda8 --- /dev/null +++ b/cscmd/utf8ing.h @@ -0,0 +1,22 @@ + +#ifndef __UTF8_H +#define __UTF8_H + + +typedef unsigned int ucs4_t; +typedef unsigned char utf8_t; + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern int copy_ucs4_to_utf8( utf8_t *, const ucs4_t * ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __UTF8_H */ diff --git a/cscmd/xalloc.c b/cscmd/xalloc.c new file mode 100644 index 0000000..80f2581 --- /dev/null +++ b/cscmd/xalloc.c @@ -0,0 +1,36 @@ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include +#include + + +void *xmalloc( size_t n ) +{ + void *p = NULL; + + p = malloc( n ); + if( !p ) no_space(); + bzero( p, n ); + + return( p ); +} + +void *xrealloc( void *b, size_t n ) +{ + void *p = NULL; + + p = realloc( b , n ); + if( !p ) no_space(); + + return( p ); +} diff --git a/cscmd/xalloc.h b/cscmd/xalloc.h new file mode 100644 index 0000000..3c9691b --- /dev/null +++ b/cscmd/xalloc.h @@ -0,0 +1,19 @@ + +#ifndef __XALLOC_H +#define __XALLOC_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern void *xmalloc ( size_t ); +extern void *xrealloc ( void *, size_t ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __XALLOC_H */ -- cgit v1.2.3