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(4D). 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 msg = (char *)mp->b_cont->b_rptr; 275 if (log_msgid == 0 && 276 (msgid_start = strstr(msg, "[ID ")) != NULL && 277 (msgid_end = strstr(msgid_start, "] ")) != NULL) { 278 /* Strip out the message ID */ 279 idlen = msgid_end + 2 - msgid_start; 280 ovbcopy(msg, msg + idlen, msgid_start - msg); 281 mp->b_cont->b_rptr += idlen; 282 } 283 mp->b_band = 0; 284 putnext(q, mp); 285 } 286 return (0); 287 } 288 289 static struct module_info logm_info = 290 { LOG_MID, "LOG", LOG_MINPS, LOG_MAXPS, LOG_HIWAT, LOG_LOWAT }; 291 292 static struct qinit logrinit = 293 { NULL, log_rsrv, log_open, log_close, NULL, &logm_info, NULL }; 294 295 static struct qinit logwinit = 296 { log_wput, NULL, NULL, NULL, NULL, &logm_info, NULL }; 297 298 static struct streamtab loginfo = { &logrinit, &logwinit, NULL, NULL }; 299 300 DDI_DEFINE_STREAM_OPS(log_ops, nulldev, nulldev, log_attach, nodev, 301 nodev, log_info, D_NEW | D_MP | D_MTPERMOD, &loginfo, 302 ddi_quiesce_not_needed); 303 304 static struct modldrv modldrv = 305 { &mod_driverops, "streams log driver", &log_ops }; 306 307 static struct modlinkage modlinkage = { MODREV_1, (void *)&modldrv, NULL }; 308 309 int 310 _init() 311 { 312 return (mod_install(&modlinkage)); 313 } 314 315 int 316 _fini() 317 { 318 return (mod_remove(&modlinkage)); 319 } 320 321 int 322 _info(struct modinfo *modinfop) 323 { 324 return (mod_info(&modlinkage, modinfop)); 325 } 326