diff options
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); |
