d16549a1c6e222cfcd6f979c57acca4116ab6a2e
[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         /* remote host authorization check */
79         if (!check_default("statd", nfs_getrpccaller(transp), SM_PROG)) {
80                 svcerr_auth (transp, AUTH_FAILED);
81                 return;
82         }
83
84         sm_prog_1 (rqstp, transp);
85 }
86
87 #define sm_prog_1 sm_prog_1_wrapper
88 #endif
89
90 static void
91 statd_unregister(void) {
92         nfs_svc_unregister(SM_PROG, SM_VERS);
93 }
94
95 /*
96  * Signal handler.
97  */
98 static void 
99 killer (int sig)
100 {
101         statd_unregister ();
102         xlog_err ("Caught signal %d, un-registering and exiting", sig);
103 }
104
105 static void
106 sigusr (int sig)
107 {
108         extern void my_svc_exit (void);
109         xlog(D_GENERAL, "Caught signal %d, re-notifying (state %d)", sig,
110                                                                 MY_STATE);
111         my_svc_exit();
112 }
113
114 /*
115  * Startup information.
116  */
117 static void log_modes(void)
118 {
119         char buf[128];          /* watch stack size... */
120
121         /* No flags = no message */
122         if (!run_mode) return;
123
124         memset(buf,0,128);
125         sprintf(buf,"Flags: ");
126         if (run_mode & MODE_NODAEMON)
127                 strcat(buf,"No-Daemon ");
128         if (run_mode & MODE_LOG_STDERR)
129                 strcat(buf,"Log-STDERR ");
130 #ifdef HAVE_LIBTIRPC
131         strcat(buf, "TI-RPC ");
132 #endif
133
134         xlog_warn(buf);
135 }
136
137 /*
138  * Since we do more than standard statd stuff, we might need to
139  * help the occasional admin. 
140  */
141 static void 
142 usage(void)
143 {
144         fprintf(stderr,"usage: %s [options]\n", name_p);
145         fprintf(stderr,"      -h, -?, --help       Print this help screen.\n");
146         fprintf(stderr,"      -F, --foreground     Foreground (no-daemon mode)\n");
147         fprintf(stderr,"      -d, --no-syslog      Verbose logging to stderr.  Foreground mode only.\n");
148         fprintf(stderr,"      -p, --port           Port to listen on\n");
149         fprintf(stderr,"      -o, --outgoing-port  Port for outgoing connections\n");
150         fprintf(stderr,"      -V, -v, --version    Display version information and exit.\n");
151         fprintf(stderr,"      -n, --name           Specify a local hostname.\n");
152         fprintf(stderr,"      -P                   State directory path.\n");
153         fprintf(stderr,"      -N                   Run in notify only mode.\n");
154         fprintf(stderr,"      -L, --no-notify      Do not perform any notification.\n");
155         fprintf(stderr,"      -H                   Specify a high-availability callout program.\n");
156 }
157
158 static const char *pidfile = "/var/run/rpc.statd.pid";
159
160 int pidfd = -1;
161 static void create_pidfile(void)
162 {
163         FILE *fp;
164
165         unlink(pidfile);
166         fp = fopen(pidfile, "w");
167         if (!fp)
168                 xlog_err("Opening %s failed: %m\n", pidfile);
169         fprintf(fp, "%d\n", getpid());
170         pidfd = dup(fileno(fp));
171         if (fclose(fp) < 0) {
172                 xlog_warn("Flushing pid file failed: errno %d (%m)\n",
173                         errno);
174         }
175 }
176
177 static void truncate_pidfile(void)
178 {
179         if (pidfd >= 0) {
180                 if (ftruncate(pidfd, 0) < 0) {
181                         xlog_warn("truncating pid file failed: errno %d (%m)\n",
182                                 errno);
183                 }
184         }
185 }
186
187 static void run_sm_notify(int outport)
188 {
189         char op[20];
190         char *av[6];
191         int ac = 0;
192
193         av[ac++] = "/sbin/sm-notify";
194         if (run_mode & MODE_NODAEMON)
195                 av[ac++] = "-d";
196         if (outport) {
197                 sprintf(op, "-p%d", outport);
198                 av[ac++] = op;
199         }
200         if (run_mode & STATIC_HOSTNAME) {
201                 av[ac++] = "-v";
202                 av[ac++] = MY_NAME;
203         }
204         av[ac] = NULL;
205         execv(av[0], av);
206         fprintf(stderr, "%s: failed to run %s\n", name_p, av[0]);
207         exit(2);
208
209 }
210 /* 
211  * Entry routine/main loop.
212  */
213 int main (int argc, char **argv)
214 {
215         extern char *optarg;
216         int pid;
217         int arg;
218         int port = 0, out_port = 0;
219         struct rlimit rlim;
220
221         int pipefds[2] = { -1, -1};
222         char status;
223
224         /* Default: daemon mode, no other options */
225         run_mode = 0;
226         xlog_stderr(0);
227         xlog_syslog(1);
228
229         /* Set the basename */
230         if ((name_p = strrchr(argv[0],'/')) != NULL) {
231                 name_p ++;
232         } else {
233                 name_p = argv[0];
234         }
235
236         /* Set hostname */
237         MY_NAME = NULL;
238
239         /* Process command line switches */
240         while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:L", longopts, NULL)) != EOF) {
241                 switch (arg) {
242                 case 'V':       /* Version */
243                 case 'v':
244                         printf("%s version " VERSION "\n",name_p);
245                         exit(0);
246                 case 'F':       /* Foreground/nodaemon mode */
247                         run_mode |= MODE_NODAEMON;
248                         break;
249                 case 'N':
250                         run_mode |= MODE_NOTIFY_ONLY;
251                         break;
252                 case 'L': /* Listen only */
253                         run_mode |= MODE_NO_NOTIFY;
254                         break;
255                 case 'd':       /* No daemon only - log to stderr */
256                         run_mode |= MODE_LOG_STDERR;
257                         break;
258                 case 'o':
259                         out_port = atoi(optarg);
260                         if (out_port < 1 || out_port > 65535) {
261                                 fprintf(stderr, "%s: bad port number: %s\n",
262                                         argv[0], optarg);
263                                 usage();
264                                 exit(1);
265                         }
266                         break;
267                 case 'p':
268                         port = atoi(optarg);
269                         if (port < 1 || port > 65535) {
270                                 fprintf(stderr, "%s: bad port number: %s\n",
271                                         argv[0], optarg);
272                                 usage();
273                                 exit(1);
274                         }
275                         break;
276                 case 'n':       /* Specify local hostname */
277                         run_mode |= STATIC_HOSTNAME;
278                         MY_NAME = xstrdup(optarg);
279                         break;
280                 case 'P':
281                         if (!nsm_setup_pathnames(argv[0], optarg))
282                                 exit(1);
283                         break;
284                 case 'H': /* PRC: specify the ha-callout program */
285                         if ((ha_callout_prog = xstrdup(optarg)) == NULL) {
286                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
287                                         argv[0], optarg);
288                                 exit(1);
289                         }
290                         break;
291                 case '?':       /* heeeeeelllllllpppp? heh */
292                 case 'h':
293                         usage();
294                         exit (0);
295                 default:        /* oh dear ... heh */
296                         usage();
297                         exit(-1);
298                 }
299         }
300
301         if (port == out_port && port != 0) {
302                 fprintf(stderr, "Listening and outgoing ports cannot be the same!\n");
303                 exit(-1);
304         }
305
306         if (run_mode & MODE_NOTIFY_ONLY) {
307                 fprintf(stderr, "%s: -N deprecated, consider using /usr/sbin/sm-notify directly\n",
308                         name_p);
309                 run_sm_notify(out_port);
310         }
311
312         if (!(run_mode & MODE_NODAEMON)) {
313                 run_mode &= ~MODE_LOG_STDERR;   /* Never log to console in
314                                                    daemon mode. */
315         }
316
317         if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
318                 fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
319                                 argv [0], strerror(errno));
320         else {
321                 /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
322                 if (rlim.rlim_cur > FD_SETSIZE) {
323                         rlim.rlim_cur = FD_SETSIZE;
324
325                         if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
326                                 fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
327                                         argv [0], strerror(errno));
328                         }
329                 }
330         }
331
332 #ifdef SIMULATIONS
333         if (argc > 1)
334                 /* LH - I _really_ need to update simulator... */
335                 simulator (--argc, ++argv);     /* simulator() does exit() */
336 #endif
337         
338         if (!(run_mode & MODE_NODAEMON)) {
339                 int tempfd;
340
341                 if (pipe(pipefds)<0) {
342                         perror("statd: unable to create pipe");
343                         exit(1);
344                 }
345                 if ((pid = fork ()) < 0) {
346                         perror ("statd: Could not fork");
347                         exit (1);
348                 } else if (pid != 0) {
349                         /* Parent.
350                          * Wait for status from child.
351                          */
352                         close(pipefds[1]);
353                         if (read(pipefds[0], &status, 1) != 1)
354                                 exit(1);
355                         exit (0);
356                 }
357                 /* Child.       */
358                 close(pipefds[0]);
359                 setsid ();
360
361                 while (pipefds[1] <= 2) {
362                         pipefds[1] = dup(pipefds[1]);
363                         if (pipefds[1]<0) {
364                                 perror("statd: dup");
365                                 exit(1);
366                         }
367                 }
368                 tempfd = open("/dev/null", O_RDWR);
369                 dup2(tempfd, 0);
370                 dup2(tempfd, 1);
371                 dup2(tempfd, 2);
372                 dup2(pipefds[1], 3);
373                 pipefds[1] = 3;
374                 closeall(4);
375         }
376
377         /* Child. */
378
379         if (run_mode & MODE_LOG_STDERR) {
380                 xlog_syslog(0);
381                 xlog_stderr(1);
382                 xlog_config(D_ALL, 1);
383         }
384         xlog_open(name_p);
385         xlog(L_NOTICE, "Version " VERSION " starting");
386
387         log_modes();
388
389         signal (SIGHUP, killer);
390         signal (SIGINT, killer);
391         signal (SIGTERM, killer);
392         /* PRC: trap SIGUSR1 to re-read notify list from disk */
393         signal(SIGUSR1, sigusr);
394         /* WARNING: the following works on Linux and SysV, but not BSD! */
395         signal(SIGCHLD, SIG_IGN);
396         /*
397          * Ignore SIGPIPE to avoid statd dying when peers close their
398          * TCP connection while we're trying to reply to them.
399          */
400         signal(SIGPIPE, SIG_IGN);
401
402         create_pidfile();
403         atexit(truncate_pidfile);
404
405         if (! (run_mode & MODE_NO_NOTIFY))
406                 switch (pid = fork()) {
407                 case 0:
408                         run_sm_notify(out_port);
409                         break;
410                 case -1:
411                         break;
412                 default:
413                         waitpid(pid, NULL, 0);
414                 }
415
416         /* Make sure we have a privilege port for calling into the kernel */
417         if (statd_get_socket() < 0)
418                 exit(1);
419
420         /* If sm-notify didn't take all the state files, load
421          * state information into our notify-list so we can
422          * pass on any SM_NOTIFY that arrives
423          */
424         load_state();
425
426         MY_STATE = nsm_get_state(0);
427         if (MY_STATE == 0)
428                 exit(1);
429         xlog(D_GENERAL, "Local NSM state number: %d", MY_STATE);
430         nsm_update_kernel_state(MY_STATE);
431
432         /*
433          * ORDER
434          * Clear old listeners while still root, to override any
435          * permission checking done by rpcbind.
436          */
437         statd_unregister();
438
439         /*
440          * ORDER
441          */
442         if (!nsm_drop_privileges(pidfd))
443                 exit(1);
444
445         /*
446          * ORDER
447          * Create RPC listeners after dropping privileges.  This permits
448          * statd to unregister its own listeners when it exits.
449          */
450         if (nfs_svc_create("statd", SM_PROG, SM_VERS, sm_prog_1, port) == 0) {
451                 xlog(L_ERROR, "failed to create RPC listeners, exiting");
452                 exit(1);
453         }
454         atexit(statd_unregister);
455
456         /* If we got this far, we have successfully started, so notify parent */
457         if (pipefds[1] > 0) {
458                 status = 0;
459                 if (write(pipefds[1], &status, 1) != 1) {
460                         xlog_warn("writing to parent pipe failed: errno %d (%s)\n",
461                                 errno, strerror(errno));
462                 }
463                 close(pipefds[1]);
464                 pipefds[1] = -1;
465         }
466
467         for (;;) {
468                 /*
469                  * Handle incoming requests:  SM_NOTIFY socket requests, as
470                  * well as callbacks from lockd.
471                  */
472                 my_svc_run();   /* I rolled my own, Olaf made it better... */
473
474                 /* Only get here when simulating a crash so we should probably
475                  * start sm-notify running again.  As we have already dropped
476                  * privileges, this might not work, but I don't think
477                  * responding to SM_SIMU_CRASH is an important use cases to
478                  * get perfect.
479                  */
480                 if (! (run_mode & MODE_NO_NOTIFY))
481                         switch (pid = fork()) {
482                         case 0:
483                                 run_sm_notify(out_port);
484                                 break;
485                         case -1:
486                                 break;
487                         default:
488                                 waitpid(pid, NULL, 0);
489                         }
490
491         }
492         return 0;
493 }