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 2005 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 * In both cases a new minor device is created. Up to 16 /dev/log devices 92 * may be created per zone. Up to LOG_NUMCONS global /dev/conslog 93 * devices may be created. Most of the allocation details are handled in 94 * log_alloc. 95 */ 96 /* ARGSUSED */ 97 static int 98 log_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *cr) 99 { 100 log_t *lp; 101 minor_t minor; 102 103 if (sflag & (MODOPEN | CLONEOPEN)) 104 return (ENXIO); 105 106 switch (minor = getminor(*devp)) { 107 case LOG_CONSMIN: /* clone open of /dev/conslog */ 108 if (flag & FREAD) 109 return (EINVAL); /* write-only device */ 110 if (q->q_ptr) 111 return (0); 112 break; 113 114 case LOG_LOGMIN: /* clone open of /dev/log */ 115 break; 116 117 default: 118 return (ENXIO); 119 } 120 121 lp = log_alloc(minor); 122 if (lp == NULL) 123 return (ENXIO); 124 *devp = makedevice(getmajor(*devp), lp->log_minor); 125 q->q_ptr = lp; 126 WR(q)->q_ptr = lp; 127 lp->log_inuse = 1; 128 qprocson(q); 129 130 return (0); 131 } 132 133 /* ARGSUSED */ 134 static int 135 log_close(queue_t *q, int flag, cred_t *cr) 136 { 137 log_t *lp = (log_t *)q->q_ptr; 138 139 qprocsoff(q); 140 141 lp->log_inuse = 0; 142 log_update(lp, NULL, 0, NULL); 143 freemsg(lp->log_data); 144 lp->log_data = NULL; 145 if (lp->log_major == LOG_CONSMIN) 146 log_free(lp); 147 q->q_ptr = NULL; 148 WR(q)->q_ptr = NULL; 149 150 return (0); 151 } 152 153 static int 154 log_wput(queue_t *q, mblk_t *mp) 155 { 156 log_t *lp = (log_t *)q->q_ptr; 157 struct iocblk *iocp; 158 mblk_t *mp2; 159 cred_t *cr = DB_CRED(mp); 160 zoneid_t zoneid; 161 162 /* 163 * Default to global zone if dblk doesn't have a valid cred. 164 * Calls to syslog() go through putmsg(), which does set up 165 * the cred. 166 */ 167 zoneid = (cr != NULL) ? crgetzoneid(cr) : GLOBAL_ZONEID; 168 169 switch (DB_TYPE(mp)) { 170 case M_FLUSH: 171 if (*mp->b_rptr & FLUSHW) { 172 flushq(q, FLUSHALL); 173 *mp->b_rptr &= ~FLUSHW; 174 } 175 if (*mp->b_rptr & FLUSHR) { 176 flushq(RD(q), FLUSHALL); 177 qreply(q, mp); 178 return (0); 179 } 180 break; 181 182 case M_IOCTL: 183 iocp = (struct iocblk *)mp->b_rptr; 184 185 if (lp->log_major != LOG_LOGMIN) { 186 /* write-only device */ 187 miocnak(q, mp, 0, EINVAL); 188 return (0); 189 } 190 191 if (iocp->ioc_count == TRANSPARENT) { 192 miocnak(q, mp, 0, EINVAL); 193 return (0); 194 } 195 196 if (lp->log_flags) { 197 miocnak(q, mp, 0, EBUSY); 198 return (0); 199 } 200 201 freemsg(lp->log_data); 202 lp->log_data = mp->b_cont; 203 mp->b_cont = NULL; 204 205 switch (iocp->ioc_cmd) { 206 207 case I_CONSLOG: 208 log_update(lp, RD(q), SL_CONSOLE, log_console); 209 break; 210 211 case I_TRCLOG: 212 if (lp->log_data == NULL) { 213 miocnak(q, mp, 0, EINVAL); 214 return (0); 215 } 216 log_update(lp, RD(q), SL_TRACE, log_trace); 217 break; 218 219 case I_ERRLOG: 220 log_update(lp, RD(q), SL_ERROR, log_error); 221 break; 222 223 default: 224 miocnak(q, mp, 0, EINVAL); 225 return (0); 226 } 227 miocack(q, mp, 0, 0); 228 return (0); 229 230 case M_PROTO: 231 if (MBLKL(mp) == sizeof (log_ctl_t) && mp->b_cont != NULL) { 232 log_ctl_t *lc = (log_ctl_t *)mp->b_rptr; 233 /* This code is used by savecore to log dump msgs */ 234 if (mp->b_band != 0 && 235 secpolicy_sys_config(CRED(), B_FALSE) == 0) { 236 (void) putq(log_consq, mp); 237 return (0); 238 } 239 if ((lc->pri & LOG_FACMASK) == LOG_KERN) 240 lc->pri |= LOG_USER; 241 mp2 = log_makemsg(LOG_MID, LOG_CONSMIN, lc->level, 242 lc->flags, lc->pri, mp->b_cont->b_rptr, 243 MBLKL(mp->b_cont) + 1, 0); 244 if (mp2 != NULL) 245 log_sendmsg(mp2, zoneid); 246 } 247 break; 248 249 case M_DATA: 250 mp2 = log_makemsg(LOG_MID, LOG_CONSMIN, 0, SL_CONSOLE, 251 LOG_USER | LOG_INFO, mp->b_rptr, MBLKL(mp) + 1, 0); 252 if (mp2 != NULL) 253 log_sendmsg(mp2, zoneid); 254 break; 255 } 256 257 freemsg(mp); 258 return (0); 259 } 260 261 static int 262 log_rsrv(queue_t *q) 263 { 264 mblk_t *mp; 265 char *msg, *msgid_start, *msgid_end; 266 size_t idlen; 267 268 while (canputnext(q) && (mp = getq(q)) != NULL) { 269 if (log_msgid == 0) { 270 /* 271 * Strip out the message ID. If it's a kernel 272 * SL_CONSOLE message, replace msgid with "unix: ". 273 */ 274 msg = (char *)mp->b_cont->b_rptr; 275 if ((msgid_start = strstr(msg, "[ID ")) != NULL && 276 (msgid_end = strstr(msgid_start, "] ")) != NULL) { 277 log_ctl_t *lc = (log_ctl_t *)mp->b_rptr; 278 if ((lc->flags & SL_CONSOLE) && 279 (lc->pri & LOG_FACMASK) == LOG_KERN) 280 msgid_start = msg + snprintf(msg, 281 7, "unix: "); 282 idlen = msgid_end + 2 - msgid_start; 283 ovbcopy(msg, msg + idlen, msgid_start - msg); 284 mp->b_cont->b_rptr += idlen; 285 } 286 } 287 mp->b_band = 0; 288 putnext(q, mp); 289 } 290 return (0); 291 } 292 293 static struct module_info logm_info = 294 { LOG_MID, "LOG", LOG_MINPS, LOG_MAXPS, LOG_HIWAT, LOG_LOWAT }; 295 296 static struct qinit logrinit = 297 { NULL, log_rsrv, log_open, log_close, NULL, &logm_info, NULL }; 298 299 static struct qinit logwinit = 300 { log_wput, NULL, NULL, NULL, NULL, &logm_info, NULL }; 301 302 static struct streamtab loginfo = { &logrinit, &logwinit, NULL, NULL }; 303 304 DDI_DEFINE_STREAM_OPS(log_ops, nulldev, nulldev, log_attach, nodev, 305 nodev, log_info, D_NEW | D_MP | D_MTPERMOD, &loginfo); 306 307 static struct modldrv modldrv = 308 { &mod_driverops, "streams log driver", &log_ops }; 309 310 static struct modlinkage modlinkage = { MODREV_1, (void *)&modldrv, NULL }; 311 312 int 313 _init() 314 { 315 return (mod_install(&modlinkage)); 316 } 317 318 int 319 _fini() 320 { 321 return (mod_remove(&modlinkage)); 322 } 323 324 int 325 _info(struct modinfo *modinfop) 326 { 327 return (mod_info(&modlinkage, modinfop)); 328 } 329