D-Bus
1.6.8
|
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 00024 #include <config.h> 00025 #include "dbus-internals.h" 00026 #include "dbus-protocol.h" 00027 #include "dbus-marshal-basic.h" 00028 #include "dbus-test.h" 00029 #include "dbus-valgrind-internal.h" 00030 #include <stdio.h> 00031 #include <stdarg.h> 00032 #include <string.h> 00033 #include <stdlib.h> 00034 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING 00035 #include <windows.h> 00036 #include <mbstring.h> 00037 #endif 00038 00199 const char *_dbus_no_memory_message = "Not enough memory"; 00200 00201 static dbus_bool_t warn_initted = FALSE; 00202 static dbus_bool_t fatal_warnings = FALSE; 00203 static dbus_bool_t fatal_warnings_on_check_failed = TRUE; 00204 00205 static void 00206 init_warnings(void) 00207 { 00208 if (!warn_initted) 00209 { 00210 const char *s; 00211 s = _dbus_getenv ("DBUS_FATAL_WARNINGS"); 00212 if (s && *s) 00213 { 00214 if (*s == '0') 00215 { 00216 fatal_warnings = FALSE; 00217 fatal_warnings_on_check_failed = FALSE; 00218 } 00219 else if (*s == '1') 00220 { 00221 fatal_warnings = TRUE; 00222 fatal_warnings_on_check_failed = TRUE; 00223 } 00224 else 00225 { 00226 fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'", 00227 s); 00228 } 00229 } 00230 00231 warn_initted = TRUE; 00232 } 00233 } 00234 00244 void 00245 _dbus_warn (const char *format, 00246 ...) 00247 { 00248 va_list args; 00249 00250 if (!warn_initted) 00251 init_warnings (); 00252 00253 va_start (args, format); 00254 vfprintf (stderr, format, args); 00255 va_end (args); 00256 00257 if (fatal_warnings) 00258 { 00259 fflush (stderr); 00260 _dbus_abort (); 00261 } 00262 } 00263 00272 void 00273 _dbus_warn_check_failed(const char *format, 00274 ...) 00275 { 00276 va_list args; 00277 00278 if (!warn_initted) 00279 init_warnings (); 00280 00281 fprintf (stderr, "process %lu: ", _dbus_pid_for_log ()); 00282 00283 va_start (args, format); 00284 vfprintf (stderr, format, args); 00285 va_end (args); 00286 00287 if (fatal_warnings_on_check_failed) 00288 { 00289 fflush (stderr); 00290 _dbus_abort (); 00291 } 00292 } 00293 00294 #ifdef DBUS_ENABLE_VERBOSE_MODE 00295 00296 static dbus_bool_t verbose_initted = FALSE; 00297 static dbus_bool_t verbose = TRUE; 00298 00300 #define PTHREAD_IN_VERBOSE 0 00301 #if PTHREAD_IN_VERBOSE 00302 #include <pthread.h> 00303 #endif 00304 00305 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING 00306 static char module_name[1024]; 00307 #endif 00308 00309 static inline void 00310 _dbus_verbose_init (void) 00311 { 00312 if (!verbose_initted) 00313 { 00314 const char *p = _dbus_getenv ("DBUS_VERBOSE"); 00315 verbose = p != NULL && *p == '1'; 00316 verbose_initted = TRUE; 00317 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING 00318 { 00319 char *last_period, *last_slash; 00320 GetModuleFileName(0,module_name,sizeof(module_name)-1); 00321 last_period = _mbsrchr(module_name,'.'); 00322 if (last_period) 00323 *last_period ='\0'; 00324 last_slash = _mbsrchr(module_name,'\\'); 00325 if (last_slash) 00326 strcpy(module_name,last_slash+1); 00327 strcat(module_name,": "); 00328 } 00329 #endif 00330 } 00331 } 00332 00338 #ifdef DBUS_WIN 00339 #define DBUS_IS_DIR_SEPARATOR(c) (c == '\\' || c == '/') 00340 #else 00341 #define DBUS_IS_DIR_SEPARATOR(c) (c == '/') 00342 #endif 00343 00348 static char *_dbus_file_path_extract_elements_from_tail(const char *file,int level) 00349 { 00350 static int prefix = -1; 00351 00352 if (prefix == -1) 00353 { 00354 char *p = (char *)file + strlen(file); 00355 int i = 0; 00356 prefix = 0; 00357 for (;p >= file;p--) 00358 { 00359 if (DBUS_IS_DIR_SEPARATOR(*p)) 00360 { 00361 if (++i >= level) 00362 { 00363 prefix = p-file+1; 00364 break; 00365 } 00366 } 00367 } 00368 } 00369 return (char *)file+prefix; 00370 } 00371 00377 dbus_bool_t 00378 _dbus_is_verbose_real (void) 00379 { 00380 _dbus_verbose_init (); 00381 return verbose; 00382 } 00383 00392 void 00393 _dbus_verbose_real ( 00394 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS 00395 const char *file, 00396 const int line, 00397 const char *function, 00398 #endif 00399 const char *format, 00400 ...) 00401 { 00402 va_list args; 00403 static dbus_bool_t need_pid = TRUE; 00404 int len; 00405 00406 /* things are written a bit oddly here so that 00407 * in the non-verbose case we just have the one 00408 * conditional and return immediately. 00409 */ 00410 if (!_dbus_is_verbose_real()) 00411 return; 00412 00413 #ifndef DBUS_USE_OUTPUT_DEBUG_STRING 00414 /* Print out pid before the line */ 00415 if (need_pid) 00416 { 00417 #if PTHREAD_IN_VERBOSE 00418 fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ()); 00419 #else 00420 fprintf (stderr, "%lu: ", _dbus_pid_for_log ()); 00421 #endif 00422 } 00423 #endif 00424 00425 /* Only print pid again if the next line is a new line */ 00426 len = strlen (format); 00427 if (format[len-1] == '\n') 00428 need_pid = TRUE; 00429 else 00430 need_pid = FALSE; 00431 00432 va_start (args, format); 00433 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING 00434 { 00435 char buf[1024]; 00436 strcpy(buf,module_name); 00437 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS 00438 sprintf (buf+strlen(buf), "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function); 00439 #endif 00440 vsprintf (buf+strlen(buf),format, args); 00441 va_end (args); 00442 OutputDebugStringA(buf); 00443 } 00444 #else 00445 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS 00446 fprintf (stderr, "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function); 00447 #endif 00448 00449 vfprintf (stderr, format, args); 00450 va_end (args); 00451 00452 fflush (stderr); 00453 #endif 00454 } 00455 00462 void 00463 _dbus_verbose_reset_real (void) 00464 { 00465 verbose_initted = FALSE; 00466 } 00467 00468 void 00469 _dbus_trace_ref (const char *obj_name, 00470 void *obj, 00471 int old_refcount, 00472 int new_refcount, 00473 const char *why, 00474 const char *env_var, 00475 int *enabled) 00476 { 00477 _dbus_assert (obj_name != NULL); 00478 _dbus_assert (obj != NULL); 00479 _dbus_assert (old_refcount >= -1); 00480 _dbus_assert (new_refcount >= -1); 00481 00482 if (old_refcount == -1) 00483 { 00484 _dbus_assert (new_refcount == -1); 00485 } 00486 else 00487 { 00488 _dbus_assert (new_refcount >= 0); 00489 _dbus_assert (old_refcount >= 0); 00490 _dbus_assert (old_refcount > 0 || new_refcount > 0); 00491 } 00492 00493 _dbus_assert (why != NULL); 00494 _dbus_assert (env_var != NULL); 00495 _dbus_assert (enabled != NULL); 00496 00497 if (*enabled < 0) 00498 { 00499 const char *s = _dbus_getenv (env_var); 00500 00501 *enabled = FALSE; 00502 00503 if (s && *s) 00504 { 00505 if (*s == '0') 00506 *enabled = FALSE; 00507 else if (*s == '1') 00508 *enabled = TRUE; 00509 else 00510 _dbus_warn ("%s should be 0 or 1 if set, not '%s'", env_var, s); 00511 } 00512 } 00513 00514 if (*enabled) 00515 { 00516 if (old_refcount == -1) 00517 { 00518 VALGRIND_PRINTF_BACKTRACE ("%s %p ref stolen (%s)", 00519 obj_name, obj, why); 00520 _dbus_verbose ("%s %p ref stolen (%s)", 00521 obj_name, obj, why); 00522 } 00523 else 00524 { 00525 VALGRIND_PRINTF_BACKTRACE ("%s %p %d -> %d refs (%s)", 00526 obj_name, obj, 00527 old_refcount, new_refcount, why); 00528 _dbus_verbose ("%s %p %d -> %d refs (%s)", 00529 obj_name, obj, old_refcount, new_refcount, why); 00530 } 00531 } 00532 } 00533 00534 #endif /* DBUS_ENABLE_VERBOSE_MODE */ 00535 00544 char* 00545 _dbus_strdup (const char *str) 00546 { 00547 size_t len; 00548 char *copy; 00549 00550 if (str == NULL) 00551 return NULL; 00552 00553 len = strlen (str); 00554 00555 copy = dbus_malloc (len + 1); 00556 if (copy == NULL) 00557 return NULL; 00558 00559 memcpy (copy, str, len + 1); 00560 00561 return copy; 00562 } 00563 00572 void* 00573 _dbus_memdup (const void *mem, 00574 size_t n_bytes) 00575 { 00576 void *copy; 00577 00578 copy = dbus_malloc (n_bytes); 00579 if (copy == NULL) 00580 return NULL; 00581 00582 memcpy (copy, mem, n_bytes); 00583 00584 return copy; 00585 } 00586 00595 char** 00596 _dbus_dup_string_array (const char **array) 00597 { 00598 int len; 00599 int i; 00600 char **copy; 00601 00602 if (array == NULL) 00603 return NULL; 00604 00605 for (len = 0; array[len] != NULL; ++len) 00606 ; 00607 00608 copy = dbus_new0 (char*, len + 1); 00609 if (copy == NULL) 00610 return NULL; 00611 00612 i = 0; 00613 while (i < len) 00614 { 00615 copy[i] = _dbus_strdup (array[i]); 00616 if (copy[i] == NULL) 00617 { 00618 dbus_free_string_array (copy); 00619 return NULL; 00620 } 00621 00622 ++i; 00623 } 00624 00625 return copy; 00626 } 00627 00635 dbus_bool_t 00636 _dbus_string_array_contains (const char **array, 00637 const char *str) 00638 { 00639 int i; 00640 00641 i = 0; 00642 while (array[i] != NULL) 00643 { 00644 if (strcmp (array[i], str) == 0) 00645 return TRUE; 00646 ++i; 00647 } 00648 00649 return FALSE; 00650 } 00651 00658 void 00659 _dbus_generate_uuid (DBusGUID *uuid) 00660 { 00661 long now; 00662 00663 /* don't use monotonic time because the UUID may be saved to disk, e.g. 00664 * it may persist across reboots 00665 */ 00666 _dbus_get_real_time (&now, NULL); 00667 00668 uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now); 00669 00670 _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4); 00671 } 00672 00680 dbus_bool_t 00681 _dbus_uuid_encode (const DBusGUID *uuid, 00682 DBusString *encoded) 00683 { 00684 DBusString binary; 00685 _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES); 00686 return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded)); 00687 } 00688 00689 static dbus_bool_t 00690 _dbus_read_uuid_file_without_creating (const DBusString *filename, 00691 DBusGUID *uuid, 00692 DBusError *error) 00693 { 00694 DBusString contents; 00695 DBusString decoded; 00696 int end; 00697 00698 if (!_dbus_string_init (&contents)) 00699 { 00700 _DBUS_SET_OOM (error); 00701 return FALSE; 00702 } 00703 00704 if (!_dbus_string_init (&decoded)) 00705 { 00706 _dbus_string_free (&contents); 00707 _DBUS_SET_OOM (error); 00708 return FALSE; 00709 } 00710 00711 if (!_dbus_file_get_contents (&contents, filename, error)) 00712 goto error; 00713 00714 _dbus_string_chop_white (&contents); 00715 00716 if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX) 00717 { 00718 dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, 00719 "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text", 00720 _dbus_string_get_const_data (filename), 00721 DBUS_UUID_LENGTH_HEX, 00722 _dbus_string_get_length (&contents)); 00723 goto error; 00724 } 00725 00726 if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0)) 00727 { 00728 _DBUS_SET_OOM (error); 00729 goto error; 00730 } 00731 00732 if (end == 0) 00733 { 00734 dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, 00735 "UUID file '%s' contains invalid hex data", 00736 _dbus_string_get_const_data (filename)); 00737 goto error; 00738 } 00739 00740 if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES) 00741 { 00742 dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, 00743 "UUID file '%s' contains %d bytes of hex-encoded data instead of %d", 00744 _dbus_string_get_const_data (filename), 00745 _dbus_string_get_length (&decoded), 00746 DBUS_UUID_LENGTH_BYTES); 00747 goto error; 00748 } 00749 00750 _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES); 00751 00752 _dbus_string_free (&decoded); 00753 _dbus_string_free (&contents); 00754 00755 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00756 00757 return TRUE; 00758 00759 error: 00760 _DBUS_ASSERT_ERROR_IS_SET (error); 00761 _dbus_string_free (&contents); 00762 _dbus_string_free (&decoded); 00763 return FALSE; 00764 } 00765 00766 static dbus_bool_t 00767 _dbus_create_uuid_file_exclusively (const DBusString *filename, 00768 DBusGUID *uuid, 00769 DBusError *error) 00770 { 00771 DBusString encoded; 00772 00773 if (!_dbus_string_init (&encoded)) 00774 { 00775 _DBUS_SET_OOM (error); 00776 return FALSE; 00777 } 00778 00779 _dbus_generate_uuid (uuid); 00780 00781 if (!_dbus_uuid_encode (uuid, &encoded)) 00782 { 00783 _DBUS_SET_OOM (error); 00784 goto error; 00785 } 00786 00787 if (!_dbus_string_append_byte (&encoded, '\n')) 00788 { 00789 _DBUS_SET_OOM (error); 00790 goto error; 00791 } 00792 00793 if (!_dbus_string_save_to_file (&encoded, filename, TRUE, error)) 00794 goto error; 00795 00796 _dbus_string_free (&encoded); 00797 00798 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00799 return TRUE; 00800 00801 error: 00802 _DBUS_ASSERT_ERROR_IS_SET (error); 00803 _dbus_string_free (&encoded); 00804 return FALSE; 00805 } 00806 00817 dbus_bool_t 00818 _dbus_read_uuid_file (const DBusString *filename, 00819 DBusGUID *uuid, 00820 dbus_bool_t create_if_not_found, 00821 DBusError *error) 00822 { 00823 DBusError read_error = DBUS_ERROR_INIT; 00824 00825 if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error)) 00826 return TRUE; 00827 00828 if (!create_if_not_found) 00829 { 00830 dbus_move_error (&read_error, error); 00831 return FALSE; 00832 } 00833 00834 /* If the file exists and contains junk, we want to keep that error 00835 * message instead of overwriting it with a "file exists" error 00836 * message when we try to write 00837 */ 00838 if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT)) 00839 { 00840 dbus_move_error (&read_error, error); 00841 return FALSE; 00842 } 00843 else 00844 { 00845 dbus_error_free (&read_error); 00846 return _dbus_create_uuid_file_exclusively (filename, uuid, error); 00847 } 00848 } 00849 00850 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid); 00851 static int machine_uuid_initialized_generation = 0; 00852 static DBusGUID machine_uuid; 00853 00864 dbus_bool_t 00865 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str) 00866 { 00867 dbus_bool_t ok; 00868 00869 _DBUS_LOCK (machine_uuid); 00870 if (machine_uuid_initialized_generation != _dbus_current_generation) 00871 { 00872 DBusError error = DBUS_ERROR_INIT; 00873 00874 if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE, 00875 &error)) 00876 { 00877 #ifndef DBUS_BUILD_TESTS 00878 /* For the test suite, we may not be installed so just continue silently 00879 * here. But in a production build, we want to be nice and loud about 00880 * this. 00881 */ 00882 _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n" 00883 "See the manual page for dbus-uuidgen to correct this issue.\n", 00884 error.message); 00885 #endif 00886 00887 dbus_error_free (&error); 00888 00889 _dbus_generate_uuid (&machine_uuid); 00890 } 00891 } 00892 00893 ok = _dbus_uuid_encode (&machine_uuid, uuid_str); 00894 00895 _DBUS_UNLOCK (machine_uuid); 00896 00897 return ok; 00898 } 00899 00900 #ifndef DBUS_DISABLE_CHECKS 00901 00902 const char *_dbus_return_if_fail_warning_format = 00903 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n" 00904 "This is normally a bug in some application using the D-Bus library.\n"; 00905 #endif 00906 00907 #ifndef DBUS_DISABLE_ASSERT 00908 00920 void 00921 _dbus_real_assert (dbus_bool_t condition, 00922 const char *condition_text, 00923 const char *file, 00924 int line, 00925 const char *func) 00926 { 00927 if (_DBUS_UNLIKELY (!condition)) 00928 { 00929 _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n", 00930 _dbus_pid_for_log (), condition_text, file, line, func); 00931 _dbus_abort (); 00932 } 00933 } 00934 00945 void 00946 _dbus_real_assert_not_reached (const char *explanation, 00947 const char *file, 00948 int line) 00949 { 00950 _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n", 00951 file, line, _dbus_pid_for_log (), explanation); 00952 _dbus_abort (); 00953 } 00954 #endif /* DBUS_DISABLE_ASSERT */ 00955 00956 #ifdef DBUS_BUILD_TESTS 00957 static dbus_bool_t 00958 run_failing_each_malloc (int n_mallocs, 00959 const char *description, 00960 DBusTestMemoryFunction func, 00961 void *data) 00962 { 00963 n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */ 00964 00965 while (n_mallocs >= 0) 00966 { 00967 _dbus_set_fail_alloc_counter (n_mallocs); 00968 00969 _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n", 00970 description, n_mallocs, 00971 _dbus_get_fail_alloc_failures ()); 00972 00973 if (!(* func) (data)) 00974 return FALSE; 00975 00976 n_mallocs -= 1; 00977 } 00978 00979 _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); 00980 00981 return TRUE; 00982 } 00983 00997 dbus_bool_t 00998 _dbus_test_oom_handling (const char *description, 00999 DBusTestMemoryFunction func, 01000 void *data) 01001 { 01002 int approx_mallocs; 01003 const char *setting; 01004 int max_failures_to_try; 01005 int i; 01006 01007 /* Run once to see about how many mallocs are involved */ 01008 01009 _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); 01010 01011 _dbus_verbose ("Running once to count mallocs\n"); 01012 01013 if (!(* func) (data)) 01014 return FALSE; 01015 01016 approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter (); 01017 01018 _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n", 01019 description, approx_mallocs); 01020 01021 setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES"); 01022 if (setting != NULL) 01023 { 01024 DBusString str; 01025 long v; 01026 _dbus_string_init_const (&str, setting); 01027 v = 4; 01028 if (!_dbus_string_parse_int (&str, 0, &v, NULL)) 01029 _dbus_warn ("couldn't parse '%s' as integer\n", setting); 01030 max_failures_to_try = v; 01031 } 01032 else 01033 { 01034 max_failures_to_try = 4; 01035 } 01036 01037 i = setting ? max_failures_to_try - 1 : 1; 01038 while (i < max_failures_to_try) 01039 { 01040 _dbus_set_fail_alloc_failures (i); 01041 if (!run_failing_each_malloc (approx_mallocs, description, func, data)) 01042 return FALSE; 01043 ++i; 01044 } 01045 01046 _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n", 01047 description); 01048 01049 return TRUE; 01050 } 01051 #endif /* DBUS_BUILD_TESTS */ 01052