1174914Srwatson/*- 2174914Srwatson * Copyright (c) 2007 Robert N. M. Watson 3174914Srwatson * All rights reserved. 4174914Srwatson * 5174914Srwatson * Redistribution and use in source and binary forms, with or without 6174914Srwatson * modification, are permitted provided that the following conditions 7174914Srwatson * are met: 8174914Srwatson * 1. Redistributions of source code must retain the above copyright 9174914Srwatson * notice, this list of conditions and the following disclaimer. 10174914Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11174914Srwatson * notice, this list of conditions and the following disclaimer in the 12174914Srwatson * documentation and/or other materials provided with the distribution. 13174914Srwatson * 14174914Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15174914Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16174914Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17174914Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18174914Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19174914Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20174914Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21174914Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22174914Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23174914Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24174914Srwatson * SUCH DAMAGE. 25174914Srwatson */ 26174914Srwatson 27174914Srwatson/*- 28174914Srwatson * Simple DDB scripting mechanism. Each script consists of a named list of 29174914Srwatson * DDB commands to execute sequentially. A more sophisticated scripting 30174914Srwatson * language might be desirable, but would be significantly more complex to 31174914Srwatson * implement. A more interesting syntax might allow general use of variables 32174914Srwatson * and extracting of useful values, such as a thread's process identifier, 33174914Srwatson * for passing into further DDB commands. Certain scripts are run 34174914Srwatson * automatically at kdb_enter(), if defined, based on how the debugger is 35174914Srwatson * entered, allowing scripted responses to panics, break signals, etc. 36174914Srwatson * 37174914Srwatson * Scripts may be managed from within DDB using the script, scripts, and 38174914Srwatson * unscript commands. They may also be managed from userspace using ddb(8), 39174914Srwatson * which operates using a set of sysctls. 40174914Srwatson * 41174914Srwatson * TODO: 42174914Srwatson * - Allow scripts to be defined using tunables so that they can be defined 43174914Srwatson * before boot and be present in single-user mode without boot scripts 44174914Srwatson * running. 45174914Srwatson * - Memory allocation is not possible from within DDB, so we use a set of 46174914Srwatson * statically allocated buffers to hold defined scripts. However, when 47174914Srwatson * scripts are being defined from userspace via sysctl, we could in fact be 48174914Srwatson * using malloc(9) and therefore not impose a static limit, giving greater 49174914Srwatson * flexibility and avoiding hard-defined buffer limits. 50174914Srwatson * - When scripts run automatically on entrance to DDB, placing "continue" at 51174914Srwatson * the end still results in being in the debugger, as we unconditionally 52174914Srwatson * run db_command_loop() after the script. There should be a way to avoid 53174914Srwatson * this. 54174914Srwatson */ 55174914Srwatson 56174914Srwatson#include <sys/cdefs.h> 57174914Srwatson__FBSDID("$FreeBSD: releng/10.2/sys/ddb/db_script.c 174919 2007-12-26 10:51:07Z rwatson $"); 58174914Srwatson 59174914Srwatson#include <sys/param.h> 60174914Srwatson#include <sys/kdb.h> 61174914Srwatson#include <sys/kernel.h> 62174914Srwatson#include <sys/libkern.h> 63174914Srwatson#include <sys/lock.h> 64174914Srwatson#include <sys/malloc.h> 65174914Srwatson#include <sys/mutex.h> 66174914Srwatson#include <sys/sbuf.h> 67174914Srwatson#include <sys/sysctl.h> 68174914Srwatson#include <sys/systm.h> 69174914Srwatson 70174914Srwatson#include <ddb/ddb.h> 71174914Srwatson#include <ddb/db_command.h> 72174914Srwatson#include <ddb/db_lex.h> 73174914Srwatson 74174914Srwatson#include <machine/setjmp.h> 75174914Srwatson 76174914Srwatson/* 77174914Srwatson * struct ddb_script describes an individual script. 78174914Srwatson */ 79174914Srwatsonstruct ddb_script { 80174914Srwatson char ds_scriptname[DB_MAXSCRIPTNAME]; 81174914Srwatson char ds_script[DB_MAXSCRIPTLEN]; 82174914Srwatson}; 83174914Srwatson 84174914Srwatson/* 85174914Srwatson * Global list of scripts -- defined scripts have non-empty name fields. 86174914Srwatson */ 87174914Srwatsonstatic struct ddb_script db_script_table[DB_MAXSCRIPTS]; 88174914Srwatson 89174914Srwatson/* 90174914Srwatson * While executing a script, we parse it using strsep(), so require a 91174914Srwatson * temporary buffer that may be used destructively. Since we support weak 92174914Srwatson * recursion of scripts (one may reference another), we need one buffer for 93174914Srwatson * each concurrently executing script. 94174914Srwatson */ 95174914Srwatsonstatic struct db_recursion_data { 96174914Srwatson char drd_buffer[DB_MAXSCRIPTLEN]; 97174914Srwatson} db_recursion_data[DB_MAXSCRIPTRECURSION]; 98174914Srwatsonstatic int db_recursion = -1; 99174914Srwatson 100174914Srwatson/* 101174914Srwatson * We use a separate static buffer for script validation so that it is safe 102174914Srwatson * to validate scripts from within a script. This is used only in 103174914Srwatson * db_script_valid(), which should never be called reentrantly. 104174914Srwatson */ 105174914Srwatsonstatic char db_static_buffer[DB_MAXSCRIPTLEN]; 106174914Srwatson 107174914Srwatson/* 108174914Srwatson * Synchronization is not required from within the debugger, as it is 109174914Srwatson * singe-threaded (although reentrance must be carefully considered). 110174914Srwatson * However, it is required when interacting with scripts from user space 111174914Srwatson * processes. Sysctl procedures acquire db_script_mtx before accessing the 112174914Srwatson * global script data structures. 113174914Srwatson */ 114174914Srwatsonstatic struct mtx db_script_mtx; 115174914SrwatsonMTX_SYSINIT(db_script_mtx, &db_script_mtx, "db_script_mtx", MTX_DEF); 116174914Srwatson 117174914Srwatson/* 118174914Srwatson * Some script names have special meaning, such as those executed 119174914Srwatson * automatically when KDB is entered. 120174914Srwatson */ 121174914Srwatson#define DB_SCRIPT_KDBENTER_PREFIX "kdb.enter" /* KDB has entered. */ 122174914Srwatson#define DB_SCRIPT_KDBENTER_DEFAULT "kdb.enter.default" 123174914Srwatson 124174914Srwatson/* 125174914Srwatson * Find the existing script slot for a named script, if any. 126174914Srwatson */ 127174914Srwatsonstatic struct ddb_script * 128174914Srwatsondb_script_lookup(const char *scriptname) 129174914Srwatson{ 130174914Srwatson int i; 131174914Srwatson 132174914Srwatson for (i = 0; i < DB_MAXSCRIPTS; i++) { 133174914Srwatson if (strcmp(db_script_table[i].ds_scriptname, scriptname) == 134174914Srwatson 0) 135174914Srwatson return (&db_script_table[i]); 136174914Srwatson } 137174914Srwatson return (NULL); 138174914Srwatson} 139174914Srwatson 140174914Srwatson/* 141174914Srwatson * Find a new slot for a script, if available. Does not mark as allocated in 142174914Srwatson * any way--this must be done by the caller. 143174914Srwatson */ 144174914Srwatsonstatic struct ddb_script * 145174914Srwatsondb_script_new(void) 146174914Srwatson{ 147174914Srwatson int i; 148174914Srwatson 149174914Srwatson for (i = 0; i < DB_MAXSCRIPTS; i++) { 150174914Srwatson if (strlen(db_script_table[i].ds_scriptname) == 0) 151174914Srwatson return (&db_script_table[i]); 152174914Srwatson } 153174914Srwatson return (NULL); 154174914Srwatson} 155174914Srwatson 156174914Srwatson/* 157174914Srwatson * Perform very rudimentary validation of a proposed script. It would be 158174914Srwatson * easy to imagine something more comprehensive. The script string is 159174914Srwatson * validated in a static buffer. 160174914Srwatson */ 161174914Srwatsonstatic int 162174914Srwatsondb_script_valid(const char *scriptname, const char *script) 163174914Srwatson{ 164174914Srwatson char *buffer, *command; 165174914Srwatson 166174914Srwatson if (strlen(scriptname) == 0) 167174914Srwatson return (EINVAL); 168174914Srwatson if (strlen(scriptname) >= DB_MAXSCRIPTNAME) 169174914Srwatson return (EINVAL); 170174914Srwatson if (strlen(script) >= DB_MAXSCRIPTLEN) 171174914Srwatson return (EINVAL); 172174914Srwatson buffer = db_static_buffer; 173174914Srwatson strcpy(buffer, script); 174174914Srwatson while ((command = strsep(&buffer, ";")) != NULL) { 175174914Srwatson if (strlen(command) >= DB_MAXLINE) 176174914Srwatson return (EINVAL); 177174914Srwatson } 178174914Srwatson return (0); 179174914Srwatson} 180174914Srwatson 181174914Srwatson/* 182174914Srwatson * Modify an existing script or add a new script with the specified script 183174914Srwatson * name and contents. If there are no script slots available, an error will 184174914Srwatson * be returned. 185174914Srwatson */ 186174914Srwatsonstatic int 187174914Srwatsondb_script_set(const char *scriptname, const char *script) 188174914Srwatson{ 189174914Srwatson struct ddb_script *dsp; 190174914Srwatson int error; 191174914Srwatson 192174914Srwatson error = db_script_valid(scriptname, script); 193174914Srwatson if (error) 194174914Srwatson return (error); 195174914Srwatson dsp = db_script_lookup(scriptname); 196174914Srwatson if (dsp == NULL) { 197174914Srwatson dsp = db_script_new(); 198174914Srwatson if (dsp == NULL) 199174914Srwatson return (ENOSPC); 200174914Srwatson strlcpy(dsp->ds_scriptname, scriptname, 201174914Srwatson sizeof(dsp->ds_scriptname)); 202174914Srwatson } 203174914Srwatson strlcpy(dsp->ds_script, script, sizeof(dsp->ds_script)); 204174914Srwatson return (0); 205174914Srwatson} 206174914Srwatson 207174914Srwatson/* 208174914Srwatson * Delete an existing script by name, if found. 209174914Srwatson */ 210174914Srwatsonstatic int 211174914Srwatsondb_script_unset(const char *scriptname) 212174914Srwatson{ 213174914Srwatson struct ddb_script *dsp; 214174914Srwatson 215174914Srwatson dsp = db_script_lookup(scriptname); 216174914Srwatson if (dsp == NULL) 217174914Srwatson return (ENOENT); 218174914Srwatson strcpy(dsp->ds_scriptname, ""); 219174914Srwatson strcpy(dsp->ds_script, ""); 220174914Srwatson return (0); 221174914Srwatson} 222174914Srwatson 223174914Srwatson/* 224174914Srwatson * Trim leading/trailing white space in a command so that we don't pass 225174914Srwatson * carriage returns, etc, into DDB command parser. 226174914Srwatson */ 227174914Srwatsonstatic int 228174914Srwatsondb_command_trimmable(char ch) 229174914Srwatson{ 230174914Srwatson 231174914Srwatson switch (ch) { 232174914Srwatson case ' ': 233174914Srwatson case '\t': 234174914Srwatson case '\n': 235174914Srwatson case '\r': 236174914Srwatson return (1); 237174914Srwatson 238174914Srwatson default: 239174914Srwatson return (0); 240174914Srwatson } 241174914Srwatson} 242174914Srwatson 243174914Srwatsonstatic void 244174914Srwatsondb_command_trim(char **commandp) 245174914Srwatson{ 246174914Srwatson char *command; 247174914Srwatson 248174914Srwatson command = *commandp; 249174914Srwatson while (db_command_trimmable(*command)) 250174914Srwatson command++; 251174914Srwatson while ((strlen(command) > 0) && 252174914Srwatson db_command_trimmable(command[strlen(command) - 1])) 253174914Srwatson command[strlen(command) - 1] = 0; 254174914Srwatson *commandp = command; 255174914Srwatson} 256174914Srwatson 257174914Srwatson/* 258174914Srwatson * Execute a script, breaking it up into individual commands and passing them 259174914Srwatson * sequentially into DDB's input processing. Use the KDB jump buffer to 260174914Srwatson * restore control to the main script loop if things get too wonky when 261174914Srwatson * processing a command -- i.e., traps, etc. Also, make sure we don't exceed 262174914Srwatson * practical limits on recursion. 263174914Srwatson * 264174914Srwatson * XXXRW: If any individual command is too long, it will be truncated when 265174914Srwatson * injected into the input at a lower layer. We should validate the script 266174914Srwatson * before configuring it to avoid this scenario. 267174914Srwatson */ 268174914Srwatsonstatic int 269174914Srwatsondb_script_exec(const char *scriptname, int warnifnotfound) 270174914Srwatson{ 271174914Srwatson struct db_recursion_data *drd; 272174914Srwatson struct ddb_script *dsp; 273174914Srwatson char *buffer, *command; 274174914Srwatson void *prev_jb; 275174914Srwatson jmp_buf jb; 276174914Srwatson 277174914Srwatson dsp = db_script_lookup(scriptname); 278174914Srwatson if (dsp == NULL) { 279174914Srwatson if (warnifnotfound) 280174914Srwatson db_printf("script '%s' not found\n", scriptname); 281174914Srwatson return (ENOENT); 282174914Srwatson } 283174914Srwatson 284174914Srwatson if (db_recursion >= DB_MAXSCRIPTRECURSION) { 285174914Srwatson db_printf("Script stack too deep\n"); 286174914Srwatson return (E2BIG); 287174914Srwatson } 288174914Srwatson db_recursion++; 289174914Srwatson drd = &db_recursion_data[db_recursion]; 290174914Srwatson 291174914Srwatson /* 292174914Srwatson * Parse script in temporary buffer, since strsep() is destructive. 293174914Srwatson */ 294174914Srwatson buffer = drd->drd_buffer; 295174914Srwatson strcpy(buffer, dsp->ds_script); 296174914Srwatson while ((command = strsep(&buffer, ";")) != NULL) { 297174914Srwatson db_printf("db:%d:%s> %s\n", db_recursion, scriptname, 298174914Srwatson command); 299174914Srwatson db_command_trim(&command); 300174914Srwatson prev_jb = kdb_jmpbuf(jb); 301174914Srwatson if (setjmp(jb) == 0) 302174914Srwatson db_command_script(command); 303174914Srwatson else 304174914Srwatson db_printf("Script command '%s' returned error\n", 305174914Srwatson command); 306174914Srwatson kdb_jmpbuf(prev_jb); 307174914Srwatson } 308174914Srwatson db_recursion--; 309174914Srwatson return (0); 310174914Srwatson} 311174914Srwatson 312174914Srwatson/* 313174914Srwatson * Wrapper for exec path that is called on KDB enter. Map reason for KDB 314174914Srwatson * enter to a script name, and don't whine if the script doesn't exist. If 315174914Srwatson * there is no matching script, try the catch-all script. 316174914Srwatson */ 317174914Srwatsonvoid 318174914Srwatsondb_script_kdbenter(const char *eventname) 319174914Srwatson{ 320174914Srwatson char scriptname[DB_MAXSCRIPTNAME]; 321174914Srwatson 322174914Srwatson snprintf(scriptname, sizeof(scriptname), "%s.%s", 323174914Srwatson DB_SCRIPT_KDBENTER_PREFIX, eventname); 324174914Srwatson if (db_script_exec(scriptname, 0) == ENOENT) 325174914Srwatson (void)db_script_exec(DB_SCRIPT_KDBENTER_DEFAULT, 0); 326174914Srwatson} 327174914Srwatson 328174914Srwatson/*- 329174914Srwatson * DDB commands for scripting, as reached via the DDB user interface: 330174914Srwatson * 331174914Srwatson * scripts - lists scripts 332174914Srwatson * run <scriptname> - run a script 333174914Srwatson * script <scriptname> - prints script 334174914Srwatson * script <scriptname> <script> - set a script 335174914Srwatson * unscript <scriptname> - remove a script 336174914Srwatson */ 337174914Srwatson 338174914Srwatson/* 339174914Srwatson * List scripts and their contents. 340174914Srwatson */ 341174914Srwatsonvoid 342174914Srwatsondb_scripts_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 343174914Srwatson char *modif) 344174914Srwatson{ 345174914Srwatson int i; 346174914Srwatson 347174914Srwatson for (i = 0; i < DB_MAXSCRIPTS; i++) { 348174914Srwatson if (strlen(db_script_table[i].ds_scriptname) != 0) { 349174914Srwatson db_printf("%s=%s\n", 350174914Srwatson db_script_table[i].ds_scriptname, 351174914Srwatson db_script_table[i].ds_script); 352174914Srwatson } 353174914Srwatson } 354174914Srwatson} 355174914Srwatson 356174914Srwatson/* 357174914Srwatson * Execute a script. 358174914Srwatson */ 359174914Srwatsonvoid 360174914Srwatsondb_run_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif) 361174914Srwatson{ 362174914Srwatson int t; 363174914Srwatson 364174914Srwatson /* 365174914Srwatson * Right now, we accept exactly one argument. In the future, we 366174914Srwatson * might want to accept flags and arguments to the script itself. 367174914Srwatson */ 368174914Srwatson t = db_read_token(); 369174914Srwatson if (t != tIDENT) 370174914Srwatson db_error("?\n"); 371174914Srwatson 372174914Srwatson if (db_read_token() != tEOL) 373174914Srwatson db_error("?\n"); 374174914Srwatson 375174914Srwatson db_script_exec(db_tok_string, 1); 376174914Srwatson} 377174914Srwatson 378174914Srwatson/* 379174914Srwatson * Print or set a named script, with the set portion broken out into its own 380174914Srwatson * function. We must directly access the remainder of the DDB line input as 381174914Srwatson * we do not wish to use db_lex's token processing. 382174914Srwatson */ 383174914Srwatsonvoid 384174914Srwatsondb_script_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 385174914Srwatson char *modif) 386174914Srwatson{ 387174914Srwatson char *buf, scriptname[DB_MAXSCRIPTNAME]; 388174914Srwatson struct ddb_script *dsp; 389174914Srwatson int error, t; 390174914Srwatson 391174914Srwatson t = db_read_token(); 392174914Srwatson if (t != tIDENT) { 393174914Srwatson db_printf("usage: script scriptname=script\n"); 394174914Srwatson db_skip_to_eol(); 395174914Srwatson return; 396174914Srwatson } 397174914Srwatson 398174914Srwatson if (strlcpy(scriptname, db_tok_string, sizeof(scriptname)) >= 399174914Srwatson sizeof(scriptname)) { 400174914Srwatson db_printf("scriptname too long\n"); 401174914Srwatson db_skip_to_eol(); 402174914Srwatson return; 403174914Srwatson } 404174914Srwatson 405174914Srwatson t = db_read_token(); 406174914Srwatson if (t == tEOL) { 407174914Srwatson dsp = db_script_lookup(scriptname); 408174914Srwatson if (dsp == NULL) { 409174914Srwatson db_printf("script '%s' not found\n", scriptname); 410174914Srwatson db_skip_to_eol(); 411174914Srwatson return; 412174914Srwatson } 413174914Srwatson db_printf("%s=%s\n", scriptname, dsp->ds_script); 414174914Srwatson } else if (t == tEQ) { 415174914Srwatson buf = db_get_line(); 416174914Srwatson if (buf[strlen(buf)-1] == '\n') 417174914Srwatson buf[strlen(buf)-1] = '\0'; 418174914Srwatson error = db_script_set(scriptname, buf); 419174914Srwatson if (error != 0) 420174914Srwatson db_printf("Error: %d\n", error); 421174914Srwatson } else 422174914Srwatson db_printf("?\n"); 423174914Srwatson db_skip_to_eol(); 424174914Srwatson} 425174914Srwatson 426174914Srwatson/* 427174914Srwatson * Remove a named script. 428174914Srwatson */ 429174914Srwatsonvoid 430174914Srwatsondb_unscript_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 431174914Srwatson char *modif) 432174914Srwatson{ 433174914Srwatson int error, t; 434174914Srwatson 435174914Srwatson t = db_read_token(); 436174914Srwatson if (t != tIDENT) { 437174914Srwatson db_printf("?\n"); 438174914Srwatson db_skip_to_eol(); 439174914Srwatson return; 440174914Srwatson } 441174914Srwatson 442174914Srwatson error = db_script_unset(db_tok_string); 443174914Srwatson if (error == ENOENT) { 444174914Srwatson db_printf("script '%s' not found\n", db_tok_string); 445174914Srwatson db_skip_to_eol(); 446174914Srwatson return; 447174914Srwatson } 448174914Srwatson db_skip_to_eol(); 449174914Srwatson} 450174914Srwatson 451174914Srwatson/* 452174914Srwatson * Sysctls for managing DDB scripting: 453174914Srwatson * 454174914Srwatson * debug.ddb.scripting.script - Define a new script 455174914Srwatson * debug.ddb.scripting.scripts - List of names *and* scripts 456174914Srwatson * debug.ddb.scripting.unscript - Remove an existing script 457174914Srwatson * 458174914Srwatson * Since we don't want to try to manage arbitrary extensions to the sysctl 459174914Srwatson * name space from the debugger, the script/unscript sysctls are a bit more 460174914Srwatson * like RPCs and a bit less like normal get/set requests. The ddb(8) command 461174914Srwatson * line tool wraps them to make things a bit more user-friendly. 462174914Srwatson */ 463174914Srwatsonstatic SYSCTL_NODE(_debug_ddb, OID_AUTO, scripting, CTLFLAG_RW, 0, 464174914Srwatson "DDB script settings"); 465174914Srwatson 466174914Srwatsonstatic int 467174914Srwatsonsysctl_debug_ddb_scripting_scripts(SYSCTL_HANDLER_ARGS) 468174914Srwatson{ 469174914Srwatson struct sbuf sb; 470174914Srwatson int error, i, len; 471174914Srwatson char *buffer; 472174914Srwatson 473174914Srwatson /* 474174914Srwatson * Make space to include a maximum-length name, = symbol, 475174914Srwatson * maximum-length script, and carriage return for every script that 476174914Srwatson * may be defined. 477174914Srwatson */ 478174914Srwatson len = DB_MAXSCRIPTS * (DB_MAXSCRIPTNAME + 1 + DB_MAXSCRIPTLEN + 1); 479174914Srwatson buffer = malloc(len, M_TEMP, M_WAITOK); 480174914Srwatson (void)sbuf_new(&sb, buffer, len, SBUF_FIXEDLEN); 481174914Srwatson mtx_lock(&db_script_mtx); 482174914Srwatson for (i = 0; i < DB_MAXSCRIPTS; i++) { 483174914Srwatson if (strlen(db_script_table[i].ds_scriptname) == 0) 484174914Srwatson continue; 485174914Srwatson (void)sbuf_printf(&sb, "%s=%s\n", 486174914Srwatson db_script_table[i].ds_scriptname, 487174914Srwatson db_script_table[i].ds_script); 488174914Srwatson } 489174914Srwatson mtx_unlock(&db_script_mtx); 490174914Srwatson sbuf_finish(&sb); 491174914Srwatson error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb) + 1); 492174914Srwatson sbuf_delete(&sb); 493174914Srwatson free(buffer, M_TEMP); 494174914Srwatson return (error); 495174914Srwatson} 496174914SrwatsonSYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, scripts, CTLTYPE_STRING | 497174914Srwatson CTLFLAG_RD, 0, 0, sysctl_debug_ddb_scripting_scripts, "A", 498174914Srwatson "List of defined scripts"); 499174914Srwatson 500174914Srwatsonstatic int 501174914Srwatsonsysctl_debug_ddb_scripting_script(SYSCTL_HANDLER_ARGS) 502174914Srwatson{ 503174914Srwatson char *buffer, *script, *scriptname; 504174914Srwatson int error, len; 505174914Srwatson 506174914Srwatson /* 507174914Srwatson * Maximum length for an input string is DB_MAXSCRIPTNAME + '=' 508174914Srwatson * symbol + DB_MAXSCRIPT. 509174914Srwatson */ 510174914Srwatson len = DB_MAXSCRIPTNAME + DB_MAXSCRIPTLEN + 1; 511174914Srwatson buffer = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 512174914Srwatson error = sysctl_handle_string(oidp, buffer, len, req); 513174914Srwatson if (error) 514174914Srwatson goto out; 515174914Srwatson 516174914Srwatson /* 517174914Srwatson * Argument will be in form scriptname=script, so split into the 518174914Srwatson * scriptname and script. 519174914Srwatson */ 520174914Srwatson script = buffer; 521174914Srwatson scriptname = strsep(&script, "="); 522174914Srwatson if (script == NULL) { 523174914Srwatson error = EINVAL; 524174914Srwatson goto out; 525174914Srwatson } 526174914Srwatson mtx_lock(&db_script_mtx); 527174914Srwatson error = db_script_set(scriptname, script); 528174914Srwatson mtx_unlock(&db_script_mtx); 529174914Srwatsonout: 530174914Srwatson free(buffer, M_TEMP); 531174914Srwatson return (error); 532174914Srwatson} 533174914SrwatsonSYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, script, CTLTYPE_STRING | 534174914Srwatson CTLFLAG_RW, 0, 0, sysctl_debug_ddb_scripting_script, "A", 535174914Srwatson "Set a script"); 536174914Srwatson 537174914Srwatson/* 538174914Srwatson * debug.ddb.scripting.unscript has somewhat unusual sysctl semantics -- set 539174914Srwatson * the name of the script that you want to delete. 540174914Srwatson */ 541174914Srwatsonstatic int 542174914Srwatsonsysctl_debug_ddb_scripting_unscript(SYSCTL_HANDLER_ARGS) 543174914Srwatson{ 544174914Srwatson char name[DB_MAXSCRIPTNAME]; 545174914Srwatson int error; 546174914Srwatson 547174914Srwatson bzero(name, sizeof(name)); 548174914Srwatson error = sysctl_handle_string(oidp, name, sizeof(name), req); 549174914Srwatson if (error) 550174914Srwatson return (error); 551174914Srwatson if (req->newptr == NULL) 552174914Srwatson return (0); 553174914Srwatson mtx_lock(&db_script_mtx); 554174914Srwatson error = db_script_unset(name); 555174914Srwatson mtx_unlock(&db_script_mtx); 556174914Srwatson if (error == ENOENT) 557174914Srwatson return (EINVAL); /* Don't confuse sysctl consumers. */ 558174914Srwatson return (0); 559174914Srwatson} 560174914SrwatsonSYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, unscript, CTLTYPE_STRING | 561174914Srwatson CTLFLAG_RW, 0, 0, sysctl_debug_ddb_scripting_unscript, "A", 562174914Srwatson "Unset a script"); 563