xref: /onnv-gate/usr/src/cmd/sgs/librtld_db/common/rtld_db.c (revision 6830:296b2ea3c777)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52712Snn35248  * Common Development and Distribution License (the "License").
62712Snn35248  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*6830Sedp  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include	<stdlib.h>
300Sstevel@tonic-gate #include	<stdio.h>
312712Snn35248 #include	<string.h>
320Sstevel@tonic-gate #include	<proc_service.h>
330Sstevel@tonic-gate #include	<link.h>
340Sstevel@tonic-gate #include	<rtld_db.h>
350Sstevel@tonic-gate #include	<rtld.h>
360Sstevel@tonic-gate #include	<_rtld_db.h>
370Sstevel@tonic-gate #include	<msg.h>
382712Snn35248 #include	<sys/param.h>
390Sstevel@tonic-gate 
400Sstevel@tonic-gate /*
410Sstevel@tonic-gate  * Mutex to protect global data
420Sstevel@tonic-gate  */
430Sstevel@tonic-gate mutex_t	glob_mutex = DEFAULTMUTEX;
440Sstevel@tonic-gate int	rtld_db_version = RD_VERSION1;
450Sstevel@tonic-gate int	rtld_db_logging = 0;
462712Snn35248 char	rtld_db_helper_path[MAXPATHLEN];
470Sstevel@tonic-gate 
480Sstevel@tonic-gate 
490Sstevel@tonic-gate void
rd_log(const int on_off)500Sstevel@tonic-gate rd_log(const int on_off)
510Sstevel@tonic-gate {
520Sstevel@tonic-gate 	(void) mutex_lock(&glob_mutex);
530Sstevel@tonic-gate 	rtld_db_logging = on_off;
540Sstevel@tonic-gate 	(void) mutex_unlock(&glob_mutex);
550Sstevel@tonic-gate 	LOG(ps_plog(MSG_ORIG(MSG_DB_LOGENABLE)));
560Sstevel@tonic-gate }
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /*
590Sstevel@tonic-gate  * Versioning Notes.
600Sstevel@tonic-gate  *
610Sstevel@tonic-gate  * The following have been added as the versions of librtld_db
620Sstevel@tonic-gate  * have grown:
630Sstevel@tonic-gate  *
640Sstevel@tonic-gate  *	RD_VERSION1:
650Sstevel@tonic-gate  *		o baseline version
660Sstevel@tonic-gate  *
670Sstevel@tonic-gate  *	RD_VERSION2:
680Sstevel@tonic-gate  *		o added support for the use of the AT_SUN_LDBASE auxvector
690Sstevel@tonic-gate  *		  to find the initialial debugging (r_debug) structures
700Sstevel@tonic-gate  *		  in ld.so.1
710Sstevel@tonic-gate  *		o added the rl_dynamic field to rd_loadobj_t
720Sstevel@tonic-gate  *		o added the RD_FLG_MEM_OBJECT to be used with the
730Sstevel@tonic-gate  *		  rl_dynamic->rl_flags field.
740Sstevel@tonic-gate  *
750Sstevel@tonic-gate  *	RD_VERSION3:
760Sstevel@tonic-gate  *		o added the following fields/flags to the rd_plt_info_t
770Sstevel@tonic-gate  *		  type:
780Sstevel@tonic-gate  *			pi_baddr	- bound address of PLT (if bound)
790Sstevel@tonic-gate  *			pi_flags	- flag field
800Sstevel@tonic-gate  *			RD_FLG_PI_PLTBOUND	(flag for pi_flags)
810Sstevel@tonic-gate  *				if set - the PLT is bound and pi_baddr
820Sstevel@tonic-gate  *				is filled in with the destination of the PLT.
830Sstevel@tonic-gate  *
840Sstevel@tonic-gate  *	RD_VERSION4:
850Sstevel@tonic-gate  *		o added the following field to the rd_loadobj_t structure:
860Sstevel@tonic-gate  *			rl_tlsmodid	- module ID for TLS references
870Sstevel@tonic-gate  */
880Sstevel@tonic-gate rd_err_e
rd_init(int version)890Sstevel@tonic-gate rd_init(int version)
900Sstevel@tonic-gate {
910Sstevel@tonic-gate 	if ((version < RD_VERSION1) ||
920Sstevel@tonic-gate 	    (version > RD_VERSION))
930Sstevel@tonic-gate 		return (RD_NOCAPAB);
940Sstevel@tonic-gate 	rtld_db_version = version;
950Sstevel@tonic-gate 	LOG(ps_plog(MSG_ORIG(MSG_DB_RDINIT), rtld_db_version));
962712Snn35248 
970Sstevel@tonic-gate 	return (RD_OK);
980Sstevel@tonic-gate }
990Sstevel@tonic-gate 
1002712Snn35248 rd_err_e
rd_ctl(int cmd,void * arg)1012712Snn35248 rd_ctl(int cmd, void *arg)
1022712Snn35248 {
1032712Snn35248 	if (cmd != RD_CTL_SET_HELPPATH || arg == NULL ||
1042712Snn35248 	    strlen((char *)arg) >= MAXPATHLEN)
1052712Snn35248 		return (RD_ERR);
1062712Snn35248 
1072712Snn35248 	(void) strcpy(rtld_db_helper_path, (char *)arg);
1082712Snn35248 
1092712Snn35248 	return (RD_OK);
1102712Snn35248 }
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate rd_err_e
rd_get_dyns(rd_agent_t * rap,psaddr_t addr,void ** dynpp,size_t * dynpp_sz)113*6830Sedp rd_get_dyns(rd_agent_t *rap, psaddr_t addr, void **dynpp, size_t *dynpp_sz)
114*6830Sedp {
115*6830Sedp 	if (rap->rd_helper.rh_ops != NULL)
116*6830Sedp 		return (rap->rd_helper.rh_ops->rho_get_dyns(
117*6830Sedp 		    rap->rd_helper.rh_data, addr, dynpp, dynpp_sz));
118*6830Sedp 
119*6830Sedp #ifdef _LP64
120*6830Sedp 	if (rap->rd_dmodel == PR_MODEL_LP64)
121*6830Sedp 		return (_rd_get_dyns64(rap,
122*6830Sedp 		    addr, (Elf64_Dyn **)dynpp, dynpp_sz));
123*6830Sedp 	else
124*6830Sedp #endif
125*6830Sedp 		return (_rd_get_dyns32(rap,
126*6830Sedp 		    addr, (Dyn **)dynpp, dynpp_sz));
127*6830Sedp }
128*6830Sedp 
129*6830Sedp rd_err_e
rd_reset(struct rd_agent * rap)1300Sstevel@tonic-gate rd_reset(struct rd_agent *rap)
1310Sstevel@tonic-gate {
1320Sstevel@tonic-gate 	rd_err_e			err;
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate 	RDAGLOCK(rap);
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 	rap->rd_flags = 0;
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate #ifdef _LP64
1390Sstevel@tonic-gate 	/*
1400Sstevel@tonic-gate 	 * Determine if client is 32-bit or 64-bit.
1410Sstevel@tonic-gate 	 */
1420Sstevel@tonic-gate 	if (ps_pdmodel(rap->rd_psp, &rap->rd_dmodel) != PS_OK) {
1430Sstevel@tonic-gate 		LOG(ps_plog(MSG_ORIG(MSG_DB_DMLOOKFAIL)));
1440Sstevel@tonic-gate 		RDAGUNLOCK(rap);
1450Sstevel@tonic-gate 		return (RD_DBERR);
1460Sstevel@tonic-gate 	}
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 	if (rap->rd_dmodel == PR_MODEL_LP64)
1490Sstevel@tonic-gate 		err = _rd_reset64(rap);
1500Sstevel@tonic-gate 	else
1510Sstevel@tonic-gate #endif
1520Sstevel@tonic-gate 		err = _rd_reset32(rap);
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 	RDAGUNLOCK(rap);
1550Sstevel@tonic-gate 	return (err);
1560Sstevel@tonic-gate }
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate rd_agent_t *
rd_new(struct ps_prochandle * php)1600Sstevel@tonic-gate rd_new(struct ps_prochandle *php)
1610Sstevel@tonic-gate {
1620Sstevel@tonic-gate 	rd_agent_t	*rap;
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	LOG(ps_plog(MSG_ORIG(MSG_DB_RDNEW), php));
1650Sstevel@tonic-gate 	if ((rap = (rd_agent_t *)calloc(sizeof (rd_agent_t), 1)) == NULL)
1660Sstevel@tonic-gate 		return (0);
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	rap->rd_psp = php;
1690Sstevel@tonic-gate 	(void) mutex_init(&rap->rd_mutex, USYNC_THREAD, 0);
1700Sstevel@tonic-gate 	if (rd_reset(rap) != RD_OK) {
171*6830Sedp 		if (rap->rd_helper.rh_dlhandle != NULL) {
172*6830Sedp 			rap->rd_helper.rh_ops->rho_fini(rap->rd_helper.rh_data);
173*6830Sedp 			(void) dlclose(rap->rd_helper.rh_dlhandle);
174*6830Sedp 		}
1750Sstevel@tonic-gate 		free(rap);
1760Sstevel@tonic-gate 		LOG(ps_plog(MSG_ORIG(MSG_DB_RESETFAIL)));
1770Sstevel@tonic-gate 		return ((rd_agent_t *)0);
1780Sstevel@tonic-gate 	}
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	return (rap);
1810Sstevel@tonic-gate }
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate void
rd_delete(rd_agent_t * rap)1850Sstevel@tonic-gate rd_delete(rd_agent_t *rap)
1860Sstevel@tonic-gate {
1870Sstevel@tonic-gate 	LOG(ps_plog(MSG_ORIG(MSG_DB_RDDELETE), rap));
188*6830Sedp 	if (rap->rd_helper.rh_dlhandle != NULL) {
189*6830Sedp 		rap->rd_helper.rh_ops->rho_fini(rap->rd_helper.rh_data);
190*6830Sedp 		(void) dlclose(rap->rd_helper.rh_dlhandle);
191*6830Sedp 	}
1920Sstevel@tonic-gate 	free(rap);
1930Sstevel@tonic-gate }
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate rd_err_e
rd_loadobj_iter(rd_agent_t * rap,rl_iter_f * cb,void * client_data)1970Sstevel@tonic-gate rd_loadobj_iter(rd_agent_t *rap, rl_iter_f *cb, void *client_data)
1980Sstevel@tonic-gate {
1990Sstevel@tonic-gate 	rd_err_e	err;
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	RDAGLOCK(rap);
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate #ifdef _LP64
2040Sstevel@tonic-gate 	if (rap->rd_dmodel == PR_MODEL_LP64)
2050Sstevel@tonic-gate 		err = _rd_loadobj_iter64(rap, cb, client_data);
2060Sstevel@tonic-gate 	else
2070Sstevel@tonic-gate #endif
2080Sstevel@tonic-gate 		err = _rd_loadobj_iter32(rap, cb, client_data);
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	RDAGUNLOCK(rap);
2110Sstevel@tonic-gate 	return (err);
2120Sstevel@tonic-gate }
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate rd_err_e
rd_plt_resolution(rd_agent_t * rap,psaddr_t pc,lwpid_t lwpid,psaddr_t pltbase,rd_plt_info_t * rpi)2160Sstevel@tonic-gate rd_plt_resolution(rd_agent_t *rap, psaddr_t pc, lwpid_t lwpid,
2170Sstevel@tonic-gate 	psaddr_t pltbase, rd_plt_info_t *rpi)
2180Sstevel@tonic-gate {
2190Sstevel@tonic-gate 	rd_err_e	err;
2200Sstevel@tonic-gate 	RDAGLOCK(rap);
2210Sstevel@tonic-gate #ifdef	_LP64
2220Sstevel@tonic-gate 	if (rap->rd_dmodel == PR_MODEL_LP64)
2230Sstevel@tonic-gate 		err = plt64_resolution(rap, pc, lwpid, pltbase,
2240Sstevel@tonic-gate 		    rpi);
2250Sstevel@tonic-gate 	else
2260Sstevel@tonic-gate #endif
2270Sstevel@tonic-gate 		err = plt32_resolution(rap, pc, lwpid, pltbase,
2280Sstevel@tonic-gate 		    rpi);
2290Sstevel@tonic-gate 	RDAGUNLOCK(rap);
2300Sstevel@tonic-gate 	return (err);
2310Sstevel@tonic-gate }
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate rd_err_e
rd_event_addr(rd_agent_t * rap,rd_event_e num,rd_notify_t * np)2340Sstevel@tonic-gate rd_event_addr(rd_agent_t *rap, rd_event_e num, rd_notify_t *np)
2350Sstevel@tonic-gate {
2360Sstevel@tonic-gate 	rd_err_e	rc = RD_OK;
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	RDAGLOCK(rap);
2390Sstevel@tonic-gate 	switch (num) {
2400Sstevel@tonic-gate 	case RD_NONE:
2410Sstevel@tonic-gate 		break;
2420Sstevel@tonic-gate 	case RD_PREINIT:
2430Sstevel@tonic-gate 		np->type = RD_NOTIFY_BPT;
2440Sstevel@tonic-gate 		np->u.bptaddr = rap->rd_preinit;
2450Sstevel@tonic-gate 		break;
2460Sstevel@tonic-gate 	case RD_POSTINIT:
2470Sstevel@tonic-gate 		np->type = RD_NOTIFY_BPT;
2480Sstevel@tonic-gate 		np->u.bptaddr = rap->rd_postinit;
2490Sstevel@tonic-gate 		break;
2500Sstevel@tonic-gate 	case RD_DLACTIVITY:
2510Sstevel@tonic-gate 		np->type = RD_NOTIFY_BPT;
2520Sstevel@tonic-gate 		np->u.bptaddr = rap->rd_dlact;
2530Sstevel@tonic-gate 		break;
2540Sstevel@tonic-gate 	default:
2550Sstevel@tonic-gate 		LOG(ps_plog(MSG_ORIG(MSG_DB_UNEXPEVENT), num));
2560Sstevel@tonic-gate 		rc = RD_ERR;
2570Sstevel@tonic-gate 		break;
2580Sstevel@tonic-gate 	}
2590Sstevel@tonic-gate 	if (rc == RD_OK) {
2600Sstevel@tonic-gate 		LOG(ps_plog(MSG_ORIG(MSG_DB_RDEVENTADDR), num,
2612712Snn35248 		    EC_ADDR(np->u.bptaddr)));
2620Sstevel@tonic-gate 	}
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	RDAGUNLOCK(rap);
2650Sstevel@tonic-gate 	return (rc);
2660Sstevel@tonic-gate }
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate /* ARGSUSED 0 */
2700Sstevel@tonic-gate rd_err_e
rd_event_enable(rd_agent_t * rap,int onoff)2710Sstevel@tonic-gate rd_event_enable(rd_agent_t *rap, int onoff)
2720Sstevel@tonic-gate {
2730Sstevel@tonic-gate 	rd_err_e	err;
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	RDAGLOCK(rap);
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate #ifdef _LP64
2780Sstevel@tonic-gate 	if (rap->rd_dmodel == PR_MODEL_LP64)
2790Sstevel@tonic-gate 		err = _rd_event_enable64(rap, onoff);
2800Sstevel@tonic-gate 	else
2810Sstevel@tonic-gate #endif
2820Sstevel@tonic-gate 		err = _rd_event_enable32(rap, onoff);
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 	RDAGUNLOCK(rap);
2850Sstevel@tonic-gate 	return (err);
2860Sstevel@tonic-gate }
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate rd_err_e
rd_event_getmsg(rd_agent_t * rap,rd_event_msg_t * emsg)2900Sstevel@tonic-gate rd_event_getmsg(rd_agent_t *rap, rd_event_msg_t *emsg)
2910Sstevel@tonic-gate {
2920Sstevel@tonic-gate 	rd_err_e	err;
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	RDAGLOCK(rap);
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate #ifdef _LP64
2970Sstevel@tonic-gate 	if (rap->rd_dmodel == PR_MODEL_LP64)
2980Sstevel@tonic-gate 		err = _rd_event_getmsg64(rap, emsg);
2990Sstevel@tonic-gate 	else
3000Sstevel@tonic-gate #endif
3010Sstevel@tonic-gate 		err = _rd_event_getmsg32(rap, emsg);
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 	RDAGUNLOCK(rap);
3040Sstevel@tonic-gate 	return (err);
3050Sstevel@tonic-gate }
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate rd_err_e
rd_binder_exit_addr(struct rd_agent * rap,const char * bname,psaddr_t * beaddr)3090Sstevel@tonic-gate rd_binder_exit_addr(struct rd_agent *rap, const char *bname, psaddr_t *beaddr)
3100Sstevel@tonic-gate {
3110Sstevel@tonic-gate 	ps_sym_t	sym;
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	if (rap->rd_tbinder) {
3140Sstevel@tonic-gate 		*beaddr = rap->rd_tbinder;
3150Sstevel@tonic-gate 		return (RD_OK);
3160Sstevel@tonic-gate 	}
3170Sstevel@tonic-gate 	if (ps_pglobal_sym(rap->rd_psp, PS_OBJ_LDSO, bname, &sym) != PS_OK) {
3180Sstevel@tonic-gate 		LOG(ps_plog(MSG_ORIG(MSG_DB_UNFNDSYM),
3190Sstevel@tonic-gate 		    bname));
3200Sstevel@tonic-gate 		return (RD_ERR);
3210Sstevel@tonic-gate 	}
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	rap->rd_tbinder = *beaddr = sym.st_value + sym.st_size - M_BIND_ADJ;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	return (RD_OK);
3260Sstevel@tonic-gate }
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate rd_err_e
rd_objpad_enable(struct rd_agent * rap,size_t padsize)3300Sstevel@tonic-gate rd_objpad_enable(struct rd_agent *rap, size_t padsize)
3310Sstevel@tonic-gate {
3320Sstevel@tonic-gate 	rd_err_e	err;
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	RDAGLOCK(rap);
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate #ifdef _LP64
3370Sstevel@tonic-gate 	if (rap->rd_dmodel == PR_MODEL_LP64)
3380Sstevel@tonic-gate 		err = _rd_objpad_enable64(rap, padsize);
3390Sstevel@tonic-gate 	else
3400Sstevel@tonic-gate #endif
3410Sstevel@tonic-gate 		err = _rd_objpad_enable32(rap, padsize);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	RDAGUNLOCK(rap);
3440Sstevel@tonic-gate 	return (err);
3450Sstevel@tonic-gate }
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate char *
rd_errstr(rd_err_e rderr)3490Sstevel@tonic-gate rd_errstr(rd_err_e rderr)
3500Sstevel@tonic-gate {
3510Sstevel@tonic-gate 	/*
3520Sstevel@tonic-gate 	 * Convert an 'rd_err_e' to a string
3530Sstevel@tonic-gate 	 */
3540Sstevel@tonic-gate 	switch (rderr) {
3550Sstevel@tonic-gate 	case RD_OK:
3560Sstevel@tonic-gate 		return ((char *)MSG_ORIG(MSG_ER_OK));
3570Sstevel@tonic-gate 	case RD_ERR:
3580Sstevel@tonic-gate 		return ((char *)MSG_ORIG(MSG_ER_ERR));
3590Sstevel@tonic-gate 	case RD_DBERR:
3600Sstevel@tonic-gate 		return ((char *)MSG_ORIG(MSG_ER_DBERR));
3610Sstevel@tonic-gate 	case RD_NOCAPAB:
3620Sstevel@tonic-gate 		return ((char *)MSG_ORIG(MSG_ER_NOCAPAB));
3630Sstevel@tonic-gate 	case RD_NODYNAM:
3640Sstevel@tonic-gate 		return ((char *)MSG_ORIG(MSG_ER_NODYNAM));
3650Sstevel@tonic-gate 	case RD_NOBASE:
3660Sstevel@tonic-gate 		return ((char *)MSG_ORIG(MSG_ER_NOBASE));
3670Sstevel@tonic-gate 	case RD_NOMAPS:
3680Sstevel@tonic-gate 		return ((char *)MSG_ORIG(MSG_ER_NOMAPS));
3690Sstevel@tonic-gate 	default:
3700Sstevel@tonic-gate 		return ((char *)MSG_ORIG(MSG_ER_DEFAULT));
3710Sstevel@tonic-gate 	}
3720Sstevel@tonic-gate }
373