diff options
| author | jm199354 <none@none> | 2008-04-10 17:45:54 -0700 |
|---|---|---|
| committer | jm199354 <none@none> | 2008-04-10 17:45:54 -0700 |
| commit | bfc848c632c9eacb2a640246d96e198f1b185c03 (patch) | |
| tree | aae2fdba1698a61caccf8de366114f2e2254854d /usr/src | |
| parent | 76396fcc7c8d694e8cb7822a4aeda9cf2b30fb3a (diff) | |
| download | illumos-joyent-bfc848c632c9eacb2a640246d96e198f1b185c03.tar.gz | |
6570963 vscan: snas: Can't Enable vscan service after,disabling it while scan was in progress for few files
6599614 Log message says "vscan: (eng #) no socket, errno 24" when copying 1000 2k-files with 4 scan engines
6656150 vscan threading model should be revised to address system resource usage
6660667 Engine name is used directly as property group name without any prefix
6663788 SUNWvscanr needs to be split into SUNWvscanr and SUNWvscankr
6663792 links are required in /dev for the vscan pseudo device - to support non-global zones
6663812 attempt to start a second instance of vscand, first instance crashes
6672296 per-engine error counts not shown after vscan stats reset using vscanadm stats -z
6673579 vscan log file should be parsable and use utf8
6678058 If stats door file already exists when vscand starts up an initialization failure message is shown
6678525 vscand exits on SIGPIPE
6679808 Interupted read/write (EINTR) to scan engine can cause invalid data to be exchanged
6681465 Core file generated while adding and performing virus scan operations over nfs mount
6681675 Connection remains in ESTABLISHED State for long time, after scan engine goes down.
6684670 vscand should maintain connections with scan engines instead of connecting per request
Diffstat (limited to 'usr/src')
35 files changed, 2839 insertions, 1125 deletions
diff --git a/usr/src/cmd/devfsadm/Makefile.com b/usr/src/cmd/devfsadm/Makefile.com index 01ee1836e3..ebafe17839 100644 --- a/usr/src/cmd/devfsadm/Makefile.com +++ b/usr/src/cmd/devfsadm/Makefile.com @@ -18,7 +18,7 @@ # # CDDL HEADER END # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -65,6 +65,7 @@ LINK_OBJS_CMN = \ smp_link.o \ md_link.o \ dtrace_link.o \ + vscan_link.o \ zfs_link.o LINK_OBJS = $(LINK_OBJS_CMN) \ diff --git a/usr/src/cmd/devfsadm/vscan_link.c b/usr/src/cmd/devfsadm/vscan_link.c new file mode 100644 index 0000000000..c84ea231f0 --- /dev/null +++ b/usr/src/cmd/devfsadm/vscan_link.c @@ -0,0 +1,58 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <devfsadm.h> +#include <strings.h> +#include <stdio.h> +#include <sys/vscan.h> + +static int vscan(di_minor_t minor, di_node_t node); + +static devfsadm_create_t vscan_create_cbt[] = { + { "pseudo", "ddi_pseudo", "vscan", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, vscan }, +}; +DEVFSADM_CREATE_INIT_V0(vscan_create_cbt); + +static devfsadm_remove_t vscan_remove_cbt[] = { + { "vscan", "^vscan/vscan[0-9]+$", RM_HOT | RM_POST, + ILEVEL_0, devfsadm_rm_all + } +}; +DEVFSADM_REMOVE_INIT_V0(vscan_remove_cbt); + +static int +vscan(di_minor_t minor, di_node_t node) +{ + char *mname = di_minor_name(minor); + char path[MAXPATHLEN]; + + (void) snprintf(path, sizeof (path), "vscan/%s", mname); + (void) devfsadm_mklink(path, node, minor, 0); + + return (DEVFSADM_CONTINUE); +} diff --git a/usr/src/cmd/vscan/vscand/vs_door.c b/usr/src/cmd/vscan/vscand/vs_door.c index 1d2bbd522d..8061318d56 100644 --- a/usr/src/cmd/vscan/vscand/vs_door.c +++ b/usr/src/cmd/vscan/vscand/vs_door.c @@ -111,30 +111,14 @@ static void vs_door_scan_req(void *cookie, char *ptr, size_t size, door_desc_t *dp, uint_t n_desc) { - int flags = 0; - vs_scan_req_t scan_rsp; vs_scan_req_t *scan_req; - char devname[MAXPATHLEN]; - vs_attr_t fattr; + uint32_t result = VS_STATUS_ERROR; - if (ptr == NULL) { - scan_rsp.vsr_result = VS_STATUS_ERROR; - scan_rsp.vsr_scanstamp[0] = '\0'; - } else { + if (ptr != NULL) { /* LINTED E_BAD_PTR_CAST_ALIGN - to be fixed with encoding */ scan_req = (vs_scan_req_t *)ptr; - (void) snprintf(devname, MAXPATHLEN, "%s%d", - VS_DRV_PATH, scan_req->vsr_id); - - fattr.vsa_size = scan_req->vsr_size; - fattr.vsa_modified = scan_req->vsr_modified; - fattr.vsa_quarantined = scan_req->vsr_quarantined; - (void) strlcpy(fattr.vsa_scanstamp, scan_req->vsr_scanstamp, - sizeof (vs_scanstamp_t)); - - scan_rsp.vsr_result = vs_svc_scan_file(devname, - scan_req->vsr_path, &fattr, flags, &scan_rsp.vsr_scanstamp); + result = vs_svc_queue_scan_req(scan_req); } - (void) door_return((char *)&scan_rsp, sizeof (vs_scan_req_t), NULL, 0); + (void) door_return((char *)&result, sizeof (uint32_t), NULL, 0); } diff --git a/usr/src/cmd/vscan/vscand/vs_eng.c b/usr/src/cmd/vscan/vscand/vs_eng.c index 24ededc6af..0bdcb3b7bb 100644 --- a/usr/src/cmd/vscan/vscand/vs_eng.c +++ b/usr/src/cmd/vscan/vscand/vs_eng.c @@ -38,6 +38,7 @@ #include <sys/filio.h> #include <sys/ioctl.h> #include <sys/debug.h> +#include <sys/time.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> @@ -50,14 +51,76 @@ #include <pthread.h> #include <time.h> +#include <signal.h> +#include <thread.h> + #include "vs_incl.h" +/* max connections per scan engine */ +#define VS_CXN_MAX VS_VAL_SE_MAXCONN_MAX + +/* + * vs_eng_state_t - connection state + * + * Each configured scan engine supports up to vse_cfg.vep_maxconn + * connections. These connections are represented by a vs_connection_t + * which defines the connection state, associated socket descriptor + * and how long the connection has been available. A connection + * that has been available but unused for vs_inactivity_timeout + * seconds will be closed by the housekeeper thread. + * + * When a scan engine is reconfigured to have less connections + * (or is disabled) any of he superflous connections which are in + * AVAILABLE state are closed (DISCONNECTED). Others are set to + * CLOSE_PENDING to be closed (DISCONNECTED) when the engine is + * released (when the current request completes). + * + * +---------------------+ + * |---------->| VS_ENG_DISCONNECTED |<-----------------| + * | +---------------------+ | + * | | | + * | | eng_get | + * | v | release/ + * | shutdown +---------------------+ reconfig | shutdown + * |<----------| VS_ENG_RESERVED | -----------| | + * | +---------------------+ | | + * | | v | + * | | +----------------------+ + * | | connect | VS_ENG_CLOSE_PENDING | + * | | +----------------------+ + * | v ^ + * | shutdown +---------------------+ | + * |<----------| VS_ENG_INUSE |------------| + * | +---------------------+ reconfig/error + * | | ^ + * | | release | eng_get + * | reconfig/ | | + * | timeout/ v | + * | shutdown +---------------------+ + * |<----------| VS_ENG_AVAILABLE | + * +---------------------+ + * + */ + +typedef enum { + VS_ENG_DISCONNECTED = 0, + VS_ENG_RESERVED, + VS_ENG_INUSE, + VS_ENG_AVAILABLE, + VS_ENG_CLOSE_PENDING +} vs_eng_state_t; + +typedef struct vs_connection { + vs_eng_state_t vsc_state; + int vsc_sockfd; + struct timeval vsc_avail_time; +} vs_connection_t; typedef struct vs_engine { - vs_props_se_t vse_cfg; /* host, port, maxcon */ - int vse_in_use; /* # connections in use */ - int vse_error; - vs_eng_conn_t vse_conn_root; + vs_props_se_t vse_cfg; /* host, port, maxconn */ + int vse_inuse; /* # connections in use */ + boolean_t vse_error; + vs_connection_t vse_cxns[VS_CXN_MAX]; } vs_engine_t; static vs_engine_t vs_engines[VS_SE_MAX]; @@ -70,22 +133,24 @@ static int vs_eng_wait_count; /* # threads waiting for connection */ static pthread_mutex_t vs_eng_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t vs_eng_cv; -static pthread_cond_t vs_eng_shutdown_cv; +int vs_inactivity_timeout = 60; /* seconds */ +int vs_reuse_connection = 1; -static time_t vs_eng_wait = VS_ENG_WAIT_DFLT; +time_t vs_eng_wait = VS_ENG_WAIT_DFLT; /* local functions */ -static int vs_eng_check_errors(void); -static int vs_eng_find_next(int); -static void vs_eng_add_connection(vs_eng_conn_t *); -static void vs_eng_remove_connection(vs_eng_conn_t *); -static void vs_eng_close_connections(void); +static int vs_eng_connect(char *, int); +static boolean_t vs_eng_check_errors(void); +static int vs_eng_find_connection(int *, int *, boolean_t); +static int vs_eng_find_next(boolean_t); static int vs_eng_compare(int, char *, int); +static void vs_eng_config_close(vs_engine_t *, int); +static void *vs_eng_housekeeper(void *); #ifdef FIONBIO /* non-blocking connect */ -static int nbio_connect(vs_eng_conn_t *, const struct sockaddr *, int); +static int nbio_connect(int, const struct sockaddr *, int); int vs_connect_timeout = 5000; /* milliseconds */ #endif /* FIONBIO */ @@ -96,18 +161,21 @@ int vs_connect_timeout = 5000; /* milliseconds */ void vs_eng_init() { + pthread_t tid; + (void) pthread_cond_init(&vs_eng_cv, NULL); - (void) pthread_cond_init(&vs_eng_shutdown_cv, NULL); (void) pthread_mutex_lock(&vs_eng_mutex); - (void) memset(vs_engines, 0, - sizeof (vs_engine_t) * VS_SE_MAX); + (void) memset(vs_engines, 0, sizeof (vs_engine_t) * VS_SE_MAX); + vs_eng_total_maxcon = 0; vs_eng_total_inuse = 0; vs_eng_count = 0; vs_eng_next = 0; (void) pthread_mutex_unlock(&vs_eng_mutex); + + (void) pthread_create(&tid, NULL, vs_eng_housekeeper, NULL); } @@ -119,6 +187,10 @@ vs_eng_init() * If a scan engine has been reconfigured (different host or port) * the scan engine's error count is reset. * + * If the host/port has changed, the engine has been disabled + * or less connections are configured now, connections need + * to be closed or placed in CLOSE_PENDING state (vs_eng_config_close) + * * vs_icap_config is invoked to reset engine-specific data stored * in vs_icap. * @@ -139,14 +211,20 @@ vs_eng_config(vs_props_all_t *config) cfg = &config->va_se[i]; eng = &vs_engines[i]; - if (vs_eng_compare(i, cfg->vep_host, cfg->vep_port) != 0) - eng->vse_error = 0; + if (vs_eng_compare(i, cfg->vep_host, cfg->vep_port) != 0) { + vs_eng_config_close(eng, 0); + eng->vse_error = B_FALSE; + } if (cfg->vep_enable) { + if (cfg->vep_maxconn < eng->vse_cfg.vep_maxconn) + vs_eng_config_close(eng, cfg->vep_maxconn); + eng->vse_cfg = *cfg; vs_eng_total_maxcon += cfg->vep_maxconn; vs_eng_count++; } else { + vs_eng_config_close(eng, 0); (void) memset(&eng->vse_cfg, 0, sizeof (vs_props_se_t)); } @@ -161,29 +239,93 @@ vs_eng_config(vs_props_all_t *config) /* - * vs_eng_fini + * vs_eng_config_close * - * Close all scan engine connections to abort in-progress scans, - * and wait until all to sessions are complete, and there are no - * waiting threads. - * Set vs_eng_total_maxcon to 0 to ensure no new engine sessions - * can be initiated while we're waiting. + * If the host/port has changed, the engine has been disabled + * or less connections are configured now, connections need + * to be closed or placed in CLOSE_PENDING state + */ +static void +vs_eng_config_close(vs_engine_t *eng, int start_idx) +{ + int i; + vs_connection_t *cxn; + + for (i = start_idx; i < eng->vse_cfg.vep_maxconn; i++) { + cxn = &(eng->vse_cxns[i]); + + switch (cxn->vsc_state) { + case VS_ENG_RESERVED: + case VS_ENG_INUSE: + cxn->vsc_state = VS_ENG_CLOSE_PENDING; + break; + case VS_ENG_AVAILABLE: + (void) close(cxn->vsc_sockfd); + cxn->vsc_sockfd = -1; + cxn->vsc_state = VS_ENG_DISCONNECTED; + break; + case VS_ENG_CLOSE_PENDING: + case VS_ENG_DISCONNECTED: + break; + } + } +} + + +/* + * vs_eng_fini */ void vs_eng_fini() { - (void) pthread_mutex_lock(&vs_eng_mutex); + (void) pthread_cond_destroy(&vs_eng_cv); +} - vs_eng_total_maxcon = 0; - vs_eng_close_connections(); +/* + * vs_eng_housekeeper + * + * Wakeup every (vs_inactivity_timeout / 2) seconds and close + * any connections that are in AVAILABLE state but have not + * been used for vs_inactivity_timeout seconds. + */ +/* ARGSUSED */ +static void * +vs_eng_housekeeper(void *arg) +{ + struct timeval now; + long expire; + int i, j; + vs_engine_t *eng; + vs_connection_t *cxn; - while (vs_eng_total_inuse > 0 || vs_eng_wait_count > 0) - (void) pthread_cond_wait(&vs_eng_shutdown_cv, &vs_eng_mutex); + for (;;) { + (void) sleep(vs_inactivity_timeout / 2); - (void) pthread_mutex_unlock(&vs_eng_mutex); - (void) pthread_cond_destroy(&vs_eng_cv); - (void) pthread_cond_destroy(&vs_eng_shutdown_cv); + if (vscand_get_state() == VS_STATE_SHUTDOWN) + break; + + (void) gettimeofday(&now, NULL); + expire = now.tv_sec - vs_inactivity_timeout; + + (void) pthread_mutex_lock(&vs_eng_mutex); + for (i = 0; i < VS_SE_MAX; i++) { + eng = &(vs_engines[i]); + for (j = 0; j < eng->vse_cfg.vep_maxconn; j++) { + cxn = &(eng->vse_cxns[j]); + + if ((cxn->vsc_state == VS_ENG_AVAILABLE) && + (cxn->vsc_avail_time.tv_sec < expire)) { + (void) close(cxn->vsc_sockfd); + cxn->vsc_sockfd = -1; + cxn->vsc_state = VS_ENG_DISCONNECTED; + } + } + } + (void) pthread_mutex_unlock(&vs_eng_mutex); + } + + return (NULL); } @@ -194,45 +336,56 @@ vs_eng_fini() * engine in vs_engines set or clear the error state of the * engine and update the error statistics. * - * If error == 0, clear the error state(0), else set the error - * state (1) + * If error == 0, clear the error state(B_FALSE), else set + * the error state (B_TRUE) and increment engine error stats */ void -vs_eng_set_error(vs_eng_conn_t *conn, int error) +vs_eng_set_error(vs_eng_ctx_t *eng_ctx, int error) { - int idx = conn->vsc_idx; + int eidx = eng_ctx->vse_eidx; + int cidx = eng_ctx->vse_cidx; + vs_engine_t *eng; (void) pthread_mutex_lock(&vs_eng_mutex); - if (vs_eng_compare(idx, conn->vsc_host, conn->vsc_port) == 0) - vs_engines[idx].vse_error = error ? 1 : 0; + eng = &(vs_engines[eidx]); + + if (vs_eng_compare(eidx, eng_ctx->vse_host, eng_ctx->vse_port) == 0) + eng->vse_error = (error == 0) ? B_FALSE : B_TRUE; + + if (error != 0) { + eng->vse_cxns[cidx].vsc_state = VS_ENG_CLOSE_PENDING; + vs_stats_eng_err(eng_ctx->vse_engid); + } (void) pthread_mutex_unlock(&vs_eng_mutex); } - /* * vs_eng_get * Get next available scan engine connection. - * If retry != 0 look for a scan engine with no errors. + * If retry == B_TRUE look for a scan engine with no errors. * * Returns: 0 - success * -1 - error */ int -vs_eng_get(vs_eng_conn_t *conn, int retry) +vs_eng_get(vs_eng_ctx_t *eng_ctx, boolean_t retry) { struct timespec tswait; - int idx; + int eidx, cidx, sockfd; + vs_engine_t *eng; + vs_connection_t *cxn; (void) pthread_mutex_lock(&vs_eng_mutex); /* - * If no engines connections configured or + * If no engines connections configured OR * retry and only one engine configured, give up */ - if ((vs_eng_total_maxcon <= 0) || (retry && (vs_eng_count <= 1))) { + if ((vs_eng_total_maxcon <= 0) || + ((retry == B_TRUE) && (vs_eng_count <= 1))) { (void) pthread_mutex_unlock(&vs_eng_mutex); return (-1); } @@ -241,9 +394,9 @@ vs_eng_get(vs_eng_conn_t *conn, int retry) tswait.tv_nsec = 0; while ((vscand_get_state() != VS_STATE_SHUTDOWN) && - ((idx = vs_eng_find_next(retry)) == -1)) { + (vs_eng_find_connection(&eidx, &cidx, retry) == -1)) { /* If retry and all configured engines have errors, give up */ - if (retry && vs_eng_check_errors()) { + if (retry && vs_eng_check_errors() == B_TRUE) { (void) pthread_mutex_unlock(&vs_eng_mutex); return (-1); } @@ -255,8 +408,6 @@ vs_eng_get(vs_eng_conn_t *conn, int retry) syslog(LOG_WARNING, "Scan Engine " "- timeout waiting for available engine"); vs_eng_wait_count--; - if (vscand_get_state() == VS_STATE_SHUTDOWN) - (void) pthread_cond_signal(&vs_eng_shutdown_cv); (void) pthread_mutex_unlock(&vs_eng_mutex); return (-1); } @@ -264,31 +415,81 @@ vs_eng_get(vs_eng_conn_t *conn, int retry) } if (vscand_get_state() == VS_STATE_SHUTDOWN) { - (void) pthread_cond_signal(&vs_eng_shutdown_cv); (void) pthread_mutex_unlock(&vs_eng_mutex); return (-1); } - conn->vsc_idx = idx; - (void) strlcpy(conn->vsc_engid, vs_engines[idx].vse_cfg.vep_engid, - sizeof (conn->vsc_engid)); - (void) strlcpy(conn->vsc_host, vs_engines[idx].vse_cfg.vep_host, - sizeof (conn->vsc_host)); - conn->vsc_port = vs_engines[idx].vse_cfg.vep_port; + eng = &(vs_engines[eidx]); + cxn = &(eng->vse_cxns[cidx]); /* update in use counts */ - vs_engines[idx].vse_in_use++; + eng->vse_inuse++; vs_eng_total_inuse++; - /* add to connections list for engine */ - vs_eng_add_connection(conn); - /* update round-robin index */ if (!retry) - vs_eng_next = (idx == VS_SE_MAX) ? 0 : idx + 1; + vs_eng_next = (eidx == VS_SE_MAX) ? 0 : eidx + 1; + + /* populate vs_eng_ctx_t */ + eng_ctx->vse_eidx = eidx; + eng_ctx->vse_cidx = cidx; + (void) strlcpy(eng_ctx->vse_engid, eng->vse_cfg.vep_engid, + sizeof (eng_ctx->vse_engid)); + (void) strlcpy(eng_ctx->vse_host, eng->vse_cfg.vep_host, + sizeof (eng_ctx->vse_host)); + eng_ctx->vse_port = eng->vse_cfg.vep_port; + eng_ctx->vse_sockfd = cxn->vsc_sockfd; + + if (cxn->vsc_state == VS_ENG_INUSE) { + (void) pthread_mutex_unlock(&vs_eng_mutex); + return (0); + } + + /* state == VS_ENG_RESERVED, need to connect */ (void) pthread_mutex_unlock(&vs_eng_mutex); + sockfd = vs_eng_connect(eng_ctx->vse_host, eng_ctx->vse_port); + + /* retry a failed connection once */ + if (sockfd == -1) { + (void) sleep(1); + sockfd = vs_eng_connect(eng_ctx->vse_host, eng_ctx->vse_port); + } + + if (sockfd == -1) { + syslog(LOG_WARNING, "Scan Engine - connection error (%s:%d) %s", + eng_ctx->vse_host, eng_ctx->vse_port, + errno ? strerror(errno) : ""); + vs_eng_set_error(eng_ctx, 1); + vs_eng_release(eng_ctx); + return (-1); + } + + (void) pthread_mutex_lock(&vs_eng_mutex); + switch (cxn->vsc_state) { + case VS_ENG_DISCONNECTED: + /* SHUTDOWN occured */ + (void) pthread_mutex_unlock(&vs_eng_mutex); + vs_eng_release(eng_ctx); + return (-1); + case VS_ENG_RESERVED: + cxn->vsc_state = VS_ENG_INUSE; + break; + case VS_ENG_CLOSE_PENDING: + /* reconfigure occured. Connection will be closed after use */ + break; + case VS_ENG_INUSE: + case VS_ENG_AVAILABLE: + default: + ASSERT(0); + break; + } + + cxn->vsc_sockfd = sockfd; + eng_ctx->vse_sockfd = sockfd; + + (void) pthread_mutex_unlock(&vs_eng_mutex); return (0); } @@ -296,24 +497,73 @@ vs_eng_get(vs_eng_conn_t *conn, int retry) /* * vs_eng_check_errors * - * Check if there are any engines, with maxcon > 0, - * which are not in error state + * Check if all engines with maxconn > 0 are in error state * - * Returns: 1 - all (valid) engines are in error state - * 0 - otherwise + * Returns: B_TRUE - all (valid) engines are in error state + * B_FALSE - otherwise */ -static int +static boolean_t vs_eng_check_errors() { int i; for (i = 0; i < VS_SE_MAX; i++) { if (vs_engines[i].vse_cfg.vep_maxconn > 0 && - (vs_engines[i].vse_error == 0)) + (vs_engines[i].vse_error == B_FALSE)) + return (B_FALSE); + } + + return (B_TRUE); +} + + +/* + * vs_eng_find_connection + * + * Identify the next engine to be used (vs_eng_find_next()). + * Select the engine's first connection in AVAILABLE state. + * If no connection is in AVAILABLE state, select the first + * that is in DISCONNECTED state. + * + * Returns: 0 success + * -1 no engine connections available (eng_idx & cxn_idx undefined) + */ +static int +vs_eng_find_connection(int *eng_idx, int *cxn_idx, boolean_t retry) +{ + int i, idx; + vs_engine_t *eng; + vs_connection_t *cxn; + + /* identify engine */ + if ((idx = vs_eng_find_next(retry)) == -1) + return (-1); + + eng = &(vs_engines[idx]); + *eng_idx = idx; + + /* identify connection */ + idx = -1; + for (i = 0; i < eng->vse_cfg.vep_maxconn; i++) { + cxn = &(eng->vse_cxns[i]); + if (cxn->vsc_state == VS_ENG_AVAILABLE) { + *cxn_idx = i; + cxn->vsc_state = VS_ENG_INUSE; return (0); + } + + if ((idx == -1) && + (cxn->vsc_state == VS_ENG_DISCONNECTED)) { + idx = i; + } } - return (1); + if (idx == -1) + return (-1); + + eng->vse_cxns[idx].vsc_state = VS_ENG_RESERVED; + *cxn_idx = idx; + return (0); } @@ -321,25 +571,25 @@ vs_eng_check_errors() * vs_eng_find_next * * Returns: -1 no engine connections available - * idx of engine to use + * idx of engine to use */ static int -vs_eng_find_next(int retry) +vs_eng_find_next(boolean_t retry) { int i; for (i = vs_eng_next; i < VS_SE_MAX; i++) { - if (vs_engines[i].vse_in_use < + if (vs_engines[i].vse_inuse < vs_engines[i].vse_cfg.vep_maxconn) { - if (!retry || (vs_engines[i].vse_error == 0)) + if (!retry || (vs_engines[i].vse_error == B_FALSE)) return (i); } } for (i = 0; i < vs_eng_next; i++) { - if (vs_engines[i].vse_in_use < + if (vs_engines[i].vse_inuse < vs_engines[i].vse_cfg.vep_maxconn) { - if (!retry || (vs_engines[i].vse_error == 0)) + if (!retry || (vs_engines[i].vse_error == B_FALSE)) return (i); } } @@ -352,135 +602,144 @@ vs_eng_find_next(int retry) * vs_eng_release */ void -vs_eng_release(vs_eng_conn_t *conn) +vs_eng_release(const vs_eng_ctx_t *eng_ctx) { - int idx = conn->vsc_idx; - - /* disconnect */ - if (conn->vsc_sockfd != -1) { - (void) close(conn->vsc_sockfd); - conn->vsc_sockfd = -1; - } + int eidx = eng_ctx->vse_eidx; + int cidx = eng_ctx->vse_cidx; + vs_connection_t *cxn; (void) pthread_mutex_lock(&vs_eng_mutex); + cxn = &(vs_engines[eidx].vse_cxns[cidx]); + + switch (cxn->vsc_state) { + case VS_ENG_DISCONNECTED: + break; + case VS_ENG_RESERVED: + cxn->vsc_state = VS_ENG_DISCONNECTED; + break; + case VS_ENG_INUSE: + if (vs_reuse_connection) { + cxn->vsc_state = VS_ENG_AVAILABLE; + (void) gettimeofday(&cxn->vsc_avail_time, NULL); + break; + } + /* LINTED E_CASE_FALL_THROUGH - close connection */ + case VS_ENG_CLOSE_PENDING: + (void) close(cxn->vsc_sockfd); + cxn->vsc_sockfd = -1; + cxn->vsc_state = VS_ENG_DISCONNECTED; + break; + case VS_ENG_AVAILABLE: + default: + ASSERT(0); + break; + } /* decrement in use counts */ - vs_engines[idx].vse_in_use--; + vs_engines[eidx].vse_inuse--; vs_eng_total_inuse--; - /* remove from connections list for engine */ - vs_eng_remove_connection(conn); - /* wake up next thread waiting for a connection */ (void) pthread_cond_signal(&vs_eng_cv); - /* if shutdown, send shutdown signal */ - if (vscand_get_state() == VS_STATE_SHUTDOWN) - (void) pthread_cond_signal(&vs_eng_shutdown_cv); - (void) pthread_mutex_unlock(&vs_eng_mutex); } /* - * vs_eng_add_connection - * Add a connection into appropriate engine's connections list - */ -static void -vs_eng_add_connection(vs_eng_conn_t *conn) -{ - vs_eng_conn_t *conn_root; - - conn_root = &(vs_engines[conn->vsc_idx].vse_conn_root); - conn->vsc_prev = conn_root; - conn->vsc_next = conn_root->vsc_next; - if (conn->vsc_next) - (conn->vsc_next)->vsc_prev = conn; - conn_root->vsc_next = conn; -} - - -/* - * vs_eng_remove_connection - * Remove a connection from appropriate engine's connections list - */ -static void -vs_eng_remove_connection(vs_eng_conn_t *conn) -{ - (conn->vsc_prev)->vsc_next = conn->vsc_next; - if (conn->vsc_next) - (conn->vsc_next)->vsc_prev = conn->vsc_prev; -} - - -/* * vs_eng_close_connections + * + * Set vs_eng_total_maxcon to 0 to ensure no new engine sessions + * can be initiated. * Close all open connections to abort in-progress scans. + * Set connection state to DISCONNECTED. */ -static void +void vs_eng_close_connections(void) { - int i; - vs_eng_conn_t *conn; + int i, j; + vs_connection_t *cxn; + + (void) pthread_mutex_lock(&vs_eng_mutex); + vs_eng_total_maxcon = 0; for (i = 0; i < VS_SE_MAX; i++) { - conn = vs_engines[i].vse_conn_root.vsc_next; - while (conn) { - (void) close(conn->vsc_sockfd); - conn->vsc_sockfd = -1; - conn = conn->vsc_next; + for (j = 0; j < VS_CXN_MAX; j++) { + cxn = &(vs_engines[i].vse_cxns[j]); + + switch (cxn->vsc_state) { + case VS_ENG_INUSE: + case VS_ENG_AVAILABLE: + case VS_ENG_CLOSE_PENDING: + (void) close(cxn->vsc_sockfd); + cxn->vsc_sockfd = -1; + break; + case VS_ENG_DISCONNECTED: + case VS_ENG_RESERVED: + default: + break; + + } + + cxn->vsc_state = VS_ENG_DISCONNECTED; } } + (void) pthread_mutex_unlock(&vs_eng_mutex); } /* * vs_eng_connect * open socket connection to remote scan engine + * + * Returns: sockfd or -1 (error) */ -int -vs_eng_connect(vs_eng_conn_t *conn) +static int +vs_eng_connect(char *host, int port) { - int rc, sock_opt, err_num; + int rc, sockfd, opt_nodelay, opt_keepalive, opt_reuseaddr, err_num; struct sockaddr_in addr; struct hostent *hp; - if ((conn->vsc_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) return (-1); - hp = getipnodebyname(conn->vsc_host, AF_INET, 0, &err_num); - if (hp == NULL) + hp = getipnodebyname(host, AF_INET, 0, &err_num); + if (hp == NULL) { + (void) close(sockfd); return (-1); + } (void) memset(&addr, 0, sizeof (addr)); (void) memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); - addr.sin_port = htons(conn->vsc_port); + addr.sin_port = htons(port); addr.sin_family = hp->h_addrtype; freehostent(hp); #ifdef FIONBIO /* Use non-blocking mode for connect. */ - rc = nbio_connect(conn, (struct sockaddr *)&addr, + rc = nbio_connect(sockfd, (struct sockaddr *)&addr, sizeof (struct sockaddr)); #else - rc = connect(conn->vsc_sockfd, (struct sockaddr *)&addr, + rc = connect(sockfd, (struct sockaddr *)&addr, sizeof (struct sockaddr)); #endif - sock_opt = 1; - - if ((rc < 0) || (vscand_get_state() == VS_STATE_SHUTDOWN) || - (setsockopt(conn->vsc_sockfd, IPPROTO_TCP, TCP_NODELAY, - &sock_opt, sizeof (sock_opt)) < 0) || - (setsockopt(conn->vsc_sockfd, SOL_SOCKET, SO_KEEPALIVE, - &sock_opt, sizeof (sock_opt)) < 0)) { - syslog(LOG_WARNING, "Scan Engine - connection error (%s:%d) %s", - conn->vsc_host, conn->vsc_port, strerror(errno)); - (void) close(conn->vsc_sockfd); - conn->vsc_sockfd = -1; + opt_nodelay = 1; + opt_keepalive = 1; + opt_reuseaddr = 1; + + if ((rc < 0) || + (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, + &opt_nodelay, sizeof (opt_nodelay)) < 0) || + (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, + &opt_keepalive, sizeof (opt_keepalive)) < 0) || + (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + &opt_reuseaddr, sizeof (opt_reuseaddr)) < 0)) { + (void) close(sockfd); return (-1); } - return (0); + return (sockfd); } @@ -493,20 +752,20 @@ vs_eng_connect(vs_eng_conn_t *conn) */ #ifdef FIONBIO static int -nbio_connect(vs_eng_conn_t *conn, const struct sockaddr *sa, int sa_len) +nbio_connect(int sockfd, const struct sockaddr *sa, int sa_len) { struct pollfd pfd; int nbio, rc; - int soc = conn->vsc_sockfd; int error, len = sizeof (error); nbio = 1; - if ((ioctl(soc, FIONBIO, &nbio)) < 0) - return (connect(soc, sa, sa_len)); + if ((ioctl(sockfd, FIONBIO, &nbio)) < 0) + return (connect(sockfd, sa, sa_len)); - if ((rc = connect(soc, sa, sa_len)) != 0) { + if ((rc = connect(sockfd, sa, sa_len)) != 0) { if (errno == EINPROGRESS || errno == EINTR) { - pfd.fd = soc; + errno = 0; + pfd.fd = sockfd; pfd.events = POLLOUT; pfd.revents = 0; @@ -515,16 +774,24 @@ nbio_connect(vs_eng_conn_t *conn, const struct sockaddr *sa, int sa_len) errno = ETIMEDOUT; rc = -1; } else { - rc = getsockopt(soc, SOL_SOCKET, SO_ERROR, - &error, &len); - if (rc != 0 || error != 0) + if ((pfd.revents & + (POLLHUP | POLLERR | POLLNVAL)) || + (!(pfd.revents & POLLOUT))) { rc = -1; + } else { + rc = getsockopt(sockfd, SOL_SOCKET, + SO_ERROR, &error, &len); + if (rc != 0 || error != 0) + rc = -1; + if (error != 0) + errno = error; + } } } } nbio = 0; - (void) ioctl(soc, FIONBIO, &nbio); + (void) ioctl(sockfd, FIONBIO, &nbio); return (rc); } @@ -555,7 +822,7 @@ vs_eng_scanstamp_current(vs_scanstamp_t scanstamp) (void) pthread_mutex_lock(&vs_eng_mutex); for (i = 0; i < VS_SE_MAX; i++) { if ((vs_engines[i].vse_cfg.vep_enable) && - (vs_engines[i].vse_error == 0) && + (vs_engines[i].vse_error == B_FALSE) && (vs_icap_compare_scanstamp(i, scanstamp) == 0)) break; } diff --git a/usr/src/cmd/vscan/vscand/vs_icap.c b/usr/src/cmd/vscan/vscand/vs_icap.c index cb970ffe32..ec9634841a 100644 --- a/usr/src/cmd/vscan/vscand/vs_icap.c +++ b/usr/src/cmd/vscan/vscand/vs_icap.c @@ -235,24 +235,33 @@ vs_icap_config(int idx, char *host, int port) * Returns: result->vsr_rc */ int -vs_icap_scan_file(vs_eng_conn_t *conn, char *devname, char *fname, +vs_icap_scan_file(vs_eng_ctx_t *eng, char *devname, char *fname, uint64_t fsize, int flags, vs_result_t *result) { vs_scan_ctx_t ctx; int fd; - if ((fd = open(devname, O_RDONLY)) == -1) { - syslog(LOG_ERR, "Failed to open device %s", devname); + fd = open(devname, O_RDONLY); + + /* retry once on ENOENT as /dev link may not be created yet */ + if ((fd == -1) && (errno == ENOENT)) { + (void) sleep(1); + fd = open(devname, O_RDONLY); + } + + if (fd == -1) { + syslog(LOG_ERR, "Failed to open device %s - %s", + devname, strerror(errno)); result->vsr_rc = VS_RESULT_ERROR; return (result->vsr_rc); } /* initialize context */ (void) memset(&ctx, 0, sizeof (vs_scan_ctx_t)); - ctx.vsc_idx = conn->vsc_idx; - (void) strlcpy(ctx.vsc_host, conn->vsc_host, sizeof (ctx.vsc_host)); - ctx.vsc_port = conn->vsc_port; - ctx.vsc_sockfd = conn->vsc_sockfd; + ctx.vsc_idx = eng->vse_eidx; + (void) strlcpy(ctx.vsc_host, eng->vse_host, sizeof (ctx.vsc_host)); + ctx.vsc_port = eng->vse_port; + ctx.vsc_sockfd = eng->vse_sockfd; ctx.vsc_fd = fd; ctx.vsc_fname = fname; ctx.vsc_fsize = fsize; @@ -1141,7 +1150,7 @@ vs_icap_write(int fd, char *buf, int buflen) while (resid > 0) { errno = 0; - bytes_sent = write(fd, buf, resid); + bytes_sent = write(fd, ptr, resid); if (bytes_sent < 0) { if (errno == EINTR) continue; @@ -1171,7 +1180,7 @@ vs_icap_read(int fd, char *buf, int len) while (resid > 0) { errno = 0; - bytes_read = read(fd, buf, resid); + bytes_read = read(fd, ptr, resid); if (bytes_read < 0) { if (errno == EINTR) continue; @@ -1275,9 +1284,10 @@ vs_icap_readline(vs_scan_ctx_t *ctx, char *buf, int buflen) continue; if (retval <= 0) { - syslog(LOG_ERR, "Error receiving data from Scan Engine:" - " %s", retval == 0 ? "Scan Engine disconnected" - : strerror(errno)); + if (vscand_get_state() != VS_STATE_SHUTDOWN) { + syslog(LOG_ERR, "Error receiving data from " + "Scan Engine: %s", strerror(errno)); + } return (-1); } diff --git a/usr/src/cmd/vscan/vscand/vs_incl.h b/usr/src/cmd/vscan/vscand/vs_incl.h index 9977f2a1c2..488ef4243b 100644 --- a/usr/src/cmd/vscan/vscand/vs_incl.h +++ b/usr/src/cmd/vscan/vscand/vs_incl.h @@ -90,51 +90,43 @@ typedef struct vs_result { } vs_result_t; -/* scan engine connection */ -typedef struct vs_eng_conn { - int vsc_idx; - char vsc_engid[VS_SE_NAME_LEN]; - char vsc_host[MAXHOSTNAMELEN]; - int vsc_port; - int vsc_sockfd; - struct vs_eng_conn *vsc_next; - struct vs_eng_conn *vsc_prev; -} vs_eng_conn_t; - - -/* file attributes used by virus scanning */ -typedef struct vs_attr { - int vsa_modified; - int vsa_quarantined; - uint64_t vsa_size; - vs_scanstamp_t vsa_scanstamp; -}vs_attr_t; +/* scan engine connection context */ +typedef struct vs_eng_ctx { + int vse_eidx; /* engine index */ + int vse_cidx; /* connection index */ + char vse_engid[VS_SE_NAME_LEN]; + char vse_host[MAXHOSTNAMELEN]; + int vse_port; + int vse_sockfd; +} vs_eng_ctx_t; /* Function Prototypes */ vs_daemon_state_t vscand_get_state(void); char *vscand_viruslog(void); +int vscand_kernel_result(vs_scan_rsp_t *); int vs_door_init(void); void vs_door_fini(void); -void vs_svc_init(void); +int vs_svc_init(uint32_t); void vs_svc_fini(void); -int vs_svc_scan_file(char *, char *, vs_attr_t *, int, vs_scanstamp_t *); +int vs_svc_queue_scan_req(vs_scan_req_t *); +void vs_svc_terminate(void); void vs_eng_init(void); void vs_eng_fini(void); void vs_eng_config(vs_props_all_t *); -void vs_eng_set_error(vs_eng_conn_t *, int); -int vs_eng_get(vs_eng_conn_t *, int); -int vs_eng_connect(vs_eng_conn_t *); -void vs_eng_release(vs_eng_conn_t *); +void vs_eng_set_error(vs_eng_ctx_t *, int); +int vs_eng_get(vs_eng_ctx_t *, boolean_t); +void vs_eng_release(const vs_eng_ctx_t *); +void vs_eng_close_connections(void); int vs_eng_scanstamp_current(vs_scanstamp_t); void vs_icap_init(void); void vs_icap_fini(void); void vs_icap_config(int, char *, int); -int vs_icap_scan_file(vs_eng_conn_t *, char *, char *, uint64_t, +int vs_icap_scan_file(vs_eng_ctx_t *, char *, char *, uint64_t, int, vs_result_t *); void vs_icap_print_options(int); int vs_icap_compare_scanstamp(int, vs_scanstamp_t); diff --git a/usr/src/cmd/vscan/vscand/vs_main.c b/usr/src/cmd/vscan/vscand/vs_main.c index dd138557cc..b66d762a91 100644 --- a/usr/src/cmd/vscan/vscand/vs_main.c +++ b/usr/src/cmd/vscan/vscand/vs_main.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -57,13 +57,18 @@ #include <pwd.h> #include <grp.h> #include <priv_utils.h> +#include <rctl.h> #include "vs_incl.h" +#define VS_FILE_DESCRIPTORS 512 + static int vscand_fg = 0; /* daemon by default */ static vs_daemon_state_t vscand_state = VS_STATE_INIT; static int vscand_sigval = 0; static int vscand_kdrv_fd = -1; static pthread_mutex_t vscand_cfg_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t vscand_cfg_cv; +static pthread_t vscand_cfg_tid = 0; /* virus log path */ static char vscand_vlog[MAXPATHLEN]; @@ -83,14 +88,20 @@ static int vscand_daemonize_init(void); static void vscand_daemonize_fini(int, int); static int vscand_init(void); static void vscand_fini(void); +static int vscand_cfg_init(void); +static void vscand_cfg_fini(void); +static void *vscand_cfg_handler(void *); static int vscand_configure(void); +static void vscand_dtrace_cfg(vs_props_all_t *); static int vscand_kernel_bind(void); static void vscand_kernel_unbind(void); static int vscand_kernel_enable(int); static void vscand_kernel_disable(void); static int vscand_kernel_config(vs_config_t *); +static int vscand_kernel_max_req(uint32_t *); static void vscand_error(const char *); static int vscand_get_viruslog(void); +static int vscand_set_resource_limits(void); /* @@ -137,6 +148,8 @@ main(int argc, char **argv) int err_stat = 0, pfd = -1; sigset_t set; struct sigaction act; + int sigval; + mode_t log_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; mode_t door_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; @@ -172,14 +185,12 @@ main(int argc, char **argv) *vscand_vlog = 0; } - (void) unlink(VS_STATS_DOOR_NAME); (void) vscand_init_file(VS_STATS_DOOR_NAME, daemon_uid, sys_gid, door_mode); /* * Once we're done setting our global state up, set up signal handlers - * for ensuring orderly termination on SIGTERM. If we are starting in - * the foreground, we also use the same handler for SIGINT and SIGHUP. + * for ensuring orderly termination on SIGTERM. */ (void) sigfillset(&set); (void) sigdelset(&set, SIGABRT); /* always unblocked for ASSERT() */ @@ -191,11 +202,11 @@ main(int argc, char **argv) (void) sigaction(SIGTERM, &act, NULL); (void) sigaction(SIGHUP, &act, NULL); /* Refresh config */ (void) sigaction(SIGINT, &act, NULL); - (void) sigaction(SIGUSR1, &act, NULL); + (void) sigaction(SIGPIPE, &act, NULL); (void) sigdelset(&set, SIGTERM); (void) sigdelset(&set, SIGHUP); (void) sigdelset(&set, SIGINT); - (void) sigdelset(&set, SIGUSR1); + (void) sigdelset(&set, SIGPIPE); if (vscand_fg) { (void) sigdelset(&set, SIGTSTP); @@ -227,23 +238,23 @@ main(int argc, char **argv) /* Wait here until shutdown */ while (vscand_state == VS_STATE_RUNNING) { + if (vscand_sigval == 0) + (void) sigsuspend(&set); - (void) sigsuspend(&set); + sigval = vscand_sigval; + vscand_sigval = 0; - switch (vscand_sigval) { + switch (sigval) { case 0: - case SIGUSR1: + case SIGPIPE: break; case SIGHUP: - if (vscand_configure() != 0) - vscand_state = VS_STATE_SHUTDOWN; + (void) pthread_cond_signal(&vscand_cfg_cv); break; default: vscand_state = VS_STATE_SHUTDOWN; break; } - - vscand_sigval = 0; } vscand_fini(); @@ -252,7 +263,7 @@ main(int argc, char **argv) /* - * vscand_parse_args - + * vscand_parse_args * Routine to parse the arguments to the daemon program * 'f' argument runs process in the foreground instead of as a daemon */ @@ -424,20 +435,20 @@ vscand_daemonize_fini(int fd, int err_status) * create specified file and set its uid, gid and mode */ static int -vscand_init_file(char *filepath, uid_t uid, gid_t gid, mode_t mode) +vscand_init_file(char *filepath, uid_t uid, gid_t gid, mode_t access_mode) { int fd, rc = 0; struct stat stat_buf; char buf[MAXPATHLEN]; - if ((fd = open(filepath, O_RDONLY | O_CREAT, mode)) == -1) + if ((fd = open(filepath, O_RDONLY | O_CREAT, access_mode)) == -1) { rc = -1; - else { - if (fstat(fd, &stat_buf) != 0) + } else { + if (fstat(fd, &stat_buf) != 0) { rc = -1; - else { - if (stat_buf.st_mode != mode) { - if (fchmod(fd, mode) != 0) + } else { + if ((stat_buf.st_mode & S_IAMB) != access_mode) { + if (fchmod(fd, access_mode) != 0) rc = -1; } @@ -478,23 +489,33 @@ static int vscand_init(void) { int door_fd = -1; + uint32_t max_req; if (vscand_kernel_bind() < 0) return (-1); + if (vscand_kernel_max_req(&max_req) == -1) + return (-1); + + if (vs_svc_init(max_req) != 0) + return (-1); + if (vs_stats_init() != 0) vscand_error( gettext("failed to initialize statistics interface")); - vs_svc_init(); vs_icap_init(); vs_eng_init(); - if (vscand_configure() != 0) { + /* initialize configuration and handler thread */ + if (vscand_cfg_init() != 0) { vscand_error(gettext("failed to initialize configuration")); + vscand_fini(); return (-1); } + (void) vscand_set_resource_limits(); + if (((door_fd = vs_door_init()) < 0) || (vscand_kernel_enable(door_fd) < 0)) { vscand_fini(); @@ -510,8 +531,8 @@ vscand_init(void) * * vscand_kernel_disable - should be called first to ensure that no * more scan requests are initiated from the kernel module - * vs_door_fini - shouldn't be called until after the in-progress - * scans complete (vs_eng_fini waits for in progress scans) + * vs_svc_terminate - terminate requests and wait for thread completion + * vs_xxx_fini - module cleanup routines * vscand_kernel_unbind - should be called last to tell the kernel module * that vscand is shutdown. */ @@ -520,10 +541,16 @@ vscand_fini(void) { vscand_kernel_disable(); + /* terminate reconfiguration handler thread */ + vscand_cfg_fini(); + + /* terminate requests and wait for completion */ + vs_svc_terminate(); + + /* clean up */ vs_svc_fini(); vs_eng_fini(); vs_icap_fini(); - vs_door_fini(); vs_stats_fini(); @@ -532,6 +559,75 @@ vscand_fini(void) /* + * vscand_cfg_init + * + * initialize configuration and reconfiguration handler thread + */ +static int +vscand_cfg_init(void) +{ + int rc; + + (void) pthread_cond_init(&vscand_cfg_cv, NULL); + + (void) pthread_mutex_lock(&vscand_cfg_mutex); + rc = vscand_configure(); + (void) pthread_mutex_unlock(&vscand_cfg_mutex); + + if (rc != 0) + return (-1); + + if (pthread_create(&vscand_cfg_tid, NULL, vscand_cfg_handler, 0) != 0) { + vscand_cfg_tid = 0; + return (-1); + } + + return (0); +} + + +/* + * vscand_cfg_fini + * + * terminate reconfiguration handler thread + */ +static void +vscand_cfg_fini() +{ + if (vscand_cfg_tid != 0) { + (void) pthread_cond_signal(&vscand_cfg_cv); + (void) pthread_join(vscand_cfg_tid, NULL); + vscand_cfg_tid = 0; + } + (void) pthread_cond_destroy(&vscand_cfg_cv); +} + + +/* + * vscand_cfg_handler + * wait for reconfiguration event and reload configuration + * exit on VS_STATE_SHUTDOWN + */ +/*ARGSUSED*/ +static void * +vscand_cfg_handler(void *arg) +{ + (void) pthread_mutex_lock(&vscand_cfg_mutex); + + while (pthread_cond_wait(&vscand_cfg_cv, &vscand_cfg_mutex) == 0) { + if (vscand_state == VS_STATE_SHUTDOWN) + break; + + (void) vscand_configure(); + } + + (void) pthread_mutex_unlock(&vscand_cfg_mutex); + + return (NULL); +} + + +/* * vscand_configure */ static int @@ -541,12 +637,9 @@ vscand_configure(void) vs_config_t kconfig; vs_props_all_t config; - (void) pthread_mutex_lock(&vscand_cfg_mutex); - (void) memset(&config, 0, sizeof (vs_props_all_t)); if (vs_props_get_all(&config) != VS_ERR_NONE) { vscand_error(gettext("configuration data error")); - (void) pthread_mutex_unlock(&vscand_cfg_mutex); return (-1); } @@ -555,7 +648,6 @@ vscand_configure(void) if (vs_parse_types(config.va_props.vp_types, kconfig.vsc_types, &len) != 0) { vscand_error(gettext("configuration data error - types")); - (void) pthread_mutex_unlock(&vscand_cfg_mutex); return (-1); } kconfig.vsc_types_len = len; @@ -564,24 +656,22 @@ vscand_configure(void) if (vs_strtonum(config.va_props.vp_maxsize, &kconfig.vsc_max_size) != 0) { vscand_error(gettext("configuration data error - max-size")); - (void) pthread_mutex_unlock(&vscand_cfg_mutex); return (-1); } kconfig.vsc_allow = config.va_props.vp_maxsize_action ? 1LL : 0LL; /* Send configuration update to kernel */ if (vscand_kernel_config(&kconfig) != 0) { - (void) pthread_mutex_unlock(&vscand_cfg_mutex); return (-1); } - /* Tell vs_eng things have changed. */ - vs_eng_config(&config); + /* dtrace the configuration data */ + vscand_dtrace_cfg(&config); - /* Tell vs_stats things have changed */ + /* propagate configuration changes */ + vs_eng_config(&config); vs_stats_config(&config); - (void) pthread_mutex_unlock(&vscand_cfg_mutex); return (0); } @@ -667,7 +757,7 @@ vscand_kernel_unbind(void) static int vscand_kernel_enable(int door_fd) { - if (ioctl(vscand_kdrv_fd, VS_DRV_IOCTL_ENABLE, door_fd) < 0) { + if (ioctl(vscand_kdrv_fd, VS_IOCTL_ENABLE, door_fd) < 0) { vscand_error(gettext("failed to bind to kernel")); (void) close(vscand_kdrv_fd); vscand_kdrv_fd = -1; @@ -684,7 +774,7 @@ static void vscand_kernel_disable() { if (vscand_kdrv_fd >= 0) - (void) ioctl(vscand_kdrv_fd, VS_DRV_IOCTL_DISABLE); + (void) ioctl(vscand_kdrv_fd, VS_IOCTL_DISABLE); } @@ -694,11 +784,41 @@ vscand_kernel_disable() int vscand_kernel_config(vs_config_t *conf) { - if (vscand_kdrv_fd < 0) + if ((vscand_kdrv_fd < 0) || + (ioctl(vscand_kdrv_fd, VS_IOCTL_CONFIG, conf) < 0)) { + vscand_error(gettext("failed to send config to kernel")); return (-1); + } - if (ioctl(vscand_kdrv_fd, VS_DRV_IOCTL_CONFIG, conf) < 0) { - vscand_error(gettext("failed to send config to kernel")); + return (0); +} + + +/* + * vscand_kernel_result + */ +int +vscand_kernel_result(vs_scan_rsp_t *scan_rsp) +{ + if ((vscand_kdrv_fd < 0) || + (ioctl(vscand_kdrv_fd, VS_IOCTL_RESULT, scan_rsp) < 0)) { + vscand_error(gettext("failed to send result to kernel")); + return (-1); + } + + return (0); +} + + +/* + * vscand_kernel_max_req + */ +int +vscand_kernel_max_req(uint32_t *max_req) +{ + if ((vscand_kdrv_fd < 0) || + (ioctl(vscand_kdrv_fd, VS_IOCTL_MAX_REQ, max_req) < 0)) { + vscand_error(gettext("failed to get config data from kernel")); return (-1); } @@ -707,6 +827,35 @@ vscand_kernel_config(vs_config_t *conf) /* + * vscand_set_resource_limits + * + * If the process's max file descriptor limit is less than + * VS_FILE_DESCRIPTORS, increae it to VS_FILE_DESCRIPTORS. + */ +static int +vscand_set_resource_limits(void) +{ + int rc = -1; + rctlblk_t *rblk; + char *limit = "process.max-file-descriptor"; + + rblk = (rctlblk_t *)malloc(rctlblk_size()); + + if (rblk != NULL) { + rc = getrctl(limit, NULL, rblk, 0); + if ((rc == 0) && + (rctlblk_get_value(rblk) < VS_FILE_DESCRIPTORS)) { + rctlblk_set_value(rblk, VS_FILE_DESCRIPTORS); + rc = setrctl(limit, NULL, rblk, 0); + } + (void) free(rblk); + } + + return (rc); +} + + +/* * vscand_error */ static void @@ -715,3 +864,42 @@ vscand_error(const char *errmsg) (void) fprintf(stderr, "vscand: %s", errmsg); syslog(LOG_ERR, "%s\n", errmsg); } + + +/* + * vscand_dtrace_cfg + * vscand_dtrace_gen + * vscand_dtrace_eng + * + * Support for dtracing vscand configuration when processing + * a reconfiguration event (SIGHUP) + */ +/*ARGSUSED*/ +static void +vscand_dtrace_eng(char *id, boolean_t enable, char *host, int port, int conn) +{ +} +/*ARGSUSED*/ +static void +vscand_dtrace_gen(char *size, boolean_t action, char *types, char *log) +{ +} +static void +vscand_dtrace_cfg(vs_props_all_t *config) +{ + int i; + + vscand_dtrace_gen(config->va_props.vp_maxsize, + config->va_props.vp_maxsize_action, + config->va_props.vp_types, + config->va_props.vp_vlog); + + for (i = 0; i < VS_SE_MAX; i++) { + if (config->va_se[i].vep_engid[0] != 0) + vscand_dtrace_eng(config->va_se[i].vep_engid, + config->va_se[i].vep_enable, + config->va_se[i].vep_host, + config->va_se[i].vep_port, + config->va_se[i].vep_maxconn); + } +} diff --git a/usr/src/cmd/vscan/vscand/vs_stats.c b/usr/src/cmd/vscan/vscand/vs_stats.c index 0d30c40d32..c053169727 100644 --- a/usr/src/cmd/vscan/vscand/vs_stats.c +++ b/usr/src/cmd/vscan/vscand/vs_stats.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -53,6 +53,7 @@ static pthread_mutex_t vs_stats_mutex = PTHREAD_MUTEX_INITIALIZER; /* function prototype */ static int vs_stats_check_auth(void); +static void vs_stats_reset(void); static void vs_stats_door_call(void *, char *, size_t, door_desc_t *, uint_t); @@ -144,35 +145,67 @@ static void vs_stats_door_call(void *cookie, char *ptr, size_t size, door_desc_t *dp, uint_t n_desc) { - /* LINTED E_BAD_PTR_CAST_ALIGN - to be fixed with encoding */ + /* LINTED E_BAD_PTR_CAST_ALIGN */ vs_stats_req_t *req = (vs_stats_req_t *)ptr; - vs_stats_t rsp; + vs_stats_rsp_t rsp; - switch (*req) { + if ((cookie != &vs_stats_door_cookie) || + (ptr == NULL) || + (size != sizeof (vs_stats_req_t)) || + (req->vsr_magic != VS_STATS_DOOR_MAGIC)) { + return; + } + + rsp.vsr_magic = VS_STATS_DOOR_MAGIC; + + switch (req->vsr_id) { case VS_STATS_GET: (void) pthread_mutex_lock(&vs_stats_mutex); - (void) memcpy(&rsp, &vscan_stats, sizeof (vs_stats_t)); + rsp.vsr_stats = vscan_stats; (void) pthread_mutex_unlock(&vs_stats_mutex); - (void) door_return((char *)&rsp, sizeof (vs_stats_t), NULL, 0); + (void) door_return((char *)&rsp, sizeof (vs_stats_rsp_t), + NULL, 0); break; case VS_STATS_RESET: - if (vs_stats_check_auth() == 0) { - (void) pthread_mutex_lock(&vs_stats_mutex); - (void) memset(&vscan_stats, 0, sizeof (vs_stats_t)); - (void) pthread_mutex_unlock(&vs_stats_mutex); - } + vs_stats_reset(); (void) door_return(NULL, 0, NULL, 0); break; default: - (void) door_return(NULL, 0, NULL, 0); - break; + return; } } /* + * vs_stats_reset + * + * Reset totals and per-engine statistics to 0 + */ +static void +vs_stats_reset() +{ + int i; + + if (vs_stats_check_auth() != 0) + return; + + (void) pthread_mutex_lock(&vs_stats_mutex); + + vscan_stats.vss_scanned = 0; + vscan_stats.vss_infected = 0; + vscan_stats.vss_cleaned = 0; + vscan_stats.vss_failed = 0; + + for (i = 0; i < VS_SE_MAX; i++) + vscan_stats.vss_eng[i].vss_errors = 0; + + (void) pthread_mutex_unlock(&vs_stats_mutex); +} + + +/* * vs_stats_set * * Update scan request stats diff --git a/usr/src/cmd/vscan/vscand/vs_svc.c b/usr/src/cmd/vscan/vscand/vs_svc.c index d0fee6db30..80029fec08 100644 --- a/usr/src/cmd/vscan/vscand/vs_svc.c +++ b/usr/src/cmd/vscan/vscand/vs_svc.c @@ -39,26 +39,183 @@ #include <fcntl.h> #include <bsm/adt.h> #include <bsm/adt_event.h> +#include <pthread.h> #include "vs_incl.h" +/* + * vs_svc_nodes - table of scan requests and their thread id and + * scan engine context. + * The table is sized by the value passed to vs_svc_init. This + * value is obtained from the kernel and represents the maximum + * request idx that the kernel will request vscand to process. + * The table is indexed by the vsr_idx value passed in + * the scan request - always non-zero. This value is also the index + * into the kernel scan request table and identifies the instance of + * the driver being used to access file data for the scan. Although + * this is of no consequence here, it is useful information for debug. + * + * When a scan request is received a response is sent indicating + * one of the following: + * VS_STATUS_ERROR - an error occurred + * VS_STATUS_NO_SCAN - no scan is required + * VS_STATUS_SCANNING - request has been queued for async processing + * + * If the scan is required (VS_STATUS_SCANNING) a thread is created + * to perform the scan. It's tid is saved in vs_svc_nodes. + * + * In the case of SHUTDOWN, vs_terminate requests that all scan + * engine connections be closed, thus termintaing any in-progress + * scans, then awaits completion of all scanning threads as identified + * in vs_svc_nodes. + */ + +typedef struct vs_svc_node { + pthread_t vsn_tid; + vs_scan_req_t vsn_req; + vs_eng_ctx_t vsn_eng; +} vs_svc_node_t; + +static vs_svc_node_t *vs_svc_nodes; +static uint32_t vs_svc_max_node; /* max idx into vs_svc_nodes */ +static pthread_mutex_t vs_svc_mutex = PTHREAD_MUTEX_INITIALIZER; + + /* local functions */ +static void *vs_svc_async_scan(void *); +static int vs_svc_scan_file(vs_svc_node_t *, vs_scanstamp_t *); static void vs_svc_vlog(char *, vs_result_t *); static void vs_svc_audit(char *, vs_result_t *); + /* * vs_svc_init, vs_svc_fini * * Invoked on daemon load and unload */ -void -vs_svc_init() +int +vs_svc_init(uint32_t max_req) { + vs_svc_max_node = max_req; + vs_svc_nodes = (vs_svc_node_t *) + calloc(max_req + 1, sizeof (vs_svc_node_t)); + + return (vs_svc_nodes == NULL ? -1 : 0); } void vs_svc_fini() { + if (vs_svc_nodes) + free(vs_svc_nodes); +} + + +/* + * vs_svc_terminate + * + * Close all scan engine connections to terminate in-progress scan + * requests, and wait for all threads in vs_svc_nodes to complete + */ +void +vs_svc_terminate() +{ + int i; + pthread_t tid; + + /* close connections to abort requests */ + vs_eng_close_connections(); + + /* wait for threads */ + for (i = 1; i <= vs_svc_max_node; i++) { + + (void) pthread_mutex_lock(&vs_svc_mutex); + tid = vs_svc_nodes[i].vsn_tid; + (void) pthread_mutex_unlock(&vs_svc_mutex); + + if (tid != 0) + (void) pthread_join(tid, NULL); + } +} + + +/* + * vs_svc_queue_scan_req + * + * Determine if the file needs to be scanned - either it has + * been modified or its scanstamp is not current. + * Initiate a thread to process the request, saving the tid + * in vs_svc_nodes[idx].vsn_tid, where idx is the vsr_idx passed in + * the scan request. + * + * Returns: VS_STATUS_ERROR - error + * VS_STATUS_NO_SCAN - no scan required + * VS_STATUS_SCANNING - async scan initiated + */ +int +vs_svc_queue_scan_req(vs_scan_req_t *req) +{ + pthread_t tid; + vs_svc_node_t *node; + + /* No scan if file quarantined */ + if (req->vsr_quarantined) + return (VS_STATUS_NO_SCAN); + + /* No scan if file not modified AND scanstamp is current */ + if ((req->vsr_modified == 0) && + vs_eng_scanstamp_current(req->vsr_scanstamp)) { + return (VS_STATUS_NO_SCAN); + } + + /* scan required */ + node = &(vs_svc_nodes[req->vsr_idx]); + + (void) pthread_mutex_lock(&vs_svc_mutex); + if ((node->vsn_tid != 0) || (req->vsr_idx > vs_svc_max_node)) { + (void) pthread_mutex_unlock(&vs_svc_mutex); + return (VS_STATUS_ERROR); + } + + node->vsn_req = *req; + + if (pthread_create(&tid, NULL, vs_svc_async_scan, (void *)node) != 0) { + (void) pthread_mutex_unlock(&vs_svc_mutex); + return (VS_STATUS_ERROR); + } + + node->vsn_tid = tid; + (void) pthread_mutex_unlock(&vs_svc_mutex); + + return (VS_STATUS_SCANNING); +} + + +/* + * vs_svc_async_scan + * + * Initialize response structure, invoke vs_svc_scan_file to + * perform the scan, then send the result to the kernel. + */ +static void * +vs_svc_async_scan(void *arg) +{ + vs_svc_node_t *node = (vs_svc_node_t *)arg; + vs_scan_req_t *scan_req = &(node->vsn_req); + vs_scan_rsp_t scan_rsp; + + scan_rsp.vsr_idx = scan_req->vsr_idx; + scan_rsp.vsr_seqnum = scan_req->vsr_seqnum; + scan_rsp.vsr_result = vs_svc_scan_file(node, &scan_rsp.vsr_scanstamp); + + /* clear node and send async response to kernel */ + (void) pthread_mutex_lock(&vs_svc_mutex); + (void) memset(node, 0, sizeof (vs_svc_node_t)); + (void) pthread_mutex_unlock(&vs_svc_mutex); + + (void) vscand_kernel_result(&scan_rsp); + + return (NULL); } @@ -66,7 +223,6 @@ vs_svc_fini() * vs_svc_scan_file * * vs_svc_scan_file is responsible for: - * - determining if a scan is required * - obtaining & releasing a scan engine connection * - invoking the scan engine interface code to do the scan * - retrying a failed scan (up to VS_MAX_RETRY times) @@ -75,78 +231,66 @@ vs_svc_fini() * * * Returns: - * VS_STATUS_NO_SCAN - scan not reqd, or daemon shutting down + * VS_STATUS_NO_SCAN - scan not reqd; daemon shutting down * VS_STATUS_CLEAN - scan success. File clean. * new scanstamp returned in scanstamp param. * VS_STATUS_INFECTED - scan success. File infected. * VS_STATUS_ERROR - scan failure either in vscand or scan engine. */ -int -vs_svc_scan_file(char *devname, char *fname, vs_attr_t *fattr, int flags, - vs_scanstamp_t *scanstamp) +static int +vs_svc_scan_file(vs_svc_node_t *node, vs_scanstamp_t *scanstamp) { - vs_eng_conn_t conn; + char devname[MAXPATHLEN]; + int flags = 0; int retries; vs_result_t result; + vs_scan_req_t *req = &(node->vsn_req); + vs_eng_ctx_t *eng = &(node->vsn_eng); - /* initialize response scanstamp to current scanstamp value */ - (void) strlcpy(*scanstamp, fattr->vsa_scanstamp, - sizeof (vs_scanstamp_t)); - - - /* No scan if file quarantined */ - if (fattr->vsa_quarantined) - return (VS_STATUS_NO_SCAN); + (void) snprintf(devname, MAXPATHLEN, "%s%d", VS_DRV_PATH, req->vsr_idx); - /* No scan if file not modified AND scanstamp is current */ - if ((fattr->vsa_modified == 0) && - vs_eng_scanstamp_current(fattr->vsa_scanstamp)) { - return (VS_STATUS_NO_SCAN); - } + /* initialize response scanstamp to current scanstamp value */ + (void) strlcpy(*scanstamp, req->vsr_scanstamp, sizeof (vs_scanstamp_t)); (void) memset(&result, 0, sizeof (vs_result_t)); result.vsr_rc = VS_RESULT_UNDEFINED; for (retries = 0; retries <= VS_MAX_RETRY; retries++) { - /* identify available engine connection */ - if (vs_eng_get(&conn, retries) != 0) { + /* get engine connection */ + if (vs_eng_get(eng, (retries != 0)) != 0) { result.vsr_rc = VS_RESULT_ERROR; continue; } - /* connect to engine and scan file */ - if (vs_eng_connect(&conn) != 0) { - result.vsr_rc = VS_RESULT_SE_ERROR; - } else { - if (vscand_get_state() == VS_STATE_SHUTDOWN) { - vs_eng_release(&conn); - return (VS_STATUS_NO_SCAN); - } - - (void) vs_icap_scan_file(&conn, devname, fname, - fattr->vsa_size, flags, &result); + /* shutdown could occur while waiting for engine connection */ + if (vscand_get_state() == VS_STATE_SHUTDOWN) { + vs_eng_release(eng); + return (VS_STATUS_NO_SCAN); } + /* scan file */ + (void) vs_icap_scan_file(eng, devname, req->vsr_path, + req->vsr_size, flags, &result); + /* if no error, clear error state on engine and break */ if ((result.vsr_rc != VS_RESULT_SE_ERROR) && (result.vsr_rc != VS_RESULT_ERROR)) { - vs_eng_set_error(&conn, 0); - vs_eng_release(&conn); + vs_eng_set_error(eng, 0); + vs_eng_release(eng); break; } /* treat error on shutdown as scan not required */ if (vscand_get_state() == VS_STATE_SHUTDOWN) { - vs_eng_release(&conn); + vs_eng_release(eng); return (VS_STATUS_NO_SCAN); } /* set engine's error state and update engine stats */ - if (result.vsr_rc == VS_RESULT_SE_ERROR) { - vs_eng_set_error(&conn, 1); - vs_stats_eng_err(conn.vsc_engid); - } - vs_eng_release(&conn); + if (result.vsr_rc == VS_RESULT_SE_ERROR) + vs_eng_set_error(eng, 1); + + vs_eng_release(eng); } vs_stats_set(result.vsr_rc); @@ -158,8 +302,8 @@ vs_svc_scan_file(char *devname, char *fname, vs_attr_t *fattr, int flags, */ if (result.vsr_rc == VS_RESULT_CLEANED || result.vsr_rc == VS_RESULT_FORBIDDEN) { - vs_svc_vlog(fname, &result); - vs_svc_audit(fname, &result); + vs_svc_vlog(req->vsr_path, &result); + vs_svc_audit(req->vsr_path, &result); return (VS_STATUS_INFECTED); } @@ -177,8 +321,8 @@ vs_svc_scan_file(char *devname, char *fname, vs_attr_t *fattr, int flags, /* * vs_svc_vlog * - * log details of infections detected in file - * If virus log is not configured or cannot be opened, use syslog. + * log details of infections detected in syslig + * If virus log is configured log details there too */ static void vs_svc_vlog(char *filepath, vs_result_t *result) @@ -190,40 +334,42 @@ vs_svc_vlog(char *filepath, vs_result_t *result) int i; char *log; - if ((log = vscand_viruslog()) != NULL) - fp = fopen(log, "a"); + /* syslog */ + if (result->vsr_nviolations == 0) { + syslog(LOG_WARNING, "quarantine %s\n", filepath); + } else { + for (i = 0; i < result->vsr_nviolations; i++) { + syslog(LOG_WARNING, "quarantine %s %d - %s\n", + filepath, + result->vsr_vrec[i].vr_id, + result->vsr_vrec[i].vr_desc); + } + } - if (fp) { - (void) time(&sec); - timestamp = localtime(&sec); - (void) strftime(timebuf, sizeof (timebuf), "%D %T", timestamp); + /* log file */ + if (((log = vscand_viruslog()) == NULL) || + ((fp = fopen(log, "a")) == NULL)) { + return; } + (void) time(&sec); + timestamp = localtime(&sec); + (void) strftime(timebuf, sizeof (timebuf), "%D %T", timestamp); + if (result->vsr_nviolations == 0) { - if (fp) { - (void) fprintf(fp, "%s quarantine %s", - timebuf, filepath); - } else { - syslog(LOG_WARNING, "quarantine %s\n", filepath); - } + (void) fprintf(fp, "%s quarantine %d[%s]\n", + timebuf, strlen(filepath), filepath); } else { for (i = 0; i < result->vsr_nviolations; i++) { - if (fp) { - (void) fprintf(fp, "%s quarantine %s %d - %s\n", - timebuf, filepath, - result->vsr_vrec[i].vr_id, - result->vsr_vrec[i].vr_desc); - } else { - syslog(LOG_WARNING, "quarantine %s %d - %s\n", - filepath, - result->vsr_vrec[i].vr_id, - result->vsr_vrec[i].vr_desc); - } + (void) fprintf(fp, "%s quarantine %d[%s] %d - %d[%s]\n", + timebuf, strlen(filepath), filepath, + result->vsr_vrec[i].vr_id, + strlen(result->vsr_vrec[i].vr_desc), + result->vsr_vrec[i].vr_desc); } } - if (fp) - (void) fclose(fp); + (void) fclose(fp); } diff --git a/usr/src/cmd/vscan/vscand/vscan.d b/usr/src/cmd/vscan/vscand/vscan.d index f7a16172d6..62a88f23a8 100755 --- a/usr/src/cmd/vscan/vscand/vscan.d +++ b/usr/src/cmd/vscan/vscand/vscan.d @@ -26,155 +26,251 @@ #pragma ident "%Z%%M% %I% %E% SMI" +/* #pragma D option flowindent +*/ /* *** vscan kernel pseudo driver *** */ -/* vscan_svc.c */ -sdt:vscan::vscan-scan-file +/* + * vscan_svc.c + */ +sdt:vscan::vscan-req-counts { - printf("%s (%s)", stringof(arg0), arg1 ? "async" : "sync"); + printf("%s reql: %d, node: %d, taskq: %d", + stringof(arg0), + ((vscan_svc_counts_t *)arg1)->vsc_reql, + ((vscan_svc_counts_t *)arg1)->vsc_node, + ((vscan_svc_counts_t *)arg1)->vsc_tq); } -sdt:vscan::vscan-exempt-filesize +sdt:vscan::vscan-svc-state-violation { - printf("%s EXEMPT (%s)", stringof(arg0), arg1 ? "DENY" : "ALLOW"); + printf("%d %s", arg0, + arg0 == 0 ? "UNCONFIG" : + arg0 == 1 ? "IDLE" : + arg0 == 2 ? "ENABLED" : + arg0 == 3 ? "DISABLED" : "UNKNOWN"); } -sdt:vscan::vscan-type-match +sdt:vscan::vscan-scan-timeout { - printf("ext: %s matched: %s", stringof(arg0), stringof(arg1)); + printf("idx: %d, seqnum: %d - %s", + ((vscan_req_t *)arg0)->vsr_idx, + ((vscan_req_t *)arg0)->vsr_seqnum, + stringof(((vscan_req_t *)arg0)->vsr_vp->v_path)); } -sdt:vscan::vscan-exempt-filetype -{ - printf("%s EXEMPT", stringof(arg0)); -} - -sdt:vscan::vscan-wait-scan +sdt:vscan::vscan-scan-file { - printf("%s (%d) waiters: %d", - stringof(((vscan_file_t *)arg0)->vsf_req.vsr_vp->v_path), - arg1, ((vscan_file_t *)arg0)->vsf_wait_count); + printf("%s (%s)", stringof(arg0), arg1 ? "async" : "sync"); } -sdt:vscan::vscan-wait-slot +sdt:vscan::vscan-exempt-filesize { - printf("%s", stringof(arg0)); + printf("%s EXEMPT (%s)", stringof(arg0), arg1 ? "DENY" : "ALLOW"); } -sdt:vscan::vscan-insert +sdt:vscan::vscan-type-match { - printf("idx: %d - %s", arg1, stringof(arg0)); + printf("ext: %s matched: %s", stringof(arg0), stringof(arg1)); } -sdt:vscan::vscan-release +sdt:vscan::vscan-exempt-filetype { - printf("idx: %d - %s", arg1, stringof(arg0)); + printf("%s EXEMPT", stringof(arg0)); } sdt:vscan::vscan-getattr { printf("%s, m: %d, q: %d, scanstamp: %s", - stringof(((vscan_file_t *)arg0)->vsf_req.vsr_vp->v_path), - ((vscan_file_t *)arg0)->vsf_modified, - ((vscan_file_t *)arg0)->vsf_quarantined, - stringof(((vscan_file_t *)arg0)->vsf_scanstamp)); + stringof(((vscan_svc_node_t *)arg0)->vsn_req->vsr_vp->v_path), + ((vscan_svc_node_t *)arg0)->vsn_modified, + ((vscan_svc_node_t *)arg0)->vsn_quarantined, + stringof(((vscan_svc_node_t *)arg0)->vsn_scanstamp)); } sdt:vscan::vscan-setattr { /* XAT_AV_QUARANTINED */ printf("%s", (arg1 & 0x400) == 0 ? "" : - ((vscan_file_t *)arg0)->vsf_quarantined ? "q: 1, " : "q: 0, "); + ((vscan_svc_node_t *)arg0)->vsn_quarantined ? "q: 1, " : "q: 0, "); /* XAT_AV_MODIFIED */ printf("%s", (arg1 & 0x800) == 0 ? "" : - ((vscan_file_t *)arg0)->vsf_modified ? "m: 1, " : "m: 0, "); + ((vscan_svc_node_t *)arg0)->vsn_modified ? "m: 1, " : "m: 0, "); /* XAT_AV_SCANSTAMP */ printf("%s", (arg1 & 0x1000) == 0 ? "" : "scanstamp: "); printf("%s", (arg1 & 0x1000) == 0 ? "" : - stringof(((vscan_file_t *)arg0)->vsf_scanstamp)); + stringof(((vscan_svc_node_t *)arg0)->vsn_scanstamp)); } sdt:vscan::vscan-mtime-changed { printf("%s", - stringof(((vscan_file_t *)arg0)->vsf_req.vsr_vp->v_path)); + stringof(((vscan_svc_node_t *)arg0)->vsn_req->vsr_vp->v_path)); } sdt:vscan::vscan-result { - printf("VS_STATUS_%s - VS_ACCESS_%s", - arg0 == 0 ? "UNDEFINED" : - arg0 == 1 ? "NO_SCAN" : - arg0 == 2 ? "ERROR" : - arg0 == 3 ? "CLEAN" : - arg0 == 4 ? "INFECTED" : "XXX unknown", - arg1 == 0 ? "UNDEFINED" : - arg1 == 1 ? "ALLOW" : "DENY"); + printf("idx: %d, seqnum: %d, VS_STATUS_%s - VS_ACCESS_%s", + arg0, arg1, + arg2 == 0 ? "UNDEFINED" : + arg2 == 1 ? "NO_SCAN" : + arg2 == 2 ? "ERROR" : + arg2 == 3 ? "CLEAN" : + arg2 == 4 ? "INFECTED" : + arg2 == 5 ? "SCANNING" : "XXX unknown", + arg3 == 0 ? "UNDEFINED" : + arg3 == 1 ? "ALLOW" : "DENY"); +} + +/* insert request into request list */ +fbt:vscan:vscan_svc_reql_insert:entry +{ + printf("%s", stringof(args[0]->v_path)); +} +fbt:vscan:vscan_svc_reql_insert:return +/args[1] != 0/ +{ + printf("seqnum %d %s", args[1]->vsr_seqnum, + stringof(args[1]->vsr_vp->v_path)); +} +fbt:vscan:vscan_svc_reql_insert:return +/args[1] == 0/ +{ + printf("request list full"); +} +/* insert request into scan table */ +fbt:vscan:vscan_svc_insert_req:entry +{ + printf("seqnum: %d - %s", + args[0]->vsr_seqnum, stringof(args[0]->vsr_vp->v_path)); +} +fbt:vscan:vscan_svc_insert_req:return +{ + printf("idx: %d", args[1]); +} +/* remove request from request list and scan table and delete it*/ +fbt:vscan:vscan_svc_delete_req:entry +{ + printf("idx: %d, seqnum: %d - %s", + args[0]->vsr_idx, args[0]->vsr_seqnum, + stringof(args[0]->vsr_vp->v_path)); } +fbt:vscan:vscan_svc_delete_req:return, +fbt:vscan:vscan_svc_reql_handler:entry, +fbt:vscan:vscan_svc_reql_handler:return +{ +} + +fbt:vscan:vscan_svc_taskq_callback:entry, +fbt:vscan:vscan_svc_do_scan:entry +{ + printf("idx: %d, seqnum: %d - %s", + ((vscan_req_t *)(args[0]))->vsr_idx, + ((vscan_req_t *)(args[0]))->vsr_seqnum, + stringof(((vscan_req_t *)(args[0]))->vsr_vp->v_path)); +} +fbt:vscan:vscan_svc_scan_complete:entry +{ + printf("idx: %d, seqnum: %d, state: %s - %s", + args[0]->vsr_idx, args[0]->vsr_seqnum, + args[0]->vsr_state == 0 ? "INIT" : + args[0]->vsr_state == 1 ? "QUEUED" : + args[0]->vsr_state == 2 ? "IN_PROGRESS" : + args[0]->vsr_state == 3 ? "SCANNING" : + args[0]->vsr_state == 4 ? "ASYNC_COMPLETE" : + args[0]->vsr_state == 5 ? "COMPLETE" : "UNKNOWN", + stringof(args[0]->vsr_vp->v_path)); +} + +fbt:vscan:vscan_svc_taskq_callback:return, +fbt:vscan:vscan_svc_do_scan:return, +fbt:vscan:vscan_svc_scan_complete:return +{ +} + +sdt:vscan::vscan-abort +{ + printf("idx: %d, seqnum: %d - %s", + ((vscan_req_t *)(arg0))->vsr_idx, + ((vscan_req_t *)(arg0))->vsr_seqnum, + stringof(((vscan_req_t *)(arg0))->vsr_vp->v_path)); +} fbt:vscan:vscan_svc_enable:entry, fbt:vscan:vscan_svc_enable:return, fbt:vscan:vscan_svc_disable:entry, fbt:vscan:vscan_svc_disable:return, fbt:vscan:vscan_svc_configure:entry, -fbt:vscan:vscan_svc_configure:return, -fbt:vscan:vscan_svc_exempt_filetype:entry, -fbt:vscan:vscan_svc_scan_file:return, -fbt:vscan:vscan_svc_taskq_callback:entry, -fbt:vscan:vscan_svc_taskq_callback:return, -fbt:vscan:vscan_svc_do_scan:return +fbt:vscan:vscan_svc_configure:return { } /* -fbt:vscan:vscan_svc_match_ext:entry + * vscan_door.c + */ +fbt:vscan:vscan_door_open:entry, +fbt:vscan:vscan_door_open:return, +fbt:vscan:vscan_door_close:entry, +fbt:vscan:vscan_door_close:return { - printf("ext: %s, check: %s", stringof(args[1]), stringof(args[0])); } -fbt:vscan:vscan_svc_match_ext:return -{ -} -*/ - -/* vscan_door.c */ fbt:vscan:vscan_door_scan_file:entry { - printf("%s (%d)", args[0]->vsr_path, args[0]->vsr_id); + printf("idx: %d, seqnum: %d - %s", + args[0]->vsr_idx, args[0]->vsr_seqnum, args[0]->vsr_path); } fbt:vscan:vscan_door_scan_file:return { - printf("%s", args[1] == 0 ? "success" : "error"); + printf("VS_STATUS_%s", + args[1] == 0 ? "UNDEFINED" : + args[1] == 1 ? "NO_SCAN" : + args[1] == 2 ? "ERROR" : + args[1] == 3 ? "CLEAN" : + args[1] == 4 ? "INFECTED" : + args[1] == 5 ? "SCANNING" : "XXX unknown"); } -/* vscan_drv.c */ + +/* + * vscan_drv.c + */ +sdt:vscan::vscan-drv-state-violation +{ + printf("%d %s", arg0, + arg0 == 0 ? "UNCONFIG" : + arg0 == 1 ? "IDLE" : + arg0 == 2 ? "CONNECTED" : + arg0 == 3 ? "ENABLED" : + arg0 == 4 ? "DELAYED_DISABLE" : "UNKNOWN"); +} sdt:vscan::vscan-minor-node { printf("vscan%d %s", arg0, arg1 != 0 ? "created" : "error"); } -/* - * unprivileged vscan driver access attempt - */ +/* unprivileged vscan driver access attempt */ sdt:vscan::vscan-priv /arg0 != 0/ { printf("vscan driver access attempt by unprivileged process"); } -/* - * daemon-driver synchronization - */ +/* daemon-driver synchronization */ +sdt:vscan::vscan-reconnect +{ +} + fbt:vscan:vscan_drv_open:entry / *(int *)args[0] == 0/ { @@ -193,37 +289,31 @@ fbt:vscan:vscan_drv_ioctl:entry printf("vscan daemon ioctl %d %s", args[1], args[1] == 1 ? "ENABLE" : args[1] == 2 ? "DISABLE" : - args[1] == 4 ? "CONFIG" : "unknown"); + args[1] == 3 ? "CONFIG" : + args[1] == 4 ? "RESULT" : + args[1] == 5 ? "MAX FILES" : "unknown"); } fbt:vscan:vscan_drv_delayed_disable:entry, -fbt:vscan:vscan_drv_delayed_disable:return -{ -} - -sdt:vscan::vscan-reconnect +fbt:vscan:vscan_drv_delayed_disable:return, +fbt:vscan:vscan_drv_attach:entry, +fbt:vscan:vscan_drv_detach:entry { } -/* -fbt:vscan:vscan_drv_attach:entry, fbt:vscan:vscan_drv_attach:return, -fbt:vscan:vscan_drv_detach:entry, fbt:vscan:vscan_drv_detach:return { + printf("%s", args[1] ? "DDI_FAILURE" : "DDI_SUCCESS"); } -fbt:vscan:vscan_drv_in_use:return, -fbt:vscan:vscan_svc_in_use:return +fbt:vscan:vscan_drv_in_use:return { - printf("%s", args[1] ? "in use" : "not in use"); + printf("%s", args[1] ? "TRUE" : "FALSE"); } -*/ -/* - * file access - */ +/* file access */ /* fbt:vscan:vscan_drv_open:entry @@ -245,13 +335,30 @@ fbt:vscan:vscan_drv_read:entry *** vscan daemon - vscand *** */ +pid$target::vs_svc_init:entry +{ + printf("Max concurrent scan requests from kernel: %d", arg1); +} + +pid$target::vs_svc_init:return +{ +} + + pid$target::vs_door_scan_req:entry, pid$target::vs_svc_scan_file:entry, +pid$target::vs_svc_queue_scan_req:entry, +pid$target::vs_svc_async_scan:entry, pid$target::vs_eng_scanstamp_current:entry, pid$target::vs_icap_scan_file:entry { } +pid$target::vs_svc_queue_scan_req:return, +pid$target::vs_svc_async_scan:return +{ +} + pid$target::vs_svc_scan_file:return { printf("VS_STATUS_%s", @@ -259,7 +366,8 @@ pid$target::vs_svc_scan_file:return arg1 == 1 ? "NO_SCAN" : arg1 == 2 ? "ERROR" : arg1 == 3 ? "CLEAN" : - arg1 == 4 ? "INFECTED" : "XXX unknown"); + arg1 == 4 ? "INFECTED" : + arg1 == 5 ? "SCANNING" : "XXX unknown"); } pid$target::vs_eng_scanstamp_current:return @@ -269,11 +377,11 @@ pid$target::vs_eng_scanstamp_current:return pid$target::vs_icap_scan_file:return { - printf("%ld VS_RESULT_%s", arg1, - arg1 == 0 ? "UNDEFINED" : - arg1 == 1 ? "CLEAN" : - arg1 == 2 ? "CLEANED" : - arg1 == 3 ? "FORBIDDEN" : "(SE)_ERROR"); + printf("%d VS_RESULT_%s", (int)arg1, + (int)arg1 == 0 ? "UNDEFINED" : + (int)arg1 == 1 ? "CLEAN" : + (int)arg1 == 2 ? "CLEANED" : + (int)arg1 == 3 ? "FORBIDDEN" : "(SE)_ERROR"); } pid$target::vs_stats_set:entry @@ -289,7 +397,9 @@ pid$target::vs_stats_set:return /* get engine connection */ pid$target::vs_eng_get:entry, -pid$target::vs_eng_connect:entry +pid$target::vs_eng_connect:entry, +pid$target::vs_eng_release:entry, +pid$target::vs_eng_release:return { } pid$target::vs_eng_get:return, @@ -302,9 +412,35 @@ pid$target::vs_eng_connect:return pid$target::vs_eng_set_error:entry / arg1 == 1 / { - printf("scan engine %d error", arg0 + 1); + printf("scan engine error"); } +/* configuration */ +pid$target::vscand_cfg_init:entry, +pid$target::vscand_cfg_fini:entry, +pid$target::vscand_cfg_init:return, +pid$target::vscand_cfg_fini:return, +pid$target::vscand_cfg_handler:entry, +pid$target::vscand_cfg_handler:return +{ +} + +pid$target::vscand_dtrace_gen:entry +{ + printf("maxsize: %s action: %s\n", + copyinstr(arg0), (arg1 == 1) ? "allow" : "deny"); + printf("types: %s\n", copyinstr(arg2)); + printf("log: %s\n", copyinstr(arg3)); +} +pid$target::vscand_dtrace_eng:entry +{ + printf("\n%s %s \nhost: %s \nport: %d \nmax connections: %d\n", + copyinstr(arg0), (arg1 == 1) ? "enabled" : "disabled", + copyinstr(arg2), arg3, arg4); +} + + + /* shutdown */ pid$target::vscand_sig_handler:entry { @@ -317,6 +453,10 @@ pid$target::vscand_kernel_disable:entry, pid$target::vscand_kernel_disable:return, pid$target::vscand_kernel_unbind:entry, pid$target::vscand_kernel_unbind:return, +pid$target::vscand_kernel_result:entry, +pid$target::vscand_kernel_result:return, +pid$target::vs_svc_terminate:entry, +pid$target::vs_svc_terminate:return, pid$target::vs_eng_fini:entry, pid$target::vs_eng_fini:return, pid$target::vs_eng_close_connections:entry, @@ -346,7 +486,7 @@ pid$target::vs_icap_send_preview:return, pid$target::vs_icap_send_respmod_hdr:return, pid$target::vs_icap_read_respmod_resp:return { - printf("%s", arg1 < 0 ? "error" : "success"); + printf("%s", (int)arg1 < 0 ? "error" : "success"); } pid$target::vs_icap_may_preview:return @@ -368,7 +508,7 @@ pid$target::vs_icap_read:return, pid$target::vs_icap_readline:return, pid$target::vs_icap_send_chunk:return, pid$target::gethostname:return -/arg1 < 0/ +/(int)arg1 == -1/ { printf("error"); } @@ -389,10 +529,12 @@ pid$target::vs_icap_resp_encap:return pid$target::write:return, pid$target::read:return, -pid$target::recv:return, pid$target::open:return, pid$target::calloc:return /arg1 <= 0/ { printf("error"); } +/* +pid$target::recv:return, +*/ diff --git a/usr/src/lib/libvscan/common/libvscan.c b/usr/src/lib/libvscan/common/libvscan.c index f7d5170750..cfd012c2b2 100644 --- a/usr/src/lib/libvscan/common/libvscan.c +++ b/usr/src/lib/libvscan/common/libvscan.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -48,8 +48,8 @@ /* SMF property group and property names */ #define VS_PGNAME_GENERAL "vs_general" -#define VS_PGNAME_ENGINE "vs_engine_%d" -#define VS_PGNAME_ENGINE_LEN 16 +#define VS_PGNAME_ENGINE_PREFIX "vs_engine_" +#define VS_PGNAME_ENGINE_LEN VS_SE_NAME_LEN + 16 #define VS_PNAME_MAXSIZE "maxsize" #define VS_PNAME_MAXSIZE_ACTION "maxsize_action" @@ -166,6 +166,7 @@ static int vs_scf_get(const vs_propdef_t *, vs_prop_hd_t *, vs_scfctx_t *, int); static int vs_scf_values_set(const char *, vs_prop_hd_t *); static int vs_scf_set(const vs_propdef_t *, vs_prop_hd_t *, vs_scfctx_t *, int); static int vs_scf_pg_create(const char *, vs_prop_hd_t *); +static int vs_scf_pg_delete(const char *); static int vs_scf_ctx_open(vs_scfctx_t *); static void vs_scf_ctx_close(vs_scfctx_t *); @@ -175,8 +176,8 @@ static int vs_is_valid_types(const char *); static int vs_is_valid_host(const char *); static int vs_checkauth(char *); -typedef char vs_engid_t[VS_SE_NAME_LEN]; -static int vs_props_get_engines(vs_engid_t *engids, int *count); +static int vs_props_get_engines(char *[], int *); +static void vs_engid_to_pgname(const char *, char [VS_PGNAME_ENGINE_LEN]); static int vs_scf_pg_count(void); static int vs_strtoshift(const char *); @@ -199,8 +200,7 @@ int vs_props_get_all(vs_props_all_t *va) { int i, rc, n; - char *engid; - vs_engid_t engids[VS_SE_MAX]; + char *engids[VS_SE_MAX]; (void) memset(va, 0, sizeof (vs_props_all_t)); if ((rc = vs_props_get(&va->va_props, VS_PROPID_GEN_ALL)) @@ -211,17 +211,19 @@ vs_props_get_all(vs_props_all_t *va) if ((rc = vs_props_get_engines(engids, &n)) != VS_ERR_NONE) return (rc); - if (n > VS_SE_MAX) - n = VS_SE_MAX; - for (i = 0; i < n; i++) { - engid = engids[i]; - rc = vs_props_se_get(engid, &va->va_se[i], VS_PROPID_SE_ALL); - if (rc != VS_ERR_NONE) - return (rc); + if ((rc = vs_props_se_get(engids[i], + &va->va_se[i], VS_PROPID_SE_ALL)) != VS_ERR_NONE) + break; } - return (VS_ERR_NONE); + /* free engids allocated in vs_props_get_engines */ + for (i = 0; i < VS_SE_MAX; i++) { + if (engids[i] != NULL) + free(engids[i]); + } + + return (rc); } @@ -311,6 +313,7 @@ int vs_props_se_get(char *engid, vs_props_se_t *sep, uint64_t propids) { int rc; + char pgname[VS_PGNAME_ENGINE_LEN]; vs_prop_hd_t prop_hd; /* VS_PGNAME_GENERAL is a reserved for GENERAL property group */ @@ -331,7 +334,8 @@ vs_props_se_get(char *engid, vs_props_se_t *sep, uint64_t propids) prop_hd.vp_ids |= VS_PROPID_SE_HOST; /* Load values from the repository */ - rc = vs_scf_values_get(engid, &prop_hd); + vs_engid_to_pgname(engid, pgname); + rc = vs_scf_values_get(pgname, &prop_hd); if (rc != VS_ERR_NONE) return (rc); @@ -371,6 +375,7 @@ int vs_props_se_set(char *engid, const vs_props_se_t *sep, uint64_t propids) { int rc; + char pgname[VS_PGNAME_ENGINE_LEN]; vs_prop_hd_t prop_hd; /* VS_PGNAME_GENERAL is a reserved for GENERAL property group */ @@ -384,6 +389,8 @@ vs_props_se_set(char *engid, const vs_props_se_t *sep, uint64_t propids) prop_hd.vp_type = VS_PTYPE_SE; prop_hd.vp_all = VS_PROPID_SE_ALL; + vs_engid_to_pgname(engid, pgname); + /* * if enabling a scan engine, ensure that a valid host * is also being set, or already exists in the repository @@ -392,7 +399,7 @@ vs_props_se_set(char *engid, const vs_props_se_t *sep, uint64_t propids) !(propids & VS_PROPID_SE_HOST)) { prop_hd.vp_ids = VS_PROPID_SE_HOST; - if ((rc = vs_scf_values_get(engid, &prop_hd)) != VS_ERR_NONE) + if ((rc = vs_scf_values_get(pgname, &prop_hd)) != VS_ERR_NONE) return (rc); if (vs_validate(&prop_hd, VS_PROPID_SE_HOST) != VS_ERR_NONE) @@ -402,7 +409,7 @@ vs_props_se_set(char *engid, const vs_props_se_t *sep, uint64_t propids) prop_hd.vp_ids = propids; prop_hd.vp_se = *sep; - return (vs_scf_values_set(engid, &prop_hd)); + return (vs_scf_values_set(pgname, &prop_hd)); } @@ -413,6 +420,7 @@ int vs_props_se_create(char *engid, const vs_props_se_t *sep, uint64_t propids) { int n; + char pgname[VS_PGNAME_ENGINE_LEN]; vs_prop_hd_t prop_hd; if ((propids & VS_PROPID_SE_ALL) != propids) @@ -428,14 +436,21 @@ vs_props_se_create(char *engid, const vs_props_se_t *sep, uint64_t propids) if (n == VS_SE_MAX) return (VS_ERR_MAX_SE); + vs_engid_to_pgname(engid, pgname); + (void) memset(&prop_hd, 0, sizeof (vs_prop_hd_t)); prop_hd.vp_type = VS_PTYPE_SE; prop_hd.vp_all = VS_PROPID_SE_ALL; prop_hd.vp_ids = propids | VS_PROPID_VALUE_AUTH; prop_hd.vp_se = *sep; - return (vs_scf_pg_create(engid, &prop_hd)); + /* if hostname not specified, default it to engid */ + if ((propids & VS_PROPID_SE_HOST) == 0) { + (void) strlcpy(prop_hd.vp_se.vep_host, engid, MAXHOSTNAMELEN); + prop_hd.vp_ids |= VS_PROPID_SE_HOST; + } + return (vs_scf_pg_create(pgname, &prop_hd)); } @@ -445,50 +460,15 @@ vs_props_se_create(char *engid, const vs_props_se_t *sep, uint64_t propids) int vs_props_se_delete(const char *engid) { - int rc; - vs_scfctx_t vsc; + char pgname[VS_PGNAME_ENGINE_LEN]; /* VS_PGNAME_GENERAL is a reserved for GENERAL property group */ if (strcmp(engid, VS_PGNAME_GENERAL) == 0) return (VS_ERR_INVALID_SE); - /* ensure that caller has authorization to refresh service */ - if ((rc = vs_checkauth(VS_ACTION_AUTH)) != VS_ERR_NONE) - return (rc); - - if (vs_scf_ctx_open(&vsc) != 0) { - vs_scf_ctx_close(&vsc); - return (VS_ERR_SCF); - } - - if (scf_instance_get_pg(vsc.vscf_inst, engid, vsc.vscf_pgroup) == -1) { - vs_scf_ctx_close(&vsc); - rc = scf_error(); - if ((rc == SCF_ERROR_NOT_FOUND) || - (rc == SCF_ERROR_INVALID_ARGUMENT)) - return (VS_ERR_INVALID_SE); - else - return (VS_ERR_SCF); - } - - if (scf_pg_delete(vsc.vscf_pgroup) == -1) { - vs_scf_ctx_close(&vsc); - rc = scf_error(); - if ((rc == SCF_ERROR_NOT_FOUND) || - (rc == SCF_ERROR_INVALID_ARGUMENT)) - return (VS_ERR_INVALID_SE); - - return (VS_ERR_SCF); - } - - vs_scf_ctx_close(&vsc); - - /* Notify the daemon that things have changed */ - if ((smf_refresh_instance(VS_INSTANCE_FMRI)) == -1) { - return (VS_ERR_SCF); - } + vs_engid_to_pgname(engid, pgname); - return (VS_ERR_NONE); + return (vs_scf_pg_delete(pgname)); } @@ -769,7 +749,6 @@ vs_scf_pg_create(const char *pgname, vs_prop_hd_t *prop_hd) { int rc; uint64_t propid; - uint64_t propids = prop_hd->vp_ids; vs_scfctx_t vsc; /* ensure that caller has authorization to refresh service */ @@ -795,23 +774,68 @@ vs_scf_pg_create(const char *pgname, vs_prop_hd_t *prop_hd) if ((propid & prop_hd->vp_all) && !(propid & prop_hd->vp_ids)) vs_default_value(prop_hd, propid); } - prop_hd->vp_ids = prop_hd->vp_all; - - - if ((propids & VS_PROPID_SE_HOST) == 0) - (void) strlcpy(prop_hd->vp_se.vep_host, pgname, MAXHOSTNAMELEN); + prop_hd->vp_ids = prop_hd->vp_all; prop_hd->vp_ids |= VS_PROPID_VALUE_AUTH; rc = vs_scf_values_set(pgname, prop_hd); if (rc != VS_ERR_NONE) - (void) vs_props_se_delete(pgname); + (void) vs_scf_pg_delete(pgname); return (rc); } /* + * vs_scf_pg_delete + */ +static int +vs_scf_pg_delete(const char *pgname) +{ + int rc; + vs_scfctx_t vsc; + + /* ensure that caller has authorization to refresh service */ + if ((rc = vs_checkauth(VS_ACTION_AUTH)) != VS_ERR_NONE) + return (rc); + + if (vs_scf_ctx_open(&vsc) != 0) { + vs_scf_ctx_close(&vsc); + return (VS_ERR_SCF); + } + + if (scf_instance_get_pg(vsc.vscf_inst, pgname, vsc.vscf_pgroup) == -1) { + vs_scf_ctx_close(&vsc); + rc = scf_error(); + if ((rc == SCF_ERROR_NOT_FOUND) || + (rc == SCF_ERROR_INVALID_ARGUMENT)) + return (VS_ERR_INVALID_SE); + else + return (VS_ERR_SCF); + } + + if (scf_pg_delete(vsc.vscf_pgroup) == -1) { + vs_scf_ctx_close(&vsc); + rc = scf_error(); + if ((rc == SCF_ERROR_NOT_FOUND) || + (rc == SCF_ERROR_INVALID_ARGUMENT)) + return (VS_ERR_INVALID_SE); + + return (VS_ERR_SCF); + } + + vs_scf_ctx_close(&vsc); + + /* Notify the daemon that things have changed */ + if ((smf_refresh_instance(VS_INSTANCE_FMRI)) == -1) { + return (VS_ERR_SCF); + } + + return (VS_ERR_NONE); +} + + +/* * vs_scf_values_set * * Sets property values in the repository. This is the single @@ -832,7 +856,6 @@ vs_scf_values_set(const char *pgname, vs_prop_hd_t *prop_hd) uint64_t propid; vs_scfctx_t vsc; - /* ensure that caller has authorization to refresh service */ if ((rc = vs_checkauth(VS_ACTION_AUTH)) != VS_ERR_NONE) return (rc); @@ -1321,41 +1344,44 @@ vs_statistics(vs_stats_t *stats) { int door_fd, rc = VS_ERR_NONE; vs_stats_req_t *req; - vs_stats_t *buf; + vs_stats_rsp_t *rsp; door_arg_t arg; if ((req = calloc(1, sizeof (vs_stats_req_t))) == NULL) return (VS_ERR_SYS); - if ((buf = calloc(1, sizeof (vs_stats_t))) == NULL) { + if ((rsp = calloc(1, sizeof (vs_stats_rsp_t))) == NULL) { free(req); return (VS_ERR_SYS); } if ((door_fd = open(VS_STATS_DOOR_NAME, O_RDONLY)) < 0) { free(req); - free(buf); + free(rsp); return (VS_ERR_DAEMON_COMM); } - *req = VS_STATS_GET; + req->vsr_magic = VS_STATS_DOOR_MAGIC; + req->vsr_id = VS_STATS_GET; arg.data_ptr = (char *)req; arg.data_size = sizeof (vs_stats_req_t); arg.desc_ptr = NULL; arg.desc_num = 0; - arg.rbuf = (char *)buf; - arg.rsize = sizeof (vs_stats_t); + arg.rbuf = (char *)rsp; + arg.rsize = sizeof (vs_stats_rsp_t); - if (door_call(door_fd, &arg) < 0) + if ((door_call(door_fd, &arg) < 0) || + (rsp->vsr_magic != VS_STATS_DOOR_MAGIC)) { rc = VS_ERR_DAEMON_COMM; - else - *stats = *buf; + } else { + *stats = rsp->vsr_stats; + } (void) close(door_fd); free(req); - free(buf); + free(rsp); return (rc); } @@ -1382,7 +1408,8 @@ vs_statistics_reset() return (VS_ERR_DAEMON_COMM); } - *req = VS_STATS_RESET; + req->vsr_magic = VS_STATS_DOOR_MAGIC; + req->vsr_id = VS_STATS_RESET; arg.data_ptr = (char *)req; arg.data_size = sizeof (vs_stats_req_t); @@ -1426,15 +1453,19 @@ vs_checkauth(char *auth) /* * vs_props_get_engines + * * On input, count specifies the maximum number of engine ids to * return. engids must be an array with count entries. * On return, count specifies the number of engine ids being * returned in engids. + * + * Caller is responsible for free'ing the engids allocated herein. */ static int -vs_props_get_engines(vs_engid_t *engids, int *count) +vs_props_get_engines(char *engids[], int *count) { - int i = 0; + int i, prefix_len; + char pgname[VS_PGNAME_ENGINE_LEN]; vs_scfctx_t vsc; @@ -1446,19 +1477,26 @@ vs_props_get_engines(vs_engid_t *engids, int *count) return (VS_ERR_SCF); } + for (i = 0; i < *count; i++) + engids[i] = NULL; + + i = 0; + prefix_len = sizeof (VS_PGNAME_ENGINE_PREFIX) - 1; + while ((i < VS_SE_MAX) && (scf_iter_next_pg(vsc.vscf_iter, vsc.vscf_pgroup) == 1)) { - if (scf_pg_get_name(vsc.vscf_pgroup, engids[i], - VS_SE_NAME_LEN) < 0) { + if (scf_pg_get_name(vsc.vscf_pgroup, pgname, + VS_PGNAME_ENGINE_LEN) < 0) { vs_scf_ctx_close(&vsc); return (VS_ERR_SCF); } - if (strcmp(engids[i], VS_PGNAME_GENERAL) == 0) - *engids[i] = 0; - else - if (++i == *count) - break; + if (strncmp(pgname, VS_PGNAME_ENGINE_PREFIX, prefix_len) == 0) { + if ((engids[i] = strdup(pgname + prefix_len)) != NULL) { + if (++i == *count) + break; + } + } } vs_scf_ctx_close(&vsc); @@ -1494,6 +1532,20 @@ vs_scf_pg_count(void) /* + * vs_engid_to_pgname + * + * To convert an engine id (engid) to a property group name (pgname), + * the engine id is prefixed with VS_PGNAME_ENGINE_PREFIX. + */ +static void +vs_engid_to_pgname(const char *engid, char pgname[VS_PGNAME_ENGINE_LEN]) +{ + (void) snprintf(pgname, VS_PGNAME_ENGINE_LEN, "%s%s", + VS_PGNAME_ENGINE_PREFIX, engid); +} + + +/* * vs_strtonum * * Converts a size string in the format into an integer. diff --git a/usr/src/lib/libvscan/common/libvscan.h b/usr/src/lib/libvscan/common/libvscan.h index 42de60a6a5..cb8121f16b 100644 --- a/usr/src/lib/libvscan/common/libvscan.h +++ b/usr/src/lib/libvscan/common/libvscan.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -99,11 +99,17 @@ extern "C" { /* statistics door interface */ #define VS_STATS_DOOR_NAME "/var/run/vscan_stats_door" #define VS_STATS_DOOR_VERSION 1 +#define VS_STATS_DOOR_MAGIC 0x56535354 /* VSST - VScanStats */ /* scan statistics door request type */ typedef enum { VS_STATS_GET, VS_STATS_RESET +} vs_stats_req_type_t; + +typedef struct vs_stats_req { + uint32_t vsr_magic; + vs_stats_req_type_t vsr_id; } vs_stats_req_t; typedef struct vs_stats { @@ -117,6 +123,13 @@ typedef struct vs_stats { } vss_eng[VS_SE_MAX]; } vs_stats_t; +typedef struct vs_stats_rsp { + uint32_t vsr_magic; + vs_stats_t vsr_stats; +} vs_stats_rsp_t; + + + /* * General service configuration properties */ diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile index 8de91acdcd..c5414a9557 100644 --- a/usr/src/pkgdefs/Makefile +++ b/usr/src/pkgdefs/Makefile @@ -418,6 +418,7 @@ COMMON_SUBDIRS= \ SUNWusb \ SUNWusbs \ SUNWusbu \ + SUNWvscankr \ SUNWvscanr \ SUNWvscanu \ SUNWxcu4 \ diff --git a/usr/src/pkgdefs/SUNWvscankr/Makefile b/usr/src/pkgdefs/SUNWvscankr/Makefile new file mode 100644 index 0000000000..d48aa623bc --- /dev/null +++ b/usr/src/pkgdefs/SUNWvscankr/Makefile @@ -0,0 +1,41 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SRC=../.. + +include ../Makefile.com + +DATAFILES += depend + +.KEEP_STATE: + +all: $(FILES) postinstall preremove + +install: all pkg + +include ../Makefile.targ diff --git a/usr/src/pkgdefs/SUNWvscankr/pkginfo.tmpl b/usr/src/pkgdefs/SUNWvscankr/pkginfo.tmpl new file mode 100644 index 0000000000..c22bed8bf1 --- /dev/null +++ b/usr/src/pkgdefs/SUNWvscankr/pkginfo.tmpl @@ -0,0 +1,45 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +PKG="SUNWvscankr" +NAME="Virus Scan Service Kernel (Root)" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="root" +MAXINST="1000" +CATEGORY="system" +DESC="Virus Scan Service Kernel Root Components" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none" +BASEDIR=/ +SUNW_PKGVERS="1.0" +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="true" +SUNW_PKG_THISZONE="false" diff --git a/usr/src/pkgdefs/SUNWvscankr/postinstall b/usr/src/pkgdefs/SUNWvscankr/postinstall new file mode 100644 index 0000000000..49b07dbaf0 --- /dev/null +++ b/usr/src/pkgdefs/SUNWvscankr/postinstall @@ -0,0 +1,61 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PATH="/usr/bin:/usr/sbin:${PATH}" +export PATH + +PKG_NAME=SUNWvscankr +DRV=vscan +DRVPERM='* 0640 root sys' + +ADD_DRV=/usr/sbin/add_drv + +# +# Check if the BASEDIR option is needed +# +if [ "${BASEDIR}" = "/" ]; then + ADD_DRV_FLAGS="" + NAME_TO_MAJOR="/etc/name_to_major" +else + ADD_DRV_FLAGS="-b ${BASEDIR}" + NAME_TO_MAJOR="${BASEDIR}/etc/name_to_major" +fi + +# +# Make sure add_drv has not been previously executed before attempting +# to add the driver +# +grep "^${DRV} " ${NAME_TO_MAJOR} > /dev/null 2>&1 +if [ $? -ne 0 ]; then + ${ADD_DRV} ${ADD_DRV_FLAGS} -m "${DRVPERM}" ${DRV} + if [ $? -ne 0 ]; then + echo "${PKG_NAME}: add_drv ${DRV} failed\n" >&2 + exit 1 + fi +fi + +exit 0 diff --git a/usr/src/pkgdefs/SUNWvscankr/preremove b/usr/src/pkgdefs/SUNWvscankr/preremove new file mode 100644 index 0000000000..47ff0c8245 --- /dev/null +++ b/usr/src/pkgdefs/SUNWvscankr/preremove @@ -0,0 +1,68 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PATH="/usr/bin:/usr/sbin:${PATH}" +export PATH + +PKG_NAME=SUNWvscankr +DRV=vscan +DMON=vscand + +# +# Make sure that the VSCAN client daemon (vscand) is not +# running before removing this package. +# +if [ "${PKG_INSTALL_ROOT:-/}" = "/" ]; then + /usr/bin/pgrep -x "${DMON}" >/dev/null + if [ $? -ne 1 ]; then + echo "\nUnable to remove ${PKG_NAME}: the vscan service is enabled" + echo "Disable vscan service and try again:" + echo "\tsvcadm disable system/filesystem/vscan\n" + exit 1 + fi +fi + +# +# Remove driver, if this has not already been done +# +BASEDIR=${BASEDIR:-/} +if [ "$BASEDIR" = "/" ]; then + REM_DRV="rem_drv" +else + REM_DRV="rem_drv -b ${BASEDIR}" +fi + +grep "^${DRV}" ${BASEDIR}/etc/name_to_major > /dev/null 2>&1 +if [ $? -eq 0 ]; then + ${REM_DRV} ${DRV} + if [ $? -ne 0 ]; then + echo "${PKG_NAME}: rem_drv ${DRV} failed\n" >&2 + exit 1 + fi +fi + +exit 0 diff --git a/usr/src/pkgdefs/SUNWvscankr/prototype_com b/usr/src/pkgdefs/SUNWvscankr/prototype_com new file mode 100644 index 0000000000..9150cc750a --- /dev/null +++ b/usr/src/pkgdefs/SUNWvscankr/prototype_com @@ -0,0 +1,41 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# packaging files +i copyright +i depend +i pkginfo +i postinstall +i preremove + +# +# SUNWvscankr +# +d none kernel 0755 root sys +d none kernel/drv 0755 root sys +f none kernel/drv/vscan.conf 0644 root sys diff --git a/usr/src/pkgdefs/SUNWvscankr/prototype_i386 b/usr/src/pkgdefs/SUNWvscankr/prototype_i386 new file mode 100644 index 0000000000..111191565b --- /dev/null +++ b/usr/src/pkgdefs/SUNWvscankr/prototype_i386 @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +!include prototype_com +# +# SUNWvscankr +# +f none kernel/drv/vscan 0755 root sys +d none kernel/drv/amd64 0755 root sys +f none kernel/drv/amd64/vscan 0755 root sys diff --git a/usr/src/pkgdefs/SUNWvscankr/prototype_sparc b/usr/src/pkgdefs/SUNWvscankr/prototype_sparc new file mode 100644 index 0000000000..bec40b506d --- /dev/null +++ b/usr/src/pkgdefs/SUNWvscankr/prototype_sparc @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +!include prototype_com +# +# SUNWvscankr +# +d none kernel/drv/sparcv9 0755 root sys +f none kernel/drv/sparcv9/vscan 0755 root sys diff --git a/usr/src/pkgdefs/SUNWvscanr/Makefile b/usr/src/pkgdefs/SUNWvscanr/Makefile index 251db0af11..7c6dca3c1d 100644 --- a/usr/src/pkgdefs/SUNWvscanr/Makefile +++ b/usr/src/pkgdefs/SUNWvscanr/Makefile @@ -20,7 +20,7 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -34,7 +34,7 @@ DATAFILES += i.manifest r.manifest .KEEP_STATE: -all: $(FILES) depend postinstall preremove +all: $(FILES) depend preremove install: all pkg diff --git a/usr/src/pkgdefs/SUNWvscanr/depend b/usr/src/pkgdefs/SUNWvscanr/depend index 89a5efbdaf..a8ae81714c 100644 --- a/usr/src/pkgdefs/SUNWvscanr/depend +++ b/usr/src/pkgdefs/SUNWvscanr/depend @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -51,3 +51,4 @@ P SUNWckr Core Solaris Kernel (Root) P SUNWcsu Core Solaris, (Usr) P SUNWcsd Core Solaris Devices P SUNWcsl Core Solaris Libraries +P SUNWvscankr Virus Scan Service Kernel (Root) diff --git a/usr/src/pkgdefs/SUNWvscanr/pkginfo.tmpl b/usr/src/pkgdefs/SUNWvscanr/pkginfo.tmpl index e99243e52d..267323f468 100644 --- a/usr/src/pkgdefs/SUNWvscanr/pkginfo.tmpl +++ b/usr/src/pkgdefs/SUNWvscanr/pkginfo.tmpl @@ -21,7 +21,7 @@ # #ident "%Z%%M% %I% %E% SMI" # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # PKG="SUNWvscanr" @@ -41,5 +41,5 @@ CLASSES="none manifest" BASEDIR=/ SUNW_PKGVERS="1.0" SUNW_PKG_ALLZONES="true" -SUNW_PKG_HOLLOW="true" +SUNW_PKG_HOLLOW="false" SUNW_PKG_THISZONE="false" diff --git a/usr/src/pkgdefs/SUNWvscanr/preremove b/usr/src/pkgdefs/SUNWvscanr/preremove index bd870496e9..1e009d8d29 100644 --- a/usr/src/pkgdefs/SUNWvscanr/preremove +++ b/usr/src/pkgdefs/SUNWvscanr/preremove @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -29,7 +29,6 @@ PATH="/usr/bin:/usr/sbin:${PATH}" export PATH PKG_NAME=SUNWvscanr -DRV=vscan DMON=vscand # @@ -46,23 +45,4 @@ if [ "${PKG_INSTALL_ROOT:-/}" = "/" ]; then fi fi -# -# Remove driver, if this has not already been done -# -BASEDIR=${BASEDIR:-/} -if [ "$BASEDIR" = "/" ]; then - REM_DRV="rem_drv" -else - REM_DRV="rem_drv -b ${BASEDIR}" -fi - -grep "^${DRV}" ${BASEDIR}/etc/name_to_major > /dev/null 2>&1 -if [ $? -eq 0 ]; then - ${REM_DRV} ${DRV} - if [ $? -ne 0 ]; then - echo "${PKG_NAME}: rem_drv ${DRV} failed\n" >&2 - exit 1 - fi -fi - exit 0 diff --git a/usr/src/pkgdefs/SUNWvscanr/prototype_com b/usr/src/pkgdefs/SUNWvscanr/prototype_com index b6083181ab..db0451b2e7 100644 --- a/usr/src/pkgdefs/SUNWvscanr/prototype_com +++ b/usr/src/pkgdefs/SUNWvscanr/prototype_com @@ -22,7 +22,7 @@ # #ident "%Z%%M% %I% %E% SMI" # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -32,15 +32,11 @@ i depend i pkginfo i i.manifest i r.manifest -i postinstall i preremove # # SUNWvscanr # -d none kernel 0755 root sys -d none kernel/drv 0755 root sys -f none kernel/drv/vscan.conf 0644 root sys d none var 0755 root sys d none var/svc 0755 root sys d none var/svc/manifest 0755 root sys diff --git a/usr/src/pkgdefs/SUNWvscanr/prototype_i386 b/usr/src/pkgdefs/SUNWvscanr/prototype_i386 index 00fb40069f..5c7ab6ea86 100644 --- a/usr/src/pkgdefs/SUNWvscanr/prototype_i386 +++ b/usr/src/pkgdefs/SUNWvscanr/prototype_i386 @@ -20,16 +20,10 @@ # # -#pragma ident "%Z%%M% %I% %E% SMI" +# ident "%Z%%M% %I% %E% SMI" # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # !include prototype_com -# -# SUNWvscanr -# -f none kernel/drv/vscan 0755 root sys -d none kernel/drv/amd64 0755 root sys -f none kernel/drv/amd64/vscan 0755 root sys diff --git a/usr/src/pkgdefs/SUNWvscanr/prototype_sparc b/usr/src/pkgdefs/SUNWvscanr/prototype_sparc index 1a1978e8ab..5c7ab6ea86 100644 --- a/usr/src/pkgdefs/SUNWvscanr/prototype_sparc +++ b/usr/src/pkgdefs/SUNWvscanr/prototype_sparc @@ -20,15 +20,10 @@ # # -#pragma ident "%Z%%M% %I% %E% SMI" +# ident "%Z%%M% %I% %E% SMI" # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # !include prototype_com -# -# SUNWvscanr -# -d none kernel/drv/sparcv9 0755 root sys -f none kernel/drv/sparcv9/vscan 0755 root sys diff --git a/usr/src/pkgdefs/SUNWvscanu/depend b/usr/src/pkgdefs/SUNWvscanu/depend index 647e83abf8..ad3fc529f2 100644 --- a/usr/src/pkgdefs/SUNWvscanu/depend +++ b/usr/src/pkgdefs/SUNWvscanu/depend @@ -52,4 +52,5 @@ P SUNWcsu Core Solaris, (Usr) P SUNWcsd Core Solaris Devices P SUNWcsl Core Solaris Libraries P SUNWlibmsr Math & Microtasking Libraries (Root) +P SUNWvscankr Virus Scan Service Kernel (Root) P SUNWvscanr Virus Scan Service (Root) diff --git a/usr/src/pkgdefs/SUNWvscanu/prototype_com b/usr/src/pkgdefs/SUNWvscanu/prototype_com index 2051b67b47..334e853b1c 100644 --- a/usr/src/pkgdefs/SUNWvscanu/prototype_com +++ b/usr/src/pkgdefs/SUNWvscanu/prototype_com @@ -20,7 +20,7 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -37,6 +37,9 @@ i preremove # d none usr 755 root sys d none usr/lib 755 root bin +d none usr/lib/devfsadm 755 root sys +d none usr/lib/devfsadm/linkmod 755 root sys +f none usr/lib/devfsadm/linkmod/SUNW_vscan_link.so 755 root sys d none usr/lib/vscan 755 root bin f none usr/lib/vscan/libvscan.so.1 755 root bin s none usr/lib/vscan/libvscan.so=libvscan.so.1 diff --git a/usr/src/pkgdefs/SUNWvscanu/prototype_i386 b/usr/src/pkgdefs/SUNWvscanu/prototype_i386 index 248ca0b5e4..5c7ab6ea86 100644 --- a/usr/src/pkgdefs/SUNWvscanu/prototype_i386 +++ b/usr/src/pkgdefs/SUNWvscanu/prototype_i386 @@ -20,9 +20,9 @@ # # -#pragma ident "%Z%%M% %I% %E% SMI" +# ident "%Z%%M% %I% %E% SMI" # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # diff --git a/usr/src/pkgdefs/SUNWvscanu/prototype_sparc b/usr/src/pkgdefs/SUNWvscanu/prototype_sparc index 248ca0b5e4..5c7ab6ea86 100644 --- a/usr/src/pkgdefs/SUNWvscanu/prototype_sparc +++ b/usr/src/pkgdefs/SUNWvscanu/prototype_sparc @@ -20,9 +20,9 @@ # # -#pragma ident "%Z%%M% %I% %E% SMI" +# ident "%Z%%M% %I% %E% SMI" # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # diff --git a/usr/src/uts/common/io/vscan/vscan_door.c b/usr/src/uts/common/io/vscan/vscan_door.c index 69f17809fe..737e00245a 100644 --- a/usr/src/uts/common/io/vscan/vscan_door.c +++ b/usr/src/uts/common/io/vscan/vscan_door.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -36,7 +36,10 @@ #include <sys/vscan.h> -static int vscan_door_id = -1; +/* max time (secs) to wait for door calls to complete during door_close */ +#define VS_DOOR_CLOSE_TIMEOUT_DEFAULT 30 +uint32_t vs_door_close_timeout = VS_DOOR_CLOSE_TIMEOUT_DEFAULT; + static door_handle_t vscan_door_handle = NULL; static kmutex_t vscan_door_mutex; static kcondvar_t vscan_door_cv; @@ -74,19 +77,17 @@ vscan_door_open(int door_id) { mutex_enter(&vscan_door_mutex); - if (vscan_door_handle == NULL) { - vscan_door_id = door_id; + if (vscan_door_handle == NULL) vscan_door_handle = door_ki_lookup(door_id); - } - - mutex_exit(&vscan_door_mutex); if (vscan_door_handle == NULL) { cmn_err(CE_WARN, "Internal communication error " "- failed to access vscan service daemon."); + mutex_exit(&vscan_door_mutex); return (-1); } + mutex_exit(&vscan_door_mutex); return (0); } @@ -97,13 +98,21 @@ vscan_door_open(int door_id) void vscan_door_close(void) { + clock_t timeout, time_left; + mutex_enter(&vscan_door_mutex); /* wait for any in-progress requests to complete */ - while (vscan_door_call_count > 0) { - cv_wait(&vscan_door_cv, &vscan_door_mutex); + time_left = SEC_TO_TICK(vs_door_close_timeout); + while ((vscan_door_call_count > 0) && (time_left > 0)) { + timeout = lbolt + time_left; + time_left = cv_timedwait(&vscan_door_cv, + &vscan_door_mutex, timeout); } + if (time_left == -1) + cmn_err(CE_WARN, "Timeout waiting for door calls to complete"); + if (vscan_door_handle) { door_ki_rele(vscan_door_handle); vscan_door_handle = NULL; @@ -115,16 +124,18 @@ vscan_door_close(void) /* * vscan_door_scan_file + * + * Returns: result returned in door response or VS_STATUS_ERROR */ int vscan_door_scan_file(vs_scan_req_t *scan_req) { - int err, rc = 0; + int err; door_arg_t arg; + uint32_t result = 0; - if (!vscan_door_handle && - vscan_door_open(vscan_door_id) != 0) - return (-1); + if (!vscan_door_handle) + return (VS_STATUS_ERROR); mutex_enter(&vscan_door_mutex); vscan_door_call_count++; @@ -134,14 +145,13 @@ vscan_door_scan_file(vs_scan_req_t *scan_req) arg.data_size = sizeof (vs_scan_req_t); arg.desc_ptr = NULL; arg.desc_num = 0; - arg.rbuf = (char *)scan_req; - arg.rsize = sizeof (vs_scan_req_t); + arg.rbuf = (char *)&result; + arg.rsize = sizeof (uint32_t); if ((err = door_ki_upcall(vscan_door_handle, &arg)) != 0) { cmn_err(CE_WARN, "Internal communication error (%d)" "- failed to send scan request to vscand", err); - vscan_door_close(); - rc = -1; + result = VS_STATUS_ERROR; } mutex_enter(&vscan_door_mutex); @@ -149,5 +159,5 @@ vscan_door_scan_file(vs_scan_req_t *scan_req) cv_signal(&vscan_door_cv); mutex_exit(&vscan_door_mutex); - return (rc); + return (result); } diff --git a/usr/src/uts/common/io/vscan/vscan_drv.c b/usr/src/uts/common/io/vscan/vscan_drv.c index 37b150cf59..5d2cdb851d 100644 --- a/usr/src/uts/common/io/vscan/vscan_drv.c +++ b/usr/src/uts/common/io/vscan/vscan_drv.c @@ -42,28 +42,109 @@ #include <sys/policy.h> #include <sys/sdt.h> -#define VS_DRV_NODENAME_LEN 16 +/* seconds to wait for daemon to reconnect before disabling */ +#define VS_DAEMON_WAIT_SEC 60 + +/* length of minor node name - vscan%d */ +#define VS_NODENAME_LEN 16 + +/* global variables - tunable via /etc/system */ +uint32_t vs_reconnect_timeout = VS_DAEMON_WAIT_SEC; +extern uint32_t vs_nodes_max; /* max in-progress scan requests */ /* - * Instance States: VS_INIT (initial state), VS_OPEN, VS_READING + * vscan_drv_state + * + * Operations on instance 0 represent vscand initiated state + * transition events: + * open(0) - vscand connect + * close(0) - vscan disconnect + * enable(0) - vscand enable (ready to hand requests) + * disable(0) - vscand disable (shutting down) + * + * +------------------------+ + * | VS_DRV_UNCONFIG | + * +------------------------+ + * | ^ + * | attach | detach + * v | + * +------------------------+ + * | VS_DRV_IDLE |<------| + * +------------------------+ | + * | ^ | + * | open(0) | close(0) | + * v | | + * +------------------------+ | + * | VS_DRV_CONNECTED |<-| | + * +------------------------+ | | + * | ^ | | + * | enable(0) | disable(0) | | + * v | | | + * +------------------------+ | | + * | VS_DRV_ENABLED | | | + * +------------------------+ | | + * | | | + * | close(0) open(0) | + * v | | + * +------------------------+ | | timeout + * | VS_DRV_DELAYED_DISABLE | -- | + * +------------------------+ ------| * - * Instance 0 controls the state of the driver: vscan_drv_connected. - * vscan_drv_state[0] should NOT be used. - * Actions: - * open: VS_INIT->VS_OPEN, otherwise ERROR - * close: any->VS_INIT - * read: VS_OPEN->VS_READING, otherwise ERROR */ typedef enum { - VS_INIT, - VS_OPEN, - VS_READING + VS_DRV_UNCONFIG, + VS_DRV_IDLE, + VS_DRV_CONNECTED, + VS_DRV_ENABLED, + VS_DRV_DELAYED_DISABLE } vscan_drv_state_t; +static vscan_drv_state_t vscan_drv_state = VS_DRV_UNCONFIG; -static vscan_drv_state_t vscan_drv_state[VS_DRV_MAX_FILES + 1]; -static boolean_t vscan_drv_nodes[VS_DRV_MAX_FILES + 1]; -static boolean_t vscan_drv_connected = B_FALSE; /* vscand daemon connected */ + +/* + * vscan_drv_inst_state + * + * Instance 0 controls the state of the driver: vscan_drv_state. + * vscan_drv_inst_state[0] should NOT be used. + * + * vscan_drv_inst_state[n] represents the state of driver + * instance n, used by vscand to access file data for the + * scan request with index n in vscan_svc_reqs. + * Minor nodes are created as required then all are destroyed + * during driver detach. + * + * +------------------------+ + * | VS_DRV_INST_UNCONFIG | + * +------------------------+ + * | ^ + * | create_node(n) | detach + * v | + * +------------------------+ + * | VS_DRV_INST_INIT |<-| + * +------------------------+ | + * | | + * | open(n) | + * v | + * +------------------------+ | + * | VS_DRV_INST_OPEN |--| + * +------------------------+ | + * | | + * | read(n) | + * v | close(n) + * +------------------------+ | + * | VS_DRV_INST_READING |--| + * +------------------------+ + */ +typedef enum { + VS_DRV_INST_UNCONFIG = 0, /* minor node not created */ + VS_DRV_INST_INIT, + VS_DRV_INST_OPEN, + VS_DRV_INST_READING +} vscan_drv_inst_state_t; + +static vscan_drv_inst_state_t *vscan_drv_inst_state; +static int vscan_drv_inst_state_sz; static dev_info_t *vscan_drv_dip; static kmutex_t vscan_drv_mutex; @@ -87,7 +168,6 @@ static void vscan_drv_delayed_disable(void); /* * module linkage info for the kernel */ - static struct cb_ops cbops = { vscan_drv_open, /* cb_open */ vscan_drv_close, /* cb_close */ @@ -145,29 +225,30 @@ _init(void) { int rc; - mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL); + vscan_drv_inst_state_sz = + sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1); - if (vscan_door_init() != 0) { - mutex_destroy(&vscan_drv_mutex); + if (vscan_door_init() != 0) return (DDI_FAILURE); - } if (vscan_svc_init() != 0) { vscan_door_fini(); - mutex_destroy(&vscan_drv_mutex); return (DDI_FAILURE); } - (void) memset(&vscan_drv_state, 0, sizeof (vscan_drv_state)); - (void) memset(&vscan_drv_nodes, 0, sizeof (vscan_drv_nodes)); + mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL); + vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP); + + cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL); if ((rc = mod_install(&modlinkage)) != 0) { vscan_door_fini(); vscan_svc_fini(); + kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz); + cv_destroy(&vscan_drv_cv); mutex_destroy(&vscan_drv_mutex); } - cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL); return (rc); } @@ -196,6 +277,7 @@ _fini(void) if ((rc = mod_remove(&modlinkage)) == 0) { vscan_door_fini(); vscan_svc_fini(); + kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz); cv_destroy(&vscan_drv_cv); mutex_destroy(&vscan_drv_mutex); } @@ -247,6 +329,7 @@ vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) if (vscan_drv_create_node(0) == B_FALSE) return (DDI_FAILURE); + vscan_drv_state = VS_DRV_IDLE; return (DDI_SUCCESS); } @@ -257,6 +340,8 @@ vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) static int vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { + int i; + if (cmd != DDI_DETACH) return (DDI_FAILURE); @@ -269,8 +354,10 @@ vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) /* remove all minor nodes */ vscan_drv_dip = NULL; ddi_remove_minor_node(dip, NULL); - (void) memset(&vscan_drv_nodes, 0, sizeof (vscan_drv_nodes)); + for (i = 0; i <= vs_nodes_max; i++) + vscan_drv_inst_state[i] = VS_DRV_INST_UNCONFIG; + vscan_drv_state = VS_DRV_UNCONFIG; return (DDI_SUCCESS); } @@ -278,35 +365,40 @@ vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) /* * vscan_drv_in_use * - * If vscand is connected (vscan_drv_connected == B_TRUE) the - * vscan driver is obviously in use. Otherwise invoke - * vscan_svc_in_use() to determine if the driver is in use, - * even though the daemon has disconnected. - * For example, there may be requests not yet complete, or - * the driver may still be enabled waiting for the daemon to - * reconnect. - * Used to determine whether the driver can be unloaded. + * If the driver state is not IDLE or UNCONFIG then the + * driver is in use. Otherwise, check the service interface + * (vscan_svc) to see if it is still in use - for example + * there there may be requests still in progress. */ static boolean_t vscan_drv_in_use() { - boolean_t in_use; + boolean_t in_use = B_FALSE; mutex_enter(&vscan_drv_mutex); - in_use = vscan_drv_connected; + if ((vscan_drv_state != VS_DRV_IDLE) && + (vscan_drv_state != VS_DRV_UNCONFIG)) { + in_use = B_TRUE; + } mutex_exit(&vscan_drv_mutex); - if (in_use == B_FALSE) - in_use = vscan_svc_in_use(); - - return (in_use); + if (in_use) + return (B_TRUE); + else + return (vscan_svc_in_use()); } /* * vscan_drv_open - * if inst == 0, this is vscand initializing. - * Otherwise, open the file associated with inst. + * + * If inst == 0, this is vscand initializing. + * If the driver is in DELAYED_DISABLE, ie vscand previously + * disconnected without a clean shutdown and the driver is + * waiting for a period to allow vscand to reconnect, signal + * vscan_drv_cv to cancel the delayed disable. + * + * If inst != 0, open the file associated with inst. */ /* ARGSUSED */ static int @@ -315,7 +407,7 @@ vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp) int rc; int inst = getminor(*devp); - if ((inst < 0) || (inst > VS_DRV_MAX_FILES)) + if ((inst < 0) || (inst > vs_nodes_max)) return (EINVAL); /* check if caller has privilege for virus scanning */ @@ -326,20 +418,27 @@ vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp) mutex_enter(&vscan_drv_mutex); if (inst == 0) { - if (vscan_drv_connected) { + switch (vscan_drv_state) { + case VS_DRV_IDLE: + vscan_drv_state = VS_DRV_CONNECTED; + break; + case VS_DRV_DELAYED_DISABLE: + cv_signal(&vscan_drv_cv); + vscan_drv_state = VS_DRV_CONNECTED; + break; + default: + DTRACE_PROBE1(vscan__drv__state__violation, + int, vscan_drv_state); mutex_exit(&vscan_drv_mutex); return (EINVAL); } - vscan_drv_connected = B_TRUE; - /* wake any pending delayed disable */ - cv_signal(&vscan_drv_cv); } else { - if ((!vscan_drv_connected) || - (vscan_drv_state[inst] != VS_INIT)) { - mutex_exit(&vscan_drv_mutex); - return (EINVAL); + if ((vscan_drv_state != VS_DRV_ENABLED) || + (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) { + mutex_exit(&vscan_drv_mutex); + return (EINVAL); } - vscan_drv_state[inst] = VS_OPEN; + vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN; } mutex_exit(&vscan_drv_mutex); @@ -349,8 +448,14 @@ vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp) /* * vscan_drv_close - * if inst == 0, this is vscand detaching - * Otherwise close the file associated with inst + * + * If inst == 0, this is vscand detaching. + * If the driver is in ENABLED state vscand has terminated without + * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE + * state and initiate a delayed disable to allow vscand time to + * reconnect. + * + * If inst != 0, close the file associated with inst */ /* ARGSUSED */ static int @@ -358,27 +463,46 @@ vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp) { int i, inst = getminor(dev); - if ((inst < 0) || (inst > VS_DRV_MAX_FILES)) + if ((inst < 0) || (inst > vs_nodes_max)) return (EINVAL); mutex_enter(&vscan_drv_mutex); - if (inst == 0) { - for (i = 1; i <= VS_DRV_MAX_FILES; i++) - vscan_drv_state[i] = VS_INIT; - - vscan_drv_connected = B_FALSE; - if (vscan_svc_is_enabled()) { - if (thread_create(NULL, 0, vscan_drv_delayed_disable, - 0, 0, &p0, TS_RUN, minclsyspri) == NULL) { - vscan_svc_enable(); - } + if (inst != 0) { + vscan_drv_inst_state[inst] = VS_DRV_INST_INIT; + mutex_exit(&vscan_drv_mutex); + return (0); + } + + /* instance 0 - daemon disconnect */ + if ((vscan_drv_state != VS_DRV_CONNECTED) && + (vscan_drv_state != VS_DRV_ENABLED)) { + DTRACE_PROBE1(vscan__drv__state__violation, + int, vscan_drv_state); + mutex_exit(&vscan_drv_mutex); + return (EINVAL); + } + + for (i = 1; i <= vs_nodes_max; i++) { + if (vscan_drv_inst_state[i] != VS_DRV_INST_UNCONFIG) + vscan_drv_inst_state[i] = VS_DRV_INST_INIT; + } + + if (vscan_drv_state == VS_DRV_CONNECTED) { + vscan_drv_state = VS_DRV_IDLE; + } else { /* VS_DRV_ENABLED */ + cmn_err(CE_WARN, "Detected vscand exit without clean shutdown"); + if (thread_create(NULL, 0, vscan_drv_delayed_disable, + 0, 0, &p0, TS_RUN, minclsyspri) == NULL) { + vscan_svc_disable(); + vscan_drv_state = VS_DRV_IDLE; + } else { + vscan_drv_state = VS_DRV_DELAYED_DISABLE; } - vscan_door_close(); - } else { - vscan_drv_state[inst] = VS_INIT; } mutex_exit(&vscan_drv_mutex); + vscan_svc_scan_abort(); + vscan_door_close(); return (0); } @@ -388,22 +512,23 @@ vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp) * * Invoked from vscan_drv_close if the daemon disconnects * without first sending disable (e.g. daemon crashed). - * Delays for VS_DAEMON_WAIT_SEC before disabling, to allow + * Delays for vs_reconnect_timeout before disabling, to allow * the daemon to reconnect. During this time, scan requests * will be processed locally (see vscan_svc.c) */ static void vscan_drv_delayed_disable(void) { - clock_t timeout = lbolt + SEC_TO_TICK(VS_DAEMON_WAIT_SEC); + clock_t timeout = lbolt + SEC_TO_TICK(vs_reconnect_timeout); mutex_enter(&vscan_drv_mutex); (void) cv_timedwait(&vscan_drv_cv, &vscan_drv_mutex, timeout); - if (vscan_drv_connected) { - DTRACE_PROBE(vscan__reconnect); - } else { + if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) { vscan_svc_disable(); + vscan_drv_state = VS_DRV_IDLE; + } else { + DTRACE_PROBE(vscan__reconnect); } mutex_exit(&vscan_drv_mutex); } @@ -420,15 +545,16 @@ vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp) int inst = getminor(dev); vnode_t *vp; - if ((inst <= 0) || (inst > VS_DRV_MAX_FILES)) + if ((inst <= 0) || (inst > vs_nodes_max)) return (EINVAL); mutex_enter(&vscan_drv_mutex); - if ((!vscan_drv_connected) || (vscan_drv_state[inst] != VS_OPEN)) { + if ((vscan_drv_state != VS_DRV_ENABLED) || + (vscan_drv_inst_state[inst] != VS_DRV_INST_OPEN)) { mutex_exit(&vscan_drv_mutex); return (EINVAL); } - vscan_drv_state[inst] = VS_READING; + vscan_drv_inst_state[inst] = VS_DRV_INST_READING; mutex_exit(&vscan_drv_mutex); if ((vp = vscan_svc_get_vnode(inst)) == NULL) @@ -439,8 +565,8 @@ vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp) VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); mutex_enter(&vscan_drv_mutex); - if (vscan_drv_state[inst] == VS_READING) - vscan_drv_state[inst] = VS_OPEN; + if (vscan_drv_inst_state[inst] == VS_DRV_INST_READING) + vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN; mutex_exit(&vscan_drv_mutex); return (rc); @@ -449,6 +575,15 @@ vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp) /* * vscan_drv_ioctl + * + * Process ioctls from vscand: + * VS_IOCTL_ENABLE - vscand is ready to handle scan requests, + * enable VFS interface. + * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface + * VS_IOCTL_RESULT - scan response data + * VS_IOCTL_CONFIG - configuration data from vscand + * VS_IOCTL_MAX_REQ - provide the max request idx to vscand, + * to allow vscand to set appropriate resource allocation limits */ /* ARGSUSED */ static int @@ -457,31 +592,64 @@ vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, { int inst = getminor(dev); vs_config_t conf; + vs_scan_rsp_t rsp; if (inst != 0) return (EINVAL); switch (cmd) { - case VS_DRV_IOCTL_ENABLE: + case VS_IOCTL_ENABLE: mutex_enter(&vscan_drv_mutex); - if ((!vscan_drv_connected) || - (vscan_door_open((int)arg) != 0)) { + if (vscan_drv_state != VS_DRV_CONNECTED) { + DTRACE_PROBE1(vscan__drv__state__violation, + int, vscan_drv_state); mutex_exit(&vscan_drv_mutex); return (EINVAL); } - vscan_svc_enable(); + if ((vscan_door_open((int)arg) != 0) || + (vscan_svc_enable() != 0)) { + mutex_exit(&vscan_drv_mutex); + return (EINVAL); + } + vscan_drv_state = VS_DRV_ENABLED; mutex_exit(&vscan_drv_mutex); break; - case VS_DRV_IOCTL_DISABLE: + + case VS_IOCTL_DISABLE: + mutex_enter(&vscan_drv_mutex); + if (vscan_drv_state != VS_DRV_ENABLED) { + DTRACE_PROBE1(vscan__drv__state__violation, + int, vscan_drv_state); + mutex_exit(&vscan_drv_mutex); + return (EINVAL); + } vscan_svc_disable(); + vscan_drv_state = VS_DRV_CONNECTED; + mutex_exit(&vscan_drv_mutex); break; - case VS_DRV_IOCTL_CONFIG: + + case VS_IOCTL_RESULT: + if (ddi_copyin((void *)arg, &rsp, + sizeof (vs_scan_rsp_t), 0) == -1) + return (EFAULT); + else + vscan_svc_scan_result(&rsp); + break; + + case VS_IOCTL_CONFIG: if (ddi_copyin((void *)arg, &conf, sizeof (vs_config_t), 0) == -1) return (EFAULT); if (vscan_svc_configure(&conf) == -1) return (EINVAL); break; + + case VS_IOCTL_MAX_REQ: + if (ddi_copyout(&vs_nodes_max, (void *)arg, + sizeof (uint32_t), 0) == -1) + return (EFAULT); + break; + default: return (ENOTTY); } @@ -503,22 +671,22 @@ vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, boolean_t vscan_drv_create_node(int idx) { - char name[VS_DRV_NODENAME_LEN]; - boolean_t *pnode, rc; + char name[VS_NODENAME_LEN]; + boolean_t rc = B_TRUE; mutex_enter(&vscan_drv_mutex); - pnode = &vscan_drv_nodes[idx]; - if (*pnode == B_FALSE) { - (void) snprintf(name, VS_DRV_NODENAME_LEN, "vscan%d", idx); + if (vscan_drv_inst_state[idx] == VS_DRV_INST_UNCONFIG) { + (void) snprintf(name, VS_NODENAME_LEN, "vscan%d", idx); if (ddi_create_minor_node(vscan_drv_dip, name, S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) { - *pnode = B_TRUE; + vscan_drv_inst_state[idx] = VS_DRV_INST_INIT; + } else { + rc = B_FALSE; } - DTRACE_PROBE2(vscan__minor__node, int, idx, int, *pnode); + DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc); } - rc = *pnode; mutex_exit(&vscan_drv_mutex); return (rc); diff --git a/usr/src/uts/common/io/vscan/vscan_svc.c b/usr/src/uts/common/io/vscan/vscan_svc.c index aec070734f..bb07530321 100644 --- a/usr/src/uts/common/io/vscan/vscan_svc.c +++ b/usr/src/uts/common/io/vscan/vscan_svc.c @@ -41,19 +41,162 @@ #include <sys/disp.h> #include <sys/sdt.h> #include <sys/cred.h> +#include <sys/list.h> #include <sys/vscan.h> -#define VS_TASKQ_NUM_THREADS VS_DRV_MAX_FILES +#define VS_REQ_MAGIC 0x52515354 /* 'RQST' */ + +#define VS_REQS_DEFAULT 20000 /* pending scan requests - reql */ +#define VS_NODES_DEFAULT 128 /* concurrent file scans */ +#define VS_WORKERS_DEFAULT 32 /* worker threads */ +#define VS_SCANWAIT_DEFAULT 15*60 /* seconds to wait for scan result */ +#define VS_REQL_HANDLER_TIMEOUT 30 #define VS_EXT_RECURSE_DEPTH 8 + +/* access derived from scan result (VS_STATUS_XXX) and file attributes */ +#define VS_ACCESS_UNDEFINED 0 +#define VS_ACCESS_ALLOW 1 /* return 0 */ +#define VS_ACCESS_DENY 2 /* return EACCES */ + #define tolower(C) (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C)) +#define offsetof(s, m) (size_t)(&(((s *)0)->m)) + +/* global variables - tunable via /etc/system */ +uint32_t vs_reqs_max = VS_REQS_DEFAULT; /* max scan requests */ +uint32_t vs_nodes_max = VS_NODES_DEFAULT; /* max in-progress scan requests */ +uint32_t vs_workers = VS_WORKERS_DEFAULT; /* max workers send reqs to vscand */ +uint32_t vs_scan_wait = VS_SCANWAIT_DEFAULT; /* secs to wait for scan result */ + + +/* + * vscan_svc_state + * + * +-----------------+ + * | VS_SVC_UNCONFIG | + * +-----------------+ + * | ^ + * | svc_init | svc_fini + * v | + * +-----------------+ + * | VS_SVC_IDLE |<----| + * +-----------------+ | + * | | + * | svc_enable | + * |<----------------| | + * v | | + * +-----------------+ | | + * | VS_SVC_ENABLED |--| | + * +-----------------+ | + * | | + * | svc_disable | handler thread exit, + * v | all requests complete + * +-----------------+ | + * | VS_SVC_DISABLED |-----| + * +-----------------+ + * + * svc_enable may occur when we are already in the ENABLED + * state if vscand has exited without clean shutdown and + * then reconnected within the delayed disable time period + * (vs_reconnect_timeout) - see vscan_drv + */ + +typedef enum { + VS_SVC_UNCONFIG, + VS_SVC_IDLE, + VS_SVC_ENABLED, /* service enabled and registered */ + VS_SVC_DISABLED /* service disabled and nunregistered */ +} vscan_svc_state_t; +static vscan_svc_state_t vscan_svc_state = VS_SVC_UNCONFIG; + -/* represents request received from filesystem - currently only use vp */ -typedef struct vscan_fs_req { +/* + * vscan_svc_req_state + * + * When a scan request is received from the file system it is + * identified in or inserted into the vscan_svc_reql (INIT). + * If the request is asynchronous 0 is then returned to the caller. + * If the request is synchronous the req's refcnt is incremented + * and the caller waits for the request to complete. + * The refcnt is also incremented when the request is inserted + * in vscan_svc_nodes, and decremented on scan_complete. + * + * vscan_svc_handler processes requests from the request list, + * inserting them into vscan_svc_nodes and the task queue (QUEUED). + * When the task queue call back (vscan_svc_do_scan) is invoked + * the request transitions to IN_PROGRESS state. If the request + * is sucessfully sent to vscand (door_call) and the door response + * is SCANNING then the scan result will be received asynchronously. + * Although unusual, it is possible that the async response is + * received before the door call returns (hence the ASYNC_COMPLETE + * state). + * When the result has been determined / received, + * vscan_svc_scan_complete is invoked to transition the request to + * COMPLETE state, decrement refcnt and signal all waiting callers. + * When the last waiting caller has processed the result (refcnt == 0) + * the request is removed from vscan_svc_reql and vscan_svc_nodes + * and deleted. + * + * | ^ + * | reql_insert | refcnt == 0 + * v | (delete) + * +------------------------+ +---------------------+ + * | VS_SVC_REQ_INIT | -----DISABLE----> | VS_SVC_REQ_COMPLETE | + * +------------------------+ +---------------------+ + * | ^ + * | insert_req, tq_dispatch | + * v | + * +------------------------+ | + * | VS_SVC_REQ_QUEUED | scan_complete + * +------------------------+ | + * | | + * | tq_callback (do_scan) | + * | | + * v scan not req'd, error, | + * +------------------------+ or door_result != SCANNING | + * | VS_SVC_REQ_IN_PROGRESS |----------------->-------------| + * +------------------------+ | + * | | | + * | | door_result == SCANNING | + * | v | + * | +---------------------------+ async result | + * | | VS_SVC_REQ_SCANNING |-------->---------| + * | +---------------------------+ | + * | | + * | async result | + * v | + * +---------------------------+ door_result = SCANNING | + * | VS_SVC_REQ_ASYNC_COMPLETE |-------->------------------| + * +---------------------------+ + */ +typedef enum { + VS_SVC_REQ_INIT, + VS_SVC_REQ_QUEUED, + VS_SVC_REQ_IN_PROGRESS, + VS_SVC_REQ_SCANNING, + VS_SVC_REQ_ASYNC_COMPLETE, + VS_SVC_REQ_COMPLETE +} vscan_svc_req_state_t; + + +/* + * vscan_svc_reql - the list of pending and in-progress scan requests + */ +typedef struct vscan_req { + uint32_t vsr_magic; /* VS_REQ_MAGIC */ + list_node_t vsr_lnode; vnode_t *vsr_vp; -} vscan_fs_req_t; + uint32_t vsr_idx; /* vscan_svc_nodes index */ + uint32_t vsr_seqnum; /* unigue request id */ + uint32_t vsr_refcnt; + kcondvar_t vsr_cv; + vscan_svc_req_state_t vsr_state; +} vscan_req_t; + +static list_t vscan_svc_reql; + /* - * vscan_svc_files - table of files being scanned + * vscan_svc_nodes - table of files being scanned * * The index into this table is passed in the door call to * vscand. vscand uses the idx to determine which minor node @@ -64,34 +207,42 @@ typedef struct vscan_fs_req { * Instance 0 is reserved for the daemon/driver control * interface: enable/configure/disable */ -typedef struct vscan_file { - vscan_fs_req_t vsf_req; - uint32_t vsf_wait_count; - kcondvar_t vsf_cv; /* wait for in progress scan */ - uint8_t vsf_quarantined; - uint8_t vsf_modified; - uint64_t vsf_size; - timestruc_t vsf_mtime; - vs_scanstamp_t vsf_scanstamp; - uint32_t vsf_result; - uint32_t vsf_access; -} vscan_file_t; - -static vscan_file_t vscan_svc_files[VS_DRV_MAX_FILES + 1]; -static kcondvar_t vscan_svc_cv; /* wait for slot in vscan_svc_files */ -static int vscan_svc_wait_count = 0; /* # waiting for slot in vscan_svc_files */ -static int vscan_svc_req_count = 0; /* # scan requests */ - +typedef struct vscan_svc_node { + vscan_req_t *vsn_req; + uint8_t vsn_quarantined; + uint8_t vsn_modified; + uint64_t vsn_size; + timestruc_t vsn_mtime; + vs_scanstamp_t vsn_scanstamp; + uint32_t vsn_result; + uint32_t vsn_access; +} vscan_svc_node_t; + +static vscan_svc_node_t *vscan_svc_nodes; +static int vscan_svc_nodes_sz; + + +/* vscan_svc_taskq - queue of requests waiting to be sent to vscand */ static taskq_t *vscan_svc_taskq = NULL; -static boolean_t vscan_svc_enabled = B_FALSE; + +/* counts of entries in vscan_svc_reql, vscan_svc_nodes & vscan_svc_taskq */ +typedef struct { + uint32_t vsc_reql; + uint32_t vsc_node; + uint32_t vsc_tq; +} vscan_svc_counts_t; +static vscan_svc_counts_t vscan_svc_counts; /* * vscan_svc_mutex protects the data pertaining to scan requests: - * file table - vscan_svc_files - * counts - vscan_svc_wait_count, vscan_svc_req_count + * request list - vscan_svc_reql + * node table - vscan_svc_nodes */ static kmutex_t vscan_svc_mutex; +/* unique request id for vscand request/response correlation */ +static uint32_t vscan_svc_seqnum = 0; + /* * vscan_svc_cfg_mutex protects the configuration data: * vscan_svc_config, vscan_svc_types @@ -102,24 +253,33 @@ static kmutex_t vscan_svc_cfg_mutex; static vs_config_t vscan_svc_config; static char *vscan_svc_types[VS_TYPES_MAX]; +/* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */ +static kthread_t *vscan_svc_reql_thread; +static kcondvar_t vscan_svc_reql_cv; +static vscan_req_t *vscan_svc_reql_next; /* next pending scan request */ + /* local functions */ int vscan_svc_scan_file(vnode_t *, cred_t *, int); -void vscan_svc_taskq_callback(void *); +static void vscan_svc_taskq_callback(void *); static int vscan_svc_exempt_file(vnode_t *, boolean_t *); static int vscan_svc_exempt_filetype(char *); static int vscan_svc_match_ext(char *, char *, int); -static int vscan_svc_do_scan(vscan_fs_req_t *); -static int vscan_svc_wait_for_scan(vnode_t *); -static int vscan_svc_insert_file(vscan_fs_req_t *); -static void vscan_svc_release_file(int); -static int vscan_svc_find_slot(void); +static void vscan_svc_do_scan(vscan_req_t *); +static vs_scan_req_t *vscan_svc_populate_req(int); static void vscan_svc_process_scan_result(int); -static void vscan_svc_notify_scan_complete(int); +static void vscan_svc_scan_complete(vscan_req_t *); +static void vscan_svc_delete_req(vscan_req_t *); +static int vscan_svc_insert_req(vscan_req_t *); +static void vscan_svc_remove_req(int); +static vscan_req_t *vscan_svc_reql_find(vnode_t *); +static vscan_req_t *vscan_svc_reql_insert(vnode_t *); +static void vscan_svc_reql_remove(vscan_req_t *); + static int vscan_svc_getattr(int); static int vscan_svc_setattr(int, int); -static vs_scan_req_t *vscan_svc_populate_req(int); -static void vscan_svc_parse_rsp(int, vs_scan_req_t *); +/* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */ +static void vscan_svc_reql_handler(void); /* @@ -128,115 +288,164 @@ static void vscan_svc_parse_rsp(int, vs_scan_req_t *); int vscan_svc_init() { - mutex_init(&vscan_svc_mutex, NULL, MUTEX_DRIVER, NULL); - mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DRIVER, NULL); - (void) memset(&vscan_svc_files, 0, sizeof (vscan_svc_files)); - cv_init(&vscan_svc_cv, NULL, CV_DEFAULT, NULL); + if (vscan_svc_state != VS_SVC_UNCONFIG) { + DTRACE_PROBE1(vscan__svc__state__violation, + int, vscan_svc_state); + return (-1); + } + + mutex_init(&vscan_svc_mutex, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DEFAULT, NULL); + cv_init(&vscan_svc_reql_cv, NULL, CV_DEFAULT, NULL); + + vscan_svc_nodes_sz = sizeof (vscan_svc_node_t) * (vs_nodes_max + 1); + vscan_svc_nodes = kmem_zalloc(vscan_svc_nodes_sz, KM_SLEEP); + + vscan_svc_counts.vsc_reql = 0; + vscan_svc_counts.vsc_node = 0; + vscan_svc_counts.vsc_tq = 0; + + vscan_svc_state = VS_SVC_IDLE; return (0); } + /* * vscan_svc_fini */ void vscan_svc_fini() { - ASSERT(vscan_svc_enabled == B_FALSE); - ASSERT(vscan_svc_in_use() == B_FALSE); + if (vscan_svc_state != VS_SVC_IDLE) { + DTRACE_PROBE1(vscan__svc__state__violation, + int, vscan_svc_state); + return; + } - cv_destroy(&vscan_svc_cv); + kmem_free(vscan_svc_nodes, vscan_svc_nodes_sz); + + cv_destroy(&vscan_svc_reql_cv); mutex_destroy(&vscan_svc_mutex); mutex_destroy(&vscan_svc_cfg_mutex); + vscan_svc_state = VS_SVC_UNCONFIG; } + /* * vscan_svc_enable */ -void +int vscan_svc_enable(void) { mutex_enter(&vscan_svc_mutex); - vscan_svc_enabled = B_TRUE; - - if (vscan_svc_taskq == NULL) { - if ((vscan_svc_taskq = taskq_create("vscan", - VS_TASKQ_NUM_THREADS, MINCLSYSPRI, 1, - INT_MAX, TASKQ_DYNAMIC)) == NULL) { - cmn_err(CE_WARN, "All scan requests " - "will be processed synchronously"); - } + + switch (vscan_svc_state) { + case VS_SVC_ENABLED: + /* + * it's possible (and okay) for vscan_svc_enable to be + * called when already enabled if vscand reconnects + * during a delayed disable + */ + break; + case VS_SVC_IDLE: + list_create(&vscan_svc_reql, sizeof (vscan_req_t), + offsetof(vscan_req_t, vsr_lnode)); + vscan_svc_reql_next = list_head(&vscan_svc_reql); + + vscan_svc_taskq = taskq_create("vscan_taskq", vs_workers, + MINCLSYSPRI, 1, INT_MAX, TASKQ_DYNAMIC); + ASSERT(vscan_svc_taskq != NULL); + + vscan_svc_reql_thread = thread_create(NULL, 0, + vscan_svc_reql_handler, 0, 0, &p0, TS_RUN, MINCLSYSPRI); + ASSERT(vscan_svc_reql_thread != NULL); + + /* ready to start processing requests */ + vscan_svc_state = VS_SVC_ENABLED; + fs_vscan_register(vscan_svc_scan_file); + break; + default: + DTRACE_PROBE1(vscan__svc__state__violation, + int, vscan_svc_state); + return (-1); } - fs_vscan_register(vscan_svc_scan_file); mutex_exit(&vscan_svc_mutex); + return (0); } /* * vscan_svc_disable + * + * Resources allocated during vscan_svc_enable are free'd by + * the handler thread immediately prior to exiting */ void vscan_svc_disable(void) { mutex_enter(&vscan_svc_mutex); - vscan_svc_enabled = B_FALSE; - fs_vscan_register(NULL); - if (vscan_svc_taskq) { - taskq_destroy(vscan_svc_taskq); - vscan_svc_taskq = NULL; + switch (vscan_svc_state) { + case VS_SVC_ENABLED: + fs_vscan_register(NULL); + vscan_svc_state = VS_SVC_DISABLED; + cv_signal(&vscan_svc_reql_cv); /* wake handler thread */ + break; + default: + DTRACE_PROBE1(vscan__svc__state__violation, int, + vscan_svc_state); } - mutex_exit(&vscan_svc_mutex); -} - - -/* - * vscan_svc_is_enabled - */ -boolean_t -vscan_svc_is_enabled() -{ - return (vscan_svc_enabled); + mutex_exit(&vscan_svc_mutex); } /* * vscan_svc_in_use - * - * The vscan driver is considered to be in use if it is - * enabled or if there are in-progress scan requests. - * Used to determine whether the driver can be unloaded. */ boolean_t vscan_svc_in_use() { - boolean_t rc; + boolean_t in_use; mutex_enter(&vscan_svc_mutex); - rc = (vscan_svc_enabled == B_TRUE) || (vscan_svc_req_count > 0); - mutex_exit(&vscan_svc_mutex); - return (rc); + switch (vscan_svc_state) { + case VS_SVC_IDLE: + case VS_SVC_UNCONFIG: + in_use = B_FALSE; + break; + default: + in_use = B_TRUE; + break; + } + + mutex_exit(&vscan_svc_mutex); + return (in_use); } + /* * vscan_svc_get_vnode * * Get the file vnode indexed by idx. - * Returns NULL if idx not valid. */ vnode_t * vscan_svc_get_vnode(int idx) { + vnode_t *vp = NULL; + ASSERT(idx > 0); - ASSERT(idx <= VS_DRV_MAX_FILES); + ASSERT(idx <= vs_nodes_max); - if ((idx <= 0) || (idx > VS_DRV_MAX_FILES)) - return (NULL); - else - return (vscan_svc_files[idx].vsf_req.vsr_vp); + mutex_enter(&vscan_svc_mutex); + if (vscan_svc_nodes[idx].vsn_req) + vp = vscan_svc_nodes[idx].vsn_req->vsr_vp; + mutex_exit(&vscan_svc_mutex); + + return (vp); } @@ -245,453 +454,459 @@ vscan_svc_get_vnode(int idx) * * This function is the entry point for the file system to * request that a file be virus scanned. - * - * Asynchronous requests: - * If an async scan request cannot be queued it is discarded. - * By definition the caller of an async request is not dependent - * on the outcome of the result. Although the file will thus - * not be scanned at this time, it will be scanned - * (synchronously) on subsequent access. - * This scenario should not occur during normal operation. - * - * Before queuing an async request do VN_HOLD(vp). VN_RELE(vp) - * will be done when the scan completes or if the request - * couldn't be queued. - * - * The vscan_fs_req_t, allocated to hold the request information - * passed from the fs, will be free'd when the scan completes. */ int vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async) { - int rc = 0; - vscan_fs_req_t *req; + int access; + vscan_req_t *req; boolean_t allow; + clock_t timeout, time_left; - mutex_enter(&vscan_svc_mutex); - - if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL) { - mutex_exit(&vscan_svc_mutex); + if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL) return (0); - } DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async); /* check if size or type exempts file from scanning */ if (vscan_svc_exempt_file(vp, &allow)) { - mutex_exit(&vscan_svc_mutex); if ((allow == B_TRUE) || (async != 0)) return (0); return (EACCES); } - vscan_svc_req_count++; - mutex_exit(&vscan_svc_mutex); + mutex_enter(&vscan_svc_mutex); - req = kmem_zalloc(sizeof (vscan_fs_req_t), KM_SLEEP); - req->vsr_vp = vp; + if (vscan_svc_state != VS_SVC_ENABLED) { + DTRACE_PROBE1(vscan__svc__state__violation, + int, vscan_svc_state); + mutex_exit(&vscan_svc_mutex); + return (0); + } - if (async) { - VN_HOLD(vp); - if (vscan_svc_taskq && - taskq_dispatch(vscan_svc_taskq, vscan_svc_taskq_callback, - (void *)req, TQ_SLEEP)) { - return (0); - } else { - VN_RELE(vp); - kmem_free(req, sizeof (vscan_fs_req_t)); - } - } else { - rc = vscan_svc_do_scan(req); - kmem_free(req, sizeof (vscan_fs_req_t)); + /* insert (or find) request in list */ + if ((req = vscan_svc_reql_insert(vp)) == NULL) { + mutex_exit(&vscan_svc_mutex); + cmn_err(CE_WARN, "Virus scan request list full"); + return ((async != 0) ? 0 : EACCES); } - mutex_enter(&vscan_svc_mutex); - vscan_svc_req_count--; - mutex_exit(&vscan_svc_mutex); + /* asynchronous request: return 0 */ + if (async) { + mutex_exit(&vscan_svc_mutex); + return (0); + } - return (rc); -} + /* synchronous scan request: wait for result */ + ++(req->vsr_refcnt); + time_left = SEC_TO_TICK(vs_scan_wait); + while ((time_left > 0) && (req->vsr_state != VS_SVC_REQ_COMPLETE)) { + timeout = lbolt + time_left; + time_left = cv_timedwait_sig(&(req->vsr_cv), + &vscan_svc_mutex, timeout); + } + if (time_left == -1) { + cmn_err(CE_WARN, "Virus scan request timeout %s (%d) \n", + vp->v_path, req->vsr_seqnum); + DTRACE_PROBE1(vscan__scan__timeout, vscan_req_t *, req); + } -/* - * vscan_svc_taskq_callback - * - * Callback function for async scan requests - */ -void -vscan_svc_taskq_callback(void *data) -{ - vscan_fs_req_t *req = (vscan_fs_req_t *)data; + ASSERT(req->vsr_magic == VS_REQ_MAGIC); + if (vscan_svc_state == VS_SVC_DISABLED) + access = VS_ACCESS_ALLOW; + else if (req->vsr_idx == 0) + access = VS_ACCESS_DENY; + else + access = vscan_svc_nodes[req->vsr_idx].vsn_access; - (void) vscan_svc_do_scan(req); - VN_RELE(req->vsr_vp); /* VN_HOLD done before request queued */ - kmem_free(req, sizeof (vscan_fs_req_t)); + if ((--req->vsr_refcnt) == 0) + vscan_svc_delete_req(req); - mutex_enter(&vscan_svc_mutex); - vscan_svc_req_count--; mutex_exit(&vscan_svc_mutex); + return ((access == VS_ACCESS_ALLOW) ? 0 : EACCES); } /* - * vscan_svc_do_scan + * vscan_svc_reql_handler * - * Should never be called directly. Invoke via vscan_svc_scan_file() - * If scan is in progress wait for it to complete, otherwise - * initiate door call to scan the file. + * inserts scan requests (from vscan_svc_reql) into + * vscan_svc_nodes and vscan_svc_taskq */ -static int -vscan_svc_do_scan(vscan_fs_req_t *req) +static void +vscan_svc_reql_handler(void) { - int rc = -1, idx; - vs_scan_req_t *scan_req; - vscan_file_t *svc_file; + vscan_req_t *req, *next; - mutex_enter(&vscan_svc_mutex); + for (;;) { + mutex_enter(&vscan_svc_mutex); + + if ((vscan_svc_state == VS_SVC_DISABLED) && + (vscan_svc_counts.vsc_reql == 0)) { + /* free resources allocated durining enable */ + taskq_destroy(vscan_svc_taskq); + vscan_svc_taskq = NULL; + list_destroy(&vscan_svc_reql); + vscan_svc_state = VS_SVC_IDLE; + mutex_exit(&vscan_svc_mutex); + return; + } - /* - * if a scan is in progress on the files vscan_svc_wait_for_scan will - * wait for it to complete and return the idx of the scan request. - * Otherwise it will return -1 and we will initiate a scan here. - */ - if ((idx = vscan_svc_wait_for_scan(req->vsr_vp)) != -1) { - svc_file = &vscan_svc_files[idx]; - } else { - /* insert the scan request into vscan_svc_files */ - idx = vscan_svc_insert_file(req); - svc_file = &vscan_svc_files[idx]; - - if (vscan_svc_enabled) { - if (vscan_svc_getattr(idx) == 0) { - /* valid scan_req ptr guaranteed */ - scan_req = vscan_svc_populate_req(idx); - mutex_exit(&vscan_svc_mutex); - if (vscan_drv_create_node(idx) == B_TRUE) - rc = vscan_door_scan_file(scan_req); - mutex_enter(&vscan_svc_mutex); - if (rc == 0) - vscan_svc_parse_rsp(idx, scan_req); - kmem_free(scan_req, sizeof (vs_scan_req_t)); - - /* process scan result */ - vscan_svc_process_scan_result(idx); - DTRACE_PROBE2(vscan__result, int, - svc_file->vsf_result, int, - svc_file->vsf_access); + /* + * If disabled, scan_complete any pending requests. + * Otherwise insert pending requests into vscan_svc_nodes + * and vscan_svc_taskq. If no slots are available in + * vscan_svc_nodes break loop and wait for one + */ + req = vscan_svc_reql_next; + + while (req != NULL) { + ASSERT(req->vsr_magic == VS_REQ_MAGIC); + next = list_next(&vscan_svc_reql, req); + + if (vscan_svc_state == VS_SVC_DISABLED) { + vscan_svc_scan_complete(req); } else { - /* if getattr fails: log error, deny access */ - cmn_err(CE_WARN, "Can't access xattr for %s\n", - svc_file->vsf_req.vsr_vp->v_path); - svc_file->vsf_access = VS_ACCESS_DENY; + /* insert request into vscan_svc_nodes */ + if (vscan_svc_insert_req(req) == -1) + break; + + /* add the scan request into the taskq */ + (void) taskq_dispatch(vscan_svc_taskq, + vscan_svc_taskq_callback, + (void *)req, TQ_SLEEP); + ++(vscan_svc_counts.vsc_tq); + + req->vsr_state = VS_SVC_REQ_QUEUED; } - } else { - /* if vscan not enabled (shutting down), allow ACCESS */ - svc_file->vsf_access = VS_ACCESS_ALLOW; + req = next; } + + vscan_svc_reql_next = req; + + DTRACE_PROBE2(vscan__req__counts, char *, "handler wait", + vscan_svc_counts_t *, &vscan_svc_counts); + + (void) cv_timedwait(&vscan_svc_reql_cv, &vscan_svc_mutex, + lbolt + SEC_TO_TICK(VS_REQL_HANDLER_TIMEOUT)); + + DTRACE_PROBE2(vscan__req__counts, char *, "handler wake", + vscan_svc_counts_t *, &vscan_svc_counts); + + mutex_exit(&vscan_svc_mutex); } +} - /* When a scan completes the result is saved in vscan_svc_files */ - rc = (svc_file->vsf_access == VS_ACCESS_ALLOW) ? 0 : EACCES; - /* wake threads waiting for result, or for a slot in vscan_svc_files */ - vscan_svc_notify_scan_complete(idx); +static void +vscan_svc_taskq_callback(void *data) +{ + vscan_req_t *req; - /* remove the entry from vscan_svc_files if nobody else is waiting */ - vscan_svc_release_file(idx); + mutex_enter(&vscan_svc_mutex); - mutex_exit(&vscan_svc_mutex); + req = (vscan_req_t *)data; + ASSERT(req->vsr_magic == VS_REQ_MAGIC); + vscan_svc_do_scan(req); + if (req->vsr_state != VS_SVC_REQ_SCANNING) + vscan_svc_scan_complete(req); - return (rc); + --(vscan_svc_counts.vsc_tq); + mutex_exit(&vscan_svc_mutex); } /* - * vscan_svc_process_scan_result - * - * Sets vsf_access and updates file attributes based on vsf_result, - * as follows: + * vscan_svc_do_scan * - * VS_STATUS_INFECTED - * deny access, set quarantine attribute, clear scanstamp - * VS_STATUS_CLEAN - * allow access, set scanstamp, - * if file not modified since scan initiated, clear modified attribute - * VS_STATUS_NO_SCAN - * deny access if file quarantined, otherwise allow access - * VS_STATUS_UNDEFINED, VS_STATUS_ERROR - * deny access if file quarantined, modified or no scanstamp - * otherwise, allow access + * Note: To avoid potential deadlock it is important that + * vscan_svc_mutex is not held during the call to + * vscan_drv_create_note. vscan_drv_create_note enters + * the vscan_drv_mutex and it is possible that a thread + * holding that mutex could be waiting for vscan_svc_mutex. */ static void -vscan_svc_process_scan_result(int idx) +vscan_svc_do_scan(vscan_req_t *req) { - struct vattr attr; - vnode_t *vp; - timestruc_t *mtime; - vscan_file_t *svc_file; + int idx, result; + vscan_svc_node_t *node; + vs_scan_req_t *door_req; ASSERT(MUTEX_HELD(&vscan_svc_mutex)); - svc_file = &vscan_svc_files[idx]; - - switch (svc_file->vsf_result) { - case VS_STATUS_INFECTED: - svc_file->vsf_access = VS_ACCESS_DENY; - svc_file->vsf_quarantined = 1; - svc_file->vsf_scanstamp[0] = '\0'; - (void) vscan_svc_setattr(idx, - XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP); - return; - - case VS_STATUS_CLEAN: - svc_file->vsf_access = VS_ACCESS_ALLOW; + idx = req->vsr_idx; + node = &vscan_svc_nodes[idx]; - /* if mtime has changed, don't clear the modified attribute */ - vp = svc_file->vsf_req.vsr_vp; - mtime = &(svc_file->vsf_mtime); - attr.va_mask = AT_MTIME; - if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) || - (mtime->tv_sec != attr.va_mtime.tv_sec) || - (mtime->tv_nsec != attr.va_mtime.tv_nsec)) { - DTRACE_PROBE1(vscan__mtime__changed, vscan_file_t *, - svc_file); - (void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP); - return; - } + req->vsr_state = VS_SVC_REQ_IN_PROGRESS; - svc_file->vsf_modified = 0; - (void) vscan_svc_setattr(idx, - XAT_AV_SCANSTAMP | XAT_AV_MODIFIED); + /* if vscan not enabled (shutting down), allow ACCESS */ + if (vscan_svc_state != VS_SVC_ENABLED) { + node->vsn_access = VS_ACCESS_ALLOW; return; + } - case VS_STATUS_NO_SCAN: - if (svc_file->vsf_quarantined) - svc_file->vsf_access = VS_ACCESS_DENY; - else - svc_file->vsf_access = VS_ACCESS_ALLOW; + if (vscan_svc_getattr(idx) != 0) { + cmn_err(CE_WARN, "Can't access xattr for %s\n", + req->vsr_vp->v_path); + node->vsn_access = VS_ACCESS_DENY; return; + } - case VS_STATUS_ERROR: - case VS_STATUS_UNDEFINED: - default: - if ((svc_file->vsf_quarantined) || - (svc_file->vsf_modified) || - (svc_file->vsf_scanstamp[0] == '\0')) - svc_file->vsf_access = VS_ACCESS_DENY; - else - svc_file->vsf_access = VS_ACCESS_ALLOW; - return; + /* valid scan_req ptr guaranteed */ + door_req = vscan_svc_populate_req(idx); + + /* free up mutex around create node and door call */ + mutex_exit(&vscan_svc_mutex); + if (vscan_drv_create_node(idx) != B_TRUE) + result = VS_STATUS_ERROR; + else + result = vscan_door_scan_file(door_req); + kmem_free(door_req, sizeof (vs_scan_req_t)); + mutex_enter(&vscan_svc_mutex); + + if (result != VS_STATUS_SCANNING) { + vscan_svc_nodes[idx].vsn_result = result; + vscan_svc_process_scan_result(idx); + } else { /* async response */ + if (req->vsr_state == VS_SVC_REQ_IN_PROGRESS) + req->vsr_state = VS_SVC_REQ_SCANNING; } } /* - * vscan_svc_wait_for_scan + * vscan_svc_populate_req * - * Search for vp in vscan_svc_files. If vp already exists in - * vscan_svc_files scan is already in progress on file so wait - * for the inprogress scan to complete. + * Allocate a scan request to be sent to vscand, populating it + * from the data in vscan_svc_nodes[idx]. * - * Returns: idx of file waited for - * -1 if file not already scanning + * Returns: scan request object */ -static int -vscan_svc_wait_for_scan(vnode_t *vp) +static vs_scan_req_t * +vscan_svc_populate_req(int idx) { - int idx; - vscan_file_t *svc_file; + vs_scan_req_t *scan_req; + vscan_req_t *req; + vscan_svc_node_t *node; - ASSERT(vp); ASSERT(MUTEX_HELD(&vscan_svc_mutex)); - for (idx = 1; idx <= VS_DRV_MAX_FILES; idx++) { - if (vscan_svc_files[idx].vsf_req.vsr_vp == vp) - break; - } - - /* file not found in table thus not currently being scanned */ - if (idx > VS_DRV_MAX_FILES) - return (-1); - - /* file found - wait for scan to complete */ - svc_file = &vscan_svc_files[idx]; - svc_file->vsf_wait_count++; - - DTRACE_PROBE2(vscan__wait__scan, vscan_file_t *, svc_file, int, idx); - - while (svc_file->vsf_access == VS_ACCESS_UNDEFINED) - cv_wait(&(svc_file->vsf_cv), &vscan_svc_mutex); + node = &vscan_svc_nodes[idx]; + req = node->vsn_req; + scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP); - svc_file->vsf_wait_count--; + scan_req->vsr_idx = idx; + scan_req->vsr_seqnum = req->vsr_seqnum; + (void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN); + scan_req->vsr_size = node->vsn_size; + scan_req->vsr_modified = node->vsn_modified; + scan_req->vsr_quarantined = node->vsn_quarantined; + scan_req->vsr_flags = 0; + (void) strncpy(scan_req->vsr_scanstamp, + node->vsn_scanstamp, sizeof (vs_scanstamp_t)); - return (idx); + return (scan_req); } /* - * vscan_svc_find_slot - * - * Find empty slot in vscan_svc_files table. - * - * Returns idx of slot, or -1 if not found + * vscan_svc_scan_complete */ -static int -vscan_svc_find_slot(void) +static void +vscan_svc_scan_complete(vscan_req_t *req) { - int idx; - ASSERT(MUTEX_HELD(&vscan_svc_mutex)); - for (idx = 1; idx <= VS_DRV_MAX_FILES; idx++) { - if (vscan_svc_files[idx].vsf_req.vsr_vp == NULL) - return (idx); - } + ASSERT(req != NULL); - return (-1); + req->vsr_state = VS_SVC_REQ_COMPLETE; + + if ((--req->vsr_refcnt) == 0) + vscan_svc_delete_req(req); + else + cv_broadcast(&(req->vsr_cv)); } /* - * vscan_svc_insert_file - * - * Find the next available flot in vscan_svc_files and - * initialize it for the scan request. If no slot is - * available, vscan_svc_find_slot will wait for one. - * - * Returns: idx of scan request in vscan_svc_files table + * vscan_svc_delete_req */ -static int -vscan_svc_insert_file(vscan_fs_req_t *req) +static void +vscan_svc_delete_req(vscan_req_t *req) { int idx; - vscan_file_t *svc_file; ASSERT(MUTEX_HELD(&vscan_svc_mutex)); + ASSERT(req != NULL); + ASSERT(req->vsr_refcnt == 0); + ASSERT(req->vsr_state == VS_SVC_REQ_COMPLETE); - while ((idx = vscan_svc_find_slot()) == -1) { - DTRACE_PROBE1(vscan__wait__slot, char *, req->vsr_vp->v_path); - vscan_svc_wait_count++; - cv_wait(&(vscan_svc_cv), &vscan_svc_mutex); - vscan_svc_wait_count--; - } - - svc_file = &vscan_svc_files[idx]; + if ((idx = req->vsr_idx) != 0) + vscan_svc_remove_req(idx); - (void) memset(svc_file, 0, sizeof (vscan_file_t)); - svc_file->vsf_req = *req; - svc_file->vsf_modified = 1; - svc_file->vsf_result = VS_STATUS_UNDEFINED; - svc_file->vsf_access = VS_ACCESS_UNDEFINED; - cv_init(&(svc_file->vsf_cv), NULL, CV_DEFAULT, NULL); + vscan_svc_reql_remove(req); - DTRACE_PROBE2(vscan__insert, char *, req->vsr_vp->v_path, int, idx); - return (idx); + cv_signal(&vscan_svc_reql_cv); } /* - * vscan_svc_release_file + * vscan_svc_scan_result * - * Release the file (free the slot in vscan_svc_files) - * if no thread is waiting on it. + * Invoked from vscan_drv.c on receipt of an ioctl containing + * an async scan result (VS_DRV_IOCTL_RESULT) + * If the vsr_seqnum in the response does not match that in the + * vscan_svc_nodes entry the result is discarded. */ -static void -vscan_svc_release_file(int idx) +void +vscan_svc_scan_result(vs_scan_rsp_t *scan_rsp) { - vscan_file_t *svc_file; + vscan_req_t *req; + vscan_svc_node_t *node; - ASSERT(MUTEX_HELD(&vscan_svc_mutex)); - svc_file = &vscan_svc_files[idx]; + mutex_enter(&vscan_svc_mutex); + + node = &vscan_svc_nodes[scan_rsp->vsr_idx]; + + if ((req = node->vsn_req) == NULL) { + mutex_exit(&vscan_svc_mutex); + return; + } - if (svc_file->vsf_wait_count != 0) + ASSERT(req->vsr_magic == VS_REQ_MAGIC); + + if (scan_rsp->vsr_seqnum != req->vsr_seqnum) { + mutex_exit(&vscan_svc_mutex); return; + } - DTRACE_PROBE2(vscan__release, char *, - svc_file->vsf_req.vsr_vp->v_path, int, idx); + node->vsn_result = scan_rsp->vsr_result; + (void) strncpy(node->vsn_scanstamp, + scan_rsp->vsr_scanstamp, sizeof (vs_scanstamp_t)); - cv_destroy(&(svc_file->vsf_cv)); - (void) memset(svc_file, 0, sizeof (vscan_file_t)); + vscan_svc_process_scan_result(scan_rsp->vsr_idx); + + if (node->vsn_req->vsr_state == VS_SVC_REQ_SCANNING) + vscan_svc_scan_complete(node->vsn_req); + else + node->vsn_req->vsr_state = VS_SVC_REQ_ASYNC_COMPLETE; + + mutex_exit(&vscan_svc_mutex); } /* - * vscan_svc_populate_req + * vscan_svc_scan_abort * - * Allocate a scan request to be sent to vscand, populating it - * from the data in vscan_svc_files[idx]. - * - * Returns: scan request object + * Abort in-progress scan requests. */ -static vs_scan_req_t * -vscan_svc_populate_req(int idx) +void +vscan_svc_scan_abort() { - vs_scan_req_t *scan_req; - vscan_fs_req_t *req; - vscan_file_t *svc_file; + int idx; + vscan_req_t *req; - ASSERT(MUTEX_HELD(&vscan_svc_mutex)); + mutex_enter(&vscan_svc_mutex); - svc_file = &vscan_svc_files[idx]; - req = &(svc_file->vsf_req); - scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP); + for (idx = 1; idx <= vs_nodes_max; idx++) { + if ((req = vscan_svc_nodes[idx].vsn_req) == NULL) + continue; - scan_req->vsr_id = idx; - (void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN); - scan_req->vsr_size = svc_file->vsf_size; - scan_req->vsr_modified = svc_file->vsf_modified; - scan_req->vsr_quarantined = svc_file->vsf_quarantined; - scan_req->vsr_flags = 0; - (void) strncpy(scan_req->vsr_scanstamp, - svc_file->vsf_scanstamp, sizeof (vs_scanstamp_t)); + ASSERT(req->vsr_magic == VS_REQ_MAGIC); - return (scan_req); + if (req->vsr_state == VS_SVC_REQ_SCANNING) { + DTRACE_PROBE1(vscan__abort, vscan_req_t *, req); + vscan_svc_process_scan_result(idx); + vscan_svc_scan_complete(req); + } + } + + mutex_exit(&vscan_svc_mutex); } /* - * vscan_svc_parse_rsp + * vscan_svc_process_scan_result + * + * Sets vsn_access and updates file attributes based on vsn_result, + * as follows: * - * Parse scan response data and save in vscan_svc_files[idx] + * VS_STATUS_INFECTED + * deny access, set quarantine attribute, clear scanstamp + * VS_STATUS_CLEAN + * allow access, set scanstamp, + * if file not modified since scan initiated, clear modified attribute + * VS_STATUS_NO_SCAN + * deny access if file quarantined, otherwise allow access + * VS_STATUS_UNDEFINED, VS_STATUS_ERROR + * deny access if file quarantined, modified or no scanstamp + * otherwise, allow access */ static void -vscan_svc_parse_rsp(int idx, vs_scan_req_t *scan_req) +vscan_svc_process_scan_result(int idx) { - vscan_file_t *svc_file; + struct vattr attr; + vnode_t *vp; + timestruc_t *mtime; + vscan_svc_node_t *node; ASSERT(MUTEX_HELD(&vscan_svc_mutex)); - svc_file = &vscan_svc_files[idx]; - svc_file->vsf_result = scan_req->vsr_result; - (void) strncpy(svc_file->vsf_scanstamp, - scan_req->vsr_scanstamp, sizeof (vs_scanstamp_t)); -} + node = &vscan_svc_nodes[idx]; + switch (node->vsn_result) { + case VS_STATUS_INFECTED: + node->vsn_access = VS_ACCESS_DENY; + node->vsn_quarantined = 1; + node->vsn_scanstamp[0] = '\0'; + (void) vscan_svc_setattr(idx, + XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP); + break; -/* - * vscan_svc_notify_scan_complete - * - * signal vscan_svc_files.vsf_cv and vscan_svc_cv to wake - * threads waiting for the scan result for the specified - * file (vscan_svc_files[idx].vsf_cv) or for a slot in - * vscan_svc_files table (vscan_svc_cv) - */ -static void -vscan_svc_notify_scan_complete(int idx) -{ - vscan_file_t *svc_file; + case VS_STATUS_CLEAN: + node->vsn_access = VS_ACCESS_ALLOW; - ASSERT(MUTEX_HELD(&vscan_svc_mutex)); + /* if mtime has changed, don't clear the modified attribute */ + vp = node->vsn_req->vsr_vp; + mtime = &(node->vsn_mtime); + attr.va_mask = AT_MTIME; + if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) || + (mtime->tv_sec != attr.va_mtime.tv_sec) || + (mtime->tv_nsec != attr.va_mtime.tv_nsec)) { + DTRACE_PROBE1(vscan__mtime__changed, vscan_svc_node_t *, + node); + (void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP); + break; + } + + node->vsn_modified = 0; + (void) vscan_svc_setattr(idx, + XAT_AV_SCANSTAMP | XAT_AV_MODIFIED); + break; - svc_file = &vscan_svc_files[idx]; + case VS_STATUS_NO_SCAN: + if (node->vsn_quarantined) + node->vsn_access = VS_ACCESS_DENY; + else + node->vsn_access = VS_ACCESS_ALLOW; + break; - /* if someone waiting for result, cv_signal */ - if (svc_file->vsf_wait_count > 0) - cv_signal(&(svc_file->vsf_cv)); + case VS_STATUS_ERROR: + case VS_STATUS_UNDEFINED: + default: + if ((node->vsn_quarantined) || + (node->vsn_modified) || + (node->vsn_scanstamp[0] == '\0')) + node->vsn_access = VS_ACCESS_DENY; + else + node->vsn_access = VS_ACCESS_ALLOW; + break; + } - /* signal vscan_svc_cv if any threads waiting for a slot */ - if (vscan_svc_wait_count > 0) - cv_signal(&vscan_svc_cv); + DTRACE_PROBE4(vscan__result, + int, idx, int, node->vsn_req->vsr_seqnum, + int, node->vsn_result, int, node->vsn_access); } @@ -706,12 +921,12 @@ vscan_svc_getattr(int idx) xvattr_t xvattr; xoptattr_t *xoap = NULL; vnode_t *vp; - vscan_file_t *svc_file; + vscan_svc_node_t *node; ASSERT(MUTEX_HELD(&vscan_svc_mutex)); - svc_file = &vscan_svc_files[idx]; - if ((vp = svc_file->vsf_req.vsr_vp) == NULL) + node = &vscan_svc_nodes[idx]; + if ((vp = node->vsn_req->vsr_vp) == NULL) return (-1); /* get the attributes */ @@ -732,24 +947,24 @@ vscan_svc_getattr(int idx) return (-1); } - svc_file->vsf_size = xvattr.xva_vattr.va_size; - svc_file->vsf_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec; - svc_file->vsf_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec; + node->vsn_size = xvattr.xva_vattr.va_size; + node->vsn_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec; + node->vsn_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec; if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0) return (-1); - svc_file->vsf_modified = xoap->xoa_av_modified; + node->vsn_modified = xoap->xoa_av_modified; if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0) return (-1); - svc_file->vsf_quarantined = xoap->xoa_av_quarantined; + node->vsn_quarantined = xoap->xoa_av_quarantined; if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) { - (void) memcpy(svc_file->vsf_scanstamp, + (void) memcpy(node->vsn_scanstamp, xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ); } - DTRACE_PROBE1(vscan__getattr, vscan_file_t *, svc_file); + DTRACE_PROBE1(vscan__getattr, vscan_svc_node_t *, node); return (0); } @@ -766,12 +981,12 @@ vscan_svc_setattr(int idx, int which) xoptattr_t *xoap = NULL; vnode_t *vp; int len; - vscan_file_t *svc_file; + vscan_svc_node_t *node; ASSERT(MUTEX_HELD(&vscan_svc_mutex)); - svc_file = &vscan_svc_files[idx]; - if ((vp = svc_file->vsf_req.vsr_vp) == NULL) + node = &vscan_svc_nodes[idx]; + if ((vp = node->vsn_req->vsr_vp) == NULL) return (-1); /* update the attributes */ @@ -781,23 +996,23 @@ vscan_svc_setattr(int idx, int which) if (which & XAT_AV_MODIFIED) { XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED); - xoap->xoa_av_modified = svc_file->vsf_modified; + xoap->xoa_av_modified = node->vsn_modified; } if (which & XAT_AV_QUARANTINED) { XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED); - xoap->xoa_av_quarantined = svc_file->vsf_quarantined; + xoap->xoa_av_quarantined = node->vsn_quarantined; } if (which & XAT_AV_SCANSTAMP) { XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP); - len = strlen(svc_file->vsf_scanstamp); + len = strlen(node->vsn_scanstamp); (void) memcpy(xoap->xoa_av_scanstamp, - svc_file->vsf_scanstamp, len); + node->vsn_scanstamp, len); } /* if access is denied, set mtime to invalidate client cache */ - if (svc_file->vsf_access != VS_ACCESS_ALLOW) { + if (node->vsn_access != VS_ACCESS_ALLOW) { xvattr.xva_vattr.va_mask |= AT_MTIME; gethrestime(&xvattr.xva_vattr.va_mtime); } @@ -806,7 +1021,7 @@ vscan_svc_setattr(int idx, int which) return (-1); DTRACE_PROBE2(vscan__setattr, - vscan_file_t *, svc_file, int, which); + vscan_svc_node_t *, node, int, which); return (0); } @@ -930,7 +1145,6 @@ vscan_svc_exempt_filetype(char *filepath) else ext++; - for (i = 0; i < VS_TYPES_MAX; i ++) { if (vscan_svc_types[i] == 0) break; @@ -1009,3 +1223,147 @@ vscan_svc_match_ext(char *patn, char *str, int depth) } /* NOT REACHED */ } + + +/* + * vscan_svc_insert_req + * + * Insert request in next available available slot in vscan_svc_nodes + * + * Returns: idx of slot, or -1 if no slot available + */ +static int +vscan_svc_insert_req(vscan_req_t *req) +{ + int idx; + vscan_svc_node_t *node; + + ASSERT(MUTEX_HELD(&vscan_svc_mutex)); + + if (vscan_svc_counts.vsc_node == vs_nodes_max) + return (-1); + + for (idx = 1; idx <= vs_nodes_max; idx++) { + if (vscan_svc_nodes[idx].vsn_req == NULL) { + req->vsr_idx = idx; + + node = &vscan_svc_nodes[idx]; + (void) memset(node, 0, sizeof (vscan_svc_node_t)); + node->vsn_req = req; + node->vsn_modified = 1; + node->vsn_result = VS_STATUS_UNDEFINED; + node->vsn_access = VS_ACCESS_UNDEFINED; + + ++(vscan_svc_counts.vsc_node); + return (idx); + } + } + + return (-1); +} + + +/* + * vscan_svc_remove_req + */ +static void +vscan_svc_remove_req(int idx) +{ + ASSERT(MUTEX_HELD(&vscan_svc_mutex)); + + if (idx != 0) { + (void) memset(&vscan_svc_nodes[idx], 0, + sizeof (vscan_svc_node_t)); + --(vscan_svc_counts.vsc_node); + } +} + + +/* + * vscan_svc_reql_find + */ +static vscan_req_t * +vscan_svc_reql_find(vnode_t *vp) +{ + vscan_req_t *req; + ASSERT(MUTEX_HELD(&vscan_svc_mutex)); + + req = list_head(&vscan_svc_reql); + + while (req != NULL) { + ASSERT(req->vsr_magic == VS_REQ_MAGIC); + if ((req->vsr_vp == vp) && + (req->vsr_state != VS_SVC_REQ_COMPLETE)) + break; + + req = list_next(&vscan_svc_reql, req); + } + + return (req); +} + + +/* + * vscan_svc_reql_insert + */ +static vscan_req_t * +vscan_svc_reql_insert(vnode_t *vp) +{ + vscan_req_t *req; + + ASSERT(MUTEX_HELD(&vscan_svc_mutex)); + + /* if request already in list then return it */ + if ((req = vscan_svc_reql_find(vp)) != NULL) + return (req); + + /* if list is full return NULL */ + if (vscan_svc_counts.vsc_reql == vs_reqs_max) + return (NULL); + + /* create a new request and insert into list */ + VN_HOLD(vp); + + req = kmem_zalloc(sizeof (vscan_req_t), KM_SLEEP); + + req->vsr_magic = VS_REQ_MAGIC; + if (vscan_svc_seqnum == UINT32_MAX) + vscan_svc_seqnum = 0; + req->vsr_seqnum = ++vscan_svc_seqnum; + req->vsr_vp = vp; + req->vsr_refcnt = 1; /* decremented in vscan_svc_scan_complete */ + req->vsr_state = VS_SVC_REQ_INIT; + cv_init(&(req->vsr_cv), NULL, CV_DEFAULT, NULL); + + list_insert_tail(&vscan_svc_reql, req); + if (vscan_svc_reql_next == NULL) + vscan_svc_reql_next = req; + + ++(vscan_svc_counts.vsc_reql); + + /* wake reql handler thread */ + cv_signal(&vscan_svc_reql_cv); + + return (req); +} + + +/* + * vscan_svc_reql_remove + */ +static void +vscan_svc_reql_remove(vscan_req_t *req) +{ + ASSERT(MUTEX_HELD(&vscan_svc_mutex)); + ASSERT(req->vsr_magic == VS_REQ_MAGIC); + + if (vscan_svc_reql_next == req) + vscan_svc_reql_next = list_next(&vscan_svc_reql, req); + + list_remove(&vscan_svc_reql, req); + cv_destroy(&(req->vsr_cv)); + VN_RELE(req->vsr_vp); + + kmem_free(req, sizeof (vscan_req_t)); + --(vscan_svc_counts.vsc_reql); +} diff --git a/usr/src/uts/common/sys/vscan.h b/usr/src/uts/common/sys/vscan.h index 2cf188cab6..9674d060a2 100644 --- a/usr/src/uts/common/sys/vscan.h +++ b/usr/src/uts/common/sys/vscan.h @@ -39,20 +39,26 @@ extern "C" { * vscan.h provides definitions for vscan kernel module */ -#define VS_DRV_MAX_FILES 1024 /* max concurent file scans */ -#define VS_DRV_PATH "/devices/pseudo/vscan@0:vscan" -#define VS_DRV_IOCTL_ENABLE 0x0001 /* door rendezvous */ -#define VS_DRV_IOCTL_DISABLE 0x0002 /* vscand shutting down */ -#define VS_DRV_IOCTL_CONFIG 0x0004 /* vscand config data update */ +#define VS_DRV_PATH "/dev/vscan/vscan" /* append minor dev num */ + +#define VS_IOCTL_ENABLE 0x01 /* door rendezvous */ +#define VS_IOCTL_DISABLE 0x02 /* vscand shutting down */ +#define VS_IOCTL_CONFIG 0x03 /* vscand config data update */ +#define VS_IOCTL_RESULT 0x04 /* scan result */ +#define VS_IOCTL_MAX_REQ 0x05 /* max in-progress req vscand */ /* Scan Result - vsr_result */ #define VS_STATUS_UNDEFINED 0 -#define VS_STATUS_NO_SCAN 1 /* scan not required */ -#define VS_STATUS_ERROR 2 /* scan failed */ -#define VS_STATUS_CLEAN 3 /* scan successful, file clean */ -#define VS_STATUS_INFECTED 4 /* scan successful, file infected */ +#define VS_STATUS_NO_SCAN 1 /* scan not required */ +#define VS_STATUS_ERROR 2 /* scan failed */ +#define VS_STATUS_CLEAN 3 /* scan successful, file clean */ +#define VS_STATUS_INFECTED 4 /* scan successful, file infected */ +#define VS_STATUS_SCANNING 5 /* scan in progress - async */ +/* Configuration data vs_config_t - vsc_types */ #define VS_TYPES_LEN 4096 /* vs_config_t - types buffer */ +#define VS_TYPES_MAX VS_TYPES_LEN / 2 + /* * AV_SCANSTAMP_SZ is the size of the scanstamp stored in the @@ -61,20 +67,29 @@ extern "C" { */ typedef char vs_scanstamp_t[AV_SCANSTAMP_SZ + 1]; -/* used for both request to and response from vscand */ +/* used for door request to vscand */ typedef struct vs_scan_req { - uint32_t vsr_id; - uint32_t vsr_flags; + uint32_t vsr_idx; + uint32_t vsr_seqnum; uint64_t vsr_size; + uint32_t vsr_flags; uint8_t vsr_modified; uint8_t vsr_quarantined; char vsr_path[MAXPATHLEN]; vs_scanstamp_t vsr_scanstamp; - uint32_t vsr_result; } vs_scan_req_t; -/* passed in VS_DRV_IOCTL_CONFIG */ +/* passed in VS_IOCTL_RESULT - async response from vscand */ +typedef struct vs_scan_rsp { + uint32_t vsr_idx; + uint32_t vsr_seqnum; + uint32_t vsr_result; + vs_scanstamp_t vsr_scanstamp; +} vs_scan_rsp_t; + + +/* passed in VS_IOCTL_CONFIG */ typedef struct vs_config { char vsc_types[VS_TYPES_LEN]; uint64_t vsc_types_len; @@ -84,33 +99,14 @@ typedef struct vs_config { #ifdef _KERNEL - -/* - * max no of types in vs_config_t.vsc_types - * used as dimention for array of pointers to types - */ -#define VS_TYPES_MAX VS_TYPES_LEN / 2 - -/* - * seconds to wait for daemon to reconnect before unregistering from VFS - * during this time, the kernel will: - * - allow access to files that have not been modified since last scanned - * - deny access to files which have been modified since last scanned - */ -#define VS_DAEMON_WAIT_SEC 60 - -/* access derived from scan result (VS_STATUS_XXX) and file attributes */ -#define VS_ACCESS_UNDEFINED 0 -#define VS_ACCESS_ALLOW 1 -#define VS_ACCESS_DENY 2 - int vscan_svc_init(void); void vscan_svc_fini(void); -void vscan_svc_enable(void); +int vscan_svc_enable(void); void vscan_svc_disable(void); int vscan_svc_configure(vs_config_t *); -boolean_t vscan_svc_is_enabled(void); boolean_t vscan_svc_in_use(void); +void vscan_svc_scan_result(vs_scan_rsp_t *); +void vscan_svc_scan_abort(void); vnode_t *vscan_svc_get_vnode(int); int vscan_door_init(void); |
