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