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