1192908Szml/*-
2192908Szml * Copyright (c) 2009 Isilon Inc http://www.isilon.com/
3192908Szml *
4192908Szml * Redistribution and use in source and binary forms, with or without
5192908Szml * modification, are permitted provided that the following conditions
6192908Szml * are met:
7192908Szml * 1. Redistributions of source code must retain the above copyright
8192908Szml *    notice, this list of conditions and the following disclaimer.
9192908Szml * 2. Redistributions in binary form must reproduce the above copyright
10192908Szml *    notice, this list of conditions and the following disclaimer in the
11192908Szml *    documentation and/or other materials provided with the distribution.
12192908Szml *
13192908Szml * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14192908Szml * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15192908Szml * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16192908Szml * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17192908Szml * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18192908Szml * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19192908Szml * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20192908Szml * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21192908Szml * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22192908Szml * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23192908Szml * SUCH DAMAGE.
24192908Szml */
25192908Szml/**
26192908Szml * @file
27192908Szml *
28192908Szml * fail(9) Facility.
29192908Szml *
30192908Szml * @ingroup failpoint_private
31192908Szml */
32192908Szml/**
33192908Szml * @defgroup failpoint fail(9) Facility
34192908Szml *
35192908Szml * Failpoints allow for injecting fake errors into running code on the fly,
36192908Szml * without modifying code or recompiling with flags.  Failpoints are always
37192908Szml * present, and are very efficient when disabled.  Failpoints are described
38192908Szml * in man fail(9).
39192908Szml */
40192908Szml/**
41192908Szml * @defgroup failpoint_private Private fail(9) Implementation functions
42192908Szml *
43192908Szml * Private implementations for the actual failpoint code.
44192908Szml *
45192908Szml * @ingroup failpoint
46192908Szml */
47192908Szml/**
48192908Szml * @addtogroup failpoint_private
49192908Szml * @{
50192908Szml */
51192908Szml
52192908Szml#include <sys/cdefs.h>
53192908Szml__FBSDID("$FreeBSD$");
54192908Szml
55223875Smdf#include <sys/ctype.h>
56192908Szml#include <sys/errno.h>
57192908Szml#include <sys/fail.h>
58192908Szml#include <sys/kernel.h>
59192908Szml#include <sys/libkern.h>
60192908Szml#include <sys/lock.h>
61192908Szml#include <sys/malloc.h>
62192908Szml#include <sys/mutex.h>
63223876Smdf#include <sys/proc.h>
64192908Szml#include <sys/sbuf.h>
65192908Szml
66192908Szml#include <machine/stdarg.h>
67192908Szml
68192908Szml#ifdef ILOG_DEFINE_FOR_FILE
69192908SzmlILOG_DEFINE_FOR_FILE(L_ISI_FAIL_POINT, L_ILOG, fail_point);
70192908Szml#endif
71192908Szml
72227293Sedstatic MALLOC_DEFINE(M_FAIL_POINT, "Fail Points", "fail points system");
73192908Szml#define fp_free(ptr) free(ptr, M_FAIL_POINT)
74192908Szml#define fp_malloc(size, flags) malloc((size), M_FAIL_POINT, (flags))
75192908Szml
76192908Szmlstatic struct mtx g_fp_mtx;
77192908SzmlMTX_SYSINIT(g_fp_mtx, &g_fp_mtx, "fail point mtx", MTX_DEF);
78192908Szml#define FP_LOCK()	mtx_lock(&g_fp_mtx)
79192908Szml#define FP_UNLOCK()	mtx_unlock(&g_fp_mtx)
80192908Szml
81216616Smdf/**
82216616Smdf * Failpoint types.
83216616Smdf * Don't change these without changing fail_type_strings in fail.c.
84216616Smdf * @ingroup failpoint_private
85216616Smdf */
86216616Smdfenum fail_point_t {
87216616Smdf	FAIL_POINT_OFF,		/**< don't fail */
88216616Smdf	FAIL_POINT_PANIC,	/**< panic */
89216616Smdf	FAIL_POINT_RETURN,	/**< return an errorcode */
90216616Smdf	FAIL_POINT_BREAK,	/**< break into the debugger */
91216616Smdf	FAIL_POINT_PRINT,	/**< print a message */
92216616Smdf	FAIL_POINT_SLEEP,	/**< sleep for some msecs */
93223875Smdf	FAIL_POINT_NUMTYPES
94216616Smdf};
95216616Smdf
96223875Smdfstatic struct {
97223875Smdf	const char *name;
98223875Smdf	int	nmlen;
99223875Smdf} fail_type_strings[] = {
100223875Smdf#define	FP_TYPE_NM_LEN(s)	{ s, sizeof(s) - 1 }
101223875Smdf	[FAIL_POINT_OFF] =	FP_TYPE_NM_LEN("off"),
102223875Smdf	[FAIL_POINT_PANIC] =	FP_TYPE_NM_LEN("panic"),
103223875Smdf	[FAIL_POINT_RETURN] =	FP_TYPE_NM_LEN("return"),
104223875Smdf	[FAIL_POINT_BREAK] =	FP_TYPE_NM_LEN("break"),
105223875Smdf	[FAIL_POINT_PRINT] =	FP_TYPE_NM_LEN("print"),
106223875Smdf	[FAIL_POINT_SLEEP] =	FP_TYPE_NM_LEN("sleep"),
107216616Smdf};
108216616Smdf
109216616Smdf/**
110216616Smdf * Internal structure tracking a single term of a complete failpoint.
111216616Smdf * @ingroup failpoint_private
112216616Smdf */
113216616Smdfstruct fail_point_entry {
114216616Smdf	enum fail_point_t fe_type;	/**< type of entry */
115216616Smdf	int		fe_arg;		/**< argument to type (e.g. return value) */
116216616Smdf	int		fe_prob;	/**< likelihood of firing in millionths */
117216616Smdf	int		fe_count;	/**< number of times to fire, 0 means always */
118223876Smdf	pid_t		fe_pid;		/**< only fail for this process */
119216616Smdf	TAILQ_ENTRY(fail_point_entry) fe_entries; /**< next entry in fail point */
120216616Smdf};
121216616Smdf
122192908Szmlstatic inline void
123192908Szmlfail_point_sleep(struct fail_point *fp, struct fail_point_entry *ent,
124192908Szml    int msecs, enum fail_point_return_code *pret)
125192908Szml{
126192908Szml	/* convert from millisecs to ticks, rounding up */
127192908Szml	int timo = ((msecs * hz) + 999) / 1000;
128192908Szml
129223875Smdf	if (timo > 0) {
130192908Szml		if (fp->fp_sleep_fn == NULL) {
131192908Szml			msleep(fp, &g_fp_mtx, PWAIT, "failpt", timo);
132192908Szml		} else {
133192908Szml			timeout(fp->fp_sleep_fn, fp->fp_sleep_arg, timo);
134192908Szml			*pret = FAIL_POINT_RC_QUEUED;
135192908Szml		}
136192908Szml	}
137192908Szml}
138192908Szml
139192908Szml
140192908Szml/**
141192908Szml * Defines stating the equivalent of probablilty one (100%)
142192908Szml */
143192908Szmlenum {
144192908Szml	PROB_MAX = 1000000,	/* probability between zero and this number */
145192908Szml	PROB_DIGITS = 6,        /* number of zero's in above number */
146192908Szml};
147192908Szml
148192908Szmlstatic char *parse_fail_point(struct fail_point_entries *, char *);
149192908Szmlstatic char *parse_term(struct fail_point_entries *, char *);
150192908Szmlstatic char *parse_number(int *out_units, int *out_decimal, char *);
151192908Szmlstatic char *parse_type(struct fail_point_entry *, char *);
152192908Szmlstatic void free_entry(struct fail_point_entries *, struct fail_point_entry *);
153192908Szmlstatic void clear_entries(struct fail_point_entries *);
154192908Szml
155192908Szml/**
156192908Szml * Initialize a fail_point.  The name is formed in a printf-like fashion
157192908Szml * from "fmt" and subsequent arguments.  This function is generally used
158192908Szml * for custom failpoints located at odd places in the sysctl tree, and is
159192908Szml * not explicitly needed for standard in-line-declared failpoints.
160192908Szml *
161192908Szml * @ingroup failpoint
162192908Szml */
163192908Szmlvoid
164192908Szmlfail_point_init(struct fail_point *fp, const char *fmt, ...)
165192908Szml{
166192908Szml	va_list ap;
167192908Szml	char *name;
168192908Szml	int n;
169192908Szml
170192908Szml	TAILQ_INIT(&fp->fp_entries);
171192908Szml	fp->fp_flags = 0;
172192908Szml
173192908Szml	/* Figure out the size of the name. */
174192908Szml	va_start(ap, fmt);
175192908Szml	n = vsnprintf(NULL, 0, fmt, ap);
176192908Szml	va_end(ap);
177192908Szml
178192908Szml	/* Allocate the name and fill it in. */
179192908Szml	name = fp_malloc(n + 1, M_WAITOK);
180192908Szml	if (name != NULL) {
181192908Szml		va_start(ap, fmt);
182192908Szml		vsnprintf(name, n + 1, fmt, ap);
183192908Szml		va_end(ap);
184192908Szml	}
185192908Szml	fp->fp_name = name;
186216620Smdf	fp->fp_location = "";
187192908Szml	fp->fp_flags |= FAIL_POINT_DYNAMIC_NAME;
188192908Szml	fp->fp_sleep_fn = NULL;
189192908Szml	fp->fp_sleep_arg = NULL;
190192908Szml}
191192908Szml
192192908Szml/**
193192908Szml * Free the resources held by a fail_point.
194192908Szml *
195192908Szml * @ingroup failpoint
196192908Szml */
197192908Szmlvoid
198192908Szmlfail_point_destroy(struct fail_point *fp)
199192908Szml{
200192908Szml
201223875Smdf	if ((fp->fp_flags & FAIL_POINT_DYNAMIC_NAME) != 0) {
202223875Smdf		fp_free(__DECONST(void *, fp->fp_name));
203192908Szml		fp->fp_name = NULL;
204192908Szml	}
205192908Szml	fp->fp_flags = 0;
206223875Smdf	clear_entries(&fp->fp_entries);
207192908Szml}
208192908Szml
209192908Szml/**
210192908Szml * This does the real work of evaluating a fail point. If the fail point tells
211192908Szml * us to return a value, this function returns 1 and fills in 'return_value'
212192908Szml * (return_value is allowed to be null). If the fail point tells us to panic,
213192908Szml * we never return. Otherwise we just return 0 after doing some work, which
214192908Szml * means "keep going".
215192908Szml */
216192908Szmlenum fail_point_return_code
217192908Szmlfail_point_eval_nontrivial(struct fail_point *fp, int *return_value)
218192908Szml{
219192908Szml	enum fail_point_return_code ret = FAIL_POINT_RC_CONTINUE;
220192908Szml	struct fail_point_entry *ent, *next;
221192908Szml	int msecs;
222192908Szml
223192908Szml	FP_LOCK();
224192908Szml
225223875Smdf	TAILQ_FOREACH_SAFE(ent, &fp->fp_entries, fe_entries, next) {
226192908Szml		int cont = 0; /* don't continue by default */
227192908Szml
228192908Szml		if (ent->fe_prob < PROB_MAX &&
229223875Smdf		    ent->fe_prob < random() % PROB_MAX)
230223875Smdf			continue;
231223876Smdf		if (ent->fe_pid != NO_PID && ent->fe_pid != curproc->p_pid)
232223876Smdf			continue;
233192908Szml
234192908Szml		switch (ent->fe_type) {
235192908Szml		case FAIL_POINT_PANIC:
236192908Szml			panic("fail point %s panicking", fp->fp_name);
237192908Szml			/* NOTREACHED */
238192908Szml
239192908Szml		case FAIL_POINT_RETURN:
240223875Smdf			if (return_value != NULL)
241192908Szml				*return_value = ent->fe_arg;
242192908Szml			ret = FAIL_POINT_RC_RETURN;
243192908Szml			break;
244192908Szml
245192908Szml		case FAIL_POINT_BREAK:
246223875Smdf			printf("fail point %s breaking to debugger\n",
247223875Smdf			    fp->fp_name);
248192908Szml			breakpoint();
249192908Szml			break;
250192908Szml
251192908Szml		case FAIL_POINT_PRINT:
252192908Szml			printf("fail point %s executing\n", fp->fp_name);
253192908Szml			cont = ent->fe_arg;
254192908Szml			break;
255192908Szml
256192908Szml		case FAIL_POINT_SLEEP:
257192908Szml			/*
258192908Szml			 * Free the entry now if necessary, since
259192908Szml			 * we're about to drop the mutex and sleep.
260192908Szml			 */
261192908Szml			msecs = ent->fe_arg;
262192908Szml			if (ent->fe_count > 0 && --ent->fe_count == 0) {
263192908Szml				free_entry(&fp->fp_entries, ent);
264192908Szml				ent = NULL;
265192908Szml			}
266192908Szml
267192908Szml			if (msecs)
268192908Szml				fail_point_sleep(fp, ent, msecs, &ret);
269192908Szml			break;
270192908Szml
271192908Szml		default:
272192908Szml			break;
273192908Szml		}
274192908Szml
275223875Smdf		if (ent != NULL && ent->fe_count > 0 && --ent->fe_count == 0)
276192908Szml			free_entry(&fp->fp_entries, ent);
277223875Smdf		if (cont == 0)
278192908Szml			break;
279192908Szml	}
280192908Szml
281192908Szml	/* Get rid of "off"s at the end. */
282192908Szml	while ((ent = TAILQ_LAST(&fp->fp_entries, fail_point_entries)) &&
283192908Szml	       ent->fe_type == FAIL_POINT_OFF)
284192908Szml		free_entry(&fp->fp_entries, ent);
285192908Szml
286192908Szml	FP_UNLOCK();
287192908Szml
288223875Smdf	return (ret);
289192908Szml}
290192908Szml
291192908Szml/**
292192908Szml * Translate internal fail_point structure into human-readable text.
293192908Szml */
294192908Szmlstatic void
295192908Szmlfail_point_get(struct fail_point *fp, struct sbuf *sb)
296192908Szml{
297192908Szml	struct fail_point_entry *ent;
298192908Szml
299192908Szml	FP_LOCK();
300192908Szml
301192908Szml	TAILQ_FOREACH(ent, &fp->fp_entries, fe_entries) {
302192908Szml		if (ent->fe_prob < PROB_MAX) {
303192908Szml			int decimal = ent->fe_prob % (PROB_MAX / 100);
304192908Szml			int units = ent->fe_prob / (PROB_MAX / 100);
305192908Szml			sbuf_printf(sb, "%d", units);
306192908Szml			if (decimal) {
307192908Szml				int digits = PROB_DIGITS - 2;
308192908Szml				while (!(decimal % 10)) {
309192908Szml					digits--;
310192908Szml					decimal /= 10;
311192908Szml				}
312192908Szml				sbuf_printf(sb, ".%0*d", digits, decimal);
313192908Szml			}
314192908Szml			sbuf_printf(sb, "%%");
315192908Szml		}
316192908Szml		if (ent->fe_count > 0)
317192908Szml			sbuf_printf(sb, "%d*", ent->fe_count);
318223875Smdf		sbuf_printf(sb, "%s", fail_type_strings[ent->fe_type].name);
319192908Szml		if (ent->fe_arg)
320192908Szml			sbuf_printf(sb, "(%d)", ent->fe_arg);
321223876Smdf		if (ent->fe_pid != NO_PID)
322223876Smdf			sbuf_printf(sb, "[pid %d]", ent->fe_pid);
323192908Szml		if (TAILQ_NEXT(ent, fe_entries))
324192908Szml			sbuf_printf(sb, "->");
325192908Szml	}
326192908Szml	if (TAILQ_EMPTY(&fp->fp_entries))
327192908Szml		sbuf_printf(sb, "off");
328192908Szml
329192908Szml	FP_UNLOCK();
330192908Szml}
331192908Szml
332192908Szml/**
333192908Szml * Set an internal fail_point structure from a human-readable failpoint string
334192908Szml * in a lock-safe manner.
335192908Szml */
336192908Szmlstatic int
337192908Szmlfail_point_set(struct fail_point *fp, char *buf)
338192908Szml{
339192908Szml	int error = 0;
340192908Szml	struct fail_point_entry *ent, *ent_next;
341192908Szml	struct fail_point_entries new_entries;
342192908Szml
343192908Szml	/* Parse new entries. */
344192908Szml	TAILQ_INIT(&new_entries);
345192908Szml	if (!parse_fail_point(&new_entries, buf)) {
346192908Szml	        clear_entries(&new_entries);
347192908Szml		error = EINVAL;
348192908Szml		goto end;
349192908Szml	}
350192908Szml
351192908Szml	FP_LOCK();
352192908Szml
353192908Szml	/* Move new entries in. */
354192908Szml	TAILQ_SWAP(&fp->fp_entries, &new_entries, fail_point_entry, fe_entries);
355192908Szml	clear_entries(&new_entries);
356192908Szml
357192908Szml	/* Get rid of useless zero probability entries. */
358192908Szml	TAILQ_FOREACH_SAFE(ent, &fp->fp_entries, fe_entries, ent_next) {
359192908Szml		if (ent->fe_prob == 0)
360192908Szml			free_entry(&fp->fp_entries, ent);
361192908Szml	}
362192908Szml
363192908Szml	/* Get rid of "off"s at the end. */
364192908Szml	while ((ent = TAILQ_LAST(&fp->fp_entries, fail_point_entries)) &&
365192908Szml		ent->fe_type == FAIL_POINT_OFF)
366192908Szml		free_entry(&fp->fp_entries, ent);
367192908Szml
368192908Szml	FP_UNLOCK();
369192908Szml
370192908Szml end:
371192908Szml#ifdef IWARNING
372192908Szml	if (error)
373216620Smdf		IWARNING("Failed to set %s %s to %s",
374192908Szml		    fp->fp_name, fp->fp_location, buf);
375192908Szml	else
376216620Smdf		INOTICE("Set %s %s to %s",
377192908Szml		    fp->fp_name, fp->fp_location, buf);
378192908Szml#endif /* IWARNING */
379192908Szml
380223875Smdf	return (error);
381192908Szml}
382192908Szml
383192908Szml#define MAX_FAIL_POINT_BUF	1023
384192908Szml
385192908Szml/**
386192908Szml * Handle kernel failpoint set/get.
387192908Szml */
388192908Szmlint
389192908Szmlfail_point_sysctl(SYSCTL_HANDLER_ARGS)
390192908Szml{
391192908Szml	struct fail_point *fp = arg1;
392192908Szml	char *buf = NULL;
393192908Szml	struct sbuf sb;
394192908Szml	int error;
395192908Szml
396192908Szml	/* Retrieving */
397192908Szml	sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
398192908Szml	fail_point_get(fp, &sb);
399192908Szml	sbuf_trim(&sb);
400192908Szml	sbuf_finish(&sb);
401192908Szml	error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
402192908Szml	sbuf_delete(&sb);
403192908Szml
404192908Szml	/* Setting */
405192908Szml	if (!error && req->newptr) {
406192908Szml		if (req->newlen > MAX_FAIL_POINT_BUF) {
407192908Szml			error = EINVAL;
408192908Szml			goto out;
409192908Szml		}
410192908Szml
411192908Szml		buf = fp_malloc(req->newlen + 1, M_WAITOK);
412192908Szml
413192908Szml		error = SYSCTL_IN(req, buf, req->newlen);
414192908Szml		if (error)
415192908Szml			goto out;
416192908Szml		buf[req->newlen] = '\0';
417192908Szml
418192908Szml		error = fail_point_set(fp, buf);
419192908Szml        }
420192908Szml
421192908Szmlout:
422223875Smdf	fp_free(buf);
423223875Smdf	return (error);
424192908Szml}
425192908Szml
426192908Szml/**
427192908Szml * Internal helper function to translate a human-readable failpoint string
428192908Szml * into a internally-parsable fail_point structure.
429192908Szml */
430192908Szmlstatic char *
431192908Szmlparse_fail_point(struct fail_point_entries *ents, char *p)
432192908Szml{
433192908Szml	/*  <fail_point> ::
434192908Szml	 *      <term> ( "->" <term> )*
435192908Szml	 */
436223875Smdf	p = parse_term(ents, p);
437223875Smdf	if (p == NULL)
438223875Smdf		return (NULL);
439223875Smdf	while (*p != '\0') {
440223875Smdf		if (p[0] != '-' || p[1] != '>')
441223875Smdf			return (NULL);
442223875Smdf		p = parse_term(ents, p + 2);
443223875Smdf		if (p == NULL)
444223875Smdf			return (NULL);
445223875Smdf	}
446223875Smdf	return (p);
447192908Szml}
448192908Szml
449192908Szml/**
450192908Szml * Internal helper function to parse an individual term from a failpoint.
451192908Szml */
452192908Szmlstatic char *
453192908Szmlparse_term(struct fail_point_entries *ents, char *p)
454192908Szml{
455192908Szml	struct fail_point_entry *ent;
456192908Szml
457192908Szml	ent = fp_malloc(sizeof *ent, M_WAITOK | M_ZERO);
458192908Szml	ent->fe_prob = PROB_MAX;
459223876Smdf	ent->fe_pid = NO_PID;
460192908Szml	TAILQ_INSERT_TAIL(ents, ent, fe_entries);
461192908Szml
462192908Szml	/*
463192908Szml	 * <term> ::
464192908Szml	 *     ( (<float> "%") | (<integer> "*" ) )*
465192908Szml	 *     <type>
466192908Szml	 *     [ "(" <integer> ")" ]
467223876Smdf	 *     [ "[pid " <integer> "]" ]
468192908Szml	 */
469192908Szml
470192908Szml	/* ( (<float> "%") | (<integer> "*" ) )* */
471223875Smdf	while (isdigit(*p) || *p == '.') {
472192908Szml		int units, decimal;
473192908Szml
474223875Smdf		p = parse_number(&units, &decimal, p);
475223875Smdf		if (p == NULL)
476223875Smdf			return (NULL);
477192908Szml
478192908Szml		if (*p == '%') {
479192908Szml			if (units > 100) /* prevent overflow early */
480192908Szml				units = 100;
481192908Szml			ent->fe_prob = units * (PROB_MAX / 100) + decimal;
482192908Szml			if (ent->fe_prob > PROB_MAX)
483192908Szml				ent->fe_prob = PROB_MAX;
484192908Szml		} else if (*p == '*') {
485192908Szml			if (!units || decimal)
486223875Smdf				return (NULL);
487201758Smbr			ent->fe_count = units;
488223875Smdf		} else
489223875Smdf			return (NULL);
490192908Szml		p++;
491192908Szml	}
492192908Szml
493192908Szml	/* <type> */
494223875Smdf	p = parse_type(ent, p);
495223875Smdf	if (p == NULL)
496223875Smdf		return (NULL);
497192908Szml	if (*p == '\0')
498223875Smdf		return (p);
499192908Szml
500192908Szml	/* [ "(" <integer> ")" ] */
501192908Szml	if (*p != '(')
502192908Szml		return p;
503192908Szml	p++;
504223875Smdf	if (!isdigit(*p) && *p != '-')
505223875Smdf		return (NULL);
506223875Smdf	ent->fe_arg = strtol(p, &p, 0);
507192908Szml	if (*p++ != ')')
508223875Smdf		return (NULL);
509192908Szml
510223876Smdf	/* [ "[pid " <integer> "]" ] */
511223876Smdf#define	PID_STRING	"[pid "
512223876Smdf	if (strncmp(p, PID_STRING, sizeof(PID_STRING) - 1) != 0)
513223876Smdf		return (p);
514223876Smdf	p += sizeof(PID_STRING) - 1;
515223876Smdf	if (!isdigit(*p))
516223876Smdf		return (NULL);
517223876Smdf	ent->fe_pid = strtol(p, &p, 0);
518223876Smdf	if (*p++ != ']')
519223876Smdf		return (NULL);
520223876Smdf
521223875Smdf	return (p);
522192908Szml}
523192908Szml
524192908Szml/**
525192908Szml * Internal helper function to parse a numeric for a failpoint term.
526192908Szml */
527192908Szmlstatic char *
528192908Szmlparse_number(int *out_units, int *out_decimal, char *p)
529192908Szml{
530192908Szml	char *old_p;
531192908Szml
532192908Szml	/*
533192908Szml	 *  <number> ::
534192908Szml	 *      <integer> [ "." <integer> ] |
535192908Szml	 *      "." <integer>
536192908Szml	 */
537192908Szml
538192908Szml	/* whole part */
539192908Szml	old_p = p;
540201758Smbr	*out_units = strtol(p, &p, 10);
541192908Szml	if (p == old_p && *p != '.')
542223875Smdf		return (NULL);
543192908Szml
544192908Szml	/* fractional part */
545192908Szml	*out_decimal = 0;
546192908Szml	if (*p == '.') {
547192908Szml		int digits = 0;
548192908Szml		p++;
549223875Smdf		while (isdigit(*p)) {
550192908Szml			int digit = *p - '0';
551192908Szml			if (digits < PROB_DIGITS - 2)
552192908Szml				*out_decimal = *out_decimal * 10 + digit;
553192908Szml			else if (digits == PROB_DIGITS - 2 && digit >= 5)
554192908Szml				(*out_decimal)++;
555192908Szml			digits++;
556192908Szml			p++;
557192908Szml		}
558192908Szml		if (!digits) /* need at least one digit after '.' */
559223875Smdf			return (NULL);
560192908Szml		while (digits++ < PROB_DIGITS - 2) /* add implicit zeros */
561192908Szml			*out_decimal *= 10;
562192908Szml	}
563192908Szml
564223875Smdf	return (p); /* success */
565192908Szml}
566192908Szml
567192908Szml/**
568192908Szml * Internal helper function to parse an individual type for a failpoint term.
569192908Szml */
570192908Szmlstatic char *
571192908Szmlparse_type(struct fail_point_entry *ent, char *beg)
572192908Szml{
573192908Szml	enum fail_point_t type;
574223875Smdf	int len;
575223875Smdf
576223875Smdf	for (type = FAIL_POINT_OFF; type < FAIL_POINT_NUMTYPES; type++) {
577223875Smdf		len = fail_type_strings[type].nmlen;
578223875Smdf		if (strncmp(fail_type_strings[type].name, beg, len) == 0) {
579192908Szml			ent->fe_type = type;
580223875Smdf			return (beg + len);
581192908Szml		}
582192908Szml	}
583223875Smdf	return (NULL);
584192908Szml}
585192908Szml
586192908Szml/**
587192908Szml * Internal helper function to free an individual failpoint term.
588192908Szml */
589192908Szmlstatic void
590192908Szmlfree_entry(struct fail_point_entries *ents, struct fail_point_entry *ent)
591192908Szml{
592192908Szml	TAILQ_REMOVE(ents, ent, fe_entries);
593192908Szml	fp_free(ent);
594192908Szml}
595192908Szml
596192908Szml/**
597192908Szml * Internal helper function to clear out all failpoint terms for a single
598192908Szml * failpoint.
599192908Szml */
600192908Szmlstatic void
601192908Szmlclear_entries(struct fail_point_entries *ents)
602192908Szml{
603192908Szml	struct fail_point_entry *ent, *ent_next;
604223875Smdf
605192908Szml	TAILQ_FOREACH_SAFE(ent, ents, fe_entries, ent_next)
606192908Szml		fp_free(ent);
607192908Szml	TAILQ_INIT(ents);
608192908Szml}
609192908Szml
610192908Szml/* The fail point sysctl tree. */
611192908SzmlSYSCTL_NODE(_debug, OID_AUTO, fail_point, CTLFLAG_RW, 0, "fail points");
612