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