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
)
43 MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND
, 0, 0, 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_voucher_port
= MACH_PORT_NULL
;
48 my_kmsg
->msgh_id
= msgh_id
;
49 my_kr
= mach_msg( my_kmsg
,
50 MACH_SEND_MSG
| MACH_MSG_OPTION_NONE
,
54 MACH_MSG_TIMEOUT_NONE
,
56 vm_deallocate( mach_task_self(), (vm_address_t
)my_kmsg
, size
);
61 kmsg_recv(mach_port_t portset
, mach_port_t port
, int * msgh_id_return
)
64 mach_msg_header_t
* my_kmsg
= NULL
;
66 my_kr
= vm_allocate( mach_task_self(),
67 (vm_address_t
*)&my_kmsg
,
69 VM_MAKE_TAG(VM_MEMORY_MACH_MSG
) | TRUE
);
70 if (my_kr
!= KERN_SUCCESS
)
72 my_kr
= mach_msg( my_kmsg
,
73 MACH_RCV_MSG
| MACH_MSG_OPTION_NONE
,
75 PAGE_SIZE
, /* receive size */
77 MACH_MSG_TIMEOUT_NONE
,
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
);
87 kmsg_consumer_thread(void * arg
)
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
);
135 /* **************************************************************************************************************
136 * Test kevent, kqueue system calls.
137 * **************************************************************************************************************
139 int kqueue_tests( void * the_argp
)
141 int my_err
, my_status
;
142 void *my_pthread_join_status
;
144 int my_kqueue64
= -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];
151 struct kevent64_s my_kevent64
;
152 struct timespec my_timeout
;
153 char my_buffer
[ 16 ];
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
;
163 strcat( my_pathp
, &g_target_path
[0] );
164 strcat( my_pathp
, "/" );
166 /* create a test file */
167 my_err
= create_random_name( my_pathp
, 1 );
169 goto test_failed_exit
;
172 my_fd
= open( my_pathp
, O_RDWR
, 0 );
174 printf( "open call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
175 goto test_failed_exit
;
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
;
184 /* fork here and use pipe to communicate */
186 if ( my_pid
== -1 ) {
187 printf( "fork failed with errno %d - %s \n", errno
, strerror( errno
) );
188 goto test_failed_exit
;
190 else if ( my_pid
== 0 ) {
192 * child process - tell parent we are ready to go.
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
) );
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
) );
205 if ( my_buffer
[0] != 'g' ) {
206 printf( "read call on socket failed to get \"all done\" message \n" );
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
) );
217 my_err
= unlink( my_pathp
);
218 if ( my_err
== -1 ) {
219 printf( "unlink failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
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
) );
229 if ( my_buffer
[0] != 'e' ) {
230 printf( "read call on socket failed to get \"all done\" message \n" );
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
;
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
;
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
;
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 );
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
;
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
;
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
;
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
;
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
;
298 printf( "kevent call to get vnode events did not return any when it should have \n" );
299 goto test_failed_exit
;
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
;
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
;
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
;
322 printf( "kevent call to get proc exit event did not return any when it should have \n" );
323 goto test_failed_exit
;
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
;
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
;
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
;
344 printf( "kevent64 call to get proc exit event did not return any when it should have \n" );
345 goto test_failed_exit
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
409 pthread_t my_threadv
[3];
414 my_err
= pthread_create( &my_threadv
[my_index
], NULL
, kmsg_consumer_thread
, (void *)&my_kqueue
);
416 printf( "pthread_create failed with error %d - %s \n", my_err
, strerror(my_err
) );
417 goto test_failed_exit
;
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
;
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
;
434 /* send some Mach messages */
436 my_index
<= msg_count
;
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
;
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
);
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
;
462 my_err
= pthread_join( my_threadv
[my_index
], &my_pthread_join_status
);
464 printf( "pthread_join failed with error %d - %s \n", my_err
, strerror(my_err
) );
465 goto test_failed_exit
;
467 if ( my_pthread_join_status
!= 0 ) {
468 goto test_failed_exit
;
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
;
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
;
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
;
494 printf( "kevent call to get machport event did not return any when it should have \n" );
495 goto test_failed_exit
;
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
;
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
;
509 goto test_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 )
521 if ( my_kqueue64
!= -1 )
525 if ( my_pathp
!= NULL
) {
527 vm_deallocate(mach_task_self(), (vm_address_t
)my_pathp
, PATH_MAX
);