2006-10-01 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-internals.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-internals.c  random utility stuff (internal to D-Bus implementation)
3  *
4  * Copyright (C) 2002, 2003  Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include "dbus-internals.h"
24 #include "dbus-protocol.h"
25 #include "dbus-test.h"
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 /**
32  * @defgroup DBusInternals D-Bus internal implementation details
33  * @brief Documentation useful when developing or debugging D-Bus itself.
34  * 
35  */
36
37 /**
38  * @defgroup DBusInternalsUtils Utilities and portability
39  * @ingroup DBusInternals
40  * @brief Utility functions (_dbus_assert(), _dbus_warn(), etc.)
41  * @{
42  */
43
44 /**
45  * @def _dbus_assert
46  *
47  * Aborts with an error message if the condition is false.
48  * 
49  * @param condition condition which must be true.
50  */
51
52 /**
53  * @def _dbus_assert_not_reached
54  *
55  * Aborts with an error message if called.
56  * The given explanation will be printed.
57  * 
58  * @param explanation explanation of what happened if the code was reached.
59  */
60
61 /**
62  * @def _DBUS_N_ELEMENTS
63  *
64  * Computes the number of elements in a fixed-size array using
65  * sizeof().
66  *
67  * @param array the array to count elements in.
68  */
69
70 /**
71  * @def _DBUS_POINTER_TO_INT
72  *
73  * Safely casts a void* to an integer; should only be used on void*
74  * that actually contain integers, for example one created with
75  * _DBUS_INT_TO_POINTER.  Only guaranteed to preserve 32 bits.
76  * (i.e. it's used to store 32-bit ints in pointers, but
77  * can't be used to store 64-bit pointers in ints.)
78  *
79  * @param pointer pointer to extract an integer from.
80  */
81 /**
82  * @def _DBUS_INT_TO_POINTER
83  *
84  * Safely stuffs an integer into a pointer, to be extracted later with
85  * _DBUS_POINTER_TO_INT. Only guaranteed to preserve 32 bits.
86  *
87  * @param integer the integer to stuff into a pointer.
88  */
89 /**
90  * @def _DBUS_ZERO
91  *
92  * Sets all bits in an object to zero.
93  *
94  * @param object the object to be zeroed.
95  */
96 /**
97  * @def _DBUS_INT16_MIN
98  *
99  * Minimum value of type "int16"
100  */
101 /**
102  * @def _DBUS_INT16_MAX
103  *
104  * Maximum value of type "int16"
105  */
106 /**
107  * @def _DBUS_UINT16_MAX
108  *
109  * Maximum value of type "uint16"
110  */
111
112 /**
113  * @def _DBUS_INT32_MIN
114  *
115  * Minimum value of type "int32"
116  */
117 /**
118  * @def _DBUS_INT32_MAX
119  *
120  * Maximum value of type "int32"
121  */
122 /**
123  * @def _DBUS_UINT32_MAX
124  *
125  * Maximum value of type "uint32"
126  */
127
128 /**
129  * @def _DBUS_INT_MIN
130  *
131  * Minimum value of type "int"
132  */
133 /**
134  * @def _DBUS_INT_MAX
135  *
136  * Maximum value of type "int"
137  */
138 /**
139  * @def _DBUS_UINT_MAX
140  *
141  * Maximum value of type "uint"
142  */
143
144 /**
145  * @typedef DBusForeachFunction
146  * 
147  * Used to iterate over each item in a collection, such as
148  * a DBusList.
149  */
150
151 /**
152  * @def _DBUS_LOCK_NAME
153  *
154  * Expands to name of a global lock variable.
155  */
156
157 /**
158  * @def _DBUS_DEFINE_GLOBAL_LOCK
159  *
160  * Defines a global lock variable with the given name.
161  * The lock must be added to the list to initialize
162  * in dbus_threads_init().
163  */
164
165 /**
166  * @def _DBUS_DECLARE_GLOBAL_LOCK
167  *
168  * Expands to declaration of a global lock defined
169  * with _DBUS_DEFINE_GLOBAL_LOCK.
170  * The lock must be added to the list to initialize
171  * in dbus_threads_init().
172  */
173
174 /**
175  * @def _DBUS_LOCK
176  *
177  * Locks a global lock
178  */
179
180 /**
181  * @def _DBUS_UNLOCK
182  *
183  * Unlocks a global lock
184  */
185
186 /**
187  * Fixed "out of memory" error message, just to avoid
188  * making up a different string every time and wasting
189  * space.
190  */
191 const char _dbus_no_memory_message[] = "Not enough memory";
192
193 static dbus_bool_t warn_initted = FALSE;
194 static dbus_bool_t fatal_warnings = FALSE;
195
196 /**
197  * Prints a warning message to stderr.
198  *
199  * @param format printf-style format string.
200  */
201 void
202 _dbus_warn (const char *format,
203             ...)
204 {
205   va_list args;
206
207   if (!warn_initted)
208     {
209       const char *s;
210       s = _dbus_getenv ("DBUS_FATAL_WARNINGS");
211       if (s && *s)
212         fatal_warnings = TRUE;
213
214       warn_initted = TRUE;
215     }
216   
217   va_start (args, format);
218   vfprintf (stderr, format, args);
219   va_end (args);
220
221   if (fatal_warnings)
222     {
223       fflush (stderr);
224       _dbus_abort ();
225     }
226 }
227
228 #ifdef DBUS_ENABLE_VERBOSE_MODE
229
230 static dbus_bool_t verbose_initted = FALSE;
231 static dbus_bool_t verbose = TRUE;
232
233 #define PTHREAD_IN_VERBOSE 0
234 #if PTHREAD_IN_VERBOSE
235 #include <pthread.h>
236 #endif
237
238 static inline void
239 _dbus_verbose_init (void)
240 {
241   if (!verbose_initted)
242     {
243       const char *p = _dbus_getenv ("DBUS_VERBOSE"); 
244       verbose = p != NULL && *p == '1';
245       verbose_initted = TRUE;
246     }
247 }
248
249 dbus_bool_t
250 _dbus_is_verbose_real (void)
251 {
252   _dbus_verbose_init ();
253   return verbose;
254 }
255
256 /**
257  * Prints a warning message to stderr
258  * if the user has enabled verbose mode.
259  * This is the real function implementation,
260  * use _dbus_verbose() macro in code.
261  *
262  * @param format printf-style format string.
263  */
264 void
265 _dbus_verbose_real (const char *format,
266                     ...)
267 {
268   va_list args;
269   static dbus_bool_t need_pid = TRUE;
270   int len;
271   
272   /* things are written a bit oddly here so that
273    * in the non-verbose case we just have the one
274    * conditional and return immediately.
275    */
276   if (!_dbus_is_verbose_real())
277     return;
278
279   /* Print out pid before the line */
280   if (need_pid)
281     {
282 #if PTHREAD_IN_VERBOSE
283       fprintf (stderr, "%lu: 0x%lx: ", _dbus_getpid (), pthread_self ());
284 #else
285       fprintf (stderr, "%lu: ", _dbus_getpid ());
286 #endif
287     }
288       
289
290   /* Only print pid again if the next line is a new line */
291   len = strlen (format);
292   if (format[len-1] == '\n')
293     need_pid = TRUE;
294   else
295     need_pid = FALSE;
296   
297   va_start (args, format);
298   vfprintf (stderr, format, args);
299   va_end (args);
300
301   fflush (stderr);
302 }
303
304 /**
305  * Reinitializes the verbose logging code, used
306  * as a hack in dbus-spawn.c so that a child
307  * process re-reads its pid
308  *
309  */
310 void
311 _dbus_verbose_reset_real (void)
312 {
313   verbose_initted = FALSE;
314 }
315
316 #endif /* DBUS_ENABLE_VERBOSE_MODE */
317
318 /**
319  * Duplicates a string. Result must be freed with
320  * dbus_free(). Returns #NULL if memory allocation fails.
321  * If the string to be duplicated is #NULL, returns #NULL.
322  * 
323  * @param str string to duplicate.
324  * @returns newly-allocated copy.
325  */
326 char*
327 _dbus_strdup (const char *str)
328 {
329   size_t len;
330   char *copy;
331   
332   if (str == NULL)
333     return NULL;
334   
335   len = strlen (str);
336
337   copy = dbus_malloc (len + 1);
338   if (copy == NULL)
339     return NULL;
340
341   memcpy (copy, str, len + 1);
342   
343   return copy;
344 }
345
346 /**
347  * Duplicates a block of memory. Returns
348  * #NULL on failure.
349  *
350  * @param mem memory to copy
351  * @param n_bytes number of bytes to copy
352  * @returns the copy
353  */
354 void*
355 _dbus_memdup (const void  *mem,
356               size_t       n_bytes)
357 {
358   void *copy;
359
360   copy = dbus_malloc (n_bytes);
361   if (copy == NULL)
362     return NULL;
363
364   memcpy (copy, mem, n_bytes);
365   
366   return copy;
367 }
368
369 /**
370  * Duplicates a string array. Result may be freed with
371  * dbus_free_string_array(). Returns #NULL if memory allocation fails.
372  * If the array to be duplicated is #NULL, returns #NULL.
373  * 
374  * @param array array to duplicate.
375  * @returns newly-allocated copy.
376  */
377 char**
378 _dbus_dup_string_array (const char **array)
379 {
380   int len;
381   int i;
382   char **copy;
383   
384   if (array == NULL)
385     return NULL;
386
387   for (len = 0; array[len] != NULL; ++len)
388     ;
389
390   copy = dbus_new0 (char*, len + 1);
391   if (copy == NULL)
392     return NULL;
393
394   i = 0;
395   while (i < len)
396     {
397       copy[i] = _dbus_strdup (array[i]);
398       if (copy[i] == NULL)
399         {
400           dbus_free_string_array (copy);
401           return NULL;
402         }
403
404       ++i;
405     }
406
407   return copy;
408 }
409
410 /**
411  * Checks whether a string array contains the given string.
412  * 
413  * @param array array to search.
414  * @param str string to look for
415  * @returns #TRUE if array contains string
416  */
417 dbus_bool_t
418 _dbus_string_array_contains (const char **array,
419                              const char  *str)
420 {
421   int i;
422
423   i = 0;
424   while (array[i] != NULL)
425     {
426       if (strcmp (array[i], str) == 0)
427         return TRUE;
428       ++i;
429     }
430
431   return FALSE;
432 }
433
434 /**
435  * Generates a new UUID. If you change how this is done,
436  * there's some text about it in the spec that should also change.
437  *
438  * @param uuid the uuid to initialize
439  */
440 void
441 _dbus_generate_uuid (DBusGUID *uuid)
442 {
443   long now;
444   char *p;
445   int ts_size;
446
447   _dbus_get_current_time (&now, NULL);
448
449   uuid->as_uint32s[0] = now;
450
451   ts_size = sizeof (uuid->as_uint32s[0]);
452   p = ((char*)uuid->as_bytes) + ts_size;
453   
454   _dbus_generate_random_bytes_buffer (p,
455                                       sizeof (uuid->as_bytes) - ts_size);
456 }
457
458 /**
459  * Hex-encode a UUID.
460  *
461  * @param uuid the uuid
462  * @param encoded string to append hex uuid to
463  * @returns #FALSE if no memory
464  */
465 dbus_bool_t
466 _dbus_uuid_encode (const DBusGUID *uuid,
467                    DBusString     *encoded)
468 {
469   DBusString binary;
470   _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
471   return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
472 }
473
474 static dbus_bool_t
475 _dbus_read_uuid_file_without_creating (const DBusString *filename,
476                                        DBusGUID         *uuid,
477                                        DBusError        *error)
478 {
479   DBusString contents;
480   DBusString decoded;
481   int end;
482   
483   _dbus_string_init (&contents);
484   _dbus_string_init (&decoded);
485   
486   if (!_dbus_file_get_contents (&contents, filename, error))
487     goto error;
488
489   _dbus_string_chop_white (&contents);
490
491   if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
492     {
493       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
494                       "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
495                       _dbus_string_get_const_data (filename),
496                       DBUS_UUID_LENGTH_HEX,
497                       _dbus_string_get_length (&contents));
498       goto error;
499     }
500
501   if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
502     {
503       _DBUS_SET_OOM (error);
504       goto error;
505     }
506
507   if (end == 0)
508     {
509       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
510                       "UUID file '%s' contains invalid hex data",
511                       _dbus_string_get_const_data (filename));
512       goto error;
513     }
514
515   if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
516     {
517       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
518                       "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
519                       _dbus_string_get_const_data (filename),
520                       _dbus_string_get_length (&decoded),
521                       DBUS_UUID_LENGTH_BYTES);
522       goto error;
523     }
524
525   _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
526
527   _dbus_string_free (&decoded);
528   _dbus_string_free (&contents);
529
530   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
531
532   return TRUE;
533   
534  error:
535   _DBUS_ASSERT_ERROR_IS_SET (error);
536   _dbus_string_free (&contents);
537   _dbus_string_free (&decoded);
538   return FALSE;
539 }
540
541 static dbus_bool_t
542 _dbus_create_uuid_file_exclusively (const DBusString *filename,
543                                     DBusGUID         *uuid,
544                                     DBusError        *error)
545 {
546   DBusString encoded;
547
548   _dbus_string_init (&encoded);
549
550   _dbus_generate_uuid (uuid);
551   
552   if (!_dbus_uuid_encode (uuid, &encoded))
553     {
554       _DBUS_SET_OOM (error);
555       goto error;
556     }
557   
558   /* FIXME this is racy; we need a save_file_exclusively
559    * function. But in practice this should be fine for now.
560    *
561    * - first be sure we can create the file and it
562    *   doesn't exist by creating it empty with O_EXCL
563    * - then create it by creating a temporary file and
564    *   overwriting atomically with rename()
565    */
566   if (!_dbus_create_file_exclusively (filename, error))
567     goto error;
568
569   if (!_dbus_string_append_byte (&encoded, '\n'))
570     {
571       _DBUS_SET_OOM (error);
572       goto error;
573     }
574   
575   if (!_dbus_string_save_to_file (&encoded, filename, error))
576     goto error;
577
578
579   _dbus_string_free (&encoded);
580
581   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
582   return TRUE;
583   
584  error:
585   _DBUS_ASSERT_ERROR_IS_SET (error);
586   _dbus_string_free (&encoded);
587   return FALSE;        
588 }
589
590 /**
591  * Reads (and optionally writes) a uuid to a file. Initializes the uuid
592  * unless an error is returned.
593  *
594  * @param filename the name of the file
595  * @param uuid uuid to be initialized with the loaded uuid
596  * @param create_if_not_found #TRUE to create a new uuid and save it if the file doesn't exist
597  * @param error the error return
598  * @returns #FALSE if the error is set
599  */
600 dbus_bool_t
601 _dbus_read_uuid_file (const DBusString *filename,
602                       DBusGUID         *uuid,
603                       dbus_bool_t       create_if_not_found,
604                       DBusError        *error)
605 {
606   DBusError read_error;
607
608   dbus_error_init (&read_error);
609
610   if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
611     return TRUE;
612
613   if (!create_if_not_found)
614     {
615       dbus_move_error (&read_error, error);
616       return FALSE;
617     }
618
619   /* If the file exists and contains junk, we want to keep that error
620    * message instead of overwriting it with a "file exists" error
621    * message when we try to write
622    */
623   if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
624     {
625       dbus_move_error (&read_error, error);
626       return FALSE;
627     }
628   else
629     {
630       dbus_error_free (&read_error);
631       return _dbus_create_uuid_file_exclusively (filename, uuid, error);
632     }
633 }
634
635 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
636 static int machine_uuid_initialized_generation = 0;
637 static DBusGUID machine_uuid;
638
639 /**
640  * Gets the hex-encoded UUID of the machine this function is
641  * executed on. This UUID is guaranteed to be the same for a given
642  * machine at least until it next reboots, though it also
643  * makes some effort to be the same forever, it may change if the
644  * machine is reconfigured or its hardware is modified.
645  * 
646  * @param uuid_str string to append hex-encoded machine uuid to
647  * @returns #FALSE if no memory
648  */
649 dbus_bool_t
650 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
651 {
652   dbus_bool_t ok;
653   
654   _DBUS_LOCK (machine_uuid);
655   if (machine_uuid_initialized_generation != _dbus_current_generation)
656     {
657       DBusError error;
658       dbus_error_init (&error);
659       if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
660                                           &error))
661         {          
662 #ifndef DBUS_BUILD_TESTS
663           /* For the test suite, we may not be installed so just continue silently
664            * here. But in a production build, we want to be nice and loud about
665            * this.
666            */
667           _dbus_warn ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n",
668                       error.message);
669           _dbus_warn ("See the manual page for dbus-uuidgen to correct this issue.\n");
670           _dbus_warn ("Continuing with a bogus made-up machine UUID, which may cause problems.");
671 #endif
672           
673           dbus_error_free (&error);
674           
675           _dbus_generate_uuid (&machine_uuid);
676         }
677     }
678
679   ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
680
681   _DBUS_UNLOCK (machine_uuid);
682
683   return ok;
684 }
685
686 #ifdef DBUS_BUILD_TESTS
687 /**
688  * Returns a string describing the given name.
689  *
690  * @param header_field the field to describe
691  * @returns a constant string describing the field
692  */
693 const char *
694 _dbus_header_field_to_string (int header_field)
695 {
696   switch (header_field)
697     {
698     case DBUS_HEADER_FIELD_INVALID:
699       return "invalid";
700     case DBUS_HEADER_FIELD_PATH:
701       return "path";
702     case DBUS_HEADER_FIELD_INTERFACE:
703       return "interface";
704     case DBUS_HEADER_FIELD_MEMBER:
705       return "member";
706     case DBUS_HEADER_FIELD_ERROR_NAME:
707       return "error-name";
708     case DBUS_HEADER_FIELD_REPLY_SERIAL:
709       return "reply-serial";
710     case DBUS_HEADER_FIELD_DESTINATION:
711       return "destination";
712     case DBUS_HEADER_FIELD_SENDER:
713       return "sender";
714     case DBUS_HEADER_FIELD_SIGNATURE:
715       return "signature";
716     default:
717       return "unknown";
718     }
719 }
720 #endif /* DBUS_BUILD_TESTS */
721
722 #ifndef DBUS_DISABLE_CHECKS
723 /** String used in _dbus_return_if_fail macro */
724 const char _dbus_return_if_fail_warning_format[] =
725 "%lu: arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
726 "This is normally a bug in some application using the D-Bus library.\n";
727 #endif
728
729 #ifndef DBUS_DISABLE_ASSERT
730 /**
731  * Internals of _dbus_assert(); it's a function
732  * rather than a macro with the inline code so
733  * that the assertion failure blocks don't show up
734  * in test suite coverage, and to shrink code size.
735  *
736  * @param condition TRUE if assertion succeeded
737  * @param condition_text condition as a string
738  * @param file file the assertion is in
739  * @param line line the assertion is in
740  * @param func function the assertion is in
741  */
742 void
743 _dbus_real_assert (dbus_bool_t  condition,
744                    const char  *condition_text,
745                    const char  *file,
746                    int          line,
747                    const char  *func)
748 {
749   if (_DBUS_UNLIKELY (!condition))
750     {
751       _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
752                   _dbus_getpid (), condition_text, file, line, func);
753       _dbus_abort ();
754     }
755 }
756
757 /**
758  * Internals of _dbus_assert_not_reached(); it's a function
759  * rather than a macro with the inline code so
760  * that the assertion failure blocks don't show up
761  * in test suite coverage, and to shrink code size.
762  *
763  * @param explanation what was reached that shouldn't have been
764  * @param file file the assertion is in
765  * @param line line the assertion is in
766  */
767 void
768 _dbus_real_assert_not_reached (const char *explanation,
769                                const char *file,
770                                int         line)
771 {
772   _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
773               file, line, _dbus_getpid (), explanation);
774   _dbus_abort ();
775 }
776 #endif /* DBUS_DISABLE_ASSERT */
777   
778 #ifdef DBUS_BUILD_TESTS
779 static dbus_bool_t
780 run_failing_each_malloc (int                    n_mallocs,
781                          const char            *description,
782                          DBusTestMemoryFunction func,
783                          void                  *data)
784 {
785   n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
786   
787   while (n_mallocs >= 0)
788     {      
789       _dbus_set_fail_alloc_counter (n_mallocs);
790
791       _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
792                      description, n_mallocs,
793                      _dbus_get_fail_alloc_failures ());
794
795       if (!(* func) (data))
796         return FALSE;
797       
798       n_mallocs -= 1;
799     }
800
801   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
802
803   return TRUE;
804 }                        
805
806 /**
807  * Tests how well the given function responds to out-of-memory
808  * situations. Calls the function repeatedly, failing a different
809  * call to malloc() each time. If the function ever returns #FALSE,
810  * the test fails. The function should return #TRUE whenever something
811  * valid (such as returning an error, or succeeding) occurs, and #FALSE
812  * if it gets confused in some way.
813  *
814  * @param description description of the test used in verbose output
815  * @param func function to call
816  * @param data data to pass to function
817  * @returns #TRUE if the function never returns FALSE
818  */
819 dbus_bool_t
820 _dbus_test_oom_handling (const char             *description,
821                          DBusTestMemoryFunction  func,
822                          void                   *data)
823 {
824   int approx_mallocs;
825   const char *setting;
826   int max_failures_to_try;
827   int i;
828
829   /* Run once to see about how many mallocs are involved */
830   
831   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
832
833   _dbus_verbose ("Running once to count mallocs\n");
834   
835   if (!(* func) (data))
836     return FALSE;
837   
838   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
839
840   _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
841                  description, approx_mallocs);
842
843   setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
844   if (setting != NULL)
845     {
846       DBusString str;
847       long v;
848       _dbus_string_init_const (&str, setting);
849       v = 4;
850       if (!_dbus_string_parse_int (&str, 0, &v, NULL))
851         _dbus_warn ("couldn't parse '%s' as integer\n", setting);
852       max_failures_to_try = v;
853     }
854   else
855     {
856       max_failures_to_try = 4;
857     }
858
859   i = setting ? max_failures_to_try - 1 : 1;
860   while (i < max_failures_to_try)
861     {
862       _dbus_set_fail_alloc_failures (i);
863       if (!run_failing_each_malloc (approx_mallocs, description, func, data))
864         return FALSE;
865       ++i;
866     }
867   
868   _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
869                  description);
870
871   return TRUE;
872 }
873 #endif /* DBUS_BUILD_TESTS */
874
875 /** @} */