| /* |
| * nfsd |
| * |
| * This is the user level part of nfsd. This is very primitive, because |
| * all the work is now done in the kernel module. |
| * |
| * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <getopt.h> |
| #include <netdb.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| |
| #include "nfslib.h" |
| #include "nfssvc.h" |
| #include "xlog.h" |
| |
| /* |
| * IPv6 support for nfsd was finished before some of the other daemons (mountd |
| * and statd in particular). That could be a problem in the future if someone |
| * were to boot a kernel that supports IPv6 serving with an older nfs-utils. For |
| * now, hardcode the IPv6 switch into the off position until the other daemons |
| * are functional. |
| */ |
| #undef IPV6_SUPPORTED |
| |
| static void usage(const char *); |
| |
| static struct option longopts[] = |
| { |
| { "host", 1, 0, 'H' }, |
| { "help", 0, 0, 'h' }, |
| { "no-nfs-version", 1, 0, 'N' }, |
| { "no-tcp", 0, 0, 'T' }, |
| { "no-udp", 0, 0, 'U' }, |
| { "port", 1, 0, 'P' }, |
| { "port", 1, 0, 'p' }, |
| { "debug", 0, 0, 'd' }, |
| { "syslog", 0, 0, 's' }, |
| { NULL, 0, 0, 0 } |
| }; |
| |
| /* given a family and ctlbits, disable any that aren't listed in netconfig */ |
| #ifdef HAVE_LIBTIRPC |
| static void |
| nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6) |
| { |
| struct netconfig *nconf; |
| unsigned int *famproto; |
| void *handle; |
| |
| xlog(D_GENERAL, "Checking netconfig for visible protocols."); |
| |
| handle = setnetconfig(); |
| while((nconf = getnetconfig(handle))) { |
| if (!(nconf->nc_flag & NC_VISIBLE)) |
| continue; |
| |
| if (!strcmp(nconf->nc_protofmly, NC_INET)) |
| famproto = proto4; |
| else if (!strcmp(nconf->nc_protofmly, NC_INET6)) |
| famproto = proto6; |
| else |
| continue; |
| |
| if (!strcmp(nconf->nc_proto, NC_TCP)) |
| NFSCTL_TCPSET(*famproto); |
| else if (!strcmp(nconf->nc_proto, NC_UDP)) |
| NFSCTL_UDPSET(*famproto); |
| |
| xlog(D_GENERAL, "Enabling %s %s.", nconf->nc_protofmly, |
| nconf->nc_proto); |
| } |
| endnetconfig(handle); |
| return; |
| } |
| #else /* HAVE_LIBTIRPC */ |
| static void |
| nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6) |
| { |
| /* Enable all IPv4 protocols if no TIRPC support */ |
| *proto4 = NFSCTL_ALLBITS; |
| *proto6 = 0; |
| } |
| #endif /* HAVE_LIBTIRPC */ |
| |
| int |
| main(int argc, char **argv) |
| { |
| int count = 1, c, error = 0, portnum = 0, fd, found_one; |
| char *p, *progname, *port; |
| char *haddr = NULL; |
| int socket_up = 0; |
| int minorvers4 = NFSD_MAXMINORVERS4; /* nfsv4 minor version */ |
| unsigned int versbits = NFSCTL_ALLBITS; |
| unsigned int protobits = NFSCTL_ALLBITS; |
| unsigned int proto4 = 0; |
| unsigned int proto6 = 0; |
| |
| progname = strdup(basename(argv[0])); |
| if (!progname) { |
| fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]); |
| exit(1); |
| } |
| |
| port = strdup("nfs"); |
| if (!port) { |
| fprintf(stderr, "%s: unable to allocate memory.\n", progname); |
| exit(1); |
| } |
| |
| xlog_syslog(0); |
| xlog_stderr(1); |
| |
| while ((c = getopt_long(argc, argv, "dH:hN:p:P:sTU", longopts, NULL)) != EOF) { |
| switch(c) { |
| case 'd': |
| xlog_config(D_ALL, 1); |
| break; |
| case 'H': |
| /* |
| * for now, this only handles one -H option. Use the |
| * last one specified. |
| */ |
| free(haddr); |
| haddr = strdup(optarg); |
| if (!haddr) { |
| fprintf(stderr, "%s: unable to allocate " |
| "memory.\n", progname); |
| exit(1); |
| } |
| break; |
| case 'P': /* XXX for nfs-server compatibility */ |
| case 'p': |
| /* only the last -p option has any effect */ |
| portnum = atoi(optarg); |
| if (portnum <= 0 || portnum > 65535) { |
| fprintf(stderr, "%s: bad port number: %s\n", |
| progname, optarg); |
| usage(progname); |
| } |
| free(port); |
| port = strdup(optarg); |
| if (!port) { |
| fprintf(stderr, "%s: unable to allocate " |
| "memory.\n", progname); |
| exit(1); |
| } |
| break; |
| case 'N': |
| switch((c = strtol(optarg, &p, 0))) { |
| case 4: |
| if (*p == '.') { |
| minorvers4 = -atoi(p + 1); |
| break; |
| } |
| case 3: |
| case 2: |
| NFSCTL_VERUNSET(versbits, c); |
| break; |
| default: |
| fprintf(stderr, "%s: Unsupported version\n", optarg); |
| exit(1); |
| } |
| break; |
| case 's': |
| xlog_syslog(1); |
| xlog_stderr(0); |
| break; |
| case 'T': |
| NFSCTL_TCPUNSET(protobits); |
| break; |
| case 'U': |
| NFSCTL_UDPUNSET(protobits); |
| break; |
| default: |
| fprintf(stderr, "Invalid argument: '%c'\n", c); |
| case 'h': |
| usage(progname); |
| } |
| } |
| |
| if (optind < argc) { |
| if ((count = atoi(argv[optind])) < 0) { |
| /* insane # of servers */ |
| fprintf(stderr, |
| "%s: invalid server count (%d), using 1\n", |
| argv[0], count); |
| count = 1; |
| } else if (count == 0) { |
| /* |
| * don't bother setting anything else if the threads |
| * are coming down anyway. |
| */ |
| socket_up = 1; |
| goto set_threads; |
| } |
| } |
| |
| xlog_open(progname); |
| |
| nfsd_enable_protos(&proto4, &proto6); |
| |
| if (!NFSCTL_TCPISSET(protobits)) { |
| NFSCTL_TCPUNSET(proto4); |
| NFSCTL_TCPUNSET(proto6); |
| } |
| |
| if (!NFSCTL_UDPISSET(protobits)) { |
| NFSCTL_UDPUNSET(proto4); |
| NFSCTL_UDPUNSET(proto6); |
| } |
| |
| /* make sure that at least one version is enabled */ |
| found_one = 0; |
| for (c = NFSD_MINVERS; c <= NFSD_MAXVERS; c++) { |
| if (NFSCTL_VERISSET(versbits, c)) |
| found_one = 1; |
| } |
| if (!found_one) { |
| xlog(L_ERROR, "no version specified"); |
| exit(1); |
| } |
| |
| if (NFSCTL_VERISSET(versbits, 4) && |
| !NFSCTL_TCPISSET(proto4) && |
| !NFSCTL_TCPISSET(proto6)) { |
| xlog(L_ERROR, "version 4 requires the TCP protocol"); |
| exit(1); |
| } |
| |
| if (chdir(NFS_STATEDIR)) { |
| xlog(L_ERROR, "chdir(%s) failed: %m", NFS_STATEDIR); |
| exit(1); |
| } |
| |
| /* can only change number of threads if nfsd is already up */ |
| if (nfssvc_inuse()) { |
| socket_up = 1; |
| goto set_threads; |
| } |
| |
| /* |
| * must set versions before the fd's so that the right versions get |
| * registered with rpcbind. Note that on older kernels w/o the right |
| * interfaces, these are a no-op. |
| */ |
| nfssvc_setvers(versbits, minorvers4); |
| |
| error = nfssvc_set_sockets(AF_INET, proto4, haddr, port); |
| if (!error) |
| socket_up = 1; |
| |
| #ifdef IPV6_SUPPORTED |
| error = nfssvc_set_sockets(AF_INET6, proto6, haddr, port); |
| if (!error) |
| socket_up = 1; |
| #endif /* IPV6_SUPPORTED */ |
| |
| set_threads: |
| /* don't start any threads if unable to hand off any sockets */ |
| if (!socket_up) { |
| xlog(L_ERROR, "unable to set any sockets for nfsd"); |
| goto out; |
| } |
| error = 0; |
| |
| /* |
| * KLUDGE ALERT: |
| * Some kernels let nfsd kernel threads inherit open files |
| * from the program that spawns them (i.e. us). So close |
| * everything before spawning kernel threads. --Chip |
| */ |
| fd = open("/dev/null", O_RDWR); |
| if (fd == -1) |
| xlog(L_ERROR, "Unable to open /dev/null: %m"); |
| else { |
| /* switch xlog output to syslog since stderr is being closed */ |
| xlog_syslog(1); |
| xlog_stderr(0); |
| (void) dup2(fd, 0); |
| (void) dup2(fd, 1); |
| (void) dup2(fd, 2); |
| } |
| closeall(3); |
| |
| if ((error = nfssvc_threads(portnum, count)) < 0) |
| xlog(L_ERROR, "error starting threads: errno %d (%m)", errno); |
| out: |
| free(port); |
| free(haddr); |
| free(progname); |
| return (error != 0); |
| } |
| |
| static void |
| usage(const char *prog) |
| { |
| fprintf(stderr, "Usage:\n" |
| "%s [-d|--debug] [-H hostname] [-p|-P|--port port] [-N|--no-nfs-version version ] [-s|--syslog] [-T|--no-tcp] [-U|--no-udp] nrservs\n", |
| prog); |
| exit(2); |
| } |