diff options
Diffstat (limited to 'server/monitor')
-rw-r--r-- | server/monitor/monitor.c | 190 |
1 files changed, 150 insertions, 40 deletions
diff --git a/server/monitor/monitor.c b/server/monitor/monitor.c index 19161e2e..8ecb8733 100644 --- a/server/monitor/monitor.c +++ b/server/monitor/monitor.c @@ -99,8 +99,11 @@ struct config_file_ctx { struct confdb_ctx *cdb; struct tevent_context *ev; int fd; + int retries; + int wd; char *filename; time_t modified; + bool needs_update; confdb_reconf_fn reconf_fn; void *reconf_pvt; }; @@ -126,7 +129,7 @@ static int get_provider_config(struct mt_ctx *ctx, const char *name, static int add_new_service(struct mt_ctx *ctx, const char *name); static int add_new_provider(struct mt_ctx *ctx, const char *name); -static int monitor_signal_reconf(struct confdb_ctx *cdb, void *pvt); +static int monitor_signal_reconf(struct config_file_ctx *file_ctx); static int update_monitor_config(struct mt_ctx *ctx); static int monitor_cleanup(void); @@ -506,10 +509,21 @@ done: dbus_message_unref(reply); } -static int monitor_signal_reconf(struct confdb_ctx *cdb, void *pvt) +static int monitor_signal_reconf(struct config_file_ctx *file_ctx) { - struct mt_ctx *ctx = talloc_get_type(pvt, struct mt_ctx); + int ret; + struct mt_ctx *ctx = talloc_get_type(file_ctx->reconf_pvt, struct mt_ctx); DEBUG(1, ("Configuration has changed. Reloading.\n")); + + /* Update the confdb configuration */ + ret = confdb_init_db(file_ctx->filename, file_ctx->cdb); + if (ret != EOK) { + DEBUG(0, ("Could not reload configuration!")); + kill(getpid(), SIGTERM); + return ret; + } + + /* Update the monitor's configuration and signal children */ return update_monitor_config(ctx); } @@ -1160,21 +1174,61 @@ int read_config_file(const char *config_file) } #ifdef HAVE_SYS_INOTIFY_H +static void process_config_file(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr); + static void config_file_changed(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, void *data) + struct tevent_fd *fde, + uint16_t flags, void *data) +{ + struct tevent_timer *te = NULL; + struct timeval tv; + struct config_file_ctx *file_ctx; + + file_ctx = talloc_get_type(data, struct config_file_ctx); + if (file_ctx->needs_update) { + /* Skip updating. It's already queued for update. + */ + return; + } + + /* We will queue the file for update in one second. + * This way, if there is a script writing to the file + * repeatedly, we won't be attempting to update multiple + * times. + */ + gettimeofday(&tv, NULL); + tv.tv_sec += 1; + + te = tevent_add_timer(ev, ev, tv, process_config_file, file_ctx); + if (!te) { + DEBUG(0, ("Unable to queue config file update! Exiting.")); + kill(getpid(), SIGTERM); + return; + } + file_ctx->needs_update = 1; +} + +static void rewatch_config_file(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr); +static void process_config_file(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr) { - int ret; TALLOC_CTX *tmp_ctx; struct inotify_event *in_event; char *buf; char *name; ssize_t len, total_len; - ssize_t event_size = sizeof(struct inotify_event); - struct config_file_ctx *file_ctx = - talloc_get_type(data, struct config_file_ctx); + ssize_t event_size; + struct config_file_ctx *file_ctx; + + event_size = sizeof(struct inotify_event); + file_ctx = talloc_get_type(ptr, struct config_file_ctx); - DEBUG(1, ("Config file changed\n")); + DEBUG(1, ("Processing config file changes\n")); tmp_ctx = talloc_new(NULL); if (!tmp_ctx) return; @@ -1215,17 +1269,79 @@ static void config_file_changed(struct tevent_context *ev, } } - /* Parse the configuration file */ - ret = confdb_init_db(file_ctx->filename, file_ctx->cdb); - if (ret != EOK) { - DEBUG(0, ("Could not reload configuration!")); - kill(getpid(), SIGTERM); + talloc_free(tmp_ctx); + + if (in_event->mask & IN_IGNORED) { + /* Some text editors will move a new file on top of the + * existing one instead of modifying it. In this case, + * the kernel will send us an IN_IGNORE signal. + * We will try to open a new watch descriptor on the + * new file. + */ + struct timeval tv; + struct tevent_timer *tev; + tv.tv_sec = t.tv_sec+5; + tv.tv_usec = t.tv_usec; + + file_ctx->retries = 0; + tev = tevent_add_timer(ev, ev, tv, rewatch_config_file, file_ctx); + if (te == NULL) { + DEBUG(0, ("Could not restore inotify watch. Quitting!\n")); + close(file_ctx->fd); + kill(getpid(), SIGTERM); + } + return; } /* Tell the monitor to signal the children */ - file_ctx->reconf_fn(file_ctx->cdb, file_ctx->reconf_pvt); + file_ctx->reconf_fn(file_ctx); + file_ctx->needs_update = 0; +} - talloc_free(tmp_ctx); +static void rewatch_config_file(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr) +{ + int err; + struct tevent_timer *tev = NULL; + struct timeval tv; + + struct config_file_ctx *file_ctx; + file_ctx = talloc_get_type(ptr, struct config_file_ctx); + file_ctx->retries++; + + /* Retry six times at five-second intervals before giving up */ + if (file_ctx->retries > 6) { + DEBUG(0, ("Could not restore inotify watch. Quitting!\n")); + close(file_ctx->fd); + kill(getpid(), SIGTERM); + } + + file_ctx->wd = inotify_add_watch(file_ctx->fd, file_ctx->filename, + IN_MODIFY); + if (file_ctx->wd < 0) { + err = errno; + + tv.tv_sec = t.tv_sec+5; + tv.tv_usec = t.tv_usec; + + DEBUG(1, ("Could not add inotify watch for file [%s]. Error [%d:%s]\n", + file_ctx->filename, err, strerror(err))); + + tev = tevent_add_timer(ev, ev, tv, rewatch_config_file, file_ctx); + if (te == NULL) { + DEBUG(0, ("Could not restore inotify watch. Quitting!\n")); + close(file_ctx->fd); + kill(getpid(), SIGTERM); + } + + return; + } + file_ctx->retries = 0; + + /* Tell the monitor to signal the children */ + file_ctx->reconf_fn(file_ctx); + file_ctx->needs_update = 0; } #endif @@ -1257,14 +1373,8 @@ static void poll_config_file(struct tevent_context *ev, DEBUG(1, ("Config file changed\n")); file_ctx->modified = file_stat.st_mtime; - ret = confdb_init_db(file_ctx->filename, file_ctx->cdb); - if (ret != EOK) { - DEBUG(0, ("Could not reload configuration!")); - kill(getpid(), SIGTERM); - } - /* Tell the monitor to signal the children */ - file_ctx->reconf_fn(file_ctx->cdb, file_ctx->reconf_pvt); + file_ctx->reconf_fn(file_ctx); } gettimeofday(&tv, NULL); @@ -1280,50 +1390,50 @@ static void poll_config_file(struct tevent_context *ev, static int try_inotify(struct config_file_ctx *file_ctx) { #ifdef HAVE_SYS_INOTIFY_H - int fd, wd, err, fd_args, ret; + int err, fd_args, ret; struct tevent_fd *tfd; /* Set up inotify to monitor the config file for changes */ - fd = inotify_init(); - if (fd < 0) { + file_ctx->fd = inotify_init(); + if (file_ctx->fd < 0) { err = errno; DEBUG(0, ("Could not initialize inotify, error [%d:%s]\n", err, strerror(err))); return err; } - fd_args = fcntl(fd, F_GETFL, NULL); + fd_args = fcntl(file_ctx->fd, F_GETFL, NULL); if (fd_args < 0) { /* Could not set nonblocking */ - close(fd); + close(file_ctx->fd); return EINVAL; } fd_args |= O_NONBLOCK; - ret = fcntl(fd, F_SETFL, fd_args); + ret = fcntl(file_ctx->fd, F_SETFL, fd_args); if (ret < 0) { /* Could not set nonblocking */ - close(fd); + close(file_ctx->fd); return EINVAL; } - wd = inotify_add_watch(fd, file_ctx->filename, IN_MODIFY); - if (wd < 0) { + file_ctx->wd = inotify_add_watch(file_ctx->fd, file_ctx->filename, + IN_MODIFY); + if (file_ctx->wd < 0) { err = errno; DEBUG(0, ("Could not add inotify watch for file [%s]. Error [%d:%s]\n", file_ctx->filename, err, strerror(err))); - close(fd); + close(file_ctx->fd); return err; } /* Add the inotify file descriptor to the TEvent context */ - file_ctx->fd = fd; - - tfd = tevent_add_fd(file_ctx->ev, file_ctx, fd, TEVENT_FD_READ, - config_file_changed, file_ctx); + tfd = tevent_add_fd(file_ctx->ev, file_ctx, file_ctx->fd, + TEVENT_FD_READ, config_file_changed, + file_ctx); if (!tfd) { - inotify_rm_watch(fd, wd); - close(fd); + inotify_rm_watch(file_ctx->fd, file_ctx->wd); + close(file_ctx->fd); return EIO; } return EOK; |