]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/sm-notify.c
7a7ae1ac59b00685a9ff29381ae83e5283e58fc6
[nfs-utils.git] / utils / statd / sm-notify.c
1 /*
2  * Send NSM notify calls to all hosts listed in /var/lib/sm
3  *
4  * Copyright (C) 2004-2006 Olaf Kirch <okir@suse.de>
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <sys/stat.h>
14 #include <sys/poll.h>
15 #include <sys/param.h>
16 #include <sys/syslog.h>
17 #include <arpa/inet.h>
18 #include <dirent.h>
19 #include <time.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <netdb.h>
28 #include <errno.h>
29 #include <grp.h>
30
31 #ifndef BASEDIR
32 # ifdef NFS_STATEDIR
33 #  define BASEDIR               NFS_STATEDIR
34 # else
35 #  define BASEDIR               "/var/lib/nfs"
36 # endif
37 #endif
38
39 #define DEFAULT_SM_STATE_PATH   BASEDIR "/state"
40 #define DEFAULT_SM_DIR_PATH     BASEDIR "/sm"
41 #define DEFAULT_SM_BAK_PATH     DEFAULT_SM_DIR_PATH ".bak"
42
43 char *_SM_BASE_PATH = BASEDIR;
44 char *_SM_STATE_PATH = DEFAULT_SM_STATE_PATH;
45 char *_SM_DIR_PATH = DEFAULT_SM_DIR_PATH;
46 char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH;
47
48 #define NSM_PROG        100024
49 #define NSM_PROGRAM     100024
50 #define NSM_VERSION     1
51 #define NSM_TIMEOUT     2
52 #define NSM_NOTIFY      6
53 #define NSM_MAX_TIMEOUT 120     /* don't make this too big */
54 #define MAXMSGSIZE      256
55
56 struct nsm_host {
57         struct nsm_host *       next;
58         char *                  name;
59         char *                  path;
60         struct sockaddr_storage addr;
61         struct addrinfo         *ai;
62         time_t                  last_used;
63         time_t                  send_next;
64         unsigned int            timeout;
65         unsigned int            retries;
66         unsigned int            xid;
67 };
68
69 static char             nsm_hostname[256];
70 static uint32_t         nsm_state;
71 static int              opt_debug = 0;
72 static int              opt_quiet = 0;
73 static int              opt_update_state = 1;
74 static unsigned int     opt_max_retry = 15 * 60;
75 static char *           opt_srcaddr = 0;
76 static uint16_t         opt_srcport = 0;
77 static int              log_syslog = 0;
78
79 static unsigned int     nsm_get_state(int);
80 static void             notify(void);
81 static int              notify_host(int, struct nsm_host *);
82 static void             recv_reply(int);
83 static void             backup_hosts(const char *, const char *);
84 static void             get_hosts(const char *);
85 static void             insert_host(struct nsm_host *);
86 static struct nsm_host *find_host(uint32_t);
87 static void             nsm_log(int fac, const char *fmt, ...);
88 static int              record_pid(void);
89 static void             drop_privs(void);
90 static void             set_kernel_nsm_state(int state);
91
92 static struct nsm_host *        hosts = NULL;
93
94 /*
95  * Address handling utilities
96  */
97
98 static unsigned short smn_get_port(const struct sockaddr *sap)
99 {
100         switch (sap->sa_family) {
101         case AF_INET:
102                 return ntohs(((struct sockaddr_in *)sap)->sin_port);
103         case AF_INET6:
104                 return ntohs(((struct sockaddr_in6 *)sap)->sin6_port);
105         }
106         return 0;
107 }
108
109 static void smn_set_port(struct sockaddr *sap, const unsigned short port)
110 {
111         switch (sap->sa_family) {
112         case AF_INET:
113                 ((struct sockaddr_in *)sap)->sin_port = htons(port);
114                 break;
115         case AF_INET6:
116                 ((struct sockaddr_in6 *)sap)->sin6_port = htons(port);
117                 break;
118         }
119 }
120
121 static struct addrinfo *smn_lookup(const sa_family_t family, const char *name)
122 {
123         struct addrinfo *ai, hint = {
124                 .ai_family      = family,
125                 .ai_protocol    = IPPROTO_UDP,
126         };
127
128         if (getaddrinfo(name, NULL, &hint, &ai) != 0)
129                 return NULL;
130
131         return ai;
132 }
133
134 int
135 main(int argc, char **argv)
136 {
137         int     c;
138         int     force = 0;
139
140         while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) {
141                 switch (c) {
142                 case 'f':
143                         force = 1;
144                         break;
145                 case 'd':
146                         opt_debug++;
147                         break;
148                 case 'm':
149                         opt_max_retry = atoi(optarg) * 60;
150                         break;
151                 case 'n':
152                         opt_update_state = 0;
153                         break;
154                 case 'p':
155                         opt_srcport = atoi(optarg);
156                         break;
157                 case 'v':
158                         opt_srcaddr = optarg;
159                         break;
160                 case 'q':
161                         opt_quiet = 1;
162                         break;
163                 case 'P':
164                         _SM_BASE_PATH = strdup(optarg);
165                         _SM_STATE_PATH = malloc(strlen(optarg)+1+sizeof("state"));
166                         _SM_DIR_PATH = malloc(strlen(optarg)+1+sizeof("sm"));
167                         _SM_BAK_PATH = malloc(strlen(optarg)+1+sizeof("sm.bak"));
168                         if (_SM_BASE_PATH == NULL ||
169                             _SM_STATE_PATH == NULL ||
170                             _SM_DIR_PATH == NULL ||
171                             _SM_BAK_PATH == NULL) {
172                                 nsm_log(LOG_ERR, "unable to allocate memory");
173                                 exit(1);
174                         }
175                         strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state");
176                         strcat(strcpy(_SM_DIR_PATH, _SM_BASE_PATH), "/sm");
177                         strcat(strcpy(_SM_BAK_PATH, _SM_BASE_PATH), "/sm.bak");
178                         break;
179
180                 default:
181                         goto usage;
182                 }
183         }
184
185         if (optind < argc) {
186 usage:          fprintf(stderr,
187                         "Usage: sm-notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
188                         "            [-P /path/to/state/directory] [-v my_host_name]\n");
189                 exit(1);
190         }
191
192         log_syslog = 1;
193         openlog("sm-notify", LOG_PID, LOG_DAEMON);
194
195         if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) {
196                 if (record_pid() == 0 && force == 0 && opt_update_state == 1)
197                         /* already run, don't try again */
198                         nsm_log(LOG_NOTICE, "Already notifying clients; Exiting!");
199                         exit(0);
200         }
201
202         if (opt_srcaddr) {
203                 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
204         } else
205         if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
206                 nsm_log(LOG_ERR, "Failed to obtain name of local host: %s",
207                         strerror(errno));
208                 exit(1);
209         }
210
211         backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
212         get_hosts(_SM_BAK_PATH);
213
214         /* If there are not hosts to notify, just exit */
215         if (!hosts) {
216                 nsm_log(LOG_DEBUG, "No hosts to notify; exiting");
217                 return 0;
218         }
219
220         /* Get and update the NSM state. This will call sync() */
221         nsm_state = nsm_get_state(opt_update_state);
222         set_kernel_nsm_state(nsm_state);
223
224         if (!opt_debug) {
225                 if (!opt_quiet)
226                         printf("Backgrounding to notify hosts...\n");
227
228                 if (daemon(0, 0) < 0) {
229                         nsm_log(LOG_ERR, "unable to background: %s",
230                                         strerror(errno));
231                         exit(1);
232                 }
233
234                 close(0);
235                 close(1);
236                 close(2);
237         }
238
239         notify();
240
241         if (hosts) {
242                 struct nsm_host *hp;
243
244                 while ((hp = hosts) != 0) {
245                         hosts = hp->next;
246                         nsm_log(LOG_NOTICE,
247                                 "Unable to notify %s, giving up",
248                                 hp->name);
249                 }
250                 exit(1);
251         }
252
253         exit(0);
254 }
255
256 /*
257  * Notify hosts
258  */
259 static void
260 notify(void)
261 {
262         struct sockaddr_storage address;
263         struct sockaddr *local_addr = (struct sockaddr *)&address;
264         time_t  failtime = 0;
265         int     sock = -1;
266         int retry_cnt = 0;
267
268  retry:
269         sock = socket(AF_INET, SOCK_DGRAM, 0);
270         if (sock < 0) {
271                 nsm_log(LOG_ERR, "Failed to create RPC socket: %s",
272                         strerror(errno));
273                 exit(1);
274         }
275         fcntl(sock, F_SETFL, O_NONBLOCK);
276
277         memset(&address, 0, sizeof(address));
278         local_addr->sa_family = AF_INET;        /* Default to IPv4 */
279
280         /* Bind source IP if provided on command line */
281         if (opt_srcaddr) {
282                 struct addrinfo *ai = smn_lookup(AF_INET, opt_srcaddr);
283                 if (!ai) {
284                         nsm_log(LOG_ERR,
285                                 "Not a valid hostname or address: \"%s\"",
286                                 opt_srcaddr);
287                         exit(1);
288                 }
289
290                 /* We know it's IPv4 at this point */
291                 memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
292
293                 freeaddrinfo(ai);
294         }
295
296         /* Use source port if provided on the command line,
297          * otherwise use bindresvport */
298         if (opt_srcport) {
299                 smn_set_port(local_addr, opt_srcport);
300                 if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
301                         nsm_log(LOG_ERR, "Failed to bind RPC socket: %s",
302                                 strerror(errno));
303                         exit(1);
304                 }
305         } else {
306                 struct servent *se;
307                 struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
308                 (void) bindresvport(sock, sin);
309                 /* try to avoid known ports */
310                 se = getservbyport(sin->sin_port, "udp");
311                 if (se && retry_cnt < 100) {
312                         retry_cnt++;
313                         close(sock);
314                         goto retry;
315                 }
316         }
317
318         if (opt_max_retry)
319                 failtime = time(NULL) + opt_max_retry;
320
321         drop_privs();
322
323         while (hosts) {
324                 struct pollfd   pfd;
325                 time_t          now = time(NULL);
326                 unsigned int    sent = 0;
327                 struct nsm_host *hp;
328                 long            wait;
329
330                 if (failtime && now >= failtime)
331                         break;
332
333                 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
334                         /* Never send more than 10 packets at once */
335                         if (sent++ >= 10)
336                                 break;
337
338                         /* Remove queue head */
339                         hp = hosts;
340                         hosts = hp->next;
341
342                         if (notify_host(sock, hp)){
343                                 unlink(hp->path);
344                                 free(hp->name);
345                                 free(hp->path);
346                                 free(hp);
347                                 continue;
348                         }
349
350                         /* Set the timeout for this call, using an
351                            exponential timeout strategy */
352                         wait = hp->timeout;
353                         if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
354                                 hp->timeout = NSM_MAX_TIMEOUT;
355                         hp->send_next = now + wait;
356                         hp->retries++;
357
358                         insert_host(hp);
359                 }
360                 if (hosts == NULL)
361                         return;
362
363                 nsm_log(LOG_DEBUG, "Host %s due in %ld seconds",
364                                 hosts->name, wait);
365
366                 pfd.fd = sock;
367                 pfd.events = POLLIN;
368
369                 wait *= 1000;
370                 if (wait < 100)
371                         wait = 100;
372                 if (poll(&pfd, 1, wait) != 1)
373                         continue;
374
375                 recv_reply(sock);
376         }
377 }
378
379 /*
380  * Send notification to a single host
381  */
382 static int
383 notify_host(int sock, struct nsm_host *host)
384 {
385         struct sockaddr_storage address;
386         struct sockaddr *dest = (struct sockaddr *)&address;
387         socklen_t destlen = sizeof(address);
388         static unsigned int     xid = 0;
389         uint32_t                msgbuf[MAXMSGSIZE], *p;
390         unsigned int            len;
391
392         if (!xid)
393                 xid = getpid() + time(NULL);
394         if (!host->xid)
395                 host->xid = xid++;
396
397         if (host->ai == NULL) {
398                 host->ai = smn_lookup(AF_UNSPEC, host->name);
399                 if (host->ai == NULL) {
400                         nsm_log(LOG_WARNING,
401                                 "%s doesn't seem to be a valid address,"
402                                 " skipped", host->name);
403                         return 1;
404                 }
405         }
406
407         memset(msgbuf, 0, sizeof(msgbuf));
408         p = msgbuf;
409         *p++ = htonl(host->xid);
410         *p++ = 0;
411         *p++ = htonl(2);
412
413         /* If we retransmitted 4 times, reset the port to force
414          * a new portmap lookup (in case statd was restarted).
415          * We also rotate through multiple IP addresses at this
416          * point.
417          */
418         if (host->retries >= 4) {
419                 struct addrinfo *first = host->ai;
420                 struct addrinfo **next = &host->ai;
421
422                 /* remove the first entry from the list */
423                 host->ai = first->ai_next;
424                 first->ai_next = NULL;
425                 /* find the end of the list */
426                 next = &first->ai_next;
427                 while ( *next )
428                         next = & (*next)->ai_next;
429                 /* put first entry at end */
430                 *next = first;
431                 memcpy(&host->addr, first->ai_addr, first->ai_addrlen);
432                 smn_set_port((struct sockaddr *)&host->addr, 0);
433                 host->retries = 0;
434         }
435
436         memcpy(dest, &host->addr, destlen);
437         if (smn_get_port(dest) == 0) {
438                 /* Build a PMAP packet */
439                 nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
440
441                 smn_set_port(dest, 111);
442                 *p++ = htonl(100000);
443                 *p++ = htonl(2);
444                 *p++ = htonl(3);
445
446                 /* Auth and verf */
447                 *p++ = 0; *p++ = 0;
448                 *p++ = 0; *p++ = 0;
449
450                 *p++ = htonl(NSM_PROGRAM);
451                 *p++ = htonl(NSM_VERSION);
452                 *p++ = htonl(IPPROTO_UDP);
453                 *p++ = 0;
454         } else {
455                 /* Build an SM_NOTIFY packet */
456                 nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name);
457
458                 *p++ = htonl(NSM_PROGRAM);
459                 *p++ = htonl(NSM_VERSION);
460                 *p++ = htonl(NSM_NOTIFY);
461
462                 /* Auth and verf */
463                 *p++ = 0; *p++ = 0;
464                 *p++ = 0; *p++ = 0;
465
466                 /* state change */
467                 len = strlen(nsm_hostname);
468                 *p++ = htonl(len);
469                 memcpy(p, nsm_hostname, len);
470                 p += (len + 3) >> 2;
471                 *p++ = htonl(nsm_state);
472         }
473         len = (p - msgbuf) << 2;
474
475         if (sendto(sock, msgbuf, len, 0, dest, destlen) < 0)
476                 nsm_log(LOG_WARNING, "Sending Reboot Notification to "
477                         "'%s' failed: errno %d (%s)", host->name, errno, strerror(errno));
478         
479         return 0;
480 }
481
482 /*
483  * Receive reply from remote host
484  */
485 static void
486 recv_reply(int sock)
487 {
488         struct nsm_host *hp;
489         struct sockaddr *sap;
490         uint32_t        msgbuf[MAXMSGSIZE], *p, *end;
491         uint32_t        xid;
492         int             res;
493
494         res = recv(sock, msgbuf, sizeof(msgbuf), 0);
495         if (res < 0)
496                 return;
497
498         nsm_log(LOG_DEBUG, "Received packet...");
499
500         p = msgbuf;
501         end = p + (res >> 2);
502
503         xid = ntohl(*p++);
504         if (*p++ != htonl(1)    /* must be REPLY */
505          || *p++ != htonl(0)    /* must be ACCEPTED */
506          || *p++ != htonl(0)    /* must be NULL verifier */
507          || *p++ != htonl(0)
508          || *p++ != htonl(0))   /* must be SUCCESS */
509                 return;
510
511         /* Before we look at the data, find the host struct for
512            this reply */
513         if ((hp = find_host(xid)) == NULL)
514                 return;
515         sap = (struct sockaddr *)&hp->addr;
516
517         if (smn_get_port(sap) == 0) {
518                 /* This was a portmap request */
519                 unsigned int    port;
520
521                 port = ntohl(*p++);
522                 if (p > end)
523                         goto fail;
524
525                 hp->send_next = time(NULL);
526                 if (port == 0) {
527                         /* No binding for statd. Delay the next
528                          * portmap query for max timeout */
529                         nsm_log(LOG_DEBUG, "No statd on %s", hp->name);
530                         hp->timeout = NSM_MAX_TIMEOUT;
531                         hp->send_next += NSM_MAX_TIMEOUT;
532                 } else {
533                         smn_set_port(sap, port);
534                         if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
535                                 hp->timeout = NSM_MAX_TIMEOUT / 4;
536                 }
537                 hp->xid = 0;
538         } else {
539                 /* Successful NOTIFY call. Server returns void,
540                  * so nothing we need to do here (except
541                  * check that we didn't read past the end of the
542                  * packet)
543                  */
544                 if (p <= end) {
545                         nsm_log(LOG_DEBUG, "Host %s notified successfully",
546                                         hp->name);
547                         unlink(hp->path);
548                         free(hp->name);
549                         free(hp->path);
550                         free(hp);
551                         freeaddrinfo(hp->ai);
552                         return;
553                 }
554         }
555
556 fail:   /* Re-insert the host */
557         insert_host(hp);
558 }
559
560 /*
561  * Back up all hosts from the sm directory to sm.bak
562  */
563 static void
564 backup_hosts(const char *dirname, const char *bakname)
565 {
566         struct dirent   *de;
567         DIR             *dir;
568
569         if (!(dir = opendir(dirname))) {
570                 nsm_log(LOG_WARNING,
571                         "Failed to open %s: %s", dirname, strerror(errno));
572                 return;
573         }
574
575         while ((de = readdir(dir)) != NULL) {
576                 char    src[1024], dst[1024];
577
578                 if (de->d_name[0] == '.')
579                         continue;
580
581                 snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name);
582                 snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name);
583                 if (rename(src, dst) < 0) {
584                         nsm_log(LOG_WARNING,
585                                 "Failed to rename %s -> %s: %m",
586                                 src, dst);
587                 }
588         }
589         closedir(dir);
590 }
591
592 /*
593  * Get all entries from sm.bak and convert them to host entries
594  */
595 static void
596 get_hosts(const char *dirname)
597 {
598         struct nsm_host *host;
599         struct dirent   *de;
600         DIR             *dir;
601
602         if (!(dir = opendir(dirname))) {
603                 nsm_log(LOG_WARNING,
604                         "Failed to open %s: %s", dirname, strerror(errno));
605                 return;
606         }
607
608         host = NULL;
609         while ((de = readdir(dir)) != NULL) {
610                 struct stat     stb;
611                 char            path[1024];
612
613                 if (de->d_name[0] == '.')
614                         continue;
615                 if (host == NULL)
616                         host = calloc(1, sizeof(*host));
617                 if (host == NULL) {
618                         nsm_log(LOG_WARNING, "Unable to allocate memory");
619                         return;
620                 }
621
622                 snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
623                 if (stat(path, &stb) < 0)
624                         continue;
625
626                 host->last_used = stb.st_mtime;
627                 host->timeout = NSM_TIMEOUT;
628                 host->path = strdup(path);
629                 host->name = strdup(de->d_name);
630                 host->retries = 100; /* force address retry */
631
632                 insert_host(host);
633                 host = NULL;
634         }
635         closedir(dir);
636
637         if (host)
638                 free(host);
639 }
640
641 /*
642  * Insert host into sorted list
643  */
644 static void
645 insert_host(struct nsm_host *host)
646 {
647         struct nsm_host **where, *p;
648
649         where = &hosts;
650         while ((p = *where) != 0) {
651                 /* Sort in ascending order of timeout */
652                 if (host->send_next < p->send_next)
653                         break;
654                 /* If we have the same timeout, put the
655                  * most recently used host first.
656                  * This makes sure that "recent" hosts
657                  * get notified first.
658                  */
659                 if (host->send_next == p->send_next
660                  && host->last_used > p->last_used)
661                         break;
662                 where = &p->next;
663         }
664
665         host->next = *where;
666         *where = host;
667 }
668
669 /*
670  * Find host given the XID
671  */
672 static struct nsm_host *
673 find_host(uint32_t xid)
674 {
675         struct nsm_host **where, *p;
676
677         where = &hosts;
678         while ((p = *where) != 0) {
679                 if (p->xid == xid) {
680                         *where = p->next;
681                         return p;
682                 }
683                 where = &p->next;
684         }
685         return NULL;
686 }
687
688
689 /*
690  * Retrieve the current NSM state
691  */
692 static unsigned int
693 nsm_get_state(int update)
694 {
695         char            newfile[PATH_MAX];
696         int             fd, state;
697
698         if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) {
699                 if (!opt_quiet) {
700                         nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH);
701                         nsm_log(LOG_WARNING, "Creating %s, set initial state 1",
702                                 _SM_STATE_PATH);
703                 }
704                 state = 1;
705                 update = 1;
706         } else {
707                 if (read(fd, &state, sizeof(state)) != sizeof(state)) {
708                         nsm_log(LOG_WARNING,
709                                 "%s: bad file size, setting state = 1",
710                                 _SM_STATE_PATH);
711                         state = 1;
712                         update = 1;
713                 } else {
714                         if (!(state & 1))
715                                 state += 1;
716                 }
717                 close(fd);
718         }
719
720         if (update) {
721                 state += 2;
722                 snprintf(newfile, sizeof(newfile),
723                                 "%s.new", _SM_STATE_PATH);
724                 if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) {
725                         nsm_log(LOG_ERR, "Cannot create %s: %m", newfile);
726                         exit(1);
727                 }
728                 if (write(fd, &state, sizeof(state)) != sizeof(state)) {
729                         nsm_log(LOG_ERR,
730                                 "Failed to write state to %s", newfile);
731                         exit(1);
732                 }
733                 close(fd);
734                 if (rename(newfile, _SM_STATE_PATH) < 0) {
735                         nsm_log(LOG_ERR,
736                                 "Cannot create %s: %m", _SM_STATE_PATH);
737                         exit(1);
738                 }
739                 sync();
740         }
741
742         return state;
743 }
744
745 /*
746  * Log a message
747  */
748 static void
749 nsm_log(int fac, const char *fmt, ...)
750 {
751         va_list ap;
752
753         if (fac == LOG_DEBUG && !opt_debug)
754                 return;
755
756         va_start(ap, fmt);
757         if (log_syslog)
758                 vsyslog(fac, fmt, ap);
759         else {
760                 vfprintf(stderr, fmt, ap);
761                 fputs("\n", stderr);
762         }
763         va_end(ap);
764 }
765
766 /*
767  * Record pid in /var/run/sm-notify.pid
768  * This file should remain until a reboot, even if the
769  * program exits.
770  * If file already exists, fail.
771  */
772 static int record_pid(void)
773 {
774         char pid[20];
775         int fd;
776
777         snprintf(pid, 20, "%d\n", getpid());
778         fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
779         if (fd < 0)
780                 return 0;
781         write(fd, pid, strlen(pid));
782         close(fd);
783         return 1;
784 }
785
786 /* Drop privileges to match owner of state-directory
787  * (in case a reply triggers some unknown bug).
788  */
789 static void drop_privs(void)
790 {
791         struct stat st;
792
793         if (stat(_SM_DIR_PATH, &st) == -1 &&
794             stat(_SM_BASE_PATH, &st) == -1) {
795                 st.st_uid = 0;
796                 st.st_gid = 0;
797         }
798
799         if (st.st_uid == 0) {
800                 nsm_log(LOG_WARNING,
801                         "sm-notify running as root. chown %s to choose different user",
802                     _SM_DIR_PATH);
803                 return;
804         }
805
806         setgroups(0, NULL);
807         if (setgid(st.st_gid) == -1
808             || setuid(st.st_uid) == -1) {
809                 nsm_log(LOG_ERR, "Fail to drop privileges");
810                 exit(1);
811         }
812 }
813
814 static void set_kernel_nsm_state(int state)
815 {
816         int fd;
817
818         fd = open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY);
819         if (fd >= 0) {
820                 char buf[20];
821                 snprintf(buf, sizeof(buf), "%d", state);
822                 write(fd, buf, strlen(buf));
823                 close(fd);
824         }
825 }