1#define _GNU_SOURCE
2#include <sys/inotify.h>
3#include <errno.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8#include <ctype.h>
9#include <sys/types.h>
10#include <syslog.h>
11#include "restore.h"
12#include <glob.h>
13#include <libgen.h>
14#include <sys/stat.h>
15#include <string.h>
16#include <stdio.h>
17#include <fcntl.h>
18#include <selinux/selinux.h>
19#include "restorecond.h"
20#include "stringslist.h"
21#include "utmpwatcher.h"
22
23/* size of the event structure, not counting name */
24#define EVENT_SIZE  (sizeof (struct inotify_event))
25/* reasonable guess as to size of 1024 events */
26#define BUF_LEN        (1024 * (EVENT_SIZE + 16))
27
28struct watchList {
29	struct watchList *next;
30	int wd;
31	char *dir;
32	struct stringsList *files;
33};
34struct watchList *firstDir = NULL;
35
36int watch_list_isempty(void) {
37	return firstDir == NULL;
38}
39
40void watch_list_add(int fd, const char *path)
41{
42	struct watchList *ptr = NULL;
43	size_t i = 0;
44	struct watchList *prev = NULL;
45	glob_t globbuf;
46	char *x = strdup(path);
47	if (!x) exitApp("Out of Memory");
48	char *file = basename(x);
49	char *dir = dirname(x);
50	ptr = firstDir;
51	int len;
52
53	globbuf.gl_offs = 1;
54	if (glob(path,
55		 GLOB_TILDE | GLOB_PERIOD,
56		 NULL,
57		 &globbuf) >= 0) {
58		for (i = 0; i < globbuf.gl_pathc; i++) {
59			len = strlen(globbuf.gl_pathv[i]) - 2;
60			if (len > 0 &&
61			    strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0)
62				continue;
63			if (len > 0 &&
64			    strcmp(&globbuf.gl_pathv[i][len], "/..") == 0)
65				continue;
66			selinux_restorecon(globbuf.gl_pathv[i],
67					   r_opts.restorecon_flags);
68		}
69		globfree(&globbuf);
70	}
71
72	while (ptr != NULL) {
73		if (strcmp(dir, ptr->dir) == 0) {
74			strings_list_add(&ptr->files, file);
75			goto end;
76		}
77		prev = ptr;
78		ptr = ptr->next;
79	}
80	ptr = calloc(1, sizeof(struct watchList));
81
82	if (!ptr) exitApp("Out of Memory");
83
84	ptr->wd = inotify_add_watch(fd, dir, IN_CREATE | IN_MOVED_TO);
85	if (ptr->wd == -1) {
86		free(ptr);
87		if (! run_as_user)
88			syslog(LOG_ERR, "Unable to watch (%s) %s\n",
89			       path, strerror(errno));
90		goto end;
91	}
92
93	ptr->dir = strdup(dir);
94	if (!ptr->dir)
95		exitApp("Out of Memory");
96
97	strings_list_add(&ptr->files, file);
98	if (prev)
99		prev->next = ptr;
100	else
101		firstDir = ptr;
102
103	if (debug_mode)
104		printf("%d: Dir=%s, File=%s\n", ptr->wd, ptr->dir, file);
105
106end:
107	free(x);
108	return;
109}
110
111/*
112   A file was in a direcroty has been created. This function checks to
113   see if it is one that we are watching.
114*/
115
116int watch_list_find(int wd, const char *file)
117{
118	struct watchList *ptr = NULL;
119
120	ptr = firstDir;
121
122	if (debug_mode)
123		printf("%d: File=%s\n", wd, file);
124	while (ptr != NULL) {
125		if (ptr->wd == wd) {
126			int exact=0;
127			if (strings_list_find(ptr->files, file, &exact) == 0) {
128				char *path = NULL;
129				if (asprintf(&path, "%s/%s", ptr->dir, file) <
130				    0)
131					exitApp("Error allocating memory.");
132
133				selinux_restorecon(path,
134						   r_opts.restorecon_flags);
135				free(path);
136				return 0;
137			}
138			if (debug_mode)
139				strings_list_print(ptr->files);
140
141			/* Not found in this directory */
142			return -1;
143		}
144		ptr = ptr->next;
145	}
146	/* Did not find a directory */
147	return -1;
148}
149
150void watch_list_free(int fd)
151{
152	struct watchList *ptr = NULL;
153	struct watchList *prev = NULL;
154	ptr = firstDir;
155
156	while (ptr != NULL) {
157		inotify_rm_watch(fd, ptr->wd);
158		strings_list_free(ptr->files);
159		free(ptr->dir);
160		prev = ptr;
161		ptr = ptr->next;
162		free(prev);
163	}
164	firstDir = NULL;
165}
166
167/*
168   Inotify watch loop
169*/
170int watch(int fd, const char *watch_file)
171{
172	char buf[BUF_LEN];
173	int len, i = 0;
174	if (firstDir == NULL) return 0;
175
176	len = read(fd, buf, BUF_LEN);
177	if (len < 0) {
178		if (terminate == 0) {
179			syslog(LOG_ERR, "Read error (%s)", strerror(errno));
180			return 0;
181		}
182		syslog(LOG_INFO, "terminated");
183		return -1;
184	} else if (!len)
185		/* BUF_LEN too small? */
186		return -1;
187	while (i < len) {
188		struct inotify_event *event;
189		event = (struct inotify_event *)&buf[i];
190		if (debug_mode)
191			printf("wd=%d mask=%u cookie=%u len=%u\n",
192			       event->wd, event->mask,
193			       event->cookie, event->len);
194		if (event->mask & ~IN_IGNORED) {
195			if (event->wd == master_wd)
196				read_config(fd, watch_file);
197			else {
198				switch (utmpwatcher_handle(fd, event->wd)) {
199				case -1:	/* Message was not for utmpwatcher */
200					if (event->len)
201						watch_list_find(event->wd, event->name);
202					break;
203				case 1:	/* utmp has changed need to reload */
204					read_config(fd, watch_file);
205					break;
206
207				default:	/* No users logged in or out */
208					break;
209				}
210			}
211		}
212
213		i += EVENT_SIZE + event->len;
214	}
215	return 0;
216}
217
218static void process_config(int fd, FILE * cfg)
219{
220	char *line_buf = NULL;
221	size_t len = 0;
222
223	while (getline(&line_buf, &len, cfg) > 0) {
224		char *buffer = line_buf;
225		while (isspace(*buffer))
226			buffer++;
227		if (buffer[0] == '#')
228			continue;
229		int l = strlen(buffer) - 1;
230		if (l <= 0)
231			continue;
232		buffer[l] = 0;
233		if (buffer[0] == '~') {
234			if (run_as_user) {
235				char *ptr=NULL;
236				if (asprintf(&ptr, "%s%s", homedir, &buffer[1]) < 0)
237					exitApp("Error allocating memory.");
238
239				watch_list_add(fd, ptr);
240				free(ptr);
241			} else {
242				utmpwatcher_add(fd, &buffer[1]);
243			}
244		} else {
245			watch_list_add(fd, buffer);
246		}
247	}
248	free(line_buf);
249}
250
251/*
252   Read config file ignoring Comment lines
253   Files specified one per line.  Files with "~" will be expanded to the logged in users
254   homedirs.
255*/
256
257void read_config(int fd, const char *watch_file_path)
258{
259
260	FILE *cfg = NULL;
261	if (debug_mode)
262		printf("Read Config\n");
263
264	watch_list_free(fd);
265
266	cfg = fopen(watch_file_path, "r");
267	if (!cfg){
268		perror(watch_file_path);
269		exitApp("Error reading config file");
270	}
271	process_config(fd, cfg);
272	fclose(cfg);
273
274	inotify_rm_watch(fd, master_wd);
275	master_wd =
276	    inotify_add_watch(fd, watch_file_path, IN_MOVED_FROM | IN_MODIFY);
277	if (master_wd == -1)
278		exitApp("Error watching config file.");
279}
280