summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2018-02-14 16:05:56 +0200
committerSergey Poznyakoff <gray@gnu.org>2018-02-14 16:12:13 +0200
commit5c691054bb7e8623b203fcbae31e468781597607 (patch)
tree3a42d647ea1795fec67192fa075a995d662ec8b5
parent0531ff6127b03a917f99b8eb2cff1fa74a230cc7 (diff)
downloadfileserv-5c691054bb7e8623b203fcbae31e468781597607.tar.gz
fileserv-5c691054bb7e8623b203fcbae31e468781597607.tar.bz2
Rewrite configuration support. Introduce global (system-wide) configuration file.
* src/Makefile.am (AM_CPPFLAGS): Define SYSCONFDIR. * src/dirconfig.c: Rename to src/config.c. Implement global configuration file. * src/fileserv.c: Remove unnecessary options. * src/fileserv.h: Rewrite configuration-related prototypes and declarations.
-rw-r--r--src/Makefile.am4
-rw-r--r--src/config.c596
-rw-r--r--src/dirconfig.c340
-rw-r--r--src/fileserv.c58
-rw-r--r--src/fileserv.h44
-rw-r--r--src/idx.c6
-rw-r--r--src/logger.c9
7 files changed, 653 insertions, 404 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index f7b02da..657e47b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,6 @@
bin_PROGRAMS=fileserv
fileserv_SOURCES=fileserv.c runas.c fileserv.h logger.c pidfile.c\
- wordsplit.c wordsplit.h catfile.c dirconfig.c idx.c defidx.h\
+ wordsplit.c wordsplit.h catfile.c config.c idx.c defidx.h\
mem.c remoteip.c
BUILT_SOURCES=defidx.h
if FSRV_WRAP
@@ -8,7 +8,7 @@ if FSRV_WRAP
endif
dist_man_MANS=fileserv.8
LDADD = ../mimetypes/libmimetypes.a
-AM_CPPFLAGS = -I $(top_srcdir)/mimetypes
+AM_CPPFLAGS = -I $(top_srcdir)/mimetypes -DSYSCONFDIR=\"$(sysconfdir)\"
EXTRA_DIST=ftoc.sed defidx.html
.html.h:
$(AM_V_GEN)sed -f $(srcdir)/ftoc.sed $< > $@
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..874bd29
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,596 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "fileserv.h"
+#include "wordsplit.h"
+
+#ifndef SYSCONFDIR
+# define SYSCONFDIR "/etc"
+#endif
+
+#define DEFAULT_CONF_FILE "fileserv.conf"
+char *config_file;
+char *dotfile = ".fileserv";
+
+static CONFIG *global_config;
+
+void
+readconfig(void)
+{
+ if (!config_file) {
+ config_file = catfile(SYSCONFDIR, DEFAULT_CONF_FILE);
+ if (access(config_file, F_OK)) {
+ if (verbose)
+ error("warning: no configuration file %s",
+ config_file);
+ free(config_file);
+ config_file = NULL;
+ return;
+ }
+ }
+ global_config = config_init();
+ if (config_parse(config_file, global_config, CTXGLOB))
+ exit(1);
+}
+
+CONFIG *
+dirconfig(char const *path, size_t prefix_len)
+{
+ CONFIG *conf = config_clone(global_config);
+ char *tmp = NULL;
+ struct stat st;
+
+ if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
+ tmp = catfile(path, ".");
+ path = tmp;
+ }
+ while (path[prefix_len]) {
+ char *name = catfile_n(path, prefix_len, dotfile);
+ config_parse(name, conf, CTXDIR);
+ free(name);
+ if (path[prefix_len] == '/')
+ prefix_len++;
+ while (path[prefix_len] && path[prefix_len] != '/')
+ prefix_len++;
+ }
+ free(tmp);
+ return conf;
+}
+
+static void
+index_files_free(CONFIG *conf)
+{
+ size_t i;
+
+ for (i = conf->index_files_n; i < conf->index_files_c; i++)
+ free(conf->index_files_v[i]);
+ conf->index_files_c = 0;
+ conf->index_files_n = 0;
+ free(conf->index_files_v);
+ conf->index_files_v = NULL;
+}
+
+static void
+hidden_files_free(CONFIG *conf)
+{
+ size_t i;
+
+ for (i = conf->hidden_files_rxn; i < conf->hidden_files_rxc; i++)
+ regfree(&conf->hidden_files_rxv[i]);
+ conf->hidden_files_rxc = 0;
+ conf->hidden_files_rxn = 0;
+ free(conf->hidden_files_rxv);
+ conf->hidden_files_rxv = NULL;
+}
+
+CONFIG *
+config_init(void)
+{
+ CONFIG *p = xcalloc(1, sizeof(*p));
+ p->list_unreadable = 1;
+ return p;
+}
+
+CONFIG *
+config_clone(CONFIG const *source)
+{
+ size_t i;
+ CONFIG *p = config_init();
+
+ if (!source)
+ return p;
+ p->follow = source->follow;
+ p->listing = source->listing;
+ p->list_unreadable = source->list_unreadable;
+
+ if (source->index_files_c) {
+ p->index_files_v = xcalloc(source->index_files_c,
+ sizeof(p->index_files_v[0]));
+ for (i = 0; i < source->index_files_c; i++)
+ p->index_files_v[i] = source->index_files_v[i];
+ p->index_files_v[i] = NULL;
+ }
+ p->index_files_c = source->index_files_c;
+ p->index_files_n = source->index_files_c;
+
+ if (source->hidden_files_rxc) {
+ p->hidden_files_rxv = xcalloc(source->hidden_files_rxc,
+ sizeof(p->hidden_files_rxv[0]));
+ for (i = 0; i < source->hidden_files_rxc; i++)
+ p->hidden_files_rxv[i] = source->hidden_files_rxv[i];
+ }
+ p->hidden_files_rxc = source->hidden_files_rxc;
+ p->hidden_files_rxn = source->hidden_files_rxc;
+
+ return p;
+}
+
+void
+config_free(CONFIG *conf)
+{
+ if (conf) {
+ index_files_free(conf);
+ hidden_files_free(conf);
+ free(conf);
+ }
+}
+
+static int
+bool_decode(char const *s)
+{
+ static char *yes[] = {
+ "1", "yes", "true", "on", NULL
+ };
+ static char *no[] = {
+ "0", "no", "false", "off", NULL
+ };
+ int i;
+ for (i = 0; yes[i]; i++) {
+ if (strcasecmp(s, yes[i]) == 0)
+ return 1;
+ }
+ for (i = 0; no[i]; i++) {
+ if (strcasecmp(s, no[i]) == 0)
+ return 0;
+ }
+ return -1;
+}
+
+static int
+bad_argc(const char *file, int line, char const *id)
+{
+ error("%s:%d: bad number of arguments to '%s'", file, line, id);
+ return 1;
+}
+
+static int
+not_a_bool(const char *file, int line)
+{
+ error("%s:%d: invalid boolean value", file, line);
+ return 1;
+}
+
+static int
+set_follow(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ int n;
+ if (argc != 2)
+ return bad_argc(file, line, argv[0]);
+ n = bool_decode(argv[1]);
+ if (n == -1)
+ return not_a_bool(file, line);
+ conf->follow = n;
+ return 0;
+}
+
+static int
+set_listing(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ int n;
+ if (argc != 2)
+ return bad_argc(file, line, argv[0]);
+ n = bool_decode(argv[1]);
+ if (n == -1)
+ return not_a_bool(file, line);
+ conf->listing = n;
+ return 0;
+}
+
+static int
+set_list_unreadable(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ int n;
+ if (argc != 2)
+ return bad_argc(file, line, argv[0]);
+ n = bool_decode(argv[1]);
+ if (n == -1)
+ return not_a_bool(file, line);
+ conf->list_unreadable = n;
+ return 0;
+}
+
+static int
+set_index_files(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ size_t i;
+
+ if (argc < 2)
+ return bad_argc(file, line, argv[0]);
+
+ i = 1;
+ if (strcmp(argv[i], "+") == 0)
+ i++;
+ else
+ index_files_free(conf);
+
+ conf->index_files_v = xnrealloc(conf->index_files_v,
+ argc + conf->index_files_c,
+ sizeof(conf->index_files_v[0]));
+ for (; argv[i]; i++)
+ conf->index_files_v[conf->index_files_c++] = xstrdup(argv[i]);
+ conf->index_files_v[conf->index_files_c] = NULL;
+ return 0;
+}
+
+static int
+set_hidden_files(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ size_t i;
+
+ if (argc < 2)
+ return bad_argc(file, line, argv[0]);
+
+ i = 1;
+ if (strcmp(argv[i], "+") == 0)
+ i++;
+ else
+ hidden_files_free(conf);
+
+ conf->hidden_files_rxv = xnrealloc(conf->hidden_files_rxv,
+ argc + conf->hidden_files_rxc,
+ sizeof(conf->hidden_files_rxv[0]));
+ for (; argv[i]; i++) {
+ int ec;
+ ec = regcomp(&conf->hidden_files_rxv[conf->hidden_files_rxc],
+ argv[i], REG_EXTENDED|REG_NOSUB);
+ if (ec) {
+ char buf[512];
+ regerror(ec,
+ &conf->hidden_files_rxv[conf->hidden_files_rxc],
+ buf, sizeof buf);
+ error("%s: %s", argv[i], buf);
+ } else
+ conf->hidden_files_rxc++;
+ }
+ return 0;
+}
+
+static int
+set_user(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ if (argc != 2) {
+ error("%s:%d: expected exactly one argument", file, line);
+ return 1;
+ }
+ user = xstrdup(argv[1]);
+ return 0;
+}
+
+static int
+set_group(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ if (argc != 2) {
+ error("%s:%d: expected exactly one argument", file, line);
+ return 1;
+ }
+ group = xstrdup(argv[1]);
+ return 0;
+}
+
+static int
+set_listen(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ if (argc != 2) {
+ error("%s:%d: expected exactly one argument", file, line);
+ return 1;
+ }
+ address = xstrdup(argv[1]);
+ return 0;
+}
+
+static int
+set_index_template(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ if (argc != 2) {
+ error("%s:%d: expected exactly one argument", file, line);
+ return 1;
+ }
+ parse_template_file(argv[1]);
+ return 0;
+}
+
+static int
+set_dotfile(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ if (argc != 2) {
+ error("%s:%d: expected exactly one argument", file, line);
+ return 1;
+ }
+ dotfile = xstrdup(argv[1]);
+ return 0;
+}
+
+static int
+set_forwarded_header(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ if (argc != 2) {
+ error("%s:%d: expected exactly one argument", file, line);
+ return 1;
+ }
+ forwarded_header = xstrdup(argv[1]);
+ return 0;
+}
+
+static int
+set_temp_dir(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ if (argc != 2) {
+ error("%s:%d: expected exactly one argument", file, line);
+ return 1;
+ }
+ tmpdir = xstrdup(argv[1]);
+ return 0;
+}
+
+static int
+set_mime_magic_file(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ if (argc != 2) {
+ error("%s:%d: expected exactly one argument", file, line);
+ return 1;
+ }
+ mime_types_file = xstrdup(argv[1]);
+ return 0;
+}
+
+static int
+set_trusted_proxy(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ size_t i;
+
+ if (argc == 1) {
+ error("%s:%d: expected one or more arguments", file, line);
+ return 1;
+ }
+ for (i = 1; i < argc; i++)
+ trusted_ip_add(argv[i]);
+ return 0;
+}
+
+static int
+set_pidfile(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ if (argc != 2) {
+ error("%s:%d: expected exactly one argument", file, line);
+ return 1;
+ }
+ pidfile = xstrdup(argv[1]);
+ return 0;
+}
+
+static int
+set_syslog(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ if (argc != 2) {
+ error("%s:%d: expected exactly one argument", file, line);
+ return 1;
+ }
+ if (set_log_facility(argv[1])) {
+ error("%s:%d: not a valid syslog facility", file, line);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+set_mapping(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line)
+{
+ size_t i;
+
+ if (argc == 1) {
+ error("%s:%d: expected one or more arguments", file, line);
+ return 1;
+ }
+ for (i = 1; i < argc; i++)
+ urimap_add(argv[i]);
+ return 0;
+}
+
+
+
+struct configkeyword {
+ int context;
+ char const *ident;
+ int (*setter)(size_t argc, char **argv, CONFIG *conf,
+ char const *file, int line);
+};
+
+static struct configkeyword keywords[] = {
+ { CTXGLOB, "user", set_user },
+ { CTXGLOB, "group", set_group },
+ { CTXGLOB, "listen", set_listen },
+ { CTXGLOB, "index-template", set_index_template },
+ { CTXGLOB, "access-file-name", set_dotfile },
+ { CTXGLOB, "forwarded-header", set_forwarded_header },
+ { CTXGLOB, "trusted-proxy", set_trusted_proxy },
+ { CTXGLOB, "temp-dir", set_temp_dir },
+ { CTXGLOB, "mime-magic-file", set_mime_magic_file },
+ { CTXGLOB, "pid-file", set_pidfile },
+ { CTXGLOB, "syslog", set_syslog },
+ { CTXGLOB, "mapping", set_mapping },
+ { CTXGLOB | CTXDIR, "directory-index", set_index_files },
+ { CTXGLOB | CTXDIR, "follow", set_follow },
+ { CTXGLOB | CTXDIR, "listing", set_listing },
+ { CTXGLOB | CTXDIR, "list-unreadable", set_list_unreadable },
+ { CTXGLOB | CTXDIR, "hidden-files", set_hidden_files },
+ { CTXNONE, NULL }
+};
+
+static int
+line_interpret(struct wordsplit const *ws,
+ CONFIG *conf, int ctx, char const *file, int line)
+{
+ struct configkeyword *kw;
+ for (kw = keywords; kw->ident; kw++) {
+ if ((kw->context & ctx)
+ && strcmp(kw->ident, ws->ws_wordv[0]) == 0)
+ return kw->setter(ws->ws_wordc, ws->ws_wordv,
+ conf, file, line);
+ }
+ error("%s:%d: unrecognized keyword", file, line);
+ return 1;
+}
+
+int
+config_parse(const char *file, CONFIG *conf, int ctx)
+{
+ FILE *fp;
+ char buf[1024];
+ unsigned ln = 0;
+ int wsflags = WRDSF_DEFFLAGS;
+ struct wordsplit ws;
+ int err = 0;
+
+ fp = fopen(file, "r");
+ if (!fp) {
+ if (ctx == CTXGLOB || errno != ENOENT)
+ error("can't open file %s: %s", file,
+ strerror(errno));
+ return 1;
+ }
+
+ ws.ws_comment = "#";
+ wsflags |= WRDSF_COMMENT;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ int len;
+
+ ln++;
+ len = strlen(buf);
+ if (len == 0)
+ continue;
+ if (buf[len-1] == '\n')
+ buf[--len] = 0;
+ else if (!feof(fp)) {
+ error("%s:%d: line too long", file, ln);
+ err = 1;
+ break;
+ }
+
+ if (wordsplit(buf, &ws, wsflags)) {
+ error("%s:%d: %s", file, ln, wordsplit_strerror(&ws));
+ err = 1;
+ break;
+ }
+
+ wsflags |= WRDSF_REUSE;
+ if (ws.ws_wordc == 0)
+ continue;
+ if (line_interpret(&ws, conf, ctx, file, ln)) {
+ err = 1;
+ break;
+ }
+ }
+
+ if (wsflags & WRDSF_REUSE)
+ wordsplit_free(&ws);
+
+ if (ferror(fp)) {
+ err = 1;
+ error("%s: %s", file, strerror(errno));
+ }
+ fclose(fp);
+
+ return err;
+}
+
+int
+filename_is_valid(char const *name)
+{
+ enum { a_chr, a_sla, a_dot, a_end, A_MAX };
+ enum { s_ini, s_cur, s_sla, s_dot, s_dt2, s_err, s_end, S_MAX = s_end }
+ state = s_ini;
+ static char trans[A_MAX][S_MAX] = {
+ /* ini cur sla dot dt2 err */
+ /* chr */ { s_cur, s_cur, s_cur, s_cur, s_cur, s_err },
+ /* sla */ { s_sla, s_sla, s_err, s_err, s_err, s_err },
+ /* dot */ { s_dot, s_cur, s_dot, s_dt2, s_cur, s_err },
+ /* end */ { s_end, s_end, s_end, s_err, s_err, s_err },
+ };
+
+ int c;
+ while (state != s_err && state != s_end) {
+ switch (*name++) {
+ case '/':
+ c = a_sla;
+ break;
+ case '.':
+ c = a_dot;
+ break;
+ case 0:
+ c = a_end;
+ break;
+ default:
+ c = a_chr;
+ }
+ state = trans[c][state];
+ }
+ return state == s_end;
+}
+
+int
+filename_is_special(char const *name)
+{
+ return (name[0] == '.'
+ && (name[1] == 0
+ || (name[1] == '.' && name[2] == 0)));
+}
+
+int
+filename_is_hidden(char const *name, CONFIG const *conf)
+{
+ if (filename_is_special(name) || strcmp(name, dotfile) == 0)
+ return 1;
+
+ if (conf && conf->hidden_files_rxv) {
+ size_t i;
+ for (i = 0; i < conf->hidden_files_rxc; i++)
+ if (regexec(&conf->hidden_files_rxv[i], name,
+ 0, NULL, 0) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+
diff --git a/src/dirconfig.c b/src/dirconfig.c
deleted file mode 100644
index d40475d..0000000
--- a/src/dirconfig.c
+++ /dev/null
@@ -1,340 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include "fileserv.h"
-#include "wordsplit.h"
-
-char *dotfile = ".fileserv";
-
-DIRCONFIG *
-dirconfig(char const *path, size_t prefix_len)
-{
- DIRCONFIG *conf = dirconfig_init();
- char *tmp = NULL;
- struct stat st;
-
- if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
- tmp = catfile(path, ".");
- path = tmp;
- }
- while (path[prefix_len]) {
- char *name = catfile_n(path, prefix_len, dotfile);
- dirconfig_parse(name, conf);
- free(name);
- if (path[prefix_len] == '/')
- prefix_len++;
- while (path[prefix_len] && path[prefix_len] != '/')
- prefix_len++;
- }
- free(tmp);
- return conf;
-}
-
-static int
-bool_decode(char const *s)
-{
- static char *yes[] = {
- "1", "yes", "true", "on", NULL
- };
- static char *no[] = {
- "0", "no", "false", "off", NULL
- };
- int i;
- for (i = 0; yes[i]; i++) {
- if (strcasecmp(s, yes[i]) == 0)
- return 1;
- }
- for (i = 0; no[i]; i++) {
- if (strcasecmp(s, no[i]) == 0)
- return 0;
- }
- return -1;
-}
-
-static int
-bad_argc(const char *file, int line, char const *id)
-{
- error("%s:%d: bad number of arguments to '%s'", file, line, id);
- return 1;
-}
-
-static int
-not_a_bool(const char *file, int line)
-{
- error("%s:%d: invalid boolean value", file, line);
- return 1;
-}
-
-static int
-set_follow(size_t argc, char **argv, DIRCONFIG *conf,
- char const *file, int line)
-{
- int n;
- if (argc != 2)
- return bad_argc(file, line, argv[0]);
- n = bool_decode(argv[1]);
- if (n == -1)
- return not_a_bool(file, line);
- conf->follow = n;
- return 0;
-}
-
-static int
-set_listing(size_t argc, char **argv, DIRCONFIG *conf,
- char const *file, int line)
-{
- int n;
- if (argc != 2)
- return bad_argc(file, line, argv[0]);
- n = bool_decode(argv[1]);
- if (n == -1)
- return not_a_bool(file, line);
- conf->listing = n;
- return 0;
-}
-
-static int
-set_list_unreadable(size_t argc, char **argv, DIRCONFIG *conf,
- char const *file, int line)
-{
- int n;
- if (argc != 2)
- return bad_argc(file, line, argv[0]);
- n = bool_decode(argv[1]);
- if (n == -1)
- return not_a_bool(file, line);
- conf->list_unreadable = n;
- return 0;
-}
-
-static int
-set_index_files(size_t argc, char **argv, DIRCONFIG *conf,
- char const *file, int line)
-{
- size_t i;
-
- if (argc < 2)
- return bad_argc(file, line, argv[0]);
- conf->index_files = xcalloc(argc, sizeof(conf->index_files[0]));
- for (i = 1; i < argc; i++)
- conf->index_files[i-1] = xstrdup(argv[i]);
- conf->index_files[i-1] = NULL;
- return 0;
-}
-
-static void
-hidden_files_free(DIRCONFIG *conf)
-{
- size_t i;
-
- for (i = 0; i < conf->hidden_files_rxc; i++)
- regfree(&conf->hidden_files_rxv[i]);
- conf->hidden_files_rxc = 0;
- free(conf->hidden_files_rxv);
- conf->hidden_files_rxv = NULL;
-}
-
-static int
-set_hidden_files(size_t argc, char **argv, DIRCONFIG *conf,
- char const *file, int line)
-{
- size_t i;
-
- if (argc < 2)
- return bad_argc(file, line, argv[0]);
-
- i = 1;
- if (strcmp(argv[i], "+") == 0)
- i++;
- else
- hidden_files_free(conf);
-
- conf->hidden_files_rxv = xnrealloc(conf->hidden_files_rxv,
- argc + conf->hidden_files_rxc,
- sizeof(conf->hidden_files_rxv[0]));
- for (; argv[i]; i++) {
- int ec;
- ec = regcomp(&conf->hidden_files_rxv[conf->hidden_files_rxc],
- argv[i], REG_EXTENDED|REG_NOSUB);
- if (ec) {
- char buf[512];
- regerror(ec,
- &conf->hidden_files_rxv[conf->hidden_files_rxc],
- buf, sizeof buf);
- error("%s: %s", argv[i], buf);
- } else
- conf->hidden_files_rxc++;
- }
- return 0;
-}
-
-struct dirconfig_keyword {
- char const *ident;
- int (*setter)(size_t argc, char **argv, DIRCONFIG *conf,
- char const *file, int line);
-};
-
-static struct dirconfig_keyword keywords[] = {
- { "directory-index", set_index_files },
- { "follow", set_follow },
- { "listing", set_listing },
- { "list-unreadable", set_list_unreadable },
- { "hidden-files", set_hidden_files },
- { NULL }
-};
-
-static int
-line_interpret(struct wordsplit const *ws,
- DIRCONFIG *conf, char const *file, int line)
-{
- struct dirconfig_keyword *kw;
- for (kw = keywords; kw->ident; kw++) {
- if (strcmp(kw->ident, ws->ws_wordv[0]) == 0)
- return kw->setter(ws->ws_wordc, ws->ws_wordv,
- conf, file, line);
- }
- error("%s:%d: unrecognized keyword", file, line);
- return 1;
-}
-
-void
-dirconfig_parse(const char *file, DIRCONFIG *conf)
-{
- FILE *fp;
- char buf[1024];
- unsigned ln = 0;
- int wsflags = WRDSF_DEFFLAGS;
- struct wordsplit ws;
-
- fp = fopen(file, "r");
- if (!fp) {
- if (errno != ENOENT)
- error("can't open file %s: %s", file,
- strerror(errno));
- return;
- }
-
- ws.ws_comment = "#";
- wsflags |= WRDSF_COMMENT;
-
- while (fgets(buf, sizeof(buf), fp)) {
- int len;
-
- ln++;
- len = strlen(buf);
- if (len == 0)
- continue;
- if (buf[len-1] == '\n')
- buf[--len] = 0;
- else if (!feof(fp)) {
- error("%s:%d: line too long", file, ln);
- break;
- }
-
- if (wordsplit(buf, &ws, wsflags)) {
- error("%s:%d: %s", file, ln, wordsplit_strerror(&ws));
- break;
- }
-
- wsflags |= WRDSF_REUSE;
- if (ws.ws_wordc == 0)
- continue;
- if (line_interpret(&ws, conf, file, ln)) {
- break;
- }
- }
-
- if (wsflags & WRDSF_REUSE)
- wordsplit_free(&ws);
-
- if (ferror(fp)) {
- error("%s: %s", file, strerror(errno));
- }
- fclose(fp);
-}
-
-DIRCONFIG *
-dirconfig_init(void)
-{
- DIRCONFIG *p = xcalloc(1, sizeof(*p));
- p->list_unreadable = 1;
- return p;
-}
-
-void
-dirconfig_free(DIRCONFIG *conf)
-{
- if (conf) {
- if (conf->index_files) {
- size_t i;
- for (i = 0; conf->index_files[i]; i++)
- free(conf->index_files[i]);
- free(conf->index_files);
- }
- hidden_files_free(conf);
- free(conf);
- }
-}
-
-int
-filename_is_valid(char const *name)
-{
- enum { a_chr, a_sla, a_dot, a_end, A_MAX };
- enum { s_ini, s_cur, s_sla, s_dot, s_dt2, s_err, s_end, S_MAX = s_end }
- state = s_ini;
- static char trans[A_MAX][S_MAX] = {
- /* ini cur sla dot dt2 err */
- /* chr */ { s_cur, s_cur, s_cur, s_cur, s_cur, s_err },
- /* sla */ { s_sla, s_sla, s_err, s_err, s_err, s_err },
- /* dot */ { s_dot, s_cur, s_dot, s_dt2, s_cur, s_err },
- /* end */ { s_end, s_end, s_end, s_err, s_err, s_err },
- };
-
- int c;
- while (state != s_err && state != s_end) {
- switch (*name++) {
- case '/':
- c = a_sla;
- break;
- case '.':
- c = a_dot;
- break;
- case 0:
- c = a_end;
- break;
- default:
- c = a_chr;
- }
- state = trans[c][state];
- }
- return state == s_end;
-}
-
-int
-filename_is_special(char const *name)
-{
- return (name[0] == '.'
- && (name[1] == 0
- || (name[1] == '.' && name[2] == 0)));
-}
-
-int
-filename_is_hidden(char const *name, DIRCONFIG const *conf)
-{
- if (filename_is_special(name) || strcmp(name, dotfile) == 0)
- return 1;
-
- if (conf && conf->hidden_files_rxv) {
- size_t i;
- for (i = 0; i < conf->hidden_files_rxc; i++)
- if (regexec(&conf->hidden_files_rxv[i], name,
- 0, NULL, 0) == 0)
- return 1;
- }
- return 0;
-}
-
-
-
diff --git a/src/fileserv.c b/src/fileserv.c
index 9b6aa10..b81c9e7 100644
--- a/src/fileserv.c
+++ b/src/fileserv.c
@@ -42,6 +42,9 @@ char *address = "0.0.0.0";
char *forwarded_header = "X-Forwarded-For";
char *index_css;
char *tmpdir;
+char *user;
+char *group;
+char *mime_types_file;
#ifndef DEFAULT_SERVICE
# define DEFAULT_SERVICE "8080"
@@ -63,15 +66,10 @@ usage(void)
{
printf("usage: %s [OPTIONS] [HOST:]URL:DIR...\n", progname);
printf("OPTIONS are:\n\n");
- printf(" -a ADDRESS:PORT listen on the given address\n");
- printf(" -F FACILITY use this syslog facility\n");
+ printf(" -c FILE read configuration from FILE\n");
printf(" -f remain in the foreground\n");
- printf(" -g GROUP run with privileges of GROUP\n");
printf(" -h show this help summary\n");
- printf(" -p FILE store PID in FILE\n");
- printf(" -t IP add trusted IP address\n");
- printf(" -x HEADER use HEADER instead of X-Forwarded-For\n");
- printf(" -u USER run with this USER privileges\n");
+ printf(" -v increase verbosity\n");
}
int
@@ -245,17 +243,17 @@ errno_to_http_code(int ec)
}
static int
-find_index_file(char const *dir, DIRCONFIG *conf, char **index_file,
+find_index_file(char const *dir, CONFIG *conf, char **index_file,
struct stat *pst)
{
char *cf;
- if (conf->index_files) {
+ if (conf->index_files_v) {
int i;
int lasterr = 0;
- for (i = 0; conf->index_files[i]; i++) {
+ for (i = 0; conf->index_files_v[i]; i++) {
struct stat st;
- cf = catfile(dir, conf->index_files[i]);
+ cf = catfile(dir, conf->index_files_v[i]);
if (access(cf, F_OK) || lstat(cf, &st)) {
free(cf);
lasterr = errno;
@@ -278,7 +276,7 @@ find_index_file(char const *dir, DIRCONFIG *conf, char **index_file,
typedef struct file_resp {
int fd;
char *file_name;
- DIRCONFIG *conf;
+ CONFIG *conf;
struct stat st;
char const *type;
char *location;
@@ -297,7 +295,7 @@ file_resp_free(FILE_RESP *resp)
if (resp->fd >= 0)
close(resp->fd);
free(resp->file_name);
- dirconfig_free(resp->conf);
+ config_free(resp->conf);
free(resp->location);
}
@@ -594,7 +592,6 @@ main(int argc, char **argv)
int c, i;
int fd = -1;
struct MHD_Daemon *mhd;
- char *user = NULL, *group = NULL;
struct sockaddr *server_addr;
int foreground = 0;
sigset_t sigs;
@@ -605,7 +602,6 @@ main(int argc, char **argv)
SIGTERM,
0
};
- char *mime_types_file = NULL;
progname = basename(argv[0]);
@@ -614,41 +610,17 @@ main(int argc, char **argv)
if (!tmpdir)
tmpdir = "/tmp";
- while ((c = getopt(argc, argv, "a:F:fg:i:hm:p:t:x:u:v")) != EOF) {
+ while ((c = getopt(argc, argv, "c:fhv")) != EOF) {
switch (c) {
- case 'a':
- address = optarg;
- break;
- case 'F':
- set_log_facility(optarg);
+ case 'c':
+ config_file = optarg;
break;
case 'f':
foreground = 1;
break;
- case 'g':
- group = optarg;
- break;
case 'h':
usage();
exit(0);
- case 'i':
- parse_template_file(optarg);
- break;
- case 'm':
- mime_types_file = optarg;
- break;
- case 'p':
- pidfile = optarg;
- break;
- case 't':
- trusted_ip_add(optarg);
- break;
- case 'x':
- forwarded_header = optarg;
- break;
- case 'u':
- user = optarg;
- break;
case 'v':
verbose++;
break;
@@ -657,6 +629,8 @@ main(int argc, char **argv)
}
}
+ readconfig();
+
pidfile_check();
if (mime_types_file) {
diff --git a/src/fileserv.h b/src/fileserv.h
index e188e28..6c2ea4c 100644
--- a/src/fileserv.h
+++ b/src/fileserv.h
@@ -1,5 +1,5 @@
/* This file is part of fileserv.
- Copyright (C) 2017 Sergey Poznyakoff
+ Copyright (C) 2017-2018 Sergey Poznyakoff
Fileserv is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,15 +24,21 @@
#endif
extern char const *progname;
+extern char *config_file;
extern char *pidfile;
extern char *index_css;
extern char *tmpdir;
extern char *forwarded_header;
+extern char *mime_types_file;
+extern char *user;
+extern char *group;
+extern char *address;
+extern int verbose;
void error(char const *fmt, ...);
void info(char const *fmt, ...);
void syslog_enable(void);
-void set_log_facility(char const *arg);
+int set_log_facility(char const *arg);
void fileserv_logger(void *arg, const char *fmt, va_list ap);
@@ -55,22 +61,36 @@ void pidfile_remove(void);
void pidfile_create(void);
void pidfile_check(void);
-typedef struct fileserv_dirconfig {
+void urimap_add(char *arg);
+
+typedef struct fileserv_config {
int follow:1; /* Follow symbolic links */
int listing:1; /* Show directory listing, if a directory
is requested and no index file exists */
int list_unreadable:1; /* List unreadable files */
- char **index_files; /* Names of index files */
+
+ char **index_files_v; /* Names of index files */
+ size_t index_files_c; /* Number of index files */
+ size_t index_files_n; /* Index of the first allocated index file */
+
regex_t *hidden_files_rxv;
size_t hidden_files_rxc;
-} DIRCONFIG;
+ size_t hidden_files_rxn;
+} CONFIG;
+
+enum {
+ CTXNONE = 0,
+ CTXGLOB = 0x01,
+ CTXDIR = 0x02
+};
-#define DIRCONFIG_INITIALIZER { 0, 0, 1, NULL }
+void readconfig(void);
-DIRCONFIG *dirconfig(char const *path, size_t prefix_len);
-void dirconfig_parse(const char *file, DIRCONFIG *conf);
-DIRCONFIG *dirconfig_init(void);
-void dirconfig_free(DIRCONFIG *conf);
+CONFIG *dirconfig(char const *path, size_t prefix_len);
+int config_parse(const char *file, CONFIG *conf, int ctx);
+CONFIG *config_init(void);
+CONFIG *config_clone(CONFIG const *source);
+void config_free(CONFIG *conf);
char *catfile_n(char const *dir, size_t len, char const *file);
char *catfile(char const *dir, char const *file);
@@ -88,7 +108,7 @@ typedef enum {
} INDEX_SORT_ORD;
int directory_index(int fd,
- DIRCONFIG const *conf, char const *uri, char const *path,
+ CONFIG const *conf, char const *uri, char const *path,
INDEX_SORT_COL col, INDEX_SORT_ORD ord);
INDEX_SORT_COL index_sort_col_from_arg(int c);
@@ -97,7 +117,7 @@ INDEX_SORT_ORD index_sort_ord_from_arg(int c);
int index_sort_ord_to_arg(INDEX_SORT_ORD c);
int filename_is_special(char const *name);
-int filename_is_hidden(char const *name, DIRCONFIG const *conf);
+int filename_is_hidden(char const *name, CONFIG const *conf);
int filename_is_valid(char const *name);
void parse_template_string(char const *str);
diff --git a/src/idx.c b/src/idx.c
index 63b89bb..8a74754 100644
--- a/src/idx.c
+++ b/src/idx.c
@@ -287,7 +287,7 @@ idxlist_append(IDXLIST *lst, IDXENT *ent)
}
static int
-idxlist_scan(IDXLIST *idx_list, char const *path, DIRCONFIG const *conf)
+idxlist_scan(IDXLIST *idx_list, char const *path, CONFIG const *conf)
{
DIR *dir;
struct dirent *ent;
@@ -429,7 +429,7 @@ index_sort_ord_to_arg(INDEX_SORT_ORD c)
typedef struct {
int fd;
- DIRCONFIG const *conf;
+ CONFIG const *conf;
IDXENT *ent;
int n;
struct wordsplit ws;
@@ -766,7 +766,7 @@ static int (*comp[])(IDXENT const *, IDXENT c