dbus-internals.c 26.3 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-test-tap.h"
30
#include "dbus-valgrind-internal.h"
Havoc Pennington's avatar
Havoc Pennington committed
31 32
#include <stdio.h>
#include <stdarg.h>
33
#include <string.h>
34
#include <stdlib.h>
35 36
#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
#include <windows.h>
37
#include <mbstring.h>
38
#endif
Havoc Pennington's avatar
Havoc Pennington committed
39 40

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

/**
47
 * @defgroup DBusInternalsUtils Utilities and portability
Havoc Pennington's avatar
Havoc Pennington committed
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 97
 * @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.
 */
98 99 100 101 102 103 104
/**
 * @def _DBUS_ZERO
 *
 * Sets all bits in an object to zero.
 *
 * @param object the object to be zeroed.
 */
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 136
/**
 * @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"
 */

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

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

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

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

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

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

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

static void
init_warnings(void)
{
  if (!warn_initted)
    {
      const char *s;
197
      s = _dbus_getenv ("DBUS_FATAL_WARNINGS");
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
      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;
    }
}
220

Havoc Pennington's avatar
Havoc Pennington committed
221
/**
222 223 224 225 226
 * 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
227 228 229 230 231 232 233
 *
 * @param format printf-style format string.
 */
void
_dbus_warn (const char *format,
            ...)
{
234
  DBusSystemLogSeverity severity = DBUS_SYSTEM_LOG_WARNING;
Havoc Pennington's avatar
Havoc Pennington committed
235 236
  va_list args;

237
  if (!warn_initted)
238
    init_warnings ();
239 240

  if (fatal_warnings)
241
    severity = DBUS_SYSTEM_LOG_ERROR;
242

243
  va_start (args, format);
244
  _dbus_logv (severity, format, args);
245
  va_end (args);
246

247 248 249 250
  if (fatal_warnings)
    {
      fflush (stderr);
      _dbus_abort ();
251
    }
252 253 254 255
}

/**
 * Prints a "critical" warning to stderr when an assertion fails;
256
 * differs from _dbus_warn primarily in that it
257 258 259 260 261 262 263 264 265
 * 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,
                        ...)
{
266
  DBusSystemLogSeverity severity = DBUS_SYSTEM_LOG_WARNING;
267 268 269 270 271
  va_list args;
  
  if (!warn_initted)
    init_warnings ();

272
  if (fatal_warnings_on_check_failed)
273
    severity = DBUS_SYSTEM_LOG_ERROR;
274

Havoc Pennington's avatar
Havoc Pennington committed
275
  va_start (args, format);
276
  _dbus_logv (severity, format, args);
Havoc Pennington's avatar
Havoc Pennington committed
277
  va_end (args);
278

279
  if (fatal_warnings_on_check_failed)
280 281 282 283
    {
      fflush (stderr);
      _dbus_abort ();
    }
Havoc Pennington's avatar
Havoc Pennington committed
284 285
}

286 287
#ifdef DBUS_ENABLE_VERBOSE_MODE

288
static dbus_bool_t verbose_initted = FALSE;
289
static dbus_bool_t verbose = TRUE;
290

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

295 296 297 298 299
static inline void
_dbus_verbose_init (void)
{
  if (!verbose_initted)
    {
300
      const char *p = _dbus_getenv ("DBUS_VERBOSE");
301 302
      verbose = p != NULL && *p == '1';
      verbose_initted = TRUE;
303
#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
Tor Lillqvist's avatar
Tor Lillqvist committed
304 305 306 307 308 309 310 311 312 313 314
      {
        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,": ");
      }
315
#endif
316 317 318
    }
}

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
/** @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)
{
336 337 338
  int prefix = 0;
  char *p = (char *)file + strlen(file);
  int i = 0;
339

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

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

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

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

dbus_bool_t _dbus_get_verbose (void)
{
    return verbose;
}

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

407
#ifndef DBUS_USE_OUTPUT_DEBUG_STRING
408
  /* Print out pid before the line */
409
  if (need_pid)
410
    {
411
      _dbus_print_thread ();
412
    }
413
  _dbus_get_real_time (&sec, &usec);
414
  fprintf (stderr, "%ld.%06ld ", sec, usec);
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
/**
 * Reinitializes the verbose logging code, used
450
 * as a hack in dbus-spawn-unix.c so that a child
451 452 453 454 455 456 457 458 459
 * 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 649 650 651 652 653 654 655 656 657 658
/**
 * Returns the size of a string array
 *
 * @param array array to search.
 * @returns size of array
 */
size_t
_dbus_string_array_length (const char **array)
{
  size_t i;
  for (i = 0; array[i]; i++) {}
  return i;
}


659 660 661 662 663
/**
 * 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
664 665
 * @param error location to store reason for failure
 * @returns #TRUE on success
666
 */
667 668 669
dbus_bool_t
_dbus_generate_uuid (DBusGUID  *uuid,
                     DBusError *error)
670
{
671
  DBusError rand_error;
672 673
  long now;

674 675
  dbus_error_init (&rand_error);

676 677 678 679
  /* 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);
680

681
  uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
682
  
683 684 685 686 687 688 689 690 691 692
  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;
    }

693
  return TRUE;
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
}

/**
 * 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
721 722 723 724 725 726 727 728 729 730 731 732
  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;
    }
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 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
  
  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;
}

789 790 791 792 793 794 795 796 797 798 799 800
/**
 * 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)
801 802 803
{
  DBusString encoded;

John Palmieri's avatar
John Palmieri committed
804 805 806 807 808
  if (!_dbus_string_init (&encoded))
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }
809 810 811 812 813 814 815 816 817 818 819 820 821
  
  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
822
  if (!_dbus_string_save_to_file (&encoded, filename, TRUE, error))
Thiago Macieira's avatar
Thiago Macieira committed
823
    goto error;
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851

  _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)
{
852
  DBusError read_error = DBUS_ERROR_INIT;
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874

  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);
875 876 877 878

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

879
      return _dbus_write_uuid_file (filename, uuid, error);
880 881 882
    }
}

883
/* Protected by _DBUS_LOCK (machine_uuid) */
884 885 886 887 888 889 890 891 892 893 894
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
895 896
 * @param error location to store reason for failure
 * @returns #TRUE if successful
897 898
 */
dbus_bool_t
899 900
_dbus_get_local_machine_uuid_encoded (DBusString *uuid_str,
                                      DBusError  *error)
901
{
902
  dbus_bool_t ok = TRUE;
903
  
904
  if (!_DBUS_LOCK (machine_uuid))
905 906 907 908
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }
909

910 911
  if (machine_uuid_initialized_generation != _dbus_current_generation)
    {
912 913
      if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE, error))
        ok = FALSE;
914 915
    }

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

  _DBUS_UNLOCK (machine_uuid);

  return ok;
}

930
#ifndef DBUS_DISABLE_CHECKS
931 932 933 934 935 936 937 938 939 940 941
void
_dbus_warn_return_if_fail (const char *function,
                           const char *assertion,
                           const char *file,
                           int line)
{
  _dbus_warn_check_failed (
      "arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
      "This is normally a bug in some application using the D-Bus library.\n",
      function, assertion, file, line);
}
942 943
#endif

944 945 946 947 948 949 950 951 952 953 954
#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
955
 * @param func function the assertion is in
956 957 958 959 960
 */
void
_dbus_real_assert (dbus_bool_t  condition,
                   const char  *condition_text,
                   const char  *file,
961 962
                   int          line,
                   const char  *func)
963
{
964
  if (_DBUS_UNLIKELY (!condition))
965
    {
966 967
      _dbus_warn ("assertion failed \"%s\" file \"%s\" line %d function %s",
                  condition_text, file, line, func);
968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
      _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)
{
987 988
  _dbus_warn ("File \"%s\" line %d should not have been reached: %s",
              file, line, explanation);
989 990 991 992
  _dbus_abort ();
}
#endif /* DBUS_DISABLE_ASSERT */
  
993
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
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 ());

1010
      if (!(* func) (data, FALSE))
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
        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;
1040 1041 1042
  const char *setting;
  int max_failures_to_try;
  int i;
1043 1044 1045 1046 1047

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

1048 1049
  _dbus_test_diag ("Running \"%s\" once to count mallocs", description);

1050
  if (!(* func) (data, TRUE))
1051
    return FALSE;
1052

1053 1054
  approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();

1055 1056
  _dbus_test_diag ("\"%s\" has about %d mallocs in total",
                   description, approx_mallocs);
1057

1058
  setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
  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;
    }
1073

1074 1075
  if (max_failures_to_try < 1)
    {
1076
      _dbus_test_diag ("not testing OOM handling");
1077 1078 1079
      return TRUE;
    }

1080 1081 1082
  _dbus_test_diag ("testing \"%s\" with up to %d consecutive malloc failures",
                   description, max_failures_to_try);

1083 1084 1085
  i = setting ? max_failures_to_try - 1 : 1;
  while (i < max_failures_to_try)
    {
1086 1087 1088
      _dbus_test_diag ("testing \"%s\" with %d consecutive malloc failures",
                       description, i + 1);

1089 1090 1091 1092 1093
      _dbus_set_fail_alloc_failures (i);
      if (!run_failing_each_malloc (approx_mallocs, description, func, data))
        return FALSE;
      ++i;
    }
1094
  
1095
  _dbus_verbose ("\"%s\" coped OK with malloc failures", description);
1096 1097 1098

  return TRUE;
}
1099
#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
1100

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