]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/statd.c
f61914d5787e4642fcfda1c55406f21c5b58816e
[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 #include "config.h"
11 #include <limits.h>
12 #include <signal.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <getopt.h>
18 #include <rpc/rpc.h>
19 #include <rpc/pmap_clnt.h>
20 #include <rpcmisc.h>
21 #include <sys/resource.h>
22 #include <grp.h>
23 #include "statd.h"
24 #include "version.h"
25 #include "nfslib.h"
26
27 /* Socket operations */
28 #include <sys/types.h>
29 #include <sys/socket.h>
30
31 /* Added to enable specification of state directory path at run-time
32  * j_carlos_gomez@yahoo.com
33  */
34
35 char * DIR_BASE = DEFAULT_DIR_BASE;
36
37 char *  SM_DIR = DEFAULT_SM_DIR;
38 char *  SM_BAK_DIR =  DEFAULT_SM_BAK_DIR;
39 char *  SM_STAT_PATH = DEFAULT_SM_STAT_PATH;
40
41 /* ----- end of state directory path stuff ------- */
42
43 short int restart = 0;
44 int     run_mode = 0;           /* foreground logging mode */
45
46 /* LH - I had these local to main, but it seemed silly to have 
47  * two copies of each - one in main(), one static in log.c... 
48  * It also eliminates the 256-char static in log.c */
49 char *name_p = NULL;
50 char *version_p = NULL;
51
52 /* PRC: a high-availability callout program can be specified with -H
53  * When this is done, the program will receive callouts whenever clients
54  * are added or deleted to the notify list */
55 char *ha_callout_prog = NULL;
56
57 static struct option longopts[] =
58 {
59         { "foreground", 0, 0, 'F' },
60         { "no-syslog", 0, 0, 'd' },
61         { "help", 0, 0, 'h' },
62         { "version", 0, 0, 'v' },
63         { "outgoing-port", 1, 0, 'o' },
64         { "port", 1, 0, 'p' },
65         { "name", 1, 0, 'n' },
66         { "state-directory-path", 1, 0, 'P' },
67         { "notify-mode", 0, 0, 'N' },
68         { "ha-callout", 1, 0, 'H' },
69         { NULL, 0, 0, 0 }
70 };
71
72 extern void sm_prog_1 (struct svc_req *, register SVCXPRT *);
73 extern int statd_get_socket(int port);
74
75 #ifdef SIMULATIONS
76 extern void simulator (int, char **);
77 #endif
78
79
80 #ifdef HAVE_TCP_WRAPPER 
81 #include "tcpwrapper.h"
82
83 static void 
84 sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
85 {
86         /* remote host authorization check */
87         if (!check_default("statd", svc_getcaller(transp),
88                                  rqstp->rq_proc, SM_PROG)) {
89                 svcerr_auth (transp, AUTH_FAILED);
90                 return;
91         }
92
93         sm_prog_1 (rqstp, transp);
94 }
95
96 #define sm_prog_1 sm_prog_1_wrapper
97 #endif
98
99 /*
100  * Signal handler.
101  */
102 static void 
103 killer (int sig)
104 {
105         note (N_FATAL, "Caught signal %d, un-registering and exiting.", sig);
106         if (!(run_mode & MODE_NOTIFY_ONLY))
107                 pmap_unset (SM_PROG, SM_VERS);
108
109         exit (0);
110 }
111
112 static void
113 sigusr (int sig)
114 {
115         dprintf (N_DEBUG, "Caught signal %d, re-notifying (state %d).", sig,
116                                                                 MY_STATE);
117         re_notify = 1;
118 }
119
120 /*
121  * Startup information.
122  */
123 static void log_modes(void)
124 {
125         char buf[128];          /* watch stack size... */
126
127         /* No flags = no message */
128         if (!run_mode) return;
129
130         memset(buf,0,128);
131         sprintf(buf,"Flags: ");
132         if (run_mode & MODE_NODAEMON)
133                 strcat(buf,"No-Daemon ");
134         if (run_mode & MODE_LOG_STDERR)
135                 strcat(buf,"Log-STDERR ");
136
137         if (run_mode & MODE_NOTIFY_ONLY)
138         {
139                 strcat(buf,"Notify-Only ");
140         }
141         note(N_WARNING,buf);
142         /* future: IP aliasing
143         if (run_mode & MODE_NOTIFY_ONLY)
144         {
145                 dprintf(N_DEBUG,"Notify IP: %s",svr_addr);
146         } */
147 }
148
149 /*
150  * Since we do more than standard statd stuff, we might need to
151  * help the occasional admin. 
152  */
153 static void 
154 usage()
155 {
156         fprintf(stderr,"usage: %s [options]\n", name_p);
157         fprintf(stderr,"      -h, -?, --help       Print this help screen.\n");
158         fprintf(stderr,"      -F, --foreground     Foreground (no-daemon mode)\n");
159         fprintf(stderr,"      -d, --no-syslog      Verbose logging to stderr.  Foreground mode only.\n");
160         fprintf(stderr,"      -p, --port           Port to listen on\n");
161         fprintf(stderr,"      -o, --outgoing-port  Port for outgoing connections\n");
162         fprintf(stderr,"      -V, -v, --version    Display version information and exit.\n");
163         fprintf(stderr,"      -n, --name           Specify a local hostname.\n");
164         fprintf(stderr,"      -P                   State directory path.\n");
165         fprintf(stderr,"      -N                   Run in notify only mode.\n");
166         fprintf(stderr,"      -H                   Specify a high-availability callout program.\n");
167 }
168
169 static const char *pidfile = "/var/run/rpc.statd.pid";
170
171 int pidfd = -1;
172 static void create_pidfile(void)
173 {
174         FILE *fp;
175
176         unlink(pidfile);
177         fp = fopen(pidfile, "w");
178         if (!fp)
179                 die("Opening %s failed: %s\n",
180                     pidfile, strerror(errno));
181         fprintf(fp, "%d\n", getpid());
182         pidfd = dup(fileno(fp));
183         if (fclose(fp) < 0)
184                 note(N_WARNING, "Flushing pid file failed.\n");
185 }
186
187 static void truncate_pidfile(void)
188 {
189         if (pidfd >= 0)
190                 ftruncate(pidfd, 0);
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                 note(N_WARNING, "statd running as root. chown %s to choose different user\n",
205                     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                 fchown(pidfd, st.st_uid, st.st_gid);
213
214         setgroups(0, NULL);
215         if (setgid(st.st_gid) == -1
216             || setuid(st.st_uid) == -1) {
217                 note(N_ERROR, "Fail to drop privileges");
218                 exit(1);
219         }
220 }
221
222 /* 
223  * Entry routine/main loop.
224  */
225 int main (int argc, char **argv)
226 {
227         extern char *optarg;
228         int pid;
229         int arg;
230         int port = 0, out_port = 0;
231         struct rlimit rlim;
232
233         int pipefds[2] = { -1, -1};
234         char status;
235
236         /* Default: daemon mode, no other options */
237         run_mode = 0;
238
239         /* Set the basename */
240         if ((name_p = strrchr(argv[0],'/')) != NULL) {
241                 name_p ++;
242         } else {
243                 name_p = argv[0];
244         }
245
246         /* Get the version */
247         if ((version_p = strrchr(VERSION,' ')) != NULL) {
248                 version_p++;
249         } else {
250                 version_p = VERSION;
251         }
252         
253         /* Set hostname */
254         MY_NAME = NULL;
255
256         /* Process command line switches */
257         while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:", longopts, NULL)) != EOF) {
258                 switch (arg) {
259                 case 'V':       /* Version */
260                 case 'v':
261                         printf("%s version %s\n",name_p,version_p);
262                         exit(0);
263                 case 'F':       /* Foreground/nodaemon mode */
264                         run_mode |= MODE_NODAEMON;
265                         break;
266                 case 'N':
267                         run_mode |= MODE_NOTIFY_ONLY;
268                         break;
269                 case 'd':       /* No daemon only - log to stderr */
270                         run_mode |= MODE_LOG_STDERR;
271                         break;
272                 case 'o':
273                         out_port = atoi(optarg);
274                         if (out_port < 1 || out_port > 65535) {
275                                 fprintf(stderr, "%s: bad port number: %s\n",
276                                         argv[0], optarg);
277                                 usage();
278                                 exit(1);
279                         }
280                         break;
281                 case 'p':
282                         port = atoi(optarg);
283                         if (port < 1 || port > 65535) {
284                                 fprintf(stderr, "%s: bad port number: %s\n",
285                                         argv[0], optarg);
286                                 usage();
287                                 exit(1);
288                         }
289                         break;
290                 case 'n':       /* Specify local hostname */
291                         run_mode |= STATIC_HOSTNAME;
292                         MY_NAME = xstrdup(optarg);
293                         break;
294                 case 'P':
295
296                         if ((DIR_BASE = xstrdup(optarg)) == NULL) {
297                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
298                                         argv[0], optarg);
299                                 exit(1);
300                         }
301
302                         SM_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm"));
303                         SM_BAK_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm.bak"));
304                         SM_STAT_PATH = xmalloc(strlen(DIR_BASE) + 1 + sizeof("state"));
305
306                         if ((SM_DIR == NULL) 
307                             || (SM_BAK_DIR == NULL) 
308                             || (SM_STAT_PATH == NULL)) {
309
310                                 fprintf(stderr, "%s: xmalloc() failed!\n",
311                                         argv[0]);
312                                 exit(1);
313                         }
314                         if (DIR_BASE[strlen(DIR_BASE)-1] == '/') {
315                                 sprintf(SM_DIR, "%ssm", DIR_BASE );
316                                 sprintf(SM_BAK_DIR, "%ssm.bak", DIR_BASE );
317                                 sprintf(SM_STAT_PATH, "%sstate", DIR_BASE );
318                         } else {
319                                 sprintf(SM_DIR, "%s/sm", DIR_BASE );
320                                 sprintf(SM_BAK_DIR, "%s/sm.bak", DIR_BASE );
321                                 sprintf(SM_STAT_PATH, "%s/state", DIR_BASE );
322                         }
323                         break;
324                 case 'H': /* PRC: specify the ha-callout program */
325                         if ((ha_callout_prog = xstrdup(optarg)) == NULL) {
326                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
327                                         argv[0], optarg);
328                                 exit(1);
329                         }
330                         break;
331                 case '?':       /* heeeeeelllllllpppp? heh */
332                 case 'h':
333                         usage();
334                         exit (0);
335                 default:        /* oh dear ... heh */
336                         usage();
337                         exit(-1);
338                 }
339         }
340
341         if (port == out_port && port != 0) {
342                 fprintf(stderr, "Listening and outgoing ports cannot be the same!\n");
343                 exit(-1);
344         }
345
346         if (!(run_mode & MODE_NODAEMON)) {
347                 run_mode &= ~MODE_LOG_STDERR;   /* Never log to console in
348                                                    daemon mode. */
349         }
350
351         if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
352                 fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
353                                 argv [0], strerror(errno));
354         else {
355                 /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
356                 if (rlim.rlim_cur > FD_SETSIZE) {
357                         rlim.rlim_cur = FD_SETSIZE;
358
359                         if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
360                                 fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
361                                         argv [0], strerror(errno));
362                         }
363                 }
364         }
365
366 #ifdef SIMULATIONS
367         if (argc > 1)
368                 /* LH - I _really_ need to update simulator... */
369                 simulator (--argc, ++argv);     /* simulator() does exit() */
370 #endif
371         
372         if (!(run_mode & MODE_NODAEMON)) {
373                 int filedes, fdmax, tempfd;
374
375                 if (pipe(pipefds)<0) {
376                         perror("statd: unable to create pipe");
377                         exit(1);
378                 }
379                 if ((pid = fork ()) < 0) {
380                         perror ("statd: Could not fork");
381                         exit (1);
382                 } else if (pid != 0) {
383                         /* Parent.
384                          * Wait for status from child.
385                          */
386                         close(pipefds[1]);
387                         if (read(pipefds[0], &status, 1) != 1)
388                                 exit(1);
389                         exit (0);
390                 }
391                 /* Child.       */
392                 close(pipefds[0]);
393                 setsid ();
394                 if (chdir (DIR_BASE) == -1) {
395                         perror("statd: Could not chdir");
396                         exit(1);
397                 }
398
399                 while (pipefds[1] <= 2) {
400                         pipefds[1] = dup(pipefds[1]);
401                         if (pipefds[1]<0) {
402                                 perror("statd: dup");
403                                 exit(1);
404                         }
405                 }
406                 tempfd = open("/dev/null", O_RDWR);
407                 dup2(tempfd, 0);
408                 dup2(tempfd, 1);
409                 dup2(tempfd, 2);
410                 dup2(pipefds[1], 3);
411                 pipefds[1] = 3;
412                 closeall(4);
413         }
414
415         /* Child. */
416
417         log_init (name_p,version_p);
418
419         log_modes();
420
421         signal (SIGHUP, killer);
422         signal (SIGINT, killer);
423         signal (SIGTERM, killer);
424         /* PRC: trap SIGUSR1 to re-read notify list from disk */
425         signal(SIGUSR1, sigusr);
426         /* WARNING: the following works on Linux and SysV, but not BSD! */
427         signal(SIGCHLD, SIG_IGN);
428         /*
429          * Ignore SIGPIPE to avoid statd dying when peers close their
430          * TCP connection while we're trying to reply to them.
431          */
432         signal(SIGPIPE, SIG_IGN);
433
434         /* initialize out_port */
435         statd_get_socket(out_port);
436
437         create_pidfile();
438         atexit(truncate_pidfile);
439         drop_privs();
440
441         for (;;) {
442                 if (!(run_mode & MODE_NOTIFY_ONLY)) {
443                         /* Do not do pmap_unset() when running in notify mode.
444                          * We may clear the portmapper record for a statd not
445                          * running in notify mode disabling it.
446                          * Juan C. Gomez j_carlos_gomez@yahoo.com
447                          */
448                         pmap_unset (SM_PROG, SM_VERS);
449                 }
450                 change_state ();
451                 shuffle_dirs ();        /* Move directory names around */
452
453                 /* If we got this far, we have successfully started, so notify parent */
454                 if (pipefds[1] > 0) {
455                         status = 0;
456                         write(pipefds[1], &status, 1);
457                         close(pipefds[1]);
458                         pipefds[1] = -1;
459                 }
460
461                 notify_hosts ();        /* Send out notify requests */
462                 ++restart;
463
464                 /* this registers both UDP and TCP services */
465                 if (!(run_mode & MODE_NOTIFY_ONLY)) {
466                         rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
467                 } 
468
469                 /*
470                  * Handle incoming requests:  SM_NOTIFY socket requests, as
471                  * well as callbacks from lockd.
472                  */
473                 my_svc_run();   /* I rolled my own, Olaf made it better... */
474
475                 if ((run_mode & MODE_NOTIFY_ONLY))
476                         break;                  
477         }
478         return 0;
479 }