Home | History | Annotate | Download | only in bus
      1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
      2 /* config-parser-trivial.c  XML-library-agnostic configuration file parser
      3  *                  Does not do includes or anything remotely complicated.
      4  *
      5  * Copyright (C) 2003, 2004, 2007 Red Hat, Inc.
      6  *
      7  * Licensed under the Academic Free License version 2.1
      8  *
      9  * This program is free software; you can redistribute it and/or modify
     10  * it under the terms of the GNU General Public License as published by
     11  * the Free Software Foundation; either version 2 of the License, or
     12  * (at your option) any later version.
     13  *
     14  * This program is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  * GNU General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU General Public License
     20  * along with this program; if not, write to the Free Software
     21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     22  *
     23  */
     24 
     25 #include <config.h>
     26 #include "config-parser-common.h"
     27 #include "config-parser-trivial.h"
     28 #include "utils.h"
     29 #include <dbus/dbus-list.h>
     30 #include <dbus/dbus-internals.h>
     31 #include <string.h>
     32 
     33 /**
     34  * TRIVIAL parser for bus configuration file.
     35  */
     36 struct BusConfigParser
     37 {
     38   ElementType type;
     39   DBusString user;                  /**< User the dbus-daemon runs as */
     40   DBusString bus_type;              /**< Message bus type */
     41   DBusString service_helper;        /**< Location of the setuid helper */
     42   DBusList *service_dirs;           /**< Directories to look for services in */
     43 };
     44 
     45 static dbus_bool_t
     46 service_dirs_find_dir (DBusList **service_dirs,
     47                        const char *dir)
     48 {
     49   DBusList *link;
     50 
     51   _dbus_assert (dir != NULL);
     52 
     53   for (link = *service_dirs; link; link = _dbus_list_get_next_link(service_dirs, link))
     54     {
     55       const char *link_dir;
     56 
     57       link_dir = (const char *)link->data;
     58       if (strcmp (dir, link_dir) == 0)
     59         return TRUE;
     60     }
     61 
     62   return FALSE;
     63 }
     64 
     65 static void
     66 service_dirs_append_link_unique_or_free (DBusList **service_dirs,
     67                                          DBusList *dir_link)
     68 {
     69   if (!service_dirs_find_dir (service_dirs, dir_link->data))
     70     {
     71       _dbus_list_append_link (service_dirs, dir_link);
     72     }
     73   else
     74     {
     75       dbus_free (dir_link->data);
     76       _dbus_list_free_link (dir_link);
     77     }
     78 }
     79 
     80 BusConfigParser*
     81 bus_config_parser_new (const DBusString             *basedir,
     82                        dbus_bool_t                   is_toplevel,
     83                        const BusConfigParser        *parent)
     84 {
     85   BusConfigParser *parser;
     86 
     87   parser = dbus_new0 (BusConfigParser, 1);
     88   if (parser == NULL)
     89     goto failed;
     90 
     91   parser->type = ELEMENT_NONE;
     92 
     93   /* init the lists */
     94   parser->service_dirs = NULL;
     95 
     96   /* init the strings */
     97   if (!_dbus_string_init (&parser->user))
     98     goto failed_parser;
     99   if (!_dbus_string_init (&parser->bus_type))
    100     goto failed_type;
    101   if (!_dbus_string_init (&parser->service_helper))
    102     goto failed_helper;
    103 
    104   /* woot! */
    105   return parser;
    106 
    107 /* argh. we have do do this carefully because of OOM */
    108 failed_helper:
    109   _dbus_string_free (&parser->bus_type);
    110 failed_type:
    111   _dbus_string_free (&parser->user);
    112 failed_parser:
    113   dbus_free (parser);
    114 failed:
    115   return NULL;
    116 }
    117 
    118 void
    119 bus_config_parser_unref (BusConfigParser *parser)
    120 {
    121   _dbus_string_free (&parser->user);
    122   _dbus_string_free (&parser->service_helper);
    123   _dbus_string_free (&parser->bus_type);
    124 
    125   _dbus_list_foreach (&parser->service_dirs,
    126                       (DBusForeachFunction) dbus_free,
    127                       NULL);
    128 
    129   _dbus_list_clear (&parser->service_dirs);
    130 
    131   dbus_free (parser);
    132 }
    133 
    134 dbus_bool_t
    135 bus_config_parser_check_doctype (BusConfigParser   *parser,
    136                                  const char        *doctype,
    137                                  DBusError         *error)
    138 {
    139   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    140 
    141   if (strcmp (doctype, "busconfig") != 0)
    142     {
    143       dbus_set_error (error,
    144                       DBUS_ERROR_FAILED,
    145                       "Configuration file has the wrong document type %s",
    146                       doctype);
    147       return FALSE;
    148     }
    149   else
    150     return TRUE;
    151 }
    152 
    153 dbus_bool_t
    154 bus_config_parser_start_element (BusConfigParser   *parser,
    155                                  const char        *element_name,
    156                                  const char       **attribute_names,
    157                                  const char       **attribute_values,
    158                                  DBusError         *error)
    159 {
    160   /* we don't do processing of attribute names, we don't need to */
    161   parser->type = bus_config_parser_element_name_to_type (element_name);
    162 
    163   switch (parser->type)
    164     {
    165     case ELEMENT_SERVICEHELPER:
    166     case ELEMENT_USER:
    167     case ELEMENT_CONFIGTYPE:
    168       /* content about to be handled */
    169       break;
    170 
    171     case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
    172       {
    173         DBusList *link;
    174         DBusList *dirs;
    175         dirs = NULL;
    176 
    177         if (!_dbus_get_standard_system_servicedirs (&dirs))
    178           {
    179             BUS_SET_OOM (error);
    180             return FALSE;
    181           }
    182 
    183           while ((link = _dbus_list_pop_first_link (&dirs)))
    184             service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
    185         break;
    186       }
    187 
    188     default:
    189       {
    190         /* we really don't care about the others... */
    191         _dbus_verbose (" START We don't care about '%s' type '%i'\n", element_name, parser->type);
    192         break;
    193       }
    194     }
    195   return TRUE;
    196 }
    197 
    198 dbus_bool_t
    199 bus_config_parser_end_element (BusConfigParser   *parser,
    200                                const char               *element_name,
    201                                DBusError                *error)
    202 {
    203   /* we don't care */
    204   return TRUE;
    205 }
    206 
    207 dbus_bool_t
    208 bus_config_parser_content (BusConfigParser   *parser,
    209                            const DBusString  *content,
    210                            DBusError         *error)
    211 {
    212   DBusString content_sane;
    213   dbus_bool_t retval;
    214 
    215   retval = FALSE;
    216 
    217   if (!_dbus_string_init (&content_sane))
    218     {
    219       BUS_SET_OOM (error);
    220       goto out;
    221     }
    222   if (!_dbus_string_copy (content, 0, &content_sane, 0))
    223     {
    224       BUS_SET_OOM (error);
    225       goto out_content;
    226     }
    227 
    228   /* rip out white space */
    229   _dbus_string_chop_white (&content_sane);
    230   if (_dbus_string_get_length (&content_sane) == 0)
    231     {
    232       /* optimise, there is no content */
    233       retval = TRUE;
    234       goto out_content;
    235     }
    236 
    237   switch (parser->type)
    238     {
    239     case ELEMENT_SERVICEDIR:
    240       {
    241       	char *cpath;
    242 
    243         /* copy the sane data into a char array */
    244         if (!_dbus_string_copy_data(&content_sane, &cpath))
    245           {
    246             BUS_SET_OOM (error);
    247             goto out_content;
    248           }
    249 
    250         /* append the dynamic char string to service dirs */
    251         if (!_dbus_list_append (&parser->service_dirs, cpath))
    252           {
    253             dbus_free (cpath);
    254             BUS_SET_OOM (error);
    255             goto out_content;
    256           }
    257       }
    258       break;
    259 
    260     case ELEMENT_SERVICEHELPER:
    261       {
    262         if (!_dbus_string_copy (&content_sane, 0, &parser->service_helper, 0))
    263           {
    264             BUS_SET_OOM (error);
    265             goto out_content;
    266           }
    267       }
    268       break;
    269 
    270     case ELEMENT_USER:
    271       {
    272         if (!_dbus_string_copy (&content_sane, 0, &parser->user, 0))
    273           {
    274             BUS_SET_OOM (error);
    275             goto out_content;
    276           }
    277       }
    278       break;
    279 
    280     case ELEMENT_CONFIGTYPE:
    281       {
    282         if (!_dbus_string_copy (&content_sane, 0, &parser->bus_type, 0))
    283           {
    284             BUS_SET_OOM (error);
    285             goto out_content;
    286           }
    287       }
    288       break;
    289     default:
    290       {
    291         /* we don't care about the others... really */
    292         _dbus_verbose (" CONTENTS We don't care about '%s' type '%i'\n", _dbus_string_get_const_data (&content_sane), parser->type);
    293         break;
    294       }
    295     }
    296 
    297   /* woot! */
    298   retval = TRUE;
    299 
    300 out_content:
    301   _dbus_string_free (&content_sane);
    302 out:
    303   return retval;
    304 }
    305 
    306 dbus_bool_t
    307 bus_config_parser_finished (BusConfigParser   *parser,
    308                             DBusError         *error)
    309 {
    310   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    311   _dbus_verbose ("finished scanning!\n");
    312   return TRUE;
    313 }
    314 
    315 const char*
    316 bus_config_parser_get_user (BusConfigParser *parser)
    317 {
    318   return _dbus_string_get_const_data (&parser->user);
    319 }
    320 
    321 const char*
    322 bus_config_parser_get_type (BusConfigParser *parser)
    323 {
    324   return _dbus_string_get_const_data (&parser->bus_type);
    325 }
    326 
    327 DBusList**
    328 bus_config_parser_get_service_dirs (BusConfigParser *parser)
    329 {
    330   return &parser->service_dirs;
    331 }
    332 
    333 #ifdef DBUS_BUILD_TESTS
    334 #include <stdio.h>
    335 #include "test.h"
    336 
    337 typedef enum
    338 {
    339   VALID,
    340   INVALID,
    341   UNKNOWN
    342 } Validity;
    343 
    344 static dbus_bool_t
    345 check_return_values (const DBusString *full_path)
    346 {
    347   BusConfigParser *parser;
    348   DBusError error;
    349   dbus_bool_t retval;
    350   const char *user;
    351   const char *type;
    352   DBusList **dirs;
    353 
    354   dbus_error_init (&error);
    355   retval = FALSE;
    356 
    357   printf ("Testing values from: %s\n", _dbus_string_get_const_data (full_path));
    358 
    359   parser = bus_config_load (full_path, TRUE, NULL, &error);
    360   if (parser == NULL)
    361     {
    362       _DBUS_ASSERT_ERROR_IS_SET (&error);
    363       if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
    364         _dbus_verbose ("Failed to load valid file due to OOM\n");
    365       goto finish;
    366     }
    367   _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
    368 
    369   /* check user return value is okay */
    370   user = bus_config_parser_get_user (parser);
    371   if (user == NULL)
    372     {
    373       _dbus_warn ("User was NULL!\n");
    374       goto finish;
    375     }
    376 #if 0
    377   /* the username can be configured in configure.in so this test doesn't work */
    378   if (strcmp (user, "dbus") != 0)
    379     {
    380       _dbus_warn ("User was invalid; '%s'!\n", user);
    381       goto finish;
    382     }
    383   printf ("    <user>dbus</user> OKAY!\n");
    384 #endif
    385 
    386   /* check type return value is okay */
    387   type = bus_config_parser_get_type (parser);
    388   if (type == NULL)
    389     {
    390       _dbus_warn ("Type was NULL!\n");
    391       goto finish;
    392     }
    393   if (strcmp (type, "system") != 0)
    394     {
    395       _dbus_warn ("Type was invalid; '%s'!\n", user);
    396       goto finish;
    397     }
    398   printf ("    <type>system</type> OKAY!\n");
    399 
    400   /* check dirs return value is okay */
    401   dirs = bus_config_parser_get_service_dirs (parser);
    402   if (dirs == NULL)
    403     {
    404       _dbus_warn ("Service dirs are NULL!\n");
    405       goto finish;
    406     }
    407   printf ("    <standard_system_service_dirs/> OKAY!\n");
    408   /* NOTE: We tested the specific return values in the config-parser tests */
    409 
    410   /* woohoo! */
    411   retval = TRUE;
    412 finish:
    413   if (parser != NULL)
    414     bus_config_parser_unref (parser);
    415   dbus_error_free (&error);
    416   return retval;
    417 }
    418 
    419 static dbus_bool_t
    420 do_load (const DBusString *full_path,
    421          Validity          validity,
    422          dbus_bool_t       oom_possible)
    423 {
    424   BusConfigParser *parser;
    425   DBusError error;
    426 
    427   dbus_error_init (&error);
    428 
    429   parser = bus_config_load (full_path, TRUE, NULL, &error);
    430   if (parser == NULL)
    431     {
    432       _DBUS_ASSERT_ERROR_IS_SET (&error);
    433 
    434       if (oom_possible &&
    435           dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
    436         {
    437           _dbus_verbose ("Failed to load valid file due to OOM\n");
    438           dbus_error_free (&error);
    439           return TRUE;
    440         }
    441       else if (validity == VALID)
    442         {
    443           _dbus_warn ("Failed to load valid file but still had memory: %s\n",
    444                       error.message);
    445 
    446           dbus_error_free (&error);
    447           return FALSE;
    448         }
    449       else
    450         {
    451           dbus_error_free (&error);
    452           return TRUE;
    453         }
    454     }
    455   else
    456     {
    457       _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
    458 
    459       bus_config_parser_unref (parser);
    460 
    461       if (validity == INVALID)
    462         {
    463           _dbus_warn ("Accepted invalid file\n");
    464           return FALSE;
    465         }
    466 
    467       return TRUE;
    468     }
    469 }
    470 
    471 typedef struct
    472 {
    473   const DBusString *full_path;
    474   Validity          validity;
    475 } LoaderOomData;
    476 
    477 static dbus_bool_t
    478 check_loader_oom_func (void *data)
    479 {
    480   LoaderOomData *d = data;
    481 
    482   return do_load (d->full_path, d->validity, TRUE);
    483 }
    484 
    485 static dbus_bool_t
    486 process_test_valid_subdir (const DBusString *test_base_dir,
    487                            const char       *subdir,
    488                            Validity          validity)
    489 {
    490   DBusString test_directory;
    491   DBusString filename;
    492   DBusDirIter *dir;
    493   dbus_bool_t retval;
    494   DBusError error;
    495 
    496   retval = FALSE;
    497   dir = NULL;
    498 
    499   if (!_dbus_string_init (&test_directory))
    500     _dbus_assert_not_reached ("didn't allocate test_directory\n");
    501 
    502   _dbus_string_init_const (&filename, subdir);
    503 
    504   if (!_dbus_string_copy (test_base_dir, 0,
    505                           &test_directory, 0))
    506     _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
    507 
    508   if (!_dbus_concat_dir_and_file (&test_directory, &filename))
    509     _dbus_assert_not_reached ("couldn't allocate full path");
    510 
    511   _dbus_string_free (&filename);
    512   if (!_dbus_string_init (&filename))
    513     _dbus_assert_not_reached ("didn't allocate filename string\n");
    514 
    515   dbus_error_init (&error);
    516   dir = _dbus_directory_open (&test_directory, &error);
    517   if (dir == NULL)
    518     {
    519       _dbus_warn ("Could not open %s: %s\n",
    520                   _dbus_string_get_const_data (&test_directory),
    521                   error.message);
    522       dbus_error_free (&error);
    523       goto failed;
    524     }
    525 
    526   if (validity == VALID)
    527     printf ("Testing valid files:\n");
    528   else if (validity == INVALID)
    529     printf ("Testing invalid files:\n");
    530   else
    531     printf ("Testing unknown files:\n");
    532 
    533  next:
    534   while (_dbus_directory_get_next_file (dir, &filename, &error))
    535     {
    536       DBusString full_path;
    537       LoaderOomData d;
    538 
    539       if (!_dbus_string_init (&full_path))
    540         _dbus_assert_not_reached ("couldn't init string");
    541 
    542       if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
    543         _dbus_assert_not_reached ("couldn't copy dir to full_path");
    544 
    545       if (!_dbus_concat_dir_and_file (&full_path, &filename))
    546         _dbus_assert_not_reached ("couldn't concat file to dir");
    547 
    548       if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
    549         {
    550           _dbus_verbose ("Skipping non-.conf file %s\n",
    551                          _dbus_string_get_const_data (&filename));
    552           _dbus_string_free (&full_path);
    553           goto next;
    554         }
    555 
    556       printf ("    %s\n", _dbus_string_get_const_data (&filename));
    557 
    558       _dbus_verbose (" expecting %s\n",
    559                      validity == VALID ? "valid" :
    560                      (validity == INVALID ? "invalid" :
    561                       (validity == UNKNOWN ? "unknown" : "???")));
    562 
    563       d.full_path = &full_path;
    564       d.validity = validity;
    565 
    566       /* FIXME hackaround for an expat problem, see
    567        * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=124747
    568        * http://freedesktop.org/pipermail/dbus/2004-May/001153.html
    569        */
    570       /* if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d)) */
    571       if (!check_loader_oom_func (&d))
    572         _dbus_assert_not_reached ("test failed");
    573 
    574       _dbus_string_free (&full_path);
    575     }
    576 
    577   if (dbus_error_is_set (&error))
    578     {
    579       _dbus_warn ("Could not get next file in %s: %s\n",
    580                   _dbus_string_get_const_data (&test_directory),
    581                   error.message);
    582       dbus_error_free (&error);
    583       goto failed;
    584     }
    585 
    586   retval = TRUE;
    587 
    588  failed:
    589 
    590   if (dir)
    591     _dbus_directory_close (dir);
    592   _dbus_string_free (&test_directory);
    593   _dbus_string_free (&filename);
    594 
    595   return retval;
    596 }
    597 
    598 /* convenience function, do not reuse outside of TEST */
    599 static dbus_bool_t
    600 make_full_path (const DBusString *test_data_dir,
    601 			          const char       *subdir,
    602 			          const char       *file,
    603 			          DBusString       *full_path)
    604 {
    605   DBusString filename;
    606   dbus_bool_t retval;
    607 
    608   retval = FALSE;
    609 
    610   if (!_dbus_string_init (full_path))
    611     {
    612       _dbus_warn ("couldn't allocate full path");
    613       goto finish;
    614     }
    615 
    616   if (!_dbus_string_copy (test_data_dir, 0, full_path, 0))
    617     {
    618       _dbus_warn ("couldn't allocate full path");
    619       goto finish;
    620     }
    621 
    622   _dbus_string_init_const (&filename, subdir);
    623   if (!_dbus_concat_dir_and_file (full_path, &filename))
    624     {
    625       _dbus_warn ("couldn't allocate full path");
    626       goto finish;
    627     }
    628   _dbus_string_free (&filename);
    629 
    630   _dbus_string_init_const (&filename, file);
    631   if (!_dbus_concat_dir_and_file (full_path, &filename))
    632     {
    633       _dbus_warn ("couldn't allocate full path");
    634       goto finish;
    635     }
    636 
    637   /* woot! */
    638   retval = TRUE;
    639 
    640 finish:
    641   _dbus_string_free (&filename);
    642   return retval;
    643 }
    644 
    645 static dbus_bool_t
    646 check_file_valid (DBusString *full_path,
    647 			            Validity    validity)
    648 {
    649   dbus_bool_t retval;
    650 
    651   if (validity == VALID)
    652     printf ("Testing valid file:\n");
    653   else if (validity == INVALID)
    654     printf ("Testing invalid file:\n");
    655   else
    656     printf ("Testing unknown file:\n");
    657 
    658   /* print the filename, just so we match the other output */
    659   printf ("    %s\n", _dbus_string_get_const_data (full_path));
    660 
    661   /* only test one file */
    662   retval = do_load (full_path, validity, TRUE);
    663 
    664   return retval;
    665 }
    666 
    667 dbus_bool_t
    668 bus_config_parser_trivial_test (const DBusString *test_data_dir)
    669 {
    670   DBusString full_path;
    671   dbus_bool_t retval;
    672 
    673   retval = FALSE;
    674 
    675   if (test_data_dir == NULL ||
    676       _dbus_string_get_length (test_data_dir) == 0)
    677     {
    678       printf ("No test data\n");
    679       return TRUE;
    680     }
    681 
    682   /* We already test default_session_servicedirs and default_system_servicedirs
    683    * in bus_config_parser_test() */
    684   if (!process_test_valid_subdir (test_data_dir, "valid-config-files", VALID))
    685     goto finish;
    686 
    687   /* we don't process all the invalid files, as the trivial parser can't hope
    688    * to validate them all for all different syntaxes. We just check one broken
    689    * file to see if junk is received */
    690   if (!make_full_path (test_data_dir, "invalid-config-files", "not-well-formed.conf", &full_path))
    691     goto finish;
    692   if (!check_file_valid (&full_path, INVALID))
    693     goto finish;
    694   _dbus_string_free (&full_path);
    695 
    696   /* just test if the check_file_valid works okay and we got sane values */
    697   if (!make_full_path (test_data_dir, "valid-config-files", "system.conf", &full_path))
    698     goto finish;
    699   if (!check_file_valid (&full_path, VALID))
    700     goto finish;
    701   /* check to see if we got the correct values from the parser */
    702   if (!check_return_values (&full_path))
    703     goto finish;
    704 
    705   /* woot! */
    706   retval = TRUE;
    707 
    708 finish:
    709   _dbus_string_free (&full_path);
    710 
    711   /* we don't process equiv-config-files as we don't handle <include> */
    712   return retval;
    713 }
    714 
    715 #endif /* DBUS_BUILD_TESTS */
    716 
    717