]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/sm-notify.c
sm-notify: always exiting without any notification
[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
203         if (opt_srcaddr) {
204                 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
205         } else
206         if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
207                 nsm_log(LOG_ERR, "Failed to obtain name of local host: %s",
208                         strerror(errno));
209                 exit(1);
210         }
211
212         backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
213         get_hosts(_SM_BAK_PATH);
214
215         /* If there are not hosts to notify, just exit */
216         if (!hosts) {
217                 nsm_log(LOG_DEBUG, "No hosts to notify; exiting");
218                 return 0;
219         }
220
221         /* Get and update the NSM state. This will call sync() */
222         nsm_state = nsm_get_state(opt_update_state);
223         set_kernel_nsm_state(nsm_state);
224
225         if (!opt_debug) {
226                 if (!opt_quiet)
227                         printf("Backgrounding to notify hosts...\n");
228
229                 if (daemon(0, 0) < 0) {
230                         nsm_log(LOG_ERR, "unable to background: %s",
231                                         strerror(errno));
232                         exit(1);
233                 }
234
235                 close(0);
236                 close(1);
237                 close(2);
238         }
239
240         notify();
241
242         if (hosts) {
243                 struct nsm_host *hp;
244
245                 while ((hp = hosts) != 0) {
246                         hosts = hp->next;
247                         nsm_log(LOG_NOTICE,
248                                 "Unable to notify %s, giving up",
249                                 hp->name);
250                 }
251                 exit(1);
252         }
253
254         exit(0);
255 }
256
257 /*
258  * Notify hosts
259  */
260 static void
261 notify(void)
262 {
263         struct sockaddr_storage address;
264         struct sockaddr *local_addr = (struct sockaddr *)&address;
265         time_t  failtime = 0;
266         int     sock = -1;
267         int retry_cnt = 0;
268
269  retry:
270         sock = socket(AF_INET, SOCK_DGRAM, 0);
271         if (sock < 0) {
272                 nsm_log(LOG_ERR, "Failed to create RPC socket: %s",
273                         strerror(errno));
274                 exit(1);
275         }
276         fcntl(sock, F_SETFL, O_NONBLOCK);
277
278         memset(&address, 0, sizeof(address));
279         local_addr->sa_family = AF_INET;        /* Default to IPv4 */
280
281         /* Bind source IP if provided on command line */
282         if (opt_srcaddr) {
283                 struct addrinfo *ai = smn_lookup(AF_INET, opt_srcaddr);
284                 if (!ai) {
285                         nsm_log(LOG_ERR,
286                                 "Not a valid hostname or address: \"%s\"",
287                                 opt_srcaddr);
288                         exit(1);
289                 }
290
291                 /* We know it's IPv4 at this point */
292                 memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
293
294                 freeaddrinfo(ai);
295         }
296
297         /* Use source port if provided on the command line,
298          * otherwise use bindresvport */
299         if (opt_srcport) {
300                 smn_set_port(local_addr, opt_srcport);
301                 if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
302                         nsm_log(LOG_ERR, "Failed to bind RPC socket: %s",
303                                 strerror(errno));
304                         exit(1);
305                 }
306         } else {
307                 struct servent *se;
308                 struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
309                 (void) bindresvport(sock, sin);
310                 /* try to avoid known ports */
311                 se = getservbyport(sin->sin_port, "udp");
312                 if (se && retry_cnt < 100) {
313                         retry_cnt++;
314                         close(sock);
315                         goto retry;
316                 }
317         }
318
319         if (opt_max_retry)
320                 failtime = time(NULL) + opt_max_retry;
321
322         drop_privs();
323
324         while (hosts) {
325                 struct pollfd   pfd;
326                 time_t          now = time(NULL);
327                 unsigned int    sent = 0;
328                 struct nsm_host *hp;
329                 long            wait;
330
331                 if (failtime && now >= failtime)
332                         break;
333
334                 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
335                         /* Never send more than 10 packets at once */
336                         if (sent++ >= 10)
337                                 break;
338
339                         /* Remove queue head */
340                         hp = hosts;
341                         hosts = hp->next;
342
343                         if (notify_host(sock, hp)){
344                                 unlink(hp->path);
345                                 free(hp->name);
346                                 free(hp->path);
347                                 free(hp);
348                                 continue;
349                         }
350
351                         /* Set the timeout for this call, using an
352                            exponential timeout strategy */
353                         wait = hp->timeout;
354                         if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
355                                 hp->timeout = NSM_MAX_TIMEOUT;
356                         hp->send_next = now + wait;
357                         hp->retries++;
358
359                         insert_host(hp);
360                 }
361                 if (hosts == NULL)
362                         return;
363
364                 nsm_log(LOG_DEBUG, "Host %s due in %ld seconds",
365                                 hosts->name, wait);
366
367                 pfd.fd = sock;
368                 pfd.events = POLLIN;
369
370                 wait *= 1000;
371                 if (wait < 100)
372                         wait = 100;
373                 if (poll(&pfd, 1, wait) != 1)
374                         continue;
375
376                 recv_reply(sock);
377         }
378 }
379
380 /*
381  * Send notification to a single host
382  */
383 static int
384 notify_host(int sock, struct nsm_host *host)
385 {
386         struct sockaddr_storage address;
387         struct sockaddr *dest = (struct sockaddr *)&address;
388         socklen_t destlen = sizeof(address);
389         static unsigned int     xid = 0;
390         uint32_t                msgbuf[MAXMSGSIZE], *p;
391         unsigned int            len;
392
393         if (!xid)
394                 xid = getpid() + time(NULL);
395         if (!host->xid)
396                 host->xid = xid++;
397
398         if (host->ai == NULL) {
399                 host->ai = smn_lookup(AF_UNSPEC, host->name);
400                 if (host->ai == NULL) {
401                         nsm_log(LOG_WARNING,
402                                 "%s doesn't seem to be a valid address,"
403                                 " skipped", host->name);
404                         return 1;
405                 }
406         }
407
408         memset(msgbuf, 0, sizeof(msgbuf));
409         p = msgbuf;
410         *p++ = htonl(host->xid);
411         *p++ = 0;
412         *p++ = htonl(2);
413
414         /* If we retransmitted 4 times, reset the port to force
415          * a new portmap lookup (in case statd was restarted).
416          * We also rotate through multiple IP addresses at this
417          * point.
418          */
419         if (host->retries >= 4) {
420                 struct addrinfo *first = host->ai;
421                 struct addrinfo **next = &host->ai;
422
423                 /* remove the first entry from the list */
424                 host->ai = first->ai_next;
425                 first->ai_next = NULL;
426                 /* find the end of the list */
427                 next = &first->ai_next;
428                 while ( *next )
429                         next = & (*next)->ai_next;
430                 /* put first entry at end */
431                 *next = first;
432                 memcpy(&host->addr, first->ai_addr, first->ai_addrlen);
433                 smn_set_port((struct sockaddr *)&host->addr, 0);
434                 host->retries = 0;
435         }
436
437         memcpy(dest, &host->addr, destlen);
438         if (smn_get_port(dest) == 0) {
439                 /* Build a PMAP packet */
440                 nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
441
442                 smn_set_port(dest, 111);
443                 *p++ = htonl(100000);
444                 *p++ = htonl(2);
445                 *p++ = htonl(3);
446
447                 /* Auth and verf */
448                 *p++ = 0; *p++ = 0;
449                 *p++ = 0; *p++ = 0;
450
451                 *p++ = htonl(NSM_PROGRAM);
452                 *p++ = htonl(NSM_VERSION);
453                 *p++ = htonl(IPPROTO_UDP);
454                 *p++ = 0;
455         } else {
456                 /* Build an SM_NOTIFY packet */
457                 nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name);
458
459                 *p++ = htonl(NSM_PROGRAM);
460                 *p++ = htonl(NSM_VERSION);
461                 *p++ = htonl(NSM_NOTIFY);
462
463                 /* Auth and verf */
464                 *p++ = 0; *p++ = 0;
465                 *p++ = 0; *p++ = 0;
466
467                 /* state change */
468                 len = strlen(nsm_hostname);
469                 *p++ = htonl(len);
470                 memcpy(p, nsm_hostname, len);
471                 p += (len + 3) >> 2;
472                 *p++ = htonl(nsm_state);
473         }
474         len = (p - msgbuf) << 2;
475
476         if (sendto(sock, msgbuf, len, 0, dest, destlen) < 0)
477                 nsm_log(LOG_WARNING, "Sending Reboot Notification to "
478                         "'%s' failed: errno %d (%s)", host->name, errno, strerror(errno));
479         
480         return 0;
481 }
482
483 /*
484  * Receive reply from remote host
485  */
486 static void
487 recv_reply(int sock)
488 {
489         struct nsm_host *hp;
490         struct sockaddr *sap;
491         uint32_t        msgbuf[MAXMSGSIZE], *p, *end;
492         uint32_t        xid;
493         int             res;
494
495         res = recv(sock, msgbuf, sizeof(msgbuf), 0);
496         if (res < 0)
497                 return;
498
499         nsm_log(LOG_DEBUG, "Received packet...");
500
501         p = msgbuf;
502         end = p + (res >> 2);
503
504         xid = ntohl(*p++);
505         if (*p++ != htonl(1)    /* must be REPLY */
506          || *p++ != htonl(0)    /* must be ACCEPTED */
507          || *p++ != htonl(0)    /* must be NULL verifier */
508          || *p++ != htonl(0)
509          || *p++ != htonl(0))   /* must be SUCCESS */
510                 return;
511
512         /* Before we look at the data, find the host struct for
513            this reply */
514         if ((hp = find_host(xid)) == NULL)
515                 return;
516         sap = (struct sockaddr *)&hp->addr;
517
518         if (smn_get_port(sap) == 0) {
519                 /* This was a portmap request */
520                 unsigned int    port;
521
522                 port = ntohl(*p++);
523                 if (p > end)
524                         goto fail;
525
526                 hp->send_next = time(NULL);
527                 if (port == 0) {
528                         /* No binding for statd. Delay the next
529                          * portmap query for max timeout */
530                         nsm_log(LOG_DEBUG, "No statd on %s", hp->name);
531                         hp->timeout = NSM_MAX_TIMEOUT;
532                         hp->send_next += NSM_MAX_TIMEOUT;
533                 } else {
534                         smn_set_port(sap, port);
535                         if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
536                                 hp->timeout = NSM_MAX_TIMEOUT / 4;
537                 }
538                 hp->xid = 0;
539         } else {
540                 /* Successful NOTIFY call. Server returns void,
541                  * so nothing we need to do here (except
542                  * check that we didn't read past the end of the
543                  * packet)
544                  */
545                 if (p <= end) {
546                         nsm_log(LOG_DEBUG, "Host %s notified successfully",
547                                         hp->name);
548                         unlink(hp->path);
549                         free(hp->name);
550                         free(hp->path);
551                         free(hp);
552                         freeaddrinfo(hp->ai);
553                         return;
554                 }
555         }
556
557 fail:   /* Re-insert the host */
558         insert_host(hp);
559 }
560
561 /*
562  * Back up all hosts from the sm directory to sm.bak
563  */
564 static void
565 backup_hosts(const char *dirname, const char *bakname)
566 {
567         struct dirent   *de;
568         DIR             *dir;
569
570         if (!(dir = opendir(dirname))) {
571                 nsm_log(LOG_WARNING,
572                         "Failed to open %s: %s", dirname, strerror(errno));
573                 return;
574         }
575
576         while ((de = readdir(dir)) != NULL) {
577                 char    src[1024], dst[1024];
578
579                 if (de->d_name[0] == '.')
580                         continue;
581
582                 snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name);
583                 snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name);
584                 if (rename(src, dst) < 0) {
585                         nsm_log(LOG_WARNING,
586                                 "Failed to rename %s -> %s: %m",
587                                 src, dst);
588                 }
589         }
590         closedir(dir);
591 }
592
593 /*
594  * Get all entries from sm.bak and convert them to host entries
595  */
596 static void
597 get_hosts(const char *dirname)
598 {
599         struct nsm_host *host;
600         struct dirent   *de;
601         DIR             *dir;
602
603         if (!(dir = opendir(dirname))) {
604                 nsm_log(LOG_WARNING,
605                         "Failed to open %s: %s", dirname, strerror(errno));
606                 return;
607         }
608
609         host = NULL;
610         while ((de = readdir(dir)) != NULL) {
611                 struct stat     stb;
612                 char            path[1024];
613
614                 if (de->d_name[0] == '.')
615                         continue;
616                 if (host == NULL)
617                         host = calloc(1, sizeof(*host));
618                 if (host == NULL) {
619                         nsm_log(LOG_WARNING, "Unable to allocate memory");
620                         return;
621                 }
622
623                 snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
624                 if (stat(path, &stb) < 0)
625                         continue;
626
627                 host->last_used = stb.st_mtime;
628                 host->timeout = NSM_TIMEOUT;
629                 host->path = strdup(path);
630                 host->name = strdup(de->d_name);
631                 host->retries = 100; /* force address retry */
632
633                 insert_host(host);
634                 host = NULL;
635         }
636         closedir(dir);
637
638         if (host)
639                 free(host);
640 }
641
642 /*
643  * Insert host into sorted list
644  */
645 static void
646 insert_host(struct nsm_host *host)
647 {
648         struct nsm_host **where, *p;
649
650         where = &hosts;
651         while ((p = *where) != 0) {
652                 /* Sort in ascending order of timeout */
653                 if (host->send_next < p->send_next)
654                         break;
655                 /* If we have the same timeout, put the
656                  * most recently used host first.
657                  * This makes sure that "recent" hosts
658                  * get notified first.
659                  */
660                 if (host->send_next == p->send_next
661                  && host->last_used > p->last_used)
662                         break;
663                 where = &p->next;
664         }
665
666         host->next = *where;
667         *where = host;
668 }
669
670 /*
671  * Find host given the XID
672  */
673 static struct nsm_host *
674 find_host(uint32_t xid)
675 {
676         struct nsm_host **where, *p;
677
678         where = &hosts;
679         while ((p = *where) != 0) {
680                 if (p->xid == xid) {
681                         *where = p->next;
682                         return p;
683                 }
684                 where = &p->next;
685         }
686         return NULL;
687 }
688
689
690 /*
691  * Retrieve the current NSM state
692  */
693 static unsigned int
694 nsm_get_state(int update)
695 {
696         char            newfile[PATH_MAX];
697         int             fd, state;
698
699         if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) {
700                 if (!opt_quiet) {
701                         nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH);
702                         nsm_log(LOG_WARNING, "Creating %s, set initial state 1",
703                                 _SM_STATE_PATH);
704                 }
705                 state = 1;
706                 update = 1;
707         } else {
708                 if (read(fd, &state, sizeof(state)) != sizeof(state)) {
709                         nsm_log(LOG_WARNING,
710                                 "%s: bad file size, setting state = 1",
711                                 _SM_STATE_PATH);
712                         state = 1;
713                         update = 1;
714                 } else {
715                         if (!(state & 1))
716                                 state += 1;
717                 }
718                 close(fd);
719         }
720
721         if (update) {
722                 state += 2;
723                 snprintf(newfile, sizeof(newfile),
724                                 "%s.new", _SM_STATE_PATH);
725                 if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) {
726                         nsm_log(LOG_ERR, "Cannot create %s: %m", newfile);
727                         exit(1);
728                 }
729                 if (write(fd, &state, sizeof(state)) != sizeof(state)) {
730                         nsm_log(LOG_ERR,
731                                 "Failed to write state to %s", newfile);
732                         exit(1);
733                 }
734                 close(fd);
735                 if (rename(newfile, _SM_STATE_PATH) < 0) {
736                         nsm_log(LOG_ERR,
737                                 "Cannot create %s: %m", _SM_STATE_PATH);
738                         exit(1);
739                 }
740                 sync();
741         }
742
743         return state;
744 }
745
746 /*
747  * Log a message
748  */
749 static void
750 nsm_log(int fac, const char *fmt, ...)
751 {
752         va_list ap;
753
754         if (fac == LOG_DEBUG && !opt_debug)
755                 return;
756
757         va_start(ap, fmt);
758         if (log_syslog)
759                 vsyslog(fac, fmt, ap);
760         else {
761                 vfprintf(stderr, fmt, ap);
762                 fputs("\n", stderr);
763         }
764         va_end(ap);
765 }
766
767 /*
768  * Record pid in /var/run/sm-notify.pid
769  * This file should remain until a reboot, even if the
770  * program exits.
771  * If file already exists, fail.
772  */
773 static int record_pid(void)
774 {
775         char pid[20];
776         int fd;
777
778         snprintf(pid, 20, "%d\n", getpid());
779         fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
780         if (fd < 0)
781                 return 0;
782         write(fd, pid, strlen(pid));
783         close(fd);
784         return 1;
785 }
786
787 /* Drop privileges to match owner of state-directory
788  * (in case a reply triggers some unknown bug).
789  */
790 static void drop_privs(void)
791 {
792         struct stat st;
793
794         if (stat(_SM_DIR_PATH, &st) == -1 &&
795             stat(_SM_BASE_PATH, &st) == -1) {
796                 st.st_uid = 0;
797                 st.st_gid = 0;
798         }
799
800         if (st.st_uid == 0) {
801                 nsm_log(LOG_WARNING,
802                         "sm-notify running as root. chown %s to choose different user",
803                     _SM_DIR_PATH);
804                 return;
805         }
806
807         setgroups(0, NULL);
808         if (setgid(st.st_gid) == -1
809             || setuid(st.st_uid) == -1) {
810                 nsm_log(LOG_ERR, "Fail to drop privileges");
811                 exit(1);
812         }
813 }
814
815 static void set_kernel_nsm_state(int state)
816 {
817         int fd;
818
819         fd = open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY);
820         if (fd >= 0) {
821                 char buf[20];
822                 snprintf(buf, sizeof(buf), "%d", state);
823                 write(fd, buf, strlen(buf));
824                 close(fd);
825         }
826 }