config.c 10.2 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
        log(LOG_WARNING, "Boolean keyword '%s' expects 0 or 1", c->key);
167
        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
    config_compare(list, "Address",        CONFIG_TYPE_STRING, &options->Address) ||
196
197
198
199
200
201
202
203

    /* 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) ||
204
    config_compare(list, "MaxOnionsPending",CONFIG_TYPE_INT, &options->MaxOnionsPending) ||
205
    config_compare(list, "NewCircuitPeriod",CONFIG_TYPE_INT, &options->NewCircuitPeriod) ||
206
    config_compare(list, "TotalBandwidth",  CONFIG_TYPE_INT, &options->TotalBandwidth) ||
207
    config_compare(list, "NumCpus",         CONFIG_TYPE_INT, &options->NumCpus) ||
208

209
    config_compare(list, "OnionRouter",     CONFIG_TYPE_BOOL, &options->OnionRouter) ||
210
211
212
    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) ||
213
    config_compare(list, "IgnoreVersion",   CONFIG_TYPE_BOOL, &options->IgnoreVersion) ||
214

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

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

    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;
232
  char *fname;
233
234
235
  int i;
  int result = 0;

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

/* 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 */
256
257
258
259
260
261
262
    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);
263
  if(!cf) {
264
265
    log(LOG_ERR, "Unable to open configuration file '%s'.",fname);
    return -1;
266
  }
267
268
269
270
271

  cl = config_get_lines(cf);
  config_assign(options,cl);
  config_free_lines(cl);
  config_close(cf);
272
273
274
275
276
277
278
279
280
 
/* 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) {
281
    if(!strcmp(options->LogLevel,"err"))
282
283
284
285
286
287
288
289
      options->loglevel = LOG_ERR;
    else if(!strcmp(options->LogLevel,"warning"))
      options->loglevel = LOG_WARNING;
    else if(!strcmp(options->LogLevel,"info"))
      options->loglevel = LOG_INFO;
    else if(!strcmp(options->LogLevel,"debug"))
      options->loglevel = LOG_DEBUG;
    else {
290
      log(LOG_ERR,"LogLevel must be one of err|warning|info|debug.");
291
292
293
294
295
296
297
298
299
      result = -1;
    }
  }

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

300
  if(options->ORPort < 0) {
301
    log(LOG_ERR,"ORPort option can't be negative.");
302
303
304
    result = -1;
  }

305
306
  if(options->OnionRouter && options->ORPort == 0) {
    log(LOG_ERR,"If OnionRouter is set, then ORPort must be positive.");
307
308
309
    result = -1;
  }

310
311
  if(options->OnionRouter && options->DataDirectory == NULL) {
    log(LOG_ERR,"DataDirectory option required for OnionRouter, but not found.");
312
313
314
    result = -1;
  }

Roger Dingledine's avatar
Roger Dingledine committed
315
316
  if(options->OnionRouter && options->Nickname == NULL) {
    log_fn(LOG_ERR,"Nickname required for OnionRouter, but not found.");
317
318
319
    result = -1;
  }

320
321
  if(options->APPort < 0) {
    log(LOG_ERR,"APPort option can't be negative.");
322
323
324
    result = -1;
  }

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

330
  if(options->APPort > 1 &&
331
     (options->CoinWeight < 0.0 || options->CoinWeight >= 1.0)) {
332
    log(LOG_ERR,"CoinWeight option must be >=0.0 and <1.0.");
333
334
335
    result = -1;
  }

336
  if(options->MaxConn < 1) {
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
    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;
357
358
}

359
360
361
362
363
364
365
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/