/*
 * Name: syscalls.c
 * Description: This module contains wrappers for system calls that are
 *     different on various systems All system dependent C-code should go
 *     here. The interface of this module is defined in "syshdr.h".
 * Author: Christian Starkjohann <cs@hal.kph.tuwien.ac.at>
 * Date: 1996-12-14
 * Copyright: GNU-GPL
 * Tabsize: 4
 */

#define NFSCLIENT
#include "syshdr.h"
#include <rpc/rpc.h>
#if !(defined HP || defined IRIX)
#include "nfs_prot.h"
#endif
#include <netdb.h>
#include <arpa/inet.h>
#ifdef __NetBSD__
#include <nfs/nfsmount.h>
#if __NetBSD_Version__ >= 299000900
#define statfs statvfs
#endif
#endif
#include "my_defines.h"

/* --------------------------- general constants --------------------------- */

#define NFS_TIMEOUT_SECONDS		10	/* nfs timeout in seconds */

/* ------------------------------------------------------------------------- */
#ifdef NeXT
#include <nfs/nfs_mount.h>
int	syscall_mount(char *dir, void *root_fh, int sock_fd,
									struct sockaddr_in *socket, char *fsname)
{
struct nfs_args		nfs_args;

	bzero(&nfs_args, sizeof(nfs_args));
	nfs_args.wsize		= 8192;
	nfs_args.rsize		= 8192;
	nfs_args.retrans	= 0;
	nfs_args.timeo		= NFS_TIMEOUT_SECONDS * 10;	/* timeout is in 100ms */
	nfs_args.addr		= socket;
	nfs_args.flags		= NFSMNT_SOFT | NFSMNT_HOSTNAME |
							NFSMNT_RETRANS | NFSMNT_TIMEO |
							NFSMNT_WSIZE | NFSMNT_RSIZE;
	nfs_args.fh			= (char *)root_fh;
	nfs_args.hostname	= "shlight";
	nfs_args.netname	= "net";
	return mount(MOUNT_NFS, dir, 0, (caddr_t)&nfs_args);
}

int syscall_unmount(char* dir)
{
	return unmount(dir);
}
#endif
/* ------------------------------------------------------------------------- */
#ifdef BSD4_4
#define NFSX_V2FH 32
#ifdef BSD4_4_LITE2
#	include <nfs/rpcv2.h>
#	include <nfs/nfs.h>
#endif
#ifdef NETBSD
# include <nfs/nfsmount.h>
#endif
int	syscall_mount(char *dir, void *root_fh, int sock_fd,
									struct sockaddr_in *socket, char *mntfrom)
{
struct nfs_args		nfs_args;
#if defined(__FreeBSD__) || defined(__DragonFly__)
#	ifdef _NEW_VFSCONF
		struct vfsconf vfc;
		int error;
#	else
		struct vfsconf* vfc;
#	endif
#endif

	bzero(&nfs_args, sizeof(nfs_args));
#if defined(BSD4_4_LITE2) || defined(NETBSD)
	nfs_args.version	= NFS_ARGSVERSION;
#endif
	nfs_args.wsize		= 8192;
	nfs_args.rsize		= 8192;
	nfs_args.retrans	= 0;
	nfs_args.timeo		= NFS_TIMEOUT_SECONDS * 10;	/* timeout in 100ms */
	nfs_args.addr		= (struct sockaddr*)socket;
	nfs_args.addrlen	= sizeof(*socket);
	nfs_args.sotype		= SOCK_DGRAM;
	nfs_args.flags		= NFSMNT_SOFT |
							NFSMNT_RETRANS | NFSMNT_TIMEO |
							NFSMNT_WSIZE | NFSMNT_RSIZE;
	nfs_args.fh			= (char *)root_fh;
	nfs_args.fhsize		= NFSX_V2FH;
	nfs_args.hostname	= mntfrom;
#ifdef BSD4_4_LITE2
#	define NFSNAME "nfs"
#else
#	define NFSNAME MOUNT_NFS
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
#	ifdef _NEW_VFSCONF
#		define GETVFSBYNAME	error = getvfsbyname("nfs", &vfc)
#		define VFCERROR		error
#		define VFCNFSNAME	vfc.vfc_name
#	else
#		define GETVFSBYNAME	vfc = getvfsbyname("nfs")
#		define VFCERROR		!vfc
#		define VFCNFSNAME	vfc->vfc_index
#	endif
	GETVFSBYNAME;
	if (VFCERROR && vfsisloadable("nfs")) {
		if(vfsload("nfs"))
			return -1;
		endvfsent();	/* clear cache */
		GETVFSBYNAME;
	}
	if (VFCERROR)
		return -1;
	return mount(VFCNFSNAME, dir, 0, &nfs_args);
#elif defined(__NetBSD__) && __NetBSD_Version__ >=499002400
	return mount(NFSNAME, dir, 0, &nfs_args, sizeof(nfs_args));
#else	/* __FreeBSD__ */
	return mount(NFSNAME, dir, 0, &nfs_args);
#endif	/* __FreeBSD__ */
}

int syscall_unmount(char* dir)
{
struct statfs* mntbuf;
int count, i;

	if ((count = getmntinfo(&mntbuf, MNT_NOWAIT)) < 0)
		return -1;
	for (i=0; i<count; i++) {
		if (strcmp(mntbuf[i].f_mntonname, dir) == 0) {
			return unmount(dir,0);
		}
	}
	errno = ENOENT;
	return -1;
}

#endif	/* BSD4_4 */
/* ------------------------------------------------------------------------- */
#ifdef linux
#  include <linux/nfs_mount.h>
int	syscall_mount(char *dir, void *root_fh, int sock_fd,
								struct sockaddr_in *sockaddr, char *fsname)
{
struct nfs_mount_data	nfs_args;
int						ksock, kport;
struct sockaddr_in		kaddr;

/*
 * We have to do the connect for the kernel before we can call mount().
 */
	ksock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(ksock < 0){
		eprintf("syscall_mount: socket: [%d] %s\n", errno, strerror(errno));
		exit(1);
	}
	kaddr.sin_family = AF_INET;
	kaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	for(kport = IPPORT_RESERVED - 1; kport > IPPORT_RESERVED/2; kport--){
		kaddr.sin_port = htons(kport);
		if(bind(ksock, (struct sockaddr *)&kaddr, sizeof(kaddr)) >= 0)
			break;
	}
	if (kport <= IPPORT_RESERVED/2){
		eprintf("syscall_mount: bind to reserved port: [%d] %s\n",
													errno, strerror(errno));
		exit(1);
	}
	bzero(&nfs_args, sizeof(nfs_args));
	nfs_args.version	= NFS_MOUNT_VERSION;
	nfs_args.fd			= ksock;
	nfs_args.flags		= NFS_MOUNT_SOFT;
	nfs_args.wsize		= 8192;
	nfs_args.rsize		= 8192;
	nfs_args.retrans	= 0;
	nfs_args.timeo		= NFS_TIMEOUT_SECONDS * 10;	/* timeout in 100ms */
	nfs_args.acregmin = 3;		/* 3s */
	nfs_args.acregmax = 10;		/* 10s */
	nfs_args.acdirmin = 10;		/* 10s */
	nfs_args.acdirmax = 10;		/* 10s */
	nfs_args.addr		= *sockaddr;
	nfs_args.root		= *(nfs_fh *)root_fh;
	strcpy(nfs_args.hostname, "shlight");

	if(connect(ksock, (struct sockaddr *)&nfs_args.addr,
												sizeof(nfs_args.addr)) < 0){
		eprintf("syscall_mount: connect: [%d] %s\n", errno, strerror(errno));
		exit(1);
	}
	return mount("shlight:/", dir, "nfs", 0xc0ed0000, &nfs_args);
}

int syscall_unmount(char* dir)
{
	return umount(dir);
}
#endif	/* linux */
/* ------------------------------------------------------------------------- */
#ifdef HP

#include <nfs/nfs.h>

int	syscall_mount(char *dir, void *root_fh, int sock_fd,
						struct sockaddr_in *socket, char *fsname)
{
struct nfs_args		nfs_args;

	bzero(&nfs_args, sizeof(nfs_args));
	nfs_args.wsize		= 8192;
	nfs_args.rsize		= 8192;
	nfs_args.retrans	= 2;
	nfs_args.timeo		= NFS_TIMEOUT_SECONDS * 10;	/* timeout in 100ms */
	nfs_args.addr		= socket;
	nfs_args.flags		= NFSMNT_INT  | NFSMNT_HOSTNAME | NFSMNT_RETRANS | 
							 NFSMNT_TIMEO ;
	nfs_args.fh			= (char *)root_fh;
	nfs_args.hostname	= "shlight";
	return vfsmount(MOUNT_NFS, dir, 0, (caddr_t)&nfs_args);
}

int syscall_unmount(char *dir)
{
	return umount(dir);
}

#endif /* HP */
/* ------------------------------------------------------------------------- */
#ifdef SOLARIS
#  include <netconfig.h>
#  include <nfs/mount.h>
int	syscall_mount(char *dir, void *root_fh, int sock_fd,
									struct sockaddr_in *socket, char *fsname)
{
struct nfs_args		nfs_args;
struct netbuf nb;
struct netconfig *nc;
struct knetconfig kn;
struct stat stb;

	nb.buf = (char*)socket;
	nb.maxlen = nb.len = sizeof(struct sockaddr_in);

	nc = getnetconfigent("udp");
	if(!nc)
		return -1;

	bzero(&kn, sizeof(kn));
	kn.knc_semantics = nc->nc_semantics;
	kn.knc_protofmly = nc->nc_protofmly;
	kn.knc_proto = nc->nc_proto;
	if(stat(nc->nc_device, &stb) == -1)
		return -1;
	kn.knc_rdev = stb.st_rdev;

	bzero(&nfs_args, sizeof(nfs_args));
	nfs_args.wsize		= 8192;
	nfs_args.rsize		= 8192;
	nfs_args.retrans	= 0;
	nfs_args.timeo		= NFS_TIMEOUT_SECONDS * 10;	/* timeout in 100ms */
	nfs_args.addr		= &nb;
	nfs_args.knconf		= &kn;
	nfs_args.flags		= NFSMNT_SOFT | NFSMNT_HOSTNAME |
							NFSMNT_KNCONF |
							NFSMNT_RETRANS | NFSMNT_TIMEO |
							NFSMNT_WSIZE | NFSMNT_RSIZE;
	nfs_args.fh			= (char *)root_fh;
	nfs_args.hostname	= "shlight";
	return mount("", dir, MS_DATA, "nfs", (caddr_t)&nfs_args, sizeof(nfs_args));
}

int syscall_unmount(char *dir)
{
	return umount(dir);
}

#endif	/* SOLARIS */
/* ------------------------------------------------------------------------- */
#ifdef IRIX
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/fs/nfs.h>
#include <sys/fs/nfs_clnt.h>
#include <sys/fsid.h>

int syscall_mount(char *dir, void *root_fh, int sock_fd,
                		struct sockaddr_in *socket, char *fsname)
{
struct nfs_args	nfs_args;
int				findex;
	
	findex = sysfs(GETFSIND, FSID_NFS);
	bzero(&nfs_args, sizeof(nfs_args));
	
	nfs_args.wsize          = 8192;
	nfs_args.rsize          = 8192;
	nfs_args.retrans        = 0;
	nfs_args.timeo          = NFS_TIMEOUT_SECONDS * 10;  /* timeout in 100ms */
	nfs_args.addr           = socket;
	nfs_args.acregmin = 3;		/* 3s */
	nfs_args.acregmax = 10;		/* 10s */
	nfs_args.acdirmin = 10;		/* 10s */
	nfs_args.acdirmax = 10;		/* 10s */
	
	nfs_args.flags          = ( NFSMNT_SOFT | NFSMNT_HOSTNAME |
								NFSMNT_RETRANS | NFSMNT_TIMEO |
								NFSMNT_WSIZE | NFSMNT_RSIZE );
	nfs_args.fh                     = (fhandle_t *)root_fh;
	nfs_args.hostname       = "shlight";
/*	printf("Mounting on %s (%d)\n",dir, findex);*/
	return mount(dir, dir, (MS_FSS|MS_DATA),findex, &nfs_args, sizeof(struct nfs_args));
}

int syscall_unmount(char *dir)
{
	return umount(dir);
}

#endif
/* ------------------------------------------------------------------------- */
#ifdef __bsdi__
#define NFSX_V2FH 32
#define       NFS_NPROCS              23
#include <nfs/rpcv2.h>
#include <nfs/nfs.h>
int     syscall_mount(char *dir, void *root_fh, int sock_fd,
        struct sockaddr_in *socket, char *mntfrom)
{
struct nfs_args         nfs_args;

        bzero(&nfs_args, sizeof(nfs_args));
        nfs_args.wsize          = 8192;
        nfs_args.rsize          = 8192;
        nfs_args.retrans        = 0;
        nfs_args.timeo          = NFS_TIMEOUT_SECONDS * 10;
        nfs_args.addr           = (struct sockaddr*)socket;
        nfs_args.addrlen        = sizeof(*socket);
        nfs_args.sotype         = SOCK_DGRAM;
        nfs_args.flags          = NFSMNT_SOFT |
                                NFSMNT_RETRANS | NFSMNT_TIMEO |
                                NFSMNT_WSIZE | NFSMNT_RSIZE;
        nfs_args.fh             = (nfsv2fh_t *)root_fh;
        nfs_args.hostname       = mntfrom;
#define NFSNAME MOUNT_NFS
        return mount(NFSNAME, dir, 0, (caddr_t)&nfs_args);
}

int syscall_unmount(char* dir)
{
struct statfs* mntbuf;
int count, i;

        if ((count = getmntinfo(&mntbuf, MNT_NOWAIT)) < 0)
                return -1;
        for (i=0; i<count; i++) {
                if (strcmp(mntbuf[i].f_mntonname, dir) == 0) {
                        return unmount(dir,0);
                }
        }
        errno = ENOENT;
        return -1;
}

#endif
/* ------------------------------------------------------------------------- */
/*
 * Mtab magic. Only done where not done by the mount system call.
 */
#if defined NeXT || defined linux || defined HP || defined IRIX
#include <mntent.h>

#define	MTAB_TMP	(MOUNTED ".tmp")

int	syscall_insert_mtab(char *mountpoint, char *fsname)
{
FILE			*fp;
struct mntent	mnt;

	bzero(&mnt, sizeof(mnt));
	mnt.mnt_fsname = fsname;
	mnt.mnt_dir = mountpoint;
	mnt.mnt_type = "nfs";
	mnt.mnt_opts ="soft";
	mnt.mnt_freq = mnt.mnt_passno = 0;
#ifdef HP
	mnt.mnt_time = time(NULL);
#endif
	if((fp = setmntent(MOUNTED, "a")))
		addmntent(fp, &mnt);
	else
		return -1;
	endmntent(fp);
	return 0;
}

int	syscall_delete_mtab(char *mountpoint)
{
FILE			*fpout, *fpin;
struct mntent	*ent;

	if((fpout = setmntent(MTAB_TMP, "w")) == NULL){
		return -1;
	}else if((fpin = setmntent(MOUNTED, "r")) == NULL){
		endmntent(fpout);
		unlink(MTAB_TMP);
		return -1;
	}else{
		while((ent = getmntent(fpin)) != NULL){
			if(strcmp(ent->mnt_dir, mountpoint) != 0)
				addmntent(fpout, ent);
		}
		endmntent(fpin);
		endmntent(fpout);
		if(rename(MTAB_TMP, MOUNTED) != 0){
			unlink(MTAB_TMP);
			return -1;
		}
	}
	return 0;
}
#endif	/* defined NeXT || defined linux || defined HP || defined IRIX */
/* ------------------------------------------------------------------------- */
#ifdef SOLARIS

#include <sys/mnttab.h>

#define	MTAB_TMP	(MNTTAB ".tmp")

static int lock_mnttab_fd = -1;

static	int lock_mnttab(void)
{
int 			fd;
struct flock	fl;
int				my_errno;

	if(lock_mnttab_fd != -1)
		return FALSE;

	fd = open("/etc/.mnttab.lock", O_RDWR|O_CREAT|O_TRUNC, 0644);
	if(fd < 0) {
		return FALSE;
	}
	bzero(&fl, sizeof(fl));
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_CUR;
	if(fcntl(fd, F_SETLKW, &fl) == -1) {
		my_errno = errno;
		close(fd);
		errno = my_errno;
		return FALSE;
	}
	lock_mnttab_fd = fd;
	return TRUE;
}

static	void unlock_mnttab()
{
	if(lock_mnttab_fd != -1) {
		close(lock_mnttab_fd);
		lock_mnttab_fd = -1;
	}
}

int	syscall_insert_mtab(char *mountpoint, char *fsname)
{
FILE			*fp;
struct mnttab	mnt;
char			mnttime[20];

	bzero(&mnt, sizeof(mnt));
	mnt.mnt_special = fsname;
	mnt.mnt_mountp = mountpoint;
	mnt.mnt_fstype = "nfs";
	mnt.mnt_mntopts ="soft";
	sprintf(mnttime, "%ld", time(NULL));
	mnt.mnt_time = mnttime;
	if(!lock_mnttab()){		/* mtab file busy, try again */
		sleep(1);
		if(!lock_mnttab())	/* don't wait for ever.... */
			return -1;
	}
	if((fp = fopen(MNTTAB, "a")))
		putmntent(fp, &mnt);
	else{
		unlock_mnttab();
		return -1;
	}
	fclose(fp);
	unlock_mnttab();
	return 0;
}

int	syscall_delete_mtab(char *mountpoint)
{
FILE			*fpout, *fpin;
struct mnttab	ent[1];

	if(!lock_mnttab()){		/* mtab file busy, try again */
		sleep(1);
		if(!lock_mnttab())	/* don't wait for ever.... */
			return -1;
	}
	if((fpout = fopen(MTAB_TMP, "w")) == NULL){
		unlock_mnttab();
		return -1;
	}else if((fpin = fopen(MNTTAB, "r")) == NULL){
		fclose(fpout);
		unlink(MTAB_TMP);
		unlock_mnttab();
		return -1;
	}else{
		while((getmntent(fpin, ent)) == 0){
			if(strcmp(ent->mnt_mountp, mountpoint) != 0)
				putmntent(fpout, ent);
		}
		fclose(fpin);
		fclose(fpout);
		if(rename(MTAB_TMP, MNTTAB) != 0){
			unlink(MTAB_TMP);
			unlock_mnttab();
			return -1;
		}
	}
	unlock_mnttab();
	return 0;
}
#endif
/* ------------------------------------------------------------------------- */
#if defined BSD4_4
int	syscall_insert_mtab(char *mountpoint, char *fsname)
{
	return 0;
}

int	syscall_delete_mtab(char *mountpoint)
{
	return 0;
}
#endif /* defined BSD4_4 */
/* ------------------------------------------------------------------------- */
