xref: /haiku/src/add-ons/kernel/debugger/run_on_exit/run_on_exit.cpp (revision 24df65921befcd0ad0c5c7866118f922da61cb96)
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 
11 static sem_id sRequestSem = -1;
12 static char sCommandBuffer[1024];
13 static uint32 sCommandOffset = 0;
14 static uint32 sCommandCount = 0;
15 
16 
17 static int32
run_on_exit_loop(void * data)18 run_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 
49 static int
add_run_on_exit_command(int argc,char ** argv)50 add_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 
85 static void
exit_debugger()86 exit_debugger()
87 {
88 	if (sCommandCount > 0)
89 		release_sem_etc(sRequestSem, 1, B_DO_NOT_RESCHEDULE);
90 }
91 
92 
93 static status_t
std_ops(int32 op,...)94 std_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 
126 static 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 
139 module_info *modules[] = {
140 	(module_info *)&sModuleInfo,
141 	NULL
142 };
143