/* $Id$ * * Original version of this file from p3nfsd-5.4 by * Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) * * Modifications for plputils by Fritz Elfert * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if defined(__SVR4) || defined(__sgi) || defined(linux) #include #include #include /* strdup */ #ifndef linux extern int inet_addr(char *); #endif #define PORTMAP /* I need svcudp_create in rpc/svc_soc.h, sol 2.4 */ #endif #include "OSdefs.h" #include #include #include #include #ifdef __sgi #include #endif #if defined(__NetBSD__) || defined(__FreeBSD__) #include /* Needs NGROUPS for mount.h */ #define umount(dir) unmount(dir, 1) #endif #if defined(__NeXT__) #include #include #include #define umount(dir) unmount(dir) #endif static char nfshost[128]; #define NFSCLIENT #ifdef __FreeBSD__ #define NFS #endif /* __FreeBSD__ */ #include #ifdef __FreeBSD__ #include #include #include #include #include #if __FreeBSD_version >= 300001 #include #endif #endif /* __FreeBSD__ */ #ifdef __NetBSD__ #include #include #include #include #include #include #include #include #endif /* __NetBSD__ */ #include #include "nfs_prot.h" #ifdef linux #ifndef __GLIBC__ #include /* struct nfs_mount_data */ #endif #include "linux-misc.h" /* struct nfs_mount_data */ #include /* inet_addr() */ #endif #ifdef _IBMR2 #include "os-aix3.h" #include "misc-aix3.h" #include #endif #ifndef DONT_UPDATE_MTAB #if defined(sun) && defined(__SVR4) #include #else #include #endif #endif #if defined(sun) && defined(__SVR4) /*gec */ #include #include #include #include #include #define PORTMAP #endif #if defined(sun) && !defined(__SVR4) extern int _rpc_dtablesize(); #define umount unmount #endif #include #include #include "rfsv_api.h" #ifdef __sgi #define vfork fork #define NFSMNT_NOCTO 0 #endif #if defined(sun) && defined(__SVR4) #define setmntent fopen #define endmntent fclose #define addmntent putmntent #define mntent mnttab #endif static char *mntdir; /* where we mounted the psion */ #ifdef __STDC__ static void doexit(void); #endif static RETSIGTYPE usr1_handler SIGARG { debug = (debug + 1) & 3; infolog("Set debug level to %d\n", debug); }; static RETSIGTYPE hup_handler SIGARG { if (debug > 1) debuglog("Got HUP signal\n"); exiting = 5; }; static RETSIGTYPE term_handler SIGARG { if (debug > 1) debuglog("Got TERM signal\n"); exiting = 5; }; static void doexit() { #ifndef DONT_UPDATE_MTAB FILE *fpin, *fpout; #if defined(sun) && defined(__SVR4) struct mntent entr; #else struct mntent *ent; #endif #endif #if !defined(__FreeBSD__) && !defined(__NetBSD__) struct stat statb; #endif exiting--; debuglog("Doing exit\n"); #ifdef _IBMR2 if (stat(mntdir, &statb)) { errorlog("stat %s: %m\n", mntdir); return; } debuglog("Next call: uvmount(%d, 0)\n", statb.st_vfs); if (uvmount(statb.st_vfs, 0)) { errorlog("uvmount: %m\n"); return; } #else if (umount(mntdir)) { errorlog("umount %s: %m\n", mntdir); return; } #endif #ifndef DONT_UPDATE_MTAB debuglog("unmount succeeded, trying to fix mtab.\n"); if (!(fpout = setmntent(MTAB_TMP, "w"))) { errorlog("%s: %m\n", MTAB_TMP); return; } if (!(fpin = setmntent(MTAB_PATH, "r"))) { endmntent(fpout); unlink(MTAB_TMP); errorlog("%s: %m\n", MTAB_PATH); exit(0); } if (fstat(fileno(fpin), &statb)) errorlog("fstat: %m\n"); else fchmod(fileno(fpout), statb.st_mode); #if defined(sun) && defined(__SVR4) while (!getmntent(fpin, &entr)) if (strcmp(entr.mnt_special, nfshost) || strcmp(entr.mnt_mountp, mntdir)) putmntent(fpout, &entr); #else while ((ent = getmntent(fpin))) if (strcmp(ent->mnt_fsname, nfshost) || strcmp(ent->mnt_dir, mntdir)) addmntent(fpout, ent); #endif endmntent(fpin); endmntent(fpout); if (rename(MTAB_TMP, MTAB_PATH)) { errorlog("%s: %m\n", MTAB_PATH); unlink(MTAB_TMP); } #else debuglog("no mtab fixing needed\n"); #endif infolog("plpnfsd: terminating.\n"); exit(0); } void cache_flush() { struct cache *cp; struct dcache *dcp; /* * Solaris sends blocks in a way which is not very pleasent * for us. It sends blocks 0,1,2,3,4,5,6, then 9,10,11 and * do on. A little bit later block 7 and 8 arrives. This "bit" * is more that 2 seconds, it is about 6 seconds. It occurs, * if we're rewriting a file. We set MAXWRITE to 15, meaning * that we are waiting for 30 seconds to receive the missing * blocks. */ #define MAXWRITE 15 int doclean = 1; for (cp = attrcache; cp; cp = cp->next) for (dcp = cp->dcache; dcp; dcp = dcp->next) if (dcp->towrite) { debuglog("waiting for block %d in %s to write (%d)\n", dcp->offset, get_num(cp->inode)->name, dcp->towrite); if (++dcp->towrite <= MAXWRITE) doclean = 0; /* Wait with cleaning */ } if (doclean || force_cache_clean) { for (cp = attrcache; cp; cp = cp->next) for (dcp = cp->dcache; dcp; dcp = dcp->next) if (dcp->towrite) errorlog("PLPNFSD WARNING: file %s block at %d not written\n", get_num(cp->inode)->name, dcp->offset); clean_cache(&attrcache); if (force_cache_clean || ((time(0) - devcache_stamp) > devcache_keep)) query_cache = 0; /* clear the GETDENTS "cache". */ rfsv_closecached(); force_cache_clean = 0; } } void mount_and_run(char *dir, void (*proc)(), nfs_fh *root_fh) { int sock, port, pid, doclean; struct sockaddr_in sain; int isalive = 0, ret, dtbsize; SVCXPRT *nfsxprt; int bufsiz = 0xc000; /* room for a few biods */ #ifdef linux struct nfs_mount_data nfs_mount_data; int mount_flags; int ksock, kport; struct sockaddr_in kaddr; #else struct nfs_args nfs_args; #endif #if defined(__FreeBSD__) || defined(__NetBSD__) int mount_flags; #endif sprintf(nfshost, "localhost:/psion"); bzero((char *) &sain, sizeof(struct sockaddr_in)); #ifdef linux bzero(&nfs_mount_data, sizeof(struct nfs_mount_data)); #else bzero((char *) &nfs_args, sizeof(struct nfs_args)); #endif /*** First part: set up the rpc service */ /* Create udp socket */ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *) &bufsiz, sizeof(bufsiz))) errorlog("setsockopt: %m\n"); /* Bind it to a reserved port */ sain.sin_family = AF_INET; sain.sin_addr.s_addr = inet_addr("127.0.0.1"); for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) { sain.sin_port = htons(port); if (bind(sock, (struct sockaddr *) &sain, sizeof(sain)) >= 0) break; } if (port <= IPPORT_RESERVED / 2) { errorlog("bind to reserved port: %m\n"); exit(1); } if ((nfsxprt = svcudp_create(sock)) == 0) { errorlog("svcudp_create: %m\n"); exit(1); } if (!svc_register(nfsxprt, NFS_PROGRAM, NFS_VERSION, proc, 0)) { errorlog("svc_register: %m\n"); exit(1); } debuglog("created svc PROG=%d, VER=%d port=%d\n", NFS_PROGRAM, NFS_VERSION, port); /*** Second part: mount the directory */ #ifdef linux /* * Hold your hat! Another odd internet socket coming up. The linux * kernel needs the socket to talk to the nfs daemon. */ ksock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (ksock < 0) { errorlog("Cannot create kernel socket: %m\n"); 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) { errorlog("bind to reserved port: %m\n"); exit(1); } nfs_mount_data.version = NFS_MOUNT_VERSION; nfs_mount_data.fd = ksock; nfs_mount_data.root = *root_fh; /* structure copy */ nfs_mount_data.flags = NFS_MOUNT_INTR | NFS_MOUNT_NOCTO; /* NFS_MOUNT_SECURE | NFS_MOUNT_POSIX | NFS_MOUNT_SOFT | NFS_MOUNT_NOAC */ nfs_mount_data.rsize = PBUFSIZE; nfs_mount_data.wsize = PBUFSIZE; nfs_mount_data.timeo = 600; nfs_mount_data.retrans = 10; /* default 3 */ nfs_mount_data.acregmin = 3; /* default 3 seconds */ nfs_mount_data.acregmax = 60; /* default 60 seconds */ nfs_mount_data.acdirmin = 30; /* default 30 seconds */ nfs_mount_data.acdirmax = 60; /* default 60 seconds */ nfs_mount_data.addr = sain; /* structure copy */ strcpy(nfs_mount_data.hostname, PSIONHOSTNAME); if (connect(ksock, (struct sockaddr *) &nfs_mount_data.addr, sizeof(nfs_mount_data.addr)) < 0) { errorlog("Cannot connect to plpnfsd: %m\n"); exit(1); } mount_flags = MS_MGC_VAL; /* | MS_SYNC | MS_RDONLY | MS_NOEXEC | MS_NODEV | MS_NOSUID */ #endif #if defined(sun) && defined (__SVR4) { /*gec */ struct netbuf myaddr; struct stat stb; struct knetconfig knc; struct netconfig *nc = getnetconfigent("udp"); if (!nc) { extern int errno; errorlog("getnetconfigent \"udp\": (errno %d) %m\n", errno); exit(1); } knc.knc_semantics = nc->nc_semantics; knc.knc_protofmly = strdup(nc->nc_protofmly); knc.knc_proto = strdup(nc->nc_proto); if (stat(nc->nc_device, &stb)) { extern int errno; errorlog("stat \"%s\": (errno %d) %m\n", nc->nc_device, errno); exit(1); } knc.knc_rdev = stb.st_rdev; /*freenetconfigent(nc) has the struct been allocated, or is it static ?!? */ debuglog("%d,%s,%s,%lx (1,inet,udp,0x002c0029)\n", (int) knc.knc_semantics, knc.knc_protofmly, knc.knc_proto, knc.knc_rdev); myaddr.maxlen = myaddr.len = sizeof(sain); myaddr.buf = (char *) &sain; nfs_args.knconf = &knc; nfs_args.wsize = PBUFSIZE; nfs_args.rsize = PBUFSIZE; nfs_args.addr = &myaddr; nfs_args.fh = (char *) root_fh; nfs_args.retrans = 10; /* 1 minute timeout - see below */ nfs_args.timeo = 600; nfs_args.hostname = PSIONHOSTNAME; nfs_args.flags = NFSMNT_INT | NFSMNT_HOSTNAME | NFSMNT_NOCTO | NFSMNT_RETRANS | NFSMNT_TIMEO | NFSMNT_WSIZE | NFSMNT_RSIZE | NFSMNT_KNCONF /* | NFSMNT_NOAC */ ; /* eventually drop knc-strings now (memory-leak) */ } #endif /* solaris */ /* BSD Derived systems */ #if (defined(sun) && !defined(__SVR4)) || defined(hpux) || defined(__sgi) || defined(__NeXT__) nfs_args.addr = &sain; nfs_args.fh = (void *) root_fh; nfs_args.wsize = PBUFSIZE; nfs_args.rsize = PBUFSIZE; nfs_args.retrans = 10; /* 1 minute timeout - means that we receive double requests in worst case, if the file is longer than 100k. So long for the theory. Since SunOS uses dynamic retransmission (hardwired), nfs_args.timeo is only a hint. :-( */ nfs_args.timeo = 600; nfs_args.hostname = PSIONHOSTNAME; nfs_args.flags = NFSMNT_INT | NFSMNT_HOSTNAME | NFSMNT_NOCTO | NFSMNT_RETRANS | NFSMNT_TIMEO | NFSMNT_WSIZE | NFSMNT_RSIZE; #endif #ifdef __FreeBSD__ #if __FreeBSD_version >= 300001 nfs_args.version = NFS_ARGSVERSION; #endif nfs_args.addr = (struct sockaddr *) &sain; nfs_args.addrlen = sizeof(sain); nfs_args.sotype = SOCK_DGRAM; nfs_args.fh = (void *) root_fh; nfs_args.fhsize = sizeof(*root_fh); nfs_args.wsize = PBUFSIZE; nfs_args.rsize = PBUFSIZE; nfs_args.retrans = 10; /* 1 minute timeout - see below */ nfs_args.timeo = 600; nfs_args.hostname = PSIONHOSTNAME; nfs_args.flags = NFSMNT_INT | NFSMNT_SOFT | NFSMNT_RETRANS | NFSMNT_TIMEO | NFSMNT_WSIZE | NFSMNT_RSIZE; mount_flags = MNT_NOSUID | MNT_NODEV | MNT_NOEXEC | MNT_NOATIME; #endif #ifdef __NetBSD__ nfs_args.version = NFS_ARGSVERSION; nfs_args.addrlen = sizeof(sain); nfs_args.sotype = SOCK_DGRAM; nfs_args.maxgrouplist = NGROUPS; nfs_args.readahead = 1; nfs_args.addr = (struct sockaddr *) &sain; nfs_args.fh = (void *) root_fh; nfs_args.fhsize = sizeof(*root_fh); nfs_args.wsize = PBUFSIZE; nfs_args.rsize = PBUFSIZE; nfs_args.retrans = 10; nfs_args.timeo = 600; nfs_args.hostname = PSIONHOSTNAME; nfs_args.flags = NFSMNT_INT | NFSMNT_RETRANS | NFSMNT_TIMEO | NFSMNT_NOCONN | NFSMNT_DUMBTIMR | NFSMNT_WSIZE | NFSMNT_RSIZE; mount_flags = MNT_NOSUID | MNT_NODEV | MNT_NOEXEC | MNT_NOATIME; #endif #if defined(_IBMR2) nfs_args.addr = sain; nfs_args.fh = *(fhandle_t *) root_fh; nfs_args.wsize = PBUFSIZE; nfs_args.rsize = PBUFSIZE; nfs_args.retrans = 0; /* Shouldn't need bigger retrans since there is only one biod. Somebody prove it ? Nice feature. */ nfs_args.biods = 1; nfs_args.timeo = 150; nfs_args.hostname = PSIONHOSTNAME; nfs_args.flags = NFSMNT_INT | NFSMNT_HOSTNAME | NFSMNT_BIODS | NFSMNT_RETRANS | NFSMNT_TIMEO | NFSMNT_WSIZE | NFSMNT_RSIZE; #endif mntdir = dir; switch (pid = fork()) { case 0: break; case -1: errorlog("fork: %m\n"); exit(1); default: debuglog("Going to mount...\n"); #if defined(__sgi) || (defined(sun) && defined(__SVR4)) if (mount("", dir, MS_DATA, "nfs", &nfs_args, sizeof(nfs_args))) #endif #if defined(__NeXT__) if (mount(MOUNT_NFS, dir, 0, (caddr_t) & nfs_args)) #endif #ifdef hpux if (vfsmount(MOUNT_NFS, dir, 0, &nfs_args)) #endif #if defined(sun) && !defined(__SVR4) if (mount("nfs", dir, M_NEWTYPE, (caddr_t) & nfs_args)) #endif #ifdef linux if (mount("nfs", dir, "nfs", mount_flags, &nfs_mount_data)) #endif #ifdef _IBMR2 if (aix3_mount("psion:loc", dir, 0, MOUNT_TYPE_NFS, &nfs_args, "plpnfsd")) #endif #if defined(__NetBSD__) || defined(__FreeBSD__) #if __FreeBSD_version >= 300001 if (mount("nfs", dir, mount_flags, &nfs_args)) #else if (mount(MOUNT_NFS, dir, mount_flags, &nfs_args)) #endif #endif { errorlog("nfs mount %s: %m\n", dir); kill(pid, SIGTERM); exit(0); } else { #ifndef DONT_UPDATE_MTAB FILE *mfp; struct mntent mnt; #if defined(sun) && defined(__SVR4) /*gec */ char tim[32]; #endif debuglog("Mount succeeded, making mtab entry.\n"); #if defined(sun) && defined(__SVR4) /*gec */ mnt.mnt_special = nfshost; mnt.mnt_mountp = mntdir; mnt.mnt_fstype = "nfs"; mnt.mnt_mntopts = "hard,intr"; /* dev=??? */ sprintf(tim, "%ld", time(NULL)); /* umount crashes! without this */ mnt.mnt_time = tim; #else mnt.mnt_fsname = nfshost; mnt.mnt_dir = mntdir; mnt.mnt_type = "nfs"; mnt.mnt_opts = "hard,intr"; mnt.mnt_freq = mnt.mnt_passno = 0; #endif if ((mfp = setmntent(MTAB_PATH, "a"))) addmntent(mfp, &mnt); else errorlog("%s: %m\n", MTAB_PATH); endmntent(mfp); #endif /* !DONT_UPDATE_MTAB */ } exit(0); } /*** Third part: let's go */ infolog("to stop the server do \"echo stop > %s/proc/exit\".\n", mntdir); #if defined(sun) && !defined(__SVR4) dtbsize = _rpc_dtablesize(); #else dtbsize = FD_SETSIZE; #endif /* * Signal handling, etc. should only happen in one process, so let's * make it this one (the other one might well go away). We really * ought to block off the other signals, but that's OK... */ signal(SIGUSR1, usr1_handler); signal(SIGHUP, hup_handler); signal(SIGTERM, term_handler); for (;;) { fd_set readfd; struct timeval tv; readfd = svc_fdset; tv.tv_sec = 2; tv.tv_usec = 0; ret = select(dtbsize, &readfd, 0, 0, &tv); if (ret > 0) svc_getreqset(&readfd); if (ret != 0) continue; /* Do some housekeeping */ if (exiting) doexit(); cache_flush(); ret = rfsv_isalive(); if (isalive) { if (!ret) { debuglog("Disconnected...\n"); doclean = 1; root_fattr.mtime.seconds = time(0); } } else { if (ret) { debuglog("Connected...\n"); root_fattr.mtime.seconds = time(0); } } isalive = ret; } }