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