dbus-internals.c revision 51781f541094a4936d47119cd62682e0431c41e9
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 1.2
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 <sys/types.h>
30#include <errno.h>
31#include <unistd.h>
32#include <fcntl.h>
33#include <stdlib.h>
34
35/**
36 * @defgroup DBusInternals D-BUS internal implementation details
37 * @brief Documentation useful when developing or debugging D-BUS itself.
38 *
39 */
40
41/**
42 * @defgroup DBusInternalsUtils Utilities and portability
43 * @ingroup DBusInternals
44 * @brief Utility functions (_dbus_assert(), _dbus_warn(), etc.)
45 * @{
46 */
47
48/**
49 * @def _dbus_assert
50 *
51 * Aborts with an error message if the condition is false.
52 *
53 * @param condition condition which must be true.
54 */
55
56/**
57 * @def _dbus_assert_not_reached
58 *
59 * Aborts with an error message if called.
60 * The given explanation will be printed.
61 *
62 * @param explanation explanation of what happened if the code was reached.
63 */
64
65/**
66 * @def _DBUS_N_ELEMENTS
67 *
68 * Computes the number of elements in a fixed-size array using
69 * sizeof().
70 *
71 * @param array the array to count elements in.
72 */
73
74/**
75 * @def _DBUS_POINTER_TO_INT
76 *
77 * Safely casts a void* to an integer; should only be used on void*
78 * that actually contain integers, for example one created with
79 * _DBUS_INT_TO_POINTER.  Only guaranteed to preserve 32 bits.
80 * (i.e. it's used to store 32-bit ints in pointers, but
81 * can't be used to store 64-bit pointers in ints.)
82 *
83 * @param pointer pointer to extract an integer from.
84 */
85/**
86 * @def _DBUS_INT_TO_POINTER
87 *
88 * Safely stuffs an integer into a pointer, to be extracted later with
89 * _DBUS_POINTER_TO_INT. Only guaranteed to preserve 32 bits.
90 *
91 * @param integer the integer to stuff into a pointer.
92 */
93/**
94 * @def _DBUS_ZERO
95 *
96 * Sets all bits in an object to zero.
97 *
98 * @param object the object to be zeroed.
99 */
100/**
101 * @def _DBUS_INT_MIN
102 *
103 * Minimum value of type "int"
104 */
105/**
106 * @def _DBUS_INT_MAX
107 *
108 * Maximum value of type "int"
109 */
110
111/**
112 * @typedef DBusForeachFunction
113 *
114 * Used to iterate over each item in a collection, such as
115 * a DBusList.
116 */
117
118/**
119 * @def _DBUS_LOCK_NAME
120 *
121 * Expands to name of a global lock variable.
122 */
123
124/**
125 * @def _DBUS_DEFINE_GLOBAL_LOCK
126 *
127 * Defines a global lock variable with the given name.
128 * The lock must be added to the list to initialize
129 * in dbus_threads_init().
130 */
131
132/**
133 * @def _DBUS_DECLARE_GLOBAL_LOCK
134 *
135 * Expands to declaration of a global lock defined
136 * with _DBUS_DEFINE_GLOBAL_LOCK.
137 * The lock must be added to the list to initialize
138 * in dbus_threads_init().
139 */
140
141/**
142 * @def _DBUS_LOCK
143 *
144 * Locks a global lock
145 */
146
147/**
148 * @def _DBUS_UNLOCK
149 *
150 * Unlocks a global lock
151 */
152
153/**
154 * Fixed "out of memory" error message, just to avoid
155 * making up a different string every time and wasting
156 * space.
157 */
158const char _dbus_no_memory_message[] = "Not enough memory";
159
160/**
161 * Prints a warning message to stderr.
162 *
163 * @param format printf-style format string.
164 */
165void
166_dbus_warn (const char *format,
167            ...)
168{
169  /* FIXME not portable enough? */
170  va_list args;
171
172  va_start (args, format);
173  vfprintf (stderr, format, args);
174  va_end (args);
175}
176
177/**
178 * Prints a warning message to stderr
179 * if the user has enabled verbose mode.
180 * This is the real function implementation,
181 * use _dbus_verbose() macro in code.
182 *
183 * @param format printf-style format string.
184 */
185void
186_dbus_verbose_real (const char *format,
187                    ...)
188{
189  va_list args;
190  static dbus_bool_t verbose = TRUE;
191  static dbus_bool_t initted = FALSE;
192  static unsigned long pid;
193
194  /* things are written a bit oddly here so that
195   * in the non-verbose case we just have the one
196   * conditional and return immediately.
197   */
198  if (!verbose)
199    return;
200
201  if (!initted)
202    {
203      verbose = _dbus_getenv ("DBUS_VERBOSE") != NULL;
204      pid = _dbus_getpid ();
205      initted = TRUE;
206      if (!verbose)
207        return;
208    }
209
210  fprintf (stderr, "%lu: ", pid);
211
212  va_start (args, format);
213  vfprintf (stderr, format, args);
214  va_end (args);
215}
216
217/**
218 * Duplicates a string. Result must be freed with
219 * dbus_free(). Returns #NULL if memory allocation fails.
220 * If the string to be duplicated is #NULL, returns #NULL.
221 *
222 * @param str string to duplicate.
223 * @returns newly-allocated copy.
224 */
225char*
226_dbus_strdup (const char *str)
227{
228  int len;
229  char *copy;
230
231  if (str == NULL)
232    return NULL;
233
234  len = strlen (str);
235
236  copy = dbus_malloc (len + 1);
237  if (copy == NULL)
238    return NULL;
239
240  memcpy (copy, str, len + 1);
241
242  return copy;
243}
244
245/**
246 * Duplicates a string array. Result may be freed with
247 * dbus_free_string_array(). Returns #NULL if memory allocation fails.
248 * If the array to be duplicated is #NULL, returns #NULL.
249 *
250 * @param array array to duplicate.
251 * @returns newly-allocated copy.
252 */
253char**
254_dbus_dup_string_array (const char **array)
255{
256  int len;
257  int i;
258  char **copy;
259
260  if (array == NULL)
261    return NULL;
262
263  for (len = 0; array[len] != NULL; ++len)
264    ;
265
266  copy = dbus_new0 (char*, len + 1);
267  if (copy == NULL)
268    return NULL;
269
270  i = 0;
271  while (i < len)
272    {
273      copy[i] = _dbus_strdup (array[i]);
274      if (copy[i] == NULL)
275        {
276          dbus_free_string_array (copy);
277          return NULL;
278        }
279
280      ++i;
281    }
282
283  return copy;
284}
285
286/**
287 * Checks whether a string array contains the given string.
288 *
289 * @param array array to search.
290 * @param str string to look for
291 * @returns #TRUE if array contains string
292 */
293dbus_bool_t
294_dbus_string_array_contains (const char **array,
295                             const char  *str)
296{
297  int i;
298
299  i = 0;
300  while (array[i] != NULL)
301    {
302      if (strcmp (array[i], str) == 0)
303        return TRUE;
304      ++i;
305    }
306
307  return FALSE;
308}
309
310/**
311 * Returns a string describing the given type.
312 *
313 * @param type the type to describe
314 * @returns a constant string describing the type
315 */
316const char *
317_dbus_type_to_string (int type)
318{
319  switch (type)
320    {
321    case DBUS_TYPE_INVALID:
322      return "invalid";
323    case DBUS_TYPE_NIL:
324      return "nil";
325    case DBUS_TYPE_BOOLEAN:
326      return "boolean";
327    case DBUS_TYPE_INT32:
328      return "int32";
329    case DBUS_TYPE_UINT32:
330      return "uint32";
331    case DBUS_TYPE_DOUBLE:
332      return "double";
333    case DBUS_TYPE_STRING:
334      return "string";
335    case DBUS_TYPE_NAMED:
336      return "named";
337    case DBUS_TYPE_ARRAY:
338      return "array";
339    case DBUS_TYPE_DICT:
340      return "dict";
341    default:
342      return "unknown";
343    }
344}
345
346#ifndef DBUS_DISABLE_ASSERT
347/**
348 * Internals of _dbus_assert(); it's a function
349 * rather than a macro with the inline code so
350 * that the assertion failure blocks don't show up
351 * in test suite coverage, and to shrink code size.
352 *
353 * @param condition TRUE if assertion succeeded
354 * @param condition_text condition as a string
355 * @param file file the assertion is in
356 * @param line line the assertion is in
357 */
358void
359_dbus_real_assert (dbus_bool_t  condition,
360                   const char  *condition_text,
361                   const char  *file,
362                   int          line)
363{
364  if (!condition)
365    {
366      _dbus_warn ("Assertion failed \"%s\" file \"%s\" line %d process %lu\n",
367                  condition_text, file, line, _dbus_getpid ());
368      _dbus_abort ();
369    }
370}
371
372/**
373 * Internals of _dbus_assert_not_reached(); it's a function
374 * rather than a macro with the inline code so
375 * that the assertion failure blocks don't show up
376 * in test suite coverage, and to shrink code size.
377 *
378 * @param explanation what was reached that shouldn't have been
379 * @param file file the assertion is in
380 * @param line line the assertion is in
381 */
382void
383_dbus_real_assert_not_reached (const char *explanation,
384                               const char *file,
385                               int         line)
386{
387  _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
388              file, line, _dbus_getpid (), explanation);
389  _dbus_abort ();
390}
391#endif /* DBUS_DISABLE_ASSERT */
392
393#ifdef DBUS_BUILD_TESTS
394static dbus_bool_t
395run_failing_each_malloc (int                    n_mallocs,
396                         const char            *description,
397                         DBusTestMemoryFunction func,
398                         void                  *data)
399{
400  n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
401
402  while (n_mallocs >= 0)
403    {
404      _dbus_set_fail_alloc_counter (n_mallocs);
405
406      _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
407                     description, n_mallocs,
408                     _dbus_get_fail_alloc_failures ());
409
410      if (!(* func) (data))
411        return FALSE;
412
413      n_mallocs -= 1;
414    }
415
416  _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
417
418  return TRUE;
419}
420
421/**
422 * Tests how well the given function responds to out-of-memory
423 * situations. Calls the function repeatedly, failing a different
424 * call to malloc() each time. If the function ever returns #FALSE,
425 * the test fails. The function should return #TRUE whenever something
426 * valid (such as returning an error, or succeeding) occurs, and #FALSE
427 * if it gets confused in some way.
428 *
429 * @param description description of the test used in verbose output
430 * @param func function to call
431 * @param data data to pass to function
432 * @returns #TRUE if the function never returns FALSE
433 */
434dbus_bool_t
435_dbus_test_oom_handling (const char             *description,
436                         DBusTestMemoryFunction  func,
437                         void                   *data)
438{
439  int approx_mallocs;
440
441  /* Run once to see about how many mallocs are involved */
442
443  _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
444
445  _dbus_verbose ("Running once to count mallocs\n");
446
447  if (!(* func) (data))
448    return FALSE;
449
450  approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
451
452  _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
453                 description, approx_mallocs);
454
455  _dbus_set_fail_alloc_failures (1);
456  if (!run_failing_each_malloc (approx_mallocs, description, func, data))
457    return FALSE;
458
459  _dbus_set_fail_alloc_failures (2);
460  if (!run_failing_each_malloc (approx_mallocs, description, func, data))
461    return FALSE;
462
463  _dbus_set_fail_alloc_failures (3);
464  if (!run_failing_each_malloc (approx_mallocs, description, func, data))
465    return FALSE;
466
467  _dbus_set_fail_alloc_failures (4);
468  if (!run_failing_each_malloc (approx_mallocs, description, func, data))
469    return FALSE;
470
471  _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
472                 description);
473
474  return TRUE;
475}
476#endif /* DBUS_BUILD_TESTS */
477
478/** @} */
479