xref: /freebsd/contrib/wpa/src/utils/os_unix.c (revision 7cc42f6d)
1 /*
2  * OS specific functions for UNIX/POSIX systems
3  * Copyright (c) 2005-2019, Jouni Malinen <j@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 #ifdef __FreeBSD__
222 #include <err.h>
223 #include <libutil.h>
224 #include <stdint.h>
225 #endif /* __FreeBSD__ */
226 
227 int os_daemonize(const char *pid_file)
228 {
229 #if defined(__uClinux__) || defined(__sun__)
230 	return -1;
231 #else /* defined(__uClinux__) || defined(__sun__) */
232 #ifdef __FreeBSD__
233 	pid_t otherpid;
234 	struct pidfh *pfh;
235 
236 	pfh = pidfile_open(pid_file, 0600, &otherpid);
237 	if (pfh == NULL) {
238 		if (errno == EEXIST) {
239 			errx(1, "Daemon already running, pid: %jd.",
240 			    (intmax_t)otherpid);
241 		}
242 		warn("Cannot open or create pidfile.");
243 	}
244 #endif /* __FreeBSD__ */
245 
246 	if (os_daemon(0, 0)) {
247 		perror("daemon");
248 #ifdef __FreeBSD__
249 		pidfile_remove(pfh);
250 #endif /* __FreeBSD__ */
251 		return -1;
252 	}
253 
254 #ifndef __FreeBSD__
255 	if (pid_file) {
256 		FILE *f = fopen(pid_file, "w");
257 		if (f) {
258 			fprintf(f, "%u\n", getpid());
259 			fclose(f);
260 		}
261 	}
262 #else /* __FreeBSD__ */
263 	pidfile_write(pfh);
264 #endif /* __FreeBSD__ */
265 
266 	return -0;
267 #endif /* defined(__uClinux__) || defined(__sun__) */
268 }
269 
270 
271 void os_daemonize_terminate(const char *pid_file)
272 {
273 	if (pid_file)
274 		unlink(pid_file);
275 }
276 
277 
278 int os_get_random(unsigned char *buf, size_t len)
279 {
280 #ifdef TEST_FUZZ
281 	size_t i;
282 
283 	for (i = 0; i < len; i++)
284 		buf[i] = i & 0xff;
285 	return 0;
286 #else /* TEST_FUZZ */
287 	FILE *f;
288 	size_t rc;
289 
290 	if (TEST_FAIL())
291 		return -1;
292 
293 	f = fopen("/dev/urandom", "rb");
294 	if (f == NULL) {
295 		printf("Could not open /dev/urandom.\n");
296 		return -1;
297 	}
298 
299 	rc = fread(buf, 1, len, f);
300 	fclose(f);
301 
302 	return rc != len ? -1 : 0;
303 #endif /* TEST_FUZZ */
304 }
305 
306 
307 unsigned long os_random(void)
308 {
309 	return random();
310 }
311 
312 
313 char * os_rel2abs_path(const char *rel_path)
314 {
315 	char *buf = NULL, *cwd, *ret;
316 	size_t len = 128, cwd_len, rel_len, ret_len;
317 	int last_errno;
318 
319 	if (!rel_path)
320 		return NULL;
321 
322 	if (rel_path[0] == '/')
323 		return os_strdup(rel_path);
324 
325 	for (;;) {
326 		buf = os_malloc(len);
327 		if (buf == NULL)
328 			return NULL;
329 		cwd = getcwd(buf, len);
330 		if (cwd == NULL) {
331 			last_errno = errno;
332 			os_free(buf);
333 			if (last_errno != ERANGE)
334 				return NULL;
335 			len *= 2;
336 			if (len > 2000)
337 				return NULL;
338 		} else {
339 			buf[len - 1] = '\0';
340 			break;
341 		}
342 	}
343 
344 	cwd_len = os_strlen(cwd);
345 	rel_len = os_strlen(rel_path);
346 	ret_len = cwd_len + 1 + rel_len + 1;
347 	ret = os_malloc(ret_len);
348 	if (ret) {
349 		os_memcpy(ret, cwd, cwd_len);
350 		ret[cwd_len] = '/';
351 		os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
352 		ret[ret_len - 1] = '\0';
353 	}
354 	os_free(buf);
355 	return ret;
356 }
357 
358 
359 int os_program_init(void)
360 {
361 #ifdef ANDROID
362 	/*
363 	 * We ignore errors here since errors are normal if we
364 	 * are already running as non-root.
365 	 */
366 #ifdef ANDROID_SETGROUPS_OVERRIDE
367 	gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE };
368 #else /* ANDROID_SETGROUPS_OVERRIDE */
369 	gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
370 #endif /* ANDROID_SETGROUPS_OVERRIDE */
371 	struct __user_cap_header_struct header;
372 	struct __user_cap_data_struct cap;
373 
374 	setgroups(ARRAY_SIZE(groups), groups);
375 
376 	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
377 
378 	setgid(AID_WIFI);
379 	setuid(AID_WIFI);
380 
381 	header.version = _LINUX_CAPABILITY_VERSION;
382 	header.pid = 0;
383 	cap.effective = cap.permitted =
384 		(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
385 	cap.inheritable = 0;
386 	capset(&header, &cap);
387 #endif /* ANDROID */
388 
389 	return 0;
390 }
391 
392 
393 void os_program_deinit(void)
394 {
395 #ifdef WPA_TRACE
396 	struct os_alloc_trace *a;
397 	unsigned long total = 0;
398 	dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) {
399 		total += a->len;
400 		if (a->magic != ALLOC_MAGIC) {
401 			wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x "
402 				   "len %lu",
403 				   a, a->magic, (unsigned long) a->len);
404 			continue;
405 		}
406 		wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu",
407 			   a, (unsigned long) a->len);
408 		wpa_trace_dump("memleak", a);
409 	}
410 	if (total)
411 		wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes",
412 			   (unsigned long) total);
413 	wpa_trace_deinit();
414 #endif /* WPA_TRACE */
415 }
416 
417 
418 int os_setenv(const char *name, const char *value, int overwrite)
419 {
420 	return setenv(name, value, overwrite);
421 }
422 
423 
424 int os_unsetenv(const char *name)
425 {
426 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \
427     defined(__OpenBSD__)
428 	unsetenv(name);
429 	return 0;
430 #else
431 	return unsetenv(name);
432 #endif
433 }
434 
435 
436 char * os_readfile(const char *name, size_t *len)
437 {
438 	FILE *f;
439 	char *buf;
440 	long pos;
441 
442 	f = fopen(name, "rb");
443 	if (f == NULL)
444 		return NULL;
445 
446 	if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
447 		fclose(f);
448 		return NULL;
449 	}
450 	*len = pos;
451 	if (fseek(f, 0, SEEK_SET) < 0) {
452 		fclose(f);
453 		return NULL;
454 	}
455 
456 	buf = os_malloc(*len);
457 	if (buf == NULL) {
458 		fclose(f);
459 		return NULL;
460 	}
461 
462 	if (fread(buf, 1, *len, f) != *len) {
463 		fclose(f);
464 		os_free(buf);
465 		return NULL;
466 	}
467 
468 	fclose(f);
469 
470 	return buf;
471 }
472 
473 
474 int os_file_exists(const char *fname)
475 {
476 	return access(fname, F_OK) == 0;
477 }
478 
479 
480 int os_fdatasync(FILE *stream)
481 {
482 	if (!fflush(stream)) {
483 #ifdef __linux__
484 		return fdatasync(fileno(stream));
485 #else /* !__linux__ */
486 #ifdef F_FULLFSYNC
487 		/* OS X does not implement fdatasync(). */
488 		return fcntl(fileno(stream), F_FULLFSYNC);
489 #else /* F_FULLFSYNC */
490 		return fsync(fileno(stream));
491 #endif /* F_FULLFSYNC */
492 #endif /* __linux__ */
493 	}
494 
495 	return -1;
496 }
497 
498 
499 #ifndef WPA_TRACE
500 void * os_zalloc(size_t size)
501 {
502 	return calloc(1, size);
503 }
504 #endif /* WPA_TRACE */
505 
506 
507 size_t os_strlcpy(char *dest, const char *src, size_t siz)
508 {
509 	const char *s = src;
510 	size_t left = siz;
511 
512 	if (left) {
513 		/* Copy string up to the maximum size of the dest buffer */
514 		while (--left != 0) {
515 			if ((*dest++ = *s++) == '\0')
516 				break;
517 		}
518 	}
519 
520 	if (left == 0) {
521 		/* Not enough room for the string; force NUL-termination */
522 		if (siz != 0)
523 			*dest = '\0';
524 		while (*s++)
525 			; /* determine total src string length */
526 	}
527 
528 	return s - src - 1;
529 }
530 
531 
532 int os_memcmp_const(const void *a, const void *b, size_t len)
533 {
534 	const u8 *aa = a;
535 	const u8 *bb = b;
536 	size_t i;
537 	u8 res;
538 
539 	for (res = 0, i = 0; i < len; i++)
540 		res |= aa[i] ^ bb[i];
541 
542 	return res;
543 }
544 
545 
546 void * os_memdup(const void *src, size_t len)
547 {
548 	void *r = os_malloc(len);
549 
550 	if (r && src)
551 		os_memcpy(r, src, len);
552 	return r;
553 }
554 
555 
556 #ifdef WPA_TRACE
557 
558 #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
559 char wpa_trace_fail_func[256] = { 0 };
560 unsigned int wpa_trace_fail_after;
561 
562 static int testing_fail_alloc(void)
563 {
564 	const char *func[WPA_TRACE_LEN];
565 	size_t i, res, len;
566 	char *pos, *next;
567 	int match;
568 
569 	if (!wpa_trace_fail_after)
570 		return 0;
571 
572 	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
573 	i = 0;
574 	if (i < res && os_strcmp(func[i], __func__) == 0)
575 		i++;
576 	if (i < res && os_strcmp(func[i], "os_malloc") == 0)
577 		i++;
578 	if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
579 		i++;
580 	if (i < res && os_strcmp(func[i], "os_calloc") == 0)
581 		i++;
582 	if (i < res && os_strcmp(func[i], "os_realloc") == 0)
583 		i++;
584 	if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
585 		i++;
586 	if (i < res && os_strcmp(func[i], "os_strdup") == 0)
587 		i++;
588 	if (i < res && os_strcmp(func[i], "os_memdup") == 0)
589 		i++;
590 
591 	pos = wpa_trace_fail_func;
592 
593 	match = 0;
594 	while (i < res) {
595 		int allow_skip = 1;
596 		int maybe = 0;
597 
598 		if (*pos == '=') {
599 			allow_skip = 0;
600 			pos++;
601 		} else if (*pos == '?') {
602 			maybe = 1;
603 			pos++;
604 		}
605 		next = os_strchr(pos, ';');
606 		if (next)
607 			len = next - pos;
608 		else
609 			len = os_strlen(pos);
610 		if (os_memcmp(pos, func[i], len) != 0) {
611 			if (maybe && next) {
612 				pos = next + 1;
613 				continue;
614 			}
615 			if (allow_skip) {
616 				i++;
617 				continue;
618 			}
619 			return 0;
620 		}
621 		if (!next) {
622 			match = 1;
623 			break;
624 		}
625 		pos = next + 1;
626 		i++;
627 	}
628 	if (!match)
629 		return 0;
630 
631 	wpa_trace_fail_after--;
632 	if (wpa_trace_fail_after == 0) {
633 		wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
634 			   wpa_trace_fail_func);
635 		for (i = 0; i < res; i++)
636 			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
637 				   (int) i, func[i]);
638 		return 1;
639 	}
640 
641 	return 0;
642 }
643 
644 
645 char wpa_trace_test_fail_func[256] = { 0 };
646 unsigned int wpa_trace_test_fail_after;
647 
648 int testing_test_fail(void)
649 {
650 	const char *func[WPA_TRACE_LEN];
651 	size_t i, res, len;
652 	char *pos, *next;
653 	int match;
654 
655 	if (!wpa_trace_test_fail_after)
656 		return 0;
657 
658 	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
659 	i = 0;
660 	if (i < res && os_strcmp(func[i], __func__) == 0)
661 		i++;
662 
663 	pos = wpa_trace_test_fail_func;
664 
665 	match = 0;
666 	while (i < res) {
667 		int allow_skip = 1;
668 		int maybe = 0;
669 
670 		if (*pos == '=') {
671 			allow_skip = 0;
672 			pos++;
673 		} else if (*pos == '?') {
674 			maybe = 1;
675 			pos++;
676 		}
677 		next = os_strchr(pos, ';');
678 		if (next)
679 			len = next - pos;
680 		else
681 			len = os_strlen(pos);
682 		if (os_memcmp(pos, func[i], len) != 0) {
683 			if (maybe && next) {
684 				pos = next + 1;
685 				continue;
686 			}
687 			if (allow_skip) {
688 				i++;
689 				continue;
690 			}
691 			return 0;
692 		}
693 		if (!next) {
694 			match = 1;
695 			break;
696 		}
697 		pos = next + 1;
698 		i++;
699 	}
700 	if (!match)
701 		return 0;
702 
703 	wpa_trace_test_fail_after--;
704 	if (wpa_trace_test_fail_after == 0) {
705 		wpa_printf(MSG_INFO, "TESTING: fail at %s",
706 			   wpa_trace_test_fail_func);
707 		for (i = 0; i < res; i++)
708 			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
709 				   (int) i, func[i]);
710 		return 1;
711 	}
712 
713 	return 0;
714 }
715 
716 #else
717 
718 static inline int testing_fail_alloc(void)
719 {
720 	return 0;
721 }
722 #endif
723 
724 void * os_malloc(size_t size)
725 {
726 	struct os_alloc_trace *a;
727 
728 	if (testing_fail_alloc())
729 		return NULL;
730 
731 	a = malloc(sizeof(*a) + size);
732 	if (a == NULL)
733 		return NULL;
734 	a->magic = ALLOC_MAGIC;
735 	dl_list_add(&alloc_list, &a->list);
736 	a->len = size;
737 	wpa_trace_record(a);
738 	return a + 1;
739 }
740 
741 
742 void * os_realloc(void *ptr, size_t size)
743 {
744 	struct os_alloc_trace *a;
745 	size_t copy_len;
746 	void *n;
747 
748 	if (ptr == NULL)
749 		return os_malloc(size);
750 
751 	a = (struct os_alloc_trace *) ptr - 1;
752 	if (a->magic != ALLOC_MAGIC) {
753 		wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
754 			   a, a->magic,
755 			   a->magic == FREED_MAGIC ? " (already freed)" : "");
756 		wpa_trace_show("Invalid os_realloc() call");
757 		abort();
758 	}
759 	n = os_malloc(size);
760 	if (n == NULL)
761 		return NULL;
762 	copy_len = a->len;
763 	if (copy_len > size)
764 		copy_len = size;
765 	os_memcpy(n, a + 1, copy_len);
766 	os_free(ptr);
767 	return n;
768 }
769 
770 
771 void os_free(void *ptr)
772 {
773 	struct os_alloc_trace *a;
774 
775 	if (ptr == NULL)
776 		return;
777 	a = (struct os_alloc_trace *) ptr - 1;
778 	if (a->magic != ALLOC_MAGIC) {
779 		wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
780 			   a, a->magic,
781 			   a->magic == FREED_MAGIC ? " (already freed)" : "");
782 		wpa_trace_show("Invalid os_free() call");
783 		abort();
784 	}
785 	dl_list_del(&a->list);
786 	a->magic = FREED_MAGIC;
787 
788 	wpa_trace_check_ref(ptr);
789 	free(a);
790 }
791 
792 
793 void * os_zalloc(size_t size)
794 {
795 	void *ptr = os_malloc(size);
796 	if (ptr)
797 		os_memset(ptr, 0, size);
798 	return ptr;
799 }
800 
801 
802 char * os_strdup(const char *s)
803 {
804 	size_t len;
805 	char *d;
806 	len = os_strlen(s);
807 	d = os_malloc(len + 1);
808 	if (d == NULL)
809 		return NULL;
810 	os_memcpy(d, s, len);
811 	d[len] = '\0';
812 	return d;
813 }
814 
815 #endif /* WPA_TRACE */
816 
817 
818 int os_exec(const char *program, const char *arg, int wait_completion)
819 {
820 	pid_t pid;
821 	int pid_status;
822 
823 	pid = fork();
824 	if (pid < 0) {
825 		perror("fork");
826 		return -1;
827 	}
828 
829 	if (pid == 0) {
830 		/* run the external command in the child process */
831 		const int MAX_ARG = 30;
832 		char *_program, *_arg, *pos;
833 		char *argv[MAX_ARG + 1];
834 		int i;
835 
836 		_program = os_strdup(program);
837 		_arg = os_strdup(arg);
838 
839 		argv[0] = _program;
840 
841 		i = 1;
842 		pos = _arg;
843 		while (i < MAX_ARG && pos && *pos) {
844 			while (*pos == ' ')
845 				pos++;
846 			if (*pos == '\0')
847 				break;
848 			argv[i++] = pos;
849 			pos = os_strchr(pos, ' ');
850 			if (pos)
851 				*pos++ = '\0';
852 		}
853 		argv[i] = NULL;
854 
855 		execv(program, argv);
856 		perror("execv");
857 		os_free(_program);
858 		os_free(_arg);
859 		exit(0);
860 		return -1;
861 	}
862 
863 	if (wait_completion) {
864 		/* wait for the child process to complete in the parent */
865 		waitpid(pid, &pid_status, 0);
866 	}
867 
868 	return 0;
869 }
870