1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 /*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39 /*
40 * Copyright (c) 2012 by Delphix. All rights reserved.
41 */
42
43 /*
44 * sm_statd.c consists of routines used for the intermediate
45 * statd implementation(3.2 rpc.statd);
46 * it creates an entry in "current" directory for each site that it monitors;
47 * after crash and recovery, it moves all entries in "current"
48 * to "backup" directory, and notifies the corresponding statd of its recovery.
49 */
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <string.h>
55 #include <syslog.h>
56 #include <netdb.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <sys/file.h>
60 #include <sys/param.h>
61 #include <arpa/inet.h>
62 #include <dirent.h>
63 #include <rpc/rpc.h>
64 #include <rpcsvc/sm_inter.h>
65 #include <rpcsvc/nsm_addr.h>
66 #include <errno.h>
67 #include <memory.h>
68 #include <signal.h>
69 #include <synch.h>
70 #include <thread.h>
71 #include <limits.h>
72 #include <strings.h>
73 #include "sm_statd.h"
74
75
76 int LOCAL_STATE;
77
78 sm_hash_t mon_table[MAX_HASHSIZE];
79 static sm_hash_t record_table[MAX_HASHSIZE];
80 static sm_hash_t recov_q;
81
82 static name_entry *find_name(name_entry **namepp, char *name);
83 static name_entry *insert_name(name_entry **namepp, char *name,
84 int need_alloc);
85 static void delete_name(name_entry **namepp, char *name);
86 static void remove_name(char *name, int op, int startup);
87 static int statd_call_statd(char *name);
88 static void pr_name(char *name, int flag);
89 static void *thr_statd_init();
90 static void *sm_try();
91 static void *thr_call_statd(void *);
92 static void remove_single_name(char *name, char *dir1, char *dir2);
93 static int move_file(char *fromdir, char *file, char *todir);
94 static int count_symlinks(char *dir, char *name, int *count);
95 static char *family2string(sa_family_t family);
96
97 /*
98 * called when statd first comes up; it searches /etc/sm to gather
99 * all entries to notify its own failure
100 */
101 void
102 statd_init()
103 {
104 struct dirent *dirp;
105 DIR *dp;
106 FILE *fp, *fp_tmp;
107 int i, tmp_state;
108 char state_file[MAXPATHLEN+SM_MAXPATHLEN];
109
110 if (debug)
111 (void) printf("enter statd_init\n");
112
113 /*
114 * First try to open the file. If that fails, try to create it.
115 * If that fails, give up.
116 */
117 if ((fp = fopen(STATE, "r+")) == (FILE *)NULL)
118 if ((fp = fopen(STATE, "w+")) == (FILE *)NULL) {
119 syslog(LOG_ERR, "can't open %s: %m", STATE);
120 exit(1);
121 } else
122 (void) chmod(STATE, 0644);
123 if ((fscanf(fp, "%d", &LOCAL_STATE)) == EOF) {
124 if (debug >= 2)
125 (void) printf("empty file\n");
126 LOCAL_STATE = 0;
127 }
128
129 /*
130 * Scan alternate paths for largest "state" number
131 */
132 for (i = 0; i < pathix; i++) {
133 (void) sprintf(state_file, "%s/statmon/state", path_name[i]);
134 if ((fp_tmp = fopen(state_file, "r+")) == (FILE *)NULL) {
135 if ((fp_tmp = fopen(state_file, "w+")) ==
136 (FILE *)NULL) {
137 if (debug)
138 syslog(LOG_ERR,
139 "can't open %s: %m",
140 state_file);
141 continue;
142 } else
143 (void) chmod(state_file, 0644);
144 }
145 if ((fscanf(fp_tmp, "%d", &tmp_state)) == EOF) {
146 if (debug)
147 syslog(LOG_ERR,
148 "statd: %s: file empty\n", state_file);
149 (void) fclose(fp_tmp);
150 continue;
151 }
152 if (tmp_state > LOCAL_STATE) {
153 LOCAL_STATE = tmp_state;
154 if (debug)
155 (void) printf("Update LOCAL STATE: %d\n",
156 tmp_state);
157 }
158 (void) fclose(fp_tmp);
159 }
160
161 LOCAL_STATE = ((LOCAL_STATE%2) == 0) ? LOCAL_STATE+1 : LOCAL_STATE+2;
162
163 /* IF local state overflows, reset to value 1 */
164 if (LOCAL_STATE < 0) {
165 LOCAL_STATE = 1;
166 }
167
168 /* Copy the LOCAL_STATE value back to all stat files */
169 if (fseek(fp, 0, 0) == -1) {
170 syslog(LOG_ERR, "statd: fseek failed\n");
171 exit(1);
172 }
173
174 (void) fprintf(fp, "%-10d", LOCAL_STATE);
175 (void) fflush(fp);
176 if (fsync(fileno(fp)) == -1) {
177 syslog(LOG_ERR, "statd: fsync failed\n");
178 exit(1);
179 }
180 (void) fclose(fp);
181
182 for (i = 0; i < pathix; i++) {
183 (void) sprintf(state_file, "%s/statmon/state", path_name[i]);
184 if ((fp_tmp = fopen(state_file, "r+")) == (FILE *)NULL) {
185 if ((fp_tmp = fopen(state_file, "w+")) ==
186 (FILE *)NULL) {
187 syslog(LOG_ERR,
188 "can't open %s: %m", state_file);
189 continue;
190 } else
191 (void) chmod(state_file, 0644);
192 }
193 (void) fprintf(fp_tmp, "%-10d", LOCAL_STATE);
194 (void) fflush(fp_tmp);
195 if (fsync(fileno(fp_tmp)) == -1) {
196 syslog(LOG_ERR,
197 "statd: %s: fsync failed\n", state_file);
198 (void) fclose(fp_tmp);
199 exit(1);
200 }
201 (void) fclose(fp_tmp);
202 }
203
204 if (debug)
205 (void) printf("local state = %d\n", LOCAL_STATE);
206
207 if ((mkdir(CURRENT, SM_DIRECTORY_MODE)) == -1) {
208 if (errno != EEXIST) {
209 syslog(LOG_ERR, "statd: mkdir current, error %m\n");
210 exit(1);
211 }
212 }
213 if ((mkdir(BACKUP, SM_DIRECTORY_MODE)) == -1) {
214 if (errno != EEXIST) {
215 syslog(LOG_ERR, "statd: mkdir backup, error %m\n");
216 exit(1);
217 }
218 }
219
220 /* get all entries in CURRENT into BACKUP */
221 if ((dp = opendir(CURRENT)) == (DIR *)NULL) {
222 syslog(LOG_ERR, "statd: open current directory, error %m\n");
223 exit(1);
224 }
225
226 while ((dirp = readdir(dp)) != NULL) {
227 if (strcmp(dirp->d_name, ".") != 0 &&
228 strcmp(dirp->d_name, "..") != 0) {
229 /* rename all entries from CURRENT to BACKUP */
230 (void) move_file(CURRENT, dirp->d_name, BACKUP);
231 }
232 }
233
234 (void) closedir(dp);
235
236 /* Contact hosts' statd */
237 if (thr_create(NULL, NULL, thr_statd_init, NULL, THR_DETACHED, 0)) {
238 syslog(LOG_ERR,
239 "statd: unable to create thread for thr_statd_init\n");
240 exit(1);
241 }
242 }
243
244 /*
245 * Work thread which contacts hosts' statd.
246 */
247 void *
248 thr_statd_init()
249 {
250 struct dirent *dirp;
251 DIR *dp;
252 int num_threads;
253 int num_join;
254 int i;
255 char *name;
256 char buf[MAXPATHLEN+SM_MAXPATHLEN];
257
258 /* Go thru backup directory and contact hosts */
259 if ((dp = opendir(BACKUP)) == (DIR *)NULL) {
260 syslog(LOG_ERR, "statd: open backup directory, error %m\n");
261 exit(1);
262 }
263
264 /*
265 * Create "UNDETACHED" threads for each symlink and (unlinked)
266 * regular file in backup directory to initiate statd_call_statd.
267 * NOTE: These threads are the only undetached threads in this
268 * program and thus, the thread id is not needed to join the threads.
269 */
270 num_threads = 0;
271 while ((dirp = readdir(dp)) != NULL) {
272 /*
273 * If host file is not a symlink, don't bother to
274 * spawn a thread for it. If any link(s) refer to
275 * it, the host will be contacted using the link(s).
276 * If not, we'll deal with it during the legacy pass.
277 */
278 (void) sprintf(buf, "%s/%s", BACKUP, dirp->d_name);
279 if (is_symlink(buf) == 0) {
280 continue;
281 }
282
283 /*
284 * If the num_threads has exceeded, wait until
285 * a certain amount of threads have finished.
286 * Currently, 10% of threads created should be joined.
287 */
288 if (num_threads > MAX_THR) {
289 num_join = num_threads/PERCENT_MINJOIN;
290 for (i = 0; i < num_join; i++)
291 thr_join(0, 0, 0);
292 num_threads -= num_join;
293 }
294
295 /*
296 * If can't alloc name then print error msg and
297 * continue to next item on list.
298 */
299 name = strdup(dirp->d_name);
300 if (name == (char *)NULL) {
301 syslog(LOG_ERR,
302 "statd: unable to allocate space for name %s\n",
303 dirp->d_name);
304 continue;
305 }
306
307 /* Create a thread to do a statd_call_statd for name */
308 if (thr_create(NULL, NULL, thr_call_statd,
309 (void *) name, 0, 0)) {
310 syslog(LOG_ERR,
311 "statd: unable to create thr_call_statd() "
312 "for name %s.\n", dirp->d_name);
313 free(name);
314 continue;
315 }
316 num_threads++;
317 }
318
319 /*
320 * Join the other threads created above before processing the
321 * legacies. This allows all symlinks and the regular files
322 * to which they correspond to be processed and deleted.
323 */
324 for (i = 0; i < num_threads; i++) {
325 thr_join(0, 0, 0);
326 }
327
328 /*
329 * The second pass checks for `legacies': regular files which
330 * never had symlinks pointing to them at all, just like in the
331 * good old (pre-1184192 fix) days. Once a machine has cleaned
332 * up its legacies they should only reoccur due to catastrophes
333 * (e.g., severed symlinks).
334 */
335 rewinddir(dp);
336 num_threads = 0;
337 while ((dirp = readdir(dp)) != NULL) {
338 if (strcmp(dirp->d_name, ".") == 0 ||
339 strcmp(dirp->d_name, "..") == 0) {
340 continue;
341 }
342
343 (void) sprintf(buf, "%s/%s", BACKUP, dirp->d_name);
344 if (is_symlink(buf)) {
345 /*
346 * We probably couldn't reach this host and it's
347 * been put on the recovery queue for retry.
348 * Skip it and keep looking for regular files.
349 */
350 continue;
351 }
352
353 if (debug) {
354 (void) printf("thr_statd_init: legacy %s\n",
355 dirp->d_name);
356 }
357
358 /*
359 * If the number of threads exceeds the maximum, wait
360 * for some fraction of them to finish before
361 * continuing.
362 */
363 if (num_threads > MAX_THR) {
364 num_join = num_threads/PERCENT_MINJOIN;
365 for (i = 0; i < num_join; i++)
366 thr_join(0, 0, 0);
367 num_threads -= num_join;
368 }
369
370 /*
371 * If can't alloc name then print error msg and
372 * continue to next item on list.
373 */
374 name = strdup(dirp->d_name);
375 if (name == (char *)NULL) {
376 syslog(LOG_ERR,
377 "statd: unable to allocate space for name %s\n",
378 dirp->d_name);
379 continue;
380 }
381
382 /* Create a thread to do a statd_call_statd for name */
383 if (thr_create(NULL, NULL, thr_call_statd,
384 (void *) name, 0, 0)) {
385 syslog(LOG_ERR,
386 "statd: unable to create thr_call_statd() "
387 "for name %s.\n", dirp->d_name);
388 free(name);
389 continue;
390 }
391 num_threads++;
392 }
393
394 (void) closedir(dp);
395
396 /*
397 * Join the other threads created above before creating thread
398 * to process items in recovery table.
399 */
400 for (i = 0; i < num_threads; i++) {
401 thr_join(0, 0, 0);
402 }
403
404 /*
405 * Need to only copy /var/statmon/sm.bak to alternate paths, since
406 * the only hosts in /var/statmon/sm should be the ones currently
407 * being monitored and already should be in alternate paths as part
408 * of insert_mon().
409 */
410 for (i = 0; i < pathix; i++) {
411 (void) sprintf(buf, "%s/statmon/sm.bak", path_name[i]);
412 if ((mkdir(buf, SM_DIRECTORY_MODE)) == -1) {
413 if (errno != EEXIST)
414 syslog(LOG_ERR, "statd: mkdir %s error %m\n",
415 buf);
416 else
417 copydir_from_to(BACKUP, buf);
418 } else
419 copydir_from_to(BACKUP, buf);
420 }
421
422
423 /*
424 * Reset the die and in_crash variable and signal other threads
425 * that have issued an sm_crash and are waiting.
426 */
427 mutex_lock(&crash_lock);
428 die = 0;
429 in_crash = 0;
430 mutex_unlock(&crash_lock);
431 cond_broadcast(&crash_finish);
432
433 if (debug)
434 (void) printf("Creating thread for sm_try\n");
435
436 /* Continue to notify statd on hosts that were unreachable. */
437 if (thr_create(NULL, NULL, sm_try, NULL, THR_DETACHED, 0))
438 syslog(LOG_ERR,
439 "statd: unable to create thread for sm_try().\n");
440 thr_exit((void *) 0);
441 #ifdef lint
442 return (0);
443 #endif
444 }
445
446 /*
447 * Work thread to make call to statd_call_statd.
448 */
449 void *
450 thr_call_statd(void *namep)
451 {
452 char *name = (char *)namep;
453
454 /*
455 * If statd of name is unreachable, add name to recovery table
456 * otherwise if statd_call_statd was successful, remove from backup.
457 */
458 if (statd_call_statd(name) != 0) {
459 int n;
460 char *tail;
461 char path[MAXPATHLEN];
462 /*
463 * since we are constructing this pathname below we add
464 * another space for the terminating NULL so we don't
465 * overflow our buffer when we do the readlink
466 */
467 char rname[MAXNAMELEN + 1];
468
469 if (debug) {
470 (void) printf(
471 "statd call failed, inserting %s in recov_q\n", name);
472 }
473 mutex_lock(&recov_q.lock);
474 (void) insert_name(&recov_q.sm_recovhdp, name, 0);
475 mutex_unlock(&recov_q.lock);
476
477 /*
478 * If we queued a symlink name in the recovery queue,
479 * we now clean up the regular file to which it referred.
480 * This may leave a severed symlink if multiple links
481 * referred to one regular file; this is unaesthetic but
482 * it works. The big benefit is that it prevents us
483 * from recovering the same host twice (as symlink and
484 * as regular file) needlessly, usually on separate reboots.
485 */
486 (void) strcpy(path, BACKUP);
487 (void) strcat(path, "/");
488 (void) strcat(path, name);
489 if (is_symlink(path)) {
490 n = readlink(path, rname, MAXNAMELEN);
491 if (n <= 0) {
492 if (debug >= 2) {
493 (void) printf(
494 "thr_call_statd: can't read "
495 "link %s\n", path);
496 }
497 } else {
498 rname[n] = '\0';
499
500 tail = strrchr(path, '/') + 1;
501
502 if ((strlen(BACKUP) + strlen(rname) + 2) <=
503 MAXPATHLEN) {
504 (void) strcpy(tail, rname);
505 delete_file(path);
506 } else if (debug) {
507 printf("thr_call_statd: path over"
508 "maxpathlen!\n");
509 }
510 }
511
512 }
513
514 if (debug)
515 pr_name(name, 0);
516
517 } else {
518 /*
519 * If `name' is an IP address symlink to a name file,
520 * remove it now. If it is the last such symlink,
521 * remove the name file as well. Regular files with
522 * no symlinks to them are assumed to be legacies and
523 * are removed as well.
524 */
525 remove_name(name, 1, 1);
526 free(name);
527 }
528 thr_exit((void *) 0);
529 #ifdef lint
530 return (0);
531 #endif
532 }
533
534 /*
535 * Notifies the statd of host specified by name to indicate that
536 * state has changed for this server.
537 */
538 static int
539 statd_call_statd(name)
540 char *name;
541 {
542 enum clnt_stat clnt_stat;
543 struct timeval tottimeout;
544 CLIENT *clnt;
545 char *name_or_addr;
546 stat_chge ntf;
547 int i;
548 int rc;
549 int dummy1, dummy2, dummy3, dummy4;
550 char ascii_addr[MAXNAMELEN];
551 size_t unq_len;
552
553 ntf.mon_name = hostname;
554 ntf.state = LOCAL_STATE;
555 if (debug)
556 (void) printf("statd_call_statd at %s\n", name);
557
558 /*
559 * If it looks like an ASCII <address family>.<address> specifier,
560 * strip off the family - we just want the address when obtaining
561 * a client handle.
562 * If it's anything else, just pass it on to create_client().
563 */
564 unq_len = strcspn(name, ".");
565
566 if ((strncmp(name, SM_ADDR_IPV4, unq_len) == 0) ||
567 (strncmp(name, SM_ADDR_IPV6, unq_len) == 0)) {
568 name_or_addr = strchr(name, '.') + 1;
569 } else {
570 name_or_addr = name;
571 }
572
573 /*
574 * NOTE: We depend here upon the fact that the RPC client code
575 * allows us to use ASCII dotted quad `names', i.e. "192.9.200.1".
576 * This may change in a future release.
577 */
578 if (debug) {
579 (void) printf("statd_call_statd: calling create_client(%s)\n",
580 name_or_addr);
581 }
582
583 tottimeout.tv_sec = SM_RPC_TIMEOUT;
584 tottimeout.tv_usec = 0;
585
586 if ((clnt = create_client(name_or_addr, SM_PROG, SM_VERS, NULL,
587 &tottimeout)) == NULL) {
588 return (-1);
589 }
590
591 /* Perform notification to client */
592 rc = 0;
593 clnt_stat = clnt_call(clnt, SM_NOTIFY, xdr_stat_chge, (char *)&ntf,
594 xdr_void, NULL, tottimeout);
595 if (debug) {
596 (void) printf("clnt_stat=%s(%d)\n",
597 clnt_sperrno(clnt_stat), clnt_stat);
598 }
599 if (clnt_stat != (int)RPC_SUCCESS) {
600 syslog(LOG_WARNING,
601 "statd: cannot talk to statd at %s, %s(%d)\n",
602 name_or_addr, clnt_sperrno(clnt_stat), clnt_stat);
603 rc = -1;
604 }
605
606 /* For HA systems and multi-homed hosts */
607 ntf.state = LOCAL_STATE;
608 for (i = 0; i < addrix; i++) {
609 ntf.mon_name = host_name[i];
610 if (debug)
611 (void) printf("statd_call_statd at %s\n", name_or_addr);
612 clnt_stat = clnt_call(clnt, SM_NOTIFY, xdr_stat_chge,
613 (char *)&ntf, xdr_void, NULL,
614 tottimeout);
615 if (clnt_stat != (int)RPC_SUCCESS) {
616 syslog(LOG_WARNING,
617 "statd: cannot talk to statd at %s, %s(%d)\n",
618 name_or_addr, clnt_sperrno(clnt_stat), clnt_stat);
619 rc = -1;
620 }
621 }
622 clnt_destroy(clnt);
623 return (rc);
624 }
625
626 /*
627 * Continues to contact hosts in recovery table that were unreachable.
628 * NOTE: There should only be one sm_try thread executing and
629 * thus locks are not needed for recovery table. Die is only cleared
630 * after all the hosts has at least been contacted once. The reader/writer
631 * lock ensures to finish this code before an sm_crash is started. Die
632 * variable will signal it.
633 */
634 void *
635 sm_try()
636 {
637 name_entry *nl, *next;
638 timestruc_t wtime;
639 int delay = 0;
640
641 rw_rdlock(&thr_rwlock);
642 if (mutex_trylock(&sm_trylock))
643 goto out;
644 mutex_lock(&crash_lock);
645
646 while (!die) {
647 wtime.tv_sec = delay;
648 wtime.tv_nsec = 0;
649 /*
650 * Wait until signalled to wakeup or time expired.
651 * If signalled to be awoken, then a crash has occurred
652 * or otherwise time expired.
653 */
654 if (cond_reltimedwait(&retrywait, &crash_lock, &wtime) == 0) {
655 break;
656 }
657
658 /* Exit loop if queue is empty */
659 if ((next = recov_q.sm_recovhdp) == NULL)
660 break;
661
662 mutex_unlock(&crash_lock);
663
664 while (((nl = next) != (name_entry *)NULL) && (!die)) {
665 next = next->nxt;
666 if (statd_call_statd(nl->name) == 0) {
667 /* remove name from BACKUP */
668 remove_name(nl->name, 1, 0);
669 mutex_lock(&recov_q.lock);
670 /* remove entry from recovery_q */
671 delete_name(&recov_q.sm_recovhdp, nl->name);
672 mutex_unlock(&recov_q.lock);
673 } else {
674 /*
675 * Print message only once since unreachable
676 * host can be contacted forever.
677 */
678 if (delay == 0)
679 syslog(LOG_WARNING,
680 "statd: host %s is not "
681 "responding\n", nl->name);
682 }
683 }
684 /*
685 * Increment the amount of delay before restarting again.
686 * The amount of delay should not exceed the MAX_DELAYTIME.
687 */
688 if (delay <= MAX_DELAYTIME)
689 delay += INC_DELAYTIME;
690 mutex_lock(&crash_lock);
691 }
692
693 mutex_unlock(&crash_lock);
694 mutex_unlock(&sm_trylock);
695 out:
696 rw_unlock(&thr_rwlock);
697 if (debug)
698 (void) printf("EXITING sm_try\n");
699 thr_exit((void *) 0);
700 #ifdef lint
701 return (0);
702 #endif
703 }
704
705 /*
706 * Malloc's space and returns the ptr to malloc'ed space. NULL if unsuccessful.
707 */
708 char *
709 xmalloc(len)
710 unsigned len;
711 {
712 char *new;
713
714 if ((new = malloc(len)) == 0) {
715 syslog(LOG_ERR, "statd: malloc, error %m\n");
716 return ((char *)NULL);
717 } else {
718 (void) memset(new, 0, len);
719 return (new);
720 }
721 }
722
723 /*
724 * the following two routines are very similar to
725 * insert_mon and delete_mon in sm_proc.c, except the structture
726 * is different
727 */
728 static name_entry *
729 insert_name(namepp, name, need_alloc)
730 name_entry **namepp;
731 char *name;
732 int need_alloc;
733 {
734 name_entry *new;
735
736 new = (name_entry *)xmalloc(sizeof (name_entry));
737 if (new == (name_entry *) NULL)
738 return (NULL);
739
740 /* Allocate name when needed which is only when adding to record_t */
741 if (need_alloc) {
742 if ((new->name = strdup(name)) == (char *)NULL) {
743 syslog(LOG_ERR, "statd: strdup, error %m\n");
744 free(new);
745 return (NULL);
746 }
747 } else
748 new->name = name;
749
750 new->nxt = *namepp;
751 if (new->nxt != (name_entry *)NULL)
752 new->nxt->prev = new;
753
754 new->prev = (name_entry *) NULL;
755
756 *namepp = new;
757 if (debug) {
758 (void) printf("insert_name: inserted %s at %p\n",
759 name, (void *)namepp);
760 }
761
762 return (new);
763 }
764
765 /*
766 * Deletes name from specified list (namepp).
767 */
768 static void
769 delete_name(namepp, name)
770 name_entry **namepp;
771 char *name;
772 {
773 name_entry *nl;
774
775 nl = *namepp;
776 while (nl != (name_entry *)NULL) {
777 if (str_cmp_address_specifier(nl->name, name) == 0 ||
778 str_cmp_unqual_hostname(nl->name, name) == 0) {
779 if (nl->prev != (name_entry *)NULL)
780 nl->prev->nxt = nl->nxt;
781 else
782 *namepp = nl->nxt;
783 if (nl->nxt != (name_entry *)NULL)
784 nl->nxt->prev = nl->prev;
785 free(nl->name);
786 free(nl);
787 return;
788 }
789 nl = nl->nxt;
790 }
791 }
792
793 /*
794 * Finds name from specified list (namep).
795 */
796 static name_entry *
797 find_name(namep, name)
798 name_entry **namep;
799 char *name;
800 {
801 name_entry *nl;
802
803 nl = *namep;
804
805 while (nl != (name_entry *)NULL) {
806 if (str_cmp_unqual_hostname(nl->name, name) == 0) {
807 return (nl);
808 }
809 nl = nl->nxt;
810 }
811 return ((name_entry *)NULL);
812 }
813
814 /*
815 * Creates a file.
816 */
817
818 int
819 create_file(name)
820 char *name;
821 {
822 int fd;
823
824 /*
825 * The file might already exist. If it does, we ask for only write
826 * permission, since that's all the file was created with.
827 */
828 if ((fd = open(name, O_CREAT | O_WRONLY, S_IWUSR)) == -1) {
829 if (errno != EEXIST) {
830 syslog(LOG_ERR, "can't open %s: %m", name);
831 return (1);
832 }
833 }
834
835 if (debug >= 2)
836 (void) printf("%s is created\n", name);
837 if (close(fd)) {
838 syslog(LOG_ERR, "statd: close, error %m\n");
839 return (1);
840 }
841
842 return (0);
843 }
844
845 /*
846 * Deletes the file specified by name.
847 */
848 void
849 delete_file(name)
850 char *name;
851 {
852 if (debug >= 2)
853 (void) printf("Remove monitor entry %s\n", name);
854 if (unlink(name) == -1) {
855 if (errno != ENOENT)
856 syslog(LOG_ERR, "statd: unlink of %s, error %m", name);
857 }
858 }
859
860 /*
861 * Return 1 if file is a symlink, else 0.
862 */
863 int
864 is_symlink(file)
865 char *file;
866 {
867 int error;
868 struct stat lbuf;
869
870 do {
871 bzero((caddr_t)&lbuf, sizeof (lbuf));
872 error = lstat(file, &lbuf);
873 } while (error == EINTR);
874
875 if (error == 0) {
876 return ((lbuf.st_mode & S_IFMT) == S_IFLNK);
877 }
878
879 return (0);
880 }
881
882 /*
883 * Moves the file specified by `from' to `to' only if the
884 * new file is guaranteed to be created (which is presumably
885 * why we don't just do a rename(2)). If `from' is a
886 * symlink, the destination file will be a similar symlink
887 * in the directory of `to'.
888 *
889 * Returns 0 for success, 1 for failure.
890 */
891 static int
892 move_file(fromdir, file, todir)
893 char *fromdir;
894 char *file;
895 char *todir;
896 {
897 int n;
898 char rname[MAXNAMELEN + 1]; /* +1 for the terminating NULL */
899 char from[MAXPATHLEN];
900 char to[MAXPATHLEN];
901
902 (void) strcpy(from, fromdir);
903 (void) strcat(from, "/");
904 (void) strcat(from, file);
905 if (is_symlink(from)) {
906 /*
907 * Dig out the name of the regular file the link points to.
908 */
909 n = readlink(from, rname, MAXNAMELEN);
910 if (n <= 0) {
911 if (debug >= 2) {
912 (void) printf("move_file: can't read link %s\n",
913 from);
914 }
915 return (1);
916 }
917 rname[n] = '\0';
918
919 /*
920 * Create the link.
921 */
922 if (create_symlink(todir, rname, file) != 0) {
923 return (1);
924 }
925 } else {
926 /*
927 * Do what we've always done to move regular files.
928 */
929 (void) strcpy(to, todir);
930 (void) strcat(to, "/");
931 (void) strcat(to, file);
932 if (create_file(to) != 0) {
933 return (1);
934 }
935 }
936
937 /*
938 * Remove the old file if we've created the new one.
939 */
940 if (unlink(from) < 0) {
941 syslog(LOG_ERR, "move_file: unlink of %s, error %m", from);
942 return (1);
943 }
944
945 return (0);
946 }
947
948 /*
949 * Create a symbolic link named `lname' to regular file `rname'.
950 * Both files should be in directory `todir'.
951 */
952 int
953 create_symlink(todir, rname, lname)
954 char *todir;
955 char *rname;
956 char *lname;
957 {
958 int error;
959 char lpath[MAXPATHLEN];
960
961 /*
962 * Form the full pathname of the link.
963 */
964 (void) strcpy(lpath, todir);
965 (void) strcat(lpath, "/");
966 (void) strcat(lpath, lname);
967
968 /*
969 * Now make the new symlink ...
970 */
971 if (symlink(rname, lpath) < 0) {
972 error = errno;
973 if (error != 0 && error != EEXIST) {
974 if (debug >= 2) {
975 (void) printf(
976 "create_symlink: can't link %s/%s -> %s\n",
977 todir, lname, rname);
978 }
979 return (1);
980 }
981 }
982
983 if (debug) {
984 if (error == EEXIST) {
985 (void) printf("link %s/%s -> %s already exists\n",
986 todir, lname, rname);
987 } else {
988 (void) printf("created link %s/%s -> %s\n",
989 todir, lname, rname);
990 }
991 }
992
993 return (0);
994 }
995
996 /*
997 * remove the name from the specified directory
998 * op = 0: CURRENT
999 * op = 1: BACKUP
1000 */
1001 static void
1002 remove_name(char *name, int op, int startup)
1003 {
1004 int i;
1005 char *alt_dir;
1006 char *queue;
1007
1008 if (op == 0) {
1009 alt_dir = "statmon/sm";
1010 queue = CURRENT;
1011 } else {
1012 alt_dir = "statmon/sm.bak";
1013 queue = BACKUP;
1014 }
1015
1016 remove_single_name(name, queue, NULL);
1017 /*
1018 * At startup, entries have not yet been copied to alternate
1019 * directories and thus do not need to be removed.
1020 */
1021 if (startup == 0) {
1022 for (i = 0; i < pathix; i++) {
1023 remove_single_name(name, path_name[i], alt_dir);
1024 }
1025 }
1026 }
1027
1028 /*
1029 * Remove the name from the specified directory, which is dir1/dir2 or
1030 * dir1, depending on whether dir2 is NULL.
1031 */
1032 static void
1033 remove_single_name(char *name, char *dir1, char *dir2)
1034 {
1035 int n, error;
1036 char path[MAXPATHLEN+MAXNAMELEN+SM_MAXPATHLEN]; /* why > MAXPATHLEN? */
1037 char dirpath[MAXPATHLEN];
1038 char rname[MAXNAMELEN + 1]; /* +1 for NULL term */
1039
1040 if (strlen(name) + strlen(dir1) + (dir2 != NULL ? strlen(dir2) : 0) +
1041 3 > MAXPATHLEN) {
1042 if (dir2 != NULL)
1043 syslog(LOG_ERR,
1044 "statd: pathname too long: %s/%s/%s\n",
1045 dir1, dir2, name);
1046 else
1047 syslog(LOG_ERR,
1048 "statd: pathname too long: %s/%s\n",
1049 dir1, name);
1050
1051 return;
1052 }
1053
1054 (void) strcpy(path, dir1);
1055 (void) strcat(path, "/");
1056 if (dir2 != NULL) {
1057 (void) strcat(path, dir2);
1058 (void) strcat(path, "/");
1059 }
1060 (void) strcpy(dirpath, path); /* save here - we may need it shortly */
1061 (void) strcat(path, name);
1062
1063 /*
1064 * Despite the name of this routine :-@), `path' may be a symlink
1065 * to a regular file. If it is, and if that file has no other
1066 * links to it, we must remove it now as well.
1067 */
1068 if (is_symlink(path)) {
1069 n = readlink(path, rname, MAXNAMELEN);
1070 if (n > 0) {
1071 rname[n] = '\0';
1072
1073 if (count_symlinks(dirpath, rname, &n) < 0) {
1074 return;
1075 }
1076
1077 if (n == 1) {
1078 (void) strcat(dirpath, rname);
1079 error = unlink(dirpath);
1080 if (debug >= 2) {
1081 if (error < 0) {
1082 (void) printf(
1083 "remove_name: can't "
1084 "unlink %s\n",
1085 dirpath);
1086 } else {
1087 (void) printf(
1088 "remove_name: unlinked ",
1089 "%s\n", dirpath);
1090 }
1091 }
1092 }
1093 } else {
1094 /*
1095 * Policy: if we can't read the symlink, leave it
1096 * here for analysis by the system administrator.
1097 */
1098 syslog(LOG_ERR,
1099 "statd: can't read link %s: %m\n", path);
1100 }
1101 }
1102
1103 /*
1104 * If it's a regular file, we can assume all symlinks and the
1105 * files to which they refer have been processed already - just
1106 * fall through to here to remove it.
1107 */
1108 delete_file(path);
1109 }
1110
1111 /*
1112 * Count the number of symlinks in `dir' which point to `name' (also in dir).
1113 * Passes back symlink count in `count'.
1114 * Returns 0 for success, < 0 for failure.
1115 */
1116 static int
1117 count_symlinks(char *dir, char *name, int *count)
1118 {
1119 int cnt = 0;
1120 int n;
1121 DIR *dp;
1122 struct dirent *dirp;
1123 char lpath[MAXPATHLEN];
1124 char rname[MAXNAMELEN + 1]; /* +1 for term NULL */
1125
1126 if ((dp = opendir(dir)) == (DIR *)NULL) {
1127 syslog(LOG_ERR, "count_symlinks: open %s dir, error %m\n",
1128 dir);
1129 return (-1);
1130 }
1131
1132 while ((dirp = readdir(dp)) != NULL) {
1133 if (strcmp(dirp->d_name, ".") == 0 ||
1134 strcmp(dirp->d_name, "..") == 0) {
1135 continue;
1136 }
1137
1138 (void) sprintf(lpath, "%s%s", dir, dirp->d_name);
1139 if (is_symlink(lpath)) {
1140 /*
1141 * Fetch the name of the file the symlink refers to.
1142 */
1143 n = readlink(lpath, rname, MAXNAMELEN);
1144 if (n <= 0) {
1145 if (debug >= 2) {
1146 (void) printf(
1147 "count_symlinks: can't read link "
1148 "%s\n", lpath);
1149 }
1150 continue;
1151 }
1152 rname[n] = '\0';
1153
1154 /*
1155 * If `rname' matches `name', bump the count. There
1156 * may well be multiple symlinks to the same name, so
1157 * we must continue to process the entire directory.
1158 */
1159 if (strcmp(rname, name) == 0) {
1160 cnt++;
1161 }
1162 }
1163 }
1164
1165 (void) closedir(dp);
1166
1167 if (debug) {
1168 (void) printf("count_symlinks: found %d symlinks\n", cnt);
1169 }
1170 *count = cnt;
1171 return (0);
1172 }
1173
1174 /*
1175 * Manage the cache of hostnames. An entry for each host that has recently
1176 * locked a file is kept. There is an in-ram table (rec_table) and an empty
1177 * file in the file system name space (/var/statmon/sm/<name>). This
1178 * routine adds (deletes) the name to (from) the in-ram table and the entry
1179 * to (from) the file system name space.
1180 *
1181 * If op == 1 then the name is added to the queue otherwise the name is
1182 * deleted.
1183 */
1184 void
1185 record_name(name, op)
1186 char *name;
1187 int op;
1188 {
1189 name_entry *nl;
1190 int i;
1191 char path[MAXPATHLEN+MAXNAMELEN+SM_MAXPATHLEN];
1192 name_entry **record_q;
1193 unsigned int hash;
1194
1195 /*
1196 * These names are supposed to be just host names, not paths or
1197 * other arbitrary files.
1198 * manipulating the empty pathname unlinks CURRENT,
1199 * manipulating files with '/' would allow you to create and unlink
1200 * files all over the system; LOG_AUTH, it's a security thing.
1201 * Don't remove the directories . and ..
1202 */
1203 if (name == NULL)
1204 return;
1205
1206 if (name[0] == '\0' || strchr(name, '/') != NULL ||
1207 strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
1208 syslog(LOG_ERR|LOG_AUTH, "statd: attempt to %s \"%s/%s\"",
1209 op == 1 ? "create" : "remove", CURRENT, name);
1210 return;
1211 }
1212
1213 SMHASH(name, hash);
1214 if (debug) {
1215 if (op == 1)
1216 (void) printf("inserting %s at hash %d,\n",
1217 name, hash);
1218 else
1219 (void) printf("deleting %s at hash %d\n", name, hash);
1220 pr_name(name, 1);
1221 }
1222
1223
1224 if (op == 1) { /* insert */
1225 mutex_lock(&record_table[hash].lock);
1226 record_q = &record_table[hash].sm_rechdp;
1227 if ((nl = find_name(record_q, name)) == (name_entry *)NULL) {
1228
1229 int path_len;
1230
1231 if ((nl = insert_name(record_q, name, 1)) !=
1232 (name_entry *) NULL)
1233 nl->count++;
1234 mutex_unlock(&record_table[hash].lock);
1235 /* make an entry in current directory */
1236
1237 path_len = strlen(CURRENT) + strlen(name) + 2;
1238 if (path_len > MAXPATHLEN) {
1239 syslog(LOG_ERR,
1240 "statd: pathname too long: %s/%s\n",
1241 CURRENT, name);
1242 return;
1243 }
1244 (void) strcpy(path, CURRENT);
1245 (void) strcat(path, "/");
1246 (void) strcat(path, name);
1247 (void) create_file(path);
1248 if (debug) {
1249 (void) printf("After insert_name\n");
1250 pr_name(name, 1);
1251 }
1252 /* make an entry in alternate paths */
1253 for (i = 0; i < pathix; i++) {
1254 path_len = strlen(path_name[i]) +
1255 strlen("/statmon/sm/") +
1256 strlen(name) + 1;
1257
1258 if (path_len > MAXPATHLEN) {
1259 syslog(LOG_ERR,
1260 "statd: pathname too long: %s/statmon/sm/%s\n",
1261 path_name[i], name);
1262 continue;
1263 }
1264 (void) strcpy(path, path_name[i]);
1265 (void) strcat(path, "/statmon/sm/");
1266 (void) strcat(path, name);
1267 (void) create_file(path);
1268 }
1269 return;
1270 }
1271 nl->count++;
1272 mutex_unlock(&record_table[hash].lock);
1273
1274 } else { /* delete */
1275 mutex_lock(&record_table[hash].lock);
1276 record_q = &record_table[hash].sm_rechdp;
1277 if ((nl = find_name(record_q, name)) == (name_entry *)NULL) {
1278 mutex_unlock(&record_table[hash].lock);
1279 return;
1280 }
1281 nl->count--;
1282 if (nl->count == 0) {
1283 delete_name(record_q, name);
1284 mutex_unlock(&record_table[hash].lock);
1285 /* remove this entry from current directory */
1286 remove_name(name, 0, 0);
1287 } else
1288 mutex_unlock(&record_table[hash].lock);
1289 if (debug) {
1290 (void) printf("After delete_name \n");
1291 pr_name(name, 1);
1292 }
1293 }
1294 }
1295
1296 /*
1297 * This routine adds a symlink in the form of an ASCII dotted quad
1298 * IP address that is linked to the name already recorded in the
1299 * filesystem name space by record_name(). Enough information is
1300 * (hopefully) provided to support other address types in the future.
1301 * The purpose of this is to cache enough information to contact
1302 * hosts in other domains during server crash recovery (see bugid
1303 * 1184192).
1304 *
1305 * The worst failure mode here is that the symlink is not made, and
1306 * statd falls back to the old buggy behavior.
1307 */
1308 void
1309 record_addr(char *name, sa_family_t family, struct netobj *ah)
1310 {
1311 int i;
1312 int path_len;
1313 char *famstr;
1314 struct in_addr addr;
1315 char *addr6;
1316 char ascii_addr[MAXNAMELEN];
1317 char path[MAXPATHLEN];
1318
1319 if (family == AF_INET) {
1320 if (ah->n_len != sizeof (struct in_addr))
1321 return;
1322 addr = *(struct in_addr *)ah->n_bytes;
1323 } else if (family == AF_INET6) {
1324 if (ah->n_len != sizeof (struct in6_addr))
1325 return;
1326 addr6 = (char *)ah->n_bytes;
1327 } else
1328 return;
1329
1330 if (debug) {
1331 if (family == AF_INET)
1332 (void) printf("record_addr: addr= %x\n", addr.s_addr);
1333 else if (family == AF_INET6)
1334 (void) printf("record_addr: addr= %x\n", \
1335 ((struct in6_addr *)addr6)->s6_addr);
1336 }
1337
1338 if (family == AF_INET) {
1339 if (addr.s_addr == INADDR_ANY ||
1340 ((addr.s_addr && 0xff000000) == 0)) {
1341 syslog(LOG_DEBUG,
1342 "record_addr: illegal IP address %x\n",
1343 addr.s_addr);
1344 return;
1345 }
1346 }
1347
1348 /* convert address to ASCII */
1349 famstr = family2string(family);
1350 if (famstr == NULL) {
1351 syslog(LOG_DEBUG,
1352 "record_addr: unsupported address family %d\n",
1353 family);
1354 return;
1355 }
1356
1357 switch (family) {
1358 char abuf[INET6_ADDRSTRLEN];
1359 case AF_INET:
1360 (void) sprintf(ascii_addr, "%s.%s", famstr, inet_ntoa(addr));
1361 break;
1362
1363 case AF_INET6:
1364 (void) sprintf(ascii_addr, "%s.%s", famstr,\
1365 inet_ntop(family, addr6, abuf, sizeof (abuf)));
1366 break;
1367
1368 default:
1369 if (debug) {
1370 (void) printf(
1371 "record_addr: family2string supports unknown "
1372 "family %d (%s)\n", family, famstr);
1373 }
1374 free(famstr);
1375 return;
1376 }
1377
1378 if (debug) {
1379 (void) printf("record_addr: ascii_addr= %s\n", ascii_addr);
1380 }
1381 free(famstr);
1382
1383 /*
1384 * Make the symlink in CURRENT. The `name' file should have
1385 * been created previously by record_name().
1386 */
1387 (void) create_symlink(CURRENT, name, ascii_addr);
1388
1389 /*
1390 * Similarly for alternate paths.
1391 */
1392 for (i = 0; i < pathix; i++) {
1393 path_len = strlen(path_name[i]) +
1394 strlen("/statmon/sm/") +
1395 strlen(name) + 1;
1396
1397 if (path_len > MAXPATHLEN) {
1398 syslog(LOG_ERR,
1399 "statd: pathname too long: %s/statmon/sm/%s\n",
1400 path_name[i], name);
1401 continue;
1402 }
1403 (void) strcpy(path, path_name[i]);
1404 (void) strcat(path, "/statmon/sm");
1405 (void) create_symlink(path, name, ascii_addr);
1406 }
1407 }
1408
1409 /*
1410 * SM_CRASH - simulate a crash of statd.
1411 */
1412 void
1413 sm_crash()
1414 {
1415 name_entry *nl, *next;
1416 mon_entry *nl_monp, *mon_next;
1417 int k;
1418 my_id *nl_idp;
1419
1420 for (k = 0; k < MAX_HASHSIZE; k++) {
1421 mutex_lock(&mon_table[k].lock);
1422 if ((mon_next = mon_table[k].sm_monhdp) ==
1423 (mon_entry *) NULL) {
1424 mutex_unlock(&mon_table[k].lock);
1425 continue;
1426 } else {
1427 while ((nl_monp = mon_next) != (mon_entry *)NULL) {
1428 mon_next = mon_next->nxt;
1429 nl_idp = &nl_monp->id.mon_id.my_id;
1430 free(nl_monp->id.mon_id.mon_name);
1431 free(nl_idp->my_name);
1432 free(nl_monp);
1433 }
1434 mon_table[k].sm_monhdp = (mon_entry *)NULL;
1435 }
1436 mutex_unlock(&mon_table[k].lock);
1437 }
1438
1439 /* Clean up entries in record table */
1440 for (k = 0; k < MAX_HASHSIZE; k++) {
1441 mutex_lock(&record_table[k].lock);
1442 if ((next = record_table[k].sm_rechdp) ==
1443 (name_entry *) NULL) {
1444 mutex_unlock(&record_table[k].lock);
1445 continue;
1446 } else {
1447 while ((nl = next) != (name_entry *)NULL) {
1448 next = next->nxt;
1449 free(nl->name);
1450 free(nl);
1451 }
1452 record_table[k].sm_rechdp = (name_entry *)NULL;
1453 }
1454 mutex_unlock(&record_table[k].lock);
1455 }
1456
1457 /* Clean up entries in recovery table */
1458 mutex_lock(&recov_q.lock);
1459 if ((next = recov_q.sm_recovhdp) != (name_entry *)NULL) {
1460 while ((nl = next) != (name_entry *)NULL) {
1461 next = next->nxt;
1462 free(nl->name);
1463 free(nl);
1464 }
1465 recov_q.sm_recovhdp = (name_entry *)NULL;
1466 }
1467 mutex_unlock(&recov_q.lock);
1468 statd_init();
1469 }
1470
1471 /*
1472 * Initialize the hash tables: mon_table, record_table, recov_q and
1473 * locks.
1474 */
1475 void
1476 sm_inithash()
1477 {
1478 int k;
1479
1480 if (debug)
1481 (void) printf("Initializing hash tables\n");
1482 for (k = 0; k < MAX_HASHSIZE; k++) {
1483 mon_table[k].sm_monhdp = (mon_entry *)NULL;
1484 record_table[k].sm_rechdp = (name_entry *)NULL;
1485 mutex_init(&mon_table[k].lock, USYNC_THREAD, NULL);
1486 mutex_init(&record_table[k].lock, USYNC_THREAD, NULL);
1487 }
1488 mutex_init(&recov_q.lock, USYNC_THREAD, NULL);
1489 recov_q.sm_recovhdp = (name_entry *)NULL;
1490
1491 }
1492
1493 /*
1494 * Maps a socket address family to a name string, or NULL if the family
1495 * is not supported by statd.
1496 * Caller is responsible for freeing storage used by result string, if any.
1497 */
1498 static char *
1499 family2string(sa_family_t family)
1500 {
1501 char *rc;
1502
1503 switch (family) {
1504 case AF_INET:
1505 rc = strdup(SM_ADDR_IPV4);
1506 break;
1507
1508 case AF_INET6:
1509 rc = strdup(SM_ADDR_IPV6);
1510 break;
1511
1512 default:
1513 rc = NULL;
1514 break;
1515 }
1516
1517 return (rc);
1518 }
1519
1520 /*
1521 * Prints out list in record_table if flag is 1 otherwise
1522 * prints out each list in recov_q specified by name.
1523 */
1524 static void
1525 pr_name(name, flag)
1526 char *name;
1527 int flag;
1528 {
1529 name_entry *nl;
1530 unsigned int hash;
1531
1532 if (!debug)
1533 return;
1534 if (flag) {
1535 SMHASH(name, hash);
1536 (void) printf("*****record_q: ");
1537 mutex_lock(&record_table[hash].lock);
1538 nl = record_table[hash].sm_rechdp;
1539 while (nl != (name_entry *)NULL) {
1540 (void) printf("(%x), ", (int)nl);
1541 nl = nl->nxt;
1542 }
1543 mutex_unlock(&record_table[hash].lock);
1544 } else {
1545 (void) printf("*****recovery_q: ");
1546 mutex_lock(&recov_q.lock);
1547 nl = recov_q.sm_recovhdp;
1548 while (nl != (name_entry *)NULL) {
1549 (void) printf("(%x), ", (int)nl);
1550 nl = nl->nxt;
1551 }
1552 mutex_unlock(&recov_q.lock);
1553
1554 }
1555 (void) printf("\n");
1556 }