The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/ddb/db_script.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 6b75f9b1ec475be04181a906bab716e1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.