blob: 691df82482a38b5d66dd667c93bc1a1c0e17347e [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
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
Eric Paris48681bb2011-08-03 11:40:47 -040028struct watchList {
29 struct watchList *next;
30 int wd;
31 char *dir;
32 struct stringsList *files;
33};
34struct watchList *firstDir = NULL;
35
Nicolas Ioossc4a4a1a2014-09-14 23:41:49 +020036int watch_list_isempty(void) {
Eric Paris48681bb2011-08-03 11:40:47 -040037 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;
Richard Hainese0531282016-07-26 09:45:16 +010051 int len;
Eric Paris48681bb2011-08-03 11:40:47 -040052
53 globbuf.gl_offs = 1;
54 if (glob(path,
55 GLOB_TILDE | GLOB_PERIOD,
56 NULL,
57 &globbuf) >= 0) {
Richard Hainese0531282016-07-26 09:45:16 +010058 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);
Eric Paris48681bb2011-08-03 11:40:47 -040068 }
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;
Richard Hainese0531282016-07-26 09:45:16 +0100119
Eric Paris48681bb2011-08-03 11:40:47 -0400120 ptr = firstDir;
Richard Hainese0531282016-07-26 09:45:16 +0100121
Eric Paris48681bb2011-08-03 11:40:47 -0400122 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
Richard Hainese0531282016-07-26 09:45:16 +0100133 selinux_restorecon(path,
134 r_opts.restorecon_flags);
Eric Paris48681bb2011-08-03 11:40:47 -0400135 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 }
Vit Mojzisaa115d02017-01-04 10:59:07 +0100182 syslog(LOG_INFO, "terminated");
Eric Paris48681bb2011-08-03 11:40:47 -0400183 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);
Martin Orr72ea5de2012-03-26 17:31:25 +0200194 if (event->mask & ~IN_IGNORED) {
195 if (event->wd == master_wd)
Eric Paris48681bb2011-08-03 11:40:47 -0400196 read_config(fd, watch_file);
Martin Orr72ea5de2012-03-26 17:31:25 +0200197 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;
Eric Paris48681bb2011-08-03 11:40:47 -0400206
Martin Orr72ea5de2012-03-26 17:31:25 +0200207 default: /* No users logged in or out */
208 break;
209 }
Eric Paris48681bb2011-08-03 11:40:47 -0400210 }
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;
Dan Walsh9961ca62011-08-09 09:58:53 -0400236 if (asprintf(&ptr, "%s%s", homedir, &buffer[1]) < 0)
237 exitApp("Error allocating memory.");
238
Eric Paris48681bb2011-08-03 11:40:47 -0400239 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}