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., 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 = FALSE;
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_pid_for_log ());
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 #ifdef DBUS_WIN
00299 #define inline
00300 #endif
00301 
00302 static inline void
00303 _dbus_verbose_init (void)
00304 {
00305   if (!verbose_initted)
00306     {
00307       const char *p = _dbus_getenv ("DBUS_VERBOSE"); 
00308       verbose = p != NULL && *p == '1';
00309       verbose_initted = TRUE;
00310     }
00311 }
00312 
00318 dbus_bool_t
00319 _dbus_is_verbose_real (void)
00320 {
00321   _dbus_verbose_init ();
00322   return verbose;
00323 }
00324 
00333 void
00334 _dbus_verbose_real (const char *format,
00335                     ...)
00336 {
00337   va_list args;
00338   static dbus_bool_t need_pid = TRUE;
00339   int len;
00340   
00341   /* things are written a bit oddly here so that
00342    * in the non-verbose case we just have the one
00343    * conditional and return immediately.
00344    */
00345   if (!_dbus_is_verbose_real())
00346     return;
00347 
00348   /* Print out pid before the line */
00349   if (need_pid)
00350     {
00351 #if PTHREAD_IN_VERBOSE
00352       fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ());
00353 #else
00354       fprintf (stderr, "%lu: ", _dbus_pid_for_log ());
00355 #endif
00356     }
00357       
00358 
00359   /* Only print pid again if the next line is a new line */
00360   len = strlen (format);
00361   if (format[len-1] == '\n')
00362     need_pid = TRUE;
00363   else
00364     need_pid = FALSE;
00365   
00366   va_start (args, format);
00367   vfprintf (stderr, format, args);
00368   va_end (args);
00369 
00370   fflush (stderr);
00371 }
00372 
00379 void
00380 _dbus_verbose_reset_real (void)
00381 {
00382   verbose_initted = FALSE;
00383 }
00384 
00385 #endif /* DBUS_ENABLE_VERBOSE_MODE */
00386 
00395 char*
00396 _dbus_strdup (const char *str)
00397 {
00398   size_t len;
00399   char *copy;
00400   
00401   if (str == NULL)
00402     return NULL;
00403   
00404   len = strlen (str);
00405 
00406   copy = dbus_malloc (len + 1);
00407   if (copy == NULL)
00408     return NULL;
00409 
00410   memcpy (copy, str, len + 1);
00411   
00412   return copy;
00413 }
00414 
00423 void*
00424 _dbus_memdup (const void  *mem,
00425               size_t       n_bytes)
00426 {
00427   void *copy;
00428 
00429   copy = dbus_malloc (n_bytes);
00430   if (copy == NULL)
00431     return NULL;
00432 
00433   memcpy (copy, mem, n_bytes);
00434   
00435   return copy;
00436 }
00437 
00446 char**
00447 _dbus_dup_string_array (const char **array)
00448 {
00449   int len;
00450   int i;
00451   char **copy;
00452   
00453   if (array == NULL)
00454     return NULL;
00455 
00456   for (len = 0; array[len] != NULL; ++len)
00457     ;
00458 
00459   copy = dbus_new0 (char*, len + 1);
00460   if (copy == NULL)
00461     return NULL;
00462 
00463   i = 0;
00464   while (i < len)
00465     {
00466       copy[i] = _dbus_strdup (array[i]);
00467       if (copy[i] == NULL)
00468         {
00469           dbus_free_string_array (copy);
00470           return NULL;
00471         }
00472 
00473       ++i;
00474     }
00475 
00476   return copy;
00477 }
00478 
00486 dbus_bool_t
00487 _dbus_string_array_contains (const char **array,
00488                              const char  *str)
00489 {
00490   int i;
00491 
00492   i = 0;
00493   while (array[i] != NULL)
00494     {
00495       if (strcmp (array[i], str) == 0)
00496         return TRUE;
00497       ++i;
00498     }
00499 
00500   return FALSE;
00501 }
00502 
00509 void
00510 _dbus_generate_uuid (DBusGUID *uuid)
00511 {
00512   long now;
00513 
00514   _dbus_get_current_time (&now, NULL);
00515 
00516   uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
00517   
00518   _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4);
00519 }
00520 
00528 dbus_bool_t
00529 _dbus_uuid_encode (const DBusGUID *uuid,
00530                    DBusString     *encoded)
00531 {
00532   DBusString binary;
00533   _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00534   return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
00535 }
00536 
00537 static dbus_bool_t
00538 _dbus_read_uuid_file_without_creating (const DBusString *filename,
00539                                        DBusGUID         *uuid,
00540                                        DBusError        *error)
00541 {
00542   DBusString contents;
00543   DBusString decoded;
00544   int end;
00545   
00546   _dbus_string_init (&contents);
00547   _dbus_string_init (&decoded);
00548   
00549   if (!_dbus_file_get_contents (&contents, filename, error))
00550     goto error;
00551 
00552   _dbus_string_chop_white (&contents);
00553 
00554   if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
00555     {
00556       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00557                       "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
00558                       _dbus_string_get_const_data (filename),
00559                       DBUS_UUID_LENGTH_HEX,
00560                       _dbus_string_get_length (&contents));
00561       goto error;
00562     }
00563 
00564   if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
00565     {
00566       _DBUS_SET_OOM (error);
00567       goto error;
00568     }
00569 
00570   if (end == 0)
00571     {
00572       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00573                       "UUID file '%s' contains invalid hex data",
00574                       _dbus_string_get_const_data (filename));
00575       goto error;
00576     }
00577 
00578   if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
00579     {
00580       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
00581                       "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
00582                       _dbus_string_get_const_data (filename),
00583                       _dbus_string_get_length (&decoded),
00584                       DBUS_UUID_LENGTH_BYTES);
00585       goto error;
00586     }
00587 
00588   _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
00589 
00590   _dbus_string_free (&decoded);
00591   _dbus_string_free (&contents);
00592 
00593   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00594 
00595   return TRUE;
00596   
00597  error:
00598   _DBUS_ASSERT_ERROR_IS_SET (error);
00599   _dbus_string_free (&contents);
00600   _dbus_string_free (&decoded);
00601   return FALSE;
00602 }
00603 
00604 static dbus_bool_t
00605 _dbus_create_uuid_file_exclusively (const DBusString *filename,
00606                                     DBusGUID         *uuid,
00607                                     DBusError        *error)
00608 {
00609   DBusString encoded;
00610 
00611   _dbus_string_init (&encoded);
00612 
00613   _dbus_generate_uuid (uuid);
00614   
00615   if (!_dbus_uuid_encode (uuid, &encoded))
00616     {
00617       _DBUS_SET_OOM (error);
00618       goto error;
00619     }
00620   
00621   /* FIXME this is racy; we need a save_file_exclusively
00622    * function. But in practice this should be fine for now.
00623    *
00624    * - first be sure we can create the file and it
00625    *   doesn't exist by creating it empty with O_EXCL
00626    * - then create it by creating a temporary file and
00627    *   overwriting atomically with rename()
00628    */
00629   if (!_dbus_create_file_exclusively (filename, error))
00630     goto error;
00631 
00632   if (!_dbus_string_append_byte (&encoded, '\n'))
00633     {
00634       _DBUS_SET_OOM (error);
00635       goto error;
00636     }
00637   
00638   if (!_dbus_string_save_to_file (&encoded, filename, error))
00639     goto error;
00640 
00641   if (!_dbus_make_file_world_readable (filename, error))
00642     goto error;
00643 
00644   _dbus_string_free (&encoded);
00645 
00646   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00647   return TRUE;
00648   
00649  error:
00650   _DBUS_ASSERT_ERROR_IS_SET (error);
00651   _dbus_string_free (&encoded);
00652   return FALSE;        
00653 }
00654 
00665 dbus_bool_t
00666 _dbus_read_uuid_file (const DBusString *filename,
00667                       DBusGUID         *uuid,
00668                       dbus_bool_t       create_if_not_found,
00669                       DBusError        *error)
00670 {
00671   DBusError read_error;
00672 
00673   dbus_error_init (&read_error);
00674 
00675   if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
00676     return TRUE;
00677 
00678   if (!create_if_not_found)
00679     {
00680       dbus_move_error (&read_error, error);
00681       return FALSE;
00682     }
00683 
00684   /* If the file exists and contains junk, we want to keep that error
00685    * message instead of overwriting it with a "file exists" error
00686    * message when we try to write
00687    */
00688   if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
00689     {
00690       dbus_move_error (&read_error, error);
00691       return FALSE;
00692     }
00693   else
00694     {
00695       dbus_error_free (&read_error);
00696       return _dbus_create_uuid_file_exclusively (filename, uuid, error);
00697     }
00698 }
00699 
00700 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
00701 static int machine_uuid_initialized_generation = 0;
00702 static DBusGUID machine_uuid;
00703 
00714 dbus_bool_t
00715 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
00716 {
00717   dbus_bool_t ok;
00718   
00719   _DBUS_LOCK (machine_uuid);
00720   if (machine_uuid_initialized_generation != _dbus_current_generation)
00721     {
00722       DBusError error;
00723       dbus_error_init (&error);
00724       if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
00725                                           &error))
00726         {          
00727 #ifndef DBUS_BUILD_TESTS
00728           /* For the test suite, we may not be installed so just continue silently
00729            * here. But in a production build, we want to be nice and loud about
00730            * this.
00731            */
00732           _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
00733                                    "See the manual page for dbus-uuidgen to correct this issue.\n",
00734                                    error.message);
00735 #endif
00736           
00737           dbus_error_free (&error);
00738           
00739           _dbus_generate_uuid (&machine_uuid);
00740         }
00741     }
00742 
00743   ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
00744 
00745   _DBUS_UNLOCK (machine_uuid);
00746 
00747   return ok;
00748 }
00749 
00750 #ifdef DBUS_BUILD_TESTS
00751 
00757 const char *
00758 _dbus_header_field_to_string (int header_field)
00759 {
00760   switch (header_field)
00761     {
00762     case DBUS_HEADER_FIELD_INVALID:
00763       return "invalid";
00764     case DBUS_HEADER_FIELD_PATH:
00765       return "path";
00766     case DBUS_HEADER_FIELD_INTERFACE:
00767       return "interface";
00768     case DBUS_HEADER_FIELD_MEMBER:
00769       return "member";
00770     case DBUS_HEADER_FIELD_ERROR_NAME:
00771       return "error-name";
00772     case DBUS_HEADER_FIELD_REPLY_SERIAL:
00773       return "reply-serial";
00774     case DBUS_HEADER_FIELD_DESTINATION:
00775       return "destination";
00776     case DBUS_HEADER_FIELD_SENDER:
00777       return "sender";
00778     case DBUS_HEADER_FIELD_SIGNATURE:
00779       return "signature";
00780     default:
00781       return "unknown";
00782     }
00783 }
00784 #endif /* DBUS_BUILD_TESTS */
00785 
00786 #ifndef DBUS_DISABLE_CHECKS
00787 
00788 const char _dbus_return_if_fail_warning_format[] =
00789 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
00790 "This is normally a bug in some application using the D-Bus library.\n";
00791 #endif
00792 
00793 #ifndef DBUS_DISABLE_ASSERT
00794 
00806 void
00807 _dbus_real_assert (dbus_bool_t  condition,
00808                    const char  *condition_text,
00809                    const char  *file,
00810                    int          line,
00811                    const char  *func)
00812 {
00813   if (_DBUS_UNLIKELY (!condition))
00814     {
00815       _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
00816                   _dbus_pid_for_log (), condition_text, file, line, func);
00817       _dbus_abort ();
00818     }
00819 }
00820 
00831 void
00832 _dbus_real_assert_not_reached (const char *explanation,
00833                                const char *file,
00834                                int         line)
00835 {
00836   _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
00837               file, line, _dbus_pid_for_log (), explanation);
00838   _dbus_abort ();
00839 }
00840 #endif /* DBUS_DISABLE_ASSERT */
00841   
00842 #ifdef DBUS_BUILD_TESTS
00843 static dbus_bool_t
00844 run_failing_each_malloc (int                    n_mallocs,
00845                          const char            *description,
00846                          DBusTestMemoryFunction func,
00847                          void                  *data)
00848 {
00849   n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
00850   
00851   while (n_mallocs >= 0)
00852     {      
00853       _dbus_set_fail_alloc_counter (n_mallocs);
00854 
00855       _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
00856                      description, n_mallocs,
00857                      _dbus_get_fail_alloc_failures ());
00858 
00859       if (!(* func) (data))
00860         return FALSE;
00861       
00862       n_mallocs -= 1;
00863     }
00864 
00865   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00866 
00867   return TRUE;
00868 }                        
00869 
00883 dbus_bool_t
00884 _dbus_test_oom_handling (const char             *description,
00885                          DBusTestMemoryFunction  func,
00886                          void                   *data)
00887 {
00888   int approx_mallocs;
00889   const char *setting;
00890   int max_failures_to_try;
00891   int i;
00892 
00893   /* Run once to see about how many mallocs are involved */
00894   
00895   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
00896 
00897   _dbus_verbose ("Running once to count mallocs\n");
00898   
00899   if (!(* func) (data))
00900     return FALSE;
00901   
00902   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
00903 
00904   _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
00905                  description, approx_mallocs);
00906 
00907   setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
00908   if (setting != NULL)
00909     {
00910       DBusString str;
00911       long v;
00912       _dbus_string_init_const (&str, setting);
00913       v = 4;
00914       if (!_dbus_string_parse_int (&str, 0, &v, NULL))
00915         _dbus_warn ("couldn't parse '%s' as integer\n", setting);
00916       max_failures_to_try = v;
00917     }
00918   else
00919     {
00920       max_failures_to_try = 4;
00921     }
00922 
00923   i = setting ? max_failures_to_try - 1 : 1;
00924   while (i < max_failures_to_try)
00925     {
00926       _dbus_set_fail_alloc_failures (i);
00927       if (!run_failing_each_malloc (approx_mallocs, description, func, data))
00928         return FALSE;
00929       ++i;
00930     }
00931   
00932   _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
00933                  description);
00934 
00935   return TRUE;
00936 }
00937 #endif /* DBUS_BUILD_TESTS */
00938 

Generated on Mon Dec 14 22:26:11 2009 for D-Bus by  doxygen 1.4.7