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