2008-11-03 19:27:20 +00:00
|
|
|
/*
|
2009-02-23 12:35:34 +00:00
|
|
|
* ClamdTOP
|
2008-11-03 19:27:20 +00:00
|
|
|
*
|
2025-02-14 10:24:30 -05:00
|
|
|
* Copyright (C) 2013-2025 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
2019-01-25 10:15:50 -05:00
|
|
|
* Copyright (C) 2008-2013 Sourcefire, Inc.
|
2008-11-03 19:27:20 +00:00
|
|
|
*
|
|
|
|
* Authors: Török Edvin
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
* MA 02110-1301, USA.
|
|
|
|
*/
|
2008-11-12 12:06:15 +00:00
|
|
|
#define _GNU_SOURCE
|
|
|
|
#define __EXTENSIONS
|
|
|
|
#define GCC_PRINTF
|
|
|
|
#define GCC_SCANF
|
2009-02-23 12:35:34 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "clamav-config.h"
|
|
|
|
#endif
|
|
|
|
|
2009-05-29 11:37:14 +00:00
|
|
|
#ifdef HAVE_STDINT_H
|
|
|
|
#include <stdint.h>
|
|
|
|
#endif
|
|
|
|
|
2019-11-03 12:46:21 -05:00
|
|
|
#ifdef HAVE_UNISTD_H
|
2008-11-03 19:27:20 +00:00
|
|
|
#include <unistd.h>
|
2019-11-03 12:46:21 -05:00
|
|
|
#endif
|
2008-11-03 19:27:20 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
2009-02-23 12:35:34 +00:00
|
|
|
#include CURSES_INCLUDE
|
2008-11-03 19:27:20 +00:00
|
|
|
#include <time.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <signal.h>
|
2008-11-09 22:04:22 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <windows.h>
|
|
|
|
#include <winsock2.h>
|
|
|
|
/* this is not correct, perhaps winsock errors are not mapped on errno */
|
|
|
|
#define herror perror
|
|
|
|
#else
|
2008-11-03 19:27:20 +00:00
|
|
|
#include <netdb.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <sys/time.h>
|
2019-11-03 12:46:21 -05:00
|
|
|
#endif
|
2008-11-04 10:40:38 +00:00
|
|
|
#include <assert.h>
|
2008-11-06 14:27:35 +00:00
|
|
|
#include <errno.h>
|
2008-11-03 19:27:20 +00:00
|
|
|
|
2019-11-03 12:46:21 -05:00
|
|
|
#include "platform.h"
|
2009-02-23 12:35:34 +00:00
|
|
|
|
Add CMake build tooling
This patch adds experimental-quality CMake build tooling.
The libmspack build required a modification to use "" instead of <> for
header #includes. This will hopefully be included in the libmspack
upstream project when adding CMake build tooling to libmspack.
Removed use of libltdl when using CMake.
Flex & Bison are now required to build.
If -DMAINTAINER_MODE, then GPERF is also required, though it currently
doesn't actually do anything. TODO!
I found that the autotools build system was generating the lexer output
but not actually compiling it, instead using previously generated (and
manually renamed) lexer c source. As a consequence, changes to the .l
and .y files weren't making it into the build. To resolve this, I
removed generated flex/bison files and fixed the tooling to use the
freshly generated files. Flex and bison are now required build tools.
On Windows, this adds a dependency on the winflexbison package,
which can be obtained using Chocolatey or may be manually installed.
CMake tooling only has partial support for building with external LLVM
library, and no support for the internal LLVM (to be removed in the
future). I.e. The CMake build currently only supports the bytecode
interpreter.
Many files used include paths relative to the top source directory or
relative to the current project, rather than relative to each build
target. Modern CMake support requires including internal dependency
headers the same way you would external dependency headers (albeit
with "" instead of <>). This meant correcting all header includes to
be relative to the build targets and not relative to the workspace.
For example, ...
```c
include "../libclamav/clamav.h"
include "clamd/clamd_others.h"
```
... becomes:
```c
// libclamav
include "clamav.h"
// clamd
include "clamd_others.h"
```
Fixes header name conflicts by renaming a few of the files.
Converted the "shared" code into a static library, which depends on
libclamav. The ironically named "shared" static library provides
features common to the ClamAV apps which are not required in
libclamav itself and are not intended for use by downstream projects.
This change was required for correct modern CMake practices but was
also required to use the automake "subdir-objects" option.
This eliminates warnings when running autoreconf which, in the next
version of autoconf & automake are likely to break the build.
libclamav used to build in multiple stages where an earlier stage is
a static library containing utils required by the "shared" code.
Linking clamdscan and clamdtop with this libclamav utils static lib
allowed these two apps to function without libclamav. While this is
nice in theory, the practical gains are minimal and it complicates
the build system. As such, the autotools and CMake tooling was
simplified for improved maintainability and this feature was thrown
out. clamdtop and clamdscan now require libclamav to function.
Removed the nopthreads version of the autotools
libclamav_internal_utils static library and added pthread linking to
a couple apps that may have issues building on some platforms without
it, with the intention of removing needless complexity from the
source. Kept the regular version of libclamav_internal_utils.la
though it is no longer used anywhere but in libclamav.
Added an experimental doxygen build option which attempts to build
clamav.h and libfreshclam doxygen html docs.
The CMake build tooling also may build the example program(s), which
isn't a feature in the Autotools build system.
Changed C standard to C90+ due to inline linking issues with socket.h
when linking libfreshclam.so on Linux.
Generate common.rc for win32.
Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef
from misc.c.
Add CMake files to the automake dist, so users can try the new
CMake tooling w/out having to build from a git clone.
clamonacc changes:
- Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other
similar macros.
- Added a new clamav-clamonacc.service systemd unit file, based on
the work of ChadDevOps & Aaron Brighton.
- Added missing clamonacc man page.
Updates to clamdscan man page, add missing options.
Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use
libclamav).
Rename Windows mspack.dll to libmspack.dll so all ClamAV-built
libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
|
|
|
// libclamav
|
|
|
|
#include "clamav.h"
|
|
|
|
|
2021-03-04 19:39:50 -08:00
|
|
|
// common
|
Add CMake build tooling
This patch adds experimental-quality CMake build tooling.
The libmspack build required a modification to use "" instead of <> for
header #includes. This will hopefully be included in the libmspack
upstream project when adding CMake build tooling to libmspack.
Removed use of libltdl when using CMake.
Flex & Bison are now required to build.
If -DMAINTAINER_MODE, then GPERF is also required, though it currently
doesn't actually do anything. TODO!
I found that the autotools build system was generating the lexer output
but not actually compiling it, instead using previously generated (and
manually renamed) lexer c source. As a consequence, changes to the .l
and .y files weren't making it into the build. To resolve this, I
removed generated flex/bison files and fixed the tooling to use the
freshly generated files. Flex and bison are now required build tools.
On Windows, this adds a dependency on the winflexbison package,
which can be obtained using Chocolatey or may be manually installed.
CMake tooling only has partial support for building with external LLVM
library, and no support for the internal LLVM (to be removed in the
future). I.e. The CMake build currently only supports the bytecode
interpreter.
Many files used include paths relative to the top source directory or
relative to the current project, rather than relative to each build
target. Modern CMake support requires including internal dependency
headers the same way you would external dependency headers (albeit
with "" instead of <>). This meant correcting all header includes to
be relative to the build targets and not relative to the workspace.
For example, ...
```c
include "../libclamav/clamav.h"
include "clamd/clamd_others.h"
```
... becomes:
```c
// libclamav
include "clamav.h"
// clamd
include "clamd_others.h"
```
Fixes header name conflicts by renaming a few of the files.
Converted the "shared" code into a static library, which depends on
libclamav. The ironically named "shared" static library provides
features common to the ClamAV apps which are not required in
libclamav itself and are not intended for use by downstream projects.
This change was required for correct modern CMake practices but was
also required to use the automake "subdir-objects" option.
This eliminates warnings when running autoreconf which, in the next
version of autoconf & automake are likely to break the build.
libclamav used to build in multiple stages where an earlier stage is
a static library containing utils required by the "shared" code.
Linking clamdscan and clamdtop with this libclamav utils static lib
allowed these two apps to function without libclamav. While this is
nice in theory, the practical gains are minimal and it complicates
the build system. As such, the autotools and CMake tooling was
simplified for improved maintainability and this feature was thrown
out. clamdtop and clamdscan now require libclamav to function.
Removed the nopthreads version of the autotools
libclamav_internal_utils static library and added pthread linking to
a couple apps that may have issues building on some platforms without
it, with the intention of removing needless complexity from the
source. Kept the regular version of libclamav_internal_utils.la
though it is no longer used anywhere but in libclamav.
Added an experimental doxygen build option which attempts to build
clamav.h and libfreshclam doxygen html docs.
The CMake build tooling also may build the example program(s), which
isn't a feature in the Autotools build system.
Changed C standard to C90+ due to inline linking issues with socket.h
when linking libfreshclam.so on Linux.
Generate common.rc for win32.
Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef
from misc.c.
Add CMake files to the automake dist, so users can try the new
CMake tooling w/out having to build from a git clone.
clamonacc changes:
- Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other
similar macros.
- Added a new clamav-clamonacc.service systemd unit file, based on
the work of ChadDevOps & Aaron Brighton.
- Added missing clamonacc man page.
Updates to clamdscan man page, add missing options.
Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use
libclamav).
Rename Windows mspack.dll to libmspack.dll so all ClamAV-built
libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
|
|
|
#include "optparser.h"
|
|
|
|
#include "misc.h"
|
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
/* Types, prototypes and globals*/
|
|
|
|
typedef struct connection {
|
2018-12-03 12:40:13 -05:00
|
|
|
int sd;
|
|
|
|
char *remote;
|
|
|
|
int tcp;
|
|
|
|
struct timeval tv_conn;
|
|
|
|
char *version;
|
|
|
|
int line;
|
2008-11-12 12:06:15 +00:00
|
|
|
} conn_t;
|
|
|
|
|
|
|
|
struct global_stats {
|
2018-12-03 12:40:13 -05:00
|
|
|
struct task *tasks;
|
|
|
|
ssize_t n;
|
|
|
|
struct stats *all_stats;
|
|
|
|
size_t num_clamd;
|
|
|
|
conn_t *conn;
|
2008-11-12 12:06:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct stats {
|
2018-12-03 12:40:13 -05:00
|
|
|
const char *remote;
|
|
|
|
char *engine_version;
|
|
|
|
char *db_version;
|
|
|
|
struct tm db_time;
|
|
|
|
const char *version;
|
|
|
|
int stats_unsupp;
|
|
|
|
uint8_t conn_hr, conn_min, conn_sec;
|
|
|
|
/* threads - primary */
|
|
|
|
unsigned prim_live, prim_idle, prim_max;
|
|
|
|
/* threads - sum */
|
|
|
|
unsigned live, idle, max;
|
|
|
|
/* queue */
|
|
|
|
unsigned biggest_queue, current_q;
|
|
|
|
double mem; /* in megabytes */
|
2020-06-07 19:07:17 -04:00
|
|
|
double heapu, mmapu, totalu, totalf, releasable, pools_used, pools_total;
|
2018-12-03 12:40:13 -05:00
|
|
|
unsigned pools_cnt;
|
2008-11-12 12:06:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void cleanup(void);
|
|
|
|
static int send_string_noreconn(conn_t *conn, const char *cmd);
|
|
|
|
static void send_string(conn_t *conn, const char *cmd);
|
2009-03-01 10:15:27 +00:00
|
|
|
static int read_version(conn_t *conn);
|
2025-06-04 16:47:57 +02:00
|
|
|
static int check_stats_available(conn_t *conn);
|
2014-07-11 09:30:58 -04:00
|
|
|
char *get_ip(const char *ip);
|
|
|
|
char *get_port(const char *ip);
|
|
|
|
char *make_ip(const char *host, const char *port);
|
2008-11-12 12:06:15 +00:00
|
|
|
|
2008-11-12 16:53:27 +00:00
|
|
|
enum exit_reason {
|
2018-12-03 12:40:13 -05:00
|
|
|
FAIL_CMDLINE = 1,
|
|
|
|
FAIL_INITIAL_CONN,
|
|
|
|
OUT_OF_MEMORY,
|
|
|
|
RECONNECT_FAIL,
|
|
|
|
SIGINT_REASON
|
2008-11-12 16:53:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void exit_program(enum exit_reason reason, const char *func, unsigned line);
|
2009-03-01 10:15:23 +00:00
|
|
|
#if __GNUC__ >= 3
|
2008-11-12 16:53:27 +00:00
|
|
|
#define EXIT_PROGRAM(r) exit_program(r, __PRETTY_FUNCTION__, __LINE__);
|
2009-03-01 10:15:23 +00:00
|
|
|
#else
|
|
|
|
#define EXIT_PROGRAM(r) exit_program(r, "<unknown>", __LINE__);
|
|
|
|
#endif
|
2018-12-03 12:40:13 -05:00
|
|
|
#define OOM_CHECK(p) \
|
|
|
|
do { \
|
|
|
|
if (!p) EXIT_PROGRAM(OUT_OF_MEMORY); \
|
|
|
|
} while (0)
|
2008-11-12 16:53:27 +00:00
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
static struct global_stats global;
|
2020-06-07 19:07:17 -04:00
|
|
|
static SCREEN *curses_scr = NULL;
|
|
|
|
static int curses_inited = 0;
|
2018-12-03 12:40:13 -05:00
|
|
|
static int maxystats = 0;
|
2008-11-13 16:00:10 +00:00
|
|
|
static int detail_selected = -1;
|
|
|
|
|
2009-02-23 12:35:34 +00:00
|
|
|
static int detail_exists(void)
|
2008-11-13 16:00:10 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
return global.num_clamd != 1;
|
2008-11-13 16:00:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int detail_is_selected(int idx)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!detail_exists()) {
|
|
|
|
assert(idx == 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return idx == detail_selected;
|
2008-11-13 16:00:10 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 14:27:35 +00:00
|
|
|
/* ---------------------- NCurses routines -----------------*/
|
2008-11-03 19:27:20 +00:00
|
|
|
enum colors {
|
2018-12-03 12:40:13 -05:00
|
|
|
header_color = 1,
|
|
|
|
version_color,
|
|
|
|
error_color,
|
|
|
|
value_color,
|
|
|
|
descr_color,
|
|
|
|
selected_color,
|
|
|
|
queue_header_color,
|
|
|
|
activ_color,
|
|
|
|
dim_color,
|
|
|
|
red_color,
|
2008-11-03 19:27:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define UPDATE_INTERVAL 2
|
|
|
|
#define MIN_INTERVAL 1
|
|
|
|
|
|
|
|
/* the default color of the terminal in ncurses */
|
|
|
|
#define DEFAULT_COLOR -1
|
|
|
|
|
|
|
|
#define VALUE_ATTR A_BOLD | COLOR_PAIR(value_color)
|
|
|
|
#define DESCR_ATTR COLOR_PAIR(descr_color)
|
2008-11-12 16:53:27 +00:00
|
|
|
#define ERROR_ATTR A_BOLD | COLOR_PAIR(error_color)
|
2008-11-03 19:27:20 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
static WINDOW *header_window = NULL;
|
2008-11-03 19:27:20 +00:00
|
|
|
static WINDOW *stats_head_window = NULL;
|
2018-12-03 12:40:13 -05:00
|
|
|
static WINDOW *stats_window = NULL;
|
2008-11-03 19:27:20 +00:00
|
|
|
static WINDOW *status_bar_window = NULL;
|
2018-12-03 12:40:13 -05:00
|
|
|
static WINDOW *mem_window = NULL;
|
2008-11-03 19:27:20 +00:00
|
|
|
|
Windows: Fix C/Rust FFI compat issue + Windows compile warnings
Primarily this commit fixes an issue with the size of the parameters
passed to cli_checklimits(). The parameters were "unsigned long", which
varies in size depending on platform.
I've switched them to uint64_t / u64.
While working on this, I observed some concerning warnigns on Windows,
and some less serious ones, primarily regarding inconsistencies with
`const` parameters.
Finally, in `scanmem.c`, there is a warning regarding use of `wchar_t *`
with `GetModuleFileNameEx()` instead of `GetModuleFileNameExW()`.
This made me realize this code assumes we're not defining `UNICODE`,
which would have such macros use the 'A' variant.
I have fixed it the best I can, although I'm still a little
uncomfortable with some of this code that uses `char` or `wchar_t`
instead of TCHAR.
I also remove the `if (GetModuleFileNameEx) {` conditional, because this
macro/function will always be defined. The original code was checking a
function pointer, and so this was a bug when integrating into ClamAV.
Regarding the changes to `rijndael.c`, I found that this module assumes
`unsigned long` == 32bits. It does not.
I have corrected it to use `uint32_t`.
2024-03-20 12:21:40 -04:00
|
|
|
static char *status_bar_keys[10];
|
2018-12-03 12:40:13 -05:00
|
|
|
static unsigned maxy = 0, maxx = 0;
|
2020-06-07 19:07:17 -04:00
|
|
|
static char *queue_header = NULL;
|
|
|
|
static char *multi_queue_header = NULL;
|
|
|
|
static char *clamd_header = NULL;
|
2008-11-12 12:06:15 +00:00
|
|
|
|
2020-07-24 08:32:47 -07:00
|
|
|
#define CMDHEAD " COMMAND QUEUEDSINCE FILE"
|
2020-06-07 19:07:17 -04:00
|
|
|
#define CMDHEAD2 "NO COMMAND QUEUEDSINCE FILE"
|
2008-11-12 12:06:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* CLAMD - which local/remote clamd this is
|
|
|
|
* CONNTIM - since when we are connected (TODO: zeroed at reconnect)
|
|
|
|
* QUEUE - no of items in queue (total)
|
|
|
|
* MAXQUEUE - max no of items in queue observed
|
|
|
|
* LIVETHR - sum of live threads
|
|
|
|
* IDLETHR - sum of idle threads
|
|
|
|
*/
|
2020-06-07 19:07:17 -04:00
|
|
|
#define SUMHEAD "NO CONNTIME LIV IDL QUEUE MAXQ MEM ENGINE DBVER DBTIME HOST"
|
2008-11-03 19:27:20 +00:00
|
|
|
|
|
|
|
static void resize(void)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
char *p;
|
2023-08-07 16:27:20 -07:00
|
|
|
int new_maxy, new_maxx;
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
getmaxyx(stdscr, new_maxy, new_maxx);
|
2023-08-07 16:27:20 -07:00
|
|
|
if (new_maxy == -1 || new_maxx == -1) {
|
|
|
|
fprintf(stderr, "Failed to get terminal size\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((unsigned int)new_maxy == maxy && (unsigned int)new_maxx == maxx) {
|
|
|
|
// no change
|
2018-12-03 12:40:13 -05:00
|
|
|
return;
|
2023-08-07 16:27:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
maxx = (unsigned int)new_maxx;
|
|
|
|
maxy = (unsigned int)new_maxy;
|
2018-12-03 12:40:13 -05:00
|
|
|
free(queue_header);
|
|
|
|
free(clamd_header);
|
|
|
|
queue_header = malloc(maxx + 1);
|
|
|
|
OOM_CHECK(queue_header);
|
|
|
|
clamd_header = malloc(maxx + 1);
|
|
|
|
OOM_CHECK(clamd_header);
|
|
|
|
assert(clamd_header && queue_header);
|
2020-06-07 19:07:17 -04:00
|
|
|
strncpy(queue_header, CMDHEAD, maxx);
|
2018-12-03 12:40:13 -05:00
|
|
|
strncpy(clamd_header, SUMHEAD, maxx);
|
|
|
|
queue_header[maxx] = '\0';
|
|
|
|
clamd_header[maxx] = '\0';
|
2020-07-24 08:32:47 -07:00
|
|
|
p = queue_header + strlen(queue_header);
|
2018-12-03 12:40:13 -05:00
|
|
|
while (p < queue_header + maxx)
|
|
|
|
*p++ = ' ';
|
|
|
|
p = clamd_header + strlen(clamd_header);
|
|
|
|
while (p < clamd_header + maxx)
|
|
|
|
*p++ = ' ';
|
2020-06-07 19:07:17 -04:00
|
|
|
if (global.num_clamd > 1) {
|
|
|
|
free(multi_queue_header);
|
|
|
|
multi_queue_header = malloc(maxx + 1);
|
|
|
|
OOM_CHECK(multi_queue_header);
|
|
|
|
assert(multi_queue_header);
|
|
|
|
strncpy(multi_queue_header, CMDHEAD2, maxx);
|
|
|
|
multi_queue_header[maxx] = '\0';
|
2020-07-24 08:32:47 -07:00
|
|
|
p = multi_queue_header + strlen(multi_queue_header);
|
2020-06-07 19:07:17 -04:00
|
|
|
while (p < multi_queue_header + maxx)
|
|
|
|
*p++ = ' ';
|
|
|
|
}
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rm_windows(void)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
if (header_window) {
|
|
|
|
delwin(header_window);
|
|
|
|
header_window = NULL;
|
|
|
|
}
|
|
|
|
if (mem_window) {
|
|
|
|
delwin(mem_window);
|
|
|
|
mem_window = NULL;
|
|
|
|
}
|
|
|
|
if (stats_window) {
|
|
|
|
delwin(stats_window);
|
|
|
|
stats_window = NULL;
|
|
|
|
}
|
|
|
|
if (stats_head_window) {
|
|
|
|
delwin(stats_head_window);
|
|
|
|
stats_head_window = NULL;
|
|
|
|
}
|
|
|
|
if (status_bar_window) {
|
|
|
|
delwin(status_bar_window);
|
|
|
|
status_bar_window = NULL;
|
|
|
|
}
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
static void init_windows(int num_clamd)
|
2008-11-03 19:27:20 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
resize();
|
|
|
|
|
|
|
|
rm_windows();
|
|
|
|
/* non-overlapping windows */
|
|
|
|
header_window = subwin(stdscr, 1, maxx, 0, 0);
|
|
|
|
stats_head_window = subwin(stdscr, num_clamd + 1, maxx, 1, 0);
|
|
|
|
maxystats = maxy - num_clamd - 3;
|
|
|
|
stats_window = subwin(stdscr, maxystats, maxx, num_clamd + 2, 0);
|
|
|
|
status_bar_window = subwin(stdscr, 1, maxx, maxy - 1, 0);
|
|
|
|
/* memwindow overlaps, used only in details mode */
|
|
|
|
mem_window = derwin(stats_window, 6, 41, 1, maxx - 41);
|
|
|
|
touchwin(stdscr);
|
|
|
|
werase(stdscr);
|
|
|
|
refresh();
|
|
|
|
memset(status_bar_keys, 0, sizeof(status_bar_keys));
|
|
|
|
status_bar_keys[0] = "H - help";
|
|
|
|
status_bar_keys[1] = "Q - quit";
|
|
|
|
status_bar_keys[2] = "R - reset maximums";
|
|
|
|
if (num_clamd > 1) {
|
|
|
|
status_bar_keys[3] = "^ - previous clamd";
|
|
|
|
status_bar_keys[4] = "v - next clamd";
|
|
|
|
}
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
2009-03-01 10:15:32 +00:00
|
|
|
static void init_ncurses(int num_clamd, int use_default)
|
2008-11-03 19:27:20 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
int default_bg = use_default ? DEFAULT_COLOR : COLOR_BLACK;
|
|
|
|
int default_fg = use_default ? DEFAULT_COLOR : COLOR_WHITE;
|
2020-06-07 19:07:17 -04:00
|
|
|
|
|
|
|
/* newterm() allows us to free curses-allocated memory with delscreen() */
|
|
|
|
if (!(curses_scr = newterm(NULL, stdout, stdin))) {
|
|
|
|
fprintf(stderr, "Failed to initialize curses\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
curses_inited = 1;
|
|
|
|
|
|
|
|
start_color();
|
|
|
|
keypad(stdscr, TRUE); /* enable keyboard mapping */
|
|
|
|
nonl(); /* tell curses not to do NL->CR/NL on output */
|
|
|
|
halfdelay(UPDATE_INTERVAL * 10); /* timeout of 2s when waiting for input*/
|
2023-11-26 15:01:19 -08:00
|
|
|
noecho(); /* don't echo input */
|
2018-12-03 12:40:13 -05:00
|
|
|
curs_set(0); /* turn off cursor */
|
|
|
|
if (use_default)
|
|
|
|
use_default_colors();
|
|
|
|
|
|
|
|
init_pair(header_color, COLOR_BLACK, COLOR_WHITE);
|
|
|
|
init_pair(version_color, default_fg, default_bg);
|
|
|
|
init_pair(error_color, COLOR_WHITE, COLOR_RED);
|
|
|
|
init_pair(value_color, COLOR_GREEN, default_bg);
|
|
|
|
init_pair(descr_color, COLOR_CYAN, default_bg);
|
|
|
|
init_pair(selected_color, COLOR_BLACK, COLOR_CYAN);
|
|
|
|
init_pair(queue_header_color, COLOR_BLACK, COLOR_GREEN);
|
|
|
|
init_pair(activ_color, COLOR_MAGENTA, default_bg);
|
|
|
|
init_pair(dim_color, COLOR_GREEN, default_bg);
|
|
|
|
init_pair(red_color, COLOR_RED, default_bg);
|
|
|
|
|
|
|
|
init_windows(num_clamd);
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void win_start(WINDOW *win, enum colors col)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
wattrset(win, COLOR_PAIR(col));
|
|
|
|
wbkgd(win, COLOR_PAIR(col));
|
|
|
|
werase(win);
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
static void print_colored(WINDOW *win, const char *p)
|
2008-11-03 19:27:20 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
while (*p) {
|
|
|
|
wattron(win, DESCR_ATTR);
|
|
|
|
while (*p && !isdigit(*p))
|
|
|
|
waddch(win, *p++);
|
|
|
|
wattroff(win, DESCR_ATTR);
|
|
|
|
wattron(win, VALUE_ATTR);
|
|
|
|
while (*p && isdigit(*p))
|
|
|
|
waddch(win, *p++);
|
|
|
|
wattroff(win, VALUE_ATTR);
|
|
|
|
}
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void header(void)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
size_t i, x = 0;
|
|
|
|
time_t t;
|
2008-11-03 19:27:20 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
win_start(header_window, header_color);
|
|
|
|
mvwprintw(header_window, 0, 0, " ClamdTOP version %s ", get_version());
|
|
|
|
time(&t);
|
|
|
|
wprintw(header_window, "%s", ctime(&t));
|
|
|
|
wrefresh(header_window);
|
2008-11-12 12:06:15 +00:00
|
|
|
|
2020-07-24 08:32:47 -07:00
|
|
|
/*
|
|
|
|
win_start(version_window, version_color);
|
|
|
|
mvwprintw(version_window, 0, 0, "Connected to: ");
|
|
|
|
print_colored(version_window, clamd_version ? clamd_version : "Unknown");
|
|
|
|
wrefresh(version_window);
|
|
|
|
*/
|
2008-11-03 19:27:20 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
werase(status_bar_window);
|
|
|
|
for (i = 0; i < sizeof(status_bar_keys) / sizeof(status_bar_keys[0]); i++) {
|
|
|
|
const char *s = status_bar_keys[i];
|
|
|
|
if (!s)
|
|
|
|
continue;
|
|
|
|
wattron(status_bar_window, A_REVERSE);
|
|
|
|
if (s[0] == '^') {
|
|
|
|
mvwaddch(status_bar_window, 0, x, ACS_UARROW);
|
|
|
|
s++;
|
|
|
|
x++;
|
|
|
|
} else if (s[0] == 'v') {
|
|
|
|
mvwaddch(status_bar_window, 0, x, ACS_DARROW);
|
|
|
|
s++;
|
|
|
|
x++;
|
|
|
|
}
|
|
|
|
mvwprintw(status_bar_window, 0, x, "%s", s);
|
|
|
|
wattroff(status_bar_window, A_REVERSE);
|
2020-06-07 19:07:17 -04:00
|
|
|
x += strlen(s) + 1;
|
2018-12-03 12:40:13 -05:00
|
|
|
}
|
|
|
|
wrefresh(status_bar_window);
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void show_bar(WINDOW *win, size_t i, unsigned live, unsigned idle,
|
2018-12-03 12:40:13 -05:00
|
|
|
unsigned max, int blink)
|
2008-11-03 19:27:20 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
int y, x, z = 0;
|
|
|
|
unsigned len = 39;
|
|
|
|
unsigned start = 1;
|
|
|
|
unsigned activ = max ? ((live - idle) * (len - start - 2) + (max / 2)) / max : 0;
|
|
|
|
unsigned dim = max ? idle * (len - start - 2) / max : 0;
|
|
|
|
unsigned rem = len - activ - dim - start - 2;
|
|
|
|
|
|
|
|
assert(activ + 2 < len && activ + dim + 2 < len && activ + dim + rem + 2 < len && "Invalid values");
|
|
|
|
mvwaddch(win, i, start, '[' | A_BOLD);
|
|
|
|
wattron(win, A_BOLD | COLOR_PAIR(activ_color));
|
|
|
|
for (i = 0; i < activ; i++)
|
|
|
|
waddch(win, '|');
|
|
|
|
wattroff(win, A_BOLD | COLOR_PAIR(activ_color));
|
|
|
|
wattron(win, A_DIM | COLOR_PAIR(dim_color));
|
|
|
|
for (i = 0; i < dim; i++)
|
|
|
|
waddch(win, '|');
|
|
|
|
wattroff(win, A_DIM | COLOR_PAIR(dim_color));
|
|
|
|
for (i = 0; i < rem; i++)
|
|
|
|
waddch(win, ' ');
|
|
|
|
waddch(win, ']' | A_BOLD);
|
|
|
|
if (blink) {
|
|
|
|
getyx(win, y, x);
|
|
|
|
if ((x < 0) || (y < 0)) {
|
|
|
|
return; /* if getyx() failed, nevermind the blinking */
|
|
|
|
}
|
|
|
|
if (x >= 2) {
|
|
|
|
z = x - 2;
|
|
|
|
}
|
|
|
|
mvwaddch(win, y, z, '>' | A_BLINK | COLOR_PAIR(red_color));
|
|
|
|
move(y, z);
|
|
|
|
}
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
/* --------------------- Error handling ---------------------*/
|
2018-12-03 12:40:13 -05:00
|
|
|
static int normal_exit = 0;
|
2008-11-12 12:06:15 +00:00
|
|
|
static const char *exit_reason = NULL;
|
2018-12-03 12:40:13 -05:00
|
|
|
static const char *exit_func = NULL;
|
|
|
|
static unsigned exit_line = 0;
|
2008-11-12 12:06:15 +00:00
|
|
|
|
|
|
|
static void cleanup(void)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
unsigned i;
|
|
|
|
if (curses_inited) {
|
|
|
|
if (status_bar_window) {
|
|
|
|
werase(status_bar_window);
|
|
|
|
wrefresh(status_bar_window);
|
|
|
|
}
|
|
|
|
rm_windows();
|
2020-01-03 15:53:29 -05:00
|
|
|
endwin();
|
2020-06-07 19:07:17 -04:00
|
|
|
delscreen(curses_scr);
|
2018-12-03 12:40:13 -05:00
|
|
|
}
|
|
|
|
curses_inited = 0;
|
|
|
|
for (i = 0; i < global.num_clamd; i++) {
|
|
|
|
if (global.conn[i].sd && global.conn[i].sd != -1) {
|
2020-05-11 14:46:12 -04:00
|
|
|
(void)send_string_noreconn(&global.conn[i], "nEND\n");
|
Add CMake build tooling
This patch adds experimental-quality CMake build tooling.
The libmspack build required a modification to use "" instead of <> for
header #includes. This will hopefully be included in the libmspack
upstream project when adding CMake build tooling to libmspack.
Removed use of libltdl when using CMake.
Flex & Bison are now required to build.
If -DMAINTAINER_MODE, then GPERF is also required, though it currently
doesn't actually do anything. TODO!
I found that the autotools build system was generating the lexer output
but not actually compiling it, instead using previously generated (and
manually renamed) lexer c source. As a consequence, changes to the .l
and .y files weren't making it into the build. To resolve this, I
removed generated flex/bison files and fixed the tooling to use the
freshly generated files. Flex and bison are now required build tools.
On Windows, this adds a dependency on the winflexbison package,
which can be obtained using Chocolatey or may be manually installed.
CMake tooling only has partial support for building with external LLVM
library, and no support for the internal LLVM (to be removed in the
future). I.e. The CMake build currently only supports the bytecode
interpreter.
Many files used include paths relative to the top source directory or
relative to the current project, rather than relative to each build
target. Modern CMake support requires including internal dependency
headers the same way you would external dependency headers (albeit
with "" instead of <>). This meant correcting all header includes to
be relative to the build targets and not relative to the workspace.
For example, ...
```c
include "../libclamav/clamav.h"
include "clamd/clamd_others.h"
```
... becomes:
```c
// libclamav
include "clamav.h"
// clamd
include "clamd_others.h"
```
Fixes header name conflicts by renaming a few of the files.
Converted the "shared" code into a static library, which depends on
libclamav. The ironically named "shared" static library provides
features common to the ClamAV apps which are not required in
libclamav itself and are not intended for use by downstream projects.
This change was required for correct modern CMake practices but was
also required to use the automake "subdir-objects" option.
This eliminates warnings when running autoreconf which, in the next
version of autoconf & automake are likely to break the build.
libclamav used to build in multiple stages where an earlier stage is
a static library containing utils required by the "shared" code.
Linking clamdscan and clamdtop with this libclamav utils static lib
allowed these two apps to function without libclamav. While this is
nice in theory, the practical gains are minimal and it complicates
the build system. As such, the autotools and CMake tooling was
simplified for improved maintainability and this feature was thrown
out. clamdtop and clamdscan now require libclamav to function.
Removed the nopthreads version of the autotools
libclamav_internal_utils static library and added pthread linking to
a couple apps that may have issues building on some platforms without
it, with the intention of removing needless complexity from the
source. Kept the regular version of libclamav_internal_utils.la
though it is no longer used anywhere but in libclamav.
Added an experimental doxygen build option which attempts to build
clamav.h and libfreshclam doxygen html docs.
The CMake build tooling also may build the example program(s), which
isn't a feature in the Autotools build system.
Changed C standard to C90+ due to inline linking issues with socket.h
when linking libfreshclam.so on Linux.
Generate common.rc for win32.
Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef
from misc.c.
Add CMake files to the automake dist, so users can try the new
CMake tooling w/out having to build from a git clone.
clamonacc changes:
- Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other
similar macros.
- Added a new clamav-clamonacc.service systemd unit file, based on
the work of ChadDevOps & Aaron Brighton.
- Added missing clamonacc man page.
Updates to clamdscan man page, add missing options.
Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use
libclamav).
Rename Windows mspack.dll to libmspack.dll so all ClamAV-built
libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
|
|
|
#ifndef _WIN32
|
2020-01-03 15:53:29 -05:00
|
|
|
close(global.conn[i].sd);
|
2019-11-03 12:46:21 -05:00
|
|
|
#else
|
2020-01-03 15:53:29 -05:00
|
|
|
closesocket(global.conn[i].sd);
|
2019-11-03 12:46:21 -05:00
|
|
|
#endif
|
2018-12-03 12:40:13 -05:00
|
|
|
}
|
|
|
|
free(global.conn[i].version);
|
|
|
|
free(global.conn[i].remote);
|
|
|
|
}
|
|
|
|
free(global.all_stats);
|
|
|
|
free(global.conn);
|
|
|
|
free(queue_header);
|
2020-06-07 19:07:17 -04:00
|
|
|
if (global.num_clamd > 1)
|
|
|
|
free(multi_queue_header);
|
2018-12-03 12:40:13 -05:00
|
|
|
free(clamd_header);
|
|
|
|
if (!normal_exit) {
|
|
|
|
fprintf(stderr, "Abnormal program termination");
|
|
|
|
if (exit_reason) fprintf(stderr, ": %s", exit_reason);
|
|
|
|
if (exit_func) fprintf(stderr, " in %s", exit_func);
|
|
|
|
if (exit_line) fprintf(stderr, " at line %u", exit_line);
|
|
|
|
fputc('\n', stderr);
|
|
|
|
}
|
2008-11-12 12:06:15 +00:00
|
|
|
}
|
|
|
|
|
2008-11-13 21:59:13 +00:00
|
|
|
#ifdef __GNUC__
|
|
|
|
#define __noreturn __attribute__((noreturn))
|
|
|
|
#else
|
|
|
|
#define __noreturn
|
|
|
|
#endif
|
2008-11-12 12:06:15 +00:00
|
|
|
|
2008-11-13 21:59:13 +00:00
|
|
|
static void __noreturn exit_program(enum exit_reason reason, const char *func, unsigned line)
|
2008-11-12 12:06:15 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
switch (reason) {
|
|
|
|
case FAIL_CMDLINE:
|
|
|
|
exit_reason = "Invalid command-line arguments";
|
|
|
|
break;
|
|
|
|
case FAIL_INITIAL_CONN:
|
|
|
|
exit_reason = "Unable to connect to all clamds";
|
|
|
|
break;
|
|
|
|
case OUT_OF_MEMORY:
|
|
|
|
exit_reason = "Out of memory";
|
|
|
|
break;
|
|
|
|
case RECONNECT_FAIL:
|
|
|
|
exit_reason = "Failed to reconnect to clamd after connection was lost";
|
|
|
|
break;
|
|
|
|
case SIGINT_REASON:
|
|
|
|
exit_reason = "User interrupt";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
exit_reason = "Unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
exit_func = func;
|
|
|
|
exit_line = line;
|
|
|
|
exit(reason);
|
2008-11-12 12:06:15 +00:00
|
|
|
}
|
|
|
|
|
2008-11-03 19:27:20 +00:00
|
|
|
struct task {
|
2018-12-03 12:40:13 -05:00
|
|
|
char *line;
|
|
|
|
double tim;
|
|
|
|
int clamd_no;
|
2008-11-03 19:27:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int tasks_compare(const void *a, const void *b)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
const struct task *ta = a;
|
|
|
|
const struct task *tb = b;
|
|
|
|
if (ta->tim < tb->tim)
|
|
|
|
return 1;
|
|
|
|
if (ta->tim > tb->tim)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 14:27:35 +00:00
|
|
|
/* ----------- Socket routines ----------------------- */
|
2009-02-23 12:35:34 +00:00
|
|
|
#ifdef __GNUC__
|
|
|
|
static void print_con_info(conn_t *conn, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
|
|
|
#endif
|
2008-11-12 16:53:27 +00:00
|
|
|
static void print_con_info(conn_t *conn, const char *fmt, ...)
|
2008-11-06 14:27:35 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
if (stats_head_window) {
|
2020-06-07 19:07:17 -04:00
|
|
|
char *buf = malloc(maxx + 1);
|
|
|
|
char *nl = NULL;
|
2018-12-03 12:40:13 -05:00
|
|
|
OOM_CHECK(buf);
|
2020-06-07 19:07:17 -04:00
|
|
|
memset(buf, ' ', maxx + 1);
|
|
|
|
vsnprintf(buf, maxx + 1, fmt, ap);
|
|
|
|
if ((nl = strrchr(buf, '\n')) != NULL)
|
|
|
|
*nl = ' ';
|
2018-12-03 12:40:13 -05:00
|
|
|
buf[strlen(buf)] = ' ';
|
2020-06-07 19:07:17 -04:00
|
|
|
buf[maxx] = '\0';
|
2018-12-03 12:40:13 -05:00
|
|
|
wattron(stats_head_window, ERROR_ATTR);
|
|
|
|
mvwprintw(stats_head_window, conn->line, 0, "%s", buf);
|
|
|
|
wattroff(stats_head_window, ERROR_ATTR);
|
|
|
|
wrefresh(stats_head_window);
|
|
|
|
free(buf);
|
|
|
|
} else
|
|
|
|
vfprintf(stdout, fmt, ap);
|
|
|
|
va_end(ap);
|
2008-11-06 14:27:35 +00:00
|
|
|
}
|
|
|
|
|
2014-02-20 12:13:47 -05:00
|
|
|
char *get_ip(const char *ip)
|
|
|
|
{
|
2020-03-30 10:05:52 -07:00
|
|
|
char *dupip = NULL;
|
2020-07-24 08:32:47 -07:00
|
|
|
char *p1 = NULL;
|
2014-03-07 18:04:05 -05:00
|
|
|
unsigned int i;
|
2014-02-20 12:13:47 -05:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Expected format of ip:
|
2014-03-07 18:04:05 -05:00
|
|
|
* 1) IPv4
|
|
|
|
* 2) IPv4:Port
|
|
|
|
* 3) IPv6
|
|
|
|
* 4) [IPv6]:Port
|
2020-01-03 15:44:07 -05:00
|
|
|
*
|
2014-02-20 12:13:47 -05:00
|
|
|
* Use of IPv6:Port is incorrect. An IPv6 address must be enclosed in brackets.
|
|
|
|
*/
|
|
|
|
|
|
|
|
dupip = strdup(ip);
|
|
|
|
if (!(dupip))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (dupip[0] == '[') {
|
|
|
|
/* IPv6 */
|
|
|
|
p1 = strchr(dupip, ']');
|
|
|
|
if (!(p1)) {
|
|
|
|
free(dupip);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*p1 = '\0';
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
p1 = strdup(dupip + 1);
|
2014-03-07 18:04:05 -05:00
|
|
|
free(dupip);
|
2020-03-30 10:05:52 -07:00
|
|
|
dupip = NULL;
|
2014-03-07 18:04:05 -05:00
|
|
|
return p1;
|
2014-02-20 12:13:47 -05:00
|
|
|
}
|
|
|
|
|
2014-03-07 18:04:05 -05:00
|
|
|
p1 = dupip;
|
2018-12-03 12:40:13 -05:00
|
|
|
i = 0;
|
2014-03-26 13:33:14 -04:00
|
|
|
while ((p1 = strchr(p1, ':'))) {
|
2014-03-07 18:04:05 -05:00
|
|
|
i++;
|
|
|
|
p1++;
|
2014-02-20 12:13:47 -05:00
|
|
|
}
|
|
|
|
|
2020-06-16 18:46:59 -04:00
|
|
|
if (i == 1) {
|
2018-12-03 12:40:13 -05:00
|
|
|
p1 = strchr(dupip, ':');
|
2014-03-07 18:04:05 -05:00
|
|
|
*p1 = '\0';
|
2014-02-20 12:13:47 -05:00
|
|
|
}
|
2020-06-16 18:46:59 -04:00
|
|
|
|
|
|
|
return dupip;
|
2014-02-20 12:13:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
char *get_port(const char *ip)
|
|
|
|
{
|
|
|
|
char *dupip, *p;
|
2018-12-03 12:40:13 -05:00
|
|
|
unsigned int offset = 0;
|
2014-02-20 12:13:47 -05:00
|
|
|
|
|
|
|
dupip = get_ip(ip);
|
|
|
|
if (!(dupip))
|
|
|
|
return NULL;
|
|
|
|
|
2014-03-07 18:04:05 -05:00
|
|
|
if (ip[0] == '[')
|
|
|
|
offset += 2;
|
|
|
|
|
2014-03-26 13:33:14 -04:00
|
|
|
p = (char *)ip + strlen(dupip) + offset;
|
2014-03-07 18:04:05 -05:00
|
|
|
if (*p == ':') {
|
2018-12-03 12:40:13 -05:00
|
|
|
p = strdup(p + 1);
|
2014-03-07 18:04:05 -05:00
|
|
|
free(dupip);
|
|
|
|
return p;
|
|
|
|
}
|
2014-02-20 12:13:47 -05:00
|
|
|
|
2020-05-11 14:46:12 -04:00
|
|
|
free(dupip);
|
2014-03-07 18:04:05 -05:00
|
|
|
return NULL;
|
2014-02-20 12:13:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
char *make_ip(const char *host, const char *port)
|
|
|
|
{
|
|
|
|
char *ip;
|
|
|
|
size_t len;
|
|
|
|
int ipv6;
|
|
|
|
|
2020-07-24 08:32:47 -07:00
|
|
|
if (!host || !port) {
|
2020-03-30 10:05:52 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-02-20 12:13:47 -05:00
|
|
|
len = strlen(host) + strlen(port);
|
|
|
|
|
|
|
|
ipv6 = (strchr(host, ':') != NULL);
|
|
|
|
|
2014-03-03 13:06:39 -05:00
|
|
|
len += (ipv6 ? 4 : 3);
|
2014-02-20 12:13:47 -05:00
|
|
|
|
|
|
|
ip = calloc(1, len);
|
|
|
|
if (!(ip))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
snprintf(ip, len, "%s%s%s:%s", ipv6 ? "[" : "", host, ipv6 ? "]" : "", port);
|
|
|
|
|
|
|
|
return ip;
|
|
|
|
}
|
2008-11-03 19:27:20 +00:00
|
|
|
|
2009-03-01 10:15:27 +00:00
|
|
|
static int make_connection_real(const char *soname, conn_t *conn)
|
2008-11-06 14:27:35 +00:00
|
|
|
{
|
2018-01-22 15:41:22 -05:00
|
|
|
int s = -1;
|
2014-02-06 16:54:29 -05:00
|
|
|
struct timeval tv;
|
2020-06-07 19:07:17 -04:00
|
|
|
char *port = NULL;
|
|
|
|
char *pt = NULL;
|
|
|
|
char *host = pt;
|
2018-12-03 12:40:13 -05:00
|
|
|
struct addrinfo hints, *res = NULL, *p;
|
2014-02-20 12:13:47 -05:00
|
|
|
int err;
|
2020-03-30 10:05:52 -07:00
|
|
|
int ret = 0;
|
2014-02-06 16:54:29 -05:00
|
|
|
|
2020-05-11 14:46:12 -04:00
|
|
|
pt = strdup(soname);
|
|
|
|
OOM_CHECK(pt);
|
|
|
|
|
2014-02-06 16:54:29 -05:00
|
|
|
conn->tcp = 0;
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
2018-12-03 12:40:13 -05:00
|
|
|
if (cli_is_abspath(soname) || (access(soname, F_OK) == 0)) {
|
2014-02-06 16:54:29 -05:00
|
|
|
struct sockaddr_un addr;
|
|
|
|
|
|
|
|
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
2018-12-03 12:40:13 -05:00
|
|
|
if (s < 0) {
|
2014-02-06 16:54:29 -05:00
|
|
|
perror("socket");
|
2020-03-30 10:05:52 -07:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
2014-02-06 16:54:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
strncpy(addr.sun_path, soname, sizeof(addr.sun_path));
|
|
|
|
addr.sun_path[sizeof(addr.sun_path) - 1] = 0x0;
|
|
|
|
|
|
|
|
print_con_info(conn, "Connecting to: %s\n", soname);
|
|
|
|
if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
|
|
|
|
perror("connect");
|
2020-01-03 15:53:29 -05:00
|
|
|
close(s);
|
2020-03-30 10:05:52 -07:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
2014-02-06 16:54:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
goto end;
|
|
|
|
}
|
2008-11-09 22:04:22 +00:00
|
|
|
#endif
|
2014-02-06 16:54:29 -05:00
|
|
|
|
|
|
|
memset(&hints, 0x00, sizeof(struct addrinfo));
|
2018-12-03 12:40:13 -05:00
|
|
|
hints.ai_family = AF_UNSPEC;
|
2014-02-06 16:54:29 -05:00
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
2018-12-03 12:40:13 -05:00
|
|
|
hints.ai_flags = AI_PASSIVE;
|
2014-02-06 16:54:29 -05:00
|
|
|
|
2014-02-20 12:13:47 -05:00
|
|
|
host = get_ip(soname);
|
2020-03-30 10:05:52 -07:00
|
|
|
if (!(host)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
2014-02-20 12:13:47 -05:00
|
|
|
|
|
|
|
port = get_port(soname);
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
conn->tcp = 1;
|
2014-02-06 16:54:29 -05:00
|
|
|
|
2014-02-20 12:13:47 -05:00
|
|
|
print_con_info(conn, "Looking up: %s:%s\n", host, port ? port : "3310");
|
|
|
|
if ((err = getaddrinfo(host, (port != NULL) ? port : "3310", &hints, &res))) {
|
2020-06-07 19:07:17 -04:00
|
|
|
print_con_info(conn, "Could not look up %s:%s, getaddrinfo returned: %s\n",
|
|
|
|
host, port ? port : "3310", gai_strerror(err));
|
2020-03-30 10:05:52 -07:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
2014-02-20 12:13:47 -05:00
|
|
|
}
|
2014-02-06 16:54:29 -05:00
|
|
|
|
|
|
|
for (p = res; p != NULL; p = p->ai_next) {
|
|
|
|
if ((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
|
|
|
|
perror("socket");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-02-20 12:13:47 -05:00
|
|
|
print_con_info(conn, "Connecting to: %s\n", soname);
|
2014-02-06 16:54:29 -05:00
|
|
|
if (connect(s, p->ai_addr, p->ai_addrlen)) {
|
|
|
|
perror("connect");
|
Add CMake build tooling
This patch adds experimental-quality CMake build tooling.
The libmspack build required a modification to use "" instead of <> for
header #includes. This will hopefully be included in the libmspack
upstream project when adding CMake build tooling to libmspack.
Removed use of libltdl when using CMake.
Flex & Bison are now required to build.
If -DMAINTAINER_MODE, then GPERF is also required, though it currently
doesn't actually do anything. TODO!
I found that the autotools build system was generating the lexer output
but not actually compiling it, instead using previously generated (and
manually renamed) lexer c source. As a consequence, changes to the .l
and .y files weren't making it into the build. To resolve this, I
removed generated flex/bison files and fixed the tooling to use the
freshly generated files. Flex and bison are now required build tools.
On Windows, this adds a dependency on the winflexbison package,
which can be obtained using Chocolatey or may be manually installed.
CMake tooling only has partial support for building with external LLVM
library, and no support for the internal LLVM (to be removed in the
future). I.e. The CMake build currently only supports the bytecode
interpreter.
Many files used include paths relative to the top source directory or
relative to the current project, rather than relative to each build
target. Modern CMake support requires including internal dependency
headers the same way you would external dependency headers (albeit
with "" instead of <>). This meant correcting all header includes to
be relative to the build targets and not relative to the workspace.
For example, ...
```c
include "../libclamav/clamav.h"
include "clamd/clamd_others.h"
```
... becomes:
```c
// libclamav
include "clamav.h"
// clamd
include "clamd_others.h"
```
Fixes header name conflicts by renaming a few of the files.
Converted the "shared" code into a static library, which depends on
libclamav. The ironically named "shared" static library provides
features common to the ClamAV apps which are not required in
libclamav itself and are not intended for use by downstream projects.
This change was required for correct modern CMake practices but was
also required to use the automake "subdir-objects" option.
This eliminates warnings when running autoreconf which, in the next
version of autoconf & automake are likely to break the build.
libclamav used to build in multiple stages where an earlier stage is
a static library containing utils required by the "shared" code.
Linking clamdscan and clamdtop with this libclamav utils static lib
allowed these two apps to function without libclamav. While this is
nice in theory, the practical gains are minimal and it complicates
the build system. As such, the autotools and CMake tooling was
simplified for improved maintainability and this feature was thrown
out. clamdtop and clamdscan now require libclamav to function.
Removed the nopthreads version of the autotools
libclamav_internal_utils static library and added pthread linking to
a couple apps that may have issues building on some platforms without
it, with the intention of removing needless complexity from the
source. Kept the regular version of libclamav_internal_utils.la
though it is no longer used anywhere but in libclamav.
Added an experimental doxygen build option which attempts to build
clamav.h and libfreshclam doxygen html docs.
The CMake build tooling also may build the example program(s), which
isn't a feature in the Autotools build system.
Changed C standard to C90+ due to inline linking issues with socket.h
when linking libfreshclam.so on Linux.
Generate common.rc for win32.
Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef
from misc.c.
Add CMake files to the automake dist, so users can try the new
CMake tooling w/out having to build from a git clone.
clamonacc changes:
- Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other
similar macros.
- Added a new clamav-clamonacc.service systemd unit file, based on
the work of ChadDevOps & Aaron Brighton.
- Added missing clamonacc man page.
Updates to clamdscan man page, add missing options.
Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use
libclamav).
Rename Windows mspack.dll to libmspack.dll so all ClamAV-built
libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
|
|
|
#ifndef _WIN32
|
2013-02-12 11:12:45 -05:00
|
|
|
close(s);
|
2019-11-03 12:46:21 -05:00
|
|
|
#else
|
2020-01-03 15:53:29 -05:00
|
|
|
closesocket(s);
|
2019-11-03 12:46:21 -05:00
|
|
|
#endif
|
2014-02-06 16:54:29 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-03-30 10:05:52 -07:00
|
|
|
if (p == NULL) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
2020-06-07 19:07:17 -04:00
|
|
|
|
2020-03-30 10:05:52 -07:00
|
|
|
end:
|
|
|
|
conn->sd = s;
|
|
|
|
gettimeofday(&conn->tv_conn, NULL);
|
|
|
|
tv.tv_sec = 30;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
setsockopt(conn->sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
|
|
|
2020-06-07 19:07:17 -04:00
|
|
|
if (conn->remote != soname) {
|
|
|
|
/* when we reconnect, they are the same */
|
|
|
|
if (NULL != conn->remote) {
|
|
|
|
free(conn->remote);
|
|
|
|
conn->remote = NULL;
|
|
|
|
}
|
|
|
|
conn->remote = make_ip(host, (port != NULL) ? port : "3310");
|
|
|
|
}
|
|
|
|
|
2020-03-30 10:05:52 -07:00
|
|
|
done:
|
|
|
|
if (NULL != res) {
|
2014-02-06 16:54:29 -05:00
|
|
|
freeaddrinfo(res);
|
2020-03-30 10:05:52 -07:00
|
|
|
res = NULL;
|
|
|
|
}
|
2014-02-06 16:54:29 -05:00
|
|
|
|
2020-03-30 10:05:52 -07:00
|
|
|
if (NULL != pt) {
|
|
|
|
free(pt);
|
|
|
|
pt = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != host) {
|
|
|
|
free(host);
|
|
|
|
host = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != port) {
|
|
|
|
free(port);
|
|
|
|
port = NULL;
|
|
|
|
}
|
2014-02-06 16:54:29 -05:00
|
|
|
|
2020-03-30 10:05:52 -07:00
|
|
|
return ret;
|
2008-11-06 14:27:35 +00:00
|
|
|
}
|
|
|
|
|
2009-03-01 10:15:27 +00:00
|
|
|
static int make_connection(const char *soname, conn_t *conn)
|
|
|
|
{
|
|
|
|
int rc;
|
2025-06-04 16:47:57 +02:00
|
|
|
int rv;
|
2009-03-01 10:15:27 +00:00
|
|
|
|
2020-03-30 10:05:52 -07:00
|
|
|
if (!soname) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-03-01 10:15:27 +00:00
|
|
|
if ((rc = make_connection_real(soname, conn)))
|
2014-02-06 16:54:29 -05:00
|
|
|
return rc;
|
|
|
|
|
2009-03-01 10:15:27 +00:00
|
|
|
send_string(conn, "nIDSESSION\nnVERSION\n");
|
|
|
|
free(conn->version);
|
|
|
|
conn->version = NULL;
|
2025-06-04 16:47:57 +02:00
|
|
|
|
|
|
|
rv = read_version(conn);
|
|
|
|
if (rv == -3) {
|
|
|
|
print_con_info(conn, "VERSION command unavailable, consider enabling it in the clamd configuration.\n");
|
|
|
|
EXIT_PROGRAM(FAIL_INITIAL_CONN);
|
|
|
|
} else if (!rv) {
|
|
|
|
// check if STATS command is available
|
|
|
|
if (check_stats_available(conn)) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
print_con_info(conn, "STATS command unavailable, consider enabling it in the clamd configuration.\n");
|
|
|
|
EXIT_PROGRAM(FAIL_INITIAL_CONN);
|
|
|
|
}
|
|
|
|
}
|
2009-03-01 10:15:27 +00:00
|
|
|
|
|
|
|
/* clamd < 0.95 */
|
|
|
|
if ((rc = make_connection_real(soname, conn)))
|
2014-02-06 16:54:29 -05:00
|
|
|
return rc;
|
|
|
|
|
2009-03-01 10:15:27 +00:00
|
|
|
send_string(conn, "nSESSION\nnVERSION\n");
|
|
|
|
conn->version = NULL;
|
|
|
|
if (!read_version(conn))
|
2014-02-06 16:54:29 -05:00
|
|
|
return 0;
|
|
|
|
|
2009-03-01 10:15:27 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
static void reconnect(conn_t *conn);
|
2008-11-06 14:27:35 +00:00
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
static int send_string_noreconn(conn_t *conn, const char *cmd)
|
2008-11-06 14:27:35 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(cmd);
|
|
|
|
assert(conn && conn->sd > 0);
|
|
|
|
return send(conn->sd, cmd, strlen(cmd), 0);
|
2008-11-12 12:06:15 +00:00
|
|
|
}
|
2008-11-06 14:27:35 +00:00
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
static void send_string(conn_t *conn, const char *cmd)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
while (send_string_noreconn(conn, cmd) == -1) {
|
|
|
|
reconnect(conn);
|
|
|
|
}
|
2008-11-06 14:27:35 +00:00
|
|
|
}
|
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
static int tries = 0;
|
|
|
|
static void reconnect(conn_t *conn)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
if (++tries > 3) {
|
|
|
|
EXIT_PROGRAM(RECONNECT_FAIL);
|
|
|
|
}
|
2020-01-03 15:53:29 -05:00
|
|
|
if (conn->sd != -1) {
|
Add CMake build tooling
This patch adds experimental-quality CMake build tooling.
The libmspack build required a modification to use "" instead of <> for
header #includes. This will hopefully be included in the libmspack
upstream project when adding CMake build tooling to libmspack.
Removed use of libltdl when using CMake.
Flex & Bison are now required to build.
If -DMAINTAINER_MODE, then GPERF is also required, though it currently
doesn't actually do anything. TODO!
I found that the autotools build system was generating the lexer output
but not actually compiling it, instead using previously generated (and
manually renamed) lexer c source. As a consequence, changes to the .l
and .y files weren't making it into the build. To resolve this, I
removed generated flex/bison files and fixed the tooling to use the
freshly generated files. Flex and bison are now required build tools.
On Windows, this adds a dependency on the winflexbison package,
which can be obtained using Chocolatey or may be manually installed.
CMake tooling only has partial support for building with external LLVM
library, and no support for the internal LLVM (to be removed in the
future). I.e. The CMake build currently only supports the bytecode
interpreter.
Many files used include paths relative to the top source directory or
relative to the current project, rather than relative to each build
target. Modern CMake support requires including internal dependency
headers the same way you would external dependency headers (albeit
with "" instead of <>). This meant correcting all header includes to
be relative to the build targets and not relative to the workspace.
For example, ...
```c
include "../libclamav/clamav.h"
include "clamd/clamd_others.h"
```
... becomes:
```c
// libclamav
include "clamav.h"
// clamd
include "clamd_others.h"
```
Fixes header name conflicts by renaming a few of the files.
Converted the "shared" code into a static library, which depends on
libclamav. The ironically named "shared" static library provides
features common to the ClamAV apps which are not required in
libclamav itself and are not intended for use by downstream projects.
This change was required for correct modern CMake practices but was
also required to use the automake "subdir-objects" option.
This eliminates warnings when running autoreconf which, in the next
version of autoconf & automake are likely to break the build.
libclamav used to build in multiple stages where an earlier stage is
a static library containing utils required by the "shared" code.
Linking clamdscan and clamdtop with this libclamav utils static lib
allowed these two apps to function without libclamav. While this is
nice in theory, the practical gains are minimal and it complicates
the build system. As such, the autotools and CMake tooling was
simplified for improved maintainability and this feature was thrown
out. clamdtop and clamdscan now require libclamav to function.
Removed the nopthreads version of the autotools
libclamav_internal_utils static library and added pthread linking to
a couple apps that may have issues building on some platforms without
it, with the intention of removing needless complexity from the
source. Kept the regular version of libclamav_internal_utils.la
though it is no longer used anywhere but in libclamav.
Added an experimental doxygen build option which attempts to build
clamav.h and libfreshclam doxygen html docs.
The CMake build tooling also may build the example program(s), which
isn't a feature in the Autotools build system.
Changed C standard to C90+ due to inline linking issues with socket.h
when linking libfreshclam.so on Linux.
Generate common.rc for win32.
Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef
from misc.c.
Add CMake files to the automake dist, so users can try the new
CMake tooling w/out having to build from a git clone.
clamonacc changes:
- Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other
similar macros.
- Added a new clamav-clamonacc.service systemd unit file, based on
the work of ChadDevOps & Aaron Brighton.
- Added missing clamonacc man page.
Updates to clamdscan man page, add missing options.
Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use
libclamav).
Rename Windows mspack.dll to libmspack.dll so all ClamAV-built
libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
|
|
|
#ifndef _WIN32
|
2020-01-03 15:53:29 -05:00
|
|
|
close(conn->sd);
|
2019-11-03 12:46:21 -05:00
|
|
|
#else
|
2020-01-03 15:53:29 -05:00
|
|
|
closesocket(conn->sd);
|
2019-11-03 12:46:21 -05:00
|
|
|
#endif
|
2020-01-03 15:53:29 -05:00
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
if (make_connection(conn->remote, conn) < 0) {
|
|
|
|
print_con_info(conn, "Unable to reconnect to %s: %s", conn->remote, strerror(errno));
|
|
|
|
EXIT_PROGRAM(RECONNECT_FAIL);
|
|
|
|
}
|
|
|
|
tries = 0;
|
2008-11-12 12:06:15 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 14:27:35 +00:00
|
|
|
static int recv_line(conn_t *conn, char *buf, size_t len)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(len > 0);
|
|
|
|
assert(conn);
|
|
|
|
assert(buf);
|
|
|
|
|
|
|
|
len--;
|
|
|
|
if (!len || conn->sd == -1)
|
|
|
|
return 0;
|
|
|
|
assert(conn->sd > 0);
|
|
|
|
while (len > 0) {
|
|
|
|
ssize_t nread = recv(conn->sd, buf, len, MSG_PEEK);
|
|
|
|
if (nread <= 0) {
|
|
|
|
print_con_info(conn, "%s: %s", conn->remote, strerror(errno));
|
|
|
|
/* it could be a timeout, be nice and send an END */
|
2020-05-11 14:46:12 -04:00
|
|
|
(void)send_string_noreconn(conn, "nEND\n");
|
Add CMake build tooling
This patch adds experimental-quality CMake build tooling.
The libmspack build required a modification to use "" instead of <> for
header #includes. This will hopefully be included in the libmspack
upstream project when adding CMake build tooling to libmspack.
Removed use of libltdl when using CMake.
Flex & Bison are now required to build.
If -DMAINTAINER_MODE, then GPERF is also required, though it currently
doesn't actually do anything. TODO!
I found that the autotools build system was generating the lexer output
but not actually compiling it, instead using previously generated (and
manually renamed) lexer c source. As a consequence, changes to the .l
and .y files weren't making it into the build. To resolve this, I
removed generated flex/bison files and fixed the tooling to use the
freshly generated files. Flex and bison are now required build tools.
On Windows, this adds a dependency on the winflexbison package,
which can be obtained using Chocolatey or may be manually installed.
CMake tooling only has partial support for building with external LLVM
library, and no support for the internal LLVM (to be removed in the
future). I.e. The CMake build currently only supports the bytecode
interpreter.
Many files used include paths relative to the top source directory or
relative to the current project, rather than relative to each build
target. Modern CMake support requires including internal dependency
headers the same way you would external dependency headers (albeit
with "" instead of <>). This meant correcting all header includes to
be relative to the build targets and not relative to the workspace.
For example, ...
```c
include "../libclamav/clamav.h"
include "clamd/clamd_others.h"
```
... becomes:
```c
// libclamav
include "clamav.h"
// clamd
include "clamd_others.h"
```
Fixes header name conflicts by renaming a few of the files.
Converted the "shared" code into a static library, which depends on
libclamav. The ironically named "shared" static library provides
features common to the ClamAV apps which are not required in
libclamav itself and are not intended for use by downstream projects.
This change was required for correct modern CMake practices but was
also required to use the automake "subdir-objects" option.
This eliminates warnings when running autoreconf which, in the next
version of autoconf & automake are likely to break the build.
libclamav used to build in multiple stages where an earlier stage is
a static library containing utils required by the "shared" code.
Linking clamdscan and clamdtop with this libclamav utils static lib
allowed these two apps to function without libclamav. While this is
nice in theory, the practical gains are minimal and it complicates
the build system. As such, the autotools and CMake tooling was
simplified for improved maintainability and this feature was thrown
out. clamdtop and clamdscan now require libclamav to function.
Removed the nopthreads version of the autotools
libclamav_internal_utils static library and added pthread linking to
a couple apps that may have issues building on some platforms without
it, with the intention of removing needless complexity from the
source. Kept the regular version of libclamav_internal_utils.la
though it is no longer used anywhere but in libclamav.
Added an experimental doxygen build option which attempts to build
clamav.h and libfreshclam doxygen html docs.
The CMake build tooling also may build the example program(s), which
isn't a feature in the Autotools build system.
Changed C standard to C90+ due to inline linking issues with socket.h
when linking libfreshclam.so on Linux.
Generate common.rc for win32.
Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef
from misc.c.
Add CMake files to the automake dist, so users can try the new
CMake tooling w/out having to build from a git clone.
clamonacc changes:
- Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other
similar macros.
- Added a new clamav-clamonacc.service systemd unit file, based on
the work of ChadDevOps & Aaron Brighton.
- Added missing clamonacc man page.
Updates to clamdscan man page, add missing options.
Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use
libclamav).
Rename Windows mspack.dll to libmspack.dll so all ClamAV-built
libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
|
|
|
#ifndef _WIN32
|
2020-01-03 15:53:29 -05:00
|
|
|
close(conn->sd);
|
2019-11-03 12:46:21 -05:00
|
|
|
#else
|
2020-01-03 15:53:29 -05:00
|
|
|
closesocket(conn->sd);
|
2019-11-03 12:46:21 -05:00
|
|
|
#endif
|
2018-12-03 12:40:13 -05:00
|
|
|
conn->sd = -1;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
char *p = memchr(buf, '\n', nread);
|
|
|
|
if (p) {
|
|
|
|
len = p - buf + 1;
|
|
|
|
} else
|
|
|
|
len = nread;
|
|
|
|
assert(len > 0);
|
|
|
|
assert(len <= (size_t)nread);
|
|
|
|
nread = recv(conn->sd, buf, len, 0);
|
|
|
|
if (nread == -1)
|
|
|
|
reconnect(conn);
|
|
|
|
else {
|
|
|
|
assert(nread > 0 && (size_t)nread == len);
|
|
|
|
buf += nread;
|
|
|
|
}
|
|
|
|
if (p)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*buf = '\0';
|
|
|
|
return 1;
|
2008-11-06 14:27:35 +00:00
|
|
|
}
|
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
static void output_queue(size_t line, ssize_t max)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
ssize_t i, j;
|
2020-06-07 19:07:17 -04:00
|
|
|
int tasks_truncd = 0;
|
2018-12-03 12:40:13 -05:00
|
|
|
struct task *tasks = global.tasks;
|
|
|
|
struct task *filtered_tasks = calloc(global.n, sizeof(*filtered_tasks));
|
|
|
|
OOM_CHECK(filtered_tasks);
|
|
|
|
for (i = 0, j = 0; i < global.n; i++) {
|
|
|
|
if (detail_selected == -1 || detail_is_selected(tasks[i].clamd_no - 1)) {
|
|
|
|
filtered_tasks[j++] = tasks[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wattron(stats_window, COLOR_PAIR(queue_header_color));
|
2020-06-07 19:07:17 -04:00
|
|
|
if (detail_selected == -1 && global.num_clamd > 1)
|
|
|
|
mvwprintw(stats_window, line, 0, "%s", multi_queue_header);
|
2018-12-03 12:40:13 -05:00
|
|
|
else
|
2020-06-07 19:07:17 -04:00
|
|
|
mvwprintw(stats_window, line, 0, "%s", queue_header);
|
|
|
|
wattroff(stats_window, COLOR_PAIR(queue_header_color));
|
|
|
|
if (max < j) {
|
2018-12-03 12:40:13 -05:00
|
|
|
--max;
|
2020-06-07 19:07:17 -04:00
|
|
|
tasks_truncd = 1;
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
if (max < 0) max = 0;
|
2020-06-07 19:07:17 -04:00
|
|
|
for (i = 0; i < j && i < max; i++) {
|
2018-12-03 12:40:13 -05:00
|
|
|
char *cmde;
|
|
|
|
assert(tasks);
|
|
|
|
cmde = strchr(filtered_tasks[i].line, ' ');
|
|
|
|
if (cmde) {
|
|
|
|
char cmd[16];
|
|
|
|
const char *filstart = strchr(cmde + 1, ' ');
|
|
|
|
strncpy(cmd, filtered_tasks[i].line, sizeof(cmd) - 1);
|
|
|
|
cmd[15] = '\0';
|
|
|
|
if (filtered_tasks[i].line + 15 > cmde)
|
|
|
|
cmd[cmde - filtered_tasks[i].line] = '\0';
|
|
|
|
if (filstart) {
|
2020-06-07 19:07:17 -04:00
|
|
|
size_t oldline = ++line;
|
2020-07-24 08:32:47 -07:00
|
|
|
char *nl = strrchr(++filstart, '\n');
|
2020-06-07 19:07:17 -04:00
|
|
|
if (nl != NULL)
|
|
|
|
*nl = '\0';
|
|
|
|
wattron(stats_window, A_BOLD);
|
2018-12-03 12:40:13 -05:00
|
|
|
if (detail_selected == -1 && global.num_clamd > 1)
|
2020-06-07 19:07:17 -04:00
|
|
|
mvwprintw(stats_window, line, 0, "%2u %s", filtered_tasks[i].clamd_no, cmd + 1);
|
2018-12-03 12:40:13 -05:00
|
|
|
else
|
2020-06-07 19:07:17 -04:00
|
|
|
mvwprintw(stats_window, line, 0, " %s", cmd + 1);
|
|
|
|
wattroff(stats_window, A_BOLD);
|
|
|
|
mvwprintw(stats_window, line, 15, "%10.03fs", filtered_tasks[i].tim);
|
|
|
|
mvwprintw(stats_window, line, 30, "%s", filstart);
|
|
|
|
line = getcury(stats_window);
|
|
|
|
if (line > oldline)
|
|
|
|
max -= line - oldline;
|
|
|
|
if (!tasks_truncd && max < j) {
|
|
|
|
--max;
|
|
|
|
tasks_truncd = 1;
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-07 19:07:17 -04:00
|
|
|
if (tasks_truncd) {
|
2018-12-03 12:40:13 -05:00
|
|
|
/* in summary mode we can only show a max amount of tasks */
|
|
|
|
wattron(stats_window, A_DIM | COLOR_PAIR(header_color));
|
2020-06-07 19:07:17 -04:00
|
|
|
mvwprintw(stats_window, maxystats - 1, 0, "*** %u more task(s) not shown ***", (unsigned)(j - i));
|
2018-12-03 12:40:13 -05:00
|
|
|
wattroff(stats_window, A_DIM | COLOR_PAIR(header_color));
|
|
|
|
}
|
|
|
|
free(filtered_tasks);
|
2008-11-12 12:06:15 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 14:27:35 +00:00
|
|
|
/* ---------------------- stats parsing routines ------------------- */
|
2008-11-12 12:06:15 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
static void parse_queue(conn_t *conn, char *buf, size_t len, unsigned idx)
|
2008-11-03 19:27:20 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
do {
|
|
|
|
double tim;
|
|
|
|
const char *t = strchr(buf, ' ');
|
|
|
|
if (!t)
|
|
|
|
continue;
|
|
|
|
if (sscanf(t, "%lf", &tim) != 1)
|
|
|
|
continue;
|
|
|
|
++global.n;
|
|
|
|
global.tasks = realloc(global.tasks, sizeof(*global.tasks) * global.n);
|
|
|
|
OOM_CHECK(global.tasks);
|
|
|
|
global.tasks[global.n - 1].line = strdup(buf);
|
|
|
|
OOM_CHECK(global.tasks[global.n - 1].line);
|
|
|
|
global.tasks[global.n - 1].tim = tim;
|
|
|
|
global.tasks[global.n - 1].clamd_no = idx + 1;
|
|
|
|
} while (recv_line(conn, buf, len) && buf[0] == '\t' && strcmp("END\n", buf) != 0);
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
2008-11-16 16:38:14 +00:00
|
|
|
static unsigned biggest_mem = 0;
|
2008-11-03 19:27:20 +00:00
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
static void output_memstats(struct stats *stats)
|
2008-11-03 19:27:20 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
char buf[128];
|
|
|
|
unsigned long totalmem;
|
|
|
|
int blink = 0;
|
|
|
|
|
|
|
|
werase(mem_window);
|
2020-06-07 19:07:17 -04:00
|
|
|
if (stats->mem > 0 || (stats->mem >= 0 && (stats->pools_total > 0))) {
|
2018-12-03 12:40:13 -05:00
|
|
|
box(mem_window, 0, 0);
|
|
|
|
|
|
|
|
if (stats->mem > 0)
|
2020-06-07 19:07:17 -04:00
|
|
|
snprintf(buf, sizeof(buf), "heap %4.0fM mmap %4.0fM unused%4.0fM",
|
|
|
|
stats->heapu, stats->mmapu, stats->releasable);
|
2018-12-03 12:40:13 -05:00
|
|
|
else
|
|
|
|
snprintf(buf, sizeof(buf), "heap N/A mmap N/A unused N/A");
|
|
|
|
mvwprintw(mem_window, 1, 1, "Mem: ");
|
|
|
|
print_colored(mem_window, buf);
|
|
|
|
|
|
|
|
mvwprintw(mem_window, 2, 1, "Libc: ");
|
|
|
|
if (stats->mem > 0)
|
2020-06-07 19:07:17 -04:00
|
|
|
snprintf(buf, sizeof(buf), "used %4.0fM free %4.0fM total %4.0fM",
|
|
|
|
stats->totalu, stats->totalf, stats->totalu + stats->totalf);
|
2018-12-03 12:40:13 -05:00
|
|
|
else
|
|
|
|
snprintf(buf, sizeof(buf), "used N/A free N/A total N/A");
|
|
|
|
print_colored(mem_window, buf);
|
|
|
|
|
|
|
|
mvwprintw(mem_window, 3, 1, "Pool: ");
|
2020-06-07 19:07:17 -04:00
|
|
|
snprintf(buf, sizeof(buf), "count %4u used %4.0fM total %4.0fM",
|
|
|
|
stats->pools_cnt, stats->pools_used, stats->pools_total);
|
2018-12-03 12:40:13 -05:00
|
|
|
print_colored(mem_window, buf);
|
|
|
|
|
2020-06-07 19:07:17 -04:00
|
|
|
totalmem = (stats->heapu + stats->mmapu + stats->pools_total) * 1000;
|
2018-12-03 12:40:13 -05:00
|
|
|
if (totalmem > biggest_mem) {
|
|
|
|
biggest_mem = totalmem;
|
|
|
|
blink = 1;
|
|
|
|
}
|
2020-06-07 19:07:17 -04:00
|
|
|
show_bar(mem_window, 4, totalmem,
|
|
|
|
(stats->mmapu + stats->releasable + stats->pools_total - stats->pools_used) * 1000,
|
2018-12-03 12:40:13 -05:00
|
|
|
biggest_mem, blink);
|
|
|
|
}
|
|
|
|
wrefresh(mem_window);
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
static void parse_memstats(const char *line, struct stats *stats)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
if (sscanf(line, " heap %lfM mmap %lfM used %lfM free %lfM releasable %lfM pools %u pools_used %lfM pools_total %lfM",
|
2020-06-07 19:07:17 -04:00
|
|
|
&stats->heapu, &stats->mmapu, &stats->totalu, &stats->totalf, &stats->releasable,
|
|
|
|
&stats->pools_cnt, &stats->pools_used, &stats->pools_total) != 8) {
|
2018-12-03 12:40:13 -05:00
|
|
|
if (sscanf(line, " heap N/A mmap N/A used N/A free N/A releasable N/A pools %u pools_used %lfM pools_total %lfM",
|
2020-06-07 19:07:17 -04:00
|
|
|
&stats->pools_cnt, &stats->pools_used, &stats->pools_total) != 3) {
|
2018-12-03 12:40:13 -05:00
|
|
|
stats->mem = -1;
|
|
|
|
return;
|
|
|
|
}
|
2020-06-07 19:07:17 -04:00
|
|
|
stats->mem = 0;
|
2018-12-03 12:40:13 -05:00
|
|
|
return;
|
|
|
|
}
|
2020-06-07 19:07:17 -04:00
|
|
|
stats->mem = stats->heapu + stats->mmapu + stats->pools_total;
|
2008-11-12 12:06:15 +00:00
|
|
|
}
|
2008-11-03 19:27:20 +00:00
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
static int output_stats(struct stats *stats, unsigned idx)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
char buf[128];
|
2020-06-07 19:07:17 -04:00
|
|
|
char timbuf[14];
|
2018-12-03 12:40:13 -05:00
|
|
|
int blink = 0;
|
|
|
|
size_t i = 0;
|
|
|
|
char mem[6];
|
|
|
|
WINDOW *win = stats_head_window;
|
|
|
|
int sel = detail_is_selected(idx);
|
|
|
|
char *line = malloc(maxx + 1);
|
2020-06-07 19:07:17 -04:00
|
|
|
int len = 0;
|
2018-12-03 12:40:13 -05:00
|
|
|
|
|
|
|
OOM_CHECK(line);
|
|
|
|
|
|
|
|
if (stats->mem <= 0 || stats->stats_unsupp) {
|
|
|
|
strncpy(mem, "N/A", sizeof(mem));
|
|
|
|
mem[sizeof(mem) - 1] = '\0';
|
|
|
|
} else {
|
2020-06-07 19:07:17 -04:00
|
|
|
const char *format;
|
2018-12-03 12:40:13 -05:00
|
|
|
char c;
|
|
|
|
double s;
|
2020-06-07 19:07:17 -04:00
|
|
|
if (stats->mem >= 1024) {
|
2018-12-03 12:40:13 -05:00
|
|
|
c = 'G';
|
|
|
|
s = stats->mem / 1024.0;
|
|
|
|
} else {
|
|
|
|
c = 'M';
|
|
|
|
s = stats->mem;
|
|
|
|
}
|
2020-06-07 19:07:17 -04:00
|
|
|
if (s >= 99.95)
|
|
|
|
format = "%.0f%c";
|
|
|
|
else if (s >= 9.995)
|
|
|
|
format = "%.1f%c";
|
|
|
|
else
|
|
|
|
format = "%.2f%c";
|
|
|
|
|
|
|
|
snprintf(mem, sizeof(mem), format, s, c);
|
|
|
|
mem[sizeof(mem) - 1] = '\0';
|
2018-12-03 12:40:13 -05:00
|
|
|
}
|
|
|
|
i = idx + 1;
|
|
|
|
|
|
|
|
if (!stats->db_time.tm_year) {
|
|
|
|
strncpy(timbuf, "N/A", sizeof(timbuf));
|
|
|
|
timbuf[sizeof(timbuf) - 1] = '\0';
|
|
|
|
} else
|
2020-06-07 19:07:17 -04:00
|
|
|
snprintf(timbuf, sizeof(timbuf), "%04u-%02u-%02uT%02u",
|
2018-12-03 12:40:13 -05:00
|
|
|
1900 + stats->db_time.tm_year,
|
|
|
|
stats->db_time.tm_mon + 1,
|
|
|
|
stats->db_time.tm_mday,
|
|
|
|
stats->db_time.tm_hour);
|
|
|
|
|
|
|
|
memset(line, ' ', maxx + 1);
|
|
|
|
if (!stats->stats_unsupp) {
|
2020-06-07 19:07:17 -04:00
|
|
|
len = snprintf(line, maxx + 1, "%2u %02u:%02u:%02u %3u %3u %5u %5u %5s %-7s %5s %-13s %s",
|
|
|
|
idx + 1, stats->conn_hr, stats->conn_min, stats->conn_sec,
|
|
|
|
stats->live, stats->idle,
|
|
|
|
stats->current_q, stats->biggest_queue,
|
|
|
|
mem,
|
|
|
|
stats->engine_version, stats->db_version, timbuf, stats->remote);
|
2018-12-03 12:40:13 -05:00
|
|
|
} else {
|
2020-06-07 19:07:17 -04:00
|
|
|
len = snprintf(line, maxx + 1, "%2u %02u:%02u:%02u N/A N/A N/A N/A N/A %-7s %5s %-13s %s",
|
|
|
|
idx + 1, stats->conn_hr, stats->conn_min, stats->conn_sec,
|
|
|
|
stats->engine_version, stats->db_version, timbuf, stats->remote);
|
2018-12-03 12:40:13 -05:00
|
|
|
}
|
|
|
|
line[maxx] = '\0';
|
|
|
|
line[strlen(line)] = ' ';
|
|
|
|
if (sel) {
|
|
|
|
wattron(win, COLOR_PAIR(selected_color));
|
|
|
|
}
|
|
|
|
mvwprintw(win, i, 0, "%s", line);
|
|
|
|
if (sel) {
|
|
|
|
wattroff(win, COLOR_PAIR(selected_color));
|
|
|
|
}
|
2020-07-24 08:32:47 -07:00
|
|
|
if ((unsigned)len > maxx) {
|
2020-06-07 19:07:17 -04:00
|
|
|
wattron(win, A_DIM | COLOR_PAIR(header_color));
|
|
|
|
mvwprintw(win, i, maxx - 3, "...");
|
|
|
|
wattroff(win, A_DIM | COLOR_PAIR(header_color));
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
win = stats_window;
|
|
|
|
i = 0;
|
|
|
|
if (sel && !stats->stats_unsupp) {
|
|
|
|
memset(line, ' ', maxx + 1);
|
2020-06-07 19:07:17 -04:00
|
|
|
snprintf(line, maxx + 1, "Details for Clamd version: %s", stats->version);
|
2018-12-03 12:40:13 -05:00
|
|
|
line[maxx] = '\0';
|
|
|
|
line[strlen(line)] = ' ';
|
|
|
|
wattron(win, COLOR_PAIR(queue_header_color));
|
|
|
|
mvwprintw(win, i++, 0, "%s", line);
|
|
|
|
wattroff(win, COLOR_PAIR(queue_header_color));
|
|
|
|
mvwprintw(win, i++, 0, "Primary threads: ");
|
2020-06-07 19:07:17 -04:00
|
|
|
snprintf(buf, sizeof(buf), "live%3u idle%3u max%3u", stats->prim_live, stats->prim_idle, stats->prim_max);
|
2018-12-03 12:40:13 -05:00
|
|
|
print_colored(win, buf);
|
|
|
|
show_bar(win, i++, stats->prim_live, stats->prim_idle, stats->prim_max, 0);
|
2020-07-24 08:32:47 -07:00
|
|
|
/*
|
|
|
|
mvwprintw(win, i++, 0, "Multiscan pool : ");
|
|
|
|
snprintf(buf, sizeof(buf), "live %3u idle %3u max %3u", stats->live, stats->idle, stats->max);
|
|
|
|
print_colored(win, buf);
|
|
|
|
show_bar(win, i++, stats->live, stats->idle, stats->max, 0);
|
|
|
|
*/
|
2008-11-12 12:06:15 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
blink = 0;
|
|
|
|
if (stats->current_q > stats->biggest_queue) {
|
|
|
|
stats->biggest_queue = stats->current_q;
|
|
|
|
blink = 1;
|
|
|
|
}
|
|
|
|
mvwprintw(win, i++, 0, "Queue:");
|
|
|
|
snprintf(buf, sizeof(buf), "%6u items %6u max", stats->current_q, stats->biggest_queue);
|
|
|
|
print_colored(win, buf);
|
|
|
|
show_bar(win, i++, stats->current_q, 0, stats->biggest_queue, blink);
|
|
|
|
i += 2;
|
|
|
|
werase(mem_window);
|
|
|
|
output_memstats(stats);
|
|
|
|
}
|
|
|
|
free(line);
|
|
|
|
return i;
|
2008-11-12 12:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void output_all(void)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
unsigned i, stats_line = 0;
|
|
|
|
werase(stats_head_window);
|
|
|
|
werase(stats_window);
|
|
|
|
wattron(stats_head_window, COLOR_PAIR(queue_header_color));
|
|
|
|
mvwprintw(stats_head_window, 0, 0, "%s", clamd_header);
|
|
|
|
wattroff(stats_head_window, COLOR_PAIR(queue_header_color));
|
|
|
|
for (i = 0; i < global.num_clamd; i++) {
|
|
|
|
unsigned j = output_stats(&global.all_stats[i], i);
|
|
|
|
if (j > stats_line)
|
|
|
|
stats_line = j;
|
|
|
|
}
|
|
|
|
output_queue(stats_line, maxystats - stats_line - 1);
|
|
|
|
wrefresh(stats_head_window);
|
|
|
|
wrefresh(stats_window);
|
|
|
|
if (detail_exists()) {
|
|
|
|
/* overlaps, must be done at the end */
|
|
|
|
wrefresh(mem_window);
|
|
|
|
}
|
2008-11-12 12:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_stats(conn_t *conn, struct stats *stats, unsigned idx)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
char buf[1025];
|
|
|
|
size_t j;
|
|
|
|
struct timeval tv;
|
|
|
|
unsigned conn_dt;
|
|
|
|
int primary = 0;
|
|
|
|
const char *pstart, *p, *vstart;
|
|
|
|
|
|
|
|
if (conn->tcp)
|
|
|
|
stats->remote = conn->remote;
|
|
|
|
else
|
|
|
|
stats->remote = "local";
|
|
|
|
|
|
|
|
if (!conn->version) {
|
|
|
|
stats->engine_version = strdup("???");
|
|
|
|
OOM_CHECK(stats->engine_version);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p = pstart = vstart = strchr(conn->version, ' ');
|
|
|
|
if (!vstart) {
|
|
|
|
stats->engine_version = strdup("???");
|
|
|
|
OOM_CHECK(stats->engine_version);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* find digit in version */
|
|
|
|
while (*p && !isdigit(*p))
|
|
|
|
p++;
|
|
|
|
/* rewind to first space or dash */
|
|
|
|
while (p > pstart && *p && *p != ' ' && *p != '-')
|
|
|
|
p--;
|
|
|
|
if (*p) p++;
|
|
|
|
/* keep only base version, and cut -exp, and -gittags */
|
|
|
|
pstart = p;
|
2020-06-07 19:07:17 -04:00
|
|
|
while (*p && *p != ' ' && *p != '-' && *p != '/')
|
2018-12-03 12:40:13 -05:00
|
|
|
p++;
|
|
|
|
|
|
|
|
stats->engine_version = malloc(p - pstart + 1);
|
|
|
|
OOM_CHECK(stats->engine_version);
|
|
|
|
|
|
|
|
memcpy(stats->engine_version, pstart, p - pstart);
|
|
|
|
stats->engine_version[p - pstart] = '\0';
|
|
|
|
|
|
|
|
pstart = strchr(p, '/');
|
|
|
|
if (!pstart) {
|
|
|
|
stats->db_version = strdup("????");
|
|
|
|
OOM_CHECK(stats->db_version);
|
|
|
|
} else {
|
|
|
|
pstart++;
|
|
|
|
p = strchr(pstart, '/');
|
|
|
|
if (!p)
|
|
|
|
p = pstart + strlen(pstart);
|
|
|
|
stats->db_version = malloc(p - pstart + 1);
|
|
|
|
OOM_CHECK(stats->db_version);
|
|
|
|
memcpy(stats->db_version, pstart, p - pstart);
|
|
|
|
stats->db_version[p - pstart] = '\0';
|
|
|
|
if (*p) p++;
|
|
|
|
if (!*p || !strptime(p, "%a %b %d %H:%M:%S %Y", &stats->db_time)) {
|
|
|
|
memset(&stats->db_time, 0, sizeof(stats->db_time));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (maxx > 61 && strlen(stats->db_version) > (maxx - 61)) {
|
|
|
|
stats->db_version[maxx - 61] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
stats->version = vstart; /* for details view */
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
tv.tv_sec -= conn->tv_conn.tv_sec;
|
|
|
|
tv.tv_usec -= conn->tv_conn.tv_usec;
|
|
|
|
conn_dt = tv.tv_sec + tv.tv_usec / 1e6;
|
|
|
|
|
|
|
|
stats->live = stats->idle = stats->max = 0;
|
|
|
|
stats->conn_hr = conn_dt / 3600;
|
|
|
|
stats->conn_min = (conn_dt / 60) % 60;
|
|
|
|
stats->conn_sec = conn_dt % 60;
|
|
|
|
stats->current_q = 0;
|
|
|
|
buf[sizeof(buf) - 1] = 0x0;
|
|
|
|
while (recv_line(conn, buf, sizeof(buf) - 1) && strcmp("END\n", buf) != 0) {
|
|
|
|
char *val = strchr(buf, ':');
|
|
|
|
|
|
|
|
if (buf[0] == '\t') {
|
|
|
|
parse_queue(conn, buf, sizeof(buf) - 1, idx);
|
|
|
|
continue;
|
|
|
|
} else if (val)
|
|
|
|
*val++ = '\0';
|
|
|
|
if (!strcmp("MEMSTATS", buf)) {
|
|
|
|
parse_memstats(val, stats);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp("UNKNOWN COMMAND", buf, 15)) {
|
|
|
|
stats->stats_unsupp = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (j = 1; j < strlen(buf); j++)
|
|
|
|
buf[j] = tolower(buf[j]);
|
2020-07-24 08:32:47 -07:00
|
|
|
/*
|
|
|
|
mvwprintw(win, i, 0, "%s", buf);
|
|
|
|
if(!val) {
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
waddch(win, ':');
|
|
|
|
print_colored(win, val);
|
|
|
|
i++;
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!strncmp("State", buf, 5)) {
|
|
|
|
if (strstr(val, "PRIMARY")) {
|
|
|
|
/* primary thread pool */
|
|
|
|
primary = 1;
|
|
|
|
} else {
|
|
|
|
/* multiscan pool */
|
|
|
|
primary = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!strcmp("Threads", buf)) {
|
|
|
|
unsigned live, idle, max;
|
|
|
|
if (sscanf(val, " live %u idle %u max %u", &live, &idle, &max) != 3)
|
|
|
|
continue;
|
|
|
|
if (primary) {
|
|
|
|
stats->prim_live = live;
|
|
|
|
stats->prim_idle = idle;
|
|
|
|
assert(!stats->prim_max && "There can be only one primary pool!");
|
|
|
|
stats->prim_max = max;
|
|
|
|
}
|
|
|
|
stats->live += live;
|
|
|
|
stats->idle += idle;
|
|
|
|
stats->max += max;
|
|
|
|
} else if (!strcmp("Queue", buf)) {
|
|
|
|
unsigned len;
|
|
|
|
if (sscanf(val, "%u", &len) != 1)
|
|
|
|
continue;
|
|
|
|
stats->current_q += len;
|
|
|
|
}
|
|
|
|
}
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
2009-03-01 10:15:27 +00:00
|
|
|
static int read_version(conn_t *conn)
|
2008-11-03 19:27:20 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
char buf[1024];
|
|
|
|
unsigned i;
|
2025-10-02 11:46:14 -04:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!recv_line(conn, buf, sizeof(buf)))
|
|
|
|
return -1;
|
2025-10-02 11:46:14 -04:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!strcmp(buf, "UNKNOWN COMMAND\n"))
|
|
|
|
return -2;
|
2025-10-02 11:46:14 -04:00
|
|
|
|
|
|
|
char *p = strchr(buf, ':');
|
|
|
|
if (NULL == p)
|
|
|
|
return -1;
|
|
|
|
|
2025-06-04 16:47:57 +02:00
|
|
|
// check if VERSION command is available
|
2025-10-02 11:46:14 -04:00
|
|
|
if (!strcmp(p, ": COMMAND UNAVAILABLE\n"))
|
2025-06-04 16:47:57 +02:00
|
|
|
return -3;
|
2018-12-03 12:40:13 -05:00
|
|
|
|
|
|
|
conn->version = strdup(buf);
|
|
|
|
OOM_CHECK(conn->version);
|
|
|
|
for (i = 0; i < strlen(conn->version); i++)
|
|
|
|
if (conn->version[i] == '\n')
|
|
|
|
conn->version[i] = ' ';
|
|
|
|
return 0;
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|
|
|
|
|
2025-06-04 16:47:57 +02:00
|
|
|
static int check_stats_available(conn_t *conn)
|
|
|
|
{
|
|
|
|
char buf[1024];
|
|
|
|
send_string(conn, "nSTATS\n");
|
2025-10-02 11:46:14 -04:00
|
|
|
|
2025-06-04 16:47:57 +02:00
|
|
|
if (!recv_line(conn, buf, sizeof(buf)))
|
|
|
|
return 0;
|
2025-10-02 11:46:14 -04:00
|
|
|
|
|
|
|
char *p = strchr(buf, ':');
|
|
|
|
if (NULL == p)
|
2025-06-04 16:47:57 +02:00
|
|
|
return 0;
|
2025-10-02 11:46:14 -04:00
|
|
|
|
|
|
|
if (!strcmp(p, ": COMMAND UNAVAILABLE\n"))
|
|
|
|
return 0;
|
|
|
|
|
2025-06-04 16:47:57 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-11-12 16:53:27 +00:00
|
|
|
static void sigint(int a)
|
|
|
|
{
|
2014-07-11 09:30:58 -04:00
|
|
|
UNUSEDPARAM(a);
|
2018-12-03 12:40:13 -05:00
|
|
|
EXIT_PROGRAM(SIGINT_REASON);
|
2008-11-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
2009-02-23 12:35:34 +00:00
|
|
|
static void help(void)
|
|
|
|
{
|
|
|
|
printf("\n");
|
2018-02-06 16:23:07 -05:00
|
|
|
printf(" Clam AntiVirus: Monitoring Tool %s\n", get_version());
|
2018-03-27 13:06:46 -04:00
|
|
|
printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n");
|
2025-02-14 10:24:30 -05:00
|
|
|
printf(" (C) 2025 Cisco Systems, Inc.\n");
|
2018-02-06 16:23:07 -05:00
|
|
|
printf("\n");
|
2023-05-29 23:10:38 +02:00
|
|
|
printf(" clamdtop [-hVc] [host[:port] /path/to/clamd.sock ...]\n");
|
2018-02-06 16:23:07 -05:00
|
|
|
printf("\n");
|
|
|
|
printf(" --help -h Show this help\n");
|
2009-02-23 12:35:34 +00:00
|
|
|
printf(" --version -V Show version\n");
|
|
|
|
printf(" --config-file=FILE -c FILE Read clamd's configuration files from FILE\n");
|
2020-07-24 08:32:47 -07:00
|
|
|
printf(" --defaultcolors -d Use default terminal colors\n");
|
2018-02-06 16:23:07 -05:00
|
|
|
printf(" host[:port] Connect to clamd on host at port (default 3310)\n");
|
2023-05-29 23:10:38 +02:00
|
|
|
printf(" /path/to/clamd.sock Connect to clamd over a local socket\n");
|
2009-02-23 12:35:34 +00:00
|
|
|
printf("\n");
|
|
|
|
return;
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
static int default_colors = 0;
|
2008-11-12 12:06:15 +00:00
|
|
|
/* -------------------------- Initialization ---------------- */
|
|
|
|
static void setup_connections(int argc, char *argv[])
|
2008-11-03 19:27:20 +00:00
|
|
|
{
|
2014-02-06 16:54:29 -05:00
|
|
|
struct optstruct *opts;
|
|
|
|
struct optstruct *clamd_opts;
|
|
|
|
unsigned i;
|
|
|
|
char *conn = NULL;
|
|
|
|
|
|
|
|
opts = optparse(NULL, argc, argv, 1, OPT_CLAMDTOP, 0, NULL);
|
|
|
|
if (!opts) {
|
|
|
|
fprintf(stderr, "ERROR: Can't parse command line options\n");
|
|
|
|
EXIT_PROGRAM(FAIL_CMDLINE);
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "help")->enabled) {
|
2014-02-06 16:54:29 -05:00
|
|
|
optfree(opts);
|
|
|
|
help();
|
|
|
|
normal_exit = 1;
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "version")->enabled) {
|
2014-02-06 16:54:29 -05:00
|
|
|
printf("Clam AntiVirus Monitoring Tool %s\n", get_version());
|
|
|
|
optfree(opts);
|
|
|
|
normal_exit = 1;
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "defaultcolors")->enabled)
|
2014-02-06 16:54:29 -05:00
|
|
|
default_colors = 1;
|
|
|
|
|
|
|
|
memset(&global, 0, sizeof(global));
|
|
|
|
if (!opts->filename || !opts->filename[0]) {
|
|
|
|
const struct optstruct *opt;
|
|
|
|
const char *clamd_conf = optget(opts, "config-file")->strarg;
|
|
|
|
|
|
|
|
if ((clamd_opts = optparse(clamd_conf, 0, NULL, 1, OPT_CLAMD, 0, NULL)) == NULL) {
|
|
|
|
fprintf(stderr, "Can't parse clamd configuration file %s\n", clamd_conf);
|
|
|
|
EXIT_PROGRAM(FAIL_CMDLINE);
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(clamd_opts, "LocalSocket"))->enabled) {
|
2014-02-06 16:54:29 -05:00
|
|
|
conn = strdup(opt->strarg);
|
|
|
|
if (!conn) {
|
|
|
|
fprintf(stderr, "Can't strdup LocalSocket value\n");
|
|
|
|
EXIT_PROGRAM(FAIL_INITIAL_CONN);
|
|
|
|
}
|
|
|
|
} else if ((opt = optget(clamd_opts, "TCPSocket"))->enabled) {
|
|
|
|
char buf[512];
|
|
|
|
const struct optstruct *opt_addr;
|
|
|
|
const char *host = "localhost";
|
|
|
|
if ((opt_addr = optget(clamd_opts, "TCPAddr"))->enabled) {
|
|
|
|
host = opt_addr->strarg;
|
|
|
|
}
|
2014-02-20 12:13:47 -05:00
|
|
|
snprintf(buf, sizeof(buf), "%lld", opt->numarg);
|
|
|
|
conn = make_ip(host, buf);
|
2014-02-06 16:54:29 -05:00
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Can't find how to connect to clamd\n");
|
|
|
|
EXIT_PROGRAM(FAIL_INITIAL_CONN);
|
|
|
|
}
|
|
|
|
|
|
|
|
optfree(clamd_opts);
|
|
|
|
global.num_clamd = 1;
|
|
|
|
} else {
|
|
|
|
unsigned i = 0;
|
2018-12-03 12:40:13 -05:00
|
|
|
while (opts->filename[i]) {
|
|
|
|
i++;
|
|
|
|
}
|
2014-02-06 16:54:29 -05:00
|
|
|
global.num_clamd = i;
|
|
|
|
}
|
2009-02-23 12:35:34 +00:00
|
|
|
|
2008-11-09 22:04:22 +00:00
|
|
|
#ifdef _WIN32
|
2014-02-06 16:54:29 -05:00
|
|
|
WSADATA wsaData;
|
2018-12-03 12:40:13 -05:00
|
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) {
|
2014-02-06 16:54:29 -05:00
|
|
|
fprintf(stderr, "Error at WSAStartup(): %d\n", WSAGetLastError());
|
|
|
|
EXIT_PROGRAM(FAIL_INITIAL_CONN);
|
|
|
|
}
|
2008-11-12 12:06:15 +00:00
|
|
|
#endif
|
2014-02-06 16:54:29 -05:00
|
|
|
/* clamdtop */
|
2018-12-03 12:40:13 -05:00
|
|
|
puts(" __ ____");
|
2014-02-06 16:54:29 -05:00
|
|
|
puts(" _____/ /___ _____ ___ ____/ / /_____ ____");
|
|
|
|
puts(" / ___/ / __ `/ __ `__ \\/ __ / __/ __ \\/ __ \\");
|
|
|
|
puts("/ /__/ / /_/ / / / / / / /_/ / /_/ /_/ / /_/ /");
|
|
|
|
puts("\\___/_/\\__,_/_/ /_/ /_/\\__,_/\\__/\\____/ .___/");
|
|
|
|
puts(" /_/");
|
|
|
|
|
|
|
|
global.all_stats = calloc(global.num_clamd, sizeof(*global.all_stats));
|
|
|
|
OOM_CHECK(global.all_stats);
|
|
|
|
global.conn = calloc(global.num_clamd, sizeof(*global.conn));
|
|
|
|
OOM_CHECK(global.conn);
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
for (i = 0; i < global.num_clamd; i++) {
|
2020-03-30 10:05:52 -07:00
|
|
|
const char *soname;
|
|
|
|
if (!conn && !opts->filename) {
|
|
|
|
soname = NULL;
|
|
|
|
} else {
|
2020-07-24 08:32:47 -07:00
|
|
|
soname = conn ? conn : opts->filename[i];
|
2020-03-30 10:05:52 -07:00
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
global.conn[i].line = i + 1;
|
2014-02-06 16:54:29 -05:00
|
|
|
if (make_connection(soname, &global.conn[i]) < 0) {
|
2020-07-24 08:32:47 -07:00
|
|
|
EXIT_PROGRAM(FAIL_INITIAL_CONN);
|
2014-02-06 16:54:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
optfree(opts);
|
|
|
|
free(conn);
|
2008-11-12 12:06:15 +00:00
|
|
|
#ifndef _WIN32
|
2014-02-06 16:54:29 -05:00
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
signal(SIGINT, sigint);
|
2008-11-09 22:04:22 +00:00
|
|
|
#endif
|
2008-11-12 12:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void free_global_stats(void)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
unsigned i;
|
|
|
|
for (i = 0; i < (unsigned)global.n; i++) {
|
|
|
|
free(global.tasks[i].line);
|
|
|
|
}
|
|
|
|
for (i = 0; i < global.num_clamd; i++) {
|
|
|
|
free(global.all_stats[i].engine_version);
|
|
|
|
free(global.all_stats[i].db_version);
|
|
|
|
}
|
|
|
|
free(global.tasks);
|
|
|
|
global.tasks = NULL;
|
|
|
|
global.n = 0;
|
2008-11-12 12:06:15 +00:00
|
|
|
}
|
|
|
|
|
2008-11-12 21:23:35 +00:00
|
|
|
static int help_line;
|
|
|
|
static void explain(const char *abbrev, const char *msg)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
wattron(stdscr, A_BOLD);
|
|
|
|
mvwprintw(stdscr, help_line++, 0, "%-15s", abbrev);
|
|
|
|
wattroff(stdscr, A_BOLD);
|
|
|
|
wprintw(stdscr, " %s", msg);
|
2008-11-12 21:23:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int show_help(void)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
int ch;
|
|
|
|
werase(stdscr);
|
|
|
|
help_line = 0;
|
|
|
|
|
|
|
|
explain("NO", "Unique clamd number");
|
|
|
|
explain("CONNTIME", "How long it is connected");
|
|
|
|
explain("LIV", "Total number of live threads");
|
|
|
|
explain("IDL", "Total number of idle threads");
|
|
|
|
explain("QUEUE", "Number of items in queue");
|
|
|
|
explain("MAXQ", "Maximum number of items observed in queue");
|
|
|
|
explain("MEM", "Total memory usage (if available)");
|
|
|
|
explain("ENGINE", "Engine version");
|
|
|
|
explain("DBVER", "Database version");
|
|
|
|
explain("DBTIME", "Database publish time");
|
2020-06-07 19:07:17 -04:00
|
|
|
explain("HOST", "Which clamd, local means unix socket");
|
2018-12-03 12:40:13 -05:00
|
|
|
explain("Primary threads", "Threadpool used to receive commands");
|
2020-06-07 19:07:17 -04:00
|
|
|
/*explain("Multiscan pool", "Threadpool used for multiscan");*/
|
2018-12-03 12:40:13 -05:00
|
|
|
explain("live", "Executing commands, or scanning");
|
|
|
|
explain("idle", "Waiting for commands, will exit after idle_timeout");
|
|
|
|
explain("max", "Maximum number of threads configured for this pool");
|
|
|
|
explain("Queue", "Tasks queued for processing, but not yet picked up by a thread");
|
|
|
|
explain("COMMAND", "Command this thread is executing");
|
|
|
|
explain("QUEUEDSINCE", "How long this task is executing");
|
|
|
|
explain("FILE", "Which file it is processing (if applicable)");
|
|
|
|
explain("Mem", "Memory usage reported by libc");
|
|
|
|
explain("Libc", "Used/free memory reported by libc");
|
|
|
|
explain("Pool", "Memory usage reported by libclamav's pool");
|
|
|
|
|
|
|
|
wrefresh(stdscr);
|
|
|
|
werase(status_bar_window);
|
|
|
|
wattron(status_bar_window, A_REVERSE);
|
|
|
|
mvwprintw(status_bar_window, 0, 0, "Press any key to exit help");
|
|
|
|
wattroff(status_bar_window, A_REVERSE);
|
|
|
|
wrefresh(status_bar_window);
|
|
|
|
/* getch() times out after a few seconds */
|
|
|
|
do {
|
|
|
|
ch = getch();
|
|
|
|
/* we do need to exit on resize, because the text scroll out of
|
2020-07-24 08:32:47 -07:00
|
|
|
* view */
|
2018-12-03 12:40:13 -05:00
|
|
|
} while (ch == -1 /*|| ch == KEY_RESIZE*/);
|
|
|
|
return ch == KEY_RESIZE ? KEY_RESIZE : -1;
|
2008-11-12 21:23:35 +00:00
|
|
|
}
|
|
|
|
|
2008-11-12 12:06:15 +00:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
int ch = 0;
|
|
|
|
struct timeval tv_last, tv;
|
|
|
|
unsigned i;
|
|
|
|
|
2025-07-25 22:42:43 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
SetConsoleOutputCP(CP_UTF8);
|
|
|
|
#endif
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
atexit(cleanup);
|
|
|
|
setup_connections(argc, argv);
|
|
|
|
init_ncurses(global.num_clamd, default_colors);
|
|
|
|
|
|
|
|
memset(&tv_last, 0, sizeof(tv_last));
|
|
|
|
do {
|
|
|
|
if (toupper(ch) == 'H') {
|
|
|
|
ch = show_help();
|
|
|
|
}
|
|
|
|
switch (ch) {
|
|
|
|
case KEY_RESIZE:
|
|
|
|
resize();
|
|
|
|
endwin();
|
|
|
|
refresh();
|
|
|
|
init_windows(global.num_clamd);
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
case 'r':
|
|
|
|
for (i = 0; i < global.num_clamd; i++)
|
|
|
|
global.all_stats[i].biggest_queue = 1;
|
|
|
|
biggest_mem = 0;
|
|
|
|
break;
|
|
|
|
case KEY_UP:
|
|
|
|
if (global.num_clamd > 1) {
|
|
|
|
if (detail_selected == -1)
|
|
|
|
detail_selected = global.num_clamd - 1;
|
|
|
|
else
|
|
|
|
--detail_selected;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case KEY_DOWN:
|
|
|
|
if (global.num_clamd > 1) {
|
|
|
|
if (detail_selected == -1)
|
|
|
|
detail_selected = 0;
|
|
|
|
else {
|
|
|
|
if ((unsigned)++detail_selected >= global.num_clamd)
|
|
|
|
detail_selected = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
header();
|
|
|
|
if (tv.tv_sec - tv_last.tv_sec >= MIN_INTERVAL) {
|
|
|
|
free_global_stats();
|
|
|
|
for (i = 0; i < global.num_clamd; i++) {
|
|
|
|
unsigned biggest_q;
|
|
|
|
struct stats *stats = &global.all_stats[i];
|
|
|
|
if (global.conn[i].sd != -1)
|
|
|
|
send_string(&global.conn[i], "nSTATS\n");
|
|
|
|
biggest_q = stats->biggest_queue;
|
|
|
|
memset(stats, 0, sizeof(*stats));
|
|
|
|
stats->biggest_queue = biggest_q;
|
|
|
|
parse_stats(&global.conn[i], stats, i);
|
|
|
|
}
|
|
|
|
if (global.tasks)
|
|
|
|
qsort(global.tasks, global.n, sizeof(*global.tasks), tasks_compare);
|
|
|
|
tv_last = tv;
|
|
|
|
}
|
|
|
|
/* always show, so that screen resizes take effect instantly*/
|
|
|
|
output_all();
|
|
|
|
for (i = 0; i < global.num_clamd; i++) {
|
|
|
|
if (global.conn[i].sd == -1)
|
|
|
|
reconnect(&global.conn[i]);
|
|
|
|
}
|
|
|
|
} while (toupper(ch = getch()) != 'Q');
|
|
|
|
free_global_stats();
|
|
|
|
normal_exit = 1;
|
|
|
|
return 0;
|
2008-11-03 19:27:20 +00:00
|
|
|
}
|