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