xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_subr.c (revision cb6207858a9fcc2feaee22e626912fba281ac969)
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