]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/statd.c
statd: Use the new nsm_ file.c calls in rpc.statd
[nfs-utils.git] / utils / statd / statd.c
1 /* 
2  * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
3  * Modified by Olaf Kirch, Oct. 1996.
4  * Modified by H.J. Lu, 1998.
5  * Modified by L. Hohberger of Mission Critical Linux, 2000.
6  *
7  * NSM for Linux.
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <sys/stat.h>
15 #include <limits.h>
16 #include <signal.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <getopt.h>
22 #include <rpc/rpc.h>
23 #include <rpc/pmap_clnt.h>
24 #include <rpcmisc.h>
25 #include <sys/resource.h>
26 #include <sys/wait.h>
27 #include <grp.h>
28
29 #include "statd.h"
30 #include "nfslib.h"
31 #include "nsm.h"
32
33 /* Socket operations */
34 #include <sys/types.h>
35 #include <sys/socket.h>
36
37 int     run_mode = 0;           /* foreground logging mode */
38
39 /* LH - I had these local to main, but it seemed silly to have 
40  * two copies of each - one in main(), one static in log.c... 
41  * It also eliminates the 256-char static in log.c */
42 static char *name_p = NULL;
43
44 /* PRC: a high-availability callout program can be specified with -H
45  * When this is done, the program will receive callouts whenever clients
46  * are added or deleted to the notify list */
47 char *ha_callout_prog = NULL;
48
49 static struct option longopts[] =
50 {
51         { "foreground", 0, 0, 'F' },
52         { "no-syslog", 0, 0, 'd' },
53         { "help", 0, 0, 'h' },
54         { "version", 0, 0, 'v' },
55         { "outgoing-port", 1, 0, 'o' },
56         { "port", 1, 0, 'p' },
57         { "name", 1, 0, 'n' },
58         { "state-directory-path", 1, 0, 'P' },
59         { "notify-mode", 0, 0, 'N' },
60         { "ha-callout", 1, 0, 'H' },
61         { "no-notify", 0, 0, 'L' },
62         { NULL, 0, 0, 0 }
63 };
64
65 extern void sm_prog_1 (struct svc_req *, register SVCXPRT *);
66
67 #ifdef SIMULATIONS
68 extern void simulator (int, char **);
69 #endif
70
71
72 #ifdef HAVE_TCP_WRAPPER 
73 #include "tcpwrapper.h"
74
75 static void 
76 sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
77 {
78         struct sockaddr_in *sin = nfs_getrpccaller_in(transp);
79
80         /* remote host authorization check */
81         if (sin->sin_family == AF_INET &&
82             !check_default("statd", sin, rqstp->rq_proc, SM_PROG)) {
83                 svcerr_auth (transp, AUTH_FAILED);
84                 return;
85         }
86
87         sm_prog_1 (rqstp, transp);
88 }
89
90 #define sm_prog_1 sm_prog_1_wrapper
91 #endif
92
93 /*
94  * Signal handler.
95  */
96 static void 
97 killer (int sig)
98 {
99         pmap_unset (SM_PROG, SM_VERS);
100         xlog_err ("Caught signal %d, un-registering and exiting", sig);
101 }
102
103 static void
104 sigusr (int sig)
105 {
106         extern void my_svc_exit (void);
107         xlog(D_GENERAL, "Caught signal %d, re-notifying (state %d)", sig,
108                                                                 MY_STATE);
109         my_svc_exit();
110 }
111
112 /*
113  * Startup information.
114  */
115 static void log_modes(void)
116 {
117         char buf[128];          /* watch stack size... */
118
119         /* No flags = no message */
120         if (!run_mode) return;
121
122         memset(buf,0,128);
123         sprintf(buf,"Flags: ");
124         if (run_mode & MODE_NODAEMON)
125                 strcat(buf,"No-Daemon ");
126         if (run_mode & MODE_LOG_STDERR)
127                 strcat(buf,"Log-STDERR ");
128
129         xlog_warn(buf);
130 }
131
132 /*
133  * Since we do more than standard statd stuff, we might need to
134  * help the occasional admin. 
135  */
136 static void 
137 usage(void)
138 {
139         fprintf(stderr,"usage: %s [options]\n", name_p);
140         fprintf(stderr,"      -h, -?, --help       Print this help screen.\n");
141         fprintf(stderr,"      -F, --foreground     Foreground (no-daemon mode)\n");
142         fprintf(stderr,"      -d, --no-syslog      Verbose logging to stderr.  Foreground mode only.\n");
143         fprintf(stderr,"      -p, --port           Port to listen on\n");
144         fprintf(stderr,"      -o, --outgoing-port  Port for outgoing connections\n");
145         fprintf(stderr,"      -V, -v, --version    Display version information and exit.\n");
146         fprintf(stderr,"      -n, --name           Specify a local hostname.\n");
147         fprintf(stderr,"      -P                   State directory path.\n");
148         fprintf(stderr,"      -N                   Run in notify only mode.\n");
149         fprintf(stderr,"      -L, --no-notify      Do not perform any notification.\n");
150         fprintf(stderr,"      -H                   Specify a high-availability callout program.\n");
151 }
152
153 static const char *pidfile = "/var/run/rpc.statd.pid";
154
155 int pidfd = -1;
156 static void create_pidfile(void)
157 {
158         FILE *fp;
159
160         unlink(pidfile);
161         fp = fopen(pidfile, "w");
162         if (!fp)
163                 xlog_err("Opening %s failed: %m\n", pidfile);
164         fprintf(fp, "%d\n", getpid());
165         pidfd = dup(fileno(fp));
166         if (fclose(fp) < 0) {
167                 xlog_warn("Flushing pid file failed: errno %d (%m)\n",
168                         errno);
169         }
170 }
171
172 static void truncate_pidfile(void)
173 {
174         if (pidfd >= 0) {
175                 if (ftruncate(pidfd, 0) < 0) {
176                         xlog_warn("truncating pid file failed: errno %d (%m)\n",
177                                 errno);
178                 }
179         }
180 }
181
182 static void run_sm_notify(int outport)
183 {
184         char op[20];
185         char *av[6];
186         int ac = 0;
187
188         av[ac++] = "/usr/sbin/sm-notify";
189         if (run_mode & MODE_NODAEMON)
190                 av[ac++] = "-d";
191         if (outport) {
192                 sprintf(op, "-p%d", outport);
193                 av[ac++] = op;
194         }
195         if (run_mode & STATIC_HOSTNAME) {
196                 av[ac++] = "-v";
197                 av[ac++] = MY_NAME;
198         }
199         av[ac] = NULL;
200         execv(av[0], av);
201         fprintf(stderr, "%s: failed to run %s\n", name_p, av[0]);
202         exit(2);
203
204 }
205 /* 
206  * Entry routine/main loop.
207  */
208 int main (int argc, char **argv)
209 {
210         extern char *optarg;
211         int pid;
212         int arg;
213         int port = 0, out_port = 0;
214         struct rlimit rlim;
215
216         int pipefds[2] = { -1, -1};
217         char status;
218
219         /* Default: daemon mode, no other options */
220         run_mode = 0;
221         xlog_stderr(0);
222         xlog_syslog(1);
223
224         /* Set the basename */
225         if ((name_p = strrchr(argv[0],'/')) != NULL) {
226                 name_p ++;
227         } else {
228                 name_p = argv[0];
229         }
230
231         /* Set hostname */
232         MY_NAME = NULL;
233
234         /* Process command line switches */
235         while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:L", longopts, NULL)) != EOF) {
236                 switch (arg) {
237                 case 'V':       /* Version */
238                 case 'v':
239                         printf("%s version " VERSION "\n",name_p);
240                         exit(0);
241                 case 'F':       /* Foreground/nodaemon mode */
242                         run_mode |= MODE_NODAEMON;
243                         break;
244                 case 'N':
245                         run_mode |= MODE_NOTIFY_ONLY;
246                         break;
247                 case 'L': /* Listen only */
248                         run_mode |= MODE_NO_NOTIFY;
249                         break;
250                 case 'd':       /* No daemon only - log to stderr */
251                         run_mode |= MODE_LOG_STDERR;
252                         break;
253                 case 'o':
254                         out_port = atoi(optarg);
255                         if (out_port < 1 || out_port > 65535) {
256                                 fprintf(stderr, "%s: bad port number: %s\n",
257                                         argv[0], optarg);
258                                 usage();
259                                 exit(1);
260                         }
261                         break;
262                 case 'p':
263                         port = atoi(optarg);
264                         if (port < 1 || port > 65535) {
265                                 fprintf(stderr, "%s: bad port number: %s\n",
266                                         argv[0], optarg);
267                                 usage();
268                                 exit(1);
269                         }
270                         break;
271                 case 'n':       /* Specify local hostname */
272                         run_mode |= STATIC_HOSTNAME;
273                         MY_NAME = xstrdup(optarg);
274                         break;
275                 case 'P':
276                         if (!nsm_setup_pathnames(argv[0], optarg))
277                                 exit(1);
278                         break;
279                 case 'H': /* PRC: specify the ha-callout program */
280                         if ((ha_callout_prog = xstrdup(optarg)) == NULL) {
281                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
282                                         argv[0], optarg);
283                                 exit(1);
284                         }
285                         break;
286                 case '?':       /* heeeeeelllllllpppp? heh */
287                 case 'h':
288                         usage();
289                         exit (0);
290                 default:        /* oh dear ... heh */
291                         usage();
292                         exit(-1);
293                 }
294         }
295
296         if (port == out_port && port != 0) {
297                 fprintf(stderr, "Listening and outgoing ports cannot be the same!\n");
298                 exit(-1);
299         }
300
301         if (run_mode & MODE_NOTIFY_ONLY) {
302                 fprintf(stderr, "%s: -N deprecated, consider using /usr/sbin/sm-notify directly\n",
303                         name_p);
304                 run_sm_notify(out_port);
305         }
306
307         if (!(run_mode & MODE_NODAEMON)) {
308                 run_mode &= ~MODE_LOG_STDERR;   /* Never log to console in
309                                                    daemon mode. */
310         }
311
312         if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
313                 fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
314                                 argv [0], strerror(errno));
315         else {
316                 /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
317                 if (rlim.rlim_cur > FD_SETSIZE) {
318                         rlim.rlim_cur = FD_SETSIZE;
319
320                         if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
321                                 fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
322                                         argv [0], strerror(errno));
323                         }
324                 }
325         }
326
327 #ifdef SIMULATIONS
328         if (argc > 1)
329                 /* LH - I _really_ need to update simulator... */
330                 simulator (--argc, ++argv);     /* simulator() does exit() */
331 #endif
332         
333         if (!(run_mode & MODE_NODAEMON)) {
334                 int tempfd;
335
336                 if (pipe(pipefds)<0) {
337                         perror("statd: unable to create pipe");
338                         exit(1);
339                 }
340                 if ((pid = fork ()) < 0) {
341                         perror ("statd: Could not fork");
342                         exit (1);
343                 } else if (pid != 0) {
344                         /* Parent.
345                          * Wait for status from child.
346                          */
347                         close(pipefds[1]);
348                         if (read(pipefds[0], &status, 1) != 1)
349                                 exit(1);
350                         exit (0);
351                 }
352                 /* Child.       */
353                 close(pipefds[0]);
354                 setsid ();
355
356                 while (pipefds[1] <= 2) {
357                         pipefds[1] = dup(pipefds[1]);
358                         if (pipefds[1]<0) {
359                                 perror("statd: dup");
360                                 exit(1);
361                         }
362                 }
363                 tempfd = open("/dev/null", O_RDWR);
364                 dup2(tempfd, 0);
365                 dup2(tempfd, 1);
366                 dup2(tempfd, 2);
367                 dup2(pipefds[1], 3);
368                 pipefds[1] = 3;
369                 closeall(4);
370         }
371
372         /* Child. */
373
374         if (run_mode & MODE_LOG_STDERR) {
375                 xlog_syslog(0);
376                 xlog_stderr(1);
377                 xlog_config(D_ALL, 1);
378         }
379         xlog_open(name_p);
380         xlog(L_NOTICE, "Version " VERSION " starting");
381
382         log_modes();
383
384         signal (SIGHUP, killer);
385         signal (SIGINT, killer);
386         signal (SIGTERM, killer);
387         /* PRC: trap SIGUSR1 to re-read notify list from disk */
388         signal(SIGUSR1, sigusr);
389         /* WARNING: the following works on Linux and SysV, but not BSD! */
390         signal(SIGCHLD, SIG_IGN);
391         /*
392          * Ignore SIGPIPE to avoid statd dying when peers close their
393          * TCP connection while we're trying to reply to them.
394          */
395         signal(SIGPIPE, SIG_IGN);
396
397         create_pidfile();
398         atexit(truncate_pidfile);
399
400         if (! (run_mode & MODE_NO_NOTIFY))
401                 switch (pid = fork()) {
402                 case 0:
403                         run_sm_notify(out_port);
404                         break;
405                 case -1:
406                         break;
407                 default:
408                         waitpid(pid, NULL, 0);
409                 }
410
411         /* Make sure we have a privilege port for calling into the kernel */
412         if (statd_get_socket() < 0)
413                 exit(1);
414
415         /* If sm-notify didn't take all the state files, load
416          * state information into our notify-list so we can
417          * pass on any SM_NOTIFY that arrives
418          */
419         load_state();
420
421         MY_STATE = nsm_get_state(0);
422         if (MY_STATE == 0)
423                 exit(1);
424         xlog(D_GENERAL, "Local NSM state number: %d", MY_STATE);
425         nsm_update_kernel_state(MY_STATE);
426
427         pmap_unset (SM_PROG, SM_VERS);
428
429         /* this registers both UDP and TCP services */
430         rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
431
432         /* If we got this far, we have successfully started, so notify parent */
433         if (pipefds[1] > 0) {
434                 status = 0;
435                 if (write(pipefds[1], &status, 1) != 1) {
436                         xlog_warn("writing to parent pipe failed: errno %d (%s)\n",
437                                 errno, strerror(errno));
438                 }
439                 close(pipefds[1]);
440                 pipefds[1] = -1;
441         }
442
443         if (!nsm_drop_privileges(pidfd))
444                 exit(1);
445
446         for (;;) {
447                 /*
448                  * Handle incoming requests:  SM_NOTIFY socket requests, as
449                  * well as callbacks from lockd.
450                  */
451                 my_svc_run();   /* I rolled my own, Olaf made it better... */
452
453                 /* Only get here when simulating a crash so we should probably
454                  * start sm-notify running again.  As we have already dropped
455                  * privileges, this might not work, but I don't think
456                  * responding to SM_SIMU_CRASH is an important use cases to
457                  * get perfect.
458                  */
459                 if (! (run_mode & MODE_NO_NOTIFY))
460                         switch (pid = fork()) {
461                         case 0:
462                                 run_sm_notify(out_port);
463                                 break;
464                         case -1:
465                                 break;
466                         default:
467                                 waitpid(pid, NULL, 0);
468                         }
469
470         }
471         return 0;
472 }