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