1/*
2 * xnu_quick_test - this tool will do a quick test of every (well, to be
3 * honest most) system calls we support in xnu.
4 *
5 * WARNING - this is not meant to be a full regression test of all the
6 * system calls.  The intent is to have a quick test of each system call that
7 * can be run very easily and quickly when a new kerenl is built.
8 *
9 * This tool is meant to grow as we find xnu problems that could have be
10 * caught before we submit to a build train.  So please add more tests and
11 * make the existing ones better.  Some of the original tests are nothing
12 * more than place holders and quite lame.  Just keep in mind that the tool
13 * should run as fast as possible.  If it gets too slow then most people
14 * will stop running it.
15 *
16 * LP64 testing tip - when adding or modifying tests, keep in mind the
17 * variants in the LP64 world.  If xnu gets passed a structure the varies in
18 * size between 32 and 64-bit processes, try to test that a field in the
19 * sructure contains valid data.  For example if we know foo structure
20 * looks like:
21 * struct foo {
22 *		int		an_int;
23 *		long	a_long;
24 *		int		another_int;
25 * }
26 * And if we know what another_int should contain then test for the known
27 * value since it's offset will vary depending on whether the calling process
28 * is 32 or 64 bits.
29 *
30 * NOTE - we have several workarounds and test exceptions for some
31 * outstanding bugs in xnu.  All the workarounds are marked with "todo" and
32 * some comments noting the radar number of the offending bug.  Do a seach
33 * for "todo" in the source files for this project to locate which tests have
34 * known failures.   And please tag any new exceptions you find with "todo"
35 * in the comment and the radar number of the bug.
36 */
37
38#include <errno.h>
39#include <fcntl.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <time.h>
44#include <grp.h>
45#include <unistd.h>
46#include <ctype.h>
47#include <sys/mount.h>
48#include <sys/param.h>
49#include <sys/select.h>
50#include <sys/stat.h>
51#include <sys/syslimits.h>
52#include <sys/types.h>
53#include <sys/ucred.h>
54#include <sys/uio.h>
55#include <mach-o/ldsyms.h>
56#include <mach-o/loader.h>
57#include <mach-o/arch.h>
58#include "tests.h"
59
60
61
62
63/* our table of tests to run  */
64struct test_entry   g_tests[] =
65{
66	{1, &syscall_test, NULL, "syscall"},
67	{1, &fork_wait4_exit_test, NULL, "fork, wait4, exit"},
68	{1, &read_write_test, NULL, "fsync, ftruncate, lseek, pread, pwrite, read, readv, truncate, write, writev"},
69	{1, &open_close_test, NULL, "close, fpathconf, fstat, open, pathconf"},
70	{1, &link_stat_unlink_test, NULL, "link, stat, unlink"},
71	{1, &chdir_fchdir_test, NULL, "chdir, fchdir"},
72	{1, &access_chmod_fchmod_test, NULL, "access, chmod, fchmod"},
73	{1, &chown_fchown_lchown_lstat_symlink_test, NULL, "chown, fchown, lchown, lstat, readlink, symlink"},
74	{1, &fs_stat_tests, NULL, "fstatfs, getfsstat, statfs, fstatfs64, getfsstat64, statfs64"},
75	{1, &statfs_32bit_inode_tests, NULL, "32-bit inode versions: fstatfs, getfsstat, statfs"},
76	{1, &getpid_getppid_pipe_test, NULL, "getpid, getppid, pipe"},
77	{1, &uid_tests, NULL, "getauid, gettid, getuid, geteuid, issetugid, setaudit_addr, seteuid, settid, settid_with_pid, setuid"},
78	{1, &mkdir_rmdir_umask_test, NULL, "mkdir, rmdir, umask"},
79	{1, &mknod_sync_test, NULL, "mknod, sync"},
80	{1, &socket2_tests, NULL, "fsync, getsockopt, poll, select, setsockopt, socketpair"},
81	{1, &socket_tests, NULL, "accept, bind, connect, getpeername, getsockname, listen, socket, recvmsg, sendmsg, sendto, sendfile"},
82	{1, &chflags_fchflags_test, NULL, "chflags, fchflags"},
83	{1, &execve_kill_vfork_test, NULL, "kill, vfork, execve, posix_spawn"},
84	{1, &groups_test, NULL, "getegid, getgid, getgroups, setegid, setgid, setgroups"},
85	{1, &dup_test, NULL, "dup, dup2, getdtablesize"},
86	{1, &getrusage_test, NULL, "getrusage"},
87	{1, &signals_test, NULL, "getitimer, setitimer, sigaction, sigpending, sigprocmask, sigsuspend, sigwait"},
88	{1, &acct_test, NULL, "acct"},
89	{1, &ioctl_test, NULL, "ioctl"},
90	{1, &chroot_test, NULL, "chroot"},
91	{1, &memory_tests, NULL, "madvise, mincore, minherit, mlock, mlock, mmap, mprotect, msync, munmap"},
92	{1, &process_group_test, NULL, "getpgrp, getpgid, getsid, setpgid, setpgrp, setsid"},
93	{1, &fcntl_test, NULL, "fcntl"},
94	{1, &getlogin_setlogin_test, NULL, "getlogin, setlogin"},
95	{1, &getpriority_setpriority_test, NULL, "getpriority, setpriority"},
96	{1, &time_tests, NULL, "futimes, gettimeofday, settimeofday, utimes"},
97	{1, &rename_test, NULL, "rename, stat"},
98	{1, &locking_test, NULL, "flock"},
99	{1, &mkfifo_test, NULL, "mkfifo, read, write"},
100	{1, &quotactl_test, NULL, "quotactl"},
101	{1, &limit_tests, NULL, "getrlimit, setrlimit"},
102	{1, &directory_tests, NULL, "getattrlist, getdirentriesattr, setattrlist"},
103	{1, &getdirentries_test, NULL, "getdirentries"},
104	{1, &exchangedata_test, NULL, "exchangedata"},
105	{1, &searchfs_test, NULL, "searchfs"},
106	{1, &sema2_tests, NULL, "sem_close, sem_open, sem_post, sem_trywait, sem_unlink, sem_wait"},
107	{1, &sema_tests, NULL, "semctl, semget, semop"},
108	{1, &bsd_shm_tests, NULL, "shm_open, shm_unlink"},
109	{1, &shm_tests, NULL, "shmat, shmctl, shmdt, shmget"},
110	{1, &xattr_tests, NULL, "fgetxattr, flistxattr, fremovexattr, fsetxattr, getxattr, listxattr, removexattr, setxattr"},
111	{1, &aio_tests, NULL, "aio_cancel, aio_error, aio_read, aio_return, aio_suspend, aio_write, fcntl, lio_listio"},
112	{1, &kqueue_tests, NULL, "kevent, kqueue"},
113	{1, &message_queue_tests, NULL, "msgctl, msgget, msgrcv, msgsnd"},
114	{1, &data_exec_tests, NULL, "data/stack execution"},
115	{1, &machvm_tests, NULL, "Mach VM calls"},
116	{1, &commpage_data_tests, NULL, "Commpage data"},
117#if defined(i386) || defined(__x86_64__)
118	{1, &atomic_fifo_queue_test, NULL, "OSAtomicFifoEnqueue, OSAtomicFifoDequeue"},
119#endif
120	{1, &sched_tests, NULL, "Scheduler tests"},
121	{1, &pipes_test, NULL, "Pipes tests"},
122	{1, &kaslr_test, NULL, "KASLR tests"},
123	{1, &getattrlistbulk_test, NULL, "getattrlistbulk"},
124	{1, &openat_close_test, NULL, "openat, fpathconf, fstatat, close"},
125	{1, &linkat_fstatat_unlinkat_test, NULL, "linkat, statat, unlinkat"},
126	{1, &faccessat_fchmodat_fchmod_test, NULL, "faccessat, fchmodat, fchmod"},
127	{1, &fchownat_fchown_symlinkat_test, NULL, "fchownat, symlinkat, readlinkat"},
128	{1, &mkdirat_unlinkat_umask_test, NULL, "mkdirat, unlinkat, umask"},
129	{1, &renameat_test, NULL, "renameat, fstatat"},
130	{1, &set_exception_ports_test, NULL, "thread_set_exception_ports, task_set_exception_ports, host_set_exception_ports"},
131	{0, NULL, NULL, "last one"}
132};
133
134static void create_target_directory( const char * the_targetp );
135static void list_all_tests( void );
136static void mark_tests_to_run( long my_start, long my_end );
137static int parse_tests_to_run( int argc, const char * argv[], int * indexp );
138static void usage( void );
139static int setgroups_if_single_user(void);
140static const char *current_arch( void );
141
142/* globals */
143long		g_max_failures = 0;
144int		g_skip_setuid_tests = 0;
145const char *	g_cmd_namep;
146char		g_target_path[ PATH_MAX ];
147int		g_is_single_user = 0;
148int		g_testbots_active = 0;
149
150int main( int argc, const char * argv[] )
151{
152	#pragma unused(argc)
153	#pragma unused(argv)
154	int				my_tests_count, i;
155	int				err;
156	int				my_failures = 0;
157	int				list_the_tests = 0;
158	const char *	my_targetp;
159	time_t			my_start_time, my_end_time;
160	struct stat		my_stat_buf;
161	char			my_buffer[64];
162	uid_t		sudo_uid = 0;
163	const char *	sudo_uid_env;
164	gid_t		sudo_gid;
165	const char *	sudo_gid_env;
166	sranddev( );				/* set up seed for our random name generator */
167	g_cmd_namep = argv[0];
168
169	/* make sure SIGCHLD is not ignored, so wait4 calls work */
170	signal(SIGCHLD, SIG_DFL);
171
172	/* NOTE - code in create_target_directory will append '/' if it is necessary */
173	my_targetp = getenv("TMPDIR");
174	if ( my_targetp == NULL )
175		my_targetp = "/tmp";
176
177	/* make sure we are running as root */
178	if ( ( getuid() != 0 ) || ( geteuid() != 0 ) ) {
179		printf( "Test must be run as root\n", g_cmd_namep );
180		exit( -1 );
181	}
182
183	sudo_uid_env = getenv("SUDO_UID");
184	if ( sudo_uid_env ) {
185		sudo_uid = strtol(sudo_uid_env, NULL, 10);
186	}
187
188	/* switch real uid to a non_root user, while keeping effective uid as root */
189	if ( sudo_uid != 0 ) {
190		setreuid( sudo_uid, 0 );
191	}
192	else {
193		/* Default to 501 if no sudo uid found */
194		setreuid( 501, 0 );
195	}
196
197	/* restore the gid if run through sudo */
198	sudo_gid_env = getenv("SUDO_GID");
199	if ( sudo_gid_env ) {
200		sudo_gid = strtol(sudo_gid_env, NULL, 10);
201	}
202
203	if ( getgid() == 0 ) {
204
205		if ( sudo_gid != 0 ) {
206			setgid( sudo_gid );
207		}
208		else {
209			/* Default to 20 if no sudo gid found */
210			setgid( 20 );
211		}
212	}
213
214	/* parse input parameters */
215	for ( i = 1; i < argc; i++ ) {
216		if ( strcmp( argv[i], "-u" ) == 0 ) {
217			usage( );
218		}
219		if ( strcmp( argv[i], "-t" ) == 0 ||
220			 strcmp( argv[i], "-target" ) == 0 ) {
221			if ( ++i >= argc ) {
222				printf( "invalid target parameter \n" );
223				usage( );
224			}
225			/* verify our target directory exists */
226			my_targetp = argv[i];
227			err = stat( my_targetp, &my_stat_buf );
228			if ( err != 0 || S_ISDIR(my_stat_buf.st_mode) == 0 ) {
229				printf( "invalid target path \n" );
230				if ( err != 0 ) {
231					printf( "stat call failed with error %d - \"%s\" \n", errno, strerror( errno) );
232				}
233				usage( );
234			}
235			continue;
236		}
237		if ( strcmp( argv[i], "-f" ) == 0 ||
238			 strcmp( argv[i], "-failures" ) == 0 ) {
239			if ( ++i >= argc ) {
240				printf( "invalid failures parameter \n" );
241				usage( );
242			}
243
244			/* get our max number of failures */
245			g_max_failures = strtol( argv[i], NULL, 10 );
246			continue;
247		}
248		if ( strcmp( argv[i], "-l" ) == 0 ||
249			 strcmp( argv[i], "-list" ) == 0 ) {
250			/* list all the tests this tool will do.
251			 */
252			list_the_tests = 1;
253			continue;
254		}
255		if ( strcmp( argv[i], "-r" ) == 0 ||
256			 strcmp( argv[i], "-run" ) == 0 ) {
257			if ( ++i >= argc ) {
258				printf( "invalid run tests parameter \n" );
259				usage( );
260			}
261
262			/* get which tests to run */
263			if ( parse_tests_to_run( argc, argv, &i ) != 0 ) {
264				printf( "invalid run tests parameter \n" );
265				usage( );
266			}
267			continue;
268		}
269		if ( strcmp( argv[i], "-s" ) == 0 ||
270			 strcmp( argv[i], "-skip" ) == 0 ) {
271			/* set that want to skip the setuid related tests - this is useful for debgugging since since I can't
272			 * get setuid tests to work while in gdb.
273			 */
274			g_skip_setuid_tests = 1;
275			continue;
276		}
277		if ( strcmp( argv[i], "-testbot" ) == 0 ) {
278			g_testbots_active = 1;
279			continue;
280		}
281		printf( "invalid argument \"%s\" \n", argv[i] );
282		usage( );
283	}
284
285	/* done parsing.
286	 */
287
288/* Check if we are running under testbots */
289#if RUN_UNDER_TESTBOTS
290g_testbots_active = 1;
291#endif
292	/* Code added to run xnu_quick_test under testbots */
293	if ( g_testbots_active == 1 ) {
294		printf("[TEST] xnu_quick_test \n");	/* Declare the beginning of test suite */
295	}
296
297	/* Populate groups list if we're in single user mode */
298	if (setgroups_if_single_user()) {
299		return 1;
300	}
301	if ( list_the_tests != 0 ) {
302		list_all_tests( );
303		return 0;
304	}
305
306	/* build a test target directory that we use as our path to create any test
307	 * files and directories.
308	 */
309	create_target_directory( my_targetp );
310	printf( "Will allow %ld failures before testing is aborted \n", g_max_failures );
311
312	my_start_time = time( NULL );
313	printf( "\nBegin testing - %s \n", ctime_r( &my_start_time, &my_buffer[0] ) );
314	printf( "Current architecture is %s\n", current_arch() );
315
316	/* Code added to run xnu_quick_test under testbots */
317
318	/* run each test that is marked to run in our table until we complete all of them or
319	 * hit the maximum number of failures.
320	 */
321	my_tests_count = (sizeof( g_tests ) / sizeof( g_tests[0] ));
322	for ( i = 0; i < (my_tests_count - 1); i++ ) {
323		int				my_err;
324		test_entryp		my_testp;
325
326		my_testp = &g_tests[i];
327		if ( my_testp->test_run_it == 0 || my_testp->test_routine == NULL )
328			continue;
329
330		if ( g_testbots_active == 1 ) {
331			printf("[BEGIN] %s \n", my_testp->test_infop);
332		}
333
334		printf( "test #%d - %s \n", (i + 1), my_testp->test_infop );
335		fflush(stdout);
336		my_err = my_testp->test_routine( my_testp->test_input );
337		if ( my_err != 0 ) {
338			printf("\t--> FAILED \n");
339			printf("SysCall %s failed", my_testp->test_infop);
340			printf("Result %d", my_err);
341			my_failures++;
342			if ( my_failures > g_max_failures ) {
343				printf( "\n Reached the maximum number of failures - Aborting xnu_quick_test. \n" );
344	                        /* Code added to run xnu_quick_test under testbots */
345        	                if ( g_testbots_active == 1 ) {
346					printf("[FAIL] %s \n", my_testp->test_infop);
347				}
348				goto exit_this_routine;
349			}
350			/* Code added to run xnu_quick_test under testbots */
351			if ( g_testbots_active == 1 ) {
352				printf("\n[FAIL] %s \n", my_testp->test_infop);
353			}
354			continue;
355		}
356		/* Code added to run xnu_quick_test under testbots */
357		if ( g_testbots_active == 1 ) {
358			printf("[PASS] %s \n", my_testp->test_infop);
359		}
360	}
361
362exit_this_routine:
363	my_end_time = time( NULL );
364	printf( "\nEnd testing - %s \n", ctime_r( &my_end_time, &my_buffer[0] ) );
365
366	/* clean up our test directory */
367	rmdir( &g_target_path[0] );
368
369	/* exit non zero if there are any failures */
370	return my_failures != 0;
371} /* main */
372
373
374/*
375 * parse_tests_to_run - parse the -run argument parameters.  the run argument tells us which tests the user
376 * wants to run.  we accept ranges (example  1 - 44) and runs of tests (example 2, 33, 34, 100) or a mix of
377 * both (example  1, 44 - 100, 200, 250)
378 */
379static int parse_tests_to_run( int argc, const char * argv[], int * indexp )
380{
381	int				my_tests_count, is_range = 0, i;
382	const char *	my_ptr;
383	char *			my_temp_ptr;
384	long			my_first_test_number, my_last_test_number;
385	char			my_buffer[ 128 ];
386
387	/* set tests table to not run any tests then go back and set the specific tests the caller asked for */
388	my_tests_count = (sizeof( g_tests ) / sizeof( g_tests[0] ));
389	for ( i = 0; i < (my_tests_count - 1); i++ ) {
390		g_tests[ i ].test_run_it = 0;
391	}
392
393	for ( i = *indexp; i < argc; i++ ) {
394		my_ptr = argv[ i ];
395		if ( strlen( my_ptr ) > 1 && *my_ptr == '-' && isalpha( *(my_ptr + 1) ) ) {
396			/* we have hit a new argument - need to make sure caller uses this argument on the next
397			 * pass through its parse loop (which will bump the index value so we want to be one less
398			 * than the actual index).
399			 */
400			*indexp = (i - 1);
401			return 0;
402		}
403
404		if ( strlen( my_ptr ) == 1 && *my_ptr == '-' ) {
405			/* we are dealing with a range of tests, for example:  33 - 44  */
406			is_range = 1;
407			continue;
408		}
409
410		if ( strlen( my_ptr ) > (sizeof( my_buffer ) - 1) ) {
411			printf( "-run argument has too many test parameters (max of %lu characters) \n", sizeof( my_buffer ) );
412			return -1;
413		}
414		/* get a local copy of the parameter string to work with - break range into two strings */
415		strcpy( &my_buffer[0], my_ptr );
416
417		my_temp_ptr = strrchr( &my_buffer[0], '-' );
418		if ( my_temp_ptr != NULL ) {
419			/* we are dealing with a range of tests with no white space, for example:  33-44  or  33- 44  */
420			my_temp_ptr = strrchr( &my_buffer[0], '-' );
421			*my_temp_ptr = 0x00;
422			my_first_test_number = strtol( &my_buffer[0], NULL, 10 );
423			if ( *(my_temp_ptr + 1) == 0x00 ) {
424				/* we hit the case where the range indicator is at the end of one string, for example:  33-  */
425				is_range = 1;
426				continue;
427			}
428			my_last_test_number = strtol( (my_temp_ptr + 1), NULL, 10 );
429			if ( my_first_test_number < 1 || my_first_test_number > my_last_test_number ) {
430				printf( "-run argument has invalid range parmeters \n" );
431				return -1;
432			}
433			mark_tests_to_run( my_first_test_number, my_last_test_number );
434			is_range = 0;
435			continue;
436		}
437
438		if ( is_range ) {
439			/* should be the second part of the test number range */
440			my_last_test_number = strtol( &my_buffer[0], NULL, 10 );
441			if ( my_first_test_number < 1 || my_first_test_number > my_last_test_number ) {
442				printf( "-run argument has invalid range parmeters \n" );
443				return -1;
444			}
445
446			mark_tests_to_run( my_first_test_number, my_last_test_number );
447			is_range = 0;
448			continue;
449		}
450		else {
451			my_first_test_number = strtol( &my_buffer[0], NULL, 10 );
452			if ( my_first_test_number < 1 ) {
453				printf( "-run argument has invalid test number parameter \n" );
454				return -1;
455			}
456			mark_tests_to_run( my_first_test_number, my_first_test_number );
457			continue;
458		}
459	}
460
461	*indexp = i;
462	return 0;
463
464} /* parse_tests_to_run */
465
466
467static void create_target_directory( const char * the_targetp )
468{
469	int			err;
470
471	if ( strlen( the_targetp ) > (sizeof(g_target_path) - 1) ) {
472		printf( "target path too long - \"%s\" \n", the_targetp );
473		exit( 1 );
474	}
475
476	for ( ;; ) {
477        int			my_rand;
478        char		my_name[64];
479
480        my_rand = rand( );
481        sprintf( &my_name[0], "xnu_quick_test-%d", my_rand );
482        if ( (strlen( &my_name[0] ) + strlen( the_targetp ) + 2) > PATH_MAX ) {
483			printf( "target path plus our test directory name is too long: \n" );
484			printf( "target path - \"%s\"  \n", the_targetp );
485			printf( "test directory name - \"%s\"  \n", &my_name[0] );
486			exit( 1 );
487		}
488
489        /* append generated directory name onto our path */
490		g_target_path[0] = 0x00;
491        strcat( &g_target_path[0], the_targetp );
492		if ( g_target_path[ (strlen(the_targetp) - 1) ] != '/' ) {
493			strcat( &g_target_path[0], "/" );
494		}
495        strcat( &g_target_path[0], &my_name[0] );
496
497		/* try to create the test directory */
498		err = mkdir( &g_target_path[0], (S_IRWXU | S_IRWXG | S_IROTH) );
499		if ( err == 0 ) {
500			break;
501		}
502		err = errno;
503		if ( EEXIST != err ) {
504			printf( "test directory creation failed - \"%s\" \n", &g_target_path[0] );
505			printf( "mkdir call failed with error %d - \"%s\" \n", errno, strerror( err) );
506			exit( 1 );
507		}
508	}
509	printf( "created test directory at \"%s\" \n", &g_target_path[0] );
510
511} /* create_target_directory */
512
513
514static void mark_tests_to_run( long my_start, long my_end )
515{
516	int			my_tests_count, i;
517
518	my_tests_count = (sizeof( g_tests ) / sizeof( g_tests[0] ));
519	my_end = (my_end < (my_tests_count - 1)) ? my_end : (my_tests_count - 1);
520	for ( i = (my_start - 1); i < my_end; i++ ) {
521		g_tests[ i ].test_run_it = 1;  /* run this test */
522	}
523	return;
524} /* mark_tests_to_run */
525
526
527static void usage( void )
528{
529	char *		my_ptr;
530
531	/* skip past full path and just show the tool name */
532	my_ptr = strrchr( g_cmd_namep, '/' );
533	if ( my_ptr != NULL ) {
534		my_ptr++;
535	}
536
537	printf( "\nUSAGE:  %s -target TARGET_PATH \n\n", (my_ptr != NULL) ? my_ptr : g_cmd_namep );
538	printf( "\t -f[ailures] MAX_FAILS_ALLOWED   # number of test cases that may fail before we give up.  defaults to 0  \n" );
539	printf( "\t -l[ist]                         # list all the tests this tool performs   \n" );
540	printf( "\t -r[un] 1, 3, 10 - 19            # run specific tests.  enter individual test numbers and/or range of numbers.  use -list to list tests.   \n" );
541	printf( "\t -s[kip]                         # skip setuid tests   \n" );
542	printf( "\t -t[arget] TARGET_PATH           # path to directory where tool will create test files.  defaults to \"/tmp/\"   \n" );
543	printf( "\t -testbot                        # output results in CoreOS TestBot compatible format  \n" );
544	printf( "\nexamples:  \n" );
545	printf( "--- Place all test files and directories at the root of volume \"test_vol\" --- \n" );
546	printf( "%s -t /Volumes/test_vol/ \n", (my_ptr != NULL) ? my_ptr : g_cmd_namep );
547	printf( " \n" );
548	printf( "--- Run the tool for tests 10 thru 15, test 18 and test 20 --- \n" );
549	printf( "%s -r 10-15, 18, 20 \n", (my_ptr != NULL) ? my_ptr : g_cmd_namep );
550	printf( " \n" );
551	exit( 1 );
552
553} /* usage */
554
555/* This is a private API between Libinfo, Libc, and the DirectoryService daemon.
556 * Since we are trying to determine if an external provider will back group
557 * lookups, we can use this, without relying on additional APIs or tools
558 * that might not work yet */
559extern int _ds_running(void);
560
561#define NUM_GROUPS	6
562static int
563setgroups_if_single_user(void)
564{
565	int i, retval = -1;
566	struct group *grp;
567	gid_t gids[NUM_GROUPS];
568
569	if (!_ds_running()) {
570		printf("In single-user mode.\n");
571		g_is_single_user = 1;
572
573		/* We skip 'nobody' and 'anyone' */
574		getgrent();
575		getgrent();
576		for (i = 0; i < NUM_GROUPS; i++) {
577			grp = getgrent();
578			if (!grp) {
579				break;
580			}
581
582			gids[i] = grp->gr_gid;
583		}
584
585		endgrent();
586
587		/* Only succeed if we find at least NUM_GROUPS */
588		if (i == NUM_GROUPS) {
589			retval = setgroups(NUM_GROUPS, gids);
590			if (retval == 0) {
591				getgroups(NUM_GROUPS, gids);
592				printf("After single-user hack, groups are: ");
593				for (i = 0; i < NUM_GROUPS; i++) {
594					printf("%d, ", gids[i]);
595				}
596				putchar('\n');
597			} else {
598				printf("Setgroups failed.\n");
599			}
600		} else {
601			printf("Couldn't get sufficient number of groups.\n");
602		}
603	} else {
604		printf("Not in single user mode.\n");
605		retval = 0;
606	}
607
608
609	return retval;
610}
611
612static const char *current_arch( void )
613{
614	cpu_type_t cputype = _mh_execute_header.cputype;
615	cpu_subtype_t cpusubtype = _mh_execute_header.cpusubtype;
616
617	const NXArchInfo *arch = NXGetArchInfoFromCpuType(cputype, cpusubtype);
618
619	if (arch) {
620		return arch->name;
621	} else {
622		return "<unknown>";
623	}
624}
625
626#undef printf	/* this makes the "-l" output easier to read */
627static void list_all_tests( void )
628{
629	int		i, my_tests_count;
630
631	my_tests_count = (sizeof( g_tests ) / sizeof( g_tests[0] ));
632	printf( "\nList of all tests this tool performs... \n" );
633
634	for ( i = 0; i < (my_tests_count - 1); i++ ) {
635		printf( " %d \t   %s \n", (i + 1), g_tests[ i ].test_infop );
636	}
637
638	return;
639} /* list_all_tests */
640