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