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