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