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