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 /* 24 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <atomic.h> 31 #include <alloca.h> 32 #include <syslog.h> 33 #include <strings.h> 34 #include <unistd.h> 35 #include <stdlib.h> 36 #include <stdarg.h> 37 #include <stdio.h> 38 #include <exacct.h> 39 40 #include <fmd_subr.h> 41 #include <fmd_conf.h> 42 #include <fmd_error.h> 43 #include <fmd_thread.h> 44 #include <fmd_protocol.h> 45 #include <fmd_event.h> 46 #include <fmd_dispq.h> 47 #include <fmd_log.h> 48 49 #include <fmd.h> 50 51 struct _rwlock; 52 struct _lwp_mutex; 53 54 int 55 fmd_rw_read_held(pthread_rwlock_t *lock) 56 { 57 extern int _rw_read_held(struct _rwlock *); 58 return (_rw_read_held((struct _rwlock *)lock)); 59 } 60 61 int 62 fmd_rw_write_held(pthread_rwlock_t *lock) 63 { 64 extern int _rw_write_held(struct _rwlock *); 65 return (_rw_write_held((struct _rwlock *)lock)); 66 } 67 68 int 69 fmd_mutex_held(pthread_mutex_t *lock) 70 { 71 extern int _mutex_held(struct _lwp_mutex *); 72 return (_mutex_held((struct _lwp_mutex *)lock)); 73 } 74 75 int 76 fmd_assert(const char *expr, const char *file, int line) 77 { 78 fmd_panic("\"%s\", line %d: assertion failed: %s\n", file, line, expr); 79 /*NOTREACHED*/ 80 return (0); 81 } 82 83 /* 84 * To implement a reasonable panic() equivalent for fmd, we atomically bump a 85 * global counter of calls to fmd_vpanic() and attempt to print a panic message 86 * to stderr and dump core as a result of raising SIGABRT. This function must 87 * not attempt to grab any locks so that it can be called from any fmd code. 88 */ 89 void 90 fmd_vpanic(const char *format, va_list ap) 91 { 92 int oserr = errno; 93 pthread_t tid = pthread_self(); 94 95 fmd_thread_t *tp; 96 char msg[BUFSIZ]; 97 size_t len; 98 99 /* 100 * If this is not the first call to fmd_vpanic(), then check d_panictid 101 * to see if we are the panic thread. If so, then proceed directly to 102 * abort() because we have recursively panicked. If not, then pause() 103 * indefinitely waiting for the panic thread to terminate the daemon. 104 */ 105 if (atomic_add_32_nv(&fmd.d_panicrefs, 1) != 1) { 106 while (fmd.d_panictid != tid) 107 (void) pause(); 108 goto abort; 109 } 110 111 /* 112 * Use fmd.d_pid != 0 as a cheap test to see if fmd.d_key is valid 113 * (i.e. we're after fmd_create() and before fmd_destroy()). 114 */ 115 if (fmd.d_pid != 0 && (tp = pthread_getspecific(fmd.d_key)) != NULL) 116 (void) tp->thr_trfunc(tp->thr_trdata, FMD_DBG_ERR, format, ap); 117 118 fmd.d_panicstr = msg; 119 fmd.d_panictid = tid; 120 121 (void) snprintf(msg, sizeof (msg), "%s: ABORT: ", 122 fmd.d_pname ? fmd.d_pname : "fmd"); 123 124 len = strlen(msg); 125 (void) vsnprintf(msg + len, sizeof (msg) - len, format, ap); 126 127 if (strchr(format, '\n') == NULL) { 128 len = strlen(msg); 129 (void) snprintf(msg + len, sizeof (msg) - len, ": %s\n", 130 fmd_strerror(oserr)); 131 } 132 133 (void) write(STDERR_FILENO, msg, strlen(msg)); 134 135 abort: 136 abort(); 137 _exit(FMD_EXIT_ERROR); 138 } 139 140 /*PRINTFLIKE1*/ 141 void 142 fmd_panic(const char *format, ...) 143 { 144 va_list ap; 145 146 va_start(ap, format); 147 fmd_vpanic(format, ap); 148 va_end(ap); 149 } 150 151 void 152 fmd_verror(int err, const char *format, va_list ap) 153 { 154 int oserr = errno; 155 fmd_thread_t *tp; 156 nvlist_t *nvl; 157 fmd_event_t *e; 158 char *class; 159 160 if ((tp = pthread_getspecific(fmd.d_key)) != NULL) { 161 (void) tp->thr_trfunc(tp->thr_trdata, FMD_DBG_ERR, format, ap); 162 tp->thr_errdepth++; 163 } 164 165 (void) pthread_mutex_lock(&fmd.d_err_lock); 166 167 if (fmd.d_errstats != NULL && err >= EFMD_UNKNOWN && err < EFMD_END) 168 fmd.d_errstats[err - EFMD_UNKNOWN].fmds_value.ui64++; 169 170 if (fmd.d_fg || !fmd.d_running) { 171 (void) fprintf(stderr, "%s: ", fmd.d_pname); 172 (void) vfprintf(stderr, format, ap); 173 174 if (strchr(format, '\n') == NULL) 175 (void) fprintf(stderr, ": %s\n", fmd_strerror(oserr)); 176 } 177 178 (void) pthread_mutex_unlock(&fmd.d_err_lock); 179 180 /* 181 * If we are at error nesting level one and running in the background, 182 * log the error as an ereport to our own log and dispatch it. If the 183 * FMD_LF_BUSY flag is set, we can't attempt to log the event because 184 * a replay is running and we will deadlock on ourself in log_append. 185 */ 186 if (!fmd.d_fg && fmd.d_running && tp->thr_errdepth == 1 && 187 (nvl = fmd_protocol_fmderror(err, format, ap)) != NULL) { 188 189 (void) nvlist_lookup_string(nvl, FM_CLASS, &class); 190 e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); 191 192 (void) pthread_rwlock_rdlock(&fmd.d_log_lock); 193 if (!(fmd.d_errlog->log_flags & FMD_LF_BUSY)) 194 fmd_log_append(fmd.d_errlog, e, NULL); 195 (void) pthread_rwlock_unlock(&fmd.d_log_lock); 196 197 fmd_dispq_dispatch(fmd.d_disp, e, class); 198 } 199 200 if (tp != NULL) 201 tp->thr_errdepth--; 202 203 if (err == EFMD_EXIT) { 204 int core = 0; 205 206 (void) fmd_conf_getprop(fmd.d_conf, "core", &core); 207 if (core) 208 fmd_panic("forcing core dump at user request\n"); 209 210 exit(FMD_EXIT_ERROR); 211 } 212 } 213 214 /*PRINTFLIKE2*/ 215 void 216 fmd_error(int err, const char *format, ...) 217 { 218 va_list ap; 219 220 va_start(ap, format); 221 fmd_verror(err, format, ap); 222 va_end(ap); 223 } 224 225 void 226 fmd_vdprintf(int mask, const char *format, va_list ap) 227 { 228 fmd_thread_t *tp; 229 char *msg; 230 size_t len; 231 char c; 232 233 if (!(fmd.d_fmd_debug & mask)) 234 return; /* none of the specified modes are enabled */ 235 236 if ((tp = pthread_getspecific(fmd.d_key)) != NULL) 237 (void) tp->thr_trfunc(tp->thr_trdata, mask, format, ap); 238 239 if (fmd.d_fmd_dbout == 0) 240 return; /* no debugging output sinks are enabled */ 241 242 len = vsnprintf(&c, 1, format, ap); 243 msg = alloca(len + 2); 244 (void) vsnprintf(msg, len + 1, format, ap); 245 246 if (msg[len - 1] != '\n') 247 (void) strcpy(&msg[len], "\n"); 248 249 if (fmd.d_fmd_dbout & FMD_DBOUT_STDERR) { 250 (void) pthread_mutex_lock(&fmd.d_err_lock); 251 (void) fprintf(stderr, "%s DEBUG: %s", fmd.d_pname, msg); 252 (void) pthread_mutex_unlock(&fmd.d_err_lock); 253 } 254 255 if (fmd.d_fmd_dbout & FMD_DBOUT_SYSLOG) { 256 syslog(LOG_DEBUG | LOG_DAEMON, 257 "%s DEBUG: %s", fmd.d_pname, msg); 258 } 259 } 260 261 /*PRINTFLIKE2*/ 262 void 263 fmd_dprintf(int mask, const char *format, ...) 264 { 265 va_list ap; 266 267 va_start(ap, format); 268 fmd_vdprintf(mask, format, ap); 269 va_end(ap); 270 } 271 272 /* 273 * The fmd_trace.c routines set tr_file and tr_line to NULL and 0 respectively. 274 * If they are invoked from a macro (see <fmd_subr.h>) this tail function is 275 * called as part of the TRACE() macro to fill in these fields from the cpp 276 * macro values for __FILE__ and __LINE__. No locking is needed because all 277 * trace buffers are allocated separately for each fmd thread. 278 */ 279 void 280 fmd_trace_cpp(void *ptr, const char *file, int line) 281 { 282 fmd_tracerec_t *trp = ptr; 283 284 if (trp != NULL) { 285 trp->tr_file = file; 286 trp->tr_line = line; 287 } 288 } 289 290 /* 291 * The fmd_trace() function is the wrapper for the tracing routines provided in 292 * fmd_trace.c. It is invoked by the TRACE() macro in <fmd_subr.h>, and uses 293 * the per-thread trace buffer set up in fmd_thread.c to trace debugging info. 294 */ 295 /*PRINTFLIKE2*/ 296 void * 297 fmd_trace(uint_t tag, const char *format, ...) 298 { 299 fmd_thread_t *tp = pthread_getspecific(fmd.d_key); 300 va_list ap; 301 void *trp; 302 303 if (tp == NULL) 304 return (NULL); /* drop trace record if not ready yet */ 305 306 va_start(ap, format); 307 trp = tp->thr_trfunc(tp->thr_trdata, tag, format, ap); 308 va_end(ap); 309 310 return (trp); 311 } 312 313 const char * 314 fmd_ea_strerror(int err) 315 { 316 switch (err) { 317 case EXR_OK: return ("no exacct error"); 318 case EXR_SYSCALL_FAIL: return (fmd_strerror(errno)); 319 case EXR_CORRUPT_FILE: return ("file corruption detected"); 320 case EXR_EOF: return ("end-of-file reached"); 321 case EXR_NO_CREATOR: return ("creator tag mismatch"); 322 case EXR_INVALID_BUF: return ("invalid unpack buffer"); 323 case EXR_NOTSUPP: return ("exacct operation not supported"); 324 case EXR_UNKN_VERSION: return ("unsupported exacct file version"); 325 case EXR_INVALID_OBJ: return ("invalid exacct object"); 326 default: return ("unknown exacct error"); 327 } 328 } 329 330 /* 331 * Create a local ENA value for fmd-generated ereports. We use ENA Format 1 332 * with the low bits of gethrtime() and pthread_self() as the processor ID. 333 */ 334 uint64_t 335 fmd_ena(void) 336 { 337 hrtime_t hrt = fmd_time_gethrtime(); 338 339 return ((uint64_t)((FM_ENA_FMT1 & ENA_FORMAT_MASK) | 340 ((pthread_self() << ENA_FMT1_CPUID_SHFT) & ENA_FMT1_CPUID_MASK) | 341 ((hrt << ENA_FMT1_TIME_SHFT) & ENA_FMT1_TIME_MASK))); 342 } 343 344 /* 345 * fmd_ntz32() computes the number of trailing zeroes. The algorithm here is 346 * from "Hacker's Delight" by Henry Warren, Jr. 347 */ 348 uint32_t 349 fmd_ntz32(uint32_t x) 350 { 351 uint_t n = 1; 352 353 if (x == 0) 354 return (32); 355 356 if ((x & 0xFFFF) == 0) { 357 n += 16; 358 x >>= 16; 359 } 360 361 if ((x & 0xFF) == 0) { 362 n += 8; 363 x >>= 8; 364 } 365 366 if ((x & 0xF) == 0) { 367 n += 4; 368 x >>= 4; 369 } 370 371 if ((x & 0x3) == 0) { 372 n += 2; 373 x >>= 2; 374 } 375 376 return (n - (x & 1)); 377 } 378