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