dbus-internals.c 26.5 KB
Newer Older
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
John Palmieri's avatar
John Palmieri committed
2
/* dbus-internals.c  random utility stuff (internal to D-Bus implementation)
Havoc Pennington's avatar
Havoc Pennington committed
3
 *
4
 * Copyright (C) 2002, 2003  Red Hat, Inc.
Havoc Pennington's avatar
Havoc Pennington committed
5
 *
6
 * Licensed under the Academic Free License version 2.1
Havoc Pennington's avatar
Havoc Pennington committed
7 8 9 10 11 12 13 14 15 16 17 18 19
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Havoc Pennington's avatar
Havoc Pennington committed
21 22
 *
 */
23 24

#include <config.h>
Havoc Pennington's avatar
Havoc Pennington committed
25
#include "dbus-internals.h"
26
#include "dbus-protocol.h"
27
#include "dbus-marshal-basic.h"
28
#include "dbus-test.h"
29
#include "dbus-valgrind-internal.h"
Havoc Pennington's avatar
Havoc Pennington committed
30 31
#include <stdio.h>
#include <stdarg.h>
32
#include <string.h>
33
#include <stdlib.h>
34 35
#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
#include <windows.h>
36
#include <mbstring.h>
37
#endif
Havoc Pennington's avatar
Havoc Pennington committed
38 39

/**
40
 * @defgroup DBusInternals D-Bus secret internal implementation details
John Palmieri's avatar
John Palmieri committed
41
 * @brief Documentation useful when developing or debugging D-Bus itself.
Havoc Pennington's avatar
Havoc Pennington committed
42 43 44 45
 * 
 */

/**
46
 * @defgroup DBusInternalsUtils Utilities and portability
Havoc Pennington's avatar
Havoc Pennington committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
 * @ingroup DBusInternals
 * @brief Utility functions (_dbus_assert(), _dbus_warn(), etc.)
 * @{
 */

/**
 * @def _dbus_assert
 *
 * Aborts with an error message if the condition is false.
 * 
 * @param condition condition which must be true.
 */

/**
 * @def _dbus_assert_not_reached
 *
 * Aborts with an error message if called.
 * The given explanation will be printed.
 * 
 * @param explanation explanation of what happened if the code was reached.
 */

/**
 * @def _DBUS_N_ELEMENTS
 *
 * Computes the number of elements in a fixed-size array using
 * sizeof().
 *
 * @param array the array to count elements in.
 */

/**
 * @def _DBUS_POINTER_TO_INT
 *
 * Safely casts a void* to an integer; should only be used on void*
 * that actually contain integers, for example one created with
 * _DBUS_INT_TO_POINTER.  Only guaranteed to preserve 32 bits.
 * (i.e. it's used to store 32-bit ints in pointers, but
 * can't be used to store 64-bit pointers in ints.)
 *
 * @param pointer pointer to extract an integer from.
 */
/**
 * @def _DBUS_INT_TO_POINTER
 *
 * Safely stuffs an integer into a pointer, to be extracted later with
 * _DBUS_POINTER_TO_INT. Only guaranteed to preserve 32 bits.
 *
 * @param integer the integer to stuff into a pointer.
 */
97 98 99 100 101 102 103
/**
 * @def _DBUS_ZERO
 *
 * Sets all bits in an object to zero.
 *
 * @param object the object to be zeroed.
 */
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
/**
 * @def _DBUS_INT16_MIN
 *
 * Minimum value of type "int16"
 */
/**
 * @def _DBUS_INT16_MAX
 *
 * Maximum value of type "int16"
 */
/**
 * @def _DBUS_UINT16_MAX
 *
 * Maximum value of type "uint16"
 */

/**
 * @def _DBUS_INT32_MIN
 *
 * Minimum value of type "int32"
 */
/**
 * @def _DBUS_INT32_MAX
 *
 * Maximum value of type "int32"
 */
/**
 * @def _DBUS_UINT32_MAX
 *
 * Maximum value of type "uint32"
 */

136 137 138 139 140 141 142 143 144 145
/**
 * @def _DBUS_INT_MIN
 *
 * Minimum value of type "int"
 */
/**
 * @def _DBUS_INT_MAX
 *
 * Maximum value of type "int"
 */
146 147 148 149 150
/**
 * @def _DBUS_UINT_MAX
 *
 * Maximum value of type "uint"
 */
151 152 153 154 155 156 157

/**
 * @typedef DBusForeachFunction
 * 
 * Used to iterate over each item in a collection, such as
 * a DBusList.
 */
158

159 160 161 162 163 164 165 166 167
/**
 * @def _DBUS_LOCK_NAME
 *
 * Expands to name of a global lock variable.
 */

/**
 * @def _DBUS_LOCK
 *
168 169 170
 * Locks a global lock, initializing it first if necessary.
 *
 * @returns #FALSE if not enough memory
171 172 173 174 175 176 177 178
 */

/**
 * @def _DBUS_UNLOCK
 *
 * Unlocks a global lock
 */

179 180 181 182 183
/**
 * Fixed "out of memory" error message, just to avoid
 * making up a different string every time and wasting
 * space.
 */
184
const char *_dbus_no_memory_message = "Not enough memory";
185

186 187
static dbus_bool_t warn_initted = FALSE;
static dbus_bool_t fatal_warnings = FALSE;
188 189 190 191 192 193 194 195
static dbus_bool_t fatal_warnings_on_check_failed = TRUE;

static void
init_warnings(void)
{
  if (!warn_initted)
    {
      const char *s;
196
      s = _dbus_getenv ("DBUS_FATAL_WARNINGS");
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
      if (s && *s)
        {
          if (*s == '0')
            {
              fatal_warnings = FALSE;
              fatal_warnings_on_check_failed = FALSE;
            }
          else if (*s == '1')
            {
              fatal_warnings = TRUE;
              fatal_warnings_on_check_failed = TRUE;
            }
          else
            {
              fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'",
                      s);
            }
        }

      warn_initted = TRUE;
    }
}
219

Havoc Pennington's avatar
Havoc Pennington committed
220
/**
221 222 223 224 225
 * Prints a warning message to stderr. Can optionally be made to exit
 * fatally by setting DBUS_FATAL_WARNINGS, but this is rarely
 * used. This function should be considered pretty much equivalent to
 * fprintf(stderr). _dbus_warn_check_failed() on the other hand is
 * suitable for use when a programming mistake has been made.
Havoc Pennington's avatar
Havoc Pennington committed
226 227 228 229 230 231 232 233 234
 *
 * @param format printf-style format string.
 */
void
_dbus_warn (const char *format,
            ...)
{
  va_list args;

235
  if (!warn_initted)
236 237 238 239 240
    init_warnings ();
  
  va_start (args, format);
  vfprintf (stderr, format, args);
  va_end (args);
241

242 243 244 245
  if (fatal_warnings)
    {
      fflush (stderr);
      _dbus_abort ();
246
    }
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
}

/**
 * Prints a "critical" warning to stderr when an assertion fails;
 * differs from _dbus_warn primarily in that it prefixes the pid and
 * defaults to fatal. This should be used only when a programming
 * error has been detected. (NOT for unavoidable errors that an app
 * might handle - those should be returned as DBusError.) Calling this
 * means "there is a bug"
 */
void
_dbus_warn_check_failed(const char *format,
                        ...)
{
  va_list args;
  
  if (!warn_initted)
    init_warnings ();

266
  fprintf (stderr, "process %lu: ", _dbus_pid_for_log ());
267
  
Havoc Pennington's avatar
Havoc Pennington committed
268 269 270
  va_start (args, format);
  vfprintf (stderr, format, args);
  va_end (args);
271

272
  if (fatal_warnings_on_check_failed)
273 274 275 276
    {
      fflush (stderr);
      _dbus_abort ();
    }
Havoc Pennington's avatar
Havoc Pennington committed
277 278
}

279 280
#ifdef DBUS_ENABLE_VERBOSE_MODE

281
static dbus_bool_t verbose_initted = FALSE;
282
static dbus_bool_t verbose = TRUE;
283

284
/** Whether to show the current thread in verbose messages */
285 286
#define PTHREAD_IN_VERBOSE 0
#if PTHREAD_IN_VERBOSE
287
#include <pthread.h>
288 289
#endif

290 291 292
#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
static char module_name[1024];
#endif
293

294 295 296 297 298
static inline void
_dbus_verbose_init (void)
{
  if (!verbose_initted)
    {
299
      const char *p = _dbus_getenv ("DBUS_VERBOSE");
300 301
      verbose = p != NULL && *p == '1';
      verbose_initted = TRUE;
302
#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
303 304 305 306 307 308 309 310 311 312 313
      {
        char *last_period, *last_slash;
        GetModuleFileName(0,module_name,sizeof(module_name)-1);
        last_period = _mbsrchr(module_name,'.');
        if (last_period)
          *last_period ='\0';
        last_slash = _mbsrchr(module_name,'\\');
        if (last_slash)
          strcpy(module_name,last_slash+1);
        strcat(module_name,": ");
      }
314
#endif
315 316 317
    }
}

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
/** @def DBUS_IS_DIR_SEPARATOR(c)
 * macro for checking if character c is a patch separator
 * 
 * @todo move to a header file so that others can use this too
 */
#ifdef DBUS_WIN 
#define DBUS_IS_DIR_SEPARATOR(c) (c == '\\' || c == '/')
#else
#define DBUS_IS_DIR_SEPARATOR(c) (c == '/')
#endif

/** 
 remove source root from file path 
 the source root is determined by 
*/ 
static char *_dbus_file_path_extract_elements_from_tail(const char *file,int level)
{
335 336 337
  int prefix = 0;
  char *p = (char *)file + strlen(file);
  int i = 0;
338

339
  for (;p >= file;p--)
340
    {
341
      if (DBUS_IS_DIR_SEPARATOR(*p))
342
        {
343
          if (++i >= level)
344
            {
345 346 347 348
              prefix = p-file+1;
              break;
            }
       }
349
    }
350

351 352 353
  return (char *)file+prefix;
}

354 355 356 357 358
/**
 * Implementation of dbus_is_verbose() macro if built with verbose logging
 * enabled.
 * @returns whether verbose logging is active.
 */
359 360 361 362 363 364 365
dbus_bool_t
_dbus_is_verbose_real (void)
{
  _dbus_verbose_init ();
  return verbose;
}

366 367 368 369 370 371 372 373 374 375
void _dbus_set_verbose (dbus_bool_t state)
{
    verbose = state;
}

dbus_bool_t _dbus_get_verbose (void)
{
    return verbose;
}

376 377 378
/**
 * Prints a warning message to stderr
 * if the user has enabled verbose mode.
379 380
 * This is the real function implementation,
 * use _dbus_verbose() macro in code.
381 382 383 384
 *
 * @param format printf-style format string.
 */
void
385
_dbus_verbose_real (
386
#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
387
                    const char *file,
388 389 390
                    const int line, 
                    const char *function, 
#endif
391
                    const char *format,
392
                    ...)
393 394
{
  va_list args;
395
  static dbus_bool_t need_pid = TRUE;
396
  int len;
397
  
398 399 400 401
  /* things are written a bit oddly here so that
   * in the non-verbose case we just have the one
   * conditional and return immediately.
   */
402
  if (!_dbus_is_verbose_real())
403
    return;
404

405
#ifndef DBUS_USE_OUTPUT_DEBUG_STRING
406
  /* Print out pid before the line */
407
  if (need_pid)
408 409
    {
#if PTHREAD_IN_VERBOSE
410
      fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ());
411
#else
412
      fprintf (stderr, "%lu: ", _dbus_pid_for_log ());
413 414
#endif
    }
415
#endif
416 417 418 419 420 421 422

  /* Only print pid again if the next line is a new line */
  len = strlen (format);
  if (format[len-1] == '\n')
    need_pid = TRUE;
  else
    need_pid = FALSE;
423

424
  va_start (args, format);
425 426 427 428
#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
  {
  char buf[1024];
  strcpy(buf,module_name);
429
#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
430
  sprintf (buf+strlen(buf), "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
431
#endif
432 433
  vsprintf (buf+strlen(buf),format, args);
  va_end (args);
434
  OutputDebugStringA(buf);
435 436
  }
#else
437 438 439 440
#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
  fprintf (stderr, "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
#endif

441 442
  vfprintf (stderr, format, args);
  va_end (args);
443 444

  fflush (stderr);
445
#endif
446 447
}

448 449 450 451 452 453 454 455 456 457 458 459
/**
 * Reinitializes the verbose logging code, used
 * as a hack in dbus-spawn.c so that a child
 * process re-reads its pid
 *
 */
void
_dbus_verbose_reset_real (void)
{
  verbose_initted = FALSE;
}

460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
void
_dbus_trace_ref (const char *obj_name,
                 void       *obj,
                 int         old_refcount,
                 int         new_refcount,
                 const char *why,
                 const char *env_var,
                 int        *enabled)
{
  _dbus_assert (obj_name != NULL);
  _dbus_assert (obj != NULL);
  _dbus_assert (old_refcount >= -1);
  _dbus_assert (new_refcount >= -1);

  if (old_refcount == -1)
    {
      _dbus_assert (new_refcount == -1);
    }
  else
    {
      _dbus_assert (new_refcount >= 0);
      _dbus_assert (old_refcount >= 0);
      _dbus_assert (old_refcount > 0 || new_refcount > 0);
    }

  _dbus_assert (why != NULL);
  _dbus_assert (env_var != NULL);
  _dbus_assert (enabled != NULL);

  if (*enabled < 0)
    {
      const char *s = _dbus_getenv (env_var);

      *enabled = FALSE;

      if (s && *s)
        {
          if (*s == '0')
            *enabled = FALSE;
          else if (*s == '1')
            *enabled = TRUE;
          else
            _dbus_warn ("%s should be 0 or 1 if set, not '%s'", env_var, s);
        }
    }

  if (*enabled)
    {
      if (old_refcount == -1)
        {
          VALGRIND_PRINTF_BACKTRACE ("%s %p ref stolen (%s)",
                                     obj_name, obj, why);
512
          _dbus_verbose ("%s %p ref stolen (%s)\n",
513 514 515 516 517 518 519
                         obj_name, obj, why);
        }
      else
        {
          VALGRIND_PRINTF_BACKTRACE ("%s %p %d -> %d refs (%s)",
                                     obj_name, obj,
                                     old_refcount, new_refcount, why);
520
          _dbus_verbose ("%s %p %d -> %d refs (%s)\n",
521 522 523 524 525
                         obj_name, obj, old_refcount, new_refcount, why);
        }
    }
}

526 527
#endif /* DBUS_ENABLE_VERBOSE_MODE */

Havoc Pennington's avatar
Havoc Pennington committed
528 529 530 531 532 533 534 535 536 537 538
/**
 * Duplicates a string. Result must be freed with
 * dbus_free(). Returns #NULL if memory allocation fails.
 * If the string to be duplicated is #NULL, returns #NULL.
 * 
 * @param str string to duplicate.
 * @returns newly-allocated copy.
 */
char*
_dbus_strdup (const char *str)
{
539
  size_t len;
Havoc Pennington's avatar
Havoc Pennington committed
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
  char *copy;
  
  if (str == NULL)
    return NULL;
  
  len = strlen (str);

  copy = dbus_malloc (len + 1);
  if (copy == NULL)
    return NULL;

  memcpy (copy, str, len + 1);
  
  return copy;
}

556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
/**
 * Duplicates a block of memory. Returns
 * #NULL on failure.
 *
 * @param mem memory to copy
 * @param n_bytes number of bytes to copy
 * @returns the copy
 */
void*
_dbus_memdup (const void  *mem,
              size_t       n_bytes)
{
  void *copy;

  copy = dbus_malloc (n_bytes);
  if (copy == NULL)
    return NULL;

  memcpy (copy, mem, n_bytes);
  
  return copy;
}

579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
/**
 * Duplicates a string array. Result may be freed with
 * dbus_free_string_array(). Returns #NULL if memory allocation fails.
 * If the array to be duplicated is #NULL, returns #NULL.
 * 
 * @param array array to duplicate.
 * @returns newly-allocated copy.
 */
char**
_dbus_dup_string_array (const char **array)
{
  int len;
  int i;
  char **copy;
  
  if (array == NULL)
    return NULL;

  for (len = 0; array[len] != NULL; ++len)
    ;

  copy = dbus_new0 (char*, len + 1);
  if (copy == NULL)
    return NULL;

  i = 0;
  while (i < len)
    {
      copy[i] = _dbus_strdup (array[i]);
      if (copy[i] == NULL)
        {
          dbus_free_string_array (copy);
          return NULL;
        }

      ++i;
    }

  return copy;
}

/**
 * Checks whether a string array contains the given string.
 * 
 * @param array array to search.
 * @param str string to look for
 * @returns #TRUE if array contains string
 */
dbus_bool_t
_dbus_string_array_contains (const char **array,
                             const char  *str)
{
  int i;

  i = 0;
  while (array[i] != NULL)
    {
      if (strcmp (array[i], str) == 0)
        return TRUE;
      ++i;
    }

  return FALSE;
}

644 645 646 647 648
/**
 * Generates a new UUID. If you change how this is done,
 * there's some text about it in the spec that should also change.
 *
 * @param uuid the uuid to initialize
649 650
 * @param error location to store reason for failure
 * @returns #TRUE on success
651
 */
652 653 654
dbus_bool_t
_dbus_generate_uuid (DBusGUID  *uuid,
                     DBusError *error)
655
{
656
  DBusError rand_error;
657 658
  long now;

659 660
  dbus_error_init (&rand_error);

661 662 663 664
  /* don't use monotonic time because the UUID may be saved to disk, e.g.
   * it may persist across reboots
   */
  _dbus_get_real_time (&now, NULL);
665

666
  uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
667
  
668 669 670 671 672 673 674 675 676 677
  if (!_dbus_generate_random_bytes_buffer (uuid->as_bytes,
                                           DBUS_UUID_LENGTH_BYTES - 4,
                                           &rand_error))
    {
      dbus_set_error (error, rand_error.name,
                      "Failed to generate UUID: %s", rand_error.message);
      dbus_error_free (&rand_error);
      return FALSE;
    }

678
  return TRUE;
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
}

/**
 * Hex-encode a UUID.
 *
 * @param uuid the uuid
 * @param encoded string to append hex uuid to
 * @returns #FALSE if no memory
 */
dbus_bool_t
_dbus_uuid_encode (const DBusGUID *uuid,
                   DBusString     *encoded)
{
  DBusString binary;
  _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
  return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
}

static dbus_bool_t
_dbus_read_uuid_file_without_creating (const DBusString *filename,
                                       DBusGUID         *uuid,
                                       DBusError        *error)
{
  DBusString contents;
  DBusString decoded;
  int end;
  
John Palmieri's avatar
John Palmieri committed
706 707 708 709 710 711 712 713 714 715 716 717
  if (!_dbus_string_init (&contents))
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }

  if (!_dbus_string_init (&decoded))
    {
      _dbus_string_free (&contents);
      _DBUS_SET_OOM (error);
      return FALSE;
    }
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
  
  if (!_dbus_file_get_contents (&contents, filename, error))
    goto error;

  _dbus_string_chop_white (&contents);

  if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
    {
      dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
                      "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
                      _dbus_string_get_const_data (filename),
                      DBUS_UUID_LENGTH_HEX,
                      _dbus_string_get_length (&contents));
      goto error;
    }

  if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
    {
      _DBUS_SET_OOM (error);
      goto error;
    }

  if (end == 0)
    {
      dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
                      "UUID file '%s' contains invalid hex data",
                      _dbus_string_get_const_data (filename));
      goto error;
    }

  if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
    {
      dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
                      "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
                      _dbus_string_get_const_data (filename),
                      _dbus_string_get_length (&decoded),
                      DBUS_UUID_LENGTH_BYTES);
      goto error;
    }

  _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);

  _dbus_string_free (&decoded);
  _dbus_string_free (&contents);

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

  return TRUE;
  
 error:
  _DBUS_ASSERT_ERROR_IS_SET (error);
  _dbus_string_free (&contents);
  _dbus_string_free (&decoded);
  return FALSE;
}

774 775 776 777 778 779 780 781 782 783 784 785
/**
 * Write the give UUID to a file.
 *
 * @param filename the file to write
 * @param uuid the UUID to save
 * @param error used to raise an error
 * @returns #FALSE on error
 */
dbus_bool_t
_dbus_write_uuid_file (const DBusString *filename,
                       const DBusGUID   *uuid,
                       DBusError        *error)
786 787 788
{
  DBusString encoded;

John Palmieri's avatar
John Palmieri committed
789 790 791 792 793
  if (!_dbus_string_init (&encoded))
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }
794 795 796 797 798 799 800 801 802 803 804 805 806
  
  if (!_dbus_uuid_encode (uuid, &encoded))
    {
      _DBUS_SET_OOM (error);
      goto error;
    }
  
  if (!_dbus_string_append_byte (&encoded, '\n'))
    {
      _DBUS_SET_OOM (error);
      goto error;
    }
  
Colin Walters's avatar
Colin Walters committed
807
  if (!_dbus_string_save_to_file (&encoded, filename, TRUE, error))
808
    goto error;
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836

  _dbus_string_free (&encoded);

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
  return TRUE;
  
 error:
  _DBUS_ASSERT_ERROR_IS_SET (error);
  _dbus_string_free (&encoded);
  return FALSE;        
}

/**
 * Reads (and optionally writes) a uuid to a file. Initializes the uuid
 * unless an error is returned.
 *
 * @param filename the name of the file
 * @param uuid uuid to be initialized with the loaded uuid
 * @param create_if_not_found #TRUE to create a new uuid and save it if the file doesn't exist
 * @param error the error return
 * @returns #FALSE if the error is set
 */
dbus_bool_t
_dbus_read_uuid_file (const DBusString *filename,
                      DBusGUID         *uuid,
                      dbus_bool_t       create_if_not_found,
                      DBusError        *error)
{
837
  DBusError read_error = DBUS_ERROR_INIT;
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859

  if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
    return TRUE;

  if (!create_if_not_found)
    {
      dbus_move_error (&read_error, error);
      return FALSE;
    }

  /* If the file exists and contains junk, we want to keep that error
   * message instead of overwriting it with a "file exists" error
   * message when we try to write
   */
  if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
    {
      dbus_move_error (&read_error, error);
      return FALSE;
    }
  else
    {
      dbus_error_free (&read_error);
860 861 862 863

      if (!_dbus_generate_uuid (uuid, error))
        return FALSE;

864
      return _dbus_write_uuid_file (filename, uuid, error);
865 866 867
    }
}

868
/* Protected by _DBUS_LOCK (machine_uuid) */
869 870 871 872 873 874 875 876 877 878 879
static int machine_uuid_initialized_generation = 0;
static DBusGUID machine_uuid;

/**
 * Gets the hex-encoded UUID of the machine this function is
 * executed on. This UUID is guaranteed to be the same for a given
 * machine at least until it next reboots, though it also
 * makes some effort to be the same forever, it may change if the
 * machine is reconfigured or its hardware is modified.
 * 
 * @param uuid_str string to append hex-encoded machine uuid to
880 881
 * @param error location to store reason for failure
 * @returns #TRUE if successful
882 883
 */
dbus_bool_t
884 885
_dbus_get_local_machine_uuid_encoded (DBusString *uuid_str,
                                      DBusError  *error)
886
{
887
  dbus_bool_t ok = TRUE;
888
  
889
  if (!_DBUS_LOCK (machine_uuid))
890 891 892 893
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }
894

895 896
  if (machine_uuid_initialized_generation != _dbus_current_generation)
    {
897
      DBusError local_error = DBUS_ERROR_INIT;
898

899
      if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
900
                                          &local_error))
901
        {          
902
#ifndef DBUS_ENABLE_EMBEDDED_TESTS
903 904 905 906
          /* For the test suite, we may not be installed so just continue silently
           * here. But in a production build, we want to be nice and loud about
           * this.
           */
907 908
          _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
                                   "See the manual page for dbus-uuidgen to correct this issue.\n",
909
                                   local_error.message);
910 911
#endif
          
912
          dbus_error_free (&local_error);
913
          
914
          ok = _dbus_generate_uuid (&machine_uuid, error);
915 916 917
        }
    }

918 919 920 921 922 923 924 925
  if (ok)
    {
      if (!_dbus_uuid_encode (&machine_uuid, uuid_str))
        {
          ok = FALSE;
          _DBUS_SET_OOM (error);
        }
    }
926 927 928 929 930 931

  _DBUS_UNLOCK (machine_uuid);

  return ok;
}

932
#ifndef DBUS_DISABLE_CHECKS
933
/** String used in _dbus_return_if_fail macro */
934
const char *_dbus_return_if_fail_warning_format =
935
"arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
John Palmieri's avatar
John Palmieri committed
936
"This is normally a bug in some application using the D-Bus library.\n";
937 938
#endif

939 940 941 942 943 944 945 946 947 948 949
#ifndef DBUS_DISABLE_ASSERT
/**
 * Internals of _dbus_assert(); it's a function
 * rather than a macro with the inline code so
 * that the assertion failure blocks don't show up
 * in test suite coverage, and to shrink code size.
 *
 * @param condition TRUE if assertion succeeded
 * @param condition_text condition as a string
 * @param file file the assertion is in
 * @param line line the assertion is in
950
 * @param func function the assertion is in
951 952 953 954 955
 */
void
_dbus_real_assert (dbus_bool_t  condition,
                   const char  *condition_text,
                   const char  *file,
956 957
                   int          line,
                   const char  *func)
958
{
959
  if (_DBUS_UNLIKELY (!condition))
960
    {
961
      _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
962
                  _dbus_pid_for_log (), condition_text, file, line, func);
963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
      _dbus_abort ();
    }
}

/**
 * Internals of _dbus_assert_not_reached(); it's a function
 * rather than a macro with the inline code so
 * that the assertion failure blocks don't show up
 * in test suite coverage, and to shrink code size.
 *
 * @param explanation what was reached that shouldn't have been
 * @param file file the assertion is in
 * @param line line the assertion is in
 */
void
_dbus_real_assert_not_reached (const char *explanation,
                               const char *file,
                               int         line)
{
982
  _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
983
              file, line, _dbus_pid_for_log (), explanation);
984 985 986 987
  _dbus_abort ();
}
#endif /* DBUS_DISABLE_ASSERT */
  
988
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
static dbus_bool_t
run_failing_each_malloc (int                    n_mallocs,
                         const char            *description,
                         DBusTestMemoryFunction func,
                         void                  *data)
{
  n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
  
  while (n_mallocs >= 0)
    {      
      _dbus_set_fail_alloc_counter (n_mallocs);

      _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
                     description, n_mallocs,
                     _dbus_get_fail_alloc_failures ());

      if (!(* func) (data))
        return FALSE;
      
      n_mallocs -= 1;
    }

  _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);

  return TRUE;
}                        

/**
 * Tests how well the given function responds to out-of-memory
 * situations. Calls the function repeatedly, failing a different
 * call to malloc() each time. If the function ever returns #FALSE,
 * the test fails. The function should return #TRUE whenever something
 * valid (such as returning an error, or succeeding) occurs, and #FALSE
 * if it gets confused in some way.
 *
 * @param description description of the test used in verbose output
 * @param func function to call
 * @param data data to pass to function
 * @returns #TRUE if the function never returns FALSE
 */
dbus_bool_t
_dbus_test_oom_handling (const char             *description,
                         DBusTestMemoryFunction  func,
                         void                   *data)
{
  int approx_mallocs;
1035 1036 1037
  const char *setting;
  int max_failures_to_try;
  int i;
1038 1039 1040 1041 1042

  /* Run once to see about how many mallocs are involved */
  
  _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);

1043 1044
  _dbus_verbose ("Running once to count mallocs\n");
  
1045 1046
  if (!(* func) (data))
    return FALSE;
1047
  
1048 1049
  approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();

1050
  _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
1051 1052
                 description, approx_mallocs);

1053
  setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
  if (setting != NULL)
    {
      DBusString str;
      long v;
      _dbus_string_init_const (&str, setting);
      v = 4;
      if (!_dbus_string_parse_int (&str, 0, &v, NULL))
        _dbus_warn ("couldn't parse '%s' as integer\n", setting);
      max_failures_to_try = v;
    }
  else
    {
      max_failures_to_try = 4;
    }
1068

1069 1070 1071 1072 1073 1074
  if (max_failures_to_try < 1)
    {
      _dbus_verbose ("not testing OOM handling\n");
      return TRUE;
    }

1075 1076 1077 1078 1079 1080 1081 1082
  i = setting ? max_failures_to_try - 1 : 1;
  while (i < max_failures_to_try)
    {
      _dbus_set_fail_alloc_failures (i);
      if (!run_failing_each_malloc (approx_mallocs, description, func, data))
        return FALSE;
      ++i;
    }
1083
  
1084
  _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
1085 1086 1087 1088
                 description);

  return TRUE;
}
1089
#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
1090

Havoc Pennington's avatar
Havoc Pennington committed
1091
/** @} */