1/*
2 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch
3 * Distributed under the terms of the MIT License.
4 */
5#include <debug.h>
6#include <signal.h>
7#include <string.h>
8#include <image.h>
9
10
11static sem_id sRequestSem = -1;
12static char sCommandBuffer[1024];
13static uint32 sCommandOffset = 0;
14static uint32 sCommandCount = 0;
15
16
17static int32
18run_on_exit_loop(void *data)
19{
20	while (true) {
21		if (acquire_sem(sRequestSem) != B_OK)
22			break;
23
24		char *pointer = sCommandBuffer;
25		while (sCommandCount > 0) {
26			uint8 argCount = (uint8)pointer[0];
27			pointer++;
28
29			const char *args[argCount];
30			for (uint8 i = 0; i < argCount; i++) {
31				args[i] = pointer;
32				uint32 length = strlen(pointer);
33				pointer += length + 1;
34			}
35
36			thread_id thread = load_image(argCount, args, NULL);
37			if (thread >= B_OK)
38				resume_thread(thread);
39			sCommandCount--;
40		}
41
42		sCommandOffset = 0;
43	}
44
45	return 0;
46}
47
48
49static int
50add_run_on_exit_command(int argc, char **argv)
51{
52	if (argc < 2 || strcmp(argv[1], "--help") == 0) {
53		print_debugger_command_usage(argv[0]);
54		return 0;
55	}
56
57	if (argc > 256) {
58		kprintf("too many arguments\n");
59		return 0;
60	}
61
62	size_t totalLength = 1;
63	for (int32 i = 1; i < argc; i++)
64		totalLength += strlen(argv[i]) + 1;
65
66	if (sCommandOffset + totalLength > sizeof(sCommandBuffer)) {
67		kprintf("no space left in command buffer\n");
68		return 0;
69	}
70
71	char *pointer = sCommandBuffer + sCommandOffset;
72	*pointer++ = (char)(argc - 1);
73
74	for (int32 i = 1; i < argc; i++) {
75		strcpy(pointer, argv[i]);
76		pointer += strlen(argv[i]) + 1;
77	}
78
79	sCommandOffset += totalLength;
80	sCommandCount++;
81	return 0;
82}
83
84
85static void
86exit_debugger()
87{
88	if (sCommandCount > 0)
89		release_sem_etc(sRequestSem, 1, B_DO_NOT_RESCHEDULE);
90}
91
92
93static status_t
94std_ops(int32 op, ...)
95{
96	if (op == B_MODULE_INIT) {
97		sRequestSem = create_sem(0, "run_on_exit_request");
98		if (sRequestSem < B_OK)
99			return sRequestSem;
100
101		thread_id thread = spawn_kernel_thread(&run_on_exit_loop,
102			"run_on_exit_loop", B_NORMAL_PRIORITY, NULL);
103		if (thread < B_OK)
104			return thread;
105
106		resume_thread(thread);
107
108		add_debugger_command_etc("on_exit", &add_run_on_exit_command,
109			"Adds a command to be run when leaving the kernel debugger",
110			"<command> [<arguments>]\n"
111			"Adds a command to be run when leaving the kernel debugger.\n", 0);
112
113		return B_OK;
114	} else if (op == B_MODULE_UNINIT) {
115		remove_debugger_command("on_exit", &add_run_on_exit_command);
116		// deleting the sem will also cause the thread to exit
117		delete_sem(sRequestSem);
118		sRequestSem = -1;
119		return B_OK;
120	}
121
122	return B_BAD_VALUE;
123}
124
125
126static struct debugger_module_info sModuleInfo = {
127	{
128		"debugger/run_on_exit/v1",
129		B_KEEP_LOADED,
130		&std_ops
131	},
132
133	NULL,
134	exit_debugger,
135	NULL,
136	NULL
137};
138
139module_info *modules[] = {
140	(module_info *)&sModuleInfo,
141	NULL
142};
143