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 = 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 59static kern_return_t 60kmsg_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 85static void * 86kmsg_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 */ 143int 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 521test_failed_exit: 522 my_err = -1; 523 524test_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} 541