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