* dbus/dbus-sysdeps.h:
[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   if (!_dbus_make_file_world_readable (filename, error))
643     goto error;
644
645   _dbus_string_free (&encoded);
646
647   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
648   return TRUE;
649   
650  error:
651   _DBUS_ASSERT_ERROR_IS_SET (error);
652   _dbus_string_free (&encoded);
653   return FALSE;        
654 }
655
656 /**
657  * Reads (and optionally writes) a uuid to a file. Initializes the uuid
658  * unless an error is returned.
659  *
660  * @param filename the name of the file
661  * @param uuid uuid to be initialized with the loaded uuid
662  * @param create_if_not_found #TRUE to create a new uuid and save it if the file doesn't exist
663  * @param error the error return
664  * @returns #FALSE if the error is set
665  */
666 dbus_bool_t
667 _dbus_read_uuid_file (const DBusString *filename,
668                       DBusGUID         *uuid,
669                       dbus_bool_t       create_if_not_found,
670                       DBusError        *error)
671 {
672   DBusError read_error;
673
674   dbus_error_init (&read_error);
675
676   if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
677     return TRUE;
678
679   if (!create_if_not_found)
680     {
681       dbus_move_error (&read_error, error);
682       return FALSE;
683     }
684
685   /* If the file exists and contains junk, we want to keep that error
686    * message instead of overwriting it with a "file exists" error
687    * message when we try to write
688    */
689   if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
690     {
691       dbus_move_error (&read_error, error);
692       return FALSE;
693     }
694   else
695     {
696       dbus_error_free (&read_error);
697       return _dbus_create_uuid_file_exclusively (filename, uuid, error);
698     }
699 }
700
701 _DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
702 static int machine_uuid_initialized_generation = 0;
703 static DBusGUID machine_uuid;
704
705 /**
706  * Gets the hex-encoded UUID of the machine this function is
707  * executed on. This UUID is guaranteed to be the same for a given
708  * machine at least until it next reboots, though it also
709  * makes some effort to be the same forever, it may change if the
710  * machine is reconfigured or its hardware is modified.
711  * 
712  * @param uuid_str string to append hex-encoded machine uuid to
713  * @returns #FALSE if no memory
714  */
715 dbus_bool_t
716 _dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
717 {
718   dbus_bool_t ok;
719   
720   _DBUS_LOCK (machine_uuid);
721   if (machine_uuid_initialized_generation != _dbus_current_generation)
722     {
723       DBusError error;
724       dbus_error_init (&error);
725       if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
726                                           &error))
727         {          
728 #ifndef DBUS_BUILD_TESTS
729           /* For the test suite, we may not be installed so just continue silently
730            * here. But in a production build, we want to be nice and loud about
731            * this.
732            */
733           _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
734                                    "See the manual page for dbus-uuidgen to correct this issue.\n",
735                                    error.message);
736 #endif
737           
738           dbus_error_free (&error);
739           
740           _dbus_generate_uuid (&machine_uuid);
741         }
742     }
743
744   ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
745
746   _DBUS_UNLOCK (machine_uuid);
747
748   return ok;
749 }
750
751 #ifdef DBUS_BUILD_TESTS
752 /**
753  * Returns a string describing the given name.
754  *
755  * @param header_field the field to describe
756  * @returns a constant string describing the field
757  */
758 const char *
759 _dbus_header_field_to_string (int header_field)
760 {
761   switch (header_field)
762     {
763     case DBUS_HEADER_FIELD_INVALID:
764       return "invalid";
765     case DBUS_HEADER_FIELD_PATH:
766       return "path";
767     case DBUS_HEADER_FIELD_INTERFACE:
768       return "interface";
769     case DBUS_HEADER_FIELD_MEMBER:
770       return "member";
771     case DBUS_HEADER_FIELD_ERROR_NAME:
772       return "error-name";
773     case DBUS_HEADER_FIELD_REPLY_SERIAL:
774       return "reply-serial";
775     case DBUS_HEADER_FIELD_DESTINATION:
776       return "destination";
777     case DBUS_HEADER_FIELD_SENDER:
778       return "sender";
779     case DBUS_HEADER_FIELD_SIGNATURE:
780       return "signature";
781     default:
782       return "unknown";
783     }
784 }
785 #endif /* DBUS_BUILD_TESTS */
786
787 #ifndef DBUS_DISABLE_CHECKS
788 /** String used in _dbus_return_if_fail macro */
789 const char _dbus_return_if_fail_warning_format[] =
790 "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
791 "This is normally a bug in some application using the D-Bus library.\n";
792 #endif
793
794 #ifndef DBUS_DISABLE_ASSERT
795 /**
796  * Internals of _dbus_assert(); it's a function
797  * rather than a macro with the inline code so
798  * that the assertion failure blocks don't show up
799  * in test suite coverage, and to shrink code size.
800  *
801  * @param condition TRUE if assertion succeeded
802  * @param condition_text condition as a string
803  * @param file file the assertion is in
804  * @param line line the assertion is in
805  * @param func function the assertion is in
806  */
807 void
808 _dbus_real_assert (dbus_bool_t  condition,
809                    const char  *condition_text,
810                    const char  *file,
811                    int          line,
812                    const char  *func)
813 {
814   if (_DBUS_UNLIKELY (!condition))
815     {
816       _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
817                   _dbus_getpid (), condition_text, file, line, func);
818       _dbus_abort ();
819     }
820 }
821
822 /**
823  * Internals of _dbus_assert_not_reached(); it's a function
824  * rather than a macro with the inline code so
825  * that the assertion failure blocks don't show up
826  * in test suite coverage, and to shrink code size.
827  *
828  * @param explanation what was reached that shouldn't have been
829  * @param file file the assertion is in
830  * @param line line the assertion is in
831  */
832 void
833 _dbus_real_assert_not_reached (const char *explanation,
834                                const char *file,
835                                int         line)
836 {
837   _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
838               file, line, _dbus_getpid (), explanation);
839   _dbus_abort ();
840 }
841 #endif /* DBUS_DISABLE_ASSERT */
842   
843 #ifdef DBUS_BUILD_TESTS
844 static dbus_bool_t
845 run_failing_each_malloc (int                    n_mallocs,
846                          const char            *description,
847                          DBusTestMemoryFunction func,
848                          void                  *data)
849 {
850   n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
851   
852   while (n_mallocs >= 0)
853     {      
854       _dbus_set_fail_alloc_counter (n_mallocs);
855
856       _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
857                      description, n_mallocs,
858                      _dbus_get_fail_alloc_failures ());
859
860       if (!(* func) (data))
861         return FALSE;
862       
863       n_mallocs -= 1;
864     }
865
866   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
867
868   return TRUE;
869 }                        
870
871 /**
872  * Tests how well the given function responds to out-of-memory
873  * situations. Calls the function repeatedly, failing a different
874  * call to malloc() each time. If the function ever returns #FALSE,
875  * the test fails. The function should return #TRUE whenever something
876  * valid (such as returning an error, or succeeding) occurs, and #FALSE
877  * if it gets confused in some way.
878  *
879  * @param description description of the test used in verbose output
880  * @param func function to call
881  * @param data data to pass to function
882  * @returns #TRUE if the function never returns FALSE
883  */
884 dbus_bool_t
885 _dbus_test_oom_handling (const char             *description,
886                          DBusTestMemoryFunction  func,
887                          void                   *data)
888 {
889   int approx_mallocs;
890   const char *setting;
891   int max_failures_to_try;
892   int i;
893
894   /* Run once to see about how many mallocs are involved */
895   
896   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
897
898   _dbus_verbose ("Running once to count mallocs\n");
899   
900   if (!(* func) (data))
901     return FALSE;
902   
903   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
904
905   _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
906                  description, approx_mallocs);
907
908   setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
909   if (setting != NULL)
910     {
911       DBusString str;
912       long v;
913       _dbus_string_init_const (&str, setting);
914       v = 4;
915       if (!_dbus_string_parse_int (&str, 0, &v, NULL))
916         _dbus_warn ("couldn't parse '%s' as integer\n", setting);
917       max_failures_to_try = v;
918     }
919   else
920     {
921       max_failures_to_try = 4;
922     }
923
924   i = setting ? max_failures_to_try - 1 : 1;
925   while (i < max_failures_to_try)
926     {
927       _dbus_set_fail_alloc_failures (i);
928       if (!run_failing_each_malloc (approx_mallocs, description, func, data))
929         return FALSE;
930       ++i;
931     }
932   
933   _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
934                  description);
935
936   return TRUE;
937 }
938 #endif /* DBUS_BUILD_TESTS */
939
940 /** @} */