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