Home | History | Annotate | Download | only in bus
      1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
      2 /* dir-watch-kqueue.c  OS specific directory change notification for message bus
      3  *
      4  * Copyright (C) 2003 Red Hat, Inc.
      5  *
      6  * Licensed under the Academic Free License version 2.1
      7  *
      8  * This program is free software; you can redistribute it and/or modify
      9  * it under the terms of the GNU General Public License as published by
     10  * the Free Software Foundation; either version 2 of the License, or
     11  * (at your option) any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  * GNU General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program; if not, write to the Free Software
     20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #include <config.h>
     25 
     26 #include <sys/types.h>
     27 #include <sys/event.h>
     28 #include <sys/time.h>
     29 #include <signal.h>
     30 #include <fcntl.h>
     31 #include <unistd.h>
     32 #ifdef HAVE_ERRNO_H
     33 #include <errno.h>
     34 #endif
     35 
     36 #include "bus.h"
     37 #include <dbus/dbus-watch.h>
     38 
     39 #include <dbus/dbus-internals.h>
     40 #include <dbus/dbus-list.h>
     41 #include "dir-watch.h"
     42 
     43 #define MAX_DIRS_TO_WATCH 128
     44 
     45 static int kq = -1;
     46 static int fds[MAX_DIRS_TO_WATCH];
     47 static char *dirs[MAX_DIRS_TO_WATCH];
     48 static int num_fds = 0;
     49 static DBusWatch *watch = NULL;
     50 static DBusLoop *loop = NULL;
     51 
     52 static dbus_bool_t
     53 _kqueue_watch_callback (DBusWatch *watch, unsigned int condition, void *data)
     54 {
     55   return dbus_watch_handle (watch, condition);
     56 }
     57 
     58 static dbus_bool_t
     59 _handle_kqueue_watch (DBusWatch *watch, unsigned int flags, void *data)
     60 {
     61   struct kevent ev;
     62   struct timespec nullts = { 0, 0 };
     63   int res;
     64   pid_t pid;
     65 
     66   res = kevent (kq, NULL, 0, &ev, 1, &nullts);
     67 
     68   /* Sleep for half a second to avoid a race when files are install(1)'d
     69    * to system.d. */
     70   usleep(500000);
     71 
     72   if (res > 0)
     73     {
     74       pid = getpid ();
     75       _dbus_verbose ("Sending SIGHUP signal on reception of a kevent\n");
     76       (void) kill (pid, SIGHUP);
     77     }
     78   else if (res < 0 && errno == EBADF)
     79     {
     80       kq = -1;
     81       if (watch != NULL)
     82 	{
     83 	  _dbus_loop_remove_watch (loop, watch, _kqueue_watch_callback, NULL);
     84           _dbus_watch_unref (watch);
     85 	  watch = NULL;
     86 	}
     87       pid = getpid ();
     88       _dbus_verbose ("Sending SIGHUP signal since kqueue has been closed\n");
     89       (void) kill (pid, SIGHUP);
     90     }
     91 
     92   return TRUE;
     93 }
     94 
     95 static int
     96 _init_kqueue (BusContext *context)
     97 {
     98   int ret = 0;
     99 
    100   if (kq < 0)
    101     {
    102 
    103       kq = kqueue ();
    104       if (kq < 0)
    105         {
    106           _dbus_warn ("Cannot create kqueue; error '%s'\n", _dbus_strerror (errno));
    107 	  goto out;
    108 	}
    109 
    110         loop = bus_context_get_loop (context);
    111 
    112         watch = _dbus_watch_new (kq, DBUS_WATCH_READABLE, TRUE,
    113                                  _handle_kqueue_watch, NULL, NULL);
    114 
    115 	if (watch == NULL)
    116           {
    117             _dbus_warn ("Unable to create kqueue watch\n");
    118 	    close (kq);
    119 	    kq = -1;
    120 	    goto out;
    121 	  }
    122 
    123 	if (!_dbus_loop_add_watch (loop, watch, _kqueue_watch_callback,
    124                                    NULL, NULL))
    125           {
    126             _dbus_warn ("Unable to add reload watch to main loop");
    127 	    close (kq);
    128 	    kq = -1;
    129 	    _dbus_watch_unref (watch);
    130 	    watch = NULL;
    131             goto out;
    132 	  }
    133     }
    134 
    135   ret = 1;
    136 
    137 out:
    138   return ret;
    139 }
    140 
    141 void
    142 bus_set_watched_dirs (BusContext *context, DBusList **directories)
    143 {
    144   int new_fds[MAX_DIRS_TO_WATCH];
    145   char *new_dirs[MAX_DIRS_TO_WATCH];
    146   DBusList *link;
    147   int i, j, f, fd;
    148   struct kevent ev;
    149 
    150   if (!_init_kqueue (context))
    151     goto out;
    152 
    153   for (i = 0; i < MAX_DIRS_TO_WATCH; i++)
    154     {
    155       new_fds[i] = -1;
    156       new_dirs[i] = NULL;
    157     }
    158 
    159   i = 0;
    160   link = _dbus_list_get_first_link (directories);
    161   while (link != NULL)
    162     {
    163       new_dirs[i++] = (char *)link->data;
    164       link = _dbus_list_get_next_link (directories, link);
    165     }
    166 
    167   /* Look for directories in both the old and new sets, if
    168    * we find one, move its data into the new set.
    169    */
    170   for (i = 0; new_dirs[i]; i++)
    171     {
    172       for (j = 0; j < num_fds; j++)
    173         {
    174           if (dirs[j] && strcmp (new_dirs[i], dirs[j]) == 0)
    175             {
    176               new_fds[i] = fds[j];
    177 	      new_dirs[i] = dirs[j];
    178 	      fds[j] = -1;
    179 	      dirs[j] = NULL;
    180 	      break;
    181 	    }
    182 	}
    183     }
    184 
    185   /* Any directory we find in "fds" with a nonzero fd must
    186    * not be in the new set, so perform cleanup now.
    187    */
    188   for (j = 0; j < num_fds; j++)
    189     {
    190       if (fds[j] != -1)
    191         {
    192           close (fds[j]);
    193 	  dbus_free (dirs[j]);
    194 	  fds[j] = -1;
    195 	  dirs[j] = NULL;
    196 	}
    197     }
    198 
    199   for (i = 0; new_dirs[i]; i++)
    200     {
    201       if (new_fds[i] == -1)
    202         {
    203           /* FIXME - less lame error handling for failing to add a watch;
    204 	   * we may need to sleep.
    205 	   */
    206           fd = open (new_dirs[i], O_RDONLY);
    207           if (fd < 0)
    208             {
    209               if (errno != ENOENT)
    210                 {
    211                   _dbus_warn ("Cannot open directory '%s'; error '%s'\n", new_dirs[i], _dbus_strerror (errno));
    212                   goto out;
    213                 }
    214               else
    215                 {
    216                   new_fds[i] = -1;
    217                   new_dirs[i] = NULL;
    218                   continue;
    219                 }
    220             }
    221 
    222           EV_SET (&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
    223                   NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_RENAME, 0, 0);
    224           if (kevent (kq, &ev, 1, NULL, 0, NULL) == -1)
    225             {
    226               _dbus_warn ("Cannot setup a kevent for '%s'; error '%s'\n", new_dirs[i], _dbus_strerror (errno));
    227               close (fd);
    228               goto out;
    229             }
    230 
    231 	  new_fds[i] = fd;
    232 	  new_dirs[i] = _dbus_strdup (new_dirs[i]);
    233 	  if (!new_dirs[i])
    234             {
    235               /* FIXME have less lame handling for OOM, we just silently fail to
    236 	       * watch.  (In reality though, the whole OOM handling in dbus is
    237 	       * stupid but we won't go into that in this comment =) )
    238 	       */
    239               close (fd);
    240 	      new_fds[i] = -1;
    241 	    }
    242 	}
    243     }
    244 
    245   num_fds = i;
    246 
    247   for (i = 0; i < MAX_DIRS_TO_WATCH; i++)
    248     {
    249       fds[i] = new_fds[i];
    250       dirs[i] = new_dirs[i];
    251     }
    252 
    253  out:
    254   ;
    255 }
    256