Blame src/daemon/abrt-inotify.c

Packit 8ea169
/*
Packit 8ea169
    Copyright (C) 2013  RedHat inc.
Packit 8ea169
Packit 8ea169
    This program is free software; you can redistribute it and/or modify
Packit 8ea169
    it under the terms of the GNU General Public License as published by
Packit 8ea169
    the Free Software Foundation; either version 2 of the License, or
Packit 8ea169
    (at your option) any later version.
Packit 8ea169
Packit 8ea169
    This program is distributed in the hope that it will be useful,
Packit 8ea169
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8ea169
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8ea169
    GNU General Public License for more details.
Packit 8ea169
Packit 8ea169
    You should have received a copy of the GNU General Public License along
Packit 8ea169
    with this program; if not, write to the Free Software Foundation, Inc.,
Packit 8ea169
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 8ea169
*/
Packit 8ea169
Packit 8ea169
#include "abrt-inotify.h"
Packit 8ea169
#include "abrt_glib.h"
Packit 8ea169
#include "libabrt.h"
Packit 8ea169
Packit 8ea169
#include <stdio.h>
Packit 8ea169
#include <sys/ioctl.h> /* ioctl(FIONREAD) */
Packit 8ea169
Packit 8ea169
struct abrt_inotify_watch
Packit 8ea169
{
Packit 8ea169
    abrt_inotify_watch_handler handler;
Packit 8ea169
    void *user_data;
Packit 8ea169
    int inotify_fd;
Packit 8ea169
    int inotify_wd;
Packit 8ea169
    GIOChannel *channel_inotify;
Packit 8ea169
    guint channel_inotify_source_id;
Packit 8ea169
};
Packit 8ea169
Packit 8ea169
/* Inotify handler */
Packit 8ea169
Packit 8ea169
static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpointer user_data)
Packit 8ea169
{
Packit 8ea169
    /* Default size: 128 simultaneous actions (about 1/2 meg) */
Packit 8ea169
#define INOTIFY_BUF_SIZE ((sizeof(struct inotify_event) + FILENAME_MAX)*128)
Packit 8ea169
    /* Determine how much to read (it usually is much smaller) */
Packit 8ea169
    /* NB: this variable _must_ be int-sized, ioctl expects that! */
Packit 8ea169
    int inotify_bytes = INOTIFY_BUF_SIZE;
Packit 8ea169
    if (ioctl(g_io_channel_unix_get_fd(gio), FIONREAD, &inotify_bytes) != 0
Packit 8ea169
    /*|| inotify_bytes < sizeof(struct inotify_event)
Packit 8ea169
         ^^^^^^^^^^^^^^^^^^^ - WRONG: legitimate 0 was seen when flooded with inotify events
Packit 8ea169
    */
Packit 8ea169
     || inotify_bytes > INOTIFY_BUF_SIZE
Packit 8ea169
    ) {
Packit 8ea169
        inotify_bytes = INOTIFY_BUF_SIZE;
Packit 8ea169
    }
Packit 8ea169
    log_debug("FIONREAD:%d", inotify_bytes);
Packit 8ea169
Packit 8ea169
    if (inotify_bytes == 0)
Packit 8ea169
        return TRUE; /* "please don't remove this event" */
Packit 8ea169
Packit 8ea169
    /* We may race: more inotify events may happen after ioctl(FIONREAD).
Packit 8ea169
     * To be more efficient, allocate a bit more space to eat those events too.
Packit 8ea169
     * This also would help against a bug we once had where
Packit 8ea169
     * g_io_channel_read_chars() was buffering reads
Packit 8ea169
     * and we were going out of sync wrt struct inotify_event's layout.
Packit 8ea169
     */
Packit 8ea169
    inotify_bytes += 2 * (sizeof(struct inotify_event) + FILENAME_MAX);
Packit 8ea169
    char *buf = xmalloc(inotify_bytes);
Packit 8ea169
    errno = 0;
Packit 8ea169
    gsize len;
Packit 8ea169
    GError *gerror = NULL;
Packit 8ea169
    /* Note: we ensured elsewhere that this read is non-blocking, making it ok
Packit 8ea169
     * for buffer len (inotify_bytes) to be larger than actual available byte count.
Packit 8ea169
     */
Packit 8ea169
    GIOStatus err = g_io_channel_read_chars(gio, buf, inotify_bytes, &len, &gerror);
Packit 8ea169
    if (err != G_IO_STATUS_NORMAL)
Packit 8ea169
    {
Packit 8ea169
        error_msg("Error reading inotify fd: %s", gerror ? gerror->message : "unknown");
Packit 8ea169
        free(buf);
Packit 8ea169
        if (gerror)
Packit 8ea169
            g_error_free(gerror);
Packit 8ea169
        return FALSE; /* "remove this event" (huh??) */
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    struct abrt_inotify_watch *aic = (struct abrt_inotify_watch *)user_data;
Packit 8ea169
    /* Reconstruct each event */
Packit 8ea169
    gsize i = 0;
Packit 8ea169
    for (;;)
Packit 8ea169
    {
Packit 8ea169
        if (i >= len)
Packit 8ea169
        {
Packit 8ea169
            /* This would catch one of our former bugs. Let's be paranoid */
Packit 8ea169
            if (i > len)
Packit 8ea169
                error_msg("warning: ran off struct inotify (this should never happen): %u > %u", (int)i, (int)len);
Packit 8ea169
            break;
Packit 8ea169
        }
Packit 8ea169
        struct inotify_event *event = (struct inotify_event *) &buf[i];
Packit 8ea169
        i += sizeof(*event) + event->len;
Packit 8ea169
Packit 8ea169
        aic->handler(aic, event, aic->user_data);
Packit 8ea169
    }
Packit 8ea169
    free(buf);
Packit 8ea169
    return TRUE;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
struct abrt_inotify_watch *
Packit 8ea169
abrt_inotify_watch_init(const char *path, int inotify_flags, abrt_inotify_watch_handler handler, void *user_data)
Packit 8ea169
{
Packit 8ea169
    struct abrt_inotify_watch *aiw = xmalloc(sizeof(*aiw));
Packit 8ea169
    aiw->handler = handler;
Packit 8ea169
    aiw->user_data = user_data;
Packit 8ea169
Packit 8ea169
    log_notice("Initializing inotify");
Packit 8ea169
    errno = 0;
Packit 8ea169
    aiw->inotify_fd = inotify_init();
Packit 8ea169
    if (aiw->inotify_fd == -1)
Packit 8ea169
        perror_msg_and_die("inotify_init failed");
Packit 8ea169
    close_on_exec_on(aiw->inotify_fd);
Packit 8ea169
Packit 8ea169
    aiw->inotify_wd = inotify_add_watch(aiw->inotify_fd, path, inotify_flags);
Packit 8ea169
    if (aiw->inotify_wd < 0)
Packit 8ea169
        perror_msg_and_die("inotify_add_watch failed on '%s'", path);
Packit 8ea169
Packit 8ea169
    log_notice("Adding inotify watch to glib main loop");
Packit 8ea169
    /* Without nonblocking mode, users observed abrtd blocking
Packit 8ea169
     * on inotify read forever. Must set fd to non-blocking:
Packit 8ea169
     */
Packit 8ea169
    ndelay_on(aiw->inotify_fd);
Packit 8ea169
    aiw->channel_inotify = abrt_gio_channel_unix_new(aiw->inotify_fd);
Packit 8ea169
Packit 8ea169
    /*
Packit 8ea169
     * glib's read buffering must be disabled, or else
Packit 8ea169
     * FIONREAD-reported "available data" sizes and sizes of reads
Packit 8ea169
     * can become inconsistent, and worse, buffering can split
Packit 8ea169
     * struct inotify's (very bad!).
Packit 8ea169
     */
Packit 8ea169
    g_io_channel_set_buffered(aiw->channel_inotify, false);
Packit 8ea169
Packit 8ea169
    errno = 0;
Packit 8ea169
    aiw->channel_inotify_source_id = g_io_add_watch(aiw->channel_inotify,
Packit 8ea169
            G_IO_IN | G_IO_PRI | G_IO_HUP,
Packit 8ea169
            handle_inotify_cb,
Packit 8ea169
            aiw);
Packit 8ea169
    if (!aiw->channel_inotify_source_id)
Packit 8ea169
        error_msg_and_die("g_io_add_watch failed");
Packit 8ea169
Packit 8ea169
    return aiw;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
void
Packit 8ea169
abrt_inotify_watch_reset(struct abrt_inotify_watch *watch, const char *path, int inotify_flags)
Packit 8ea169
{
Packit 8ea169
    inotify_rm_watch(watch->inotify_fd, watch->inotify_wd);
Packit 8ea169
    watch->inotify_wd = inotify_add_watch(watch->inotify_fd, path, inotify_flags);
Packit 8ea169
    if (watch->inotify_wd < 0)
Packit 8ea169
        error_msg_and_die("inotify_add_watch failed on '%s'", path);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
void
Packit 8ea169
abrt_inotify_watch_destroy(struct abrt_inotify_watch *watch)
Packit 8ea169
{
Packit 8ea169
    if (!watch)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    inotify_rm_watch(watch->inotify_fd, watch->inotify_wd);
Packit 8ea169
    g_source_remove(watch->channel_inotify_source_id);
Packit 8ea169
Packit 8ea169
    GError *error = NULL;
Packit 8ea169
    g_io_channel_shutdown(watch->channel_inotify, FALSE, &error);
Packit 8ea169
    if (error)
Packit 8ea169
    {
Packit 8ea169
        log_notice("Can't shutdown inotify gio channel: '%s'", error->message);
Packit 8ea169
        g_error_free(error);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    g_io_channel_unref(watch->channel_inotify);
Packit 8ea169
    free(watch);
Packit 8ea169
}