D-Bus 1.4.6

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 <stdio.h>
00030 #include <stdarg.h>
00031 #include <string.h>
00032 #include <stdlib.h>
00033 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING
00034 #include <windows.h>
00035 #include <mbstring.h>
00036 #endif
00037 
00198 const char *_dbus_no_memory_message = "Not enough memory";
00199 
00200 static dbus_bool_t warn_initted = FALSE;
00201 static dbus_bool_t fatal_warnings = FALSE;
00202 static dbus_bool_t fatal_warnings_on_check_failed = TRUE;
00203 
00204 static void
00205 init_warnings(void)
00206 {
00207   if (!warn_initted)
00208     {
00209       const char *s;
00210       s = _dbus_getenv ("DBUS_FATAL_WARNINGS");
00211       if (s && *s)
00212         {
00213           if (*s == '0')
00214             {
00215               fatal_warnings = FALSE;
00216               fatal_warnings_on_check_failed = FALSE;
00217             }
00218           else if (*s == '1')
00219             {
00220               fatal_warnings = TRUE;
00221               fatal_warnings_on_check_failed = TRUE;
00222             }
00223           else
00224             {
00225               fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'",
00226                       s);
00227             }
00228         }
00229 
00230       warn_initted = TRUE;
00231     }
00232 }
00233 
00243 void
00244 _dbus_warn (const char *format,
00245             ...)
00246 {
00247   va_list args;
00248 
00249   if (!warn_initted)
00250     init_warnings ();
00251   
00252   va_start (args, format);
00253   vfprintf (stderr, format, args);
00254   va_end (args);
00255 
00256   if (fatal_warnings)
00257     {
00258       fflush (stderr);
00259       _dbus_abort ();
00260     }
00261 }
00262 
00271 void
00272 _dbus_warn_check_failed(const char *format,
00273                         ...)
00274 {
00275   va_list args;
00276   
00277   if (!warn_initted)
00278     init_warnings ();
00279 
00280   fprintf (stderr, "process %lu: ", _dbus_pid_for_log ());
00281   
00282   va_start (args, format);
00283   vfprintf (stderr, format, args);
00284   va_end (args);
00285 
00286   if (fatal_warnings_on_check_failed)
00287     {
00288       fflush (stderr);
00289       _dbus_abort ();
00290     }
00291 }
00292 
00293 #ifdef DBUS_ENABLE_VERBOSE_MODE
00294 
00295 static dbus_bool_t verbose_initted = FALSE;
00296 static dbus_bool_t verbose = TRUE;
00297 
00299 #define PTHREAD_IN_VERBOSE 0
00300 #if PTHREAD_IN_VERBOSE
00301 #include <pthread.h>
00302 #endif
00303 
00304 #ifdef _MSC_VER
00305 #define inline
00306 #endif
00307 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING
00308 static char module_name[1024];
00309 #endif
00310 
00311 static inline void
00312 _dbus_verbose_init (void)
00313 {
00314   if (!verbose_initted)
00315     {
00316       const char *p = _dbus_getenv ("DBUS_VERBOSE");
00317       verbose = p != NULL && *p == '1';
00318       verbose_initted = TRUE;
00319 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING
00320       {
00321         char *last_period, *last_slash;
00322         GetModuleFileName(0,module_name,sizeof(module_name)-1);
00323         last_period = _mbsrchr(module_name,'.');
00324         if (last_period)
00325           *last_period ='\0';
00326         last_slash = _mbsrchr(module_name,'\\');
00327         if (last_slash)
00328           strcpy(module_name,last_slash+1);
00329         strcat(module_name,": ");
00330       }
00331 #endif
00332     }
00333 }
00334 
00340 #ifdef DBUS_WIN 
00341 #define DBUS_IS_DIR_SEPARATOR(c) (c == '\\' || c == '/')
00342 #else
00343 #define DBUS_IS_DIR_SEPARATOR(c) (c == '/')
00344 #endif
00345 
00350 static char *_dbus_file_path_extract_elements_from_tail(const char *file,int level)
00351 {
00352   static int prefix = -1;
00353   char *p;
00354 
00355   if (prefix == -1) 
00356     {
00357       char *p = (char *)file + strlen(file);
00358       int i = 0;
00359       prefix = 0;
00360       for (;p >= file;p--)
00361         {
00362           if (DBUS_IS_DIR_SEPARATOR(*p))
00363             {
00364               if (++i >= level) 
00365                 {
00366                   prefix = p-file+1;
00367                   break;
00368                 }
00369            }
00370         }
00371     }
00372   return (char *)file+prefix;
00373 }
00374 
00380 dbus_bool_t
00381 _dbus_is_verbose_real (void)
00382 {
00383   _dbus_verbose_init ();
00384   return verbose;
00385 }
00386 
00395 void
00396 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
00397 _dbus_verbose_real (const char *file, 
00398                     const int line, 
00399                     const char *function, 
00400                     const char *format,
00401 #else
00402 _dbus_verbose_real (const char *format,
00403 #endif
00404                     ...)
00405 {
00406   va_list args;
00407   static dbus_bool_t need_pid = TRUE;
00408   int len;
00409   
00410   /* things are written a bit oddly here so that
00411    * in the non-verbose case we just have the one
00412    * conditional and return immediately.
00413    */
00414   if (!_dbus_is_verbose_real())
00415     return;
00416 
00417 #ifndef DBUS_USE_OUTPUT_DEBUG_STRING
00418   /* Print out pid before the line */
00419   if (need_pid)
00420     {
00421 #if PTHREAD_IN_VERBOSE
00422       fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ());
00423 #else
00424       fprintf (stderr, "%lu: ", _dbus_pid_for_log ());
00425 #endif
00426     }
00427 #endif
00428 
00429   /* Only print pid again if the next line is a new line */
00430   len = strlen (format);
00431   if (format[len-1] == '\n')
00432     need_pid = TRUE;
00433   else
00434     need_pid = FALSE;
00435 
00436   va_start (args, format);
00437 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING
00438   {
00439   char buf[1024];
00440   strcpy(buf,module_name);
00441 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
00442   sprintf (buf+strlen(buf), "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
00443 #endif
00444   vsprintf (buf+strlen(buf),format, args);
00445   va_end (args);
00446   OutputDebugStringA(buf);
00447   }
00448 #else
00449 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
00450   fprintf (stderr, "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
00451 #endif
00452 
00453   vfprintf (stderr, format, args);
00454   va_end (args);
00455 
00456   fflush (stderr);
00457 #endif
00458 }
00459 
00466 void
00467 _dbus_verbose_reset_real (void)
00468 {
00469   verbose_initted = FALSE;
00470 }
00471 
00472 #endif /* DBUS_ENABLE_VERBOSE_MODE */
00473 
00482 char*
00483 _dbus_strdup (const char *str)
00484 {
00485   size_t len;
00486   char *copy;
00487   
00488   if (str == NULL)
00489     return NULL;
00490   
00491   len = strlen (str);
00492 
00493   copy = dbus_malloc (len + 1);
00494   if (copy == NULL)
00495     return NULL;
00496 
00497   memcpy (copy, str, len + 1);
00498   
00499   return copy;
00500 }
00501 
00510 void*
00511 _dbus_memdup (const void  *mem,
00512               size_t       n_bytes)
00513 {
00514   void *copy;
00515 
00516   copy = dbus_malloc (n_bytes);
00517   if (copy == NULL)
00518     return NULL;
00519 
00520   memcpy (copy, mem, n_bytes);
00521   
00522   return copy;
00523 }
00524 
00533 char**
00534 _dbus_dup_string_array (const char **array)
00535 {
00536   int len;
00537   int i;
00538   char **copy;
00539   
00540   if (array == NULL)
00541     return NULL;
00542 
00543   for (len = 0; array[len] != NULL; ++len)
00544     ;
00545 
00546   copy = dbus_new0 (char*, len + 1);
00547   if (copy == NULL)
00548     return NULL;
00549 
00550   i = 0;
00551   while (i < len)
00552     {
00553       copy[i] = _dbus_strdup (array[i]);
00554       if (copy[i] == NULL)
00555         {
00556           dbus_free_string_array (copy);
00557           return NULL;
00558         }
00559 
00560       ++i;
00561     }
00562 
00563   return copy;
00564 }
00565 
00573 dbus_bool_t
00574 _dbus_string_array_contains (const char **array,
00575                              const char  *str)
00576 {
00577   int i;
00578 
00579   i = 0;
00580   while (array[i] != NULL)
00581     {
00582       if (strcmp (array[i], str) == 0)
00583         return TRUE;
00584       ++i;
00585     }
00586 
00587   return FALSE;
00588 }
00589 
00596 void
00597 _dbus_generate_uuid (DBusGUID *uuid)
00598 {
00599   long now;
00600 
00601   _dbus_get_current_time (&now, NULL);
00602 
00603   uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
00604   
00605   _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4);
00606 }
00607 
00615 dbus_bool_t
00616 _dbus_uuid_encode (const DBusGUID *uuid,
00617                    DBusString     *encoded)
00618 {
00619   DBusString binary;
00620   _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00621   return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
00622 }
00623 
00624 static dbus_bool_t
00625 _dbus_read_uuid_file_without_creating (const DBusString *filename,
00626                                        DBusGUID         *uuid,
00627                                        DBusError        *error)
00628 {
00629   DBusString contents;
00630   DBusString decoded;
00631   int end;
00632   
00633   if (!_dbus_string_init (&contents))
00634     {
00635       _DBUS_SET_OOM (error);
00636       return FALSE;
00637     }
00638 
00639   if (!_dbus_string_init (&decoded))
00640     {
00641       _dbus_string_free (&contents);
00642       _DBUS_SET_OOM (error);
00643       return FALSE;
00644     }
00645   
00646   if (!_dbus_file_get_contents (&contents, filename, error))
00647     goto error;
00648 
00649   _dbus_string_chop_white (&contents);
00650 
00651   if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
00652     {
00653       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00654                       "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
00655                       _dbus_string_get_const_data (filename),
00656                       DBUS_UUID_LENGTH_HEX,
00657                       _dbus_string_get_length (&contents));
00658       goto error;
00659     }
00660 
00661   if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
00662     {
00663       _DBUS_SET_OOM (error);
00664       goto error;
00665     }
00666 
00667   if (end == 0)
00668     {
00669       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00670                       "UUID file '%s' contains invalid hex data",
00671                       _dbus_string_get_const_data (filename));
00672       goto error;
00673     }
00674 
00675   if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
00676     {
00677       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00678                       "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
00679                       _dbus_string_get_const_data (filename),
00680                       _dbus_string_get_length (&decoded),
00681                       DBUS_UUID_LENGTH_BYTES);
00682       goto error;
00683     }
00684 
00685   _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00686 
00687   _dbus_string_free (&decoded);
00688   _dbus_string_free (&contents);
00689 
00690   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00691 
00692   return TRUE;
00693   
00694  error:
00695   _DBUS_ASSERT_ERROR_IS_SET (error);
00696   _dbus_string_free (&contents);
00697   _dbus_string_free (&decoded);
00698   return FALSE;
00699 }
00700 
00701 static dbus_bool_t
00702 _dbus_create_uuid_file_exclusively (const DBusString *filename,
00703                                     DBusGUID         *uuid,
00704                                     DBusError        *error)
00705 {
00706   DBusString encoded;
00707 
00708   if (!_dbus_string_init (&encoded))
00709     {
00710       _DBUS_SET_OOM (error);
00711       return FALSE;
00712     }
00713 
00714   _dbus_generate_uuid (uuid);
00715   
00716   if (!_dbus_uuid_encode (uuid, &encoded))
00717     {
00718       _DBUS_SET_OOM (error);
00719       goto error;
00720     }
00721   
00722   if (!_dbus_string_append_byte (&encoded, '\n'))
00723     {
00724       _DBUS_SET_OOM (error);
00725       goto error;
00726     }
00727   
00728   if (!_dbus_string_save_to_file (&encoded, filename, TRUE, error))
00729     goto error;
00730 
00731   _dbus_string_free (&encoded);
00732 
00733   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00734   return TRUE;
00735   
00736  error:
00737   _DBUS_ASSERT_ERROR_IS_SET (error);
00738   _dbus_string_free (&encoded);
00739   return FALSE;        
00740 }
00741 
00752 dbus_bool_t
00753 _dbus_read_uuid_file (const DBusString *filename,
00754                       DBusGUID         *uuid,
00755                       dbus_bool_t       create_if_not_found,
00756                       DBusError        *error)
00757 {
00758   DBusError read_error = DBUS_ERROR_INIT;
00759 
00760   if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
00761     return TRUE;
00762 
00763   if (!create_if_not_found)
00764     {
00765       dbus_move_error (&read_error, error);
00766       return FALSE;
00767     }
00768 
00769   /* If the file exists and contains junk, we want to keep that error
00770    * message instead of overwriting it with a "file exists" error
00771    * message when we try to write
00772    */
00773   if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
00774     {
00775       dbus_move_error (&read_error, error);
00776       return FALSE;
00777     }
00778   else
00779     {
00780       dbus_error_free (&read_error);
00781       return _dbus_create_uuid_file_exclusively (filename, uuid, error);
00782     }
00783 }
00784 
00785 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
00786 static int machine_uuid_initialized_generation = 0;
00787 static DBusGUID machine_uuid;
00788 
00799 dbus_bool_t
00800 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
00801 {
00802   dbus_bool_t ok;
00803   
00804   _DBUS_LOCK (machine_uuid);
00805   if (machine_uuid_initialized_generation != _dbus_current_generation)
00806     {
00807       DBusError error = DBUS_ERROR_INIT;
00808 
00809       if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
00810                                           &error))
00811         {          
00812 #ifndef DBUS_BUILD_TESTS
00813           /* For the test suite, we may not be installed so just continue silently
00814            * here. But in a production build, we want to be nice and loud about
00815            * this.
00816            */
00817           _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
00818                                    "See the manual page for dbus-uuidgen to correct this issue.\n",
00819                                    error.message);
00820 #endif
00821           
00822           dbus_error_free (&error);
00823           
00824           _dbus_generate_uuid (&machine_uuid);
00825         }
00826     }
00827 
00828   ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
00829 
00830   _DBUS_UNLOCK (machine_uuid);
00831 
00832   return ok;
00833 }
00834 
00835 #ifdef DBUS_BUILD_TESTS
00836 
00842 const char *
00843 _dbus_header_field_to_string (int header_field)
00844 {
00845   switch (header_field)
00846     {
00847     case DBUS_HEADER_FIELD_INVALID:
00848       return "invalid";
00849     case DBUS_HEADER_FIELD_PATH:
00850       return "path";
00851     case DBUS_HEADER_FIELD_INTERFACE:
00852       return "interface";
00853     case DBUS_HEADER_FIELD_MEMBER:
00854       return "member";
00855     case DBUS_HEADER_FIELD_ERROR_NAME:
00856       return "error-name";
00857     case DBUS_HEADER_FIELD_REPLY_SERIAL:
00858       return "reply-serial";
00859     case DBUS_HEADER_FIELD_DESTINATION:
00860       return "destination";
00861     case DBUS_HEADER_FIELD_SENDER:
00862       return "sender";
00863     case DBUS_HEADER_FIELD_SIGNATURE:
00864       return "signature";
00865     default:
00866       return "unknown";
00867     }
00868 }
00869 #endif /* DBUS_BUILD_TESTS */
00870 
00871 #ifndef DBUS_DISABLE_CHECKS
00872 
00873 const char *_dbus_return_if_fail_warning_format =
00874 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
00875 "This is normally a bug in some application using the D-Bus library.\n";
00876 #endif
00877 
00878 #ifndef DBUS_DISABLE_ASSERT
00879 
00891 void
00892 _dbus_real_assert (dbus_bool_t  condition,
00893                    const char  *condition_text,
00894                    const char  *file,
00895                    int          line,
00896                    const char  *func)
00897 {
00898   if (_DBUS_UNLIKELY (!condition))
00899     {
00900       _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
00901                   _dbus_pid_for_log (), condition_text, file, line, func);
00902       _dbus_abort ();
00903     }
00904 }
00905 
00916 void
00917 _dbus_real_assert_not_reached (const char *explanation,
00918                                const char *file,
00919                                int         line)
00920 {
00921   _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
00922               file, line, _dbus_pid_for_log (), explanation);
00923   _dbus_abort ();
00924 }
00925 #endif /* DBUS_DISABLE_ASSERT */
00926   
00927 #ifdef DBUS_BUILD_TESTS
00928 static dbus_bool_t
00929 run_failing_each_malloc (int                    n_mallocs,
00930                          const char            *description,
00931                          DBusTestMemoryFunction func,
00932                          void                  *data)
00933 {
00934   n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
00935   
00936   while (n_mallocs >= 0)
00937     {      
00938       _dbus_set_fail_alloc_counter (n_mallocs);
00939 
00940       _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
00941                      description, n_mallocs,
00942                      _dbus_get_fail_alloc_failures ());
00943 
00944       if (!(* func) (data))
00945         return FALSE;
00946       
00947       n_mallocs -= 1;
00948     }
00949 
00950   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00951 
00952   return TRUE;
00953 }                        
00954 
00968 dbus_bool_t
00969 _dbus_test_oom_handling (const char             *description,
00970                          DBusTestMemoryFunction  func,
00971                          void                   *data)
00972 {
00973   int approx_mallocs;
00974   const char *setting;
00975   int max_failures_to_try;
00976   int i;
00977 
00978   /* Run once to see about how many mallocs are involved */
00979   
00980   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00981 
00982   _dbus_verbose ("Running once to count mallocs\n");
00983   
00984   if (!(* func) (data))
00985     return FALSE;
00986   
00987   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
00988 
00989   _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
00990                  description, approx_mallocs);
00991 
00992   setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
00993   if (setting != NULL)
00994     {
00995       DBusString str;
00996       long v;
00997       _dbus_string_init_const (&str, setting);
00998       v = 4;
00999       if (!_dbus_string_parse_int (&str, 0, &v, NULL))
01000         _dbus_warn ("couldn't parse '%s' as integer\n", setting);
01001       max_failures_to_try = v;
01002     }
01003   else
01004     {
01005       max_failures_to_try = 4;
01006     }
01007 
01008   i = setting ? max_failures_to_try - 1 : 1;
01009   while (i < max_failures_to_try)
01010     {
01011       _dbus_set_fail_alloc_failures (i);
01012       if (!run_failing_each_malloc (approx_mallocs, description, func, data))
01013         return FALSE;
01014       ++i;
01015     }
01016   
01017   _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
01018                  description);
01019 
01020   return TRUE;
01021 }
01022 #endif /* DBUS_BUILD_TESTS */
01023