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