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

Cache object: e0024d7ffab906a6fabefe8c30a78311


[ 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.