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