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