D-Bus 1.4.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 <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 DBUS_USE_OUTPUT_DEBUG_STRING
00305 static char module_name[1024];
00306 #endif
00307 
00308 static inline void
00309 _dbus_verbose_init (void)
00310 {
00311   if (!verbose_initted)
00312     {
00313       const char *p = _dbus_getenv ("DBUS_VERBOSE");
00314       verbose = p != NULL && *p == '1';
00315       verbose_initted = TRUE;
00316 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING
00317       {
00318         char *last_period, *last_slash;
00319         GetModuleFileName(0,module_name,sizeof(module_name)-1);
00320         last_period = _mbsrchr(module_name,'.');
00321         if (last_period)
00322           *last_period ='\0';
00323         last_slash = _mbsrchr(module_name,'\\');
00324         if (last_slash)
00325           strcpy(module_name,last_slash+1);
00326         strcat(module_name,": ");
00327       }
00328 #endif
00329     }
00330 }
00331 
00337 #ifdef DBUS_WIN 
00338 #define DBUS_IS_DIR_SEPARATOR(c) (c == '\\' || c == '/')
00339 #else
00340 #define DBUS_IS_DIR_SEPARATOR(c) (c == '/')
00341 #endif
00342 
00347 static char *_dbus_file_path_extract_elements_from_tail(const char *file,int level)
00348 {
00349   static int prefix = -1;
00350   char *p;
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 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
00394 _dbus_verbose_real (const char *file, 
00395                     const int line, 
00396                     const char *function, 
00397                     const char *format,
00398 #else
00399 _dbus_verbose_real (const char *format,
00400 #endif
00401                     ...)
00402 {
00403   va_list args;
00404   static dbus_bool_t need_pid = TRUE;
00405   int len;
00406   
00407   /* things are written a bit oddly here so that
00408    * in the non-verbose case we just have the one
00409    * conditional and return immediately.
00410    */
00411   if (!_dbus_is_verbose_real())
00412     return;
00413 
00414 #ifndef DBUS_USE_OUTPUT_DEBUG_STRING
00415   /* Print out pid before the line */
00416   if (need_pid)
00417     {
00418 #if PTHREAD_IN_VERBOSE
00419       fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ());
00420 #else
00421       fprintf (stderr, "%lu: ", _dbus_pid_for_log ());
00422 #endif
00423     }
00424 #endif
00425 
00426   /* Only print pid again if the next line is a new line */
00427   len = strlen (format);
00428   if (format[len-1] == '\n')
00429     need_pid = TRUE;
00430   else
00431     need_pid = FALSE;
00432 
00433   va_start (args, format);
00434 #ifdef DBUS_USE_OUTPUT_DEBUG_STRING
00435   {
00436   char buf[1024];
00437   strcpy(buf,module_name);
00438 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
00439   sprintf (buf+strlen(buf), "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
00440 #endif
00441   vsprintf (buf+strlen(buf),format, args);
00442   va_end (args);
00443   OutputDebugStringA(buf);
00444   }
00445 #else
00446 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
00447   fprintf (stderr, "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
00448 #endif
00449 
00450   vfprintf (stderr, format, args);
00451   va_end (args);
00452 
00453   fflush (stderr);
00454 #endif
00455 }
00456 
00463 void
00464 _dbus_verbose_reset_real (void)
00465 {
00466   verbose_initted = FALSE;
00467 }
00468 
00469 #endif /* DBUS_ENABLE_VERBOSE_MODE */
00470 
00479 char*
00480 _dbus_strdup (const char *str)
00481 {
00482   size_t len;
00483   char *copy;
00484   
00485   if (str == NULL)
00486     return NULL;
00487   
00488   len = strlen (str);
00489 
00490   copy = dbus_malloc (len + 1);
00491   if (copy == NULL)
00492     return NULL;
00493 
00494   memcpy (copy, str, len + 1);
00495   
00496   return copy;
00497 }
00498 
00507 void*
00508 _dbus_memdup (const void  *mem,
00509               size_t       n_bytes)
00510 {
00511   void *copy;
00512 
00513   copy = dbus_malloc (n_bytes);
00514   if (copy == NULL)
00515     return NULL;
00516 
00517   memcpy (copy, mem, n_bytes);
00518   
00519   return copy;
00520 }
00521 
00530 char**
00531 _dbus_dup_string_array (const char **array)
00532 {
00533   int len;
00534   int i;
00535   char **copy;
00536   
00537   if (array == NULL)
00538     return NULL;
00539 
00540   for (len = 0; array[len] != NULL; ++len)
00541     ;
00542 
00543   copy = dbus_new0 (char*, len + 1);
00544   if (copy == NULL)
00545     return NULL;
00546 
00547   i = 0;
00548   while (i < len)
00549     {
00550       copy[i] = _dbus_strdup (array[i]);
00551       if (copy[i] == NULL)
00552         {
00553           dbus_free_string_array (copy);
00554           return NULL;
00555         }
00556 
00557       ++i;
00558     }
00559 
00560   return copy;
00561 }
00562 
00570 dbus_bool_t
00571 _dbus_string_array_contains (const char **array,
00572                              const char  *str)
00573 {
00574   int i;
00575 
00576   i = 0;
00577   while (array[i] != NULL)
00578     {
00579       if (strcmp (array[i], str) == 0)
00580         return TRUE;
00581       ++i;
00582     }
00583 
00584   return FALSE;
00585 }
00586 
00593 void
00594 _dbus_generate_uuid (DBusGUID *uuid)
00595 {
00596   long now;
00597 
00598   _dbus_get_current_time (&now, NULL);
00599 
00600   uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
00601   
00602   _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4);
00603 }
00604 
00612 dbus_bool_t
00613 _dbus_uuid_encode (const DBusGUID *uuid,
00614                    DBusString     *encoded)
00615 {
00616   DBusString binary;
00617   _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00618   return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
00619 }
00620 
00621 static dbus_bool_t
00622 _dbus_read_uuid_file_without_creating (const DBusString *filename,
00623                                        DBusGUID         *uuid,
00624                                        DBusError        *error)
00625 {
00626   DBusString contents;
00627   DBusString decoded;
00628   int end;
00629   
00630   if (!_dbus_string_init (&contents))
00631     {
00632       _DBUS_SET_OOM (error);
00633       return FALSE;
00634     }
00635 
00636   if (!_dbus_string_init (&decoded))
00637     {
00638       _dbus_string_free (&contents);
00639       _DBUS_SET_OOM (error);
00640       return FALSE;
00641     }
00642   
00643   if (!_dbus_file_get_contents (&contents, filename, error))
00644     goto error;
00645 
00646   _dbus_string_chop_white (&contents);
00647 
00648   if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
00649     {
00650       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00651                       "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
00652                       _dbus_string_get_const_data (filename),
00653                       DBUS_UUID_LENGTH_HEX,
00654                       _dbus_string_get_length (&contents));
00655       goto error;
00656     }
00657 
00658   if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
00659     {
00660       _DBUS_SET_OOM (error);
00661       goto error;
00662     }
00663 
00664   if (end == 0)
00665     {
00666       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00667                       "UUID file '%s' contains invalid hex data",
00668                       _dbus_string_get_const_data (filename));
00669       goto error;
00670     }
00671 
00672   if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
00673     {
00674       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00675                       "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
00676                       _dbus_string_get_const_data (filename),
00677                       _dbus_string_get_length (&decoded),
00678                       DBUS_UUID_LENGTH_BYTES);
00679       goto error;
00680     }
00681 
00682   _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00683 
00684   _dbus_string_free (&decoded);
00685   _dbus_string_free (&contents);
00686 
00687   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00688 
00689   return TRUE;
00690   
00691  error:
00692   _DBUS_ASSERT_ERROR_IS_SET (error);
00693   _dbus_string_free (&contents);
00694   _dbus_string_free (&decoded);
00695   return FALSE;
00696 }
00697 
00698 static dbus_bool_t
00699 _dbus_create_uuid_file_exclusively (const DBusString *filename,
00700                                     DBusGUID         *uuid,
00701                                     DBusError        *error)
00702 {
00703   DBusString encoded;
00704 
00705   if (!_dbus_string_init (&encoded))
00706     {
00707       _DBUS_SET_OOM (error);
00708       return FALSE;
00709     }
00710 
00711   _dbus_generate_uuid (uuid);
00712   
00713   if (!_dbus_uuid_encode (uuid, &encoded))
00714     {
00715       _DBUS_SET_OOM (error);
00716       goto error;
00717     }
00718   
00719   if (!_dbus_string_append_byte (&encoded, '\n'))
00720     {
00721       _DBUS_SET_OOM (error);
00722       goto error;
00723     }
00724   
00725   if (!_dbus_string_save_to_file (&encoded, filename, TRUE, error))
00726     goto error;
00727 
00728   _dbus_string_free (&encoded);
00729 
00730   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00731   return TRUE;
00732   
00733  error:
00734   _DBUS_ASSERT_ERROR_IS_SET (error);
00735   _dbus_string_free (&encoded);
00736   return FALSE;        
00737 }
00738 
00749 dbus_bool_t
00750 _dbus_read_uuid_file (const DBusString *filename,
00751                       DBusGUID         *uuid,
00752                       dbus_bool_t       create_if_not_found,
00753                       DBusError        *error)
00754 {
00755   DBusError read_error = DBUS_ERROR_INIT;
00756 
00757   if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
00758     return TRUE;
00759 
00760   if (!create_if_not_found)
00761     {
00762       dbus_move_error (&read_error, error);
00763       return FALSE;
00764     }
00765 
00766   /* If the file exists and contains junk, we want to keep that error
00767    * message instead of overwriting it with a "file exists" error
00768    * message when we try to write
00769    */
00770   if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
00771     {
00772       dbus_move_error (&read_error, error);
00773       return FALSE;
00774     }
00775   else
00776     {
00777       dbus_error_free (&read_error);
00778       return _dbus_create_uuid_file_exclusively (filename, uuid, error);
00779     }
00780 }
00781 
00782 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
00783 static int machine_uuid_initialized_generation = 0;
00784 static DBusGUID machine_uuid;
00785 
00796 dbus_bool_t
00797 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
00798 {
00799   dbus_bool_t ok;
00800   
00801   _DBUS_LOCK (machine_uuid);
00802   if (machine_uuid_initialized_generation != _dbus_current_generation)
00803     {
00804       DBusError error = DBUS_ERROR_INIT;
00805 
00806       if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
00807                                           &error))
00808         {          
00809 #ifndef DBUS_BUILD_TESTS
00810           /* For the test suite, we may not be installed so just continue silently
00811            * here. But in a production build, we want to be nice and loud about
00812            * this.
00813            */
00814           _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
00815                                    "See the manual page for dbus-uuidgen to correct this issue.\n",
00816                                    error.message);
00817 #endif
00818           
00819           dbus_error_free (&error);
00820           
00821           _dbus_generate_uuid (&machine_uuid);
00822         }
00823     }
00824 
00825   ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
00826 
00827   _DBUS_UNLOCK (machine_uuid);
00828 
00829   return ok;
00830 }
00831 
00832 #ifdef DBUS_BUILD_TESTS
00833 
00839 const char *
00840 _dbus_header_field_to_string (int header_field)
00841 {
00842   switch (header_field)
00843     {
00844     case DBUS_HEADER_FIELD_INVALID:
00845       return "invalid";
00846     case DBUS_HEADER_FIELD_PATH:
00847       return "path";
00848     case DBUS_HEADER_FIELD_INTERFACE:
00849       return "interface";
00850     case DBUS_HEADER_FIELD_MEMBER:
00851       return "member";
00852     case DBUS_HEADER_FIELD_ERROR_NAME:
00853       return "error-name";
00854     case DBUS_HEADER_FIELD_REPLY_SERIAL:
00855       return "reply-serial";
00856     case DBUS_HEADER_FIELD_DESTINATION:
00857       return "destination";
00858     case DBUS_HEADER_FIELD_SENDER:
00859       return "sender";
00860     case DBUS_HEADER_FIELD_SIGNATURE:
00861       return "signature";
00862     default:
00863       return "unknown";
00864     }
00865 }
00866 #endif /* DBUS_BUILD_TESTS */
00867 
00868 #ifndef DBUS_DISABLE_CHECKS
00869 
00870 const char *_dbus_return_if_fail_warning_format =
00871 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
00872 "This is normally a bug in some application using the D-Bus library.\n";
00873 #endif
00874 
00875 #ifndef DBUS_DISABLE_ASSERT
00876 
00888 void
00889 _dbus_real_assert (dbus_bool_t  condition,
00890                    const char  *condition_text,
00891                    const char  *file,
00892                    int          line,
00893                    const char  *func)
00894 {
00895   if (_DBUS_UNLIKELY (!condition))
00896     {
00897       _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
00898                   _dbus_pid_for_log (), condition_text, file, line, func);
00899       _dbus_abort ();
00900     }
00901 }
00902 
00913 void
00914 _dbus_real_assert_not_reached (const char *explanation,
00915                                const char *file,
00916                                int         line)
00917 {
00918   _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
00919               file, line, _dbus_pid_for_log (), explanation);
00920   _dbus_abort ();
00921 }
00922 #endif /* DBUS_DISABLE_ASSERT */
00923   
00924 #ifdef DBUS_BUILD_TESTS
00925 static dbus_bool_t
00926 run_failing_each_malloc (int                    n_mallocs,
00927                          const char            *description,
00928                          DBusTestMemoryFunction func,
00929                          void                  *data)
00930 {
00931   n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
00932   
00933   while (n_mallocs >= 0)
00934     {      
00935       _dbus_set_fail_alloc_counter (n_mallocs);
00936 
00937       _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
00938                      description, n_mallocs,
00939                      _dbus_get_fail_alloc_failures ());
00940 
00941       if (!(* func) (data))
00942         return FALSE;
00943       
00944       n_mallocs -= 1;
00945     }
00946 
00947   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00948 
00949   return TRUE;
00950 }                        
00951 
00965 dbus_bool_t
00966 _dbus_test_oom_handling (const char             *description,
00967                          DBusTestMemoryFunction  func,
00968                          void                   *data)
00969 {
00970   int approx_mallocs;
00971   const char *setting;
00972   int max_failures_to_try;
00973   int i;
00974 
00975   /* Run once to see about how many mallocs are involved */
00976   
00977   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00978 
00979   _dbus_verbose ("Running once to count mallocs\n");
00980   
00981   if (!(* func) (data))
00982     return FALSE;
00983   
00984   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
00985 
00986   _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
00987                  description, approx_mallocs);
00988 
00989   setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
00990   if (setting != NULL)
00991     {
00992       DBusString str;
00993       long v;
00994       _dbus_string_init_const (&str, setting);
00995       v = 4;
00996       if (!_dbus_string_parse_int (&str, 0, &v, NULL))
00997         _dbus_warn ("couldn't parse '%s' as integer\n", setting);
00998       max_failures_to_try = v;
00999     }
01000   else
01001     {
01002       max_failures_to_try = 4;
01003     }
01004 
01005   i = setting ? max_failures_to_try - 1 : 1;
01006   while (i < max_failures_to_try)
01007     {
01008       _dbus_set_fail_alloc_failures (i);
01009       if (!run_failing_each_malloc (approx_mallocs, description, func, data))
01010         return FALSE;
01011       ++i;
01012     }
01013   
01014   _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
01015                  description);
01016 
01017   return TRUE;
01018 }
01019 #endif /* DBUS_BUILD_TESTS */
01020