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

Generated on Tue Feb 24 16:40:39 2009 for D-Bus by  doxygen 1.5.1