Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/ddb/db_script.c
39476 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
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/param.h>
59
#include <sys/kdb.h>
60
#include <sys/kernel.h>
61
#include <sys/libkern.h>
62
#include <sys/lock.h>
63
#include <sys/malloc.h>
64
#include <sys/mutex.h>
65
#include <sys/sbuf.h>
66
#include <sys/sysctl.h>
67
#include <sys/systm.h>
68
69
#include <ddb/ddb.h>
70
#include <ddb/db_command.h>
71
#include <ddb/db_lex.h>
72
73
#include <machine/setjmp.h>
74
75
/*
76
* struct ddb_script describes an individual script.
77
*/
78
struct ddb_script {
79
char ds_scriptname[DB_MAXSCRIPTNAME];
80
char ds_script[DB_MAXSCRIPTLEN];
81
};
82
83
/*
84
* Global list of scripts -- defined scripts have non-empty name fields.
85
*/
86
static struct ddb_script db_script_table[DB_MAXSCRIPTS];
87
88
/*
89
* While executing a script, we parse it using strsep(), so require a
90
* temporary buffer that may be used destructively. Since we support weak
91
* recursion of scripts (one may reference another), we need one buffer for
92
* each concurrently executing script.
93
*/
94
static struct db_recursion_data {
95
char drd_buffer[DB_MAXSCRIPTLEN];
96
} db_recursion_data[DB_MAXSCRIPTRECURSION];
97
static int db_recursion = -1;
98
99
/*
100
* We use a separate static buffer for script validation so that it is safe
101
* to validate scripts from within a script. This is used only in
102
* db_script_valid(), which should never be called reentrantly.
103
*/
104
static char db_static_buffer[DB_MAXSCRIPTLEN];
105
106
/*
107
* Synchronization is not required from within the debugger, as it is
108
* singe-threaded (although reentrance must be carefully considered).
109
* However, it is required when interacting with scripts from user space
110
* processes. Sysctl procedures acquire db_script_mtx before accessing the
111
* global script data structures.
112
*/
113
static struct mtx db_script_mtx;
114
MTX_SYSINIT(db_script_mtx, &db_script_mtx, "db_script_mtx", MTX_DEF);
115
116
/*
117
* Some script names have special meaning, such as those executed
118
* automatically when KDB is entered.
119
*/
120
#define DB_SCRIPT_KDBENTER_PREFIX "kdb.enter" /* KDB has entered. */
121
#define DB_SCRIPT_KDBENTER_DEFAULT "kdb.enter.default"
122
123
/*
124
* Find the existing script slot for a named script, if any.
125
*/
126
static struct ddb_script *
127
db_script_lookup(const char *scriptname)
128
{
129
int i;
130
131
for (i = 0; i < DB_MAXSCRIPTS; i++) {
132
if (strcmp(db_script_table[i].ds_scriptname, scriptname) ==
133
0)
134
return (&db_script_table[i]);
135
}
136
return (NULL);
137
}
138
139
/*
140
* Find a new slot for a script, if available. Does not mark as allocated in
141
* any way--this must be done by the caller.
142
*/
143
static struct ddb_script *
144
db_script_new(void)
145
{
146
int i;
147
148
for (i = 0; i < DB_MAXSCRIPTS; i++) {
149
if (strlen(db_script_table[i].ds_scriptname) == 0)
150
return (&db_script_table[i]);
151
}
152
return (NULL);
153
}
154
155
/*
156
* Perform very rudimentary validation of a proposed script. It would be
157
* easy to imagine something more comprehensive. The script string is
158
* validated in a static buffer.
159
*/
160
static int
161
db_script_valid(const char *scriptname, const char *script)
162
{
163
char *buffer, *command;
164
165
if (strlen(scriptname) == 0)
166
return (EINVAL);
167
if (strlen(scriptname) >= DB_MAXSCRIPTNAME)
168
return (EINVAL);
169
if (strlen(script) >= DB_MAXSCRIPTLEN)
170
return (EINVAL);
171
buffer = db_static_buffer;
172
strcpy(buffer, script);
173
while ((command = strsep(&buffer, ";")) != NULL) {
174
if (strlen(command) >= DB_MAXLINE)
175
return (EINVAL);
176
}
177
return (0);
178
}
179
180
/*
181
* Modify an existing script or add a new script with the specified script
182
* name and contents. If there are no script slots available, an error will
183
* be returned.
184
*/
185
static int
186
db_script_set(const char *scriptname, const char *script)
187
{
188
struct ddb_script *dsp;
189
int error;
190
191
error = db_script_valid(scriptname, script);
192
if (error)
193
return (error);
194
dsp = db_script_lookup(scriptname);
195
if (dsp == NULL) {
196
dsp = db_script_new();
197
if (dsp == NULL)
198
return (ENOSPC);
199
strlcpy(dsp->ds_scriptname, scriptname,
200
sizeof(dsp->ds_scriptname));
201
}
202
strlcpy(dsp->ds_script, script, sizeof(dsp->ds_script));
203
return (0);
204
}
205
206
/*
207
* Delete an existing script by name, if found.
208
*/
209
static int
210
db_script_unset(const char *scriptname)
211
{
212
struct ddb_script *dsp;
213
214
dsp = db_script_lookup(scriptname);
215
if (dsp == NULL)
216
return (ENOENT);
217
strcpy(dsp->ds_scriptname, "");
218
strcpy(dsp->ds_script, "");
219
return (0);
220
}
221
222
/*
223
* Trim leading/trailing white space in a command so that we don't pass
224
* carriage returns, etc, into DDB command parser.
225
*/
226
static int
227
db_command_trimmable(char ch)
228
{
229
230
switch (ch) {
231
case ' ':
232
case '\t':
233
case '\n':
234
case '\r':
235
return (1);
236
237
default:
238
return (0);
239
}
240
}
241
242
static void
243
db_command_trim(char **commandp)
244
{
245
char *command;
246
247
command = *commandp;
248
while (db_command_trimmable(*command))
249
command++;
250
while ((strlen(command) > 0) &&
251
db_command_trimmable(command[strlen(command) - 1]))
252
command[strlen(command) - 1] = 0;
253
*commandp = command;
254
}
255
256
/*
257
* Execute a script, breaking it up into individual commands and passing them
258
* sequentially into DDB's input processing. Use the KDB jump buffer to
259
* restore control to the main script loop if things get too wonky when
260
* processing a command -- i.e., traps, etc. Also, make sure we don't exceed
261
* practical limits on recursion.
262
*
263
* XXXRW: If any individual command is too long, it will be truncated when
264
* injected into the input at a lower layer. We should validate the script
265
* before configuring it to avoid this scenario.
266
*/
267
static int
268
db_script_exec(const char *scriptname, int warnifnotfound)
269
{
270
struct db_recursion_data *drd;
271
struct ddb_script *dsp;
272
char *buffer, *command;
273
void *prev_jb;
274
jmp_buf jb;
275
276
dsp = db_script_lookup(scriptname);
277
if (dsp == NULL) {
278
if (warnifnotfound)
279
db_printf("script '%s' not found\n", scriptname);
280
return (ENOENT);
281
}
282
283
if (db_recursion >= DB_MAXSCRIPTRECURSION) {
284
db_printf("Script stack too deep\n");
285
return (E2BIG);
286
}
287
db_recursion++;
288
drd = &db_recursion_data[db_recursion];
289
290
/*
291
* Parse script in temporary buffer, since strsep() is destructive.
292
*/
293
buffer = drd->drd_buffer;
294
strcpy(buffer, dsp->ds_script);
295
while ((command = strsep(&buffer, ";")) != NULL) {
296
db_printf("db:%d:%s> %s\n", db_recursion, dsp->ds_scriptname,
297
command);
298
db_command_trim(&command);
299
prev_jb = kdb_jmpbuf(jb);
300
if (setjmp(jb) == 0)
301
db_command_script(command);
302
else
303
db_printf("Script command '%s' returned error\n",
304
command);
305
kdb_jmpbuf(prev_jb);
306
}
307
db_recursion--;
308
return (0);
309
}
310
311
/*
312
* Wrapper for exec path that is called on KDB enter. Map reason for KDB
313
* enter to a script name, and don't whine if the script doesn't exist. If
314
* there is no matching script, try the catch-all script.
315
*/
316
void
317
db_script_kdbenter(const char *eventname)
318
{
319
char scriptname[DB_MAXSCRIPTNAME];
320
321
snprintf(scriptname, sizeof(scriptname), "%s.%s",
322
DB_SCRIPT_KDBENTER_PREFIX, eventname);
323
if (db_script_exec(scriptname, 0) == ENOENT)
324
(void)db_script_exec(DB_SCRIPT_KDBENTER_DEFAULT, 0);
325
}
326
327
/*-
328
* DDB commands for scripting, as reached via the DDB user interface:
329
*
330
* scripts - lists scripts
331
* run <scriptname> - run a script
332
* script <scriptname> - prints script
333
* script <scriptname> <script> - set a script
334
* unscript <scriptname> - remove a script
335
*/
336
337
/*
338
* List scripts and their contents.
339
*/
340
void
341
db_scripts_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
342
char *modif)
343
{
344
int i;
345
346
for (i = 0; i < DB_MAXSCRIPTS; i++) {
347
if (strlen(db_script_table[i].ds_scriptname) != 0) {
348
db_printf("%s=%s\n",
349
db_script_table[i].ds_scriptname,
350
db_script_table[i].ds_script);
351
}
352
}
353
}
354
355
/*
356
* Execute a script.
357
*/
358
void
359
db_run_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
360
{
361
int t;
362
363
/*
364
* Right now, we accept exactly one argument. In the future, we
365
* might want to accept flags and arguments to the script itself.
366
*/
367
t = db_read_token();
368
if (t != tIDENT)
369
db_error("?\n");
370
371
if (db_read_token() != tEOL)
372
db_error("?\n");
373
374
db_script_exec(db_tok_string, 1);
375
}
376
377
/*
378
* Print or set a named script, with the set portion broken out into its own
379
* function. We must directly access the remainder of the DDB line input as
380
* we do not wish to use db_lex's token processing.
381
*/
382
void
383
db_script_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
384
char *modif)
385
{
386
char *buf, scriptname[DB_MAXSCRIPTNAME];
387
struct ddb_script *dsp;
388
int error, t;
389
390
t = db_read_token();
391
if (t != tIDENT) {
392
db_printf("usage: script scriptname=script\n");
393
db_skip_to_eol();
394
return;
395
}
396
397
if (strlcpy(scriptname, db_tok_string, sizeof(scriptname)) >=
398
sizeof(scriptname)) {
399
db_printf("scriptname too long\n");
400
db_skip_to_eol();
401
return;
402
}
403
404
t = db_read_token();
405
if (t == tEOL) {
406
dsp = db_script_lookup(scriptname);
407
if (dsp == NULL) {
408
db_printf("script '%s' not found\n", scriptname);
409
db_skip_to_eol();
410
return;
411
}
412
db_printf("%s=%s\n", scriptname, dsp->ds_script);
413
} else if (t == tEQ) {
414
buf = db_get_line();
415
if (buf[strlen(buf)-1] == '\n')
416
buf[strlen(buf)-1] = '\0';
417
error = db_script_set(scriptname, buf);
418
if (error != 0)
419
db_printf("Error: %d\n", error);
420
} else
421
db_printf("?\n");
422
db_skip_to_eol();
423
}
424
425
/*
426
* Remove a named script.
427
*/
428
void
429
db_unscript_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
430
char *modif)
431
{
432
int error, t;
433
434
t = db_read_token();
435
if (t != tIDENT) {
436
db_printf("?\n");
437
db_skip_to_eol();
438
return;
439
}
440
441
error = db_script_unset(db_tok_string);
442
if (error == ENOENT) {
443
db_printf("script '%s' not found\n", db_tok_string);
444
db_skip_to_eol();
445
return;
446
}
447
db_skip_to_eol();
448
}
449
450
/*
451
* Sysctls for managing DDB scripting:
452
*
453
* debug.ddb.scripting.script - Define a new script
454
* debug.ddb.scripting.scripts - List of names *and* scripts
455
* debug.ddb.scripting.unscript - Remove an existing script
456
*
457
* Since we don't want to try to manage arbitrary extensions to the sysctl
458
* name space from the debugger, the script/unscript sysctls are a bit more
459
* like RPCs and a bit less like normal get/set requests. The ddb(8) command
460
* line tool wraps them to make things a bit more user-friendly.
461
*/
462
static SYSCTL_NODE(_debug_ddb, OID_AUTO, scripting,
463
CTLFLAG_RW | CTLFLAG_MPSAFE, 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,
497
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 0,
498
sysctl_debug_ddb_scripting_scripts, "A",
499
"List of defined scripts");
500
501
static int
502
sysctl_debug_ddb_scripting_script(SYSCTL_HANDLER_ARGS)
503
{
504
char *buffer, *script, *scriptname;
505
int error, len;
506
507
/*
508
* Maximum length for an input string is DB_MAXSCRIPTNAME + '='
509
* symbol + DB_MAXSCRIPT.
510
*/
511
len = DB_MAXSCRIPTNAME + DB_MAXSCRIPTLEN + 1;
512
buffer = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
513
error = sysctl_handle_string(oidp, buffer, len, req);
514
if (error)
515
goto out;
516
517
/*
518
* Argument will be in form scriptname=script, so split into the
519
* scriptname and script.
520
*/
521
script = buffer;
522
scriptname = strsep(&script, "=");
523
if (script == NULL) {
524
error = EINVAL;
525
goto out;
526
}
527
mtx_lock(&db_script_mtx);
528
error = db_script_set(scriptname, script);
529
mtx_unlock(&db_script_mtx);
530
out:
531
free(buffer, M_TEMP);
532
return (error);
533
}
534
SYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, script,
535
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0,
536
sysctl_debug_ddb_scripting_script, "A",
537
"Set a script");
538
539
/*
540
* debug.ddb.scripting.unscript has somewhat unusual sysctl semantics -- set
541
* the name of the script that you want to delete.
542
*/
543
static int
544
sysctl_debug_ddb_scripting_unscript(SYSCTL_HANDLER_ARGS)
545
{
546
char name[DB_MAXSCRIPTNAME];
547
int error;
548
549
bzero(name, sizeof(name));
550
error = sysctl_handle_string(oidp, name, sizeof(name), req);
551
if (error)
552
return (error);
553
if (req->newptr == NULL)
554
return (0);
555
mtx_lock(&db_script_mtx);
556
error = db_script_unset(name);
557
mtx_unlock(&db_script_mtx);
558
if (error == ENOENT)
559
return (EINVAL); /* Don't confuse sysctl consumers. */
560
return (0);
561
}
562
SYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, unscript,
563
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0,
564
sysctl_debug_ddb_scripting_unscript, "A",
565
"Unset a script");
566
567