| ... | ... |
@@ -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/" },
|