2006-10-21 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 secret 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 static dbus_bool_t fatal_warnings_on_check_failed = TRUE;
196
197 static void
198 init_warnings(void)
199 {
200   if (!warn_initted)
201     {
202       const char *s;
203       s = _dbus_getenv ("DBUS_FATAL_WARNINGS");
204       if (s && *s)
205         {
206           if (*s == '0')
207             {
208               fatal_warnings = FALSE;
209               fatal_warnings_on_check_failed = FALSE;
210             }
211           else if (*s == '1')
212             {
213               fatal_warnings = TRUE;
214               fatal_warnings_on_check_failed = TRUE;
215             }
216           else
217             {
218               fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'",
219                       s);
220             }
221         }
222
223       warn_initted = TRUE;
224     }
225 }
226
227 /**
228  * Prints a warning message to stderr. Can optionally be made to exit
229  * fatally by setting DBUS_FATAL_WARNINGS, but this is rarely
230  * used. This function should be considered pretty much equivalent to
231  * fprintf(stderr). _dbus_warn_check_failed() on the other hand is
232  * suitable for use when a programming mistake has been made.
233  *
234  * @param format printf-style format string.
235  */
236 void
237 _dbus_warn (const char *format,
238             ...)
239 {
240   va_list args;
241
242   if (!warn_initted)
243     init_warnings ();
244   
245   va_start (args, format);
246   vfprintf (stderr, format, args);
247   va_end (args);
248
249   if (fatal_warnings)
250     {
251       fflush (stderr);
252       _dbus_abort ();
253     }
254 }
255
256 /**
257  * Prints a "critical" warning to stderr when an assertion fails;
258  * differs from _dbus_warn primarily in that it prefixes the pid and
259  * defaults to fatal. This should be used only when a programming
260  * error has been detected. (NOT for unavoidable errors that an app
261  * might handle - those should be returned as DBusError.) Calling this
262  * means "there is a bug"
263  */
264 void
265 _dbus_warn_check_failed(const char *format,
266                         ...)
267 {
268   va_list args;
269   
270   if (!warn_initted)
271     init_warnings ();
272
273   fprintf (stderr, "process %lu: ", _dbus_getpid ());
274   
275   va_start (args, format);
276   vfprintf (stderr, format, args);
277   va_end (args);
278
279   if (fatal_warnings_on_check_failed)
280     {
281       fflush (stderr);
282       _dbus_abort ();
283     }
284 }
285
286 #ifdef DBUS_ENABLE_VERBOSE_MODE
287
288 static dbus_bool_t verbose_initted = FALSE;
289 static dbus_bool_t verbose = TRUE;
290
291 /** Whether to show the current thread in verbose messages */
292 #define PTHREAD_IN_VERBOSE 0
293 #if PTHREAD_IN_VERBOSE
294 #include <pthread.h>
295 #endif
296
297 static inline void
298 _dbus_verbose_init (void)
299 {
300   if (!verbose_initted)
301     {
302       const char *p = _dbus_getenv ("DBUS_VERBOSE"); 
303       verbose = p != NULL && *p == '1';
304       verbose_initted = TRUE;
305     }
306 }
307
308 /**
309  * Implementation of dbus_is_verbose() macro if built with verbose logging
310  * enabled.
311  * @returns whether verbose logging is active.
312  */
313 dbus_bool_t
314 _dbus_is_verbose_real (void)
315 {
316   _dbus_verbose_init ();
317   return verbose;
318 }
319
320 /**
321  * Prints a warning message to stderr
322  * if the user has enabled verbose mode.
323  * This is the real function implementation,
324  * use _dbus_verbose() macro in code.
325  *
326  * @param format printf-style format string.
327  */
328 void
329 _dbus_verbose_real (const char *format,
330                     ...)
331 {
332   va_list args;
333   static dbus_bool_t need_pid = TRUE;
334   int len;
335   
336   /* things are written a bit oddly here so that
337    * in the non-verbose case we just have the one
338    * conditional and return immediately.
339    */
340   if (!_dbus_is_verbose_real())
341     return;
342
343   /* Print out pid before the line */
344   if (need_pid)
345     {
346 #if PTHREAD_IN_VERBOSE
347       fprintf (stderr, "%lu: 0x%lx: ", _dbus_getpid (), pthread_self ());
348 #else
349       fprintf (stderr, "%lu: ", _dbus_getpid ());
350 #endif
351     }
352       
353
354   /* Only print pid again if the next line is a new line */
355   len = strlen (format);
356   if (format[len-1] == '\n')
357     need_pid = TRUE;
358   else
359     need_pid = FALSE;
360   
361   va_start (args, format);
362   vfprintf (stderr, format, args);
363   va_end (args);
364
365   fflush (stderr);
366 }
367
368 /**
369  * Reinitializes the verbose logging code, used
370  * as a hack in dbus-spawn.c so that a child
371  * process re-reads its pid
372  *
373  */
374 void
375 _dbus_verbose_reset_real (void)
376 {
377   verbose_initted = FALSE;
378 }
379
380 #endif /* DBUS_ENABLE_VERBOSE_MODE */
381
382 /**
383  * Duplicates a string. Result must be freed with
384  * dbus_free(). Returns #NULL if memory allocation fails.
385  * If the string to be duplicated is #NULL, returns #NULL.
386  * 
387  * @param str string to duplicate.
388  * @returns newly-allocated copy.
389  */
390 char*
391 _dbus_strdup (const char *str)
392 {
393   size_t len;
394   char *copy;
395   
396   if (str == NULL)
397     return NULL;
398   
399   len = strlen (str);
400
401   copy = dbus_malloc (len + 1);
402   if (copy == NULL)
403     return NULL;
404
405   memcpy (copy, str, len + 1);
406   
407   return copy;
408 }
409
410 /**
411  * Duplicates a block of memory. Returns
412  * #NULL on failure.
413  *
414  * @param mem memory to copy
415  * @param n_bytes number of bytes to copy
416  * @returns the copy
417  */
418 void*
419 _dbus_memdup (const void  *mem,
420               size_t       n_bytes)
421 {
422   void *copy;
423
424   copy = dbus_malloc (n_bytes);
425   if (copy == NULL)
426     return NULL;
427
428   memcpy (copy, mem, n_bytes);
429   
430   return copy;
431 }
432
433 /**
434  * Duplicates a string array. Result may be freed with
435  * dbus_free_string_array(). Returns #NULL if memory allocation fails.
436  * If the array to be duplicated is #NULL, returns #NULL.
437  * 
438  * @param array array to duplicate.
439  * @returns newly-allocated copy.
440  */
441 char**
442 _dbus_dup_string_array (const char **array)
443 {
444   int len;
445   int i;
446   char **copy;
447   
448   if (array == NULL)
449     return NULL;
450
451   for (len = 0; array[len] != NULL; ++len)
452     ;
453
454   copy = dbus_new0 (char*, len + 1);
455   if (copy == NULL)
456     return NULL;
457
458   i = 0;
459   while (i < len)
460     {
461       copy[i] = _dbus_strdup (array[i]);
462       if (copy[i] == NULL)
463         {
464           dbus_free_string_array (copy);
465           return NULL;
466         }
467
468       ++i;
469     }
470
471   return copy;
472 }
473
474 /**
475  * Checks whether a string array contains the given string.
476  * 
477  * @param array array to search.
478  * @param str string to look for
479  * @returns #TRUE if array contains string
480  */
481 dbus_bool_t
482 _dbus_string_array_contains (const char **array,
483                              const char  *str)
484 {
485   int i;
486
487   i = 0;
488   while (array[i] != NULL)
489     {
490       if (strcmp (array[i], str) == 0)
491         return TRUE;
492       ++i;
493     }
494
495   return FALSE;
496 }
497
498 /**
499  * Generates a new UUID. If you change how this is done,
500  * there's some text about it in the spec that should also change.
501  *
502  * @param uuid the uuid to initialize
503  */
504 void
505 _dbus_generate_uuid (DBusGUID *uuid)
506 {
507   long now;
508   char *p;
509   int ts_size;
510
511   _dbus_get_current_time (&now, NULL);
512
513   uuid->as_uint32s[0] = now;
514
515   ts_size = sizeof (uuid->as_uint32s[0]);
516   p = ((char*)uuid->as_bytes) + ts_size;
517   
518   _dbus_generate_random_bytes_buffer (p,
519                                       sizeof (uuid->as_bytes) - ts_size);
520 }
521
522 /**
523  * Hex-encode a UUID.
524  *
525  * @param uuid the uuid
526  * @param encoded string to append hex uuid to
527  * @returns #FALSE if no memory
528  */
529 dbus_bool_t
530 _dbus_uuid_encode (const DBusGUID *uuid,
531                    DBusString     *encoded)
532 {
533   DBusString binary;
534   _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
535   return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
536 }
537
538 static dbus_bool_t
539 _dbus_read_uuid_file_without_creating (const DBusString *filename,
540                                        DBusGUID         *uuid,
541                                        DBusError        *error)
542 {
543   DBusString contents;
544   DBusString decoded;
545   int end;
546   
547   _dbus_string_init (&contents);
548   _dbus_string_init (&decoded);
549   
550   if (!_dbus_file_get_contents (&contents, filename, error))
551     goto error;
552
553   _dbus_string_chop_white (&contents);
554
555   if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
556     {
557       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
558                       "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
559                       _dbus_string_get_const_data (filename),
560                       DBUS_UUID_LENGTH_HEX,
561                       _dbus_string_get_length (&contents));
562       goto error;
563     }
564
565   if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
566     {
567       _DBUS_SET_OOM (error);
568       goto error;
569     }
570
571   if (end == 0)
572     {
573       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
574                       "UUID file '%s' contains invalid hex data",
575                       _dbus_string_get_const_data (filename));
576       goto error;
577     }
578
579   if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
580     {
581       dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
582                       "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
583                       _dbus_string_get_const_data (filename),
584                       _dbus_string_get_length (&decoded),
585                       DBUS_UUID_LENGTH_BYTES);
586       goto error;
587     }
588
589   _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
590
591   _dbus_string_free (&decoded);
592   _dbus_string_free (&contents);
593
594   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
595
596   return TRUE;
597   
598  error:
599   _DBUS_ASSERT_ERROR_IS_SET (error);
600   _dbus_string_free (&contents);
601   _dbus_string_free (&decoded);
602   return FALSE;
603 }
604
605 static dbus_bool_t
606 _dbus_create_uuid_file_exclusively (const DBusString *filename,
607                                     DBusGUID         *uuid,
608                                     DBusError        *error)
609 {
610   DBusString encoded;
611
612   _dbus_string_init (&encoded);
613
614   _dbus_generate_uuid (uuid);
615   
616   if (!_dbus_uuid_encode (uuid, &encoded))
617     {
618       _DBUS_SET_OOM (error);
619       goto error;
620     }
621   
622   /* FIXME this is racy; we need a save_file_exclusively
623    * function. But in practice this should be fine for now.
624    *
625    * - first be sure we can create the file and it
626    *   doesn't exist by creating it empty with O_EXCL
627    * - then create it by creating a temporary file and
628    *   overwriting atomically with rename()
629    */
630   if (!_dbus_create_file_exclusively (filename, error))
631     goto error;
632
633   if (!_dbus_string_append_byte (&encoded, '\n'))
634     {
635       _DBUS_SET_OOM (error);
636       goto error;
637     }
638   
639   if (!_dbus_string_save_to_file (&encoded, filename, error))
640     goto error;
641
642
643   _dbus_string_free (&encoded);
644
645   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
646   return TRUE;
647   
648  error:
649   _DBUS_ASSERT_ERROR_IS_SET (error);
650   _dbus_string_free (&encoded);
651   return FALSE;        
652 }
653
654 /**
655  * Reads (and optionally writes) a uuid to a file. Initializes the uuid
656  * unless an error is returned.
657  *
658  * @param filename the name of the file
659  * @param uuid uuid to be initialized with the loaded uuid
660  * @param create_if_not_found #TRUE to create a new uuid and save it if the file doesn't exist
661  * @param error the error return
662  * @returns #FALSE if the error is set
663  */
664 dbus_bool_t
665 _dbus_read_uuid_file (const DBusString *filename,
666                       DBusGUID         *uuid,
667                       dbus_bool_t       create_if_not_found,
668                       DBusError        *error)
669 {
670   DBusError read_error;
671
672   dbus_error_init (&read_error);
673
674   if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
675     return TRUE;
676
677   if (!create_if_not_found)
678     {
679       dbus_move_error (&read_error, error);
680       return FALSE;
681     }
682
683   /* If the file exists and contains junk, we want to keep that error
684    * message instead of overwriting it with a "file exists" error
685    * message when we try to write
686    */
687   if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
688     {
689       dbus_move_error (&read_error, error);
690       return FALSE;
691     }
692   else
693     {
694       dbus_error_free (&read_error);
695       return _dbus_create_uuid_file_exclusively (filename, uuid, error);
696     }
697 }
698
699 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
700 static int machine_uuid_initialized_generation = 0;
701 static DBusGUID machine_uuid;
702
703 /**
704  * Gets the hex-encoded UUID of the machine this function is
705  * executed on. This UUID is guaranteed to be the same for a given
706  * machine at least until it next reboots, though it also
707  * makes some effort to be the same forever, it may change if the
708  * machine is reconfigured or its hardware is modified.
709  * 
710  * @param uuid_str string to append hex-encoded machine uuid to
711  * @returns #FALSE if no memory
712  */
713 dbus_bool_t
714 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
715 {
716   dbus_bool_t ok;
717   
718   _DBUS_LOCK (machine_uuid);
719   if (machine_uuid_initialized_generation != _dbus_current_generation)
720     {
721       DBusError error;
722       dbus_error_init (&error);
723       if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
724                                           &error))
725         {          
726 #ifndef DBUS_BUILD_TESTS
727           /* For the test suite, we may not be installed so just continue silently
728            * here. But in a production build, we want to be nice and loud about
729            * this.
730            */
731           _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
732                                    "See the manual page for dbus-uuidgen to correct this issue.\n",
733                                    error.message);
734 #endif
735           
736           dbus_error_free (&error);
737           
738           _dbus_generate_uuid (&machine_uuid);
739         }
740     }
741
742   ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
743
744   _DBUS_UNLOCK (machine_uuid);
745
746   return ok;
747 }
748
749 #ifdef DBUS_BUILD_TESTS
750 /**
751  * Returns a string describing the given name.
752  *
753  * @param header_field the field to describe
754  * @returns a constant string describing the field
755  */
756 const char *
757 _dbus_header_field_to_string (int header_field)
758 {
759   switch (header_field)
760     {
761     case DBUS_HEADER_FIELD_INVALID:
762       return "invalid";
763     case DBUS_HEADER_FIELD_PATH:
764       return "path";
765     case DBUS_HEADER_FIELD_INTERFACE:
766       return "interface";
767     case DBUS_HEADER_FIELD_MEMBER:
768       return "member";
769     case DBUS_HEADER_FIELD_ERROR_NAME:
770       return "error-name";
771     case DBUS_HEADER_FIELD_REPLY_SERIAL:
772       return "reply-serial";
773     case DBUS_HEADER_FIELD_DESTINATION:
774       return "destination";
775     case DBUS_HEADER_FIELD_SENDER:
776       return "sender";
777     case DBUS_HEADER_FIELD_SIGNATURE:
778       return "signature";
779     default:
780       return "unknown";
781     }
782 }
783 #endif /* DBUS_BUILD_TESTS */
784
785 #ifndef DBUS_DISABLE_CHECKS
786 /** String used in _dbus_return_if_fail macro */
787 const char _dbus_return_if_fail_warning_format[] =
788 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
789 "This is normally a bug in some application using the D-Bus library.\n";
790 #endif
791
792 #ifndef DBUS_DISABLE_ASSERT
793 /**
794  * Internals of _dbus_assert(); it's a function
795  * rather than a macro with the inline code so
796  * that the assertion failure blocks don't show up
797  * in test suite coverage, and to shrink code size.
798  *
799  * @param condition TRUE if assertion succeeded
800  * @param condition_text condition as a string
801  * @param file file the assertion is in
802  * @param line line the assertion is in
803  * @param func function the assertion is in
804  */
805 void
806 _dbus_real_assert (dbus_bool_t  condition,
807                    const char  *condition_text,
808                    const char  *file,
809                    int          line,
810                    const char  *func)
811 {
812   if (_DBUS_UNLIKELY (!condition))
813     {
814       _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
815                   _dbus_getpid (), condition_text, file, line, func);
816       _dbus_abort ();
817     }
818 }
819
820 /**
821  * Internals of _dbus_assert_not_reached(); it's a function
822  * rather than a macro with the inline code so
823  * that the assertion failure blocks don't show up
824  * in test suite coverage, and to shrink code size.
825  *
826  * @param explanation what was reached that shouldn't have been
827  * @param file file the assertion is in
828  * @param line line the assertion is in
829  */
830 void
831 _dbus_real_assert_not_reached (const char *explanation,
832                                const char *file,
833                                int         line)
834 {
835   _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
836               file, line, _dbus_getpid (), explanation);
837   _dbus_abort ();
838 }
839 #endif /* DBUS_DISABLE_ASSERT */
840   
841 #ifdef DBUS_BUILD_TESTS
842 static dbus_bool_t
843 run_failing_each_malloc (int                    n_mallocs,
844                          const char            *description,
845                          DBusTestMemoryFunction func,
846                          void                  *data)
847 {
848   n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
849   
850   while (n_mallocs >= 0)
851     {      
852       _dbus_set_fail_alloc_counter (n_mallocs);
853
854       _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
855                      description, n_mallocs,
856                      _dbus_get_fail_alloc_failures ());
857
858       if (!(* func) (data))
859         return FALSE;
860       
861       n_mallocs -= 1;
862     }
863
864   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
865
866   return TRUE;
867 }                        
868
869 /**
870  * Tests how well the given function responds to out-of-memory
871  * situations. Calls the function repeatedly, failing a different
872  * call to malloc() each time. If the function ever returns #FALSE,
873  * the test fails. The function should return #TRUE whenever something
874  * valid (such as returning an error, or succeeding) occurs, and #FALSE
875  * if it gets confused in some way.
876  *
877  * @param description description of the test used in verbose output
878  * @param func function to call
879  * @param data data to pass to function
880  * @returns #TRUE if the function never returns FALSE
881  */
882 dbus_bool_t
883 _dbus_test_oom_handling (const char             *description,
884                          DBusTestMemoryFunction  func,
885                          void                   *data)
886 {
887   int approx_mallocs;
888   const char *setting;
889   int max_failures_to_try;
890   int i;
891
892   /* Run once to see about how many mallocs are involved */
893   
894   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
895
896   _dbus_verbose ("Running once to count mallocs\n");
897   
898   if (!(* func) (data))
899     return FALSE;
900   
901   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
902
903   _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
904                  description, approx_mallocs);
905
906   setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
907   if (setting != NULL)
908     {
909       DBusString str;
910       long v;
911       _dbus_string_init_const (&str, setting);
912       v = 4;
913       if (!_dbus_string_parse_int (&str, 0, &v, NULL))
914         _dbus_warn ("couldn't parse '%s' as integer\n", setting);
915       max_failures_to_try = v;
916     }
917   else
918     {
919       max_failures_to_try = 4;
920     }
921
922   i = setting ? max_failures_to_try - 1 : 1;
923   while (i < max_failures_to_try)
924     {
925       _dbus_set_fail_alloc_failures (i);
926       if (!run_failing_each_malloc (approx_mallocs, description, func, data))
927         return FALSE;
928       ++i;
929     }
930   
931   _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
932                  description);
933
934   return TRUE;
935 }
936 #endif /* DBUS_BUILD_TESTS */
937
938 /** @} */