xref: /onnv-gate/usr/src/uts/common/io/log.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * Streams log driver.  See log(7D).
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include <sys/types.h>
34*0Sstevel@tonic-gate #include <sys/param.h>
35*0Sstevel@tonic-gate #include <sys/errno.h>
36*0Sstevel@tonic-gate #include <sys/stropts.h>
37*0Sstevel@tonic-gate #include <sys/stream.h>
38*0Sstevel@tonic-gate #include <sys/strsun.h>
39*0Sstevel@tonic-gate #include <sys/debug.h>
40*0Sstevel@tonic-gate #include <sys/cred.h>
41*0Sstevel@tonic-gate #include <sys/file.h>
42*0Sstevel@tonic-gate #include <sys/ddi.h>
43*0Sstevel@tonic-gate #include <sys/stat.h>
44*0Sstevel@tonic-gate #include <sys/syslog.h>
45*0Sstevel@tonic-gate #include <sys/log.h>
46*0Sstevel@tonic-gate #include <sys/systm.h>
47*0Sstevel@tonic-gate #include <sys/modctl.h>
48*0Sstevel@tonic-gate #include <sys/policy.h>
49*0Sstevel@tonic-gate #include <sys/zone.h>
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate #include <sys/conf.h>
52*0Sstevel@tonic-gate #include <sys/sunddi.h>
53*0Sstevel@tonic-gate 
54*0Sstevel@tonic-gate static dev_info_t *log_devi;	/* private copy of devinfo pointer */
55*0Sstevel@tonic-gate int log_msgid;			/* log.conf tunable: enable msgid generation */
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate /* ARGSUSED */
58*0Sstevel@tonic-gate static int
59*0Sstevel@tonic-gate log_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
60*0Sstevel@tonic-gate {
61*0Sstevel@tonic-gate 	switch (infocmd) {
62*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
63*0Sstevel@tonic-gate 		*result = log_devi;
64*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
65*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
66*0Sstevel@tonic-gate 		*result = 0;
67*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
68*0Sstevel@tonic-gate 	}
69*0Sstevel@tonic-gate 	return (DDI_FAILURE);
70*0Sstevel@tonic-gate }
71*0Sstevel@tonic-gate 
72*0Sstevel@tonic-gate /* ARGSUSED */
73*0Sstevel@tonic-gate static int
74*0Sstevel@tonic-gate log_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
75*0Sstevel@tonic-gate {
76*0Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "conslog", S_IFCHR,
77*0Sstevel@tonic-gate 	    LOG_CONSMIN, DDI_PSEUDO, NULL) == DDI_FAILURE ||
78*0Sstevel@tonic-gate 	    ddi_create_minor_node(devi, "log", S_IFCHR,
79*0Sstevel@tonic-gate 	    LOG_LOGMIN, DDI_PSEUDO, NULL) == DDI_FAILURE) {
80*0Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
81*0Sstevel@tonic-gate 		return (DDI_FAILURE);
82*0Sstevel@tonic-gate 	}
83*0Sstevel@tonic-gate 	log_devi = devi;
84*0Sstevel@tonic-gate 	log_msgid = ddi_getprop(DDI_DEV_T_ANY, log_devi,
85*0Sstevel@tonic-gate 	    DDI_PROP_CANSLEEP, "msgid", 1);
86*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
87*0Sstevel@tonic-gate }
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate /*
90*0Sstevel@tonic-gate  * log_open can be called for one of two devices, /dev/conslog or
91*0Sstevel@tonic-gate  * /dev/log.  In the case of /dev/conslog it returns the global
92*0Sstevel@tonic-gate  * console device (i.e., multiple opens return the same device), while
93*0Sstevel@tonic-gate  * for /dev/log a new device is created for each open (up to a limit
94*0Sstevel@tonic-gate  * of 16 per zone).  Most of the allocation details are handled in
95*0Sstevel@tonic-gate  * log_alloc.
96*0Sstevel@tonic-gate  */
97*0Sstevel@tonic-gate /* ARGSUSED */
98*0Sstevel@tonic-gate static int
99*0Sstevel@tonic-gate log_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *cr)
100*0Sstevel@tonic-gate {
101*0Sstevel@tonic-gate 	log_t *lp;
102*0Sstevel@tonic-gate 	minor_t minor;
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate 	if (sflag & (MODOPEN | CLONEOPEN))
105*0Sstevel@tonic-gate 		return (ENXIO);
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate 	switch (minor = getminor(*devp)) {
108*0Sstevel@tonic-gate 	case LOG_CONSMIN:		/* normal open of /dev/conslog */
109*0Sstevel@tonic-gate 		if (flag & FREAD)
110*0Sstevel@tonic-gate 			return (EINVAL);	/* write-only device */
111*0Sstevel@tonic-gate 		if (q->q_ptr)
112*0Sstevel@tonic-gate 			return (0);
113*0Sstevel@tonic-gate 		break;
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate 	case LOG_LOGMIN:		/* clone open of /dev/log */
116*0Sstevel@tonic-gate 		break;
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate 	default:
119*0Sstevel@tonic-gate 		return (ENXIO);
120*0Sstevel@tonic-gate 	}
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate 	lp = log_alloc(minor);
123*0Sstevel@tonic-gate 	if (lp == NULL)
124*0Sstevel@tonic-gate 		return (ENXIO);
125*0Sstevel@tonic-gate 	*devp = makedevice(getmajor(*devp), lp->log_minor);
126*0Sstevel@tonic-gate 	q->q_ptr = lp;
127*0Sstevel@tonic-gate 	WR(q)->q_ptr = lp;
128*0Sstevel@tonic-gate 	lp->log_inuse = 1;
129*0Sstevel@tonic-gate 	qprocson(q);
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate 	return (0);
132*0Sstevel@tonic-gate }
133*0Sstevel@tonic-gate 
134*0Sstevel@tonic-gate /* ARGSUSED */
135*0Sstevel@tonic-gate static int
136*0Sstevel@tonic-gate log_close(queue_t *q, int flag, cred_t *cr)
137*0Sstevel@tonic-gate {
138*0Sstevel@tonic-gate 	log_t *lp = (log_t *)q->q_ptr;
139*0Sstevel@tonic-gate 
140*0Sstevel@tonic-gate 	qprocsoff(q);
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate 	lp->log_inuse = 0;
143*0Sstevel@tonic-gate 	log_update(lp, NULL, 0, NULL);
144*0Sstevel@tonic-gate 	freemsg(lp->log_data);
145*0Sstevel@tonic-gate 	lp->log_data = NULL;
146*0Sstevel@tonic-gate 	q->q_ptr = NULL;
147*0Sstevel@tonic-gate 	WR(q)->q_ptr = NULL;
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate 	return (0);
150*0Sstevel@tonic-gate }
151*0Sstevel@tonic-gate 
152*0Sstevel@tonic-gate static int
153*0Sstevel@tonic-gate log_wput(queue_t *q, mblk_t *mp)
154*0Sstevel@tonic-gate {
155*0Sstevel@tonic-gate 	log_t *lp = (log_t *)q->q_ptr;
156*0Sstevel@tonic-gate 	struct iocblk *iocp;
157*0Sstevel@tonic-gate 	mblk_t *mp2;
158*0Sstevel@tonic-gate 	cred_t *cr = DB_CRED(mp);
159*0Sstevel@tonic-gate 	zoneid_t zoneid;
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate 	/*
162*0Sstevel@tonic-gate 	 * Default to global zone if dblk doesn't have a valid cred.
163*0Sstevel@tonic-gate 	 * Calls to syslog() go through putmsg(), which does set up
164*0Sstevel@tonic-gate 	 * the cred.
165*0Sstevel@tonic-gate 	 */
166*0Sstevel@tonic-gate 	zoneid = (cr != NULL) ? crgetzoneid(cr) : GLOBAL_ZONEID;
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate 	switch (DB_TYPE(mp)) {
169*0Sstevel@tonic-gate 	case M_FLUSH:
170*0Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
171*0Sstevel@tonic-gate 			flushq(q, FLUSHALL);
172*0Sstevel@tonic-gate 			*mp->b_rptr &= ~FLUSHW;
173*0Sstevel@tonic-gate 		}
174*0Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR) {
175*0Sstevel@tonic-gate 			flushq(RD(q), FLUSHALL);
176*0Sstevel@tonic-gate 			qreply(q, mp);
177*0Sstevel@tonic-gate 			return (0);
178*0Sstevel@tonic-gate 		}
179*0Sstevel@tonic-gate 		break;
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate 	case M_IOCTL:
182*0Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
183*0Sstevel@tonic-gate 
184*0Sstevel@tonic-gate 		if (lp->log_minor <= LOG_LOGMIN) {
185*0Sstevel@tonic-gate 			/* not a cloned dev_t */
186*0Sstevel@tonic-gate 			miocnak(q, mp, 0, EINVAL);
187*0Sstevel@tonic-gate 			return (0);
188*0Sstevel@tonic-gate 		}
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 		if (iocp->ioc_count == TRANSPARENT) {
191*0Sstevel@tonic-gate 			miocnak(q, mp, 0, EINVAL);
192*0Sstevel@tonic-gate 			return (0);
193*0Sstevel@tonic-gate 		}
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate 		if (lp->log_flags) {
196*0Sstevel@tonic-gate 			miocnak(q, mp, 0, EBUSY);
197*0Sstevel@tonic-gate 			return (0);
198*0Sstevel@tonic-gate 		}
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 		freemsg(lp->log_data);
201*0Sstevel@tonic-gate 		lp->log_data = mp->b_cont;
202*0Sstevel@tonic-gate 		mp->b_cont = NULL;
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate 		case I_CONSLOG:
207*0Sstevel@tonic-gate 			log_update(lp, RD(q), SL_CONSOLE, log_console);
208*0Sstevel@tonic-gate 			break;
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate 		case I_TRCLOG:
211*0Sstevel@tonic-gate 			if (lp->log_data == NULL) {
212*0Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
213*0Sstevel@tonic-gate 				return (0);
214*0Sstevel@tonic-gate 			}
215*0Sstevel@tonic-gate 			log_update(lp, RD(q), SL_TRACE, log_trace);
216*0Sstevel@tonic-gate 			break;
217*0Sstevel@tonic-gate 
218*0Sstevel@tonic-gate 		case I_ERRLOG:
219*0Sstevel@tonic-gate 			log_update(lp, RD(q), SL_ERROR, log_error);
220*0Sstevel@tonic-gate 			break;
221*0Sstevel@tonic-gate 
222*0Sstevel@tonic-gate 		default:
223*0Sstevel@tonic-gate 			miocnak(q, mp, 0, EINVAL);
224*0Sstevel@tonic-gate 			return (0);
225*0Sstevel@tonic-gate 		}
226*0Sstevel@tonic-gate 		miocack(q, mp, 0, 0);
227*0Sstevel@tonic-gate 		return (0);
228*0Sstevel@tonic-gate 
229*0Sstevel@tonic-gate 	case M_PROTO:
230*0Sstevel@tonic-gate 		if (MBLKL(mp) == sizeof (log_ctl_t) && mp->b_cont != NULL) {
231*0Sstevel@tonic-gate 			log_ctl_t *lc = (log_ctl_t *)mp->b_rptr;
232*0Sstevel@tonic-gate 			/* This code is used by savecore to log dump msgs */
233*0Sstevel@tonic-gate 			if (mp->b_band != 0 &&
234*0Sstevel@tonic-gate 			    secpolicy_sys_config(CRED(), B_FALSE) == 0) {
235*0Sstevel@tonic-gate 				(void) putq(log_consq, mp);
236*0Sstevel@tonic-gate 				return (0);
237*0Sstevel@tonic-gate 			}
238*0Sstevel@tonic-gate 			if ((lc->pri & LOG_FACMASK) == LOG_KERN)
239*0Sstevel@tonic-gate 				lc->pri |= LOG_USER;
240*0Sstevel@tonic-gate 			mp2 = log_makemsg(LOG_MID, LOG_CONSMIN, lc->level,
241*0Sstevel@tonic-gate 			    lc->flags, lc->pri, mp->b_cont->b_rptr,
242*0Sstevel@tonic-gate 			    MBLKL(mp->b_cont) + 1, 0);
243*0Sstevel@tonic-gate 			if (mp2 != NULL)
244*0Sstevel@tonic-gate 				log_sendmsg(mp2, zoneid);
245*0Sstevel@tonic-gate 		}
246*0Sstevel@tonic-gate 		break;
247*0Sstevel@tonic-gate 
248*0Sstevel@tonic-gate 	case M_DATA:
249*0Sstevel@tonic-gate 		mp2 = log_makemsg(LOG_MID, LOG_CONSMIN, 0, SL_CONSOLE,
250*0Sstevel@tonic-gate 		    LOG_USER | LOG_INFO, mp->b_rptr, MBLKL(mp) + 1, 0);
251*0Sstevel@tonic-gate 		if (mp2 != NULL)
252*0Sstevel@tonic-gate 			log_sendmsg(mp2, zoneid);
253*0Sstevel@tonic-gate 		break;
254*0Sstevel@tonic-gate 	}
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 	freemsg(mp);
257*0Sstevel@tonic-gate 	return (0);
258*0Sstevel@tonic-gate }
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate static int
261*0Sstevel@tonic-gate log_rsrv(queue_t *q)
262*0Sstevel@tonic-gate {
263*0Sstevel@tonic-gate 	mblk_t *mp;
264*0Sstevel@tonic-gate 	char *msg, *msgid_start, *msgid_end;
265*0Sstevel@tonic-gate 	size_t idlen;
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 	while (canputnext(q) && (mp = getq(q)) != NULL) {
268*0Sstevel@tonic-gate 		if (log_msgid == 0) {
269*0Sstevel@tonic-gate 			/*
270*0Sstevel@tonic-gate 			 * Strip out the message ID.  If it's a kernel
271*0Sstevel@tonic-gate 			 * SL_CONSOLE message, replace msgid with "unix: ".
272*0Sstevel@tonic-gate 			 */
273*0Sstevel@tonic-gate 			msg = (char *)mp->b_cont->b_rptr;
274*0Sstevel@tonic-gate 			if ((msgid_start = strstr(msg, "[ID ")) != NULL &&
275*0Sstevel@tonic-gate 			    (msgid_end = strstr(msgid_start, "] ")) != NULL) {
276*0Sstevel@tonic-gate 				log_ctl_t *lc = (log_ctl_t *)mp->b_rptr;
277*0Sstevel@tonic-gate 				if ((lc->flags & SL_CONSOLE) &&
278*0Sstevel@tonic-gate 				    (lc->pri & LOG_FACMASK) == LOG_KERN)
279*0Sstevel@tonic-gate 					msgid_start = msg + snprintf(msg,
280*0Sstevel@tonic-gate 					    7, "unix: ");
281*0Sstevel@tonic-gate 				idlen = msgid_end + 2 - msgid_start;
282*0Sstevel@tonic-gate 				ovbcopy(msg, msg + idlen, msgid_start - msg);
283*0Sstevel@tonic-gate 				mp->b_cont->b_rptr += idlen;
284*0Sstevel@tonic-gate 			}
285*0Sstevel@tonic-gate 		}
286*0Sstevel@tonic-gate 		mp->b_band = 0;
287*0Sstevel@tonic-gate 		putnext(q, mp);
288*0Sstevel@tonic-gate 	}
289*0Sstevel@tonic-gate 	return (0);
290*0Sstevel@tonic-gate }
291*0Sstevel@tonic-gate 
292*0Sstevel@tonic-gate static struct module_info logm_info =
293*0Sstevel@tonic-gate 	{ LOG_MID, "LOG", LOG_MINPS, LOG_MAXPS, LOG_HIWAT, LOG_LOWAT };
294*0Sstevel@tonic-gate 
295*0Sstevel@tonic-gate static struct qinit logrinit =
296*0Sstevel@tonic-gate 	{ NULL, log_rsrv, log_open, log_close, NULL, &logm_info, NULL };
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate static struct qinit logwinit =
299*0Sstevel@tonic-gate 	{ log_wput, NULL, NULL, NULL, NULL, &logm_info, NULL };
300*0Sstevel@tonic-gate 
301*0Sstevel@tonic-gate static struct streamtab loginfo = { &logrinit, &logwinit, NULL, NULL };
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(log_ops, nulldev, nulldev, log_attach, nodev,
304*0Sstevel@tonic-gate 	nodev, log_info, D_NEW | D_MP | D_MTPERMOD, &loginfo);
305*0Sstevel@tonic-gate 
306*0Sstevel@tonic-gate static struct modldrv modldrv =
307*0Sstevel@tonic-gate 	{ &mod_driverops, "streams log driver", &log_ops };
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate static struct modlinkage modlinkage = { MODREV_1, (void *)&modldrv, NULL };
310*0Sstevel@tonic-gate 
311*0Sstevel@tonic-gate int
312*0Sstevel@tonic-gate _init()
313*0Sstevel@tonic-gate {
314*0Sstevel@tonic-gate 	return (mod_install(&modlinkage));
315*0Sstevel@tonic-gate }
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate int
318*0Sstevel@tonic-gate _fini()
319*0Sstevel@tonic-gate {
320*0Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
321*0Sstevel@tonic-gate }
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate int
324*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
325*0Sstevel@tonic-gate {
326*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
327*0Sstevel@tonic-gate }
328