|
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 |
}
|