]> 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.\n");
184 }
185
186 static void truncate_pidfile(void)
187 {
188         if (pidfd >= 0)
189                 ftruncate(pidfd, 0);
190 }
191
192 static void drop_privs(void)
193 {
194         struct stat st;
195
196         if (stat(SM_DIR, &st) == -1 &&
197             stat(DIR_BASE, &st) == -1) {
198                 st.st_uid = 0;
199                 st.st_gid = 0;
200         }
201
202         if (st.st_uid == 0) {
203                 note(N_WARNING, "statd running as root. chown %s to choose different user\n",
204                     SM_DIR);
205                 return;
206         }
207         /* better chown the pid file before dropping, as if it
208          * if over nfs we might loose access
209          */
210         if (pidfd >= 0)
211                 fchown(pidfd, st.st_uid, st.st_gid);
212
213         setgroups(0, NULL);
214         if (setgid(st.st_gid) == -1
215             || setuid(st.st_uid) == -1) {
216                 note(N_ERROR, "Fail to drop privileges");
217                 exit(1);
218         }
219 }
220
221 static void run_sm_notify(int outport)
222 {
223         char op[20];
224         char *av[6];
225         int ac = 0;
226
227         av[ac++] = "/usr/sbin/sm-notify";
228         if (run_mode & MODE_NODAEMON)
229                 av[ac++] = "-d";
230         if (outport) {
231                 sprintf(op, "-p%d", outport);
232                 av[ac++] = op;
233         }
234         if (run_mode & STATIC_HOSTNAME) {
235                 av[ac++] = "-v";
236                 av[ac++] = MY_NAME;
237         }
238         av[ac] = NULL;
239         execv(av[0], av);
240         fprintf(stderr, "%s: failed to run %s\n", name_p, av[0]);
241         exit(2);
242
243 }
244 /* 
245  * Entry routine/main loop.
246  */
247 int main (int argc, char **argv)
248 {
249         extern char *optarg;
250         int pid;
251         int arg;
252         int port = 0, out_port = 0;
253         struct rlimit rlim;
254
255         int pipefds[2] = { -1, -1};
256         char status;
257
258         /* Default: daemon mode, no other options */
259         run_mode = 0;
260
261         /* Set the basename */
262         if ((name_p = strrchr(argv[0],'/')) != NULL) {
263                 name_p ++;
264         } else {
265                 name_p = argv[0];
266         }
267
268         /* Get the version */
269         if ((version_p = strrchr(VERSION,' ')) != NULL) {
270                 version_p++;
271         } else {
272                 version_p = VERSION;
273         }
274         
275         /* Set hostname */
276         MY_NAME = NULL;
277
278         /* Process command line switches */
279         while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:L", longopts, NULL)) != EOF) {
280                 switch (arg) {
281                 case 'V':       /* Version */
282                 case 'v':
283                         printf("%s version %s\n",name_p,version_p);
284                         exit(0);
285                 case 'F':       /* Foreground/nodaemon mode */
286                         run_mode |= MODE_NODAEMON;
287                         break;
288                 case 'N':
289                         run_mode |= MODE_NOTIFY_ONLY;
290                         break;
291                 case 'L': /* Listen only */
292                         run_mode |= MODE_NO_NOTIFY;
293                         break;
294                 case 'd':       /* No daemon only - log to stderr */
295                         run_mode |= MODE_LOG_STDERR;
296                         break;
297                 case 'o':
298                         out_port = atoi(optarg);
299                         if (out_port < 1 || out_port > 65535) {
300                                 fprintf(stderr, "%s: bad port number: %s\n",
301                                         argv[0], optarg);
302                                 usage();
303                                 exit(1);
304                         }
305                         break;
306                 case 'p':
307                         port = atoi(optarg);
308                         if (port < 1 || 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 'n':       /* Specify local hostname */
316                         run_mode |= STATIC_HOSTNAME;
317                         MY_NAME = xstrdup(optarg);
318                         break;
319                 case 'P':
320
321                         if ((DIR_BASE = xstrdup(optarg)) == NULL) {
322                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
323                                         argv[0], optarg);
324                                 exit(1);
325                         }
326
327                         SM_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm"));
328                         SM_BAK_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm.bak"));
329                         SM_STAT_PATH = xmalloc(strlen(DIR_BASE) + 1 + sizeof("state"));
330
331                         if ((SM_DIR == NULL) 
332                             || (SM_BAK_DIR == NULL) 
333                             || (SM_STAT_PATH == NULL)) {
334
335                                 fprintf(stderr, "%s: xmalloc() failed!\n",
336                                         argv[0]);
337                                 exit(1);
338                         }
339                         if (DIR_BASE[strlen(DIR_BASE)-1] == '/') {
340                                 sprintf(SM_DIR, "%ssm", DIR_BASE );
341                                 sprintf(SM_BAK_DIR, "%ssm.bak", DIR_BASE );
342                                 sprintf(SM_STAT_PATH, "%sstate", DIR_BASE );
343                         } else {
344                                 sprintf(SM_DIR, "%s/sm", DIR_BASE );
345                                 sprintf(SM_BAK_DIR, "%s/sm.bak", DIR_BASE );
346                                 sprintf(SM_STAT_PATH, "%s/state", DIR_BASE );
347                         }
348                         break;
349                 case 'H': /* PRC: specify the ha-callout program */
350                         if ((ha_callout_prog = xstrdup(optarg)) == NULL) {
351                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
352                                         argv[0], optarg);
353                                 exit(1);
354                         }
355                         break;
356                 case '?':       /* heeeeeelllllllpppp? heh */
357                 case 'h':
358                         usage();
359                         exit (0);
360                 default:        /* oh dear ... heh */
361                         usage();
362                         exit(-1);
363                 }
364         }
365
366         if (port == out_port && port != 0) {
367                 fprintf(stderr, "Listening and outgoing ports cannot be the same!\n");
368                 exit(-1);
369         }
370
371         if (run_mode & MODE_NOTIFY_ONLY) {
372                 fprintf(stderr, "%s: -N deprecated, consider using /usr/sbin/sm-notify directly\n",
373                         name_p);
374                 run_sm_notify(out_port);
375         }
376
377
378         if (!(run_mode & MODE_NODAEMON)) {
379                 run_mode &= ~MODE_LOG_STDERR;   /* Never log to console in
380                                                    daemon mode. */
381         }
382
383         if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
384                 fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
385                                 argv [0], strerror(errno));
386         else {
387                 /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
388                 if (rlim.rlim_cur > FD_SETSIZE) {
389                         rlim.rlim_cur = FD_SETSIZE;
390
391                         if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
392                                 fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
393                                         argv [0], strerror(errno));
394                         }
395                 }
396         }
397
398 #ifdef SIMULATIONS
399         if (argc > 1)
400                 /* LH - I _really_ need to update simulator... */
401                 simulator (--argc, ++argv);     /* simulator() does exit() */
402 #endif
403         
404         if (!(run_mode & MODE_NODAEMON)) {
405                 int tempfd;
406
407                 if (pipe(pipefds)<0) {
408                         perror("statd: unable to create pipe");
409                         exit(1);
410                 }
411                 if ((pid = fork ()) < 0) {
412                         perror ("statd: Could not fork");
413                         exit (1);
414                 } else if (pid != 0) {
415                         /* Parent.
416                          * Wait for status from child.
417                          */
418                         close(pipefds[1]);
419                         if (read(pipefds[0], &status, 1) != 1)
420                                 exit(1);
421                         exit (0);
422                 }
423                 /* Child.       */
424                 close(pipefds[0]);
425                 setsid ();
426                 if (chdir (DIR_BASE) == -1) {
427                         perror("statd: Could not chdir");
428                         exit(1);
429                 }
430
431                 while (pipefds[1] <= 2) {
432                         pipefds[1] = dup(pipefds[1]);
433                         if (pipefds[1]<0) {
434                                 perror("statd: dup");
435                                 exit(1);
436                         }
437                 }
438                 tempfd = open("/dev/null", O_RDWR);
439                 dup2(tempfd, 0);
440                 dup2(tempfd, 1);
441                 dup2(tempfd, 2);
442                 dup2(pipefds[1], 3);
443                 pipefds[1] = 3;
444                 closeall(4);
445         }
446
447         /* Child. */
448
449         log_init (/*name_p,version_p*/);
450
451         log_modes();
452
453         signal (SIGHUP, killer);
454         signal (SIGINT, killer);
455         signal (SIGTERM, killer);
456         /* PRC: trap SIGUSR1 to re-read notify list from disk */
457         signal(SIGUSR1, sigusr);
458         /* WARNING: the following works on Linux and SysV, but not BSD! */
459         signal(SIGCHLD, SIG_IGN);
460         /*
461          * Ignore SIGPIPE to avoid statd dying when peers close their
462          * TCP connection while we're trying to reply to them.
463          */
464         signal(SIGPIPE, SIG_IGN);
465
466         create_pidfile();
467         atexit(truncate_pidfile);
468
469         if (! (run_mode & MODE_NO_NOTIFY))
470                 switch (pid = fork()) {
471                 case 0:
472                         run_sm_notify(out_port);
473                         break;
474                 case -1:
475                         break;
476                 default:
477                         waitpid(pid, NULL, 0);
478                 }
479
480         /* Make sure we have a privilege port for calling into the kernel */
481         if (statd_get_socket() < 0)
482                 exit(1);
483
484         /* If sm-notify didn't take all the state files, load
485          * state information into our notify-list so we can
486          * pass on any SM_NOTIFY that arrives
487          */
488         load_state();
489         load_state_number();
490         pmap_unset (SM_PROG, SM_VERS);
491
492         /* this registers both UDP and TCP services */
493         rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
494
495         /* If we got this far, we have successfully started, so notify parent */
496         if (pipefds[1] > 0) {
497                 status = 0;
498                 write(pipefds[1], &status, 1);
499                 close(pipefds[1]);
500                 pipefds[1] = -1;
501         }
502
503         drop_privs();
504
505         for (;;) {
506                 /*
507                  * Handle incoming requests:  SM_NOTIFY socket requests, as
508                  * well as callbacks from lockd.
509                  */
510                 my_svc_run();   /* I rolled my own, Olaf made it better... */
511
512                 /* Only get here when simulating a crash so we should probably
513                  * start sm-notify running again.  As we have already dropped
514                  * privileges, this might not work, but I don't think
515                  * responding to SM_SIMU_CRASH is an important use cases to
516                  * get perfect.
517                  */
518                 if (! (run_mode & MODE_NO_NOTIFY))
519                         switch (pid = fork()) {
520                         case 0:
521                                 run_sm_notify(out_port);
522                                 break;
523                         case -1:
524                                 break;
525                         default:
526                                 waitpid(pid, NULL, 0);
527                         }
528
529         }
530         return 0;
531 }
532
533 static void
534 load_state_number(void)
535 {
536         int fd;
537
538         if ((fd = open(SM_STAT_PATH, O_RDONLY)) == -1)
539                 return;
540
541         read(fd, &MY_STATE, sizeof(MY_STATE));
542         close(fd);
543         fd = open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY);
544         if (fd >= 0) {
545                 char buf[20];
546                 snprintf(buf, sizeof(buf), "%d", MY_STATE);
547                 write(fd, buf, strlen(buf));
548                 close(fd);
549         }
550
551 }