1c9b0cc3bSRobert Watson /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3753c4e83SPedro F. Giffuni * 4c9b0cc3bSRobert Watson * Copyright (c) 2007 Robert N. M. Watson 5c9b0cc3bSRobert Watson * All rights reserved. 6c9b0cc3bSRobert Watson * 7c9b0cc3bSRobert Watson * Redistribution and use in source and binary forms, with or without 8c9b0cc3bSRobert Watson * modification, are permitted provided that the following conditions 9c9b0cc3bSRobert Watson * are met: 10c9b0cc3bSRobert Watson * 1. Redistributions of source code must retain the above copyright 11c9b0cc3bSRobert Watson * notice, this list of conditions and the following disclaimer. 12c9b0cc3bSRobert Watson * 2. Redistributions in binary form must reproduce the above copyright 13c9b0cc3bSRobert Watson * notice, this list of conditions and the following disclaimer in the 14c9b0cc3bSRobert Watson * documentation and/or other materials provided with the distribution. 15c9b0cc3bSRobert Watson * 16c9b0cc3bSRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17c9b0cc3bSRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18c9b0cc3bSRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19c9b0cc3bSRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20c9b0cc3bSRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21c9b0cc3bSRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22c9b0cc3bSRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23c9b0cc3bSRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24c9b0cc3bSRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25c9b0cc3bSRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26c9b0cc3bSRobert Watson * SUCH DAMAGE. 27c9b0cc3bSRobert Watson */ 28c9b0cc3bSRobert Watson 29c9b0cc3bSRobert Watson /*- 30c9b0cc3bSRobert Watson * Simple DDB scripting mechanism. Each script consists of a named list of 31c9b0cc3bSRobert Watson * DDB commands to execute sequentially. A more sophisticated scripting 32c9b0cc3bSRobert Watson * language might be desirable, but would be significantly more complex to 33c9b0cc3bSRobert Watson * implement. A more interesting syntax might allow general use of variables 34c9b0cc3bSRobert Watson * and extracting of useful values, such as a thread's process identifier, 35c9b0cc3bSRobert Watson * for passing into further DDB commands. Certain scripts are run 36c9b0cc3bSRobert Watson * automatically at kdb_enter(), if defined, based on how the debugger is 37c9b0cc3bSRobert Watson * entered, allowing scripted responses to panics, break signals, etc. 38c9b0cc3bSRobert Watson * 39c9b0cc3bSRobert Watson * Scripts may be managed from within DDB using the script, scripts, and 40c9b0cc3bSRobert Watson * unscript commands. They may also be managed from userspace using ddb(8), 41c9b0cc3bSRobert Watson * which operates using a set of sysctls. 42c9b0cc3bSRobert Watson * 43c9b0cc3bSRobert Watson * TODO: 44c9b0cc3bSRobert Watson * - Allow scripts to be defined using tunables so that they can be defined 45c9b0cc3bSRobert Watson * before boot and be present in single-user mode without boot scripts 46c9b0cc3bSRobert Watson * running. 47c9b0cc3bSRobert Watson * - Memory allocation is not possible from within DDB, so we use a set of 48c9b0cc3bSRobert Watson * statically allocated buffers to hold defined scripts. However, when 49c9b0cc3bSRobert Watson * scripts are being defined from userspace via sysctl, we could in fact be 50c9b0cc3bSRobert Watson * using malloc(9) and therefore not impose a static limit, giving greater 51c9b0cc3bSRobert Watson * flexibility and avoiding hard-defined buffer limits. 52c9b0cc3bSRobert Watson * - When scripts run automatically on entrance to DDB, placing "continue" at 53c9b0cc3bSRobert Watson * the end still results in being in the debugger, as we unconditionally 54c9b0cc3bSRobert Watson * run db_command_loop() after the script. There should be a way to avoid 55c9b0cc3bSRobert Watson * this. 56c9b0cc3bSRobert Watson */ 57c9b0cc3bSRobert Watson 58c9b0cc3bSRobert Watson #include <sys/cdefs.h> 59c9b0cc3bSRobert Watson __FBSDID("$FreeBSD$"); 60c9b0cc3bSRobert Watson 61c9b0cc3bSRobert Watson #include <sys/param.h> 62c9b0cc3bSRobert Watson #include <sys/kdb.h> 63c9b0cc3bSRobert Watson #include <sys/kernel.h> 64c9b0cc3bSRobert Watson #include <sys/libkern.h> 65c9b0cc3bSRobert Watson #include <sys/lock.h> 66c9b0cc3bSRobert Watson #include <sys/malloc.h> 67c9b0cc3bSRobert Watson #include <sys/mutex.h> 68c9b0cc3bSRobert Watson #include <sys/sbuf.h> 69c9b0cc3bSRobert Watson #include <sys/sysctl.h> 70c9b0cc3bSRobert Watson #include <sys/systm.h> 71c9b0cc3bSRobert Watson 72c9b0cc3bSRobert Watson #include <ddb/ddb.h> 73c9b0cc3bSRobert Watson #include <ddb/db_command.h> 74c9b0cc3bSRobert Watson #include <ddb/db_lex.h> 75c9b0cc3bSRobert Watson 76c9b0cc3bSRobert Watson #include <machine/setjmp.h> 77c9b0cc3bSRobert Watson 78c9b0cc3bSRobert Watson /* 79c9b0cc3bSRobert Watson * struct ddb_script describes an individual script. 80c9b0cc3bSRobert Watson */ 81c9b0cc3bSRobert Watson struct ddb_script { 82c9b0cc3bSRobert Watson char ds_scriptname[DB_MAXSCRIPTNAME]; 83c9b0cc3bSRobert Watson char ds_script[DB_MAXSCRIPTLEN]; 84c9b0cc3bSRobert Watson }; 85c9b0cc3bSRobert Watson 86c9b0cc3bSRobert Watson /* 87c9b0cc3bSRobert Watson * Global list of scripts -- defined scripts have non-empty name fields. 88c9b0cc3bSRobert Watson */ 89c9b0cc3bSRobert Watson static struct ddb_script db_script_table[DB_MAXSCRIPTS]; 90c9b0cc3bSRobert Watson 91c9b0cc3bSRobert Watson /* 92c9b0cc3bSRobert Watson * While executing a script, we parse it using strsep(), so require a 93c9b0cc3bSRobert Watson * temporary buffer that may be used destructively. Since we support weak 94c9b0cc3bSRobert Watson * recursion of scripts (one may reference another), we need one buffer for 95c9b0cc3bSRobert Watson * each concurrently executing script. 96c9b0cc3bSRobert Watson */ 97c9b0cc3bSRobert Watson static struct db_recursion_data { 98c9b0cc3bSRobert Watson char drd_buffer[DB_MAXSCRIPTLEN]; 99c9b0cc3bSRobert Watson } db_recursion_data[DB_MAXSCRIPTRECURSION]; 100c9b0cc3bSRobert Watson static int db_recursion = -1; 101c9b0cc3bSRobert Watson 102c9b0cc3bSRobert Watson /* 103c9b0cc3bSRobert Watson * We use a separate static buffer for script validation so that it is safe 104c9b0cc3bSRobert Watson * to validate scripts from within a script. This is used only in 105c9b0cc3bSRobert Watson * db_script_valid(), which should never be called reentrantly. 106c9b0cc3bSRobert Watson */ 107c9b0cc3bSRobert Watson static char db_static_buffer[DB_MAXSCRIPTLEN]; 108c9b0cc3bSRobert Watson 109c9b0cc3bSRobert Watson /* 110c9b0cc3bSRobert Watson * Synchronization is not required from within the debugger, as it is 111c9b0cc3bSRobert Watson * singe-threaded (although reentrance must be carefully considered). 112c9b0cc3bSRobert Watson * However, it is required when interacting with scripts from user space 113c9b0cc3bSRobert Watson * processes. Sysctl procedures acquire db_script_mtx before accessing the 114c9b0cc3bSRobert Watson * global script data structures. 115c9b0cc3bSRobert Watson */ 116c9b0cc3bSRobert Watson static struct mtx db_script_mtx; 117c9b0cc3bSRobert Watson MTX_SYSINIT(db_script_mtx, &db_script_mtx, "db_script_mtx", MTX_DEF); 118c9b0cc3bSRobert Watson 119c9b0cc3bSRobert Watson /* 120c9b0cc3bSRobert Watson * Some script names have special meaning, such as those executed 121c9b0cc3bSRobert Watson * automatically when KDB is entered. 122c9b0cc3bSRobert Watson */ 123c9b0cc3bSRobert Watson #define DB_SCRIPT_KDBENTER_PREFIX "kdb.enter" /* KDB has entered. */ 124c9b0cc3bSRobert Watson #define DB_SCRIPT_KDBENTER_DEFAULT "kdb.enter.default" 125c9b0cc3bSRobert Watson 126c9b0cc3bSRobert Watson /* 127c9b0cc3bSRobert Watson * Find the existing script slot for a named script, if any. 128c9b0cc3bSRobert Watson */ 129c9b0cc3bSRobert Watson static struct ddb_script * 130c9b0cc3bSRobert Watson db_script_lookup(const char *scriptname) 131c9b0cc3bSRobert Watson { 132c9b0cc3bSRobert Watson int i; 133c9b0cc3bSRobert Watson 134c9b0cc3bSRobert Watson for (i = 0; i < DB_MAXSCRIPTS; i++) { 135c9b0cc3bSRobert Watson if (strcmp(db_script_table[i].ds_scriptname, scriptname) == 136c9b0cc3bSRobert Watson 0) 137c9b0cc3bSRobert Watson return (&db_script_table[i]); 138c9b0cc3bSRobert Watson } 139c9b0cc3bSRobert Watson return (NULL); 140c9b0cc3bSRobert Watson } 141c9b0cc3bSRobert Watson 142c9b0cc3bSRobert Watson /* 143c9b0cc3bSRobert Watson * Find a new slot for a script, if available. Does not mark as allocated in 144c9b0cc3bSRobert Watson * any way--this must be done by the caller. 145c9b0cc3bSRobert Watson */ 146c9b0cc3bSRobert Watson static struct ddb_script * 147c9b0cc3bSRobert Watson db_script_new(void) 148c9b0cc3bSRobert Watson { 149c9b0cc3bSRobert Watson int i; 150c9b0cc3bSRobert Watson 151c9b0cc3bSRobert Watson for (i = 0; i < DB_MAXSCRIPTS; i++) { 152c9b0cc3bSRobert Watson if (strlen(db_script_table[i].ds_scriptname) == 0) 153c9b0cc3bSRobert Watson return (&db_script_table[i]); 154c9b0cc3bSRobert Watson } 155c9b0cc3bSRobert Watson return (NULL); 156c9b0cc3bSRobert Watson } 157c9b0cc3bSRobert Watson 158c9b0cc3bSRobert Watson /* 159c9b0cc3bSRobert Watson * Perform very rudimentary validation of a proposed script. It would be 160c9b0cc3bSRobert Watson * easy to imagine something more comprehensive. The script string is 161c9b0cc3bSRobert Watson * validated in a static buffer. 162c9b0cc3bSRobert Watson */ 163c9b0cc3bSRobert Watson static int 164c9b0cc3bSRobert Watson db_script_valid(const char *scriptname, const char *script) 165c9b0cc3bSRobert Watson { 166c9b0cc3bSRobert Watson char *buffer, *command; 167c9b0cc3bSRobert Watson 168c9b0cc3bSRobert Watson if (strlen(scriptname) == 0) 169c9b0cc3bSRobert Watson return (EINVAL); 170c9b0cc3bSRobert Watson if (strlen(scriptname) >= DB_MAXSCRIPTNAME) 171c9b0cc3bSRobert Watson return (EINVAL); 172c9b0cc3bSRobert Watson if (strlen(script) >= DB_MAXSCRIPTLEN) 173c9b0cc3bSRobert Watson return (EINVAL); 174c9b0cc3bSRobert Watson buffer = db_static_buffer; 175c9b0cc3bSRobert Watson strcpy(buffer, script); 176c9b0cc3bSRobert Watson while ((command = strsep(&buffer, ";")) != NULL) { 177c9b0cc3bSRobert Watson if (strlen(command) >= DB_MAXLINE) 178c9b0cc3bSRobert Watson return (EINVAL); 179c9b0cc3bSRobert Watson } 180c9b0cc3bSRobert Watson return (0); 181c9b0cc3bSRobert Watson } 182c9b0cc3bSRobert Watson 183c9b0cc3bSRobert Watson /* 184c9b0cc3bSRobert Watson * Modify an existing script or add a new script with the specified script 185c9b0cc3bSRobert Watson * name and contents. If there are no script slots available, an error will 186c9b0cc3bSRobert Watson * be returned. 187c9b0cc3bSRobert Watson */ 188c9b0cc3bSRobert Watson static int 189c9b0cc3bSRobert Watson db_script_set(const char *scriptname, const char *script) 190c9b0cc3bSRobert Watson { 191c9b0cc3bSRobert Watson struct ddb_script *dsp; 192c9b0cc3bSRobert Watson int error; 193c9b0cc3bSRobert Watson 194c9b0cc3bSRobert Watson error = db_script_valid(scriptname, script); 195c9b0cc3bSRobert Watson if (error) 196c9b0cc3bSRobert Watson return (error); 197c9b0cc3bSRobert Watson dsp = db_script_lookup(scriptname); 198c9b0cc3bSRobert Watson if (dsp == NULL) { 199c9b0cc3bSRobert Watson dsp = db_script_new(); 200c9b0cc3bSRobert Watson if (dsp == NULL) 201c9b0cc3bSRobert Watson return (ENOSPC); 202c9b0cc3bSRobert Watson strlcpy(dsp->ds_scriptname, scriptname, 203c9b0cc3bSRobert Watson sizeof(dsp->ds_scriptname)); 204c9b0cc3bSRobert Watson } 205c9b0cc3bSRobert Watson strlcpy(dsp->ds_script, script, sizeof(dsp->ds_script)); 206c9b0cc3bSRobert Watson return (0); 207c9b0cc3bSRobert Watson } 208c9b0cc3bSRobert Watson 209c9b0cc3bSRobert Watson /* 210c9b0cc3bSRobert Watson * Delete an existing script by name, if found. 211c9b0cc3bSRobert Watson */ 212c9b0cc3bSRobert Watson static int 213c9b0cc3bSRobert Watson db_script_unset(const char *scriptname) 214c9b0cc3bSRobert Watson { 215c9b0cc3bSRobert Watson struct ddb_script *dsp; 216c9b0cc3bSRobert Watson 217c9b0cc3bSRobert Watson dsp = db_script_lookup(scriptname); 218c9b0cc3bSRobert Watson if (dsp == NULL) 219c9b0cc3bSRobert Watson return (ENOENT); 220c9b0cc3bSRobert Watson strcpy(dsp->ds_scriptname, ""); 221c9b0cc3bSRobert Watson strcpy(dsp->ds_script, ""); 222c9b0cc3bSRobert Watson return (0); 223c9b0cc3bSRobert Watson } 224c9b0cc3bSRobert Watson 225c9b0cc3bSRobert Watson /* 226c9b0cc3bSRobert Watson * Trim leading/trailing white space in a command so that we don't pass 227c9b0cc3bSRobert Watson * carriage returns, etc, into DDB command parser. 228c9b0cc3bSRobert Watson */ 229c9b0cc3bSRobert Watson static int 230c9b0cc3bSRobert Watson db_command_trimmable(char ch) 231c9b0cc3bSRobert Watson { 232c9b0cc3bSRobert Watson 233c9b0cc3bSRobert Watson switch (ch) { 234c9b0cc3bSRobert Watson case ' ': 235c9b0cc3bSRobert Watson case '\t': 236c9b0cc3bSRobert Watson case '\n': 237c9b0cc3bSRobert Watson case '\r': 238c9b0cc3bSRobert Watson return (1); 239c9b0cc3bSRobert Watson 240c9b0cc3bSRobert Watson default: 241c9b0cc3bSRobert Watson return (0); 242c9b0cc3bSRobert Watson } 243c9b0cc3bSRobert Watson } 244c9b0cc3bSRobert Watson 245c9b0cc3bSRobert Watson static void 246c9b0cc3bSRobert Watson db_command_trim(char **commandp) 247c9b0cc3bSRobert Watson { 248c9b0cc3bSRobert Watson char *command; 249c9b0cc3bSRobert Watson 250c9b0cc3bSRobert Watson command = *commandp; 251c9b0cc3bSRobert Watson while (db_command_trimmable(*command)) 252c9b0cc3bSRobert Watson command++; 253c9b0cc3bSRobert Watson while ((strlen(command) > 0) && 254c9b0cc3bSRobert Watson db_command_trimmable(command[strlen(command) - 1])) 255c9b0cc3bSRobert Watson command[strlen(command) - 1] = 0; 256c9b0cc3bSRobert Watson *commandp = command; 257c9b0cc3bSRobert Watson } 258c9b0cc3bSRobert Watson 259c9b0cc3bSRobert Watson /* 260c9b0cc3bSRobert Watson * Execute a script, breaking it up into individual commands and passing them 261c9b0cc3bSRobert Watson * sequentially into DDB's input processing. Use the KDB jump buffer to 262c9b0cc3bSRobert Watson * restore control to the main script loop if things get too wonky when 263c9b0cc3bSRobert Watson * processing a command -- i.e., traps, etc. Also, make sure we don't exceed 264c9b0cc3bSRobert Watson * practical limits on recursion. 265c9b0cc3bSRobert Watson * 266c9b0cc3bSRobert Watson * XXXRW: If any individual command is too long, it will be truncated when 267c9b0cc3bSRobert Watson * injected into the input at a lower layer. We should validate the script 268c9b0cc3bSRobert Watson * before configuring it to avoid this scenario. 269c9b0cc3bSRobert Watson */ 270c9b0cc3bSRobert Watson static int 271c9b0cc3bSRobert Watson db_script_exec(const char *scriptname, int warnifnotfound) 272c9b0cc3bSRobert Watson { 273c9b0cc3bSRobert Watson struct db_recursion_data *drd; 274c9b0cc3bSRobert Watson struct ddb_script *dsp; 275c9b0cc3bSRobert Watson char *buffer, *command; 276c9b0cc3bSRobert Watson void *prev_jb; 277c9b0cc3bSRobert Watson jmp_buf jb; 278c9b0cc3bSRobert Watson 279c9b0cc3bSRobert Watson dsp = db_script_lookup(scriptname); 280c9b0cc3bSRobert Watson if (dsp == NULL) { 281c9b0cc3bSRobert Watson if (warnifnotfound) 282c9b0cc3bSRobert Watson db_printf("script '%s' not found\n", scriptname); 283c9b0cc3bSRobert Watson return (ENOENT); 284c9b0cc3bSRobert Watson } 285c9b0cc3bSRobert Watson 286c9b0cc3bSRobert Watson if (db_recursion >= DB_MAXSCRIPTRECURSION) { 287c9b0cc3bSRobert Watson db_printf("Script stack too deep\n"); 288c9b0cc3bSRobert Watson return (E2BIG); 289c9b0cc3bSRobert Watson } 290c9b0cc3bSRobert Watson db_recursion++; 291c9b0cc3bSRobert Watson drd = &db_recursion_data[db_recursion]; 292c9b0cc3bSRobert Watson 293c9b0cc3bSRobert Watson /* 294c9b0cc3bSRobert Watson * Parse script in temporary buffer, since strsep() is destructive. 295c9b0cc3bSRobert Watson */ 296c9b0cc3bSRobert Watson buffer = drd->drd_buffer; 297c9b0cc3bSRobert Watson strcpy(buffer, dsp->ds_script); 298c9b0cc3bSRobert Watson while ((command = strsep(&buffer, ";")) != NULL) { 2993761beadSAndriy Gapon db_printf("db:%d:%s> %s\n", db_recursion, dsp->ds_scriptname, 300c9b0cc3bSRobert Watson command); 301c9b0cc3bSRobert Watson db_command_trim(&command); 302c9b0cc3bSRobert Watson prev_jb = kdb_jmpbuf(jb); 303c9b0cc3bSRobert Watson if (setjmp(jb) == 0) 304c9b0cc3bSRobert Watson db_command_script(command); 305c9b0cc3bSRobert Watson else 306c9b0cc3bSRobert Watson db_printf("Script command '%s' returned error\n", 307c9b0cc3bSRobert Watson command); 308c9b0cc3bSRobert Watson kdb_jmpbuf(prev_jb); 309c9b0cc3bSRobert Watson } 310c9b0cc3bSRobert Watson db_recursion--; 311c9b0cc3bSRobert Watson return (0); 312c9b0cc3bSRobert Watson } 313c9b0cc3bSRobert Watson 314c9b0cc3bSRobert Watson /* 315c9b0cc3bSRobert Watson * Wrapper for exec path that is called on KDB enter. Map reason for KDB 316c9b0cc3bSRobert Watson * enter to a script name, and don't whine if the script doesn't exist. If 317c9b0cc3bSRobert Watson * there is no matching script, try the catch-all script. 318c9b0cc3bSRobert Watson */ 319c9b0cc3bSRobert Watson void 320c9b0cc3bSRobert Watson db_script_kdbenter(const char *eventname) 321c9b0cc3bSRobert Watson { 322c9b0cc3bSRobert Watson char scriptname[DB_MAXSCRIPTNAME]; 323c9b0cc3bSRobert Watson 324c9b0cc3bSRobert Watson snprintf(scriptname, sizeof(scriptname), "%s.%s", 325c9b0cc3bSRobert Watson DB_SCRIPT_KDBENTER_PREFIX, eventname); 326c9b0cc3bSRobert Watson if (db_script_exec(scriptname, 0) == ENOENT) 327c9b0cc3bSRobert Watson (void)db_script_exec(DB_SCRIPT_KDBENTER_DEFAULT, 0); 328c9b0cc3bSRobert Watson } 329c9b0cc3bSRobert Watson 330c9b0cc3bSRobert Watson /*- 331c9b0cc3bSRobert Watson * DDB commands for scripting, as reached via the DDB user interface: 332c9b0cc3bSRobert Watson * 333c9b0cc3bSRobert Watson * scripts - lists scripts 334c9b0cc3bSRobert Watson * run <scriptname> - run a script 335c9b0cc3bSRobert Watson * script <scriptname> - prints script 336c9b0cc3bSRobert Watson * script <scriptname> <script> - set a script 337c9b0cc3bSRobert Watson * unscript <scriptname> - remove a script 338c9b0cc3bSRobert Watson */ 339c9b0cc3bSRobert Watson 340c9b0cc3bSRobert Watson /* 341c9b0cc3bSRobert Watson * List scripts and their contents. 342c9b0cc3bSRobert Watson */ 343c9b0cc3bSRobert Watson void 344cd508278SPedro F. Giffuni db_scripts_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 345c9b0cc3bSRobert Watson char *modif) 346c9b0cc3bSRobert Watson { 347c9b0cc3bSRobert Watson int i; 348c9b0cc3bSRobert Watson 349c9b0cc3bSRobert Watson for (i = 0; i < DB_MAXSCRIPTS; i++) { 350c9b0cc3bSRobert Watson if (strlen(db_script_table[i].ds_scriptname) != 0) { 351c9b0cc3bSRobert Watson db_printf("%s=%s\n", 352c9b0cc3bSRobert Watson db_script_table[i].ds_scriptname, 353c9b0cc3bSRobert Watson db_script_table[i].ds_script); 354c9b0cc3bSRobert Watson } 355c9b0cc3bSRobert Watson } 356c9b0cc3bSRobert Watson } 357c9b0cc3bSRobert Watson 358c9b0cc3bSRobert Watson /* 359c9b0cc3bSRobert Watson * Execute a script. 360c9b0cc3bSRobert Watson */ 361c9b0cc3bSRobert Watson void 362cd508278SPedro F. Giffuni db_run_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 363c9b0cc3bSRobert Watson { 364c9b0cc3bSRobert Watson int t; 365c9b0cc3bSRobert Watson 366c9b0cc3bSRobert Watson /* 367c9b0cc3bSRobert Watson * Right now, we accept exactly one argument. In the future, we 368c9b0cc3bSRobert Watson * might want to accept flags and arguments to the script itself. 369c9b0cc3bSRobert Watson */ 370c9b0cc3bSRobert Watson t = db_read_token(); 371c9b0cc3bSRobert Watson if (t != tIDENT) 372c9b0cc3bSRobert Watson db_error("?\n"); 373c9b0cc3bSRobert Watson 374c9b0cc3bSRobert Watson if (db_read_token() != tEOL) 375c9b0cc3bSRobert Watson db_error("?\n"); 376c9b0cc3bSRobert Watson 377c9b0cc3bSRobert Watson db_script_exec(db_tok_string, 1); 378c9b0cc3bSRobert Watson } 379c9b0cc3bSRobert Watson 380c9b0cc3bSRobert Watson /* 381c9b0cc3bSRobert Watson * Print or set a named script, with the set portion broken out into its own 382c9b0cc3bSRobert Watson * function. We must directly access the remainder of the DDB line input as 383c9b0cc3bSRobert Watson * we do not wish to use db_lex's token processing. 384c9b0cc3bSRobert Watson */ 385c9b0cc3bSRobert Watson void 386cd508278SPedro F. Giffuni db_script_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 387c9b0cc3bSRobert Watson char *modif) 388c9b0cc3bSRobert Watson { 389c9b0cc3bSRobert Watson char *buf, scriptname[DB_MAXSCRIPTNAME]; 390c9b0cc3bSRobert Watson struct ddb_script *dsp; 391c9b0cc3bSRobert Watson int error, t; 392c9b0cc3bSRobert Watson 393c9b0cc3bSRobert Watson t = db_read_token(); 394c9b0cc3bSRobert Watson if (t != tIDENT) { 395c9b0cc3bSRobert Watson db_printf("usage: script scriptname=script\n"); 396c9b0cc3bSRobert Watson db_skip_to_eol(); 397c9b0cc3bSRobert Watson return; 398c9b0cc3bSRobert Watson } 399c9b0cc3bSRobert Watson 400c9b0cc3bSRobert Watson if (strlcpy(scriptname, db_tok_string, sizeof(scriptname)) >= 401c9b0cc3bSRobert Watson sizeof(scriptname)) { 402c9b0cc3bSRobert Watson db_printf("scriptname too long\n"); 403c9b0cc3bSRobert Watson db_skip_to_eol(); 404c9b0cc3bSRobert Watson return; 405c9b0cc3bSRobert Watson } 406c9b0cc3bSRobert Watson 407c9b0cc3bSRobert Watson t = db_read_token(); 408c9b0cc3bSRobert Watson if (t == tEOL) { 409c9b0cc3bSRobert Watson dsp = db_script_lookup(scriptname); 410c9b0cc3bSRobert Watson if (dsp == NULL) { 411c9b0cc3bSRobert Watson db_printf("script '%s' not found\n", scriptname); 412c9b0cc3bSRobert Watson db_skip_to_eol(); 413c9b0cc3bSRobert Watson return; 414c9b0cc3bSRobert Watson } 415c9b0cc3bSRobert Watson db_printf("%s=%s\n", scriptname, dsp->ds_script); 416c9b0cc3bSRobert Watson } else if (t == tEQ) { 417c9b0cc3bSRobert Watson buf = db_get_line(); 418c9b0cc3bSRobert Watson if (buf[strlen(buf)-1] == '\n') 419c9b0cc3bSRobert Watson buf[strlen(buf)-1] = '\0'; 420c9b0cc3bSRobert Watson error = db_script_set(scriptname, buf); 421c9b0cc3bSRobert Watson if (error != 0) 422c9b0cc3bSRobert Watson db_printf("Error: %d\n", error); 423c9b0cc3bSRobert Watson } else 424c9b0cc3bSRobert Watson db_printf("?\n"); 425c9b0cc3bSRobert Watson db_skip_to_eol(); 426c9b0cc3bSRobert Watson } 427c9b0cc3bSRobert Watson 428c9b0cc3bSRobert Watson /* 429c9b0cc3bSRobert Watson * Remove a named script. 430c9b0cc3bSRobert Watson */ 431c9b0cc3bSRobert Watson void 432cd508278SPedro F. Giffuni db_unscript_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 433c9b0cc3bSRobert Watson char *modif) 434c9b0cc3bSRobert Watson { 435c9b0cc3bSRobert Watson int error, t; 436c9b0cc3bSRobert Watson 437c9b0cc3bSRobert Watson t = db_read_token(); 438c9b0cc3bSRobert Watson if (t != tIDENT) { 439c9b0cc3bSRobert Watson db_printf("?\n"); 440c9b0cc3bSRobert Watson db_skip_to_eol(); 441c9b0cc3bSRobert Watson return; 442c9b0cc3bSRobert Watson } 443c9b0cc3bSRobert Watson 444c9b0cc3bSRobert Watson error = db_script_unset(db_tok_string); 445c9b0cc3bSRobert Watson if (error == ENOENT) { 446c9b0cc3bSRobert Watson db_printf("script '%s' not found\n", db_tok_string); 447c9b0cc3bSRobert Watson db_skip_to_eol(); 448c9b0cc3bSRobert Watson return; 449c9b0cc3bSRobert Watson } 450c9b0cc3bSRobert Watson db_skip_to_eol(); 451c9b0cc3bSRobert Watson } 452c9b0cc3bSRobert Watson 453c9b0cc3bSRobert Watson /* 454c9b0cc3bSRobert Watson * Sysctls for managing DDB scripting: 455c9b0cc3bSRobert Watson * 456c9b0cc3bSRobert Watson * debug.ddb.scripting.script - Define a new script 457c9b0cc3bSRobert Watson * debug.ddb.scripting.scripts - List of names *and* scripts 458c9b0cc3bSRobert Watson * debug.ddb.scripting.unscript - Remove an existing script 459c9b0cc3bSRobert Watson * 460c9b0cc3bSRobert Watson * Since we don't want to try to manage arbitrary extensions to the sysctl 461c9b0cc3bSRobert Watson * name space from the debugger, the script/unscript sysctls are a bit more 462c9b0cc3bSRobert Watson * like RPCs and a bit less like normal get/set requests. The ddb(8) command 463c9b0cc3bSRobert Watson * line tool wraps them to make things a bit more user-friendly. 464c9b0cc3bSRobert Watson */ 4657029da5cSPawel Biernacki static SYSCTL_NODE(_debug_ddb, OID_AUTO, scripting, 4667029da5cSPawel Biernacki CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 467c9b0cc3bSRobert Watson "DDB script settings"); 468c9b0cc3bSRobert Watson 469c9b0cc3bSRobert Watson static int 470c9b0cc3bSRobert Watson sysctl_debug_ddb_scripting_scripts(SYSCTL_HANDLER_ARGS) 471c9b0cc3bSRobert Watson { 472c9b0cc3bSRobert Watson struct sbuf sb; 473c9b0cc3bSRobert Watson int error, i, len; 474c9b0cc3bSRobert Watson char *buffer; 475c9b0cc3bSRobert Watson 476c9b0cc3bSRobert Watson /* 477c9b0cc3bSRobert Watson * Make space to include a maximum-length name, = symbol, 478c9b0cc3bSRobert Watson * maximum-length script, and carriage return for every script that 479c9b0cc3bSRobert Watson * may be defined. 480c9b0cc3bSRobert Watson */ 481c9b0cc3bSRobert Watson len = DB_MAXSCRIPTS * (DB_MAXSCRIPTNAME + 1 + DB_MAXSCRIPTLEN + 1); 482c9b0cc3bSRobert Watson buffer = malloc(len, M_TEMP, M_WAITOK); 483c9b0cc3bSRobert Watson (void)sbuf_new(&sb, buffer, len, SBUF_FIXEDLEN); 484c9b0cc3bSRobert Watson mtx_lock(&db_script_mtx); 485c9b0cc3bSRobert Watson for (i = 0; i < DB_MAXSCRIPTS; i++) { 486c9b0cc3bSRobert Watson if (strlen(db_script_table[i].ds_scriptname) == 0) 487c9b0cc3bSRobert Watson continue; 488c9b0cc3bSRobert Watson (void)sbuf_printf(&sb, "%s=%s\n", 489c9b0cc3bSRobert Watson db_script_table[i].ds_scriptname, 490c9b0cc3bSRobert Watson db_script_table[i].ds_script); 491c9b0cc3bSRobert Watson } 492c9b0cc3bSRobert Watson mtx_unlock(&db_script_mtx); 493c9b0cc3bSRobert Watson sbuf_finish(&sb); 494c9b0cc3bSRobert Watson error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb) + 1); 495c9b0cc3bSRobert Watson sbuf_delete(&sb); 496c9b0cc3bSRobert Watson free(buffer, M_TEMP); 497c9b0cc3bSRobert Watson return (error); 498c9b0cc3bSRobert Watson } 4997029da5cSPawel Biernacki SYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, scripts, 5007029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 0, 5017029da5cSPawel Biernacki sysctl_debug_ddb_scripting_scripts, "A", 502c9b0cc3bSRobert Watson "List of defined scripts"); 503c9b0cc3bSRobert Watson 504c9b0cc3bSRobert Watson static int 505c9b0cc3bSRobert Watson sysctl_debug_ddb_scripting_script(SYSCTL_HANDLER_ARGS) 506c9b0cc3bSRobert Watson { 507c9b0cc3bSRobert Watson char *buffer, *script, *scriptname; 508c9b0cc3bSRobert Watson int error, len; 509c9b0cc3bSRobert Watson 510c9b0cc3bSRobert Watson /* 511c9b0cc3bSRobert Watson * Maximum length for an input string is DB_MAXSCRIPTNAME + '=' 512c9b0cc3bSRobert Watson * symbol + DB_MAXSCRIPT. 513c9b0cc3bSRobert Watson */ 514c9b0cc3bSRobert Watson len = DB_MAXSCRIPTNAME + DB_MAXSCRIPTLEN + 1; 515c9b0cc3bSRobert Watson buffer = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 516c9b0cc3bSRobert Watson error = sysctl_handle_string(oidp, buffer, len, req); 517c9b0cc3bSRobert Watson if (error) 518c9b0cc3bSRobert Watson goto out; 519c9b0cc3bSRobert Watson 520c9b0cc3bSRobert Watson /* 521c9b0cc3bSRobert Watson * Argument will be in form scriptname=script, so split into the 522c9b0cc3bSRobert Watson * scriptname and script. 523c9b0cc3bSRobert Watson */ 524c9b0cc3bSRobert Watson script = buffer; 525c9b0cc3bSRobert Watson scriptname = strsep(&script, "="); 526c9b0cc3bSRobert Watson if (script == NULL) { 527c9b0cc3bSRobert Watson error = EINVAL; 528c9b0cc3bSRobert Watson goto out; 529c9b0cc3bSRobert Watson } 530c9b0cc3bSRobert Watson mtx_lock(&db_script_mtx); 531c9b0cc3bSRobert Watson error = db_script_set(scriptname, script); 532c9b0cc3bSRobert Watson mtx_unlock(&db_script_mtx); 533c9b0cc3bSRobert Watson out: 534c9b0cc3bSRobert Watson free(buffer, M_TEMP); 535c9b0cc3bSRobert Watson return (error); 536c9b0cc3bSRobert Watson } 5377029da5cSPawel Biernacki SYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, script, 5387029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0, 5397029da5cSPawel Biernacki sysctl_debug_ddb_scripting_script, "A", 540c9b0cc3bSRobert Watson "Set a script"); 541c9b0cc3bSRobert Watson 542c9b0cc3bSRobert Watson /* 543c9b0cc3bSRobert Watson * debug.ddb.scripting.unscript has somewhat unusual sysctl semantics -- set 544c9b0cc3bSRobert Watson * the name of the script that you want to delete. 545c9b0cc3bSRobert Watson */ 546c9b0cc3bSRobert Watson static int 547c9b0cc3bSRobert Watson sysctl_debug_ddb_scripting_unscript(SYSCTL_HANDLER_ARGS) 548c9b0cc3bSRobert Watson { 549c9b0cc3bSRobert Watson char name[DB_MAXSCRIPTNAME]; 550c9b0cc3bSRobert Watson int error; 551c9b0cc3bSRobert Watson 552c9b0cc3bSRobert Watson bzero(name, sizeof(name)); 553c9b0cc3bSRobert Watson error = sysctl_handle_string(oidp, name, sizeof(name), req); 554c9b0cc3bSRobert Watson if (error) 555c9b0cc3bSRobert Watson return (error); 556c9b0cc3bSRobert Watson if (req->newptr == NULL) 557c9b0cc3bSRobert Watson return (0); 558c9b0cc3bSRobert Watson mtx_lock(&db_script_mtx); 559c9b0cc3bSRobert Watson error = db_script_unset(name); 560c9b0cc3bSRobert Watson mtx_unlock(&db_script_mtx); 561c9b0cc3bSRobert Watson if (error == ENOENT) 562c9b0cc3bSRobert Watson return (EINVAL); /* Don't confuse sysctl consumers. */ 563c9b0cc3bSRobert Watson return (0); 564c9b0cc3bSRobert Watson } 5657029da5cSPawel Biernacki SYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, unscript, 5667029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0, 5677029da5cSPawel Biernacki sysctl_debug_ddb_scripting_unscript, "A", 568c9b0cc3bSRobert Watson "Unset a script"); 569