345 lines
8.3 KiB
C
345 lines
8.3 KiB
C
/***************************************************************************
|
|
* Copyright (C) 10/2006 by Olaf Rempel *
|
|
* razzor@kopf-tisch.de *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "sys/inotify.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#define MAX_WATCHES 8192
|
|
|
|
#define WATCH_MASK (IN_DONT_FOLLOW | IN_ONLYDIR | IN_MOVE | IN_DELETE | IN_CREATE)
|
|
|
|
struct watch_entry {
|
|
int parent;
|
|
char *path;
|
|
};
|
|
|
|
static struct watch_entry *watch_arr;
|
|
|
|
/*
|
|
* adds a watch
|
|
* - updates watch_arr info (parent & path)
|
|
*
|
|
* returns watch discriptor or -1 on failure
|
|
*/
|
|
static int add_watch(int inotify_fd, char *path, int parent)
|
|
{
|
|
int wd = inotify_add_watch(inotify_fd, path, WATCH_MASK);
|
|
if (wd < 0) {
|
|
log_print(LOG_ERROR, "inotify_add_watch('%s')", path);
|
|
return -1;
|
|
}
|
|
|
|
if (watch_arr[wd].path != NULL) {
|
|
log_print(LOG_DEBUG, "add_watch(): removing old data: '%s' = %d",
|
|
watch_arr[wd].path, wd);
|
|
watch_arr[wd].parent = -1;
|
|
free(watch_arr[wd].path);
|
|
}
|
|
|
|
watch_arr[wd].parent = parent;
|
|
watch_arr[wd].path = strdup(path);
|
|
|
|
log_print(LOG_DEBUG, "add_watch('%s') = %d", path, wd);
|
|
|
|
return wd;
|
|
}
|
|
|
|
/*
|
|
* removes a watch, and all childrens!
|
|
* - updates watch_arr info
|
|
*
|
|
* returns -1 on failure
|
|
*/
|
|
static int remove_watch_recursive(int inotify_fd, int wd)
|
|
{
|
|
log_print(LOG_DEBUG, "remove_watch('%s')", watch_arr[wd].path);
|
|
|
|
int i;
|
|
for (i = 0; i < MAX_WATCHES; i++)
|
|
if (watch_arr[i].parent == wd)
|
|
remove_watch_recursive(inotify_fd, i);
|
|
|
|
int ret = inotify_rm_watch(inotify_fd, wd);
|
|
if (ret < 0)
|
|
log_print(LOG_ERROR, "inotify_rm_watch('%s')", watch_arr[wd].path);
|
|
|
|
watch_arr[wd].parent = -1;
|
|
free(watch_arr[wd].path);
|
|
watch_arr[wd].path = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_watch_recursive_real(int inotify_fd, char *path, int parent)
|
|
{
|
|
struct stat statbuf;
|
|
if (stat(path, &statbuf) == -1) {
|
|
log_print(LOG_ERROR, "add_watch_recursive_real(): stat('%s')", path);
|
|
return 0;
|
|
|
|
} else if (!S_ISDIR (statbuf.st_mode)) {
|
|
return 0;
|
|
}
|
|
|
|
int wd = add_watch(inotify_fd, path, parent);
|
|
if (wd < 0)
|
|
return 0;
|
|
|
|
int watches = 1;
|
|
|
|
DIR *dp = opendir(path);
|
|
if (dp == NULL) {
|
|
log_print(LOG_ERROR, "add_watch_recursive_real(): opendir('%s')", path);
|
|
|
|
} else {
|
|
char *end = path + strlen(path);
|
|
|
|
struct dirent *dentry;
|
|
while ((dentry = readdir(dp)) != NULL) {
|
|
if (!strcmp(dentry->d_name, "."))
|
|
continue;
|
|
|
|
if (!strcmp(dentry->d_name, ".."))
|
|
continue;
|
|
|
|
*end = '/';
|
|
strcpy(end +1, dentry->d_name);
|
|
watches += add_watch_recursive_real(inotify_fd, path, wd);
|
|
}
|
|
closedir(dp);
|
|
|
|
*end = '\0';
|
|
}
|
|
return watches;
|
|
}
|
|
|
|
static char * prepare_path(int parent, char *name)
|
|
{
|
|
char *tmppath = malloc(PATH_MAX);
|
|
if (tmppath == NULL) {
|
|
log_print(LOG_ERROR, "add_recursive_watch(): out of memory");
|
|
return 0;
|
|
}
|
|
|
|
if (parent != -1) {
|
|
char *tmp = tmppath + strlen(watch_arr[parent].path);
|
|
strcpy(tmppath, watch_arr[parent].path);
|
|
*tmp++ = '/';
|
|
|
|
strcpy(tmp, name);
|
|
|
|
} else {
|
|
strcpy(tmppath, name);
|
|
}
|
|
|
|
return tmppath;
|
|
}
|
|
|
|
static int find_child_by_name(int parent, char *name)
|
|
{
|
|
char *path = prepare_path(parent, name);
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_WATCHES; i++) {
|
|
if (watch_arr[i].parent == parent && !strcmp(watch_arr[i].path, path)) {
|
|
free(path);
|
|
return i;
|
|
}
|
|
}
|
|
|
|
log_print(LOG_ERROR, "find_child_by_name: '%s' not found", path);
|
|
free(path);
|
|
return -1;
|
|
}
|
|
|
|
int add_watch_recursive(int fd, char *name)
|
|
{
|
|
char *tmppath = prepare_path(-1, name);
|
|
int num = add_watch_recursive_real(fd, tmppath, -1);
|
|
|
|
free(tmppath);
|
|
return num;
|
|
}
|
|
|
|
|
|
static char * mask_to_str(int mask)
|
|
{
|
|
char *retval = malloc(256);
|
|
*retval = '\0';
|
|
|
|
if (mask & IN_ACCESS)
|
|
strcat(retval, "IN_ACCESS ");
|
|
|
|
if (mask & IN_MODIFY)
|
|
strcat(retval, "IN_MODIFY ");
|
|
|
|
if (mask & IN_ATTRIB)
|
|
strcat(retval, "IN_ATTRIB ");
|
|
|
|
if (mask & IN_CLOSE_WRITE)
|
|
strcat(retval, "IN_CLOSE_WRITE ");
|
|
|
|
if (mask & IN_CLOSE_NOWRITE)
|
|
strcat(retval, "IN_CLOSE_NOWRITE ");
|
|
|
|
if (mask & IN_OPEN)
|
|
strcat(retval, "IN_OPEN ");
|
|
|
|
if (mask & IN_MOVED_FROM)
|
|
strcat(retval, "IN_MOVED_FROM ");
|
|
|
|
if (mask & IN_MOVED_TO)
|
|
strcat(retval, "IN_MOVED_TO ");
|
|
|
|
if (mask & IN_CREATE)
|
|
strcat(retval, "IN_CREATE ");
|
|
|
|
if (mask & IN_DELETE)
|
|
strcat(retval, "IN_DELETE ");
|
|
|
|
if (mask & IN_DELETE_SELF)
|
|
strcat(retval, "IN_DELETE_SELF ");
|
|
|
|
if (mask & IN_MOVE_SELF)
|
|
strcat(retval, "IN_MOVE_SELF ");
|
|
|
|
if (mask & IN_UNMOUNT)
|
|
strcat(retval, "IN_UNMOUNT ");
|
|
|
|
if (mask & IN_Q_OVERFLOW)
|
|
strcat(retval, "IN_Q_OVERFLOW ");
|
|
|
|
if (mask & IN_IGNORED)
|
|
strcat(retval, "IN_IGNORED ");
|
|
|
|
if (mask & IN_ISDIR)
|
|
strcat(retval, "IN_ISDIR ");
|
|
|
|
if (mask & IN_ONESHOT)
|
|
strcat(retval, "IN_ONESHOT ");
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
int watch_callback(int fd, void *privdata)
|
|
{
|
|
unsigned int size;
|
|
int i = ioctl(fd, FIONREAD, &size);
|
|
if (i < 0) {
|
|
perror ("ioctl()");
|
|
return -1;
|
|
}
|
|
|
|
char *buf = malloc(size);
|
|
if (buf == NULL) {
|
|
perror("malloc()");
|
|
return -1;
|
|
}
|
|
|
|
int len, j = 0;
|
|
len = read(fd, buf, size);
|
|
while (j < len) {
|
|
struct inotify_event *event = (struct inotify_event *) &buf[j];
|
|
|
|
if (event->len)
|
|
log_print(LOG_INFO, "wd=%03d mask=%08X cookie=%08X name=%s/%s %s",
|
|
event->wd, event->mask, event->cookie, watch_arr[event->wd].path, event->name,
|
|
mask_to_str(event->mask));
|
|
else
|
|
log_print(LOG_INFO, "wd=%03d mask=%08X cookie=%08X (name=%s) %s",
|
|
event->wd, event->mask, event->cookie, watch_arr[event->wd].path,
|
|
mask_to_str(event->mask));
|
|
|
|
/* new directory created -> watch it */
|
|
if ((event->mask & (IN_CREATE | IN_ISDIR)) == (IN_CREATE | IN_ISDIR)) {
|
|
char *tmp = prepare_path(event->wd, event->name);
|
|
add_watch_recursive_real(fd, tmp, event->wd);
|
|
free(tmp);
|
|
}
|
|
|
|
/* directory removed -> remove watch info (the watch was removed by the kernel) */
|
|
if (event->mask & IN_IGNORED) {
|
|
if (watch_arr[event->wd].path != NULL) {
|
|
log_print(LOG_DEBUG, "kernel removed watch ('%s')", watch_arr[event->wd].path);
|
|
watch_arr[event->wd].parent = -1;
|
|
free(watch_arr[event->wd].path);
|
|
watch_arr[event->wd].path = NULL;
|
|
}
|
|
}
|
|
|
|
/* directory moved away -> find child by name-serach, then remove all his children rekursive */
|
|
if ((event->mask & (IN_MOVED_FROM | IN_ISDIR)) == (IN_MOVED_FROM | IN_ISDIR)) {
|
|
int wd = find_child_by_name(event->wd, event->name);
|
|
remove_watch_recursive(fd, wd);
|
|
}
|
|
|
|
/* directory moved to us -> add watches */
|
|
if ((event->mask & (IN_MOVED_TO | IN_ISDIR)) == (IN_MOVED_TO | IN_ISDIR)) {
|
|
char *tmp = prepare_path(event->wd, event->name);
|
|
add_watch_recursive_real(fd, tmp, event->wd);
|
|
free(tmp);
|
|
}
|
|
|
|
j += sizeof(struct inotify_event) + event->len;
|
|
}
|
|
free(buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int inotify_open()
|
|
{
|
|
int fd = inotify_init();
|
|
if (fd < 0)
|
|
log_print(LOG_ERROR, "inotify_open()");
|
|
|
|
if (watch_arr == NULL) {
|
|
watch_arr = malloc(MAX_WATCHES * sizeof(struct watch_entry));
|
|
if (watch_arr == NULL) {
|
|
log_print(LOG_ERROR, "inotify_open(): out of memory");
|
|
return 0;
|
|
}
|
|
|
|
int i;
|
|
for (i = 0; i < MAX_WATCHES; i++) {
|
|
watch_arr[i].parent = -1;
|
|
watch_arr[i].path = NULL;
|
|
}
|
|
}
|
|
|
|
return fd;
|
|
}
|