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