"rpc.nfsd XX" should not fail if ports are already open.
[nfs-utils.git] / support / nfs / nfssvc.c
1 /*
2  * support/nfs/nfssvc.c
3  *
4  * Run an NFS daemon.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <errno.h>
19 #include <syslog.h>
20
21
22 #include "nfslib.h"
23
24 #define NFSD_PORTS_FILE     "/proc/fs/nfsd/portlist"
25 #define NFSD_VERS_FILE    "/proc/fs/nfsd/versions"
26 #define NFSD_THREAD_FILE  "/proc/fs/nfsd/threads"
27
28 static void
29 nfssvc_setfds(int port, unsigned int ctlbits, char *haddr)
30 {
31         int fd, n, on=1;
32         char buf[BUFSIZ];
33         int udpfd = -1, tcpfd = -1;
34         struct sockaddr_in sin;
35
36         fd = open(NFSD_PORTS_FILE, O_RDONLY);
37         if (fd < 0)
38                 return;
39         n = read(fd, buf, BUFSIZ);
40         close(fd);
41         if (n != 0)
42                 return;
43         /* there are no ports currently open, so it is safe to
44          * try to open some and pass them through.
45          * Note: If the user explicitly asked for 'udp', then
46          * we should probably check if that is open, and should
47          * open it if not.  However we don't yet.  All sockets
48          * have to be opened when the first daemon is started.
49          */
50         fd = open(NFSD_PORTS_FILE, O_WRONLY);
51         if (fd < 0)
52                 return;
53         sin.sin_family = AF_INET;
54         sin.sin_port   = htons(port);
55         sin.sin_addr.s_addr =  inet_addr(haddr);
56
57         if (NFSCTL_UDPISSET(ctlbits)) {
58                 udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
59                 if (udpfd < 0) {
60                         syslog(LOG_ERR, "nfssvc: unable to create UPD socket: "
61                                 "errno %d (%s)\n", errno, strerror(errno));
62                         exit(1);
63                 }
64                 if (bind(udpfd, (struct  sockaddr  *)&sin, sizeof(sin)) < 0){
65                         syslog(LOG_ERR, "nfssvc: unable to bind UPD socket: "
66                                 "errno %d (%s)\n", errno, strerror(errno));
67                         exit(1);
68                 }
69         }
70
71         if (NFSCTL_TCPISSET(ctlbits)) {
72                 tcpfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
73                 if (tcpfd < 0) {
74                         syslog(LOG_ERR, "nfssvc: unable to createt tcp socket: "
75                                 "errno %d (%s)\n", errno, strerror(errno));
76                         exit(1);
77                 }
78                 if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
79                         syslog(LOG_ERR, "nfssvc: unable to set SO_REUSEADDR: "
80                                 "errno %d (%s)\n", errno, strerror(errno));
81                         exit(1);
82                 }
83                 if (bind(tcpfd, (struct  sockaddr  *)&sin, sizeof(sin)) < 0){
84                         syslog(LOG_ERR, "nfssvc: unable to bind TCP socket: "
85                                 "errno %d (%s)\n", errno, strerror(errno));
86                         exit(1);
87                 }
88                 if (listen(tcpfd, 64) < 0){
89                         syslog(LOG_ERR, "nfssvc: unable to create listening socket: "
90                                 "errno %d (%s)\n", errno, strerror(errno));
91                         exit(1);
92                 }
93         }
94         if (udpfd >= 0) {
95                 snprintf(buf, BUFSIZ,"%d\n", udpfd); 
96                 if (write(fd, buf, strlen(buf)) != strlen(buf)) {
97                         syslog(LOG_ERR, 
98                                "nfssvc: writting fds to kernel failed: errno %d (%s)", 
99                                errno, strerror(errno));
100                 }
101                 close(fd);
102                 fd = -1;
103         }
104         if (tcpfd >= 0) {
105                 if (fd < 0)
106                         fd = open(NFSD_PORTS_FILE, O_WRONLY);
107                 snprintf(buf, BUFSIZ,"%d\n", tcpfd); 
108                 if (write(fd, buf, strlen(buf)) != strlen(buf)) {
109                         syslog(LOG_ERR, 
110                                "nfssvc: writting fds to kernel failed: errno %d (%s)", 
111                                errno, strerror(errno));
112                 }
113         }
114         close(fd);
115
116         return;
117 }
118 static void
119 nfssvc_versbits(unsigned int ctlbits)
120 {
121         int fd, n, off;
122         char buf[BUFSIZ], *ptr;
123
124         ptr = buf;
125         off = 0;
126         fd = open(NFSD_VERS_FILE, O_WRONLY);
127         if (fd < 0)
128                 return;
129
130         for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) {
131                 if (NFSCTL_VERISSET(ctlbits, n))
132                     off += snprintf(ptr+off, BUFSIZ - off, "+%d ", n);
133                 else
134                     off += snprintf(ptr+off, BUFSIZ - off, "-%d ", n);
135         }
136         snprintf(ptr+off, BUFSIZ - off, "\n");
137         if (write(fd, buf, strlen(buf)) != strlen(buf)) {
138                 syslog(LOG_ERR, "nfssvc: Setting version failed: errno %d (%s)", 
139                         errno, strerror(errno));
140         }
141         close(fd);
142
143         return;
144 }
145 int
146 nfssvc(int port, int nrservs, unsigned int versbits, unsigned protobits,
147         char *haddr)
148 {
149         struct nfsctl_arg       arg;
150         int fd;
151
152         /* Note: must set versions before fds so that
153          * the ports get registered with portmap against correct
154          * versions
155          */
156         nfssvc_versbits(versbits);
157         nfssvc_setfds(port, protobits, haddr);
158
159         fd = open(NFSD_THREAD_FILE, O_WRONLY);
160         if (fd < 0)
161                 fd = open("/proc/fs/nfs/threads", O_WRONLY);
162         if (fd >= 0) {
163                 /* 2.5+ kernel with nfsd filesystem mounted.
164                  * Just write the number in.
165                  * Cannot handle port number yet, but does anyone care?
166                  */
167                 char buf[20];
168                 int n;
169                 snprintf(buf, 20,"%d\n", nrservs);
170                 n = write(fd, buf, strlen(buf));
171                 close(fd);
172                 if (n != strlen(buf))
173                         return -1;
174                 else
175                         return 0;
176         }
177
178         arg.ca_version = NFSCTL_VERSION;
179         arg.ca_svc.svc_nthreads = nrservs;
180         arg.ca_svc.svc_port = port;
181         return nfsctl(NFSCTL_SVC, &arg, NULL);
182 }