1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2/* dbus-sysdeps.c Wrappers around system/libc features shared between UNIX and Windows (internal to D-Bus implementation)
3 *
4 * Copyright (C) 2002, 2003, 2006  Red Hat, Inc.
5 * Copyright (C) 2003 CodeFactory AB
6 *
7 * Licensed under the Academic Free License version 2.1
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 */
24
25#include <config.h>
26#include "dbus-internals.h"
27#include "dbus-sysdeps.h"
28#include "dbus-threads.h"
29#include "dbus-protocol.h"
30#include "dbus-string.h"
31#include "dbus-list.h"
32
33/* NOTE: If you include any unix/windows-specific headers here, you are probably doing something
34 * wrong and should be putting some code in dbus-sysdeps-unix.c or dbus-sysdeps-win.c.
35 *
36 * These are the standard ANSI C headers...
37 */
38#if HAVE_LOCALE_H
39#include <locale.h>
40#endif
41#include <stdlib.h>
42#include <string.h>
43#include <stdio.h>
44
45#ifdef HAVE_ERRNO_H
46#include <errno.h>
47#endif
48
49_DBUS_DEFINE_GLOBAL_LOCK (win_fds);
50_DBUS_DEFINE_GLOBAL_LOCK (sid_atom_cache);
51_DBUS_DEFINE_GLOBAL_LOCK (system_users);
52
53#ifdef DBUS_WIN
54  #include <stdlib.h>
55#elif (defined __APPLE__)
56# include <crt_externs.h>
57# define environ (*_NSGetEnviron())
58#else
59extern char **environ;
60#endif
61
62/**
63 * @defgroup DBusSysdeps Internal system-dependent API
64 * @ingroup DBusInternals
65 * @brief Internal system-dependent API available on UNIX and Windows
66 *
67 * The system-dependent API has a dual purpose. First, it encapsulates
68 * all usage of operating system APIs for ease of auditing and to
69 * avoid cluttering the rest of the code with bizarre OS quirks and
70 * headers. Second, it abstracts different operating system APIs for
71 * portability.
72 *
73 * @{
74 */
75
76/**
77 * Aborts the program with SIGABRT (dumping core).
78 */
79void
80_dbus_abort (void)
81{
82  const char *s;
83
84  _dbus_print_backtrace ();
85
86  s = _dbus_getenv ("DBUS_BLOCK_ON_ABORT");
87  if (s && *s)
88    {
89      /* don't use _dbus_warn here since it can _dbus_abort() */
90      fprintf (stderr, "  Process %lu sleeping for gdb attach\n", _dbus_pid_for_log ());
91      _dbus_sleep_milliseconds (1000 * 180);
92    }
93
94  abort ();
95  _dbus_exit (1); /* in case someone manages to ignore SIGABRT ? */
96}
97
98/**
99 * Wrapper for setenv(). If the value is #NULL, unsets
100 * the environment variable.
101 *
102 * There is an unfixable memleak in that it is unsafe to
103 * free memory malloced for use with setenv. This is because
104 * we can not rely on internal implementation details of
105 * the underlying libc library.
106 *
107 * @param varname name of environment variable
108 * @param value value of environment variable
109 * @returns #TRUE on success.
110 */
111dbus_bool_t
112_dbus_setenv (const char *varname,
113              const char *value)
114{
115  _dbus_assert (varname != NULL);
116
117  if (value == NULL)
118    {
119#ifdef HAVE_UNSETENV
120      unsetenv (varname);
121      return TRUE;
122#else
123      char *putenv_value;
124      size_t len;
125
126      len = strlen (varname);
127
128      /* Use system malloc to avoid memleaks that dbus_malloc
129       * will get upset about.
130       */
131
132      putenv_value = malloc (len + 2);
133      if (putenv_value == NULL)
134        return FALSE;
135
136      strcpy (putenv_value, varname);
137#if defined(DBUS_WIN)
138      strcat (putenv_value, "=");
139#endif
140
141      return (putenv (putenv_value) == 0);
142#endif
143    }
144  else
145    {
146#ifdef HAVE_SETENV
147      return (setenv (varname, value, TRUE) == 0);
148#else
149      char *putenv_value;
150      size_t len;
151      size_t varname_len;
152      size_t value_len;
153
154      varname_len = strlen (varname);
155      value_len = strlen (value);
156
157      len = varname_len + value_len + 1 /* '=' */ ;
158
159      /* Use system malloc to avoid memleaks that dbus_malloc
160       * will get upset about.
161       */
162
163      putenv_value = malloc (len + 1);
164      if (putenv_value == NULL)
165        return FALSE;
166
167      strcpy (putenv_value, varname);
168      strcpy (putenv_value + varname_len, "=");
169      strcpy (putenv_value + varname_len + 1, value);
170
171      return (putenv (putenv_value) == 0);
172#endif
173    }
174}
175
176/**
177 * Wrapper for getenv().
178 *
179 * @param varname name of environment variable
180 * @returns value of environment variable or #NULL if unset
181 */
182const char*
183_dbus_getenv (const char *varname)
184{
185  /* Don't respect any environment variables if the current process is
186   * setuid.  This is the equivalent of glibc's __secure_getenv().
187   */
188  if (_dbus_check_setuid ())
189    return NULL;
190  return getenv (varname);
191}
192
193/**
194 * Wrapper for clearenv().
195 *
196 * @returns #TRUE on success.
197 */
198dbus_bool_t
199_dbus_clearenv (void)
200{
201  dbus_bool_t rc = TRUE;
202
203#ifdef HAVE_CLEARENV
204  if (clearenv () != 0)
205     rc = FALSE;
206#else
207
208  if (environ != NULL)
209    environ[0] = NULL;
210#endif
211
212  return rc;
213}
214
215/**
216 * Split paths into a list of char strings
217 *
218 * @param dirs string with pathes
219 * @param suffix string concated to each path in dirs
220 * @param dir_list contains a list of splitted pathes
221 * return #TRUE is pathes could be splittes,#FALSE in oom case
222 */
223dbus_bool_t
224_dbus_split_paths_and_append (DBusString *dirs,
225                              const char *suffix,
226                              DBusList  **dir_list)
227{
228   int start;
229   int i;
230   int len;
231   char *cpath;
232   DBusString file_suffix;
233
234   start = 0;
235   i = 0;
236
237   _dbus_string_init_const (&file_suffix, suffix);
238
239   len = _dbus_string_get_length (dirs);
240
241   while (_dbus_string_find (dirs, start, _DBUS_PATH_SEPARATOR, &i))
242     {
243       DBusString path;
244
245       if (!_dbus_string_init (&path))
246          goto oom;
247
248       if (!_dbus_string_copy_len (dirs,
249                                   start,
250                                   i - start,
251                                   &path,
252                                   0))
253          {
254            _dbus_string_free (&path);
255            goto oom;
256          }
257
258        _dbus_string_chop_white (&path);
259
260        /* check for an empty path */
261        if (_dbus_string_get_length (&path) == 0)
262          goto next;
263
264        if (!_dbus_concat_dir_and_file (&path,
265                                        &file_suffix))
266          {
267            _dbus_string_free (&path);
268            goto oom;
269          }
270
271        if (!_dbus_string_copy_data(&path, &cpath))
272          {
273            _dbus_string_free (&path);
274            goto oom;
275          }
276
277        if (!_dbus_list_append (dir_list, cpath))
278          {
279            _dbus_string_free (&path);
280            dbus_free (cpath);
281            goto oom;
282          }
283
284       next:
285        _dbus_string_free (&path);
286        start = i + 1;
287    }
288
289  if (start != len)
290    {
291      DBusString path;
292
293      if (!_dbus_string_init (&path))
294        goto oom;
295
296      if (!_dbus_string_copy_len (dirs,
297                                  start,
298                                  len - start,
299                                  &path,
300                                  0))
301        {
302          _dbus_string_free (&path);
303          goto oom;
304        }
305
306      if (!_dbus_concat_dir_and_file (&path,
307                                      &file_suffix))
308        {
309          _dbus_string_free (&path);
310          goto oom;
311        }
312
313      if (!_dbus_string_copy_data(&path, &cpath))
314        {
315          _dbus_string_free (&path);
316          goto oom;
317        }
318
319      if (!_dbus_list_append (dir_list, cpath))
320        {
321          _dbus_string_free (&path);
322          dbus_free (cpath);
323          goto oom;
324        }
325
326      _dbus_string_free (&path);
327    }
328
329  return TRUE;
330
331 oom:
332  _dbus_list_foreach (dir_list, (DBusForeachFunction)dbus_free, NULL);
333  _dbus_list_clear (dir_list);
334  return FALSE;
335}
336
337/** @} */
338
339/**
340 * @addtogroup DBusString
341 *
342 * @{
343 */
344/**
345 * Appends an integer to a DBusString.
346 *
347 * @param str the string
348 * @param value the integer value
349 * @returns #FALSE if not enough memory or other failure.
350 */
351dbus_bool_t
352_dbus_string_append_int (DBusString *str,
353                         long        value)
354{
355  /* this calculation is from comp.lang.c faq */
356#define MAX_LONG_LEN ((sizeof (long) * 8 + 2) / 3 + 1)  /* +1 for '-' */
357  int orig_len;
358  int i;
359  char *buf;
360
361  orig_len = _dbus_string_get_length (str);
362
363  if (!_dbus_string_lengthen (str, MAX_LONG_LEN))
364    return FALSE;
365
366  buf = _dbus_string_get_data_len (str, orig_len, MAX_LONG_LEN);
367
368  snprintf (buf, MAX_LONG_LEN, "%ld", value);
369
370  i = 0;
371  while (*buf)
372    {
373      ++buf;
374      ++i;
375    }
376
377  _dbus_string_shorten (str, MAX_LONG_LEN - i);
378
379  return TRUE;
380}
381
382/**
383 * Appends an unsigned integer to a DBusString.
384 *
385 * @param str the string
386 * @param value the integer value
387 * @returns #FALSE if not enough memory or other failure.
388 */
389dbus_bool_t
390_dbus_string_append_uint (DBusString    *str,
391                          unsigned long  value)
392{
393  /* this is wrong, but definitely on the high side. */
394#define MAX_ULONG_LEN (MAX_LONG_LEN * 2)
395  int orig_len;
396  int i;
397  char *buf;
398
399  orig_len = _dbus_string_get_length (str);
400
401  if (!_dbus_string_lengthen (str, MAX_ULONG_LEN))
402    return FALSE;
403
404  buf = _dbus_string_get_data_len (str, orig_len, MAX_ULONG_LEN);
405
406  snprintf (buf, MAX_ULONG_LEN, "%lu", value);
407
408  i = 0;
409  while (*buf)
410    {
411      ++buf;
412      ++i;
413    }
414
415  _dbus_string_shorten (str, MAX_ULONG_LEN - i);
416
417  return TRUE;
418}
419
420/**
421 * Parses an integer contained in a DBusString. Either return parameter
422 * may be #NULL if you aren't interested in it. The integer is parsed
423 * and stored in value_return. Return parameters are not initialized
424 * if the function returns #FALSE.
425 *
426 * @param str the string
427 * @param start the byte index of the start of the integer
428 * @param value_return return location of the integer value or #NULL
429 * @param end_return return location of the end of the integer, or #NULL
430 * @returns #TRUE on success
431 */
432dbus_bool_t
433_dbus_string_parse_int (const DBusString *str,
434                        int               start,
435                        long             *value_return,
436                        int              *end_return)
437{
438  long v;
439  const char *p;
440  char *end;
441
442  p = _dbus_string_get_const_data_len (str, start,
443                                       _dbus_string_get_length (str) - start);
444
445  end = NULL;
446  _dbus_set_errno_to_zero ();
447  v = strtol (p, &end, 0);
448  if (end == NULL || end == p || errno != 0)
449    return FALSE;
450
451  if (value_return)
452    *value_return = v;
453  if (end_return)
454    *end_return = start + (end - p);
455
456  return TRUE;
457}
458
459/**
460 * Parses an unsigned integer contained in a DBusString. Either return
461 * parameter may be #NULL if you aren't interested in it. The integer
462 * is parsed and stored in value_return. Return parameters are not
463 * initialized if the function returns #FALSE.
464 *
465 * @param str the string
466 * @param start the byte index of the start of the integer
467 * @param value_return return location of the integer value or #NULL
468 * @param end_return return location of the end of the integer, or #NULL
469 * @returns #TRUE on success
470 */
471dbus_bool_t
472_dbus_string_parse_uint (const DBusString *str,
473                         int               start,
474                         unsigned long    *value_return,
475                         int              *end_return)
476{
477  unsigned long v;
478  const char *p;
479  char *end;
480
481  p = _dbus_string_get_const_data_len (str, start,
482                                       _dbus_string_get_length (str) - start);
483
484  end = NULL;
485  _dbus_set_errno_to_zero ();
486  v = strtoul (p, &end, 0);
487  if (end == NULL || end == p || errno != 0)
488    return FALSE;
489
490  if (value_return)
491    *value_return = v;
492  if (end_return)
493    *end_return = start + (end - p);
494
495  return TRUE;
496}
497
498/** @} */ /* DBusString group */
499
500/**
501 * @addtogroup DBusInternalsUtils
502 * @{
503 */
504
505void
506_dbus_generate_pseudorandom_bytes_buffer (char *buffer,
507                                          int   n_bytes)
508{
509  long tv_usec;
510  int i;
511
512  /* fall back to pseudorandom */
513  _dbus_verbose ("Falling back to pseudorandom for %d bytes\n",
514                 n_bytes);
515
516  _dbus_get_real_time (NULL, &tv_usec);
517  srand (tv_usec);
518
519  i = 0;
520  while (i < n_bytes)
521    {
522      double r;
523      unsigned int b;
524
525      r = rand ();
526      b = (r / (double) RAND_MAX) * 255.0;
527
528      buffer[i] = b;
529
530      ++i;
531    }
532}
533
534/**
535 * Fills n_bytes of the given buffer with random bytes.
536 *
537 * @param buffer an allocated buffer
538 * @param n_bytes the number of bytes in buffer to write to
539 */
540void
541_dbus_generate_random_bytes_buffer (char *buffer,
542                                    int   n_bytes)
543{
544  DBusString str;
545
546  if (!_dbus_string_init (&str))
547    {
548      _dbus_generate_pseudorandom_bytes_buffer (buffer, n_bytes);
549      return;
550    }
551
552  if (!_dbus_generate_random_bytes (&str, n_bytes))
553    {
554      _dbus_string_free (&str);
555      _dbus_generate_pseudorandom_bytes_buffer (buffer, n_bytes);
556      return;
557    }
558
559  _dbus_string_copy_to_buffer (&str, buffer, n_bytes);
560
561  _dbus_string_free (&str);
562}
563
564/**
565 * Generates the given number of random bytes, where the bytes are
566 * chosen from the alphanumeric ASCII subset.
567 *
568 * @param str the string
569 * @param n_bytes the number of random ASCII bytes to append to string
570 * @returns #TRUE on success, #FALSE if no memory or other failure
571 */
572dbus_bool_t
573_dbus_generate_random_ascii (DBusString *str,
574                             int         n_bytes)
575{
576  static const char letters[] =
577    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
578  int i;
579  int len;
580
581  if (!_dbus_generate_random_bytes (str, n_bytes))
582    return FALSE;
583
584  len = _dbus_string_get_length (str);
585  i = len - n_bytes;
586  while (i < len)
587    {
588      _dbus_string_set_byte (str, i,
589                             letters[_dbus_string_get_byte (str, i) %
590                                     (sizeof (letters) - 1)]);
591
592      ++i;
593    }
594
595  _dbus_assert (_dbus_string_validate_ascii (str, len - n_bytes,
596                                             n_bytes));
597
598  return TRUE;
599}
600
601/**
602 * Converts a UNIX errno, or Windows errno or WinSock error value into
603 * a #DBusError name.
604 *
605 * @todo should cover more errnos, specifically those
606 * from open().
607 *
608 * @param error_number the errno.
609 * @returns an error name
610 */
611const char*
612_dbus_error_from_errno (int error_number)
613{
614  switch (error_number)
615    {
616    case 0:
617      return DBUS_ERROR_FAILED;
618
619#ifdef EPROTONOSUPPORT
620    case EPROTONOSUPPORT:
621      return DBUS_ERROR_NOT_SUPPORTED;
622#elif defined(WSAEPROTONOSUPPORT)
623    case WSAEPROTONOSUPPORT:
624      return DBUS_ERROR_NOT_SUPPORTED;
625#endif
626#ifdef EAFNOSUPPORT
627    case EAFNOSUPPORT:
628      return DBUS_ERROR_NOT_SUPPORTED;
629#elif defined(WSAEAFNOSUPPORT)
630    case WSAEAFNOSUPPORT:
631      return DBUS_ERROR_NOT_SUPPORTED;
632#endif
633#ifdef ENFILE
634    case ENFILE:
635      return DBUS_ERROR_LIMITS_EXCEEDED; /* kernel out of memory */
636#endif
637#ifdef EMFILE
638    case EMFILE:
639      return DBUS_ERROR_LIMITS_EXCEEDED;
640#endif
641#ifdef EACCES
642    case EACCES:
643      return DBUS_ERROR_ACCESS_DENIED;
644#endif
645#ifdef EPERM
646    case EPERM:
647      return DBUS_ERROR_ACCESS_DENIED;
648#endif
649#ifdef ENOBUFS
650    case ENOBUFS:
651      return DBUS_ERROR_NO_MEMORY;
652#endif
653#ifdef ENOMEM
654    case ENOMEM:
655      return DBUS_ERROR_NO_MEMORY;
656#endif
657#ifdef ECONNREFUSED
658    case ECONNREFUSED:
659      return DBUS_ERROR_NO_SERVER;
660#elif defined(WSAECONNREFUSED)
661    case WSAECONNREFUSED:
662      return DBUS_ERROR_NO_SERVER;
663#endif
664#ifdef ETIMEDOUT
665    case ETIMEDOUT:
666      return DBUS_ERROR_TIMEOUT;
667#elif defined(WSAETIMEDOUT)
668    case WSAETIMEDOUT:
669      return DBUS_ERROR_TIMEOUT;
670#endif
671#ifdef ENETUNREACH
672    case ENETUNREACH:
673      return DBUS_ERROR_NO_NETWORK;
674#elif defined(WSAENETUNREACH)
675    case WSAENETUNREACH:
676      return DBUS_ERROR_NO_NETWORK;
677#endif
678#ifdef EADDRINUSE
679    case EADDRINUSE:
680      return DBUS_ERROR_ADDRESS_IN_USE;
681#elif defined(WSAEADDRINUSE)
682    case WSAEADDRINUSE:
683      return DBUS_ERROR_ADDRESS_IN_USE;
684#endif
685#ifdef EEXIST
686    case EEXIST:
687      return DBUS_ERROR_FILE_EXISTS;
688#endif
689#ifdef ENOENT
690    case ENOENT:
691      return DBUS_ERROR_FILE_NOT_FOUND;
692#endif
693    }
694
695  return DBUS_ERROR_FAILED;
696}
697
698/**
699 * Converts the current system errno value into a #DBusError name.
700 *
701 * @returns an error name
702 */
703const char*
704_dbus_error_from_system_errno (void)
705{
706  return _dbus_error_from_errno (errno);
707}
708
709/**
710 * Assign 0 to the global errno variable
711 */
712void
713_dbus_set_errno_to_zero (void)
714{
715#ifdef DBUS_WINCE
716  SetLastError (0);
717#else
718  errno = 0;
719#endif
720}
721
722/**
723 * See if errno is set
724 * @returns #TRUE if errno is not 0
725 */
726dbus_bool_t
727_dbus_get_is_errno_nonzero (void)
728{
729  return errno != 0;
730}
731
732/**
733 * See if errno is ENOMEM
734 * @returns #TRUE if errno == ENOMEM
735 */
736dbus_bool_t
737_dbus_get_is_errno_enomem (void)
738{
739  return errno == ENOMEM;
740}
741
742/**
743 * See if errno is EINTR
744 * @returns #TRUE if errno == EINTR
745 */
746dbus_bool_t
747_dbus_get_is_errno_eintr (void)
748{
749  return errno == EINTR;
750}
751
752/**
753 * See if errno is EPIPE
754 * @returns #TRUE if errno == EPIPE
755 */
756dbus_bool_t
757_dbus_get_is_errno_epipe (void)
758{
759  return errno == EPIPE;
760}
761
762/**
763 * Get error message from errno
764 * @returns _dbus_strerror(errno)
765 */
766const char*
767_dbus_strerror_from_errno (void)
768{
769  return _dbus_strerror (errno);
770}
771
772/** @} end of sysdeps */
773
774/* tests in dbus-sysdeps-util.c */
775