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