5 * Created by Jerry Cottingham on 3/25/05.
6 * Copyright 2005 Apple Computer Inc. All rights reserved.
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() */
19 extern char g_target_path
[ PATH_MAX
];
20 extern int g_skip_setuid_tests
;
23 int last_msg_seen
= 0;
24 pthread_cond_t my_cond
= PTHREAD_COND_INITIALIZER
;
25 pthread_mutex_t my_mutex
= PTHREAD_MUTEX_INITIALIZER
;
29 kmsg_send(mach_port_t remote_port
, int index
)
31 int msgh_id
= 1000 + index
;
33 mach_msg_header_t
* my_kmsg
= NULL
;
34 mach_msg_size_t size
= sizeof(mach_msg_header_t
) + sizeof(int)*index
;
36 my_kr
= vm_allocate( mach_task_self(),
37 (vm_address_t
*)&my_kmsg
,
39 VM_MAKE_TAG(VM_MEMORY_MACH_MSG
) | TRUE
);
40 if (my_kr
!= KERN_SUCCESS
)
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
,
53 MACH_MSG_TIMEOUT_NONE
,
55 vm_deallocate( mach_task_self(), (vm_address_t
)my_kmsg
, size
);
60 kmsg_recv(mach_port_t portset
, mach_port_t port
, int * msgh_id_return
)
63 mach_msg_header_t
* my_kmsg
= NULL
;
65 my_kr
= vm_allocate( mach_task_self(),
66 (vm_address_t
*)&my_kmsg
,
68 VM_MAKE_TAG(VM_MEMORY_MACH_MSG
) | TRUE
);
69 if (my_kr
!= KERN_SUCCESS
)
71 my_kr
= mach_msg( my_kmsg
,
72 MACH_RCV_MSG
| MACH_MSG_OPTION_NONE
,
74 PAGE_SIZE
, /* receive size */
76 MACH_MSG_TIMEOUT_NONE
,
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
);
86 kmsg_consumer_thread(void * arg
)
88 #if !TARGET_OS_EMBEDDED
89 int my_kqueue
= *(int *)arg
;
92 struct kevent my_keventv
[3];
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
) );
105 printf( "kevent call from consumer thread did not return any events when it should have \n" );
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" );
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
) );
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
) );
124 if (msgid
== 1000 + msg_count
) {
125 pthread_mutex_lock(&my_mutex
);
127 pthread_cond_signal(&my_cond
);
128 pthread_mutex_unlock(&my_mutex
);
134 printf( "\t--> Not supported on EMBEDDED TARGET\n" );
139 /* **************************************************************************************************************
140 * Test kevent, kqueue system calls.
141 * **************************************************************************************************************
143 int kqueue_tests( void * the_argp
)
145 int my_err
, my_status
;
146 void *my_pthread_join_status
;
148 int my_kqueue64
= -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
;
158 struct timespec my_timeout
;
159 char my_buffer
[ 16 ];
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
;
169 strcat( my_pathp
, &g_target_path
[0] );
170 strcat( my_pathp
, "/" );
172 /* create a test file */
173 my_err
= create_random_name( my_pathp
, 1 );
175 goto test_failed_exit
;
178 my_fd
= open( my_pathp
, O_RDWR
, 0 );
180 printf( "open call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
181 goto test_failed_exit
;
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
;
190 /* fork here and use pipe to communicate */
192 if ( my_pid
== -1 ) {
193 printf( "fork failed with errno %d - %s \n", errno
, strerror( errno
) );
194 goto test_failed_exit
;
196 else if ( my_pid
== 0 ) {
198 * child process - tell parent we are ready to go.
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
) );
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
) );
211 if ( my_buffer
[0] != 'g' ) {
212 printf( "read call on socket failed to get \"all done\" message \n" );
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
) );
223 my_err
= unlink( my_pathp
);
224 if ( my_err
== -1 ) {
225 printf( "unlink failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
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
) );
235 if ( my_buffer
[0] != 'e' ) {
236 printf( "read call on socket failed to get \"all done\" message \n" );
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
;
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
;
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
;
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 );
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
;
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
;
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
;
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
;
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
;
306 printf( "kevent call to get vnode events did not return any when it should have \n" );
307 goto test_failed_exit
;
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
;
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
;
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
;
330 printf( "kevent call to get proc exit event did not return any when it should have \n" );
331 goto test_failed_exit
;
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
;
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
;
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
;
353 printf( "kevent64 call to get proc exit event did not return any when it should have \n" );
354 goto test_failed_exit
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
418 pthread_t my_threadv
[3];
423 my_err
= pthread_create( &my_threadv
[my_index
], NULL
, kmsg_consumer_thread
, (void *)&my_kqueue
);
425 printf( "pthread_create failed with error %d - %s \n", my_err
, strerror(my_err
) );
426 goto test_failed_exit
;
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
;
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
;
443 /* send some Mach messages */
445 my_index
<= msg_count
;
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
;
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
);
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
;
471 my_err
= pthread_join( my_threadv
[my_index
], &my_pthread_join_status
);
473 printf( "pthread_join failed with error %d - %s \n", my_err
, strerror(my_err
) );
474 goto test_failed_exit
;
476 if ( my_pthread_join_status
!= 0 ) {
477 goto test_failed_exit
;
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
;
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
;
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
;
503 printf( "kevent call to get machport event did not return any when it should have \n" );
504 goto test_failed_exit
;
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
;
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
;
519 goto 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 )
531 if ( my_kqueue64
!= -1 )
535 if ( my_pathp
!= NULL
) {
537 vm_deallocate(mach_task_self(), (vm_address_t
)my_pathp
, PATH_MAX
);