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, "actl_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