dbus-sysdeps.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* dbus-sysdeps.c Wrappers around system/libc features shared between UNIX and Windows (internal to D-Bus implementation)
00003  * 
00004  * Copyright (C) 2002, 2003, 2006  Red Hat, Inc.
00005  * Copyright (C) 2003 CodeFactory AB
00006  *
00007  * Licensed under the Academic Free License version 2.1
00008  * 
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  * 
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022  *
00023  */
00024 
00025 #include "dbus-internals.h"
00026 #include "dbus-sysdeps.h"
00027 #include "dbus-threads.h"
00028 #include "dbus-protocol.h"
00029 #include "dbus-string.h"
00030 
00031 /* NOTE: If you include any unix/windows-specific headers here, you are probably doing something
00032  * wrong and should be putting some code in dbus-sysdeps-unix.c or dbus-sysdeps-win.c.
00033  *
00034  * These are the standard ANSI C headers...
00035  */
00036 #include <locale.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 #include <stdio.h>
00040 
00041 /* This is UNIX-specific (on windows it's just in stdlib.h I believe)
00042  * but OK since the same stuff does exist on Windows in stdlib.h
00043  * and covered by a configure check.
00044  */
00045 #ifdef HAVE_ERRNO_H
00046 #include <errno.h>
00047 #endif
00048 
00049 _DBUS_DEFINE_GLOBAL_LOCK (win_fds);
00050 _DBUS_DEFINE_GLOBAL_LOCK (sid_atom_cache);
00051 
00056 #ifndef DBUS_DISABLE_ASSERT
00057 
00060 void
00061 _dbus_abort (void)
00062 {
00063 #ifdef DBUS_ENABLE_VERBOSE_MODE
00064   const char *s;
00065   s = _dbus_getenv ("DBUS_PRINT_BACKTRACE");
00066   if (s && *s)
00067     _dbus_print_backtrace ();
00068 #endif
00069   abort ();
00070   _dbus_exit (1); /* in case someone manages to ignore SIGABRT */
00071 }
00072 #endif
00073 
00087 dbus_bool_t
00088 _dbus_setenv (const char *varname,
00089               const char *value)
00090 {
00091   _dbus_assert (varname != NULL);
00092   
00093   if (value == NULL)
00094     {
00095 #ifdef HAVE_UNSETENV
00096       unsetenv (varname);
00097       return TRUE;
00098 #else
00099       char *putenv_value;
00100       size_t len;
00101 
00102       len = strlen (varname);
00103 
00104       /* Use system malloc to avoid memleaks that dbus_malloc
00105        * will get upset about.
00106        */
00107       
00108       putenv_value = malloc (len + 1);
00109       if (putenv_value == NULL)
00110         return FALSE;
00111 
00112       strcpy (putenv_value, varname);
00113       
00114       return (putenv (putenv_value) == 0);
00115 #endif
00116     }
00117   else
00118     {
00119 #ifdef HAVE_SETENV
00120       return (setenv (varname, value, TRUE) == 0);
00121 #else
00122       char *putenv_value;
00123       size_t len;
00124       size_t varname_len;
00125       size_t value_len;
00126 
00127       varname_len = strlen (varname);
00128       value_len = strlen (value);
00129       
00130       len = varname_len + value_len + 1 /* '=' */ ;
00131 
00132       /* Use system malloc to avoid memleaks that dbus_malloc
00133        * will get upset about.
00134        */
00135       
00136       putenv_value = malloc (len + 1);
00137       if (putenv_value == NULL)
00138         return FALSE;
00139 
00140       strcpy (putenv_value, varname);
00141       strcpy (putenv_value + varname_len, "=");
00142       strcpy (putenv_value + varname_len + 1, value);
00143       
00144       return (putenv (putenv_value) == 0);
00145 #endif
00146     }
00147 }
00148 
00155 const char*
00156 _dbus_getenv (const char *varname)
00157 {  
00158   return getenv (varname);
00159 }
00160 
00175 dbus_bool_t
00176 _dbus_string_append_int (DBusString *str,
00177                          long        value)
00178 {
00179   /* this calculation is from comp.lang.c faq */
00180 #define MAX_LONG_LEN ((sizeof (long) * 8 + 2) / 3 + 1)  /* +1 for '-' */
00181   int orig_len;
00182   int i;
00183   char *buf;
00184   
00185   orig_len = _dbus_string_get_length (str);
00186 
00187   if (!_dbus_string_lengthen (str, MAX_LONG_LEN))
00188     return FALSE;
00189 
00190   buf = _dbus_string_get_data_len (str, orig_len, MAX_LONG_LEN);
00191 
00192   snprintf (buf, MAX_LONG_LEN, "%ld", value);
00193 
00194   i = 0;
00195   while (*buf)
00196     {
00197       ++buf;
00198       ++i;
00199     }
00200   
00201   _dbus_string_shorten (str, MAX_LONG_LEN - i);
00202   
00203   return TRUE;
00204 }
00205 
00213 dbus_bool_t
00214 _dbus_string_append_uint (DBusString    *str,
00215                           unsigned long  value)
00216 {
00217   /* this is wrong, but definitely on the high side. */
00218 #define MAX_ULONG_LEN (MAX_LONG_LEN * 2)
00219   int orig_len;
00220   int i;
00221   char *buf;
00222   
00223   orig_len = _dbus_string_get_length (str);
00224 
00225   if (!_dbus_string_lengthen (str, MAX_ULONG_LEN))
00226     return FALSE;
00227 
00228   buf = _dbus_string_get_data_len (str, orig_len, MAX_ULONG_LEN);
00229 
00230   snprintf (buf, MAX_ULONG_LEN, "%lu", value);
00231 
00232   i = 0;
00233   while (*buf)
00234     {
00235       ++buf;
00236       ++i;
00237     }
00238   
00239   _dbus_string_shorten (str, MAX_ULONG_LEN - i);
00240   
00241   return TRUE;
00242 }
00243 
00244 #ifdef DBUS_BUILD_TESTS
00245 
00252 dbus_bool_t
00253 _dbus_string_append_double (DBusString *str,
00254                             double      value)
00255 {
00256 #define MAX_DOUBLE_LEN 64 /* this is completely made up :-/ */
00257   int orig_len;
00258   char *buf;
00259   int i;
00260   
00261   orig_len = _dbus_string_get_length (str);
00262 
00263   if (!_dbus_string_lengthen (str, MAX_DOUBLE_LEN))
00264     return FALSE;
00265 
00266   buf = _dbus_string_get_data_len (str, orig_len, MAX_DOUBLE_LEN);
00267 
00268   snprintf (buf, MAX_LONG_LEN, "%g", value);
00269 
00270   i = 0;
00271   while (*buf)
00272     {
00273       ++buf;
00274       ++i;
00275     }
00276   
00277   _dbus_string_shorten (str, MAX_DOUBLE_LEN - i);
00278   
00279   return TRUE;
00280 }
00281 #endif /* DBUS_BUILD_TESTS */
00282 
00295 dbus_bool_t
00296 _dbus_string_parse_int (const DBusString *str,
00297                         int               start,
00298                         long             *value_return,
00299                         int              *end_return)
00300 {
00301   long v;
00302   const char *p;
00303   char *end;
00304 
00305   p = _dbus_string_get_const_data_len (str, start,
00306                                        _dbus_string_get_length (str) - start);
00307 
00308   end = NULL;
00309   errno = 0;
00310   v = strtol (p, &end, 0);
00311   if (end == NULL || end == p || errno != 0)
00312     return FALSE;
00313 
00314   if (value_return)
00315     *value_return = v;
00316   if (end_return)
00317     *end_return = start + (end - p);
00318 
00319   return TRUE;
00320 }
00321 
00334 dbus_bool_t
00335 _dbus_string_parse_uint (const DBusString *str,
00336                          int               start,
00337                          unsigned long    *value_return,
00338                          int              *end_return)
00339 {
00340   unsigned long v;
00341   const char *p;
00342   char *end;
00343 
00344   p = _dbus_string_get_const_data_len (str, start,
00345                                        _dbus_string_get_length (str) - start);
00346 
00347   end = NULL;
00348   errno = 0;
00349   v = strtoul (p, &end, 0);
00350   if (end == NULL || end == p || errno != 0)
00351     return FALSE;
00352 
00353   if (value_return)
00354     *value_return = v;
00355   if (end_return)
00356     *end_return = start + (end - p);
00357 
00358   return TRUE;
00359 }
00360 
00361 #ifdef DBUS_BUILD_TESTS
00362 static dbus_bool_t
00363 ascii_isspace (char c)
00364 {
00365   return (c == ' ' ||
00366           c == '\f' ||
00367           c == '\n' ||
00368           c == '\r' ||
00369           c == '\t' ||
00370           c == '\v');
00371 }
00372 #endif /* DBUS_BUILD_TESTS */
00373 
00374 #ifdef DBUS_BUILD_TESTS
00375 static dbus_bool_t
00376 ascii_isdigit (char c)
00377 {
00378   return c >= '0' && c <= '9';
00379 }
00380 #endif /* DBUS_BUILD_TESTS */
00381 
00382 #ifdef DBUS_BUILD_TESTS
00383 static dbus_bool_t
00384 ascii_isxdigit (char c)
00385 {
00386   return (ascii_isdigit (c) ||
00387           (c >= 'a' && c <= 'f') ||
00388           (c >= 'A' && c <= 'F'));
00389 }
00390 #endif /* DBUS_BUILD_TESTS */
00391 
00392 #ifdef DBUS_BUILD_TESTS
00393 /* Calls strtod in a locale-independent fashion, by looking at
00394  * the locale data and patching the decimal comma to a point.
00395  *
00396  * Relicensed from glib.
00397  */
00398 static double
00399 ascii_strtod (const char *nptr,
00400               char      **endptr)
00401 {
00402   char *fail_pos;
00403   double val;
00404   struct lconv *locale_data;
00405   const char *decimal_point;
00406   int decimal_point_len;
00407   const char *p, *decimal_point_pos;
00408   const char *end = NULL; /* Silence gcc */
00409 
00410   fail_pos = NULL;
00411 
00412   locale_data = localeconv ();
00413   decimal_point = locale_data->decimal_point;
00414   decimal_point_len = strlen (decimal_point);
00415 
00416   _dbus_assert (decimal_point_len != 0);
00417   
00418   decimal_point_pos = NULL;
00419   if (decimal_point[0] != '.' ||
00420       decimal_point[1] != 0)
00421     {
00422       p = nptr;
00423       /* Skip leading space */
00424       while (ascii_isspace (*p))
00425         p++;
00426       
00427       /* Skip leading optional sign */
00428       if (*p == '+' || *p == '-')
00429         p++;
00430       
00431       if (p[0] == '0' &&
00432           (p[1] == 'x' || p[1] == 'X'))
00433         {
00434           p += 2;
00435           /* HEX - find the (optional) decimal point */
00436           
00437           while (ascii_isxdigit (*p))
00438             p++;
00439           
00440           if (*p == '.')
00441             {
00442               decimal_point_pos = p++;
00443               
00444               while (ascii_isxdigit (*p))
00445                 p++;
00446               
00447               if (*p == 'p' || *p == 'P')
00448                 p++;
00449               if (*p == '+' || *p == '-')
00450                 p++;
00451               while (ascii_isdigit (*p))
00452                 p++;
00453               end = p;
00454             }
00455         }
00456       else
00457         {
00458           while (ascii_isdigit (*p))
00459             p++;
00460           
00461           if (*p == '.')
00462             {
00463               decimal_point_pos = p++;
00464               
00465               while (ascii_isdigit (*p))
00466                 p++;
00467               
00468               if (*p == 'e' || *p == 'E')
00469                 p++;
00470               if (*p == '+' || *p == '-')
00471                 p++;
00472               while (ascii_isdigit (*p))
00473                 p++;
00474               end = p;
00475             }
00476         }
00477       /* For the other cases, we need not convert the decimal point */
00478     }
00479 
00480   /* Set errno to zero, so that we can distinguish zero results
00481      and underflows */
00482   errno = 0;
00483   
00484   if (decimal_point_pos)
00485     {
00486       char *copy, *c;
00487 
00488       /* We need to convert the '.' to the locale specific decimal point */
00489       copy = dbus_malloc (end - nptr + 1 + decimal_point_len);
00490       
00491       c = copy;
00492       memcpy (c, nptr, decimal_point_pos - nptr);
00493       c += decimal_point_pos - nptr;
00494       memcpy (c, decimal_point, decimal_point_len);
00495       c += decimal_point_len;
00496       memcpy (c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
00497       c += end - (decimal_point_pos + 1);
00498       *c = 0;
00499 
00500       val = strtod (copy, &fail_pos);
00501 
00502       if (fail_pos)
00503         {
00504           if (fail_pos > decimal_point_pos)
00505             fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1);
00506           else
00507             fail_pos = (char *)nptr + (fail_pos - copy);
00508         }
00509       
00510       dbus_free (copy);
00511           
00512     }
00513   else
00514     val = strtod (nptr, &fail_pos);
00515 
00516   if (endptr)
00517     *endptr = fail_pos;
00518   
00519   return val;
00520 }
00521 #endif /* DBUS_BUILD_TESTS */
00522 
00523 #ifdef DBUS_BUILD_TESTS
00524 
00536 dbus_bool_t
00537 _dbus_string_parse_double (const DBusString *str,
00538                            int               start,
00539                            double           *value_return,
00540                            int              *end_return)
00541 {
00542   double v;
00543   const char *p;
00544   char *end;
00545 
00546   p = _dbus_string_get_const_data_len (str, start,
00547                                        _dbus_string_get_length (str) - start);
00548 
00549   end = NULL;
00550   errno = 0;
00551   v = ascii_strtod (p, &end);
00552   if (end == NULL || end == p || errno != 0)
00553     return FALSE;
00554 
00555   if (value_return)
00556     *value_return = v;
00557   if (end_return)
00558     *end_return = start + (end - p);
00559 
00560   return TRUE;
00561 }
00562 #endif /* DBUS_BUILD_TESTS */
00563  /* DBusString group */
00565 
00576 void
00577 _dbus_user_info_free (DBusUserInfo *info)
00578 {
00579   dbus_free (info->group_ids);
00580   dbus_free (info->username);
00581   dbus_free (info->homedir);
00582 }
00583 
00589 void
00590 _dbus_group_info_free (DBusGroupInfo    *info)
00591 {
00592   dbus_free (info->groupname);
00593 }
00594 
00601 void
00602 _dbus_credentials_clear (DBusCredentials *credentials)
00603 {
00604   credentials->pid = DBUS_PID_UNSET;
00605   credentials->uid = DBUS_UID_UNSET;
00606   credentials->gid = DBUS_GID_UNSET;
00607 }
00608 
00617 dbus_bool_t
00618 _dbus_credentials_match (const DBusCredentials *expected_credentials,
00619                          const DBusCredentials *provided_credentials)
00620 {
00621   if (provided_credentials->uid == DBUS_UID_UNSET)
00622     return FALSE;
00623   else if (expected_credentials->uid == DBUS_UID_UNSET)
00624     return FALSE;
00625   else if (provided_credentials->uid == 0)
00626     return TRUE;
00627   else if (provided_credentials->uid == expected_credentials->uid)
00628     return TRUE;
00629   else
00630     return FALSE;
00631 }
00632 
00633 _DBUS_DEFINE_GLOBAL_LOCK (atomic);
00634 
00635 #ifdef DBUS_USE_ATOMIC_INT_486
00636 /* Taken from CVS version 1.7 of glibc's sysdeps/i386/i486/atomicity.h */
00637 /* Since the asm stuff here is gcc-specific we go ahead and use "inline" also */
00638 static inline dbus_int32_t
00639 atomic_exchange_and_add (DBusAtomic            *atomic,
00640                          volatile dbus_int32_t  val)
00641 {
00642   register dbus_int32_t result;
00643 
00644   __asm__ __volatile__ ("lock; xaddl %0,%1"
00645                         : "=r" (result), "=m" (atomic->value)
00646                         : "0" (val), "m" (atomic->value));
00647   return result;
00648 }
00649 #endif
00650 
00659 dbus_int32_t
00660 _dbus_atomic_inc (DBusAtomic *atomic)
00661 {
00662 #ifdef DBUS_USE_ATOMIC_INT_486
00663   return atomic_exchange_and_add (atomic, 1);
00664 #else
00665   dbus_int32_t res;
00666   _DBUS_LOCK (atomic);
00667   res = atomic->value;
00668   atomic->value += 1;
00669   _DBUS_UNLOCK (atomic);
00670   return res;
00671 #endif
00672 }
00673 
00682 dbus_int32_t
00683 _dbus_atomic_dec (DBusAtomic *atomic)
00684 {
00685 #ifdef DBUS_USE_ATOMIC_INT_486
00686   return atomic_exchange_and_add (atomic, -1);
00687 #else
00688   dbus_int32_t res;
00689   
00690   _DBUS_LOCK (atomic);
00691   res = atomic->value;
00692   atomic->value -= 1;
00693   _DBUS_UNLOCK (atomic);
00694   return res;
00695 #endif
00696 }
00697 
00698 void
00699 _dbus_generate_pseudorandom_bytes_buffer (char *buffer,
00700                                           int   n_bytes)
00701 {
00702   long tv_usec;
00703   int i;
00704   
00705   /* fall back to pseudorandom */
00706   _dbus_verbose ("Falling back to pseudorandom for %d bytes\n",
00707                  n_bytes);
00708   
00709   _dbus_get_current_time (NULL, &tv_usec);
00710   srand (tv_usec);
00711   
00712   i = 0;
00713   while (i < n_bytes)
00714     {
00715       double r;
00716       unsigned int b;
00717           
00718       r = rand ();
00719       b = (r / (double) RAND_MAX) * 255.0;
00720 
00721       buffer[i] = b;
00722 
00723       ++i;
00724     }
00725 }
00726 
00733 void
00734 _dbus_generate_random_bytes_buffer (char *buffer,
00735                                     int   n_bytes)
00736 {
00737   DBusString str;
00738 
00739   if (!_dbus_string_init (&str))
00740     {
00741       _dbus_generate_pseudorandom_bytes_buffer (buffer, n_bytes);
00742       return;
00743     }
00744 
00745   if (!_dbus_generate_random_bytes (&str, n_bytes))
00746     {
00747       _dbus_string_free (&str);
00748       _dbus_generate_pseudorandom_bytes_buffer (buffer, n_bytes);
00749       return;
00750     }
00751 
00752   _dbus_string_copy_to_buffer (&str, buffer, n_bytes);
00753 
00754   _dbus_string_free (&str);
00755 }
00756 
00765 dbus_bool_t
00766 _dbus_generate_random_ascii (DBusString *str,
00767                              int         n_bytes)
00768 {
00769   static const char letters[] =
00770     "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
00771   int i;
00772   int len;
00773   
00774   if (!_dbus_generate_random_bytes (str, n_bytes))
00775     return FALSE;
00776   
00777   len = _dbus_string_get_length (str);
00778   i = len - n_bytes;
00779   while (i < len)
00780     {
00781       _dbus_string_set_byte (str, i,
00782                              letters[_dbus_string_get_byte (str, i) %
00783                                      (sizeof (letters) - 1)]);
00784 
00785       ++i;
00786     }
00787 
00788   _dbus_assert (_dbus_string_validate_ascii (str, len - n_bytes,
00789                                              n_bytes));
00790 
00791   return TRUE;
00792 }
00793 
00801 dbus_bool_t
00802 _dbus_parse_uid (const DBusString      *uid_str,
00803                  dbus_uid_t            *uid)
00804 {
00805   int end;
00806   long val;
00807   
00808   if (_dbus_string_get_length (uid_str) == 0)
00809     {
00810       _dbus_verbose ("UID string was zero length\n");
00811       return FALSE;
00812     }
00813 
00814   val = -1;
00815   end = 0;
00816   if (!_dbus_string_parse_int (uid_str, 0, &val,
00817                                &end))
00818     {
00819       _dbus_verbose ("could not parse string as a UID\n");
00820       return FALSE;
00821     }
00822   
00823   if (end != _dbus_string_get_length (uid_str))
00824     {
00825       _dbus_verbose ("string contained trailing stuff after UID\n");
00826       return FALSE;
00827     }
00828 
00829   *uid = val;
00830 
00831   return TRUE;
00832 }
00833 
00844 const char*
00845 _dbus_error_from_errno (int error_number)
00846 {
00847   switch (error_number)
00848     {
00849     case 0:
00850       return DBUS_ERROR_FAILED;
00851       
00852 #ifdef EPROTONOSUPPORT
00853     case EPROTONOSUPPORT:
00854       return DBUS_ERROR_NOT_SUPPORTED;
00855 #endif
00856 #ifdef EAFNOSUPPORT
00857     case EAFNOSUPPORT:
00858       return DBUS_ERROR_NOT_SUPPORTED;
00859 #endif
00860 #ifdef ENFILE
00861     case ENFILE:
00862       return DBUS_ERROR_LIMITS_EXCEEDED; /* kernel out of memory */
00863 #endif
00864 #ifdef EMFILE
00865     case EMFILE:
00866       return DBUS_ERROR_LIMITS_EXCEEDED;
00867 #endif
00868 #ifdef EACCES
00869     case EACCES:
00870       return DBUS_ERROR_ACCESS_DENIED;
00871 #endif
00872 #ifdef EPERM
00873     case EPERM:
00874       return DBUS_ERROR_ACCESS_DENIED;
00875 #endif
00876 #ifdef ENOBUFS
00877     case ENOBUFS:
00878       return DBUS_ERROR_NO_MEMORY;
00879 #endif
00880 #ifdef ENOMEM
00881     case ENOMEM:
00882       return DBUS_ERROR_NO_MEMORY;
00883 #endif
00884 #ifdef EINVAL
00885     case EINVAL:
00886       return DBUS_ERROR_FAILED;
00887 #endif
00888 #ifdef EBADF
00889     case EBADF:
00890       return DBUS_ERROR_FAILED;
00891 #endif
00892 #ifdef EFAULT
00893     case EFAULT:
00894       return DBUS_ERROR_FAILED;
00895 #endif
00896 #ifdef ENOTSOCK
00897     case ENOTSOCK:
00898       return DBUS_ERROR_FAILED;
00899 #endif
00900 #ifdef EISCONN
00901     case EISCONN:
00902       return DBUS_ERROR_FAILED;
00903 #endif
00904 #ifdef ECONNREFUSED
00905     case ECONNREFUSED:
00906       return DBUS_ERROR_NO_SERVER;
00907 #endif
00908 #ifdef ETIMEDOUT
00909     case ETIMEDOUT:
00910       return DBUS_ERROR_TIMEOUT;
00911 #endif
00912 #ifdef ENETUNREACH
00913     case ENETUNREACH:
00914       return DBUS_ERROR_NO_NETWORK;
00915 #endif
00916 #ifdef EADDRINUSE
00917     case EADDRINUSE:
00918       return DBUS_ERROR_ADDRESS_IN_USE;
00919 #endif
00920 #ifdef EEXIST
00921     case EEXIST:
00922       return DBUS_ERROR_FILE_NOT_FOUND;
00923 #endif
00924 #ifdef ENOENT
00925     case ENOENT:
00926       return DBUS_ERROR_FILE_NOT_FOUND;
00927 #endif
00928     }
00929 
00930   return DBUS_ERROR_FAILED;
00931 }
00932 
00935 /* tests in dbus-sysdeps-util.c */

Generated on Fri Oct 20 11:46:24 2006 for D-Bus by  doxygen 1.4.7