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