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; 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; 42 my_kmsg->msgh_bits = 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, 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{ 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} 134 135/* ************************************************************************************************************** 136 * Test kevent, kqueue system calls. 137 * ************************************************************************************************************** 138 */ 139int kqueue_tests( void * the_argp ) 140{ 141 int my_err, my_status; 142 void *my_pthread_join_status; 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]; 151 struct kevent64_s my_kevent64; 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 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 } 274 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; 281 } 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 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; 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++) { 462 my_err = pthread_join( my_threadv[my_index], &my_pthread_join_status ); 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 } 467 if ( my_pthread_join_status != 0 ) { 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 } 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} 531