271 lines
5.6 KiB
C
271 lines
5.6 KiB
C
|
#define _GNU_SOURCE
|
||
|
#include <sys/inotify.h>
|
||
|
#include <errno.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <ctype.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <syslog.h>
|
||
|
#include "../setfiles/restore.h"
|
||
|
#include <glob.h>
|
||
|
#include <libgen.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <string.h>
|
||
|
#include <stdio.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <selinux/selinux.h>
|
||
|
#include "restorecond.h"
|
||
|
#include "stringslist.h"
|
||
|
#include "utmpwatcher.h"
|
||
|
|
||
|
/* size of the event structure, not counting name */
|
||
|
#define EVENT_SIZE (sizeof (struct inotify_event))
|
||
|
/* reasonable guess as to size of 1024 events */
|
||
|
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
|
||
|
|
||
|
|
||
|
struct watchList {
|
||
|
struct watchList *next;
|
||
|
int wd;
|
||
|
char *dir;
|
||
|
struct stringsList *files;
|
||
|
};
|
||
|
struct watchList *firstDir = NULL;
|
||
|
|
||
|
int watch_list_isempty() {
|
||
|
return firstDir == NULL;
|
||
|
}
|
||
|
|
||
|
void watch_list_add(int fd, const char *path)
|
||
|
{
|
||
|
struct watchList *ptr = NULL;
|
||
|
size_t i = 0;
|
||
|
struct watchList *prev = NULL;
|
||
|
glob_t globbuf;
|
||
|
char *x = strdup(path);
|
||
|
if (!x) exitApp("Out of Memory");
|
||
|
char *file = basename(x);
|
||
|
char *dir = dirname(x);
|
||
|
ptr = firstDir;
|
||
|
|
||
|
if (exclude(path)) goto end;
|
||
|
|
||
|
globbuf.gl_offs = 1;
|
||
|
if (glob(path,
|
||
|
GLOB_TILDE | GLOB_PERIOD,
|
||
|
NULL,
|
||
|
&globbuf) >= 0) {
|
||
|
for (i=0; i < globbuf.gl_pathc; i++) {
|
||
|
int len = strlen(globbuf.gl_pathv[i]) -2;
|
||
|
if (len > 0 && strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0) continue;
|
||
|
if (len > 0 && strcmp(&globbuf.gl_pathv[i][len], "/..") == 0) continue;
|
||
|
if (process_one_realpath(globbuf.gl_pathv[i], 0) > 0)
|
||
|
process_one_realpath(globbuf.gl_pathv[i], 1);
|
||
|
}
|
||
|
globfree(&globbuf);
|
||
|
}
|
||
|
|
||
|
while (ptr != NULL) {
|
||
|
if (strcmp(dir, ptr->dir) == 0) {
|
||
|
strings_list_add(&ptr->files, file);
|
||
|
goto end;
|
||
|
}
|
||
|
prev = ptr;
|
||
|
ptr = ptr->next;
|
||
|
}
|
||
|
ptr = calloc(1, sizeof(struct watchList));
|
||
|
|
||
|
if (!ptr) exitApp("Out of Memory");
|
||
|
|
||
|
ptr->wd = inotify_add_watch(fd, dir, IN_CREATE | IN_MOVED_TO);
|
||
|
if (ptr->wd == -1) {
|
||
|
free(ptr);
|
||
|
if (! run_as_user)
|
||
|
syslog(LOG_ERR, "Unable to watch (%s) %s\n",
|
||
|
path, strerror(errno));
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
ptr->dir = strdup(dir);
|
||
|
if (!ptr->dir)
|
||
|
exitApp("Out of Memory");
|
||
|
|
||
|
strings_list_add(&ptr->files, file);
|
||
|
if (prev)
|
||
|
prev->next = ptr;
|
||
|
else
|
||
|
firstDir = ptr;
|
||
|
|
||
|
if (debug_mode)
|
||
|
printf("%d: Dir=%s, File=%s\n", ptr->wd, ptr->dir, file);
|
||
|
|
||
|
end:
|
||
|
free(x);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
A file was in a direcroty has been created. This function checks to
|
||
|
see if it is one that we are watching.
|
||
|
*/
|
||
|
|
||
|
int watch_list_find(int wd, const char *file)
|
||
|
{
|
||
|
struct watchList *ptr = NULL;
|
||
|
ptr = firstDir;
|
||
|
if (debug_mode)
|
||
|
printf("%d: File=%s\n", wd, file);
|
||
|
while (ptr != NULL) {
|
||
|
if (ptr->wd == wd) {
|
||
|
int exact=0;
|
||
|
if (strings_list_find(ptr->files, file, &exact) == 0) {
|
||
|
char *path = NULL;
|
||
|
if (asprintf(&path, "%s/%s", ptr->dir, file) <
|
||
|
0)
|
||
|
exitApp("Error allocating memory.");
|
||
|
|
||
|
process_one_realpath(path, 0);
|
||
|
free(path);
|
||
|
return 0;
|
||
|
}
|
||
|
if (debug_mode)
|
||
|
strings_list_print(ptr->files);
|
||
|
|
||
|
/* Not found in this directory */
|
||
|
return -1;
|
||
|
}
|
||
|
ptr = ptr->next;
|
||
|
}
|
||
|
/* Did not find a directory */
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
void watch_list_free(int fd)
|
||
|
{
|
||
|
struct watchList *ptr = NULL;
|
||
|
struct watchList *prev = NULL;
|
||
|
ptr = firstDir;
|
||
|
|
||
|
while (ptr != NULL) {
|
||
|
inotify_rm_watch(fd, ptr->wd);
|
||
|
strings_list_free(ptr->files);
|
||
|
free(ptr->dir);
|
||
|
prev = ptr;
|
||
|
ptr = ptr->next;
|
||
|
free(prev);
|
||
|
}
|
||
|
firstDir = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Inotify watch loop
|
||
|
*/
|
||
|
int watch(int fd, const char *watch_file)
|
||
|
{
|
||
|
char buf[BUF_LEN];
|
||
|
int len, i = 0;
|
||
|
if (firstDir == NULL) return 0;
|
||
|
|
||
|
len = read(fd, buf, BUF_LEN);
|
||
|
if (len < 0) {
|
||
|
if (terminate == 0) {
|
||
|
syslog(LOG_ERR, "Read error (%s)", strerror(errno));
|
||
|
return 0;
|
||
|
}
|
||
|
syslog(LOG_ERR, "terminated");
|
||
|
return -1;
|
||
|
} else if (!len)
|
||
|
/* BUF_LEN too small? */
|
||
|
return -1;
|
||
|
while (i < len) {
|
||
|
struct inotify_event *event;
|
||
|
event = (struct inotify_event *)&buf[i];
|
||
|
if (debug_mode)
|
||
|
printf("wd=%d mask=%u cookie=%u len=%u\n",
|
||
|
event->wd, event->mask,
|
||
|
event->cookie, event->len);
|
||
|
if (event->wd == master_wd)
|
||
|
read_config(fd, watch_file);
|
||
|
else {
|
||
|
switch (utmpwatcher_handle(fd, event->wd)) {
|
||
|
case -1: /* Message was not for utmpwatcher */
|
||
|
if (event->len)
|
||
|
watch_list_find(event->wd, event->name);
|
||
|
break;
|
||
|
case 1: /* utmp has changed need to reload */
|
||
|
read_config(fd, watch_file);
|
||
|
break;
|
||
|
|
||
|
default: /* No users logged in or out */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
i += EVENT_SIZE + event->len;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void process_config(int fd, FILE * cfg)
|
||
|
{
|
||
|
char *line_buf = NULL;
|
||
|
size_t len = 0;
|
||
|
|
||
|
while (getline(&line_buf, &len, cfg) > 0) {
|
||
|
char *buffer = line_buf;
|
||
|
while (isspace(*buffer))
|
||
|
buffer++;
|
||
|
if (buffer[0] == '#')
|
||
|
continue;
|
||
|
int l = strlen(buffer) - 1;
|
||
|
if (l <= 0)
|
||
|
continue;
|
||
|
buffer[l] = 0;
|
||
|
if (buffer[0] == '~') {
|
||
|
if (run_as_user) {
|
||
|
char *ptr=NULL;
|
||
|
asprintf(&ptr, "%s%s", homedir, &buffer[1]);
|
||
|
watch_list_add(fd, ptr);
|
||
|
free(ptr);
|
||
|
} else {
|
||
|
utmpwatcher_add(fd, &buffer[1]);
|
||
|
}
|
||
|
} else {
|
||
|
watch_list_add(fd, buffer);
|
||
|
}
|
||
|
}
|
||
|
free(line_buf);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Read config file ignoring Comment lines
|
||
|
Files specified one per line. Files with "~" will be expanded to the logged in users
|
||
|
homedirs.
|
||
|
*/
|
||
|
|
||
|
void read_config(int fd, const char *watch_file_path)
|
||
|
{
|
||
|
|
||
|
FILE *cfg = NULL;
|
||
|
if (debug_mode)
|
||
|
printf("Read Config\n");
|
||
|
|
||
|
watch_list_free(fd);
|
||
|
|
||
|
cfg = fopen(watch_file_path, "r");
|
||
|
if (!cfg){
|
||
|
perror(watch_file_path);
|
||
|
exitApp("Error reading config file");
|
||
|
}
|
||
|
process_config(fd, cfg);
|
||
|
fclose(cfg);
|
||
|
|
||
|
inotify_rm_watch(fd, master_wd);
|
||
|
master_wd =
|
||
|
inotify_add_watch(fd, watch_file_path, IN_MOVED_FROM | IN_MODIFY);
|
||
|
if (master_wd == -1)
|
||
|
exitApp("Error watching config file.");
|
||
|
}
|