#include "tests.h" #include extern int g_testbots_active; /* * create_random_name - creates a file with a random / unique name in the given directory. * when do_open is true we create a file else we generaate a name that does not exist in the * given directory (we do not create anything when do_open is 0). * WARNING - caller provides enough space in path buffer for longest possible name. * WARNING - assumes caller has appended a trailing '/' on the path passed to us. * RAND_MAX is currently 2147483647 (ten characters plus one for a slash) */ int create_random_name( char *the_pathp, int do_open ) { int i, my_err; int my_fd = -1; for ( i = 0; i < 1; i++ ) { int my_rand; char *myp; char my_name[32]; my_rand = rand( ); sprintf( &my_name[0], "%d", my_rand ); if ( (strlen( &my_name[0] ) + strlen( the_pathp ) + 2) > PATH_MAX ) { printf( "%s - path to test file greater than PATH_MAX \n", __FUNCTION__ ); return( -1 ); } // append generated file name onto our path myp = strrchr( the_pathp, '/' ); *(myp + 1) = 0x00; strcat( the_pathp, &my_name[0] ); if ( do_open ) { /* create a file with this name */ my_fd = open( the_pathp, (O_RDWR | O_CREAT | O_EXCL), (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) ); if ( my_fd == -1 ) { if ( errno != EEXIST ) { printf( "%s - open failed with errno %d - %s \n", __FUNCTION__, errno, strerror( errno ) ); return( -1 ); } // name already exists, try another i--; continue; } } else { /* make sure the name is unique */ struct stat my_sb; my_err = stat( the_pathp, &my_sb ); if ( my_err != 0 ) { if ( errno == ENOENT ) { break; } else { printf( "%s - open failed with errno %d - %s \n", __FUNCTION__, errno, strerror( errno ) ); return( -1 ); } } /* name already exists, try another */ i--; continue; } } if ( my_fd != -1 ) close( my_fd ); return( 0 ); } /* create_random_name */ /* * create_file_with_name - create a file in the given target directory using the given name. * If an existing file or directory is present use the value of remove_existing to determine if the * object is to be deleted. * returns 0 if file could be created, 1 if file exists, 2 if directory exists, else -1 * NOTE - will fail if a directory is present with the given name and it is not empty. */ int create_file_with_name( char *the_target_dirp, char *the_namep, int remove_existing ) { int create_test_file, my_err, my_result; int my_fd = -1; char * my_pathp = NULL; struct stat my_sb; kern_return_t my_kr; create_test_file = 0; my_kr = vm_allocate((vm_map_t) mach_task_self(), (vm_address_t*)&my_pathp, PATH_MAX, VM_FLAGS_ANYWHERE); if(my_kr != KERN_SUCCESS){ printf( "vm_allocate failed with error %d - \"%s\" \n", errno, strerror( errno) ); goto failure_exit; } strcpy( my_pathp, the_target_dirp ); strcat( my_pathp, the_namep ); /* make sure the name is unique */ my_result = 0; my_err = stat( my_pathp, &my_sb ); if ( my_err != 0 ) { create_test_file = 1; if ( errno != ENOENT ) { goto failure_exit; } } else { /* name already exists */ if ( S_ISDIR( my_sb.st_mode ) ) { my_result = 2; /* tell caller directory exists with target name */ if ( remove_existing ) { my_err = rmdir( my_pathp ); if ( my_err == -1 ) { printf( "rmdir failed with error %d - \"%s\" \n", errno, strerror( errno) ); goto failure_exit; } create_test_file = 1; } } else { my_result = 1; /* tell caller file exists with target name */ if ( remove_existing ) { my_err = unlink( my_pathp ); if ( my_err == -1 ) { printf( "unlink failed with error %d - \"%s\" \n", errno, strerror( errno) ); goto failure_exit; } create_test_file = 1; } } } if ( create_test_file ) { /* create a file with this name */ my_fd = open( my_pathp, (O_RDWR | O_CREAT | O_EXCL), (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) ); if ( my_fd == -1 ) { printf( "open failed with error %d - \"%s\" \n", errno, strerror( errno) ); goto failure_exit; } fcntl( my_fd, F_FULLFSYNC ); close( my_fd ); } goto routine_exit; failure_exit: my_result = -1; routine_exit: if ( my_pathp != NULL ) { if ( my_result == -1 && create_test_file ) { remove( my_pathp ); } vm_deallocate(mach_task_self(), (vm_address_t)my_pathp, PATH_MAX); } return( my_result ); } /* create_file_with_name */ /* * This function is needed by both xnu_quick_test proper and the execve() helper * program. It forks a child process and then exec()s an image on that child. * Path, argv, and envp are fed directly to the execve() call. * Parameter killwait decides how long to wait before killing the child. */ int do_execve_test(char * path, char * argv[], void * envp, int killwait) { int my_err = 0, my_status; pid_t my_pid, my_wait_pid; #if DEBUG printf("do_execve_test(path = %s)\n", path); printf("CWD= %s\n", getwd(NULL)); fflush(stdout); #endif /* vfork then execve sleep system command (which we will kill from the parent process) */ my_pid = vfork(); if (my_pid == -1) { printf( "vfork failed with errno %d - %s \n", errno, strerror( errno ) ); goto test_failed_exit; } if ( my_pid == 0 ) { /* * child process - use execve to start one of the customized helper * binaries, which just sleep for 120 seconds. Let our parent kill us. */ my_err = execve(path, argv, envp); if ( my_err != 0 ) { /* TODO: execve() on x86_64 inca returns weird error codes, see rdar://4655612 */ printf( "execve call failed with return value: %d, errno: %d - \"%s\"; path: %s \n", my_err, errno, strerror( errno), path ); fflush(stdout); exit(-2); } /* should never get here */ printf("Execve failed and it was not caught by our test\n"); return(-1); } /* * parent process - let's kill our sleeping child */ sleep(killwait); my_err = kill( my_pid, SIGKILL ); if ( my_err == -1 ) { printf( "kill call failed with error %d - \"%s\" \n", errno, strerror( errno) ); goto test_failed_exit; } /* wait for child to exit */ my_wait_pid = wait4( my_pid, &my_status, 0, NULL ); if ( my_wait_pid == -1 ) { printf( "wait4 failed with errno %d - %s \n", errno, strerror( errno ) ); goto test_failed_exit; } /* wait4 should return our child's pid when it exits */ if ( my_wait_pid != my_pid ) { printf( "wait4 did not return child pid - returned %d should be %d \n", my_wait_pid, my_pid ); goto test_failed_exit; } if (!(WIFSIGNALED( my_status ))) { printf( "child process was not signaled and should have been\n", my_status ); goto test_failed_exit; } if (WTERMSIG( my_status ) != SIGKILL) { printf( "wait4 returned wrong signal status - 0x%02X \n", my_status ); goto test_failed_exit; } my_err = 0; goto test_passed_exit; test_failed_exit: my_err = 1; test_passed_exit: return( my_err ); } /* do_execve_test */ /* * Helper function for posix_spawn test * arch: target architecture to spawn for */ int do_spawn_test(int arch, int shouldfail) { int my_err, my_pid, my_status; size_t my_size; posix_spawnattr_t attr; char * args[] = {"helpers/arch", NULL}; my_err = posix_spawnattr_init(&attr); if (my_err != 0) { printf("posix_spawnattr_init failed\n"); goto done; } /* set spawn to only succeed for arch 'arch' */ my_err = posix_spawnattr_setbinpref_np(&attr, 1, &arch, &my_size); if (my_err != 0 || my_size != 1) { printf("posix_spawnattr_setbinpref_np failed\n"); goto done; } /* spawn off child process */ my_err = posix_spawn(&my_pid, args[0], NULL, &attr, args, NULL); if (shouldfail) { if( my_err == 0) { printf("posix_spawn should have failed on arch %d\n", arch); goto done; } my_err = 0; } else { /* * child should exit with return code == arch; note that the * posix_spawn error numers are *returned*, NOT set in errno!!! */ if (my_err != 0) { printf("posix_spawn failed with errno %d - %s\n", my_err, strerror(my_err)); goto done; } my_err = wait4(my_pid, &my_status, 0, NULL); if (my_err == -1) { printf("wait4 failed with errno %d - %s\n", errno, strerror(errno)); goto done; } my_err = 0; if (WEXITSTATUS(my_status) != (arch & 0xff)) { printf("child exited with status %d (expected %d)\n", (WEXITSTATUS(my_status)), (arch & 0xff)); my_err = -1; goto done; } } done: return my_err; } /* * Uses sysctlbyname to determine the cpu type. Currently, XNU classifies G5 as a * 32-bit CPU, so this shouldn't be used to determine whether or not a CPU * is 64-bit. */ int get_architecture() { int rval = -1; size_t length = 0; int my_err, buf; char *errmsg = NULL; errmsg = "sysctlbyname() failed when getting hw.cputype"; if ((my_err = sysctlbyname("hw.cputype", NULL, &length, NULL, 0))) goto finished; /* get length of data */ if (length != sizeof(buf)) goto finished; if ((my_err = sysctlbyname("hw.cputype", &buf, &length, NULL, 0))) goto finished; /* copy data */ switch (buf) { case CPU_TYPE_X86: case CPU_TYPE_X86_64: rval = INTEL; break; case CPU_TYPE_ARM: rval = ARM; break; } finished: if (rval == -1 && errmsg) printf("%s", errmsg); return rval; } /* * Gets the bit'ed-ness of the current host. Returns either 32 or 64. * This get the hardware capability, but does not tell us whether this * binary is executing in 64 bit or 32 bit mode. Check sizeof long * or pointer to determine that. */ int get_bits() { int my_err, buf; size_t len = 0; int rval = 32; /* * On 32-bit systems the sysctls 64bitops and x86_64 don't * even exists, so if we don't find them then we assume * a 32-bit system. */ /* Check for PPC 64 */ if ((my_err = sysctlbyname("hw.optional.64bitops", NULL, &len, NULL, 0))) goto check64bit; /* Request size */ if (len > sizeof(buf)) goto check64bit; if ((my_err = sysctlbyname("hw.optional.64bitops", &buf, &len, NULL, 0))) goto check64bit; /* Copy value out from kernel */ if (buf == 1) rval = 64; goto finished; check64bit: #if defined(__i386__) || defined(__x86_64__) /* Check for x86_64 */ if ((my_err = sysctlbyname("hw.optional.x86_64", NULL, &len, NULL, 0))) goto finished; /* Request size */ if (len > sizeof(buf)) goto finished; if ((my_err = sysctlbyname("hw.optional.x86_64", &buf, &len, NULL, 0))) goto finished; /* Copy value out from kernel */ if (buf == 1) rval = 64; #else #error Unknown architecture. #endif finished: return rval; } /* * printf with a date and time stamp so that we can correlate printf's * with the log files of a system in case of test failure. * * NB: MY_PRINTF_DATE_FMT chosen to look like syslog to aid "grep". */ #define MY_PRINTF_DATE_FMT "%b %e %T" #undef printf /* was my_printf */ int my_printf(const char * __restrict fmt, ...) { char *bufp; char datebuf[256]; struct tm *timeptr; time_t result; int rv; va_list ap; /* if we are running under a TestBot, do a normal printf */ if (g_testbots_active) { va_start(ap, fmt); rv = vprintf(fmt, ap); va_end(ap); return rv; } /* Get the timestamp for this printf */ result = time(NULL); timeptr = localtime(&result); strftime(datebuf, sizeof(datebuf), MY_PRINTF_DATE_FMT, timeptr); /* do the printf of the requested data to a local buffer */ va_start(ap, fmt); rv = vasprintf(&bufp, fmt, ap); va_end(ap); /* * if we successfully got a local buffer, then we want to * print a timestamp plus what we would have printed before, * then free the allocated memory. */ if (rv != -1) { rv = printf("%s %s", datebuf, bufp); free(bufp); } return(rv); }