D-Bus 1.6.12

dbus-internals.c

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 = FALSE;
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