e3aaff8e |
/* |
e1cbc270 |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc. |
086eab5c |
*
* Authors: Tomasz Kojm, Trog, Török Edvin |
e3aaff8e |
*
* This program is free software; you can redistribute it and/or modify |
bb34cb31 |
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. |
e3aaff8e |
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software |
48b7b4a7 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA. |
e3aaff8e |
*/
|
98ac8d19 |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
|
bfd89d7c |
/* must be first because it may define _XOPEN_SOURCE */
#include "shared/fdpassing.h" |
e3aaff8e |
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h> |
288057e9 |
#ifdef HAVE_UNISTD_H |
e3aaff8e |
#include <unistd.h> |
67118e92 |
#endif |
e3aaff8e |
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h> |
c5e7d5cd |
#include <errno.h> |
288057e9 |
#ifndef _WIN32 |
13d4160d |
#include <sys/time.h> |
7bbd5f7f |
#include <sys/wait.h> |
e0bb54d7 |
#include <sys/socket.h>
#include <sys/ioctl.h> |
67118e92 |
#endif |
7bbd5f7f |
|
d32fc9fe |
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
|
fc83da82 |
#ifdef HAVE_SYS_TYPES_H |
13d4160d |
#include <sys/types.h>
#endif |
fc83da82 |
#ifdef HAVE_SYS_FILIO_H |
09a1f3a1 |
#include <sys/filio.h>
#endif |
c5e7d5cd |
|
1642ffba |
#include <pthread.h> |
feeaa333 |
|
c5e7d5cd |
#if HAVE_POLL
#if HAVE_POLL_H
#include <poll.h>
#else /* HAVE_POLL_H */
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */
#endif /* HAVE_POLL_H */
#endif /* HAVE_POLL */ |
e3aaff8e |
|
034994bd |
#include <limits.h> |
60d8d2c3 |
#include "libclamav/clamav.h" |
51b69a09 |
#include "libclamav/scanners.h" |
064b4a0c |
#include "shared/optparser.h" |
bd8603aa |
#include "shared/output.h" |
3b074c78 |
#include "shared/misc.h" |
de5f850d |
#include "libclamav/others.h" |
bd8603aa |
|
079229d6 |
#include "others.h" |
034994bd |
|
bbce0449 |
static pthread_mutex_t virusaction_lock = PTHREAD_MUTEX_INITIALIZER;
|
9120a4a6 |
static void xfree(void *p)
{
if (p)
free(p);
}
|
288057e9 |
#ifdef _WIN32
void virusaction(const char *filename, const char *virname,
const struct optstruct *opts) |
67118e92 |
{ |
288057e9 |
if (optget(opts, "VirusEvent")->enabled)
logg("^VirusEvent is not supported on this platform"); /* Yet */ |
67118e92 |
}
#else |
949c6fe5 |
|
288057e9 |
#define VE_FILENAME "CLAM_VIRUSEVENT_FILENAME" |
949c6fe5 |
#define VE_VIRUSNAME "CLAM_VIRUSEVENT_VIRUSNAME"
|
288057e9 |
void virusaction(const char *filename, const char *virname,
const struct optstruct *opts) |
0249f9d2 |
{ |
a5937136 |
pid_t pid;
const struct optstruct *opt; |
9120a4a6 |
char *buffer_file, *buffer_vir, *buffer_cmd, *path; |
a5937136 |
const char *pt;
size_t i, j, v = 0, len;
char *env[4];
|
288057e9 |
if (!(opt = optget(opts, "VirusEvent"))->enabled) |
a5937136 |
return;
|
288057e9 |
path = getenv("PATH"); |
9120a4a6 |
env[0] = path ? strdup(path) : NULL; |
288057e9 |
j = env[0] ? 1 : 0; |
a5937136 |
/* Allocate env vars.. to be portable env vars should not be freed */
buffer_file = |
288057e9 |
(char *)malloc(strlen(VE_FILENAME) + strlen(filename) + 2);
if (buffer_file) {
sprintf(buffer_file, "%s=%s", VE_FILENAME, filename); |
a5937136 |
env[j++] = buffer_file;
}
buffer_vir = |
288057e9 |
(char *)malloc(strlen(VE_VIRUSNAME) + strlen(virname) + 2);
if (buffer_vir) {
sprintf(buffer_vir, "%s=%s", VE_VIRUSNAME, virname); |
a5937136 |
env[j++] = buffer_vir;
}
env[j++] = NULL;
pt = opt->strarg; |
288057e9 |
while ((pt = strstr(pt, "%v"))) { |
a5937136 |
pt += 2;
v++;
} |
288057e9 |
len = strlen(opt->strarg); |
a5937136 |
buffer_cmd = |
288057e9 |
(char *)calloc(len + v * strlen(virname) + 1, sizeof(char));
if (!buffer_cmd) { |
4e2700c7 |
if (path)
xfree(env[0]); |
9120a4a6 |
|
288057e9 |
xfree(buffer_file);
xfree(buffer_vir); |
a5937136 |
return;
} |
288057e9 |
for (i = 0, j = 0; i < len; i++) {
if (i + 1 < len && opt->strarg[i] == '%' && opt->strarg[i + 1] == 'v') {
strcat(buffer_cmd, virname);
j += strlen(virname); |
a5937136 |
i++; |
288057e9 |
} else { |
a5937136 |
buffer_cmd[j++] = opt->strarg[i];
}
}
|
288057e9 |
pthread_mutex_lock(&virusaction_lock); |
a5937136 |
/* We can only call async-signal-safe functions after fork(). */ |
288057e9 |
pid = fork();
if (pid == 0) { /* child */
exit(execle("/bin/sh", "sh", "-c", buffer_cmd, NULL, env));
} else if (pid > 0) { /* parent */ |
9120a4a6 |
pthread_mutex_unlock(&virusaction_lock); |
288057e9 |
while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) continue;
} else {
pthread_mutex_unlock(&virusaction_lock);
logg("!VirusEvent: fork failed.\n"); |
a5937136 |
} |
4e2700c7 |
if (path)
xfree(env[0]); |
9120a4a6 |
|
288057e9 |
xfree(buffer_cmd);
xfree(buffer_file);
xfree(buffer_vir); |
0249f9d2 |
} |
be4bf7f4 |
#endif /* _WIN32 */ |
c5e7d5cd |
|
949c6fe5 |
/* Function: writen
Try hard to write the specified number of bytes
*/ |
288057e9 |
int writen(int fd, void *buff, unsigned int count) |
c5e7d5cd |
{ |
a5937136 |
int retval;
unsigned int todo;
unsigned char *current;
|
288057e9 |
todo = count;
current = (unsigned char *)buff; |
a5937136 |
|
288057e9 |
do {
retval = write(fd, current, todo);
if (retval < 0) {
if (errno == EINTR) { |
a5937136 |
continue;
}
return -1;
}
todo -= retval;
current += retval; |
288057e9 |
} while (todo > 0); |
a5937136 |
|
949c6fe5 |
return count;
} |
c5e7d5cd |
|
a5937136 |
static int |
288057e9 |
realloc_polldata(struct fd_data *data) |
949c6fe5 |
{
#ifdef HAVE_POLL
if (data->poll_data_nfds == data->nfds) |
a5937136 |
return 0; |
949c6fe5 |
if (data->poll_data) |
288057e9 |
free(data->poll_data);
data->poll_data = malloc(data->nfds * sizeof(*data->poll_data));
if (!data->poll_data) {
logg("!realloc_polldata: Memory allocation failed for poll_data\n"); |
a5937136 |
return -1; |
c5e7d5cd |
} |
949c6fe5 |
data->poll_data_nfds = data->nfds; |
c5e7d5cd |
#endif |
949c6fe5 |
return 0; |
c5e7d5cd |
} |
e0909dc6 |
|
288057e9 |
int poll_fd(int fd, int timeout_sec, int check_signals) |
57358cc8 |
{ |
949c6fe5 |
int ret; |
288057e9 |
struct fd_data fds = FDS_INIT(NULL); |
a5937136 |
|
288057e9 |
if (fds_add(&fds, fd, 1, timeout_sec) == -1) |
a5937136 |
return -1; |
288057e9 |
do {
ret = fds_poll_recv(&fds, timeout_sec, check_signals, NULL);
} while (ret == -1 && errno == EINTR);
fds_free(&fds); |
949c6fe5 |
return ret;
}
|
288057e9 |
void fds_cleanup(struct fd_data *data) |
949c6fe5 |
{
struct fd_buf *newbuf; |
a5937136 |
unsigned i, j;
|
288057e9 |
for (i = 0, j = 0; i < data->nfds; i++) {
if (data->buf[i].fd < 0) { |
a5937136 |
if (data->buf[i].buffer) |
288057e9 |
free(data->buf[i].buffer); |
a5937136 |
continue;
}
if (i != j)
data->buf[j] = data->buf[i];
j++; |
949c6fe5 |
} |
5f6edb22 |
if (j == data->nfds) |
a5937136 |
return;
for (i = j; i < data->nfds; i++)
data->buf[i].fd = -1; |
949c6fe5 |
data->nfds = j; |
288057e9 |
logg("$Number of file descriptors polled: %u fds\n",
(unsigned)data->nfds); |
949c6fe5 |
/* Shrink buffer */ |
288057e9 |
newbuf = realloc(data->buf, j * sizeof(*newbuf)); |
a5937136 |
if (!j)
data->buf = NULL; |
3d7d59c1 |
else if (newbuf) |
288057e9 |
data->buf = newbuf; /* non-fatal if shrink fails */ |
57358cc8 |
}
|
a5937136 |
static int |
288057e9 |
read_fd_data(struct fd_buf *buf) |
e0909dc6 |
{ |
949c6fe5 |
ssize_t n; |
e0909dc6 |
|
a5937136 |
buf->got_newdata = 1; |
288057e9 |
if (!buf->buffer) /* listen-only socket */ |
a5937136 |
return 1; |
e0909dc6 |
|
949c6fe5 |
if (buf->off >= buf->bufsize) |
a5937136 |
return -1; |
e0909dc6 |
|
288057e9 |
/* Read the pending packet, it may contain more than one command, but |
a5937136 |
* that is to the cmdparser to handle.
* It will handle 1st command, and then move leftover to beginning of buffer
*/ |
949c6fe5 |
#ifdef HAVE_FD_PASSING |
a5937136 |
{
struct msghdr msg;
struct cmsghdr *cmsg; |
288057e9 |
union {
unsigned char buff[CMSG_SPACE(sizeof(int))]; |
a5937136 |
struct cmsghdr hdr;
} b;
struct iovec iov[1];
|
288057e9 |
if (buf->recvfd != -1) {
logg("$Closing unclaimed FD: %d\n", buf->recvfd);
close(buf->recvfd); |
a5937136 |
buf->recvfd = -1;
} |
288057e9 |
memset(&msg, 0, sizeof(msg));
iov[0].iov_base = buf->buffer + buf->off;
iov[0].iov_len = buf->bufsize - buf->off;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = b.buff;
msg.msg_controllen = sizeof(b.buff);
n = recvmsg(buf->fd, &msg, 0); |
a5937136 |
if (n < 0)
return -1; |
288057e9 |
if (msg.msg_flags & MSG_TRUNC) {
logg("^Message truncated at %d bytes\n", (int)n); |
a5937136 |
return -1;
} |
288057e9 |
if (msg.msg_flags & MSG_CTRUNC) { |
a5937136 |
if (msg.msg_controllen > 0) |
288057e9 |
logg("^Control message truncated at %d bytes, %d data read\n", (int)msg.msg_controllen, (int)n); |
a5937136 |
else |
288057e9 |
logg("^Control message truncated, no control data received, %d bytes read" |
cb9a7b44 |
#ifdef C_LINUX |
288057e9 |
"(Is SELinux/AppArmor enabled, and blocking file descriptor passing?)" |
cb9a7b44 |
#endif |
288057e9 |
"\n",
(int)n); |
a5937136 |
return -1;
} |
288057e9 |
if (msg.msg_controllen) {
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) && |
a5937136 |
cmsg->cmsg_level == SOL_SOCKET && |
288057e9 |
cmsg->cmsg_type == SCM_RIGHTS) {
if (buf->recvfd != -1) {
logg("$Unclaimed file descriptor received. closing: %d\n", buf->recvfd);
close(buf->recvfd); |
a5937136 |
} |
288057e9 |
buf->recvfd = *(int *)CMSG_DATA(cmsg);
logg("$Receveived a file descriptor: %d\n", buf->recvfd); |
a5937136 |
}
}
}
} |
e0909dc6 |
#else |
288057e9 |
n = recv(buf->fd, buf->buffer + buf->off, buf->bufsize - buf->off, 0); |
a5937136 |
if (n < 0)
return -1; |
949c6fe5 |
#endif |
a5937136 |
buf->off += n;
return n; |
949c6fe5 |
} |
e0909dc6 |
|
a5937136 |
static int |
288057e9 |
buf_init(struct fd_buf *buf, int listen_only, int timeout) |
949c6fe5 |
{ |
288057e9 |
buf->off = 0; |
949c6fe5 |
buf->got_newdata = 0; |
288057e9 |
buf->recvfd = -1;
buf->mode = MODE_COMMAND;
buf->id = 0;
buf->dumpfd = -1;
buf->chunksize = 0;
buf->quota = 0;
buf->dumpname = NULL;
buf->group = NULL;
buf->term = '\0';
if (!listen_only) {
if (!buf->buffer) { |
a5937136 |
buf->bufsize = PATH_MAX + 8;
/* plus extra space for a \0 so we can make sure every command is \0
* terminated */ |
288057e9 |
if (!(buf->buffer = malloc(buf->bufsize + 1))) {
logg("!add_fd: Memory allocation failed for command buffer\n"); |
a5937136 |
return -1;
}
} |
288057e9 |
} else { |
a5937136 |
if (buf->buffer) |
288057e9 |
free(buf->buffer); |
a5937136 |
buf->bufsize = 0; |
288057e9 |
buf->buffer = NULL; |
a5937136 |
} |
288057e9 |
if (timeout) {
time(&buf->timeout_at); |
a5937136 |
buf->timeout_at += timeout; |
288057e9 |
} else { |
a5937136 |
buf->timeout_at = 0; |
5f6edb22 |
} |
949c6fe5 |
return 0;
} |
e0909dc6 |
|
288057e9 |
int fds_add(struct fd_data *data, int fd, int listen_only, int timeout) |
949c6fe5 |
{
struct fd_buf *buf;
unsigned n; |
288057e9 |
if (fd < 0) {
logg("!add_fd: invalid fd passed to add_fd\n"); |
a5937136 |
return -1; |
e0909dc6 |
} |
949c6fe5 |
/* we may already have this fd, if
* the old FD got closed, and the kernel reused the FD */
for (n = 0; n < data->nfds; n++) |
288057e9 |
if (data->buf[n].fd == fd) { |
a5937136 |
/* clear stale data in buffer */ |
288057e9 |
if (buf_init(&data->buf[n], listen_only, timeout) < 0) |
a5937136 |
return -1;
return 0;
} |
949c6fe5 |
n++; |
288057e9 |
buf = realloc(data->buf, n * sizeof(*buf));
if (!buf) {
logg("!add_fd: Memory allocation failed for fd_buf\n"); |
a5937136 |
return -1; |
e0909dc6 |
} |
288057e9 |
data->buf = buf;
data->nfds = n; |
a5937136 |
data->buf[n - 1].buffer = NULL; |
288057e9 |
if (buf_init(&data->buf[n - 1], listen_only, timeout) < 0) |
a5937136 |
return -1;
data->buf[n - 1].fd = fd; |
949c6fe5 |
return 0; |
e0909dc6 |
} |
94709323 |
|
a5937136 |
static inline void |
288057e9 |
fds_lock(struct fd_data *data) |
7660b7cb |
{
if (data->buf_mutex) |
288057e9 |
pthread_mutex_lock(data->buf_mutex); |
7660b7cb |
}
|
a5937136 |
static inline void |
288057e9 |
fds_unlock(struct fd_data *data) |
7660b7cb |
{
if (data->buf_mutex) |
288057e9 |
pthread_mutex_unlock(data->buf_mutex); |
7660b7cb |
}
|
288057e9 |
void fds_remove(struct fd_data *data, int fd) |
94709323 |
{ |
949c6fe5 |
size_t i; |
288057e9 |
fds_lock(data);
if (data->buf) {
for (i = 0; i < data->nfds; i++) {
if (data->buf[i].fd == fd) { |
a5937136 |
data->buf[i].fd = -1;
break;
}
} |
949c6fe5 |
} |
288057e9 |
fds_unlock(data); |
94709323 |
} |
7708ddfc |
|
949c6fe5 |
#define BUFFSIZE 1024
/* Wait till data is available to be read on any of the fds,
* read available data on all fds, and mark them as appropriate.
* One of the fds should be a pipe, used by the accept thread to wake us.
* timeout is specified in seconds, if check_signals is non-zero, then
* poll_recv_fds() will return upon receipt of a signal, even if no data
* is received on any of the sockets. |
7660b7cb |
* Must be called with buf_mutex lock held. |
949c6fe5 |
*/
/* TODO: handle ReadTimeout */ |
288057e9 |
int fds_poll_recv(struct fd_data *data, int timeout, int check_signals,
void *event) |
7708ddfc |
{ |
949c6fe5 |
unsigned fdsok = data->nfds;
size_t i;
int retval; |
5f6edb22 |
time_t now, closest_timeout; |
949c6fe5 |
|
6df13d04 |
UNUSEDPARAM(event);
|
949c6fe5 |
/* we must have at least one fd, the control fd! */ |
288057e9 |
fds_cleanup(data); |
4155214d |
#ifndef _WIN32 |
949c6fe5 |
if (!data->nfds) |
a5937136 |
return 0; |
4155214d |
#endif |
288057e9 |
for (i = 0; i < data->nfds; i++) { |
a5937136 |
data->buf[i].got_newdata = 0; |
5f6edb22 |
}
|
288057e9 |
time(&now); |
5f6edb22 |
if (timeout > 0) |
a5937136 |
closest_timeout = now + timeout; |
5f6edb22 |
else |
a5937136 |
closest_timeout = 0; |
288057e9 |
for (i = 0; i < data->nfds; i++) { |
a5937136 |
time_t timeout_at = data->buf[i].timeout_at; |
288057e9 |
if (timeout_at && timeout_at < now) { |
a5937136 |
/* timed out */
data->buf[i].got_newdata = -2;
/* we must return immediately from poll/select, we have a timeout! */
closest_timeout = now; |
288057e9 |
} else { |
a5937136 |
if (!closest_timeout)
closest_timeout = timeout_at;
else if (timeout_at && timeout_at < closest_timeout)
closest_timeout = timeout_at;
} |
5f6edb22 |
}
if (closest_timeout) |
a5937136 |
timeout = closest_timeout - now; |
5f6edb22 |
else |
a5937136 |
timeout = -1; |
fb6fe4f5 |
if (timeout > 0) |
288057e9 |
logg("$fds_poll_recv: timeout after %d seconds\n", timeout); |
949c6fe5 |
#ifdef HAVE_POLL
/* Use poll() if available, preferred because:
* - can poll any number of FDs
* - can notify of both data available / socket disconnected events
* - when it says POLLIN it is guaranteed that a following recv() won't
* block (select may say that data is available to read, but a following
* recv() may still block according to the manpage
*/
|
288057e9 |
if (realloc_polldata(data) == -1) |
a5937136 |
return -1; |
288057e9 |
if (timeout > 0) { |
a5937136 |
/* seconds to ms */
timeout *= 1000; |
949c6fe5 |
} |
288057e9 |
for (i = 0; i < data->nfds; i++) {
data->poll_data[i].fd = data->buf[i].fd;
data->poll_data[i].events = POLLIN; |
a5937136 |
data->poll_data[i].revents = 0; |
949c6fe5 |
} |
288057e9 |
do { |
a5937136 |
int n = data->nfds; |
949c6fe5 |
|
288057e9 |
fds_unlock(data); |
5eca45b5 |
#ifdef _WIN32 |
288057e9 |
retval = poll_with_event(data->poll_data, n, timeout, event); |
5eca45b5 |
#else |
288057e9 |
retval = poll(data->poll_data, n, timeout); |
5eca45b5 |
#endif |
288057e9 |
fds_lock(data); |
a5937136 |
|
288057e9 |
if (retval > 0) { |
a5937136 |
fdsok = 0;
/* nfds may change during poll, but not
* poll_data_nfds */ |
288057e9 |
for (i = 0; i < data->poll_data_nfds; i++) { |
a5937136 |
short revents;
if (data->buf[i].fd < 0)
continue; |
288057e9 |
if (data->buf[i].fd != data->poll_data[i].fd) { |
a5937136 |
/* should never happen */ |
288057e9 |
logg("!poll_recv_fds FD mismatch\n"); |
a5937136 |
continue;
}
revents = data->poll_data[i].revents; |
288057e9 |
if (revents & (POLLIN | POLLHUP)) {
logg("$Received POLLIN|POLLHUP on fd %d\n",
data->poll_data[i].fd); |
a5937136 |
} |
ced85d63 |
#ifndef _WIN32 |
288057e9 |
if (revents & POLLHUP) { |
a5937136 |
/* avoid SHUT_WR problem on Mac OS X */ |
288057e9 |
int ret = send(data->poll_data[i].fd, &n, 0, 0); |
a5937136 |
if (!ret || (ret == -1 && errno == EINTR))
revents &= ~POLLHUP;
} |
ced85d63 |
#endif |
288057e9 |
if (revents & POLLIN) {
int ret = read_fd_data(&data->buf[i]); |
a5937136 |
/* Data available to be read */
if (ret == -1)
revents |= POLLERR;
else if (!ret)
revents = POLLHUP;
}
|
288057e9 |
if (revents & (POLLHUP | POLLERR | POLLNVAL)) {
if (revents & (POLLHUP | POLLNVAL)) { |
a5937136 |
/* remote disconnected */ |
288057e9 |
logg("*Client disconnected (FD %d)\n",
data->poll_data[i].fd);
} else { |
a5937136 |
/* error on file descriptor */ |
288057e9 |
logg("^Error condition on fd %d\n",
data->poll_data[i].fd); |
a5937136 |
}
data->buf[i].got_newdata = -1; |
288057e9 |
} else { |
a5937136 |
fdsok++;
}
}
} |
288057e9 |
} while (retval == -1 && !check_signals && errno == EINTR); |
949c6fe5 |
#else |
3b074c78 |
{ |
a5937136 |
fd_set rfds;
struct timeval tv;
int maxfd = -1;
|
288057e9 |
for (i = 0; i < data->nfds; i++) { |
a5937136 |
int fd = data->buf[i].fd; |
288057e9 |
if (fd >= FD_SETSIZE) {
logg("!File descriptor is too high for FD_SET\n"); |
a5937136 |
return -1;
}
|
288057e9 |
maxfd = MAX(maxfd, fd); |
a5937136 |
}
|
288057e9 |
do {
FD_ZERO(&rfds);
for (i = 0; i < data->nfds; i++) { |
a5937136 |
int fd = data->buf[i].fd;
if (fd >= 0) |
288057e9 |
FD_SET(fd, &rfds); |
a5937136 |
} |
288057e9 |
tv.tv_sec = timeout; |
a5937136 |
tv.tv_usec = 0;
|
288057e9 |
fds_unlock(data); |
a5937136 |
retval = |
288057e9 |
select(maxfd + 1, &rfds, NULL, NULL,
timeout >= 0 ? &tv : NULL);
fds_lock(data);
if (retval > 0) { |
a5937136 |
fdsok = data->nfds; |
288057e9 |
for (i = 0; i < data->nfds; i++) {
if (data->buf[i].fd < 0) { |
a5937136 |
fdsok--;
continue;
} |
288057e9 |
if (FD_ISSET(data->buf[i].fd, &rfds)) {
int ret = read_fd_data(&data->buf[i]);
if (ret == -1 || !ret) { |
a5937136 |
if (ret == -1) |
288057e9 |
logg("!Error condition on fd %d\n",
data->buf[i].fd);
else { |
a5937136 |
/* avoid SHUT_WR problem on Mac OS X */ |
288057e9 |
int ret = send(data->buf[i].fd, &i, 0, 0); |
a5937136 |
if (!ret || (ret == -1 && errno == EINTR))
continue; |
288057e9 |
logg("*Client disconnected\n"); |
a5937136 |
}
data->buf[i].got_newdata = -1;
}
}
}
} |
288057e9 |
if (retval < 0 && errno == EBADF) { |
a5937136 |
/* unlike poll(), select() won't tell us which FD is bad, so
* we have to check them one by one. */ |
288057e9 |
tv.tv_sec = 0; |
a5937136 |
tv.tv_usec = 0;
/* with tv == 0 it doesn't check for EBADF */ |
288057e9 |
FD_ZERO(&rfds);
for (i = 0; i < data->nfds; i++) { |
a5937136 |
if (data->buf[i].fd == -1)
continue; |
288057e9 |
FD_SET(data->buf[i].fd, &rfds);
do { |
a5937136 |
retval = |
288057e9 |
select(data->buf[i].fd + 1, &rfds, NULL, NULL,
&tv);
} while (retval == -1 && errno == EINTR);
if (retval == -1) { |
a5937136 |
data->buf[i].fd = -1; |
288057e9 |
} else {
FD_CLR(data->buf[i].fd, &rfds); |
a5937136 |
}
}
retval = -1; |
288057e9 |
errno = EINTR; |
a5937136 |
continue;
} |
288057e9 |
} while (retval == -1 && !check_signals && errno == EINTR); |
3b074c78 |
} |
949c6fe5 |
#endif
|
288057e9 |
if (retval == -1 && errno != EINTR) { |
a5937136 |
char err[128]; |
949c6fe5 |
#ifdef HAVE_POLL |
288057e9 |
logg("!poll_recv_fds: poll failed: %s\n",
cli_strerror(errno, err, sizeof(err))); |
949c6fe5 |
#else |
288057e9 |
logg("!poll_recv_fds: select failed: %s\n",
cli_strerror(errno, err, sizeof(err))); |
949c6fe5 |
#endif |
442684f8 |
} |
949c6fe5 |
return retval;
}
|
288057e9 |
void fds_free(struct fd_data *data) |
949c6fe5 |
{
unsigned i; |
288057e9 |
fds_lock(data);
for (i = 0; i < data->nfds; i++) {
if (data->buf[i].buffer) {
free(data->buf[i].buffer); |
a5937136 |
} |
442684f8 |
} |
949c6fe5 |
if (data->buf) |
288057e9 |
free(data->buf); |
949c6fe5 |
#ifdef HAVE_POLL
if (data->poll_data) |
288057e9 |
free(data->poll_data); |
949c6fe5 |
#endif |
288057e9 |
data->buf = NULL; |
949c6fe5 |
data->nfds = 0; |
288057e9 |
fds_unlock(data); |
7708ddfc |
} |