xref: /freebsd/contrib/wpa/src/utils/os_unix.c (revision 31d62a73c2e6ac0ff413a7a17700ffc7dce254ef)
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 	wpa_trace_deinit();
403 #endif /* WPA_TRACE */
404 }
405 
406 
407 int os_setenv(const char *name, const char *value, int overwrite)
408 {
409 	return setenv(name, value, overwrite);
410 }
411 
412 
413 int os_unsetenv(const char *name)
414 {
415 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \
416     defined(__OpenBSD__)
417 	unsetenv(name);
418 	return 0;
419 #else
420 	return unsetenv(name);
421 #endif
422 }
423 
424 
425 char * os_readfile(const char *name, size_t *len)
426 {
427 	FILE *f;
428 	char *buf;
429 	long pos;
430 
431 	f = fopen(name, "rb");
432 	if (f == NULL)
433 		return NULL;
434 
435 	if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
436 		fclose(f);
437 		return NULL;
438 	}
439 	*len = pos;
440 	if (fseek(f, 0, SEEK_SET) < 0) {
441 		fclose(f);
442 		return NULL;
443 	}
444 
445 	buf = os_malloc(*len);
446 	if (buf == NULL) {
447 		fclose(f);
448 		return NULL;
449 	}
450 
451 	if (fread(buf, 1, *len, f) != *len) {
452 		fclose(f);
453 		os_free(buf);
454 		return NULL;
455 	}
456 
457 	fclose(f);
458 
459 	return buf;
460 }
461 
462 
463 int os_file_exists(const char *fname)
464 {
465 	return access(fname, F_OK) == 0;
466 }
467 
468 
469 int os_fdatasync(FILE *stream)
470 {
471 	if (!fflush(stream)) {
472 #ifdef __linux__
473 		return fdatasync(fileno(stream));
474 #else /* !__linux__ */
475 #ifdef F_FULLFSYNC
476 		/* OS X does not implement fdatasync(). */
477 		return fcntl(fileno(stream), F_FULLFSYNC);
478 #else /* F_FULLFSYNC */
479 		return fsync(fileno(stream));
480 #endif /* F_FULLFSYNC */
481 #endif /* __linux__ */
482 	}
483 
484 	return -1;
485 }
486 
487 
488 #ifndef WPA_TRACE
489 void * os_zalloc(size_t size)
490 {
491 	return calloc(1, size);
492 }
493 #endif /* WPA_TRACE */
494 
495 
496 size_t os_strlcpy(char *dest, const char *src, size_t siz)
497 {
498 	const char *s = src;
499 	size_t left = siz;
500 
501 	if (left) {
502 		/* Copy string up to the maximum size of the dest buffer */
503 		while (--left != 0) {
504 			if ((*dest++ = *s++) == '\0')
505 				break;
506 		}
507 	}
508 
509 	if (left == 0) {
510 		/* Not enough room for the string; force NUL-termination */
511 		if (siz != 0)
512 			*dest = '\0';
513 		while (*s++)
514 			; /* determine total src string length */
515 	}
516 
517 	return s - src - 1;
518 }
519 
520 
521 int os_memcmp_const(const void *a, const void *b, size_t len)
522 {
523 	const u8 *aa = a;
524 	const u8 *bb = b;
525 	size_t i;
526 	u8 res;
527 
528 	for (res = 0, i = 0; i < len; i++)
529 		res |= aa[i] ^ bb[i];
530 
531 	return res;
532 }
533 
534 
535 #ifdef WPA_TRACE
536 
537 #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
538 char wpa_trace_fail_func[256] = { 0 };
539 unsigned int wpa_trace_fail_after;
540 
541 static int testing_fail_alloc(void)
542 {
543 	const char *func[WPA_TRACE_LEN];
544 	size_t i, res, len;
545 	char *pos, *next;
546 	int match;
547 
548 	if (!wpa_trace_fail_after)
549 		return 0;
550 
551 	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
552 	i = 0;
553 	if (i < res && os_strcmp(func[i], __func__) == 0)
554 		i++;
555 	if (i < res && os_strcmp(func[i], "os_malloc") == 0)
556 		i++;
557 	if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
558 		i++;
559 	if (i < res && os_strcmp(func[i], "os_calloc") == 0)
560 		i++;
561 	if (i < res && os_strcmp(func[i], "os_realloc") == 0)
562 		i++;
563 	if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
564 		i++;
565 	if (i < res && os_strcmp(func[i], "os_strdup") == 0)
566 		i++;
567 
568 	pos = wpa_trace_fail_func;
569 
570 	match = 0;
571 	while (i < res) {
572 		int allow_skip = 1;
573 		int maybe = 0;
574 
575 		if (*pos == '=') {
576 			allow_skip = 0;
577 			pos++;
578 		} else if (*pos == '?') {
579 			maybe = 1;
580 			pos++;
581 		}
582 		next = os_strchr(pos, ';');
583 		if (next)
584 			len = next - pos;
585 		else
586 			len = os_strlen(pos);
587 		if (os_memcmp(pos, func[i], len) != 0) {
588 			if (maybe && next) {
589 				pos = next + 1;
590 				continue;
591 			}
592 			if (allow_skip) {
593 				i++;
594 				continue;
595 			}
596 			return 0;
597 		}
598 		if (!next) {
599 			match = 1;
600 			break;
601 		}
602 		pos = next + 1;
603 		i++;
604 	}
605 	if (!match)
606 		return 0;
607 
608 	wpa_trace_fail_after--;
609 	if (wpa_trace_fail_after == 0) {
610 		wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
611 			   wpa_trace_fail_func);
612 		for (i = 0; i < res; i++)
613 			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
614 				   (int) i, func[i]);
615 		return 1;
616 	}
617 
618 	return 0;
619 }
620 
621 
622 char wpa_trace_test_fail_func[256] = { 0 };
623 unsigned int wpa_trace_test_fail_after;
624 
625 int testing_test_fail(void)
626 {
627 	const char *func[WPA_TRACE_LEN];
628 	size_t i, res, len;
629 	char *pos, *next;
630 	int match;
631 
632 	if (!wpa_trace_test_fail_after)
633 		return 0;
634 
635 	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
636 	i = 0;
637 	if (i < res && os_strcmp(func[i], __func__) == 0)
638 		i++;
639 
640 	pos = wpa_trace_test_fail_func;
641 
642 	match = 0;
643 	while (i < res) {
644 		int allow_skip = 1;
645 		int maybe = 0;
646 
647 		if (*pos == '=') {
648 			allow_skip = 0;
649 			pos++;
650 		} else if (*pos == '?') {
651 			maybe = 1;
652 			pos++;
653 		}
654 		next = os_strchr(pos, ';');
655 		if (next)
656 			len = next - pos;
657 		else
658 			len = os_strlen(pos);
659 		if (os_memcmp(pos, func[i], len) != 0) {
660 			if (maybe && next) {
661 				pos = next + 1;
662 				continue;
663 			}
664 			if (allow_skip) {
665 				i++;
666 				continue;
667 			}
668 			return 0;
669 		}
670 		if (!next) {
671 			match = 1;
672 			break;
673 		}
674 		pos = next + 1;
675 		i++;
676 	}
677 	if (!match)
678 		return 0;
679 
680 	wpa_trace_test_fail_after--;
681 	if (wpa_trace_test_fail_after == 0) {
682 		wpa_printf(MSG_INFO, "TESTING: fail at %s",
683 			   wpa_trace_test_fail_func);
684 		for (i = 0; i < res; i++)
685 			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
686 				   (int) i, func[i]);
687 		return 1;
688 	}
689 
690 	return 0;
691 }
692 
693 #else
694 
695 static inline int testing_fail_alloc(void)
696 {
697 	return 0;
698 }
699 #endif
700 
701 void * os_malloc(size_t size)
702 {
703 	struct os_alloc_trace *a;
704 
705 	if (testing_fail_alloc())
706 		return NULL;
707 
708 	a = malloc(sizeof(*a) + size);
709 	if (a == NULL)
710 		return NULL;
711 	a->magic = ALLOC_MAGIC;
712 	dl_list_add(&alloc_list, &a->list);
713 	a->len = size;
714 	wpa_trace_record(a);
715 	return a + 1;
716 }
717 
718 
719 void * os_realloc(void *ptr, size_t size)
720 {
721 	struct os_alloc_trace *a;
722 	size_t copy_len;
723 	void *n;
724 
725 	if (ptr == NULL)
726 		return os_malloc(size);
727 
728 	a = (struct os_alloc_trace *) ptr - 1;
729 	if (a->magic != ALLOC_MAGIC) {
730 		wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
731 			   a, a->magic,
732 			   a->magic == FREED_MAGIC ? " (already freed)" : "");
733 		wpa_trace_show("Invalid os_realloc() call");
734 		abort();
735 	}
736 	n = os_malloc(size);
737 	if (n == NULL)
738 		return NULL;
739 	copy_len = a->len;
740 	if (copy_len > size)
741 		copy_len = size;
742 	os_memcpy(n, a + 1, copy_len);
743 	os_free(ptr);
744 	return n;
745 }
746 
747 
748 void os_free(void *ptr)
749 {
750 	struct os_alloc_trace *a;
751 
752 	if (ptr == NULL)
753 		return;
754 	a = (struct os_alloc_trace *) ptr - 1;
755 	if (a->magic != ALLOC_MAGIC) {
756 		wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
757 			   a, a->magic,
758 			   a->magic == FREED_MAGIC ? " (already freed)" : "");
759 		wpa_trace_show("Invalid os_free() call");
760 		abort();
761 	}
762 	dl_list_del(&a->list);
763 	a->magic = FREED_MAGIC;
764 
765 	wpa_trace_check_ref(ptr);
766 	free(a);
767 }
768 
769 
770 void * os_zalloc(size_t size)
771 {
772 	void *ptr = os_malloc(size);
773 	if (ptr)
774 		os_memset(ptr, 0, size);
775 	return ptr;
776 }
777 
778 
779 char * os_strdup(const char *s)
780 {
781 	size_t len;
782 	char *d;
783 	len = os_strlen(s);
784 	d = os_malloc(len + 1);
785 	if (d == NULL)
786 		return NULL;
787 	os_memcpy(d, s, len);
788 	d[len] = '\0';
789 	return d;
790 }
791 
792 #endif /* WPA_TRACE */
793 
794 
795 int os_exec(const char *program, const char *arg, int wait_completion)
796 {
797 	pid_t pid;
798 	int pid_status;
799 
800 	pid = fork();
801 	if (pid < 0) {
802 		perror("fork");
803 		return -1;
804 	}
805 
806 	if (pid == 0) {
807 		/* run the external command in the child process */
808 		const int MAX_ARG = 30;
809 		char *_program, *_arg, *pos;
810 		char *argv[MAX_ARG + 1];
811 		int i;
812 
813 		_program = os_strdup(program);
814 		_arg = os_strdup(arg);
815 
816 		argv[0] = _program;
817 
818 		i = 1;
819 		pos = _arg;
820 		while (i < MAX_ARG && pos && *pos) {
821 			while (*pos == ' ')
822 				pos++;
823 			if (*pos == '\0')
824 				break;
825 			argv[i++] = pos;
826 			pos = os_strchr(pos, ' ');
827 			if (pos)
828 				*pos++ = '\0';
829 		}
830 		argv[i] = NULL;
831 
832 		execv(program, argv);
833 		perror("execv");
834 		os_free(_program);
835 		os_free(_arg);
836 		exit(0);
837 		return -1;
838 	}
839 
840 	if (wait_completion) {
841 		/* wait for the child process to complete in the parent */
842 		waitpid(pid, &pid_status, 0);
843 	}
844 
845 	return 0;
846 }
847