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