]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/statd.c
62009111edd5a0a594c823c3dc62972fa58f2193
[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         { NULL, 0, 0, 0 }
74 };
75
76 extern void sm_prog_1 (struct svc_req *, register SVCXPRT *);
77 extern int statd_get_socket(void);
78
79 #ifdef SIMULATIONS
80 extern void simulator (int, char **);
81 #endif
82
83
84 #ifdef HAVE_TCP_WRAPPER 
85 #include "tcpwrapper.h"
86
87 static void 
88 sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
89 {
90         /* remote host authorization check */
91         if (!check_default("statd", svc_getcaller(transp),
92                                  rqstp->rq_proc, SM_PROG)) {
93                 svcerr_auth (transp, AUTH_FAILED);
94                 return;
95         }
96
97         sm_prog_1 (rqstp, transp);
98 }
99
100 #define sm_prog_1 sm_prog_1_wrapper
101 #endif
102
103 /*
104  * Signal handler.
105  */
106 static void 
107 killer (int sig)
108 {
109         note (N_FATAL, "Caught signal %d, un-registering and exiting.", sig);
110         pmap_unset (SM_PROG, SM_VERS);
111
112         exit (0);
113 }
114
115 static void
116 sigusr (int sig)
117 {
118         extern void my_svc_exit (void);
119         dprintf (N_DEBUG, "Caught signal %d, re-notifying (state %d).", sig,
120                                                                 MY_STATE);
121         my_svc_exit();
122 }
123
124 /*
125  * Startup information.
126  */
127 static void log_modes(void)
128 {
129         char buf[128];          /* watch stack size... */
130
131         /* No flags = no message */
132         if (!run_mode) return;
133
134         memset(buf,0,128);
135         sprintf(buf,"Flags: ");
136         if (run_mode & MODE_NODAEMON)
137                 strcat(buf,"No-Daemon ");
138         if (run_mode & MODE_LOG_STDERR)
139                 strcat(buf,"Log-STDERR ");
140
141         note(N_WARNING,buf);
142 }
143
144 /*
145  * Since we do more than standard statd stuff, we might need to
146  * help the occasional admin. 
147  */
148 static void 
149 usage()
150 {
151         fprintf(stderr,"usage: %s [options]\n", name_p);
152         fprintf(stderr,"      -h, -?, --help       Print this help screen.\n");
153         fprintf(stderr,"      -F, --foreground     Foreground (no-daemon mode)\n");
154         fprintf(stderr,"      -d, --no-syslog      Verbose logging to stderr.  Foreground mode only.\n");
155         fprintf(stderr,"      -p, --port           Port to listen on\n");
156         fprintf(stderr,"      -o, --outgoing-port  Port for outgoing connections\n");
157         fprintf(stderr,"      -V, -v, --version    Display version information and exit.\n");
158         fprintf(stderr,"      -n, --name           Specify a local hostname.\n");
159         fprintf(stderr,"      -P                   State directory path.\n");
160         fprintf(stderr,"      -N                   Run in notify only mode.\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                 die("Opening %s failed: %s\n",
175                     pidfile, strerror(errno));
176         fprintf(fp, "%d\n", getpid());
177         pidfd = dup(fileno(fp));
178         if (fclose(fp) < 0)
179                 note(N_WARNING, "Flushing pid file failed.\n");
180 }
181
182 static void truncate_pidfile(void)
183 {
184         if (pidfd >= 0)
185                 ftruncate(pidfd, 0);
186 }
187
188 static void drop_privs(void)
189 {
190         struct stat st;
191
192         if (stat(SM_DIR, &st) == -1 &&
193             stat(DIR_BASE, &st) == -1) {
194                 st.st_uid = 0;
195                 st.st_gid = 0;
196         }
197
198         if (st.st_uid == 0) {
199                 note(N_WARNING, "statd running as root. chown %s to choose different user\n",
200                     SM_DIR);
201                 return;
202         }
203         /* better chown the pid file before dropping, as if it
204          * if over nfs we might loose access
205          */
206         if (pidfd >= 0)
207                 fchown(pidfd, st.st_uid, st.st_gid);
208
209         setgroups(0, NULL);
210         if (setgid(st.st_gid) == -1
211             || setuid(st.st_uid) == -1) {
212                 note(N_ERROR, "Fail to drop privileges");
213                 exit(1);
214         }
215 }
216
217 static void run_sm_notify(int outport)
218 {
219         char op[20];
220         char *av[6];
221         int ac = 0;
222
223         av[ac++] = "/usr/sbin/sm-notify";
224         if (run_mode & MODE_NODAEMON)
225                 av[ac++] = "-d";
226         if (outport) {
227                 sprintf(op, "-p%d", outport);
228                 av[ac++] = op;
229         }
230         if (run_mode & STATIC_HOSTNAME) {
231                 av[ac++] = "-N";
232                 av[ac++] = MY_NAME;
233         }
234         av[ac] = NULL;
235         fprintf(stderr, "%s: -N deprecated, consider using /usr/sbin/sm-notify directly\n",
236                 name_p);
237         execv(av[0], av);
238         fprintf(stderr, "%s: failed to run %s\n", name_p, av[0]);
239         exit(2);
240
241 }
242 /* 
243  * Entry routine/main loop.
244  */
245 int main (int argc, char **argv)
246 {
247         extern char *optarg;
248         int pid;
249         int arg;
250         int port = 0, out_port = 0;
251         struct rlimit rlim;
252
253         int pipefds[2] = { -1, -1};
254         char status;
255
256         /* Default: daemon mode, no other options */
257         run_mode = 0;
258
259         /* Set the basename */
260         if ((name_p = strrchr(argv[0],'/')) != NULL) {
261                 name_p ++;
262         } else {
263                 name_p = argv[0];
264         }
265
266         /* Get the version */
267         if ((version_p = strrchr(VERSION,' ')) != NULL) {
268                 version_p++;
269         } else {
270                 version_p = VERSION;
271         }
272         
273         /* Set hostname */
274         MY_NAME = NULL;
275
276         /* Process command line switches */
277         while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:", longopts, NULL)) != EOF) {
278                 switch (arg) {
279                 case 'V':       /* Version */
280                 case 'v':
281                         printf("%s version %s\n",name_p,version_p);
282                         exit(0);
283                 case 'F':       /* Foreground/nodaemon mode */
284                         run_mode |= MODE_NODAEMON;
285                         break;
286                 case 'N':
287                         run_mode |= MODE_NOTIFY_ONLY;
288                         break;
289                 case 'd':       /* No daemon only - log to stderr */
290                         run_mode |= MODE_LOG_STDERR;
291                         break;
292                 case 'o':
293                         out_port = atoi(optarg);
294                         if (out_port < 1 || out_port > 65535) {
295                                 fprintf(stderr, "%s: bad port number: %s\n",
296                                         argv[0], optarg);
297                                 usage();
298                                 exit(1);
299                         }
300                         break;
301                 case 'p':
302                         port = atoi(optarg);
303                         if (port < 1 || port > 65535) {
304                                 fprintf(stderr, "%s: bad port number: %s\n",
305                                         argv[0], optarg);
306                                 usage();
307                                 exit(1);
308                         }
309                         break;
310                 case 'n':       /* Specify local hostname */
311                         run_mode |= STATIC_HOSTNAME;
312                         MY_NAME = xstrdup(optarg);
313                         break;
314                 case 'P':
315
316                         if ((DIR_BASE = xstrdup(optarg)) == NULL) {
317                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
318                                         argv[0], optarg);
319                                 exit(1);
320                         }
321
322                         SM_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm"));
323                         SM_BAK_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm.bak"));
324                         SM_STAT_PATH = xmalloc(strlen(DIR_BASE) + 1 + sizeof("state"));
325
326                         if ((SM_DIR == NULL) 
327                             || (SM_BAK_DIR == NULL) 
328                             || (SM_STAT_PATH == NULL)) {
329
330                                 fprintf(stderr, "%s: xmalloc() failed!\n",
331                                         argv[0]);
332                                 exit(1);
333                         }
334                         if (DIR_BASE[strlen(DIR_BASE)-1] == '/') {
335                                 sprintf(SM_DIR, "%ssm", DIR_BASE );
336                                 sprintf(SM_BAK_DIR, "%ssm.bak", DIR_BASE );
337                                 sprintf(SM_STAT_PATH, "%sstate", DIR_BASE );
338                         } else {
339                                 sprintf(SM_DIR, "%s/sm", DIR_BASE );
340                                 sprintf(SM_BAK_DIR, "%s/sm.bak", DIR_BASE );
341                                 sprintf(SM_STAT_PATH, "%s/state", DIR_BASE );
342                         }
343                         break;
344                 case 'H': /* PRC: specify the ha-callout program */
345                         if ((ha_callout_prog = xstrdup(optarg)) == NULL) {
346                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
347                                         argv[0], optarg);
348                                 exit(1);
349                         }
350                         break;
351                 case '?':       /* heeeeeelllllllpppp? heh */
352                 case 'h':
353                         usage();
354                         exit (0);
355                 default:        /* oh dear ... heh */
356                         usage();
357                         exit(-1);
358                 }
359         }
360
361         if (port == out_port && port != 0) {
362                 fprintf(stderr, "Listening and outgoing ports cannot be the same!\n");
363                 exit(-1);
364         }
365
366         if (run_mode & MODE_NOTIFY_ONLY)
367                 run_sm_notify(out_port);
368
369
370         if (!(run_mode & MODE_NODAEMON)) {
371                 run_mode &= ~MODE_LOG_STDERR;   /* Never log to console in
372                                                    daemon mode. */
373         }
374
375         if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
376                 fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
377                                 argv [0], strerror(errno));
378         else {
379                 /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
380                 if (rlim.rlim_cur > FD_SETSIZE) {
381                         rlim.rlim_cur = FD_SETSIZE;
382
383                         if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
384                                 fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
385                                         argv [0], strerror(errno));
386                         }
387                 }
388         }
389
390 #ifdef SIMULATIONS
391         if (argc > 1)
392                 /* LH - I _really_ need to update simulator... */
393                 simulator (--argc, ++argv);     /* simulator() does exit() */
394 #endif
395         
396         if (!(run_mode & MODE_NODAEMON)) {
397                 int tempfd;
398
399                 if (pipe(pipefds)<0) {
400                         perror("statd: unable to create pipe");
401                         exit(1);
402                 }
403                 if ((pid = fork ()) < 0) {
404                         perror ("statd: Could not fork");
405                         exit (1);
406                 } else if (pid != 0) {
407                         /* Parent.
408                          * Wait for status from child.
409                          */
410                         close(pipefds[1]);
411                         if (read(pipefds[0], &status, 1) != 1)
412                                 exit(1);
413                         exit (0);
414                 }
415                 /* Child.       */
416                 close(pipefds[0]);
417                 setsid ();
418                 if (chdir (DIR_BASE) == -1) {
419                         perror("statd: Could not chdir");
420                         exit(1);
421                 }
422
423                 while (pipefds[1] <= 2) {
424                         pipefds[1] = dup(pipefds[1]);
425                         if (pipefds[1]<0) {
426                                 perror("statd: dup");
427                                 exit(1);
428                         }
429                 }
430                 tempfd = open("/dev/null", O_RDWR);
431                 dup2(tempfd, 0);
432                 dup2(tempfd, 1);
433                 dup2(tempfd, 2);
434                 dup2(pipefds[1], 3);
435                 pipefds[1] = 3;
436                 closeall(4);
437         }
438
439         /* Child. */
440
441         log_init (name_p,version_p);
442
443         log_modes();
444
445         signal (SIGHUP, killer);
446         signal (SIGINT, killer);
447         signal (SIGTERM, killer);
448         /* PRC: trap SIGUSR1 to re-read notify list from disk */
449         signal(SIGUSR1, sigusr);
450         /* WARNING: the following works on Linux and SysV, but not BSD! */
451         signal(SIGCHLD, SIG_IGN);
452         /*
453          * Ignore SIGPIPE to avoid statd dying when peers close their
454          * TCP connection while we're trying to reply to them.
455          */
456         signal(SIGPIPE, SIG_IGN);
457
458         create_pidfile();
459         atexit(truncate_pidfile);
460
461         switch (pid = fork()) {
462         case 0:
463                 run_sm_notify(out_port);
464                 break;
465         case -1:
466                 break;
467         default:
468                 waitpid(pid, NULL, 0);
469         }
470
471         drop_privs();
472
473         for (;;) {
474                 pmap_unset (SM_PROG, SM_VERS);
475
476                 /* If we got this far, we have successfully started, so notify parent */
477                 if (pipefds[1] > 0) {
478                         status = 0;
479                         write(pipefds[1], &status, 1);
480                         close(pipefds[1]);
481                         pipefds[1] = -1;
482                 }
483
484                 /* this registers both UDP and TCP services */
485                 rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
486
487                 /*
488                  * Handle incoming requests:  SM_NOTIFY socket requests, as
489                  * well as callbacks from lockd.
490                  */
491                 my_svc_run();   /* I rolled my own, Olaf made it better... */
492
493                 /* Only get here when simulating a crash so we should probably
494                  * start sm-notify running again.  As we have already dropped
495                  * privileges, this might not work, but I don't think
496                  * responding to SM_SIMU_CRASH is an important use cases to
497                  * get perfect.
498                  */
499                 switch (pid = fork()) {
500                 case 0:
501                         run_sm_notify(out_port);
502                         break;
503                 case -1:
504                         break;
505                 default:
506                         waitpid(pid, NULL, 0);
507                 }
508         }
509         return 0;
510 }