dbus-internals.c 25.5 KB
Newer Older
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
John (J5) Palmieri's avatar
John (J5) 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 (J5) Palmieri's avatar
John (J5) 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
/**
 * Prints a warning message to stderr
 * if the user has enabled verbose mode.
369 370
 * This is the real function implementation,
 * use _dbus_verbose() macro in code.
371 372 373 374
 *
 * @param format printf-style format string.
 */
void
375
_dbus_verbose_real (
376
#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
377
                    const char *file,
378 379 380
                    const int line, 
                    const char *function, 
#endif
381
                    const char *format,
382
                    ...)
383 384
{
  va_list args;
385
  static dbus_bool_t need_pid = TRUE;
386
  int len;
387
  
388 389 390 391
  /* things are written a bit oddly here so that
   * in the non-verbose case we just have the one
   * conditional and return immediately.
   */
392
  if (!_dbus_is_verbose_real())
393
    return;
394

395
#ifndef DBUS_USE_OUTPUT_DEBUG_STRING
396
  /* Print out pid before the line */
397
  if (need_pid)
398 399
    {
#if PTHREAD_IN_VERBOSE
400
      fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ());
401
#else
402
      fprintf (stderr, "%lu: ", _dbus_pid_for_log ());
403 404
#endif
    }
405
#endif
406 407 408 409 410 411 412

  /* 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;
413

414
  va_start (args, format);
415 416 417 418
#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
  {
  char buf[1024];
  strcpy(buf,module_name);
419
#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
420
  sprintf (buf+strlen(buf), "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
421
#endif
422 423
  vsprintf (buf+strlen(buf),format, args);
  va_end (args);
424
  OutputDebugStringA(buf);
425 426
  }
#else
427 428 429 430
#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
  fprintf (stderr, "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
#endif

431 432
  vfprintf (stderr, format, args);
  va_end (args);
433 434

  fflush (stderr);
435
#endif
436 437
}

438 439 440 441 442 443 444 445 446 447 448 449
/**
 * 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;
}

450 451 452 453 454 455 456 457 458 459 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
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);
502
          _dbus_verbose ("%s %p ref stolen (%s)\n",
503 504 505 506 507 508 509
                         obj_name, obj, why);
        }
      else
        {
          VALGRIND_PRINTF_BACKTRACE ("%s %p %d -> %d refs (%s)",
                                     obj_name, obj,
                                     old_refcount, new_refcount, why);
510
          _dbus_verbose ("%s %p %d -> %d refs (%s)\n",
511 512 513 514 515
                         obj_name, obj, old_refcount, new_refcount, why);
        }
    }
}

516 517
#endif /* DBUS_ENABLE_VERBOSE_MODE */

Havoc Pennington's avatar
Havoc Pennington committed
518 519 520 521 522 523 524 525 526 527 528
/**
 * 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)
{
529
  size_t len;
Havoc Pennington's avatar
Havoc Pennington committed
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
  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;
}

546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
/**
 * 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;
}

569 570 571 572 573 574 575 576 577 578 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
/**
 * 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;
}

634 635 636 637 638 639 640 641 642 643 644
/**
 * 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
 */
void
_dbus_generate_uuid (DBusGUID *uuid)
{
  long now;

645 646 647 648
  /* 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);
649

650
  uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
651
  
652
  _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4);
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
}

/**
 * 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 (J5) Palmieri's avatar
John (J5) Palmieri committed
680 681 682 683 684 685 686 687 688 689 690 691
  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;
    }
692 693 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 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
  
  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;
}

748 749 750 751 752 753 754 755 756 757 758 759
/**
 * 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)
760 761 762
{
  DBusString encoded;

John (J5) Palmieri's avatar
John (J5) Palmieri committed
763 764 765 766 767
  if (!_dbus_string_init (&encoded))
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }
768 769 770 771 772 773 774 775 776 777 778 779 780
  
  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
781
  if (!_dbus_string_save_to_file (&encoded, filename, TRUE, error))
782
    goto error;
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810

  _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)
{
811
  DBusError read_error = DBUS_ERROR_INIT;
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833

  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);
834 835
      _dbus_generate_uuid (uuid);
      return _dbus_write_uuid_file (filename, uuid, error);
836 837 838
    }
}

839
/* Protected by _DBUS_LOCK (machine_uuid) */
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
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
 * @returns #FALSE if no memory
 */
dbus_bool_t
_dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
{
  dbus_bool_t ok;
  
858 859 860
  if (!_DBUS_LOCK (machine_uuid))
    return FALSE;

861 862
  if (machine_uuid_initialized_generation != _dbus_current_generation)
    {
863 864
      DBusError error = DBUS_ERROR_INIT;

865 866 867
      if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
                                          &error))
        {          
868
#ifndef DBUS_ENABLE_EMBEDDED_TESTS
869 870 871 872
          /* 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.
           */
873 874 875
          _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",
                                   error.message);
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
#endif
          
          dbus_error_free (&error);
          
          _dbus_generate_uuid (&machine_uuid);
        }
    }

  ok = _dbus_uuid_encode (&machine_uuid, uuid_str);

  _DBUS_UNLOCK (machine_uuid);

  return ok;
}

891
#ifndef DBUS_DISABLE_CHECKS
892
/** String used in _dbus_return_if_fail macro */
893
const char *_dbus_return_if_fail_warning_format =
894
"arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
John (J5) Palmieri's avatar
John (J5) Palmieri committed
895
"This is normally a bug in some application using the D-Bus library.\n";
896 897
#endif

898 899 900 901 902 903 904 905 906 907 908
#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
909
 * @param func function the assertion is in
910 911 912 913 914
 */
void
_dbus_real_assert (dbus_bool_t  condition,
                   const char  *condition_text,
                   const char  *file,
915 916
                   int          line,
                   const char  *func)
917
{
918
  if (_DBUS_UNLIKELY (!condition))
919
    {
920
      _dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
921
                  _dbus_pid_for_log (), condition_text, file, line, func);
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
      _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)
{
941
  _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
942
              file, line, _dbus_pid_for_log (), explanation);
943 944 945 946
  _dbus_abort ();
}
#endif /* DBUS_DISABLE_ASSERT */
  
947
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
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;
994 995 996
  const char *setting;
  int max_failures_to_try;
  int i;
997 998 999 1000 1001

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

1002 1003
  _dbus_verbose ("Running once to count mallocs\n");
  
1004 1005
  if (!(* func) (data))
    return FALSE;
1006
  
1007 1008
  approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();

1009
  _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
1010 1011
                 description, approx_mallocs);

1012
  setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES");
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
  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;
    }
1027

1028 1029 1030 1031 1032 1033 1034 1035
  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;
    }
1036
  
1037
  _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
1038 1039 1040 1041
                 description);

  return TRUE;
}
1042
#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
1043

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