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