D-Bus  1.8.20
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 
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