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