* remove a bunch of todo items from the 1.0 list
[platform/upstream/dbus.git] / dbus / dbus-sysdeps-util-unix.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-sysdeps-util-unix.c Would be in dbus-sysdeps-unix.c, but not used in libdbus
3  * 
4  * Copyright (C) 2002, 2003, 2004, 2005  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24 #include "dbus-sysdeps.h"
25 #include "dbus-internals.h"
26 #include "dbus-protocol.h"
27 #include "dbus-string.h"
28 #define DBUS_USERDB_INCLUDES_PRIVATE 1
29 #include "dbus-userdb.h"
30 #include "dbus-test.h"
31
32 #include <sys/types.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <signal.h>
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <sys/stat.h>
41 #include <grp.h>
42 #include <sys/socket.h>
43 #include <dirent.h>
44 #include <sys/un.h>
45
46 #ifndef O_BINARY
47 #define O_BINARY 0
48 #endif
49
50 /**
51  * @addtogroup DBusInternalsUtils
52  * @{
53  */
54
55 /**
56  * Does the chdir, fork, setsid, etc. to become a daemon process.
57  *
58  * @param pidfile #NULL, or pidfile to create
59  * @param print_pid_fd file descriptor to print daemon's pid to, or -1 for none
60  * @param error return location for errors
61  * @returns #FALSE on failure
62  */
63 dbus_bool_t
64 _dbus_become_daemon (const DBusString *pidfile,
65                      int               print_pid_fd,
66                      DBusError        *error)
67 {
68   const char *s;
69   pid_t child_pid;
70   int dev_null_fd;
71
72   _dbus_verbose ("Becoming a daemon...\n");
73
74   _dbus_verbose ("chdir to /\n");
75   if (chdir ("/") < 0)
76     {
77       dbus_set_error (error, DBUS_ERROR_FAILED,
78                       "Could not chdir() to root directory");
79       return FALSE;
80     }
81
82   _dbus_verbose ("forking...\n");
83   switch ((child_pid = fork ()))
84     {
85     case -1:
86       _dbus_verbose ("fork failed\n");
87       dbus_set_error (error, _dbus_error_from_errno (errno),
88                       "Failed to fork daemon: %s", _dbus_strerror (errno));
89       return FALSE;
90       break;
91
92     case 0:
93       _dbus_verbose ("in child, closing std file descriptors\n");
94
95       /* silently ignore failures here, if someone
96        * doesn't have /dev/null we may as well try
97        * to continue anyhow
98        */
99       
100       dev_null_fd = open ("/dev/null", O_RDWR);
101       if (dev_null_fd >= 0)
102         {
103           dup2 (dev_null_fd, 0);
104           dup2 (dev_null_fd, 1);
105           
106           s = _dbus_getenv ("DBUS_DEBUG_OUTPUT");
107           if (s == NULL || *s == '\0')
108             dup2 (dev_null_fd, 2);
109           else
110             _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n");
111         }
112
113       /* Get a predictable umask */
114       _dbus_verbose ("setting umask\n");
115       umask (022);
116       break;
117
118     default:
119       if (pidfile)
120         {
121           _dbus_verbose ("parent writing pid file\n");
122           if (!_dbus_write_pid_file (pidfile,
123                                      child_pid,
124                                      error))
125             {
126               _dbus_verbose ("pid file write failed, killing child\n");
127               kill (child_pid, SIGTERM);
128               return FALSE;
129             }
130         }
131
132       /* Write PID if requested */
133       if (print_pid_fd >= 0)
134         {
135           DBusString pid;
136           int bytes;
137           
138           if (!_dbus_string_init (&pid))
139             {
140               _DBUS_SET_OOM (error);
141               kill (child_pid, SIGTERM);
142               return FALSE;
143             }
144           
145           if (!_dbus_string_append_int (&pid, child_pid) ||
146               !_dbus_string_append (&pid, "\n"))
147             {
148               _dbus_string_free (&pid);
149               _DBUS_SET_OOM (error);
150               kill (child_pid, SIGTERM);
151               return FALSE;
152             }
153           
154           bytes = _dbus_string_get_length (&pid);
155           if (_dbus_write (print_pid_fd, &pid, 0, bytes) != bytes)
156             {
157               dbus_set_error (error, DBUS_ERROR_FAILED,
158                               "Printing message bus PID: %s\n",
159                               _dbus_strerror (errno));
160               _dbus_string_free (&pid);
161               kill (child_pid, SIGTERM);
162               return FALSE;
163             }
164           
165           _dbus_string_free (&pid);
166         }
167       _dbus_verbose ("parent exiting\n");
168       _exit (0);
169       break;
170     }
171
172   _dbus_verbose ("calling setsid()\n");
173   if (setsid () == -1)
174     _dbus_assert_not_reached ("setsid() failed");
175   
176   return TRUE;
177 }
178
179
180 /**
181  * Creates a file containing the process ID.
182  *
183  * @param filename the filename to write to
184  * @param pid our process ID
185  * @param error return location for errors
186  * @returns #FALSE on failure
187  */
188 dbus_bool_t
189 _dbus_write_pid_file (const DBusString *filename,
190                       unsigned long     pid,
191                       DBusError        *error)
192 {
193   const char *cfilename;
194   int fd;
195   FILE *f;
196
197   cfilename = _dbus_string_get_const_data (filename);
198   
199   fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644);
200   
201   if (fd < 0)
202     {
203       dbus_set_error (error, _dbus_error_from_errno (errno),
204                       "Failed to open \"%s\": %s", cfilename,
205                       _dbus_strerror (errno));
206       return FALSE;
207     }
208
209   if ((f = fdopen (fd, "w")) == NULL)
210     {
211       dbus_set_error (error, _dbus_error_from_errno (errno),
212                       "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno));
213       close (fd);
214       return FALSE;
215     }
216   
217   if (fprintf (f, "%lu\n", pid) < 0)
218     {
219       dbus_set_error (error, _dbus_error_from_errno (errno),
220                       "Failed to write to \"%s\": %s", cfilename,
221                       _dbus_strerror (errno));
222       
223       fclose (f);
224       return FALSE;
225     }
226
227   if (fclose (f) == EOF)
228     {
229       dbus_set_error (error, _dbus_error_from_errno (errno),
230                       "Failed to close \"%s\": %s", cfilename,
231                       _dbus_strerror (errno));
232       return FALSE;
233     }
234   
235   return TRUE;
236 }
237
238
239 /**
240  * Changes the user and group the bus is running as.
241  *
242  * @param uid the new user ID
243  * @param gid the new group ID
244  * @param error return location for errors
245  * @returns #FALSE on failure
246  */
247 dbus_bool_t
248 _dbus_change_identity  (dbus_uid_t     uid,
249                         dbus_gid_t     gid,
250                         DBusError     *error)
251 {
252   /* setgroups() only works if we are a privileged process,
253    * so we don't return error on failure; the only possible
254    * failure is that we don't have perms to do it.
255    *
256    * not sure this is right, maybe if setuid()
257    * is going to work then setgroups() should also work.
258    */
259   if (setgroups (0, NULL) < 0)
260     _dbus_warn ("Failed to drop supplementary groups: %s\n",
261                 _dbus_strerror (errno));
262   
263   /* Set GID first, or the setuid may remove our permission
264    * to change the GID
265    */
266   if (setgid (gid) < 0)
267     {
268       dbus_set_error (error, _dbus_error_from_errno (errno),
269                       "Failed to set GID to %lu: %s", gid,
270                       _dbus_strerror (errno));
271       return FALSE;
272     }
273   
274   if (setuid (uid) < 0)
275     {
276       dbus_set_error (error, _dbus_error_from_errno (errno),
277                       "Failed to set UID to %lu: %s", uid,
278                       _dbus_strerror (errno));
279       return FALSE;
280     }
281   
282   return TRUE;
283 }
284
285 /** Installs a UNIX signal handler
286  *
287  * @param sig the signal to handle
288  * @param handler the handler
289  */
290 void
291 _dbus_set_signal_handler (int               sig,
292                           DBusSignalHandler handler)
293 {
294   struct sigaction act;
295   sigset_t empty_mask;
296   
297   sigemptyset (&empty_mask);
298   act.sa_handler = handler;
299   act.sa_mask    = empty_mask;
300   act.sa_flags   = 0;
301   sigaction (sig,  &act, NULL);
302 }
303
304
305 /**
306  * Removes a directory; Directory must be empty
307  * 
308  * @param filename directory filename
309  * @param error initialized error object
310  * @returns #TRUE on success
311  */
312 dbus_bool_t
313 _dbus_delete_directory (const DBusString *filename,
314                         DBusError        *error)
315 {
316   const char *filename_c;
317   
318   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
319
320   filename_c = _dbus_string_get_const_data (filename);
321
322   if (rmdir (filename_c) != 0)
323     {
324       dbus_set_error (error, DBUS_ERROR_FAILED,
325                       "Failed to remove directory %s: %s\n",
326                       filename_c, _dbus_strerror (errno));
327       return FALSE;
328     }
329   
330   return TRUE;
331 }
332
333 /** Checks if a file exists
334 *
335 * @param file full path to the file
336 * @returns #TRUE if file exists
337 */
338 dbus_bool_t 
339 _dbus_file_exists (const char *file)
340 {
341   return (access (file, F_OK) == 0);
342 }
343
344 /** Checks if user is at the console
345 *
346 * @param username user to check
347 * @param error return location for errors
348 * @returns #TRUE is the user is at the consolei and there are no errors
349 */
350 dbus_bool_t 
351 _dbus_user_at_console (const char *username,
352                        DBusError  *error)
353 {
354
355   DBusString f;
356   dbus_bool_t result;
357
358   result = FALSE;
359   if (!_dbus_string_init (&f))
360     {
361       _DBUS_SET_OOM (error);
362       return FALSE;
363     }
364
365   if (!_dbus_string_append (&f, DBUS_CONSOLE_AUTH_DIR))
366     {
367       _DBUS_SET_OOM (error);
368       goto out;
369     }
370
371
372   if (!_dbus_string_append (&f, username))
373     {
374       _DBUS_SET_OOM (error);
375       goto out;
376     }
377
378   result = _dbus_file_exists (_dbus_string_get_const_data (&f));
379
380  out:
381   _dbus_string_free (&f);
382
383   return result;
384 }
385
386
387 /**
388  * Checks whether the filename is an absolute path
389  *
390  * @param filename the filename
391  * @returns #TRUE if an absolute path
392  */
393 dbus_bool_t
394 _dbus_path_is_absolute (const DBusString *filename)
395 {
396   if (_dbus_string_get_length (filename) > 0)
397     return _dbus_string_get_byte (filename, 0) == '/';
398   else
399     return FALSE;
400 }
401
402 /**
403  * stat() wrapper.
404  *
405  * @param filename the filename to stat
406  * @param statbuf the stat info to fill in
407  * @param error return location for error
408  * @returns #FALSE if error was set
409  */
410 dbus_bool_t
411 _dbus_stat (const DBusString *filename,
412             DBusStat         *statbuf,
413             DBusError        *error)
414 {
415   const char *filename_c;
416   struct stat sb;
417
418   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
419   
420   filename_c = _dbus_string_get_const_data (filename);
421
422   if (stat (filename_c, &sb) < 0)
423     {
424       dbus_set_error (error, _dbus_error_from_errno (errno),
425                       "%s", _dbus_strerror (errno));
426       return FALSE;
427     }
428
429   statbuf->mode = sb.st_mode;
430   statbuf->nlink = sb.st_nlink;
431   statbuf->uid = sb.st_uid;
432   statbuf->gid = sb.st_gid;
433   statbuf->size = sb.st_size;
434   statbuf->atime = sb.st_atime;
435   statbuf->mtime = sb.st_mtime;
436   statbuf->ctime = sb.st_ctime;
437
438   return TRUE;
439 }
440
441
442 /**
443  * Internals of directory iterator
444  */
445 struct DBusDirIter
446 {
447   DIR *d; /**< The DIR* from opendir() */
448   
449 };
450
451 /**
452  * Open a directory to iterate over.
453  *
454  * @param filename the directory name
455  * @param error exception return object or #NULL
456  * @returns new iterator, or #NULL on error
457  */
458 DBusDirIter*
459 _dbus_directory_open (const DBusString *filename,
460                       DBusError        *error)
461 {
462   DIR *d;
463   DBusDirIter *iter;
464   const char *filename_c;
465
466   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
467   
468   filename_c = _dbus_string_get_const_data (filename);
469
470   d = opendir (filename_c);
471   if (d == NULL)
472     {
473       dbus_set_error (error, _dbus_error_from_errno (errno),
474                       "Failed to read directory \"%s\": %s",
475                       filename_c,
476                       _dbus_strerror (errno));
477       return NULL;
478     }
479   iter = dbus_new0 (DBusDirIter, 1);
480   if (iter == NULL)
481     {
482       closedir (d);
483       dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
484                       "Could not allocate memory for directory iterator");
485       return NULL;
486     }
487
488   iter->d = d;
489
490   return iter;
491 }
492
493 /**
494  * Get next file in the directory. Will not return "." or ".."  on
495  * UNIX. If an error occurs, the contents of "filename" are
496  * undefined. The error is never set if the function succeeds.
497  *
498  * @todo 1.0 for thread safety, I think we have to use
499  * readdir_r(). (GLib has the same issue, should file a bug.)
500  *
501  * @param iter the iterator
502  * @param filename string to be set to the next file in the dir
503  * @param error return location for error
504  * @returns #TRUE if filename was filled in with a new filename
505  */
506 dbus_bool_t
507 _dbus_directory_get_next_file (DBusDirIter      *iter,
508                                DBusString       *filename,
509                                DBusError        *error)
510 {
511   struct dirent *ent;
512
513   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
514   
515  again:
516   errno = 0;
517   ent = readdir (iter->d);
518   if (ent == NULL)
519     {
520       if (errno != 0)
521         dbus_set_error (error,
522                         _dbus_error_from_errno (errno),
523                         "%s", _dbus_strerror (errno));
524       return FALSE;
525     }
526   else if (ent->d_name[0] == '.' &&
527            (ent->d_name[1] == '\0' ||
528             (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
529     goto again;
530   else
531     {
532       _dbus_string_set_length (filename, 0);
533       if (!_dbus_string_append (filename, ent->d_name))
534         {
535           dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
536                           "No memory to read directory entry");
537           return FALSE;
538         }
539       else
540         return TRUE;
541     }
542 }
543
544 /**
545  * Closes a directory iteration.
546  */
547 void
548 _dbus_directory_close (DBusDirIter *iter)
549 {
550   closedir (iter->d);
551   dbus_free (iter);
552 }
553
554 static dbus_bool_t
555 fill_user_info_from_group (struct group  *g,
556                            DBusGroupInfo *info,
557                            DBusError     *error)
558 {
559   _dbus_assert (g->gr_name != NULL);
560   
561   info->gid = g->gr_gid;
562   info->groupname = _dbus_strdup (g->gr_name);
563
564   /* info->members = dbus_strdupv (g->gr_mem) */
565   
566   if (info->groupname == NULL)
567     {
568       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
569       return FALSE;
570     }
571
572   return TRUE;
573 }
574
575 static dbus_bool_t
576 fill_group_info (DBusGroupInfo    *info,
577                  dbus_gid_t        gid,
578                  const DBusString *groupname,
579                  DBusError        *error)
580 {
581   const char *group_c_str;
582
583   _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET);
584   _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET);
585
586   if (groupname)
587     group_c_str = _dbus_string_get_const_data (groupname);
588   else
589     group_c_str = NULL;
590   
591   /* For now assuming that the getgrnam() and getgrgid() flavors
592    * always correspond to the pwnam flavors, if not we have
593    * to add more configure checks.
594    */
595   
596 #if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R)
597   {
598     struct group *g;
599     int result;
600     char buf[1024];
601     struct group g_str;
602
603     g = NULL;
604 #ifdef HAVE_POSIX_GETPWNAM_R
605
606     if (group_c_str)
607       result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf),
608                            &g);
609     else
610       result = getgrgid_r (gid, &g_str, buf, sizeof (buf),
611                            &g);
612 #else
613     g = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf));
614     result = 0;
615 #endif /* !HAVE_POSIX_GETPWNAM_R */
616     if (result == 0 && g == &g_str)
617       {
618         return fill_user_info_from_group (g, info, error);
619       }
620     else
621       {
622         dbus_set_error (error, _dbus_error_from_errno (errno),
623                         "Group %s unknown or failed to look it up\n",
624                         group_c_str ? group_c_str : "???");
625         return FALSE;
626       }
627   }
628 #else /* ! HAVE_GETPWNAM_R */
629   {
630     /* I guess we're screwed on thread safety here */
631     struct group *g;
632
633     g = getgrnam (group_c_str);
634
635     if (g != NULL)
636       {
637         return fill_user_info_from_group (g, info, error);
638       }
639     else
640       {
641         dbus_set_error (error, _dbus_error_from_errno (errno),
642                         "Group %s unknown or failed to look it up\n",
643                         group_c_str ? group_c_str : "???");
644         return FALSE;
645       }
646   }
647 #endif  /* ! HAVE_GETPWNAM_R */
648 }
649
650 /**
651  * Initializes the given DBusGroupInfo struct
652  * with information about the given group name.
653  *
654  * @param info the group info struct
655  * @param groupname name of group
656  * @param error the error return
657  * @returns #FALSE if error is set
658  */
659 dbus_bool_t
660 _dbus_group_info_fill (DBusGroupInfo    *info,
661                        const DBusString *groupname,
662                        DBusError        *error)
663 {
664   return fill_group_info (info, DBUS_GID_UNSET,
665                           groupname, error);
666
667 }
668
669 /**
670  * Initializes the given DBusGroupInfo struct
671  * with information about the given group ID.
672  *
673  * @param info the group info struct
674  * @param gid group ID
675  * @param error the error return
676  * @returns #FALSE if error is set
677  */
678 dbus_bool_t
679 _dbus_group_info_fill_gid (DBusGroupInfo *info,
680                            dbus_gid_t     gid,
681                            DBusError     *error)
682 {
683   return fill_group_info (info, gid, NULL, error);
684 }
685
686 /** @} */ /* End of DBusInternalsUtils functions */
687
688 /**
689  * @addtogroup DBusString
690  *
691  * @{
692  */
693 /**
694  * Get the directory name from a complete filename
695  * @param filename the filename
696  * @param dirname string to append directory name to
697  * @returns #FALSE if no memory
698  */
699 dbus_bool_t
700 _dbus_string_get_dirname  (const DBusString *filename,
701                            DBusString       *dirname)
702 {
703   int sep;
704   
705   _dbus_assert (filename != dirname);
706   _dbus_assert (filename != NULL);
707   _dbus_assert (dirname != NULL);
708
709   /* Ignore any separators on the end */
710   sep = _dbus_string_get_length (filename);
711   if (sep == 0)
712     return _dbus_string_append (dirname, "."); /* empty string passed in */
713     
714   while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
715     --sep;
716
717   _dbus_assert (sep >= 0);
718   
719   if (sep == 0)
720     return _dbus_string_append (dirname, "/");
721   
722   /* Now find the previous separator */
723   _dbus_string_find_byte_backward (filename, sep, '/', &sep);
724   if (sep < 0)
725     return _dbus_string_append (dirname, ".");
726   
727   /* skip multiple separators */
728   while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
729     --sep;
730
731   _dbus_assert (sep >= 0);
732   
733   if (sep == 0 &&
734       _dbus_string_get_byte (filename, 0) == '/')
735     return _dbus_string_append (dirname, "/");
736   else
737     return _dbus_string_copy_len (filename, 0, sep - 0,
738                                   dirname, _dbus_string_get_length (dirname));
739 }
740 /** @} */ /* DBusString stuff */
741