]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/sm-notify.c
sm-notify: sm-notify doesn't handle localhost properly
[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 <err.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <sys/stat.h>
15 #include <sys/poll.h>
16 #include <sys/param.h>
17 #include <sys/syslog.h>
18 #include <arpa/inet.h>
19 #include <dirent.h>
20 #include <time.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <stdlib.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <netdb.h>
29 #include <errno.h>
30 #include <grp.h>
31
32 #include "sockaddr.h"
33 #include "xlog.h"
34 #include "nsm.h"
35 #include "nfsrpc.h"
36
37 /* glibc before 2.3.4 */
38 #ifndef AI_NUMERICSERV
39 #define AI_NUMERICSERV  0
40 #endif
41
42 #define NSM_TIMEOUT     2
43 #define NSM_MAX_TIMEOUT 120     /* don't make this too big */
44
45 struct nsm_host {
46         struct nsm_host *       next;
47         char *                  name;
48         char *                  mon_name;
49         char *                  my_name;
50         struct addrinfo         *ai;
51         time_t                  last_used;
52         time_t                  send_next;
53         unsigned int            timeout;
54         unsigned int            retries;
55         uint32_t                xid;
56 };
57
58 static char             nsm_hostname[SM_MAXSTRLEN + 1];
59 static int              nsm_state;
60 static int              nsm_family = AF_INET;
61 static int              opt_debug = 0;
62 static _Bool            opt_update_state = true;
63 static unsigned int     opt_max_retry = 15 * 60;
64 static char *           opt_srcaddr = NULL;
65 static char *           opt_srcport = NULL;
66
67 static void             notify(const int sock);
68 static int              notify_host(int, struct nsm_host *);
69 static void             recv_reply(int);
70 static void             insert_host(struct nsm_host *);
71 static struct nsm_host *find_host(uint32_t);
72 static int              record_pid(void);
73
74 static struct nsm_host *        hosts = NULL;
75
76 __attribute_malloc__
77 static struct addrinfo *
78 smn_lookup(const char *name)
79 {
80         struct addrinfo *ai = NULL;
81         struct addrinfo hint = {
82                 .ai_family      = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
83                 .ai_protocol    = (int)IPPROTO_UDP,
84         };
85         int error;
86
87         error = getaddrinfo(name, NULL, &hint, &ai);
88         if (error != 0) {
89                 xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
90                 return NULL;
91         }
92
93         return ai;
94 }
95
96 #ifdef HAVE_GETNAMEINFO
97 static char *
98 smn_get_hostname(const struct sockaddr *sap, const socklen_t salen,
99                 const char *name)
100 {
101         char buf[NI_MAXHOST];
102         int error;
103
104         error = getnameinfo(sap, salen, buf, sizeof(buf), NULL, 0, NI_NAMEREQD);
105         if (error != 0) {
106                 xlog(L_ERROR, "my_name '%s' is unusable: %s",
107                         name, gai_strerror(error));
108                 return NULL;
109         }
110         return strdup(buf);
111 }
112 #else   /* !HAVE_GETNAMEINFO */
113 static char *
114 smn_get_hostname(const struct sockaddr *sap,
115                 __attribute__ ((unused)) const socklen_t salen,
116                 const char *name)
117 {
118         const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
119         const struct in_addr *addr = &sin->sin_addr;
120         struct hostent *hp;
121
122         if (sap->sa_family != AF_INET) {
123                 xlog(L_ERROR, "my_name '%s' is unusable: Bad address family",
124                         name);
125                 return NULL;
126         }
127
128         hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET);
129         if (hp == NULL) {
130                 xlog(L_ERROR, "my_name '%s' is unusable: %s",
131                         name, hstrerror(h_errno));
132                 return NULL;
133         }
134         return strdup(hp->h_name);
135 }
136 #endif  /* !HAVE_GETNAMEINFO */
137
138 /*
139  * Presentation addresses are converted to their canonical hostnames.
140  * If the IP address does not map to a hostname, it is an error:
141  * we never send a presentation address as the argument of SM_NOTIFY.
142  *
143  * If "name" is not a presentation address, it is left alone.  This
144  * allows the administrator some flexibility if DNS isn't configured
145  * exactly how sm-notify prefers it.
146  *
147  * Returns NUL-terminated C string containing the result, or NULL
148  * if the canonical name doesn't exist or cannot be determined.
149  * The caller must free the result with free(3).
150  */
151 __attribute_malloc__
152 static char *
153 smn_verify_my_name(const char *name)
154 {
155         struct addrinfo *ai = NULL;
156         struct addrinfo hint = {
157 #ifdef IPV6_SUPPORTED
158                 .ai_family      = AF_UNSPEC,
159 #else   /* !IPV6_SUPPORTED */
160                 .ai_family      = AF_INET,
161 #endif  /* !IPV6_SUPPORTED */
162                 .ai_flags       = AI_NUMERICHOST,
163         };
164         char *retval;
165         int error;
166
167         error = getaddrinfo(name, NULL, &hint, &ai);
168         switch (error) {
169         case 0:
170                 /* @name was a presentation address */
171                 retval = smn_get_hostname(ai->ai_addr, ai->ai_addrlen, name);
172                 freeaddrinfo(ai);
173                 if (retval == NULL)
174                         return NULL;
175                 break;
176         case EAI_NONAME:
177                 /* @name was not a presentation address */
178                 retval = strdup(name);
179                 break;
180         default:
181                 xlog(L_ERROR, "my_name '%s' is unusable: %s",
182                         name, gai_strerror(error));
183                 return NULL;
184         }
185
186         xlog(D_GENERAL, "Canonical name for my_name '%s': %s",
187                         name, retval);
188         return retval;
189 }
190
191 __attribute_malloc__
192 static struct nsm_host *
193 smn_alloc_host(const char *hostname, const char *mon_name,
194                 const char *my_name, const time_t timestamp)
195 {
196         struct nsm_host *host;
197
198         host = calloc(1, sizeof(*host));
199         if (host == NULL)
200                 goto out_nomem;
201
202         host->name = strdup(hostname);
203         host->mon_name = strdup(mon_name);
204         host->my_name = strdup(my_name);
205         if (host->name == NULL ||
206             host->mon_name == NULL ||
207             host->my_name == NULL) {
208                 free(host->my_name);
209                 free(host->mon_name);
210                 free(host->name);
211                 free(host);
212                 goto out_nomem;
213         }
214
215         host->last_used = timestamp;
216         host->timeout = NSM_TIMEOUT;
217         host->retries = 100;            /* force address retry */
218
219         return host;
220
221 out_nomem:
222         xlog_warn("Unable to allocate memory");
223         return NULL;
224 }
225
226 static void smn_forget_host(struct nsm_host *host)
227 {
228         xlog(D_CALL, "Removing %s (%s, %s) from notify list",
229                         host->name, host->mon_name, host->my_name);
230
231         nsm_delete_notified_host(host->name, host->mon_name, host->my_name);
232
233         free(host->my_name);
234         free(host->mon_name);
235         free(host->name);
236         if (host->ai)
237                 freeaddrinfo(host->ai);
238
239         free(host);
240 }
241
242 static unsigned int
243 smn_get_host(const char *hostname,
244                 __attribute__ ((unused)) const struct sockaddr *sap,
245                 const struct mon *m, const time_t timestamp)
246 {
247         struct nsm_host *host;
248
249         host = smn_alloc_host(hostname,
250                 m->mon_id.mon_name, m->mon_id.my_id.my_name, timestamp);
251         if (host == NULL)
252                 return 0;
253
254         insert_host(host);
255         xlog(D_GENERAL, "Added host %s to notify list", hostname);
256         return 1;
257 }
258
259 #ifdef IPV6_SUPPORTED
260 static int smn_socket(void)
261 {
262         int sock;
263
264         /*
265          * Use an AF_INET socket if IPv6 is disabled on the
266          * local system.
267          */
268         sock = socket(AF_INET6, SOCK_DGRAM, 0);
269         if (sock == -1) {
270                 if (errno != EAFNOSUPPORT) {
271                         xlog(L_ERROR, "Failed to create RPC socket: %m");
272                         return -1;
273                 }
274                 sock = socket(AF_INET, SOCK_DGRAM, 0);
275                 if (sock < 0) {
276                         xlog(L_ERROR, "Failed to create RPC socket: %m");
277                         return -1;
278                 }
279         } else
280                 nsm_family = AF_INET6;
281
282         if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
283                 xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
284                 goto out_close;
285         }
286
287         /*
288          * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4.  However,
289          * since sm-notify open-codes all of its RPC support, it can
290          * use a single socket and let the local network stack provide
291          * the correct mapping between address families automatically.
292          * This is the same thing that is done in the kernel.
293          */
294         if (nsm_family == AF_INET6) {
295                 const int zero = 0;
296                 socklen_t zerolen = (socklen_t)sizeof(zero);
297
298                 if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
299                                         (char *)&zero, zerolen) == -1) {
300                         xlog(L_ERROR, "setsockopt(3) on RPC socket failed: %m");
301                         goto out_close;
302                 }
303         }
304
305         return sock;
306
307 out_close:
308         (void)close(sock);
309         return -1;
310 }
311 #else   /* !IPV6_SUPPORTED */
312 static int smn_socket(void)
313 {
314         int sock;
315
316         sock = socket(AF_INET, SOCK_DGRAM, 0);
317         if (sock == -1) {
318                 xlog(L_ERROR, "Failed to create RPC socket: %m");
319                 return -1;
320         }
321
322         if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
323                 xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
324                 (void)close(sock);
325                 return -1;
326         }
327
328         return sock;
329 }
330 #endif  /* !IPV6_SUPPORTED */
331
332 /*
333  * If admin specified a source address or srcport, then convert those
334  * to a sockaddr and return it.   Otherwise, return an ANYADDR address.
335  */
336 __attribute_malloc__
337 static struct addrinfo *
338 smn_bind_address(const char *srcaddr, const char *srcport)
339 {
340         struct addrinfo *ai = NULL;
341         struct addrinfo hint = {
342                 .ai_flags       = AI_NUMERICSERV,
343                 .ai_family      = nsm_family,
344                 .ai_protocol    = (int)IPPROTO_UDP,
345         };
346         int error;
347
348         if (srcaddr == NULL)
349                 hint.ai_flags |= AI_PASSIVE;
350
351         /* Do not allow "node" and "service" parameters both to be NULL */
352         if (srcport == NULL)
353                 error = getaddrinfo(srcaddr, "", &hint, &ai);
354         else
355                 error = getaddrinfo(srcaddr, srcport, &hint, &ai);
356         if (error != 0) {
357                 xlog(L_ERROR,
358                         "Invalid bind address or port for RPC socket: %s",
359                                 gai_strerror(error));
360                 return NULL;
361         }
362
363         return ai;
364 }
365
366 #ifdef HAVE_LIBTIRPC
367 static int
368 smn_bindresvport(int sock, struct sockaddr *sap)
369 {
370         return bindresvport_sa(sock, sap);
371 }
372
373 #else   /* !HAVE_LIBTIRPC */
374 static int
375 smn_bindresvport(int sock, struct sockaddr *sap)
376 {
377         if (sap->sa_family != AF_INET) {
378                 errno = EAFNOSUPPORT;
379                 return -1;
380         }
381
382         return bindresvport(sock, (struct sockaddr_in *)(char *)sap);
383 }
384 #endif  /* !HAVE_LIBTIRPC */
385
386 /*
387  * Prepare a socket for sending RPC requests
388  *
389  * Returns a bound datagram socket file descriptor, or -1 if
390  * an error occurs.
391  */
392 static int
393 smn_create_socket(const char *srcaddr, const char *srcport)
394 {
395         int sock, retry_cnt = 0;
396         struct addrinfo *ai;
397
398 retry:
399         sock = smn_socket();
400         if (sock == -1)
401                 return -1;
402
403         ai = smn_bind_address(srcaddr, srcport);
404         if (ai == NULL) {
405                 (void)close(sock);
406                 return -1;
407         }
408
409         /* Use source port if provided on the command line,
410          * otherwise use bindresvport */
411         if (srcport) {
412                 if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
413                         xlog(L_ERROR, "Failed to bind RPC socket: %m");
414                         freeaddrinfo(ai);
415                         (void)close(sock);
416                         return -1;
417                 }
418         } else {
419                 struct servent *se;
420
421                 if (smn_bindresvport(sock, ai->ai_addr) == -1) {
422                         xlog(L_ERROR,
423                                 "bindresvport on RPC socket failed: %m");
424                         freeaddrinfo(ai);
425                         (void)close(sock);
426                         return -1;
427                 }
428
429                 /* try to avoid known ports */
430                 se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp");
431                 if (se != NULL && retry_cnt < 100) {
432                         retry_cnt++;
433                         freeaddrinfo(ai);
434                         (void)close(sock);
435                         goto retry;
436                 }
437         }
438
439         freeaddrinfo(ai);
440         return sock;
441 }
442
443 int
444 main(int argc, char **argv)
445 {
446         int     c, sock, force = 0;
447         char *  progname;
448
449         progname = strrchr(argv[0], '/');
450         if (progname != NULL)
451                 progname++;
452         else
453                 progname = argv[0];
454
455         while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
456                 switch (c) {
457                 case 'f':
458                         force = 1;
459                         break;
460                 case 'd':
461                         opt_debug++;
462                         break;
463                 case 'm':
464                         opt_max_retry = atoi(optarg) * 60;
465                         break;
466                 case 'n':
467                         opt_update_state = false;
468                         break;
469                 case 'p':
470                         opt_srcport = optarg;
471                         break;
472                 case 'v':
473                         opt_srcaddr = optarg;
474                         break;
475                 case 'P':
476                         if (!nsm_setup_pathnames(argv[0], optarg))
477                                 exit(1);
478                         break;
479
480                 default:
481                         goto usage;
482                 }
483         }
484
485         if (optind < argc) {
486 usage:          fprintf(stderr,
487                         "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
488                         "            [-P /path/to/state/directory] [-v my_host_name]\n",
489                         progname);
490                 exit(1);
491         }
492
493         if (opt_debug) {
494                 xlog_syslog(0);
495                 xlog_stderr(1);
496                 xlog_config(D_ALL, 1);
497         } else {
498                 xlog_syslog(1);
499                 xlog_stderr(0);
500         }
501
502         xlog_open(progname);
503         xlog(L_NOTICE, "Version " VERSION " starting");
504
505         if (nsm_is_default_parentdir()) {
506                 if (record_pid() == 0 && force == 0 && opt_update_state) {
507                         /* already run, don't try again */
508                         xlog(L_NOTICE, "Already notifying clients; Exiting!");
509                         exit(0);
510                 }
511         }
512
513         if (opt_srcaddr != NULL) {
514                 char *name;
515
516                 name = smn_verify_my_name(opt_srcaddr);
517                 if (name == NULL)
518                         exit(1);
519
520                 strncpy(nsm_hostname, name, sizeof(nsm_hostname));
521                 free(name);
522         }
523
524         (void)nsm_retire_monitored_hosts();
525         if (nsm_load_notify_list(smn_get_host) == 0) {
526                 xlog(D_GENERAL, "No hosts to notify; exiting");
527                 return 0;
528         }
529
530         nsm_state = nsm_get_state(opt_update_state);
531         if (nsm_state == 0)
532                 exit(1);
533         nsm_update_kernel_state(nsm_state);
534
535         if (!opt_debug) {
536                 xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
537
538                 if (daemon(0, 0) < 0) {
539                         xlog(L_ERROR, "unable to background: %m");
540                         exit(1);
541                 }
542
543                 close(0);
544                 close(1);
545                 close(2);
546         }
547
548         sock = smn_create_socket(opt_srcaddr, opt_srcport);
549         if (sock == -1)
550                 exit(1);
551
552         if (!nsm_drop_privileges(-1))
553                 exit(1);
554
555         notify(sock);
556
557         if (hosts) {
558                 struct nsm_host *hp;
559
560                 while ((hp = hosts) != 0) {
561                         hosts = hp->next;
562                         xlog(L_NOTICE, "Unable to notify %s, giving up",
563                                 hp->name);
564                 }
565                 exit(1);
566         }
567
568         exit(0);
569 }
570
571 /*
572  * Notify hosts
573  */
574 static void
575 notify(const int sock)
576 {
577         time_t  failtime = 0;
578
579         if (opt_max_retry)
580                 failtime = time(NULL) + opt_max_retry;
581
582         while (hosts) {
583                 struct pollfd   pfd;
584                 time_t          now = time(NULL);
585                 unsigned int    sent = 0;
586                 struct nsm_host *hp;
587                 long            wait;
588
589                 if (failtime && now >= failtime)
590                         break;
591
592                 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
593                         /* Never send more than 10 packets at once */
594                         if (sent++ >= 10)
595                                 break;
596
597                         /* Remove queue head */
598                         hp = hosts;
599                         hosts = hp->next;
600
601                         if (notify_host(sock, hp))
602                                 continue;
603
604                         /* Set the timeout for this call, using an
605                            exponential timeout strategy */
606                         wait = hp->timeout;
607                         if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
608                                 hp->timeout = NSM_MAX_TIMEOUT;
609                         hp->send_next = now + wait;
610                         hp->retries++;
611
612                         insert_host(hp);
613                 }
614                 if (hosts == NULL)
615                         return;
616
617                 xlog(D_GENERAL, "Host %s due in %ld seconds",
618                                 hosts->name, wait);
619
620                 pfd.fd = sock;
621                 pfd.events = POLLIN;
622
623                 wait *= 1000;
624                 if (wait < 100)
625                         wait = 100;
626                 if (poll(&pfd, 1, wait) != 1)
627                         continue;
628
629                 recv_reply(sock);
630         }
631 }
632
633 /*
634  * Send notification to a single host
635  */
636 static int
637 notify_host(int sock, struct nsm_host *host)
638 {
639         const char *my_name = (opt_srcaddr != NULL ?
640                                         nsm_hostname : host->my_name);
641         struct sockaddr *sap;
642         socklen_t salen;
643
644         if (host->ai == NULL) {
645                 host->ai = smn_lookup(host->name);
646                 if (host->ai == NULL) {
647                         xlog_warn("DNS resolution of %s failed; "
648                                 "retrying later", host->name);
649                         return 0;
650                 }
651         }
652
653         /* If we retransmitted 4 times, reset the port to force
654          * a new portmap lookup (in case statd was restarted).
655          * We also rotate through multiple IP addresses at this
656          * point.
657          */
658         if (host->retries >= 4) {
659                 /* don't rotate if there is only one addrinfo */
660                 if (host->ai->ai_next != NULL) {
661                         struct addrinfo *first = host->ai;
662                         struct addrinfo **next = &host->ai;
663
664                         /* remove the first entry from the list */
665                         host->ai = first->ai_next;
666                         first->ai_next = NULL;
667                         /* find the end of the list */
668                         next = &first->ai_next;
669                         while ( *next )
670                                 next = & (*next)->ai_next;
671                         /* put first entry at end */
672                         *next = first;
673                 }
674
675                 nfs_set_port(host->ai->ai_addr, 0);
676                 host->retries = 0;
677         }
678
679         sap = host->ai->ai_addr;
680         salen = host->ai->ai_addrlen;
681
682         if (nfs_get_port(sap) == 0)
683                 host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
684         else
685                 host->xid = nsm_xmit_notify(sock, sap, salen,
686                                         SM_PROG, my_name, nsm_state);
687
688         return 0;
689 }
690
691 /*
692  * Extract the returned port number and set up the SM_NOTIFY call.
693  */
694 static void
695 recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
696 {
697         uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
698
699         host->send_next = time(NULL);
700         host->xid = 0;
701
702         if (port == 0) {
703                 /* No binding for statd... */
704                 xlog(D_GENERAL, "No statd on host %s", host->name);
705                 host->timeout = NSM_MAX_TIMEOUT;
706                 host->send_next += NSM_MAX_TIMEOUT;
707         } else {
708                 nfs_set_port(sap, port);
709                 if (host->timeout >= NSM_MAX_TIMEOUT / 4)
710                         host->timeout = NSM_MAX_TIMEOUT / 4;
711         }
712
713         insert_host(host);
714 }
715
716 /*
717  * Successful NOTIFY call. Server returns void.
718  *
719  * Try sending another SM_NOTIFY with an unqualified "my_name"
720  * argument.  Reuse the port number.  If "my_name" is already
721  * unqualified, we're done.
722  */
723 static void
724 recv_notify_reply(struct nsm_host *host)
725 {
726         char *dot = strchr(host->my_name, '.');
727
728         if (dot != NULL) {
729                 *dot = '\0';
730                 host->send_next = time(NULL);
731                 host->xid = 0;
732                 if (host->timeout >= NSM_MAX_TIMEOUT / 4)
733                         host->timeout = NSM_MAX_TIMEOUT / 4;
734                 insert_host(host);
735         } else {
736                 xlog(D_GENERAL, "Host %s notified successfully", host->name);
737                 smn_forget_host(host);
738         }
739 }
740
741 /*
742  * Receive reply from remote host
743  */
744 static void
745 recv_reply(int sock)
746 {
747         struct nsm_host *hp;
748         struct sockaddr *sap;
749         char msgbuf[NSM_MAXMSGSIZE];
750         uint32_t        xid;
751         ssize_t         msglen;
752         XDR             xdr;
753
754         memset(msgbuf, 0 , sizeof(msgbuf));
755         msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
756         if (msglen < 0)
757                 return;
758
759         xlog(D_GENERAL, "Received packet...");
760
761         memset(&xdr, 0, sizeof(xdr));
762         xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
763         xid = nsm_parse_reply(&xdr);
764         if (xid == 0)
765                 goto out;
766
767         /* Before we look at the data, find the host struct for
768            this reply */
769         if ((hp = find_host(xid)) == NULL)
770                 goto out;
771
772         sap = hp->ai->ai_addr;
773         if (nfs_get_port(sap) == 0)
774                 recv_rpcbind_reply(sap, hp, &xdr);
775         else
776                 recv_notify_reply(hp);
777
778 out:
779         xdr_destroy(&xdr);
780 }
781
782 /*
783  * Insert host into sorted list
784  */
785 static void
786 insert_host(struct nsm_host *host)
787 {
788         struct nsm_host **where, *p;
789
790         where = &hosts;
791         while ((p = *where) != 0) {
792                 /* Sort in ascending order of timeout */
793                 if (host->send_next < p->send_next)
794                         break;
795                 /* If we have the same timeout, put the
796                  * most recently used host first.
797                  * This makes sure that "recent" hosts
798                  * get notified first.
799                  */
800                 if (host->send_next == p->send_next
801                  && host->last_used > p->last_used)
802                         break;
803                 where = &p->next;
804         }
805
806         host->next = *where;
807         *where = host;
808 }
809
810 /*
811  * Find host given the XID
812  */
813 static struct nsm_host *
814 find_host(uint32_t xid)
815 {
816         struct nsm_host **where, *p;
817
818         where = &hosts;
819         while ((p = *where) != 0) {
820                 if (p->xid == xid) {
821                         *where = p->next;
822                         return p;
823                 }
824                 where = &p->next;
825         }
826         return NULL;
827 }
828
829 /*
830  * Record pid in /var/run/sm-notify.pid
831  * This file should remain until a reboot, even if the
832  * program exits.
833  * If file already exists, fail.
834  */
835 static int record_pid(void)
836 {
837         char pid[20];
838         ssize_t len;
839         int fd;
840
841         (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid());
842         fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
843         if (fd < 0)
844                 return 0;
845
846         len = write(fd, pid, strlen(pid));
847         if ((len < 0) || ((size_t)len != strlen(pid))) {
848                 xlog_warn("Writing to pid file failed: errno %d (%m)",
849                                 errno);
850         }
851
852         (void)close(fd);
853         return 1;
854 }