Back to index

opendkim  2.6.6
config.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2006-2009 Sendmail, Inc. and its suppliers.
00003 **     All rights reserved.
00004 **
00005 **  Copyright (c) 2009-2012, The OpenDKIM Project.  All rights reserved.
00006 **
00007 **  $Id: config.c,v 1.10.10.1 2010/10/27 21:43:09 cm-msk Exp $
00008 */
00009 
00010 #ifndef lint
00011 static char config_c_id[] = "@(#)$Id: config.c,v 1.10.10.1 2010/10/27 21:43:09 cm-msk Exp $";
00012 #endif /* !lint */
00013 
00014 /* for Solaris */
00015 #ifndef _REENTRANT
00016 # define _REENTRANT
00017 #endif /* _REENTRANT */
00018 
00019 /* system includes */
00020 #include <sys/types.h>
00021 #include <string.h>
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <assert.h>
00025 
00026 /* libopendkim includes */
00027 #include <dkim.h>
00028 #include <dkim-strl.h>
00029 
00030 /* opendkim includes */
00031 #include "config.h"
00032 
00033 /* limits */
00034 #define       BUFRSZ        1024          /* generic buffer size */
00035 #define       MAXLEVEL      5             /* max. include recursion */
00036 
00037 #ifndef FALSE
00038 # define FALSE              0
00039 #endif /* ! FALSE */
00040 #ifndef TRUE
00041 # define TRUE        1
00042 #endif /* ! TRUE */
00043 
00044 /* prototypes */
00045 static void config_attach __P((struct config *, struct config **));
00046 
00047 /* errors */
00048 #define       CONF_UNKNOWN  (-1)          /* unknown status */
00049 #define       CONF_SUCCESS  0             /* no error */
00050 #define       CONF_MISSING  1             /* required value missing */
00051 #define       CONF_UNRECOG  2             /* unrecognized parameter */
00052 #define       CONF_ILLEGAL  3             /* illegal value */
00053 #define       CONF_NESTING  4             /* "include" nesting too deep */
00054 #define       CONF_READING  5             /* error reading (see errno) */
00055 #define       CONF_NMEMORY  6             /* malloc() failure */
00056 
00057 /* statics */
00058 static int conf_error;                    /* configuration error number */
00059 
00060 /*
00061 **  CONFIG_ATTACH -- attach one config to another
00062 **
00063 **  Parameters:
00064 **     c1 -- configuration to attach
00065 **     c2 -- configuration to which to attach
00066 **
00067 **  Return value:
00068 **     None.
00069 */
00070 
00071 static void
00072 config_attach(struct config *c1, struct config **c2)
00073 {
00074        struct config *prev;
00075        struct config *cur;
00076 
00077        assert(c1 != NULL);
00078 
00079        if (*c2 == NULL)
00080        {
00081               *c2 = c1;
00082        }
00083        else
00084        {
00085               prev = NULL;
00086 
00087               for (cur = c1; cur != NULL; cur = cur->cfg_next)
00088                      prev = cur;
00089 
00090               prev->cfg_next = *c2;
00091        }
00092 }
00093 
00094 /*
00095 **  CONFIG_LOAD_LEVEL -- load configuration from a file (internal version)
00096 **
00097 **  Parameters:
00098 **     file -- path from which to load; NULL or "-" implies stdin
00099 **     cd -- array of (struct configdef) elements containing the
00100 **           configuration syntax to assert
00101 **     line -- line number where an error occurred (updated)
00102 **     outpath -- configuration file in which error occurred (updated)
00103 **     outpathlen -- bytes available at "outpath"
00104 **     level -- nesting level
00105 **
00106 **  Return value:
00107 **     Pointer to a (struct config) which is the head of a list of
00108 **     loaded configuration items, or NULL on error; if NULL, "line" is
00109 **     updated to indicate which line number contained the error and,
00110 **     if the configuration file being parsed was not the one referenced
00111 **     by "in", then "path" will be updated to point to the filename
00112 **     that was being processed.
00113 */
00114 
00115 static struct config *
00116 config_load_level(char *file, struct configdef *def,
00117                   unsigned int *line, char *outpath, size_t outpathlen,
00118                   int level)
00119 {
00120        int n = -1;
00121        int err = 0;
00122        unsigned int myline = 0;
00123        int value = -1;
00124        FILE *in;
00125        char *p;
00126        char *s;
00127        char *str = NULL;
00128        struct config *new = NULL;
00129        struct config *cur = NULL;
00130        char buf[BUFRSZ + 1];
00131 
00132        assert(def != NULL);
00133 
00134        if (level > MAXLEVEL)
00135        {
00136               conf_error = CONF_NESTING;
00137               return NULL;
00138        }
00139 
00140        memset(buf, '\0', sizeof buf);
00141 
00142        if (file == NULL || (file[0] == '-' && file[1] == '\0'))
00143        {
00144               in = stdin;
00145               file = "(stdin)";
00146        }
00147        else
00148        {
00149               in = fopen(file, "r");
00150               if (in == NULL)
00151               {
00152                      conf_error = CONF_READING;
00153                      if (line != NULL)
00154                             *line = myline;
00155                      if (outpath != NULL)
00156                             strlcpy(outpath, file, outpathlen);
00157                      return NULL;
00158               }
00159        }
00160 
00161        while (fgets(buf, sizeof buf - 1, in) != NULL)
00162        {
00163               myline++;
00164               str = NULL;
00165 
00166               /* read a line; truncate at newline or "#" */
00167               for (p = buf; *p != '\0'; p++)
00168               {
00169                      if (*p == '#' || *p == '\n')
00170                      {
00171                             *p = '\0';
00172                             break;
00173                      }
00174               }
00175 
00176               /* break down the line */
00177               p = strtok_r(buf, " \t", &s);
00178               if (p != NULL)
00179               {
00180                      /* recognize the directive? */
00181                      for (n = 0; ; n++)
00182                      {
00183                             /* nope */
00184                             if (def[n].cd_name == NULL)
00185                             {
00186                                    conf_error = CONF_UNRECOG;
00187                                    err = 1;
00188                                    break;
00189                             }
00190 
00191                             if (strcasecmp(def[n].cd_name, p) == 0)
00192                                    break;
00193                      }
00194 
00195                      if (!err)
00196                      {
00197                             char *q;
00198 
00199                             /* skip leading whitespace on value */
00200                             for (p = s; *p == ' ' || *p == '\t'; p++)
00201                                    continue;
00202 
00203                             /* ...and trim trailing whitespace */
00204                             q = p + strlen(p) - 1;
00205                             while (p <= q && (*q == '\t' || *q == ' '))
00206                                    *q-- = '\0';
00207                      }
00208 
00209                      if (*p == '\0' && !err)
00210                      {
00211                             conf_error = CONF_MISSING;
00212                             err = 1;
00213                      }
00214 
00215                      if (!err)
00216                      {
00217                             char *q;
00218 
00219                             switch (def[n].cd_type)
00220                             {
00221                               case CONFIG_TYPE_STRING:
00222                               case CONFIG_TYPE_INCLUDE:
00223                                    str = p;
00224                                    break;
00225 
00226                               case CONFIG_TYPE_BOOLEAN:
00227                                    if (p[0] == 't' ||
00228                                        p[0] == 'T' ||
00229                                        p[0] == 'y' ||
00230                                        p[0] == 'Y' ||
00231                                        p[0] == '1')
00232                                    {
00233                                           value = 1;
00234                                    }
00235                                    else if (p[0] == 'f' ||
00236                                             p[0] == 'F' ||
00237                                             p[0] == 'n' ||
00238                                             p[0] == 'N' ||
00239                                             p[0] == '0')
00240                                    {
00241                                           value = 0;
00242                                    }
00243                                    else
00244                                    {
00245                                           conf_error = CONF_ILLEGAL;
00246                                           err = 1;
00247                                    }
00248 
00249                                    break;
00250 
00251                               case CONFIG_TYPE_INTEGER:
00252                                    value = (int) strtol(p, &q, 0);
00253                                    if (*q != '\0')
00254                                    {
00255                                           conf_error = CONF_ILLEGAL;
00256                                           err = 1;
00257                                    }
00258 
00259                                    str = p;
00260 
00261                                    break;
00262 
00263                               default:
00264                                    assert(0);
00265                                    /* NOTREACHED */
00266                                    return NULL;
00267                             }
00268                      }
00269               }
00270               else
00271               {
00272                      continue;                   /* blank line */
00273               }
00274 
00275               /* a parse error, or only one argument, is no good */
00276               if (err)
00277               {
00278                      config_free(cur);
00279 
00280                      if (line != NULL)
00281                             *line = myline;
00282                      if (outpath != NULL)
00283                             strlcpy(outpath, file, outpathlen);
00284 
00285                      if (in != stdin)
00286                             fclose(in);
00287 
00288                      return NULL;
00289               }
00290 
00291               if (def[n].cd_type != CONFIG_TYPE_INCLUDE)
00292               {
00293                      new = (struct config *) malloc(sizeof(struct config));
00294                      if (new == NULL)
00295                      {
00296                             config_free(cur);
00297 
00298                             conf_error = CONF_NMEMORY;
00299 
00300                             if (line != NULL)
00301                                    *line = myline;
00302                             if (outpath != NULL)
00303                                    strlcpy(outpath, file, outpathlen);
00304 
00305                             if (in != stdin)
00306                                    fclose(in);
00307 
00308                             return NULL;
00309                      }
00310 
00311                      new->cfg_next = cur;
00312                      new->cfg_name = def[n].cd_name;
00313                      new->cfg_type = def[n].cd_type;
00314               }
00315 
00316               switch (def[n].cd_type)
00317               {
00318                 case CONFIG_TYPE_INCLUDE:
00319                 {
00320                      struct config *incl;
00321 
00322                      incl = config_load_level(str, def, line, outpath,
00323                                               outpathlen, level + 1);
00324                      if (incl == NULL)
00325                      {
00326                             if (in != stdin)
00327                                    fclose(in);
00328 
00329                             return NULL;
00330                      }
00331 
00332                      config_attach(incl, &cur);
00333                      new = incl;
00334 
00335                      break;
00336                 }
00337 
00338                 case CONFIG_TYPE_STRING:
00339                      new->cfg_string = strdup(str);
00340                      break;
00341 
00342                 case CONFIG_TYPE_BOOLEAN:
00343                      new->cfg_bool = (_Bool) value;
00344                      break;
00345 
00346                 case CONFIG_TYPE_INTEGER:
00347                      new->cfg_int = value;
00348                      break;
00349 
00350                 default:
00351                      assert(0);
00352               }
00353 
00354               cur = new;
00355        }
00356 
00357        conf_error = CONF_SUCCESS;
00358 
00359        if (in != stdin)
00360               fclose(in);
00361 
00362        if (myline == 0)
00363        {
00364               cur = (struct config *) malloc(sizeof *cur);
00365               if (cur != NULL)
00366               {
00367                      cur->cfg_bool = FALSE;
00368                      cur->cfg_type = CONFIG_TYPE_STRING;
00369                      cur->cfg_int = 0;
00370                      cur->cfg_name = "";
00371                      cur->cfg_string = "";
00372                      cur->cfg_next = NULL;
00373 
00374                      return cur;
00375               }
00376               else
00377               {
00378                      conf_error = CONF_NMEMORY;
00379 
00380                      if (line != NULL)
00381                             *line = myline;
00382                      if (outpath != NULL)
00383                             strlcpy(outpath, file, outpathlen);
00384 
00385                      return NULL;
00386               }
00387        }
00388        else
00389        {
00390               return cur;
00391        }
00392 }
00393 
00394 /*
00395 **  CONFIG_ERROR -- return a string describing a configuration error
00396 **
00397 **  Parameters:
00398 **     None.
00399 **
00400 **  Return value:
00401 **     Pointer to a NULL-terminated string explaining the last error.
00402 */
00403 
00404 char *
00405 config_error(void)
00406 {
00407        switch (conf_error)
00408        {
00409          case CONF_SUCCESS:
00410               return "no error";
00411 
00412          case CONF_MISSING:
00413               return "required value missing";
00414 
00415          case CONF_UNRECOG:
00416               return "unrecognized parameter";
00417 
00418          case CONF_ILLEGAL:
00419               return "illegal value";
00420 
00421          case CONF_NESTING:
00422               return "nesting too deep";
00423 
00424          case CONF_READING:
00425               return "error reading configuration file";
00426 
00427          case CONF_NMEMORY:
00428               return "memory allocation failure";
00429 
00430          case CONF_UNKNOWN:
00431          default:
00432               return "unknown error";
00433        }
00434 
00435        /* NOTREACHED */
00436 }
00437 
00438 /*
00439 **  CONFIG_FREE -- release memory associated with a config list
00440 **
00441 **  Parameters:
00442 **     head -- head of the config list
00443 **
00444 **  Return value:
00445 **     None.
00446 */
00447 
00448 void
00449 config_free(struct config *head)
00450 {
00451        struct config *next;
00452        struct config *cur;
00453 
00454        cur = head;
00455        while (cur != NULL)
00456        {
00457               next = cur->cfg_next;
00458               if (cur->cfg_type == CONFIG_TYPE_STRING)
00459                      free(cur->cfg_string);
00460               free(cur);
00461               cur = next;
00462        }
00463 }
00464 
00465 /*
00466 **  CONFIG_LOAD -- load configuration from a file
00467 **
00468 **  Parameters:
00469 **     file -- path from which to load; NULL or "-" implies stdin
00470 **     cd -- array of (struct configdef) elements containing the
00471 **           configuration syntax to assert
00472 **     line -- line number where an error occurred (updated)
00473 **     path -- configuration file in which error occurred (updated)
00474 **     pathlen -- number of bytes available at "path"
00475 **
00476 **  Return value:
00477 **     Pointer to a (struct config) which is the head of a list of
00478 **     loaded configuration items, or NULL on error; if NULL, "line" is
00479 **     updated to indicate which line number contained the error and,
00480 **     if the configuration file being parsed was not the one referenced
00481 **     by "in", then "path" will be updated to point to the filename
00482 **     that was being processed.
00483 */
00484 
00485 struct config *
00486 config_load(char *file, struct configdef *def, unsigned int *line,
00487             char *path, size_t pathlen)
00488 {
00489        conf_error = CONF_UNKNOWN;
00490 
00491        return config_load_level(file, def, line, path, pathlen, 0);
00492 }
00493 
00494 /*
00495 **  CONFIG_CHECK -- verify that stuff marked "required" is present
00496 **
00497 **  Parameters:
00498 **     head -- head of config list
00499 **     def -- definitions
00500 **
00501 **  Return value:
00502 **     Name of the first parameter in "def" that was marked "required"
00503 **     yet absent from the configuration parsed, or NULL if nothing
00504 **     required was missing.
00505 */
00506 
00507 char *
00508 config_check(struct config *head, struct configdef *def)
00509 {
00510        int n;
00511        struct config *cur;
00512 
00513        assert(head != NULL);
00514        assert(def != NULL);
00515 
00516        conf_error = CONF_UNKNOWN;
00517 
00518        for (n = 0; ; n++)
00519        {
00520               if (def[n].cd_name == NULL)
00521               {
00522                      conf_error = CONF_SUCCESS;
00523                      return NULL;
00524               }
00525               if (!def[n].cd_req)
00526                      continue;
00527 
00528               for (cur = head; cur != NULL; cur = cur->cfg_next)
00529               {
00530                      if (cur->cfg_name == def[n].cd_name)
00531                             break;
00532               }
00533 
00534               if (cur == NULL)
00535               {
00536                      conf_error = CONF_MISSING;
00537 
00538                      return def[n].cd_name;
00539               }
00540        }
00541 
00542        /* NOTREACHED */
00543 }
00544 
00545 /*
00546 **  CONFIG_GET -- retrieve a parameter's value
00547 **
00548 **  Parameter:
00549 **     head -- head of config list
00550 **     name -- name of the parameter of interest
00551 **     value -- where to write the result (returned)
00552 **     size -- bytes available at "value"
00553 **
00554 **  Return value:
00555 **     1 if the data was found, 0 otherwise, -1 if the request was illegal
00556 **
00557 **  Notes:
00558 **     "value" is a (void *).  It can be used directly, such as:
00559 **
00560 **            int x;
00561 **
00562 **            (void) config_get(conflist, "MyInteger", (void *) &x);
00563 */
00564 
00565 int
00566 config_get(struct config *head, const char *name, void *value, size_t size)
00567 {
00568        struct config *cur;
00569 
00570        assert(head != NULL);
00571        assert(name != NULL);
00572        assert(value != NULL);
00573        assert(size > 0);
00574 
00575        conf_error = CONF_UNKNOWN;
00576 
00577        for (cur = head; cur != NULL; cur = cur->cfg_next)
00578        {
00579               if (strcasecmp(cur->cfg_name, name) == 0)
00580               {
00581                      switch (cur->cfg_type)
00582                      {
00583                        case CONFIG_TYPE_BOOLEAN:
00584                             if (size != sizeof(_Bool))
00585                             {
00586                                    conf_error = CONF_ILLEGAL;
00587                                    return -1;
00588                             }
00589                             memcpy(value, &cur->cfg_bool, size);
00590                             break;
00591 
00592                        case CONFIG_TYPE_INTEGER:
00593                             if (size != sizeof(int))
00594                             {
00595                                    conf_error = CONF_ILLEGAL;
00596                                    return -1;
00597                             }
00598                             memcpy(value, &cur->cfg_int, size);
00599                             break;
00600 
00601                        case CONFIG_TYPE_INCLUDE:
00602                             conf_error = CONF_ILLEGAL;
00603                             return -1;
00604 
00605                        default:
00606                             if (size != sizeof(char *))
00607                             {
00608                                    conf_error = CONF_ILLEGAL;
00609                                    return -1;
00610                             }
00611                             memcpy(value, &cur->cfg_string, size);
00612                             break;
00613                      }
00614 
00615                      return 1;
00616               }
00617        }
00618 
00619        conf_error = CONF_SUCCESS;
00620 
00621        return 0;
00622 }
00623 
00624 /*
00625 **  CONFIG_VALIDNAME -- return True IFF the name provided was valid
00626 **
00627 **  Parameters:
00628 **     def -- configuration definition
00629 **     name -- name of value of interest
00630 **
00631 **  Return value:
00632 **     True IFF "name" was defined inside "cd"
00633 */
00634 
00635 _Bool
00636 config_validname(struct configdef *def, const char *name)
00637 {
00638        unsigned int n;
00639 
00640        assert(def != NULL);
00641        assert(name != NULL);
00642 
00643        for (n = 0; ; n++)
00644        {
00645               if (def[n].cd_name == NULL)
00646                      return FALSE;
00647 
00648               if (strcasecmp(name, def[n].cd_name) == 0)
00649                      return TRUE;
00650        }
00651 
00652        assert(0);
00653        /* NOTREACHED */
00654 }
00655 
00656 /*
00657 **  CONFIG_DUMP -- dump configuration contents
00658 **
00659 **  Parameters:
00660 **     cfg -- head of assembled configuration values
00661 **     out -- stream to which to write
00662 **     name -- name of value of interest
00663 **
00664 **  Return value:
00665 **     Number of items that matched.
00666 */
00667 
00668 unsigned int
00669 config_dump(struct config *cfg, FILE *out, const char *name)
00670 {
00671        unsigned int nprinted = 0;
00672        struct config *cur;
00673 
00674        assert(cfg != NULL);
00675        assert(out != NULL);
00676 
00677        for (cur = cfg; cur != NULL; cur = cur->cfg_next)
00678        {
00679               if (name != NULL)
00680               {
00681                      if (strcasecmp(name, cur->cfg_name) != 0)
00682                             continue;
00683               }
00684               else
00685               {
00686                      fprintf(out, "%p: \"%s\" ", cur, cur->cfg_name);
00687               }
00688 
00689               switch (cur->cfg_type)
00690               {
00691                 case CONFIG_TYPE_STRING:
00692                      fprintf(out, "%s\n", cur->cfg_string);
00693                      break;
00694 
00695                 case CONFIG_TYPE_INTEGER:
00696                      fprintf(out, "%d\n", cur->cfg_int);
00697                      break;
00698 
00699                 case CONFIG_TYPE_BOOLEAN:
00700                      fprintf(out, "%s\n", cur->cfg_bool ? "True" : "False");
00701                      break;
00702 
00703                 default:
00704                      assert(0);
00705               }
00706 
00707               nprinted++;
00708        }
00709 
00710        return nprinted;
00711 }