patch: bus: Fix timeout restarts
[platform/upstream/dbus.git] / bus / config-parser-trivial.c
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_ENABLE_EMBEDDED_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 #ifndef DBUS_WIN
688   /* We already test default_session_servicedirs and default_system_servicedirs
689    * in bus_config_parser_test() */
690   if (!process_test_valid_subdir (test_data_dir, "valid-config-files-system", VALID))
691     goto finish;
692 #endif
693
694   /* we don't process all the invalid files, as the trivial parser can't hope
695    * to validate them all for all different syntaxes. We just check one broken
696    * file to see if junk is received */
697   if (!make_full_path (test_data_dir, "invalid-config-files", "not-well-formed.conf", &full_path))
698     goto finish;
699   if (!check_file_valid (&full_path, INVALID))
700     goto finish;
701   _dbus_string_free (&full_path);
702
703 #ifndef DBUS_WIN
704   /* just test if the check_file_valid works okay and we got sane values */
705   if (!make_full_path (test_data_dir, "valid-config-files-system", "system.conf", &full_path))
706     goto finish;
707   if (!check_file_valid (&full_path, VALID))
708     goto finish;
709   /* check to see if we got the correct values from the parser */
710   if (!check_return_values (&full_path))
711     goto finish;
712 #endif
713
714   /* woot! */
715   retval = TRUE;
716
717 finish:
718   _dbus_string_free (&full_path);
719
720   /* we don't process equiv-config-files as we don't handle <include> */
721   return retval;
722 }
723
724 #endif /* DBUS_ENABLE_EMBEDDED_TESTS */
725