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