xref: /freebsd/contrib/wpa/src/utils/os_unix.c (revision d4eeb02986980bf33dd56c41ceb9fc5f180c0d47)
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 
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 
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 
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 
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 
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>
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 
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 
250 void os_daemonize_terminate(const char *pid_file)
251 {
252 	if (pid_file)
253 		unlink(pid_file);
254 }
255 
256 
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 
286 unsigned long os_random(void)
287 {
288 	return random();
289 }
290 
291 
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 
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 
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 
402 int os_setenv(const char *name, const char *value, int overwrite)
403 {
404 	return setenv(name, value, overwrite);
405 }
406 
407 
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 
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 
458 int os_file_exists(const char *fname)
459 {
460 	return access(fname, F_OK) == 0;
461 }
462 
463 
464 #if !defined __DragonFly__
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
486 void * os_zalloc(size_t size)
487 {
488 	return calloc(1, size);
489 }
490 #endif /* WPA_TRACE */
491 
492 
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 
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 
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 char wpa_trace_fail_func[256] = { 0 };
546 unsigned int wpa_trace_fail_after;
547 
548 static int testing_fail_alloc(void)
549 {
550 	const char *func[WPA_TRACE_LEN];
551 	size_t i, res, len;
552 	char *pos, *next;
553 	int match;
554 
555 	if (!wpa_trace_fail_after)
556 		return 0;
557 
558 	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
559 	i = 0;
560 	if (i < res && os_strcmp(func[i], __func__) == 0)
561 		i++;
562 	if (i < res && os_strcmp(func[i], "os_malloc") == 0)
563 		i++;
564 	if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
565 		i++;
566 	if (i < res && os_strcmp(func[i], "os_calloc") == 0)
567 		i++;
568 	if (i < res && os_strcmp(func[i], "os_realloc") == 0)
569 		i++;
570 	if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
571 		i++;
572 	if (i < res && os_strcmp(func[i], "os_strdup") == 0)
573 		i++;
574 	if (i < res && os_strcmp(func[i], "os_memdup") == 0)
575 		i++;
576 
577 	pos = wpa_trace_fail_func;
578 
579 	match = 0;
580 	while (i < res) {
581 		int allow_skip = 1;
582 		int maybe = 0;
583 
584 		if (*pos == '=') {
585 			allow_skip = 0;
586 			pos++;
587 		} else if (*pos == '?') {
588 			maybe = 1;
589 			pos++;
590 		}
591 		next = os_strchr(pos, ';');
592 		if (next)
593 			len = next - pos;
594 		else
595 			len = os_strlen(pos);
596 		if (os_memcmp(pos, func[i], len) != 0) {
597 			if (maybe && next) {
598 				pos = next + 1;
599 				continue;
600 			}
601 			if (allow_skip) {
602 				i++;
603 				continue;
604 			}
605 			return 0;
606 		}
607 		if (!next) {
608 			match = 1;
609 			break;
610 		}
611 		pos = next + 1;
612 		i++;
613 	}
614 	if (!match)
615 		return 0;
616 
617 	wpa_trace_fail_after--;
618 	if (wpa_trace_fail_after == 0) {
619 		wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
620 			   wpa_trace_fail_func);
621 		for (i = 0; i < res; i++)
622 			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
623 				   (int) i, func[i]);
624 		return 1;
625 	}
626 
627 	return 0;
628 }
629 
630 
631 char wpa_trace_test_fail_func[256] = { 0 };
632 unsigned int wpa_trace_test_fail_after;
633 
634 int testing_test_fail(void)
635 {
636 	const char *func[WPA_TRACE_LEN];
637 	size_t i, res, len;
638 	char *pos, *next;
639 	int match;
640 
641 	if (!wpa_trace_test_fail_after)
642 		return 0;
643 
644 	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
645 	i = 0;
646 	if (i < res && os_strcmp(func[i], __func__) == 0)
647 		i++;
648 
649 	pos = wpa_trace_test_fail_func;
650 
651 	match = 0;
652 	while (i < res) {
653 		int allow_skip = 1;
654 		int maybe = 0;
655 
656 		if (*pos == '=') {
657 			allow_skip = 0;
658 			pos++;
659 		} else if (*pos == '?') {
660 			maybe = 1;
661 			pos++;
662 		}
663 		next = os_strchr(pos, ';');
664 		if (next)
665 			len = next - pos;
666 		else
667 			len = os_strlen(pos);
668 		if (os_memcmp(pos, func[i], len) != 0) {
669 			if (maybe && next) {
670 				pos = next + 1;
671 				continue;
672 			}
673 			if (allow_skip) {
674 				i++;
675 				continue;
676 			}
677 			return 0;
678 		}
679 		if (!next) {
680 			match = 1;
681 			break;
682 		}
683 		pos = next + 1;
684 		i++;
685 	}
686 	if (!match)
687 		return 0;
688 
689 	wpa_trace_test_fail_after--;
690 	if (wpa_trace_test_fail_after == 0) {
691 		wpa_printf(MSG_INFO, "TESTING: fail at %s",
692 			   wpa_trace_test_fail_func);
693 		for (i = 0; i < res; i++)
694 			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
695 				   (int) i, func[i]);
696 		return 1;
697 	}
698 
699 	return 0;
700 }
701 
702 #else
703 
704 static inline int testing_fail_alloc(void)
705 {
706 	return 0;
707 }
708 #endif
709 
710 void * os_malloc(size_t size)
711 {
712 	struct os_alloc_trace *a;
713 
714 	if (testing_fail_alloc())
715 		return NULL;
716 
717 	a = malloc(sizeof(*a) + size);
718 	if (a == NULL)
719 		return NULL;
720 	a->magic = ALLOC_MAGIC;
721 	dl_list_add(&alloc_list, &a->list);
722 	a->len = size;
723 	wpa_trace_record(a);
724 	return a + 1;
725 }
726 
727 
728 void * os_realloc(void *ptr, size_t size)
729 {
730 	struct os_alloc_trace *a;
731 	size_t copy_len;
732 	void *n;
733 
734 	if (ptr == NULL)
735 		return os_malloc(size);
736 
737 	a = (struct os_alloc_trace *) ptr - 1;
738 	if (a->magic != ALLOC_MAGIC) {
739 		wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
740 			   a, a->magic,
741 			   a->magic == FREED_MAGIC ? " (already freed)" : "");
742 		wpa_trace_show("Invalid os_realloc() call");
743 		abort();
744 	}
745 	n = os_malloc(size);
746 	if (n == NULL)
747 		return NULL;
748 	copy_len = a->len;
749 	if (copy_len > size)
750 		copy_len = size;
751 	os_memcpy(n, a + 1, copy_len);
752 	os_free(ptr);
753 	return n;
754 }
755 
756 
757 void os_free(void *ptr)
758 {
759 	struct os_alloc_trace *a;
760 
761 	if (ptr == NULL)
762 		return;
763 	a = (struct os_alloc_trace *) ptr - 1;
764 	if (a->magic != ALLOC_MAGIC) {
765 		wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
766 			   a, a->magic,
767 			   a->magic == FREED_MAGIC ? " (already freed)" : "");
768 		wpa_trace_show("Invalid os_free() call");
769 		abort();
770 	}
771 	dl_list_del(&a->list);
772 	a->magic = FREED_MAGIC;
773 
774 	wpa_trace_check_ref(ptr);
775 	free(a);
776 }
777 
778 
779 void * os_zalloc(size_t size)
780 {
781 	void *ptr = os_malloc(size);
782 	if (ptr)
783 		os_memset(ptr, 0, size);
784 	return ptr;
785 }
786 
787 
788 char * os_strdup(const char *s)
789 {
790 	size_t len;
791 	char *d;
792 	len = os_strlen(s);
793 	d = os_malloc(len + 1);
794 	if (d == NULL)
795 		return NULL;
796 	os_memcpy(d, s, len);
797 	d[len] = '\0';
798 	return d;
799 }
800 
801 #endif /* WPA_TRACE */
802 
803 
804 int os_exec(const char *program, const char *arg, int wait_completion)
805 {
806 	pid_t pid;
807 	int pid_status;
808 
809 	pid = fork();
810 	if (pid < 0) {
811 		perror("fork");
812 		return -1;
813 	}
814 
815 	if (pid == 0) {
816 		/* run the external command in the child process */
817 		const int MAX_ARG = 30;
818 		char *_program, *_arg, *pos;
819 		char *argv[MAX_ARG + 1];
820 		int i;
821 
822 		_program = os_strdup(program);
823 		_arg = os_strdup(arg);
824 
825 		argv[0] = _program;
826 
827 		i = 1;
828 		pos = _arg;
829 		while (i < MAX_ARG && pos && *pos) {
830 			while (*pos == ' ')
831 				pos++;
832 			if (*pos == '\0')
833 				break;
834 			argv[i++] = pos;
835 			pos = os_strchr(pos, ' ');
836 			if (pos)
837 				*pos++ = '\0';
838 		}
839 		argv[i] = NULL;
840 
841 		execv(program, argv);
842 		perror("execv");
843 		os_free(_program);
844 		os_free(_arg);
845 		exit(0);
846 		return -1;
847 	}
848 
849 	if (wait_completion) {
850 		/* wait for the child process to complete in the parent */
851 		waitpid(pid, &pid_status, 0);
852 	}
853 
854 	return 0;
855 }
856