git-svn: trunk@4755
Török Edvin authored on 2009/02/13 01:51:09... | ... |
@@ -1,3 +1,17 @@ |
1 |
+Thu Feb 12 19:19:42 EET 2009 (edwin) |
|
2 |
+------------------------------------ |
|
3 |
+ * clamd/, shared/: merge from clamd-proto branch: handle multiple %v parameters introduce poll_fds that also does the read into a buffer introduce thrmgr_group* commands for keeping track of groups of |
|
4 |
+ commands (multiscan, IDSESSION) introduce 2 queues in thrmgr, multiscan queues get lower |
|
5 |
+ priority. Commands are processed from both queues in a 4:1 ratio |
|
6 |
+ to avoid starvation. unify reply code: conn_reply* unify scanner code into scan_callback that is called from |
|
7 |
+ cli_ftw multiscan doesn't need stat() now if readdir() provides |
|
8 |
+ dt_type redesign clamd main loop: there is now an accept thread, and |
|
9 |
+ a recv()/dispatch thread, +MaxThreads worker (scanner) threads. introduce limiting on commands when worker threads are |
|
10 |
+ contended. introduce IDSESSION, a replacement for the old |
|
11 |
+ SESSION command, that allows asyncronous (scan) commands and scan |
|
12 |
+ replies introduce INSTREAM that allows sending the data to scan on |
|
13 |
+ same connection (vs STREAM that requires another port) introduce zCOMMANDS |
|
14 |
+ |
|
1 | 15 |
Thu Feb 12 19:08:28 EET 2009 (edwin) |
2 | 16 |
------------------------------------ |
3 | 17 |
* libclamav/libclamav.map, libclamav/others.h, |
... | ... |
@@ -99,7 +99,7 @@ int main(int argc, char **argv) |
99 | 99 |
time_t currtime; |
100 | 100 |
const char *dbdir, *cfgfile; |
101 | 101 |
char *pua_cats = NULL, *pt; |
102 |
- int ret, tcpsock = 0, localsock = 0, i; |
|
102 |
+ int ret, tcpsock = 0, localsock = 0, i, min_port, max_port; |
|
103 | 103 |
unsigned int sigs = 0; |
104 | 104 |
int lsockets[2], nlsockets = 0; |
105 | 105 |
unsigned int dboptions = 0; |
... | ... |
@@ -287,6 +287,15 @@ int main(int argc, char **argv) |
287 | 287 |
else |
288 | 288 |
logg("#Log file size limit disabled.\n"); |
289 | 289 |
|
290 |
+ min_port = optget(opts, "StreamMinPort")->numarg; |
|
291 |
+ max_port = optget(opts, "StreamMaxPort")->numarg; |
|
292 |
+ if (min_port < 1024 || min_port > max_port || max_port > 65535) { |
|
293 |
+ logg("!Invalid StreaMinPort/StreamMaxPort: %d, %d\n", min_port, max_port); |
|
294 |
+ logg_close(); |
|
295 |
+ optfree(opts); |
|
296 |
+ return 1; |
|
297 |
+ } |
|
298 |
+ |
|
290 | 299 |
if(!(engine = cl_engine_new())) { |
291 | 300 |
logg("!Can't initialize antivirus engine\n"); |
292 | 301 |
logg_close(); |
... | ... |
@@ -488,7 +497,7 @@ int main(int argc, char **argv) |
488 | 488 |
foreground = 1; |
489 | 489 |
|
490 | 490 |
|
491 |
- ret = acceptloop_th(lsockets, nlsockets, engine, dboptions, opts); |
|
491 |
+ ret = recvloop_th(lsockets, nlsockets, engine, dboptions, opts); |
|
492 | 492 |
|
493 | 493 |
#ifdef C_WINDOWS |
494 | 494 |
if(tcpsock) |
... | ... |
@@ -80,9 +80,6 @@ |
80 | 80 |
#include "session.h" |
81 | 81 |
#include "others.h" |
82 | 82 |
|
83 |
-#define ENV_FILE "CLAM_VIRUSEVENT_FILENAME" |
|
84 |
-#define ENV_VIRUS "CLAM_VIRUSEVENT_VIRUSNAME" |
|
85 |
- |
|
86 | 83 |
#ifdef C_WINDOWS |
87 | 84 |
void virusaction(const char *filename, const char *virname, const struct optstruct *opts) |
88 | 85 |
{ |
... | ... |
@@ -91,14 +88,19 @@ void virusaction(const char *filename, const char *virname, const struct optstru |
91 | 91 |
} |
92 | 92 |
|
93 | 93 |
#else |
94 |
+ |
|
95 |
+#define VE_FILENAME "CLAM_VIRUSEVENT_FILENAME" |
|
96 |
+#define VE_VIRUSNAME "CLAM_VIRUSEVENT_VIRUSNAME" |
|
97 |
+ |
|
94 | 98 |
static pthread_mutex_t virusaction_lock = PTHREAD_MUTEX_INITIALIZER; |
95 | 99 |
|
96 | 100 |
void virusaction(const char *filename, const char *virname, const struct optstruct *opts) |
97 | 101 |
{ |
98 | 102 |
pid_t pid; |
99 | 103 |
const struct optstruct *opt; |
100 |
- char *buffer, *pt, *cmd, *buffer_file, *buffer_vir; |
|
101 |
- size_t j; |
|
104 |
+ char *buffer_file, *buffer_vir, *buffer_cmd; |
|
105 |
+ const char *pt; |
|
106 |
+ size_t i, j, v = 0, len; |
|
102 | 107 |
char *env[4]; |
103 | 108 |
|
104 | 109 |
if(!(opt = optget(opts, "VirusEvent"))->enabled) |
... | ... |
@@ -107,336 +109,487 @@ void virusaction(const char *filename, const char *virname, const struct optstru |
107 | 107 |
env[0] = getenv("PATH"); |
108 | 108 |
j = env[0] ? 1 : 0; |
109 | 109 |
/* Allocate env vars.. to be portable env vars should not be freed */ |
110 |
- buffer_file = (char *) malloc(strlen(ENV_FILE) + strlen(filename) + 2); |
|
110 |
+ buffer_file = (char *) malloc(strlen(VE_FILENAME) + strlen(filename) + 2); |
|
111 | 111 |
if(buffer_file) { |
112 |
- sprintf(buffer_file, "%s=%s", ENV_FILE, filename); |
|
112 |
+ sprintf(buffer_file, "%s=%s", VE_FILENAME, filename); |
|
113 | 113 |
env[j++] = buffer_file; |
114 | 114 |
} |
115 | 115 |
|
116 |
- buffer_vir = (char *) malloc(strlen(ENV_VIRUS) + strlen(virname) + 2); |
|
116 |
+ buffer_vir = (char *) malloc(strlen(VE_VIRUSNAME) + strlen(virname) + 2); |
|
117 | 117 |
if(buffer_vir) { |
118 |
- sprintf(buffer_vir, "%s=%s", ENV_VIRUS, virname); |
|
118 |
+ sprintf(buffer_vir, "%s=%s", VE_VIRUSNAME, virname); |
|
119 | 119 |
env[j++] = buffer_vir; |
120 | 120 |
} |
121 | 121 |
env[j++] = NULL; |
122 | 122 |
|
123 |
- cmd = strdup(opt->strarg); |
|
124 |
- |
|
125 |
- if(cmd && (pt = strstr(cmd, "%v"))) { |
|
126 |
- buffer = (char *) malloc(strlen(cmd) + strlen(virname) + 10); |
|
127 |
- if(buffer) { |
|
128 |
- *pt = 0; pt += 2; |
|
129 |
- strcpy(buffer, cmd); |
|
130 |
- strcat(buffer, virname); |
|
131 |
- strcat(buffer, pt); |
|
132 |
- free(cmd); |
|
133 |
- cmd = strdup(buffer); |
|
134 |
- free(buffer); |
|
135 |
- } |
|
123 |
+ pt = opt->strarg; |
|
124 |
+ while((pt = strstr(pt, "%v"))) { |
|
125 |
+ pt += 2; |
|
126 |
+ v++; |
|
136 | 127 |
} |
137 |
- |
|
138 |
- if(!cmd) { |
|
139 |
- free(buffer_file); |
|
140 |
- free(buffer_vir); |
|
141 |
- return; |
|
128 |
+ len = strlen(opt->strarg); |
|
129 |
+ buffer_cmd = (char *) calloc(len + v * strlen(virname) + 1, sizeof(char)); |
|
130 |
+ if(!buffer_cmd) { |
|
131 |
+ free(buffer_file); |
|
132 |
+ free(buffer_vir); |
|
133 |
+ return; |
|
142 | 134 |
} |
135 |
+ for(i = 0, j = 0; i < len; i++) { |
|
136 |
+ if(i + 1 < len && opt->strarg[i] == '%' && opt->strarg[i + 1] == 'v') { |
|
137 |
+ strcat(buffer_cmd, virname); |
|
138 |
+ j += strlen(virname); |
|
139 |
+ i++; |
|
140 |
+ } else { |
|
141 |
+ buffer_cmd[j++] = opt->strarg[i]; |
|
142 |
+ } |
|
143 |
+ } |
|
144 |
+ |
|
143 | 145 |
pthread_mutex_lock(&virusaction_lock); |
144 | 146 |
/* We can only call async-signal-safe functions after fork(). */ |
145 | 147 |
pid = fork(); |
146 |
- |
|
147 |
- if ( pid == 0 ) { |
|
148 |
- /* child... */ |
|
149 |
- /* WARNING: this is uninterruptable ! */ |
|
150 |
- exit(execle("/bin/sh", "sh", "-c", cmd, NULL, env)); |
|
151 |
- } else if (pid > 0) { |
|
152 |
- pthread_mutex_unlock(&virusaction_lock); |
|
153 |
- /* parent */ |
|
154 |
- waitpid(pid, NULL, 0); |
|
148 |
+ if(!pid) { /* child */ |
|
149 |
+ exit(execle("/bin/sh", "sh", "-c", buffer_cmd, NULL, env)); |
|
150 |
+ } else if(pid > 0) { /* parent */ |
|
151 |
+ pthread_mutex_unlock(&virusaction_lock); |
|
152 |
+ waitpid(pid, NULL, 0); |
|
155 | 153 |
} else { |
156 |
- pthread_mutex_unlock(&virusaction_lock); |
|
157 |
- /* error.. */ |
|
158 |
- logg("!VirusAction: fork failed.\n"); |
|
154 |
+ logg("!VirusEvent: fork failed.\n"); |
|
159 | 155 |
} |
160 |
- free(cmd); |
|
156 |
+ free(buffer_cmd); |
|
161 | 157 |
free(buffer_file); |
162 | 158 |
free(buffer_vir); |
163 | 159 |
} |
164 | 160 |
#endif /* C_WINDOWS */ |
165 | 161 |
|
166 |
-int poll_fds(int *fds, int nfds, int timeout_sec, int check_signals) |
|
162 |
+/* Function: writen |
|
163 |
+ Try hard to write the specified number of bytes |
|
164 |
+*/ |
|
165 |
+int writen(int fd, void *buff, unsigned int count) |
|
167 | 166 |
{ |
168 | 167 |
int retval; |
169 |
- int i; |
|
170 |
-#ifdef HAVE_POLL |
|
171 |
- struct pollfd poll_1[1]; |
|
172 |
- struct pollfd *poll_data = poll_1; |
|
173 |
- |
|
174 |
- if (nfds>1) { |
|
175 |
- poll_data = malloc(nfds*sizeof(*poll_data)); |
|
176 |
- if(!poll_data) { |
|
177 |
- logg("!poll_fds: Can't allocate memory for poll_data\n"); |
|
178 |
- return -1; |
|
179 |
- } |
|
180 |
- } |
|
181 |
- |
|
182 |
- for (i=0; i<nfds; i++) { |
|
183 |
- poll_data[i].fd = fds[i]; |
|
184 |
- poll_data[i].events = POLLIN; |
|
185 |
- poll_data[i].revents = 0; |
|
186 |
- } |
|
187 |
- |
|
188 |
- if (timeout_sec > 0) { |
|
189 |
- timeout_sec *= 1000; |
|
190 |
- } |
|
191 |
- while (1) { |
|
192 |
- retval = poll(poll_data, nfds, timeout_sec); |
|
193 |
- if (retval == -1) { |
|
194 |
- if (errno == EINTR && !check_signals) { |
|
168 |
+ unsigned int todo; |
|
169 |
+ unsigned char *current; |
|
170 |
+ |
|
171 |
+ todo = count; |
|
172 |
+ current = (unsigned char *) buff; |
|
173 |
+ |
|
174 |
+ do { |
|
175 |
+ retval = write(fd, current, todo); |
|
176 |
+ if (retval < 0) { |
|
177 |
+ if (errno == EINTR) { |
|
195 | 178 |
continue; |
196 | 179 |
} |
197 |
- if (nfds>1) |
|
198 |
- free(poll_data); |
|
199 | 180 |
return -1; |
200 | 181 |
} |
201 |
- if (nfds>1) { |
|
202 |
- if (retval>0) { |
|
203 |
- for (i=0; i<nfds; i++) { |
|
204 |
- if (poll_data[i].revents) { |
|
205 |
- retval = i+1; |
|
206 |
- break; |
|
207 |
- } |
|
208 |
- } |
|
209 |
- } |
|
210 |
- free(poll_data); |
|
211 |
- } |
|
212 |
- return retval; |
|
213 |
- } |
|
214 |
- |
|
215 |
-#else |
|
216 |
- fd_set rfds; |
|
217 |
- struct timeval tv; |
|
218 |
- int maxfd = 0; |
|
219 |
- |
|
220 |
- for (i=0; i<nfds; i++) { |
|
221 |
-#ifndef C_WINDOWS |
|
222 |
- if (fds[i] >= DEFAULT_FD_SETSIZE) { |
|
223 |
- return -1; |
|
224 |
- } |
|
225 |
-#endif |
|
226 |
- if (fds[i] > maxfd) |
|
227 |
- maxfd = fds[i]; |
|
228 |
- } |
|
229 |
- |
|
230 |
- while (1) { |
|
231 |
- FD_ZERO(&rfds); |
|
232 |
- for (i=0; i<nfds; i++) |
|
233 |
- FD_SET(fds[i], &rfds); |
|
234 |
- tv.tv_sec = timeout_sec; |
|
235 |
- tv.tv_usec = 0; |
|
182 |
+ todo -= retval; |
|
183 |
+ current += retval; |
|
184 |
+ } while (todo > 0); |
|
185 |
+ |
|
186 |
+ return count; |
|
187 |
+} |
|
236 | 188 |
|
237 |
- retval = select(maxfd+1, &rfds, NULL, NULL, |
|
238 |
- (timeout_sec>0 ? &tv : NULL)); |
|
239 |
- if (retval == -1) { |
|
240 |
- if (errno == EINTR && !check_signals) { |
|
241 |
- continue; |
|
242 |
- } |
|
243 |
- return -1; |
|
244 |
- } |
|
245 |
- if ((nfds>1) && (retval>0)) { |
|
246 |
- for (i=0; i<nfds; i++) { |
|
247 |
- if (FD_ISSET(fds[i],&rfds)) { |
|
248 |
- retval = i+1; |
|
249 |
- break; |
|
250 |
- } |
|
251 |
- } |
|
252 |
- } |
|
253 |
- return retval; |
|
189 |
+static int realloc_polldata(struct fd_data *data) |
|
190 |
+{ |
|
191 |
+#ifdef HAVE_POLL |
|
192 |
+ if (data->poll_data_nfds == data->nfds) |
|
193 |
+ return 0; |
|
194 |
+ if (data->poll_data) |
|
195 |
+ free(data->poll_data); |
|
196 |
+ data->poll_data = malloc(data->nfds*sizeof(*data->poll_data)); |
|
197 |
+ if (!data->poll_data) { |
|
198 |
+ logg("!realloc_polldata: Memory allocation failed for poll_data\n"); |
|
199 |
+ return -1; |
|
254 | 200 |
} |
201 |
+ data->poll_data_nfds = data->nfds; |
|
255 | 202 |
#endif |
256 |
- |
|
257 |
- return -1; |
|
203 |
+ return 0; |
|
258 | 204 |
} |
259 | 205 |
|
260 | 206 |
int poll_fd(int fd, int timeout_sec, int check_signals) |
261 | 207 |
{ |
262 |
- return poll_fds(&fd, 1, timeout_sec, check_signals); |
|
208 |
+ int ret; |
|
209 |
+ struct fd_data fds; |
|
210 |
+ |
|
211 |
+ memset(&fds, 0, sizeof(fds)); |
|
212 |
+ if (fds_add(&fds, fd, 1) == -1) |
|
213 |
+ return -1; |
|
214 |
+ ret = fds_poll_recv(&fds, timeout_sec, check_signals); |
|
215 |
+ fds_free(&fds); |
|
216 |
+ return ret; |
|
217 |
+} |
|
218 |
+ |
|
219 |
+static void cleanup_fds(struct fd_data *data) |
|
220 |
+{ |
|
221 |
+ struct fd_buf *newbuf; |
|
222 |
+ unsigned i,j; |
|
223 |
+ for (i=0,j=0;i < data->nfds; i++) { |
|
224 |
+ if (data->buf[i].fd < 0) { |
|
225 |
+ if (data->buf[i].buffer) |
|
226 |
+ free(data->buf[i].buffer); |
|
227 |
+ continue; |
|
228 |
+ } |
|
229 |
+ if (i != j) |
|
230 |
+ data->buf[j] = data->buf[i]; |
|
231 |
+ j++; |
|
232 |
+ } |
|
233 |
+ for (i = j ; i < data->nfds; i++) |
|
234 |
+ data->buf[i].fd = -1; |
|
235 |
+ data->nfds = j; |
|
236 |
+ /* Shrink buffer */ |
|
237 |
+ newbuf = realloc(data->buf, j*sizeof(*newbuf)); |
|
238 |
+ if (newbuf) |
|
239 |
+ data->buf = newbuf;/* non-fatal if shrink fails */ |
|
263 | 240 |
} |
264 | 241 |
|
265 |
-int is_fd_connected(int fd) |
|
242 |
+static int read_fd_data(struct fd_buf *buf) |
|
266 | 243 |
{ |
267 |
-#ifdef HAVE_POLL |
|
268 |
- struct pollfd poll_data[1]; |
|
269 |
- int count; |
|
244 |
+ ssize_t n; |
|
270 | 245 |
|
271 |
- poll_data[0].fd = fd; |
|
272 |
- poll_data[0].events = POLLIN; |
|
273 |
- poll_data[0].revents = 0; |
|
246 |
+ buf->got_newdata=1; |
|
247 |
+ if (!buf->buffer) /* listen-only socket */ |
|
248 |
+ return 1; |
|
274 | 249 |
|
275 |
- if ((count=poll(poll_data, 1, 0)) == -1) { |
|
276 |
- if (errno == EINTR) { |
|
277 |
- return 1; |
|
278 |
- } |
|
279 |
- return 0; |
|
280 |
- } |
|
281 |
- if (count == 0) { |
|
282 |
- return 1; |
|
283 |
- } |
|
284 |
- if (poll_data[0].revents & POLLHUP) { |
|
285 |
- return 0; |
|
286 |
- } |
|
287 |
- if ((poll_data[0].revents & POLLIN) && (ioctl(fd, FIONREAD, &count) == 0)) { |
|
288 |
- if (count == 0) { |
|
289 |
- return 0; |
|
290 |
- } |
|
291 |
- } |
|
292 |
- return 1; |
|
250 |
+ if (buf->off >= buf->bufsize) |
|
251 |
+ return -1; |
|
293 | 252 |
|
253 |
+ /* Read the pending packet, it may contain more than one command, but |
|
254 |
+ * that is to the cmdparser to handle. |
|
255 |
+ * It will handle 1st command, and then move leftover to beginning of buffer |
|
256 |
+ */ |
|
257 |
+#ifdef HAVE_FD_PASSING |
|
258 |
+ { |
|
259 |
+ struct msghdr msg; |
|
260 |
+ struct cmsghdr *cmsg; |
|
261 |
+ unsigned char buff[CMSG_SPACE(sizeof(int))]; |
|
262 |
+ struct iovec iov[1]; |
|
263 |
+ |
|
264 |
+ if (buf->recvfd != -1) { |
|
265 |
+ close(buf->recvfd); |
|
266 |
+ buf->recvfd = -1; |
|
267 |
+ } |
|
268 |
+ memset(&msg, 0, sizeof(msg)); |
|
269 |
+ iov[0].iov_base = buf->buffer + buf->off; |
|
270 |
+ iov[0].iov_len = buf->bufsize - buf->off; |
|
271 |
+ msg.msg_iov = iov; |
|
272 |
+ msg.msg_iovlen = 1; |
|
273 |
+ msg.msg_control = buff; |
|
274 |
+ msg.msg_controllen = sizeof(buff); |
|
275 |
+ |
|
276 |
+ n = recvmsg(buf->fd, &msg, 0); |
|
277 |
+ if (n < 0) |
|
278 |
+ return -1; |
|
279 |
+ if ((msg.msg_flags & MSG_TRUNC) || (msg.msg_flags & MSG_CTRUNC)) { |
|
280 |
+ logg("!control message truncated"); |
|
281 |
+ return -1; |
|
282 |
+ } |
|
283 |
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; |
|
284 |
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
|
285 |
+ if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) && |
|
286 |
+ cmsg->cmsg_level == SOL_SOCKET && |
|
287 |
+ cmsg->cmsg_type == SCM_RIGHTS) { |
|
288 |
+ if (buf->recvfd != -1) { |
|
289 |
+ logg("^Unclaimed file descriptor received. closing\n"); |
|
290 |
+ close(buf->recvfd); |
|
291 |
+ } |
|
292 |
+ buf->recvfd = *(int *)CMSG_DATA(cmsg); |
|
293 |
+ logg("*RECVMSG: got FD %d\n", buf->recvfd); |
|
294 |
+ } |
|
295 |
+ } |
|
296 |
+ } |
|
294 | 297 |
#else |
295 |
- fd_set rfds; |
|
296 |
- struct timeval tv; |
|
297 |
- char buff[1]; |
|
298 |
+ n = recv(buf->fd, buf->buffer + buf->off, buf->bufsize - buf->off,0); |
|
299 |
+ if (n < 0) |
|
300 |
+ return -1; |
|
301 |
+#endif |
|
302 |
+ buf->off += n; |
|
303 |
+ return n; |
|
304 |
+} |
|
298 | 305 |
|
299 |
-#ifndef C_WINDOWS |
|
300 |
- if (fd >= DEFAULT_FD_SETSIZE) { |
|
301 |
- return 1; |
|
306 |
+static int buf_init(struct fd_buf *buf, int listen_only) |
|
307 |
+{ |
|
308 |
+ buf->off = 0; |
|
309 |
+ buf->got_newdata = 0; |
|
310 |
+ buf->recvfd = -1; |
|
311 |
+ buf->mode = MODE_COMMAND; |
|
312 |
+ buf->id = 0; |
|
313 |
+ buf->dumpfd = -1; |
|
314 |
+ buf->chunksize = 0; |
|
315 |
+ buf->quota = 0; |
|
316 |
+ buf->dumpname = NULL; |
|
317 |
+ buf->group = NULL; |
|
318 |
+ buf->term = '\0'; |
|
319 |
+ if (!listen_only) { |
|
320 |
+ if (!buf->buffer) { |
|
321 |
+ buf->bufsize = PATH_MAX+8; |
|
322 |
+ /* plus extra space for a \0 so we can make sure every command is \0 |
|
323 |
+ * terminated */ |
|
324 |
+ if (!(buf->buffer = malloc(buf->bufsize + 1))) { |
|
325 |
+ logg("!add_fd: Memory allocation failed for command buffer\n"); |
|
326 |
+ return -1; |
|
327 |
+ } |
|
328 |
+ } |
|
329 |
+ } else { |
|
330 |
+ if (buf->buffer) |
|
331 |
+ free(buf->buffer); |
|
332 |
+ buf->bufsize = 0; |
|
333 |
+ buf->buffer = NULL; |
|
302 | 334 |
} |
303 |
-#endif |
|
335 |
+ return 0; |
|
336 |
+} |
|
304 | 337 |
|
305 |
- FD_ZERO(&rfds); |
|
306 |
- FD_SET(fd, &rfds); |
|
307 |
- tv.tv_sec = 0; |
|
308 |
- tv.tv_usec = 0; |
|
309 |
- if (select(fd+1, &rfds, NULL, NULL, &tv) <= 0) { |
|
310 |
- return 1; |
|
338 |
+int fds_add(struct fd_data *data, int fd, int listen_only) |
|
339 |
+{ |
|
340 |
+ struct fd_buf *buf; |
|
341 |
+ unsigned n; |
|
342 |
+ if (fd < 0) { |
|
343 |
+ logg("!add_fd: invalid fd passed to add_fd\n"); |
|
344 |
+ return -1; |
|
311 | 345 |
} |
312 |
- if (FD_ISSET(fd, &rfds)) { |
|
313 |
- if (recv(fd, buff, 1, MSG_PEEK) != 1) { |
|
346 |
+ /* we may already have this fd, if |
|
347 |
+ * the old FD got closed, and the kernel reused the FD */ |
|
348 |
+ for (n = 0; n < data->nfds; n++) |
|
349 |
+ if (data->buf[n].fd == fd) { |
|
350 |
+ /* clear stale data in buffer */ |
|
351 |
+ if (buf_init(&data->buf[n], listen_only) < 0) |
|
352 |
+ return -1; |
|
314 | 353 |
return 0; |
315 | 354 |
} |
355 |
+ |
|
356 |
+ n++; |
|
357 |
+ buf = realloc(data->buf, n*sizeof(*buf)); |
|
358 |
+ if (!buf) { |
|
359 |
+ logg("!add_fd: Memory allocation failed for fd_buf\n"); |
|
360 |
+ return -1; |
|
316 | 361 |
} |
317 |
- return 1; |
|
318 |
-#endif |
|
362 |
+ data->buf = buf; |
|
363 |
+ data->nfds = n; |
|
364 |
+ data->buf[n-1].buffer = NULL; |
|
365 |
+ if (buf_init(&data->buf[n-1], listen_only) < 0) |
|
366 |
+ return -1; |
|
367 |
+ data->buf[n-1].fd = fd; |
|
368 |
+ return 0; |
|
319 | 369 |
} |
320 | 370 |
|
321 |
-/* Function: writen |
|
322 |
- Try hard to write the specified number of bytes |
|
323 |
-*/ |
|
324 |
-int writen(int fd, void *buff, unsigned int count) |
|
371 |
+void fds_remove(struct fd_data *data, int fd) |
|
325 | 372 |
{ |
326 |
- int retval; |
|
327 |
- unsigned int todo; |
|
328 |
- unsigned char *current; |
|
329 |
- |
|
330 |
- todo = count; |
|
331 |
- current = (unsigned char *) buff; |
|
332 |
- |
|
333 |
- do { |
|
334 |
- retval = write(fd, current, todo); |
|
335 |
- if (retval < 0) { |
|
336 |
- if (errno == EINTR) { |
|
337 |
- continue; |
|
373 |
+ size_t i; |
|
374 |
+ pthread_mutex_lock(&data->buf_mutex); |
|
375 |
+ if (data->buf) { |
|
376 |
+ for (i=0;i<data->nfds;i++) { |
|
377 |
+ if (data->buf[i].fd == fd) { |
|
378 |
+ data->buf[i].fd = -1; |
|
379 |
+ break; |
|
338 | 380 |
} |
339 |
- return -1; |
|
340 | 381 |
} |
341 |
- todo -= retval; |
|
342 |
- current += retval; |
|
343 |
- } while (todo > 0); |
|
344 |
- |
|
345 |
- return count; |
|
382 |
+ } |
|
383 |
+ pthread_mutex_unlock(&data->buf_mutex); |
|
346 | 384 |
} |
347 | 385 |
|
348 |
-/* FD Support Submitted by Richard Lyons <frob-clamav*webcentral.com.au> */ |
|
349 |
-/* |
|
350 |
- This procedure does timed clamd command and delimited input processing. |
|
351 |
- It is complex for several reasons: |
|
352 |
- 2) Newline delimited commands are indicated by a command which is prefixed by an 'n' character. |
|
353 |
- This character serves to indicate that the command will contain a newline which will cause |
|
354 |
- command data to be read until the command input buffer is full or a newline is encountered. |
|
355 |
- Once the delimiter is encountered, the data is returned without the prefixing 'n' byte. |
|
356 |
- 3) Legacy clamd clients presented commands which may or may not have been delimited by a newline. |
|
357 |
- If a command happens to be delimted by a newline, then only that command (and its newline) is |
|
358 |
- read and passed back, otherwise, all data read (in a single read) which fits in the specified |
|
359 |
- buffer will be returned. |
|
360 |
-*/ |
|
361 |
-int readsock(int sockfd, char *buf, size_t size, unsigned char delim, int timeout_sec, int force_delim, int read_command) |
|
386 |
+#ifndef C_WINDOWS |
|
387 |
+#define closesocket(s) close(s) |
|
388 |
+#endif |
|
389 |
+#define BUFFSIZE 1024 |
|
390 |
+/* Wait till data is available to be read on any of the fds, |
|
391 |
+ * read available data on all fds, and mark them as appropriate. |
|
392 |
+ * One of the fds should be a pipe, used by the accept thread to wake us. |
|
393 |
+ * timeout is specified in seconds, if check_signals is non-zero, then |
|
394 |
+ * poll_recv_fds() will return upon receipt of a signal, even if no data |
|
395 |
+ * is received on any of the sockets. |
|
396 |
+ * Must be called with buf_mutex held. |
|
397 |
+ */ |
|
398 |
+/* TODO: handle ReadTimeout */ |
|
399 |
+int fds_poll_recv(struct fd_data *data, int timeout, int check_signals) |
|
362 | 400 |
{ |
363 |
- ssize_t n; |
|
364 |
- size_t boff = 0; |
|
365 |
- char *pdelim; |
|
366 |
- time_t starttime, timenow; |
|
367 |
- |
|
368 |
- time(&starttime); |
|
369 |
- while(1) { |
|
370 |
- time(&timenow); |
|
371 |
- switch(poll_fd(sockfd, (timeout_sec && ((timeout_sec-(timenow-starttime)) > 0)) ? timeout_sec-(timenow-starttime) : 0, 0)) { |
|
372 |
- case 0: /* timeout */ |
|
373 |
- return -2; |
|
374 |
- case -1: |
|
375 |
- if(errno == EINTR) |
|
401 |
+ unsigned fdsok = data->nfds; |
|
402 |
+ size_t i; |
|
403 |
+ int retval; |
|
404 |
+ |
|
405 |
+ /* we must have at least one fd, the control fd! */ |
|
406 |
+ if (!data->nfds) |
|
407 |
+ return 0; |
|
408 |
+ for (i=0;i < data->nfds;i++) |
|
409 |
+ data->buf[i].got_newdata = 0; |
|
410 |
+#ifdef HAVE_POLL |
|
411 |
+ /* Use poll() if available, preferred because: |
|
412 |
+ * - can poll any number of FDs |
|
413 |
+ * - can notify of both data available / socket disconnected events |
|
414 |
+ * - when it says POLLIN it is guaranteed that a following recv() won't |
|
415 |
+ * block (select may say that data is available to read, but a following |
|
416 |
+ * recv() may still block according to the manpage |
|
417 |
+ */ |
|
418 |
+ |
|
419 |
+ if (realloc_polldata(data) == -1) |
|
420 |
+ return -1; |
|
421 |
+ if (timeout > 0) { |
|
422 |
+ /* seconds to ms */ |
|
423 |
+ timeout *= 1000; |
|
424 |
+ } |
|
425 |
+ for (i=0;i < data->nfds;i++) { |
|
426 |
+ data->poll_data[i].fd = data->buf[i].fd; |
|
427 |
+ data->poll_data[i].events = POLLIN; |
|
428 |
+ data->poll_data[i].revents = 0; |
|
429 |
+ } |
|
430 |
+ do { |
|
431 |
+ int n = data->nfds; |
|
432 |
+ |
|
433 |
+ pthread_mutex_unlock(&data->buf_mutex); |
|
434 |
+ retval = poll(data->poll_data, n, timeout); |
|
435 |
+ pthread_mutex_lock(&data->buf_mutex); |
|
436 |
+ |
|
437 |
+ if (retval > 0) { |
|
438 |
+ fdsok = 0; |
|
439 |
+ /* nfds may change during poll, but not |
|
440 |
+ * poll_data_nfds */ |
|
441 |
+ for (i=0;i < data->poll_data_nfds; i++) { |
|
442 |
+ short revents; |
|
443 |
+ if (data->buf[i].fd < 0) |
|
376 | 444 |
continue; |
377 |
- return -1; |
|
445 |
+ if (data->buf[i].fd != data->poll_data[i].fd) { |
|
446 |
+ /* should never happen */ |
|
447 |
+ logg("!poll_recv_fds FD mismatch\n"); |
|
448 |
+ continue; |
|
449 |
+ } |
|
450 |
+ revents = data->poll_data[i].revents; |
|
451 |
+ if (revents & (POLLIN|POLLHUP)) { |
|
452 |
+ logg("*POLL: POLLIN|POLLHUP on fd %d\n",data->poll_data[i].fd); |
|
453 |
+ } |
|
454 |
+ if (revents & POLLIN) { |
|
455 |
+ int ret = read_fd_data(&data->buf[i]); |
|
456 |
+ /* Data available to be read */ |
|
457 |
+ if (ret == -1) |
|
458 |
+ revents |= POLLERR; |
|
459 |
+ else if (!ret) |
|
460 |
+ revents = POLLHUP; |
|
461 |
+ } |
|
462 |
+ |
|
463 |
+ if (revents & (POLLHUP | POLLERR | POLLNVAL)) { |
|
464 |
+ if (revents & (POLLHUP| POLLNVAL)) { |
|
465 |
+ /* remote disconnected */ |
|
466 |
+ logg("^poll_recv_fds: Client disconnected (FD %d)\n", |
|
467 |
+ data->poll_data[i].fd); |
|
468 |
+ } else { |
|
469 |
+ /* error on file descriptor */ |
|
470 |
+ logg("!poll_recv_fds: Error condition on fd %d\n", |
|
471 |
+ data->poll_data[i].fd); |
|
472 |
+ } |
|
473 |
+ data->buf[i].got_newdata = -1; |
|
474 |
+ } else { |
|
475 |
+ fdsok++; |
|
476 |
+ } |
|
477 |
+ } |
|
378 | 478 |
} |
379 |
- break; |
|
380 |
- } |
|
381 |
- n = recv(sockfd, buf, size, MSG_PEEK); |
|
382 |
- if(n < 0) |
|
383 |
- return -1; |
|
384 |
- if(read_command) { |
|
385 |
- if((n >= 1) && (buf[0] == 'n')) { /* Newline delimited command */ |
|
386 |
- force_delim = 1; |
|
387 |
- delim = '\n'; |
|
479 |
+ } while (retval == -1 && !check_signals && errno == EINTR); |
|
480 |
+#else |
|
481 |
+ fd_set rfds; |
|
482 |
+ struct timeval tv; |
|
483 |
+ int maxfd = -1; |
|
484 |
+ |
|
485 |
+ for (i=0;i < data->nfds; i++) { |
|
486 |
+ int fd = data->buf[i].fd; |
|
487 |
+ if (fd >= FD_SETSIZE) { |
|
488 |
+ logg ("!poll_recv_fds: file descriptor is not valid for FD_SET\n"); |
|
489 |
+ return -1; |
|
388 | 490 |
} |
491 |
+ |
|
492 |
+ maxfd = MAX(maxfd, fd); |
|
389 | 493 |
} |
390 |
- while(boff < size) { |
|
391 |
- if(force_delim) { |
|
392 |
- pdelim = memchr(buf, delim, n+boff); |
|
393 |
- if(pdelim) { |
|
394 |
- n = recv(sockfd, buf+boff, pdelim-buf+1-boff, 0); |
|
395 |
- break; |
|
396 |
- } else { |
|
397 |
- n = recv(sockfd, buf+boff, n, 0); |
|
398 |
- if(n < 0) |
|
399 |
- return -1; |
|
400 |
- if((boff+n) == size) |
|
401 |
- break; |
|
402 |
- boff += n; |
|
494 |
+ |
|
495 |
+ do { |
|
496 |
+ FD_ZERO(&rfds); |
|
497 |
+ for(i=0;i < data->nfds;i++) { |
|
498 |
+ int fd = data->buf[i].fd; |
|
499 |
+ if (fd >= 0) |
|
500 |
+ FD_SET(fd, &rfds); |
|
501 |
+ } |
|
502 |
+ tv.tv_sec = timeout; |
|
503 |
+ tv.tv_usec = 0; |
|
504 |
+ |
|
505 |
+ pthread_mutex_unlock(&data->buf_mutex); |
|
506 |
+ retval = select(maxfd+1, &rfds, NULL, NULL, timeout > 0 ? &tv : NULL); |
|
507 |
+ pthread_mutex_lock(&data->buf_mutex); |
|
508 |
+ if (retval > 0) { |
|
509 |
+ fdsok = data->nfds; |
|
510 |
+ for (i=0; i < data->nfds; i++) { |
|
511 |
+ if (data->buf[i].fd < 0) { |
|
512 |
+ fdsok--; |
|
513 |
+ continue; |
|
514 |
+ } |
|
515 |
+ if (FD_ISSET(data->buf[i].fd, &rfds)) { |
|
516 |
+ int ret = read_fd_data(&data->buf[i]); |
|
517 |
+ if (ret == -1 || !ret) { |
|
518 |
+ if (ret == -1) |
|
519 |
+ logg("!poll_recv_fds: Error condition on fd %d\n", |
|
520 |
+ data->buf[i].fd); |
|
521 |
+ else |
|
522 |
+ logg("^poll_recv_fds: Client disconnected\n"); |
|
523 |
+ data->buf[i].got_newdata = -1; |
|
524 |
+ } |
|
525 |
+ } |
|
403 | 526 |
} |
404 |
- } else { |
|
405 |
- pdelim = memchr(buf, delim, n+boff); |
|
406 |
- if(pdelim) |
|
407 |
- n = recv(sockfd, buf+boff, pdelim-buf+1-boff, 0); |
|
408 |
- else |
|
409 |
- n = recv(sockfd, buf+boff, size-boff, 0); |
|
410 |
- break; |
|
411 | 527 |
} |
412 |
- while(1) { |
|
413 |
- time(&timenow); |
|
414 |
- switch(poll_fd(sockfd, ((timeout_sec-(timenow-starttime)) > 0) ? timeout_sec-(timenow-starttime) : 0, 0)) { |
|
415 |
- case 0: /* timeout */ |
|
416 |
- return -2; |
|
417 |
- case -1: |
|
418 |
- if(errno == EINTR) |
|
419 |
- continue; |
|
420 |
- return -1; |
|
528 |
+ if (retval < 0 && errno == EBADF) { |
|
529 |
+ /* unlike poll(), select() won't tell us which FD is bad, so |
|
530 |
+ * we have to check them one by one. */ |
|
531 |
+ tv.tv_sec = 0; |
|
532 |
+ tv.tv_usec = 0; |
|
533 |
+ /* with tv == 0 it doesn't check for EBADF */ |
|
534 |
+ FD_ZERO(&rfds); |
|
535 |
+ for (i=0; i< data->nfds; i++) { |
|
536 |
+ if (data->buf[i].fd == -1) |
|
537 |
+ continue; |
|
538 |
+ FD_SET(data->buf[i].fd, &rfds); |
|
539 |
+ do { |
|
540 |
+ retval = select(data->buf[i].fd+1, &rfds, NULL, NULL, &tv); |
|
541 |
+ } while (retval == -1 && errno == EINTR); |
|
542 |
+ if (retval == -1) { |
|
543 |
+ data->buf[i].fd = -1; |
|
544 |
+ } else { |
|
545 |
+ FD_CLR(data->buf[i].fd, &rfds); |
|
546 |
+ } |
|
421 | 547 |
} |
422 |
- break; |
|
548 |
+ retval = -1; |
|
549 |
+ errno = EINTR; |
|
550 |
+ continue; |
|
423 | 551 |
} |
424 |
- n = recv(sockfd, buf+boff, size-boff, MSG_PEEK); |
|
425 |
- if(n < 0) |
|
426 |
- return -1; |
|
427 |
- if(n == 0) |
|
428 |
- break; |
|
552 |
+ } while (retval == -1 && !check_signals && errno == EINTR); |
|
553 |
+#endif |
|
554 |
+ |
|
555 |
+ if (retval == -1 && errno != EINTR) { |
|
556 |
+ char buff[BUFFSIZE + 1]; |
|
557 |
+#ifdef HAVE_STRERROR_R |
|
558 |
+ strerror_r(errno, buff, BUFFSIZE); |
|
559 |
+#else |
|
560 |
+ buff[0] = '\0'; |
|
561 |
+#endif |
|
562 |
+#ifdef HAVE_POLL |
|
563 |
+ logg("!poll_recv_fds: poll failed: %s\n", buff); |
|
564 |
+#else |
|
565 |
+ logg("!poll_recv_fds: select failed: %s\n", buff); |
|
566 |
+#endif |
|
429 | 567 |
} |
430 |
- if(n < 0) |
|
431 |
- return -1; |
|
432 |
- n += boff; |
|
433 |
- if(read_command) { |
|
434 |
- if((n >= 1) && (buf[0] == 'n')) { /* Need to strip leading 'n' from command to attain standard command */ |
|
435 |
- --n; |
|
436 |
- memcpy(buf, buf+1, n); |
|
437 |
- buf[n] = '\0'; |
|
568 |
+ |
|
569 |
+ /* Remove closed / error fds */ |
|
570 |
+ if (fdsok != data->poll_data_nfds) |
|
571 |
+ cleanup_fds(data); |
|
572 |
+ return retval; |
|
573 |
+} |
|
574 |
+ |
|
575 |
+void fds_free(struct fd_data *data) |
|
576 |
+{ |
|
577 |
+ unsigned i; |
|
578 |
+ pthread_mutex_lock(&data->buf_mutex); |
|
579 |
+ for (i=0;i < data->nfds;i++) { |
|
580 |
+ if (data->buf[i].buffer) { |
|
581 |
+ free(data->buf[i].buffer); |
|
438 | 582 |
} |
439 |
- return !strncmp(buf, "FD", 2) ? -1 : n; /* an explicit FD command is invalid */ |
|
440 | 583 |
} |
441 |
- return n; |
|
584 |
+ if (data->buf) |
|
585 |
+ free(data->buf); |
|
586 |
+#ifdef HAVE_POLL |
|
587 |
+ if (data->poll_data) |
|
588 |
+ free(data->poll_data); |
|
589 |
+#endif |
|
590 |
+ data->buf = NULL; |
|
591 |
+ data->nfds = 0; |
|
592 |
+ pthread_mutex_unlock(&data->buf_mutex); |
|
442 | 593 |
} |
... | ... |
@@ -25,13 +25,51 @@ |
25 | 25 |
|
26 | 26 |
#include <stdlib.h> |
27 | 27 |
#include "shared/optparser.h" |
28 |
+#include "thrmgr.h" |
|
29 |
+#include "cltypes.h" |
|
30 |
+ |
|
31 |
+enum mode { |
|
32 |
+ MODE_COMMAND, |
|
33 |
+ MODE_STREAM, |
|
34 |
+ MODE_WAITREPLY, |
|
35 |
+ MODE_WAITANCILL |
|
36 |
+}; |
|
37 |
+ |
|
38 |
+struct fd_buf { |
|
39 |
+ unsigned char *buffer; |
|
40 |
+ size_t bufsize; |
|
41 |
+ size_t off; |
|
42 |
+ int fd; |
|
43 |
+ char term; |
|
44 |
+ int got_newdata; /* 0: no, 1: yes, -1: error */ |
|
45 |
+ int recvfd; |
|
46 |
+ /* TODO: these fields don't belong here, there are identical fields in conn |
|
47 |
+ * too that don't belong there either. */ |
|
48 |
+ enum mode mode; |
|
49 |
+ int id; |
|
50 |
+ int dumpfd; |
|
51 |
+ uint32_t chunksize; |
|
52 |
+ long quota; |
|
53 |
+ char *dumpname; |
|
54 |
+ jobgroup_t *group; |
|
55 |
+}; |
|
56 |
+ |
|
57 |
+struct fd_data { |
|
58 |
+ pthread_mutex_t buf_mutex; /* protects buf and nfds */ |
|
59 |
+ struct fd_buf *buf; |
|
60 |
+ size_t nfds; |
|
61 |
+#ifdef HAVE_POLL |
|
62 |
+ struct pollfd *poll_data; |
|
63 |
+ size_t poll_data_nfds; |
|
64 |
+#endif |
|
65 |
+}; |
|
28 | 66 |
|
29 |
-int poll_fds(int *fds, int nfds, int timeout_sec, int check_signals); |
|
30 | 67 |
int poll_fd(int fd, int timeout_sec, int check_signals); |
31 |
-int is_fd_connected(int fd); |
|
32 | 68 |
void virusaction(const char *filename, const char *virname, const struct optstruct *opts); |
33 | 69 |
int writen(int fd, void *buff, unsigned int count); |
34 |
- |
|
35 |
-int readsock(int sockfd, char *buf, size_t size, unsigned char delim, int timeout_sec, int force_delim, int read_command); |
|
70 |
+int fds_add(struct fd_data *data, int fd, int listen_only); |
|
71 |
+void fds_remove(struct fd_data *data, int fd); |
|
72 |
+int fds_poll_recv(struct fd_data *data, int timeout, int check_signals); |
|
73 |
+void fds_free(struct fd_data *data); |
|
36 | 74 |
|
37 | 75 |
#endif |
... | ... |
@@ -65,444 +65,252 @@ |
65 | 65 |
#include "shared.h" |
66 | 66 |
#include "network.h" |
67 | 67 |
#include "thrmgr.h" |
68 |
+#include "server.h" |
|
68 | 69 |
|
69 | 70 |
#ifdef C_LINUX |
70 | 71 |
dev_t procdev; /* /proc device */ |
71 | 72 |
#endif |
72 | 73 |
|
73 |
-extern int progexit; |
|
74 |
- |
|
75 | 74 |
#ifndef C_WINDOWS |
76 | 75 |
#define closesocket(s) close(s) |
77 | 76 |
#endif |
78 | 77 |
|
79 |
-struct multi_tag { |
|
80 |
- int sd; |
|
81 |
- unsigned int options; |
|
82 |
- const struct optstruct *opts; |
|
83 |
- char *fname; |
|
84 |
- const struct cl_engine *engine; |
|
85 |
-}; |
|
78 |
+extern int progexit; |
|
79 |
+extern time_t reloaded_time; |
|
80 |
+extern pthread_mutex_t reload_mutex; |
|
86 | 81 |
|
87 |
-static int checksymlink(const char *path) |
|
82 |
+#define BUFFSIZE 1024 |
|
83 |
+int scan_callback(struct stat *sb, char *filename, const char *msg, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) |
|
88 | 84 |
{ |
89 |
- struct stat statbuf; |
|
90 |
- |
|
91 |
- if(stat(path, &statbuf) == -1) |
|
92 |
- return -1; |
|
93 |
- |
|
94 |
- if(S_ISDIR(statbuf.st_mode)) |
|
95 |
- return 1; |
|
96 |
- |
|
97 |
- if(S_ISREG(statbuf.st_mode)) |
|
98 |
- return 2; |
|
99 |
- |
|
100 |
- return 0; |
|
101 |
-} |
|
85 |
+ struct scan_cb_data *scandata = data->data; |
|
86 |
+ const char *virname; |
|
87 |
+ int ret; |
|
88 |
+ int type = scandata->type; |
|
89 |
+ const struct optstruct *opt; |
|
90 |
+ |
|
91 |
+ if (thrmgr_group_need_terminate(scandata->conn->group)) { |
|
92 |
+ logg("^Client disconnected while scanjob was active\n"); |
|
93 |
+ if (reason == visit_file) |
|
94 |
+ free(filename); |
|
95 |
+ return CL_BREAK; |
|
96 |
+ } |
|
97 |
+ scandata->total++; |
|
98 |
+ switch (reason) { |
|
99 |
+ case error_mem: |
|
100 |
+ if (msg) |
|
101 |
+ logg("!Memory allocation failed during cli_ftw() on %s\n", |
|
102 |
+ msg); |
|
103 |
+ else |
|
104 |
+ logg("!Memory allocation failed during cli_ftw()\n"); |
|
105 |
+ scandata->errors++; |
|
106 |
+ return CL_EMEM; |
|
107 |
+ case error_stat: |
|
108 |
+ if (msg == scandata->toplevel_path) |
|
109 |
+ conn_reply_errno(scandata->conn, msg, "lstat() failed:"); |
|
110 |
+ logg("^lstat() failed on: %s\n", msg); |
|
111 |
+ scandata->errors++; |
|
112 |
+ return CL_SUCCESS; |
|
113 |
+ case warning_skipped_dir: |
|
114 |
+ logg("^Directory recursion limit reached, skipping %s\n", |
|
115 |
+ msg); |
|
116 |
+ return CL_SUCCESS; |
|
117 |
+ case warning_skipped_link: |
|
118 |
+ logg("*Skipping symlink: %s\n", msg); |
|
119 |
+ return CL_SUCCESS; |
|
120 |
+ case warning_skipped_special: |
|
121 |
+ if (msg == scandata->toplevel_path) |
|
122 |
+ conn_reply(scandata->conn, msg, "Not supported file type", "ERROR"); |
|
123 |
+ logg("*Not supported file type: %s\n", msg); |
|
124 |
+ return CL_SUCCESS; |
|
125 |
+ case visit_directory_toplev: |
|
126 |
+ return CL_SUCCESS; |
|
127 |
+ case visit_file: |
|
128 |
+ break; |
|
129 |
+ } |
|
102 | 130 |
|
103 |
-static int dirscan(const char *dirname, const char **virname, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, int odesc, unsigned int *reclev, unsigned int type, threadpool_t *multi_pool) |
|
104 |
-{ |
|
105 |
- DIR *dd; |
|
106 |
- struct dirent *dent; |
|
107 |
-#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2) |
|
108 |
- union { |
|
109 |
- struct dirent d; |
|
110 |
- char b[offsetof(struct dirent, d_name) + NAME_MAX + 1]; |
|
111 |
- } result; |
|
131 |
+ /* check whether the file is excluded */ |
|
132 |
+#ifdef C_LINUX |
|
133 |
+ if(procdev && sb && (sb->st_dev == procdev)) { |
|
134 |
+ free(filename); |
|
135 |
+ return CL_SUCCESS; |
|
136 |
+ } |
|
112 | 137 |
#endif |
113 |
- struct stat statbuf; |
|
114 |
- char *fname; |
|
115 |
- int ret = 0, scanret = 0; |
|
116 |
- unsigned int maxdirrec = 0; |
|
117 |
- struct multi_tag *scandata; |
|
118 |
- const struct optstruct *opt; |
|
119 |
- |
|
120 |
- |
|
121 |
- if((opt = optget(opts, "ExcludePath"))->enabled) { |
|
122 |
- while(opt) { |
|
123 |
- if(match_regex(dirname, opt->strarg) == 1) { |
|
124 |
- mdprintf(odesc, "%s: Excluded\n", dirname); |
|
125 |
- return 0; |
|
126 |
- } |
|
127 |
- opt = (struct optstruct *) opt->nextarg; |
|
138 |
+ if((opt = optget(scandata->opts, "ExcludePath"))->enabled) { |
|
139 |
+ /* TODO: perhaps multiscan should skip this check? |
|
140 |
+ * This should work unless the user is doing something stupid like |
|
141 |
+ * MULTISCAN / */ |
|
142 |
+ if(match_regex(filename, opt->strarg) == 1) { |
|
143 |
+ if (type != TYPE_MULTISCAN) |
|
144 |
+ conn_reply_single(scandata->conn, filename, "Excluded"); |
|
145 |
+ free(filename); |
|
146 |
+ return CL_SUCCESS; |
|
128 | 147 |
} |
129 | 148 |
} |
130 | 149 |
|
131 |
- maxdirrec = optget(opts, "MaxDirectoryRecursion")->numarg; |
|
132 |
- if(maxdirrec) { |
|
133 |
- if(*reclev > maxdirrec) { |
|
134 |
- logg("*Directory recursion limit exceeded at %s\n", dirname); |
|
135 |
- return 0; |
|
136 |
- } |
|
137 |
- (*reclev)++; |
|
150 |
+ if(sb && sb->st_size == 0) { /* empty file */ |
|
151 |
+ if (msg == scandata->toplevel_path) |
|
152 |
+ conn_reply_single(scandata->conn, filename, "Empty file"); |
|
153 |
+ free(filename); |
|
154 |
+ return CL_SUCCESS; |
|
138 | 155 |
} |
139 | 156 |
|
140 |
- if((dd = opendir(dirname)) != NULL) { |
|
141 |
-#ifdef HAVE_READDIR_R_3 |
|
142 |
- while(!readdir_r(dd, &result.d, &dent) && dent) { |
|
143 |
-#elif defined(HAVE_READDIR_R_2) |
|
144 |
- while((dent = (struct dirent *) readdir_r(dd, &result.d))) { |
|
145 |
-#else |
|
146 |
- while((dent = readdir(dd))) { |
|
147 |
-#endif |
|
148 |
- if (!is_fd_connected(odesc)) { |
|
149 |
- logg("Client disconnected\n"); |
|
150 |
- closedir(dd); |
|
151 |
- return 1; |
|
152 |
- } |
|
153 |
- |
|
154 |
- if(progexit) { |
|
155 |
- closedir(dd); |
|
156 |
- return 1; |
|
157 |
- } |
|
158 |
- |
|
159 |
-#if (!defined(C_INTERIX)) && (!defined(C_WINDOWS)) |
|
160 |
- if(dent->d_ino) |
|
161 |
-#endif |
|
162 |
- { |
|
163 |
- if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) { |
|
164 |
- /* build the full name */ |
|
165 |
- fname = (char *) malloc(strlen(dirname) + strlen(dent->d_name) + 2); |
|
166 |
- if(!fname) { |
|
167 |
- logg("!Can't allocate memory for fname\n"); |
|
168 |
- closedir(dd); |
|
169 |
- return -2; |
|
170 |
- } |
|
171 |
- sprintf(fname, "%s/%s", dirname, dent->d_name); |
|
172 |
- |
|
173 |
- /* stat the file */ |
|
174 |
- if(lstat(fname, &statbuf) != -1) { |
|
175 |
- if((S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) || (S_ISLNK(statbuf.st_mode) && (checksymlink(fname) == 1) && optget(opts, "FollowDirectorySymlinks")->enabled)) { |
|
176 |
- if(dirscan(fname, virname, scanned, engine, options, opts, odesc, reclev, type, multi_pool) == 1) { |
|
177 |
- free(fname); |
|
178 |
- closedir(dd); |
|
179 |
- return 1; |
|
180 |
- } |
|
181 |
- free(fname); |
|
182 |
- } else { |
|
183 |
- if(S_ISREG(statbuf.st_mode) || (S_ISLNK(statbuf.st_mode) && (checksymlink(fname) == 2) && optget(opts, "FollowFileSymlinks")->enabled)) { |
|
184 |
- |
|
185 |
-#ifdef C_LINUX |
|
186 |
- if(procdev && (statbuf.st_dev == procdev)) |
|
187 |
- free(fname); |
|
188 |
- else |
|
189 |
-#endif |
|
190 |
- { |
|
191 |
- if(type == TYPE_MULTISCAN) { |
|
192 |
- |
|
193 |
- scandata = (struct multi_tag *) malloc(sizeof(struct multi_tag)); |
|
194 |
- if(!scandata) { |
|
195 |
- logg("!Can't allocate memory for scandata\n"); |
|
196 |
- free(fname); |
|
197 |
- closedir(dd); |
|
198 |
- return -2; |
|
199 |
- } |
|
200 |
- scandata->sd = odesc; |
|
201 |
- scandata->options = options; |
|
202 |
- scandata->opts = opts; |
|
203 |
- scandata->fname = fname; |
|
204 |
- scandata->engine = engine; |
|
205 |
- if(!thrmgr_dispatch(multi_pool, scandata)) { |
|
206 |
- logg("!thread dispatch failed for multi_pool (file %s)\n", fname); |
|
207 |
- mdprintf(odesc, "ERROR: Can't scan file %s\n", fname); |
|
208 |
- free(fname); |
|
209 |
- free(scandata); |
|
210 |
- closedir(dd); |
|
211 |
- return 1; |
|
212 |
- } |
|
213 |
- |
|
214 |
- pthread_mutex_lock(&multi_pool->pool_mutex); |
|
215 |
- while(!multi_pool->thr_idle) /* non-critical */ { |
|
216 |
- pthread_cond_wait(&multi_pool->idle_cond, &multi_pool->pool_mutex); |
|
217 |
- } |
|
218 |
- pthread_mutex_unlock(&multi_pool->pool_mutex); |
|
219 |
- |
|
220 |
- } else { /* CONTSCAN, SCAN */ |
|
221 |
- thrmgr_setactivetask(fname, NULL); |
|
222 |
- scanret = cl_scanfile(fname, virname, scanned, engine, options); |
|
223 |
- thrmgr_setactivetask(NULL, NULL); |
|
224 |
- |
|
225 |
- if(scanret == CL_VIRUS) { |
|
226 |
- |
|
227 |
- mdprintf(odesc, "%s: %s FOUND\n", fname, *virname); |
|
228 |
- logg("~%s: %s FOUND\n", fname, *virname); |
|
229 |
- virusaction(fname, *virname, opts); |
|
230 |
- if(type == TYPE_SCAN) { |
|
231 |
- closedir(dd); |
|
232 |
- free(fname); |
|
233 |
- return 1; |
|
234 |
- } else /* CONTSCAN */ |
|
235 |
- ret = 2; |
|
236 |
- |
|
237 |
- } else if(scanret != CL_CLEAN) { |
|
238 |
- mdprintf(odesc, "%s: %s ERROR\n", fname, cl_strerror(scanret)); |
|
239 |
- logg("~%s: %s ERROR\n", fname, cl_strerror(scanret)); |
|
240 |
- if(scanret == CL_EMEM) { |
|
241 |
- closedir(dd); |
|
242 |
- free(fname); |
|
243 |
- return -2; |
|
244 |
- } |
|
245 |
- |
|
246 |
- } else if(logok) { |
|
247 |
- logg("~%s: OK\n", fname); |
|
248 |
- } |
|
249 |
- free(fname); |
|
250 |
- } |
|
251 |
- } |
|
252 |
- } else { |
|
253 |
- free(fname); |
|
254 |
- } |
|
255 |
- } |
|
256 |
- } else { |
|
257 |
- logg("^lstat failed on %s: %s\n", fname, strerror(errno)); |
|
258 |
- free(fname); |
|
259 |
- } |
|
157 |
+ if (type == TYPE_MULTISCAN) { |
|
158 |
+ client_conn_t *client_conn = (client_conn_t *) calloc(1, sizeof(struct client_conn_tag)); |
|
159 |
+ if(client_conn) { |
|
160 |
+ client_conn->scanfd = -1; |
|
161 |
+ client_conn->sd = scandata->odesc; |
|
162 |
+ client_conn->filename = filename; |
|
163 |
+ client_conn->cmdtype = COMMAND_MULTISCANFILE; |
|
164 |
+ client_conn->term = scandata->conn->term; |
|
165 |
+ client_conn->options = scandata->options; |
|
166 |
+ client_conn->opts = scandata->opts; |
|
167 |
+ client_conn->group = scandata->group; |
|
168 |
+ if(cl_engine_addref(scandata->engine)) { |
|
169 |
+ logg("!cl_engine_addref() failed\n"); |
|
170 |
+ free(filename); |
|
171 |
+ return CL_EMEM; |
|
172 |
+ } else { |
|
173 |
+ client_conn->engine = scandata->engine; |
|
174 |
+ pthread_mutex_lock(&reload_mutex); |
|
175 |
+ client_conn->engine_timestamp = reloaded_time; |
|
176 |
+ pthread_mutex_unlock(&reload_mutex); |
|
177 |
+ if(!thrmgr_group_dispatch(scandata->thr_pool, scandata->group, client_conn)) { |
|
178 |
+ logg("!thread dispatch failed\n"); |
|
179 |
+ free(filename); |
|
180 |
+ return CL_EMEM; |
|
260 | 181 |
} |
261 | 182 |
} |
183 |
+ } else { |
|
184 |
+ logg("!Can't allocate memory for client_conn\n"); |
|
185 |
+ scandata->errors++; |
|
186 |
+ free(filename); |
|
187 |
+ return CL_EMEM; |
|
262 | 188 |
} |
263 |
- closedir(dd); |
|
264 |
- } else { |
|
265 |
- return -1; |
|
189 |
+ return CL_SUCCESS; |
|
266 | 190 |
} |
267 | 191 |
|
268 |
- (*reclev)--; |
|
269 |
- return ret; |
|
270 |
-} |
|
271 |
- |
|
272 |
-static void multiscanfile(void *arg) |
|
273 |
-{ |
|
274 |
- struct multi_tag *tag = (struct multi_tag *) arg; |
|
275 |
- const char *virname; |
|
276 |
-#ifndef C_WINDOWS |
|
277 |
- sigset_t sigset; |
|
278 |
-#endif |
|
279 |
- int ret; |
|
280 |
- |
|
281 |
- |
|
282 |
-#ifndef C_WINDOWS |
|
283 |
- /* ignore all signals */ |
|
284 |
- sigfillset(&sigset); |
|
285 |
- pthread_sigmask(SIG_SETMASK, &sigset, NULL); |
|
286 |
-#endif |
|
287 |
- |
|
288 |
- thrmgr_setactivetask(tag->fname, "MULTISCANFILE"); |
|
289 |
- ret = cl_scanfile(tag->fname, &virname, NULL, tag->engine, tag->options); |
|
290 |
- thrmgr_setactivetask(NULL, NULL); |
|
291 |
- |
|
292 |
- if(ret == CL_VIRUS) { |
|
293 |
- mdprintf(tag->sd, "%s: %s FOUND\n", tag->fname, virname); |
|
294 |
- logg("~%s: %s FOUND\n", tag->fname, virname); |
|
295 |
- virusaction(tag->fname, virname, tag->opts); |
|
296 |
- } else if(ret != CL_CLEAN) { |
|
297 |
- mdprintf(tag->sd, "%s: %s ERROR\n", tag->fname, cl_strerror(ret)); |
|
298 |
- logg("~%s: %s ERROR\n", tag->fname, cl_strerror(ret)); |
|
299 |
- } else if(logok) { |
|
300 |
- logg("~%s: OK\n", tag->fname); |
|
192 |
+ if (access(filename, R_OK)) { |
|
193 |
+ conn_reply(scandata->conn, filename, "Access denied.", "ERROR"); |
|
194 |
+ logg("*Access denied: %s\n", filename); |
|
195 |
+ scandata->errors++; |
|
196 |
+ free(filename); |
|
197 |
+ return CL_SUCCESS; |
|
301 | 198 |
} |
302 | 199 |
|
303 |
- free(tag->fname); |
|
304 |
- free(tag); |
|
305 |
- return; |
|
306 |
-} |
|
307 |
- |
|
308 |
-int scan(const char *filename, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, int odesc, unsigned int type) |
|
309 |
-{ |
|
310 |
- struct stat sb; |
|
311 |
- int ret = 0; |
|
312 |
- unsigned int reclev = 0; |
|
313 |
- const char *virname; |
|
314 |
- const struct optstruct *opt; |
|
315 |
- threadpool_t *multi_pool = NULL; |
|
316 |
- |
|
200 |
+ thrmgr_setactivetask(filename, |
|
201 |
+ type == TYPE_MULTISCAN ? "MULTISCANFILE" : NULL); |
|
202 |
+ ret = cl_scanfile(filename, &virname, &scandata->scanned, scandata->engine, scandata->options); |
|
203 |
+ thrmgr_setactivetask(NULL, NULL); |
|
317 | 204 |
|
318 |
- /* stat file */ |
|
319 |
- if(lstat(filename, &sb) == -1) { |
|
320 |
- mdprintf(odesc, "%s: lstat() failed. ERROR\n", filename); |
|
321 |
- return -1; |
|
205 |
+ if (ret == CL_VIRUS) { |
|
206 |
+ scandata->infected++; |
|
207 |
+ conn_reply(scandata->conn, filename, virname, "FOUND"); |
|
208 |
+ logg("~%s: %s FOUND\n", filename, virname); |
|
209 |
+ virusaction(filename, virname, scandata->opts); |
|
210 |
+ } else if (ret != CL_CLEAN) { |
|
211 |
+ scandata->errors++; |
|
212 |
+ conn_reply(scandata->conn, filename, cl_strerror(ret), "ERROR"); |
|
213 |
+ logg("~%s: %s ERROR\n", filename, cl_strerror(ret)); |
|
214 |
+ } else if (logok) { |
|
215 |
+ logg("~%s: OK\n", filename); |
|
322 | 216 |
} |
323 | 217 |
|
324 |
- /* check permissions */ |
|
325 |
- if(access(filename, R_OK)) { |
|
326 |
- mdprintf(odesc, "%s: Access denied. ERROR\n", filename); |
|
327 |
- return -1; |
|
328 |
- } |
|
218 |
+ free(filename); |
|
219 |
+ if(ret == CL_EMEM) /* stop scanning */ |
|
220 |
+ return ret; |
|
329 | 221 |
|
330 |
- if((opt = optget(opts, "ExcludePath"))->enabled) { |
|
331 |
- if(match_regex(filename, opt->strarg) == 1) { |
|
332 |
- mdprintf(odesc, "%s: Excluded\n", filename); |
|
333 |
- return 0; |
|
334 |
- } |
|
222 |
+ if (thrmgr_group_need_terminate(scandata->conn->group)) { |
|
223 |
+ logg("^Client disconnected while scanjob was active\n"); |
|
224 |
+ return CL_BREAK; |
|
335 | 225 |
} |
336 |
- |
|
337 |
- switch(sb.st_mode & S_IFMT) { |
|
338 |
-#ifdef S_IFLNK |
|
339 |
- case S_IFLNK: |
|
340 |
- if(!optget(opts, "FollowFileSymlinks")->enabled) |
|
341 |
- break; |
|
342 |
- /* else go to the next case */ |
|
343 |
-#endif |
|
344 |
- case S_IFREG: |
|
345 |
- if(sb.st_size == 0) { /* empty file */ |
|
346 |
- mdprintf(odesc, "%s: Empty file\n", filename); |
|
347 |
- return 0; |
|
348 |
- } |
|
349 |
-#ifdef C_LINUX |
|
350 |
- if(procdev && (sb.st_dev == procdev)) |
|
351 |
- ret = CL_CLEAN; |
|
352 |
- else |
|
353 |
-#endif |
|
354 |
- { |
|
355 |
- thrmgr_setactivetask(filename, NULL); |
|
356 |
- ret = cl_scanfile(filename, &virname, scanned, engine, options); |
|
357 |
- thrmgr_setactivetask(NULL, NULL); |
|
358 |
- } |
|
359 |
- |
|
360 |
- if(ret == CL_VIRUS) { |
|
361 |
- mdprintf(odesc, "%s: %s FOUND\n", filename, virname); |
|
362 |
- logg("~%s: %s FOUND\n", filename, virname); |
|
363 |
- virusaction(filename, virname, opts); |
|
364 |
- } else if(ret != CL_CLEAN) { |
|
365 |
- mdprintf(odesc, "%s: %s ERROR\n", filename, cl_strerror(ret)); |
|
366 |
- logg("~%s: %s ERROR\n", filename, cl_strerror(ret)); |
|
367 |
- if(ret == CL_EMEM) |
|
368 |
- return -2; |
|
369 |
- } else if (logok) { |
|
370 |
- logg("~%s: OK\n", filename); |
|
371 |
- } |
|
372 |
- break; |
|
373 |
- case S_IFDIR: |
|
374 |
- if(type == TYPE_MULTISCAN) { |
|
375 |
- int idletimeout = optget(opts, "IdleTimeout")->numarg; |
|
376 |
- int max_threads = optget(opts, "MaxThreads")->numarg; |
|
377 |
- |
|
378 |
- if((multi_pool = thrmgr_new(max_threads, idletimeout, multiscanfile)) == NULL) { |
|
379 |
- logg("!thrmgr_new failed for multi_pool\n"); |
|
380 |
- mdprintf(odesc, "thrmgr_new failed for multi_pool ERROR\n"); |
|
381 |
- return -1; |
|
382 |
- } |
|
383 |
- } |
|
384 |
- |
|
385 |
- ret = dirscan(filename, &virname, scanned, engine, options, opts, odesc, &reclev, type, multi_pool); |
|
386 |
- |
|
387 |
- if(multi_pool) |
|
388 |
- thrmgr_destroy(multi_pool); |
|
389 |
- |
|
390 |
- break; |
|
391 |
- default: |
|
392 |
- mdprintf(odesc, "%s: Not supported file type. ERROR\n", filename); |
|
393 |
- return -1; |
|
226 |
+ if (type == TYPE_SCAN) { |
|
227 |
+ /* virus -> break */ |
|
228 |
+ return ret; |
|
394 | 229 |
} |
395 | 230 |
|
396 |
- if(!ret) |
|
397 |
- mdprintf(odesc, "%s: OK\n", filename); |
|
398 |
- |
|
399 |
- /* mdprintf(odesc, "\n"); */ /* Terminate response with a blank line boundary */ |
|
400 |
- return ret; |
|
231 |
+ /* keep scanning always */ |
|
232 |
+ return CL_SUCCESS; |
|
401 | 233 |
} |
402 | 234 |
|
403 |
-/* |
|
404 |
- * This function was readded by mbalmer@openbsd.org. That is the reason |
|
405 |
- * why it is so nicely formatted. |
|
406 |
- */ |
|
407 |
-int scanfd(const int fd, unsigned long int *scanned, |
|
408 |
- const struct cl_engine *engine, |
|
409 |
- unsigned int options, const struct optstruct *opts, int odesc) |
|
235 |
+int scanfd(const int fd, const client_conn_t *conn, unsigned long int *scanned, |
|
236 |
+ const struct cl_engine *engine, |
|
237 |
+ unsigned int options, const struct optstruct *opts, int odesc, int stream) |
|
410 | 238 |
{ |
411 | 239 |
int ret; |
412 | 240 |
const char *virname; |
413 | 241 |
struct stat statbuf; |
414 | 242 |
char fdstr[32]; |
415 | 243 |
|
416 |
- |
|
417 |
- if(fstat(fd, &statbuf) == -1) |
|
418 |
- return -1; |
|
419 |
- |
|
420 |
- if(!S_ISREG(statbuf.st_mode)) |
|
244 |
+ if (stream) |
|
245 |
+ strncpy(fdstr, "stream", sizeof(fdstr)); |
|
246 |
+ else |
|
247 |
+ snprintf(fdstr, sizeof(fdstr), "fd[%d]", fd); |
|
248 |
+ if(fstat(fd, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) { |
|
249 |
+ conn_reply(conn, fdstr, "Not a regular file", "ERROR"); |
|
250 |
+ logg("%s: Not a regular file. ERROR\n", fdstr); |
|
421 | 251 |
return -1; |
422 |
- |
|
423 |
- snprintf(fdstr, sizeof(fdstr), "fd[%d]", fd); |
|
252 |
+ } |
|
424 | 253 |
|
425 | 254 |
thrmgr_setactivetask(fdstr, NULL); |
426 | 255 |
ret = cl_scandesc(fd, &virname, scanned, engine, options); |
427 | 256 |
thrmgr_setactivetask(NULL, NULL); |
428 | 257 |
|
429 | 258 |
if(ret == CL_VIRUS) { |
430 |
- mdprintf(odesc, "%s: %s FOUND\n", fdstr, virname); |
|
259 |
+ conn_reply(conn, fdstr, virname, "FOUND"); |
|
431 | 260 |
logg("%s: %s FOUND\n", fdstr, virname); |
432 | 261 |
virusaction(fdstr, virname, opts); |
433 | 262 |
} else if(ret != CL_CLEAN) { |
434 |
- mdprintf(odesc, "%s: %s ERROR\n", fdstr, cl_strerror(ret)); |
|
263 |
+ conn_reply(conn, fdstr, cl_strerror(ret), "ERROR"); |
|
435 | 264 |
logg("%s: %s ERROR\n", fdstr, cl_strerror(ret)); |
436 | 265 |
} else { |
437 |
- mdprintf(odesc, "%s: OK\n", fdstr); |
|
266 |
+ conn_reply_single(conn, fdstr, "OK"); |
|
438 | 267 |
if(logok) |
439 | 268 |
logg("%s: OK\n", fdstr); |
440 | 269 |
} |
441 | 270 |
return ret; |
442 | 271 |
} |
443 | 272 |
|
444 |
-int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts) |
|
273 |
+int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, char term) |
|
445 | 274 |
{ |
446 | 275 |
int ret, sockfd, acceptd; |
447 | 276 |
int tmpd, bread, retval, timeout, btread; |
448 |
- unsigned int port = 0, portscan = 1000, min_port, max_port; |
|
449 |
- unsigned long int size = 0, maxsize = 0; |
|
450 |
- short bound = 0, rnd_port_first = 1; |
|
277 |
+ unsigned int port = 0, portscan, min_port, max_port; |
|
278 |
+ unsigned long int quota = 0, maxsize = 0; |
|
279 |
+ short bound = 0; |
|
451 | 280 |
const char *virname; |
452 | 281 |
char buff[FILEBUFF]; |
453 | 282 |
char peer_addr[32]; |
454 | 283 |
struct sockaddr_in server; |
455 | 284 |
struct sockaddr_in peer; |
456 | 285 |
socklen_t addrlen; |
457 |
- struct hostent he; |
|
458 |
- const struct optstruct *opt; |
|
459 | 286 |
char *tmpname; |
460 | 287 |
|
461 | 288 |
|
462 |
- /* get min port */ |
|
463 | 289 |
min_port = optget(opts, "StreamMinPort")->numarg; |
464 |
- if(min_port < 1024 || min_port > 65535) |
|
465 |
- min_port = 1024; |
|
466 |
- |
|
467 |
- /* get max port */ |
|
468 | 290 |
max_port = optget(opts, "StreamMaxPort")->numarg; |
469 |
- if(max_port < min_port || max_port > 65535) |
|
470 |
- max_port = 65535; |
|
471 |
- |
|
472 |
- /* bind to a free port */ |
|
473 |
- while(!bound && --portscan) { |
|
474 |
- if(rnd_port_first) { |
|
475 |
- /* try a random port first */ |
|
476 |
- port = min_port + cli_rndnum(max_port - min_port); |
|
477 |
- rnd_port_first = 0; |
|
478 |
- } else { |
|
479 |
- /* try the neighbor ports */ |
|
480 |
- if(--port < min_port) |
|
481 |
- port=max_port; |
|
482 |
- } |
|
291 |
+ |
|
292 |
+ /* search for a free port to bind to */ |
|
293 |
+ port = cli_rndnum(max_port - min_port); |
|
294 |
+ bound = 0; |
|
295 |
+ for (portscan = 0; portscan < 1000; portscan++) { |
|
296 |
+ port = (port - 1) % (max_port - min_port + 1); |
|
483 | 297 |
|
484 | 298 |
memset((char *) &server, 0, sizeof(server)); |
485 | 299 |
server.sin_family = AF_INET; |
486 |
- server.sin_port = htons(port); |
|
487 |
- |
|
488 |
- if((opt = optget(opts, "TCPAddr"))->enabled) { |
|
489 |
- if(r_gethostbyname(opt->strarg, &he, buff, sizeof(buff)) == -1) { |
|
490 |
- logg("!r_gethostbyname(%s) error: %s\n", opt->strarg, strerror(errno)); |
|
491 |
- mdprintf(odesc, "r_gethostbyname(%s) ERROR\n", opt->strarg); |
|
492 |
- return -1; |
|
493 |
- } |
|
494 |
- server.sin_addr = *(struct in_addr *) he.h_addr_list[0]; |
|
495 |
- } else |
|
496 |
- server.sin_addr.s_addr = INADDR_ANY; |
|
300 |
+ server.sin_port = htons(min_port + port); |
|
301 |
+ server.sin_addr.s_addr = htonl(INADDR_ANY); |
|
497 | 302 |
|
498 | 303 |
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) |
499 | 304 |
continue; |
500 | 305 |
|
501 | 306 |
if(bind(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) == -1) |
502 | 307 |
closesocket(sockfd); |
503 |
- else |
|
308 |
+ else { |
|
504 | 309 |
bound = 1; |
310 |
+ break; |
|
311 |
+ } |
|
505 | 312 |
} |
313 |
+ port += min_port; |
|
506 | 314 |
|
507 | 315 |
timeout = optget(opts, "ReadTimeout")->numarg; |
508 | 316 |
if(timeout == 0) |
... | ... |
@@ -510,35 +318,31 @@ int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *en |
510 | 510 |
|
511 | 511 |
if(!bound && !portscan) { |
512 | 512 |
logg("!ScanStream: Can't find any free port.\n"); |
513 |
- mdprintf(odesc, "Can't find any free port. ERROR\n"); |
|
513 |
+ mdprintf(odesc, "Can't find any free port. ERROR%c", term); |
|
514 | 514 |
closesocket(sockfd); |
515 | 515 |
return -1; |
516 | 516 |
} else { |
517 | 517 |
listen(sockfd, 1); |
518 |
- if(mdprintf(odesc, "PORT %u\n", port) <= 0) { |
|
518 |
+ if(mdprintf(odesc, "PORT %u%c", port, term) <= 0) { |
|
519 | 519 |
logg("!ScanStream: error transmitting port.\n"); |
520 | 520 |
closesocket(sockfd); |
521 | 521 |
return -1; |
522 | 522 |
} |
523 | 523 |
} |
524 | 524 |
|
525 |
- switch(retval = poll_fd(sockfd, timeout, 0)) { |
|
526 |
- case 0: /* timeout */ |
|
527 |
- mdprintf(odesc, "Accept timeout. ERROR\n"); |
|
528 |
- logg("!ScanStream %u: accept timeout.\n", port); |
|
529 |
- closesocket(sockfd); |
|
530 |
- return -1; |
|
531 |
- case -1: |
|
532 |
- mdprintf(odesc, "Accept poll. ERROR\n"); |
|
533 |
- logg("!ScanStream %u: accept poll failed.\n", port); |
|
534 |
- closesocket(sockfd); |
|
535 |
- return -1; |
|
525 |
+ retval = poll_fd(sockfd, timeout, 0); |
|
526 |
+ if (!retval || retval == -1) { |
|
527 |
+ const char *reason = !retval ? "timeout" : "poll"; |
|
528 |
+ mdprintf(odesc, "Accept %s. ERROR%c", reason, term); |
|
529 |
+ logg("!ScanStream %u: accept %s.\n", port, reason); |
|
530 |
+ closesocket(sockfd); |
|
531 |
+ return -1; |
|
536 | 532 |
} |
537 | 533 |
|
538 | 534 |
addrlen = sizeof(peer); |
539 | 535 |
if((acceptd = accept(sockfd, (struct sockaddr *) &peer, &addrlen)) == -1) { |
540 | 536 |
closesocket(sockfd); |
541 |
- mdprintf(odesc, "accept() ERROR\n"); |
|
537 |
+ mdprintf(odesc, "accept() ERROR%c", term); |
|
542 | 538 |
logg("!ScanStream %u: accept() failed.\n", port); |
543 | 539 |
return -1; |
544 | 540 |
} |
... | ... |
@@ -550,26 +354,31 @@ int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *en |
550 | 550 |
shutdown(sockfd, 2); |
551 | 551 |
closesocket(sockfd); |
552 | 552 |
closesocket(acceptd); |
553 |
- mdprintf(odesc, "cli_gentempfd() failed. ERROR\n"); |
|
553 |
+ mdprintf(odesc, "cli_gentempfd() failed. ERROR%c", term); |
|
554 | 554 |
logg("!ScanStream(%s@%u): Can't create temporary file.\n", peer_addr, port); |
555 | 555 |
return -1; |
556 | 556 |
} |
557 | 557 |
|
558 |
- maxsize = optget(opts, "StreamMaxLength")->numarg; |
|
559 |
- |
|
560 |
- btread = sizeof(buff); |
|
558 |
+ quota = maxsize = optget(opts, "StreamMaxLength")->numarg; |
|
561 | 559 |
|
562 | 560 |
while((retval = poll_fd(acceptd, timeout, 0)) == 1) { |
561 |
+ /* only read up to max */ |
|
562 |
+ btread = (maxsize && (quota < sizeof(buff))) ? quota : sizeof(buff); |
|
563 |
+ if (!btread) { |
|
564 |
+ logg("^ScanStream(%s@%u): Size limit reached (max: %lu)\n", peer_addr, port, maxsize); |
|
565 |
+ break; /* Scan what we have */ |
|
566 |
+ } |
|
563 | 567 |
bread = recv(acceptd, buff, btread, 0); |
564 | 568 |
if(bread <= 0) |
565 | 569 |
break; |
566 |
- size += bread; |
|
570 |
+ |
|
571 |
+ quota -= bread; |
|
567 | 572 |
|
568 | 573 |
if(writen(tmpd, buff, bread) != bread) { |
569 | 574 |
shutdown(sockfd, 2); |
570 | 575 |
closesocket(sockfd); |
571 | 576 |
closesocket(acceptd); |
572 |
- mdprintf(odesc, "Temporary file -> write ERROR\n"); |
|
577 |
+ mdprintf(odesc, "Temporary file -> write ERROR%c", term); |
|
573 | 578 |
logg("!ScanStream(%s@%u): Can't write to temporary file.\n", peer_addr, port); |
574 | 579 |
close(tmpd); |
575 | 580 |
if(!optget(opts, "LeaveTemporaryFiles")->enabled) |
... | ... |
@@ -577,24 +386,15 @@ int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *en |
577 | 577 |
free(tmpname); |
578 | 578 |
return -1; |
579 | 579 |
} |
580 |
- |
|
581 |
- if(maxsize && (size + btread >= maxsize)) { |
|
582 |
- btread = (maxsize - size); /* only read up to max */ |
|
583 |
- |
|
584 |
- if(btread <= 0) { |
|
585 |
- logg("^ScanStream(%s@%u): Size limit reached (max: %lu)\n", peer_addr, port, maxsize); |
|
586 |
- break; /* Scan what we have */ |
|
587 |
- } |
|
588 |
- } |
|
589 | 580 |
} |
590 | 581 |
|
591 | 582 |
switch(retval) { |
592 | 583 |
case 0: /* timeout */ |
593 |
- mdprintf(odesc, "read timeout ERROR\n"); |
|
584 |
+ mdprintf(odesc, "read timeout ERROR%c", term); |
|
594 | 585 |
logg("!ScanStream(%s@%u): read timeout.\n", peer_addr, port); |
595 | 586 |
break; |
596 | 587 |
case -1: |
597 |
- mdprintf(odesc, "read poll ERROR\n"); |
|
588 |
+ mdprintf(odesc, "read poll ERROR%c", term); |
|
598 | 589 |
logg("!ScanStream(%s@%u): read poll failed.\n", peer_addr, port); |
599 | 590 |
break; |
600 | 591 |
} |
... | ... |
@@ -616,16 +416,16 @@ int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *en |
616 | 616 |
closesocket(sockfd); |
617 | 617 |
|
618 | 618 |
if(ret == CL_VIRUS) { |
619 |
- mdprintf(odesc, "stream: %s FOUND\n", virname); |
|
619 |
+ mdprintf(odesc, "stream: %s FOUND%c", virname, term); |
|
620 | 620 |
logg("stream(%s@%u): %s FOUND\n", peer_addr, port, virname); |
621 | 621 |
virusaction("stream", virname, opts); |
622 | 622 |
} else if(ret != CL_CLEAN) { |
623 | 623 |
if(retval == 1) { |
624 |
- mdprintf(odesc, "stream: %s ERROR\n", cl_strerror(ret)); |
|
624 |
+ mdprintf(odesc, "stream: %s ERROR%c", cl_strerror(ret), term); |
|
625 | 625 |
logg("stream(%s@%u): %s ERROR\n", peer_addr, port, cl_strerror(ret)); |
626 | 626 |
} |
627 | 627 |
} else { |
628 |
- mdprintf(odesc, "stream: OK\n"); |
|
628 |
+ mdprintf(odesc, "stream: OK%c", term); |
|
629 | 629 |
if(logok) |
630 | 630 |
logg("stream(%s@%u): OK\n", peer_addr, port); |
631 | 631 |
} |
... | ... |
@@ -25,11 +25,29 @@ |
25 | 25 |
|
26 | 26 |
#include "libclamav/clamav.h" |
27 | 27 |
#include "shared/optparser.h" |
28 |
+#include "thrmgr.h" |
|
29 |
+#include "session.h" |
|
28 | 30 |
|
29 |
-int scan(const char *filename, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, int odesc, unsigned int type); |
|
31 |
+struct scan_cb_data { |
|
32 |
+ int scantype; |
|
33 |
+ int odesc; |
|
34 |
+ int type; |
|
35 |
+ int infected; |
|
36 |
+ int errors; |
|
37 |
+ int total; |
|
38 |
+ int id; |
|
39 |
+ const client_conn_t *conn; |
|
40 |
+ const char *toplevel_path; |
|
41 |
+ unsigned long scanned; |
|
42 |
+ unsigned int options; |
|
43 |
+ struct cl_engine *engine; |
|
44 |
+ const struct optstruct *opts; |
|
45 |
+ threadpool_t *thr_pool; |
|
46 |
+ jobgroup_t *group; |
|
47 |
+}; |
|
30 | 48 |
|
31 |
-int scanfd(const int fd, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, int odesc); |
|
32 |
- |
|
33 |
-int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts); |
|
49 |
+int scanfd(const int fd, const client_conn_t *conn, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, int odesc, int stream); |
|
50 |
+int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, char term); |
|
51 |
+int scan_callback(struct stat *sb, char *filename, const char *msg, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data); |
|
34 | 52 |
|
35 | 53 |
#endif |
... | ... |
@@ -42,6 +42,7 @@ |
42 | 42 |
#include <unistd.h> |
43 | 43 |
#endif |
44 | 44 |
|
45 |
+#include <arpa/inet.h> |
|
45 | 46 |
#include "libclamav/clamav.h" |
46 | 47 |
|
47 | 48 |
#include "shared/output.h" |
... | ... |
@@ -75,17 +76,7 @@ int reload = 0; |
75 | 75 |
time_t reloaded_time = 0; |
76 | 76 |
pthread_mutex_t reload_mutex = PTHREAD_MUTEX_INITIALIZER; |
77 | 77 |
int sighup = 0; |
78 |
-static struct cl_stat *dbstat = NULL; |
|
79 |
- |
|
80 |
-typedef struct client_conn_tag { |
|
81 |
- int sd; |
|
82 |
- unsigned int options; |
|
83 |
- const struct optstruct *opts; |
|
84 |
- struct cl_engine *engine; |
|
85 |
- time_t engine_timestamp; |
|
86 |
- int *socketds; |
|
87 |
- int nsockets; |
|
88 |
-} client_conn_t; |
|
78 |
+static struct cl_stat dbstat; |
|
89 | 79 |
|
90 | 80 |
static void scanner_thread(void *arg) |
91 | 81 |
{ |
... | ... |
@@ -93,8 +84,8 @@ static void scanner_thread(void *arg) |
93 | 93 |
#ifndef C_WINDOWS |
94 | 94 |
sigset_t sigset; |
95 | 95 |
#endif |
96 |
- int ret, timeout, i, session=FALSE; |
|
97 |
- |
|
96 |
+ int ret, timeout; |
|
97 |
+ unsigned virus=0, errors = 0; |
|
98 | 98 |
|
99 | 99 |
#ifndef C_WINDOWS |
100 | 100 |
/* ignore all signals */ |
... | ... |
@@ -107,61 +98,36 @@ static void scanner_thread(void *arg) |
107 | 107 |
#ifdef SIGBUS |
108 | 108 |
sigdelset(&sigset, SIGBUS); |
109 | 109 |
#endif |
110 |
+ sigdelset(&sigset, SIGTSTP); |
|
111 |
+ sigdelset(&sigset, SIGCONT); |
|
110 | 112 |
pthread_sigmask(SIG_SETMASK, &sigset, NULL); |
111 | 113 |
#endif |
112 | 114 |
|
113 | 115 |
timeout = optget(conn->opts, "ReadTimeout")->numarg; |
114 | 116 |
if(!timeout) |
115 |
- timeout = -1; |
|
116 |
- |
|
117 |
- do { |
|
118 |
- ret = command(conn->sd, conn->engine, conn->options, conn->opts, timeout); |
|
119 |
- if (ret < 0) { |
|
120 |
- break; |
|
121 |
- } |
|
122 |
- |
|
123 |
- switch(ret) { |
|
124 |
- case COMMAND_SHUTDOWN: |
|
125 |
- pthread_mutex_lock(&exit_mutex); |
|
126 |
- progexit = 1; |
|
127 |
- for(i = 0; i < conn->nsockets; i++) { |
|
128 |
- shutdown(conn->socketds[i], 2); |
|
129 |
- closesocket(conn->socketds[i]); |
|
130 |
- } |
|
131 |
- pthread_mutex_unlock(&exit_mutex); |
|
132 |
- break; |
|
133 |
- |
|
134 |
- case COMMAND_RELOAD: |
|
135 |
- pthread_mutex_lock(&reload_mutex); |
|
136 |
- reload = 1; |
|
137 |
- pthread_mutex_unlock(&reload_mutex); |
|
138 |
- break; |
|
139 |
- |
|
140 |
- case COMMAND_SESSION: |
|
141 |
- session = TRUE; |
|
142 |
- break; |
|
117 |
+ timeout = -1; |
|
143 | 118 |
|
144 |
- case COMMAND_END: |
|
145 |
- session = FALSE; |
|
146 |
- break; |
|
147 |
- } |
|
148 |
- if (session) { |
|
149 |
- pthread_mutex_lock(&exit_mutex); |
|
150 |
- if(progexit) { |
|
151 |
- session = FALSE; |
|
152 |
- } |
|
153 |
- pthread_mutex_unlock(&exit_mutex); |
|
154 |
- pthread_mutex_lock(&reload_mutex); |
|
155 |
- if (conn->engine_timestamp != reloaded_time) { |
|
156 |
- session = FALSE; |
|
157 |
- } |
|
158 |
- pthread_mutex_unlock(&reload_mutex); |
|
159 |
- } |
|
160 |
- } while (session); |
|
119 |
+ ret = command(conn, &virus); |
|
120 |
+ if (ret == -1) { |
|
121 |
+ pthread_mutex_lock(&exit_mutex); |
|
122 |
+ progexit = 1; |
|
123 |
+ pthread_mutex_unlock(&exit_mutex); |
|
124 |
+ errors = 1; |
|
125 |
+ } else |
|
126 |
+ errors = ret; |
|
161 | 127 |
|
162 |
- shutdown(conn->sd, 2); |
|
163 |
- closesocket(conn->sd); |
|
164 | 128 |
thrmgr_setactiveengine(NULL); |
129 |
+ |
|
130 |
+ if (conn->filename) |
|
131 |
+ free(conn->filename); |
|
132 |
+ logg("*SCANTH: finished\n"); |
|
133 |
+ if (thrmgr_group_finished(conn->group, virus ? EXIT_OTHER : |
|
134 |
+ errors ? EXIT_ERROR : EXIT_OK)) { |
|
135 |
+ logg("*SCANTH: connection shut down\n"); |
|
136 |
+ /* close connection if we were last in group */ |
|
137 |
+ shutdown(conn->sd, 2); |
|
138 |
+ closesocket(conn->sd); |
|
139 |
+ } |
|
165 | 140 |
cl_engine_free(conn->engine); |
166 | 141 |
free(conn); |
167 | 142 |
return; |
... | ... |
@@ -202,12 +168,12 @@ static struct cl_engine *reload_db(struct cl_engine *engine, unsigned int dbopti |
202 | 202 |
pua_cats[0] = 0; |
203 | 203 |
*ret = 0; |
204 | 204 |
if(do_check) { |
205 |
- if(dbstat == NULL) { |
|
205 |
+ if(!dbstat.entries) { |
|
206 | 206 |
logg("No stats for Database check - forcing reload\n"); |
207 | 207 |
return engine; |
208 | 208 |
} |
209 | 209 |
|
210 |
- if(cl_statchkdir(dbstat) == 1) { |
|
210 |
+ if(cl_statchkdir(&dbstat) == 1) { |
|
211 | 211 |
logg("SelfCheck: Database modification detected. Forcing reload.\n"); |
212 | 212 |
return engine; |
213 | 213 |
} else { |
... | ... |
@@ -229,19 +195,11 @@ static struct cl_engine *reload_db(struct cl_engine *engine, unsigned int dbopti |
229 | 229 |
dbdir = optget(opts, "DatabaseDirectory")->strarg; |
230 | 230 |
logg("Reading databases from %s\n", dbdir); |
231 | 231 |
|
232 |
- if(dbstat == NULL) { |
|
233 |
- dbstat = (struct cl_stat *) malloc(sizeof(struct cl_stat)); |
|
234 |
- if(!dbstat) { |
|
235 |
- logg("!Can't allocate memory for dbstat\n"); |
|
236 |
- *ret = 1; |
|
237 |
- return NULL; |
|
238 |
- } |
|
239 |
- } else { |
|
240 |
- cl_statfree(dbstat); |
|
241 |
- } |
|
232 |
+ if(dbstat.entries) |
|
233 |
+ cl_statfree(&dbstat); |
|
242 | 234 |
|
243 |
- memset(dbstat, 0, sizeof(struct cl_stat)); |
|
244 |
- if((retval = cl_statinidir(dbdir, dbstat))) { |
|
235 |
+ memset(&dbstat, 0, sizeof(struct cl_stat)); |
|
236 |
+ if((retval = cl_statinidir(dbdir, &dbstat))) { |
|
245 | 237 |
logg("!cl_statinidir() failed: %s\n", cl_strerror(retval)); |
246 | 238 |
*ret = 1; |
247 | 239 |
return NULL; |
... | ... |
@@ -281,11 +239,170 @@ static struct cl_engine *reload_db(struct cl_engine *engine, unsigned int dbopti |
281 | 281 |
return engine; |
282 | 282 |
} |
283 | 283 |
|
284 |
-int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigned int dboptions, const struct optstruct *opts) |
|
284 |
+static const char *get_cmd(struct fd_buf *buf, size_t off, size_t *len, char *term) |
|
285 |
+{ |
|
286 |
+ unsigned char *pos; |
|
287 |
+ if (!buf->off || off >= buf->off) { |
|
288 |
+ *len = 0; |
|
289 |
+ return NULL; |
|
290 |
+ } |
|
291 |
+ |
|
292 |
+ *term = '\n'; |
|
293 |
+ switch (buf->buffer[0]) { |
|
294 |
+ /* commands terminated by delimiters */ |
|
295 |
+ case 'z': |
|
296 |
+ *term = '\0'; |
|
297 |
+ case 'n': |
|
298 |
+ pos = memchr(buf->buffer + off, *term, buf->off - off); |
|
299 |
+ if (!pos) { |
|
300 |
+ /* we don't have another full command yet */ |
|
301 |
+ *len = 0; |
|
302 |
+ return NULL; |
|
303 |
+ } |
|
304 |
+ *pos = '\0'; |
|
305 |
+ if (*term) { |
|
306 |
+ *len = cli_chomp(buf->buffer + off); |
|
307 |
+ } else { |
|
308 |
+ *len = pos - buf->buffer - off; |
|
309 |
+ } |
|
310 |
+ return buf->buffer + off + 1; |
|
311 |
+ default: |
|
312 |
+ /* one packet = one command */ |
|
313 |
+ *len = buf->off - off; |
|
314 |
+ buf->buffer[buf->off] = '\0'; |
|
315 |
+ cli_chomp(buf->buffer + off); |
|
316 |
+ return buf->buffer + off; |
|
317 |
+ } |
|
318 |
+} |
|
319 |
+ |
|
320 |
+struct acceptdata { |
|
321 |
+ struct fd_data fds; |
|
322 |
+ struct fd_data recv_fds; |
|
323 |
+ int syncpipe_wake_recv[2]; |
|
324 |
+ int syncpipe_wake_accept[2]; |
|
325 |
+}; |
|
326 |
+ |
|
327 |
+static void *acceptloop_th(void *arg) |
|
328 |
+{ |
|
329 |
+#ifdef HAVE_STRERROR_R |
|
330 |
+ char buff[BUFFSIZE + 1]; |
|
331 |
+#endif |
|
332 |
+ size_t i; |
|
333 |
+ struct acceptdata *data = (struct acceptdata*)arg; |
|
334 |
+ struct fd_data *fds = &data->fds; |
|
335 |
+ struct fd_data *recv_fds = &data->recv_fds; |
|
336 |
+ |
|
337 |
+ pthread_mutex_lock(&fds->buf_mutex); |
|
338 |
+ for (;;) { |
|
339 |
+ /* Block waiting for data to become available for reading */ |
|
340 |
+ int new_sd = fds_poll_recv(fds, -1, 0); |
|
341 |
+ |
|
342 |
+ /* TODO: what about sockets that get rm-ed? */ |
|
343 |
+ if (!fds->nfds) { |
|
344 |
+ /* no more sockets to poll, all gave an error */ |
|
345 |
+ logg("!Main socket gone: fatal\n"); |
|
346 |
+ break; |
|
347 |
+ } |
|
348 |
+ |
|
349 |
+ if (new_sd == -1 && errno != EINTR) { |
|
350 |
+ logg("!Failed to poll sockets, fatal\n"); |
|
351 |
+ pthread_mutex_lock(&exit_mutex); |
|
352 |
+ progexit = 1; |
|
353 |
+ pthread_mutex_unlock(&exit_mutex); |
|
354 |
+ break; |
|
355 |
+ } |
|
356 |
+ |
|
357 |
+ /* accept() loop */ |
|
358 |
+ for (i=0;i < fds->nfds && new_sd >= 0; i++) { |
|
359 |
+ struct fd_buf *buf = &fds->buf[i]; |
|
360 |
+ if (!buf->got_newdata) |
|
361 |
+ continue; |
|
362 |
+ if (buf->fd == data->syncpipe_wake_accept[0]) { |
|
363 |
+ /* dummy sync pipe, just to wake us */ |
|
364 |
+ if (read(buf->fd, buff, sizeof(buff)) < 0) { |
|
365 |
+ logg("^Syncpipe read failed\n"); |
|
366 |
+ } |
|
367 |
+ continue; |
|
368 |
+ } |
|
369 |
+ if (buf->got_newdata == -1) { |
|
370 |
+ shutdown(buf->fd, 2); |
|
371 |
+ closesocket(buf->fd); |
|
372 |
+ buf->fd = -1; |
|
373 |
+ continue; |
|
374 |
+ } |
|
375 |
+ /* listen only socket */ |
|
376 |
+ new_sd = accept(fds->buf[i].fd, NULL, NULL); |
|
377 |
+ |
|
378 |
+ pthread_mutex_lock(&exit_mutex); |
|
379 |
+ if(progexit) { |
|
380 |
+ pthread_mutex_unlock(&exit_mutex); |
|
381 |
+ break; |
|
382 |
+ } |
|
383 |
+ pthread_mutex_unlock(&exit_mutex); |
|
384 |
+ |
|
385 |
+ if (new_sd >= 0) { |
|
386 |
+ int ret; |
|
387 |
+ pthread_mutex_lock(&recv_fds->buf_mutex); |
|
388 |
+ ret = fds_add(recv_fds, new_sd, 0); |
|
389 |
+ pthread_mutex_unlock(&recv_fds->buf_mutex); |
|
390 |
+ |
|
391 |
+ if (ret == -1) { |
|
392 |
+ logg("!fds_add failed\n"); |
|
393 |
+ closesocket(new_sd); |
|
394 |
+ continue; |
|
395 |
+ } |
|
396 |
+ |
|
397 |
+ /* notify recvloop */ |
|
398 |
+ if (write(data->syncpipe_wake_recv[1], "", 1) == -1) { |
|
399 |
+ logg("!write syncpipe failed\n"); |
|
400 |
+ continue; |
|
401 |
+ } |
|
402 |
+ } else if (errno != EINTR) { |
|
403 |
+ /* very bad - need to exit or restart */ |
|
404 |
+#ifdef HAVE_STRERROR_R |
|
405 |
+ strerror_r(errno, buff, BUFFSIZE); |
|
406 |
+ logg("!accept() failed: %s\n", buff); |
|
407 |
+#else |
|
408 |
+ logg("!accept() failed\n"); |
|
409 |
+#endif |
|
410 |
+ /* give the poll loop a chance to close disconnected FDs */ |
|
411 |
+ break; |
|
412 |
+ } |
|
413 |
+ |
|
414 |
+ } |
|
415 |
+ |
|
416 |
+ /* handle progexit */ |
|
417 |
+ pthread_mutex_lock(&exit_mutex); |
|
418 |
+ if (progexit) { |
|
419 |
+ pthread_mutex_unlock(&exit_mutex); |
|
420 |
+ break; |
|
421 |
+ } |
|
422 |
+ pthread_mutex_unlock(&exit_mutex); |
|
423 |
+ } |
|
424 |
+ pthread_mutex_unlock(&fds->buf_mutex); |
|
425 |
+ |
|
426 |
+ for (i=0;i < fds->nfds; i++) { |
|
427 |
+ if (fds->buf[i].fd == -1) |
|
428 |
+ continue; |
|
429 |
+ shutdown(fds->buf[i].fd, 2); |
|
430 |
+ closesocket(fds->buf[i].fd); |
|
431 |
+ } |
|
432 |
+ fds_free(fds); |
|
433 |
+ |
|
434 |
+ pthread_mutex_lock(&exit_mutex); |
|
435 |
+ progexit = 1; |
|
436 |
+ pthread_mutex_unlock(&exit_mutex); |
|
437 |
+ if (write(data->syncpipe_wake_recv[1], "", 1) < 0) { |
|
438 |
+ logg("^Syncpipe write failed\n"); |
|
439 |
+ } |
|
440 |
+ |
|
441 |
+ return NULL; |
|
442 |
+} |
|
443 |
+ |
|
444 |
+int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsigned int dboptions, const struct optstruct *opts) |
|
285 | 445 |
{ |
286 |
- int max_threads, i, ret = 0; |
|
446 |
+ int max_threads, max_queue, ret = 0; |
|
287 | 447 |
unsigned int options = 0; |
288 |
- threadpool_t *thr_pool; |
|
289 | 448 |
char timestr[32]; |
290 | 449 |
#ifndef C_WINDOWS |
291 | 450 |
struct sigaction sigact; |
... | ... |
@@ -293,17 +410,19 @@ int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigne |
293 | 293 |
struct rlimit rlim; |
294 | 294 |
#endif |
295 | 295 |
mode_t old_umask; |
296 |
- client_conn_t *client_conn; |
|
297 | 296 |
const struct optstruct *opt; |
298 |
-#ifdef HAVE_STRERROR_R |
|
299 | 297 |
char buff[BUFFSIZE + 1]; |
300 |
-#endif |
|
301 |
- unsigned int selfchk; |
|
302 |
- time_t start_time, current_time; |
|
303 | 298 |
pid_t mainpid; |
304 | 299 |
int idletimeout; |
305 | 300 |
uint32_t val32; |
306 | 301 |
uint64_t val64; |
302 |
+ size_t i, j, rr_last = 0; |
|
303 |
+ pthread_t accept_th; |
|
304 |
+ struct acceptdata acceptdata; |
|
305 |
+ struct fd_data *fds = &acceptdata.recv_fds; |
|
306 |
+ time_t start_time, current_time; |
|
307 |
+ unsigned int selfchk; |
|
308 |
+ threadpool_t *thr_pool; |
|
307 | 309 |
|
308 | 310 |
#ifdef CLAMUKO |
309 | 311 |
pthread_t clamuko_pid; |
... | ... |
@@ -522,6 +641,7 @@ int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigne |
522 | 522 |
} else { |
523 | 523 |
logg("Self checking every %u seconds.\n", selfchk); |
524 | 524 |
} |
525 |
+ memset(&dbstat, 0, sizeof(dbstat)); |
|
525 | 526 |
|
526 | 527 |
/* save the PID */ |
527 | 528 |
mainpid = getpid(); |
... | ... |
@@ -541,6 +661,7 @@ int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigne |
541 | 541 |
|
542 | 542 |
logg("*Listening daemon: PID: %u\n", (unsigned int) mainpid); |
543 | 543 |
max_threads = optget(opts, "MaxThreads")->numarg; |
544 |
+ max_queue = optget(opts, "MaxQueue")->numarg; |
|
544 | 545 |
|
545 | 546 |
if(optget(opts, "ClamukoScanOnAccess")->enabled) |
546 | 547 |
#ifdef CLAMUKO |
... | ... |
@@ -579,6 +700,8 @@ int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigne |
579 | 579 |
#ifdef SIGBUS |
580 | 580 |
sigdelset(&sigset, SIGBUS); |
581 | 581 |
#endif |
582 |
+ sigdelset(&sigset, SIGTSTP); |
|
583 |
+ sigdelset(&sigset, SIGCONT); |
|
582 | 584 |
sigprocmask(SIG_SETMASK, &sigset, NULL); |
583 | 585 |
|
584 | 586 |
/* SIGINT, SIGTERM, SIGSEGV */ |
... | ... |
@@ -598,114 +721,342 @@ int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigne |
598 | 598 |
|
599 | 599 |
idletimeout = optget(opts, "IdleTimeout")->numarg; |
600 | 600 |
|
601 |
- if((thr_pool=thrmgr_new(max_threads, idletimeout, scanner_thread)) == NULL) { |
|
601 |
+ memset(&acceptdata, 0, sizeof(acceptdata)); |
|
602 |
+ |
|
603 |
+ for (i=0;i < nsockets;i++) |
|
604 |
+ if (fds_add(&acceptdata.fds, socketds[i], 1) == -1) { |
|
605 |
+ logg("!fds_add failed\n"); |
|
606 |
+ cl_engine_free(engine); |
|
607 |
+ return 1; |
|
608 |
+ } |
|
609 |
+ |
|
610 |
+ if (pipe(acceptdata.syncpipe_wake_recv) == -1 || |
|
611 |
+ (pipe(acceptdata.syncpipe_wake_accept) == -1)) { |
|
612 |
+ |
|
613 |
+ logg("!pipe failed\n"); |
|
614 |
+ exit(-1); |
|
615 |
+ } |
|
616 |
+ |
|
617 |
+ if (fds_add(fds, acceptdata.syncpipe_wake_recv[0], 1) == -1 || |
|
618 |
+ fds_add(&acceptdata.fds, acceptdata.syncpipe_wake_accept[0], 1)) { |
|
619 |
+ logg("!failed to add pipe fd\n"); |
|
620 |
+ exit(-1); |
|
621 |
+ } |
|
622 |
+ |
|
623 |
+ if ((thr_pool = thrmgr_new(max_threads, idletimeout, max_queue, scanner_thread)) == NULL) { |
|
602 | 624 |
logg("!thrmgr_new failed\n"); |
603 | 625 |
exit(-1); |
604 | 626 |
} |
605 | 627 |
|
606 |
- time(&start_time); |
|
628 |
+ if (pthread_create(&accept_th, NULL, acceptloop_th, &acceptdata)) { |
|
629 |
+ logg("!pthread_create failed\n"); |
|
630 |
+ exit(-1); |
|
631 |
+ } |
|
607 | 632 |
|
608 |
- for(;;) { |
|
609 |
-#if !defined(C_WINDOWS) && !defined(C_BEOS) |
|
610 |
- struct stat st_buf; |
|
611 |
-#endif |
|
612 |
- int socketd = socketds[0]; |
|
613 |
- int new_sd = 0; |
|
614 |
- |
|
615 |
- if(nsockets > 1) { |
|
616 |
- int pollret = poll_fds(socketds, nsockets, -1, 1); |
|
617 |
- if(pollret > 0) { |
|
618 |
- socketd = socketds[pollret - 1]; |
|
619 |
- } else { |
|
620 |
- new_sd = -1; |
|
621 |
- } |
|
622 |
- } |
|
623 |
-#if !defined(C_WINDOWS) && !defined(C_BEOS) |
|
624 |
- if(new_sd != -1 && fstat(socketd, &st_buf) == -1) { |
|
625 |
- logg("!fstat(): socket descriptor gone\n"); |
|
626 |
- memmove(socketds, socketds + 1, sizeof(socketds[0]) * nsockets); |
|
627 |
- nsockets--; |
|
628 |
- if(!nsockets) { |
|
629 |
- logg("!Main socket gone: fatal\n"); |
|
630 |
- break; |
|
631 |
- } |
|
632 |
- } |
|
633 |
-#endif |
|
634 |
- if (new_sd != -1) |
|
635 |
- new_sd = accept(socketd, NULL, NULL); |
|
636 |
- if((new_sd == -1) && (errno != EINTR)) { |
|
633 |
+ time(&start_time); |
|
634 |
+ for(;;) { |
|
635 |
+ int new_sd; |
|
636 |
+ /* Block waiting for connection on any of the sockets */ |
|
637 |
+ pthread_mutex_lock(&fds->buf_mutex); |
|
638 |
+ new_sd = fds_poll_recv(fds, -1, 1); |
|
639 |
+ |
|
640 |
+ if (!fds->nfds) { |
|
641 |
+ /* at least the dummy/sync pipe should have remained */ |
|
642 |
+ logg("!All recv() descriptors gone: fatal\n"); |
|
637 | 643 |
pthread_mutex_lock(&exit_mutex); |
638 |
- if(progexit) { |
|
639 |
- pthread_mutex_unlock(&exit_mutex); |
|
640 |
- break; |
|
641 |
- } |
|
644 |
+ progexit = 1; |
|
642 | 645 |
pthread_mutex_unlock(&exit_mutex); |
643 |
- /* very bad - need to exit or restart */ |
|
644 |
-#ifdef HAVE_STRERROR_R |
|
645 |
- strerror_r(errno, buff, BUFFSIZE); |
|
646 |
- logg("!accept() failed: %s\n", buff); |
|
647 |
-#else |
|
648 |
- logg("!accept() failed\n"); |
|
649 |
-#endif |
|
650 |
- continue; |
|
646 |
+ pthread_mutex_unlock(&fds->buf_mutex); |
|
647 |
+ break; |
|
651 | 648 |
} |
652 | 649 |
|
653 |
- if (sighup) { |
|
654 |
- logg("SIGHUP caught: re-opening log file.\n"); |
|
655 |
- logg_close(); |
|
656 |
- sighup = 0; |
|
657 |
- if(!logg_file && (opt = optget(opts, "LogFile"))->enabled) |
|
658 |
- logg_file = opt->strarg; |
|
650 |
+ if (new_sd == -1 && errno != EINTR) { |
|
651 |
+ logg("!Failed to poll sockets, fatal\n"); |
|
652 |
+ pthread_mutex_lock(&exit_mutex); |
|
653 |
+ progexit = 1; |
|
654 |
+ pthread_mutex_unlock(&exit_mutex); |
|
659 | 655 |
} |
660 | 656 |
|
661 |
- if (!progexit && new_sd >= 0) { |
|
662 |
- client_conn = (client_conn_t *) malloc(sizeof(struct client_conn_tag)); |
|
663 |
- if(client_conn) { |
|
664 |
- client_conn->sd = new_sd; |
|
665 |
- client_conn->options = options; |
|
666 |
- client_conn->opts = opts; |
|
667 |
- if(cl_engine_addref(engine)) { |
|
668 |
- closesocket(client_conn->sd); |
|
669 |
- free(client_conn); |
|
670 |
- logg("!cl_engine_addref() failed\n"); |
|
671 |
- pthread_mutex_lock(&exit_mutex); |
|
672 |
- progexit = 1; |
|
673 |
- pthread_mutex_unlock(&exit_mutex); |
|
674 |
- } else { |
|
675 |
- client_conn->engine = engine; |
|
676 |
- client_conn->engine_timestamp = reloaded_time; |
|
677 |
- client_conn->socketds = socketds; |
|
678 |
- client_conn->nsockets = nsockets; |
|
679 |
- if(!thrmgr_dispatch(thr_pool, client_conn)) { |
|
680 |
- closesocket(client_conn->sd); |
|
681 |
- free(client_conn); |
|
682 |
- logg("!thread dispatch failed\n"); |
|
657 |
+ i = (rr_last + 1) % fds->nfds; |
|
658 |
+ for (j = 0; j < fds->nfds && new_sd >= 0; j++, i = (i+1) % fds->nfds) { |
|
659 |
+ size_t pos = 0; |
|
660 |
+ int error = 0; |
|
661 |
+ struct fd_buf *buf = &fds->buf[i]; |
|
662 |
+ if (!buf->got_newdata) |
|
663 |
+ continue; |
|
664 |
+ |
|
665 |
+ if (buf->fd == acceptdata.syncpipe_wake_recv[0]) { |
|
666 |
+ /* dummy sync pipe, just to wake us */ |
|
667 |
+ if (read(buf->fd, buff, sizeof(buff)) < 0) { |
|
668 |
+ logg("^Syncpipe read failed\n"); |
|
669 |
+ } |
|
670 |
+ continue; |
|
671 |
+ } |
|
672 |
+ |
|
673 |
+ if (buf->got_newdata == -1) { |
|
674 |
+ if (buf->mode == MODE_WAITREPLY) { |
|
675 |
+ logg("*RECVTH: mode WAIT_REPLY -> closed\n"); |
|
676 |
+ buf->fd = -1; |
|
677 |
+ continue; |
|
678 |
+ } else { |
|
679 |
+ logg("*RECVTH: client read error or EOF on read\n"); |
|
680 |
+ error = 1; |
|
681 |
+ } |
|
682 |
+ } |
|
683 |
+ |
|
684 |
+ rr_last = i; |
|
685 |
+ if (buf->mode == MODE_WAITANCILL) { |
|
686 |
+ buf->mode = MODE_COMMAND; |
|
687 |
+ logg("*RECVTH: mode -> MODE_COMMAND\n"); |
|
688 |
+ } |
|
689 |
+ while (!error && buf->fd != -1 && buf->buffer && pos < buf->off && |
|
690 |
+ buf->mode != MODE_WAITANCILL) { |
|
691 |
+ client_conn_t conn; |
|
692 |
+ const unsigned char *cmd = NULL; |
|
693 |
+ size_t cmdlen = 0; |
|
694 |
+ char term = '\n'; |
|
695 |
+ int rc; |
|
696 |
+ /* New data available to read on socket. */ |
|
697 |
+ |
|
698 |
+ memset(&conn, 0, sizeof(conn)); |
|
699 |
+ conn.scanfd = buf->recvfd; |
|
700 |
+ buf->recvfd = -1; |
|
701 |
+ conn.sd = buf->fd; |
|
702 |
+ conn.options = options; |
|
703 |
+ conn.opts = opts; |
|
704 |
+ conn.thrpool = thr_pool; |
|
705 |
+ conn.engine = engine; |
|
706 |
+ conn.group = buf->group; |
|
707 |
+ conn.id = buf->id; |
|
708 |
+ conn.quota = buf->quota; |
|
709 |
+ conn.filename = buf->dumpname; |
|
710 |
+ conn.mode = buf->mode; |
|
711 |
+ /* Parse & dispatch commands */ |
|
712 |
+ while ((conn.mode == MODE_COMMAND) && |
|
713 |
+ (cmd = get_cmd(buf, pos, &cmdlen, &term)) != NULL) { |
|
714 |
+ const char *argument; |
|
715 |
+ enum commands cmdtype = parse_command(cmd, &argument); |
|
716 |
+ logg("*RECVTH: got command %s (%u, %u), argument: %s\n", |
|
717 |
+ cmd, (unsigned)cmdlen, (unsigned)cmdtype, argument ? argument : ""); |
|
718 |
+ if (cmdtype == COMMAND_FILDES) { |
|
719 |
+ if (buf->buffer + buf->off <= cmd + strlen("FILDES\n")) { |
|
720 |
+ /* we need the extra byte from recvmsg */ |
|
721 |
+ conn.mode = MODE_WAITANCILL; |
|
722 |
+ buf->mode = MODE_WAITANCILL; |
|
723 |
+ cmdlen = 0; |
|
724 |
+ logg("*RECVTH: mode -> MODE_WAITANCILL\n"); |
|
725 |
+ break; |
|
683 | 726 |
} |
727 |
+ /* eat extra \0 for controlmsg */ |
|
728 |
+ cmdlen++; |
|
729 |
+ logg("*RECVTH: FILDES command complete\n"); |
|
684 | 730 |
} |
685 |
- } else { |
|
686 |
- logg("!Can't allocate memory for client_conn\n"); |
|
687 |
- closesocket(new_sd); |
|
688 |
- if(optget(opts, "ExitOnOOM")->enabled) { |
|
689 |
- pthread_mutex_lock(&exit_mutex); |
|
690 |
- progexit = 1; |
|
691 |
- pthread_mutex_unlock(&exit_mutex); |
|
731 |
+ |
|
732 |
+ conn.term = term; |
|
733 |
+ buf->term = term; |
|
734 |
+ |
|
735 |
+ if ((rc = execute_or_dispatch_command(&conn, cmdtype, argument)) < 0) { |
|
736 |
+ logg("!Command dispatch failed\n"); |
|
737 |
+ if(rc == -1 && optget(opts, "ExitOnOOM")->enabled) { |
|
738 |
+ pthread_mutex_lock(&exit_mutex); |
|
739 |
+ progexit = 1; |
|
740 |
+ pthread_mutex_unlock(&exit_mutex); |
|
741 |
+ } |
|
742 |
+ error = 1; |
|
743 |
+ } |
|
744 |
+ if (error || !conn.group || rc) { |
|
745 |
+ if (rc && thrmgr_group_finished(conn.group, EXIT_OK)) { |
|
746 |
+ logg("*RECVTH: closing conn, group finished\n"); |
|
747 |
+ /* if there are no more active jobs */ |
|
748 |
+ shutdown(conn.sd, 2); |
|
749 |
+ closesocket(conn.sd); |
|
750 |
+ buf->fd = -1; |
|
751 |
+ conn.group = NULL; |
|
752 |
+ } else if (conn.mode != MODE_STREAM) { |
|
753 |
+ logg("*RECVTH: mode -> MODE_WAITREPLY\n"); |
|
754 |
+ /* no more commands are accepted */ |
|
755 |
+ conn.mode = MODE_WAITREPLY; |
|
756 |
+ /* Stop monitoring this FD, it will be closed either |
|
757 |
+ * by us, or by the scanner thread. |
|
758 |
+ * Never close a file descriptor that is being |
|
759 |
+ * monitored by poll()/select() from another thread, |
|
760 |
+ * because this can lead to subtle bugs such as: |
|
761 |
+ * Other thread closes file descriptor -> POLLHUP is |
|
762 |
+ * set, but the poller thread doesn't wake up yet. |
|
763 |
+ * Another client opens a connection and sends some |
|
764 |
+ * data. If the socket reuses the previous file descriptor, |
|
765 |
+ * then POLLIN is set on the file descriptor too. |
|
766 |
+ * When poll() wakes up it sees POLLIN | POLLHUP |
|
767 |
+ * and thinks that the client has sent some data, |
|
768 |
+ * and closed the connection, so clamd closes the |
|
769 |
+ * connection in turn resulting in a bug. |
|
770 |
+ * |
|
771 |
+ * If we wouldn't have poll()-ed the file descriptor |
|
772 |
+ * we closed in another thread, but rather made sure |
|
773 |
+ * that we don't put a FD that we're about to close |
|
774 |
+ * into poll()'s list of watched fds; then POLLHUP |
|
775 |
+ * would be set, but the file descriptor would stay |
|
776 |
+ * open, until we wake up from poll() and close it. |
|
777 |
+ * Thus a new connection won't be able to reuse the |
|
778 |
+ * same FD, and there is no bug. |
|
779 |
+ * */ |
|
780 |
+ buf->fd = -1; |
|
781 |
+ } |
|
782 |
+ } |
|
783 |
+ pos += cmdlen+1; |
|
784 |
+ if (conn.mode == MODE_STREAM) { |
|
785 |
+ /* TODO: this doesn't belong here */ |
|
786 |
+ buf->dumpname = conn.filename; |
|
787 |
+ buf->dumpfd = conn.scanfd; |
|
788 |
+ logg("*RECVTH: STREAM: %s fd %u\n", buf->dumpname, buf->dumpfd); |
|
789 |
+ } |
|
790 |
+ if (conn.mode != MODE_COMMAND) { |
|
791 |
+ logg("*RECVTH: breaking command loop, mode is no longer MODE_COMMAND\n"); |
|
792 |
+ break; |
|
692 | 793 |
} |
794 |
+ conn.id++; |
|
693 | 795 |
} |
796 |
+ buf->mode = conn.mode; |
|
797 |
+ buf->id = conn.id; |
|
798 |
+ buf->group = conn.group; |
|
799 |
+ buf->quota = conn.quota; |
|
800 |
+ if (!error) { |
|
801 |
+ /* move partial command to beginning of buffer */ |
|
802 |
+ if (pos < buf->off) { |
|
803 |
+ memmove (buf->buffer, &buf->buffer[pos], buf->off - pos); |
|
804 |
+ buf->off -= pos; |
|
805 |
+ } else |
|
806 |
+ buf->off = 0; |
|
807 |
+ if (buf->off) |
|
808 |
+ logg("*RECVTH: moved partial command: %lu\n", (unsigned long)buf->off); |
|
809 |
+ else |
|
810 |
+ logg("*RECVTH: consumed entire command\n"); |
|
811 |
+ } |
|
812 |
+ if (conn.mode == MODE_COMMAND && !cmd) |
|
813 |
+ break; |
|
814 |
+ if (!error && buf->mode == MODE_WAITREPLY && buf->off) { |
|
815 |
+ /* Client is not supposed to send anything more */ |
|
816 |
+ logg("^Client sent garbage after last command: %lu bytes\n", (unsigned long)buf->off); |
|
817 |
+ buf->buffer[buf->off] = '\0'; |
|
818 |
+ logg("*RECVTH: garbage: %s\n", buf->buffer); |
|
819 |
+ error = 1; |
|
820 |
+ } |
|
821 |
+ if (!error && buf->mode == MODE_STREAM) { |
|
822 |
+ logg("*RECVTH: mode == MODE_STREAM\n"); |
|
823 |
+ if (!buf->chunksize) { |
|
824 |
+ /* read chunksize */ |
|
825 |
+ if (buf->off >= 4) { |
|
826 |
+ uint32_t cs = *(uint32_t*)buf->buffer; |
|
827 |
+ buf->chunksize = ntohl(cs); |
|
828 |
+ logg("*RECVTH: chunksize: %u\n", buf->chunksize); |
|
829 |
+ if (!buf->chunksize) { |
|
830 |
+ /* chunksize 0 marks end of stream */ |
|
831 |
+ conn.scanfd = buf->dumpfd; |
|
832 |
+ conn.term = buf->term; |
|
833 |
+ buf->dumpfd = -1; |
|
834 |
+ buf->mode = buf->group ? MODE_COMMAND : MODE_WAITREPLY; |
|
835 |
+ logg("*RECVTH: chunks complete\n"); |
|
836 |
+ buf->dumpname = NULL; |
|
837 |
+ if ((rc = execute_or_dispatch_command(&conn, COMMAND_INSTREAMSCAN, NULL)) < 0) { |
|
838 |
+ logg("!Command dispatch failed\n"); |
|
839 |
+ if(rc == -1 && optget(opts, "ExitOnOOM")->enabled) { |
|
840 |
+ pthread_mutex_lock(&exit_mutex); |
|
841 |
+ progexit = 1; |
|
842 |
+ pthread_mutex_unlock(&exit_mutex); |
|
843 |
+ } |
|
844 |
+ error = 1; |
|
845 |
+ } else { |
|
846 |
+ pos = 4; |
|
847 |
+ memmove (buf->buffer, &buf->buffer[pos], buf->off - pos); |
|
848 |
+ buf->off -= pos; |
|
849 |
+ pos = 0; |
|
850 |
+ buf->id++; |
|
851 |
+ continue; |
|
852 |
+ } |
|
853 |
+ } |
|
854 |
+ if (buf->chunksize > buf->quota) { |
|
855 |
+ logg("^INSTREAM: Size limit reached, (requested: %lu, max: %lu)\n", |
|
856 |
+ (unsigned long)buf->chunksize, (unsigned long)buf->quota); |
|
857 |
+ conn_reply_error(&conn, "INSTREAM size limit exceeded. ERROR"); |
|
858 |
+ error = 1; |
|
859 |
+ } else { |
|
860 |
+ buf->quota -= buf->chunksize; |
|
861 |
+ } |
|
862 |
+ logg("*RECVTH: quota: %lu\n", buf->quota); |
|
863 |
+ pos = 4; |
|
864 |
+ } else |
|
865 |
+ break; |
|
866 |
+ } else |
|
867 |
+ pos = 0; |
|
868 |
+ if (pos + buf->chunksize < buf->off) |
|
869 |
+ cmdlen = buf->chunksize; |
|
870 |
+ else |
|
871 |
+ cmdlen = buf->off - pos; |
|
872 |
+ buf->chunksize -= cmdlen; |
|
873 |
+ if (cli_writen(buf->dumpfd, buf->buffer + pos, cmdlen) < 0) { |
|
874 |
+ conn_reply_error(&conn, "Error writing to temporary file"); |
|
875 |
+ logg("!INSTREAM: Can't write to temporary file.\n"); |
|
876 |
+ error = 1; |
|
877 |
+ } |
|
878 |
+ logg("*RECVTH: processed %lu bytes of chunkdata\n", cmdlen); |
|
879 |
+ pos += cmdlen; |
|
880 |
+ if (pos == buf->off) { |
|
881 |
+ buf->off = 0; |
|
882 |
+ } |
|
883 |
+ } |
|
884 |
+ if (error) { |
|
885 |
+ conn_reply_error(&conn, "Error processing command."); |
|
886 |
+ } |
|
887 |
+ } |
|
888 |
+ if (error) { |
|
889 |
+ if (buf->dumpfd != -1) { |
|
890 |
+ close(buf->dumpfd); |
|
891 |
+ if (buf->dumpname) { |
|
892 |
+ cli_unlink(buf->dumpname); |
|
893 |
+ free(buf->dumpname); |
|
894 |
+ } |
|
895 |
+ buf->dumpfd = -1; |
|
896 |
+ } |
|
897 |
+ if (thrmgr_group_terminate(buf->group)) { |
|
898 |
+ logg("*RECVTH: shutting down socket after error\n"); |
|
899 |
+ shutdown(buf->fd, 2); |
|
900 |
+ closesocket(buf->fd); |
|
901 |
+ } else |
|
902 |
+ logg("*RECVTH: socket not shut down due to active tasks\n"); |
|
903 |
+ buf->fd = -1; |
|
904 |
+ } |
|
694 | 905 |
} |
906 |
+ pthread_mutex_unlock(&fds->buf_mutex); |
|
695 | 907 |
|
908 |
+ /* handle progexit */ |
|
696 | 909 |
pthread_mutex_lock(&exit_mutex); |
697 |
- if(progexit) { |
|
698 |
-#ifdef C_WINDOWS |
|
699 |
- closesocket(new_sd); |
|
700 |
-#else |
|
701 |
- if(new_sd >= 0) |
|
702 |
- close(new_sd); |
|
703 |
-#endif |
|
910 |
+ if (progexit) { |
|
704 | 911 |
pthread_mutex_unlock(&exit_mutex); |
912 |
+ pthread_mutex_lock(&fds->buf_mutex); |
|
913 |
+ for (i=0;i < fds->nfds; i++) { |
|
914 |
+ if (fds->buf[i].fd == -1) |
|
915 |
+ continue; |
|
916 |
+ if (thrmgr_group_terminate(fds->buf[i].group)) { |
|
917 |
+ shutdown(fds->buf[i].fd, 2); |
|
918 |
+ closesocket(fds->buf[i].fd); |
|
919 |
+ fds->buf[i].fd = -1; |
|
920 |
+ } |
|
921 |
+ } |
|
922 |
+ pthread_mutex_unlock(&fds->buf_mutex); |
|
705 | 923 |
break; |
706 | 924 |
} |
707 | 925 |
pthread_mutex_unlock(&exit_mutex); |
708 | 926 |
|
927 |
+ /* SIGHUP */ |
|
928 |
+ if (sighup) { |
|
929 |
+ logg("SIGHUP caught: re-opening log file.\n"); |
|
930 |
+ logg_close(); |
|
931 |
+ sighup = 0; |
|
932 |
+ if(!logg_file && (opt = optget(opts, "LogFile"))->enabled) |
|
933 |
+ logg_file = opt->strarg; |
|
934 |
+ } |
|
935 |
+ |
|
936 |
+ /* SelfCheck */ |
|
709 | 937 |
if(selfchk) { |
710 | 938 |
time(¤t_time); |
711 | 939 |
if((current_time - start_time) > (time_t)selfchk) { |
... | ... |
@@ -718,18 +1069,15 @@ int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigne |
718 | 718 |
} |
719 | 719 |
} |
720 | 720 |
|
721 |
+ /* DB reload */ |
|
721 | 722 |
pthread_mutex_lock(&reload_mutex); |
722 | 723 |
if(reload) { |
723 | 724 |
pthread_mutex_unlock(&reload_mutex); |
724 | 725 |
engine = reload_db(engine, dboptions, opts, FALSE, &ret); |
725 | 726 |
if(ret) { |
726 | 727 |
logg("Terminating because of a fatal error.\n"); |
727 |
-#ifdef C_WINDOWS |
|
728 |
- closesocket(new_sd); |
|
729 |
-#else |
|
730 | 728 |
if(new_sd >= 0) |
731 |
- close(new_sd); |
|
732 |
-#endif |
|
729 |
+ closesocket(new_sd); |
|
733 | 730 |
break; |
734 | 731 |
} |
735 | 732 |
|
... | ... |
@@ -751,9 +1099,16 @@ int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigne |
751 | 751 |
} |
752 | 752 |
} |
753 | 753 |
|
754 |
+ pthread_mutex_lock(&exit_mutex); |
|
755 |
+ progexit = 1; |
|
756 |
+ pthread_mutex_unlock(&exit_mutex); |
|
757 |
+ if (write(acceptdata.syncpipe_wake_accept[1], "", 1) < 0) { |
|
758 |
+ logg("^Write to syncpipe failed\n"); |
|
759 |
+ } |
|
754 | 760 |
/* Destroy the thread manager. |
755 | 761 |
* This waits for all current tasks to end |
756 | 762 |
*/ |
763 |
+ logg("*Waiting for all threads to finish\n"); |
|
757 | 764 |
thrmgr_destroy(thr_pool); |
758 | 765 |
#ifdef CLAMUKO |
759 | 766 |
if(optget(opts, "ClamukoScanOnAccess")->enabled) { |
... | ... |
@@ -767,8 +1122,12 @@ int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigne |
767 | 767 |
cl_engine_free(engine); |
768 | 768 |
} |
769 | 769 |
|
770 |
- if(dbstat) |
|
771 |
- cl_statfree(dbstat); |
|
770 |
+ pthread_join(accept_th, NULL); |
|
771 |
+ fds_free(fds); |
|
772 |
+ close(acceptdata.syncpipe_wake_accept[1]); |
|
773 |
+ close(acceptdata.syncpipe_wake_recv[1]); |
|
774 |
+ if(dbstat.entries) |
|
775 |
+ cl_statfree(&dbstat); |
|
772 | 776 |
logg("*Shutting down the main socket%s.\n", (nsockets > 1) ? "s" : ""); |
773 | 777 |
for (i = 0; i < nsockets; i++) |
774 | 778 |
shutdown(socketds[i], 2); |
... | ... |
@@ -25,7 +25,8 @@ |
25 | 25 |
|
26 | 26 |
#include "libclamav/clamav.h" |
27 | 27 |
#include "shared/optparser.h" |
28 |
- |
|
28 |
+#include "thrmgr.h" |
|
29 |
+#include "session.h" |
|
29 | 30 |
struct thrarg { |
30 | 31 |
int sid; |
31 | 32 |
int options; |
... | ... |
@@ -43,9 +44,12 @@ struct thrwarg { |
43 | 43 |
unsigned int options; |
44 | 44 |
}; |
45 | 45 |
|
46 |
-int acceptloop_th(int *socketds, int nsockets, struct cl_engine *engine, unsigned int dboptions, const struct optstruct *opts); |
|
46 |
+int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsigned int dboptions, const struct optstruct *opts); |
|
47 | 47 |
void sighandler(int sig); |
48 | 48 |
void sighandler_th(int sig); |
49 | 49 |
void sigsegv(int sig); |
50 | 50 |
|
51 |
+extern pthread_mutex_t exit_mutex, reload_mutex; |
|
52 |
+extern int progexit, reload; |
|
53 |
+ |
|
51 | 54 |
#endif |
... | ... |
@@ -67,152 +67,416 @@ |
67 | 67 |
#include "session.h" |
68 | 68 |
#include "thrmgr.h" |
69 | 69 |
|
70 |
-#ifdef HAVE_FD_PASSING |
|
71 |
-static int recvfd_and_scan(int desc, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts) |
|
70 |
+static struct { |
|
71 |
+ const char *cmd; |
|
72 |
+ const size_t len; |
|
73 |
+ enum commands cmdtype; |
|
74 |
+ int need_arg; |
|
75 |
+} commands[] = { |
|
76 |
+ {CMD1, sizeof(CMD1)-1, COMMAND_SCAN, 1}, |
|
77 |
+ {CMD3, sizeof(CMD3)-1, COMMAND_SHUTDOWN, 0}, |
|
78 |
+ {CMD4, sizeof(CMD4)-1, COMMAND_RELOAD, 0}, |
|
79 |
+ {CMD5, sizeof(CMD5)-1, COMMAND_PING, 0}, |
|
80 |
+ {CMD6, sizeof(CMD6)-1, COMMAND_CONTSCAN, 1}, |
|
81 |
+ {CMD7, sizeof(CMD7)-1, COMMAND_VERSION, 0}, |
|
82 |
+ {CMD8, sizeof(CMD8)-1, COMMAND_STREAM, 0}, |
|
83 |
+ {CMD10, sizeof(CMD10)-1, COMMAND_END, 0}, |
|
84 |
+ {CMD11, sizeof(CMD11)-1, COMMAND_SHUTDOWN, 0}, |
|
85 |
+ {CMD13, sizeof(CMD13)-1, COMMAND_MULTISCAN, 1}, |
|
86 |
+ {CMD14, sizeof(CMD14)-1, COMMAND_FILDES, 0}, |
|
87 |
+ {CMD15, sizeof(CMD15)-1, COMMAND_STATS, 0}, |
|
88 |
+ {CMD16, sizeof(CMD16)-1, COMMAND_IDSESSION, 0}, |
|
89 |
+ {CMD17, sizeof(CMD17)-1, COMMAND_INSTREAM, 0} |
|
90 |
+}; |
|
91 |
+ |
|
92 |
+ |
|
93 |
+enum commands parse_command(const char *cmd, const char **argument) |
|
72 | 94 |
{ |
73 |
- struct msghdr msg; |
|
74 |
- struct cmsghdr *cmsg; |
|
75 |
- unsigned char buf[CMSG_SPACE(sizeof(int))]; |
|
76 |
- struct iovec iov[1]; |
|
77 |
- char dummy; |
|
78 |
- int ret=-1; |
|
79 |
- |
|
80 |
- memset(&msg, 0, sizeof(msg)); |
|
81 |
- iov[0].iov_base = &dummy; |
|
82 |
- iov[0].iov_len = 1; |
|
83 |
- msg.msg_iov = iov; |
|
84 |
- msg.msg_iovlen = 1; |
|
85 |
- msg.msg_control = buf; |
|
86 |
- msg.msg_controllen = sizeof(buf); |
|
87 |
- |
|
88 |
- if (recvmsg(desc, &msg, 0) == -1) { |
|
89 |
- logg("recvmsg failed: %s!", strerror(errno)); |
|
90 |
- return -1; |
|
91 |
- } |
|
92 |
- if ((msg.msg_flags & MSG_TRUNC) || (msg.msg_flags & MSG_CTRUNC)) { |
|
93 |
- logg("control message truncated"); |
|
94 |
- return -1; |
|
95 |
- } |
|
96 |
- for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; |
|
97 |
- cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
|
98 |
- if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) && |
|
99 |
- cmsg->cmsg_level == SOL_SOCKET && |
|
100 |
- cmsg->cmsg_type == SCM_RIGHTS) { |
|
101 |
- int fd = *(int *)CMSG_DATA(cmsg); |
|
102 |
- ret = scanfd(fd, NULL, engine, options, opts, desc); |
|
103 |
- close(fd); |
|
95 |
+ size_t i; |
|
96 |
+ *argument = NULL; |
|
97 |
+ for (i=0; i < sizeof(commands)/sizeof(commands[0]); i++) { |
|
98 |
+ const size_t len = commands[i].len; |
|
99 |
+ if (!strncmp(cmd, commands[i].cmd, len)) { |
|
100 |
+ const char *arg = cmd + len; |
|
101 |
+ if (commands[i].need_arg) { |
|
102 |
+ if (!*arg) {/* missing argument */ |
|
103 |
+ logg("*Command %s missing argument!\n", commands[i].cmd); |
|
104 |
+ return COMMAND_UNKNOWN; |
|
105 |
+ } |
|
106 |
+ *argument = arg+1; |
|
107 |
+ } else { |
|
108 |
+ if (*arg) {/* extra stuff after command */ |
|
109 |
+ logg("*Command %s has trailing garbage!\n", commands[i].cmd); |
|
110 |
+ return COMMAND_UNKNOWN; |
|
104 | 111 |
} |
112 |
+ *argument = NULL; |
|
113 |
+ } |
|
114 |
+ return commands[i].cmdtype; |
|
105 | 115 |
} |
106 |
- return ret; |
|
116 |
+ } |
|
117 |
+ return COMMAND_UNKNOWN; |
|
107 | 118 |
} |
108 | 119 |
|
109 |
-#else |
|
110 |
-static int recvfd_and_scan(int desc, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts) |
|
120 |
+int conn_reply_single(const client_conn_t *conn, const char *path, const char *status) |
|
111 | 121 |
{ |
112 |
- mdprintf(desc, "ERROR: FILDES support not compiled in\n"); |
|
113 |
- return -1; |
|
122 |
+ if (conn->id) { |
|
123 |
+ if (path) |
|
124 |
+ return mdprintf(conn->sd, "%u: %s: %s%c", conn->id, path, status, conn->term); |
|
125 |
+ return mdprintf(conn->sd, "%u: %s%c", conn->id, status, conn->term); |
|
126 |
+ } |
|
127 |
+ if (path) |
|
128 |
+ return mdprintf(conn->sd, "%s: %s%c", path, status, conn->term); |
|
129 |
+ return mdprintf(conn->sd, "%s%c", status, conn->term); |
|
114 | 130 |
} |
131 |
+ |
|
132 |
+int conn_reply(const client_conn_t *conn, const char *path, |
|
133 |
+ const char *msg, const char *status) |
|
134 |
+{ |
|
135 |
+ if (conn->id) { |
|
136 |
+ if (path) |
|
137 |
+ return mdprintf(conn->sd, "%u: %s: %s %s%c", conn->id, path, msg, |
|
138 |
+ status, conn->term); |
|
139 |
+ return mdprintf(conn->sd, "%u: %s %s%c", conn->id, msg, status, |
|
140 |
+ conn->term); |
|
141 |
+ } |
|
142 |
+ if (path) |
|
143 |
+ return mdprintf(conn->sd, "%s: %s %s%c", path, msg, status, conn->term); |
|
144 |
+ return mdprintf(conn->sd, "%s %s%c", msg, status, conn->term); |
|
145 |
+} |
|
146 |
+ |
|
147 |
+int conn_reply_error(const client_conn_t *conn, const char *msg) |
|
148 |
+{ |
|
149 |
+ return conn_reply(conn, NULL, msg, "ERROR"); |
|
150 |
+} |
|
151 |
+ |
|
152 |
+#define BUFFSIZE 1024 |
|
153 |
+int conn_reply_errno(const client_conn_t *conn, const char *path, |
|
154 |
+ const char *msg) |
|
155 |
+{ |
|
156 |
+ char buf[BUFFSIZE + sizeof(". ERROR")]; |
|
157 |
+#ifdef HAVE_STRERROR_R |
|
158 |
+ strerror_r(errno, buf, BUFFSIZE-1); |
|
159 |
+ strcat(buf, ". ERROR"); |
|
160 |
+#else |
|
161 |
+ snprintf(buf, sizeof(buf), "%u. ERROR", errno); |
|
115 | 162 |
#endif |
163 |
+ return conn_reply(conn, path, msg, buf); |
|
164 |
+} |
|
116 | 165 |
|
117 |
-int command(int desc, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, int timeout) |
|
166 |
+/* returns |
|
167 |
+ * -1 on fatal error (shutdown) |
|
168 |
+ * 0 on ok |
|
169 |
+ * >0 errors encountered |
|
170 |
+ */ |
|
171 |
+int command(client_conn_t *conn, int *virus) |
|
118 | 172 |
{ |
119 |
- char buff[1025]; |
|
120 |
- int bread; |
|
173 |
+ int desc = conn->sd; |
|
174 |
+ struct cl_engine *engine = conn->engine; |
|
175 |
+ unsigned int options = conn->options; |
|
176 |
+ const struct optstruct *opts = conn->opts; |
|
177 |
+ int type = -1; /* TODO: make this enum */ |
|
178 |
+ int maxdirrec; |
|
179 |
+ int ret = 0; |
|
180 |
+ int flags = CLI_FTW_STD; |
|
121 | 181 |
|
122 |
- bread = readsock(desc, buff, sizeof(buff)-1, '\n', timeout, 0, 1); |
|
123 |
- if(bread == -2) /* timeout */ |
|
124 |
- return -2; |
|
125 |
- if(bread == 0) /* Connection closed */ |
|
126 |
- return -1; |
|
127 |
- if(bread < 0) { |
|
128 |
- mdprintf(desc, "ERROR\n"); |
|
129 |
- logg("!Command: readsock() failed.\n"); |
|
130 |
- return -1; |
|
131 |
- } |
|
182 |
+ struct scan_cb_data scandata; |
|
183 |
+ struct cli_ftw_cbdata data; |
|
184 |
+ unsigned ok, error, total; |
|
185 |
+ jobgroup_t *group = NULL; |
|
132 | 186 |
|
133 |
- buff[bread] = 0; |
|
134 |
- cli_chomp(buff); |
|
187 |
+ if (thrmgr_group_need_terminate(conn->group)) { |
|
188 |
+ logg("^Client disconnected while command was active\n"); |
|
189 |
+ if (conn->scanfd != -1) |
|
190 |
+ close(conn->scanfd); |
|
191 |
+ return 1; |
|
192 |
+ } |
|
135 | 193 |
thrmgr_setactiveengine(engine); |
136 | 194 |
|
137 |
- if(!strncmp(buff, CMD1, strlen(CMD1))) { /* SCAN */ |
|
138 |
- thrmgr_setactivetask(NULL, CMD1); |
|
139 |
- if(scan(buff + strlen(CMD1) + 1, NULL, engine, options, opts, desc, TYPE_SCAN) == -2) |
|
140 |
- if(optget(opts, "ExitOnOOM")->enabled) |
|
141 |
- return COMMAND_SHUTDOWN; |
|
142 |
- |
|
143 |
- } else if(!strncmp(buff, CMD3, strlen(CMD3))) { /* QUIT */ |
|
144 |
- thrmgr_setactivetask(NULL, CMD3); |
|
145 |
- return COMMAND_SHUTDOWN; |
|
146 |
- |
|
147 |
- } else if(!strncmp(buff, CMD4, strlen(CMD4))) { /* RELOAD */ |
|
148 |
- thrmgr_setactivetask(NULL, CMD4); |
|
149 |
- /* we'll reload, hide the engine, if we are the last |
|
150 |
- * holding a ref to the engine it'll be freed, |
|
151 |
- * we don't want STATS command to access it */ |
|
152 |
- thrmgr_setactiveengine(NULL); |
|
153 |
- mdprintf(desc, "RELOADING\n"); |
|
154 |
- return COMMAND_RELOAD; |
|
155 |
- |
|
156 |
- } else if(!strncmp(buff, CMD5, strlen(CMD5))) { /* PING */ |
|
157 |
- thrmgr_setactivetask(NULL, CMD5); |
|
158 |
- mdprintf(desc, "PONG\n"); |
|
159 |
- |
|
160 |
- } else if(!strncmp(buff, CMD6, strlen(CMD6))) { /* CONTSCAN */ |
|
161 |
- thrmgr_setactivetask(NULL, CMD6); |
|
162 |
- if(scan(buff + strlen(CMD6) + 1, NULL, engine, options, opts, desc, TYPE_CONTSCAN) == -2) |
|
163 |
- if(optget(opts, "ExitOnOOM")->enabled) |
|
164 |
- return COMMAND_SHUTDOWN; |
|
165 |
- |
|
166 |
- } else if(!strncmp(buff, CMD7, strlen(CMD7))) { /* VERSION */ |
|
167 |
- uint32_t ver; |
|
168 |
- |
|
169 |
- thrmgr_setactivetask(NULL, CMD7); |
|
170 |
- cl_engine_get(engine, CL_ENGINE_DB_VERSION, &ver); |
|
171 |
- if(ver) { |
|
172 |
- char timestr[32]; |
|
173 |
- time_t t; |
|
174 |
- |
|
175 |
- cl_engine_get(engine, CL_ENGINE_DB_TIME, &t); |
|
176 |
- mdprintf(desc, "ClamAV %s/%u/%s", get_version(), (unsigned int) ver, cli_ctime(&t, timestr, sizeof(timestr))); |
|
177 |
- } else { |
|
178 |
- mdprintf(desc, "ClamAV %s\n", get_version()); |
|
179 |
- } |
|
195 |
+ data.data = &scandata; |
|
196 |
+ memset(&scandata, 0, sizeof(scandata)); |
|
197 |
+ scandata.id = conn->id; |
|
198 |
+ scandata.group = conn->group; |
|
199 |
+ scandata.odesc = desc; |
|
200 |
+ scandata.conn = conn; |
|
201 |
+ scandata.options = options; |
|
202 |
+ scandata.engine = engine; |
|
203 |
+ scandata.opts = opts; |
|
204 |
+ scandata.thr_pool = conn->thrpool; |
|
205 |
+ scandata.toplevel_path = conn->filename; |
|
180 | 206 |
|
181 |
- } else if(!strncmp(buff, CMD8, strlen(CMD8))) { /* STREAM */ |
|
182 |
- thrmgr_setactivetask(NULL, CMD8); |
|
183 |
- if(scanstream(desc, NULL, engine, options, opts) == CL_EMEM) |
|
184 |
- if(optget(opts, "ExitOnOOM")->enabled) |
|
185 |
- return COMMAND_SHUTDOWN; |
|
186 |
- |
|
187 |
- } else if(!strncmp(buff, CMD9, strlen(CMD9))) { /* SESSION */ |
|
188 |
- thrmgr_setactivetask(NULL, CMD9); |
|
189 |
- return COMMAND_SESSION; |
|
190 |
- |
|
191 |
- } else if(!strncmp(buff, CMD10, strlen(CMD10))) { /* END */ |
|
192 |
- thrmgr_setactivetask(NULL, CMD10); |
|
193 |
- return COMMAND_END; |
|
194 |
- |
|
195 |
- } else if(!strncmp(buff, CMD11, strlen(CMD11))) { /* SHUTDOWN */ |
|
196 |
- thrmgr_setactivetask(NULL, CMD11); |
|
197 |
- return COMMAND_SHUTDOWN; |
|
198 |
- |
|
199 |
- } else if(!strncmp(buff, CMD13, strlen(CMD13))) { /* MULTISCAN */ |
|
200 |
- thrmgr_setactivetask(buff+strlen(CMD13)+1, CMD13); |
|
201 |
- if(scan(buff + strlen(CMD13) + 1, NULL, engine, options, opts, desc, TYPE_MULTISCAN) == -2) |
|
202 |
- if(optget(opts, "ExitOnOOM")->enabled) |
|
203 |
- return COMMAND_SHUTDOWN; |
|
204 |
- |
|
205 |
- } else if(!strncmp(buff, CMD14, strlen(CMD14))) { /* FILDES */ |
|
206 |
- thrmgr_setactivetask(NULL, CMD14); |
|
207 |
- if(recvfd_and_scan(desc, engine, options, opts) == -2) |
|
208 |
- if(optget(opts, "ExitOnOOM")->enabled) |
|
209 |
- return COMMAND_SHUTDOWN; |
|
210 |
- } else if(!strncmp(buff, CMD15, strlen(CMD15))) { /* STATS */ |
|
211 |
- thrmgr_setactivetask(NULL, CMD15); |
|
207 |
+ switch (conn->cmdtype) { |
|
208 |
+ case COMMAND_SCAN: |
|
209 |
+ thrmgr_setactivetask(NULL, "SCAN"); |
|
210 |
+ type = TYPE_SCAN; |
|
211 |
+ break; |
|
212 |
+ case COMMAND_CONTSCAN: |
|
213 |
+ thrmgr_setactivetask(NULL, "CONTSCAN"); |
|
214 |
+ type = TYPE_CONTSCAN; |
|
215 |
+ break; |
|
216 |
+ case COMMAND_MULTISCAN: |
|
217 |
+ flags &= ~CLI_FTW_NEED_STAT; |
|
218 |
+ thrmgr_setactivetask(NULL, "MULTISCAN"); |
|
219 |
+ type = TYPE_MULTISCAN; |
|
220 |
+ scandata.group = group = thrmgr_group_new(); |
|
221 |
+ break; |
|
222 |
+ case COMMAND_MULTISCANFILE: |
|
223 |
+ thrmgr_setactivetask(NULL, "MULTISCANFILE"); |
|
224 |
+ scandata.group = NULL; |
|
225 |
+ scandata.type = TYPE_SCAN; |
|
226 |
+ scandata.thr_pool = NULL; |
|
227 |
+ /* TODO: check ret value */ |
|
228 |
+ scan_callback(NULL, conn->filename, conn->filename, visit_file, &data); |
|
229 |
+ /* callback freed it */ |
|
230 |
+ conn->filename = NULL; |
|
231 |
+ *virus = scandata.infected; |
|
232 |
+ return 0; |
|
233 |
+ case COMMAND_FILDES: |
|
234 |
+ thrmgr_setactivetask(NULL, "FILDES"); |
|
235 |
+#ifdef HAVE_FD_PASSING |
|
236 |
+ if (conn->scanfd == -1) |
|
237 |
+ conn_reply_error(conn, "FILDES: didn't receive file descriptor."); |
|
238 |
+ else { |
|
239 |
+ ret = scanfd(conn->scanfd, conn, NULL, engine, options, opts, desc, 0); |
|
240 |
+ if (ret == CL_VIRUS) |
|
241 |
+ *virus = 1; |
|
242 |
+ if (ret == CL_EMEM) { |
|
243 |
+ if(optget(opts, "ExitOnOOM")->enabled) |
|
244 |
+ ret = -1; |
|
245 |
+ } else |
|
246 |
+ ret = 0; |
|
247 |
+ } |
|
248 |
+ logg("*SESSION: closed fd %d\n", conn->scanfd); |
|
249 |
+ close(conn->scanfd); |
|
250 |
+ return ret; |
|
251 |
+#else |
|
252 |
+ conn_reply_error(conn, "FILDES support not compiled in."); |
|
253 |
+ close(conn->scanfd); |
|
254 |
+ return 0; |
|
255 |
+#endif |
|
256 |
+ case COMMAND_STATS: |
|
257 |
+ thrmgr_setactivetask(NULL, "STATS"); |
|
212 | 258 |
thrmgr_printstats(desc); |
259 |
+ return 0; |
|
260 |
+ case COMMAND_STREAM: |
|
261 |
+ thrmgr_setactivetask(NULL, "STREAM"); |
|
262 |
+ ret = scanstream(desc, NULL, engine, options, opts, conn->term); |
|
263 |
+ if (ret == CL_VIRUS) |
|
264 |
+ *virus = 1; |
|
265 |
+ if (ret == CL_EMEM) { |
|
266 |
+ if(optget(opts, "ExitOnOOM")->enabled) |
|
267 |
+ return -1; |
|
268 |
+ } |
|
269 |
+ return 0; |
|
270 |
+ case COMMAND_INSTREAMSCAN: |
|
271 |
+ ret = scanfd(conn->scanfd, conn, NULL, engine, options, opts, desc, 1); |
|
272 |
+ if (ret == CL_VIRUS) |
|
273 |
+ *virus = 1; |
|
274 |
+ if (ret == CL_EMEM) { |
|
275 |
+ if(optget(opts, "ExitOnOOM")->enabled) |
|
276 |
+ ret = -1; |
|
277 |
+ } else |
|
278 |
+ ret = 0; |
|
279 |
+ if (ftruncate(conn->scanfd, 0) == -1) { |
|
280 |
+ /* not serious, we're going to close it and unlink it anyway */ |
|
281 |
+ logg("*ftruncate failed: %d\n", errno); |
|
282 |
+ } |
|
283 |
+ close(conn->scanfd); |
|
284 |
+ conn->scanfd = -1; |
|
285 |
+ cli_unlink(conn->filename); |
|
286 |
+ return ret; |
|
287 |
+ } |
|
288 |
+ |
|
289 |
+ scandata.type = type; |
|
290 |
+ maxdirrec = optget(opts, "MaxDirectoryRecursion")->numarg; |
|
291 |
+ if (optget(opts, "FollowDirectorySymlinks")->enabled) |
|
292 |
+ flags |= CLI_FTW_FOLLOW_DIR_SYMLINK; |
|
293 |
+ if (optget(opts, "FollowFileSymlinks")->enabled) |
|
294 |
+ flags |= CLI_FTW_FOLLOW_FILE_SYMLINK; |
|
295 |
+ if (cli_ftw(conn->filename, flags, maxdirrec ? maxdirrec : INT_MAX, scan_callback, &data) == CL_EMEM) |
|
296 |
+ if(optget(opts, "ExitOnOOM")->enabled) |
|
297 |
+ return -1; |
|
298 |
+ if (scandata.group && conn->cmdtype == COMMAND_MULTISCAN) { |
|
299 |
+ thrmgr_group_waitforall(group, &ok, &error, &total); |
|
213 | 300 |
} else { |
214 |
- mdprintf(desc, "UNKNOWN COMMAND\n"); |
|
301 |
+ error = scandata.errors; |
|
302 |
+ total = scandata.total; |
|
303 |
+ ok = total - error - scandata.infected; |
|
215 | 304 |
} |
216 | 305 |
|
217 |
- return 0; /* no error and no 'special' command executed */ |
|
306 |
+ if (ok + error == total && (error != total)) { |
|
307 |
+ conn_reply_single(conn, conn->filename, "OK"); |
|
308 |
+ } |
|
309 |
+ *virus = total - (ok + error); |
|
310 |
+ return error; |
|
311 |
+} |
|
312 |
+ |
|
313 |
+static int dispatch_command(client_conn_t *conn, enum commands cmd, const char *argument) |
|
314 |
+{ |
|
315 |
+ int ret = 0; |
|
316 |
+ client_conn_t *dup_conn = (client_conn_t *) malloc(sizeof(struct client_conn_tag)); |
|
317 |
+ |
|
318 |
+ if(!dup_conn) { |
|
319 |
+ logg("!Can't allocate memory for client_conn\n"); |
|
320 |
+ return -1; |
|
321 |
+ } |
|
322 |
+ memcpy(dup_conn, conn, sizeof(*conn)); |
|
323 |
+ dup_conn->cmdtype = cmd; |
|
324 |
+ if(cl_engine_addref(dup_conn->engine)) { |
|
325 |
+ logg("!cl_engine_addref() failed\n"); |
|
326 |
+ free(dup_conn); |
|
327 |
+ return -1; |
|
328 |
+ } |
|
329 |
+ dup_conn->scanfd = -1; |
|
330 |
+ switch (cmd) { |
|
331 |
+ case COMMAND_FILDES: |
|
332 |
+ if (conn->scanfd == -1) { |
|
333 |
+ conn_reply_error(dup_conn, "No file descriptor received."); |
|
334 |
+ ret = 1; |
|
335 |
+ } |
|
336 |
+ dup_conn->scanfd = conn->scanfd; |
|
337 |
+ /* consume FD */ |
|
338 |
+ conn->scanfd = -1; |
|
339 |
+ break; |
|
340 |
+ case COMMAND_SCAN: |
|
341 |
+ case COMMAND_CONTSCAN: |
|
342 |
+ case COMMAND_MULTISCAN: |
|
343 |
+ dup_conn->filename = strdup(argument); |
|
344 |
+ if (!dup_conn->filename) { |
|
345 |
+ logg("!Failed to allocate memory for filename\n"); |
|
346 |
+ ret = -1; |
|
347 |
+ } |
|
348 |
+ break; |
|
349 |
+ case COMMAND_INSTREAMSCAN: |
|
350 |
+ dup_conn->scanfd = conn->scanfd; |
|
351 |
+ conn->scanfd = -1; |
|
352 |
+ break; |
|
353 |
+ case COMMAND_STREAM: |
|
354 |
+ case COMMAND_STATS: |
|
355 |
+ /* just dispatch the command */ |
|
356 |
+ break; |
|
357 |
+ } |
|
358 |
+ if(!ret && !thrmgr_group_dispatch(dup_conn->thrpool, dup_conn->group, dup_conn)) { |
|
359 |
+ logg("!thread dispatch failed\n"); |
|
360 |
+ ret = -2; |
|
361 |
+ } |
|
362 |
+ if (ret) { |
|
363 |
+ cl_engine_free(dup_conn->engine); |
|
364 |
+ free(dup_conn); |
|
365 |
+ } |
|
366 |
+ return ret; |
|
367 |
+} |
|
368 |
+ |
|
369 |
+/* returns: |
|
370 |
+ * <0 for error |
|
371 |
+ * -1 out of memory |
|
372 |
+ * -2 other |
|
373 |
+ * 0 for async dispatched |
|
374 |
+ * 1 for command completed (connection can be closed) |
|
375 |
+ */ |
|
376 |
+int execute_or_dispatch_command(client_conn_t *conn, enum commands cmd, const char *argument) |
|
377 |
+{ |
|
378 |
+ int desc = conn->sd; |
|
379 |
+ char term = conn->term; |
|
380 |
+ const struct cl_engine *engine = conn->engine; |
|
381 |
+ /* execute commands that can be executed quickly on the recvloop thread, |
|
382 |
+ * these must: |
|
383 |
+ * - not involve any operation that can block for a long time, such as disk |
|
384 |
+ * I/O |
|
385 |
+ * - send of atomic message is allowed. |
|
386 |
+ * Dispatch other commands */ |
|
387 |
+ if (conn->group) { |
|
388 |
+ switch (cmd) { |
|
389 |
+ case COMMAND_FILDES: |
|
390 |
+ case COMMAND_SCAN: |
|
391 |
+ case COMMAND_END: |
|
392 |
+ case COMMAND_INSTREAM: |
|
393 |
+ case COMMAND_INSTREAMSCAN: |
|
394 |
+ /* These commands are accepted inside IDSESSION */ |
|
395 |
+ break; |
|
396 |
+ default: |
|
397 |
+ /* these commands are not recognized inside an IDSESSION */ |
|
398 |
+ conn_reply_error(conn, "Command invalid inside IDSESSION."); |
|
399 |
+ logg("*SESSION: command is not valid inside IDSESSION: %d\n", cmd); |
|
400 |
+ conn->group = NULL; |
|
401 |
+ return 1; |
|
402 |
+ } |
|
403 |
+ } |
|
404 |
+ |
|
405 |
+ switch (cmd) { |
|
406 |
+ case COMMAND_SHUTDOWN: |
|
407 |
+ pthread_mutex_lock(&exit_mutex); |
|
408 |
+ progexit = 1; |
|
409 |
+ pthread_mutex_unlock(&exit_mutex); |
|
410 |
+ return 1; |
|
411 |
+ case COMMAND_RELOAD: |
|
412 |
+ pthread_mutex_lock(&reload_mutex); |
|
413 |
+ reload = 1; |
|
414 |
+ pthread_mutex_unlock(&reload_mutex); |
|
415 |
+ mdprintf(desc, "RELOADING%c", term); |
|
416 |
+ /* we set reload flag, and we'll reload before closing the |
|
417 |
+ * connection */ |
|
418 |
+ return 1; |
|
419 |
+ case COMMAND_PING: |
|
420 |
+ mdprintf(desc, "PONG%c", term); |
|
421 |
+ return 1; |
|
422 |
+ case COMMAND_VERSION: |
|
423 |
+ { |
|
424 |
+ uint32_t ver; |
|
425 |
+ cl_engine_get(engine, CL_ENGINE_DB_VERSION, &ver); |
|
426 |
+ if(ver) { |
|
427 |
+ char timestr[32]; |
|
428 |
+ const char *tstr; |
|
429 |
+ time_t t; |
|
430 |
+ cl_engine_get(engine, CL_ENGINE_DB_TIME, &t); |
|
431 |
+ tstr = cli_ctime(&t, timestr, sizeof(timestr)); |
|
432 |
+ /* cut trailing \n */ |
|
433 |
+ timestr[strlen(tstr)-1] = '\0'; |
|
434 |
+ mdprintf(desc, "ClamAV %s/%u/%s%c", get_version(), (unsigned int) ver, tstr, term); |
|
435 |
+ } else { |
|
436 |
+ mdprintf(desc, "ClamAV %s%c", get_version(), conn->term); |
|
437 |
+ } |
|
438 |
+ return 1; |
|
439 |
+ } |
|
440 |
+ case COMMAND_INSTREAM: |
|
441 |
+ { |
|
442 |
+#if 0 |
|
443 |
+ if (!conn->group) { |
|
444 |
+ /* only valid inside IDSESSION */ |
|
445 |
+ conn_reply_single(conn, NULL, "UNKNOWN COMMAND"); |
|
446 |
+ return 1; |
|
447 |
+ } |
|
448 |
+#endif |
|
449 |
+ int rc = cli_gentempfd(NULL, &conn->filename, &conn->scanfd); |
|
450 |
+ if (rc != CL_SUCCESS) |
|
451 |
+ return rc; |
|
452 |
+ conn->quota = optget(conn->opts, "StreamMaxLength")->numarg; |
|
453 |
+ conn->mode = MODE_STREAM; |
|
454 |
+ return 0; |
|
455 |
+ } |
|
456 |
+ case COMMAND_STREAM: |
|
457 |
+ case COMMAND_MULTISCAN: |
|
458 |
+ case COMMAND_CONTSCAN: |
|
459 |
+ case COMMAND_STATS: |
|
460 |
+ case COMMAND_FILDES: |
|
461 |
+ case COMMAND_SCAN: |
|
462 |
+ case COMMAND_INSTREAMSCAN: |
|
463 |
+ return dispatch_command(conn, cmd, argument); |
|
464 |
+ case COMMAND_IDSESSION: |
|
465 |
+ conn->group = thrmgr_group_new(); |
|
466 |
+ if (!conn->group) |
|
467 |
+ return CL_EMEM; |
|
468 |
+ return 0; |
|
469 |
+ case COMMAND_END: |
|
470 |
+ if (!conn->group) { |
|
471 |
+ /* end without idsession? */ |
|
472 |
+ conn_reply_single(conn, NULL, "UNKNOWN COMMAND"); |
|
473 |
+ return 1; |
|
474 |
+ } |
|
475 |
+ /* need to close connection if we were last in group */ |
|
476 |
+ return 1; |
|
477 |
+ /*case COMMAND_UNKNOWN:*/ |
|
478 |
+ default: |
|
479 |
+ conn_reply_single(conn, NULL, "UNKNOWN COMMAND"); |
|
480 |
+ return 1; |
|
481 |
+ } |
|
218 | 482 |
} |
... | ... |
@@ -19,10 +19,6 @@ |
19 | 19 |
#ifndef __SESSION_H |
20 | 20 |
#define __SESSION_H |
21 | 21 |
|
22 |
-#define COMMAND_SHUTDOWN 1 |
|
23 |
-#define COMMAND_RELOAD 2 |
|
24 |
-#define COMMAND_END 3 |
|
25 |
-#define COMMAND_SESSION 4 |
|
26 | 22 |
|
27 | 23 |
#define CMD1 "SCAN" |
28 | 24 |
/* #define CMD2 "RAWSCAN" */ |
... | ... |
@@ -32,17 +28,66 @@ |
32 | 32 |
#define CMD6 "CONTSCAN" |
33 | 33 |
#define CMD7 "VERSION" |
34 | 34 |
#define CMD8 "STREAM" |
35 |
-#define CMD9 "SESSION" |
|
35 |
+/*#define CMD9 "SESSION"*/ |
|
36 | 36 |
#define CMD10 "END" |
37 | 37 |
#define CMD11 "SHUTDOWN" |
38 | 38 |
/* #define CMD12 "FD" */ |
39 | 39 |
#define CMD13 "MULTISCAN" |
40 | 40 |
#define CMD14 "FILDES" |
41 | 41 |
#define CMD15 "STATS" |
42 |
+#define CMD16 "IDSESSION" |
|
43 |
+#define CMD17 "INSTREAM" |
|
42 | 44 |
|
43 | 45 |
#include "libclamav/clamav.h" |
44 | 46 |
#include "shared/optparser.h" |
47 |
+#include "server.h" |
|
48 |
+#include "others.h" |
|
45 | 49 |
|
46 |
-int command(int desc, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, int timeout); |
|
50 |
+enum commands { |
|
51 |
+ COMMAND_UNKNOWN = 0, |
|
52 |
+ COMMAND_SHUTDOWN = 1, |
|
53 |
+ COMMAND_RELOAD, |
|
54 |
+ COMMAND_END, |
|
55 |
+ COMMAND_SESSION, |
|
56 |
+ COMMAND_SCAN, |
|
57 |
+ COMMAND_PING, |
|
58 |
+ COMMAND_CONTSCAN, |
|
59 |
+ COMMAND_VERSION, |
|
60 |
+ COMMAND_STREAM, |
|
61 |
+ COMMAND_MULTISCAN, |
|
62 |
+ COMMAND_FILDES, |
|
63 |
+ COMMAND_STATS, |
|
64 |
+ /* new proto commands */ |
|
65 |
+ COMMAND_IDSESSION, |
|
66 |
+ COMMAND_INSTREAM, |
|
67 |
+ /* internal commands */ |
|
68 |
+ COMMAND_MULTISCANFILE, |
|
69 |
+ COMMAND_INSTREAMSCAN |
|
70 |
+}; |
|
47 | 71 |
|
72 |
+typedef struct client_conn_tag { |
|
73 |
+ enum commands cmdtype; |
|
74 |
+ char *filename; |
|
75 |
+ int scanfd; |
|
76 |
+ int sd; |
|
77 |
+ unsigned int options; |
|
78 |
+ const struct optstruct *opts; |
|
79 |
+ struct cl_engine *engine; |
|
80 |
+ time_t engine_timestamp; |
|
81 |
+ char term; |
|
82 |
+ threadpool_t *thrpool; |
|
83 |
+ int id; |
|
84 |
+ long quota; |
|
85 |
+ jobgroup_t *group; |
|
86 |
+ enum mode mode; |
|
87 |
+} client_conn_t; |
|
88 |
+ |
|
89 |
+int command(client_conn_t *conn, int *virus); |
|
90 |
+enum commands parse_command(const char *cmd, const char **argument); |
|
91 |
+int execute_or_dispatch_command(client_conn_t *conn, enum commands command, const char *argument); |
|
92 |
+ |
|
93 |
+int conn_reply(const client_conn_t *conn, const char *path, const char *msg, const char *status); |
|
94 |
+int conn_reply_single(const client_conn_t *conn, const char *path, const char *status); |
|
95 |
+int conn_reply_error(const client_conn_t *conn, const char *msg); |
|
96 |
+int conn_reply_errno(const client_conn_t *conn, const char *path, const char *msg); |
|
48 | 97 |
#endif |
... | ... |
@@ -25,12 +25,14 @@ |
25 | 25 |
#include <pthread.h> |
26 | 26 |
#include <time.h> |
27 | 27 |
#include <errno.h> |
28 |
+#include <string.h> |
|
28 | 29 |
|
29 | 30 |
#include "shared/output.h" |
30 | 31 |
|
31 | 32 |
#include "thrmgr.h" |
32 | 33 |
#include "others.h" |
33 | 34 |
#include "mpool.h" |
35 |
+#include "server.h" |
|
34 | 36 |
|
35 | 37 |
#if defined(C_LINUX) |
36 | 38 |
#include <malloc.h> |
... | ... |
@@ -55,6 +57,7 @@ static work_queue_t *work_queue_new(void) |
55 | 55 |
|
56 | 56 |
work_q->head = work_q->tail = NULL; |
57 | 57 |
work_q->item_count = 0; |
58 |
+ work_q->popped = 0; |
|
58 | 59 |
return work_q; |
59 | 60 |
} |
60 | 61 |
|
... | ... |
@@ -152,6 +155,38 @@ static void remove_frompools(threadpool_t *t) |
152 | 152 |
pthread_mutex_unlock(&pools_lock); |
153 | 153 |
} |
154 | 154 |
|
155 |
+static void print_queue(int f, work_queue_t *queue, struct timeval *tv_now) |
|
156 |
+{ |
|
157 |
+ long umin=~0UL, umax=0, usum=0; |
|
158 |
+ unsigned invalids = 0, cnt = 0; |
|
159 |
+ work_item_t *q; |
|
160 |
+ |
|
161 |
+ if(!queue->head) |
|
162 |
+ return; |
|
163 |
+ for(q=queue->head;q;q=q->next) { |
|
164 |
+ long delta; |
|
165 |
+ delta = tv_now->tv_usec - q->time_queued.tv_usec; |
|
166 |
+ delta += (tv_now->tv_sec - q->time_queued.tv_sec)*1000000; |
|
167 |
+ if(delta < 0) { |
|
168 |
+ invalids++; |
|
169 |
+ continue; |
|
170 |
+ } |
|
171 |
+ if(delta > umax) |
|
172 |
+ umax = delta; |
|
173 |
+ if(delta < umin) |
|
174 |
+ umin = delta; |
|
175 |
+ usum += delta; |
|
176 |
+ ++cnt; |
|
177 |
+ } |
|
178 |
+ mdprintf(f," min_wait: %.6f max_wait: %.6f avg_wait: %.6f", |
|
179 |
+ umin/1e6, umax/1e6, usum /(1e6*cnt)); |
|
180 |
+ if(invalids) |
|
181 |
+ mdprintf(f," (INVALID timestamps: %u)", invalids); |
|
182 |
+ if(cnt + invalids != (unsigned)queue->item_count) |
|
183 |
+ mdprintf(f," (ERROR: %u != %u)", cnt + invalids, |
|
184 |
+ (unsigned)queue->item_count); |
|
185 |
+} |
|
186 |
+ |
|
155 | 187 |
int thrmgr_printstats(int f) |
156 | 188 |
{ |
157 | 189 |
struct threadpool_list *l; |
... | ... |
@@ -166,10 +201,7 @@ int thrmgr_printstats(int f) |
166 | 166 |
for(l= pools;l && !error_flag;l = l->nxt) { |
167 | 167 |
threadpool_t *pool = l->pool; |
168 | 168 |
const char *state; |
169 |
- work_item_t *q; |
|
170 | 169 |
struct timeval tv_now; |
171 |
- long umin=~0UL, umax=0, usum=0; |
|
172 |
- int invalids=0; |
|
173 | 170 |
struct task_desc *task; |
174 | 171 |
cnt = 0; |
175 | 172 |
|
... | ... |
@@ -199,32 +231,11 @@ int thrmgr_printstats(int f) |
199 | 199 |
mdprintf(f, "THREADS: live %u idle %u max %u idle-timeout %u\n" |
200 | 200 |
,pool->thr_alive, pool->thr_idle, pool->thr_max, |
201 | 201 |
pool->idle_timeout); |
202 |
- mdprintf(f,"QUEUE: %u items", pool->queue->item_count); |
|
202 |
+ /* TODO: show both queues */ |
|
203 |
+ mdprintf(f,"QUEUE: %u items", pool->single_queue->item_count + pool->bulk_queue->item_count); |
|
203 | 204 |
gettimeofday(&tv_now, NULL); |
204 |
- if(pool->queue->head) { |
|
205 |
- for(q=pool->queue->head;q;q=q->next) { |
|
206 |
- long delta; |
|
207 |
- delta = tv_now.tv_usec - q->time_queued.tv_usec; |
|
208 |
- delta += (tv_now.tv_sec - q->time_queued.tv_sec)*1000000; |
|
209 |
- if(delta < 0) { |
|
210 |
- invalids++; |
|
211 |
- continue; |
|
212 |
- } |
|
213 |
- if(delta > umax) |
|
214 |
- umax = delta; |
|
215 |
- if(delta < umin) |
|
216 |
- umin = delta; |
|
217 |
- usum += delta; |
|
218 |
- ++cnt; |
|
219 |
- } |
|
220 |
- mdprintf(f," min_wait: %.6f max_wait: %.6f avg_wait: %.6f", |
|
221 |
- umin/1e6, umax/1e6, usum /(1e6*cnt)); |
|
222 |
- if(invalids) |
|
223 |
- mdprintf(f," (INVALID timestamps: %u)", invalids); |
|
224 |
- } |
|
225 |
- if(cnt + invalids != (unsigned)pool->queue->item_count) |
|
226 |
- mdprintf(f," (ERROR: %u != %u)", cnt + invalids, |
|
227 |
- (unsigned)pool->queue->item_count); |
|
205 |
+ print_queue(f, pool->bulk_queue, &tv_now); |
|
206 |
+ print_queue(f, pool->single_queue, &tv_now); |
|
228 | 207 |
mdprintf(f, "\n"); |
229 | 208 |
for(task = pool->tasks; task; task = task->nxt) { |
230 | 209 |
long delta; |
... | ... |
@@ -331,15 +342,17 @@ void thrmgr_destroy(threadpool_t *threadpool) |
331 | 331 |
} |
332 | 332 |
|
333 | 333 |
pthread_mutex_destroy(&(threadpool->pool_mutex)); |
334 |
- pthread_cond_destroy(&(threadpool)->idle_cond); |
|
334 |
+ pthread_cond_destroy(&(threadpool->idle_cond)); |
|
335 |
+ pthread_cond_destroy(&(threadpool->queueable_cond)); |
|
335 | 336 |
pthread_cond_destroy(&(threadpool->pool_cond)); |
336 | 337 |
pthread_attr_destroy(&(threadpool->pool_attr)); |
337 |
- free(threadpool->queue); |
|
338 |
+ free(threadpool->single_queue); |
|
339 |
+ free(threadpool->bulk_queue); |
|
338 | 340 |
free(threadpool); |
339 | 341 |
return; |
340 | 342 |
} |
341 | 343 |
|
342 |
-threadpool_t *thrmgr_new(int max_threads, int idle_timeout, void (*handler)(void *)) |
|
344 |
+threadpool_t *thrmgr_new(int max_threads, int idle_timeout, int max_queue, void (*handler)(void *)) |
|
343 | 345 |
{ |
344 | 346 |
threadpool_t *threadpool; |
345 | 347 |
#if defined(C_BIGSTACK) |
... | ... |
@@ -350,16 +363,30 @@ threadpool_t *thrmgr_new(int max_threads, int idle_timeout, void (*handler)(void |
350 | 350 |
return NULL; |
351 | 351 |
} |
352 | 352 |
|
353 |
+ if (2*max_threads > max_queue) { |
|
354 |
+ logg("!Configuration error: MaxQueue should be at least twice MaxThreads\n"); |
|
355 |
+ return NULL; |
|
356 |
+ } |
|
357 |
+ |
|
353 | 358 |
threadpool = (threadpool_t *) malloc(sizeof(threadpool_t)); |
354 | 359 |
if (!threadpool) { |
355 | 360 |
return NULL; |
356 | 361 |
} |
357 | 362 |
|
358 |
- threadpool->queue = work_queue_new(); |
|
359 |
- if (!threadpool->queue) { |
|
363 |
+ threadpool->single_queue = work_queue_new(); |
|
364 |
+ if (!threadpool->single_queue) { |
|
360 | 365 |
free(threadpool); |
361 | 366 |
return NULL; |
362 | 367 |
} |
368 |
+ threadpool->bulk_queue = work_queue_new(); |
|
369 |
+ if (!threadpool->bulk_queue) { |
|
370 |
+ free(threadpool->single_queue); |
|
371 |
+ free(threadpool); |
|
372 |
+ return NULL; |
|
373 |
+ } |
|
374 |
+ |
|
375 |
+ threadpool->queue_max = max_queue; |
|
376 |
+ |
|
363 | 377 |
threadpool->thr_max = max_threads; |
364 | 378 |
threadpool->thr_alive = 0; |
365 | 379 |
threadpool->thr_idle = 0; |
... | ... |
@@ -368,41 +395,58 @@ threadpool_t *thrmgr_new(int max_threads, int idle_timeout, void (*handler)(void |
368 | 368 |
threadpool->tasks = NULL; |
369 | 369 |
|
370 | 370 |
if(pthread_mutex_init(&(threadpool->pool_mutex), NULL)) { |
371 |
- free(threadpool->queue); |
|
371 |
+ free(threadpool->single_queue); |
|
372 |
+ free(threadpool->bulk_queue); |
|
372 | 373 |
free(threadpool); |
373 | 374 |
return NULL; |
374 | 375 |
} |
375 | 376 |
|
376 | 377 |
if (pthread_cond_init(&(threadpool->pool_cond), NULL) != 0) { |
377 | 378 |
pthread_mutex_destroy(&(threadpool->pool_mutex)); |
378 |
- free(threadpool->queue); |
|
379 |
+ free(threadpool->single_queue); |
|
380 |
+ free(threadpool->bulk_queue); |
|
381 |
+ free(threadpool); |
|
382 |
+ return NULL; |
|
383 |
+ } |
|
384 |
+ |
|
385 |
+ if (pthread_cond_init(&(threadpool->queueable_cond), NULL) != 0) { |
|
386 |
+ pthread_cond_destroy(&(threadpool->pool_cond)); |
|
387 |
+ pthread_mutex_destroy(&(threadpool->pool_mutex)); |
|
388 |
+ free(threadpool->single_queue); |
|
389 |
+ free(threadpool->bulk_queue); |
|
379 | 390 |
free(threadpool); |
380 | 391 |
return NULL; |
381 | 392 |
} |
382 | 393 |
|
383 | 394 |
if (pthread_cond_init(&(threadpool->idle_cond),NULL) != 0) { |
395 |
+ pthread_cond_destroy(&(threadpool->queueable_cond)); |
|
384 | 396 |
pthread_cond_destroy(&(threadpool->pool_cond)); |
385 | 397 |
pthread_mutex_destroy(&(threadpool->pool_mutex)); |
386 |
- free(threadpool->queue); |
|
398 |
+ free(threadpool->single_queue); |
|
399 |
+ free(threadpool->bulk_queue); |
|
387 | 400 |
free(threadpool); |
388 | 401 |
return NULL; |
389 | 402 |
} |
390 | 403 |
|
391 | 404 |
if (pthread_attr_init(&(threadpool->pool_attr)) != 0) { |
405 |
+ pthread_cond_destroy(&(threadpool->queueable_cond)); |
|
392 | 406 |
pthread_cond_destroy(&(threadpool->idle_cond)); |
393 | 407 |
pthread_cond_destroy(&(threadpool->pool_cond)); |
394 | 408 |
pthread_mutex_destroy(&(threadpool->pool_mutex)); |
395 |
- free(threadpool->queue); |
|
409 |
+ free(threadpool->single_queue); |
|
410 |
+ free(threadpool->bulk_queue); |
|
396 | 411 |
free(threadpool); |
397 | 412 |
return NULL; |
398 | 413 |
} |
399 | 414 |
|
400 | 415 |
if (pthread_attr_setdetachstate(&(threadpool->pool_attr), PTHREAD_CREATE_DETACHED) != 0) { |
416 |
+ pthread_cond_destroy(&(threadpool->queueable_cond)); |
|
401 | 417 |
pthread_attr_destroy(&(threadpool->pool_attr)); |
402 | 418 |
pthread_cond_destroy(&(threadpool->idle_cond)); |
403 | 419 |
pthread_cond_destroy(&(threadpool->pool_cond)); |
404 | 420 |
pthread_mutex_destroy(&(threadpool->pool_mutex)); |
405 |
- free(threadpool->queue); |
|
421 |
+ free(threadpool->single_queue); |
|
422 |
+ free(threadpool->bulk_queue); |
|
406 | 423 |
free(threadpool); |
407 | 424 |
return NULL; |
408 | 425 |
} |
... | ... |
@@ -431,7 +475,7 @@ static void stats_tls_key_alloc(void) |
431 | 431 |
static const char *IDLE_TASK = "IDLE"; |
432 | 432 |
|
433 | 433 |
/* no mutex is needed, we are using thread local variable */ |
434 |
-void thrmgr_setactivetask(const char *filename, const char* command) |
|
434 |
+void thrmgr_setactivetask(const char *filename, const char* cmd) |
|
435 | 435 |
{ |
436 | 436 |
struct task_desc *desc; |
437 | 437 |
pthread_once(&stats_tls_key_once, stats_tls_key_alloc); |
... | ... |
@@ -439,10 +483,10 @@ void thrmgr_setactivetask(const char *filename, const char* command) |
439 | 439 |
if(!desc) |
440 | 440 |
return; |
441 | 441 |
desc->filename = filename; |
442 |
- if(command) { |
|
443 |
- if(command == IDLE_TASK && desc->command == command) |
|
442 |
+ if(cmd) { |
|
443 |
+ if(cmd == IDLE_TASK && desc->command == cmd) |
|
444 | 444 |
return; |
445 |
- desc->command = command; |
|
445 |
+ desc->command = cmd; |
|
446 | 446 |
gettimeofday(&desc->tv, NULL); |
447 | 447 |
} |
448 | 448 |
} |
... | ... |
@@ -490,6 +534,55 @@ static void stats_destroy(threadpool_t *pool) |
490 | 490 |
pthread_setspecific(stats_tls_key, NULL); |
491 | 491 |
} |
492 | 492 |
|
493 |
+static inline int thrmgr_contended(threadpool_t *pool) |
|
494 |
+{ |
|
495 |
+ return pool->bulk_queue->item_count + pool->single_queue->item_count |
|
496 |
+ + pool->thr_alive - pool->thr_idle >= pool->queue_max; |
|
497 |
+} |
|
498 |
+ |
|
499 |
+/* when both queues have tasks, it will pick 4 items from the single queue, |
|
500 |
+ * and 1 from the bulk */ |
|
501 |
+#define SINGLE_BULK_RATIO 4 |
|
502 |
+#define SINGLE_BULK_SUM (SINGLE_BULK_RATIO + 1) |
|
503 |
+ |
|
504 |
+/* must be called with pool_mutex held */ |
|
505 |
+static void *thrmgr_pop(threadpool_t *pool) |
|
506 |
+{ |
|
507 |
+ void *task; |
|
508 |
+ work_queue_t *first, *second; |
|
509 |
+ int ratio; |
|
510 |
+ |
|
511 |
+ if (pool->single_queue->popped < SINGLE_BULK_RATIO) { |
|
512 |
+ first = pool->single_queue; |
|
513 |
+ second = pool->bulk_queue; |
|
514 |
+ ratio = SINGLE_BULK_RATIO; |
|
515 |
+ } else { |
|
516 |
+ second = pool->single_queue; |
|
517 |
+ first = pool->bulk_queue; |
|
518 |
+ ratio = SINGLE_BULK_SUM - SINGLE_BULK_RATIO; |
|
519 |
+ } |
|
520 |
+ |
|
521 |
+ task = work_queue_pop(first); |
|
522 |
+ if (task) { |
|
523 |
+ if (++first->popped == ratio) |
|
524 |
+ second->popped = 0; |
|
525 |
+ } else { |
|
526 |
+ task = work_queue_pop(second); |
|
527 |
+ if (task) { |
|
528 |
+ if (++second->popped == ratio) |
|
529 |
+ first->popped = 0; |
|
530 |
+ } |
|
531 |
+ } |
|
532 |
+ |
|
533 |
+ if (!thrmgr_contended(pool)) { |
|
534 |
+ logg("*THRMGR: queue crossed low threshold -> signaling\n"); |
|
535 |
+ pthread_cond_signal(&pool->queueable_cond); |
|
536 |
+ } |
|
537 |
+ |
|
538 |
+ return task; |
|
539 |
+} |
|
540 |
+ |
|
541 |
+ |
|
493 | 542 |
static void *thrmgr_worker(void *arg) |
494 | 543 |
{ |
495 | 544 |
threadpool_t *threadpool = (threadpool_t *) arg; |
... | ... |
@@ -512,7 +605,7 @@ static void *thrmgr_worker(void *arg) |
512 | 512 |
timeout.tv_sec = time(NULL) + threadpool->idle_timeout; |
513 | 513 |
timeout.tv_nsec = 0; |
514 | 514 |
threadpool->thr_idle++; |
515 |
- while (((job_data=work_queue_pop(threadpool->queue)) == NULL) |
|
515 |
+ while (((job_data=thrmgr_pop(threadpool)) == NULL) |
|
516 | 516 |
&& (threadpool->state != POOL_EXIT)) { |
517 | 517 |
/* Sleep, awaiting wakeup */ |
518 | 518 |
pthread_cond_signal(&threadpool->idle_cond); |
... | ... |
@@ -557,8 +650,9 @@ static void *thrmgr_worker(void *arg) |
557 | 557 |
return NULL; |
558 | 558 |
} |
559 | 559 |
|
560 |
-int thrmgr_dispatch(threadpool_t *threadpool, void *user_data) |
|
560 |
+static int thrmgr_dispatch_internal(threadpool_t *threadpool, void *user_data, int bulk) |
|
561 | 561 |
{ |
562 |
+ int ret = TRUE; |
|
562 | 563 |
pthread_t thr_id; |
563 | 564 |
|
564 | 565 |
if (!threadpool) { |
... | ... |
@@ -571,35 +665,179 @@ int thrmgr_dispatch(threadpool_t *threadpool, void *user_data) |
571 | 571 |
return FALSE; |
572 | 572 |
} |
573 | 573 |
|
574 |
- if (threadpool->state != POOL_VALID) { |
|
575 |
- if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0) { |
|
576 |
- logg("!Mutex unlock failed\n"); |
|
577 |
- } |
|
578 |
- return FALSE; |
|
579 |
- } |
|
580 |
- if (!work_queue_add(threadpool->queue, user_data)) { |
|
581 |
- if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0) { |
|
582 |
- logg("!Mutex unlock failed\n"); |
|
583 |
- return FALSE; |
|
584 |
- } |
|
585 |
- return FALSE; |
|
586 |
- } |
|
574 |
+ do { |
|
575 |
+ work_queue_t *queue; |
|
576 |
+ int items; |
|
577 |
+ |
|
578 |
+ if (threadpool->state != POOL_VALID) { |
|
579 |
+ ret = FALSE; |
|
580 |
+ break; |
|
581 |
+ } |
|
582 |
+ |
|
583 |
+ if (bulk) |
|
584 |
+ queue = threadpool->bulk_queue; |
|
585 |
+ else |
|
586 |
+ queue = threadpool->single_queue; |
|
587 | 587 |
|
588 |
- if ((threadpool->thr_idle < threadpool->queue->item_count) && |
|
589 |
- (threadpool->thr_alive < threadpool->thr_max)) { |
|
588 |
+ while (thrmgr_contended(threadpool)) { |
|
589 |
+ logg("*THRMGR: contended, sleeping\n"); |
|
590 |
+ pthread_cond_wait(&threadpool->queueable_cond, &threadpool->pool_mutex); |
|
591 |
+ logg("*THRMGR: contended, woken\n"); |
|
592 |
+ } |
|
593 |
+ |
|
594 |
+ if (!work_queue_add(queue, user_data)) { |
|
595 |
+ ret = FALSE; |
|
596 |
+ break; |
|
597 |
+ } |
|
598 |
+ |
|
599 |
+ items = threadpool->single_queue->item_count + threadpool->bulk_queue->item_count; |
|
600 |
+ if ((threadpool->thr_idle < items) && |
|
601 |
+ (threadpool->thr_alive < threadpool->thr_max)) { |
|
590 | 602 |
/* Start a new thread */ |
591 | 603 |
if (pthread_create(&thr_id, &(threadpool->pool_attr), |
592 |
- thrmgr_worker, threadpool) != 0) { |
|
593 |
- logg("!pthread_create failed\n"); |
|
604 |
+ thrmgr_worker, threadpool) != 0) { |
|
605 |
+ logg("!pthread_create failed\n"); |
|
594 | 606 |
} else { |
595 |
- threadpool->thr_alive++; |
|
607 |
+ threadpool->thr_alive++; |
|
596 | 608 |
} |
597 |
- } |
|
598 |
- pthread_cond_signal(&(threadpool->pool_cond)); |
|
609 |
+ } |
|
610 |
+ pthread_cond_signal(&(threadpool->pool_cond)); |
|
611 |
+ |
|
612 |
+ } while (0); |
|
599 | 613 |
|
600 | 614 |
if (pthread_mutex_unlock(&(threadpool->pool_mutex)) != 0) { |
601 |
- logg("!Mutex unlock failed\n"); |
|
602 |
- return FALSE; |
|
615 |
+ logg("!Mutex unlock failed\n"); |
|
616 |
+ return FALSE; |
|
603 | 617 |
} |
604 |
- return TRUE; |
|
618 |
+ return ret; |
|
619 |
+} |
|
620 |
+ |
|
621 |
+int thrmgr_dispatch(threadpool_t *threadpool, void *user_data) |
|
622 |
+{ |
|
623 |
+ return thrmgr_dispatch_internal(threadpool, user_data, 0); |
|
624 |
+} |
|
625 |
+ |
|
626 |
+int thrmgr_group_dispatch(threadpool_t *threadpool, jobgroup_t *group, void *user_data) |
|
627 |
+{ |
|
628 |
+ int ret; |
|
629 |
+ if (group) { |
|
630 |
+ pthread_mutex_lock(&group->mutex); |
|
631 |
+ group->jobs++; |
|
632 |
+ pthread_mutex_unlock(&group->mutex); |
|
633 |
+ } |
|
634 |
+ if (!(ret = thrmgr_dispatch_internal(threadpool, user_data, 1)) && group) { |
|
635 |
+ pthread_mutex_lock(&group->mutex); |
|
636 |
+ group->jobs--; |
|
637 |
+ pthread_mutex_unlock(&group->mutex); |
|
638 |
+ } |
|
639 |
+ return ret; |
|
640 |
+} |
|
641 |
+ |
|
642 |
+/* returns |
|
643 |
+ * 0 - this was not the last thread in the group |
|
644 |
+ * 1 - this was last thread in group, group freed |
|
645 |
+ */ |
|
646 |
+int thrmgr_group_finished(jobgroup_t *group, enum thrmgr_exit exitc) |
|
647 |
+{ |
|
648 |
+ int ret = 0; |
|
649 |
+ if (!group) { |
|
650 |
+ /* there is no group, we are obviously the last one */ |
|
651 |
+ return 1; |
|
652 |
+ } |
|
653 |
+ pthread_mutex_lock(&group->mutex); |
|
654 |
+ logg("*THRMGR: group_finished: %p, %d\n", group, group->jobs); |
|
655 |
+ group->exit_total++; |
|
656 |
+ switch (exitc) { |
|
657 |
+ case EXIT_OK: |
|
658 |
+ group->exit_ok++; |
|
659 |
+ break; |
|
660 |
+ case EXIT_ERROR: |
|
661 |
+ group->exit_error++; |
|
662 |
+ break; |
|
663 |
+ } |
|
664 |
+ if (group->jobs) { |
|
665 |
+ if (!--group->jobs) { |
|
666 |
+ ret = 1; |
|
667 |
+ } |
|
668 |
+ if (group->jobs == 1) |
|
669 |
+ pthread_cond_signal(&group->only); |
|
670 |
+ } |
|
671 |
+ pthread_mutex_unlock(&group->mutex); |
|
672 |
+ if (ret) { |
|
673 |
+ logg("*THRMGR: group_finished: freeing %p\n", group); |
|
674 |
+ free(group); |
|
675 |
+ } |
|
676 |
+ return ret; |
|
677 |
+} |
|
678 |
+ |
|
679 |
+void thrmgr_group_waitforall(jobgroup_t *group, unsigned *ok, unsigned *error, unsigned *total) |
|
680 |
+{ |
|
681 |
+ int needexit = 0, needfree = 0; |
|
682 |
+ struct timespec timeout; |
|
683 |
+ pthread_mutex_lock(&group->mutex); |
|
684 |
+ while (group->jobs > 1) { |
|
685 |
+ pthread_mutex_lock(&exit_mutex); |
|
686 |
+ needexit = progexit; |
|
687 |
+ pthread_mutex_unlock(&exit_mutex); |
|
688 |
+ if (needexit) |
|
689 |
+ break; |
|
690 |
+ /* wake to check progexit */ |
|
691 |
+ timeout.tv_sec = time(NULL) + 5; |
|
692 |
+ timeout.tv_nsec = 0; |
|
693 |
+ pthread_cond_timedwait(&group->only, &group->mutex, &timeout); |
|
694 |
+ } |
|
695 |
+ *ok = group->exit_ok; |
|
696 |
+ *error = group->exit_error + needexit; |
|
697 |
+ *total = group->exit_total; |
|
698 |
+ if(!--group->jobs) |
|
699 |
+ needfree = 1; |
|
700 |
+ pthread_mutex_unlock(&group->mutex); |
|
701 |
+ if (needfree) { |
|
702 |
+ logg("*THRMGR: freeing %p\n", group); |
|
703 |
+ free(group); |
|
704 |
+ } |
|
705 |
+} |
|
706 |
+ |
|
707 |
+#define JOBGROUP_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 1, 0, 0, 0, 0 }; |
|
708 |
+jobgroup_t *thrmgr_group_new(void) |
|
709 |
+{ |
|
710 |
+ jobgroup_t *group; |
|
711 |
+ jobgroup_t dummy = JOBGROUP_INITIALIZER; |
|
712 |
+ |
|
713 |
+ group = malloc(sizeof(*group)); |
|
714 |
+ if (!group) |
|
715 |
+ return NULL; |
|
716 |
+ memcpy(group, &dummy, sizeof(dummy)); |
|
717 |
+ return group; |
|
718 |
+} |
|
719 |
+ |
|
720 |
+int thrmgr_group_need_terminate(jobgroup_t *group) |
|
721 |
+{ |
|
722 |
+ int ret; |
|
723 |
+ if (group) { |
|
724 |
+ pthread_mutex_lock(&group->mutex); |
|
725 |
+ ret = group->force_exit; |
|
726 |
+ pthread_mutex_unlock(&group->mutex); |
|
727 |
+ } else |
|
728 |
+ ret = 0; |
|
729 |
+ pthread_mutex_lock(&exit_mutex); |
|
730 |
+ ret |= progexit; |
|
731 |
+ pthread_mutex_unlock(&exit_mutex); |
|
732 |
+ return ret; |
|
733 |
+} |
|
734 |
+ |
|
735 |
+/* returns |
|
736 |
+ * 0 - flag set, jobs still active |
|
737 |
+ * 1 - last job exited */ |
|
738 |
+int thrmgr_group_terminate(jobgroup_t *group) |
|
739 |
+{ |
|
740 |
+ if (thrmgr_group_finished(group, EXIT_ERROR)) |
|
741 |
+ return 1; |
|
742 |
+ /* we are not last active job, now |
|
743 |
+ * the last active job will free resources */ |
|
744 |
+ pthread_mutex_lock(&group->mutex); |
|
745 |
+ group->force_exit = 1; |
|
746 |
+ pthread_mutex_unlock(&group->mutex); |
|
747 |
+ |
|
748 |
+ return 0; |
|
605 | 749 |
} |
... | ... |
@@ -36,6 +36,7 @@ typedef struct work_queue_tag { |
36 | 36 |
work_item_t *head; |
37 | 37 |
work_item_t *tail; |
38 | 38 |
int item_count; |
39 |
+ int popped; |
|
39 | 40 |
} work_queue_t; |
40 | 41 |
|
41 | 42 |
typedef enum { |
... | ... |
@@ -59,22 +60,47 @@ typedef struct threadpool_tag { |
59 | 59 |
pthread_attr_t pool_attr; |
60 | 60 |
|
61 | 61 |
pthread_cond_t idle_cond; |
62 |
+ pthread_cond_t queueable_cond; |
|
62 | 63 |
|
63 | 64 |
pool_state_t state; |
64 | 65 |
int thr_max; |
66 |
+ int queue_max; |
|
65 | 67 |
int thr_alive; |
66 | 68 |
int thr_idle; |
67 | 69 |
int idle_timeout; |
68 | 70 |
struct task_desc *tasks; |
69 | 71 |
|
70 | 72 |
void (*handler)(void *); |
71 |
- |
|
72 |
- work_queue_t *queue; |
|
73 |
+ |
|
74 |
+ work_queue_t *bulk_queue; |
|
75 |
+ work_queue_t *single_queue; |
|
73 | 76 |
} threadpool_t; |
74 | 77 |
|
75 |
-threadpool_t *thrmgr_new(int max_threads, int idle_timeout, void (*handler)(void *)); |
|
78 |
+typedef struct jobgroup { |
|
79 |
+ pthread_mutex_t mutex; |
|
80 |
+ pthread_cond_t only; |
|
81 |
+ unsigned jobs; |
|
82 |
+ unsigned exit_ok; |
|
83 |
+ unsigned exit_error; |
|
84 |
+ unsigned exit_total; |
|
85 |
+ int force_exit; |
|
86 |
+} jobgroup_t; |
|
87 |
+ |
|
88 |
+enum thrmgr_exit { |
|
89 |
+ EXIT_OK, |
|
90 |
+ EXIT_ERROR, |
|
91 |
+ EXIT_OTHER |
|
92 |
+}; |
|
93 |
+ |
|
94 |
+threadpool_t *thrmgr_new(int max_threads, int idle_timeout, int max_queue, void (*handler)(void *)); |
|
76 | 95 |
void thrmgr_destroy(threadpool_t *threadpool); |
77 | 96 |
int thrmgr_dispatch(threadpool_t *threadpool, void *user_data); |
97 |
+int thrmgr_group_dispatch(threadpool_t *threadpool, jobgroup_t *group, void *user_data); |
|
98 |
+void thrmgr_group_waitforall(jobgroup_t *group, unsigned *ok, unsigned *error, unsigned *total); |
|
99 |
+int thrmgr_group_finished(jobgroup_t *group, enum thrmgr_exit exitc); |
|
100 |
+int thrmgr_group_need_terminate(jobgroup_t *group); |
|
101 |
+int thrmgr_group_terminate(jobgroup_t *group); |
|
102 |
+jobgroup_t *thrmgr_group_new(void); |
|
78 | 103 |
int thrmgr_printstats(int outfd); |
79 | 104 |
void thrmgr_setactivetask(const char *filename, const char* command); |
80 | 105 |
void thrmgr_setactiveengine(const struct cl_engine *engine); |
... | ... |
@@ -188,6 +188,8 @@ const struct clam_option clam_options[] = { |
188 | 188 |
|
189 | 189 |
{ "ReadTimeout", NULL, 0, TYPE_NUMBER, MATCH_NUMBER, 120, NULL, 0, OPT_MILTER, "Waiting for data from clamd will timeout after this time (seconds).\nValue of 0 disables the timeout.", "300" }, |
190 | 190 |
|
191 |
+ { "MaxQueue", NULL, 0, TYPE_NUMBER, MATCH_NUMBER, 100, NULL, 0, OPT_CLAMD, "Maximum number of queued items (including those being processed)\nWARNING: you shouldn't increase this beyond 512, since you may run out of File Descriptors (usual max is 1024)\n", "200" }, |
|
192 |
+ |
|
191 | 193 |
{ "IdleTimeout", NULL, 0, TYPE_NUMBER, MATCH_NUMBER, 30, NULL, 0, OPT_CLAMD, "This option specifies how long (in seconds) the process should wait\nfor a new job.", "60" }, |
192 | 194 |
|
193 | 195 |
{ "ExcludePath", NULL, 0, TYPE_STRING, NULL, -1, NULL, FLAG_MULTIPLE, OPT_CLAMD, "Don't scan files/directories whose names match the provided\nregular expression. This option can be specified multiple times.", "^/proc/\n^/sys/" }, |