139beb93cSSam Leffler /* 2e28a4053SRui Paulo * OS specific functions for UNIX/POSIX systems 34bc52338SCy Schubert * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi> 439beb93cSSam Leffler * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 739beb93cSSam Leffler */ 839beb93cSSam Leffler 939beb93cSSam Leffler #include "includes.h" 1039beb93cSSam Leffler 11f05cddf9SRui Paulo #include <time.h> 125b9c547cSRui Paulo #include <sys/wait.h> 13f05cddf9SRui Paulo 14f05cddf9SRui Paulo #ifdef ANDROID 155b9c547cSRui Paulo #include <sys/capability.h> 165b9c547cSRui Paulo #include <sys/prctl.h> 17f05cddf9SRui Paulo #include <private/android_filesystem_config.h> 18f05cddf9SRui Paulo #endif /* ANDROID */ 19f05cddf9SRui Paulo 20325151a3SRui Paulo #ifdef __MACH__ 21325151a3SRui Paulo #include <CoreServices/CoreServices.h> 22325151a3SRui Paulo #include <mach/mach.h> 23325151a3SRui Paulo #include <mach/mach_time.h> 24325151a3SRui Paulo #endif /* __MACH__ */ 25325151a3SRui Paulo 2639beb93cSSam Leffler #include "os.h" 275b9c547cSRui Paulo #include "common.h" 2839beb93cSSam Leffler 29e28a4053SRui Paulo #ifdef WPA_TRACE 30e28a4053SRui Paulo 31e28a4053SRui Paulo #include "wpa_debug.h" 32e28a4053SRui Paulo #include "trace.h" 33f05cddf9SRui Paulo #include "list.h" 34e28a4053SRui Paulo 355b9c547cSRui Paulo static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list); 36e28a4053SRui Paulo 37e28a4053SRui Paulo #define ALLOC_MAGIC 0xa84ef1b2 38e28a4053SRui Paulo #define FREED_MAGIC 0x67fd487a 39e28a4053SRui Paulo 40e28a4053SRui Paulo struct os_alloc_trace { 41e28a4053SRui Paulo unsigned int magic; 42c1d255d3SCy Schubert struct dl_list list __attribute__((aligned(16))); 43e28a4053SRui Paulo size_t len; 44e28a4053SRui Paulo WPA_TRACE_INFO 45325151a3SRui Paulo } __attribute__((aligned(16))); 46e28a4053SRui Paulo 47e28a4053SRui Paulo #endif /* WPA_TRACE */ 48e28a4053SRui Paulo 49e28a4053SRui Paulo 5039beb93cSSam Leffler void os_sleep(os_time_t sec, os_time_t usec) 5139beb93cSSam Leffler { 52c1d255d3SCy Schubert #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) 53c1d255d3SCy Schubert const struct timespec req = { sec, usec * 1000 }; 54c1d255d3SCy Schubert 55c1d255d3SCy Schubert nanosleep(&req, NULL); 56c1d255d3SCy Schubert #else 5739beb93cSSam Leffler if (sec) 5839beb93cSSam Leffler sleep(sec); 5939beb93cSSam Leffler if (usec) 6039beb93cSSam Leffler usleep(usec); 61c1d255d3SCy Schubert #endif 6239beb93cSSam Leffler } 6339beb93cSSam Leffler 6439beb93cSSam Leffler 6539beb93cSSam Leffler int os_get_time(struct os_time *t) 6639beb93cSSam Leffler { 6739beb93cSSam Leffler int res; 6839beb93cSSam Leffler struct timeval tv; 6939beb93cSSam Leffler res = gettimeofday(&tv, NULL); 7039beb93cSSam Leffler t->sec = tv.tv_sec; 7139beb93cSSam Leffler t->usec = tv.tv_usec; 7239beb93cSSam Leffler return res; 7339beb93cSSam Leffler } 7439beb93cSSam Leffler 755b9c547cSRui Paulo int os_get_reltime(struct os_reltime *t) 765b9c547cSRui Paulo { 77325151a3SRui Paulo #ifndef __MACH__ 785b9c547cSRui Paulo #if defined(CLOCK_BOOTTIME) 795b9c547cSRui Paulo static clockid_t clock_id = CLOCK_BOOTTIME; 805b9c547cSRui Paulo #elif defined(CLOCK_MONOTONIC) 815b9c547cSRui Paulo static clockid_t clock_id = CLOCK_MONOTONIC; 825b9c547cSRui Paulo #else 835b9c547cSRui Paulo static clockid_t clock_id = CLOCK_REALTIME; 845b9c547cSRui Paulo #endif 855b9c547cSRui Paulo struct timespec ts; 865b9c547cSRui Paulo int res; 875b9c547cSRui Paulo 8885732ac8SCy Schubert if (TEST_FAIL()) 8985732ac8SCy Schubert return -1; 9085732ac8SCy Schubert 915b9c547cSRui Paulo while (1) { 925b9c547cSRui Paulo res = clock_gettime(clock_id, &ts); 935b9c547cSRui Paulo if (res == 0) { 945b9c547cSRui Paulo t->sec = ts.tv_sec; 955b9c547cSRui Paulo t->usec = ts.tv_nsec / 1000; 965b9c547cSRui Paulo return 0; 975b9c547cSRui Paulo } 985b9c547cSRui Paulo switch (clock_id) { 99cab9ccf3SWarner Losh #ifdef CLOCK_BOOTTIME 1005b9c547cSRui Paulo case CLOCK_BOOTTIME: 1015b9c547cSRui Paulo clock_id = CLOCK_MONOTONIC; 1025b9c547cSRui Paulo break; 1035b9c547cSRui Paulo #endif 104cab9ccf3SWarner Losh #ifdef CLOCK_MONOTONIC 105676041c4SWarner Losh /* 106676041c4SWarner Losh * FreeBSD has both BOOTTIME and MONOTONIC defined to the same value, since they 107676041c4SWarner Losh * mean the same thing. FreeBSD 14.1 and ealier don't, so need this case. 108676041c4SWarner Losh */ 109676041c4SWarner Losh #if !(defined(CLOCK_BOOTTIME) && CLOCK_BOOTTIME == CLOCK_MONOTONIC) 1105b9c547cSRui Paulo case CLOCK_MONOTONIC: 1115b9c547cSRui Paulo clock_id = CLOCK_REALTIME; 1125b9c547cSRui Paulo break; 1135b9c547cSRui Paulo #endif 114676041c4SWarner Losh #endif 1155b9c547cSRui Paulo case CLOCK_REALTIME: 1165b9c547cSRui Paulo return -1; 1175b9c547cSRui Paulo } 1185b9c547cSRui Paulo } 119325151a3SRui Paulo #else /* __MACH__ */ 120325151a3SRui Paulo uint64_t abstime, nano; 121325151a3SRui Paulo static mach_timebase_info_data_t info = { 0, 0 }; 122325151a3SRui Paulo 123325151a3SRui Paulo if (!info.denom) { 124325151a3SRui Paulo if (mach_timebase_info(&info) != KERN_SUCCESS) 125325151a3SRui Paulo return -1; 126325151a3SRui Paulo } 127325151a3SRui Paulo 128325151a3SRui Paulo abstime = mach_absolute_time(); 129325151a3SRui Paulo nano = (abstime * info.numer) / info.denom; 130325151a3SRui Paulo 131325151a3SRui Paulo t->sec = nano / NSEC_PER_SEC; 132325151a3SRui Paulo t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC; 133325151a3SRui Paulo 134325151a3SRui Paulo return 0; 135325151a3SRui Paulo #endif /* __MACH__ */ 1365b9c547cSRui Paulo } 1375b9c547cSRui Paulo 1385b9c547cSRui Paulo 13939beb93cSSam Leffler int os_mktime(int year, int month, int day, int hour, int min, int sec, 14039beb93cSSam Leffler os_time_t *t) 14139beb93cSSam Leffler { 14239beb93cSSam Leffler struct tm tm, *tm1; 14339beb93cSSam Leffler time_t t_local, t1, t2; 14439beb93cSSam Leffler os_time_t tz_offset; 14539beb93cSSam Leffler 14639beb93cSSam Leffler if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || 14739beb93cSSam Leffler hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || 14839beb93cSSam Leffler sec > 60) 14939beb93cSSam Leffler return -1; 15039beb93cSSam Leffler 15139beb93cSSam Leffler memset(&tm, 0, sizeof(tm)); 15239beb93cSSam Leffler tm.tm_year = year - 1900; 15339beb93cSSam Leffler tm.tm_mon = month - 1; 15439beb93cSSam Leffler tm.tm_mday = day; 15539beb93cSSam Leffler tm.tm_hour = hour; 15639beb93cSSam Leffler tm.tm_min = min; 15739beb93cSSam Leffler tm.tm_sec = sec; 15839beb93cSSam Leffler 15939beb93cSSam Leffler t_local = mktime(&tm); 16039beb93cSSam Leffler 16139beb93cSSam Leffler /* figure out offset to UTC */ 16239beb93cSSam Leffler tm1 = localtime(&t_local); 16339beb93cSSam Leffler if (tm1) { 16439beb93cSSam Leffler t1 = mktime(tm1); 16539beb93cSSam Leffler tm1 = gmtime(&t_local); 16639beb93cSSam Leffler if (tm1) { 16739beb93cSSam Leffler t2 = mktime(tm1); 16839beb93cSSam Leffler tz_offset = t2 - t1; 16939beb93cSSam Leffler } else 17039beb93cSSam Leffler tz_offset = 0; 17139beb93cSSam Leffler } else 17239beb93cSSam Leffler tz_offset = 0; 17339beb93cSSam Leffler 17439beb93cSSam Leffler *t = (os_time_t) t_local - tz_offset; 17539beb93cSSam Leffler return 0; 17639beb93cSSam Leffler } 17739beb93cSSam Leffler 17839beb93cSSam Leffler 179f05cddf9SRui Paulo int os_gmtime(os_time_t t, struct os_tm *tm) 180f05cddf9SRui Paulo { 181f05cddf9SRui Paulo struct tm *tm2; 182f05cddf9SRui Paulo time_t t2 = t; 183f05cddf9SRui Paulo 184f05cddf9SRui Paulo tm2 = gmtime(&t2); 185f05cddf9SRui Paulo if (tm2 == NULL) 186f05cddf9SRui Paulo return -1; 187f05cddf9SRui Paulo tm->sec = tm2->tm_sec; 188f05cddf9SRui Paulo tm->min = tm2->tm_min; 189f05cddf9SRui Paulo tm->hour = tm2->tm_hour; 190f05cddf9SRui Paulo tm->day = tm2->tm_mday; 191f05cddf9SRui Paulo tm->month = tm2->tm_mon + 1; 192f05cddf9SRui Paulo tm->year = tm2->tm_year + 1900; 193f05cddf9SRui Paulo return 0; 194f05cddf9SRui Paulo } 195f05cddf9SRui Paulo 196f05cddf9SRui Paulo 19739beb93cSSam Leffler #ifdef __APPLE__ 19839beb93cSSam Leffler #include <fcntl.h> 19939beb93cSSam Leffler static int os_daemon(int nochdir, int noclose) 20039beb93cSSam Leffler { 20139beb93cSSam Leffler int devnull; 20239beb93cSSam Leffler 20339beb93cSSam Leffler if (chdir("/") < 0) 20439beb93cSSam Leffler return -1; 20539beb93cSSam Leffler 20639beb93cSSam Leffler devnull = open("/dev/null", O_RDWR); 20739beb93cSSam Leffler if (devnull < 0) 20839beb93cSSam Leffler return -1; 20939beb93cSSam Leffler 21039beb93cSSam Leffler if (dup2(devnull, STDIN_FILENO) < 0) { 21139beb93cSSam Leffler close(devnull); 21239beb93cSSam Leffler return -1; 21339beb93cSSam Leffler } 21439beb93cSSam Leffler 21539beb93cSSam Leffler if (dup2(devnull, STDOUT_FILENO) < 0) { 21639beb93cSSam Leffler close(devnull); 21739beb93cSSam Leffler return -1; 21839beb93cSSam Leffler } 21939beb93cSSam Leffler 22039beb93cSSam Leffler if (dup2(devnull, STDERR_FILENO) < 0) { 22139beb93cSSam Leffler close(devnull); 22239beb93cSSam Leffler return -1; 22339beb93cSSam Leffler } 22439beb93cSSam Leffler 22539beb93cSSam Leffler return 0; 22639beb93cSSam Leffler } 22739beb93cSSam Leffler #else /* __APPLE__ */ 22839beb93cSSam Leffler #define os_daemon daemon 22939beb93cSSam Leffler #endif /* __APPLE__ */ 23039beb93cSSam Leffler 23139beb93cSSam Leffler 23239beb93cSSam Leffler int os_daemonize(const char *pid_file) 23339beb93cSSam Leffler { 234f05cddf9SRui Paulo #if defined(__uClinux__) || defined(__sun__) 23539beb93cSSam Leffler return -1; 236f05cddf9SRui Paulo #else /* defined(__uClinux__) || defined(__sun__) */ 23739beb93cSSam Leffler if (os_daemon(0, 0)) { 23839beb93cSSam Leffler perror("daemon"); 23939beb93cSSam Leffler return -1; 24039beb93cSSam Leffler } 24139beb93cSSam Leffler 24239beb93cSSam Leffler if (pid_file) { 24339beb93cSSam Leffler FILE *f = fopen(pid_file, "w"); 24439beb93cSSam Leffler if (f) { 24539beb93cSSam Leffler fprintf(f, "%u\n", getpid()); 24639beb93cSSam Leffler fclose(f); 24739beb93cSSam Leffler } 24839beb93cSSam Leffler } 24939beb93cSSam Leffler 25039beb93cSSam Leffler return -0; 251f05cddf9SRui Paulo #endif /* defined(__uClinux__) || defined(__sun__) */ 25239beb93cSSam Leffler } 25339beb93cSSam Leffler 25439beb93cSSam Leffler 25539beb93cSSam Leffler void os_daemonize_terminate(const char *pid_file) 25639beb93cSSam Leffler { 25739beb93cSSam Leffler if (pid_file) 25839beb93cSSam Leffler unlink(pid_file); 25939beb93cSSam Leffler } 26039beb93cSSam Leffler 26139beb93cSSam Leffler 26239beb93cSSam Leffler int os_get_random(unsigned char *buf, size_t len) 26339beb93cSSam Leffler { 2644bc52338SCy Schubert #ifdef TEST_FUZZ 2654bc52338SCy Schubert size_t i; 2664bc52338SCy Schubert 2674bc52338SCy Schubert for (i = 0; i < len; i++) 2684bc52338SCy Schubert buf[i] = i & 0xff; 2694bc52338SCy Schubert return 0; 2704bc52338SCy Schubert #else /* TEST_FUZZ */ 27139beb93cSSam Leffler FILE *f; 27239beb93cSSam Leffler size_t rc; 27339beb93cSSam Leffler 274325151a3SRui Paulo if (TEST_FAIL()) 275325151a3SRui Paulo return -1; 276325151a3SRui Paulo 27739beb93cSSam Leffler f = fopen("/dev/urandom", "rb"); 27839beb93cSSam Leffler if (f == NULL) { 27939beb93cSSam Leffler printf("Could not open /dev/urandom.\n"); 28039beb93cSSam Leffler return -1; 28139beb93cSSam Leffler } 28239beb93cSSam Leffler 28339beb93cSSam Leffler rc = fread(buf, 1, len, f); 28439beb93cSSam Leffler fclose(f); 28539beb93cSSam Leffler 28639beb93cSSam Leffler return rc != len ? -1 : 0; 2874bc52338SCy Schubert #endif /* TEST_FUZZ */ 28839beb93cSSam Leffler } 28939beb93cSSam Leffler 29039beb93cSSam Leffler 29139beb93cSSam Leffler unsigned long os_random(void) 29239beb93cSSam Leffler { 29339beb93cSSam Leffler return random(); 29439beb93cSSam Leffler } 29539beb93cSSam Leffler 29639beb93cSSam Leffler 29739beb93cSSam Leffler char * os_rel2abs_path(const char *rel_path) 29839beb93cSSam Leffler { 29939beb93cSSam Leffler char *buf = NULL, *cwd, *ret; 30039beb93cSSam Leffler size_t len = 128, cwd_len, rel_len, ret_len; 30139beb93cSSam Leffler int last_errno; 30239beb93cSSam Leffler 3035b9c547cSRui Paulo if (!rel_path) 3045b9c547cSRui Paulo return NULL; 3055b9c547cSRui Paulo 30639beb93cSSam Leffler if (rel_path[0] == '/') 307e28a4053SRui Paulo return os_strdup(rel_path); 30839beb93cSSam Leffler 30939beb93cSSam Leffler for (;;) { 310e28a4053SRui Paulo buf = os_malloc(len); 31139beb93cSSam Leffler if (buf == NULL) 31239beb93cSSam Leffler return NULL; 31339beb93cSSam Leffler cwd = getcwd(buf, len); 31439beb93cSSam Leffler if (cwd == NULL) { 31539beb93cSSam Leffler last_errno = errno; 316e28a4053SRui Paulo os_free(buf); 31739beb93cSSam Leffler if (last_errno != ERANGE) 31839beb93cSSam Leffler return NULL; 31939beb93cSSam Leffler len *= 2; 32039beb93cSSam Leffler if (len > 2000) 32139beb93cSSam Leffler return NULL; 32239beb93cSSam Leffler } else { 32339beb93cSSam Leffler buf[len - 1] = '\0'; 32439beb93cSSam Leffler break; 32539beb93cSSam Leffler } 32639beb93cSSam Leffler } 32739beb93cSSam Leffler 328e28a4053SRui Paulo cwd_len = os_strlen(cwd); 329e28a4053SRui Paulo rel_len = os_strlen(rel_path); 33039beb93cSSam Leffler ret_len = cwd_len + 1 + rel_len + 1; 331e28a4053SRui Paulo ret = os_malloc(ret_len); 33239beb93cSSam Leffler if (ret) { 333e28a4053SRui Paulo os_memcpy(ret, cwd, cwd_len); 33439beb93cSSam Leffler ret[cwd_len] = '/'; 335e28a4053SRui Paulo os_memcpy(ret + cwd_len + 1, rel_path, rel_len); 33639beb93cSSam Leffler ret[ret_len - 1] = '\0'; 33739beb93cSSam Leffler } 338e28a4053SRui Paulo os_free(buf); 33939beb93cSSam Leffler return ret; 34039beb93cSSam Leffler } 34139beb93cSSam Leffler 34239beb93cSSam Leffler 34339beb93cSSam Leffler int os_program_init(void) 34439beb93cSSam Leffler { 345c1d255d3SCy Schubert unsigned int seed; 346c1d255d3SCy Schubert 347f05cddf9SRui Paulo #ifdef ANDROID 348f05cddf9SRui Paulo /* 349f05cddf9SRui Paulo * We ignore errors here since errors are normal if we 350f05cddf9SRui Paulo * are already running as non-root. 351f05cddf9SRui Paulo */ 3525b9c547cSRui Paulo #ifdef ANDROID_SETGROUPS_OVERRIDE 3535b9c547cSRui Paulo gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE }; 3545b9c547cSRui Paulo #else /* ANDROID_SETGROUPS_OVERRIDE */ 355f05cddf9SRui Paulo gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE }; 3565b9c547cSRui Paulo #endif /* ANDROID_SETGROUPS_OVERRIDE */ 357f05cddf9SRui Paulo struct __user_cap_header_struct header; 358f05cddf9SRui Paulo struct __user_cap_data_struct cap; 359f05cddf9SRui Paulo 3605b9c547cSRui Paulo setgroups(ARRAY_SIZE(groups), groups); 361f05cddf9SRui Paulo 362f05cddf9SRui Paulo prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); 363f05cddf9SRui Paulo 364f05cddf9SRui Paulo setgid(AID_WIFI); 365f05cddf9SRui Paulo setuid(AID_WIFI); 366f05cddf9SRui Paulo 367f05cddf9SRui Paulo header.version = _LINUX_CAPABILITY_VERSION; 368f05cddf9SRui Paulo header.pid = 0; 369f05cddf9SRui Paulo cap.effective = cap.permitted = 370f05cddf9SRui Paulo (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW); 371f05cddf9SRui Paulo cap.inheritable = 0; 372f05cddf9SRui Paulo capset(&header, &cap); 373f05cddf9SRui Paulo #endif /* ANDROID */ 374f05cddf9SRui Paulo 375c1d255d3SCy Schubert if (os_get_random((unsigned char *) &seed, sizeof(seed)) == 0) 376c1d255d3SCy Schubert srandom(seed); 377c1d255d3SCy Schubert 37839beb93cSSam Leffler return 0; 37939beb93cSSam Leffler } 38039beb93cSSam Leffler 38139beb93cSSam Leffler 38239beb93cSSam Leffler void os_program_deinit(void) 38339beb93cSSam Leffler { 384e28a4053SRui Paulo #ifdef WPA_TRACE 385e28a4053SRui Paulo struct os_alloc_trace *a; 386e28a4053SRui Paulo unsigned long total = 0; 387e28a4053SRui Paulo dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) { 388e28a4053SRui Paulo total += a->len; 389e28a4053SRui Paulo if (a->magic != ALLOC_MAGIC) { 390e28a4053SRui Paulo wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x " 391e28a4053SRui Paulo "len %lu", 392e28a4053SRui Paulo a, a->magic, (unsigned long) a->len); 393e28a4053SRui Paulo continue; 394e28a4053SRui Paulo } 395e28a4053SRui Paulo wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu", 396e28a4053SRui Paulo a, (unsigned long) a->len); 397e28a4053SRui Paulo wpa_trace_dump("memleak", a); 398e28a4053SRui Paulo } 399e28a4053SRui Paulo if (total) 400e28a4053SRui Paulo wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes", 401e28a4053SRui Paulo (unsigned long) total); 402780fb4a2SCy Schubert wpa_trace_deinit(); 403e28a4053SRui Paulo #endif /* WPA_TRACE */ 40439beb93cSSam Leffler } 40539beb93cSSam Leffler 40639beb93cSSam Leffler 40739beb93cSSam Leffler int os_setenv(const char *name, const char *value, int overwrite) 40839beb93cSSam Leffler { 40939beb93cSSam Leffler return setenv(name, value, overwrite); 41039beb93cSSam Leffler } 41139beb93cSSam Leffler 41239beb93cSSam Leffler 41339beb93cSSam Leffler int os_unsetenv(const char *name) 41439beb93cSSam Leffler { 4153157ba21SRui Paulo #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \ 4163157ba21SRui Paulo defined(__OpenBSD__) 41739beb93cSSam Leffler unsetenv(name); 41839beb93cSSam Leffler return 0; 41939beb93cSSam Leffler #else 42039beb93cSSam Leffler return unsetenv(name); 42139beb93cSSam Leffler #endif 42239beb93cSSam Leffler } 42339beb93cSSam Leffler 42439beb93cSSam Leffler 42539beb93cSSam Leffler char * os_readfile(const char *name, size_t *len) 42639beb93cSSam Leffler { 42739beb93cSSam Leffler FILE *f; 42839beb93cSSam Leffler char *buf; 429f05cddf9SRui Paulo long pos; 43039beb93cSSam Leffler 43139beb93cSSam Leffler f = fopen(name, "rb"); 43239beb93cSSam Leffler if (f == NULL) 43339beb93cSSam Leffler return NULL; 43439beb93cSSam Leffler 435f05cddf9SRui Paulo if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) { 436f05cddf9SRui Paulo fclose(f); 437f05cddf9SRui Paulo return NULL; 438f05cddf9SRui Paulo } 439f05cddf9SRui Paulo *len = pos; 440f05cddf9SRui Paulo if (fseek(f, 0, SEEK_SET) < 0) { 441f05cddf9SRui Paulo fclose(f); 442f05cddf9SRui Paulo return NULL; 443f05cddf9SRui Paulo } 44439beb93cSSam Leffler 445e28a4053SRui Paulo buf = os_malloc(*len); 44639beb93cSSam Leffler if (buf == NULL) { 44739beb93cSSam Leffler fclose(f); 44839beb93cSSam Leffler return NULL; 44939beb93cSSam Leffler } 45039beb93cSSam Leffler 45139beb93cSSam Leffler if (fread(buf, 1, *len, f) != *len) { 45239beb93cSSam Leffler fclose(f); 453e28a4053SRui Paulo os_free(buf); 45439beb93cSSam Leffler return NULL; 45539beb93cSSam Leffler } 45639beb93cSSam Leffler 45739beb93cSSam Leffler fclose(f); 45839beb93cSSam Leffler 45939beb93cSSam Leffler return buf; 46039beb93cSSam Leffler } 46139beb93cSSam Leffler 46239beb93cSSam Leffler 4635b9c547cSRui Paulo int os_file_exists(const char *fname) 4645b9c547cSRui Paulo { 465780fb4a2SCy Schubert return access(fname, F_OK) == 0; 4665b9c547cSRui Paulo } 4675b9c547cSRui Paulo 4685b9c547cSRui Paulo 469c1d255d3SCy Schubert #if !defined __DragonFly__ 470325151a3SRui Paulo int os_fdatasync(FILE *stream) 471325151a3SRui Paulo { 472325151a3SRui Paulo if (!fflush(stream)) { 473c1d255d3SCy Schubert #if defined __FreeBSD__ || defined __linux__ 474325151a3SRui Paulo return fdatasync(fileno(stream)); 475c1d255d3SCy Schubert #else /* !__linux__ && !__FreeBSD__ */ 476325151a3SRui Paulo #ifdef F_FULLFSYNC 477325151a3SRui Paulo /* OS X does not implement fdatasync(). */ 478325151a3SRui Paulo return fcntl(fileno(stream), F_FULLFSYNC); 479325151a3SRui Paulo #else /* F_FULLFSYNC */ 480325151a3SRui Paulo return fsync(fileno(stream)); 481325151a3SRui Paulo #endif /* F_FULLFSYNC */ 482325151a3SRui Paulo #endif /* __linux__ */ 483325151a3SRui Paulo } 484325151a3SRui Paulo 485325151a3SRui Paulo return -1; 486325151a3SRui Paulo } 487c1d255d3SCy Schubert #endif 488325151a3SRui Paulo 489325151a3SRui Paulo 490e28a4053SRui Paulo #ifndef WPA_TRACE 49139beb93cSSam Leffler void * os_zalloc(size_t size) 49239beb93cSSam Leffler { 49339beb93cSSam Leffler return calloc(1, size); 49439beb93cSSam Leffler } 495e28a4053SRui Paulo #endif /* WPA_TRACE */ 49639beb93cSSam Leffler 49739beb93cSSam Leffler 49839beb93cSSam Leffler size_t os_strlcpy(char *dest, const char *src, size_t siz) 49939beb93cSSam Leffler { 50039beb93cSSam Leffler const char *s = src; 50139beb93cSSam Leffler size_t left = siz; 50239beb93cSSam Leffler 50339beb93cSSam Leffler if (left) { 50439beb93cSSam Leffler /* Copy string up to the maximum size of the dest buffer */ 50539beb93cSSam Leffler while (--left != 0) { 50639beb93cSSam Leffler if ((*dest++ = *s++) == '\0') 50739beb93cSSam Leffler break; 50839beb93cSSam Leffler } 50939beb93cSSam Leffler } 51039beb93cSSam Leffler 51139beb93cSSam Leffler if (left == 0) { 51239beb93cSSam Leffler /* Not enough room for the string; force NUL-termination */ 51339beb93cSSam Leffler if (siz != 0) 51439beb93cSSam Leffler *dest = '\0'; 51539beb93cSSam Leffler while (*s++) 51639beb93cSSam Leffler ; /* determine total src string length */ 51739beb93cSSam Leffler } 51839beb93cSSam Leffler 51939beb93cSSam Leffler return s - src - 1; 52039beb93cSSam Leffler } 521e28a4053SRui Paulo 522e28a4053SRui Paulo 5235b9c547cSRui Paulo int os_memcmp_const(const void *a, const void *b, size_t len) 5245b9c547cSRui Paulo { 5255b9c547cSRui Paulo const u8 *aa = a; 5265b9c547cSRui Paulo const u8 *bb = b; 5275b9c547cSRui Paulo size_t i; 5285b9c547cSRui Paulo u8 res; 5295b9c547cSRui Paulo 5305b9c547cSRui Paulo for (res = 0, i = 0; i < len; i++) 5315b9c547cSRui Paulo res |= aa[i] ^ bb[i]; 5325b9c547cSRui Paulo 5335b9c547cSRui Paulo return res; 5345b9c547cSRui Paulo } 5355b9c547cSRui Paulo 5365b9c547cSRui Paulo 53785732ac8SCy Schubert void * os_memdup(const void *src, size_t len) 53885732ac8SCy Schubert { 53985732ac8SCy Schubert void *r = os_malloc(len); 54085732ac8SCy Schubert 5414bc52338SCy Schubert if (r && src) 54285732ac8SCy Schubert os_memcpy(r, src, len); 54385732ac8SCy Schubert return r; 54485732ac8SCy Schubert } 54585732ac8SCy Schubert 54685732ac8SCy Schubert 547e28a4053SRui Paulo #ifdef WPA_TRACE 548e28a4053SRui Paulo 5495b9c547cSRui Paulo #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) 550*a90b9d01SCy Schubert struct wpa_trace_test_fail { 551*a90b9d01SCy Schubert unsigned int fail_after; 552*a90b9d01SCy Schubert char pattern[256]; 553*a90b9d01SCy Schubert } wpa_trace_test_fail[5][2]; 5545b9c547cSRui Paulo 555*a90b9d01SCy Schubert int testing_test_fail(const char *tag, bool is_alloc) 5565b9c547cSRui Paulo { 557*a90b9d01SCy Schubert const char *ignore_list[] = { 558*a90b9d01SCy Schubert "os_malloc", "os_zalloc", "os_calloc", "os_realloc", 559*a90b9d01SCy Schubert "os_realloc_array", "os_strdup", "os_memdup" 560*a90b9d01SCy Schubert }; 5615b9c547cSRui Paulo const char *func[WPA_TRACE_LEN]; 562*a90b9d01SCy Schubert size_t i, j, res, len, idx; 5635b9c547cSRui Paulo char *pos, *next; 5645b9c547cSRui Paulo int match; 5655b9c547cSRui Paulo 566*a90b9d01SCy Schubert is_alloc = !!is_alloc; 567*a90b9d01SCy Schubert 568*a90b9d01SCy Schubert for (idx = 0; idx < ARRAY_SIZE(wpa_trace_test_fail[is_alloc]); idx++) { 569*a90b9d01SCy Schubert if (wpa_trace_test_fail[is_alloc][idx].fail_after != 0) 570*a90b9d01SCy Schubert break; 571*a90b9d01SCy Schubert } 572*a90b9d01SCy Schubert if (idx >= ARRAY_SIZE(wpa_trace_test_fail[is_alloc])) 5735b9c547cSRui Paulo return 0; 5745b9c547cSRui Paulo 5755b9c547cSRui Paulo res = wpa_trace_calling_func(func, WPA_TRACE_LEN); 5765b9c547cSRui Paulo i = 0; 577*a90b9d01SCy Schubert 578*a90b9d01SCy Schubert if (is_alloc) { 579*a90b9d01SCy Schubert /* Skip our own stack frame */ 58085732ac8SCy Schubert i++; 5815b9c547cSRui Paulo 582*a90b9d01SCy Schubert /* Skip allocation helpers */ 583*a90b9d01SCy Schubert for (j = 0; j < ARRAY_SIZE(ignore_list) && i < res; j++) { 584*a90b9d01SCy Schubert if (os_strcmp(func[i], ignore_list[j]) == 0) 585*a90b9d01SCy Schubert i++; 586*a90b9d01SCy Schubert } 587*a90b9d01SCy Schubert } else { 588*a90b9d01SCy Schubert /* Not allocation, we might have a tag, if so, replace our 589*a90b9d01SCy Schubert * own stack frame with the tag, otherwise skip it. 590*a90b9d01SCy Schubert */ 591*a90b9d01SCy Schubert if (tag) 592*a90b9d01SCy Schubert func[0] = tag; 593*a90b9d01SCy Schubert else 594*a90b9d01SCy Schubert i++; 595*a90b9d01SCy Schubert } 596*a90b9d01SCy Schubert 597*a90b9d01SCy Schubert pos = wpa_trace_test_fail[is_alloc][idx].pattern; 598*a90b9d01SCy Schubert 599*a90b9d01SCy Schubert /* The prefixes mean: 600*a90b9d01SCy Schubert * - '=': The function needs to be next in the backtrace 601*a90b9d01SCy Schubert * - '?': The function is optionally present in the backtrace 602*a90b9d01SCy Schubert */ 6035b9c547cSRui Paulo 6045b9c547cSRui Paulo match = 0; 6055b9c547cSRui Paulo while (i < res) { 6065b9c547cSRui Paulo int allow_skip = 1; 6075b9c547cSRui Paulo int maybe = 0; 608*a90b9d01SCy Schubert bool prefix = false; 6095b9c547cSRui Paulo 6105b9c547cSRui Paulo if (*pos == '=') { 6115b9c547cSRui Paulo allow_skip = 0; 6125b9c547cSRui Paulo pos++; 6135b9c547cSRui Paulo } else if (*pos == '?') { 6145b9c547cSRui Paulo maybe = 1; 6155b9c547cSRui Paulo pos++; 6165b9c547cSRui Paulo } 6175b9c547cSRui Paulo next = os_strchr(pos, ';'); 6185b9c547cSRui Paulo if (next) 6195b9c547cSRui Paulo len = next - pos; 6205b9c547cSRui Paulo else 6215b9c547cSRui Paulo len = os_strlen(pos); 622*a90b9d01SCy Schubert if (len >= 1 && pos[len - 1] == '*') { 623*a90b9d01SCy Schubert prefix = true; 624*a90b9d01SCy Schubert len -= 1; 625*a90b9d01SCy Schubert } 626*a90b9d01SCy Schubert if (os_strncmp(pos, func[i], len) != 0 || 627*a90b9d01SCy Schubert (!prefix && func[i][len] != '\0')) { 6285b9c547cSRui Paulo if (maybe && next) { 6295b9c547cSRui Paulo pos = next + 1; 6305b9c547cSRui Paulo continue; 6315b9c547cSRui Paulo } 6325b9c547cSRui Paulo if (allow_skip) { 6335b9c547cSRui Paulo i++; 6345b9c547cSRui Paulo continue; 6355b9c547cSRui Paulo } 6365b9c547cSRui Paulo return 0; 6375b9c547cSRui Paulo } 6385b9c547cSRui Paulo if (!next) { 6395b9c547cSRui Paulo match = 1; 6405b9c547cSRui Paulo break; 6415b9c547cSRui Paulo } 6425b9c547cSRui Paulo pos = next + 1; 6435b9c547cSRui Paulo i++; 6445b9c547cSRui Paulo } 6455b9c547cSRui Paulo if (!match) 6465b9c547cSRui Paulo return 0; 6475b9c547cSRui Paulo 648*a90b9d01SCy Schubert wpa_trace_test_fail[is_alloc][idx].fail_after--; 649*a90b9d01SCy Schubert if (wpa_trace_test_fail[is_alloc][idx].fail_after == 0) { 650325151a3SRui Paulo wpa_printf(MSG_INFO, "TESTING: fail at %s", 651*a90b9d01SCy Schubert wpa_trace_test_fail[is_alloc][idx].pattern); 652325151a3SRui Paulo for (i = 0; i < res; i++) 653325151a3SRui Paulo wpa_printf(MSG_INFO, "backtrace[%d] = %s", 654325151a3SRui Paulo (int) i, func[i]); 655325151a3SRui Paulo return 1; 656325151a3SRui Paulo } 657325151a3SRui Paulo 658325151a3SRui Paulo return 0; 659325151a3SRui Paulo } 660325151a3SRui Paulo 6615b9c547cSRui Paulo 662*a90b9d01SCy Schubert int testing_set_fail_pattern(bool is_alloc, char *patterns) 663*a90b9d01SCy Schubert { 664*a90b9d01SCy Schubert #ifdef WPA_TRACE_BFD 665*a90b9d01SCy Schubert char *token, *context = NULL; 666*a90b9d01SCy Schubert size_t idx; 667*a90b9d01SCy Schubert 668*a90b9d01SCy Schubert is_alloc = !!is_alloc; 669*a90b9d01SCy Schubert 670*a90b9d01SCy Schubert os_memset(wpa_trace_test_fail[is_alloc], 0, 671*a90b9d01SCy Schubert sizeof(wpa_trace_test_fail[is_alloc])); 672*a90b9d01SCy Schubert 673*a90b9d01SCy Schubert idx = 0; 674*a90b9d01SCy Schubert while ((token = str_token(patterns, " \n\r\t", &context)) && 675*a90b9d01SCy Schubert idx < ARRAY_SIZE(wpa_trace_test_fail[is_alloc])) { 676*a90b9d01SCy Schubert wpa_trace_test_fail[is_alloc][idx].fail_after = atoi(token); 677*a90b9d01SCy Schubert token = os_strchr(token, ':'); 678*a90b9d01SCy Schubert if (!token) { 679*a90b9d01SCy Schubert os_memset(wpa_trace_test_fail[is_alloc], 0, 680*a90b9d01SCy Schubert sizeof(wpa_trace_test_fail[is_alloc])); 681*a90b9d01SCy Schubert return -1; 682*a90b9d01SCy Schubert } 683*a90b9d01SCy Schubert 684*a90b9d01SCy Schubert os_strlcpy(wpa_trace_test_fail[is_alloc][idx].pattern, 685*a90b9d01SCy Schubert token + 1, 686*a90b9d01SCy Schubert sizeof(wpa_trace_test_fail[is_alloc][0].pattern)); 687*a90b9d01SCy Schubert idx++; 688*a90b9d01SCy Schubert } 689*a90b9d01SCy Schubert 690*a90b9d01SCy Schubert return 0; 691*a90b9d01SCy Schubert #else /* WPA_TRACE_BFD */ 692*a90b9d01SCy Schubert return -1; 693*a90b9d01SCy Schubert #endif /* WPA_TRACE_BFD */ 694*a90b9d01SCy Schubert } 695*a90b9d01SCy Schubert 696*a90b9d01SCy Schubert 697*a90b9d01SCy Schubert int testing_get_fail_pattern(bool is_alloc, char *buf, size_t buflen) 698*a90b9d01SCy Schubert { 699*a90b9d01SCy Schubert #ifdef WPA_TRACE_BFD 700*a90b9d01SCy Schubert size_t idx, ret; 701*a90b9d01SCy Schubert char *pos = buf; 702*a90b9d01SCy Schubert char *end = buf + buflen; 703*a90b9d01SCy Schubert 704*a90b9d01SCy Schubert is_alloc = !!is_alloc; 705*a90b9d01SCy Schubert 706*a90b9d01SCy Schubert for (idx = 0; idx < ARRAY_SIZE(wpa_trace_test_fail[is_alloc]); idx++) { 707*a90b9d01SCy Schubert if (wpa_trace_test_fail[is_alloc][idx].pattern[0] == '\0') 708*a90b9d01SCy Schubert break; 709*a90b9d01SCy Schubert 710*a90b9d01SCy Schubert ret = os_snprintf(pos, end - pos, "%s%u:%s", 711*a90b9d01SCy Schubert pos == buf ? "" : " ", 712*a90b9d01SCy Schubert wpa_trace_test_fail[is_alloc][idx].fail_after, 713*a90b9d01SCy Schubert wpa_trace_test_fail[is_alloc][idx].pattern); 714*a90b9d01SCy Schubert if (os_snprintf_error(end - pos, ret)) 715*a90b9d01SCy Schubert break; 716*a90b9d01SCy Schubert pos += ret; 717*a90b9d01SCy Schubert } 718*a90b9d01SCy Schubert 719*a90b9d01SCy Schubert return pos - buf; 720*a90b9d01SCy Schubert #else /* WPA_TRACE_BFD */ 721*a90b9d01SCy Schubert return -1; 722*a90b9d01SCy Schubert #endif /* WPA_TRACE_BFD */ 723*a90b9d01SCy Schubert } 724*a90b9d01SCy Schubert 725*a90b9d01SCy Schubert #else /* defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) */ 726*a90b9d01SCy Schubert 727*a90b9d01SCy Schubert static inline int testing_test_fail(const char *tag, bool is_alloc) 7285b9c547cSRui Paulo { 7295b9c547cSRui Paulo return 0; 7305b9c547cSRui Paulo } 731*a90b9d01SCy Schubert 7325b9c547cSRui Paulo #endif 7335b9c547cSRui Paulo 734e28a4053SRui Paulo void * os_malloc(size_t size) 735e28a4053SRui Paulo { 736e28a4053SRui Paulo struct os_alloc_trace *a; 7375b9c547cSRui Paulo 738*a90b9d01SCy Schubert if (testing_test_fail(NULL, true)) 7395b9c547cSRui Paulo return NULL; 7405b9c547cSRui Paulo 741e28a4053SRui Paulo a = malloc(sizeof(*a) + size); 742e28a4053SRui Paulo if (a == NULL) 743e28a4053SRui Paulo return NULL; 744e28a4053SRui Paulo a->magic = ALLOC_MAGIC; 745e28a4053SRui Paulo dl_list_add(&alloc_list, &a->list); 746e28a4053SRui Paulo a->len = size; 747e28a4053SRui Paulo wpa_trace_record(a); 748e28a4053SRui Paulo return a + 1; 749e28a4053SRui Paulo } 750e28a4053SRui Paulo 751e28a4053SRui Paulo 752e28a4053SRui Paulo void * os_realloc(void *ptr, size_t size) 753e28a4053SRui Paulo { 754e28a4053SRui Paulo struct os_alloc_trace *a; 755e28a4053SRui Paulo size_t copy_len; 756e28a4053SRui Paulo void *n; 757e28a4053SRui Paulo 758e28a4053SRui Paulo if (ptr == NULL) 759e28a4053SRui Paulo return os_malloc(size); 760e28a4053SRui Paulo 761e28a4053SRui Paulo a = (struct os_alloc_trace *) ptr - 1; 762e28a4053SRui Paulo if (a->magic != ALLOC_MAGIC) { 763e28a4053SRui Paulo wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s", 764e28a4053SRui Paulo a, a->magic, 765e28a4053SRui Paulo a->magic == FREED_MAGIC ? " (already freed)" : ""); 766e28a4053SRui Paulo wpa_trace_show("Invalid os_realloc() call"); 767e28a4053SRui Paulo abort(); 768e28a4053SRui Paulo } 769e28a4053SRui Paulo n = os_malloc(size); 770e28a4053SRui Paulo if (n == NULL) 771e28a4053SRui Paulo return NULL; 772e28a4053SRui Paulo copy_len = a->len; 773e28a4053SRui Paulo if (copy_len > size) 774e28a4053SRui Paulo copy_len = size; 775e28a4053SRui Paulo os_memcpy(n, a + 1, copy_len); 776e28a4053SRui Paulo os_free(ptr); 777e28a4053SRui Paulo return n; 778e28a4053SRui Paulo } 779e28a4053SRui Paulo 780e28a4053SRui Paulo 781e28a4053SRui Paulo void os_free(void *ptr) 782e28a4053SRui Paulo { 783e28a4053SRui Paulo struct os_alloc_trace *a; 784e28a4053SRui Paulo 785e28a4053SRui Paulo if (ptr == NULL) 786e28a4053SRui Paulo return; 787e28a4053SRui Paulo a = (struct os_alloc_trace *) ptr - 1; 788e28a4053SRui Paulo if (a->magic != ALLOC_MAGIC) { 789e28a4053SRui Paulo wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s", 790e28a4053SRui Paulo a, a->magic, 791e28a4053SRui Paulo a->magic == FREED_MAGIC ? " (already freed)" : ""); 792e28a4053SRui Paulo wpa_trace_show("Invalid os_free() call"); 793e28a4053SRui Paulo abort(); 794e28a4053SRui Paulo } 795e28a4053SRui Paulo dl_list_del(&a->list); 796e28a4053SRui Paulo a->magic = FREED_MAGIC; 797e28a4053SRui Paulo 798e28a4053SRui Paulo wpa_trace_check_ref(ptr); 799e28a4053SRui Paulo free(a); 800e28a4053SRui Paulo } 801e28a4053SRui Paulo 802e28a4053SRui Paulo 803e28a4053SRui Paulo void * os_zalloc(size_t size) 804e28a4053SRui Paulo { 805e28a4053SRui Paulo void *ptr = os_malloc(size); 806e28a4053SRui Paulo if (ptr) 807e28a4053SRui Paulo os_memset(ptr, 0, size); 808e28a4053SRui Paulo return ptr; 809e28a4053SRui Paulo } 810e28a4053SRui Paulo 811e28a4053SRui Paulo 812e28a4053SRui Paulo char * os_strdup(const char *s) 813e28a4053SRui Paulo { 814e28a4053SRui Paulo size_t len; 815e28a4053SRui Paulo char *d; 816e28a4053SRui Paulo len = os_strlen(s); 817e28a4053SRui Paulo d = os_malloc(len + 1); 818e28a4053SRui Paulo if (d == NULL) 819e28a4053SRui Paulo return NULL; 820e28a4053SRui Paulo os_memcpy(d, s, len); 821e28a4053SRui Paulo d[len] = '\0'; 822e28a4053SRui Paulo return d; 823e28a4053SRui Paulo } 824e28a4053SRui Paulo 825e28a4053SRui Paulo #endif /* WPA_TRACE */ 8265b9c547cSRui Paulo 8275b9c547cSRui Paulo 8285b9c547cSRui Paulo int os_exec(const char *program, const char *arg, int wait_completion) 8295b9c547cSRui Paulo { 8305b9c547cSRui Paulo pid_t pid; 8315b9c547cSRui Paulo int pid_status; 8325b9c547cSRui Paulo 8335b9c547cSRui Paulo pid = fork(); 8345b9c547cSRui Paulo if (pid < 0) { 8355b9c547cSRui Paulo perror("fork"); 8365b9c547cSRui Paulo return -1; 8375b9c547cSRui Paulo } 8385b9c547cSRui Paulo 8395b9c547cSRui Paulo if (pid == 0) { 8405b9c547cSRui Paulo /* run the external command in the child process */ 8415b9c547cSRui Paulo const int MAX_ARG = 30; 8425b9c547cSRui Paulo char *_program, *_arg, *pos; 8435b9c547cSRui Paulo char *argv[MAX_ARG + 1]; 8445b9c547cSRui Paulo int i; 8455b9c547cSRui Paulo 8465b9c547cSRui Paulo _program = os_strdup(program); 8475b9c547cSRui Paulo _arg = os_strdup(arg); 8485b9c547cSRui Paulo 8495b9c547cSRui Paulo argv[0] = _program; 8505b9c547cSRui Paulo 8515b9c547cSRui Paulo i = 1; 8525b9c547cSRui Paulo pos = _arg; 8535b9c547cSRui Paulo while (i < MAX_ARG && pos && *pos) { 8545b9c547cSRui Paulo while (*pos == ' ') 8555b9c547cSRui Paulo pos++; 8565b9c547cSRui Paulo if (*pos == '\0') 8575b9c547cSRui Paulo break; 8585b9c547cSRui Paulo argv[i++] = pos; 8595b9c547cSRui Paulo pos = os_strchr(pos, ' '); 8605b9c547cSRui Paulo if (pos) 8615b9c547cSRui Paulo *pos++ = '\0'; 8625b9c547cSRui Paulo } 8635b9c547cSRui Paulo argv[i] = NULL; 8645b9c547cSRui Paulo 8655b9c547cSRui Paulo execv(program, argv); 8665b9c547cSRui Paulo perror("execv"); 8675b9c547cSRui Paulo os_free(_program); 8685b9c547cSRui Paulo os_free(_arg); 8695b9c547cSRui Paulo exit(0); 8705b9c547cSRui Paulo return -1; 8715b9c547cSRui Paulo } 8725b9c547cSRui Paulo 8735b9c547cSRui Paulo if (wait_completion) { 8745b9c547cSRui Paulo /* wait for the child process to complete in the parent */ 8755b9c547cSRui Paulo waitpid(pid, &pid_status, 0); 8765b9c547cSRui Paulo } 8775b9c547cSRui Paulo 8785b9c547cSRui Paulo return 0; 8795b9c547cSRui Paulo } 880