1 /*
2     clsync - file tree sync utility based on inotify/kqueue
3 
4     Copyright (C) 2013-2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
5 
6     This program is free software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "common.h"
20 #include "malloc.h"
21 #include "error.h"
22 #include "indexes.h"
23 #include "sync.h"
24 #include "mon_dtracepipe.h"
25 
26 #define DTRACE_SCRIPT "BEGIN\
27 {\
28     dir = $1;\
29     dirlen = strlen(dir);\
30 }\
31 \
32 syscall::open*:entry\
33 /\
34 arg1 & (O_WRONLY|O_RDWR) &&\
35     substr(copyinstr(arg0),0,dirlen)==dir\
36 /\
37 {\
38     printf("%s\n",copyinstr(arg0));\
39 }\
40 \
41 syscall::mkdir*:entry\
42 /\
43     substr(copyinstr(arg0),0,dirlen)==dir\
44 /\
45 {\
46     printf("%s\n",copyinstr(arg0));\
47 }"
48 
49 struct mondata {
50 	FILE *pipe;
51 };
52 typedef struct mondata mondata_t;
53 
54 #define DTRACEPIPE_INIT_ERROR {\
55 	free(ctx_p->fsmondata);\
56 	ctx_p->fsmondata = NULL;\
57 	return -1;\
58 }
59 
dtracepipe_init(ctx_t * ctx_p)60 int dtracepipe_init(ctx_t *ctx_p) {
61 	char cmd[BUFSIZ];
62 
63 	ctx_p->fsmondata = xcalloc(sizeof(mondata_t), 1);
64 	mondata_t *mondata = ctx_p->fsmondata;
65 
66 	if (snprintf(cmd, "%s -n '%s' '%s'", DTRACE_PATH, DTRACE_SCRIPT, ) >= BUFSIZ) {
67 		errno = EMSGSIZE;
68 		error("Too long cmd.");
69 		DTRACEPIPE_INIT_ERROR;
70 	}
71 
72 	FILE *pipe = popen(cmd, "r");
73 	if (pipe == NULL) {
74 		error("Cannot popen(\""DTRACE_PATH"\", \"r\")");
75 		DTRACEPIPE_INIT_ERROR;
76 	}
77 
78 	if (setvbuf(pipe, NULL, _IONBF, 0)) {
79 		error("Cannot set unbuffered mode for pipe of \""DTRACE_PATH"\" process");
80 		DTRACEPIPE_INIT_ERROR;
81 	}
82 
83 	mondata->pipe = pipe;
84 
85 	return 0;
86 }
87 
88 char  *dtracepipe_wait_line = NULL;
89 size_t dtracepipe_wait_line_siz;
dtracepipe_wait(struct ctx * ctx_p,struct indexes * indexes_p,struct timeval * timeout_p)90 int dtracepipe_wait(struct ctx *ctx_p, struct indexes *indexes_p, struct timeval *timeout_p) {
91 	mondata_t *mondata = ctx_p->fsmondata;
92 	struct timeval timeout_abs, tv_abs;
93 	int dontwait = 0;
94 	struct dtracepipe_event *event_p = &mondata->event;
95 
96 	if (timeout_p->tv_sec == 0 && timeout_p->tv_usec == 0)
97 		dontwait = 1;
98 
99 	if (!dontwait) {
100 		gettimeofday(&tv_abs, NULL);
101 		timeradd(&tv_abs, timeout_p, &timeout_abs);
102 	}
103 
104 	int pipe_fd = fileno(mondata->pipe);
105 
106 	while (42) {
107 		int path_count;
108 
109 		// Checking if there already a recond in mondata
110 		if (*event_p->path) {
111 			debug(2, "we have an event. return 1.");
112 			return 1;
113 		}
114 
115 		// Getting a record
116 		{
117 			debug(3, "select() with timeout %li.%06li secs (dontwait == %u).", timeout_p->tv_sec, timeout_p->tv_usec, dontwait);
118 			fd_set rfds;
119 			FD_ZERO(&rfds);
120 			FD_SET(pipe_fd, &rfds);
121 			int rc = select(pipe_fd+1, &rfds, NULL, NULL, timeout_p);
122 
123 			if (rc == 0 || rc == -1)
124 				return rc;
125 
126 			line_len = getline(&dtracepipe_wait_line, &dtracepipe_wait_line_siz, mondata->pipe);
127 			if (line_len == -1) {
128 				error("Cannot read line from \""DTRACE_PATH"\" pipe [using getline()]");
129 				return -1;
130 			}
131 
132 			if (!dontwait) {
133 				debug(5, "old timeout_p->: tv_sec == %lu; tv_usec == %lu", timeout_p->tv_sec, timeout_p->tv_usec);
134 				gettimeofday(&tv_abs, NULL);
135 				if (timercmp(&timeout_abs, &tv_abs, <))
136 					timersub(&timeout_abs, &tv_abs, timeout_p);
137 				else
138 					memset(timeout_p, 0, sizeof(*timeout_p));
139 				debug(5, "new timeout_p->: tv_sec == %lu; tv_usec == %lu", timeout_p->tv_sec, timeout_p->tv_usec);
140 			}
141 		}
142 
143 		// Parsing the record
144 		path_count = 0;
145 		debug(3, "parsing the event");
146 		while (au_parsed < au_len) {
147 
148 			if (au_fetch_tok(&tok, &au_buf[au_parsed], au_len - au_parsed) == -1)
149 				return -1;
150 			au_parsed += tok.len;
151 
152 			switch (tok.id) {
153 				case AUT_HEADER32:
154 				case AUT_HEADER32_EX:
155 				case AUT_HEADER64:
156 				case AUT_HEADER64_EX: {
157 					event_p->type = tok.tt.hdr32.e_type;
158 					path_count = 0;
159 					break;
160 				}
161 				case AUT_PATH: {
162 					char *ptr;
163 					int dir_wd, dir_iswatched;
164 
165 					ptr = memrchr(tok.tt.path.path, '/', tok.tt.path.len);
166 
167 #ifdef PARANOID
168 					if (ptr == NULL)
169 						critical("relative path received from au_fetch_tok(): \"%s\" (len: %u)", tok.tt.path.path, tok.tt.path.len);
170 #endif
171 
172 					debug(6, "Event on \"%s\".", tok.tt.path.path);
173 					*ptr = 0;
174 					dir_wd = indexes_fpath2wd(indexes_p, tok.tt.path.path);
175 					dir_iswatched = (dir_wd != -1);
176 					debug(7, "Directory is \"%s\". dir_wd == %i; dir_iswatched == %u", tok.tt.path.path, dir_wd, dir_iswatched);
177 					*ptr = '/';
178 
179 					if (dir_iswatched) {
180 						debug(5, "Event on \"%s\" is watched. Pushing. path_count == %u", tok.tt.path.path, path_count);
181 						switch (path_count) {
182 							case 0:
183 								memcpy(event_p->path,    tok.tt.path.path, tok.tt.path.len+1);
184 								break;
185 							case 1:
186 								memcpy(event_p->path_to, tok.tt.path.path, tok.tt.path.len+1);
187 								break;
188 #ifdef PARANOID
189 							default:
190 								warning("To many paths on BSM event: \"%s\" (already count: %u)", tok.tt.path.path, path_count);
191 								break;
192 #endif
193 						}
194 					}
195 					path_count++;
196 					break;
197 				}
198 				default:
199 					continue;
200 			}
201 		}
202 
203 		// Cleanup
204 		debug(4, "clean up");
205 		free(au_buf);
206 		au_buf    = NULL;
207 		au_len    = 0;
208 		au_parsed = 0;
209 	}
210 	return -1;
211 }
dtracepipe_handle(struct ctx * ctx_p,struct indexes * indexes_p)212 int dtracepipe_handle(struct ctx *ctx_p, struct indexes *indexes_p) {
213 	return -1;
214 }
dtracepipe_add_watch_dir(struct ctx * ctx_p,struct indexes * indexes_p,const char * const accpath)215 int dtracepipe_add_watch_dir(struct ctx *ctx_p, struct indexes *indexes_p, const char *const accpath) {
216 	return -1;
217 }
dtracepipe_deinit(ctx_t * ctx_p)218 int dtracepipe_deinit(ctx_t *ctx_p) {
219 	mondata_t *mondata = ctx_p->fsmondata;
220 
221 	free(dtracepipe_wait_line);
222 	free(mondata);
223 
224 	return -1;
225 }
226