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