• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
28 struct watchList {
29 	struct watchList *next;
30 	int wd;
31 	char *dir;
32 	struct stringsList *files;
33 };
34 struct watchList *firstDir = NULL;
35 
watch_list_isempty(void)36 int watch_list_isempty(void) {
37 	return firstDir == NULL;
38 }
39 
watch_list_add(int fd,const char * path)40 void 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 
106 end:
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 
watch_list_find(int wd,const char * file)116 int 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 
watch_list_free(int fd)150 void 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 */
watch(int fd,const char * watch_file)170 int 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 
process_config(int fd,FILE * cfg)218 static 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 
read_config(int fd,const char * watch_file_path)257 void 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