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