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