From a7c3a5c479812f1f69acac276402fdffc60371aa Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Wed, 3 Sep 2014 23:09:49 +0200 Subject: added initial code - based on rharchive --- src/Makefile | 110 +++++++++++++++++ src/configure | 167 +++++++++++++++++++++++++ src/daemon.h | 168 +++++++++++++++++++++++++ src/datatypes.h | 41 +++++++ src/file_list.c | 222 +++++++++++++++++++++++++++++++++ src/file_list.h | 67 ++++++++++ src/log.c | 265 ++++++++++++++++++++++++++++++++++++++++ src/log.h | 87 +++++++++++++ src/log_targets.h | 357 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/options.c | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/options.h | 67 ++++++++++ src/sig_handler.c | 113 +++++++++++++++++ src/sig_handler.h | 39 ++++++ src/slist.c | 132 ++++++++++++++++++++ src/slist.h | 53 ++++++++ src/string_list.c | 71 +++++++++++ src/string_list.h | 43 +++++++ src/sydra.c | 255 ++++++++++++++++++++++++++++++++++++++ src/sysexec.c | 226 ++++++++++++++++++++++++++++++++++ src/sysexec.h | 51 ++++++++ src/writer.c | 243 +++++++++++++++++++++++++++++++++++++ src/writer.h | 64 ++++++++++ 22 files changed, 3178 insertions(+) create mode 100644 src/Makefile create mode 100755 src/configure create mode 100644 src/daemon.h create mode 100644 src/datatypes.h create mode 100644 src/file_list.c create mode 100644 src/file_list.h create mode 100644 src/log.c create mode 100644 src/log.h create mode 100644 src/log_targets.h create mode 100644 src/options.c create mode 100644 src/options.h create mode 100644 src/sig_handler.c create mode 100644 src/sig_handler.h create mode 100644 src/slist.c create mode 100644 src/slist.h create mode 100644 src/string_list.c create mode 100644 src/string_list.h create mode 100644 src/sydra.c create mode 100644 src/sysexec.c create mode 100644 src/sysexec.h create mode 100644 src/writer.c create mode 100644 src/writer.h (limited to 'src') diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..456b20a --- /dev/null +++ b/src/Makefile @@ -0,0 +1,110 @@ +## +## sydra +## +## sydra is a toolbox which allows you to set up multiple bidirectional +## Video/Audio streams from external locations. +## sydra has been written to be used for the Elevate Festival in Graz +## Austria in order to involve external locations to present themselves +## at the festival. +## Sydra is based on GStreamer and is written in C. +## +## +## Copyright (C) 2014 Christian Pointner +## +## This file is part of sydra. +## +## sydra is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## any later version. +## +## sydra is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with sydra. If not, see . +## + +ifneq ($(MAKECMDGOALS),distclean) +include include.mk +endif + +EXECUTABLE := sydra + +C_OBJS := log.o \ + sig_handler.o \ + options.o \ + slist.o \ + string_list.o \ + sysexec.o \ + file_list.o \ + writer.o \ + sydra.o + +C_SRCS := $(C_OBJS:%.o=%.c) + +.PHONY: clean cleanall distclean manpage install install-bin uninstall remove + +all: $(EXECUTABLE) + +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -MM $(CFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$; echo '(re)building $@' + +ifneq ($(MAKECMDGOALS),distclean) +-include $(C_SRCS:%.c=%.d) +endif + +$(EXECUTABLE): $(C_OBJS) + $(CC) $(C_OBJS) -o $@ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +strip: $(EXECUTABLE) + $(STRIP) -s $(EXECUTABLE) + + +distclean: clean + find . -name *.o -exec rm -f {} \; + find . -name "*.\~*" -exec rm -rf {} \; + rm -f include.mk + rm -f config.h + +clean: + rm -f *.o + rm -f *.d + rm -f *.d.* + rm -f $(EXECUTABLE) + +INSTALL_TARGETS := install-bin +REMOVE_TARGETS := remove-bin + +ifdef MANDIR +INSTALL_TARGETS += install-man +REMOVE_TARGETS += remove-man +endif + +install: all $(INSTALL_TARGETS) + +install-bin: $(EXECUTABLE) + $(INSTALL) -d $(DESTDIR)$(BINDIR) + $(INSTALL) -m 755 $(EXECUTABLE) $(DESTDIR)$(BINDIR) + +install-man: manpage + $(INSTALL) -d $(DESTDIR)$(MANDIR)/man8/ + $(INSTALL) -m 644 ../doc/$(EXECUTABLE).8 $(DESTDIR)$(MANDIR)/man8/$(EXECUTABLE).8 + +uninstall: remove + +remove: $(REMOVE_TARGETS) + +remove-bin: + rm -f $(DESTDIR)$(BINDIR)/$(EXECUTABLE) + +remove-man: + rm -f $(DESTDIR)$(MANDIR)/man8/$(EXECUTABLE).8 diff --git a/src/configure b/src/configure new file mode 100755 index 0000000..f7ddc99 --- /dev/null +++ b/src/configure @@ -0,0 +1,167 @@ +#!/bin/sh +# +# sydra +# +# sydra is a toolbox which allows you to set up multiple bidirectional +# Video/Audio streams from external locations. +# sydra has been written to be used for the Elevate Festival in Graz +# Austria in order to involve external locations to present themselves +# at the festival. +# Sydra is based on GStreamer and is written in C. +# +# +# Copyright (C) 2014 Christian Pointner +# +# This file is part of sydra. +# +# sydra is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# any later version. +# +# sydra is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with sydra. If not, see . +# + +set -e + +TARGET=`uname -s` +EBUILD_COMPAT=0 +USE_CLANG=0 + +PREFIX='/usr/local' +BINDIR='' + +print_usage() { + echo "configure --help print this" + echo " --target= build target i.e. Linux (default: autodetect)" + echo " --prefix= the installation prefix (default: /usr/local)" + echo " --bindir= the path to the bin directory (default: $PREFIX/bin)" + echo " --use-clang use clang/llvm as compiler/linker" +} + +for arg +do + case $arg in + --target=*) + TARGET=${arg#--target=} + ;; + --prefix=*) + PREFIX=${arg#--prefix=} + ;; + --bindir=*) + BINDIR=${arg#--bindir=} + ;; + --use-clang) + USE_CLANG=1 + ;; + --ebuild-compat) + EBUILD_COMPAT=1 + ;; + --help) + print_usage + exit 0 + ;; + *) + ERRORS="$ERRORS $arg" + ;; + esac +done + +if [ -n "$ERRORS" ] && [ $EBUILD_COMPAT -ne 1 ]; then + for error in $ERRORS; do + echo "Unknown argument: $error" + done + + print_usage + exit 1 +fi + +if [ $USE_CLANG -eq 0 ]; then + CFLAGS='-g -Wall -O2' + LDFLAGS='-g -Wall -O2' + COMPILER='gcc' +else + CFLAGS='-g -O2' + LDFLAGS='-g -O2' + COMPILER='clang' +fi + +CFLAGS="$CFLAGS $(pkg-config --cflags gstreamer-0.10) -DGST_DISABLE_DEPRECATED" +LDFLAGS="$LDFLAGS $(pkg-config --libs gstreamer-0.10)" + +rm -f include.mk +rm -f config.h +case $TARGET in + Linux) + ;; + OpenBSD|FreeBSD|NetBSD|GNU/kFreeBSD) + CFLAGS=$CFLAGS' -I/usr/local/include' + LDFLAGS=$LDFLAGS' -L/usr/local/lib' + ;; + *) + echo "platform not supported" + exit 1; + ;; +esac + +if [ -z "$BINDIR" ]; then + BINDIR=$PREFIX/bin +fi + +cat > include.mk </dev/null; then + GIT_HASH=`git rev-parse HEAD 2> /dev/null` + if [ -n "$GIT_HASH" ]; then + VERSION="$VERSION (git $GIT_HASH)" + fi +fi + +HOSTNAME=`hostname` +DATE=`date +"%d.%m.%Y %H:%M:%S %Z"` + +cat > config.h < + * + * This file is part of sydra. + * + * sydra is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * sydra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sydra. If not, see . + */ + +#ifndef SYDRA_daemon_h_INCLUDED +#define SYDRA_daemon_h_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +struct priv_info_struct { + struct passwd* pw_; + struct group* gr_; +}; +typedef struct priv_info_struct priv_info_t; + +int priv_init(priv_info_t* priv, const char* username, const char* groupname) +{ + if(!priv) + return -1; + + priv->pw_ = NULL; + priv->gr_ = NULL; + + priv->pw_ = getpwnam(username); + if(!priv->pw_) { + log_printf(ERROR, "unknown user %s", username); + return -1; + } + + if(groupname) + priv->gr_ = getgrnam(groupname); + else + priv->gr_ = getgrgid(priv->pw_->pw_gid); + + if(!priv->gr_) { + log_printf(ERROR, "unknown group %s", groupname); + return -1; + } + + return 0; +} + +int priv_drop(priv_info_t* priv) +{ + if(!priv || !priv->pw_ || !priv->gr_) { + log_printf(ERROR, "privileges not initialized properly"); + return -1; + } + + if(setgid(priv->gr_->gr_gid)) { + log_printf(ERROR, "setgid('%s') failed: %s", priv->gr_->gr_name, strerror(errno)); + return -1; + } + + gid_t gr_list[1]; + gr_list[0] = priv->gr_->gr_gid; + if(setgroups (1, gr_list)) { + log_printf(ERROR, "setgroups(['%s']) failed: %s", priv->gr_->gr_name, strerror(errno)); + return -1; + } + + if(setuid(priv->pw_->pw_uid)) { + log_printf(ERROR, "setuid('%s') failed: %s", priv->pw_->pw_name, strerror(errno)); + return -1; + } + + log_printf(NOTICE, "dropped privileges to %s:%s", priv->pw_->pw_name, priv->gr_->gr_name); + return 0; +} + + +int do_chroot(const char* chrootdir) +{ + if(getuid() != 0) { + log_printf(ERROR, "this program has to be run as root in order to run in a chroot"); + return -1; + } + + if(chroot(chrootdir)) { + log_printf(ERROR, "can't chroot to %s: %s", chrootdir, strerror(errno)); + return -1; + } + log_printf(NOTICE, "we are in chroot jail (%s) now", chrootdir); + if(chdir("/")) { + log_printf(ERROR, "can't change to /: %s", strerror(errno)); + return -1; + } + + return 0; +} + +void daemonize() +{ + pid_t pid; + + pid = fork(); + if(pid < 0) { + log_printf(ERROR, "daemonizing failed at fork(): %s, exitting", strerror(errno)); + exit(-1); + } + if(pid) exit(0); + + umask(0); + + if(setsid() < 0) { + log_printf(ERROR, "daemonizing failed at setsid(): %s, exitting", strerror(errno)); + exit(-1); + } + + pid = fork(); + if(pid < 0) { + log_printf(ERROR, "daemonizing failed at fork(): %s, exitting", strerror(errno)); + exit(-1); + } + if(pid) exit(0); + + if ((chdir("/")) < 0) { + log_printf(ERROR, "daemonizing failed at chdir(): %s, exitting", strerror(errno)); + exit(-1); + } + + int fd; + for (fd=0;fd<=2;fd++) // close all file descriptors + close(fd); + fd = open("/dev/null",O_RDWR); // stdin + if(fd == -1) + log_printf(WARNING, "can't open stdin (chroot and no link to /dev/null?)"); + else { + if(dup(fd) == -1) // stdout + log_printf(WARNING, "can't open stdout"); + if(dup(fd) == -1) // stderr + log_printf(WARNING, "can't open stderr"); + } + umask(027); +} + +#endif diff --git a/src/datatypes.h b/src/datatypes.h new file mode 100644 index 0000000..365ed99 --- /dev/null +++ b/src/datatypes.h @@ -0,0 +1,41 @@ +/* + * sydra + * + * sydra is a toolbox which allows you to set up multiple bidirectional + * Video/Audio streams from external locations. + * sydra has been written to be used for the Elevate Festival in Graz + * Austria in order to involve external locations to present themselves + * at the festival. + * Sydra is based on GStreamer and is written in C. + * + * + * Copyright (C) 2014 Christian Pointner + * + * This file is part of sydra. + * + * sydra is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * sydra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sydra. If not, see . + */ + +#ifndef SYDRA_datatypes_h_INCLUDED +#define SYDRA_datatypes_h_INCLUDED + +#include + +struct buffer_struct { + uint32_t length_; + uint8_t* buf_; +}; +typedef struct buffer_struct buffer_t; + +#endif diff --git a/src/file_list.c b/src/file_list.c new file mode 100644 index 0000000..92296f0 --- /dev/null +++ b/src/file_list.c @@ -0,0 +1,222 @@ +/* + * sydra + * + * sydra is a toolbox which allows you to set up multiple bidirectional + * Video/Audio streams from external locations. + * sydra has been written to be used for the Elevate Festival in Graz + * Austria in order to involve external locations to present themselves + * at the festival. + * Sydra is based on GStreamer and is written in C. + * + * + * Copyright (C) 2014 Christian Pointner + * + * This file is part of sydra. + * + * sydra is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * sydra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sydra. If not, see . + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "datatypes.h" +#include "file_list.h" +#include "slist.h" +#include "log.h" + +static void delete_file(void* element) +{ + file_t* deletee = (file_t*)element; + log_printf(INFO, "removing/closing file '%s' -> %d", deletee->path_, deletee->fd_); + if(deletee->path_) free(deletee->path_); + if(deletee->fd_ >= 0) close(deletee->fd_); + if(deletee->pp_child_) free_child(deletee->pp_child_); +} + +int file_list_init(file_list_t* list) +{ + g_mutex_init(&(list->mutex_)); + return slist_init(&(list->list_), &delete_file); +} + +void file_list_clear(file_list_t* list) +{ + g_mutex_lock(&(list->mutex_)); + slist_clear(&(list->list_)); + g_mutex_unlock(&(list->mutex_)); +} + +file_t* file_list_add(file_list_t* list, struct tm* time, const char* type, const char* format, const char* dir, mode_t mode, int nocache) +{ + if(!list || !(&(list->mutex_))) + return NULL; + + file_t* tmp = malloc(sizeof(file_t)); + if(!tmp) + return NULL; + + log_printf(INFO, "%s time: %02d:%02d:%02d on %d.%d.%d%s", type, time->tm_hour, time->tm_min, time->tm_sec, time->tm_mday, time->tm_mon+1, time->tm_year+1900, time->tm_isdst > 0 ? " (DST)": ""); + + char name[256]; + strftime(name, sizeof(name), format, time); + int len = asprintf(&(tmp->path_), "%s/%s", dir, name); + if(len == -1) { + free(tmp); + return NULL; + } + + log_printf(INFO, "%s filename is: %s(.?)", type, tmp->path_); + tmp->fd_ = FILE_CLOSED; + tmp->mode_ = mode; + tmp->nocache_ = nocache; + tmp->pp_child_ = NULL; + + g_mutex_lock(&(list->mutex_)); + if(slist_add(&(list->list_), tmp) == NULL) { + g_mutex_unlock(&(list->mutex_)); + free(tmp->path_); + free(tmp); + return NULL; + } + g_mutex_unlock(&(list->mutex_)); + + return tmp; +} + +int file_list_remove(file_list_t* list, int fd) +{ + if(!list || !(&(list->mutex_))) + return -1; + + g_mutex_lock(&(list->mutex_)); + slist_element_t* tmp = list->list_.first_; + while(tmp) { + if(((file_t*)tmp->data_)->fd_ == fd) { + slist_remove(&(list->list_), tmp->data_); + break; + } + tmp = tmp->next_; + } + g_mutex_unlock(&(list->mutex_)); + + return 0; +} + +int file_list_call_post_process(file_list_t* list, int fd, char* script) +{ + if(!list || !(&(list->mutex_))) + return -1; + + g_mutex_lock(&(list->mutex_)); + slist_element_t* tmp = list->list_.first_; + while(tmp) { + if(((file_t*)tmp->data_)->fd_ == fd) { + log_printf(INFO, "calling post processing for '%s'", ((file_t*)tmp->data_)->path_); + close(((file_t*)tmp->data_)->fd_); + ((file_t*)tmp->data_)->fd_ = FILE_POST_PROCESS; + + char* const argv[] = { script, ((file_t*)tmp->data_)->path_, NULL }; + char* const evp[] = { NULL }; + ((file_t*)tmp->data_)->pp_child_ = sydra_exec(script, argv, evp); + if(!((file_t*)tmp->data_)->pp_child_) + slist_remove(&(list->list_), tmp->data_); + + break; + } + tmp = tmp->next_; + } + g_mutex_unlock(&(list->mutex_)); + + return 0; +} + +int file_list_waitpid(file_list_t* list) +{ + if(!list || !(&(list->mutex_))) + return -1; + + g_mutex_lock(&(list->mutex_)); + slist_element_t* tmp = list->list_.first_; + while(tmp) { + if(((file_t*)tmp->data_)->fd_ == FILE_POST_PROCESS) { + int ret = sydra_waitpid(((file_t*)tmp->data_)->pp_child_, NULL); + file_t* deletee = tmp->data_; + tmp = tmp->next_; + if(ret) + slist_remove(&(list->list_), deletee); + } + else + tmp = tmp->next_; + } + g_mutex_unlock(&(list->mutex_)); + + return 0; +} + +int open_file(file_t* file) +{ + if(!file || file->fd_ != FILE_CLOSED) // file already open! + return -1; + + char* orig_path = file->path_; + int cnt = 0; + do { + int flags = O_WRONLY | O_CREAT | O_EXCL; + if(file->nocache_) + flags |= O_DIRECT; + file->fd_ = open(file->path_, flags, file->mode_); + if(file->fd_ < 0) { + if(errno != EEXIST) { + // TODO: thread safe strerror + log_printf(ERROR, "can't open file '%s': %s", file->path_, strerror(errno)); + if(orig_path != file->path_) + free(orig_path); + file->fd_ = FILE_CLOSED; + return -1; + } + cnt++; + char* tmp; + int len = asprintf(&tmp, "%s.%d", orig_path, cnt); + if(len == -1) { + if(orig_path != file->path_) + free(orig_path); + return -2; + } + + if(file->path_ != orig_path) + free(file->path_); + file->path_ = tmp; + } + fchmod(file->fd_, file->mode_); + } + while(file->fd_ < 0); + + if(orig_path != file->path_) + free(orig_path); + + log_printf(INFO, "opened file '%s' -> %d", file->path_, file->fd_); + + return 0; +} diff --git a/src/file_list.h b/src/file_list.h new file mode 100644 index 0000000..6d8344c --- /dev/null +++ b/src/file_list.h @@ -0,0 +1,67 @@ +/* + * sydra + * + * sydra is a toolbox which allows you to set up multiple bidirectional + * Video/Audio streams from external locations. + * sydra has been written to be used for the Elevate Festival in Graz + * Austria in order to involve external locations to present themselves + * at the festival. + * Sydra is based on GStreamer and is written in C. + * + * + * Copyright (C) 2014 Christian Pointner + * + * This file is part of sydra. + * + * sydra is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * sydra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sydra. If not, see . + */ + +#ifndef SYDRA_file_list_h_INCLUDED +#define SYDRA_file_list_h_INCLUDED + +#include +#include + +#include + +#include "slist.h" +#include "sysexec.h" + +#define FILE_CLOSED -1 +#define FILE_POST_PROCESS -2 + +struct file_struct { + int fd_; + char* path_; + mode_t mode_; + int nocache_; + child_t* pp_child_; +}; +typedef struct file_struct file_t; + +struct file_list_struct { + slist_t list_; + GMutex mutex_; +}; +typedef struct file_list_struct file_list_t; + +int file_list_init(file_list_t* list); +void file_list_clear(file_list_t* list); +file_t* file_list_add(file_list_t* list, struct tm* time, const char* type, const char* format, const char* dir, mode_t mode, int nocache); +int file_list_remove(file_list_t* list, int fd); +int file_list_call_post_process(file_list_t* list, int fd, char* script); +int file_list_waitpid(file_list_t* list); +int open_file(file_t* file); + +#endif diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..7b66553 --- /dev/null +++ b/src/log.c @@ -0,0 +1,265 @@ +/* + * sydra + * + * sydra is a toolbox which allows you to set up multiple bidirectional + * Video/Audio streams from external locations. + * sydra has been written to be used for the Elevate Festival in Graz + * Austria in order to involve external locations to present themselves + * at the festival. + * Sydra is based on GStreamer and is written in C. + * + * + * Copyright (C) 2014 Christian Pointner + * + * This file is part of sydra. + * + * sydra is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * sydra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sydra. If not, see . + */ + +#include "datatypes.h" + +#include +#include +#include +#include +#include + +#include + +#define SYSLOG_NAMES +#include + +#include "log.h" + +log_t stdlog; + +#include "log_targets.h" + +const char* log_prio_to_string(log_prio_t prio) +{ + switch(prio) { + case ERROR: return "ERROR"; + case WARNING: return "WARNING"; + case NOTICE: return "NOTICE"; + case INFO: return "INFO"; + case DEBUG: return "DEBUG"; + } + return "UNKNOWN"; +} + +log_target_type_t log_target_parse_type(const char* conf) +{ + if(!conf) + return TARGET_UNKNOWN; + + if(!strncmp(conf, "syslog", 6)) return TARGET_SYSLOG; + if(!strncmp(conf, "file", 4)) return TARGET_FILE; + if(!strncmp(conf, "stdout", 6)) return TARGET_STDOUT; + if(!strncmp(conf, "stderr", 6)) return TARGET_STDERR; + + return TARGET_UNKNOWN; +} + +int log_targets_target_exists(log_targets_t* targets, log_target_type_t type) +{ + if(!targets && !targets->first_) + return 0; + + log_target_t* tmp = targets->first_; + while(tmp) { + if(tmp->type_ == type) + return 1; + tmp = tmp->next_; + } + return 0; +} + +int log_targets_add(log_targets_t* targets, const char* conf) +{ + if(!targets) + return -1; + + log_target_t* new_target = NULL; + int duplicates_allowed = 0; + switch(log_target_parse_type(conf)) { + case TARGET_SYSLOG: new_target = log_target_syslog_new(); break; + case TARGET_FILE: new_target = log_target_file_new(); duplicates_allowed = 1; break; + case TARGET_STDOUT: new_target = log_target_stdout_new(); break; + case TARGET_STDERR: new_target = log_target_stderr_new(); break; + default: return -3; + } + if(!new_target) + return -2; + + if(!duplicates_allowed && log_targets_target_exists(targets, new_target->type_)) { + free(new_target); + return -4; + } + + const char* prioptr = strchr(conf, ':'); + if(!prioptr || prioptr[1] == 0) { + free(new_target); + return -1; + } + prioptr++; + if(!isdigit(prioptr[0]) || (prioptr[1] != 0 && prioptr[1] != ',')) { + free(new_target); + return -1; + } + new_target->max_prio_ = prioptr[0] - '0'; + if(new_target->max_prio_ > 0) + new_target->enabled_ = 1; + + if(new_target->init != NULL) { + const char* confptr = NULL; + if(prioptr[1] != 0) + confptr = prioptr+2; + + int ret = (*new_target->init)(new_target, confptr); + if(ret) { + free(new_target); + return ret; + } + } + + if(new_target->open != NULL) + (*new_target->open)(new_target); + + + if(!targets->first_) { + targets->first_ = new_target; + } + else { + log_target_t* tmp = targets->first_; + while(tmp->next_) + tmp = tmp->next_; + + tmp->next_ = new_target; + } + return 0; +} + +void log_targets_log(log_targets_t* targets, log_prio_t prio, const char* msg) +{ + if(!targets) + return; + + log_target_t* tmp = targets->first_; + while(tmp) { + if(tmp->log != NULL && tmp->enabled_ && tmp->max_prio_ >= prio) + (*tmp->log)(tmp, prio, msg); + + tmp = tmp->next_; + } +} + +void log_targets_clear(log_targets_t* targets) +{ + if(!targets) + return; + + while(targets->first_) { + log_target_t* tmp = targets->first_; + targets->first_ = tmp->next_; + if(tmp->close != NULL) + (*tmp->close)(tmp); + if(tmp->clear != NULL) + (*tmp->clear)(tmp); + free(tmp); + } +} + + +void log_init() +{ + stdlog.max_prio_ = 0; + stdlog.targets_.first_ = NULL; + g_mutex_init(&(stdlog.log_mutex_)); +} + +void log_close() +{ + g_mutex_lock(&(stdlog.log_mutex_)); + log_targets_clear(&stdlog.targets_); + g_mutex_unlock(&(stdlog.log_mutex_)); +} + +void update_max_prio() +{ + log_target_t* tmp = stdlog.targets_.first_; + while(tmp) { + if(tmp->enabled_ && tmp->max_prio_ > stdlog.max_prio_) + stdlog.max_prio_ = tmp->max_prio_; + + tmp = tmp->next_; + } +} + +int log_add_target(const char* conf) +{ + if(!conf) + return -1; + + g_mutex_lock(&(stdlog.log_mutex_)); + int ret = log_targets_add(&stdlog.targets_, conf); + if(!ret) update_max_prio(); + g_mutex_unlock(&(stdlog.log_mutex_)); + return ret; +} + +void log_printf(log_prio_t prio, const char* fmt, ...) +{ + if(stdlog.max_prio_ < prio) + return; + + static char msg[MSG_LENGTH_MAX]; + va_list args; + + va_start(args, fmt); + vsnprintf(msg, MSG_LENGTH_MAX, fmt, args); + va_end(args); + + g_mutex_lock(&(stdlog.log_mutex_)); + log_targets_log(&stdlog.targets_, prio, msg); + g_mutex_unlock(&(stdlog.log_mutex_)); +} + +void log_print_hex_dump(log_prio_t prio, const u_int8_t* buf, u_int32_t len) +{ + if(stdlog.max_prio_ < prio) + return; + + static char msg[MSG_LENGTH_MAX]; + + if(!buf) { + snprintf(msg, MSG_LENGTH_MAX, "(NULL)"); + } + else { + u_int32_t i; + int offset = snprintf(msg, MSG_LENGTH_MAX, "dump(%d): ", len); + if(offset < 0) + return; + char* ptr = &msg[offset]; + + for(i=0; i < len; i++) { + if(((i+1)*3) >= (MSG_LENGTH_MAX - offset)) + break; + snprintf(ptr, 3, "%02X ", buf[i]); + ptr+=3; + } + } + g_mutex_lock(&(stdlog.log_mutex_)); + log_targets_log(&stdlog.targets_, prio, msg); + g_mutex_unlock(&(stdlog.log_mutex_)); +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..3a9c3e2 --- /dev/null +++ b/src/log.h @@ -0,0 +1,87 @@ +/* + * sydra + * + * sydra is a toolbox which allows you to set up multiple bidirectional + * Video/Audio streams from external locations. + * sydra has been written to be used for the Elevate Festival in Graz + * Austria in order to involve external locations to present themselves + * at the festival. + * Sydra is based on GStreamer and is written in C. + * + * + * Copyright (C) 2014 Christian Pointner + * + * This file is part of sydra. + * + * sydra is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * sydra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sydra. If not, see . + */ + +#ifndef SYDRA_log_h_INCLUDED +#define SYDRA_log_h_INCLUDED + +#include + +#define MSG_LENGTH_MAX 1024 + +enum log_prio_enum { ERROR = 1, WARNING = 2, NOTICE = 3, + INFO = 4, DEBUG = 5 }; +typedef enum log_prio_enum log_prio_t; + +const char* log_prio_to_string(log_prio_t prio); + +enum log_target_type_enum { TARGET_SYSLOG , TARGET_STDOUT, TARGET_STDERR, TARGET_FILE , TARGET_UNKNOWN }; +typedef enum log_target_type_enum log_target_type_t; + +struct log_target_struct { + log_target_type_t type_; + int (*init)(struct log_target_struct* self, const char* conf); + void (*open)(struct log_target_struct* self); + void (*log)(struct log_target_struct* self, log_prio_t prio, const char* msg); + void (*close)(struct log_target_struct* self); + void (*clear)(struct log_target_struct* self); + int opened_; + int enabled_; + log_prio_t max_prio_; + void* param_; + struct log_target_struct* next_; +}; +typedef struct log_target_struct log_target_t; + + +struct log_targets_struct { + log_target_t* first_; +}; +typedef struct log_targets_struct log_targets_t; + +int log_targets_target_exists(log_targets_t* targets, log_target_type_t type); +int log_targets_add(log_targets_t* targets, const char* conf); +void log_targets_log(log_targets_t* targets, log_prio_t prio, const char* msg); +void log_targets_clear(log_targets_t* targets); + + +struct log_struct { + log_prio_t max_prio_; + log_targets_t targets_; + GMutex log_mutex_; +}; +typedef struct log_struct log_t; + +void log_init(); +void log_close(); +void update_max_prio(); +int log_add_target(const char* conf); +void log_printf(log_prio_t prio, const char* fmt, ...); +void log_print_hex_dump(log_prio_t prio, const u_int8_t* buf, u_int32_t len); + +#endif diff --git a/src/log_targets.h b/src/log_targets.h new file mode 100644 index 0000000..de17f39 --- /dev/null +++ b/src/log_targets.h @@ -0,0 +1,357 @@ +/* + * sydra + * + * sydra is a toolbox which allows you to set up multiple bidirectional + * Video/Audio streams from external locations. + * sydra has been written to be used for the Elevate Festival in Graz + * Austria in order to involve external locations to present themselves + * at the festival. + * Sydra is based on GStreamer and is written in C. + * + * + * Copyright (C) 2014 Christian Pointner + * + * This file is part of sydra. + * + * sydra is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * sydra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sydra. If not, see . + */ + +#ifndef SYDRA_log_targets_h_INCLUDED +#define SYDRA_log_targets_h_INCLUDED + +#include + +static char* get_time_formatted() +{ + char* time_string; + time_t t = time(NULL); + if(t < 0) + time_string = "