|
D-Bus
1.8.20
|
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 00184 const char *_dbus_no_memory_message = "Not enough memory"; 00185 00186 static dbus_bool_t warn_initted = FALSE; 00187 static dbus_bool_t fatal_warnings = FALSE; 00188 static dbus_bool_t fatal_warnings_on_check_failed = TRUE; 00189 00190 static void 00191 init_warnings(void) 00192 { 00193 if (!warn_initted) 00194 { 00195 const char *s; 00196 s = _dbus_getenv ("DBUS_FATAL_WARNINGS"); 00197 if (s && *s) 00198 { 00199 if (*s == '0') 00200 { 00201 fatal_warnings = FALSE; 00202 fatal_warnings_on_check_failed = FALSE; 00203 } 00204 else if (*s == '1') 00205 { 00206 fatal_warnings = TRUE; 00207 fatal_warnings_on_check_failed = TRUE; 00208 } 00209 else 00210 { 00211 fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'", 00212 s); 00213 } 00214 } 00215 00216 warn_initted = TRUE; 00217 } 00218 } 00219 00229 void 00230 _dbus_warn (const char *format, 00231 ...) 00232 { 00233 va_list args; 00234 00235 if (!warn_initted) 00236 init_warnings (); 00237 00238 va_start (args, format); 00239 vfprintf (stderr, format, args); 00240 va_end (args); 00241 00242 if (fatal_warnings) 00243 { 00244 fflush (stderr); 00245 _dbus_abort (); 00246 } 00247 } 00248 00257 void 00258 _dbus_warn_check_failed(const char *format, 00259 ...) 00260 { 00261 va_list args; 00262 00263 if (!warn_initted) 00264 init_warnings (); 00265 00266 fprintf (stderr, "process %lu: ", _dbus_pid_for_log ()); 00267 00268 va_start (args, format); 00269 vfprintf (stderr, format, args); 00270 va_end (args); 00271 00272 if (fatal_warnings_on_check_failed) 00273 { 00274 fflush (stderr); 00275 _dbus_abort (); 00276 } 00277 } 00278 00279 #ifdef DBUS_ENABLE_VERBOSE_MODE 00280 00281 static dbus_bool_t verbose_initted = FALSE; 00282 static dbus_bool_t verbose = TRUE; 00283 00285 #define PTHREAD_IN_VERBOSE 0 00286 #if PTHREAD_IN_VERBOSE 00287 #include <pthread.h> 00288 #endif 00289 00290 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING 00291 static char module_name[1024]; 00292 #endif 00293 00294 static inline void 00295 _dbus_verbose_init (void) 00296 { 00297 if (!verbose_initted) 00298 { 00299 const char *p = _dbus_getenv ("DBUS_VERBOSE"); 00300 verbose = p != NULL && *p == '1'; 00301 verbose_initted = TRUE; 00302 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING 00303 { 00304 char *last_period, *last_slash; 00305 GetModuleFileName(0,module_name,sizeof(module_name)-1); 00306 last_period = _mbsrchr(module_name,'.'); 00307 if (last_period) 00308 *last_period ='\0'; 00309 last_slash = _mbsrchr(module_name,'\\'); 00310 if (last_slash) 00311 strcpy(module_name,last_slash+1); 00312 strcat(module_name,": "); 00313 } 00314 #endif 00315 } 00316 } 00317 00323 #ifdef DBUS_WIN 00324 #define DBUS_IS_DIR_SEPARATOR(c) (c == '\\' || c == '/') 00325 #else 00326 #define DBUS_IS_DIR_SEPARATOR(c) (c == '/') 00327 #endif 00328 00333 static char *_dbus_file_path_extract_elements_from_tail(const char *file,int level) 00334 { 00335 int prefix = 0; 00336 char *p = (char *)file + strlen(file); 00337 int i = 0; 00338 00339 for (;p >= file;p--) 00340 { 00341 if (DBUS_IS_DIR_SEPARATOR(*p)) 00342 { 00343 if (++i >= level) 00344 { 00345 prefix = p-file+1; 00346 break; 00347 } 00348 } 00349 } 00350 00351 return (char *)file+prefix; 00352 } 00353 00359 dbus_bool_t 00360 _dbus_is_verbose_real (void) 00361 { 00362 _dbus_verbose_init (); 00363 return verbose; 00364 } 00365 00374 void 00375 _dbus_verbose_real ( 00376 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS 00377 const char *file, 00378 const int line, 00379 const char *function, 00380 #endif 00381 const char *format, 00382 ...) 00383 { 00384 va_list args; 00385 static dbus_bool_t need_pid = TRUE; 00386 int len; 00387 00388 /* things are written a bit oddly here so that 00389 * in the non-verbose case we just have the one 00390 * conditional and return immediately. 00391 */ 00392 if (!_dbus_is_verbose_real()) 00393 return; 00394 00395 #ifndef DBUS_USE_OUTPUT_DEBUG_STRING 00396 /* Print out pid before the line */ 00397 if (need_pid) 00398 { 00399 #if PTHREAD_IN_VERBOSE 00400 fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ()); 00401 #else 00402 fprintf (stderr, "%lu: ", _dbus_pid_for_log ()); 00403 #endif 00404 } 00405 #endif 00406 00407 /* Only print pid again if the next line is a new line */ 00408 len = strlen (format); 00409 if (format[len-1] == '\n') 00410 need_pid = TRUE; 00411 else 00412 need_pid = FALSE; 00413 00414 va_start (args, format); 00415 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING 00416 { 00417 char buf[1024]; 00418 strcpy(buf,module_name); 00419 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS 00420 sprintf (buf+strlen(buf), "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function); 00421 #endif 00422 vsprintf (buf+strlen(buf),format, args); 00423 va_end (args); 00424 OutputDebugStringA(buf); 00425 } 00426 #else 00427 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS 00428 fprintf (stderr, "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function); 00429 #endif 00430 00431 vfprintf (stderr, format, args); 00432 va_end (args); 00433 00434 fflush (stderr); 00435 #endif 00436 } 00437 00444 void 00445 _dbus_verbose_reset_real (void) 00446 { 00447 verbose_initted = FALSE; 00448 } 00449 00450 void 00451 _dbus_trace_ref (const char *obj_name, 00452 void *obj, 00453 int old_refcount, 00454 int new_refcount, 00455 const char *why, 00456 const char *env_var, 00457 int *enabled) 00458 { 00459 _dbus_assert (obj_name != NULL); 00460 _dbus_assert (obj != NULL); 00461 _dbus_assert (old_refcount >= -1); 00462 _dbus_assert (new_refcount >= -1); 00463 00464 if (old_refcount == -1) 00465 { 00466 _dbus_assert (new_refcount == -1); 00467 } 00468 else 00469 { 00470 _dbus_assert (new_refcount >= 0); 00471 _dbus_assert (old_refcount >= 0); 00472 _dbus_assert (old_refcount > 0 || new_refcount > 0); 00473 } 00474 00475 _dbus_assert (why != NULL); 00476 _dbus_assert (env_var != NULL); 00477 _dbus_assert (enabled != NULL); 00478 00479 if (*enabled < 0) 00480 { 00481 const char *s = _dbus_getenv (env_var); 00482 00483 *enabled = FALSE; 00484 00485 if (s && *s) 00486 { 00487 if (*s == '0') 00488 *enabled = FALSE; 00489 else if (*s == '1') 00490 *enabled = TRUE; 00491 else 00492 _dbus_warn ("%s should be 0 or 1 if set, not '%s'", env_var, s); 00493 } 00494 } 00495 00496 if (*enabled) 00497 { 00498 if (old_refcount == -1) 00499 { 00500 VALGRIND_PRINTF_BACKTRACE ("%s %p ref stolen (%s)", 00501 obj_name, obj, why); 00502 _dbus_verbose ("%s %p ref stolen (%s)\n", 00503 obj_name, obj, why); 00504 } 00505 else 00506 { 00507 VALGRIND_PRINTF_BACKTRACE ("%s %p %d -> %d refs (%s)", 00508 obj_name, obj, 00509 old_refcount, new_refcount, why); 00510 _dbus_verbose ("%s %p %d -> %d refs (%s)\n", 00511 obj_name, obj, old_refcount, new_refcount, why); 00512 } 00513 } 00514 } 00515 00516 #endif /* DBUS_ENABLE_VERBOSE_MODE */ 00517 00526 char* 00527 _dbus_strdup (const char *str) 00528 { 00529 size_t len; 00530 char *copy; 00531 00532 if (str == NULL) 00533 return NULL; 00534 00535 len = strlen (str); 00536 00537 copy = dbus_malloc (len + 1); 00538 if (copy == NULL) 00539 return NULL; 00540 00541 memcpy (copy, str, len + 1); 00542 00543 return copy; 00544 } 00545 00554 void* 00555 _dbus_memdup (const void *mem, 00556 size_t n_bytes) 00557 { 00558 void *copy; 00559 00560 copy = dbus_malloc (n_bytes); 00561 if (copy == NULL) 00562 return NULL; 00563 00564 memcpy (copy, mem, n_bytes); 00565 00566 return copy; 00567 } 00568 00577 char** 00578 _dbus_dup_string_array (const char **array) 00579 { 00580 int len; 00581 int i; 00582 char **copy; 00583 00584 if (array == NULL) 00585 return NULL; 00586 00587 for (len = 0; array[len] != NULL; ++len) 00588 ; 00589 00590 copy = dbus_new0 (char*, len + 1); 00591 if (copy == NULL) 00592 return NULL; 00593 00594 i = 0; 00595 while (i < len) 00596 { 00597 copy[i] = _dbus_strdup (array[i]); 00598 if (copy[i] == NULL) 00599 { 00600 dbus_free_string_array (copy); 00601 return NULL; 00602 } 00603 00604 ++i; 00605 } 00606 00607 return copy; 00608 } 00609 00617 dbus_bool_t 00618 _dbus_string_array_contains (const char **array, 00619 const char *str) 00620 { 00621 int i; 00622 00623 i = 0; 00624 while (array[i] != NULL) 00625 { 00626 if (strcmp (array[i], str) == 0) 00627 return TRUE; 00628 ++i; 00629 } 00630 00631 return FALSE; 00632 } 00633 00640 void 00641 _dbus_generate_uuid (DBusGUID *uuid) 00642 { 00643 long now; 00644 00645 /* don't use monotonic time because the UUID may be saved to disk, e.g. 00646 * it may persist across reboots 00647 */ 00648 _dbus_get_real_time (&now, NULL); 00649 00650 uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now); 00651 00652 _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4); 00653 } 00654 00662 dbus_bool_t 00663 _dbus_uuid_encode (const DBusGUID *uuid, 00664 DBusString *encoded) 00665 { 00666 DBusString binary; 00667 _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES); 00668 return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded)); 00669 } 00670 00671 static dbus_bool_t 00672 _dbus_read_uuid_file_without_creating (const DBusString *filename, 00673 DBusGUID *uuid, 00674 DBusError *error) 00675 { 00676 DBusString contents; 00677 DBusString decoded; 00678 int end; 00679 00680 if (!_dbus_string_init (&contents)) 00681 { 00682 _DBUS_SET_OOM (error); 00683 return FALSE; 00684 } 00685 00686 if (!_dbus_string_init (&decoded)) 00687 { 00688 _dbus_string_free (&contents); 00689 _DBUS_SET_OOM (error); 00690 return FALSE; 00691 } 00692 00693 if (!_dbus_file_get_contents (&contents, filename, error)) 00694 goto error; 00695 00696 _dbus_string_chop_white (&contents); 00697 00698 if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX) 00699 { 00700 dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, 00701 "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text", 00702 _dbus_string_get_const_data (filename), 00703 DBUS_UUID_LENGTH_HEX, 00704 _dbus_string_get_length (&contents)); 00705 goto error; 00706 } 00707 00708 if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0)) 00709 { 00710 _DBUS_SET_OOM (error); 00711 goto error; 00712 } 00713 00714 if (end == 0) 00715 { 00716 dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, 00717 "UUID file '%s' contains invalid hex data", 00718 _dbus_string_get_const_data (filename)); 00719 goto error; 00720 } 00721 00722 if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES) 00723 { 00724 dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT, 00725 "UUID file '%s' contains %d bytes of hex-encoded data instead of %d", 00726 _dbus_string_get_const_data (filename), 00727 _dbus_string_get_length (&decoded), 00728 DBUS_UUID_LENGTH_BYTES); 00729 goto error; 00730 } 00731 00732 _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES); 00733 00734 _dbus_string_free (&decoded); 00735 _dbus_string_free (&contents); 00736 00737 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00738 00739 return TRUE; 00740 00741 error: 00742 _DBUS_ASSERT_ERROR_IS_SET (error); 00743 _dbus_string_free (&contents); 00744 _dbus_string_free (&decoded); 00745 return FALSE; 00746 } 00747 00756 dbus_bool_t 00757 _dbus_write_uuid_file (const DBusString *filename, 00758 const DBusGUID *uuid, 00759 DBusError *error) 00760 { 00761 DBusString encoded; 00762 00763 if (!_dbus_string_init (&encoded)) 00764 { 00765 _DBUS_SET_OOM (error); 00766 return FALSE; 00767 } 00768 00769 if (!_dbus_uuid_encode (uuid, &encoded)) 00770 { 00771 _DBUS_SET_OOM (error); 00772 goto error; 00773 } 00774 00775 if (!_dbus_string_append_byte (&encoded, '\n')) 00776 { 00777 _DBUS_SET_OOM (error); 00778 goto error; 00779 } 00780 00781 if (!_dbus_string_save_to_file (&encoded, filename, TRUE, error)) 00782 goto error; 00783 00784 _dbus_string_free (&encoded); 00785 00786 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00787 return TRUE; 00788 00789 error: 00790 _DBUS_ASSERT_ERROR_IS_SET (error); 00791 _dbus_string_free (&encoded); 00792 return FALSE; 00793 } 00794 00805 dbus_bool_t 00806 _dbus_read_uuid_file (const DBusString *filename, 00807 DBusGUID *uuid, 00808 dbus_bool_t create_if_not_found, 00809 DBusError *error) 00810 { 00811 DBusError read_error = DBUS_ERROR_INIT; 00812 00813 if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error)) 00814 return TRUE; 00815 00816 if (!create_if_not_found) 00817 { 00818 dbus_move_error (&read_error, error); 00819 return FALSE; 00820 } 00821 00822 /* If the file exists and contains junk, we want to keep that error 00823 * message instead of overwriting it with a "file exists" error 00824 * message when we try to write 00825 */ 00826 if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT)) 00827 { 00828 dbus_move_error (&read_error, error); 00829 return FALSE; 00830 } 00831 else 00832 { 00833 dbus_error_free (&read_error); 00834 _dbus_generate_uuid (uuid); 00835 return _dbus_write_uuid_file (filename, uuid, error); 00836 } 00837 } 00838 00839 /* Protected by _DBUS_LOCK (machine_uuid) */ 00840 static int machine_uuid_initialized_generation = 0; 00841 static DBusGUID machine_uuid; 00842 00853 dbus_bool_t 00854 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str) 00855 { 00856 dbus_bool_t ok; 00857 00858 if (!_DBUS_LOCK (machine_uuid)) 00859 return FALSE; 00860 00861 if (machine_uuid_initialized_generation != _dbus_current_generation) 00862 { 00863 DBusError error = DBUS_ERROR_INIT; 00864 00865 if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE, 00866 &error)) 00867 { 00868 #ifndef DBUS_ENABLE_EMBEDDED_TESTS 00869 /* For the test suite, we may not be installed so just continue silently 00870 * here. But in a production build, we want to be nice and loud about 00871 * this. 00872 */ 00873 _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n" 00874 "See the manual page for dbus-uuidgen to correct this issue.\n", 00875 error.message); 00876 #endif 00877 00878 dbus_error_free (&error); 00879 00880 _dbus_generate_uuid (&machine_uuid); 00881 } 00882 } 00883 00884 ok = _dbus_uuid_encode (&machine_uuid, uuid_str); 00885 00886 _DBUS_UNLOCK (machine_uuid); 00887 00888 return ok; 00889 } 00890 00891 #ifndef DBUS_DISABLE_CHECKS 00892 00893 const char *_dbus_return_if_fail_warning_format = 00894 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n" 00895 "This is normally a bug in some application using the D-Bus library.\n"; 00896 #endif 00897 00898 #ifndef DBUS_DISABLE_ASSERT 00899 00911 void 00912 _dbus_real_assert (dbus_bool_t condition, 00913 const char *condition_text, 00914 const char *file, 00915 int line, 00916 const char *func) 00917 { 00918 if (_DBUS_UNLIKELY (!condition)) 00919 { 00920 _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n", 00921 _dbus_pid_for_log (), condition_text, file, line, func); 00922 _dbus_abort (); 00923 } 00924 } 00925 00936 void 00937 _dbus_real_assert_not_reached (const char *explanation, 00938 const char *file, 00939 int line) 00940 { 00941 _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n", 00942 file, line, _dbus_pid_for_log (), explanation); 00943 _dbus_abort (); 00944 } 00945 #endif /* DBUS_DISABLE_ASSERT */ 00946 00947 #ifdef DBUS_ENABLE_EMBEDDED_TESTS 00948 static dbus_bool_t 00949 run_failing_each_malloc (int n_mallocs, 00950 const char *description, 00951 DBusTestMemoryFunction func, 00952 void *data) 00953 { 00954 n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */ 00955 00956 while (n_mallocs >= 0) 00957 { 00958 _dbus_set_fail_alloc_counter (n_mallocs); 00959 00960 _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n", 00961 description, n_mallocs, 00962 _dbus_get_fail_alloc_failures ()); 00963 00964 if (!(* func) (data)) 00965 return FALSE; 00966 00967 n_mallocs -= 1; 00968 } 00969 00970 _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); 00971 00972 return TRUE; 00973 } 00974 00988 dbus_bool_t 00989 _dbus_test_oom_handling (const char *description, 00990 DBusTestMemoryFunction func, 00991 void *data) 00992 { 00993 int approx_mallocs; 00994 const char *setting; 00995 int max_failures_to_try; 00996 int i; 00997 00998 /* Run once to see about how many mallocs are involved */ 00999 01000 _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); 01001 01002 _dbus_verbose ("Running once to count mallocs\n"); 01003 01004 if (!(* func) (data)) 01005 return FALSE; 01006 01007 approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter (); 01008 01009 _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n", 01010 description, approx_mallocs); 01011 01012 setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES"); 01013 if (setting != NULL) 01014 { 01015 DBusString str; 01016 long v; 01017 _dbus_string_init_const (&str, setting); 01018 v = 4; 01019 if (!_dbus_string_parse_int (&str, 0, &v, NULL)) 01020 _dbus_warn ("couldn't parse '%s' as integer\n", setting); 01021 max_failures_to_try = v; 01022 } 01023 else 01024 { 01025 max_failures_to_try = 4; 01026 } 01027 01028 i = setting ? max_failures_to_try - 1 : 1; 01029 while (i < max_failures_to_try) 01030 { 01031 _dbus_set_fail_alloc_failures (i); 01032 if (!run_failing_each_malloc (approx_mallocs, description, func, data)) 01033 return FALSE; 01034 ++i; 01035 } 01036 01037 _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n", 01038 description); 01039 01040 return TRUE; 01041 } 01042 #endif /* DBUS_ENABLE_EMBEDDED_TESTS */ 01043
1.7.6.1