]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/xnu_quick_test/kqueue_tests.c
xnu-1504.3.12.tar.gz
[apple/xnu.git] / tools / tests / xnu_quick_test / kqueue_tests.c
1 /*
2 * tests.c
3 * xnu_quick_test
4 *
5 * Created by Jerry Cottingham on 3/25/05.
6 * Copyright 2005 Apple Computer Inc. All rights reserved.
7 *
8 */
9
10 #include "tests.h"
11 #include <pthread.h>
12 #include <assert.h>
13 #include <sys/event.h> /* for kqueue tests */
14 #include <sys/sysctl.h> /* for determining hw */
15 #include <mach/mach.h>
16 #include <AvailabilityMacros.h> /* for determination of Mac OS X version (tiger, leopard, etc.) */
17 #include <libkern/OSByteOrder.h> /* for OSSwap32() */
18
19 extern char g_target_path[ PATH_MAX ];
20 extern int g_skip_setuid_tests;
21 extern int g_is_under_rosetta;
22
23 int msg_count = 14;
24 int last_msg_seen = 0;
25 pthread_cond_t my_cond = PTHREAD_COND_INITIALIZER;
26 pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
27
28
29 static kern_return_t
30 kmsg_send(mach_port_t remote_port, int index)
31 {
32 int msgh_id = 1000 + index;
33 kern_return_t my_kr;
34 mach_msg_header_t * my_kmsg = NULL;
35 mach_msg_size_t size = sizeof(mach_msg_header_t) + sizeof(int)*index;
36
37 my_kr = vm_allocate( mach_task_self(),
38 (vm_address_t *)&my_kmsg,
39 size,
40 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE );
41 if (my_kr != KERN_SUCCESS)
42 return my_kr;
43 my_kmsg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
44 my_kmsg->msgh_size = size;
45 my_kmsg->msgh_remote_port = remote_port;
46 my_kmsg->msgh_local_port = MACH_PORT_NULL;
47 my_kmsg->msgh_reserved = 0;
48 my_kmsg->msgh_id = msgh_id;
49 my_kr = mach_msg( my_kmsg,
50 MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
51 size,
52 0, /* receive size */
53 MACH_PORT_NULL,
54 MACH_MSG_TIMEOUT_NONE,
55 MACH_PORT_NULL );
56 vm_deallocate( mach_task_self(), (vm_address_t)my_kmsg, size );
57 return my_kr;
58 }
59
60 static kern_return_t
61 kmsg_recv(mach_port_t portset, mach_port_t port, int * msgh_id_return)
62 {
63 kern_return_t my_kr;
64 mach_msg_header_t * my_kmsg = NULL;
65
66 my_kr = vm_allocate( mach_task_self(),
67 (vm_address_t *)&my_kmsg,
68 PAGE_SIZE,
69 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE );
70 if (my_kr != KERN_SUCCESS)
71 return my_kr;
72 my_kr = mach_msg( my_kmsg,
73 MACH_RCV_MSG | MACH_MSG_OPTION_NONE,
74 0, /* send size */
75 PAGE_SIZE, /* receive size */
76 port,
77 MACH_MSG_TIMEOUT_NONE,
78 MACH_PORT_NULL );
79 if ( my_kr == KERN_SUCCESS &&
80 msgh_id_return != NULL )
81 *msgh_id_return = my_kmsg->msgh_id;
82 vm_deallocate( mach_task_self(), (vm_address_t)my_kmsg, PAGE_SIZE );
83 return my_kr;
84 }
85
86 static void *
87 kmsg_consumer_thread(void * arg)
88 {
89 #if !TARGET_OS_EMBEDDED
90 int my_kqueue = *(int *)arg;
91 int my_err;
92 kern_return_t my_kr;
93 struct kevent my_keventv[3];
94 int msgid;
95
96 EV_SET( &my_keventv[0], 0, 0, 0, 0, 0, 0 );
97 while ( !(my_keventv[0].filter == EVFILT_USER &&
98 my_keventv[0].ident == 0)) {
99 /* keep getting events */
100 my_err = kevent( my_kqueue, NULL, 0, my_keventv, 1, NULL );
101 if ( my_err == -1 ) {
102 printf( "kevent call from consumer thread failed with error %d - \"%s\" \n", errno, strerror( errno) );
103 return (void *)-1;
104 }
105 if ( my_err == 0 ) {
106 printf( "kevent call from consumer thread did not return any events when it should have \n" );
107 return (void *)-1;
108 }
109 if ( my_keventv[0].filter == EVFILT_MACHPORT ) {
110 if ( my_keventv[0].data == 0 ) {
111 printf( "kevent call to get machport event returned 0 msg_size \n" );
112 return (void *)-1;
113 }
114 my_kr = kmsg_recv( my_keventv[0].ident, my_keventv[0].data, &msgid );
115 if ( my_kr != KERN_SUCCESS ) {
116 printf( "kmsg_recv failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
117 return (void *)-1;
118 }
119 my_keventv[0].flags = EV_ENABLE;
120 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
121 if ( my_err == -1 ) {
122 printf( "kevent call to re-enable machport events failed with error %d - \"%s\" \n", errno, strerror( errno) );
123 return (void *)-1;
124 }
125 if (msgid == 1000 + msg_count) {
126 pthread_mutex_lock(&my_mutex);
127 last_msg_seen = 1;
128 pthread_cond_signal(&my_cond);
129 pthread_mutex_unlock(&my_mutex);
130 }
131 }
132 }
133 return (void *)0;
134 #else
135 printf( "\t--> Not supported on EMBEDDED TARGET\n" );
136 return (void *)0;
137 #endif
138 }
139
140 /* **************************************************************************************************************
141 * Test kevent, kqueue system calls.
142 * **************************************************************************************************************
143 */
144 int kqueue_tests( void * the_argp )
145 {
146 int my_err, my_status;
147 int my_kqueue = -1;
148 int my_kqueue64 = -1;
149 int my_fd = -1;
150 char * my_pathp = NULL;
151 pid_t my_pid, my_wait_pid;
152 size_t my_count, my_index;
153 int my_sockets[ 2 ] = {-1, -1};
154 struct kevent my_keventv[3];
155 #if !TARGET_OS_EMBEDDED
156 struct kevent64_s my_kevent64;
157 #endif
158 struct timespec my_timeout;
159 char my_buffer[ 16 ];
160 kern_return_t kr;
161
162 kr = vm_allocate((vm_map_t) mach_task_self(), (vm_address_t*)&my_pathp, PATH_MAX, VM_FLAGS_ANYWHERE);
163 if(kr != KERN_SUCCESS){
164 printf( "vm_allocate failed with error %d - \"%s\" \n", errno, strerror( errno) );
165 goto test_failed_exit;
166 }
167
168 *my_pathp = 0x00;
169 strcat( my_pathp, &g_target_path[0] );
170 strcat( my_pathp, "/" );
171
172 /* create a test file */
173 my_err = create_random_name( my_pathp, 1 );
174 if ( my_err != 0 ) {
175 goto test_failed_exit;
176 }
177
178 my_fd = open( my_pathp, O_RDWR, 0 );
179 if ( my_fd == -1 ) {
180 printf( "open call failed with error %d - \"%s\" \n", errno, strerror( errno) );
181 goto test_failed_exit;
182 }
183
184 my_err = socketpair( AF_UNIX, SOCK_STREAM, 0, &my_sockets[0] );
185 if ( my_err == -1 ) {
186 printf( "socketpair failed with errno %d - %s \n", errno, strerror( errno ) );
187 goto test_failed_exit;
188 }
189
190 /* fork here and use pipe to communicate */
191 my_pid = fork( );
192 if ( my_pid == -1 ) {
193 printf( "fork failed with errno %d - %s \n", errno, strerror( errno ) );
194 goto test_failed_exit;
195 }
196 else if ( my_pid == 0 ) {
197 /*
198 * child process - tell parent we are ready to go.
199 */
200 my_count = write( my_sockets[1], "r", 1 );
201 if ( my_count == -1 ) {
202 printf( "write call failed. got errno %d - %s. \n", errno, strerror( errno ) );
203 exit( -1 );
204 }
205
206 my_count = read( my_sockets[1], &my_buffer[0], 1 );
207 if ( my_count == -1 ) {
208 printf( "read call failed with error %d - \"%s\" \n", errno, strerror( errno) );
209 exit( -1 );
210 }
211 if ( my_buffer[0] != 'g' ) {
212 printf( "read call on socket failed to get \"all done\" message \n" );
213 exit( -1 );
214 }
215
216 /* now do some work that will trigger events our parent will track */
217 my_count = write( my_fd, "11111111", 8 );
218 if ( my_count == -1 ) {
219 printf( "write call failed with error %d - \"%s\" \n", errno, strerror( errno) );
220 exit( -1 );
221 }
222
223 my_err = unlink( my_pathp );
224 if ( my_err == -1 ) {
225 printf( "unlink failed with error %d - \"%s\" \n", errno, strerror( errno) );
226 exit( -1 );
227 }
228
229 /* wait for parent to tell us to exit */
230 my_count = read( my_sockets[1], &my_buffer[0], 1 );
231 if ( my_count == -1 ) {
232 printf( "read call failed with error %d - \"%s\" \n", errno, strerror( errno) );
233 exit( -1 );
234 }
235 if ( my_buffer[0] != 'e' ) {
236 printf( "read call on socket failed to get \"all done\" message \n" );
237 exit( -1 );
238 }
239 exit(0);
240 }
241
242 /* parent process - wait for child to spin up */
243 my_count = read( my_sockets[0], &my_buffer[0], sizeof(my_buffer) );
244 if ( my_count == -1 ) {
245 printf( "read call failed with error %d - \"%s\" \n", errno, strerror( errno) );
246 goto test_failed_exit;
247 }
248 if ( my_buffer[0] != 'r' ) {
249 printf( "read call on socket failed to get \"ready to go message\" \n" );
250 goto test_failed_exit;
251 }
252
253 /* set up a kqueue and register for some events */
254 my_kqueue = kqueue( );
255 if ( my_kqueue == -1 ) {
256 printf( "kqueue call failed with error %d - \"%s\" \n", errno, strerror( errno) );
257 goto test_failed_exit;
258 }
259
260 /* look for our test file to get unlinked or written to */
261 EV_SET( &my_keventv[0], my_fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR), (NOTE_DELETE | NOTE_WRITE), 0, 0 );
262 /* also keep an eye on our child process while we're at it */
263 EV_SET( &my_keventv[1], my_pid, EVFILT_PROC, (EV_ADD | EV_ONESHOT), NOTE_EXIT, 0, 0 );
264
265 my_timeout.tv_sec = 0;
266 my_timeout.tv_nsec = 0;
267 my_err = kevent( my_kqueue, my_keventv, 2, NULL, 0, &my_timeout);
268 if ( my_err == -1 ) {
269 printf( "kevent call to register events failed with error %d - \"%s\" \n", errno, strerror( errno) );
270 goto test_failed_exit;
271 }
272
273 #if !TARGET_OS_EMBEDDED
274 if (!g_is_under_rosetta) {
275 /* use kevent64 to test EVFILT_PROC */
276 EV_SET64( &my_kevent64, my_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0, 0, 0 );
277 my_err = kevent64( my_kqueue, &my_kevent64, 1, NULL, 0, 0, 0);
278 if ( my_err != -1 && errno != EINVAL ) {
279 printf( "kevent64 call should fail with kqueue used for kevent() - %d\n", my_err);
280 goto test_failed_exit;
281 }
282
283 my_kqueue64 = kqueue();
284 EV_SET64( &my_kevent64, my_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0, 0, 0 );
285 my_err = kevent64( my_kqueue64, &my_kevent64, 1, NULL, 0, 0, 0);
286 if ( my_err == -1 ) {
287 printf( "kevent64 call to get proc exit failed with error %d - \"%s\" \n", errno, strerror( errno) );
288 goto test_failed_exit;
289 }
290 }
291 #endif
292
293 /* tell child to get to work */
294 my_count = write( my_sockets[0], "g", 1 );
295 if ( my_count == -1 ) {
296 printf( "write call failed. got errno %d - %s. \n", errno, strerror( errno ) );
297 goto test_failed_exit;
298 }
299
300 /* go get vnode events */
301 EV_SET( &my_keventv[0], my_fd, EVFILT_VNODE, (EV_CLEAR), 0, 0, 0 );
302 my_err = kevent( my_kqueue, NULL, 0, my_keventv, 1, NULL );
303 if ( my_err == -1 ) {
304 printf( "kevent call to get vnode events failed with error %d - \"%s\" \n", errno, strerror( errno) );
305 goto test_failed_exit;
306 }
307 if ( my_err == 0 ) {
308 printf( "kevent call to get vnode events did not return any when it should have \n" );
309 goto test_failed_exit;
310 }
311 if ( (my_keventv[0].fflags & (NOTE_DELETE | NOTE_WRITE)) == 0 ) {
312 printf( "kevent call to get vnode events did not return NOTE_DELETE or NOTE_WRITE \n" );
313 printf( "fflags 0x%02X \n", my_keventv[0].fflags );
314 goto test_failed_exit;
315 }
316
317 /* tell child to exit */
318 my_count = write( my_sockets[0], "e", 1 );
319 if ( my_count == -1 ) {
320 printf( "write call failed. got errno %d - %s. \n", errno, strerror( errno ) );
321 goto test_failed_exit;
322 }
323
324 /* look for child exit notification after unregistering for vnode events */
325 EV_SET( &my_keventv[0], my_fd, EVFILT_VNODE, EV_DELETE, 0, 0, 0 );
326 my_err = kevent( my_kqueue, my_keventv, 1, my_keventv, 1, NULL );
327 if ( my_err == -1 ) {
328 printf( "kevent call to get proc exit event failed with error %d - \"%s\" \n", errno, strerror( errno) );
329 goto test_failed_exit;
330 }
331 if ( my_err == 0 ) {
332 printf( "kevent call to get proc exit event did not return any when it should have \n" );
333 goto test_failed_exit;
334 }
335 if ( my_keventv[0].filter != EVFILT_PROC ) {
336 printf( "kevent call to get proc exit event did not return EVFILT_PROC \n" );
337 printf( "filter %i \n", my_keventv[0].filter );
338 goto test_failed_exit;
339 }
340 if ( (my_keventv[0].fflags & NOTE_EXIT) == 0 ) {
341 printf( "kevent call to get proc exit event did not return NOTE_EXIT \n" );
342 printf( "fflags 0x%02X \n", my_keventv[0].fflags );
343 goto test_failed_exit;
344 }
345
346 #if !TARGET_OS_EMBEDDED
347 if (!g_is_under_rosetta) {
348 /* look for child exit notification on the kevent64 kqueue */
349 EV_SET64( &my_kevent64, my_pid, EVFILT_PROC, EV_CLEAR, NOTE_EXIT, 0, 0, 0, 0 );
350 my_err = kevent64( my_kqueue64, NULL, 0, &my_kevent64, 1, 0, 0);
351 if ( my_err == -1 ) {
352 printf( "kevent64 call to get child exit failed with error %d - \"%s\" \n", errno, strerror( errno) );
353 goto test_failed_exit;
354 }
355 if ( my_err == 0 ) {
356 printf( "kevent64 call to get proc exit event did not return any when it should have \n" );
357 goto test_failed_exit;
358 }
359 if ( my_kevent64.filter != EVFILT_PROC ) {
360 printf( "kevent64 call to get proc exit event did not return EVFILT_PROC \n" );
361 printf( "filter %i \n", my_kevent64.filter );
362 goto test_failed_exit;
363 }
364 if ( (my_kevent64.fflags & NOTE_EXIT) == 0 ) {
365 printf( "kevent64 call to get proc exit event did not return NOTE_EXIT \n" );
366 printf( "fflags 0x%02X \n", my_kevent64.fflags );
367 goto test_failed_exit;
368 }
369 }
370
371 my_wait_pid = wait4( my_pid, &my_status, 0, NULL );
372 if ( my_wait_pid == -1 ) {
373 printf( "wait4 failed with errno %d - %s \n", errno, strerror( errno ) );
374 goto test_failed_exit;
375 }
376
377 /* wait4 should return our child's pid when it exits */
378 if ( my_wait_pid != my_pid ) {
379 printf( "wait4 did not return child pid - returned %d should be %d \n", my_wait_pid, my_pid );
380 goto test_failed_exit;
381 }
382
383 if ( WIFEXITED( my_status ) && WEXITSTATUS( my_status ) != 0 ) {
384 printf( "wait4 returned wrong exit status - 0x%02X \n", my_status );
385 goto test_failed_exit;
386 }
387
388 /* now try out EVFILT_MACHPORT and EVFILT_USER */
389 mach_port_t my_pset = MACH_PORT_NULL;
390 mach_port_t my_port = MACH_PORT_NULL;
391 kern_return_t my_kr;
392
393 my_kr = mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &my_pset );
394 if ( my_kr != KERN_SUCCESS ) {
395 printf( "mach_port_allocate failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
396 goto test_failed_exit;
397 }
398
399 my_kr = mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &my_port );
400 if ( my_kr != KERN_SUCCESS ) {
401 printf( "mach_port_allocate failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
402 goto test_failed_exit;
403 }
404
405 /* try to register for events on my_port directly -- this should fail */
406 EV_SET( &my_keventv[0], my_port, EVFILT_MACHPORT, (EV_ADD | EV_DISPATCH), 0, 0, 0 );
407 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
408 if ( my_err != -1 || errno != ENOTSUP ) {
409 printf( "kevent call to register my_port should have failed, but got %s \n", strerror(errno) );
410 goto test_failed_exit;
411 }
412
413 /* now register for events on my_pset and user 0 */
414 EV_SET( &my_keventv[0], my_pset, EVFILT_MACHPORT, (EV_ADD | EV_CLEAR | EV_DISPATCH), 0, 0, 0 );
415 EV_SET( &my_keventv[1], 0, EVFILT_USER, EV_ADD, 0, 0, 0 );
416 my_err = kevent( my_kqueue, my_keventv, 2, NULL, 0, NULL );
417 if ( my_err == -1 ) {
418 printf( "kevent call to register my_pset and user 0 failed with error %d - %s \n", errno, strerror( errno) );
419 goto test_failed_exit;
420 }
421
422 pthread_t my_threadv[3];
423
424 for (my_index = 0;
425 my_index < 3;
426 my_index++) {
427 my_err = pthread_create( &my_threadv[my_index], NULL, kmsg_consumer_thread, (void *)&my_kqueue );
428 if ( my_err != 0 ) {
429 printf( "pthread_create failed with error %d - %s \n", my_err, strerror(my_err) );
430 goto test_failed_exit;
431 }
432 }
433
434 /* insert my_port into my_pset */
435 my_kr = mach_port_insert_member( mach_task_self(), my_port, my_pset );
436 if ( my_kr != KERN_SUCCESS ) {
437 printf( "mach_port_insert_member failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
438 goto test_failed_exit;
439 }
440
441 my_kr = mach_port_insert_right( mach_task_self(), my_port, my_port, MACH_MSG_TYPE_MAKE_SEND );
442 if ( my_kr != KERN_SUCCESS ) {
443 printf( "mach_port_insert_right failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
444 goto test_failed_exit;
445 }
446
447 /* send some Mach messages */
448 for (my_index = 1;
449 my_index <= msg_count;
450 my_index++) {
451 my_kr = kmsg_send( my_port, my_index );
452 if ( my_kr != KERN_SUCCESS ) {
453 printf( "kmsg_send failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
454 goto test_failed_exit;
455 }
456 }
457
458 /* make sure the last message eventually gets processed */
459 pthread_mutex_lock(&my_mutex);
460 while (last_msg_seen == 0)
461 pthread_cond_wait(&my_cond, &my_mutex);
462 pthread_mutex_unlock(&my_mutex);
463
464 /* trigger the user 0 event, telling consumer threads to exit */
465 EV_SET( &my_keventv[0], 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0 );
466 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
467 if ( my_err == -1 ) {
468 printf( "kevent call to trigger user 0 failed with error %d - %s \n", errno, strerror( errno) );
469 goto test_failed_exit;
470 }
471
472 for (my_index = 0;
473 my_index < 3;
474 my_index++) {
475 my_err = pthread_join( my_threadv[my_index], (void **)&my_status );
476 if ( my_err != 0 ) {
477 printf( "pthread_join failed with error %d - %s \n", my_err, strerror(my_err) );
478 goto test_failed_exit;
479 }
480 if ( my_status != 0 ) {
481 goto test_failed_exit;
482 }
483 }
484
485 /* clear the user 0 event */
486 EV_SET( &my_keventv[0], 0, EVFILT_USER, EV_CLEAR, 0, 0, 0 );
487 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
488 if ( my_err == -1 ) {
489 printf( "kevent call to trigger user 0 failed with error %d - %s \n", errno, strerror( errno) );
490 goto test_failed_exit;
491 }
492
493 /* delibrately destroy my_pset while it's still registered for events */
494 my_kr = mach_port_mod_refs( mach_task_self(), my_pset, MACH_PORT_RIGHT_PORT_SET, -1 );
495 if ( my_kr != KERN_SUCCESS ) {
496 printf( "mach_port_mod_refs failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
497 goto test_failed_exit;
498 }
499
500 /* look for the event to trigger with a zero msg_size */
501 my_err = kevent( my_kqueue, NULL, 0, my_keventv, 1, NULL );
502 if ( my_err == -1 ) {
503 printf( "kevent call to get machport event failed with error %d - \"%s\" \n", errno, strerror( errno) );
504 goto test_failed_exit;
505 }
506 if ( my_err == 0 ) {
507 printf( "kevent call to get machport event did not return any when it should have \n" );
508 goto test_failed_exit;
509 }
510 if ( my_keventv[0].filter != EVFILT_MACHPORT ) {
511 printf( "kevent call to get machport event did not return EVFILT_MACHPORT \n" );
512 printf( "filter %i \n", my_keventv[0].filter );
513 goto test_failed_exit;
514 }
515 if ( my_keventv[0].data != 0 ) {
516 printf( "kevent call to get machport event did not return 0 msg_size \n" );
517 printf( "data %ld \n", (long int) my_keventv[0].data );
518 goto test_failed_exit;
519 }
520 #endif
521
522 my_err = 0;
523 goto test_passed_exit;
524
525 test_failed_exit:
526 my_err = -1;
527
528 test_passed_exit:
529 if ( my_sockets[0] != -1 )
530 close( my_sockets[0] );
531 if ( my_sockets[1] != -1 )
532 close( my_sockets[1] );
533 if ( my_kqueue != -1 )
534 close( my_kqueue );
535 if ( my_kqueue64 != -1 )
536 close( my_kqueue );
537 if ( my_fd != -1 )
538 close( my_fd );
539 if ( my_pathp != NULL ) {
540 remove( my_pathp );
541 vm_deallocate(mach_task_self(), (vm_address_t)my_pathp, PATH_MAX);
542 }
543 return( my_err );
544 }