dbus-internals.c revision 7f2973c8d1e124f4744af0bee73679607511f3a4
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 <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_INT16_MIN
102 *
103 * Minimum value of type "int16"
104 */
105/**
106 * @def _DBUS_INT16_MAX
107 *
108 * Maximum value of type "int16"
109 */
110/**
111 * @def _DBUS_UINT16_MAX
112 *
113 * Maximum value of type "uint16"
114 */
115
116/**
117 * @def _DBUS_INT32_MIN
118 *
119 * Minimum value of type "int32"
120 */
121/**
122 * @def _DBUS_INT32_MAX
123 *
124 * Maximum value of type "int32"
125 */
126/**
127 * @def _DBUS_UINT32_MAX
128 *
129 * Maximum value of type "uint32"
130 */
131
132/**
133 * @def _DBUS_INT_MIN
134 *
135 * Minimum value of type "int"
136 */
137/**
138 * @def _DBUS_INT_MAX
139 *
140 * Maximum value of type "int"
141 */
142/**
143 * @def _DBUS_UINT_MAX
144 *
145 * Maximum value of type "uint"
146 */
147
148/**
149 * @typedef DBusForeachFunction
150 *
151 * Used to iterate over each item in a collection, such as
152 * a DBusList.
153 */
154
155/**
156 * @def _DBUS_LOCK_NAME
157 *
158 * Expands to name of a global lock variable.
159 */
160
161/**
162 * @def _DBUS_DEFINE_GLOBAL_LOCK
163 *
164 * Defines a global lock variable with the given name.
165 * The lock must be added to the list to initialize
166 * in dbus_threads_init().
167 */
168
169/**
170 * @def _DBUS_DECLARE_GLOBAL_LOCK
171 *
172 * Expands to declaration of a global lock defined
173 * with _DBUS_DEFINE_GLOBAL_LOCK.
174 * The lock must be added to the list to initialize
175 * in dbus_threads_init().
176 */
177
178/**
179 * @def _DBUS_LOCK
180 *
181 * Locks a global lock
182 */
183
184/**
185 * @def _DBUS_UNLOCK
186 *
187 * Unlocks a global lock
188 */
189
190/**
191 * Fixed "out of memory" error message, just to avoid
192 * making up a different string every time and wasting
193 * space.
194 */
195const char _dbus_no_memory_message[] = "Not enough memory";
196
197/**
198 * Prints a warning message to stderr.
199 *
200 * @param format printf-style format string.
201 */
202void
203_dbus_warn (const char *format,
204            ...)
205{
206  /* FIXME not portable enough? */
207  va_list args;
208
209  va_start (args, format);
210  vfprintf (stderr, format, args);
211  va_end (args);
212}
213
214#ifdef DBUS_ENABLE_VERBOSE_MODE
215
216static dbus_bool_t verbose_initted = FALSE;
217
218#include <pthread.h>
219/**
220 * Prints a warning message to stderr
221 * if the user has enabled verbose mode.
222 * This is the real function implementation,
223 * use _dbus_verbose() macro in code.
224 *
225 * @param format printf-style format string.
226 */
227void
228_dbus_verbose_real (const char *format,
229                    ...)
230{
231  va_list args;
232  static dbus_bool_t verbose = TRUE;
233  static dbus_bool_t need_pid = TRUE;
234  int len;
235
236  /* things are written a bit oddly here so that
237   * in the non-verbose case we just have the one
238   * conditional and return immediately.
239   */
240  if (!verbose)
241    return;
242
243  if (!verbose_initted)
244    {
245      verbose = _dbus_getenv ("DBUS_VERBOSE") != NULL;
246      verbose_initted = TRUE;
247      if (!verbose)
248        return;
249    }
250
251  /* Print out pid before the line */
252  if (need_pid)
253    fprintf (stderr, "%lu: 0x%lx: ", _dbus_getpid (), pthread_self ());
254
255  /* Only print pid again if the next line is a new line */
256  len = strlen (format);
257  if (format[len-1] == '\n')
258    need_pid = TRUE;
259  else
260    need_pid = FALSE;
261
262  va_start (args, format);
263  vfprintf (stderr, format, args);
264  va_end (args);
265
266  fflush (stderr);
267}
268
269/**
270 * Reinitializes the verbose logging code, used
271 * as a hack in dbus-spawn.c so that a child
272 * process re-reads its pid
273 *
274 */
275void
276_dbus_verbose_reset_real (void)
277{
278  verbose_initted = FALSE;
279}
280
281#endif /* DBUS_ENABLE_VERBOSE_MODE */
282
283/**
284 * Duplicates a string. Result must be freed with
285 * dbus_free(). Returns #NULL if memory allocation fails.
286 * If the string to be duplicated is #NULL, returns #NULL.
287 *
288 * @param str string to duplicate.
289 * @returns newly-allocated copy.
290 */
291char*
292_dbus_strdup (const char *str)
293{
294  size_t len;
295  char *copy;
296
297  if (str == NULL)
298    return NULL;
299
300  len = strlen (str);
301
302  copy = dbus_malloc (len + 1);
303  if (copy == NULL)
304    return NULL;
305
306  memcpy (copy, str, len + 1);
307
308  return copy;
309}
310
311/**
312 * Duplicates a block of memory. Returns
313 * #NULL on failure.
314 *
315 * @param mem memory to copy
316 * @param n_bytes number of bytes to copy
317 * @returns the copy
318 */
319void*
320_dbus_memdup (const void  *mem,
321              size_t       n_bytes)
322{
323  void *copy;
324
325  copy = dbus_malloc (n_bytes);
326  if (copy == NULL)
327    return NULL;
328
329  memcpy (copy, mem, n_bytes);
330
331  return copy;
332}
333
334/**
335 * Duplicates a string array. Result may be freed with
336 * dbus_free_string_array(). Returns #NULL if memory allocation fails.
337 * If the array to be duplicated is #NULL, returns #NULL.
338 *
339 * @param array array to duplicate.
340 * @returns newly-allocated copy.
341 */
342char**
343_dbus_dup_string_array (const char **array)
344{
345  int len;
346  int i;
347  char **copy;
348
349  if (array == NULL)
350    return NULL;
351
352  for (len = 0; array[len] != NULL; ++len)
353    ;
354
355  copy = dbus_new0 (char*, len + 1);
356  if (copy == NULL)
357    return NULL;
358
359  i = 0;
360  while (i < len)
361    {
362      copy[i] = _dbus_strdup (array[i]);
363      if (copy[i] == NULL)
364        {
365          dbus_free_string_array (copy);
366          return NULL;
367        }
368
369      ++i;
370    }
371
372  return copy;
373}
374
375/**
376 * Checks whether a string array contains the given string.
377 *
378 * @param array array to search.
379 * @param str string to look for
380 * @returns #TRUE if array contains string
381 */
382dbus_bool_t
383_dbus_string_array_contains (const char **array,
384                             const char  *str)
385{
386  int i;
387
388  i = 0;
389  while (array[i] != NULL)
390    {
391      if (strcmp (array[i], str) == 0)
392        return TRUE;
393      ++i;
394    }
395
396  return FALSE;
397}
398
399#ifdef DBUS_BUILD_TESTS
400/**
401 * Returns a string describing the given name.
402 *
403 * @param header_field the field to describe
404 * @returns a constant string describing the field
405 */
406const char *
407_dbus_header_field_to_string (int header_field)
408{
409  switch (header_field)
410    {
411    case DBUS_HEADER_FIELD_INVALID:
412      return "invalid";
413    case DBUS_HEADER_FIELD_PATH:
414      return "path";
415    case DBUS_HEADER_FIELD_INTERFACE:
416      return "interface";
417    case DBUS_HEADER_FIELD_MEMBER:
418      return "member";
419    case DBUS_HEADER_FIELD_ERROR_NAME:
420      return "error-name";
421    case DBUS_HEADER_FIELD_REPLY_SERIAL:
422      return "reply-serial";
423    case DBUS_HEADER_FIELD_DESTINATION:
424      return "destination";
425    case DBUS_HEADER_FIELD_SENDER:
426      return "sender";
427    case DBUS_HEADER_FIELD_SIGNATURE:
428      return "signature";
429    default:
430      return "unknown";
431    }
432}
433#endif /* DBUS_BUILD_TESTS */
434
435#ifndef DBUS_DISABLE_CHECKS
436/** String used in _dbus_return_if_fail macro */
437const char _dbus_return_if_fail_warning_format[] =
438"%lu: arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
439"This is normally a bug in some application using the D-BUS library.\n";
440#endif
441
442#ifndef DBUS_DISABLE_ASSERT
443/**
444 * Internals of _dbus_assert(); it's a function
445 * rather than a macro with the inline code so
446 * that the assertion failure blocks don't show up
447 * in test suite coverage, and to shrink code size.
448 *
449 * @param condition TRUE if assertion succeeded
450 * @param condition_text condition as a string
451 * @param file file the assertion is in
452 * @param line line the assertion is in
453 * @param func function the assertion is in
454 */
455void
456_dbus_real_assert (dbus_bool_t  condition,
457                   const char  *condition_text,
458                   const char  *file,
459                   int          line,
460                   const char  *func)
461{
462  if (_DBUS_UNLIKELY (!condition))
463    {
464      _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
465                  _dbus_getpid (), condition_text, file, line, func);
466      _dbus_abort ();
467    }
468}
469
470/**
471 * Internals of _dbus_assert_not_reached(); it's a function
472 * rather than a macro with the inline code so
473 * that the assertion failure blocks don't show up
474 * in test suite coverage, and to shrink code size.
475 *
476 * @param explanation what was reached that shouldn't have been
477 * @param file file the assertion is in
478 * @param line line the assertion is in
479 */
480void
481_dbus_real_assert_not_reached (const char *explanation,
482                               const char *file,
483                               int         line)
484{
485  _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
486              file, line, _dbus_getpid (), explanation);
487  _dbus_abort ();
488}
489#endif /* DBUS_DISABLE_ASSERT */
490
491#ifdef DBUS_BUILD_TESTS
492static dbus_bool_t
493run_failing_each_malloc (int                    n_mallocs,
494                         const char            *description,
495                         DBusTestMemoryFunction func,
496                         void                  *data)
497{
498  n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
499
500  while (n_mallocs >= 0)
501    {
502      _dbus_set_fail_alloc_counter (n_mallocs);
503
504      _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
505                     description, n_mallocs,
506                     _dbus_get_fail_alloc_failures ());
507
508      if (!(* func) (data))
509        return FALSE;
510
511      n_mallocs -= 1;
512    }
513
514  _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
515
516  return TRUE;
517}
518
519/**
520 * Tests how well the given function responds to out-of-memory
521 * situations. Calls the function repeatedly, failing a different
522 * call to malloc() each time. If the function ever returns #FALSE,
523 * the test fails. The function should return #TRUE whenever something
524 * valid (such as returning an error, or succeeding) occurs, and #FALSE
525 * if it gets confused in some way.
526 *
527 * @param description description of the test used in verbose output
528 * @param func function to call
529 * @param data data to pass to function
530 * @returns #TRUE if the function never returns FALSE
531 */
532dbus_bool_t
533_dbus_test_oom_handling (const char             *description,
534                         DBusTestMemoryFunction  func,
535                         void                   *data)
536{
537  int approx_mallocs;
538  const char *setting;
539  int max_failures_to_try;
540  int i;
541
542  /* Run once to see about how many mallocs are involved */
543
544  _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
545
546  _dbus_verbose ("Running once to count mallocs\n");
547
548  if (!(* func) (data))
549    return FALSE;
550
551  approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
552
553  _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
554                 description, approx_mallocs);
555
556  setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
557  if (setting != NULL)
558    {
559      DBusString str;
560      long v;
561      _dbus_string_init_const (&str, setting);
562      v = 4;
563      if (!_dbus_string_parse_int (&str, 0, &v, NULL))
564        _dbus_warn ("couldn't parse '%s' as integer\n", setting);
565      max_failures_to_try = v;
566    }
567  else
568    {
569      max_failures_to_try = 4;
570    }
571
572  i = setting ? max_failures_to_try - 1 : 1;
573  while (i < max_failures_to_try)
574    {
575      _dbus_set_fail_alloc_failures (i);
576      if (!run_failing_each_malloc (approx_mallocs, description, func, data))
577        return FALSE;
578      ++i;
579    }
580
581  _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
582                 description);
583
584  return TRUE;
585}
586#endif /* DBUS_BUILD_TESTS */
587
588/** @} */
589