xref: /onnv-gate/usr/src/uts/common/io/vscan/vscan_drv.c (revision 11066:cebb50cbe4f9)
15440Sjm199354 /*
25440Sjm199354  * CDDL HEADER START
35440Sjm199354  *
45440Sjm199354  * The contents of this file are subject to the terms of the
55440Sjm199354  * Common Development and Distribution License (the "License").
65440Sjm199354  * You may not use this file except in compliance with the License.
75440Sjm199354  *
85440Sjm199354  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95440Sjm199354  * or http://www.opensolaris.org/os/licensing.
105440Sjm199354  * See the License for the specific language governing permissions
115440Sjm199354  * and limitations under the License.
125440Sjm199354  *
135440Sjm199354  * When distributing Covered Code, include this CDDL HEADER in each
145440Sjm199354  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155440Sjm199354  * If applicable, add the following below this CDDL HEADER, with the
165440Sjm199354  * fields enclosed by brackets "[]" replaced with your own identifying
175440Sjm199354  * information: Portions Copyright [yyyy] [name of copyright owner]
185440Sjm199354  *
195440Sjm199354  * CDDL HEADER END
205440Sjm199354  */
215440Sjm199354 
225440Sjm199354 /*
23*11066Srafael.vanoni@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
245440Sjm199354  * Use is subject to license terms.
255440Sjm199354  */
265440Sjm199354 
275440Sjm199354 #include <sys/stat.h>
285440Sjm199354 #include <sys/ddi.h>
295440Sjm199354 #include <sys/sunddi.h>
305440Sjm199354 #include <sys/time.h>
315440Sjm199354 #include <sys/varargs.h>
325440Sjm199354 #include <sys/conf.h>
335440Sjm199354 #include <sys/modctl.h>
345440Sjm199354 #include <sys/vnode.h>
355440Sjm199354 #include <fs/fs_subr.h>
365440Sjm199354 #include <sys/types.h>
375440Sjm199354 #include <sys/file.h>
385440Sjm199354 #include <sys/disp.h>
395440Sjm199354 #include <sys/vscan.h>
405440Sjm199354 #include <sys/policy.h>
415440Sjm199354 #include <sys/sdt.h>
425440Sjm199354 
436407Sjm199354 
446407Sjm199354 /* seconds to wait for daemon to reconnect before disabling */
456407Sjm199354 #define	VS_DAEMON_WAIT_SEC	60
466407Sjm199354 
476407Sjm199354 /* length of minor node name - vscan%d */
486407Sjm199354 #define	VS_NODENAME_LEN		16
496407Sjm199354 
506407Sjm199354 /* global variables - tunable via /etc/system */
516407Sjm199354 uint32_t vs_reconnect_timeout = VS_DAEMON_WAIT_SEC;
526407Sjm199354 extern uint32_t vs_nodes_max;	/* max in-progress scan requests */
536407Sjm199354 
546407Sjm199354 /*
556407Sjm199354  * vscan_drv_state
566407Sjm199354  *
576407Sjm199354  * Operations on instance 0 represent vscand initiated state
586407Sjm199354  * transition events:
596407Sjm199354  * open(0) - vscand connect
606407Sjm199354  * close(0) - vscan disconnect
616407Sjm199354  * enable(0) - vscand enable (ready to hand requests)
626407Sjm199354  * disable(0) - vscand disable (shutting down)
636407Sjm199354  *
646407Sjm199354  *   +------------------------+
656407Sjm199354  *   | VS_DRV_UNCONFIG        |
666407Sjm199354  *   +------------------------+
676407Sjm199354  *      |           ^
686407Sjm199354  *      | attach    | detach
696407Sjm199354  *      v           |
706407Sjm199354  *   +------------------------+
716407Sjm199354  *   | VS_DRV_IDLE            |<------|
726407Sjm199354  *   +------------------------+       |
736407Sjm199354  *      |           ^                 |
746407Sjm199354  *      | open(0)   | close(0)        |
756407Sjm199354  *      v           |                 |
766407Sjm199354  *   +------------------------+       |
776407Sjm199354  *   | VS_DRV_CONNECTED       |<-|    |
786407Sjm199354  *   +------------------------+  |    |
796407Sjm199354  *      |           ^            |    |
806407Sjm199354  *      | enable(0) | disable(0) |    |
816407Sjm199354  *      v           |            |    |
826407Sjm199354  *   +------------------------+  |    |
836407Sjm199354  *   | VS_DRV_ENABLED         |  |    |
846407Sjm199354  *   +------------------------+  |    |
856407Sjm199354  *      |                        |    |
866407Sjm199354  *      | close(0)            open(0) |
876407Sjm199354  *      v                        |    |
886407Sjm199354  *   +------------------------+  |    | timeout
896407Sjm199354  *   | VS_DRV_DELAYED_DISABLE | --    |
906407Sjm199354  *   +------------------------+	------|
916407Sjm199354  *
926407Sjm199354  */
936407Sjm199354 typedef enum {
946407Sjm199354 	VS_DRV_UNCONFIG,
956407Sjm199354 	VS_DRV_IDLE,
966407Sjm199354 	VS_DRV_CONNECTED,
976407Sjm199354 	VS_DRV_ENABLED,
986407Sjm199354 	VS_DRV_DELAYED_DISABLE
996407Sjm199354 } vscan_drv_state_t;
1006407Sjm199354 static vscan_drv_state_t vscan_drv_state = VS_DRV_UNCONFIG;
1015440Sjm199354 
1025440Sjm199354 
1035440Sjm199354 /*
1046407Sjm199354  * vscan_drv_inst_state
1056407Sjm199354  *
1066407Sjm199354  * Instance 0 controls the state of the driver: vscan_drv_state.
1076407Sjm199354  * vscan_drv_inst_state[0] should NOT be used.
1086407Sjm199354  *
1096407Sjm199354  * vscan_drv_inst_state[n] represents the state of driver
1106407Sjm199354  * instance n, used by vscand to access file data for the
1116407Sjm199354  * scan request with index n in vscan_svc_reqs.
1126407Sjm199354  * Minor nodes are created as required then all are destroyed
1136407Sjm199354  * during driver detach.
1145440Sjm199354  *
1156407Sjm199354  *   +------------------------+
1166407Sjm199354  *   | VS_DRV_INST_UNCONFIG   |
1176407Sjm199354  *   +------------------------+
1186407Sjm199354  *      |                 ^
1196407Sjm199354  *      | create_node(n)  | detach
1206407Sjm199354  *      v                 |
1216407Sjm199354  *   +------------------------+
1226407Sjm199354  *   | VS_DRV_INST_INIT       |<-|
1236407Sjm199354  *   +------------------------+  |
1246407Sjm199354  *      |                        |
1256407Sjm199354  *      | open(n)                |
1266407Sjm199354  *      v                        |
1276407Sjm199354  *   +------------------------+  |
1286407Sjm199354  *   | VS_DRV_INST_OPEN       |--|
1296407Sjm199354  *   +------------------------+  |
1306407Sjm199354  *      |                        |
1316407Sjm199354  *      | read(n)                |
1326407Sjm199354  *      v                        | close(n)
1336407Sjm199354  *   +------------------------+  |
1346407Sjm199354  *   | VS_DRV_INST_READING    |--|
1356407Sjm199354  *   +------------------------+
1365440Sjm199354  */
1375440Sjm199354 typedef enum {
1386407Sjm199354 	VS_DRV_INST_UNCONFIG = 0, /* minor node not created */
1396407Sjm199354 	VS_DRV_INST_INIT,
1406407Sjm199354 	VS_DRV_INST_OPEN,
1416407Sjm199354 	VS_DRV_INST_READING
1426407Sjm199354 } vscan_drv_inst_state_t;
1435440Sjm199354 
1446407Sjm199354 static vscan_drv_inst_state_t *vscan_drv_inst_state;
1456407Sjm199354 static int vscan_drv_inst_state_sz;
1465440Sjm199354 
1475440Sjm199354 static dev_info_t *vscan_drv_dip;
1485440Sjm199354 static kmutex_t vscan_drv_mutex;
1495931Sjm199354 static kcondvar_t vscan_drv_cv; /* wait for daemon reconnect */
1505440Sjm199354 
1515440Sjm199354 /*
1525440Sjm199354  * DDI entry points.
1535440Sjm199354  */
1545440Sjm199354 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t);
1555440Sjm199354 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t);
1565440Sjm199354 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1575440Sjm199354 static int vscan_drv_open(dev_t *, int, int, cred_t *);
1585440Sjm199354 static int vscan_drv_close(dev_t, int, int, cred_t *);
1595440Sjm199354 static int vscan_drv_read(dev_t, struct uio *, cred_t *);
1605440Sjm199354 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1615440Sjm199354 
1625931Sjm199354 static boolean_t vscan_drv_in_use(void);
1635931Sjm199354 static void vscan_drv_delayed_disable(void);
1645440Sjm199354 
1655440Sjm199354 
1665440Sjm199354 /*
1675440Sjm199354  * module linkage info for the kernel
1685440Sjm199354  */
1695440Sjm199354 static struct cb_ops cbops = {
1705440Sjm199354 	vscan_drv_open,		/* cb_open */
1715440Sjm199354 	vscan_drv_close,	/* cb_close */
1725440Sjm199354 	nodev,			/* cb_strategy */
1735440Sjm199354 	nodev,			/* cb_print */
1745440Sjm199354 	nodev,			/* cb_dump */
1755440Sjm199354 	vscan_drv_read,		/* cb_read */
1765440Sjm199354 	nodev,			/* cb_write */
1775440Sjm199354 	vscan_drv_ioctl,	/* cb_ioctl */
1785440Sjm199354 	nodev,			/* cb_devmap */
1795440Sjm199354 	nodev,			/* cb_mmap */
1805440Sjm199354 	nodev,			/* cb_segmap */
1815440Sjm199354 	nochpoll,		/* cb_chpoll */
1825440Sjm199354 	ddi_prop_op,		/* cb_prop_op */
1835440Sjm199354 	NULL,			/* cb_streamtab */
1845440Sjm199354 	D_MP,			/* cb_flag */
1855440Sjm199354 	CB_REV,			/* cb_rev */
1865440Sjm199354 	nodev,			/* cb_aread */
1875440Sjm199354 	nodev,			/* cb_awrite */
1885440Sjm199354 };
1895440Sjm199354 
1905440Sjm199354 static struct dev_ops devops = {
1915440Sjm199354 	DEVO_REV,		/* devo_rev */
1925440Sjm199354 	0,			/* devo_refcnt */
1935440Sjm199354 	vscan_drv_getinfo,	/* devo_getinfo */
1945440Sjm199354 	nulldev,		/* devo_identify */
1955440Sjm199354 	nulldev,		/* devo_probe */
1965440Sjm199354 	vscan_drv_attach,	/* devo_attach */
1975440Sjm199354 	vscan_drv_detach,	/* devo_detach */
1985440Sjm199354 	nodev,			/* devo_reset */
1995440Sjm199354 	&cbops,			/* devo_cb_ops */
2005440Sjm199354 	NULL,			/* devo_bus_ops */
2015440Sjm199354 	NULL,			/* devo_power */
2027656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* devo_quiesce */
2035440Sjm199354 };
2045440Sjm199354 
2055440Sjm199354 static struct modldrv modldrv = {
2065440Sjm199354 	&mod_driverops,		/* drv_modops */
2075440Sjm199354 	"virus scanning",	/* drv_linkinfo */
2085440Sjm199354 	&devops,
2095440Sjm199354 };
2105440Sjm199354 
2115440Sjm199354 static struct modlinkage modlinkage = {
2125440Sjm199354 
2135440Sjm199354 	MODREV_1,	/* revision of the module, must be: MODREV_1	*/
2145440Sjm199354 	&modldrv,	/* ptr to linkage structures			*/
2155440Sjm199354 	NULL,
2165440Sjm199354 };
2175440Sjm199354 
2185440Sjm199354 
2195440Sjm199354 /*
2205440Sjm199354  * _init
2215440Sjm199354  */
2225440Sjm199354 int
_init(void)2235440Sjm199354 _init(void)
2245440Sjm199354 {
2255440Sjm199354 	int rc;
2265440Sjm199354 
2276407Sjm199354 	vscan_drv_inst_state_sz =
2286407Sjm199354 	    sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1);
2295440Sjm199354 
2306407Sjm199354 	if (vscan_door_init() != 0)
2316407Sjm199354 		return (DDI_FAILURE);
2326407Sjm199354 
2336407Sjm199354 	if (vscan_svc_init() != 0) {
2346407Sjm199354 		vscan_door_fini();
2355440Sjm199354 		return (DDI_FAILURE);
2365440Sjm199354 	}
2375440Sjm199354 
2386407Sjm199354 	mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL);
2396407Sjm199354 	vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP);
2405440Sjm199354 
2416407Sjm199354 	cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL);
2425440Sjm199354 
2435440Sjm199354 	if ((rc  = mod_install(&modlinkage)) != 0) {
2445440Sjm199354 		vscan_door_fini();
2455440Sjm199354 		vscan_svc_fini();
2466407Sjm199354 		kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
2476407Sjm199354 		cv_destroy(&vscan_drv_cv);
2485440Sjm199354 		mutex_destroy(&vscan_drv_mutex);
2495440Sjm199354 	}
2505440Sjm199354 
2515440Sjm199354 	return (rc);
2525440Sjm199354 }
2535440Sjm199354 
2545440Sjm199354 
2555440Sjm199354 /*
2565440Sjm199354  * _info
2575440Sjm199354  */
2585440Sjm199354 int
_info(struct modinfo * modinfop)2595440Sjm199354 _info(struct modinfo *modinfop)
2605440Sjm199354 {
2615440Sjm199354 	return (mod_info(&modlinkage, modinfop));
2625440Sjm199354 }
2635440Sjm199354 
2645440Sjm199354 
2655440Sjm199354 /*
2665440Sjm199354  * _fini
2675440Sjm199354  */
2685440Sjm199354 int
_fini(void)2695440Sjm199354 _fini(void)
2705440Sjm199354 {
2715440Sjm199354 	int rc;
2725440Sjm199354 
2735440Sjm199354 	if (vscan_drv_in_use())
2745440Sjm199354 		return (EBUSY);
2755440Sjm199354 
2765440Sjm199354 	if ((rc = mod_remove(&modlinkage)) == 0) {
2775440Sjm199354 		vscan_door_fini();
2785440Sjm199354 		vscan_svc_fini();
2796407Sjm199354 		kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
2805931Sjm199354 		cv_destroy(&vscan_drv_cv);
2815440Sjm199354 		mutex_destroy(&vscan_drv_mutex);
2825440Sjm199354 	}
2835440Sjm199354 
2845440Sjm199354 	return (rc);
2855440Sjm199354 }
2865440Sjm199354 
2875440Sjm199354 
2885440Sjm199354 /*
2895440Sjm199354  * DDI entry points.
2905440Sjm199354  */
2915440Sjm199354 
2925440Sjm199354 /*
2935440Sjm199354  * vscan_drv_getinfo
2945440Sjm199354  */
2955440Sjm199354 /* ARGSUSED */
2965440Sjm199354 static int
vscan_drv_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)2975440Sjm199354 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
2985440Sjm199354 {
2995440Sjm199354 	ulong_t inst = getminor((dev_t)arg);
3005440Sjm199354 
3015440Sjm199354 	switch (cmd) {
3025440Sjm199354 	case DDI_INFO_DEVT2DEVINFO:
3035440Sjm199354 		*result = vscan_drv_dip;
3045440Sjm199354 		return (DDI_SUCCESS);
3055440Sjm199354 	case DDI_INFO_DEVT2INSTANCE:
3065440Sjm199354 		*result = (void *)inst;
3075440Sjm199354 		return (DDI_SUCCESS);
3085440Sjm199354 	}
3095440Sjm199354 	return (DDI_FAILURE);
3105440Sjm199354 }
3115440Sjm199354 
3125440Sjm199354 
3135440Sjm199354 /*
3145440Sjm199354  * vscan_drv_attach
3155440Sjm199354  */
3165440Sjm199354 static int
vscan_drv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3175440Sjm199354 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3185440Sjm199354 {
3195440Sjm199354 	if (cmd != DDI_ATTACH)
3205440Sjm199354 		return (DDI_FAILURE);
3215440Sjm199354 
3225440Sjm199354 	if (ddi_get_instance(dip) != 0)
3235440Sjm199354 		return (DDI_FAILURE);
3245440Sjm199354 
3255440Sjm199354 	vscan_drv_dip = dip;
3265440Sjm199354 
3275931Sjm199354 	/* create minor node 0 for daemon-driver synchronization */
3285931Sjm199354 	if (vscan_drv_create_node(0) == B_FALSE)
3295931Sjm199354 		return (DDI_FAILURE);
3305440Sjm199354 
3316407Sjm199354 	vscan_drv_state = VS_DRV_IDLE;
3325440Sjm199354 	return (DDI_SUCCESS);
3335440Sjm199354 }
3345440Sjm199354 
3355440Sjm199354 
3365440Sjm199354 /*
3375440Sjm199354  * vscan_drv_detach
3385440Sjm199354  */
3395440Sjm199354 static int
vscan_drv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3405440Sjm199354 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3415440Sjm199354 {
3426407Sjm199354 	int i;
3436407Sjm199354 
3445440Sjm199354 	if (cmd != DDI_DETACH)
3455440Sjm199354 		return (DDI_FAILURE);
3465440Sjm199354 
3475440Sjm199354 	if (ddi_get_instance(dip) != 0)
3485440Sjm199354 		return (DDI_FAILURE);
3495440Sjm199354 
3505440Sjm199354 	if (vscan_drv_in_use())
3515440Sjm199354 		return (DDI_FAILURE);
3525440Sjm199354 
3535931Sjm199354 	/* remove all minor nodes */
3545440Sjm199354 	vscan_drv_dip = NULL;
3555440Sjm199354 	ddi_remove_minor_node(dip, NULL);
3566407Sjm199354 	for (i = 0; i <= vs_nodes_max; i++)
3576407Sjm199354 		vscan_drv_inst_state[i] = VS_DRV_INST_UNCONFIG;
3585440Sjm199354 
3596407Sjm199354 	vscan_drv_state = VS_DRV_UNCONFIG;
3605440Sjm199354 	return (DDI_SUCCESS);
3615440Sjm199354 }
3625440Sjm199354 
3635440Sjm199354 
3645440Sjm199354 /*
3655440Sjm199354  * vscan_drv_in_use
3665931Sjm199354  *
3676407Sjm199354  * If the driver state is not IDLE or UNCONFIG then the
3686407Sjm199354  * driver is in use. Otherwise, check the service interface
3696407Sjm199354  * (vscan_svc) to see if it is still in use - for example
3706407Sjm199354  * there there may be requests still in progress.
3715440Sjm199354  */
3725440Sjm199354 static boolean_t
vscan_drv_in_use()3735440Sjm199354 vscan_drv_in_use()
3745440Sjm199354 {
3756407Sjm199354 	boolean_t in_use = B_FALSE;
3765931Sjm199354 
3775931Sjm199354 	mutex_enter(&vscan_drv_mutex);
3786407Sjm199354 	if ((vscan_drv_state != VS_DRV_IDLE) &&
3796407Sjm199354 	    (vscan_drv_state != VS_DRV_UNCONFIG)) {
3806407Sjm199354 		in_use = B_TRUE;
3816407Sjm199354 	}
3825931Sjm199354 	mutex_exit(&vscan_drv_mutex);
3835931Sjm199354 
3846407Sjm199354 	if (in_use)
3856407Sjm199354 		return (B_TRUE);
3866407Sjm199354 	else
3876407Sjm199354 		return (vscan_svc_in_use());
3885440Sjm199354 }
3895440Sjm199354 
3905440Sjm199354 
3915440Sjm199354 /*
3925440Sjm199354  * vscan_drv_open
3936407Sjm199354  *
3946407Sjm199354  * If inst == 0, this is vscand initializing.
3956407Sjm199354  * If the driver is in DELAYED_DISABLE, ie vscand previously
3966407Sjm199354  * disconnected without a clean shutdown and the driver is
3976407Sjm199354  * waiting for a period to allow vscand to reconnect, signal
3986407Sjm199354  * vscan_drv_cv to cancel the delayed disable.
3996407Sjm199354  *
4006407Sjm199354  * If inst != 0, open the file associated with inst.
4015440Sjm199354  */
4025440Sjm199354 /* ARGSUSED */
4035440Sjm199354 static int
vscan_drv_open(dev_t * devp,int flag,int otyp,cred_t * credp)4045440Sjm199354 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4055440Sjm199354 {
4065440Sjm199354 	int rc;
4075440Sjm199354 	int inst = getminor(*devp);
4085440Sjm199354 
4096407Sjm199354 	if ((inst < 0) || (inst > vs_nodes_max))
4105440Sjm199354 		return (EINVAL);
4115440Sjm199354 
4125440Sjm199354 	/* check if caller has privilege for virus scanning */
4135440Sjm199354 	if ((rc = secpolicy_vscan(credp)) != 0) {
4145440Sjm199354 		DTRACE_PROBE1(vscan__priv, int, rc);
4155440Sjm199354 		return (EPERM);
4165440Sjm199354 	}
4175440Sjm199354 
4185440Sjm199354 	mutex_enter(&vscan_drv_mutex);
4195440Sjm199354 	if (inst == 0) {
4206407Sjm199354 		switch (vscan_drv_state) {
4216407Sjm199354 		case VS_DRV_IDLE:
4226407Sjm199354 			vscan_drv_state = VS_DRV_CONNECTED;
4236407Sjm199354 			break;
4246407Sjm199354 		case VS_DRV_DELAYED_DISABLE:
4256407Sjm199354 			cv_signal(&vscan_drv_cv);
4266407Sjm199354 			vscan_drv_state = VS_DRV_CONNECTED;
4276407Sjm199354 			break;
4286407Sjm199354 		default:
4296407Sjm199354 			DTRACE_PROBE1(vscan__drv__state__violation,
4306407Sjm199354 			    int, vscan_drv_state);
4315440Sjm199354 			mutex_exit(&vscan_drv_mutex);
4325440Sjm199354 			return (EINVAL);
4335440Sjm199354 		}
4345440Sjm199354 	} else {
4356407Sjm199354 		if ((vscan_drv_state != VS_DRV_ENABLED) ||
4366407Sjm199354 		    (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) {
4376407Sjm199354 			mutex_exit(&vscan_drv_mutex);
4386407Sjm199354 			return (EINVAL);
4395440Sjm199354 		}
4406407Sjm199354 		vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
4415440Sjm199354 	}
4425440Sjm199354 	mutex_exit(&vscan_drv_mutex);
4435440Sjm199354 
4445440Sjm199354 	return (0);
4455440Sjm199354 }
4465440Sjm199354 
4475440Sjm199354 
4485440Sjm199354 /*
4495440Sjm199354  * vscan_drv_close
4506407Sjm199354  *
4516407Sjm199354  * If inst == 0, this is vscand detaching.
4526407Sjm199354  * If the driver is in ENABLED state vscand has terminated without
4536407Sjm199354  * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE
4546407Sjm199354  * state and initiate a delayed disable to allow vscand time to
4556407Sjm199354  * reconnect.
4566407Sjm199354  *
4576407Sjm199354  * If inst != 0, close the file associated with inst
4585440Sjm199354  */
4595440Sjm199354 /* ARGSUSED */
4605440Sjm199354 static int
vscan_drv_close(dev_t dev,int flag,int otyp,cred_t * credp)4615440Sjm199354 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
4625440Sjm199354 {
4635440Sjm199354 	int i, inst = getminor(dev);
4645440Sjm199354 
4656407Sjm199354 	if ((inst < 0) || (inst > vs_nodes_max))
4665440Sjm199354 		return (EINVAL);
4675440Sjm199354 
4685440Sjm199354 	mutex_enter(&vscan_drv_mutex);
4696407Sjm199354 	if (inst != 0) {
4706407Sjm199354 		vscan_drv_inst_state[inst] = VS_DRV_INST_INIT;
4716407Sjm199354 		mutex_exit(&vscan_drv_mutex);
4726407Sjm199354 		return (0);
4736407Sjm199354 	}
4746407Sjm199354 
4756407Sjm199354 	/* instance 0 - daemon disconnect */
4766407Sjm199354 	if ((vscan_drv_state != VS_DRV_CONNECTED) &&
4776407Sjm199354 	    (vscan_drv_state != VS_DRV_ENABLED)) {
4786407Sjm199354 		DTRACE_PROBE1(vscan__drv__state__violation,
4796407Sjm199354 		    int, vscan_drv_state);
4806407Sjm199354 		mutex_exit(&vscan_drv_mutex);
4816407Sjm199354 		return (EINVAL);
4826407Sjm199354 	}
4835440Sjm199354 
4846407Sjm199354 	for (i = 1; i <= vs_nodes_max; i++) {
4856407Sjm199354 		if (vscan_drv_inst_state[i] != VS_DRV_INST_UNCONFIG)
4866407Sjm199354 			vscan_drv_inst_state[i] = VS_DRV_INST_INIT;
4876407Sjm199354 	}
4886407Sjm199354 
4896407Sjm199354 	if (vscan_drv_state == VS_DRV_CONNECTED) {
4906407Sjm199354 		vscan_drv_state = VS_DRV_IDLE;
4916407Sjm199354 	} else { /* VS_DRV_ENABLED */
4926407Sjm199354 		cmn_err(CE_WARN, "Detected vscand exit without clean shutdown");
4936407Sjm199354 		if (thread_create(NULL, 0, vscan_drv_delayed_disable,
4946407Sjm199354 		    0, 0, &p0, TS_RUN, minclsyspri) == NULL) {
4956407Sjm199354 			vscan_svc_disable();
4966407Sjm199354 			vscan_drv_state = VS_DRV_IDLE;
4976407Sjm199354 		} else {
4986407Sjm199354 			vscan_drv_state = VS_DRV_DELAYED_DISABLE;
4995931Sjm199354 		}
5005440Sjm199354 	}
5015440Sjm199354 	mutex_exit(&vscan_drv_mutex);
5025440Sjm199354 
5036407Sjm199354 	vscan_svc_scan_abort();
5046407Sjm199354 	vscan_door_close();
5055440Sjm199354 	return (0);
5065440Sjm199354 }
5075440Sjm199354 
5085440Sjm199354 
5095440Sjm199354 /*
5105931Sjm199354  * vscan_drv_delayed_disable
5115931Sjm199354  *
5125931Sjm199354  * Invoked from vscan_drv_close if the daemon disconnects
5135931Sjm199354  * without first sending disable (e.g. daemon crashed).
5146407Sjm199354  * Delays for vs_reconnect_timeout before disabling, to allow
5155931Sjm199354  * the daemon to reconnect. During this time, scan requests
5165931Sjm199354  * will be processed locally (see vscan_svc.c)
5175931Sjm199354  */
5185931Sjm199354 static void
vscan_drv_delayed_disable(void)5195931Sjm199354 vscan_drv_delayed_disable(void)
5205931Sjm199354 {
5215931Sjm199354 	mutex_enter(&vscan_drv_mutex);
522*11066Srafael.vanoni@sun.com 	(void) cv_reltimedwait(&vscan_drv_cv, &vscan_drv_mutex,
523*11066Srafael.vanoni@sun.com 	    SEC_TO_TICK(vs_reconnect_timeout), TR_CLOCK_TICK);
5245931Sjm199354 
5256407Sjm199354 	if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) {
5266407Sjm199354 		vscan_svc_disable();
5276407Sjm199354 		vscan_drv_state = VS_DRV_IDLE;
5286407Sjm199354 	} else {
5295931Sjm199354 		DTRACE_PROBE(vscan__reconnect);
5305931Sjm199354 	}
5315931Sjm199354 	mutex_exit(&vscan_drv_mutex);
5325931Sjm199354 }
5335931Sjm199354 
5345931Sjm199354 
5355931Sjm199354 /*
5365440Sjm199354  * vscan_drv_read
5375440Sjm199354  */
5385440Sjm199354 /* ARGSUSED */
5395440Sjm199354 static int
vscan_drv_read(dev_t dev,struct uio * uiop,cred_t * credp)5405440Sjm199354 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp)
5415440Sjm199354 {
5425440Sjm199354 	int rc;
5435440Sjm199354 	int inst = getminor(dev);
5445440Sjm199354 	vnode_t *vp;
5455440Sjm199354 
5466407Sjm199354 	if ((inst <= 0) || (inst > vs_nodes_max))
5475440Sjm199354 		return (EINVAL);
5485440Sjm199354 
5495440Sjm199354 	mutex_enter(&vscan_drv_mutex);
5506407Sjm199354 	if ((vscan_drv_state != VS_DRV_ENABLED) ||
5516407Sjm199354 	    (vscan_drv_inst_state[inst] != VS_DRV_INST_OPEN)) {
5525440Sjm199354 		mutex_exit(&vscan_drv_mutex);
5535440Sjm199354 		return (EINVAL);
5545440Sjm199354 	}
5556407Sjm199354 	vscan_drv_inst_state[inst] = VS_DRV_INST_READING;
5565440Sjm199354 	mutex_exit(&vscan_drv_mutex);
5575440Sjm199354 
5585440Sjm199354 	if ((vp = vscan_svc_get_vnode(inst)) == NULL)
5595440Sjm199354 		return (EINVAL);
5605440Sjm199354 
5615440Sjm199354 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
5625440Sjm199354 	rc = VOP_READ(vp, uiop, 0, kcred, NULL);
5635440Sjm199354 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
5645440Sjm199354 
5655440Sjm199354 	mutex_enter(&vscan_drv_mutex);
5666407Sjm199354 	if (vscan_drv_inst_state[inst] == VS_DRV_INST_READING)
5676407Sjm199354 		vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
5685440Sjm199354 	mutex_exit(&vscan_drv_mutex);
5695440Sjm199354 
5705440Sjm199354 	return (rc);
5715440Sjm199354 }
5725440Sjm199354 
5735440Sjm199354 
5745440Sjm199354 /*
5755440Sjm199354  * vscan_drv_ioctl
5766407Sjm199354  *
5776407Sjm199354  * Process ioctls from vscand:
5786407Sjm199354  * VS_IOCTL_ENABLE - vscand is ready to handle scan requests,
5796407Sjm199354  *    enable VFS interface.
5806407Sjm199354  * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface
5816407Sjm199354  * VS_IOCTL_RESULT - scan response data
5826407Sjm199354  * VS_IOCTL_CONFIG - configuration data from vscand
5836407Sjm199354  * VS_IOCTL_MAX_REQ - provide the max request idx to vscand,
5846407Sjm199354  *    to allow vscand to set appropriate resource allocation limits
5855440Sjm199354  */
5865440Sjm199354 /* ARGSUSED */
5875440Sjm199354 static int
vscan_drv_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)5885440Sjm199354 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
5895440Sjm199354 	cred_t *credp, int *rvalp)
5905440Sjm199354 {
5915440Sjm199354 	int inst = getminor(dev);
5925440Sjm199354 	vs_config_t conf;
5936407Sjm199354 	vs_scan_rsp_t rsp;
5945440Sjm199354 
5955440Sjm199354 	if (inst != 0)
5965440Sjm199354 		return (EINVAL);
5975440Sjm199354 
5985440Sjm199354 	switch (cmd) {
5996407Sjm199354 	case VS_IOCTL_ENABLE:
6005440Sjm199354 		mutex_enter(&vscan_drv_mutex);
6016407Sjm199354 		if (vscan_drv_state != VS_DRV_CONNECTED) {
6026407Sjm199354 			DTRACE_PROBE1(vscan__drv__state__violation,
6036407Sjm199354 			    int, vscan_drv_state);
6046407Sjm199354 			mutex_exit(&vscan_drv_mutex);
6056407Sjm199354 			return (EINVAL);
6066407Sjm199354 		}
6076407Sjm199354 		if ((vscan_door_open((int)arg) != 0) ||
6086407Sjm199354 		    (vscan_svc_enable() != 0)) {
6095440Sjm199354 			mutex_exit(&vscan_drv_mutex);
6105440Sjm199354 			return (EINVAL);
6115440Sjm199354 		}
6126407Sjm199354 		vscan_drv_state = VS_DRV_ENABLED;
6135440Sjm199354 		mutex_exit(&vscan_drv_mutex);
6145440Sjm199354 		break;
6156407Sjm199354 
6166407Sjm199354 	case VS_IOCTL_DISABLE:
6176407Sjm199354 		mutex_enter(&vscan_drv_mutex);
6186407Sjm199354 		if (vscan_drv_state != VS_DRV_ENABLED) {
6196407Sjm199354 			DTRACE_PROBE1(vscan__drv__state__violation,
6206407Sjm199354 			    int, vscan_drv_state);
6216407Sjm199354 			mutex_exit(&vscan_drv_mutex);
6226407Sjm199354 			return (EINVAL);
6236407Sjm199354 		}
6245931Sjm199354 		vscan_svc_disable();
6256407Sjm199354 		vscan_drv_state = VS_DRV_CONNECTED;
6266407Sjm199354 		mutex_exit(&vscan_drv_mutex);
6275440Sjm199354 		break;
6286407Sjm199354 
6296407Sjm199354 	case VS_IOCTL_RESULT:
6306407Sjm199354 		if (ddi_copyin((void *)arg, &rsp,
6316407Sjm199354 		    sizeof (vs_scan_rsp_t), 0) == -1)
6326407Sjm199354 			return (EFAULT);
6336407Sjm199354 		else
6346407Sjm199354 			vscan_svc_scan_result(&rsp);
6356407Sjm199354 		break;
6366407Sjm199354 
6376407Sjm199354 	case VS_IOCTL_CONFIG:
6385440Sjm199354 		if (ddi_copyin((void *)arg, &conf,
6395440Sjm199354 		    sizeof (vs_config_t), 0) == -1)
6405440Sjm199354 			return (EFAULT);
6415440Sjm199354 		if (vscan_svc_configure(&conf) == -1)
6425440Sjm199354 			return (EINVAL);
6435440Sjm199354 		break;
6446407Sjm199354 
6456407Sjm199354 	case VS_IOCTL_MAX_REQ:
6466407Sjm199354 		if (ddi_copyout(&vs_nodes_max, (void *)arg,
6476407Sjm199354 		    sizeof (uint32_t), 0) == -1)
6486407Sjm199354 			return (EFAULT);
6496407Sjm199354 		break;
6506407Sjm199354 
6515440Sjm199354 	default:
6525440Sjm199354 		return (ENOTTY);
6535440Sjm199354 	}
6545440Sjm199354 
6555440Sjm199354 	return (0);
6565440Sjm199354 }
6575931Sjm199354 
6585931Sjm199354 
6595931Sjm199354 /*
6605931Sjm199354  * vscan_drv_create_node
6615931Sjm199354  *
6625931Sjm199354  * Create minor node with which vscan daemon will communicate
6635931Sjm199354  * to access a file. Invoked from vscan_svc before scan request
6645931Sjm199354  * sent up to daemon.
6655931Sjm199354  * Minor node 0 is reserved for daemon-driver synchronization
6665931Sjm199354  * and is created during attach.
6675931Sjm199354  * All minor nodes are removed during detach.
6685931Sjm199354  */
6695931Sjm199354 boolean_t
vscan_drv_create_node(int idx)6705931Sjm199354 vscan_drv_create_node(int idx)
6715931Sjm199354 {
6726407Sjm199354 	char name[VS_NODENAME_LEN];
6736407Sjm199354 	boolean_t rc = B_TRUE;
6745931Sjm199354 
6755931Sjm199354 	mutex_enter(&vscan_drv_mutex);
6765931Sjm199354 
6776407Sjm199354 	if (vscan_drv_inst_state[idx] == VS_DRV_INST_UNCONFIG) {
6786407Sjm199354 		(void) snprintf(name, VS_NODENAME_LEN, "vscan%d", idx);
6795931Sjm199354 		if (ddi_create_minor_node(vscan_drv_dip, name,
6805931Sjm199354 		    S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) {
6816407Sjm199354 			vscan_drv_inst_state[idx] = VS_DRV_INST_INIT;
6826407Sjm199354 		} else {
6836407Sjm199354 			rc = B_FALSE;
6845931Sjm199354 		}
6856407Sjm199354 		DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc);
6865931Sjm199354 	}
6875931Sjm199354 
6885931Sjm199354 	mutex_exit(&vscan_drv_mutex);
6895931Sjm199354 
6905931Sjm199354 	return (rc);
6915931Sjm199354 }
692