1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Various support routines.
31 */
32
33#include <libintl.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <errno.h>
38#include <dirent.h>
39#include <wait.h>
40#include <stdarg.h>
41#include <limits.h>
42#include <rpc/rpc.h>
43#include <rpc/pmap_clnt.h> /* for pmap_unset */
44#include <string.h> /* strcmp */
45#include <signal.h>
46#include <unistd.h> /* setsid */
47#include <sys/utsname.h>
48#include <sys/param.h>
49#include <sys/mnttab.h>
50#include <sys/vfstab.h>
51#include <sys/types.h>
52#include <sys/stat.h>
53#include <memory.h>
54#include <stropts.h>
55#include <netconfig.h>
56#include <sys/resource.h> /* rlimit */
57#include <thread.h>
58#include <synch.h>
59#include <mdbug/mdbug.h>
60#include <sys/fs/cachefs_fs.h>
61#include <sys/fs/cachefs_dlog.h>
62#include <sys/fs/cachefs_ioctl.h>
63#include "cfsd.h"
64#include "cfsd_kmod.h"
65#include "cfsd_maptbl.h"
66#include "cfsd_logfile.h"
67#include "cfsd_fscache.h"
68#include "cfsd_cache.h"
69#include "cfsd_all.h"
70#include <common/cachefsd.h>
71#include <common/subr.h>
72
73/* forward references */
74void *subr_mount_thread(void *datap);
75int subr_fsck_cache(const char *cachedirp);
76void subr_doexec(const char *fstype, char *newargv[], const char *progp);
77
78/*
79 *			subr_add_mount
80 *
81 * Description:
82 *	Adds the specified file system to the data structures.
83 * Arguments:
84 *	allp	ptr to set of data structures
85 *	dirp	ptr to name of cache directory
86 *	idp	ptr to id of file system cache in dirp
87 * Returns:
88 * Preconditions:
89 *	precond(allp)
90 *	precond(dirp)
91 *	precond(idp)
92 */
93void
94subr_add_mount(cfsd_all_object_t *all_object_p,
95	const char *dirp,
96	const char *idp)
97{
98	int xx;
99	thread_t new_thread;
100	cfsd_cache_object_t *cache_object_p;
101	cfsd_fscache_object_t *fscache_object_p;
102
103	dbug_enter("subr_add_mount");
104
105	dbug_precond(all_object_p);
106	dbug_precond(dirp);
107	dbug_precond(idp);
108
109	dbug_print(("info", "cachedir %s, cacheid %s", dirp, idp));
110
111	/* find or create the cache object */
112	all_lock(all_object_p);
113	cache_object_p = all_cachelist_find(all_object_p, dirp);
114	if (cache_object_p == NULL) {
115		/* make the cache object */
116		cache_object_p = cfsd_cache_create();
117		xx = all_object_p->i_nextcacheid;
118		xx = cache_setup(cache_object_p, dirp, xx);
119		if (xx == 0) {
120			dbug_print(("error", "invalid cache %s", dirp));
121			cfsd_cache_destroy(cache_object_p);
122			all_unlock(all_object_p);
123			dbug_leave("subr_add_mount");
124			return;
125		}
126		all_cachelist_add(all_object_p, cache_object_p);
127		all_cachefstab_update(all_object_p);
128	}
129	cache_lock(cache_object_p);
130	cache_object_p->i_refcnt++;
131	cache_unlock(cache_object_p);
132	all_unlock(all_object_p);
133
134	/* find or create the fscache object */
135	cache_lock(cache_object_p);
136	fscache_object_p = cache_fscachelist_find(cache_object_p, idp);
137	if (fscache_object_p == NULL) {
138		/* make the fscache object and add it to the list */
139		xx = cache_object_p->i_nextfscacheid;
140		fscache_object_p = cfsd_fscache_create(idp, dirp, xx);
141		cache_fscachelist_add(cache_object_p, fscache_object_p);
142	} else {
143		/* don't do any more if already mounted */
144		fscache_lock(fscache_object_p);
145		if (fscache_object_p->i_mounted) {
146			cache_object_p->i_refcnt--;
147			fscache_unlock(fscache_object_p);
148			cache_unlock(cache_object_p);
149			dbug_print(("info", "fscache already mounted"));
150			dbug_leave("subr_add_mount");
151			return;
152		}
153		fscache_unlock(fscache_object_p);
154	}
155
156	fscache_lock(fscache_object_p);
157	fscache_object_p->i_refcnt++;
158	fscache_unlock(fscache_object_p);
159	cache_unlock(cache_object_p);
160
161	/* init the fscache object with mount information */
162	fscache_lock(fscache_object_p);
163	fscache_setup(fscache_object_p);
164
165	/* start the disconnect thread if necessary */
166	if (fscache_object_p->i_disconnectable &&
167	    fscache_object_p->i_mounted &&
168	    (fscache_object_p->i_threaded == 0) &&
169	    (strcmp(fscache_object_p->i_name, "rootcache") != 0)) {
170		fscache_object_p->i_refcnt++;
171		fscache_object_p->i_threaded = 1;
172		xx = thr_create(NULL, 0, subr_mount_thread, fscache_object_p,
173		    THR_DETACHED | THR_NEW_LWP, &new_thread);
174		if (xx) {
175			/* XXX cachefs kmod cannot allow transition */
176			dbug_print(("error", "mount thr_create failed %d", xx));
177			fscache_object_p->i_refcnt--;
178			fscache_object_p->i_threaded = 0;
179		}
180		fscache_object_p->i_threadid = new_thread;
181	}
182	fscache_object_p->i_refcnt--;
183	fscache_unlock(fscache_object_p);
184
185	cache_lock(cache_object_p);
186	cache_object_p->i_refcnt--;
187	cache_unlock(cache_object_p);
188	dbug_leave("subr_add_mount");
189}
190
191/*
192 * ------------------------------------------------------------
193 *			subr_mount_thread
194 *
195 * Description:
196 *	Called when a thread is created via thr_create to process
197 *	an fscache.
198 * Arguments:
199 *	datap	ptr to cfsd_fscache to process
200 * Returns:
201 *	Returns NULL.
202 * Preconditions:
203 *	precond(datap)
204 */
205void *
206subr_mount_thread(void *datap)
207{
208	cfsd_fscache_object_t *fscache_object_p;
209
210	dbug_enter("subr_mount_thread");
211	dbug_precond(datap);
212
213	fscache_object_p = (cfsd_fscache_object_t *)datap;
214
215	fscache_process(fscache_object_p);
216
217	fscache_lock(fscache_object_p);
218
219	/* close down the message file descriptor */
220	if (fscache_object_p->i_ofd >= 0) {
221		if (close(fscache_object_p->i_ofd))
222			dbug_print(("error", "cannot close fscache fd error %d",
223			    errno));
224		fscache_object_p->i_ofd = -1;
225	}
226
227	fscache_object_p->i_threaded = 0;
228	fscache_object_p->i_refcnt--;
229	fscache_unlock(fscache_object_p);
230
231	dbug_leave("subr_mount_thread");
232	return (NULL);
233}
234
235/*
236 * ------------------------------------------------------------
237 *			subr_cache_setup
238 *
239 * Description:
240 *	Called once when the daemon starts up to get the current state
241 *	of caches reflected in the daemon.
242 * Arguments:
243 *	allp
244 * Returns:
245 * Preconditions:
246 *	precond(allp)
247 */
248void
249subr_cache_setup(cfsd_all_object_t *all_object_p)
250{
251	cfsd_cache_object_t *cache_object_p;
252	int fixcachefstab = 0;
253	int xx;
254	FILE *fin;
255	char buf[MAXPATHLEN];
256	struct mnttab minfo;
257	struct mnttab mpref;
258	char *cp;
259	char *xcp;
260	struct vfstab vinfo;
261	struct vfstab vpref;
262	size_t index;
263	int lockfd;
264	DIR *dirp;
265	char pathname[MAXPATHLEN];
266	int len;
267	struct dirent64 *entp;
268	struct stat64 sinfo;
269
270	dbug_enter("subr_cache_setup");
271	dbug_precond(all_object_p);
272
273	all_lock(all_object_p);
274
275	/* find all the caches indicated in the CACHEFSTAB file */
276	fin = fopen(CACHEFSTAB, "r");
277	if (fin == NULL) {
278		dbug_print(("info", "%s does not exist", CACHEFSTAB));
279	} else {
280		while (fgets(buf, sizeof (buf), fin) != NULL) {
281			if (strlen(buf) == 1)
282				continue;
283			/*
284			 * if the line did not fit in the buffer
285			 * it is invalid (i.e. no newline char)
286			 */
287			dbug_precond(buf[(strlen(buf) - 1)] == '\n');
288			if (buf[(strlen(buf) - 1)] != '\n') {
289#if 0
290				/*
291				 * if the line is invalid read until
292				 * you get to the next line.
293				 * we only need to do this if we are
294				 * going to continue
295				 */
296				do {
297					cp = fgets(buf, sizeof (buf), fin);
298				} while ((cp != NULL) &&
299				    (buf[(strlen(buf) - 1)] != '\n'));
300#endif
301				break;
302			}
303			buf[strlen(buf) - 1] = '\0';
304			dbug_print(("info", "cachefstab cache \"%s\"", buf));
305			cache_object_p = all_cachelist_find(all_object_p, buf);
306			if (cache_object_p == NULL) {
307				/* make the cache object */
308				cache_object_p = cfsd_cache_create();
309				xx = all_object_p->i_nextcacheid;
310				xx = cache_setup(cache_object_p, buf, xx);
311				if (xx == 0) {
312					cfsd_cache_destroy(cache_object_p);
313					fixcachefstab++;
314				} else {
315					all_cachelist_add(all_object_p,
316					    cache_object_p);
317				}
318			} else {
319				fixcachefstab++;
320			}
321		}
322		if (fclose(fin))
323			dbug_print(("err", "cannot close %s, %d",
324			    CACHEFSTAB, errno));
325	}
326
327	/* read the mnttab file looking for caches we may have missed */
328	fin = fopen(MNTTAB, "r");
329	if (fin == NULL) {
330		dbug_print(("info", "%s does not exist", MNTTAB));
331	} else {
332		mpref.mnt_special = NULL;
333		mpref.mnt_mountp = NULL;
334		mpref.mnt_fstype = "cachefs";
335		mpref.mnt_mntopts = NULL;
336		mpref.mnt_time = NULL;
337		while ((xx = getmntany(fin, &minfo, &mpref)) != -1) {
338			if (xx != 0)
339				continue;
340			cp = hasmntopt(&minfo, "cachedir=");
341			if (cp == NULL)
342				cp = "/cache"; /* XXX define in mount.c */
343			else {
344				cp += 9;
345				xcp = strchr(cp, ',');
346				if (xcp)
347					*xcp = '\0';
348			}
349			dbug_print(("info", "mnttab cache \"%s\"", cp));
350			cache_object_p = all_cachelist_find(all_object_p, cp);
351			if (cache_object_p == NULL) {
352				/* make the cache object */
353				cache_object_p = cfsd_cache_create();
354				xx = all_object_p->i_nextcacheid;
355				xx = cache_setup(cache_object_p, cp, xx);
356				if (xx == 0) {
357					cfsd_cache_destroy(cache_object_p);
358					fixcachefstab++;
359				} else {
360					all_cachelist_add(all_object_p,
361					    cache_object_p);
362				}
363			} else {
364				fixcachefstab++;
365			}
366		}
367		if (fclose(fin))
368			dbug_print(("err", "cannot close %s, %d",
369			    MNTTAB, errno));
370	}
371
372	/* read the vfstab file looking for caches we may have missed */
373	fin = fopen(VFSTAB, "r");
374	if (fin == NULL) {
375		dbug_print(("info", "%s does not exist", VFSTAB));
376	} else {
377		vpref.vfs_special = NULL;
378		vpref.vfs_fsckdev = NULL;
379		vpref.vfs_mountp = NULL;
380		vpref.vfs_fstype = "cachefs";
381		vpref.vfs_fsckpass = NULL;
382		vpref.vfs_automnt = NULL;
383		vpref.vfs_mntopts = NULL;
384		while ((xx = getvfsany(fin, &vinfo, &vpref)) != -1) {
385			if (xx != 0)
386				continue;
387			cp = strstr(vinfo.vfs_mntopts, "cachedir=");
388			if (cp == NULL)
389				cp = "/cache"; /* XXX define in mount.c */
390			else {
391				cp += 9;
392				xcp = strchr(cp, ',');
393				if (xcp)
394					*xcp = '\0';
395			}
396			dbug_print(("info", "vfstab cache \"%s\"", cp));
397			cache_object_p = all_cachelist_find(all_object_p, cp);
398			if (cache_object_p == NULL) {
399				/* make the cache object */
400				cache_object_p = cfsd_cache_create();
401				xx = all_object_p->i_nextcacheid;
402				xx = cache_setup(cache_object_p, cp, xx);
403				if (xx == 0) {
404					cfsd_cache_destroy(cache_object_p);
405				} else {
406					all_cachelist_add(all_object_p,
407					    cache_object_p);
408					fixcachefstab++;
409				}
410			}
411		}
412		if (fclose(fin))
413			dbug_print(("err", "cannot close %s, %d",
414			    VFSTAB, errno));
415	}
416
417	/* fix up the CACHEFSTAB file if it is out of date */
418	if (fixcachefstab)
419		all_cachefstab_update(all_object_p);
420
421	/*
422	 * now for each cache we found,
423	 * find all the file systems in the cache
424	 */
425	for (index = 0; index < all_object_p->i_cachecount; index++) {
426		cache_object_p = all_cachelist_at(all_object_p, index);
427		dbug_assert(cache_object_p);
428		cache_lock(cache_object_p);
429		cache_object_p->i_refcnt++;
430		cache_unlock(cache_object_p);
431		all_unlock(all_object_p);
432
433		/* fix up the cache if necessary */
434		xx = subr_fsck_cache(cache_object_p->i_cachedir);
435		if (xx != 0) {
436			dbug_print(("error", "could not fix up cache %d",
437			    cache_object_p->i_cachedir));
438			all_lock(all_object_p);
439			cache_lock(cache_object_p);
440			cache_object_p->i_refcnt--;
441			cache_unlock(cache_object_p);
442			continue;
443		}
444
445		/* lock out activity on the cache */
446		lockfd = cachefs_dir_lock(cache_object_p->i_cachedir, 0);
447		if (lockfd < 0) {
448			dbug_print(("error", "cannot aquire cache lock on %s",
449			    cache_object_p->i_cachedir));
450			all_lock(all_object_p);
451			cache_lock(cache_object_p);
452			cache_object_p->i_refcnt--;
453			cache_unlock(cache_object_p);
454			continue;
455		}
456
457		/* open the cache directory */
458		dirp = opendir(cache_object_p->i_cachedir);
459		if (dirp == NULL) {
460			dbug_print(("error", "cannot open dir %s",
461			    cache_object_p->i_cachedir));
462			cachefs_dir_unlock(lockfd);
463			all_lock(all_object_p);
464			cache_lock(cache_object_p);
465			cache_object_p->i_refcnt--;
466			cache_unlock(cache_object_p);
467			continue;
468		}
469
470		strlcpy(pathname, cache_object_p->i_cachedir,
471		    sizeof (pathname));
472		strlcat(pathname, "/", sizeof (pathname));
473		len = strlen(pathname);
474
475		/* read the directory entries */
476		while ((entp = readdir64(dirp)) != NULL) {
477			/* skip . and .. */
478			if ((strcmp(entp->d_name, ".") == 0) ||
479			    (strcmp(entp->d_name, "..") == 0))
480				continue;
481
482			pathname[len] = '\0';
483			strlcat(pathname, entp->d_name, sizeof (pathname));
484
485			/* get info on the file */
486			xx = lstat64(pathname, &sinfo);
487			if (xx != 0) {
488				dbug_print(("error",
489				    "cannot stat %s %d", pathname, errno));
490				continue;
491			}
492
493			/* skip unless a symbolic link */
494			if (!S_ISLNK(sinfo.st_mode))
495				continue;
496
497			/* add this file system to the list */
498			subr_add_mount(all_object_p, cache_object_p->i_cachedir,
499			    entp->d_name);
500		}
501		if (closedir(dirp))
502			dbug_print(("err", "cannot close dir, %d", errno));
503		cachefs_dir_unlock(lockfd);
504		all_lock(all_object_p);
505		cache_lock(cache_object_p);
506		cache_object_p->i_refcnt--;
507		cache_unlock(cache_object_p);
508	}
509
510	all_unlock(all_object_p);
511	dbug_leave("subr_cache_setup");
512}
513
514/*
515 * ------------------------------------------------------------
516 *			subr_fsck_cache
517 *
518 * Description:
519 *	Fixes the cache if necessary.
520 * Arguments:
521 *	cachedirp
522 * Returns:
523 *	Returns 0 for success !0 if the cache is not fixed.
524 * Preconditions:
525 *	precond(cachedirp)
526 */
527int
528subr_fsck_cache(const char *cachedirp)
529{
530	char *fsck_argv[4];
531	int status = 0;
532	pid_t pid;
533
534	dbug_enter("subr_fsck_cache");
535
536	dbug_precond(cachedirp);
537
538	fsck_argv[1] = "fsck";
539	fsck_argv[2] = (char *)cachedirp;
540	fsck_argv[3] = NULL;
541
542	dbug_print(("info", "about to fsck %s", cachedirp));
543
544	/* fork */
545	if ((pid = fork()) == -1) {
546		dbug_print(("error", "could not fork fsck %d", errno));
547		dbug_leave("subr_fsck_cache");
548		return (1);
549	}
550
551	if (pid == 0) {
552		/* do the fsck */
553		subr_doexec("cachefs", fsck_argv, "fsck");
554	} else {
555		/* wait for the child to exit */
556		if (waitpid(pid, &status, 0) == -1) {
557			dbug_print(("error", "fsck wait failed %d", errno));
558			dbug_leave("subr_fsck_cache");
559			return (1);
560		}
561
562		if (!WIFEXITED(status)) {
563			dbug_print(("error", "fsck did not exit"));
564			dbug_leave("subr_fsck_cache");
565			return (1);
566		}
567
568		if (WEXITSTATUS(status) != 0) {
569			dbug_print(("error", "fsck failed"));
570			dbug_leave("subr_fsck_cache");
571			return (1);
572		}
573	}
574	dbug_leave("subr_fsck_cache");
575	return (0);
576}
577
578/*
579 * ------------------------------------------------------------
580 *			subr_doexec
581 *
582 * Description:
583 *	Execs the specified program with the specified command line arguments.
584 *	This function never returns.
585 * Arguments:
586 *	fstype	type of file system
587 *	newargv	command line arguments
588 *	progp	name of program to exec
589 * Returns:
590 * Preconditions:
591 *	precond(fstype)
592 *	precond(newargv)
593 *	precond(progp)
594 */
595void
596subr_doexec(const char *fstype, char *newargv[], const char *progp)
597{
598#define	VFS_PATH	"/usr/lib/fs"
599#define	ALT_PATH	"/etc/fs"
600
601	char	full_path[MAXPATHLEN];
602	char	alter_path[MAXPATHLEN];
603	char	*vfs_path = VFS_PATH;
604	char	*alt_path = ALT_PATH;
605
606	dbug_enter("subr_doexec");
607
608	dbug_precond(fstype);
609	dbug_precond(newargv);
610	dbug_precond(progp);
611
612	/* build the full pathname of the fstype dependent command. */
613	snprintf(full_path, sizeof (full_path), "%s/%s/%s", vfs_path,
614	    fstype, progp);
615	snprintf(alter_path, sizeof (alter_path), "%s/%s/%s", alt_path,
616	    fstype, progp);
617
618	/* if the program exists */
619	if (access(full_path, X_OK) == 0) {
620		/* invoke the program */
621		execv(full_path, &newargv[1]);
622
623		/* if wrong permissions */
624		if (errno == EACCES) {
625			dbug_print(("error", "cannot execute %s %s",
626			    full_path, strerror(errno)));
627		}
628
629#ifdef OBSOLETE
630		/* if it did not work and the shell might make it */
631		if (errno == ENOEXEC) {
632			newargv[0] = "sh";
633			newargv[1] = full_path;
634			execv("/sbin/sh", &newargv[0]);
635		}
636#endif
637	}
638
639#ifdef OBSOLETE
640	/* try the alternate path */
641	execv(alter_path, &newargv[1]);
642
643	/* if wrong permissions */
644	if (errno == EACCES) {
645		dbug_print(("error", "cannot execute %s %s",
646		    alter_path, strerror(errno)));
647	}
648
649	/* if it did not work and the shell might make it */
650	if (errno == ENOEXEC) {
651		newargv[0] = "sh";
652		newargv[1] = alter_path;
653		execv("/sbin/sh", &newargv[0]);
654	}
655
656	dbug_print(("error", "operation not applicable to FSType %s", fstype));
657#endif
658	dbug_leave("subr_doexec");
659	_exit(1);
660}
661
662/*
663 * ------------------------------------------------------------
664 *			pr_err
665 *
666 * Description:
667 * Arguments:
668 *	fmt
669 * Returns:
670 * Preconditions:
671 *	precond(fmt)
672 */
673void
674pr_err(char *fmt, ...)
675{
676	va_list ap;
677
678	va_start(ap, fmt);
679	(void) fprintf(stderr, "cachefsd -F cachefs: ");
680	(void) vfprintf(stderr, fmt, ap);
681	(void) fprintf(stderr, "\n");
682	va_end(ap);
683}
684
685
686/*
687 *			subr_strdup
688 *
689 * Description:
690 *	Returns the string dupped.  Returns NULL if passed NULL.
691 *	Calls new to allocate memory.
692 * Arguments:
693 *	strp
694 * Returns:
695 * Preconditions:
696 */
697char *
698subr_strdup(const char *strp)
699{
700	char *retp = NULL;
701	int len;
702
703	if (strp) {
704		len = strlen(strp) + 1;
705		retp = cfsd_calloc(len);
706		if (retp)
707			strlcpy(retp, strp, len);
708	}
709	return (retp);
710}
711/*
712 * -----------------------------------------------------------------
713 *			cfsd_calloc
714 *
715 * Description:
716 *	allocates memory of a given size, will retry if error
717 * Arguments:
718 *	size
719 * Returns:
720 *	pointer to memory
721 * Preconditions:
722 *	precond(size)
723 */
724
725void *
726cfsd_calloc(int size)
727{
728	void *alloc_ptr;
729
730	dbug_enter("cfsd_calloc");
731	dbug_precond(size);
732
733	/* allocate memory, if calloc fails sleep and retry */
734	while ((alloc_ptr = calloc(size, 1)) == NULL) {
735		cfsd_sleep(5);
736	}
737
738	dbug_leave("cfsd_calloc");
739	return (alloc_ptr);
740}
741/*
742 * -----------------------------------------------------------------
743 *			cfsd_free
744 *
745 * Description:
746 *	frees memory allocated from cfsd_calloc
747 * Arguments:
748 *	pointer to memeory
749 * Returns:
750 *	none
751 * Preconditions:
752 *	precond(size)
753 */
754
755void
756cfsd_free(void *free_ptr)
757{
758	dbug_enter("cfsd_free");
759	dbug_precond(free_ptr);
760
761	/* free memory */
762	if (free_ptr)
763		free(free_ptr);
764
765	dbug_leave("cfsd_free");
766}
767/*
768 * -----------------------------------------------------------------
769 *			cfsd_sleep
770 *
771 * Description:
772 *	A reimplemenation of the sleep(3c) function call using
773 *	cond_timedwait.
774 *	Problem withe sleep(3c) hanging. May return early.
775 * Arguments:
776 *	sec	number of seconds to sleep for
777 * Returns:
778 * Preconditions:
779 */
780
781void
782cfsd_sleep(int sec)
783{
784	cond_t cv;
785	mutex_t mt;
786	timestruc_t reltime;
787
788	dbug_enter("cfsd_sleep");
789
790	if (sec > 0) {
791		mutex_init(&mt, USYNC_THREAD, NULL);
792		cond_init(&cv, USYNC_THREAD, 0);
793
794		reltime.tv_sec = sec;
795		reltime.tv_nsec = 0;
796
797		mutex_lock(&mt);
798		cond_reltimedwait(&cv, &mt, &reltime);
799		mutex_unlock(&mt);
800
801		cond_destroy(&cv);
802		mutex_destroy(&mt);
803	}
804	dbug_leave("cfsd_sleep");
805}
806