| /* Parse arguments and configuration |
| * |
| * Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. |
| * Written by David Howells (dhowells@redhat.com) |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public Licence |
| * as published by the Free Software Foundation; either version |
| * 2 of the Licence, or (at your option) any later version. |
| */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <getopt.h> |
| #include <limits.h> |
| #include <sys/stat.h> |
| |
| #include "version.h" |
| #include "common/cachefilesd.h" |
| #include "common/debug.h" |
| |
| struct config cfg = { |
| .culltable_shift = 12, |
| .culltable_size = (1 << 12), |
| .thrash_limit = 5, |
| .config_file = "/etc/cachefilesd.conf", |
| .dev_file = "/dev/cachefiles", |
| .proc_file = "/proc/fs/cachefiles", |
| .pid_file = "/var/run/cachefilesd.pid", |
| }; |
| |
| static __attribute__((noreturn)) |
| void version(void) |
| { |
| printf("cachefilesd version " CACHEFILESD_VERSION "\n"); |
| exit(0); |
| } |
| |
| static __attribute__((noreturn)) |
| void help(void) |
| { |
| fprintf(stderr, |
| "Format:\n" |
| " /sbin/cachefilesd [-d]* [-s] [-n] [-p <pidfile>] [-f <configfile>]\n" |
| " /sbin/cachefilesd -v\n" |
| "\n" |
| "Options:\n" |
| " -d\tIncrease debugging level (cumulative)\n" |
| " -n\tDon't daemonise the process\n" |
| " -s\tMessage output to stderr instead of syslog\n" |
| " -p <pidfile>\tWrite the PID into the file\n" |
| " -f <configfile>\n" |
| " -v\tPrint version and exit\n" |
| " -c\tCheck cache consistency and exit\n" |
| " -F\tForce a deep-scan.\n" |
| "\tRead the specified configuration file instead of" |
| " /etc/cachefiles.conf\n"); |
| |
| exit(2); |
| } |
| |
| /* |
| * Parse the command line arguments. |
| */ |
| void parse_arguments(int argc, char *argv[]) |
| { |
| int opt; |
| |
| /* handle help request */ |
| if (argc == 2 && strcmp(argv[1], "--help") == 0) |
| help(); |
| |
| if (argc == 2 && strcmp(argv[1], "--version") == 0) |
| version(); |
| |
| /* parse the arguments */ |
| while (opt = getopt(argc, argv, "dsnf:p:vFc"), |
| opt != EOF |
| ) { |
| switch (opt) { |
| case 'd': |
| /* turn on debugging */ |
| cfg.debug_level++; |
| break; |
| |
| case 's': |
| /* disable syslog writing */ |
| cfg.no_sys_log = true; |
| break; |
| |
| case 'n': |
| /* don't daemonise */ |
| cfg.no_daemonise = true; |
| break; |
| |
| case 'f': |
| /* use a specific config file */ |
| cfg.config_file = optarg; |
| break; |
| |
| case 'p': |
| /* use a specific PID file */ |
| cfg.pid_file = optarg; |
| break; |
| |
| case 'c': |
| /* offline scanning mode. |
| * Do not engage the kernel. */ |
| cfg.scan_only = 1; |
| break; |
| |
| case 'F': |
| /* Force a deep scan */ |
| cfg.force_scan = 1; |
| break; |
| |
| case 'v': |
| /* print the version and exit */ |
| version(); |
| |
| default: |
| opterror("Unknown commandline option '%c'", optopt); |
| } |
| } |
| } |
| |
| /* |
| * Parse the configuration file. |
| */ |
| void parse_config_file(void) |
| { |
| struct command *cmd, **ppcmd; |
| struct stat st; |
| unsigned lineno; |
| ssize_t n; |
| size_t m, len; |
| FILE *f; |
| char *line, *cp; |
| |
| /* open the config file */ |
| f = fopen(cfg.config_file, "r"); |
| if (!f) |
| oserror("Unable to open %s", cfg.config_file); |
| |
| /* read the configuration */ |
| ppcmd = &cfg.commands; |
| m = 0; |
| line = NULL; |
| lineno = 0; |
| while (n = getline(&line, &m, f), |
| n != EOF |
| ) { |
| lineno++; |
| |
| if (n >= cfg.page_size) |
| cfgerror("Line too long"); |
| |
| if (memchr(line, 0, n) != 0) |
| cfgerror("Line contains a NUL character"); |
| |
| /* eat blank lines, leading white space and trailing NL */ |
| cp = strchr(line, '\n'); |
| if (!cp) |
| cfgerror("Unterminated line"); |
| |
| if (cp == line) |
| continue; |
| *cp = '\0'; |
| |
| for (cp = line; isspace(*cp); cp++) {;} |
| |
| if (!*cp) |
| continue; |
| |
| /* eat full line comments */ |
| if (*cp == '#') |
| continue; |
| |
| /* allow culling to be disabled */ |
| if (memcmp(cp, "nocull", 6) == 0 && |
| (!cp[6] || isspace(cp[6]))) |
| cfg.dont_cull = true; |
| |
| /* note the cull table size command */ |
| if (memcmp(cp, "culltable", 9) == 0 && isspace(cp[9])) { |
| unsigned long cts; |
| char *sp; |
| |
| for (sp = cp + 10; isspace(*sp); sp++) {;} |
| |
| cts = strtoul(sp, &sp, 10); |
| if (*sp) |
| cfgerror("Invalid cull table size number"); |
| if (cts < 12 || cts > 20) |
| cfgerror("Log2 of cull table size must be 12 <= N <= 20"); |
| cfg.culltable_shift = cts; |
| cfg.culltable_size = 1 << cts; |
| continue; |
| } |
| |
| /* note the dir command */ |
| if (memcmp(cp, "dir", 3) == 0 && isspace(cp[3])) { |
| char *sp; |
| |
| for (sp = cp + 4; isspace(*sp); sp++) {;} |
| |
| if (strlen(sp) > PATH_MAX - 10) |
| cfgerror("Cache pathname is too long"); |
| |
| if (stat(sp, &st) < 0) |
| oserror("Can't confirm cache location"); |
| |
| cfg.cache_root = strdup(sp); |
| if (!cfg.cache_root) |
| oserror("Can't copy cache name"); |
| } |
| |
| /* object to the bind command */ |
| if (memcmp(cp, "bind", 4) == 0 && |
| (!cp[4] || isspace(cp[4]))) |
| cfgerror("'bind' command not permitted"); |
| |
| /* Store up the commands to send to the kernel module */ |
| len = strlen(line); |
| cmd = malloc(sizeof(*cmd) + len); |
| if (!cmd) |
| oserror("CacheFiles"); |
| *ppcmd = cmd; |
| ppcmd = &cmd->next; |
| cmd->next = NULL; |
| cmd->len = len; |
| cmd->line = lineno; |
| memcpy(cmd->data, line, len); |
| } |
| |
| free(line); |
| |
| if (!feof(f)) |
| oserror("Unable to read %s", cfg.config_file); |
| |
| if (fclose(f) == EOF) |
| oserror("Unable to close %s", cfg.config_file); |
| } |