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