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   if (!_dbus_string_init (&contents))
00543     {
00544       _DBUS_SET_OOM (error);
00545       return FALSE;
00546     }
00547 
00548   if (!_dbus_string_init (&decoded))
00549     {
00550       _dbus_string_free (&contents);
00551       _DBUS_SET_OOM (error);
00552       return FALSE;
00553     }
00554   
00555   if (!_dbus_file_get_contents (&contents, filename, error))
00556     goto error;
00557 
00558   _dbus_string_chop_white (&contents);
00559 
00560   if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
00561     {
00562       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00563                       "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
00564                       _dbus_string_get_const_data (filename),
00565                       DBUS_UUID_LENGTH_HEX,
00566                       _dbus_string_get_length (&contents));
00567       goto error;
00568     }
00569 
00570   if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
00571     {
00572       _DBUS_SET_OOM (error);
00573       goto error;
00574     }
00575 
00576   if (end == 0)
00577     {
00578       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00579                       "UUID file '%s' contains invalid hex data",
00580                       _dbus_string_get_const_data (filename));
00581       goto error;
00582     }
00583 
00584   if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
00585     {
00586       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00587                       "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
00588                       _dbus_string_get_const_data (filename),
00589                       _dbus_string_get_length (&decoded),
00590                       DBUS_UUID_LENGTH_BYTES);
00591       goto error;
00592     }
00593 
00594   _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00595 
00596   _dbus_string_free (&decoded);
00597   _dbus_string_free (&contents);
00598 
00599   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00600 
00601   return TRUE;
00602   
00603  error:
00604   _DBUS_ASSERT_ERROR_IS_SET (error);
00605   _dbus_string_free (&contents);
00606   _dbus_string_free (&decoded);
00607   return FALSE;
00608 }
00609 
00610 static dbus_bool_t
00611 _dbus_create_uuid_file_exclusively (const DBusString *filename,
00612                                     DBusGUID         *uuid,
00613                                     DBusError        *error)
00614 {
00615   DBusString encoded;
00616 
00617   if (!_dbus_string_init (&encoded))
00618     {
00619       _DBUS_SET_OOM (error);
00620       return FALSE;
00621     }
00622 
00623   _dbus_generate_uuid (uuid);
00624   
00625   if (!_dbus_uuid_encode (uuid, &encoded))
00626     {
00627       _DBUS_SET_OOM (error);
00628       goto error;
00629     }
00630   
00631   /* FIXME this is racy; we need a save_file_exclusively
00632    * function. But in practice this should be fine for now.
00633    *
00634    * - first be sure we can create the file and it
00635    *   doesn't exist by creating it empty with O_EXCL
00636    * - then create it by creating a temporary file and
00637    *   overwriting atomically with rename()
00638    */
00639   if (!_dbus_create_file_exclusively (filename, error))
00640     goto error;
00641 
00642   if (!_dbus_string_append_byte (&encoded, '\n'))
00643     {
00644       _DBUS_SET_OOM (error);
00645       goto error;
00646     }
00647   
00648   if (!_dbus_string_save_to_file (&encoded, filename, error))
00649     goto error;
00650 
00651   if (!_dbus_make_file_world_readable (filename, error))
00652     goto error;
00653 
00654   _dbus_string_free (&encoded);
00655 
00656   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00657   return TRUE;
00658   
00659  error:
00660   _DBUS_ASSERT_ERROR_IS_SET (error);
00661   _dbus_string_free (&encoded);
00662   return FALSE;        
00663 }
00664 
00675 dbus_bool_t
00676 _dbus_read_uuid_file (const DBusString *filename,
00677                       DBusGUID         *uuid,
00678                       dbus_bool_t       create_if_not_found,
00679                       DBusError        *error)
00680 {
00681   DBusError read_error;
00682 
00683   dbus_error_init (&read_error);
00684 
00685   if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
00686     return TRUE;
00687 
00688   if (!create_if_not_found)
00689     {
00690       dbus_move_error (&read_error, error);
00691       return FALSE;
00692     }
00693 
00694   /* If the file exists and contains junk, we want to keep that error
00695    * message instead of overwriting it with a "file exists" error
00696    * message when we try to write
00697    */
00698   if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
00699     {
00700       dbus_move_error (&read_error, error);
00701       return FALSE;
00702     }
00703   else
00704     {
00705       dbus_error_free (&read_error);
00706       return _dbus_create_uuid_file_exclusively (filename, uuid, error);
00707     }
00708 }
00709 
00710 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
00711 static int machine_uuid_initialized_generation = 0;
00712 static DBusGUID machine_uuid;
00713 
00724 dbus_bool_t
00725 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
00726 {
00727   dbus_bool_t ok;
00728   
00729   _DBUS_LOCK (machine_uuid);
00730   if (machine_uuid_initialized_generation != _dbus_current_generation)
00731     {
00732       DBusError error;
00733       dbus_error_init (&error);
00734       if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
00735                                           &error))
00736         {          
00737 #ifndef DBUS_BUILD_TESTS
00738           /* For the test suite, we may not be installed so just continue silently
00739            * here. But in a production build, we want to be nice and loud about
00740            * this.
00741            */
00742           _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
00743                                    "See the manual page for dbus-uuidgen to correct this issue.\n",
00744                                    error.message);
00745 #endif
00746           
00747           dbus_error_free (&error);
00748           
00749           _dbus_generate_uuid (&machine_uuid);
00750         }
00751     }
00752 
00753   ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
00754 
00755   _DBUS_UNLOCK (machine_uuid);
00756 
00757   return ok;
00758 }
00759 
00760 #ifdef DBUS_BUILD_TESTS
00761 
00767 const char *
00768 _dbus_header_field_to_string (int header_field)
00769 {
00770   switch (header_field)
00771     {
00772     case DBUS_HEADER_FIELD_INVALID:
00773       return "invalid";
00774     case DBUS_HEADER_FIELD_PATH:
00775       return "path";
00776     case DBUS_HEADER_FIELD_INTERFACE:
00777       return "interface";
00778     case DBUS_HEADER_FIELD_MEMBER:
00779       return "member";
00780     case DBUS_HEADER_FIELD_ERROR_NAME:
00781       return "error-name";
00782     case DBUS_HEADER_FIELD_REPLY_SERIAL:
00783       return "reply-serial";
00784     case DBUS_HEADER_FIELD_DESTINATION:
00785       return "destination";
00786     case DBUS_HEADER_FIELD_SENDER:
00787       return "sender";
00788     case DBUS_HEADER_FIELD_SIGNATURE:
00789       return "signature";
00790     default:
00791       return "unknown";
00792     }
00793 }
00794 #endif /* DBUS_BUILD_TESTS */
00795 
00796 #ifndef DBUS_DISABLE_CHECKS
00797 
00798 const char _dbus_return_if_fail_warning_format[] =
00799 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
00800 "This is normally a bug in some application using the D-Bus library.\n";
00801 #endif
00802 
00803 #ifndef DBUS_DISABLE_ASSERT
00804 
00816 void
00817 _dbus_real_assert (dbus_bool_t  condition,
00818                    const char  *condition_text,
00819                    const char  *file,
00820                    int          line,
00821                    const char  *func)
00822 {
00823   if (_DBUS_UNLIKELY (!condition))
00824     {
00825       _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
00826                   _dbus_getpid (), condition_text, file, line, func);
00827       _dbus_abort ();
00828     }
00829 }
00830 
00841 void
00842 _dbus_real_assert_not_reached (const char *explanation,
00843                                const char *file,
00844                                int         line)
00845 {
00846   _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
00847               file, line, _dbus_getpid (), explanation);
00848   _dbus_abort ();
00849 }
00850 #endif /* DBUS_DISABLE_ASSERT */
00851   
00852 #ifdef DBUS_BUILD_TESTS
00853 static dbus_bool_t
00854 run_failing_each_malloc (int                    n_mallocs,
00855                          const char            *description,
00856                          DBusTestMemoryFunction func,
00857                          void                  *data)
00858 {
00859   n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
00860   
00861   while (n_mallocs >= 0)
00862     {      
00863       _dbus_set_fail_alloc_counter (n_mallocs);
00864 
00865       _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
00866                      description, n_mallocs,
00867                      _dbus_get_fail_alloc_failures ());
00868 
00869       if (!(* func) (data))
00870         return FALSE;
00871       
00872       n_mallocs -= 1;
00873     }
00874 
00875   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00876 
00877   return TRUE;
00878 }                        
00879 
00893 dbus_bool_t
00894 _dbus_test_oom_handling (const char             *description,
00895                          DBusTestMemoryFunction  func,
00896                          void                   *data)
00897 {
00898   int approx_mallocs;
00899   const char *setting;
00900   int max_failures_to_try;
00901   int i;
00902 
00903   /* Run once to see about how many mallocs are involved */
00904   
00905   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00906 
00907   _dbus_verbose ("Running once to count mallocs\n");
00908   
00909   if (!(* func) (data))
00910     return FALSE;
00911   
00912   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
00913 
00914   _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
00915                  description, approx_mallocs);
00916 
00917   setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
00918   if (setting != NULL)
00919     {
00920       DBusString str;
00921       long v;
00922       _dbus_string_init_const (&str, setting);
00923       v = 4;
00924       if (!_dbus_string_parse_int (&str, 0, &v, NULL))
00925         _dbus_warn ("couldn't parse '%s' as integer\n", setting);
00926       max_failures_to_try = v;
00927     }
00928   else
00929     {
00930       max_failures_to_try = 4;
00931     }
00932 
00933   i = setting ? max_failures_to_try - 1 : 1;
00934   while (i < max_failures_to_try)
00935     {
00936       _dbus_set_fail_alloc_failures (i);
00937       if (!run_failing_each_malloc (approx_mallocs, description, func, data))
00938         return FALSE;
00939       ++i;
00940     }
00941   
00942   _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
00943                  description);
00944 
00945   return TRUE;
00946 }
00947 #endif /* DBUS_BUILD_TESTS */
00948 

Generated on Tue Apr 15 15:53:56 2008 for D-Bus by  doxygen 1.5.1