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