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>
31 /* Socket operations */
32 #include <sys/types.h>
33 #include <sys/socket.h>
35 /* Added to enable specification of state directory path at run-time
36 * j_carlos_gomez@yahoo.com
39 char * DIR_BASE = DEFAULT_DIR_BASE;
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;
45 /* ----- end of state directory path stuff ------- */
47 int run_mode = 0; /* foreground logging mode */
49 /* LH - I had these local to main, but it seemed silly to have
50 * two copies of each - one in main(), one static in log.c...
51 * It also eliminates the 256-char static in log.c */
52 static char *name_p = NULL;
54 /* PRC: a high-availability callout program can be specified with -H
55 * When this is done, the program will receive callouts whenever clients
56 * are added or deleted to the notify list */
57 char *ha_callout_prog = NULL;
59 static struct option longopts[] =
61 { "foreground", 0, 0, 'F' },
62 { "no-syslog", 0, 0, 'd' },
63 { "help", 0, 0, 'h' },
64 { "version", 0, 0, 'v' },
65 { "outgoing-port", 1, 0, 'o' },
66 { "port", 1, 0, 'p' },
67 { "name", 1, 0, 'n' },
68 { "state-directory-path", 1, 0, 'P' },
69 { "notify-mode", 0, 0, 'N' },
70 { "ha-callout", 1, 0, 'H' },
71 { "no-notify", 0, 0, 'L' },
75 extern void sm_prog_1 (struct svc_req *, register SVCXPRT *);
76 static void load_state_number(void);
79 extern void simulator (int, char **);
83 #ifdef HAVE_TCP_WRAPPER
84 #include "tcpwrapper.h"
87 sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
89 struct sockaddr_in *sin = nfs_getrpccaller_in(transp);
91 /* remote host authorization check */
92 if (sin->sin_family == AF_INET &&
93 !check_default("statd", sin, rqstp->rq_proc, SM_PROG)) {
94 svcerr_auth (transp, AUTH_FAILED);
98 sm_prog_1 (rqstp, transp);
101 #define sm_prog_1 sm_prog_1_wrapper
110 pmap_unset (SM_PROG, SM_VERS);
111 xlog_err ("Caught signal %d, un-registering and exiting", sig);
117 extern void my_svc_exit (void);
118 xlog(D_GENERAL, "Caught signal %d, re-notifying (state %d)", sig,
124 * Startup information.
126 static void log_modes(void)
128 char buf[128]; /* watch stack size... */
130 /* No flags = no message */
131 if (!run_mode) return;
134 sprintf(buf,"Flags: ");
135 if (run_mode & MODE_NODAEMON)
136 strcat(buf,"No-Daemon ");
137 if (run_mode & MODE_LOG_STDERR)
138 strcat(buf,"Log-STDERR ");
144 * Since we do more than standard statd stuff, we might need to
145 * help the occasional admin.
150 fprintf(stderr,"usage: %s [options]\n", name_p);
151 fprintf(stderr," -h, -?, --help Print this help screen.\n");
152 fprintf(stderr," -F, --foreground Foreground (no-daemon mode)\n");
153 fprintf(stderr," -d, --no-syslog Verbose logging to stderr. Foreground mode only.\n");
154 fprintf(stderr," -p, --port Port to listen on\n");
155 fprintf(stderr," -o, --outgoing-port Port for outgoing connections\n");
156 fprintf(stderr," -V, -v, --version Display version information and exit.\n");
157 fprintf(stderr," -n, --name Specify a local hostname.\n");
158 fprintf(stderr," -P State directory path.\n");
159 fprintf(stderr," -N Run in notify only mode.\n");
160 fprintf(stderr," -L, --no-notify Do not perform any notification.\n");
161 fprintf(stderr," -H Specify a high-availability callout program.\n");
164 static const char *pidfile = "/var/run/rpc.statd.pid";
167 static void create_pidfile(void)
172 fp = fopen(pidfile, "w");
174 xlog_err("Opening %s failed: %m\n", pidfile);
175 fprintf(fp, "%d\n", getpid());
176 pidfd = dup(fileno(fp));
177 if (fclose(fp) < 0) {
178 xlog_warn("Flushing pid file failed: errno %d (%m)\n",
183 static void truncate_pidfile(void)
186 if (ftruncate(pidfd, 0) < 0) {
187 xlog_warn("truncating pid file failed: errno %d (%m)\n",
193 static void drop_privs(void)
197 if (stat(SM_DIR, &st) == -1 &&
198 stat(DIR_BASE, &st) == -1) {
203 if (st.st_uid == 0) {
204 xlog_warn("Running as 'root'. "
205 "chown %s to choose different user\n", SM_DIR);
208 /* better chown the pid file before dropping, as if it
209 * if over nfs we might loose access
212 if (fchown(pidfd, st.st_uid, st.st_gid) < 0) {
213 xlog(L_ERROR, "Unable to change owner of %s: %d (%s)",
214 SM_DIR, strerror (errno));
218 if (setgid(st.st_gid) == -1
219 || setuid(st.st_uid) == -1) {
220 xlog(L_ERROR, "Fail to drop privileges");
225 static void run_sm_notify(int outport)
231 av[ac++] = "/usr/sbin/sm-notify";
232 if (run_mode & MODE_NODAEMON)
235 sprintf(op, "-p%d", outport);
238 if (run_mode & STATIC_HOSTNAME) {
244 fprintf(stderr, "%s: failed to run %s\n", name_p, av[0]);
249 * Entry routine/main loop.
251 int main (int argc, char **argv)
256 int port = 0, out_port = 0;
259 int pipefds[2] = { -1, -1};
262 /* Default: daemon mode, no other options */
267 /* Set the basename */
268 if ((name_p = strrchr(argv[0],'/')) != NULL) {
277 /* Process command line switches */
278 while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:L", longopts, NULL)) != EOF) {
280 case 'V': /* Version */
282 printf("%s version " VERSION "\n",name_p);
284 case 'F': /* Foreground/nodaemon mode */
285 run_mode |= MODE_NODAEMON;
288 run_mode |= MODE_NOTIFY_ONLY;
290 case 'L': /* Listen only */
291 run_mode |= MODE_NO_NOTIFY;
293 case 'd': /* No daemon only - log to stderr */
294 run_mode |= MODE_LOG_STDERR;
297 out_port = atoi(optarg);
298 if (out_port < 1 || out_port > 65535) {
299 fprintf(stderr, "%s: bad port number: %s\n",
307 if (port < 1 || port > 65535) {
308 fprintf(stderr, "%s: bad port number: %s\n",
314 case 'n': /* Specify local hostname */
315 run_mode |= STATIC_HOSTNAME;
316 MY_NAME = xstrdup(optarg);
320 if ((DIR_BASE = xstrdup(optarg)) == NULL) {
321 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
326 SM_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm"));
327 SM_BAK_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm.bak"));
328 SM_STAT_PATH = xmalloc(strlen(DIR_BASE) + 1 + sizeof("state"));
331 || (SM_BAK_DIR == NULL)
332 || (SM_STAT_PATH == NULL)) {
334 fprintf(stderr, "%s: xmalloc() failed!\n",
338 if (DIR_BASE[strlen(DIR_BASE)-1] == '/') {
339 sprintf(SM_DIR, "%ssm", DIR_BASE );
340 sprintf(SM_BAK_DIR, "%ssm.bak", DIR_BASE );
341 sprintf(SM_STAT_PATH, "%sstate", DIR_BASE );
343 sprintf(SM_DIR, "%s/sm", DIR_BASE );
344 sprintf(SM_BAK_DIR, "%s/sm.bak", DIR_BASE );
345 sprintf(SM_STAT_PATH, "%s/state", DIR_BASE );
348 case 'H': /* PRC: specify the ha-callout program */
349 if ((ha_callout_prog = xstrdup(optarg)) == NULL) {
350 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
355 case '?': /* heeeeeelllllllpppp? heh */
359 default: /* oh dear ... heh */
365 if (port == out_port && port != 0) {
366 fprintf(stderr, "Listening and outgoing ports cannot be the same!\n");
370 if (run_mode & MODE_NOTIFY_ONLY) {
371 fprintf(stderr, "%s: -N deprecated, consider using /usr/sbin/sm-notify directly\n",
373 run_sm_notify(out_port);
376 if (!(run_mode & MODE_NODAEMON)) {
377 run_mode &= ~MODE_LOG_STDERR; /* Never log to console in
381 if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
382 fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
383 argv [0], strerror(errno));
385 /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
386 if (rlim.rlim_cur > FD_SETSIZE) {
387 rlim.rlim_cur = FD_SETSIZE;
389 if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
390 fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
391 argv [0], strerror(errno));
398 /* LH - I _really_ need to update simulator... */
399 simulator (--argc, ++argv); /* simulator() does exit() */
402 if (!(run_mode & MODE_NODAEMON)) {
405 if (pipe(pipefds)<0) {
406 perror("statd: unable to create pipe");
409 if ((pid = fork ()) < 0) {
410 perror ("statd: Could not fork");
412 } else if (pid != 0) {
414 * Wait for status from child.
417 if (read(pipefds[0], &status, 1) != 1)
424 if (chdir (DIR_BASE) == -1) {
425 perror("statd: Could not chdir");
429 while (pipefds[1] <= 2) {
430 pipefds[1] = dup(pipefds[1]);
432 perror("statd: dup");
436 tempfd = open("/dev/null", O_RDWR);
447 if (run_mode & MODE_LOG_STDERR) {
450 xlog_config(D_ALL, 1);
453 xlog(L_NOTICE, "Version " VERSION " starting");
457 signal (SIGHUP, killer);
458 signal (SIGINT, killer);
459 signal (SIGTERM, killer);
460 /* PRC: trap SIGUSR1 to re-read notify list from disk */
461 signal(SIGUSR1, sigusr);
462 /* WARNING: the following works on Linux and SysV, but not BSD! */
463 signal(SIGCHLD, SIG_IGN);
465 * Ignore SIGPIPE to avoid statd dying when peers close their
466 * TCP connection while we're trying to reply to them.
468 signal(SIGPIPE, SIG_IGN);
471 atexit(truncate_pidfile);
473 if (! (run_mode & MODE_NO_NOTIFY))
474 switch (pid = fork()) {
476 run_sm_notify(out_port);
481 waitpid(pid, NULL, 0);
484 /* Make sure we have a privilege port for calling into the kernel */
485 if (statd_get_socket() < 0)
488 /* If sm-notify didn't take all the state files, load
489 * state information into our notify-list so we can
490 * pass on any SM_NOTIFY that arrives
494 pmap_unset (SM_PROG, SM_VERS);
496 /* this registers both UDP and TCP services */
497 rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
499 /* If we got this far, we have successfully started, so notify parent */
500 if (pipefds[1] > 0) {
502 if (write(pipefds[1], &status, 1) != 1) {
503 xlog_warn("writing to parent pipe failed: errno %d (%s)\n",
504 errno, strerror(errno));
514 * Handle incoming requests: SM_NOTIFY socket requests, as
515 * well as callbacks from lockd.
517 my_svc_run(); /* I rolled my own, Olaf made it better... */
519 /* Only get here when simulating a crash so we should probably
520 * start sm-notify running again. As we have already dropped
521 * privileges, this might not work, but I don't think
522 * responding to SM_SIMU_CRASH is an important use cases to
525 if (! (run_mode & MODE_NO_NOTIFY))
526 switch (pid = fork()) {
528 run_sm_notify(out_port);
533 waitpid(pid, NULL, 0);
541 load_state_number(void)
544 const char *file = "/proc/sys/fs/nfs/nsm_local_state";
546 if ((fd = open(SM_STAT_PATH, O_RDONLY)) == -1)
549 if (read(fd, &MY_STATE, sizeof(MY_STATE)) != sizeof(MY_STATE)) {
550 xlog_warn("Unable to read state from '%s': errno %d (%s)",
551 SM_STAT_PATH, errno, strerror(errno));
554 fd = open(file, O_WRONLY);
557 snprintf(buf, sizeof(buf), "%d", MY_STATE);
558 if (write(fd, buf, strlen(buf)) != strlen(buf))
559 xlog_warn("Writing to '%s' failed: errno %d (%s)",
560 file, errno, strerror(errno));