D-Bus  1.5.8
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 
00351   if (prefix == -1) 
00352     {
00353       char *p = (char *)file + strlen(file);
00354       int i = 0;
00355       prefix = 0;
00356       for (;p >= file;p--)
00357         {
00358           if (DBUS_IS_DIR_SEPARATOR(*p))
00359             {
00360               if (++i >= level) 
00361                 {
00362                   prefix = p-file+1;
00363                   break;
00364                 }
00365            }
00366         }
00367     }
00368   return (char *)file+prefix;
00369 }
00370 
00376 dbus_bool_t
00377 _dbus_is_verbose_real (void)
00378 {
00379   _dbus_verbose_init ();
00380   return verbose;
00381 }
00382 
00391 void
00392 #ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
00393 _dbus_verbose_real (const char *file, 
00394                     const int line, 
00395                     const char *function, 
00396                     const char *format,
00397 #else
00398 _dbus_verbose_real (const char *format,
00399 #endif
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 #endif /* DBUS_ENABLE_VERBOSE_MODE */
00469 
00478 char*
00479 _dbus_strdup (const char *str)
00480 {
00481   size_t len;
00482   char *copy;
00483   
00484   if (str == NULL)
00485     return NULL;
00486   
00487   len = strlen (str);
00488 
00489   copy = dbus_malloc (len + 1);
00490   if (copy == NULL)
00491     return NULL;
00492 
00493   memcpy (copy, str, len + 1);
00494   
00495   return copy;
00496 }
00497 
00506 void*
00507 _dbus_memdup (const void  *mem,
00508               size_t       n_bytes)
00509 {
00510   void *copy;
00511 
00512   copy = dbus_malloc (n_bytes);
00513   if (copy == NULL)
00514     return NULL;
00515 
00516   memcpy (copy, mem, n_bytes);
00517   
00518   return copy;
00519 }
00520 
00529 char**
00530 _dbus_dup_string_array (const char **array)
00531 {
00532   int len;
00533   int i;
00534   char **copy;
00535   
00536   if (array == NULL)
00537     return NULL;
00538 
00539   for (len = 0; array[len] != NULL; ++len)
00540     ;
00541 
00542   copy = dbus_new0 (char*, len + 1);
00543   if (copy == NULL)
00544     return NULL;
00545 
00546   i = 0;
00547   while (i < len)
00548     {
00549       copy[i] = _dbus_strdup (array[i]);
00550       if (copy[i] == NULL)
00551         {
00552           dbus_free_string_array (copy);
00553           return NULL;
00554         }
00555 
00556       ++i;
00557     }
00558 
00559   return copy;
00560 }
00561 
00569 dbus_bool_t
00570 _dbus_string_array_contains (const char **array,
00571                              const char  *str)
00572 {
00573   int i;
00574 
00575   i = 0;
00576   while (array[i] != NULL)
00577     {
00578       if (strcmp (array[i], str) == 0)
00579         return TRUE;
00580       ++i;
00581     }
00582 
00583   return FALSE;
00584 }
00585 
00592 void
00593 _dbus_generate_uuid (DBusGUID *uuid)
00594 {
00595   long now;
00596 
00597   _dbus_get_current_time (&now, NULL);
00598 
00599   uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
00600   
00601   _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4);
00602 }
00603 
00611 dbus_bool_t
00612 _dbus_uuid_encode (const DBusGUID *uuid,
00613                    DBusString     *encoded)
00614 {
00615   DBusString binary;
00616   _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00617   return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
00618 }
00619 
00620 static dbus_bool_t
00621 _dbus_read_uuid_file_without_creating (const DBusString *filename,
00622                                        DBusGUID         *uuid,
00623                                        DBusError        *error)
00624 {
00625   DBusString contents;
00626   DBusString decoded;
00627   int end;
00628   
00629   if (!_dbus_string_init (&contents))
00630     {
00631       _DBUS_SET_OOM (error);
00632       return FALSE;
00633     }
00634 
00635   if (!_dbus_string_init (&decoded))
00636     {
00637       _dbus_string_free (&contents);
00638       _DBUS_SET_OOM (error);
00639       return FALSE;
00640     }
00641   
00642   if (!_dbus_file_get_contents (&contents, filename, error))
00643     goto error;
00644 
00645   _dbus_string_chop_white (&contents);
00646 
00647   if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
00648     {
00649       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00650                       "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
00651                       _dbus_string_get_const_data (filename),
00652                       DBUS_UUID_LENGTH_HEX,
00653                       _dbus_string_get_length (&contents));
00654       goto error;
00655     }
00656 
00657   if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
00658     {
00659       _DBUS_SET_OOM (error);
00660       goto error;
00661     }
00662 
00663   if (end == 0)
00664     {
00665       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00666                       "UUID file '%s' contains invalid hex data",
00667                       _dbus_string_get_const_data (filename));
00668       goto error;
00669     }
00670 
00671   if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
00672     {
00673       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00674                       "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
00675                       _dbus_string_get_const_data (filename),
00676                       _dbus_string_get_length (&decoded),
00677                       DBUS_UUID_LENGTH_BYTES);
00678       goto error;
00679     }
00680 
00681   _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00682 
00683   _dbus_string_free (&decoded);
00684   _dbus_string_free (&contents);
00685 
00686   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00687 
00688   return TRUE;
00689   
00690  error:
00691   _DBUS_ASSERT_ERROR_IS_SET (error);
00692   _dbus_string_free (&contents);
00693   _dbus_string_free (&decoded);
00694   return FALSE;
00695 }
00696 
00697 static dbus_bool_t
00698 _dbus_create_uuid_file_exclusively (const DBusString *filename,
00699                                     DBusGUID         *uuid,
00700                                     DBusError        *error)
00701 {
00702   DBusString encoded;
00703 
00704   if (!_dbus_string_init (&encoded))
00705     {
00706       _DBUS_SET_OOM (error);
00707       return FALSE;
00708     }
00709 
00710   _dbus_generate_uuid (uuid);
00711   
00712   if (!_dbus_uuid_encode (uuid, &encoded))
00713     {
00714       _DBUS_SET_OOM (error);
00715       goto error;
00716     }
00717   
00718   if (!_dbus_string_append_byte (&encoded, '\n'))
00719     {
00720       _DBUS_SET_OOM (error);
00721       goto error;
00722     }
00723   
00724   if (!_dbus_string_save_to_file (&encoded, filename, TRUE, error))
00725     goto error;
00726 
00727   _dbus_string_free (&encoded);
00728 
00729   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00730   return TRUE;
00731   
00732  error:
00733   _DBUS_ASSERT_ERROR_IS_SET (error);
00734   _dbus_string_free (&encoded);
00735   return FALSE;        
00736 }
00737 
00748 dbus_bool_t
00749 _dbus_read_uuid_file (const DBusString *filename,
00750                       DBusGUID         *uuid,
00751                       dbus_bool_t       create_if_not_found,
00752                       DBusError        *error)
00753 {
00754   DBusError read_error = DBUS_ERROR_INIT;
00755 
00756   if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
00757     return TRUE;
00758 
00759   if (!create_if_not_found)
00760     {
00761       dbus_move_error (&read_error, error);
00762       return FALSE;
00763     }
00764 
00765   /* If the file exists and contains junk, we want to keep that error
00766    * message instead of overwriting it with a "file exists" error
00767    * message when we try to write
00768    */
00769   if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
00770     {
00771       dbus_move_error (&read_error, error);
00772       return FALSE;
00773     }
00774   else
00775     {
00776       dbus_error_free (&read_error);
00777       return _dbus_create_uuid_file_exclusively (filename, uuid, error);
00778     }
00779 }
00780 
00781 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
00782 static int machine_uuid_initialized_generation = 0;
00783 static DBusGUID machine_uuid;
00784 
00795 dbus_bool_t
00796 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
00797 {
00798   dbus_bool_t ok;
00799   
00800   _DBUS_LOCK (machine_uuid);
00801   if (machine_uuid_initialized_generation != _dbus_current_generation)
00802     {
00803       DBusError error = DBUS_ERROR_INIT;
00804 
00805       if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
00806                                           &error))
00807         {          
00808 #ifndef DBUS_BUILD_TESTS
00809           /* For the test suite, we may not be installed so just continue silently
00810            * here. But in a production build, we want to be nice and loud about
00811            * this.
00812            */
00813           _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
00814                                    "See the manual page for dbus-uuidgen to correct this issue.\n",
00815                                    error.message);
00816 #endif
00817           
00818           dbus_error_free (&error);
00819           
00820           _dbus_generate_uuid (&machine_uuid);
00821         }
00822     }
00823 
00824   ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
00825 
00826   _DBUS_UNLOCK (machine_uuid);
00827 
00828   return ok;
00829 }
00830 
00831 #ifdef DBUS_BUILD_TESTS
00832 
00838 const char *
00839 _dbus_header_field_to_string (int header_field)
00840 {
00841   switch (header_field)
00842     {
00843     case DBUS_HEADER_FIELD_INVALID:
00844       return "invalid";
00845     case DBUS_HEADER_FIELD_PATH:
00846       return "path";
00847     case DBUS_HEADER_FIELD_INTERFACE:
00848       return "interface";
00849     case DBUS_HEADER_FIELD_MEMBER:
00850       return "member";
00851     case DBUS_HEADER_FIELD_ERROR_NAME:
00852       return "error-name";
00853     case DBUS_HEADER_FIELD_REPLY_SERIAL:
00854       return "reply-serial";
00855     case DBUS_HEADER_FIELD_DESTINATION:
00856       return "destination";
00857     case DBUS_HEADER_FIELD_SENDER:
00858       return "sender";
00859     case DBUS_HEADER_FIELD_SIGNATURE:
00860       return "signature";
00861     default:
00862       return "unknown";
00863     }
00864 }
00865 #endif /* DBUS_BUILD_TESTS */
00866 
00867 #ifndef DBUS_DISABLE_CHECKS
00868 
00869 const char *_dbus_return_if_fail_warning_format =
00870 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
00871 "This is normally a bug in some application using the D-Bus library.\n";
00872 #endif
00873 
00874 #ifndef DBUS_DISABLE_ASSERT
00875 
00887 void
00888 _dbus_real_assert (dbus_bool_t  condition,
00889                    const char  *condition_text,
00890                    const char  *file,
00891                    int          line,
00892                    const char  *func)
00893 {
00894   if (_DBUS_UNLIKELY (!condition))
00895     {
00896       _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
00897                   _dbus_pid_for_log (), condition_text, file, line, func);
00898       _dbus_abort ();
00899     }
00900 }
00901 
00912 void
00913 _dbus_real_assert_not_reached (const char *explanation,
00914                                const char *file,
00915                                int         line)
00916 {
00917   _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
00918               file, line, _dbus_pid_for_log (), explanation);
00919   _dbus_abort ();
00920 }
00921 #endif /* DBUS_DISABLE_ASSERT */
00922   
00923 #ifdef DBUS_BUILD_TESTS
00924 static dbus_bool_t
00925 run_failing_each_malloc (int                    n_mallocs,
00926                          const char            *description,
00927                          DBusTestMemoryFunction func,
00928                          void                  *data)
00929 {
00930   n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
00931   
00932   while (n_mallocs >= 0)
00933     {      
00934       _dbus_set_fail_alloc_counter (n_mallocs);
00935 
00936       _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
00937                      description, n_mallocs,
00938                      _dbus_get_fail_alloc_failures ());
00939 
00940       if (!(* func) (data))
00941         return FALSE;
00942       
00943       n_mallocs -= 1;
00944     }
00945 
00946   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00947 
00948   return TRUE;
00949 }                        
00950 
00964 dbus_bool_t
00965 _dbus_test_oom_handling (const char             *description,
00966                          DBusTestMemoryFunction  func,
00967                          void                   *data)
00968 {
00969   int approx_mallocs;
00970   const char *setting;
00971   int max_failures_to_try;
00972   int i;
00973 
00974   /* Run once to see about how many mallocs are involved */
00975   
00976   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00977 
00978   _dbus_verbose ("Running once to count mallocs\n");
00979   
00980   if (!(* func) (data))
00981     return FALSE;
00982   
00983   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
00984 
00985   _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
00986                  description, approx_mallocs);
00987 
00988   setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
00989   if (setting != NULL)
00990     {
00991       DBusString str;
00992       long v;
00993       _dbus_string_init_const (&str, setting);
00994       v = 4;
00995       if (!_dbus_string_parse_int (&str, 0, &v, NULL))
00996         _dbus_warn ("couldn't parse '%s' as integer\n", setting);
00997       max_failures_to_try = v;
00998     }
00999   else
01000     {
01001       max_failures_to_try = 4;
01002     }
01003 
01004   i = setting ? max_failures_to_try - 1 : 1;
01005   while (i < max_failures_to_try)
01006     {
01007       _dbus_set_fail_alloc_failures (i);
01008       if (!run_failing_each_malloc (approx_mallocs, description, func, data))
01009         return FALSE;
01010       ++i;
01011     }
01012   
01013   _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
01014                  description);
01015 
01016   return TRUE;
01017 }
01018 #endif /* DBUS_BUILD_TESTS */
01019