D-Bus 1.2.24
|
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 00002 /* dbus-internals.c random utility stuff (internal to D-Bus implementation) 00003 * 00004 * Copyright (C) 2002, 2003 Red Hat, Inc. 00005 * 00006 * Licensed under the Academic Free License version 2.1 00007 * 00008 * This program is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2 of the License, or 00011 * (at your option) any later version. 00012 * 00013 * This program is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with this program; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 * 00022 */ 00023 #include "dbus-internals.h" 00024 #include "dbus-protocol.h" 00025 #include "dbus-marshal-basic.h" 00026 #include "dbus-test.h" 00027 #include <stdio.h> 00028 #include <stdarg.h> 00029 #include <string.h> 00030 #include <stdlib.h> 00031 00192 const char _dbus_no_memory_message[] = "Not enough memory"; 00193 00194 static dbus_bool_t warn_initted = FALSE; 00195 static dbus_bool_t fatal_warnings = FALSE; 00196 static dbus_bool_t fatal_warnings_on_check_failed = TRUE; 00197 00198 static void 00199 init_warnings(void) 00200 { 00201 if (!warn_initted) 00202 { 00203 const char *s; 00204 s = _dbus_getenv ("DBUS_FATAL_WARNINGS"); 00205 if (s && *s) 00206 { 00207 if (*s == '0') 00208 { 00209 fatal_warnings = FALSE; 00210 fatal_warnings_on_check_failed = FALSE; 00211 } 00212 else if (*s == '1') 00213 { 00214 fatal_warnings = TRUE; 00215 fatal_warnings_on_check_failed = TRUE; 00216 } 00217 else 00218 { 00219 fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'", 00220 s); 00221 } 00222 } 00223 00224 warn_initted = TRUE; 00225 } 00226 } 00227 00237 void 00238 _dbus_warn (const char *format, 00239 ...) 00240 { 00241 va_list args; 00242 00243 if (!warn_initted) 00244 init_warnings (); 00245 00246 va_start (args, format); 00247 vfprintf (stderr, format, args); 00248 va_end (args); 00249 00250 if (fatal_warnings) 00251 { 00252 fflush (stderr); 00253 _dbus_abort (); 00254 } 00255 } 00256 00265 void 00266 _dbus_warn_check_failed(const char *format, 00267 ...) 00268 { 00269 va_list args; 00270 00271 if (!warn_initted) 00272 init_warnings (); 00273 00274 fprintf (stderr, "process %lu: ", _dbus_pid_for_log ()); 00275 00276 va_start (args, format); 00277 vfprintf (stderr, format, args); 00278 va_end (args); 00279 00280 if (fatal_warnings_on_check_failed) 00281 { 00282 fflush (stderr); 00283 _dbus_abort (); 00284 } 00285 } 00286 00287 #ifdef DBUS_ENABLE_VERBOSE_MODE 00288 00289 static dbus_bool_t verbose_initted = FALSE; 00290 static dbus_bool_t verbose = TRUE; 00291 00293 #define PTHREAD_IN_VERBOSE 0 00294 #if PTHREAD_IN_VERBOSE 00295 #include <pthread.h> 00296 #endif 00297 00298 #ifdef DBUS_WIN 00299 #define inline 00300 #endif 00301 00302 static inline void 00303 _dbus_verbose_init (void) 00304 { 00305 if (!verbose_initted) 00306 { 00307 const char *p = _dbus_getenv ("DBUS_VERBOSE"); 00308 verbose = p != NULL && *p == '1'; 00309 verbose_initted = TRUE; 00310 } 00311 } 00312 00318 dbus_bool_t 00319 _dbus_is_verbose_real (void) 00320 { 00321 _dbus_verbose_init (); 00322 return verbose; 00323 } 00324 00333 void 00334 _dbus_verbose_real (const char *format, 00335 ...) 00336 { 00337 va_list args; 00338 static dbus_bool_t need_pid = TRUE; 00339 int len; 00340 00341 /* things are written a bit oddly here so that 00342 * in the non-verbose case we just have the one 00343 * conditional and return immediately. 00344 */ 00345 if (!_dbus_is_verbose_real()) 00346 return; 00347 00348 /* Print out pid before the line */ 00349 if (need_pid) 00350 { 00351 #if PTHREAD_IN_VERBOSE 00352 fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ()); 00353 #else 00354 fprintf (stderr, "%lu: ", _dbus_pid_for_log ()); 00355 #endif 00356 } 00357 00358 00359 /* Only print pid again if the next line is a new line */ 00360 len = strlen (format); 00361 if (format[len-1] == '\n') 00362 need_pid = TRUE; 00363 else 00364 need_pid = FALSE; 00365 00366 va_start (args, format); 00367 vfprintf (stderr, format, args); 00368 va_end (args); 00369 00370 fflush (stderr); 00371 } 00372 00379 void 00380 _dbus_verbose_reset_real (void) 00381 { 00382 verbose_initted = FALSE; 00383 } 00384 00385 #endif /* DBUS_ENABLE_VERBOSE_MODE */ 00386 00395 char* 00396 _dbus_strdup (const char *str) 00397 { 00398 size_t len; 00399 char *copy; 00400 00401 if (str == NULL) 00402 return NULL; 00403 00404 len = strlen (str); 00405 00406 copy = dbus_malloc (len + 1); 00407 if (copy == NULL) 00408 return NULL; 00409 00410 memcpy (copy, str, len + 1); 00411 00412 return copy; 00413 } 00414 00423 void* 00424 _dbus_memdup (const void *mem, 00425 size_t n_bytes) 00426 { 00427 void *copy; 00428 00429 copy = dbus_malloc (n_bytes); 00430 if (copy == NULL) 00431 return NULL; 00432 00433 memcpy (copy, mem, n_bytes); 00434 00435 return copy; 00436 } 00437 00446 char** 00447 _dbus_dup_string_array (const char **array) 00448 { 00449 int len; 00450 int i; 00451 char **copy; 00452 00453 if (array == NULL) 00454 return NULL; 00455 00456 for (len = 0; array[len] != NULL; ++len) 00457 ; 00458 00459 copy = dbus_new0 (char*, len + 1); 00460 if (copy == NULL) 00461 return NULL; 00462 00463 i = 0; 00464 while (i < len) 00465 { 00466 copy[i] = _dbus_strdup (array[i]); 00467 if (copy[i] == NULL) 00468 { 00469 dbus_free_string_array (copy); 00470 return NULL; 00471 } 00472 00473 ++i; 00474 } 00475 00476 return copy; 00477 } 00478 00486 dbus_bool_t 00487 _dbus_string_array_contains (const char **array, 00488 const char *str) 00489 { 00490 int i; 00491 00492 i = 0; 00493 while (array[i] != NULL) 00494 { 00495 if (strcmp (array[i], str) == 0) 00496 return TRUE; 00497 ++i; 00498 } 00499 00500 return FALSE; 00501 } 00502 00509 void 00510 _dbus_generate_uuid (DBusGUID *uuid) 00511 { 00512 long now; 00513 00514 _dbus_get_current_time (&now, NULL); 00515 00516 uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now); 00517 00518 _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4); 00519 } 00520 00528 dbus_bool_t 00529 _dbus_uuid_encode (const DBusGUID *uuid, 00530 DBusString *encoded) 00531 { 00532 DBusString binary; 00533 _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES); 00534 return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded)); 00535 } 00536 00537 static dbus_bool_t 00538 _dbus_read_uuid_file_without_creating (const DBusString *filename, 00539 DBusGUID *uuid, 00540 DBusError *error) 00541 { 00542 DBusString contents; 00543 DBusString decoded; 00544 int end; 00545 00546 if (!_dbus_string_init (&contents)) 00547 { 00548 _DBUS_SET_OOM (error); 00549 return FALSE; 00550 } 00551 00552 if (!_dbus_string_init (&decoded)) 00553 { 00554 _dbus_string_free (&contents); 00555 _DBUS_SET_OOM (error); 00556 return FALSE; 00557 } 00558 00559 if (!_dbus_file_get_contents (&contents, filename, error)) 00560 goto error; 00561 00562 _dbus_string_chop_white (&contents); 00563 00564 if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX) 00565 { 00566 dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, 00567 "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text", 00568 _dbus_string_get_const_data (filename), 00569 DBUS_UUID_LENGTH_HEX, 00570 _dbus_string_get_length (&contents)); 00571 goto error; 00572 } 00573 00574 if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0)) 00575 { 00576 _DBUS_SET_OOM (error); 00577 goto error; 00578 } 00579 00580 if (end == 0) 00581 { 00582 dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, 00583 "UUID file '%s' contains invalid hex data", 00584 _dbus_string_get_const_data (filename)); 00585 goto error; 00586 } 00587 00588 if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES) 00589 { 00590 dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, 00591 "UUID file '%s' contains %d bytes of hex-encoded data instead of %d", 00592 _dbus_string_get_const_data (filename), 00593 _dbus_string_get_length (&decoded), 00594 DBUS_UUID_LENGTH_BYTES); 00595 goto error; 00596 } 00597 00598 _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES); 00599 00600 _dbus_string_free (&decoded); 00601 _dbus_string_free (&contents); 00602 00603 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00604 00605 return TRUE; 00606 00607 error: 00608 _DBUS_ASSERT_ERROR_IS_SET (error); 00609 _dbus_string_free (&contents); 00610 _dbus_string_free (&decoded); 00611 return FALSE; 00612 } 00613 00614 static dbus_bool_t 00615 _dbus_create_uuid_file_exclusively (const DBusString *filename, 00616 DBusGUID *uuid, 00617 DBusError *error) 00618 { 00619 DBusString encoded; 00620 00621 if (!_dbus_string_init (&encoded)) 00622 { 00623 _DBUS_SET_OOM (error); 00624 return FALSE; 00625 } 00626 00627 _dbus_generate_uuid (uuid); 00628 00629 if (!_dbus_uuid_encode (uuid, &encoded)) 00630 { 00631 _DBUS_SET_OOM (error); 00632 goto error; 00633 } 00634 00635 /* FIXME this is racy; we need a save_file_exclusively 00636 * function. But in practice this should be fine for now. 00637 * 00638 * - first be sure we can create the file and it 00639 * doesn't exist by creating it empty with O_EXCL 00640 * - then create it by creating a temporary file and 00641 * overwriting atomically with rename() 00642 */ 00643 if (!_dbus_create_file_exclusively (filename, error)) 00644 goto error; 00645 00646 if (!_dbus_string_append_byte (&encoded, '\n')) 00647 { 00648 _DBUS_SET_OOM (error); 00649 goto error; 00650 } 00651 00652 if (!_dbus_string_save_to_file (&encoded, filename, error)) 00653 goto error; 00654 00655 if (!_dbus_make_file_world_readable (filename, error)) 00656 goto error; 00657 00658 _dbus_string_free (&encoded); 00659 00660 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00661 return TRUE; 00662 00663 error: 00664 _DBUS_ASSERT_ERROR_IS_SET (error); 00665 _dbus_string_free (&encoded); 00666 return FALSE; 00667 } 00668 00679 dbus_bool_t 00680 _dbus_read_uuid_file (const DBusString *filename, 00681 DBusGUID *uuid, 00682 dbus_bool_t create_if_not_found, 00683 DBusError *error) 00684 { 00685 DBusError read_error = DBUS_ERROR_INIT; 00686 00687 if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error)) 00688 return TRUE; 00689 00690 if (!create_if_not_found) 00691 { 00692 dbus_move_error (&read_error, error); 00693 return FALSE; 00694 } 00695 00696 /* If the file exists and contains junk, we want to keep that error 00697 * message instead of overwriting it with a "file exists" error 00698 * message when we try to write 00699 */ 00700 if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT)) 00701 { 00702 dbus_move_error (&read_error, error); 00703 return FALSE; 00704 } 00705 else 00706 { 00707 dbus_error_free (&read_error); 00708 return _dbus_create_uuid_file_exclusively (filename, uuid, error); 00709 } 00710 } 00711 00712 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid); 00713 static int machine_uuid_initialized_generation = 0; 00714 static DBusGUID machine_uuid; 00715 00726 dbus_bool_t 00727 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str) 00728 { 00729 dbus_bool_t ok; 00730 00731 _DBUS_LOCK (machine_uuid); 00732 if (machine_uuid_initialized_generation != _dbus_current_generation) 00733 { 00734 DBusError error = DBUS_ERROR_INIT; 00735 00736 if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE, 00737 &error)) 00738 { 00739 #ifndef DBUS_BUILD_TESTS 00740 /* For the test suite, we may not be installed so just continue silently 00741 * here. But in a production build, we want to be nice and loud about 00742 * this. 00743 */ 00744 _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n" 00745 "See the manual page for dbus-uuidgen to correct this issue.\n", 00746 error.message); 00747 #endif 00748 00749 dbus_error_free (&error); 00750 00751 _dbus_generate_uuid (&machine_uuid); 00752 } 00753 } 00754 00755 ok = _dbus_uuid_encode (&machine_uuid, uuid_str); 00756 00757 _DBUS_UNLOCK (machine_uuid); 00758 00759 return ok; 00760 } 00761 00762 #ifdef DBUS_BUILD_TESTS 00763 00769 const char * 00770 _dbus_header_field_to_string (int header_field) 00771 { 00772 switch (header_field) 00773 { 00774 case DBUS_HEADER_FIELD_INVALID: 00775 return "invalid"; 00776 case DBUS_HEADER_FIELD_PATH: 00777 return "path"; 00778 case DBUS_HEADER_FIELD_INTERFACE: 00779 return "interface"; 00780 case DBUS_HEADER_FIELD_MEMBER: 00781 return "member"; 00782 case DBUS_HEADER_FIELD_ERROR_NAME: 00783 return "error-name"; 00784 case DBUS_HEADER_FIELD_REPLY_SERIAL: 00785 return "reply-serial"; 00786 case DBUS_HEADER_FIELD_DESTINATION: 00787 return "destination"; 00788 case DBUS_HEADER_FIELD_SENDER: 00789 return "sender"; 00790 case DBUS_HEADER_FIELD_SIGNATURE: 00791 return "signature"; 00792 default: 00793 return "unknown"; 00794 } 00795 } 00796 #endif /* DBUS_BUILD_TESTS */ 00797 00798 #ifndef DBUS_DISABLE_CHECKS 00799 00800 const char _dbus_return_if_fail_warning_format[] = 00801 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n" 00802 "This is normally a bug in some application using the D-Bus library.\n"; 00803 #endif 00804 00805 #ifndef DBUS_DISABLE_ASSERT 00806 00818 void 00819 _dbus_real_assert (dbus_bool_t condition, 00820 const char *condition_text, 00821 const char *file, 00822 int line, 00823 const char *func) 00824 { 00825 if (_DBUS_UNLIKELY (!condition)) 00826 { 00827 _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n", 00828 _dbus_pid_for_log (), condition_text, file, line, func); 00829 _dbus_abort (); 00830 } 00831 } 00832 00843 void 00844 _dbus_real_assert_not_reached (const char *explanation, 00845 const char *file, 00846 int line) 00847 { 00848 _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n", 00849 file, line, _dbus_pid_for_log (), explanation); 00850 _dbus_abort (); 00851 } 00852 #endif /* DBUS_DISABLE_ASSERT */ 00853 00854 #ifdef DBUS_BUILD_TESTS 00855 static dbus_bool_t 00856 run_failing_each_malloc (int n_mallocs, 00857 const char *description, 00858 DBusTestMemoryFunction func, 00859 void *data) 00860 { 00861 n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */ 00862 00863 while (n_mallocs >= 0) 00864 { 00865 _dbus_set_fail_alloc_counter (n_mallocs); 00866 00867 _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n", 00868 description, n_mallocs, 00869 _dbus_get_fail_alloc_failures ()); 00870 00871 if (!(* func) (data)) 00872 return FALSE; 00873 00874 n_mallocs -= 1; 00875 } 00876 00877 _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); 00878 00879 return TRUE; 00880 } 00881 00895 dbus_bool_t 00896 _dbus_test_oom_handling (const char *description, 00897 DBusTestMemoryFunction func, 00898 void *data) 00899 { 00900 int approx_mallocs; 00901 const char *setting; 00902 int max_failures_to_try; 00903 int i; 00904 00905 /* Run once to see about how many mallocs are involved */ 00906 00907 _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); 00908 00909 _dbus_verbose ("Running once to count mallocs\n"); 00910 00911 if (!(* func) (data)) 00912 return FALSE; 00913 00914 approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter (); 00915 00916 _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n", 00917 description, approx_mallocs); 00918 00919 setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES"); 00920 if (setting != NULL) 00921 { 00922 DBusString str; 00923 long v; 00924 _dbus_string_init_const (&str, setting); 00925 v = 4; 00926 if (!_dbus_string_parse_int (&str, 0, &v, NULL)) 00927 _dbus_warn ("couldn't parse '%s' as integer\n", setting); 00928 max_failures_to_try = v; 00929 } 00930 else 00931 { 00932 max_failures_to_try = 4; 00933 } 00934 00935 i = setting ? max_failures_to_try - 1 : 1; 00936 while (i < max_failures_to_try) 00937 { 00938 _dbus_set_fail_alloc_failures (i); 00939 if (!run_failing_each_malloc (approx_mallocs, description, func, data)) 00940 return FALSE; 00941 ++i; 00942 } 00943 00944 _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n", 00945 description); 00946 00947 return TRUE; 00948 } 00949 #endif /* DBUS_BUILD_TESTS */ 00950