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