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

Generated on Wed Nov 22 01:12:05 2006 for D-Bus by  doxygen 1.4.6