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