config.c 10.9 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
192

  /* 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) ||
    config_compare(list, "PrivateKeyFile", CONFIG_TYPE_STRING, &options->PrivateKeyFile) ||
193
    config_compare(list, "SigningPrivateKeyFile", CONFIG_TYPE_STRING, &options->SigningPrivateKeyFile) ||
194
    config_compare(list, "RouterFile",     CONFIG_TYPE_STRING, &options->RouterFile) ||
Roger Dingledine's avatar
Roger Dingledine committed
195
196
    config_compare(list, "CertFile",       CONFIG_TYPE_STRING, &options->CertFile) ||
    config_compare(list, "Nickname",       CONFIG_TYPE_STRING, &options->Nickname) ||
197
198
199
200
201
202
203
204

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

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

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

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

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

237
/* give reasonable values for each option. Defaults to zero. */
238
239
240
241
  memset(options,0,sizeof(or_options_t));
  options->LogLevel = "debug";
  options->loglevel = LOG_DEBUG;
  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;
Roger Dingledine's avatar
Roger Dingledine committed
249
  options->CertFile = "default.cert";
250
251
252
253
254
255
256

/* 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 */
257
258
259
260
261
262
263
264
265
266
    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;
267
  }
268
269
270
271
272

  cl = config_get_lines(cf);
  config_assign(options,cl);
  config_free_lines(cl);
  config_close(cf);
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
307
308
 
/* 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;
  }

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

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

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

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

329
330
  if(options->DirPort > 0 && options->SigningPrivateKeyFile == NULL) {
    log(LOG_ERR,"SigningPrivateKeyFile option required for DirServer, but not found.");
331
332
333
    result = -1;
  }

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

339
340
  if(options->DirPort < 0) {
    log(LOG_ERR,"DirPort option can't be negative.");
341
342
343
    result = -1;
  }

344
  if(options->APPort > 1 &&
345
     (options->CoinWeight < 0.0 || options->CoinWeight >= 1.0)) {
346
    log(LOG_ERR,"CoinWeight option must be >=0.0 and <1.0.");
347
348
349
    result = -1;
  }

350
  if(options->MaxConn < 1) {
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
    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;
371
372
}

373
374
375
376
377
378
379
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/