xref: /minix/minix/tests/rmibtest/rmibtest.c (revision fb9c64b2)
1 /* Remote MIB (RMIB) test service - by D.C. van Moolenbroek */
2 /*
3  * This test is a good start, but not an exhaustive coverage test for all
4  * possible failure cases.  The reason for that is mainly that there are
5  * various scenarios that we cannot generate without implementing our own local
6  * bogus RMIB code.  Adding that is something for later - TODO.
7  */
8 #include <minix/drivers.h>
9 #include <minix/sysctl.h>
10 #include <minix/rmib.h>
11 
12 static int running;
13 
14 /* The following is a copy of the minix.test subtree in the MIB service. */
15 static char test_string[16], test_struct[12];
16 
17 static struct rmib_node minix_test_secret_table[] = {
18 /* 0*/	[SECRET_VALUE]		= RMIB_INT(RMIB_RO, 12345, "value",
19 				    "The combination to my luggage"),
20 };
21 
22 static struct rmib_node minix_test_table[] = {
23 /* 0*/	[TEST_INT]		= RMIB_INT(RMIB_RO | CTLFLAG_HEX, 0x01020304,
24 				    "int", "Value test field"),
25 /* 1*/	[TEST_BOOL]		= RMIB_BOOL(RMIB_RW, 0, "bool",
26 				    "Boolean test field"),
27 /* 2*/	[TEST_QUAD]		= RMIB_QUAD(RMIB_RW, 0, "quad",
28 				    "Quad test field"),
29 /* 3*/	[TEST_STRING]		= RMIB_STRING(RMIB_RW, test_string, "string",
30 				    "String test field"),
31 /* 4*/	[TEST_STRUCT]		= RMIB_STRUCT(RMIB_RW, sizeof(test_struct),
32 				    test_struct, "struct",
33 				    "Structure test field"),
34 /* 5*/	[TEST_PRIVATE]		= RMIB_INT(RMIB_RW | CTLFLAG_PRIVATE, -5375,
35 				    "private", "Private test field"),
36 /* 6*/	[TEST_ANYWRITE]		= RMIB_INT(RMIB_RW | CTLFLAG_ANYWRITE, 0,
37 				    "anywrite", "AnyWrite test field"),
38 /* 7*/	[TEST_DYNAMIC]		= RMIB_INT(RMIB_RO, 0, "deleteme",
39 				    "This node will be destroyed"),
40 /* 8*/	[TEST_SECRET]		= RMIB_NODE(RMIB_RO | CTLFLAG_PRIVATE,
41 				    minix_test_secret_table, "secret",
42 				    "Private subtree"),
43 /* 9*/	[TEST_PERM]		= RMIB_INT(RMIB_RO, 1, "permanent", NULL),
44 /*10*/	[TEST_DESTROY1]		= RMIB_INT(RMIB_RO, 123, "destroy1", NULL),
45 /*11*/	[TEST_DESTROY2]		= RMIB_INT(RMIB_RO, 456, "destroy2",
46 				    "This node will be destroyed"),
47 };
48 
49 static struct rmib_node minix_test = RMIB_NODE(RMIB_RW | CTLFLAG_HIDDEN,
50     minix_test_table, "test", "Test87 testing ground");
51 /* Here ends the copy of the minix.test subtree in the MIB service. */
52 
53 static struct rmib_node test_table[] = {
54 };
55 
56 static struct rmib_node test_rnode = RMIB_NODE(RMIB_RO, test_table, "test",
57     "Test node");
58 
59 static int value = 5375123;
60 
61 static ssize_t test_func(struct rmib_call *, struct rmib_node *,
62     struct rmib_oldp *, struct rmib_newp *);
63 
64 /* No defined constants because userland will access these by name anyway. */
65 static struct rmib_node minix_rtest_table[] = {
66 	[1]			= RMIB_INTPTR(RMIB_RW, &value, "int",
67 				    "Test description"),
68 	[2]			= RMIB_FUNC(CTLTYPE_INT | RMIB_RW, sizeof(int),
69 				    test_func, "func", "Test function"),
70 };
71 
72 static struct rmib_node minix_rtest = RMIB_NODE(RMIB_RO, minix_rtest_table,
73     "rtest", "Remote test subtree");
74 
75 /*
76  * Test function that deflects reads and writes to its sibling node.  Not a
77  * super useful thing to do, but a decent test of functionality regardless.
78  */
79 static ssize_t
80 test_func(struct rmib_call * call, struct rmib_node * node,
81 	struct rmib_oldp * oldp, struct rmib_newp * newp)
82 {
83 
84 	return rmib_readwrite(call, &minix_rtest_table[1], oldp, newp);
85 }
86 
87 /*
88  * Attempt to perform registrations that should be rejected locally, and thus
89  * result in failure immediately.  Unfortunately, we cannot verify that the MIB
90  * service also verifies these aspects remotely, at least without talking to it
91  * directly.
92  */
93 static void
94 test_local_failures(void)
95 {
96 	int r, mib[CTL_SHORTNAME + 1];
97 
98 	memset(mib, 0, sizeof(mib));
99 
100 	/* Test an empty path. */
101 	if ((r = rmib_register(mib, 0, &test_rnode)) != EINVAL)
102 		panic("registering remote MIB subtree yielded: %d", r);
103 
104 	/* Test a path that is too long. */
105 	if ((r = rmib_register(mib, CTL_SHORTNAME + 1, &test_rnode)) != EINVAL)
106 		panic("registering remote MIB subtree yielded: %d", r);
107 
108 	/* Test a mount point that is not a node-type (parent) node. */
109 	mib[0] = CTL_MINIX;
110 	mib[1] = MINIX_TEST;
111 	mib[2] = TEST_INT;
112 	if ((r = rmib_register(mib, 3, &minix_test_table[TEST_INT])) != EINVAL)
113 		panic("registering remote MIB subtree yielded: %d", r);
114 }
115 
116 /*
117  * Perform a number of registrations that will not be accepted by the MIB
118  * service.  We will never know, but the userland test script can verify the
119  * difference by comparing the number of remotes before and after.
120  */
121 static void
122 test_remote_failures(void)
123 {
124 	int r, mib[CTL_SHORTNAME];
125 
126 	/* Test an existing one-node path. */
127 	mib[0] = CTL_KERN;
128 	if ((r = rmib_register(mib, 1, &test_rnode)) != OK)
129 		panic("unable to register remote MIB subtree: %d", r);
130 	rmib_reset();
131 
132 	/* Test a path in which a non-final component does not exist. */
133 	mib[1] = CREATE_BASE - 1; /* probably as safe as it gets.. */
134 	mib[2] = 0;
135 	if ((r = rmib_register(mib, 3, &test_rnode)) != OK)
136 		panic("unable to register remote MIB subtree: %d", r);
137 	rmib_reset();
138 
139 	/* Test a path in which a non-final component is not a parent node. */
140 	mib[1] = KERN_OSTYPE;
141 	if ((r = rmib_register(mib, 3, &test_rnode)) != OK)
142 		panic("unable to register remote MIB subtree: %d", r);
143 	rmib_reset();
144 
145 	/* Test a path in which a non-final component is a meta-identifier. */
146 	mib[1] = CTL_QUERY;
147 	if ((r = rmib_register(mib, 3, &test_rnode)) != OK)
148 		panic("unable to register remote MIB subtree: %d", r);
149 	rmib_reset();
150 
151 	/* Test a path in which the final component is a meta-identifier. */
152 	if ((r = rmib_register(mib, 2, &test_rnode)) != OK)
153 		panic("unable to register remote MIB subtree: %d", r);
154 	rmib_reset();
155 
156 	/* Test a path in which the final component identifies a non-parent. */
157 	mib[1] = KERN_OSTYPE;
158 	if ((r = rmib_register(mib, 2, &test_rnode)) != OK)
159 		panic("unable to register remote MIB subtree: %d", r);
160 	rmib_reset();
161 
162 	/* Test a path with unacceptable flags for the final component. */
163 	mib[0] = CTL_MINIX;
164 	mib[1] = MINIX_TEST;
165 	mib[2] = TEST_SECRET;
166 	if ((r = rmib_register(mib, 3, &test_rnode)) != OK)
167 		panic("unable to register remote MIB subtree: %d", r);
168 	rmib_reset();
169 
170 	/* Test a path of which the name, but not the ID, already exists. */
171 	mib[1] = CREATE_BASE - 1;
172 	if ((r = rmib_register(mib, 2, &test_rnode)) != OK)
173 		panic("unable to register remote MIB subtree: %d", r);
174 	/*
175 	 * Do NOT call rmib_reset() anymore now: we want to let the MIB service
176 	 * get the name from us.
177 	 */
178 }
179 
180 static int
181 init(int type __unused, sef_init_info_t * info __unused)
182 {
183 	const int new_mib[] = { CTL_MINIX, CREATE_BASE - 2 };
184 	const int shadow_mib[] = { CTL_MINIX, MINIX_TEST };
185 	int r;
186 
187 	test_local_failures();
188 
189 	test_remote_failures();
190 
191 	/*
192 	 * We must now register our new test tree before shadowing minix.test,
193 	 * because if any of the previous requests actually did succeed, the
194 	 * next registration will be rejected (ID 0 already in use) and no
195 	 * difference would be detected because of "successful" shadowing.
196 	 */
197 	r = rmib_register(new_mib, __arraycount(new_mib), &minix_rtest);
198 	if (r != OK)
199 		panic("unable to register remote MIB subtree: %d", r);
200 
201 	r = rmib_register(shadow_mib, __arraycount(shadow_mib), &minix_test);
202 	if (r != OK)
203 		panic("unable to register remote MIB subtree: %d", r);
204 
205 	running = TRUE;
206 
207 	return OK;
208 }
209 
210 static void
211 cleanup(void)
212 {
213 	int r;
214 
215 	if ((r = rmib_deregister(&minix_rtest)) != OK)
216 		panic("unable to deregister: %d", r);
217 	if ((r = rmib_deregister(&minix_test)) != OK)
218 		panic("unable to deregister: %d", r);
219 
220 	/*
221 	 * TODO: the fact that the MIB service can currently not detect the
222 	 * death of other services is creating somewhat of a problem here: if
223 	 * we deregister shortly before exiting, the asynchronous deregister
224 	 * requests may not be delivered before we actually exit (and take our
225 	 * asynsend table with us), and leave around the remote subtrees until
226 	 * a user process tries accessing them.  We work around this here by
227 	 * delaying the exit by half a second - shorter than RS's timeout, but
228 	 * long enough to allow deregistration.
229 	 */
230 	sys_setalarm(sys_hz() / 2, 0);
231 
232 	running = FALSE;
233 }
234 
235 static void
236 got_signal(int sig)
237 {
238 
239 	if (sig == SIGTERM && running)
240 		cleanup();
241 }
242 
243 int
244 main(void)
245 {
246 	message m;
247 	int r, ipc_status;
248 
249 	sef_setcb_init_fresh(init);
250 	sef_setcb_signal_handler(got_signal);
251 
252 	sef_startup();
253 
254 	for (;;) {
255 		r = sef_receive_status(ANY, &m, &ipc_status);
256 
257 		if (r != OK)
258 			panic("sef_receive_status failed: %d", r);
259 
260 		if (m.m_source == CLOCK && is_ipc_notify(ipc_status))
261 			break; /* the intended exit path; see above */
262 		if (m.m_source == MIB_PROC_NR)
263 			rmib_process(&m, ipc_status);
264 	}
265 
266 	return EXIT_SUCCESS;
267 }
268