diff --git a/configure.ac b/configure.ac index 67e44338..2d014f2d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,282 +1,286 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.63]) AC_INIT([kronosnetd], [0.1], [fabbione@kronosnet.org]) AM_INIT_AUTOMAKE([1.11.1 dist-bzip2 dist-xz color-tests -Wno-portability]) LT_PREREQ([2.2.6]) LT_INIT AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([kronosnetd/main.c]) AC_CONFIG_HEADERS([config.h]) AC_CANONICAL_HOST AC_PROG_LIBTOOL AC_LANG([C]) if test "$prefix" = "NONE"; then prefix="/usr" if test "$localstatedir" = "\${prefix}/var"; then localstatedir="/var" fi if test "$sysconfdir" = "\${prefix}/etc"; then sysconfdir="/etc" fi fi # Checks for programs. if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then AC_MSG_ERROR(["you don't seem to have GNU make; it is required"]) fi AC_PROG_CC AM_PROG_CC_C_O AC_PROG_LN_S AC_PROG_INSTALL AC_PROG_MAKE_SET AC_PROG_CXX AC_PROG_RANLIB AC_CHECK_PROGS([PUBLICAN], [publican], [:]) AC_CHECK_PROGS([PKGCONFIG], [pkg-config]) ## local helper functions # this function checks if CC support options passed as # args. Global CFLAGS are ignored during this test. cc_supports_flag() { saveCPPFLAGS="$CPPFLAGS" CPPFLAGS="$@" AC_MSG_CHECKING([whether $CC supports "$@"]) AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])], [RC=0; AC_MSG_RESULT([yes])], [RC=1; AC_MSG_RESULT([no])]) CPPFLAGS="$saveCPPFLAGS" return $RC } # helper macro to check libs without adding them to LIBS check_lib_no_libs() { lib_no_libs_arg1=$1 shift lib_no_libs_arg2=$1 shift lib_no_libs_args=$@ AC_CHECK_LIB([$lib_no_libs_arg1], [$lib_no_libs_arg2],,, [$lib_no_libs_args]) LIBS=$ac_check_lib_save_LIBS } # Checks for C features AC_C_INLINE # Checks for libraries. AC_CHECK_LIB([pthread], [pthread_create]) AC_CHECK_LIB([rt], [clock_gettime]) PKG_CHECK_MODULES([nss],[nss]) PKG_CHECK_MODULES([LIBQB], [libqb]) AC_CHECK_LIB([qb], [qb_log_thread_priority_set], [have_qb_log_thread_priority_set="yes"], [have_qb_log_thread_priority_set="no"]) if test "x${have_qb_log_thread_priority_set}" = xyes; then AC_DEFINE_UNQUOTED([HAVE_QB_LOG_THREAD_PRIORITY_SET], 1, [have qb_log_thread_priority_set]) fi # Checks for header files. AC_CHECK_HEADERS([fcntl.h]) AC_CHECK_HEADERS([stdlib.h]) AC_CHECK_HEADERS([string.h]) AC_CHECK_HEADERS([sys/ioctl.h]) AC_CHECK_HEADERS([syslog.h]) AC_CHECK_HEADERS([unistd.h]) AC_CHECK_HEADERS([netinet/in.h]) AC_CHECK_HEADERS([sys/socket.h]) AC_CHECK_HEADERS([arpa/inet.h]) AC_CHECK_HEADERS([netdb.h]) AC_CHECK_HEADERS([limits.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T AC_TYPE_PID_T AC_TYPE_SSIZE_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T # Checks for library functions. AC_FUNC_ALLOCA AC_FUNC_FORK AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_FUNCS([memset]) AC_CHECK_FUNCS([strdup]) AC_CHECK_FUNCS([strerror]) AC_CHECK_FUNCS([dup2]) AC_CHECK_FUNCS([select]) AC_CHECK_FUNCS([socket]) AC_CHECK_FUNCS([inet_ntoa]) AC_CHECK_FUNCS([memmove]) AC_CHECK_FUNCS([strchr]) AC_CHECK_FUNCS([atexit]) AC_CHECK_FUNCS([ftruncate]) AC_CHECK_FUNCS([strrchr]) AC_CHECK_FUNCS([strstr]) AC_CHECK_FUNCS([clock_gettime]) # PAM check AC_CHECK_HEADERS([security/pam_appl.h], [AC_CHECK_LIB([pam], [pam_start])], [AC_MSG_ERROR([Unable to find LinuxPAM devel files])]) AC_CHECK_HEADERS([security/pam_misc.h], [AC_CHECK_LIB([pam_misc], [misc_conv])], [AC_MSG_ERROR([Unable to find LinuxPAM MISC devel files])]) # local options AC_ARG_ENABLE([debug], [ --enable-debug enable debug build. ], [ default="no" ]) AC_ARG_ENABLE([publicandocs], [ --enable-publicandocs enable docs build. ], [ default="no" ]) AC_ARG_WITH([syslogfacility], [ --with-syslogfacility=FACILITY default syslog facility. ], [ SYSLOGFACILITY="$withval" ], - [ SYSLOGFACILITY="LOG_LOCAL4" ]) + [ SYSLOGFACILITY="LOG_DAEMON" ]) AC_ARG_WITH([sysloglevel], [ --with-sysloglevel=LEVEL default syslog level. ], [ SYSLOGLEVEL="$withval" ], [ SYSLOGLEVEL="LOG_INFO" ]) AC_ARG_WITH([defaultadmgroup], [ --with-defaultadmgroup=GROUP define PAM group. Users part of this group will be allowed to configure kronosnet. Others will only receive read-only rights. ], [ DEFAULTADMGROUP="$withval" ], [ DEFAULTADMGROUP="kronosnetadm" ]) ## random vars LOGDIR=${localstatedir}/log/ RUNDIR=${localstatedir}/run/ DEFAULT_CONFIG_DIR=${sysconfdir}/kronosnet ## do subst AM_CONDITIONAL(BUILD_DOCS, test "x${enable_publicandocs}" = xyes) AC_SUBST([DEFAULT_CONFIG_DIR]) AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_DIR], "$(eval echo ${DEFAULT_CONFIG_DIR})", [Default config directory]) AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_FILE], "$(eval echo ${DEFAULT_CONFIG_DIR}/kronosnetd.conf)", [Default config file]) AC_DEFINE_UNQUOTED([LOGDIR], "$(eval echo ${LOGDIR})", [Default logging directory]) +AC_DEFINE_UNQUOTED([DEFAULT_LOG_FILE], + "$(eval echo ${LOGDIR}/kronosnetd.log)", + [Default log file]) + AC_DEFINE_UNQUOTED([RUNDIR], "$(eval echo ${RUNDIR})", [Default run directory]) AC_DEFINE_UNQUOTED([SYSLOGFACILITY], "$(eval echo ${SYSLOGFACILITY})", [Default syslog facility]) AC_DEFINE_UNQUOTED([SYSLOGLEVEL], "$(eval echo ${SYSLOGLEVEL})", [Default syslog level]) AC_DEFINE_UNQUOTED([DEFAULTADMGROUP], "$(eval echo ${DEFAULTADMGROUP})", [Default admin group]) ## *FLAGS handling ENV_CFLAGS="$CFLAGS" ENV_CPPFLAGS="$CPPFLAGS" ENV_LDFLAGS="$LDFLAGS" # debug build stuff if test "x${enable_debug}" = xyes; then AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code]) OPT_CFLAGS="-O0" else OPT_CFLAGS="-O3" fi # gdb flags if test "x${GCC}" = xyes; then GDB_FLAGS="-ggdb3" else GDB_FLAGS="-g" fi # extra warnings EXTRA_WARNINGS="" WARNLIST=" all shadow missing-prototypes missing-declarations strict-prototypes declaration-after-statement pointer-arith write-strings cast-align bad-function-cast missing-format-attribute format=2 format-security format-nonliteral no-long-long unsigned-char gnu89-inline no-strict-aliasing error address cpp enum-compare overflow parentheses sequence-point switch uninitialized unused-but-set-variable unused-function unused-result unused-value unused-variable " for j in $WARNLIST; do if cc_supports_flag -W$j; then EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j"; fi done CFLAGS="$ENV_CFLAGS $lt_prog_compiler_pic $OPT_CFLAGS $GDB_FLAGS \ $EXTRA_WARNINGS $WERROR_CFLAGS" CPPFLAGS="$ENV_CPPFLAGS" LDFLAGS="$ENV_LDFLAGS $lt_prog_compiler_pic" AC_CONFIG_FILES([ Makefile libtap/Makefile libtap/libtap.pc kronosnetd/Makefile libknet/Makefile libknet/libknet.pc libvty/Makefile libvty/libvty.pc docs/Makefile tests/Makefile ]) AC_OUTPUT diff --git a/kronosnetd/cfg.h b/kronosnetd/cfg.h index f7d9f3d7..59c61eaf 100644 --- a/kronosnetd/cfg.h +++ b/kronosnetd/cfg.h @@ -1,40 +1,41 @@ #ifndef __CFG_H__ #define __CFG_H__ #include #include #include "libtap.h" #include "libknet.h" struct knet_cfg_eth { tap_t tap; uint8_t node_id; }; struct knet_cfg_ring { knet_handle_t knet_h; int base_port; }; struct knet_cfg { struct knet_cfg_eth cfg_eth; struct knet_cfg_ring cfg_ring; int active; struct knet_cfg *next; }; struct knet_cfg_top { char *conffile; + char *logfile; char *vty_ip; char *vty_port; struct knet_cfg *knet_cfg; }; struct knet_cfg *knet_get_iface(const char *name, const int create); void knet_destroy_iface(struct knet_cfg *knet_iface); extern struct knet_cfg_top knet_cfg_head; #endif diff --git a/kronosnetd/main.c b/kronosnetd/main.c index e0a1b320..74dabd5e 100644 --- a/kronosnetd/main.c +++ b/kronosnetd/main.c @@ -1,304 +1,318 @@ #include "config.h" #include #include #include #include #include #include #include #include #include #include "cfg.h" #include "vty.h" #include "utils.h" #define LOCKFILE_NAME RUNDIR PACKAGE ".pid" -#define OPTION_STRING "hdfVc:b:p:" +#define OPTION_STRING "hdfVc:l:b:p:" static int daemonize = 1; struct knet_cfg_top knet_cfg_head; extern int utils_debug; static void print_usage(void) { printf("Usage:\n\n"); printf(PACKAGE " [options]\n\n"); printf("Options:\n\n"); printf(" -b Bind management VTY to ip_addr (default: all)\n"); printf(" -p Bind management VTY to port (default %d)\n", KNET_VTY_DEFAULT_PORT); printf(" -c Use config file (default "DEFAULT_CONFIG_FILE")\n"); + printf(" -l Use log file (default "DEFAULT_LOG_FILE")\n"); printf(" -f Do not fork in background\n"); printf(" -d Enable debugging output\n"); printf(" -h This help\n"); printf(" -V Print program version information\n"); return; } static int read_arguments(int argc, char **argv) { int cont = 1; int optchar; int int_port; while (cont) { optchar = getopt(argc, argv, OPTION_STRING); switch (optchar) { case 'b': knet_cfg_head.vty_ip = strdup(optarg); if (!knet_cfg_head.vty_ip) return -1; break; case 'p': int_port = atoi(optarg); if ((int_port < 0) || (int_port > 65535)) { errno = EINVAL; return -1; } knet_cfg_head.vty_port = strdup(optarg); if (!knet_cfg_head.vty_port) return -1; break; case 'c': knet_cfg_head.conffile = strdup(optarg); if (!knet_cfg_head.conffile) return -1; break; + case 'l': + knet_cfg_head.logfile = strdup(optarg); + if (!knet_cfg_head.logfile) + return -1; + break; + case 'd': utils_debug = 1; break; case 'f': daemonize = 0; break; case 'h': print_usage(); exit(EXIT_SUCCESS); break; case 'V': printf(PACKAGE " " PACKAGE_VERSION " (built " __DATE__ " " __TIME__ ")\n"); exit(EXIT_SUCCESS); break; case EOF: cont = 0; break; default: fprintf(stderr, "unknown option: %c\n", optchar); print_usage(); exit(EXIT_FAILURE); break; } } return 0; } static int set_scheduler(void) { struct sched_param sched_param; int err; err = sched_get_priority_max(SCHED_RR); if (err < 0) { log_error("Could not get maximum scheduler priority"); return err; } sched_param.sched_priority = err; err = sched_setscheduler(0, SCHED_RR, &sched_param); if (err < 0) log_error("could not set SCHED_RR priority %d", sched_param.sched_priority); return err; } static void remove_lockfile(void) { unlink(LOCKFILE_NAME); } static int create_lockfile(const char *lockfile) { int fd, value; size_t bufferlen; ssize_t write_out; struct flock lock; char buffer[50]; if ((fd = open(lockfile, O_CREAT | O_WRONLY, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) < 0) { fprintf(stderr, "Cannot open lockfile [%s], error was [%s]\n", lockfile, strerror(errno)); return -1; } lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; retry_fcntl: if (fcntl(fd, F_SETLK, &lock) < 0) { switch (errno) { case EINTR: goto retry_fcntl; break; case EACCES: case EAGAIN: fprintf(stderr, "Cannot lock lockfile [%s], error was [%s]\n", lockfile, strerror(errno)); break; default: fprintf(stderr, "process is already running\n"); } goto fail_close; } if (ftruncate(fd, 0) < 0) { fprintf(stderr, "Cannot truncate pidfile [%s], error was [%s]\n", lockfile, strerror(errno)); goto fail_close_unlink; } memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer)-1, "%u\n", getpid()); bufferlen = strlen(buffer); write_out = write(fd, buffer, bufferlen); if ((write_out < 0) || (write_out == 0 && errno)) { fprintf(stderr, "Cannot write pid to pidfile [%s], error was [%s]\n", lockfile, strerror(errno)); goto fail_close_unlink; } if ((write_out == 0) || (write_out < bufferlen)) { fprintf(stderr, "Cannot write pid to pidfile [%s], shortwrite of" "[%zu] bytes, expected [%zu]\n", lockfile, write_out, bufferlen); goto fail_close_unlink; } if ((value = fcntl(fd, F_GETFD, 0)) < 0) { fprintf(stderr, "Cannot get close-on-exec flag from pidfile [%s], " "error was [%s]\n", lockfile, strerror(errno)); goto fail_close_unlink; } value |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, value) < 0) { fprintf(stderr, "Cannot set close-on-exec flag from pidfile [%s], " "error was [%s]\n", lockfile, strerror(errno)); goto fail_close_unlink; } atexit(remove_lockfile); return 0; fail_close_unlink: if (unlink(lockfile)) fprintf(stderr, "Unable to unlink %s\n", lockfile); fail_close: if (close(fd)) fprintf(stderr, "Unable to close %s file descriptor\n", lockfile); return -1; } static void set_cfg_defaults(void) { if (!knet_cfg_head.conffile) knet_cfg_head.conffile = strdup(DEFAULT_CONFIG_FILE); if (!knet_cfg_head.conffile) { - fprintf(stderr, "Unable to allocate memory for config file"); + fprintf(stderr, "Unable to allocate memory for config file\n"); + exit(EXIT_FAILURE); + } + + if (!knet_cfg_head.logfile) + knet_cfg_head.logfile = strdup(DEFAULT_LOG_FILE); + if (!knet_cfg_head.conffile) { + fprintf(stderr, "Unable to allocate memory for log file\n"); exit(EXIT_FAILURE); } if (!knet_cfg_head.vty_ip) knet_cfg_head.vty_ip = strdup("::"); if (!knet_cfg_head.vty_ip) { - fprintf(stderr, "Unable to allocate memory for default ip address"); + fprintf(stderr, "Unable to allocate memory for default ip address\n"); exit(EXIT_FAILURE); } if (!knet_cfg_head.vty_port) { char portbuf[8]; memset(&portbuf, 0, sizeof(portbuf)); snprintf(portbuf, sizeof(portbuf), "%d", KNET_VTY_DEFAULT_PORT); knet_cfg_head.vty_port = strdup(portbuf); } if (!knet_cfg_head.vty_port) { - fprintf(stderr, "Unable to allocate memory for default port address"); + fprintf(stderr, "Unable to allocate memory for default port address\n"); exit(EXIT_FAILURE); } } int main(int argc, char **argv) { int err; memset(&knet_cfg_head, 0, sizeof(struct knet_cfg_top)); utils_syslog=1; if (create_lockfile(LOCKFILE_NAME) < 0) { - fprintf(stderr, "Unable to create lockfile"); + fprintf(stderr, "Unable to create lockfile\n"); exit(EXIT_FAILURE); } if (read_arguments(argc, argv) < 0) { - fprintf(stderr, "Unable to parse options"); + fprintf(stderr, "Unable to parse options\n"); exit(EXIT_FAILURE); } set_cfg_defaults(); if (daemonize) { if (daemon(0, 0) < 0) { perror("Unable to daemonize"); exit(EXIT_FAILURE); } } log_info(PACKAGE " version " VERSION); err = set_scheduler(); if (err < 0) goto out; if (knet_vty_main_loop() < 0) log_error("Detected fatal error in main loop"); out: if (knet_cfg_head.conffile) free(knet_cfg_head.conffile); if (knet_cfg_head.vty_ip) free(knet_cfg_head.vty_ip); if (knet_cfg_head.vty_port) free(knet_cfg_head.vty_port); return 0; }