]> git.saurik.com Git - apple/xnu.git/blame - tools/tests/xnu_quick_test/kqueue_tests.c
xnu-2782.1.97.tar.gz
[apple/xnu.git] / tools / tests / xnu_quick_test / kqueue_tests.c
CommitLineData
b0d623f7
A
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
19extern char g_target_path[ PATH_MAX ];
20extern int g_skip_setuid_tests;
b0d623f7
A
21
22int msg_count = 14;
23int last_msg_seen = 0;
24pthread_cond_t my_cond = PTHREAD_COND_INITIALIZER;
25pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
26
27
28static kern_return_t
29kmsg_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;
fe8ab488
A
42 my_kmsg->msgh_bits =
43 MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0);
b0d623f7
A
44 my_kmsg->msgh_size = size;
45 my_kmsg->msgh_remote_port = remote_port;
46 my_kmsg->msgh_local_port = MACH_PORT_NULL;
fe8ab488 47 my_kmsg->msgh_voucher_port = MACH_PORT_NULL;
b0d623f7
A
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
60static kern_return_t
61kmsg_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
86static void *
87kmsg_consumer_thread(void * arg)
88{
b0d623f7
A
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;
b0d623f7
A
133}
134
135/* **************************************************************************************************************
136 * Test kevent, kqueue system calls.
137 * **************************************************************************************************************
138 */
139int kqueue_tests( void * the_argp )
140{
141 int my_err, my_status;
6d2010ae 142 void *my_pthread_join_status;
b0d623f7
A
143 int my_kqueue = -1;
144 int my_kqueue64 = -1;
145 int my_fd = -1;
146 char * my_pathp = NULL;
147 pid_t my_pid, my_wait_pid;
148 size_t my_count, my_index;
149 int my_sockets[ 2 ] = {-1, -1};
150 struct kevent my_keventv[3];
b0d623f7 151 struct kevent64_s my_kevent64;
b0d623f7
A
152 struct timespec my_timeout;
153 char my_buffer[ 16 ];
154 kern_return_t kr;
155
156 kr = vm_allocate((vm_map_t) mach_task_self(), (vm_address_t*)&my_pathp, PATH_MAX, VM_FLAGS_ANYWHERE);
157 if(kr != KERN_SUCCESS){
158 printf( "vm_allocate failed with error %d - \"%s\" \n", errno, strerror( errno) );
159 goto test_failed_exit;
160 }
161
162 *my_pathp = 0x00;
163 strcat( my_pathp, &g_target_path[0] );
164 strcat( my_pathp, "/" );
165
166 /* create a test file */
167 my_err = create_random_name( my_pathp, 1 );
168 if ( my_err != 0 ) {
169 goto test_failed_exit;
170 }
171
172 my_fd = open( my_pathp, O_RDWR, 0 );
173 if ( my_fd == -1 ) {
174 printf( "open call failed with error %d - \"%s\" \n", errno, strerror( errno) );
175 goto test_failed_exit;
176 }
177
178 my_err = socketpair( AF_UNIX, SOCK_STREAM, 0, &my_sockets[0] );
179 if ( my_err == -1 ) {
180 printf( "socketpair failed with errno %d - %s \n", errno, strerror( errno ) );
181 goto test_failed_exit;
182 }
183
184 /* fork here and use pipe to communicate */
185 my_pid = fork( );
186 if ( my_pid == -1 ) {
187 printf( "fork failed with errno %d - %s \n", errno, strerror( errno ) );
188 goto test_failed_exit;
189 }
190 else if ( my_pid == 0 ) {
191 /*
192 * child process - tell parent we are ready to go.
193 */
194 my_count = write( my_sockets[1], "r", 1 );
195 if ( my_count == -1 ) {
196 printf( "write call failed. got errno %d - %s. \n", errno, strerror( errno ) );
197 exit( -1 );
198 }
199
200 my_count = read( my_sockets[1], &my_buffer[0], 1 );
201 if ( my_count == -1 ) {
202 printf( "read call failed with error %d - \"%s\" \n", errno, strerror( errno) );
203 exit( -1 );
204 }
205 if ( my_buffer[0] != 'g' ) {
206 printf( "read call on socket failed to get \"all done\" message \n" );
207 exit( -1 );
208 }
209
210 /* now do some work that will trigger events our parent will track */
211 my_count = write( my_fd, "11111111", 8 );
212 if ( my_count == -1 ) {
213 printf( "write call failed with error %d - \"%s\" \n", errno, strerror( errno) );
214 exit( -1 );
215 }
216
217 my_err = unlink( my_pathp );
218 if ( my_err == -1 ) {
219 printf( "unlink failed with error %d - \"%s\" \n", errno, strerror( errno) );
220 exit( -1 );
221 }
222
223 /* wait for parent to tell us to exit */
224 my_count = read( my_sockets[1], &my_buffer[0], 1 );
225 if ( my_count == -1 ) {
226 printf( "read call failed with error %d - \"%s\" \n", errno, strerror( errno) );
227 exit( -1 );
228 }
229 if ( my_buffer[0] != 'e' ) {
230 printf( "read call on socket failed to get \"all done\" message \n" );
231 exit( -1 );
232 }
233 exit(0);
234 }
235
236 /* parent process - wait for child to spin up */
237 my_count = read( my_sockets[0], &my_buffer[0], sizeof(my_buffer) );
238 if ( my_count == -1 ) {
239 printf( "read call failed with error %d - \"%s\" \n", errno, strerror( errno) );
240 goto test_failed_exit;
241 }
242 if ( my_buffer[0] != 'r' ) {
243 printf( "read call on socket failed to get \"ready to go message\" \n" );
244 goto test_failed_exit;
245 }
246
247 /* set up a kqueue and register for some events */
248 my_kqueue = kqueue( );
249 if ( my_kqueue == -1 ) {
250 printf( "kqueue call failed with error %d - \"%s\" \n", errno, strerror( errno) );
251 goto test_failed_exit;
252 }
253
254 /* look for our test file to get unlinked or written to */
255 EV_SET( &my_keventv[0], my_fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR), (NOTE_DELETE | NOTE_WRITE), 0, 0 );
256 /* also keep an eye on our child process while we're at it */
257 EV_SET( &my_keventv[1], my_pid, EVFILT_PROC, (EV_ADD | EV_ONESHOT), NOTE_EXIT, 0, 0 );
258
259 my_timeout.tv_sec = 0;
260 my_timeout.tv_nsec = 0;
261 my_err = kevent( my_kqueue, my_keventv, 2, NULL, 0, &my_timeout);
262 if ( my_err == -1 ) {
263 printf( "kevent call to register events failed with error %d - \"%s\" \n", errno, strerror( errno) );
264 goto test_failed_exit;
265 }
266
6d2010ae
A
267 /* use kevent64 to test EVFILT_PROC */
268 EV_SET64( &my_kevent64, my_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0, 0, 0 );
269 my_err = kevent64( my_kqueue, &my_kevent64, 1, NULL, 0, 0, 0);
270 if ( my_err != -1 && errno != EINVAL ) {
271 printf( "kevent64 call should fail with kqueue used for kevent() - %d\n", my_err);
272 goto test_failed_exit;
273 }
b0d623f7 274
6d2010ae
A
275 my_kqueue64 = kqueue();
276 EV_SET64( &my_kevent64, my_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0, 0, 0 );
277 my_err = kevent64( my_kqueue64, &my_kevent64, 1, NULL, 0, 0, 0);
278 if ( my_err == -1 ) {
279 printf( "kevent64 call to get proc exit failed with error %d - \"%s\" \n", errno, strerror( errno) );
280 goto test_failed_exit;
b0d623f7 281 }
b0d623f7
A
282
283 /* tell child to get to work */
284 my_count = write( my_sockets[0], "g", 1 );
285 if ( my_count == -1 ) {
286 printf( "write call failed. got errno %d - %s. \n", errno, strerror( errno ) );
287 goto test_failed_exit;
288 }
289
290 /* go get vnode events */
291 EV_SET( &my_keventv[0], my_fd, EVFILT_VNODE, (EV_CLEAR), 0, 0, 0 );
292 my_err = kevent( my_kqueue, NULL, 0, my_keventv, 1, NULL );
293 if ( my_err == -1 ) {
294 printf( "kevent call to get vnode events failed with error %d - \"%s\" \n", errno, strerror( errno) );
295 goto test_failed_exit;
296 }
297 if ( my_err == 0 ) {
298 printf( "kevent call to get vnode events did not return any when it should have \n" );
299 goto test_failed_exit;
300 }
301 if ( (my_keventv[0].fflags & (NOTE_DELETE | NOTE_WRITE)) == 0 ) {
302 printf( "kevent call to get vnode events did not return NOTE_DELETE or NOTE_WRITE \n" );
303 printf( "fflags 0x%02X \n", my_keventv[0].fflags );
304 goto test_failed_exit;
305 }
306
307 /* tell child to exit */
308 my_count = write( my_sockets[0], "e", 1 );
309 if ( my_count == -1 ) {
310 printf( "write call failed. got errno %d - %s. \n", errno, strerror( errno ) );
311 goto test_failed_exit;
312 }
313
314 /* look for child exit notification after unregistering for vnode events */
315 EV_SET( &my_keventv[0], my_fd, EVFILT_VNODE, EV_DELETE, 0, 0, 0 );
316 my_err = kevent( my_kqueue, my_keventv, 1, my_keventv, 1, NULL );
317 if ( my_err == -1 ) {
318 printf( "kevent call to get proc exit event failed with error %d - \"%s\" \n", errno, strerror( errno) );
319 goto test_failed_exit;
320 }
321 if ( my_err == 0 ) {
322 printf( "kevent call to get proc exit event did not return any when it should have \n" );
323 goto test_failed_exit;
324 }
325 if ( my_keventv[0].filter != EVFILT_PROC ) {
326 printf( "kevent call to get proc exit event did not return EVFILT_PROC \n" );
327 printf( "filter %i \n", my_keventv[0].filter );
328 goto test_failed_exit;
329 }
330 if ( (my_keventv[0].fflags & NOTE_EXIT) == 0 ) {
331 printf( "kevent call to get proc exit event did not return NOTE_EXIT \n" );
332 printf( "fflags 0x%02X \n", my_keventv[0].fflags );
333 goto test_failed_exit;
334 }
335
6d2010ae
A
336 /* look for child exit notification on the kevent64 kqueue */
337 EV_SET64( &my_kevent64, my_pid, EVFILT_PROC, EV_CLEAR, NOTE_EXIT, 0, 0, 0, 0 );
338 my_err = kevent64( my_kqueue64, NULL, 0, &my_kevent64, 1, 0, 0);
339 if ( my_err == -1 ) {
340 printf( "kevent64 call to get child exit failed with error %d - \"%s\" \n", errno, strerror( errno) );
341 goto test_failed_exit;
342 }
343 if ( my_err == 0 ) {
344 printf( "kevent64 call to get proc exit event did not return any when it should have \n" );
345 goto test_failed_exit;
346 }
347 if ( my_kevent64.filter != EVFILT_PROC ) {
348 printf( "kevent64 call to get proc exit event did not return EVFILT_PROC \n" );
349 printf( "filter %i \n", my_kevent64.filter );
350 goto test_failed_exit;
351 }
352 if ( (my_kevent64.fflags & NOTE_EXIT) == 0 ) {
353 printf( "kevent64 call to get proc exit event did not return NOTE_EXIT \n" );
354 printf( "fflags 0x%02X \n", my_kevent64.fflags );
355 goto test_failed_exit;
b0d623f7
A
356 }
357
358 my_wait_pid = wait4( my_pid, &my_status, 0, NULL );
359 if ( my_wait_pid == -1 ) {
360 printf( "wait4 failed with errno %d - %s \n", errno, strerror( errno ) );
361 goto test_failed_exit;
362 }
363
364 /* wait4 should return our child's pid when it exits */
365 if ( my_wait_pid != my_pid ) {
366 printf( "wait4 did not return child pid - returned %d should be %d \n", my_wait_pid, my_pid );
367 goto test_failed_exit;
368 }
369
370 if ( WIFEXITED( my_status ) && WEXITSTATUS( my_status ) != 0 ) {
371 printf( "wait4 returned wrong exit status - 0x%02X \n", my_status );
372 goto test_failed_exit;
373 }
374
375 /* now try out EVFILT_MACHPORT and EVFILT_USER */
376 mach_port_t my_pset = MACH_PORT_NULL;
377 mach_port_t my_port = MACH_PORT_NULL;
378 kern_return_t my_kr;
379
380 my_kr = mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &my_pset );
381 if ( my_kr != KERN_SUCCESS ) {
382 printf( "mach_port_allocate failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
383 goto test_failed_exit;
384 }
385
386 my_kr = mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &my_port );
387 if ( my_kr != KERN_SUCCESS ) {
388 printf( "mach_port_allocate failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
389 goto test_failed_exit;
390 }
391
392 /* try to register for events on my_port directly -- this should fail */
393 EV_SET( &my_keventv[0], my_port, EVFILT_MACHPORT, (EV_ADD | EV_DISPATCH), 0, 0, 0 );
394 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
395 if ( my_err != -1 || errno != ENOTSUP ) {
396 printf( "kevent call to register my_port should have failed, but got %s \n", strerror(errno) );
397 goto test_failed_exit;
398 }
399
400 /* now register for events on my_pset and user 0 */
401 EV_SET( &my_keventv[0], my_pset, EVFILT_MACHPORT, (EV_ADD | EV_CLEAR | EV_DISPATCH), 0, 0, 0 );
402 EV_SET( &my_keventv[1], 0, EVFILT_USER, EV_ADD, 0, 0, 0 );
403 my_err = kevent( my_kqueue, my_keventv, 2, NULL, 0, NULL );
404 if ( my_err == -1 ) {
405 printf( "kevent call to register my_pset and user 0 failed with error %d - %s \n", errno, strerror( errno) );
406 goto test_failed_exit;
407 }
408
409 pthread_t my_threadv[3];
410
411 for (my_index = 0;
412 my_index < 3;
413 my_index++) {
414 my_err = pthread_create( &my_threadv[my_index], NULL, kmsg_consumer_thread, (void *)&my_kqueue );
415 if ( my_err != 0 ) {
416 printf( "pthread_create failed with error %d - %s \n", my_err, strerror(my_err) );
417 goto test_failed_exit;
418 }
419 }
420
421 /* insert my_port into my_pset */
422 my_kr = mach_port_insert_member( mach_task_self(), my_port, my_pset );
423 if ( my_kr != KERN_SUCCESS ) {
424 printf( "mach_port_insert_member failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
425 goto test_failed_exit;
426 }
427
428 my_kr = mach_port_insert_right( mach_task_self(), my_port, my_port, MACH_MSG_TYPE_MAKE_SEND );
429 if ( my_kr != KERN_SUCCESS ) {
430 printf( "mach_port_insert_right failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
431 goto test_failed_exit;
432 }
433
434 /* send some Mach messages */
435 for (my_index = 1;
436 my_index <= msg_count;
437 my_index++) {
438 my_kr = kmsg_send( my_port, my_index );
439 if ( my_kr != KERN_SUCCESS ) {
440 printf( "kmsg_send failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
441 goto test_failed_exit;
442 }
443 }
444
445 /* make sure the last message eventually gets processed */
446 pthread_mutex_lock(&my_mutex);
447 while (last_msg_seen == 0)
448 pthread_cond_wait(&my_cond, &my_mutex);
449 pthread_mutex_unlock(&my_mutex);
450
451 /* trigger the user 0 event, telling consumer threads to exit */
452 EV_SET( &my_keventv[0], 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0 );
453 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
454 if ( my_err == -1 ) {
455 printf( "kevent call to trigger user 0 failed with error %d - %s \n", errno, strerror( errno) );
456 goto test_failed_exit;
457 }
458
459 for (my_index = 0;
460 my_index < 3;
461 my_index++) {
6d2010ae 462 my_err = pthread_join( my_threadv[my_index], &my_pthread_join_status );
b0d623f7
A
463 if ( my_err != 0 ) {
464 printf( "pthread_join failed with error %d - %s \n", my_err, strerror(my_err) );
465 goto test_failed_exit;
466 }
6d2010ae 467 if ( my_pthread_join_status != 0 ) {
b0d623f7
A
468 goto test_failed_exit;
469 }
470 }
471
472 /* clear the user 0 event */
473 EV_SET( &my_keventv[0], 0, EVFILT_USER, EV_CLEAR, 0, 0, 0 );
474 my_err = kevent( my_kqueue, my_keventv, 1, NULL, 0, NULL );
475 if ( my_err == -1 ) {
476 printf( "kevent call to trigger user 0 failed with error %d - %s \n", errno, strerror( errno) );
477 goto test_failed_exit;
478 }
479
480 /* delibrately destroy my_pset while it's still registered for events */
481 my_kr = mach_port_mod_refs( mach_task_self(), my_pset, MACH_PORT_RIGHT_PORT_SET, -1 );
482 if ( my_kr != KERN_SUCCESS ) {
483 printf( "mach_port_mod_refs failed with error %d - %s \n", my_kr, mach_error_string(my_kr) );
484 goto test_failed_exit;
485 }
486
487 /* look for the event to trigger with a zero msg_size */
488 my_err = kevent( my_kqueue, NULL, 0, my_keventv, 1, NULL );
489 if ( my_err == -1 ) {
490 printf( "kevent call to get machport event failed with error %d - \"%s\" \n", errno, strerror( errno) );
491 goto test_failed_exit;
492 }
493 if ( my_err == 0 ) {
494 printf( "kevent call to get machport event did not return any when it should have \n" );
495 goto test_failed_exit;
496 }
497 if ( my_keventv[0].filter != EVFILT_MACHPORT ) {
498 printf( "kevent call to get machport event did not return EVFILT_MACHPORT \n" );
499 printf( "filter %i \n", my_keventv[0].filter );
500 goto test_failed_exit;
501 }
502 if ( my_keventv[0].data != 0 ) {
503 printf( "kevent call to get machport event did not return 0 msg_size \n" );
504 printf( "data %ld \n", (long int) my_keventv[0].data );
505 goto test_failed_exit;
506 }
b0d623f7
A
507
508 my_err = 0;
509 goto test_passed_exit;
510
511test_failed_exit:
512 my_err = -1;
513
514test_passed_exit:
515 if ( my_sockets[0] != -1 )
516 close( my_sockets[0] );
517 if ( my_sockets[1] != -1 )
518 close( my_sockets[1] );
519 if ( my_kqueue != -1 )
520 close( my_kqueue );
521 if ( my_kqueue64 != -1 )
522 close( my_kqueue );
523 if ( my_fd != -1 )
524 close( my_fd );
525 if ( my_pathp != NULL ) {
526 remove( my_pathp );
527 vm_deallocate(mach_task_self(), (vm_address_t)my_pathp, PATH_MAX);
528 }
529 return( my_err );
530}