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.
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.
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]
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
27 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
31 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
35 * University Copyright- Copyright (c) 1982, 1986, 1988
36 * The Regents of the University of California
39 * University Acknowledgment- Portions of this document are derived from
40 * software developed by the University of California, Berkeley, and its
45 * Copyright (c) 2012 by Delphix. All rights reserved.
49 * sm_statd.c consists of routines used for the intermediate
50 * statd implementation(3.2 rpc.statd);
51 * it creates an entry in "current" directory for each site that it monitors;
52 * after crash and recovery, it moves all entries in "current"
53 * to "backup" directory, and notifies the corresponding statd of its recovery.
62 #include <sys/types.h>
65 #include <sys/param.h>
66 #include <arpa/inet.h>
69 #include <rpcsvc/sm_inter.h>
70 #include <rpcsvc/nsm_addr.h>
83 sm_hash_t mon_table
[MAX_HASHSIZE
];
84 static sm_hash_t record_table
[MAX_HASHSIZE
];
85 static sm_hash_t recov_q
;
87 static name_entry
*find_name(name_entry
**namepp
, char *name
);
88 static name_entry
*insert_name(name_entry
**namepp
, char *name
,
90 static void delete_name(name_entry
**namepp
, char *name
);
91 static void remove_name(char *name
, int op
, int startup
);
92 static int statd_call_statd(char *name
);
93 static void pr_name(char *name
, int flag
);
94 static void *thr_statd_init(void *unused
);
95 static void *sm_try(void *unused
);
96 static void *thr_call_statd(void *);
97 static void remove_single_name(char *name
, char *dir1
, char *dir2
);
98 static int move_file(char *fromdir
, char *file
, char *todir
);
99 static int count_symlinks(char *dir
, char *name
, int *count
);
100 static char *family2string(sa_family_t family
);
103 * called when statd first comes up; it searches /etc/sm to gather
104 * all entries to notify its own failure
113 char state_file
[MAXPATHLEN
+SM_MAXPATHLEN
];
116 (void) printf("enter statd_init\n");
119 * First try to open the file. If that fails, try to create it.
120 * If that fails, give up.
122 if ((fp
= fopen(STATE
, "r+")) == NULL
) {
123 if ((fp
= fopen(STATE
, "w+")) == NULL
) {
124 syslog(LOG_ERR
, "can't open %s: %m", STATE
);
127 (void) chmod(STATE
, 0644);
129 if ((fscanf(fp
, "%d", &LOCAL_STATE
)) == EOF
) {
131 (void) printf("empty file\n");
136 * Scan alternate paths for largest "state" number
138 for (i
= 0; i
< pathix
; i
++) {
139 (void) sprintf(state_file
, "%s/statmon/state", path_name
[i
]);
140 if ((fp_tmp
= fopen(state_file
, "r+")) == NULL
) {
141 if ((fp_tmp
= fopen(state_file
, "w+")) == NULL
) {
148 (void) chmod(state_file
, 0644);
150 if ((fscanf(fp_tmp
, "%d", &tmp_state
)) == EOF
) {
153 "statd: %s: file empty\n", state_file
);
154 (void) fclose(fp_tmp
);
157 if (tmp_state
> LOCAL_STATE
) {
158 LOCAL_STATE
= tmp_state
;
160 (void) printf("Update LOCAL STATE: %d\n",
163 (void) fclose(fp_tmp
);
166 LOCAL_STATE
= ((LOCAL_STATE
%2) == 0) ? LOCAL_STATE
+1 : LOCAL_STATE
+2;
168 /* IF local state overflows, reset to value 1 */
169 if (LOCAL_STATE
< 0) {
173 /* Copy the LOCAL_STATE value back to all stat files */
174 if (fseek(fp
, 0, 0) == -1) {
175 syslog(LOG_ERR
, "statd: fseek failed\n");
179 (void) fprintf(fp
, "%-10d", LOCAL_STATE
);
181 if (fsync(fileno(fp
)) == -1) {
182 syslog(LOG_ERR
, "statd: fsync failed\n");
187 for (i
= 0; i
< pathix
; i
++) {
188 (void) sprintf(state_file
, "%s/statmon/state", path_name
[i
]);
189 if ((fp_tmp
= fopen(state_file
, "r+")) == NULL
) {
190 if ((fp_tmp
= fopen(state_file
, "w+")) == NULL
) {
192 "can't open %s: %m", state_file
);
195 (void) chmod(state_file
, 0644);
197 (void) fprintf(fp_tmp
, "%-10d", LOCAL_STATE
);
198 (void) fflush(fp_tmp
);
199 if (fsync(fileno(fp_tmp
)) == -1) {
201 "statd: %s: fsync failed\n", state_file
);
202 (void) fclose(fp_tmp
);
205 (void) fclose(fp_tmp
);
209 (void) printf("local state = %d\n", LOCAL_STATE
);
211 if ((mkdir(CURRENT
, SM_DIRECTORY_MODE
)) == -1) {
212 if (errno
!= EEXIST
) {
213 syslog(LOG_ERR
, "statd: mkdir current, error %m\n");
217 if ((mkdir(BACKUP
, SM_DIRECTORY_MODE
)) == -1) {
218 if (errno
!= EEXIST
) {
219 syslog(LOG_ERR
, "statd: mkdir backup, error %m\n");
224 /* get all entries in CURRENT into BACKUP */
225 if ((dp
= opendir(CURRENT
)) == NULL
) {
226 syslog(LOG_ERR
, "statd: open current directory, error %m\n");
230 while ((dirp
= readdir(dp
)) != NULL
) {
231 if (strcmp(dirp
->d_name
, ".") != 0 &&
232 strcmp(dirp
->d_name
, "..") != 0) {
233 /* rename all entries from CURRENT to BACKUP */
234 (void) move_file(CURRENT
, dirp
->d_name
, BACKUP
);
240 /* Contact hosts' statd */
241 if (thr_create(NULL
, 0, thr_statd_init
, NULL
, THR_DETACHED
, NULL
)) {
243 "statd: unable to create thread for thr_statd_init\n");
249 * Work thread which contacts hosts' statd.
252 thr_statd_init(void *unused
)
260 char buf
[MAXPATHLEN
+SM_MAXPATHLEN
];
262 /* Go thru backup directory and contact hosts */
263 if ((dp
= opendir(BACKUP
)) == NULL
) {
264 syslog(LOG_ERR
, "statd: open backup directory, error %m\n");
269 * Create "UNDETACHED" threads for each symlink and (unlinked)
270 * regular file in backup directory to initiate statd_call_statd.
271 * NOTE: These threads are the only undetached threads in this
272 * program and thus, the thread id is not needed to join the threads.
275 while ((dirp
= readdir(dp
)) != NULL
) {
277 * If host file is not a symlink, don't bother to
278 * spawn a thread for it. If any link(s) refer to
279 * it, the host will be contacted using the link(s).
280 * If not, we'll deal with it during the legacy pass.
282 (void) sprintf(buf
, "%s/%s", BACKUP
, dirp
->d_name
);
283 if (is_symlink(buf
) == 0) {
288 * If the num_threads has exceeded, wait until
289 * a certain amount of threads have finished.
290 * Currently, 10% of threads created should be joined.
292 if (num_threads
> MAX_THR
) {
293 num_join
= num_threads
/PERCENT_MINJOIN
;
294 for (i
= 0; i
< num_join
; i
++)
296 num_threads
-= num_join
;
300 * If can't alloc name then print error msg and
301 * continue to next item on list.
303 name
= strdup(dirp
->d_name
);
306 "statd: unable to allocate space for name %s\n",
311 /* Create a thread to do a statd_call_statd for name */
312 if (thr_create(NULL
, 0, thr_call_statd
, name
, 0, NULL
)) {
314 "statd: unable to create thr_call_statd() "
315 "for name %s.\n", dirp
->d_name
);
323 * Join the other threads created above before processing the
324 * legacies. This allows all symlinks and the regular files
325 * to which they correspond to be processed and deleted.
327 for (i
= 0; i
< num_threads
; i
++) {
332 * The second pass checks for `legacies': regular files which
333 * never had symlinks pointing to them at all, just like in the
334 * good old (pre-1184192 fix) days. Once a machine has cleaned
335 * up its legacies they should only reoccur due to catastrophes
336 * (e.g., severed symlinks).
340 while ((dirp
= readdir(dp
)) != NULL
) {
341 if (strcmp(dirp
->d_name
, ".") == 0 ||
342 strcmp(dirp
->d_name
, "..") == 0) {
346 (void) sprintf(buf
, "%s/%s", BACKUP
, dirp
->d_name
);
347 if (is_symlink(buf
)) {
349 * We probably couldn't reach this host and it's
350 * been put on the recovery queue for retry.
351 * Skip it and keep looking for regular files.
357 (void) printf("thr_statd_init: legacy %s\n",
362 * If the number of threads exceeds the maximum, wait
363 * for some fraction of them to finish before
366 if (num_threads
> MAX_THR
) {
367 num_join
= num_threads
/PERCENT_MINJOIN
;
368 for (i
= 0; i
< num_join
; i
++)
370 num_threads
-= num_join
;
374 * If can't alloc name then print error msg and
375 * continue to next item on list.
377 name
= strdup(dirp
->d_name
);
380 "statd: unable to allocate space for name %s\n",
385 /* Create a thread to do a statd_call_statd for name */
386 if (thr_create(NULL
, 0, thr_call_statd
, name
, 0, NULL
)) {
388 "statd: unable to create thr_call_statd() "
389 "for name %s.\n", dirp
->d_name
);
399 * Join the other threads created above before creating thread
400 * to process items in recovery table.
402 for (i
= 0; i
< num_threads
; i
++) {
407 * Need to only copy /var/statmon/sm.bak to alternate paths, since
408 * the only hosts in /var/statmon/sm should be the ones currently
409 * being monitored and already should be in alternate paths as part
412 for (i
= 0; i
< pathix
; i
++) {
413 (void) sprintf(buf
, "%s/statmon/sm.bak", path_name
[i
]);
414 if ((mkdir(buf
, SM_DIRECTORY_MODE
)) == -1) {
416 syslog(LOG_ERR
, "statd: mkdir %s error %m\n",
419 copydir_from_to(BACKUP
, buf
);
421 copydir_from_to(BACKUP
, buf
);
426 * Reset the die and in_crash variables.
428 mutex_lock(&crash_lock
);
431 mutex_unlock(&crash_lock
);
434 (void) printf("Creating thread for sm_try\n");
436 /* Continue to notify statd on hosts that were unreachable. */
437 if (thr_create(NULL
, 0, sm_try
, NULL
, THR_DETACHED
, NULL
))
439 "statd: unable to create thread for sm_try().\n");
444 * Work thread to make call to statd_call_statd.
447 thr_call_statd(void *namep
)
449 char *name
= (char *)namep
;
452 * If statd of name is unreachable, add name to recovery table
453 * otherwise if statd_call_statd was successful, remove from backup.
455 if (statd_call_statd(name
) != 0) {
458 char path
[MAXPATHLEN
];
460 * since we are constructing this pathname below we add
461 * another space for the terminating NULL so we don't
462 * overflow our buffer when we do the readlink
464 char rname
[MAXNAMELEN
+ 1];
468 "statd call failed, inserting %s in recov_q\n", name
);
470 mutex_lock(&recov_q
.lock
);
471 (void) insert_name(&recov_q
.sm_recovhdp
, name
, 0);
472 mutex_unlock(&recov_q
.lock
);
475 * If we queued a symlink name in the recovery queue,
476 * we now clean up the regular file to which it referred.
477 * This may leave a severed symlink if multiple links
478 * referred to one regular file; this is unaesthetic but
479 * it works. The big benefit is that it prevents us
480 * from recovering the same host twice (as symlink and
481 * as regular file) needlessly, usually on separate reboots.
483 (void) strcpy(path
, BACKUP
);
484 (void) strcat(path
, "/");
485 (void) strcat(path
, name
);
486 if (is_symlink(path
)) {
487 n
= readlink(path
, rname
, MAXNAMELEN
);
491 "thr_call_statd: can't read "
497 tail
= strrchr(path
, '/') + 1;
499 if ((strlen(BACKUP
) + strlen(rname
) + 2) <=
501 (void) strcpy(tail
, rname
);
504 printf("thr_call_statd: path over"
516 * If `name' is an IP address symlink to a name file,
517 * remove it now. If it is the last such symlink,
518 * remove the name file as well. Regular files with
519 * no symlinks to them are assumed to be legacies and
520 * are removed as well.
522 remove_name(name
, 1, 1);
529 * Notifies the statd of host specified by name to indicate that
530 * state has changed for this server.
533 statd_call_statd(char *name
)
535 enum clnt_stat clnt_stat
;
536 struct timeval tottimeout
;
542 int dummy1
, dummy2
, dummy3
, dummy4
;
543 char ascii_addr
[MAXNAMELEN
];
546 ntf
.mon_name
= hostname
;
547 ntf
.state
= LOCAL_STATE
;
549 (void) printf("statd_call_statd at %s\n", name
);
552 * If it looks like an ASCII <address family>.<address> specifier,
553 * strip off the family - we just want the address when obtaining
555 * If it's anything else, just pass it on to create_client().
557 unq_len
= strcspn(name
, ".");
559 if ((strncmp(name
, SM_ADDR_IPV4
, unq_len
) == 0) ||
560 (strncmp(name
, SM_ADDR_IPV6
, unq_len
) == 0)) {
561 name_or_addr
= strchr(name
, '.') + 1;
567 * NOTE: We depend here upon the fact that the RPC client code
568 * allows us to use ASCII dotted quad `names', i.e. "192.9.200.1".
569 * This may change in a future release.
572 (void) printf("statd_call_statd: calling create_client(%s)\n",
576 tottimeout
.tv_sec
= SM_RPC_TIMEOUT
;
577 tottimeout
.tv_usec
= 0;
579 if ((clnt
= create_client(name_or_addr
, SM_PROG
, SM_VERS
, NULL
,
580 &tottimeout
)) == NULL
) {
584 /* Perform notification to client */
586 clnt_stat
= clnt_call(clnt
, SM_NOTIFY
, xdr_stat_chge
, (char *)&ntf
,
587 xdr_void
, NULL
, tottimeout
);
589 (void) printf("clnt_stat=%s(%d)\n",
590 clnt_sperrno(clnt_stat
), clnt_stat
);
592 if (clnt_stat
!= (int)RPC_SUCCESS
) {
594 "statd: cannot talk to statd at %s, %s(%d)\n",
595 name_or_addr
, clnt_sperrno(clnt_stat
), clnt_stat
);
600 * Wait until the host_name is populated.
602 (void) mutex_lock(&merges_lock
);
604 (void) cond_wait(&merges_cond
, &merges_lock
);
605 (void) mutex_unlock(&merges_lock
);
607 /* For HA systems and multi-homed hosts */
608 ntf
.state
= LOCAL_STATE
;
609 for (i
= 0; i
< addrix
; i
++) {
610 ntf
.mon_name
= host_name
[i
];
612 (void) printf("statd_call_statd at %s\n", name_or_addr
);
613 clnt_stat
= clnt_call(clnt
, SM_NOTIFY
, xdr_stat_chge
,
614 (char *)&ntf
, xdr_void
, NULL
, tottimeout
);
615 if (clnt_stat
!= (int)RPC_SUCCESS
) {
617 "statd: cannot talk to statd at %s, %s(%d)\n",
618 name_or_addr
, clnt_sperrno(clnt_stat
), clnt_stat
);
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.
637 name_entry
*nl
, *next
;
641 rw_rdlock(&thr_rwlock
);
642 if (mutex_trylock(&sm_trylock
))
644 mutex_lock(&crash_lock
);
647 wtime
.tv_sec
= delay
;
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.
654 if (cond_reltimedwait(&retrywait
, &crash_lock
, &wtime
) == 0) {
658 /* Exit loop if queue is empty */
659 if ((next
= recov_q
.sm_recovhdp
) == NULL
)
662 mutex_unlock(&crash_lock
);
664 while (((nl
= next
) != NULL
) && (!die
)) {
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
);
675 * Print message only once since unreachable
676 * host can be contacted forever.
680 "statd: host %s is not "
681 "responding\n", nl
->name
);
685 * Increment the amount of delay before restarting again.
686 * The amount of delay should not exceed the MAX_DELAYTIME.
688 if (delay
<= MAX_DELAYTIME
)
689 delay
+= INC_DELAYTIME
;
690 mutex_lock(&crash_lock
);
693 mutex_unlock(&crash_lock
);
694 mutex_unlock(&sm_trylock
);
696 rw_unlock(&thr_rwlock
);
698 (void) printf("EXITING sm_try\n");
703 * Malloc's space and returns the ptr to malloc'ed space. NULL if unsuccessful.
706 xmalloc(unsigned len
)
710 if ((new = malloc(len
)) == 0) {
711 syslog(LOG_ERR
, "statd: malloc, error %m\n");
714 (void) memset(new, 0, len
);
720 * the following two routines are very similar to
721 * insert_mon and delete_mon in sm_proc.c, except the structture
725 insert_name(name_entry
**namepp
, char *name
, int need_alloc
)
729 new = (name_entry
*)xmalloc(sizeof (name_entry
));
730 if (new == (name_entry
*) NULL
)
733 /* Allocate name when needed which is only when adding to record_t */
735 if ((new->name
= strdup(name
)) == NULL
) {
736 syslog(LOG_ERR
, "statd: strdup, error %m\n");
744 if (new->nxt
!= NULL
)
745 new->nxt
->prev
= new;
747 new->prev
= (name_entry
*) NULL
;
751 (void) printf("insert_name: inserted %s at %p\n",
752 name
, (void *)namepp
);
759 * Deletes name from specified list (namepp).
762 delete_name(name_entry
**namepp
, char *name
)
768 if (str_cmp_address_specifier(nl
->name
, name
) == 0 ||
769 str_cmp_unqual_hostname(nl
->name
, name
) == 0) {
770 if (nl
->prev
!= NULL
)
771 nl
->prev
->nxt
= nl
->nxt
;
775 nl
->nxt
->prev
= nl
->prev
;
785 * Finds name from specified list (namep).
788 find_name(name_entry
**namep
, char *name
)
795 if (str_cmp_unqual_hostname(nl
->name
, name
) == 0) {
808 create_file(char *name
)
813 * The file might already exist. If it does, we ask for only write
814 * permission, since that's all the file was created with.
816 if ((fd
= open(name
, O_CREAT
| O_WRONLY
, S_IWUSR
)) == -1) {
817 if (errno
!= EEXIST
) {
818 syslog(LOG_ERR
, "can't open %s: %m", name
);
824 (void) printf("%s is created\n", name
);
826 syslog(LOG_ERR
, "statd: close, error %m\n");
834 * Deletes the file specified by name.
837 delete_file(char *name
)
840 (void) printf("Remove monitor entry %s\n", name
);
841 if (unlink(name
) == -1) {
843 syslog(LOG_ERR
, "statd: unlink of %s, error %m", name
);
848 * Return 1 if file is a symlink, else 0.
851 is_symlink(char *file
)
857 bzero((caddr_t
)&lbuf
, sizeof (lbuf
));
858 error
= lstat(file
, &lbuf
);
859 } while (error
== EINTR
);
862 return ((lbuf
.st_mode
& S_IFMT
) == S_IFLNK
);
869 * Moves the file specified by `from' to `to' only if the
870 * new file is guaranteed to be created (which is presumably
871 * why we don't just do a rename(2)). If `from' is a
872 * symlink, the destination file will be a similar symlink
873 * in the directory of `to'.
875 * Returns 0 for success, 1 for failure.
878 move_file(char *fromdir
, char *file
, char *todir
)
881 char rname
[MAXNAMELEN
+ 1]; /* +1 for the terminating NULL */
882 char from
[MAXPATHLEN
];
885 (void) strcpy(from
, fromdir
);
886 (void) strcat(from
, "/");
887 (void) strcat(from
, file
);
888 if (is_symlink(from
)) {
890 * Dig out the name of the regular file the link points to.
892 n
= readlink(from
, rname
, MAXNAMELEN
);
895 (void) printf("move_file: can't read link %s\n",
905 if (create_symlink(todir
, rname
, file
) != 0) {
910 * Do what we've always done to move regular files.
912 (void) strcpy(to
, todir
);
913 (void) strcat(to
, "/");
914 (void) strcat(to
, file
);
915 if (create_file(to
) != 0) {
921 * Remove the old file if we've created the new one.
923 if (unlink(from
) < 0) {
924 syslog(LOG_ERR
, "move_file: unlink of %s, error %m", from
);
932 * Create a symbolic link named `lname' to regular file `rname'.
933 * Both files should be in directory `todir'.
936 create_symlink(char *todir
, char *rname
, char *lname
)
939 char lpath
[MAXPATHLEN
];
942 * Form the full pathname of the link.
944 (void) strcpy(lpath
, todir
);
945 (void) strcat(lpath
, "/");
946 (void) strcat(lpath
, lname
);
949 * Now make the new symlink ...
951 if (symlink(rname
, lpath
) < 0) {
953 if (error
!= 0 && error
!= EEXIST
) {
955 (void) printf("create_symlink: can't link "
956 "%s/%s -> %s\n", todir
, lname
, rname
);
963 if (error
== EEXIST
) {
964 (void) printf("link %s/%s -> %s already exists\n",
965 todir
, lname
, rname
);
967 (void) printf("created link %s/%s -> %s\n",
968 todir
, lname
, rname
);
976 * remove the name from the specified directory
981 remove_name(char *name
, int op
, int startup
)
988 alt_dir
= "statmon/sm";
991 alt_dir
= "statmon/sm.bak";
995 remove_single_name(name
, queue
, NULL
);
997 * At startup, entries have not yet been copied to alternate
998 * directories and thus do not need to be removed.
1001 for (i
= 0; i
< pathix
; i
++) {
1002 remove_single_name(name
, path_name
[i
], alt_dir
);
1008 * Remove the name from the specified directory, which is dir1/dir2 or
1009 * dir1, depending on whether dir2 is NULL.
1012 remove_single_name(char *name
, char *dir1
, char *dir2
)
1015 char path
[MAXPATHLEN
+MAXNAMELEN
+SM_MAXPATHLEN
]; /* why > MAXPATHLEN? */
1016 char dirpath
[MAXPATHLEN
];
1017 char rname
[MAXNAMELEN
+ 1]; /* +1 for NULL term */
1019 if (strlen(name
) + strlen(dir1
) + (dir2
!= NULL
? strlen(dir2
) : 0) +
1023 "statd: pathname too long: %s/%s/%s\n",
1027 "statd: pathname too long: %s/%s\n",
1033 (void) strcpy(path
, dir1
);
1034 (void) strcat(path
, "/");
1036 (void) strcat(path
, dir2
);
1037 (void) strcat(path
, "/");
1039 (void) strcpy(dirpath
, path
); /* save here - we may need it shortly */
1040 (void) strcat(path
, name
);
1043 * Despite the name of this routine :-@), `path' may be a symlink
1044 * to a regular file. If it is, and if that file has no other
1045 * links to it, we must remove it now as well.
1047 if (is_symlink(path
)) {
1048 n
= readlink(path
, rname
, MAXNAMELEN
);
1052 if (count_symlinks(dirpath
, rname
, &n
) < 0) {
1057 (void) strcat(dirpath
, rname
);
1058 error
= unlink(dirpath
);
1062 "remove_name: can't "
1067 "remove_name: unlinked ",
1074 * Policy: if we can't read the symlink, leave it
1075 * here for analysis by the system administrator.
1078 "statd: can't read link %s: %m\n", path
);
1083 * If it's a regular file, we can assume all symlinks and the
1084 * files to which they refer have been processed already - just
1085 * fall through to here to remove it.
1091 * Count the number of symlinks in `dir' which point to `name' (also in dir).
1092 * Passes back symlink count in `count'.
1093 * Returns 0 for success, < 0 for failure.
1096 count_symlinks(char *dir
, char *name
, int *count
)
1101 struct dirent
*dirp
;
1102 char lpath
[MAXPATHLEN
];
1103 char rname
[MAXNAMELEN
+ 1]; /* +1 for term NULL */
1105 if ((dp
= opendir(dir
)) == NULL
) {
1106 syslog(LOG_ERR
, "count_symlinks: open %s dir, error %m\n",
1111 while ((dirp
= readdir(dp
)) != NULL
) {
1112 if (strcmp(dirp
->d_name
, ".") == 0 ||
1113 strcmp(dirp
->d_name
, "..") == 0) {
1117 (void) sprintf(lpath
, "%s%s", dir
, dirp
->d_name
);
1118 if (is_symlink(lpath
)) {
1120 * Fetch the name of the file the symlink refers to.
1122 n
= readlink(lpath
, rname
, MAXNAMELEN
);
1126 "count_symlinks: can't read link "
1134 * If `rname' matches `name', bump the count. There
1135 * may well be multiple symlinks to the same name, so
1136 * we must continue to process the entire directory.
1138 if (strcmp(rname
, name
) == 0) {
1144 (void) closedir(dp
);
1147 (void) printf("count_symlinks: found %d symlinks\n", cnt
);
1154 * Manage the cache of hostnames. An entry for each host that has recently
1155 * locked a file is kept. There is an in-ram table (record_table) and an empty
1156 * file in the file system name space (/var/statmon/sm/<name>). This
1157 * routine adds (deletes) the name to (from) the in-ram table and the entry
1158 * to (from) the file system name space.
1160 * If op == 1 then the name is added to the queue otherwise the name is
1164 record_name(char *name
, int op
)
1168 char path
[MAXPATHLEN
+MAXNAMELEN
+SM_MAXPATHLEN
];
1169 name_entry
**record_q
;
1173 * These names are supposed to be just host names, not paths or
1174 * other arbitrary files.
1175 * manipulating the empty pathname unlinks CURRENT,
1176 * manipulating files with '/' would allow you to create and unlink
1177 * files all over the system; LOG_AUTH, it's a security thing.
1178 * Don't remove the directories . and ..
1183 if (name
[0] == '\0' || strchr(name
, '/') != NULL
||
1184 strcmp(name
, ".") == 0 || strcmp(name
, "..") == 0) {
1185 syslog(LOG_ERR
|LOG_AUTH
, "statd: attempt to %s \"%s/%s\"",
1186 op
== 1 ? "create" : "remove", CURRENT
, name
);
1193 (void) printf("inserting %s at hash %d,\n",
1196 (void) printf("deleting %s at hash %d\n", name
, hash
);
1201 if (op
== 1) { /* insert */
1202 mutex_lock(&record_table
[hash
].lock
);
1203 record_q
= &record_table
[hash
].sm_rechdp
;
1204 if ((nl
= find_name(record_q
, name
)) == NULL
) {
1208 if ((nl
= insert_name(record_q
, name
, 1)) !=
1209 (name_entry
*) NULL
)
1211 mutex_unlock(&record_table
[hash
].lock
);
1212 /* make an entry in current directory */
1214 path_len
= strlen(CURRENT
) + strlen(name
) + 2;
1215 if (path_len
> MAXPATHLEN
) {
1217 "statd: pathname too long: %s/%s\n",
1221 (void) strcpy(path
, CURRENT
);
1222 (void) strcat(path
, "/");
1223 (void) strcat(path
, name
);
1224 (void) create_file(path
);
1226 (void) printf("After insert_name\n");
1229 /* make an entry in alternate paths */
1230 for (i
= 0; i
< pathix
; i
++) {
1231 path_len
= strlen(path_name
[i
]) +
1232 strlen("/statmon/sm/") + strlen(name
) + 1;
1234 if (path_len
> MAXPATHLEN
) {
1235 syslog(LOG_ERR
, "statd: pathname too "
1236 "long: %s/statmon/sm/%s\n",
1237 path_name
[i
], name
);
1240 (void) strcpy(path
, path_name
[i
]);
1241 (void) strcat(path
, "/statmon/sm/");
1242 (void) strcat(path
, name
);
1243 (void) create_file(path
);
1248 mutex_unlock(&record_table
[hash
].lock
);
1250 } else { /* delete */
1251 mutex_lock(&record_table
[hash
].lock
);
1252 record_q
= &record_table
[hash
].sm_rechdp
;
1253 if ((nl
= find_name(record_q
, name
)) == NULL
) {
1254 mutex_unlock(&record_table
[hash
].lock
);
1258 if (nl
->count
== 0) {
1259 delete_name(record_q
, name
);
1260 mutex_unlock(&record_table
[hash
].lock
);
1261 /* remove this entry from current directory */
1262 remove_name(name
, 0, 0);
1264 mutex_unlock(&record_table
[hash
].lock
);
1266 (void) printf("After delete_name \n");
1273 * This routine adds a symlink in the form of an ASCII dotted quad
1274 * IP address that is linked to the name already recorded in the
1275 * filesystem name space by record_name(). Enough information is
1276 * (hopefully) provided to support other address types in the future.
1277 * The purpose of this is to cache enough information to contact
1278 * hosts in other domains during server crash recovery (see bugid
1281 * The worst failure mode here is that the symlink is not made, and
1282 * statd falls back to the old buggy behavior.
1285 record_addr(char *name
, sa_family_t family
, struct netobj
*ah
)
1290 struct in_addr addr
;
1292 char ascii_addr
[MAXNAMELEN
];
1293 char path
[MAXPATHLEN
];
1295 if (family
== AF_INET
) {
1296 if (ah
->n_len
!= sizeof (struct in_addr
))
1298 addr
= *(struct in_addr
*)ah
->n_bytes
;
1299 } else if (family
== AF_INET6
) {
1300 if (ah
->n_len
!= sizeof (struct in6_addr
))
1302 addr6
= (char *)ah
->n_bytes
;
1307 if (family
== AF_INET
)
1308 (void) printf("record_addr: addr= %x\n", addr
.s_addr
);
1309 else if (family
== AF_INET6
)
1310 (void) printf("record_addr: addr= %x\n",
1311 ((struct in6_addr
*)addr6
)->s6_addr
);
1314 if (family
== AF_INET
) {
1315 if (addr
.s_addr
== INADDR_ANY
||
1316 ((addr
.s_addr
&& 0xff000000) == 0)) {
1318 "record_addr: illegal IP address %x\n",
1324 /* convert address to ASCII */
1325 famstr
= family2string(family
);
1326 if (famstr
== NULL
) {
1328 "record_addr: unsupported address family %d\n",
1334 char abuf
[INET6_ADDRSTRLEN
];
1336 (void) sprintf(ascii_addr
, "%s.%s", famstr
, inet_ntoa(addr
));
1340 (void) sprintf(ascii_addr
, "%s.%s", famstr
,
1341 inet_ntop(family
, addr6
, abuf
, sizeof (abuf
)));
1347 "record_addr: family2string supports unknown "
1348 "family %d (%s)\n", family
, famstr
);
1355 (void) printf("record_addr: ascii_addr= %s\n", ascii_addr
);
1360 * Make the symlink in CURRENT. The `name' file should have
1361 * been created previously by record_name().
1363 (void) create_symlink(CURRENT
, name
, ascii_addr
);
1366 * Similarly for alternate paths.
1368 for (i
= 0; i
< pathix
; i
++) {
1369 path_len
= strlen(path_name
[i
]) +
1370 strlen("/statmon/sm/") +
1373 if (path_len
> MAXPATHLEN
) {
1375 "statd: pathname too long: %s/statmon/sm/%s\n",
1376 path_name
[i
], name
);
1379 (void) strcpy(path
, path_name
[i
]);
1380 (void) strcat(path
, "/statmon/sm");
1381 (void) create_symlink(path
, name
, ascii_addr
);
1386 * SM_CRASH - simulate a crash of statd.
1391 name_entry
*nl
, *next
;
1392 mon_entry
*nl_monp
, *mon_next
;
1396 for (k
= 0; k
< MAX_HASHSIZE
; k
++) {
1397 mutex_lock(&mon_table
[k
].lock
);
1398 if ((mon_next
= mon_table
[k
].sm_monhdp
) ==
1399 (mon_entry
*) NULL
) {
1400 mutex_unlock(&mon_table
[k
].lock
);
1403 while ((nl_monp
= mon_next
) != NULL
) {
1404 mon_next
= mon_next
->nxt
;
1405 nl_idp
= &nl_monp
->id
.mon_id
.my_id
;
1406 free(nl_monp
->id
.mon_id
.mon_name
);
1407 free(nl_idp
->my_name
);
1410 mon_table
[k
].sm_monhdp
= NULL
;
1412 mutex_unlock(&mon_table
[k
].lock
);
1415 /* Clean up entries in record table */
1416 for (k
= 0; k
< MAX_HASHSIZE
; k
++) {
1417 mutex_lock(&record_table
[k
].lock
);
1418 if ((next
= record_table
[k
].sm_rechdp
) ==
1419 (name_entry
*) NULL
) {
1420 mutex_unlock(&record_table
[k
].lock
);
1423 while ((nl
= next
) != NULL
) {
1428 record_table
[k
].sm_rechdp
= NULL
;
1430 mutex_unlock(&record_table
[k
].lock
);
1433 /* Clean up entries in recovery table */
1434 mutex_lock(&recov_q
.lock
);
1435 if ((next
= recov_q
.sm_recovhdp
) != NULL
) {
1436 while ((nl
= next
) != NULL
) {
1441 recov_q
.sm_recovhdp
= NULL
;
1443 mutex_unlock(&recov_q
.lock
);
1448 * Initialize the hash tables: mon_table, record_table, recov_q and
1457 (void) printf("Initializing hash tables\n");
1458 for (k
= 0; k
< MAX_HASHSIZE
; k
++) {
1459 mon_table
[k
].sm_monhdp
= NULL
;
1460 record_table
[k
].sm_rechdp
= NULL
;
1461 mutex_init(&mon_table
[k
].lock
, USYNC_THREAD
, NULL
);
1462 mutex_init(&record_table
[k
].lock
, USYNC_THREAD
, NULL
);
1464 mutex_init(&recov_q
.lock
, USYNC_THREAD
, NULL
);
1465 recov_q
.sm_recovhdp
= NULL
;
1470 * Maps a socket address family to a name string, or NULL if the family
1471 * is not supported by statd.
1472 * Caller is responsible for freeing storage used by result string, if any.
1475 family2string(sa_family_t family
)
1481 rc
= strdup(SM_ADDR_IPV4
);
1485 rc
= strdup(SM_ADDR_IPV6
);
1497 * Prints out list in record_table if flag is 1 otherwise
1498 * prints out each list in recov_q specified by name.
1501 pr_name(char *name
, int flag
)
1510 (void) printf("*****record_q: ");
1511 mutex_lock(&record_table
[hash
].lock
);
1512 nl
= record_table
[hash
].sm_rechdp
;
1513 while (nl
!= NULL
) {
1514 (void) printf("(%x), ", (int)nl
);
1517 mutex_unlock(&record_table
[hash
].lock
);
1519 (void) printf("*****recovery_q: ");
1520 mutex_lock(&recov_q
.lock
);
1521 nl
= recov_q
.sm_recovhdp
;
1522 while (nl
!= NULL
) {
1523 (void) printf("(%x), ", (int)nl
);
1526 mutex_unlock(&recov_q
.lock
);
1529 (void) printf("\n");