conf.c 7.51 KB
Newer Older
1
/*-
2 3 4 5 6 7 8 9 10
 * Free/Libre Near Field Communication (NFC) library
 *
 * Libnfc historical contributors:
 * Copyright (C) 2009      Roel Verdult
 * Copyright (C) 2009-2013 Romuald Conty
 * Copyright (C) 2010-2012 Romain Tartière
 * Copyright (C) 2010-2013 Philippe Teuwen
 * Copyright (C) 2012-2013 Ludovic Rousseau
 * Additional contributors of this file:
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 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 Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

26 27 28 29
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif // HAVE_CONFIG_H

30 31
#include "conf.h"

32
#ifdef CONFFILES
33 34 35 36 37 38 39 40 41 42 43 44
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <regex.h>
#include <sys/stat.h>

#include <nfc/nfc.h>
#include "nfc-internal.h"
#include "log.h"

#define LOG_CATEGORY "libnfc.config"
Romuald Conty's avatar
Romuald Conty committed
45
#define LOG_GROUP    NFC_LOG_GROUP_CONFIG
46

47 48 49 50 51 52 53 54
#ifndef LIBNFC_SYSCONFDIR
// If this define does not already exists, we build it using SYSCONFDIR
#ifndef SYSCONFDIR
#error "SYSCONFDIR is not defined but required."
#endif // SYSCONFDIR
#define LIBNFC_SYSCONFDIR      SYSCONFDIR"/nfc"
#endif // LIBNFC_SYSCONFDIR

55 56 57
#define LIBNFC_CONFFILE        LIBNFC_SYSCONFDIR"/libnfc.conf"
#define LIBNFC_DEVICECONFDIR   LIBNFC_SYSCONFDIR"/devices.d"

58 59
static bool
conf_parse_file(const char *filename, void (*conf_keyvalue)(void *data, const char *key, const char *value), void *data)
60
{
61
  FILE *f = fopen(filename, "r");
62
  if (!f) {
Romuald Conty's avatar
Romuald Conty committed
63
    log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "Unable to open file: %s", filename);
64 65 66
    return false;
  }
  char line[BUFSIZ];
67
  const char *str_regex = "^[[:space:]]*([[:alnum:]_.]+)[[:space:]]*=[[:space:]]*(\"(.+)\"|([^[:space:]]+))[[:space:]]*$";
68
  regex_t preg;
69
  if (regcomp(&preg, str_regex, REG_EXTENDED | REG_NOTEOL) != 0) {
70
    log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Regular expression used for configuration file parsing is not valid.");
71 72 73
    return false;
  }
  size_t nmatch = preg.re_nsub + 1;
74 75
  regmatch_t *pmatch = malloc(sizeof(*pmatch) * nmatch);
  if (!pmatch) {
76
    log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Not enough memory: malloc failed.");
77
    regfree(&preg);
78 79 80 81 82 83
    return false;
  }

  int lineno = 0;
  while (fgets(line, BUFSIZ, f) != NULL) {
    lineno++;
84
    switch (line[0]) {
85 86 87 88 89
      case '#':
      case '\n':
        break;
      default: {
        int match;
90
        if ((match = regexec(&preg, line, nmatch, pmatch, 0)) == 0) {
91
          const size_t key_size = pmatch[1].rm_eo - pmatch[1].rm_so;
92
          const off_t  value_pmatch = pmatch[3].rm_eo != -1 ? 3 : 4;
93
          const size_t value_size = pmatch[value_pmatch].rm_eo - pmatch[value_pmatch].rm_so;
94 95 96 97 98 99
          char key[key_size + 1];
          char value[value_size + 1];
          strncpy(key, line + (pmatch[1].rm_so), key_size);
          key[key_size] = '\0';
          strncpy(value, line + (pmatch[value_pmatch].rm_so), value_size);
          value[value_size] = '\0';
100 101
          conf_keyvalue(data, key, value);
        } else {
Romuald Conty's avatar
Romuald Conty committed
102
          log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Parse error on line #%d: %s", lineno, line);
103 104 105 106 107
        }
      }
      break;
    }
  }
108 109

  free(pmatch);
110
  regfree(&preg);
111 112 113
  return false;
}

114 115
static void
conf_keyvalue_context(void *data, const char *key, const char *value)
116
{
117
  nfc_context *context = (nfc_context *)data;
118
  log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "key: [%s], value: [%s]", key, value);
119 120 121 122
  if (strcmp(key, "allow_autoscan") == 0) {
    string_as_boolean(value, &(context->allow_autoscan));
  } else if (strcmp(key, "allow_intrusive_scan") == 0) {
    string_as_boolean(value, &(context->allow_intrusive_scan));
Romuald Conty's avatar
Romuald Conty committed
123 124
  } else if (strcmp(key, "log_level") == 0) {
    context->log_level = atoi(value);
125
  } else if (strcmp(key, "device.name") == 0) {
126 127
    if ((context->user_defined_device_count == 0) || strcmp(context->user_defined_devices[context->user_defined_device_count - 1].name, "") != 0) {
      if (context->user_defined_device_count >= MAX_USER_DEFINED_DEVICES) {
128
        log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Configuration exceeded maximum user-defined devices.");
129 130
        return;
      }
131 132
      context->user_defined_device_count++;
    }
133
    strcpy(context->user_defined_devices[context->user_defined_device_count - 1].name, value);
134
  } else if (strcmp(key, "device.connstring") == 0) {
135 136
    if ((context->user_defined_device_count == 0) || strcmp(context->user_defined_devices[context->user_defined_device_count - 1].connstring, "") != 0) {
      if (context->user_defined_device_count >= MAX_USER_DEFINED_DEVICES) {
137
        log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Configuration exceeded maximum user-defined devices.");
138 139
        return;
      }
140 141
      context->user_defined_device_count++;
    }
142
    strcpy(context->user_defined_devices[context->user_defined_device_count - 1].connstring, value);
143 144 145 146 147 148 149 150 151 152
  } else if (strcmp(key, "device.optional") == 0) {
    if ((context->user_defined_device_count == 0) || context->user_defined_devices[context->user_defined_device_count - 1].optional) {
      if (context->user_defined_device_count >= MAX_USER_DEFINED_DEVICES) {
        log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Configuration exceeded maximum user-defined devices.");
        return;
      }
      context->user_defined_device_count++;
    }
    if ((strcmp(value, "true") == 0) || (strcmp(value, "True") == 0) || (strcmp(value, "1") == 0)) //optional
      context->user_defined_devices[context->user_defined_device_count - 1].optional = true;
153
  } else {
Romuald Conty's avatar
Romuald Conty committed
154
    log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "Unknown key in config line: %s = %s", key, value);
155 156 157
  }
}

158
static void
159
conf_keyvalue_device(void *data, const char *key, const char *value)
160 161 162 163 164 165
{
  char newkey[BUFSIZ];
  sprintf(newkey, "device.%s", key);
  conf_keyvalue_context(data, newkey, value);
}

166
static void
167
conf_devices_load(const char *dirname, nfc_context *context)
168
{
169
  DIR *d = opendir(dirname);
170
  if (!d) {
Romuald Conty's avatar
Romuald Conty committed
171
    log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Unable to open directory: %s", dirname);
172
  } else {
173
    struct dirent *de;
174 175 176 177
    struct dirent entry;
    struct dirent *result;
    while ((readdir_r(d, &entry, &result) == 0) && (result != NULL)) {
      de = &entry;
178
      if (de->d_name[0] != '.') {
179 180
        const size_t filename_len = strlen(de->d_name);
        const size_t extension_len = strlen(".conf");
181 182
        if ((filename_len > extension_len) &&
            (strncmp(".conf", de->d_name + (filename_len - extension_len), extension_len) == 0)) {
183
          char filename[BUFSIZ] = LIBNFC_DEVICECONFDIR"/";
184
          strcat(filename, de->d_name);
185 186 187 188 189
          struct stat s;
          if (stat(filename, &s) == -1) {
            perror("stat");
            continue;
          }
190 191
          if (S_ISREG(s.st_mode)) {
            conf_parse_file(filename, conf_keyvalue_device, context);
192
          }
193 194 195
        }
      }
    }
Philippe Teuwen's avatar
Philippe Teuwen committed
196
    closedir(d);
197 198
  }
}
199

200
void
201 202 203 204 205 206
conf_load(nfc_context *context)
{
  conf_parse_file(LIBNFC_CONFFILE, conf_keyvalue_context, context);
  conf_devices_load(LIBNFC_DEVICECONFDIR, context);
}

207
#endif // CONFFILES
208