From a17e23010a4d0b115ff39c4e39dcad013855feee Mon Sep 17 00:00:00 2001 From: Julien BLACHE Date: Sat, 18 Apr 2009 14:14:26 +0200 Subject: [PATCH] Introduce new confuse-based conffile support --- configure.in | 1 + src/Makefile.am | 5 +- src/conffile.c | 333 ++++++++++++++++++++++++++++++++++++++++++++++++ src/conffile.h | 13 ++ 4 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 src/conffile.c create mode 100644 src/conffile.h diff --git a/configure.in b/configure.in index b1c651cf..9b198241 100644 --- a/configure.in +++ b/configure.in @@ -50,6 +50,7 @@ AM_CONDITIONAL(COND_MUSEPACK, test x$use_musepack = xtrue) AC_CHECK_FUNCS(strcasestr strsep) dnl Checks for libraries. +PKG_CHECK_MODULES(CONFUSE, [ libconfuse ]) PKG_CHECK_MODULES(AVAHI, [ avahi-client >= 0.6.24 ]) PKG_CHECK_MODULES(SQLITE3, [ sqlite3 ], AC_DEFINE(HAVE_SQLITE3, 1, [define if sqlite3 is available])) diff --git a/src/Makefile.am b/src/Makefile.am index 1bce194b..0192ff8a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,12 +11,13 @@ if COND_MUSEPACK MUSEPACKSRC=scan-mpc.c endif -mt_daapd_CPPFLAGS = @AVAHI_CFLAGS@ @SQLITE3_CFLAGS@ @FFMPEG_CFLAGS@ @TAGLIB_CFLAGS@ -mt_daapd_LDADD = @AVAHI_LIBS@ @SQLITE3_LIBS@ @FFMPEG_LIBS@ @FLAC_LIBS@ @TAGLIB_LIBS@ @LIBEVENT_LIBS@ @LIBDL@ +mt_daapd_CPPFLAGS = @AVAHI_CFLAGS@ @SQLITE3_CFLAGS@ @FFMPEG_CFLAGS@ @CONFUSE_CFLAGS@ @TAGLIB_CFLAGS@ +mt_daapd_LDADD = @AVAHI_LIBS@ @SQLITE3_LIBS@ @FFMPEG_LIBS@ @CONFUSE_LIBS@ @FLAC_LIBS@ @TAGLIB_LIBS@ @LIBEVENT_LIBS@ @LIBDL@ mt_daapd_LDFLAGS = -export-dynamic mt_daapd_SOURCES = main.c daapd.h webserver.c \ webserver.h configfile.c configfile.h err.c err.h \ mp3-scanner.h mp3-scanner.c \ + conffile.c conffile.h \ filescanner_ffmpeg.c \ mdns_avahi.c mdns_avahi.h \ db-generic.c db-generic.h ff-plugins.c ff-plugins.h \ diff --git a/src/conffile.c b/src/conffile.c new file mode 100644 index 00000000..89d92c30 --- /dev/null +++ b/src/conffile.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2009 Julien BLACHE + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include + +#include + +#include + +#include "daapd.h" +#include "err.h" +#include "conffile.h" + + +/* Forward */ +static int cb_loglevel(cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result); + +/* general section structure */ +static cfg_opt_t sec_general[] = + { + CFG_STR("uid", "nobody", CFGF_NONE), + CFG_STR("admin_password", NULL, CFGF_NONE), + CFG_STR("logfile", "/var/log/mt-daapd.log", CFGF_NONE), + CFG_INT_CB("loglevel", E_LOG, CFGF_NONE, &cb_loglevel), + CFG_END() + }; + +/* library section structure */ +static cfg_opt_t sec_library[] = + { + CFG_STR("name", "%l on %h", CFGF_NONE), + CFG_INT("port", 3689, CFGF_NONE), + CFG_STR("password", NULL, CFGF_NONE), + CFG_STR_LIST("directories", NULL, CFGF_NONE), + CFG_STR_LIST("compilations", NULL, CFGF_NONE), + CFG_STR_LIST("no_transcode", NULL, CFGF_NONE), + CFG_STR_LIST("force_transcode", NULL, CFGF_NONE), + CFG_END() + }; + +/* Config file structure */ +static cfg_opt_t toplvl_cfg[] = + { + CFG_SEC("general", sec_general, CFGF_NONE), + CFG_SEC("library", sec_library, CFGF_MULTI | CFGF_TITLE), + CFG_END() + }; + +static cfg_t *cfg; + + +static int +cb_loglevel(cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result) +{ + if (strcasecmp(value, "fatal") == 0) + *(long int *)result = E_FATAL; + else if (strcasecmp(value, "log") == 0) + *(long int *)result = E_LOG; + else if (strcasecmp(value, "warning") == 0) + *(long int *)result = E_WARN; + else if (strcasecmp(value, "info") == 0) + *(long int *)result = E_INF; + else if (strcasecmp(value, "debug") == 0) + *(long int *)result = E_DBG; + else if (strcasecmp(value, "spam") == 0) + *(long int *)result = E_SPAM; + else + { + DPRINTF(E_WARN, L_CONF, "Unrecognised loglevel '%s'\n", value); + /* Default to warning */ + *(long int *)result = 1; + } + + return 0; +} + +static int +conffile_expand_libname(cfg_t *lib) +{ + char *libname; + const char *title; + char *hostname; + char *s; + char *d; + char *expanded; + struct utsname sysinfo; + size_t len; + size_t olen; + size_t titlelen; + size_t hostlen; + size_t verlen; + int ret; + + libname = cfg_getstr(lib, "name"); + + /* Fast path */ + s = strchr(libname, '%'); + if (!s) + return 0; + + /* Grab what we need */ + title = cfg_title(lib); + + ret = uname(&sysinfo); + if (ret != 0) + { + DPRINTF(E_WARN, L_CONF, "Could not get system name: %s\n", strerror(errno)); + hostname = "Unknown host"; + } + else + hostname = sysinfo.nodename; + + titlelen = strlen(title); + hostlen = strlen(hostname); + verlen = strlen(VERSION); + + olen = strlen(libname); + len = olen; + + /* Compute expanded size */ + s = libname; + while (*s) + { + if (*s == '%') + { + s++; + + switch (*s) + { + case 'h': + len += hostlen; + break; + + case 'l': + len += titlelen; + break; + + case 'v': + len += verlen; + break; + } + } + s++; + } + + expanded = (char *)malloc(len + 1); + if (!expanded) + { + DPRINTF(E_FATAL, L_CONF, "Out of memory\n"); + + return -1; + } + memset(expanded, 0, len + 1); + + /* Do the actual expansion */ + s = libname; + d = expanded; + while (*s) + { + if (*s == '%') + { + s++; + + switch (*s) + { + case 'h': + strcat(d, hostname); + d += hostlen; + break; + + case 'l': + strcat(d, title); + d += titlelen; + break; + + case 'v': + strcat(d, VERSION); + d += verlen; + break; + } + + s++; + } + else + { + *d = *s; + + s++; + d++; + } + } + + cfg_setstr(lib, "name", expanded); + + free(expanded); + + return 0; +} + + +int +conffile_load(char *file) +{ + cfg_t *lib; + int nlib; + int *libports; + int libport; + int error; + int i; + int j; + int ret; + + cfg = cfg_init(toplvl_cfg, CFGF_NONE); + + ret = cfg_parse(cfg, file); + + if (ret == CFG_FILE_ERROR) + { + DPRINTF(E_FATAL, L_CONF, "Could not open config file %s\n", file); + + cfg_free(cfg); + return -1; + } + else if (ret == CFG_PARSE_ERROR) + { + DPRINTF(E_FATAL, L_CONF, "Parse error in config file %s\n", file); + + cfg_free(cfg); + return -1; + } + + nlib = cfg_size(cfg, "library"); + + DPRINTF(E_INF, L_CONF, "%d music libraries configured\n", nlib); + + libports = (int *)malloc(nlib * sizeof(int)); + memset(libports, 0, (nlib * sizeof(int))); + + error = 0; + for (i = 0; i < nlib; i++) + { + lib = cfg_getnsec(cfg, "library", i); + libport = cfg_getint(lib, "port"); + + error = ((libport > 65535) || (libport < 1024)); + if (error) + { + DPRINTF(E_FATAL, L_CONF, "Invalid port number for library '%s', must be between 1024 and 65535\n", + cfg_title(lib)); + + break; + } + + /* Check libraries ports */ + for (j = 0; j < i; j++) + { + error = (libports[j] == libport); + if (error) + break; + } + + if (error) + { + DPRINTF(E_FATAL, L_CONF, "Port collision for library '%s' and library '%s'\n", + cfg_title(cfg_getnsec(cfg, "library", j)), cfg_title(lib)); + DPRINTF(E_FATAL, L_CONF, "Port numbers must be unique accross all libraries\n"); + + break; + } + + libports[i] = libport; + + error = (cfg_size(lib, "directories") == 0); + if (error) + { + DPRINTF(E_FATAL, L_CONF, "No directories specified for library '%s'\n", + cfg_title(lib)); + + break; + } + + /* Do keyword expansion on library names */ + ret = conffile_expand_libname(lib); + if (ret != 0) + { + DPRINTF(E_FATAL, L_CONF, "Could not expand library name\n"); + + free(libports); + cfg_free(cfg); + return -1; + } + } + + free(libports); + + if (error) + { + cfg_free(cfg); + + return -1; + } + + return 0; +} + +void +conffile_unload(void) +{ + cfg_free(cfg); +} diff --git a/src/conffile.h b/src/conffile.h new file mode 100644 index 00000000..596cfcf5 --- /dev/null +++ b/src/conffile.h @@ -0,0 +1,13 @@ + +#ifndef __CONFFILE_H__ +#define __CONFFILE_H__ + + + +int +conffile_load(char *file); + +void +conffile_unload(void); + +#endif /* !__CONFFILE_H__ */