]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/statd.c
Add option to specify directory to search for credentials cache files
[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 <grp.h>
27 #include "statd.h"
28 #include "version.h"
29 #include "nfslib.h"
30
31 /* Socket operations */
32 #include <sys/types.h>
33 #include <sys/socket.h>
34
35 /* Added to enable specification of state directory path at run-time
36  * j_carlos_gomez@yahoo.com
37  */
38
39 char * DIR_BASE = DEFAULT_DIR_BASE;
40
41 char *  SM_DIR = DEFAULT_SM_DIR;
42 char *  SM_BAK_DIR =  DEFAULT_SM_BAK_DIR;
43 char *  SM_STAT_PATH = DEFAULT_SM_STAT_PATH;
44
45 /* ----- end of state directory path stuff ------- */
46
47 short int restart = 0;
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 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(int port);
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         if (!(run_mode & MODE_NOTIFY_ONLY))
111                 pmap_unset (SM_PROG, SM_VERS);
112
113         exit (0);
114 }
115
116 static void
117 sigusr (int sig)
118 {
119         dprintf (N_DEBUG, "Caught signal %d, re-notifying (state %d).", sig,
120                                                                 MY_STATE);
121         re_notify = 1;
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         if (run_mode & MODE_NOTIFY_ONLY)
142         {
143                 strcat(buf,"Notify-Only ");
144         }
145         note(N_WARNING,buf);
146         /* future: IP aliasing
147         if (run_mode & MODE_NOTIFY_ONLY)
148         {
149                 dprintf(N_DEBUG,"Notify IP: %s",svr_addr);
150         } */
151 }
152
153 /*
154  * Since we do more than standard statd stuff, we might need to
155  * help the occasional admin. 
156  */
157 static void 
158 usage()
159 {
160         fprintf(stderr,"usage: %s [options]\n", name_p);
161         fprintf(stderr,"      -h, -?, --help       Print this help screen.\n");
162         fprintf(stderr,"      -F, --foreground     Foreground (no-daemon mode)\n");
163         fprintf(stderr,"      -d, --no-syslog      Verbose logging to stderr.  Foreground mode only.\n");
164         fprintf(stderr,"      -p, --port           Port to listen on\n");
165         fprintf(stderr,"      -o, --outgoing-port  Port for outgoing connections\n");
166         fprintf(stderr,"      -V, -v, --version    Display version information and exit.\n");
167         fprintf(stderr,"      -n, --name           Specify a local hostname.\n");
168         fprintf(stderr,"      -P                   State directory path.\n");
169         fprintf(stderr,"      -N                   Run in notify only mode.\n");
170         fprintf(stderr,"      -H                   Specify a high-availability callout program.\n");
171 }
172
173 static const char *pidfile = "/var/run/rpc.statd.pid";
174
175 int pidfd = -1;
176 static void create_pidfile(void)
177 {
178         FILE *fp;
179
180         unlink(pidfile);
181         fp = fopen(pidfile, "w");
182         if (!fp)
183                 die("Opening %s failed: %s\n",
184                     pidfile, strerror(errno));
185         fprintf(fp, "%d\n", getpid());
186         pidfd = dup(fileno(fp));
187         if (fclose(fp) < 0)
188                 note(N_WARNING, "Flushing pid file failed.\n");
189 }
190
191 static void truncate_pidfile(void)
192 {
193         if (pidfd >= 0)
194                 ftruncate(pidfd, 0);
195 }
196
197 static void drop_privs(void)
198 {
199         struct stat st;
200
201         if (stat(SM_DIR, &st) == -1 &&
202             stat(DIR_BASE, &st) == -1) {
203                 st.st_uid = 0;
204                 st.st_gid = 0;
205         }
206
207         if (st.st_uid == 0) {
208                 note(N_WARNING, "statd running as root. chown %s to choose different user\n",
209                     SM_DIR);
210                 return;
211         }
212         /* better chown the pid file before dropping, as if it
213          * if over nfs we might loose access
214          */
215         if (pidfd >= 0)
216                 fchown(pidfd, st.st_uid, st.st_gid);
217
218         setgroups(0, NULL);
219         if (setgid(st.st_gid) == -1
220             || setuid(st.st_uid) == -1) {
221                 note(N_ERROR, "Fail to drop privileges");
222                 exit(1);
223         }
224 }
225
226 /* 
227  * Entry routine/main loop.
228  */
229 int main (int argc, char **argv)
230 {
231         extern char *optarg;
232         int pid;
233         int arg;
234         int port = 0, out_port = 0;
235         struct rlimit rlim;
236
237         int pipefds[2] = { -1, -1};
238         char status;
239
240         /* Default: daemon mode, no other options */
241         run_mode = 0;
242
243         /* Set the basename */
244         if ((name_p = strrchr(argv[0],'/')) != NULL) {
245                 name_p ++;
246         } else {
247                 name_p = argv[0];
248         }
249
250         /* Get the version */
251         if ((version_p = strrchr(VERSION,' ')) != NULL) {
252                 version_p++;
253         } else {
254                 version_p = VERSION;
255         }
256         
257         /* Set hostname */
258         MY_NAME = NULL;
259
260         /* Process command line switches */
261         while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:", longopts, NULL)) != EOF) {
262                 switch (arg) {
263                 case 'V':       /* Version */
264                 case 'v':
265                         printf("%s version %s\n",name_p,version_p);
266                         exit(0);
267                 case 'F':       /* Foreground/nodaemon mode */
268                         run_mode |= MODE_NODAEMON;
269                         break;
270                 case 'N':
271                         run_mode |= MODE_NOTIFY_ONLY;
272                         break;
273                 case 'd':       /* No daemon only - log to stderr */
274                         run_mode |= MODE_LOG_STDERR;
275                         break;
276                 case 'o':
277                         out_port = atoi(optarg);
278                         if (out_port < 1 || out_port > 65535) {
279                                 fprintf(stderr, "%s: bad port number: %s\n",
280                                         argv[0], optarg);
281                                 usage();
282                                 exit(1);
283                         }
284                         break;
285                 case 'p':
286                         port = atoi(optarg);
287                         if (port < 1 || port > 65535) {
288                                 fprintf(stderr, "%s: bad port number: %s\n",
289                                         argv[0], optarg);
290                                 usage();
291                                 exit(1);
292                         }
293                         break;
294                 case 'n':       /* Specify local hostname */
295                         run_mode |= STATIC_HOSTNAME;
296                         MY_NAME = xstrdup(optarg);
297                         break;
298                 case 'P':
299
300                         if ((DIR_BASE = xstrdup(optarg)) == NULL) {
301                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
302                                         argv[0], optarg);
303                                 exit(1);
304                         }
305
306                         SM_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm"));
307                         SM_BAK_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm.bak"));
308                         SM_STAT_PATH = xmalloc(strlen(DIR_BASE) + 1 + sizeof("state"));
309
310                         if ((SM_DIR == NULL) 
311                             || (SM_BAK_DIR == NULL) 
312                             || (SM_STAT_PATH == NULL)) {
313
314                                 fprintf(stderr, "%s: xmalloc() failed!\n",
315                                         argv[0]);
316                                 exit(1);
317                         }
318                         if (DIR_BASE[strlen(DIR_BASE)-1] == '/') {
319                                 sprintf(SM_DIR, "%ssm", DIR_BASE );
320                                 sprintf(SM_BAK_DIR, "%ssm.bak", DIR_BASE );
321                                 sprintf(SM_STAT_PATH, "%sstate", DIR_BASE );
322                         } else {
323                                 sprintf(SM_DIR, "%s/sm", DIR_BASE );
324                                 sprintf(SM_BAK_DIR, "%s/sm.bak", DIR_BASE );
325                                 sprintf(SM_STAT_PATH, "%s/state", DIR_BASE );
326                         }
327                         break;
328                 case 'H': /* PRC: specify the ha-callout program */
329                         if ((ha_callout_prog = xstrdup(optarg)) == NULL) {
330                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
331                                         argv[0], optarg);
332                                 exit(1);
333                         }
334                         break;
335                 case '?':       /* heeeeeelllllllpppp? heh */
336                 case 'h':
337                         usage();
338                         exit (0);
339                 default:        /* oh dear ... heh */
340                         usage();
341                         exit(-1);
342                 }
343         }
344
345         if (port == out_port && port != 0) {
346                 fprintf(stderr, "Listening and outgoing ports cannot be the same!\n");
347                 exit(-1);
348         }
349
350         if (!(run_mode & MODE_NODAEMON)) {
351                 run_mode &= ~MODE_LOG_STDERR;   /* Never log to console in
352                                                    daemon mode. */
353         }
354
355         if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
356                 fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
357                                 argv [0], strerror(errno));
358         else {
359                 /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
360                 if (rlim.rlim_cur > FD_SETSIZE) {
361                         rlim.rlim_cur = FD_SETSIZE;
362
363                         if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
364                                 fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
365                                         argv [0], strerror(errno));
366                         }
367                 }
368         }
369
370 #ifdef SIMULATIONS
371         if (argc > 1)
372                 /* LH - I _really_ need to update simulator... */
373                 simulator (--argc, ++argv);     /* simulator() does exit() */
374 #endif
375         
376         if (!(run_mode & MODE_NODAEMON)) {
377                 int filedes, fdmax, tempfd;
378
379                 if (pipe(pipefds)<0) {
380                         perror("statd: unable to create pipe");
381                         exit(1);
382                 }
383                 if ((pid = fork ()) < 0) {
384                         perror ("statd: Could not fork");
385                         exit (1);
386                 } else if (pid != 0) {
387                         /* Parent.
388                          * Wait for status from child.
389                          */
390                         close(pipefds[1]);
391                         if (read(pipefds[0], &status, 1) != 1)
392                                 exit(1);
393                         exit (0);
394                 }
395                 /* Child.       */
396                 close(pipefds[0]);
397                 setsid ();
398                 if (chdir (DIR_BASE) == -1) {
399                         perror("statd: Could not chdir");
400                         exit(1);
401                 }
402
403                 while (pipefds[1] <= 2) {
404                         pipefds[1] = dup(pipefds[1]);
405                         if (pipefds[1]<0) {
406                                 perror("statd: dup");
407                                 exit(1);
408                         }
409                 }
410                 tempfd = open("/dev/null", O_RDWR);
411                 dup2(tempfd, 0);
412                 dup2(tempfd, 1);
413                 dup2(tempfd, 2);
414                 dup2(pipefds[1], 3);
415                 pipefds[1] = 3;
416                 closeall(4);
417         }
418
419         /* Child. */
420
421         log_init (name_p,version_p);
422
423         log_modes();
424
425         signal (SIGHUP, killer);
426         signal (SIGINT, killer);
427         signal (SIGTERM, killer);
428         /* PRC: trap SIGUSR1 to re-read notify list from disk */
429         signal(SIGUSR1, sigusr);
430         /* WARNING: the following works on Linux and SysV, but not BSD! */
431         signal(SIGCHLD, SIG_IGN);
432         /*
433          * Ignore SIGPIPE to avoid statd dying when peers close their
434          * TCP connection while we're trying to reply to them.
435          */
436         signal(SIGPIPE, SIG_IGN);
437
438         /* initialize out_port */
439         statd_get_socket(out_port);
440
441         create_pidfile();
442         atexit(truncate_pidfile);
443         drop_privs();
444
445         for (;;) {
446                 if (!(run_mode & MODE_NOTIFY_ONLY)) {
447                         /* Do not do pmap_unset() when running in notify mode.
448                          * We may clear the portmapper record for a statd not
449                          * running in notify mode disabling it.
450                          * Juan C. Gomez j_carlos_gomez@yahoo.com
451                          */
452                         pmap_unset (SM_PROG, SM_VERS);
453                 }
454                 change_state ();
455                 shuffle_dirs ();        /* Move directory names around */
456
457                 /* If we got this far, we have successfully started, so notify parent */
458                 if (pipefds[1] > 0) {
459                         status = 0;
460                         write(pipefds[1], &status, 1);
461                         close(pipefds[1]);
462                         pipefds[1] = -1;
463                 }
464
465                 notify_hosts ();        /* Send out notify requests */
466                 ++restart;
467
468                 /* this registers both UDP and TCP services */
469                 if (!(run_mode & MODE_NOTIFY_ONLY)) {
470                         rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
471                 } 
472
473                 /*
474                  * Handle incoming requests:  SM_NOTIFY socket requests, as
475                  * well as callbacks from lockd.
476                  */
477                 my_svc_run();   /* I rolled my own, Olaf made it better... */
478
479                 if ((run_mode & MODE_NOTIFY_ONLY))
480                         break;                  
481         }
482         return 0;
483 }