]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/sm-notify.c
sm-notify: Refactor insert_host() and recv_rpcbind_reply()
[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         return 1;
256 }
257
258 #ifdef IPV6_SUPPORTED
259 static int smn_socket(void)
260 {
261         int sock;
262
263         /*
264          * Use an AF_INET socket if IPv6 is disabled on the
265          * local system.
266          */
267         sock = socket(AF_INET6, SOCK_DGRAM, 0);
268         if (sock == -1) {
269                 if (errno != EAFNOSUPPORT) {
270                         xlog(L_ERROR, "Failed to create RPC socket: %m");
271                         return -1;
272                 }
273                 sock = socket(AF_INET, SOCK_DGRAM, 0);
274                 if (sock < 0) {
275                         xlog(L_ERROR, "Failed to create RPC socket: %m");
276                         return -1;
277                 }
278         } else
279                 nsm_family = AF_INET6;
280
281         if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
282                 xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
283                 goto out_close;
284         }
285
286         /*
287          * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4.  However,
288          * since sm-notify open-codes all of its RPC support, it can
289          * use a single socket and let the local network stack provide
290          * the correct mapping between address families automatically.
291          * This is the same thing that is done in the kernel.
292          */
293         if (nsm_family == AF_INET6) {
294                 const int zero = 0;
295                 socklen_t zerolen = (socklen_t)sizeof(zero);
296
297                 if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
298                                         (char *)&zero, zerolen) == -1) {
299                         xlog(L_ERROR, "setsockopt(3) on RPC socket failed: %m");
300                         goto out_close;
301                 }
302         }
303
304         return sock;
305
306 out_close:
307         (void)close(sock);
308         return -1;
309 }
310 #else   /* !IPV6_SUPPORTED */
311 static int smn_socket(void)
312 {
313         int sock;
314
315         sock = socket(AF_INET, SOCK_DGRAM, 0);
316         if (sock == -1) {
317                 xlog(L_ERROR, "Failed to create RPC socket: %m");
318                 return -1;
319         }
320
321         if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
322                 xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
323                 (void)close(sock);
324                 return -1;
325         }
326
327         return sock;
328 }
329 #endif  /* !IPV6_SUPPORTED */
330
331 /*
332  * If admin specified a source address or srcport, then convert those
333  * to a sockaddr and return it.   Otherwise, return an ANYADDR address.
334  */
335 __attribute_malloc__
336 static struct addrinfo *
337 smn_bind_address(const char *srcaddr, const char *srcport)
338 {
339         struct addrinfo *ai = NULL;
340         struct addrinfo hint = {
341                 .ai_flags       = AI_NUMERICSERV,
342                 .ai_family      = nsm_family,
343                 .ai_protocol    = (int)IPPROTO_UDP,
344         };
345         int error;
346
347         if (srcaddr == NULL)
348                 hint.ai_flags |= AI_PASSIVE;
349
350         /* Do not allow "node" and "service" parameters both to be NULL */
351         if (srcport == NULL)
352                 error = getaddrinfo(srcaddr, "", &hint, &ai);
353         else
354                 error = getaddrinfo(srcaddr, srcport, &hint, &ai);
355         if (error != 0) {
356                 xlog(L_ERROR,
357                         "Invalid bind address or port for RPC socket: %s",
358                                 gai_strerror(error));
359                 return NULL;
360         }
361
362         return ai;
363 }
364
365 #ifdef HAVE_LIBTIRPC
366 static int
367 smn_bindresvport(int sock, struct sockaddr *sap)
368 {
369         return bindresvport_sa(sock, sap);
370 }
371
372 #else   /* !HAVE_LIBTIRPC */
373 static int
374 smn_bindresvport(int sock, struct sockaddr *sap)
375 {
376         if (sap->sa_family != AF_INET) {
377                 errno = EAFNOSUPPORT;
378                 return -1;
379         }
380
381         return bindresvport(sock, (struct sockaddr_in *)(char *)sap);
382 }
383 #endif  /* !HAVE_LIBTIRPC */
384
385 /*
386  * Prepare a socket for sending RPC requests
387  *
388  * Returns a bound datagram socket file descriptor, or -1 if
389  * an error occurs.
390  */
391 static int
392 smn_create_socket(const char *srcaddr, const char *srcport)
393 {
394         int sock, retry_cnt = 0;
395         struct addrinfo *ai;
396
397 retry:
398         sock = smn_socket();
399         if (sock == -1)
400                 return -1;
401
402         ai = smn_bind_address(srcaddr, srcport);
403         if (ai == NULL) {
404                 (void)close(sock);
405                 return -1;
406         }
407
408         /* Use source port if provided on the command line,
409          * otherwise use bindresvport */
410         if (srcport) {
411                 if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
412                         xlog(L_ERROR, "Failed to bind RPC socket: %m");
413                         freeaddrinfo(ai);
414                         (void)close(sock);
415                         return -1;
416                 }
417         } else {
418                 struct servent *se;
419
420                 if (smn_bindresvport(sock, ai->ai_addr) == -1) {
421                         xlog(L_ERROR,
422                                 "bindresvport on RPC socket failed: %m");
423                         freeaddrinfo(ai);
424                         (void)close(sock);
425                         return -1;
426                 }
427
428                 /* try to avoid known ports */
429                 se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp");
430                 if (se != NULL && retry_cnt < 100) {
431                         retry_cnt++;
432                         freeaddrinfo(ai);
433                         (void)close(sock);
434                         goto retry;
435                 }
436         }
437
438         freeaddrinfo(ai);
439         return sock;
440 }
441
442 int
443 main(int argc, char **argv)
444 {
445         int     c, sock, force = 0;
446         char *  progname;
447
448         progname = strrchr(argv[0], '/');
449         if (progname != NULL)
450                 progname++;
451         else
452                 progname = argv[0];
453
454         while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
455                 switch (c) {
456                 case 'f':
457                         force = 1;
458                         break;
459                 case 'd':
460                         opt_debug++;
461                         break;
462                 case 'm':
463                         opt_max_retry = atoi(optarg) * 60;
464                         break;
465                 case 'n':
466                         opt_update_state = false;
467                         break;
468                 case 'p':
469                         opt_srcport = optarg;
470                         break;
471                 case 'v':
472                         opt_srcaddr = optarg;
473                         break;
474                 case 'P':
475                         if (!nsm_setup_pathnames(argv[0], optarg))
476                                 exit(1);
477                         break;
478
479                 default:
480                         goto usage;
481                 }
482         }
483
484         if (optind < argc) {
485 usage:          fprintf(stderr,
486                         "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
487                         "            [-P /path/to/state/directory] [-v my_host_name]\n",
488                         progname);
489                 exit(1);
490         }
491
492         if (opt_debug) {
493                 xlog_syslog(0);
494                 xlog_stderr(1);
495                 xlog_config(D_ALL, 1);
496         } else {
497                 xlog_syslog(1);
498                 xlog_stderr(0);
499         }
500
501         xlog_open(progname);
502         xlog(L_NOTICE, "Version " VERSION " starting");
503
504         if (nsm_is_default_parentdir()) {
505                 if (record_pid() == 0 && force == 0 && opt_update_state) {
506                         /* already run, don't try again */
507                         xlog(L_NOTICE, "Already notifying clients; Exiting!");
508                         exit(0);
509                 }
510         }
511
512         if (opt_srcaddr != NULL) {
513                 char *name;
514
515                 name = smn_verify_my_name(opt_srcaddr);
516                 if (name == NULL)
517                         exit(1);
518
519                 strncpy(nsm_hostname, name, sizeof(nsm_hostname));
520                 free(name);
521         }
522
523         (void)nsm_retire_monitored_hosts();
524         if (nsm_load_notify_list(smn_get_host) == 0) {
525                 xlog(D_GENERAL, "No hosts to notify; exiting");
526                 return 0;
527         }
528
529         nsm_state = nsm_get_state(opt_update_state);
530         if (nsm_state == 0)
531                 exit(1);
532         nsm_update_kernel_state(nsm_state);
533
534         if (!opt_debug) {
535                 xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
536
537                 if (daemon(0, 0) < 0) {
538                         xlog(L_ERROR, "unable to background: %m");
539                         exit(1);
540                 }
541
542                 close(0);
543                 close(1);
544                 close(2);
545         }
546
547         sock = smn_create_socket(opt_srcaddr, opt_srcport);
548         if (sock == -1)
549                 exit(1);
550
551         if (!nsm_drop_privileges(-1))
552                 exit(1);
553
554         notify(sock);
555
556         if (hosts) {
557                 struct nsm_host *hp;
558
559                 while ((hp = hosts) != 0) {
560                         hosts = hp->next;
561                         xlog(L_NOTICE, "Unable to notify %s, giving up",
562                                 hp->name);
563                 }
564                 exit(1);
565         }
566
567         exit(0);
568 }
569
570 /*
571  * Notify hosts
572  */
573 static void
574 notify(const int sock)
575 {
576         time_t  failtime = 0;
577
578         if (opt_max_retry)
579                 failtime = time(NULL) + opt_max_retry;
580
581         while (hosts) {
582                 struct pollfd   pfd;
583                 time_t          now = time(NULL);
584                 unsigned int    sent = 0;
585                 struct nsm_host *hp;
586                 long            wait;
587
588                 if (failtime && now >= failtime)
589                         break;
590
591                 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
592                         /* Never send more than 10 packets at once */
593                         if (sent++ >= 10)
594                                 break;
595
596                         /* Remove queue head */
597                         hp = hosts;
598                         hosts = hp->next;
599
600                         if (notify_host(sock, hp))
601                                 continue;
602
603                         /* Set the timeout for this call, using an
604                            exponential timeout strategy */
605                         wait = hp->timeout;
606                         if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
607                                 hp->timeout = NSM_MAX_TIMEOUT;
608                         hp->send_next = now + wait;
609                         hp->retries++;
610
611                         insert_host(hp);
612                 }
613                 if (hosts == NULL)
614                         return;
615
616                 xlog(D_GENERAL, "Host %s due in %ld seconds",
617                                 hosts->name, wait);
618
619                 pfd.fd = sock;
620                 pfd.events = POLLIN;
621
622                 wait *= 1000;
623                 if (wait < 100)
624                         wait = 100;
625                 if (poll(&pfd, 1, wait) != 1)
626                         continue;
627
628                 recv_reply(sock);
629         }
630 }
631
632 /*
633  * Send notification to a single host
634  */
635 static int
636 notify_host(int sock, struct nsm_host *host)
637 {
638         const char *my_name = (opt_srcaddr != NULL ?
639                                         nsm_hostname : host->my_name);
640         struct sockaddr *sap;
641         socklen_t salen;
642
643         if (host->ai == NULL) {
644                 host->ai = smn_lookup(host->name);
645                 if (host->ai == NULL) {
646                         xlog_warn("DNS resolution of %s failed; "
647                                 "retrying later", host->name);
648                         return 0;
649                 }
650         }
651
652         /* If we retransmitted 4 times, reset the port to force
653          * a new portmap lookup (in case statd was restarted).
654          * We also rotate through multiple IP addresses at this
655          * point.
656          */
657         if (host->retries >= 4) {
658                 /* don't rotate if there is only one addrinfo */
659                 if (host->ai->ai_next != NULL) {
660                         struct addrinfo *first = host->ai;
661                         struct addrinfo **next = &host->ai;
662
663                         /* remove the first entry from the list */
664                         host->ai = first->ai_next;
665                         first->ai_next = NULL;
666                         /* find the end of the list */
667                         next = &first->ai_next;
668                         while ( *next )
669                                 next = & (*next)->ai_next;
670                         /* put first entry at end */
671                         *next = first;
672                 }
673
674                 nfs_set_port(host->ai->ai_addr, 0);
675                 host->retries = 0;
676         }
677
678         sap = host->ai->ai_addr;
679         salen = host->ai->ai_addrlen;
680
681         if (nfs_get_port(sap) == 0)
682                 host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
683         else
684                 host->xid = nsm_xmit_notify(sock, sap, salen,
685                                         SM_PROG, my_name, nsm_state);
686
687         return 0;
688 }
689
690 static void
691 smn_defer(struct nsm_host *host)
692 {
693         host->xid = 0;
694         host->send_next = time(NULL) + NSM_MAX_TIMEOUT;
695         host->timeout = NSM_MAX_TIMEOUT;
696         insert_host(host);
697 }
698
699 static void
700 smn_schedule(struct nsm_host *host)
701 {
702         host->xid = 0;
703         host->send_next = time(NULL);
704         if (host->timeout >= NSM_MAX_TIMEOUT / 4)
705                 host->timeout = NSM_MAX_TIMEOUT / 4;
706         insert_host(host);
707 }
708
709 /*
710  * Extract the returned port number and set up the SM_NOTIFY call.
711  */
712 static void
713 recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
714 {
715         uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
716
717         if (port == 0) {
718                 /* No binding for statd... */
719                 xlog(D_GENERAL, "No statd on host %s", host->name);
720                 smn_defer(host);
721         } else {
722                 xlog(D_GENERAL, "Processing rpcbind reply for %s (port %u)",
723                         host->name, port);
724                 nfs_set_port(sap, port);
725                 smn_schedule(host);
726         }
727 }
728
729 /*
730  * Successful NOTIFY call. Server returns void.
731  *
732  * Try sending another SM_NOTIFY with an unqualified "my_name"
733  * argument.  Reuse the port number.  If "my_name" is already
734  * unqualified, we're done.
735  */
736 static void
737 recv_notify_reply(struct nsm_host *host)
738 {
739         char *dot = strchr(host->my_name, '.');
740
741         if (dot != NULL) {
742                 *dot = '\0';
743                 smn_schedule(host);
744         } else {
745                 xlog(D_GENERAL, "Host %s notified successfully", host->name);
746                 smn_forget_host(host);
747         }
748 }
749
750 /*
751  * Receive reply from remote host
752  */
753 static void
754 recv_reply(int sock)
755 {
756         struct nsm_host *hp;
757         struct sockaddr *sap;
758         char msgbuf[NSM_MAXMSGSIZE];
759         uint32_t        xid;
760         ssize_t         msglen;
761         XDR             xdr;
762
763         memset(msgbuf, 0 , sizeof(msgbuf));
764         msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
765         if (msglen < 0)
766                 return;
767
768         xlog(D_GENERAL, "Received packet...");
769
770         memset(&xdr, 0, sizeof(xdr));
771         xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
772         xid = nsm_parse_reply(&xdr);
773         if (xid == 0)
774                 goto out;
775
776         /* Before we look at the data, find the host struct for
777            this reply */
778         if ((hp = find_host(xid)) == NULL)
779                 goto out;
780
781         sap = hp->ai->ai_addr;
782         if (nfs_get_port(sap) == 0)
783                 recv_rpcbind_reply(sap, hp, &xdr);
784         else
785                 recv_notify_reply(hp);
786
787 out:
788         xdr_destroy(&xdr);
789 }
790
791 /*
792  * Insert host into notification list, sorted by next send time
793  */
794 static void
795 insert_host(struct nsm_host *host)
796 {
797         struct nsm_host **where, *p;
798
799         where = &hosts;
800         while ((p = *where) != 0) {
801                 /* Sort in ascending order of timeout */
802                 if (host->send_next < p->send_next)
803                         break;
804                 /* If we have the same timeout, put the
805                  * most recently used host first.
806                  * This makes sure that "recent" hosts
807                  * get notified first.
808                  */
809                 if (host->send_next == p->send_next
810                  && host->last_used > p->last_used)
811                         break;
812                 where = &p->next;
813         }
814
815         host->next = *where;
816         *where = host;
817         xlog(D_GENERAL, "Added host %s to notify list", host->name);
818 }
819
820 /*
821  * Find host given the XID
822  */
823 static struct nsm_host *
824 find_host(uint32_t xid)
825 {
826         struct nsm_host **where, *p;
827
828         where = &hosts;
829         while ((p = *where) != 0) {
830                 if (p->xid == xid) {
831                         *where = p->next;
832                         return p;
833                 }
834                 where = &p->next;
835         }
836         return NULL;
837 }
838
839 /*
840  * Record pid in /var/run/sm-notify.pid
841  * This file should remain until a reboot, even if the
842  * program exits.
843  * If file already exists, fail.
844  */
845 static int record_pid(void)
846 {
847         char pid[20];
848         ssize_t len;
849         int fd;
850
851         (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid());
852         fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
853         if (fd < 0)
854                 return 0;
855
856         len = write(fd, pid, strlen(pid));
857         if ((len < 0) || ((size_t)len != strlen(pid))) {
858                 xlog_warn("Writing to pid file failed: errno %d (%m)",
859                                 errno);
860         }
861
862         (void)close(fd);
863         return 1;
864 }