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
fmd_assert(const char * expr,const char * file,int line)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
fmd_vpanic(const char * format,va_list ap)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
fmd_panic(const char * format,...)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
fmd_verror(int err,const char * format,va_list ap)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
fmd_error(int err,const char * format,...)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
fmd_vdprintf(int mask,const char * format,va_list ap)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
fmd_dprintf(int mask,const char * format,...)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
fmd_trace_cpp(void * ptr,const char * file,int line)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 *
fmd_trace(uint_t tag,const char * format,...)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 *
fmd_ea_strerror(int err)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
fmd_ena(void)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
fmd_ntz32(uint32_t x)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