dbus-internals.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 #include "dbus-internals.h"
00024 #include "dbus-protocol.h"
00025 #include "dbus-marshal-basic.h"
00026 #include "dbus-test.h"
00027 #include <stdio.h>
00028 #include <stdarg.h>
00029 #include <string.h>
00030 #include <stdlib.h>
00031 
00192 const char _dbus_no_memory_message[] = "Not enough memory";
00193 
00194 static dbus_bool_t warn_initted = FALSE;
00195 static dbus_bool_t fatal_warnings = FALSE;
00196 static dbus_bool_t fatal_warnings_on_check_failed = TRUE;
00197 
00198 static void
00199 init_warnings(void)
00200 {
00201   if (!warn_initted)
00202     {
00203       const char *s;
00204       s = _dbus_getenv ("DBUS_FATAL_WARNINGS");
00205       if (s && *s)
00206         {
00207           if (*s == '0')
00208             {
00209               fatal_warnings = FALSE;
00210               fatal_warnings_on_check_failed = FALSE;
00211             }
00212           else if (*s == '1')
00213             {
00214               fatal_warnings = TRUE;
00215               fatal_warnings_on_check_failed = TRUE;
00216             }
00217           else
00218             {
00219               fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'",
00220                       s);
00221             }
00222         }
00223 
00224       warn_initted = TRUE;
00225     }
00226 }
00227 
00237 void
00238 _dbus_warn (const char *format,
00239             ...)
00240 {
00241   va_list args;
00242 
00243   if (!warn_initted)
00244     init_warnings ();
00245   
00246   va_start (args, format);
00247   vfprintf (stderr, format, args);
00248   va_end (args);
00249 
00250   if (fatal_warnings)
00251     {
00252       fflush (stderr);
00253       _dbus_abort ();
00254     }
00255 }
00256 
00265 void
00266 _dbus_warn_check_failed(const char *format,
00267                         ...)
00268 {
00269   va_list args;
00270   
00271   if (!warn_initted)
00272     init_warnings ();
00273 
00274   fprintf (stderr, "process %lu: ", _dbus_getpid ());
00275   
00276   va_start (args, format);
00277   vfprintf (stderr, format, args);
00278   va_end (args);
00279 
00280   if (fatal_warnings_on_check_failed)
00281     {
00282       fflush (stderr);
00283       _dbus_abort ();
00284     }
00285 }
00286 
00287 #ifdef DBUS_ENABLE_VERBOSE_MODE
00288 
00289 static dbus_bool_t verbose_initted = FALSE;
00290 static dbus_bool_t verbose = TRUE;
00291 
00293 #define PTHREAD_IN_VERBOSE 0
00294 #if PTHREAD_IN_VERBOSE
00295 #include <pthread.h>
00296 #endif
00297 
00298 static inline void
00299 _dbus_verbose_init (void)
00300 {
00301   if (!verbose_initted)
00302     {
00303       const char *p = _dbus_getenv ("DBUS_VERBOSE"); 
00304       verbose = p != NULL && *p == '1';
00305       verbose_initted = TRUE;
00306     }
00307 }
00308 
00314 dbus_bool_t
00315 _dbus_is_verbose_real (void)
00316 {
00317   _dbus_verbose_init ();
00318   return verbose;
00319 }
00320 
00329 void
00330 _dbus_verbose_real (const char *format,
00331                     ...)
00332 {
00333   va_list args;
00334   static dbus_bool_t need_pid = TRUE;
00335   int len;
00336   
00337   /* things are written a bit oddly here so that
00338    * in the non-verbose case we just have the one
00339    * conditional and return immediately.
00340    */
00341   if (!_dbus_is_verbose_real())
00342     return;
00343 
00344   /* Print out pid before the line */
00345   if (need_pid)
00346     {
00347 #if PTHREAD_IN_VERBOSE
00348       fprintf (stderr, "%lu: 0x%lx: ", _dbus_getpid (), pthread_self ());
00349 #else
00350       fprintf (stderr, "%lu: ", _dbus_getpid ());
00351 #endif
00352     }
00353       
00354 
00355   /* Only print pid again if the next line is a new line */
00356   len = strlen (format);
00357   if (format[len-1] == '\n')
00358     need_pid = TRUE;
00359   else
00360     need_pid = FALSE;
00361   
00362   va_start (args, format);
00363   vfprintf (stderr, format, args);
00364   va_end (args);
00365 
00366   fflush (stderr);
00367 }
00368 
00375 void
00376 _dbus_verbose_reset_real (void)
00377 {
00378   verbose_initted = FALSE;
00379 }
00380 
00381 #endif /* DBUS_ENABLE_VERBOSE_MODE */
00382 
00391 char*
00392 _dbus_strdup (const char *str)
00393 {
00394   size_t len;
00395   char *copy;
00396   
00397   if (str == NULL)
00398     return NULL;
00399   
00400   len = strlen (str);
00401 
00402   copy = dbus_malloc (len + 1);
00403   if (copy == NULL)
00404     return NULL;
00405 
00406   memcpy (copy, str, len + 1);
00407   
00408   return copy;
00409 }
00410 
00419 void*
00420 _dbus_memdup (const void  *mem,
00421               size_t       n_bytes)
00422 {
00423   void *copy;
00424 
00425   copy = dbus_malloc (n_bytes);
00426   if (copy == NULL)
00427     return NULL;
00428 
00429   memcpy (copy, mem, n_bytes);
00430   
00431   return copy;
00432 }
00433 
00442 char**
00443 _dbus_dup_string_array (const char **array)
00444 {
00445   int len;
00446   int i;
00447   char **copy;
00448   
00449   if (array == NULL)
00450     return NULL;
00451 
00452   for (len = 0; array[len] != NULL; ++len)
00453     ;
00454 
00455   copy = dbus_new0 (char*, len + 1);
00456   if (copy == NULL)
00457     return NULL;
00458 
00459   i = 0;
00460   while (i < len)
00461     {
00462       copy[i] = _dbus_strdup (array[i]);
00463       if (copy[i] == NULL)
00464         {
00465           dbus_free_string_array (copy);
00466           return NULL;
00467         }
00468 
00469       ++i;
00470     }
00471 
00472   return copy;
00473 }
00474 
00482 dbus_bool_t
00483 _dbus_string_array_contains (const char **array,
00484                              const char  *str)
00485 {
00486   int i;
00487 
00488   i = 0;
00489   while (array[i] != NULL)
00490     {
00491       if (strcmp (array[i], str) == 0)
00492         return TRUE;
00493       ++i;
00494     }
00495 
00496   return FALSE;
00497 }
00498 
00505 void
00506 _dbus_generate_uuid (DBusGUID *uuid)
00507 {
00508   long now;
00509 
00510   _dbus_get_current_time (&now, NULL);
00511 
00512   uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
00513   
00514   _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4);
00515 }
00516 
00524 dbus_bool_t
00525 _dbus_uuid_encode (const DBusGUID *uuid,
00526                    DBusString     *encoded)
00527 {
00528   DBusString binary;
00529   _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00530   return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
00531 }
00532 
00533 static dbus_bool_t
00534 _dbus_read_uuid_file_without_creating (const DBusString *filename,
00535                                        DBusGUID         *uuid,
00536                                        DBusError        *error)
00537 {
00538   DBusString contents;
00539   DBusString decoded;
00540   int end;
00541   
00542   _dbus_string_init (&contents);
00543   _dbus_string_init (&decoded);
00544   
00545   if (!_dbus_file_get_contents (&contents, filename, error))
00546     goto error;
00547 
00548   _dbus_string_chop_white (&contents);
00549 
00550   if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
00551     {
00552       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00553                       "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
00554                       _dbus_string_get_const_data (filename),
00555                       DBUS_UUID_LENGTH_HEX,
00556                       _dbus_string_get_length (&contents));
00557       goto error;
00558     }
00559 
00560   if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
00561     {
00562       _DBUS_SET_OOM (error);
00563       goto error;
00564     }
00565 
00566   if (end == 0)
00567     {
00568       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00569                       "UUID file '%s' contains invalid hex data",
00570                       _dbus_string_get_const_data (filename));
00571       goto error;
00572     }
00573 
00574   if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
00575     {
00576       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00577                       "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
00578                       _dbus_string_get_const_data (filename),
00579                       _dbus_string_get_length (&decoded),
00580                       DBUS_UUID_LENGTH_BYTES);
00581       goto error;
00582     }
00583 
00584   _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00585 
00586   _dbus_string_free (&decoded);
00587   _dbus_string_free (&contents);
00588 
00589   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00590 
00591   return TRUE;
00592   
00593  error:
00594   _DBUS_ASSERT_ERROR_IS_SET (error);
00595   _dbus_string_free (&contents);
00596   _dbus_string_free (&decoded);
00597   return FALSE;
00598 }
00599 
00600 static dbus_bool_t
00601 _dbus_create_uuid_file_exclusively (const DBusString *filename,
00602                                     DBusGUID         *uuid,
00603                                     DBusError        *error)
00604 {
00605   DBusString encoded;
00606 
00607   _dbus_string_init (&encoded);
00608 
00609   _dbus_generate_uuid (uuid);
00610   
00611   if (!_dbus_uuid_encode (uuid, &encoded))
00612     {
00613       _DBUS_SET_OOM (error);
00614       goto error;
00615     }
00616   
00617   /* FIXME this is racy; we need a save_file_exclusively
00618    * function. But in practice this should be fine for now.
00619    *
00620    * - first be sure we can create the file and it
00621    *   doesn't exist by creating it empty with O_EXCL
00622    * - then create it by creating a temporary file and
00623    *   overwriting atomically with rename()
00624    */
00625   if (!_dbus_create_file_exclusively (filename, error))
00626     goto error;
00627 
00628   if (!_dbus_string_append_byte (&encoded, '\n'))
00629     {
00630       _DBUS_SET_OOM (error);
00631       goto error;
00632     }
00633   
00634   if (!_dbus_string_save_to_file (&encoded, filename, error))
00635     goto error;
00636 
00637   if (!_dbus_make_file_world_readable (filename, error))
00638     goto error;
00639 
00640   _dbus_string_free (&encoded);
00641 
00642   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00643   return TRUE;
00644   
00645  error:
00646   _DBUS_ASSERT_ERROR_IS_SET (error);
00647   _dbus_string_free (&encoded);
00648   return FALSE;        
00649 }
00650 
00661 dbus_bool_t
00662 _dbus_read_uuid_file (const DBusString *filename,
00663                       DBusGUID         *uuid,
00664                       dbus_bool_t       create_if_not_found,
00665                       DBusError        *error)
00666 {
00667   DBusError read_error;
00668 
00669   dbus_error_init (&read_error);
00670 
00671   if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
00672     return TRUE;
00673 
00674   if (!create_if_not_found)
00675     {
00676       dbus_move_error (&read_error, error);
00677       return FALSE;
00678     }
00679 
00680   /* If the file exists and contains junk, we want to keep that error
00681    * message instead of overwriting it with a "file exists" error
00682    * message when we try to write
00683    */
00684   if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
00685     {
00686       dbus_move_error (&read_error, error);
00687       return FALSE;
00688     }
00689   else
00690     {
00691       dbus_error_free (&read_error);
00692       return _dbus_create_uuid_file_exclusively (filename, uuid, error);
00693     }
00694 }
00695 
00696 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
00697 static int machine_uuid_initialized_generation = 0;
00698 static DBusGUID machine_uuid;
00699 
00710 dbus_bool_t
00711 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
00712 {
00713   dbus_bool_t ok;
00714   
00715   _DBUS_LOCK (machine_uuid);
00716   if (machine_uuid_initialized_generation != _dbus_current_generation)
00717     {
00718       DBusError error;
00719       dbus_error_init (&error);
00720       if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
00721                                           &error))
00722         {          
00723 #ifndef DBUS_BUILD_TESTS
00724           /* For the test suite, we may not be installed so just continue silently
00725            * here. But in a production build, we want to be nice and loud about
00726            * this.
00727            */
00728           _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
00729                                    "See the manual page for dbus-uuidgen to correct this issue.\n",
00730                                    error.message);
00731 #endif
00732           
00733           dbus_error_free (&error);
00734           
00735           _dbus_generate_uuid (&machine_uuid);
00736         }
00737     }
00738 
00739   ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
00740 
00741   _DBUS_UNLOCK (machine_uuid);
00742 
00743   return ok;
00744 }
00745 
00746 #ifdef DBUS_BUILD_TESTS
00747 
00753 const char *
00754 _dbus_header_field_to_string (int header_field)
00755 {
00756   switch (header_field)
00757     {
00758     case DBUS_HEADER_FIELD_INVALID:
00759       return "invalid";
00760     case DBUS_HEADER_FIELD_PATH:
00761       return "path";
00762     case DBUS_HEADER_FIELD_INTERFACE:
00763       return "interface";
00764     case DBUS_HEADER_FIELD_MEMBER:
00765       return "member";
00766     case DBUS_HEADER_FIELD_ERROR_NAME:
00767       return "error-name";
00768     case DBUS_HEADER_FIELD_REPLY_SERIAL:
00769       return "reply-serial";
00770     case DBUS_HEADER_FIELD_DESTINATION:
00771       return "destination";
00772     case DBUS_HEADER_FIELD_SENDER:
00773       return "sender";
00774     case DBUS_HEADER_FIELD_SIGNATURE:
00775       return "signature";
00776     default:
00777       return "unknown";
00778     }
00779 }
00780 #endif /* DBUS_BUILD_TESTS */
00781 
00782 #ifndef DBUS_DISABLE_CHECKS
00783 
00784 const char _dbus_return_if_fail_warning_format[] =
00785 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
00786 "This is normally a bug in some application using the D-Bus library.\n";
00787 #endif
00788 
00789 #ifndef DBUS_DISABLE_ASSERT
00790 
00802 void
00803 _dbus_real_assert (dbus_bool_t  condition,
00804                    const char  *condition_text,
00805                    const char  *file,
00806                    int          line,
00807                    const char  *func)
00808 {
00809   if (_DBUS_UNLIKELY (!condition))
00810     {
00811       _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
00812                   _dbus_getpid (), condition_text, file, line, func);
00813       _dbus_abort ();
00814     }
00815 }
00816 
00827 void
00828 _dbus_real_assert_not_reached (const char *explanation,
00829                                const char *file,
00830                                int         line)
00831 {
00832   _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
00833               file, line, _dbus_getpid (), explanation);
00834   _dbus_abort ();
00835 }
00836 #endif /* DBUS_DISABLE_ASSERT */
00837   
00838 #ifdef DBUS_BUILD_TESTS
00839 static dbus_bool_t
00840 run_failing_each_malloc (int                    n_mallocs,
00841                          const char            *description,
00842                          DBusTestMemoryFunction func,
00843                          void                  *data)
00844 {
00845   n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
00846   
00847   while (n_mallocs >= 0)
00848     {      
00849       _dbus_set_fail_alloc_counter (n_mallocs);
00850 
00851       _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
00852                      description, n_mallocs,
00853                      _dbus_get_fail_alloc_failures ());
00854 
00855       if (!(* func) (data))
00856         return FALSE;
00857       
00858       n_mallocs -= 1;
00859     }
00860 
00861   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00862 
00863   return TRUE;
00864 }                        
00865 
00879 dbus_bool_t
00880 _dbus_test_oom_handling (const char             *description,
00881                          DBusTestMemoryFunction  func,
00882                          void                   *data)
00883 {
00884   int approx_mallocs;
00885   const char *setting;
00886   int max_failures_to_try;
00887   int i;
00888 
00889   /* Run once to see about how many mallocs are involved */
00890   
00891   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00892 
00893   _dbus_verbose ("Running once to count mallocs\n");
00894   
00895   if (!(* func) (data))
00896     return FALSE;
00897   
00898   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
00899 
00900   _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
00901                  description, approx_mallocs);
00902 
00903   setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
00904   if (setting != NULL)
00905     {
00906       DBusString str;
00907       long v;
00908       _dbus_string_init_const (&str, setting);
00909       v = 4;
00910       if (!_dbus_string_parse_int (&str, 0, &v, NULL))
00911         _dbus_warn ("couldn't parse '%s' as integer\n", setting);
00912       max_failures_to_try = v;
00913     }
00914   else
00915     {
00916       max_failures_to_try = 4;
00917     }
00918 
00919   i = setting ? max_failures_to_try - 1 : 1;
00920   while (i < max_failures_to_try)
00921     {
00922       _dbus_set_fail_alloc_failures (i);
00923       if (!run_failing_each_malloc (approx_mallocs, description, func, data))
00924         return FALSE;
00925       ++i;
00926     }
00927   
00928   _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
00929                  description);
00930 
00931   return TRUE;
00932 }
00933 #endif /* DBUS_BUILD_TESTS */
00934 

Generated on Fri Sep 21 18:12:11 2007 for D-Bus by  doxygen 1.5.1