]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/xnu_quick_test/kqueue_tests.c
xnu-2422.1.72.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
22 int msg_count = 14;
23 int last_msg_seen = 0;
24 pthread_cond_t my_cond = PTHREAD_COND_INITIALIZER;
25 pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
26
27
28 static kern_return_t
29 kmsg_send(mach_port_t remote_port, int index)
30 {
31 int msgh_id = 1000 + index;
32 kern_return_t my_kr;
33 mach_msg_header_t * my_kmsg = NULL;
34 mach_msg_size_t size = sizeof(mach_msg_header_t) + sizeof(int)*index;
35
36 my_kr = vm_allocate( mach_task_self(),
37 (vm_address_t *)&my_kmsg,
38 size,
39 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE );
40 if (my_kr != KERN_SUCCESS)
41 return my_kr;
42 my_kmsg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
43 my_kmsg->msgh_size = size;
44 my_kmsg->msgh_remote_port = remote_port;
45 my_kmsg->msgh_local_port = MACH_PORT_NULL;
46 my_kmsg->msgh_reserved = 0;
47 my_kmsg->msgh_id = msgh_id;
48 my_kr = mach_msg( my_kmsg,
49 MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
50 size,
51 0, /* receive size */
52 MACH_PORT_NULL,
53 MACH_MSG_TIMEOUT_NONE,
54 MACH_PORT_NULL );
55 vm_deallocate( mach_task_self(), (vm_address_t)my_kmsg, size );
56 return my_kr;
57 }
58
59 static kern_return_t
60 kmsg_recv(mach_port_t portset, mach_port_t port, int * msgh_id_return)
61 {
62 kern_return_t my_kr;
63 mach_msg_header_t * my_kmsg = NULL;
64
65 my_kr = vm_allocate( mach_task_self(),
66 (vm_address_t *)&my_kmsg,
67 PAGE_SIZE,
68 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE );
69 if (my_kr != KERN_SUCCESS)
70 return my_kr;
71 my_kr = mach_msg( my_kmsg,
72 MACH_RCV_MSG | MACH_MSG_OPTION_NONE,
73 0, /* send size */
74 PAGE_SIZE, /* receive size */
75 port,
76 MACH_MSG_TIMEOUT_NONE,
77 MACH_PORT_NULL );
78 if ( my_kr == KERN_SUCCESS &&
79 msgh_id_return != NULL )
80 *msgh_id_return = my_kmsg->msgh_id;
81 vm_deallocate( mach_task_self(), (vm_address_t)my_kmsg, PAGE_SIZE );
82 return my_kr;
83 }
84
85 static void *
86 kmsg_consumer_thread(void * arg)
87 {
88 #if !TARGET_OS_EMBEDDED
89 int my_kqueue = *(int *)arg;
90 int my_err;
91 kern_return_t my_kr;
92 struct kevent my_keventv[3];
93 int msgid;
94
95 EV_SET( &my_keventv[0], 0, 0, 0, 0, 0, 0 );
96 while ( !(my_keventv[0].filter == EVFILT_USER &&
97 my_keventv[0].ident == 0)) {
98 /* keep getting events */
99 my_err = kevent( my_kqueue, NULL, 0, my_keventv, 1, NULL );
100 if ( my_err == -1 ) {
101 printf( "kevent call from consumer thread failed with error %d - \"%s\" \n", errno, strerror( errno) );
102 return (void *)-1;
103 }
104 if ( my_err == 0 ) {
105 printf( "kevent call from consumer thread did not return any events when it should have \n" );
106 return (void *)-1;
107 }
108 if ( my_keventv[0].filter == EVFILT_MACHPORT ) {
109 if ( my_keventv[0].data == 0 ) {
110 printf( "kevent call to get machport event returned 0 msg_size \n" );
111 return (void *)-1;
112 }
113 my_kr = kmsg_recv( my_keventv[0].ident, my_keventv[0].data, &msgid );
114 if ( my_kr != KERN_SUCCESS ) {
115 printf( "kmsg_recv failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
116 return (void *)-1;
117 }
118 my_keventv[0].flags = EV_ENABLE;
119 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
120 if ( my_err == -1 ) {
121 printf( "kevent call to re-enable machport events failed with error %d - \"%s\" \n", errno, strerror( errno) );
122 return (void *)-1;
123 }
124 if (msgid == 1000 + msg_count) {
125 pthread_mutex_lock(&my_mutex);
126 last_msg_seen = 1;
127 pthread_cond_signal(&my_cond);
128 pthread_mutex_unlock(&my_mutex);
129 }
130 }
131 }
132 return (void *)0;
133 #else
134 printf( "\t--> Not supported on EMBEDDED TARGET\n" );
135 return (void *)0;
136 #endif
137 }
138
139 /* **************************************************************************************************************
140 * Test kevent, kqueue system calls.
141 * **************************************************************************************************************
142 */
143 int kqueue_tests( void * the_argp )
144 {
145 int my_err, my_status;
146 void *my_pthread_join_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 /* use kevent64 to test EVFILT_PROC */
275 EV_SET64( &my_kevent64, my_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0, 0, 0 );
276 my_err = kevent64( my_kqueue, &my_kevent64, 1, NULL, 0, 0, 0);
277 if ( my_err != -1 && errno != EINVAL ) {
278 printf( "kevent64 call should fail with kqueue used for kevent() - %d\n", my_err);
279 goto test_failed_exit;
280 }
281
282 my_kqueue64 = kqueue();
283 EV_SET64( &my_kevent64, my_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0, 0, 0 );
284 my_err = kevent64( my_kqueue64, &my_kevent64, 1, NULL, 0, 0, 0);
285 if ( my_err == -1 ) {
286 printf( "kevent64 call to get proc exit failed with error %d - \"%s\" \n", errno, strerror( errno) );
287 goto test_failed_exit;
288 }
289 #endif
290
291 /* tell child to get to work */
292 my_count = write( my_sockets[0], "g", 1 );
293 if ( my_count == -1 ) {
294 printf( "write call failed. got errno %d - %s. \n", errno, strerror( errno ) );
295 goto test_failed_exit;
296 }
297
298 /* go get vnode events */
299 EV_SET( &my_keventv[0], my_fd, EVFILT_VNODE, (EV_CLEAR), 0, 0, 0 );
300 my_err = kevent( my_kqueue, NULL, 0, my_keventv, 1, NULL );
301 if ( my_err == -1 ) {
302 printf( "kevent call to get vnode events failed with error %d - \"%s\" \n", errno, strerror( errno) );
303 goto test_failed_exit;
304 }
305 if ( my_err == 0 ) {
306 printf( "kevent call to get vnode events did not return any when it should have \n" );
307 goto test_failed_exit;
308 }
309 if ( (my_keventv[0].fflags & (NOTE_DELETE | NOTE_WRITE)) == 0 ) {
310 printf( "kevent call to get vnode events did not return NOTE_DELETE or NOTE_WRITE \n" );
311 printf( "fflags 0x%02X \n", my_keventv[0].fflags );
312 goto test_failed_exit;
313 }
314
315 /* tell child to exit */
316 my_count = write( my_sockets[0], "e", 1 );
317 if ( my_count == -1 ) {
318 printf( "write call failed. got errno %d - %s. \n", errno, strerror( errno ) );
319 goto test_failed_exit;
320 }
321
322 /* look for child exit notification after unregistering for vnode events */
323 EV_SET( &my_keventv[0], my_fd, EVFILT_VNODE, EV_DELETE, 0, 0, 0 );
324 my_err = kevent( my_kqueue, my_keventv, 1, my_keventv, 1, NULL );
325 if ( my_err == -1 ) {
326 printf( "kevent call to get proc exit event failed with error %d - \"%s\" \n", errno, strerror( errno) );
327 goto test_failed_exit;
328 }
329 if ( my_err == 0 ) {
330 printf( "kevent call to get proc exit event did not return any when it should have \n" );
331 goto test_failed_exit;
332 }
333 if ( my_keventv[0].filter != EVFILT_PROC ) {
334 printf( "kevent call to get proc exit event did not return EVFILT_PROC \n" );
335 printf( "filter %i \n", my_keventv[0].filter );
336 goto test_failed_exit;
337 }
338 if ( (my_keventv[0].fflags & NOTE_EXIT) == 0 ) {
339 printf( "kevent call to get proc exit event did not return NOTE_EXIT \n" );
340 printf( "fflags 0x%02X \n", my_keventv[0].fflags );
341 goto test_failed_exit;
342 }
343
344 #if !TARGET_OS_EMBEDDED
345 /* look for child exit notification on the kevent64 kqueue */
346 EV_SET64( &my_kevent64, my_pid, EVFILT_PROC, EV_CLEAR, NOTE_EXIT, 0, 0, 0, 0 );
347 my_err = kevent64( my_kqueue64, NULL, 0, &my_kevent64, 1, 0, 0);
348 if ( my_err == -1 ) {
349 printf( "kevent64 call to get child exit failed with error %d - \"%s\" \n", errno, strerror( errno) );
350 goto test_failed_exit;
351 }
352 if ( my_err == 0 ) {
353 printf( "kevent64 call to get proc exit event did not return any when it should have \n" );
354 goto test_failed_exit;
355 }
356 if ( my_kevent64.filter != EVFILT_PROC ) {
357 printf( "kevent64 call to get proc exit event did not return EVFILT_PROC \n" );
358 printf( "filter %i \n", my_kevent64.filter );
359 goto test_failed_exit;
360 }
361 if ( (my_kevent64.fflags & NOTE_EXIT) == 0 ) {
362 printf( "kevent64 call to get proc exit event did not return NOTE_EXIT \n" );
363 printf( "fflags 0x%02X \n", my_kevent64.fflags );
364 goto test_failed_exit;
365 }
366
367 my_wait_pid = wait4( my_pid, &my_status, 0, NULL );
368 if ( my_wait_pid == -1 ) {
369 printf( "wait4 failed with errno %d - %s \n", errno, strerror( errno ) );
370 goto test_failed_exit;
371 }
372
373 /* wait4 should return our child's pid when it exits */
374 if ( my_wait_pid != my_pid ) {
375 printf( "wait4 did not return child pid - returned %d should be %d \n", my_wait_pid, my_pid );
376 goto test_failed_exit;
377 }
378
379 if ( WIFEXITED( my_status ) && WEXITSTATUS( my_status ) != 0 ) {
380 printf( "wait4 returned wrong exit status - 0x%02X \n", my_status );
381 goto test_failed_exit;
382 }
383
384 /* now try out EVFILT_MACHPORT and EVFILT_USER */
385 mach_port_t my_pset = MACH_PORT_NULL;
386 mach_port_t my_port = MACH_PORT_NULL;
387 kern_return_t my_kr;
388
389 my_kr = mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &my_pset );
390 if ( my_kr != KERN_SUCCESS ) {
391 printf( "mach_port_allocate failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
392 goto test_failed_exit;
393 }
394
395 my_kr = mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &my_port );
396 if ( my_kr != KERN_SUCCESS ) {
397 printf( "mach_port_allocate failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
398 goto test_failed_exit;
399 }
400
401 /* try to register for events on my_port directly -- this should fail */
402 EV_SET( &my_keventv[0], my_port, EVFILT_MACHPORT, (EV_ADD | EV_DISPATCH), 0, 0, 0 );
403 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
404 if ( my_err != -1 || errno != ENOTSUP ) {
405 printf( "kevent call to register my_port should have failed, but got %s \n", strerror(errno) );
406 goto test_failed_exit;
407 }
408
409 /* now register for events on my_pset and user 0 */
410 EV_SET( &my_keventv[0], my_pset, EVFILT_MACHPORT, (EV_ADD | EV_CLEAR | EV_DISPATCH), 0, 0, 0 );
411 EV_SET( &my_keventv[1], 0, EVFILT_USER, EV_ADD, 0, 0, 0 );
412 my_err = kevent( my_kqueue, my_keventv, 2, NULL, 0, NULL );
413 if ( my_err == -1 ) {
414 printf( "kevent call to register my_pset and user 0 failed with error %d - %s \n", errno, strerror( errno) );
415 goto test_failed_exit;
416 }
417
418 pthread_t my_threadv[3];
419
420 for (my_index = 0;
421 my_index < 3;
422 my_index++) {
423 my_err = pthread_create( &my_threadv[my_index], NULL, kmsg_consumer_thread, (void *)&my_kqueue );
424 if ( my_err != 0 ) {
425 printf( "pthread_create failed with error %d - %s \n", my_err, strerror(my_err) );
426 goto test_failed_exit;
427 }
428 }
429
430 /* insert my_port into my_pset */
431 my_kr = mach_port_insert_member( mach_task_self(), my_port, my_pset );
432 if ( my_kr != KERN_SUCCESS ) {
433 printf( "mach_port_insert_member failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
434 goto test_failed_exit;
435 }
436
437 my_kr = mach_port_insert_right( mach_task_self(), my_port, my_port, MACH_MSG_TYPE_MAKE_SEND );
438 if ( my_kr != KERN_SUCCESS ) {
439 printf( "mach_port_insert_right failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
440 goto test_failed_exit;
441 }
442
443 /* send some Mach messages */
444 for (my_index = 1;
445 my_index <= msg_count;
446 my_index++) {
447 my_kr = kmsg_send( my_port, my_index );
448 if ( my_kr != KERN_SUCCESS ) {
449 printf( "kmsg_send failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
450 goto test_failed_exit;
451 }
452 }
453
454 /* make sure the last message eventually gets processed */
455 pthread_mutex_lock(&my_mutex);
456 while (last_msg_seen == 0)
457 pthread_cond_wait(&my_cond, &my_mutex);
458 pthread_mutex_unlock(&my_mutex);
459
460 /* trigger the user 0 event, telling consumer threads to exit */
461 EV_SET( &my_keventv[0], 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0 );
462 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
463 if ( my_err == -1 ) {
464 printf( "kevent call to trigger user 0 failed with error %d - %s \n", errno, strerror( errno) );
465 goto test_failed_exit;
466 }
467
468 for (my_index = 0;
469 my_index < 3;
470 my_index++) {
471 my_err = pthread_join( my_threadv[my_index], &my_pthread_join_status );
472 if ( my_err != 0 ) {
473 printf( "pthread_join failed with error %d - %s \n", my_err, strerror(my_err) );
474 goto test_failed_exit;
475 }
476 if ( my_pthread_join_status != 0 ) {
477 goto test_failed_exit;
478 }
479 }
480
481 /* clear the user 0 event */
482 EV_SET( &my_keventv[0], 0, EVFILT_USER, EV_CLEAR, 0, 0, 0 );
483 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
484 if ( my_err == -1 ) {
485 printf( "kevent call to trigger user 0 failed with error %d - %s \n", errno, strerror( errno) );
486 goto test_failed_exit;
487 }
488
489 /* delibrately destroy my_pset while it's still registered for events */
490 my_kr = mach_port_mod_refs( mach_task_self(), my_pset, MACH_PORT_RIGHT_PORT_SET, -1 );
491 if ( my_kr != KERN_SUCCESS ) {
492 printf( "mach_port_mod_refs failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
493 goto test_failed_exit;
494 }
495
496 /* look for the event to trigger with a zero msg_size */
497 my_err = kevent( my_kqueue, NULL, 0, my_keventv, 1, NULL );
498 if ( my_err == -1 ) {
499 printf( "kevent call to get machport event failed with error %d - \"%s\" \n", errno, strerror( errno) );
500 goto test_failed_exit;
501 }
502 if ( my_err == 0 ) {
503 printf( "kevent call to get machport event did not return any when it should have \n" );
504 goto test_failed_exit;
505 }
506 if ( my_keventv[0].filter != EVFILT_MACHPORT ) {
507 printf( "kevent call to get machport event did not return EVFILT_MACHPORT \n" );
508 printf( "filter %i \n", my_keventv[0].filter );
509 goto test_failed_exit;
510 }
511 if ( my_keventv[0].data != 0 ) {
512 printf( "kevent call to get machport event did not return 0 msg_size \n" );
513 printf( "data %ld \n", (long int) my_keventv[0].data );
514 goto test_failed_exit;
515 }
516 #endif
517
518 my_err = 0;
519 goto test_passed_exit;
520
521 test_failed_exit:
522 my_err = -1;
523
524 test_passed_exit:
525 if ( my_sockets[0] != -1 )
526 close( my_sockets[0] );
527 if ( my_sockets[1] != -1 )
528 close( my_sockets[1] );
529 if ( my_kqueue != -1 )
530 close( my_kqueue );
531 if ( my_kqueue64 != -1 )
532 close( my_kqueue );
533 if ( my_fd != -1 )
534 close( my_fd );
535 if ( my_pathp != NULL ) {
536 remove( my_pathp );
537 vm_deallocate(mach_task_self(), (vm_address_t)my_pathp, PATH_MAX);
538 }
539 return( my_err );
540 }