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 } 1557