dbus-sysdeps-util-unix.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* dbus-sysdeps-util-unix.c Would be in dbus-sysdeps-unix.c, but not used in libdbus
00003  * 
00004  * Copyright (C) 2002, 2003, 2004, 2005  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 #include "dbus-sysdeps.h"
00025 #include "dbus-sysdeps-unix.h"
00026 #include "dbus-internals.h"
00027 #include "dbus-protocol.h"
00028 #include "dbus-string.h"
00029 #define DBUS_USERDB_INCLUDES_PRIVATE 1
00030 #include "dbus-userdb.h"
00031 #include "dbus-test.h"
00032 
00033 #include <sys/types.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <signal.h>
00037 #include <unistd.h>
00038 #include <stdio.h>
00039 #include <errno.h>
00040 #include <fcntl.h>
00041 #include <sys/stat.h>
00042 #include <grp.h>
00043 #include <sys/socket.h>
00044 #include <dirent.h>
00045 #include <sys/un.h>
00046 #ifdef HAVE_LIBAUDIT
00047 #include <sys/prctl.h>
00048 #include <sys/capability.h>
00049 #include <libaudit.h>
00050 #endif /* HAVE_LIBAUDIT */
00051 
00052 #ifdef HAVE_SYS_SYSLIMITS_H
00053 #include <sys/syslimits.h>
00054 #endif
00055 
00056 #ifndef O_BINARY
00057 #define O_BINARY 0
00058 #endif
00059 
00073 dbus_bool_t
00074 _dbus_become_daemon (const DBusString *pidfile,
00075                      int               print_pid_fd,
00076                      DBusError        *error)
00077 {
00078   const char *s;
00079   pid_t child_pid;
00080   int dev_null_fd;
00081 
00082   _dbus_verbose ("Becoming a daemon...\n");
00083 
00084   _dbus_verbose ("chdir to /\n");
00085   if (chdir ("/") < 0)
00086     {
00087       dbus_set_error (error, DBUS_ERROR_FAILED,
00088                       "Could not chdir() to root directory");
00089       return FALSE;
00090     }
00091 
00092   _dbus_verbose ("forking...\n");
00093   switch ((child_pid = fork ()))
00094     {
00095     case -1:
00096       _dbus_verbose ("fork failed\n");
00097       dbus_set_error (error, _dbus_error_from_errno (errno),
00098                       "Failed to fork daemon: %s", _dbus_strerror (errno));
00099       return FALSE;
00100       break;
00101 
00102     case 0:
00103       _dbus_verbose ("in child, closing std file descriptors\n");
00104 
00105       /* silently ignore failures here, if someone
00106        * doesn't have /dev/null we may as well try
00107        * to continue anyhow
00108        */
00109       
00110       dev_null_fd = open ("/dev/null", O_RDWR);
00111       if (dev_null_fd >= 0)
00112         {
00113           dup2 (dev_null_fd, 0);
00114           dup2 (dev_null_fd, 1);
00115           
00116           s = _dbus_getenv ("DBUS_DEBUG_OUTPUT");
00117           if (s == NULL || *s == '\0')
00118             dup2 (dev_null_fd, 2);
00119           else
00120             _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n");
00121         }
00122 
00123       /* Get a predictable umask */
00124       _dbus_verbose ("setting umask\n");
00125       umask (022);
00126       break;
00127 
00128     default:
00129       if (pidfile)
00130         {
00131           _dbus_verbose ("parent writing pid file\n");
00132           if (!_dbus_write_pid_file (pidfile,
00133                                      child_pid,
00134                                      error))
00135             {
00136               _dbus_verbose ("pid file write failed, killing child\n");
00137               kill (child_pid, SIGTERM);
00138               return FALSE;
00139             }
00140         }
00141 
00142       /* Write PID if requested */
00143       if (print_pid_fd >= 0)
00144         {
00145           DBusString pid;
00146           int bytes;
00147           
00148           if (!_dbus_string_init (&pid))
00149             {
00150               _DBUS_SET_OOM (error);
00151               kill (child_pid, SIGTERM);
00152               return FALSE;
00153             }
00154           
00155           if (!_dbus_string_append_int (&pid, child_pid) ||
00156               !_dbus_string_append (&pid, "\n"))
00157             {
00158               _dbus_string_free (&pid);
00159               _DBUS_SET_OOM (error);
00160               kill (child_pid, SIGTERM);
00161               return FALSE;
00162             }
00163           
00164           bytes = _dbus_string_get_length (&pid);
00165           if (_dbus_write_socket (print_pid_fd, &pid, 0, bytes) != bytes)
00166             {
00167               dbus_set_error (error, DBUS_ERROR_FAILED,
00168                               "Printing message bus PID: %s\n",
00169                               _dbus_strerror (errno));
00170               _dbus_string_free (&pid);
00171               kill (child_pid, SIGTERM);
00172               return FALSE;
00173             }
00174           
00175           _dbus_string_free (&pid);
00176         }
00177       _dbus_verbose ("parent exiting\n");
00178       _exit (0);
00179       break;
00180     }
00181 
00182   _dbus_verbose ("calling setsid()\n");
00183   if (setsid () == -1)
00184     _dbus_assert_not_reached ("setsid() failed");
00185   
00186   return TRUE;
00187 }
00188 
00189 
00198 dbus_bool_t
00199 _dbus_write_pid_file (const DBusString *filename,
00200                       unsigned long     pid,
00201                       DBusError        *error)
00202 {
00203   const char *cfilename;
00204   int fd;
00205   FILE *f;
00206 
00207   cfilename = _dbus_string_get_const_data (filename);
00208   
00209   fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644);
00210   
00211   if (fd < 0)
00212     {
00213       dbus_set_error (error, _dbus_error_from_errno (errno),
00214                       "Failed to open \"%s\": %s", cfilename,
00215                       _dbus_strerror (errno));
00216       return FALSE;
00217     }
00218 
00219   if ((f = fdopen (fd, "w")) == NULL)
00220     {
00221       dbus_set_error (error, _dbus_error_from_errno (errno),
00222                       "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno));
00223       _dbus_close (fd, NULL);
00224       return FALSE;
00225     }
00226   
00227   if (fprintf (f, "%lu\n", pid) < 0)
00228     {
00229       dbus_set_error (error, _dbus_error_from_errno (errno),
00230                       "Failed to write to \"%s\": %s", cfilename,
00231                       _dbus_strerror (errno));
00232       
00233       fclose (f);
00234       return FALSE;
00235     }
00236 
00237   if (fclose (f) == EOF)
00238     {
00239       dbus_set_error (error, _dbus_error_from_errno (errno),
00240                       "Failed to close \"%s\": %s", cfilename,
00241                       _dbus_strerror (errno));
00242       return FALSE;
00243     }
00244   
00245   return TRUE;
00246 }
00247 
00248 
00257 dbus_bool_t
00258 _dbus_change_identity  (dbus_uid_t     uid,
00259                         dbus_gid_t     gid,
00260                         DBusError     *error)
00261 {
00262   int priv = FALSE;
00263   
00264 #ifdef HAVE_LIBAUDIT
00265   /* have a tmp set of caps that we use to transition to the usr/grp dbus should
00266    * run as ... doesn't really help. But keeps people happy.
00267    */
00268   cap_t new_caps = NULL;
00269 
00270   priv = !getuid();
00271   if (priv)
00272     {
00273       cap_value_t new_cap_list[] = { CAP_AUDIT_WRITE };
00274       cap_value_t tmp_cap_list[] = { CAP_AUDIT_WRITE, CAP_SETUID, CAP_SETGID };
00275       cap_t tmp_caps = cap_init();
00276       
00277       if (!tmp_caps || !(new_caps = cap_init()))
00278         {
00279           dbus_set_error (error, DBUS_ERROR_FAILED,
00280                           "Failed to initialize drop of capabilities\n");
00281           if (tmp_caps)
00282             cap_free(tmp_caps);
00283           return FALSE;
00284         }
00285 
00286       /* assume these work... */
00287       cap_set_flag(new_caps, CAP_PERMITTED, 1, new_cap_list, CAP_SET);
00288       cap_set_flag(new_caps, CAP_EFFECTIVE, 1, new_cap_list, CAP_SET);
00289       cap_set_flag(tmp_caps, CAP_PERMITTED, 3, tmp_cap_list, CAP_SET);
00290       cap_set_flag(tmp_caps, CAP_EFFECTIVE, 3, tmp_cap_list, CAP_SET);
00291       
00292       if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
00293         {
00294           dbus_set_error (error, _dbus_error_from_errno (errno),
00295                           "Failed to set keep-capabilities: %s\n",
00296                           _dbus_strerror (errno));
00297           cap_free(tmp_caps);
00298           goto fail;
00299         }
00300       if (cap_set_proc(tmp_caps))
00301         {
00302           dbus_set_error (error, DBUS_ERROR_FAILED,
00303                           "Failed to drop capabilities\n");
00304           cap_free(tmp_caps);
00305           goto fail;
00306         }
00307       cap_free(tmp_caps);
00308     }
00309 #endif /* HAVE_LIBAUDIT */
00310 
00311   /* setgroups() only works if we are a privileged process,
00312    * so we don't return error on failure; the only possible
00313    * failure is that we don't have perms to do it.
00314    *
00315    * not sure this is right, maybe if setuid()
00316    * is going to work then setgroups() should also work.
00317    */
00318   if (setgroups (0, NULL) < 0)
00319     _dbus_warn ("Failed to drop supplementary groups: %s\n",
00320                 _dbus_strerror (errno));
00321   
00322   /* Set GID first, or the setuid may remove our permission
00323    * to change the GID
00324    */
00325   if (setgid (gid) < 0)
00326     {
00327       dbus_set_error (error, _dbus_error_from_errno (errno),
00328                       "Failed to set GID to %lu: %s", gid,
00329                       _dbus_strerror (errno));
00330       goto fail;
00331     }
00332   
00333   if (setuid (uid) < 0)
00334     {
00335       dbus_set_error (error, _dbus_error_from_errno (errno),
00336                       "Failed to set UID to %lu: %s", uid,
00337                       _dbus_strerror (errno));
00338       goto fail;
00339     }
00340   
00341 #ifdef HAVE_LIBAUDIT
00342     if (priv)
00343       {
00344         if (cap_set_proc(new_caps))
00345           {
00346             dbus_set_error (error, DBUS_ERROR_FAILED,
00347                             "Failed to drop capabilities\n");
00348             goto fail;
00349           }
00350         cap_free(new_caps);
00351         
00352         if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == -1)
00353           { /* should always work, if it did above */
00354             dbus_set_error (error, _dbus_error_from_errno (errno),
00355                             "Failed to unset keep-capabilities: %s\n",
00356                             _dbus_strerror (errno));
00357             return FALSE;
00358           }
00359       }
00360 #endif
00361 
00362  return TRUE;
00363 
00364  fail:
00365 #ifdef HAVE_LIBAUDIT
00366   if (priv)
00367     {
00368       /* should always work, if it did above */
00369       prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0);
00370       cap_free(new_caps);
00371     }
00372 #endif
00373   return FALSE;
00374 }
00375 
00381 void
00382 _dbus_set_signal_handler (int               sig,
00383                           DBusSignalHandler handler)
00384 {
00385   struct sigaction act;
00386   sigset_t empty_mask;
00387   
00388   sigemptyset (&empty_mask);
00389   act.sa_handler = handler;
00390   act.sa_mask    = empty_mask;
00391   act.sa_flags   = 0;
00392   sigaction (sig,  &act, NULL);
00393 }
00394 
00395 
00403 dbus_bool_t
00404 _dbus_delete_directory (const DBusString *filename,
00405                         DBusError        *error)
00406 {
00407   const char *filename_c;
00408   
00409   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00410 
00411   filename_c = _dbus_string_get_const_data (filename);
00412 
00413   if (rmdir (filename_c) != 0)
00414     {
00415       dbus_set_error (error, DBUS_ERROR_FAILED,
00416                       "Failed to remove directory %s: %s\n",
00417                       filename_c, _dbus_strerror (errno));
00418       return FALSE;
00419     }
00420   
00421   return TRUE;
00422 }
00423 
00429 dbus_bool_t 
00430 _dbus_file_exists (const char *file)
00431 {
00432   return (access (file, F_OK) == 0);
00433 }
00434 
00441 dbus_bool_t 
00442 _dbus_user_at_console (const char *username,
00443                        DBusError  *error)
00444 {
00445 
00446   DBusString f;
00447   dbus_bool_t result;
00448 
00449   result = FALSE;
00450   if (!_dbus_string_init (&f))
00451     {
00452       _DBUS_SET_OOM (error);
00453       return FALSE;
00454     }
00455 
00456   if (!_dbus_string_append (&f, DBUS_CONSOLE_AUTH_DIR))
00457     {
00458       _DBUS_SET_OOM (error);
00459       goto out;
00460     }
00461 
00462 
00463   if (!_dbus_string_append (&f, username))
00464     {
00465       _DBUS_SET_OOM (error);
00466       goto out;
00467     }
00468 
00469   result = _dbus_file_exists (_dbus_string_get_const_data (&f));
00470 
00471  out:
00472   _dbus_string_free (&f);
00473 
00474   return result;
00475 }
00476 
00477 
00484 dbus_bool_t
00485 _dbus_path_is_absolute (const DBusString *filename)
00486 {
00487   if (_dbus_string_get_length (filename) > 0)
00488     return _dbus_string_get_byte (filename, 0) == '/';
00489   else
00490     return FALSE;
00491 }
00492 
00501 dbus_bool_t
00502 _dbus_stat (const DBusString *filename,
00503             DBusStat         *statbuf,
00504             DBusError        *error)
00505 {
00506   const char *filename_c;
00507   struct stat sb;
00508 
00509   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00510   
00511   filename_c = _dbus_string_get_const_data (filename);
00512 
00513   if (stat (filename_c, &sb) < 0)
00514     {
00515       dbus_set_error (error, _dbus_error_from_errno (errno),
00516                       "%s", _dbus_strerror (errno));
00517       return FALSE;
00518     }
00519 
00520   statbuf->mode = sb.st_mode;
00521   statbuf->nlink = sb.st_nlink;
00522   statbuf->uid = sb.st_uid;
00523   statbuf->gid = sb.st_gid;
00524   statbuf->size = sb.st_size;
00525   statbuf->atime = sb.st_atime;
00526   statbuf->mtime = sb.st_mtime;
00527   statbuf->ctime = sb.st_ctime;
00528 
00529   return TRUE;
00530 }
00531 
00532 
00536 struct DBusDirIter
00537 {
00538   DIR *d; 
00540 };
00541 
00549 DBusDirIter*
00550 _dbus_directory_open (const DBusString *filename,
00551                       DBusError        *error)
00552 {
00553   DIR *d;
00554   DBusDirIter *iter;
00555   const char *filename_c;
00556 
00557   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00558   
00559   filename_c = _dbus_string_get_const_data (filename);
00560 
00561   d = opendir (filename_c);
00562   if (d == NULL)
00563     {
00564       dbus_set_error (error, _dbus_error_from_errno (errno),
00565                       "Failed to read directory \"%s\": %s",
00566                       filename_c,
00567                       _dbus_strerror (errno));
00568       return NULL;
00569     }
00570   iter = dbus_new0 (DBusDirIter, 1);
00571   if (iter == NULL)
00572     {
00573       closedir (d);
00574       dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
00575                       "Could not allocate memory for directory iterator");
00576       return NULL;
00577     }
00578 
00579   iter->d = d;
00580 
00581   return iter;
00582 }
00583 
00584 /* Calculate the required buffer size (in bytes) for directory
00585  * entries read from the given directory handle.  Return -1 if this
00586  * this cannot be done. 
00587  *
00588  * If you use autoconf, include fpathconf and dirfd in your
00589  * AC_CHECK_FUNCS list.  Otherwise use some other method to detect
00590  * and use them where available.
00591  */
00592 static dbus_bool_t
00593 dirent_buf_size(DIR * dirp, size_t *size)
00594 {
00595  long name_max;
00596 #   if defined(HAVE_FPATHCONF) && defined(_PC_NAME_MAX)
00597 #      if defined(HAVE_DIRFD)
00598           name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
00599 #      elif defined(HAVE_DDFD)
00600           name_max = fpathconf(dirp->dd_fd, _PC_NAME_MAX);
00601 #      else
00602           name_max = fpathconf(dirp->__dd_fd, _PC_NAME_MAX);
00603 #      endif /* HAVE_DIRFD */
00604      if (name_max == -1)
00605 #           if defined(NAME_MAX)
00606              name_max = NAME_MAX;
00607 #           else
00608              return FALSE;
00609 #           endif
00610 #   elif defined(MAXNAMELEN)
00611      name_max = MAXNAMELEN;
00612 #   else
00613 #       if defined(NAME_MAX)
00614          name_max = NAME_MAX;
00615 #       else
00616 #           error "buffer size for readdir_r cannot be determined"
00617 #       endif
00618 #   endif
00619   if (size)
00620     *size = (size_t)offsetof(struct dirent, d_name) + name_max + 1;
00621   else
00622     return FALSE;
00623 
00624   return TRUE;
00625 }
00626 
00637 dbus_bool_t
00638 _dbus_directory_get_next_file (DBusDirIter      *iter,
00639                                DBusString       *filename,
00640                                DBusError        *error)
00641 {
00642   struct dirent *d, *ent;
00643   size_t buf_size;
00644   int err;
00645 
00646   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00647  
00648   if (!dirent_buf_size (iter->d, &buf_size))
00649     {
00650       dbus_set_error (error, DBUS_ERROR_FAILED,
00651                       "Can't calculate buffer size when reading directory");
00652       return FALSE;
00653     }
00654 
00655   d = (struct dirent *)dbus_malloc (buf_size);
00656   if (!d)
00657     {
00658       dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
00659                       "No memory to read directory entry");
00660       return FALSE;
00661     }
00662 
00663  again:
00664   err = readdir_r (iter->d, d, &ent);
00665   if (err || !ent)
00666     {
00667       if (err != 0)
00668         dbus_set_error (error,
00669                         _dbus_error_from_errno (err),
00670                         "%s", _dbus_strerror (err));
00671 
00672       dbus_free (d);
00673       return FALSE;
00674     }
00675   else if (ent->d_name[0] == '.' &&
00676            (ent->d_name[1] == '\0' ||
00677             (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
00678     goto again;
00679   else
00680     {
00681       _dbus_string_set_length (filename, 0);
00682       if (!_dbus_string_append (filename, ent->d_name))
00683         {
00684           dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
00685                           "No memory to read directory entry");
00686           dbus_free (d);
00687           return FALSE;
00688         }
00689       else
00690         {
00691           dbus_free (d);
00692           return TRUE;
00693         }
00694     }
00695 }
00696 
00700 void
00701 _dbus_directory_close (DBusDirIter *iter)
00702 {
00703   closedir (iter->d);
00704   dbus_free (iter);
00705 }
00706 
00707 static dbus_bool_t
00708 fill_user_info_from_group (struct group  *g,
00709                            DBusGroupInfo *info,
00710                            DBusError     *error)
00711 {
00712   _dbus_assert (g->gr_name != NULL);
00713   
00714   info->gid = g->gr_gid;
00715   info->groupname = _dbus_strdup (g->gr_name);
00716 
00717   /* info->members = dbus_strdupv (g->gr_mem) */
00718   
00719   if (info->groupname == NULL)
00720     {
00721       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00722       return FALSE;
00723     }
00724 
00725   return TRUE;
00726 }
00727 
00728 static dbus_bool_t
00729 fill_group_info (DBusGroupInfo    *info,
00730                  dbus_gid_t        gid,
00731                  const DBusString *groupname,
00732                  DBusError        *error)
00733 {
00734   const char *group_c_str;
00735 
00736   _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET);
00737   _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET);
00738 
00739   if (groupname)
00740     group_c_str = _dbus_string_get_const_data (groupname);
00741   else
00742     group_c_str = NULL;
00743   
00744   /* For now assuming that the getgrnam() and getgrgid() flavors
00745    * always correspond to the pwnam flavors, if not we have
00746    * to add more configure checks.
00747    */
00748   
00749 #if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R)
00750   {
00751     struct group *g;
00752     int result;
00753     char buf[1024];
00754     struct group g_str;
00755 
00756     g = NULL;
00757 #ifdef HAVE_POSIX_GETPWNAM_R
00758 
00759     if (group_c_str)
00760       result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf),
00761                            &g);
00762     else
00763       result = getgrgid_r (gid, &g_str, buf, sizeof (buf),
00764                            &g);
00765 #else
00766     g = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf));
00767     result = 0;
00768 #endif /* !HAVE_POSIX_GETPWNAM_R */
00769     if (result == 0 && g == &g_str)
00770       {
00771         return fill_user_info_from_group (g, info, error);
00772       }
00773     else
00774       {
00775         dbus_set_error (error, _dbus_error_from_errno (errno),
00776                         "Group %s unknown or failed to look it up\n",
00777                         group_c_str ? group_c_str : "???");
00778         return FALSE;
00779       }
00780   }
00781 #else /* ! HAVE_GETPWNAM_R */
00782   {
00783     /* I guess we're screwed on thread safety here */
00784     struct group *g;
00785 
00786     g = getgrnam (group_c_str);
00787 
00788     if (g != NULL)
00789       {
00790         return fill_user_info_from_group (g, info, error);
00791       }
00792     else
00793       {
00794         dbus_set_error (error, _dbus_error_from_errno (errno),
00795                         "Group %s unknown or failed to look it up\n",
00796                         group_c_str ? group_c_str : "???");
00797         return FALSE;
00798       }
00799   }
00800 #endif  /* ! HAVE_GETPWNAM_R */
00801 }
00802 
00812 dbus_bool_t
00813 _dbus_group_info_fill (DBusGroupInfo    *info,
00814                        const DBusString *groupname,
00815                        DBusError        *error)
00816 {
00817   return fill_group_info (info, DBUS_GID_UNSET,
00818                           groupname, error);
00819 
00820 }
00821 
00831 dbus_bool_t
00832 _dbus_group_info_fill_gid (DBusGroupInfo *info,
00833                            dbus_gid_t     gid,
00834                            DBusError     *error)
00835 {
00836   return fill_group_info (info, gid, NULL, error);
00837 }
00838  /* End of DBusInternalsUtils functions */
00840 
00852 dbus_bool_t
00853 _dbus_string_get_dirname  (const DBusString *filename,
00854                            DBusString       *dirname)
00855 {
00856   int sep;
00857   
00858   _dbus_assert (filename != dirname);
00859   _dbus_assert (filename != NULL);
00860   _dbus_assert (dirname != NULL);
00861 
00862   /* Ignore any separators on the end */
00863   sep = _dbus_string_get_length (filename);
00864   if (sep == 0)
00865     return _dbus_string_append (dirname, "."); /* empty string passed in */
00866     
00867   while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
00868     --sep;
00869 
00870   _dbus_assert (sep >= 0);
00871   
00872   if (sep == 0)
00873     return _dbus_string_append (dirname, "/");
00874   
00875   /* Now find the previous separator */
00876   _dbus_string_find_byte_backward (filename, sep, '/', &sep);
00877   if (sep < 0)
00878     return _dbus_string_append (dirname, ".");
00879   
00880   /* skip multiple separators */
00881   while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
00882     --sep;
00883 
00884   _dbus_assert (sep >= 0);
00885   
00886   if (sep == 0 &&
00887       _dbus_string_get_byte (filename, 0) == '/')
00888     return _dbus_string_append (dirname, "/");
00889   else
00890     return _dbus_string_copy_len (filename, 0, sep - 0,
00891                                   dirname, _dbus_string_get_length (dirname));
00892 } /* DBusString stuff */
00894 

Generated on Fri Apr 13 19:39:03 2007 for D-Bus by  doxygen 1.5.2