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