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.
23 #include <rpc/pmap_clnt.h>
25 #include <sys/resource.h>
32 /* Socket operations */
33 #include <sys/types.h>
34 #include <sys/socket.h>
36 /* Added to enable specification of state directory path at run-time
37 * j_carlos_gomez@yahoo.com
40 char * DIR_BASE
= DEFAULT_DIR_BASE
;
42 char * SM_DIR
= DEFAULT_SM_DIR
;
43 char * SM_BAK_DIR
= DEFAULT_SM_BAK_DIR
;
44 char * SM_STAT_PATH
= DEFAULT_SM_STAT_PATH
;
46 /* ----- end of state directory path stuff ------- */
48 int run_mode
= 0; /* foreground logging mode */
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 */
54 const char *version_p
= NULL
;
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
;
61 static struct option longopts
[] =
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 { "no-notify", 0, 0, 'L' },
77 extern void sm_prog_1 (struct svc_req
*, register SVCXPRT
*);
78 static void load_state_number(void);
81 extern void simulator (int, char **);
85 #ifdef HAVE_TCP_WRAPPER
86 #include "tcpwrapper.h"
89 sm_prog_1_wrapper (struct svc_req
*rqstp
, register SVCXPRT
*transp
)
91 struct sockaddr_in
*sin
= nfs_getrpccaller_in(transp
);
93 /* remote host authorization check */
94 if (sin
->sin_family
== AF_INET
&&
95 !check_default("statd", sin
, rqstp
->rq_proc
, SM_PROG
)) {
96 svcerr_auth (transp
, AUTH_FAILED
);
100 sm_prog_1 (rqstp
, transp
);
103 #define sm_prog_1 sm_prog_1_wrapper
112 note (N_FATAL
, "Caught signal %d, un-registering and exiting.", sig
);
113 pmap_unset (SM_PROG
, SM_VERS
);
121 extern void my_svc_exit (void);
122 dprintf (N_DEBUG
, "Caught signal %d, re-notifying (state %d).", sig
,
128 * Startup information.
130 static void log_modes(void)
132 char buf
[128]; /* watch stack size... */
134 /* No flags = no message */
135 if (!run_mode
) return;
138 sprintf(buf
,"Flags: ");
139 if (run_mode
& MODE_NODAEMON
)
140 strcat(buf
,"No-Daemon ");
141 if (run_mode
& MODE_LOG_STDERR
)
142 strcat(buf
,"Log-STDERR ");
148 * Since we do more than standard statd stuff, we might need to
149 * help the occasional admin.
154 fprintf(stderr
,"usage: %s [options]\n", name_p
);
155 fprintf(stderr
," -h, -?, --help Print this help screen.\n");
156 fprintf(stderr
," -F, --foreground Foreground (no-daemon mode)\n");
157 fprintf(stderr
," -d, --no-syslog Verbose logging to stderr. Foreground mode only.\n");
158 fprintf(stderr
," -p, --port Port to listen on\n");
159 fprintf(stderr
," -o, --outgoing-port Port for outgoing connections\n");
160 fprintf(stderr
," -V, -v, --version Display version information and exit.\n");
161 fprintf(stderr
," -n, --name Specify a local hostname.\n");
162 fprintf(stderr
," -P State directory path.\n");
163 fprintf(stderr
," -N Run in notify only mode.\n");
164 fprintf(stderr
," -L, --no-notify Do not perform any notification.\n");
165 fprintf(stderr
," -H Specify a high-availability callout program.\n");
168 static const char *pidfile
= "/var/run/rpc.statd.pid";
171 static void create_pidfile(void)
176 fp
= fopen(pidfile
, "w");
178 die("Opening %s failed: %s\n",
179 pidfile
, strerror(errno
));
180 fprintf(fp
, "%d\n", getpid());
181 pidfd
= dup(fileno(fp
));
183 note(N_WARNING
, "Flushing pid file failed.\n");
186 static void truncate_pidfile(void)
192 static void drop_privs(void)
196 if (stat(SM_DIR
, &st
) == -1 &&
197 stat(DIR_BASE
, &st
) == -1) {
202 if (st
.st_uid
== 0) {
203 note(N_WARNING
, "statd running as root. chown %s to choose different user\n",
207 /* better chown the pid file before dropping, as if it
208 * if over nfs we might loose access
211 fchown(pidfd
, st
.st_uid
, st
.st_gid
);
214 if (setgid(st
.st_gid
) == -1
215 || setuid(st
.st_uid
) == -1) {
216 note(N_ERROR
, "Fail to drop privileges");
221 static void run_sm_notify(int outport
)
227 av
[ac
++] = "/usr/sbin/sm-notify";
228 if (run_mode
& MODE_NODAEMON
)
231 sprintf(op
, "-p%d", outport
);
234 if (run_mode
& STATIC_HOSTNAME
) {
240 fprintf(stderr
, "%s: failed to run %s\n", name_p
, av
[0]);
245 * Entry routine/main loop.
247 int main (int argc
, char **argv
)
252 int port
= 0, out_port
= 0;
255 int pipefds
[2] = { -1, -1};
258 /* Default: daemon mode, no other options */
261 /* Set the basename */
262 if ((name_p
= strrchr(argv
[0],'/')) != NULL
) {
268 /* Get the version */
269 if ((version_p
= strrchr(VERSION
,' ')) != NULL
) {
278 /* Process command line switches */
279 while ((arg
= getopt_long(argc
, argv
, "h?vVFNH:dn:p:o:P:L", longopts
, NULL
)) != EOF
) {
281 case 'V': /* Version */
283 printf("%s version %s\n",name_p
,version_p
);
285 case 'F': /* Foreground/nodaemon mode */
286 run_mode
|= MODE_NODAEMON
;
289 run_mode
|= MODE_NOTIFY_ONLY
;
291 case 'L': /* Listen only */
292 run_mode
|= MODE_NO_NOTIFY
;
294 case 'd': /* No daemon only - log to stderr */
295 run_mode
|= MODE_LOG_STDERR
;
298 out_port
= atoi(optarg
);
299 if (out_port
< 1 || out_port
> 65535) {
300 fprintf(stderr
, "%s: bad port number: %s\n",
308 if (port
< 1 || port
> 65535) {
309 fprintf(stderr
, "%s: bad port number: %s\n",
315 case 'n': /* Specify local hostname */
316 run_mode
|= STATIC_HOSTNAME
;
317 MY_NAME
= xstrdup(optarg
);
321 if ((DIR_BASE
= xstrdup(optarg
)) == NULL
) {
322 fprintf(stderr
, "%s: xstrdup(%s) failed!\n",
327 SM_DIR
= xmalloc(strlen(DIR_BASE
) + 1 + sizeof("sm"));
328 SM_BAK_DIR
= xmalloc(strlen(DIR_BASE
) + 1 + sizeof("sm.bak"));
329 SM_STAT_PATH
= xmalloc(strlen(DIR_BASE
) + 1 + sizeof("state"));
332 || (SM_BAK_DIR
== NULL
)
333 || (SM_STAT_PATH
== NULL
)) {
335 fprintf(stderr
, "%s: xmalloc() failed!\n",
339 if (DIR_BASE
[strlen(DIR_BASE
)-1] == '/') {
340 sprintf(SM_DIR
, "%ssm", DIR_BASE
);
341 sprintf(SM_BAK_DIR
, "%ssm.bak", DIR_BASE
);
342 sprintf(SM_STAT_PATH
, "%sstate", DIR_BASE
);
344 sprintf(SM_DIR
, "%s/sm", DIR_BASE
);
345 sprintf(SM_BAK_DIR
, "%s/sm.bak", DIR_BASE
);
346 sprintf(SM_STAT_PATH
, "%s/state", DIR_BASE
);
349 case 'H': /* PRC: specify the ha-callout program */
350 if ((ha_callout_prog
= xstrdup(optarg
)) == NULL
) {
351 fprintf(stderr
, "%s: xstrdup(%s) failed!\n",
356 case '?': /* heeeeeelllllllpppp? heh */
360 default: /* oh dear ... heh */
366 if (port
== out_port
&& port
!= 0) {
367 fprintf(stderr
, "Listening and outgoing ports cannot be the same!\n");
371 if (run_mode
& MODE_NOTIFY_ONLY
) {
372 fprintf(stderr
, "%s: -N deprecated, consider using /usr/sbin/sm-notify directly\n",
374 run_sm_notify(out_port
);
378 if (!(run_mode
& MODE_NODAEMON
)) {
379 run_mode
&= ~MODE_LOG_STDERR
; /* Never log to console in
383 if (getrlimit (RLIMIT_NOFILE
, &rlim
) != 0)
384 fprintf(stderr
, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
385 argv
[0], strerror(errno
));
387 /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
388 if (rlim
.rlim_cur
> FD_SETSIZE
) {
389 rlim
.rlim_cur
= FD_SETSIZE
;
391 if (setrlimit (RLIMIT_NOFILE
, &rlim
) != 0) {
392 fprintf(stderr
, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
393 argv
[0], strerror(errno
));
400 /* LH - I _really_ need to update simulator... */
401 simulator (--argc
, ++argv
); /* simulator() does exit() */
404 if (!(run_mode
& MODE_NODAEMON
)) {
407 if (pipe(pipefds
)<0) {
408 perror("statd: unable to create pipe");
411 if ((pid
= fork ()) < 0) {
412 perror ("statd: Could not fork");
414 } else if (pid
!= 0) {
416 * Wait for status from child.
419 if (read(pipefds
[0], &status
, 1) != 1)
426 if (chdir (DIR_BASE
) == -1) {
427 perror("statd: Could not chdir");
431 while (pipefds
[1] <= 2) {
432 pipefds
[1] = dup(pipefds
[1]);
434 perror("statd: dup");
438 tempfd
= open("/dev/null", O_RDWR
);
449 log_init (/*name_p,version_p*/);
453 signal (SIGHUP
, killer
);
454 signal (SIGINT
, killer
);
455 signal (SIGTERM
, killer
);
456 /* PRC: trap SIGUSR1 to re-read notify list from disk */
457 signal(SIGUSR1
, sigusr
);
458 /* WARNING: the following works on Linux and SysV, but not BSD! */
459 signal(SIGCHLD
, SIG_IGN
);
461 * Ignore SIGPIPE to avoid statd dying when peers close their
462 * TCP connection while we're trying to reply to them.
464 signal(SIGPIPE
, SIG_IGN
);
467 atexit(truncate_pidfile
);
469 if (! (run_mode
& MODE_NO_NOTIFY
))
470 switch (pid
= fork()) {
472 run_sm_notify(out_port
);
477 waitpid(pid
, NULL
, 0);
480 /* Make sure we have a privilege port for calling into the kernel */
481 if (statd_get_socket() < 0)
484 /* If sm-notify didn't take all the state files, load
485 * state information into our notify-list so we can
486 * pass on any SM_NOTIFY that arrives
490 pmap_unset (SM_PROG
, SM_VERS
);
492 /* this registers both UDP and TCP services */
493 rpc_init("statd", SM_PROG
, SM_VERS
, sm_prog_1
, port
);
495 /* If we got this far, we have successfully started, so notify parent */
496 if (pipefds
[1] > 0) {
498 write(pipefds
[1], &status
, 1);
507 * Handle incoming requests: SM_NOTIFY socket requests, as
508 * well as callbacks from lockd.
510 my_svc_run(); /* I rolled my own, Olaf made it better... */
512 /* Only get here when simulating a crash so we should probably
513 * start sm-notify running again. As we have already dropped
514 * privileges, this might not work, but I don't think
515 * responding to SM_SIMU_CRASH is an important use cases to
518 if (! (run_mode
& MODE_NO_NOTIFY
))
519 switch (pid
= fork()) {
521 run_sm_notify(out_port
);
526 waitpid(pid
, NULL
, 0);
534 load_state_number(void)
538 if ((fd
= open(SM_STAT_PATH
, O_RDONLY
)) == -1)
541 read(fd
, &MY_STATE
, sizeof(MY_STATE
));
543 fd
= open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY
);
546 snprintf(buf
, sizeof(buf
), "%d", MY_STATE
);
547 write(fd
, buf
, strlen(buf
));