Home | History | Annotate | Download | only in utils
      1 /*
      2  * OS specific functions for UNIX/POSIX systems
      3  * Copyright (c) 2005-2009, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "includes.h"
     10 
     11 #include <time.h>
     12 #include <sys/wait.h>
     13 
     14 #ifdef ANDROID
     15 #include <sys/capability.h>
     16 #include <sys/prctl.h>
     17 #include <private/android_filesystem_config.h>
     18 #endif /* ANDROID */
     19 
     20 #ifdef __MACH__
     21 #include <CoreServices/CoreServices.h>
     22 #include <mach/mach.h>
     23 #include <mach/mach_time.h>
     24 #endif /* __MACH__ */
     25 
     26 #include "os.h"
     27 #include "common.h"
     28 
     29 #ifdef WPA_TRACE
     30 
     31 #include "wpa_debug.h"
     32 #include "trace.h"
     33 #include "list.h"
     34 
     35 static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list);
     36 
     37 #define ALLOC_MAGIC 0xa84ef1b2
     38 #define FREED_MAGIC 0x67fd487a
     39 
     40 struct os_alloc_trace {
     41 	unsigned int magic;
     42 	struct dl_list list;
     43 	size_t len;
     44 	WPA_TRACE_INFO
     45 } __attribute__((aligned(16)));
     46 
     47 #endif /* WPA_TRACE */
     48 
     49 
     50 void os_sleep(os_time_t sec, os_time_t usec)
     51 {
     52 	if (sec)
     53 		sleep(sec);
     54 	if (usec)
     55 		usleep(usec);
     56 }
     57 
     58 
     59 int os_get_time(struct os_time *t)
     60 {
     61 	int res;
     62 	struct timeval tv;
     63 	res = gettimeofday(&tv, NULL);
     64 	t->sec = tv.tv_sec;
     65 	t->usec = tv.tv_usec;
     66 	return res;
     67 }
     68 
     69 
     70 int os_get_reltime(struct os_reltime *t)
     71 {
     72 #ifndef __MACH__
     73 #if defined(CLOCK_BOOTTIME)
     74 	static clockid_t clock_id = CLOCK_BOOTTIME;
     75 #elif defined(CLOCK_MONOTONIC)
     76 	static clockid_t clock_id = CLOCK_MONOTONIC;
     77 #else
     78 	static clockid_t clock_id = CLOCK_REALTIME;
     79 #endif
     80 	struct timespec ts;
     81 	int res;
     82 
     83 	if (TEST_FAIL())
     84 		return -1;
     85 
     86 	while (1) {
     87 		res = clock_gettime(clock_id, &ts);
     88 		if (res == 0) {
     89 			t->sec = ts.tv_sec;
     90 			t->usec = ts.tv_nsec / 1000;
     91 			return 0;
     92 		}
     93 		switch (clock_id) {
     94 #ifdef CLOCK_BOOTTIME
     95 		case CLOCK_BOOTTIME:
     96 			clock_id = CLOCK_MONOTONIC;
     97 			break;
     98 #endif
     99 #ifdef CLOCK_MONOTONIC
    100 		case CLOCK_MONOTONIC:
    101 			clock_id = CLOCK_REALTIME;
    102 			break;
    103 #endif
    104 		case CLOCK_REALTIME:
    105 			return -1;
    106 		}
    107 	}
    108 #else /* __MACH__ */
    109 	uint64_t abstime, nano;
    110 	static mach_timebase_info_data_t info = { 0, 0 };
    111 
    112 	if (!info.denom) {
    113 		if (mach_timebase_info(&info) != KERN_SUCCESS)
    114 			return -1;
    115 	}
    116 
    117 	abstime = mach_absolute_time();
    118 	nano = (abstime * info.numer) / info.denom;
    119 
    120 	t->sec = nano / NSEC_PER_SEC;
    121 	t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC;
    122 
    123 	return 0;
    124 #endif /* __MACH__ */
    125 }
    126 
    127 
    128 int os_mktime(int year, int month, int day, int hour, int min, int sec,
    129 	      os_time_t *t)
    130 {
    131 	struct tm tm, *tm1;
    132 	time_t t_local, t1, t2;
    133 	os_time_t tz_offset;
    134 
    135 	if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
    136 	    hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
    137 	    sec > 60)
    138 		return -1;
    139 
    140 	memset(&tm, 0, sizeof(tm));
    141 	tm.tm_year = year - 1900;
    142 	tm.tm_mon = month - 1;
    143 	tm.tm_mday = day;
    144 	tm.tm_hour = hour;
    145 	tm.tm_min = min;
    146 	tm.tm_sec = sec;
    147 
    148 	t_local = mktime(&tm);
    149 
    150 	/* figure out offset to UTC */
    151 	tm1 = localtime(&t_local);
    152 	if (tm1) {
    153 		t1 = mktime(tm1);
    154 		tm1 = gmtime(&t_local);
    155 		if (tm1) {
    156 			t2 = mktime(tm1);
    157 			tz_offset = t2 - t1;
    158 		} else
    159 			tz_offset = 0;
    160 	} else
    161 		tz_offset = 0;
    162 
    163 	*t = (os_time_t) t_local - tz_offset;
    164 	return 0;
    165 }
    166 
    167 
    168 int os_gmtime(os_time_t t, struct os_tm *tm)
    169 {
    170 	struct tm *tm2;
    171 	time_t t2 = t;
    172 
    173 	tm2 = gmtime(&t2);
    174 	if (tm2 == NULL)
    175 		return -1;
    176 	tm->sec = tm2->tm_sec;
    177 	tm->min = tm2->tm_min;
    178 	tm->hour = tm2->tm_hour;
    179 	tm->day = tm2->tm_mday;
    180 	tm->month = tm2->tm_mon + 1;
    181 	tm->year = tm2->tm_year + 1900;
    182 	return 0;
    183 }
    184 
    185 
    186 #ifdef __APPLE__
    187 #include <fcntl.h>
    188 static int os_daemon(int nochdir, int noclose)
    189 {
    190 	int devnull;
    191 
    192 	if (chdir("/") < 0)
    193 		return -1;
    194 
    195 	devnull = open("/dev/null", O_RDWR);
    196 	if (devnull < 0)
    197 		return -1;
    198 
    199 	if (dup2(devnull, STDIN_FILENO) < 0) {
    200 		close(devnull);
    201 		return -1;
    202 	}
    203 
    204 	if (dup2(devnull, STDOUT_FILENO) < 0) {
    205 		close(devnull);
    206 		return -1;
    207 	}
    208 
    209 	if (dup2(devnull, STDERR_FILENO) < 0) {
    210 		close(devnull);
    211 		return -1;
    212 	}
    213 
    214 	return 0;
    215 }
    216 #else /* __APPLE__ */
    217 #define os_daemon daemon
    218 #endif /* __APPLE__ */
    219 
    220 
    221 int os_daemonize(const char *pid_file)
    222 {
    223 #if defined(__uClinux__) || defined(__sun__)
    224 	return -1;
    225 #else /* defined(__uClinux__) || defined(__sun__) */
    226 	if (os_daemon(0, 0)) {
    227 		perror("daemon");
    228 		return -1;
    229 	}
    230 
    231 	if (pid_file) {
    232 		FILE *f = fopen(pid_file, "w");
    233 		if (f) {
    234 			fprintf(f, "%u\n", getpid());
    235 			fclose(f);
    236 		}
    237 	}
    238 
    239 	return -0;
    240 #endif /* defined(__uClinux__) || defined(__sun__) */
    241 }
    242 
    243 
    244 void os_daemonize_terminate(const char *pid_file)
    245 {
    246 	if (pid_file)
    247 		unlink(pid_file);
    248 }
    249 
    250 
    251 int os_get_random(unsigned char *buf, size_t len)
    252 {
    253 	FILE *f;
    254 	size_t rc;
    255 
    256 	if (TEST_FAIL())
    257 		return -1;
    258 
    259 	f = fopen("/dev/urandom", "rb");
    260 	if (f == NULL) {
    261 		printf("Could not open /dev/urandom.\n");
    262 		return -1;
    263 	}
    264 
    265 	rc = fread(buf, 1, len, f);
    266 	fclose(f);
    267 
    268 	return rc != len ? -1 : 0;
    269 }
    270 
    271 
    272 unsigned long os_random(void)
    273 {
    274 	return random();
    275 }
    276 
    277 
    278 char * os_rel2abs_path(const char *rel_path)
    279 {
    280 	char *buf = NULL, *cwd, *ret;
    281 	size_t len = 128, cwd_len, rel_len, ret_len;
    282 	int last_errno;
    283 
    284 	if (!rel_path)
    285 		return NULL;
    286 
    287 	if (rel_path[0] == '/')
    288 		return os_strdup(rel_path);
    289 
    290 	for (;;) {
    291 		buf = os_malloc(len);
    292 		if (buf == NULL)
    293 			return NULL;
    294 		cwd = getcwd(buf, len);
    295 		if (cwd == NULL) {
    296 			last_errno = errno;
    297 			os_free(buf);
    298 			if (last_errno != ERANGE)
    299 				return NULL;
    300 			len *= 2;
    301 			if (len > 2000)
    302 				return NULL;
    303 		} else {
    304 			buf[len - 1] = '\0';
    305 			break;
    306 		}
    307 	}
    308 
    309 	cwd_len = os_strlen(cwd);
    310 	rel_len = os_strlen(rel_path);
    311 	ret_len = cwd_len + 1 + rel_len + 1;
    312 	ret = os_malloc(ret_len);
    313 	if (ret) {
    314 		os_memcpy(ret, cwd, cwd_len);
    315 		ret[cwd_len] = '/';
    316 		os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
    317 		ret[ret_len - 1] = '\0';
    318 	}
    319 	os_free(buf);
    320 	return ret;
    321 }
    322 
    323 
    324 int os_program_init(void)
    325 {
    326 #ifdef ANDROID
    327 	/*
    328 	 * We ignore errors here since errors are normal if we
    329 	 * are already running as non-root.
    330 	 */
    331 #ifdef ANDROID_SETGROUPS_OVERRIDE
    332 	gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE };
    333 #else /* ANDROID_SETGROUPS_OVERRIDE */
    334 	gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
    335 #endif /* ANDROID_SETGROUPS_OVERRIDE */
    336 	struct __user_cap_header_struct header;
    337 	struct __user_cap_data_struct cap;
    338 
    339 	setgroups(ARRAY_SIZE(groups), groups);
    340 
    341 	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
    342 
    343 	setgid(AID_WIFI);
    344 	setuid(AID_WIFI);
    345 
    346 	header.version = _LINUX_CAPABILITY_VERSION;
    347 	header.pid = 0;
    348 	cap.effective = cap.permitted =
    349 		(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
    350 	cap.inheritable = 0;
    351 	capset(&header, &cap);
    352 #endif /* ANDROID */
    353 
    354 	return 0;
    355 }
    356 
    357 
    358 void os_program_deinit(void)
    359 {
    360 #ifdef WPA_TRACE
    361 	struct os_alloc_trace *a;
    362 	unsigned long total = 0;
    363 	dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) {
    364 		total += a->len;
    365 		if (a->magic != ALLOC_MAGIC) {
    366 			wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x "
    367 				   "len %lu",
    368 				   a, a->magic, (unsigned long) a->len);
    369 			continue;
    370 		}
    371 		wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu",
    372 			   a, (unsigned long) a->len);
    373 		wpa_trace_dump("memleak", a);
    374 	}
    375 	if (total)
    376 		wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes",
    377 			   (unsigned long) total);
    378 	wpa_trace_deinit();
    379 #endif /* WPA_TRACE */
    380 }
    381 
    382 
    383 int os_setenv(const char *name, const char *value, int overwrite)
    384 {
    385 	return setenv(name, value, overwrite);
    386 }
    387 
    388 
    389 int os_unsetenv(const char *name)
    390 {
    391 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \
    392     defined(__OpenBSD__)
    393 	unsetenv(name);
    394 	return 0;
    395 #else
    396 	return unsetenv(name);
    397 #endif
    398 }
    399 
    400 
    401 char * os_readfile(const char *name, size_t *len)
    402 {
    403 	FILE *f;
    404 	char *buf;
    405 	long pos;
    406 
    407 	f = fopen(name, "rb");
    408 	if (f == NULL)
    409 		return NULL;
    410 
    411 	if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
    412 		fclose(f);
    413 		return NULL;
    414 	}
    415 	*len = pos;
    416 	if (fseek(f, 0, SEEK_SET) < 0) {
    417 		fclose(f);
    418 		return NULL;
    419 	}
    420 
    421 	buf = os_malloc(*len);
    422 	if (buf == NULL) {
    423 		fclose(f);
    424 		return NULL;
    425 	}
    426 
    427 	if (fread(buf, 1, *len, f) != *len) {
    428 		fclose(f);
    429 		os_free(buf);
    430 		return NULL;
    431 	}
    432 
    433 	fclose(f);
    434 
    435 	return buf;
    436 }
    437 
    438 
    439 int os_file_exists(const char *fname)
    440 {
    441 	return access(fname, F_OK) == 0;
    442 }
    443 
    444 
    445 int os_fdatasync(FILE *stream)
    446 {
    447 	if (!fflush(stream)) {
    448 #ifdef __linux__
    449 		return fdatasync(fileno(stream));
    450 #else /* !__linux__ */
    451 #ifdef F_FULLFSYNC
    452 		/* OS X does not implement fdatasync(). */
    453 		return fcntl(fileno(stream), F_FULLFSYNC);
    454 #else /* F_FULLFSYNC */
    455 		return fsync(fileno(stream));
    456 #endif /* F_FULLFSYNC */
    457 #endif /* __linux__ */
    458 	}
    459 
    460 	return -1;
    461 }
    462 
    463 
    464 #ifndef WPA_TRACE
    465 void * os_zalloc(size_t size)
    466 {
    467 	return calloc(1, size);
    468 }
    469 #endif /* WPA_TRACE */
    470 
    471 
    472 size_t os_strlcpy(char *dest, const char *src, size_t siz)
    473 {
    474 	const char *s = src;
    475 	size_t left = siz;
    476 
    477 	if (left) {
    478 		/* Copy string up to the maximum size of the dest buffer */
    479 		while (--left != 0) {
    480 			if ((*dest++ = *s++) == '\0')
    481 				break;
    482 		}
    483 	}
    484 
    485 	if (left == 0) {
    486 		/* Not enough room for the string; force NUL-termination */
    487 		if (siz != 0)
    488 			*dest = '\0';
    489 		while (*s++)
    490 			; /* determine total src string length */
    491 	}
    492 
    493 	return s - src - 1;
    494 }
    495 
    496 
    497 int os_memcmp_const(const void *a, const void *b, size_t len)
    498 {
    499 	const u8 *aa = a;
    500 	const u8 *bb = b;
    501 	size_t i;
    502 	u8 res;
    503 
    504 	for (res = 0, i = 0; i < len; i++)
    505 		res |= aa[i] ^ bb[i];
    506 
    507 	return res;
    508 }
    509 
    510 
    511 #ifdef WPA_TRACE
    512 
    513 #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
    514 char wpa_trace_fail_func[256] = { 0 };
    515 unsigned int wpa_trace_fail_after;
    516 
    517 static int testing_fail_alloc(void)
    518 {
    519 	const char *func[WPA_TRACE_LEN];
    520 	size_t i, res, len;
    521 	char *pos, *next;
    522 	int match;
    523 
    524 	if (!wpa_trace_fail_after)
    525 		return 0;
    526 
    527 	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
    528 	i = 0;
    529 	if (i < res && os_strcmp(func[i], __func__) == 0)
    530 		i++;
    531 	if (i < res && os_strcmp(func[i], "os_malloc") == 0)
    532 		i++;
    533 	if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
    534 		i++;
    535 	if (i < res && os_strcmp(func[i], "os_calloc") == 0)
    536 		i++;
    537 	if (i < res && os_strcmp(func[i], "os_realloc") == 0)
    538 		i++;
    539 	if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
    540 		i++;
    541 	if (i < res && os_strcmp(func[i], "os_strdup") == 0)
    542 		i++;
    543 
    544 	pos = wpa_trace_fail_func;
    545 
    546 	match = 0;
    547 	while (i < res) {
    548 		int allow_skip = 1;
    549 		int maybe = 0;
    550 
    551 		if (*pos == '=') {
    552 			allow_skip = 0;
    553 			pos++;
    554 		} else if (*pos == '?') {
    555 			maybe = 1;
    556 			pos++;
    557 		}
    558 		next = os_strchr(pos, ';');
    559 		if (next)
    560 			len = next - pos;
    561 		else
    562 			len = os_strlen(pos);
    563 		if (os_memcmp(pos, func[i], len) != 0) {
    564 			if (maybe && next) {
    565 				pos = next + 1;
    566 				continue;
    567 			}
    568 			if (allow_skip) {
    569 				i++;
    570 				continue;
    571 			}
    572 			return 0;
    573 		}
    574 		if (!next) {
    575 			match = 1;
    576 			break;
    577 		}
    578 		pos = next + 1;
    579 		i++;
    580 	}
    581 	if (!match)
    582 		return 0;
    583 
    584 	wpa_trace_fail_after--;
    585 	if (wpa_trace_fail_after == 0) {
    586 		wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
    587 			   wpa_trace_fail_func);
    588 		for (i = 0; i < res; i++)
    589 			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
    590 				   (int) i, func[i]);
    591 		return 1;
    592 	}
    593 
    594 	return 0;
    595 }
    596 
    597 
    598 char wpa_trace_test_fail_func[256] = { 0 };
    599 unsigned int wpa_trace_test_fail_after;
    600 
    601 int testing_test_fail(void)
    602 {
    603 	const char *func[WPA_TRACE_LEN];
    604 	size_t i, res, len;
    605 	char *pos, *next;
    606 	int match;
    607 
    608 	if (!wpa_trace_test_fail_after)
    609 		return 0;
    610 
    611 	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
    612 	i = 0;
    613 	if (i < res && os_strcmp(func[i], __func__) == 0)
    614 		i++;
    615 
    616 	pos = wpa_trace_test_fail_func;
    617 
    618 	match = 0;
    619 	while (i < res) {
    620 		int allow_skip = 1;
    621 		int maybe = 0;
    622 
    623 		if (*pos == '=') {
    624 			allow_skip = 0;
    625 			pos++;
    626 		} else if (*pos == '?') {
    627 			maybe = 1;
    628 			pos++;
    629 		}
    630 		next = os_strchr(pos, ';');
    631 		if (next)
    632 			len = next - pos;
    633 		else
    634 			len = os_strlen(pos);
    635 		if (os_memcmp(pos, func[i], len) != 0) {
    636 			if (maybe && next) {
    637 				pos = next + 1;
    638 				continue;
    639 			}
    640 			if (allow_skip) {
    641 				i++;
    642 				continue;
    643 			}
    644 			return 0;
    645 		}
    646 		if (!next) {
    647 			match = 1;
    648 			break;
    649 		}
    650 		pos = next + 1;
    651 		i++;
    652 	}
    653 	if (!match)
    654 		return 0;
    655 
    656 	wpa_trace_test_fail_after--;
    657 	if (wpa_trace_test_fail_after == 0) {
    658 		wpa_printf(MSG_INFO, "TESTING: fail at %s",
    659 			   wpa_trace_test_fail_func);
    660 		for (i = 0; i < res; i++)
    661 			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
    662 				   (int) i, func[i]);
    663 		return 1;
    664 	}
    665 
    666 	return 0;
    667 }
    668 
    669 #else
    670 
    671 static inline int testing_fail_alloc(void)
    672 {
    673 	return 0;
    674 }
    675 #endif
    676 
    677 void * os_malloc(size_t size)
    678 {
    679 	struct os_alloc_trace *a;
    680 
    681 	if (testing_fail_alloc())
    682 		return NULL;
    683 
    684 	a = malloc(sizeof(*a) + size);
    685 	if (a == NULL)
    686 		return NULL;
    687 	a->magic = ALLOC_MAGIC;
    688 	dl_list_add(&alloc_list, &a->list);
    689 	a->len = size;
    690 	wpa_trace_record(a);
    691 	return a + 1;
    692 }
    693 
    694 
    695 void * os_realloc(void *ptr, size_t size)
    696 {
    697 	struct os_alloc_trace *a;
    698 	size_t copy_len;
    699 	void *n;
    700 
    701 	if (ptr == NULL)
    702 		return os_malloc(size);
    703 
    704 	a = (struct os_alloc_trace *) ptr - 1;
    705 	if (a->magic != ALLOC_MAGIC) {
    706 		wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
    707 			   a, a->magic,
    708 			   a->magic == FREED_MAGIC ? " (already freed)" : "");
    709 		wpa_trace_show("Invalid os_realloc() call");
    710 		abort();
    711 	}
    712 	n = os_malloc(size);
    713 	if (n == NULL)
    714 		return NULL;
    715 	copy_len = a->len;
    716 	if (copy_len > size)
    717 		copy_len = size;
    718 	os_memcpy(n, a + 1, copy_len);
    719 	os_free(ptr);
    720 	return n;
    721 }
    722 
    723 
    724 void os_free(void *ptr)
    725 {
    726 	struct os_alloc_trace *a;
    727 
    728 	if (ptr == NULL)
    729 		return;
    730 	a = (struct os_alloc_trace *) ptr - 1;
    731 	if (a->magic != ALLOC_MAGIC) {
    732 		wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
    733 			   a, a->magic,
    734 			   a->magic == FREED_MAGIC ? " (already freed)" : "");
    735 		wpa_trace_show("Invalid os_free() call");
    736 		abort();
    737 	}
    738 	dl_list_del(&a->list);
    739 	a->magic = FREED_MAGIC;
    740 
    741 	wpa_trace_check_ref(ptr);
    742 	free(a);
    743 }
    744 
    745 
    746 void * os_zalloc(size_t size)
    747 {
    748 	void *ptr = os_malloc(size);
    749 	if (ptr)
    750 		os_memset(ptr, 0, size);
    751 	return ptr;
    752 }
    753 
    754 
    755 char * os_strdup(const char *s)
    756 {
    757 	size_t len;
    758 	char *d;
    759 	len = os_strlen(s);
    760 	d = os_malloc(len + 1);
    761 	if (d == NULL)
    762 		return NULL;
    763 	os_memcpy(d, s, len);
    764 	d[len] = '\0';
    765 	return d;
    766 }
    767 
    768 #endif /* WPA_TRACE */
    769 
    770 
    771 int os_exec(const char *program, const char *arg, int wait_completion)
    772 {
    773 	pid_t pid;
    774 	int pid_status;
    775 
    776 	pid = fork();
    777 	if (pid < 0) {
    778 		perror("fork");
    779 		return -1;
    780 	}
    781 
    782 	if (pid == 0) {
    783 		/* run the external command in the child process */
    784 		const int MAX_ARG = 30;
    785 		char *_program, *_arg, *pos;
    786 		char *argv[MAX_ARG + 1];
    787 		int i;
    788 
    789 		_program = os_strdup(program);
    790 		_arg = os_strdup(arg);
    791 
    792 		argv[0] = _program;
    793 
    794 		i = 1;
    795 		pos = _arg;
    796 		while (i < MAX_ARG && pos && *pos) {
    797 			while (*pos == ' ')
    798 				pos++;
    799 			if (*pos == '\0')
    800 				break;
    801 			argv[i++] = pos;
    802 			pos = os_strchr(pos, ' ');
    803 			if (pos)
    804 				*pos++ = '\0';
    805 		}
    806 		argv[i] = NULL;
    807 
    808 		execv(program, argv);
    809 		perror("execv");
    810 		os_free(_program);
    811 		os_free(_arg);
    812 		exit(0);
    813 		return -1;
    814 	}
    815 
    816 	if (wait_completion) {
    817 		/* wait for the child process to complete in the parent */
    818 		waitpid(pid, &pid_status, 0);
    819 	}
    820 
    821 	return 0;
    822 }
    823