config.c 10.5 KB
Newer Older
1
2
3
4
/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */
/* See LICENSE for licensing information */
/* $Id$ */

Roger Dingledine's avatar
Roger Dingledine committed
5
#include "or.h"
Roger Dingledine's avatar
Roger Dingledine committed
6

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* enumeration of types which option values can take */
#define CONFIG_TYPE_STRING  0
#define CONFIG_TYPE_CHAR    1
#define CONFIG_TYPE_INT     2
#define CONFIG_TYPE_LONG    3
#define CONFIG_TYPE_DOUBLE  4
#define CONFIG_TYPE_BOOL    5

#define CONFIG_LINE_MAXLEN 1024

struct config_line {
  char *key;
  char *value;
  struct config_line *next;
};

static FILE *config_open(const unsigned char *filename);
static int config_close(FILE *f);
static struct config_line *config_get_commandlines(int argc, char **argv);
static struct config_line *config_get_lines(FILE *f);
static void config_free_lines(struct config_line *front);
static int config_compare(struct config_line *c, char *key, int type, void *arg);
static void config_assign(or_options_t *options, struct config_line *list);

31
/* open configuration file for reading */
32
static FILE *config_open(const unsigned char *filename) {
33
34
35
36
37
38
39
40
41
  assert(filename);
  if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) != strlen(filename)) {
    /* filename has illegal letters */
    return NULL;
  }
  return fopen(filename, "r");
}

/* close configuration file */
42
static int config_close(FILE *f) {
43
44
45
46
  assert(f);
  return fclose(f);
}

47
static struct config_line *config_get_commandlines(int argc, char **argv) {
48
49
50
51
52
53
54
55
56
57
58
59
  struct config_line *new;
  struct config_line *front = NULL;
  char *s;
  int i = 1;

  while(i < argc-1) { 
    if(!strcmp(argv[i],"-f")) {
//      log(LOG_DEBUG,"Commandline: skipping over -f.");
      i+=2; /* this is the config file option. ignore it. */
      continue;
    }

60
    new = tor_malloc(sizeof(struct config_line));
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    s = argv[i];
    while(*s == '-')
      s++;
    new->key = strdup(s);
    new->value = strdup(argv[i+1]);

    log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'",
      new->key, new->value);
    new->next = front;
    front = new;
    i += 2;
  }
  return front;
}

/* parse the config file and strdup into key/value strings. Return list.
77
 * Warn and ignore mangled lines. */
78
static struct config_line *config_get_lines(FILE *f) {
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  struct config_line *new;
  struct config_line *front = NULL;
  char line[CONFIG_LINE_MAXLEN];
  int lineno=0; /* current line number */
  char *s;
  char *start, *end;

  assert(f);

  fseek(f,0,SEEK_SET); /* make sure we start at the beginning of file */

  while(fgets(line, CONFIG_LINE_MAXLEN, f)) {
    lineno++;

    /* first strip comments */
    s = strchr(line,'#');
    if(s) {
      *s = 0; /* stop the line there */
    }

    /* walk to the end, remove end whitespace */
100
    s = strchr(line, 0); /* now we're at the null */
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    do {
      *s = 0;
      s--;
    } while (isspace(*s));

    start = line;
    while(isspace(*start))
      start++;
    if(*start == 0)
      continue; /* this line has nothing on it */

    end = start;
    while(*end && !isspace(*end))
      end++;
    s = end;
    while(*s && isspace(*s))
      s++;
    if(!*end || !*s) { /* only a keyword on this line. no value. */
      log(LOG_WARNING,"Config line %d has keyword '%s' but no value. Skipping.",lineno,s);
    }
    *end = 0; /* null it out */

    /* prepare to parse the string into key / value */
124
    new = tor_malloc(sizeof(struct config_line));
125
126
127
128
129
130
131
    new->key = strdup(start);
    new->value = strdup(s);

    log(LOG_DEBUG,"Config line %d: parsed keyword '%s', value '%s'",
      lineno, new->key, new->value);
    new->next = front;
    front = new;
Roger Dingledine's avatar
Roger Dingledine committed
132
133
  }

134
  return front;
Roger Dingledine's avatar
Roger Dingledine committed
135
136
}

137
static void config_free_lines(struct config_line *front) {
138
139
140
141
142
143
144
145
146
147
148
149
  struct config_line *tmp;

  while(front) {
    tmp = front;
    front = tmp->next;

    free(tmp->key);
    free(tmp->value);
    free(tmp);
  }
}

150
static int config_compare(struct config_line *c, char *key, int type, void *arg) {
151
  int i;
152
153
154
155
156

  if(strncasecmp(c->key,key,strlen(c->key)))
    return 0;

  /* it's a match. cast and assign. */
157
  log_fn(LOG_DEBUG,"Recognized keyword '%s' as %s, using value '%s'.",c->key,key,c->value);
158
159
160
161

  switch(type) {
    case CONFIG_TYPE_INT:   
      *(int *)arg = atoi(c->value);
162
      break;
163
164
165
    case CONFIG_TYPE_BOOL:
      i = atoi(c->value);
      if (i != 0 && i != 1) {
166
167
        log(LOG_ERR, "Boolean keyword '%s' expects 0 or 1", c->key);
        return 0;
168
169
170
      }
      *(int *)arg = i;
      break;
171
172
    case CONFIG_TYPE_STRING:
      *(char **)arg = strdup(c->value);
173
      break;
174
175
    case CONFIG_TYPE_DOUBLE:
      *(double *)arg = atof(c->value);
176
      break;
177
178
179
180
  }
  return 1;
}

181
static void config_assign(or_options_t *options, struct config_line *list) {
182
183
184
185
186
187
188
189
190
191

  /* iterate through list. for each item convert as appropriate and assign to 'options'. */

  while(list) {
    if(

    /* order matters here! abbreviated arguments use the first match. */

    /* string options */
    config_compare(list, "LogLevel",       CONFIG_TYPE_STRING, &options->LogLevel) ||
192
    config_compare(list, "DataDirectory",  CONFIG_TYPE_STRING, &options->DataDirectory) ||
193
    config_compare(list, "RouterFile",     CONFIG_TYPE_STRING, &options->RouterFile) ||
Roger Dingledine's avatar
Roger Dingledine committed
194
    config_compare(list, "Nickname",       CONFIG_TYPE_STRING, &options->Nickname) ||
195
196
197
198
199
200
201
202

    /* int options */
    config_compare(list, "MaxConn",         CONFIG_TYPE_INT, &options->MaxConn) ||
    config_compare(list, "APPort",          CONFIG_TYPE_INT, &options->APPort) ||
    config_compare(list, "ORPort",          CONFIG_TYPE_INT, &options->ORPort) ||
    config_compare(list, "DirPort",         CONFIG_TYPE_INT, &options->DirPort) ||
    config_compare(list, "DirFetchPeriod",  CONFIG_TYPE_INT, &options->DirFetchPeriod) ||
    config_compare(list, "KeepalivePeriod", CONFIG_TYPE_INT, &options->KeepalivePeriod) ||
203
    config_compare(list, "MaxOnionsPending",CONFIG_TYPE_INT, &options->MaxOnionsPending) ||
204
    config_compare(list, "NewCircuitPeriod",CONFIG_TYPE_INT, &options->NewCircuitPeriod) ||
205
    config_compare(list, "TotalBandwidth",  CONFIG_TYPE_INT, &options->TotalBandwidth) ||
206
    config_compare(list, "NumCpus",         CONFIG_TYPE_INT, &options->NumCpus) ||
207

208
    config_compare(list, "OnionRouter",     CONFIG_TYPE_BOOL, &options->OnionRouter) ||
209
210
211
    config_compare(list, "Daemon",          CONFIG_TYPE_BOOL, &options->Daemon) ||
    config_compare(list, "TrafficShaping",  CONFIG_TYPE_BOOL, &options->TrafficShaping) ||
    config_compare(list, "LinkPadding",     CONFIG_TYPE_BOOL, &options->LinkPadding) ||
212
    config_compare(list, "IgnoreVersion",   CONFIG_TYPE_BOOL, &options->IgnoreVersion) ||
213

214
215
216
217
218
219
    /* float options */
    config_compare(list, "CoinWeight",     CONFIG_TYPE_DOUBLE, &options->CoinWeight)

    ) {
      /* then we're ok. it matched something. */
    } else {
220
      log_fn(LOG_WARNING,"Ignoring unknown keyword '%s'.",list->key);
221
222
223
224
225
226
227
228
229
230
    }

    list = list->next;
  }  
}

/* return 0 if success, <0 if failure. */
int getconfig(int argc, char **argv, or_options_t *options) {
  struct config_line *cl;
  FILE *cf;
231
  char *fname;
232
233
234
  int i;
  int result = 0;

235
/* give reasonable values for each option. Defaults to zero. */
236
237
238
  memset(options,0,sizeof(or_options_t));
  options->LogLevel = "debug";
  options->loglevel = LOG_DEBUG;
239
  options->DataDirectory = NULL;
240
  options->CoinWeight = 0.8;
241
  options->MaxConn = 900;
242
  options->DirFetchPeriod = 600;
243
  options->KeepalivePeriod = 300;
244
  options->MaxOnionsPending = 10;
245
  options->NewCircuitPeriod = 60; /* once a minute */
246
  options->TotalBandwidth = 800000; /* at most 800kB/s total sustained incoming */
247
  options->NumCpus = 1;
248
249
250
251
252
253
254

/* learn config file name, get config lines, assign them */
  i = 1;
  while(i < argc-1 && strcmp(argv[i],"-f")) {
    i++;
  }
  if(i < argc-1) { /* we found one */
255
256
257
258
259
260
261
262
263
264
    fname = argv[i+1];
  } else { /* didn't find one, try /etc/torrc */
    fname = "/etc/torrc";
  }
  log(LOG_DEBUG,"Opening config file '%s'",fname);

  cf = config_open(fname);
  if(!cf) { /* it's defined but not there. that's no good. */
    log(LOG_ERR, "Unable to open configuration file '%s'.",fname);
    return -1;
265
  }
266
267
268
269
270

  cl = config_get_lines(cf);
  config_assign(options,cl);
  config_free_lines(cl);
  config_close(cf);
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
 
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
  config_assign(options,cl);
  config_free_lines(cl);

/* Validate options */

  if(options->LogLevel) {
    if(!strcmp(options->LogLevel,"emerg"))
      options->loglevel = LOG_EMERG;
    else if(!strcmp(options->LogLevel,"alert"))
      options->loglevel = LOG_ALERT;
    else if(!strcmp(options->LogLevel,"crit"))
      options->loglevel = LOG_CRIT;
    else if(!strcmp(options->LogLevel,"err"))
      options->loglevel = LOG_ERR;
    else if(!strcmp(options->LogLevel,"warning"))
      options->loglevel = LOG_WARNING;
    else if(!strcmp(options->LogLevel,"notice"))
      options->loglevel = LOG_NOTICE;
    else if(!strcmp(options->LogLevel,"info"))
      options->loglevel = LOG_INFO;
    else if(!strcmp(options->LogLevel,"debug"))
      options->loglevel = LOG_DEBUG;
    else {
      log(LOG_ERR,"LogLevel must be one of emerg|alert|crit|err|warning|notice|info|debug.");
      result = -1;
    }
  }

  if(options->RouterFile == NULL) {
    log(LOG_ERR,"RouterFile option required, but not found.");
    result = -1;
  }

307
  if(options->ORPort < 0) {
308
    log(LOG_ERR,"ORPort option can't be negative.");
309
310
311
    result = -1;
  }

312
313
  if(options->OnionRouter && options->ORPort == 0) {
    log(LOG_ERR,"If OnionRouter is set, then ORPort must be positive.");
314
315
316
    result = -1;
  }

317
318
  if(options->OnionRouter && options->DataDirectory == NULL) {
    log(LOG_ERR,"DataDirectory option required for OnionRouter, but not found.");
319
320
321
    result = -1;
  }

Roger Dingledine's avatar
Roger Dingledine committed
322
323
  if(options->OnionRouter && options->Nickname == NULL) {
    log_fn(LOG_ERR,"Nickname required for OnionRouter, but not found.");
324
325
326
    result = -1;
  }

327
328
  if(options->APPort < 0) {
    log(LOG_ERR,"APPort option can't be negative.");
329
330
331
    result = -1;
  }

332
333
  if(options->DirPort < 0) {
    log(LOG_ERR,"DirPort option can't be negative.");
334
335
336
    result = -1;
  }

337
  if(options->APPort > 1 &&
338
     (options->CoinWeight < 0.0 || options->CoinWeight >= 1.0)) {
339
    log(LOG_ERR,"CoinWeight option must be >=0.0 and <1.0.");
340
341
342
    result = -1;
  }

343
  if(options->MaxConn < 1) {
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
    log(LOG_ERR,"MaxConn option must be a non-zero positive integer.");
    result = -1;
  }

  if(options->MaxConn >= MAXCONNECTIONS) {
    log(LOG_ERR,"MaxConn option must be less than %d.", MAXCONNECTIONS);
    result = -1;
  }

  if(options->DirFetchPeriod < 1) {
    log(LOG_ERR,"DirFetchPeriod option must be positive.");
    result = -1;
  }

  if(options->KeepalivePeriod < 1) {
    log(LOG_ERR,"KeepalivePeriod option must be positive.");
    result = -1;
  }

  return result;
364
365
}

366
367
368
369
370
371
372
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/