1/* $NetBSD: amq.c,v 1.1.1.2 2009/03/20 20:26:50 christos Exp $ */ 2 3/* 4 * Copyright (c) 1997-2009 Erez Zadok 5 * Copyright (c) 1990 Jan-Simon Pendry 6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1990 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Jan-Simon Pendry at Imperial College, London. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgment: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * 42 * File: am-utils/amq/amq.c 43 * 44 */ 45 46/* 47 * Automounter query tool 48 */ 49 50#ifdef HAVE_CONFIG_H 51# include <config.h> 52#endif /* HAVE_CONFIG_H */ 53#include <am_defs.h> 54#include <amq.h> 55 56/* locals */ 57static int flush_flag; 58static int getpid_flag; 59static int getpwd_flag; 60static int getvers_flag; 61static int minfo_flag; 62static int quiet_flag; 63static int stats_flag; 64static int unmount_flag; 65static int use_tcp_flag; 66static int use_udp_flag; 67static u_long amd_program_number = AMQ_PROGRAM; 68static char *debug_opts; 69static char *amq_logfile; 70static char *xlog_optstr; 71static char localhost[] = "localhost"; 72static char *def_server = localhost; 73 74/* externals */ 75extern int optind; 76extern char *optarg; 77 78/* structures */ 79enum show_opt { 80 Full, Stats, Calc, Short, ShowDone 81}; 82 83 84/* 85 * If (e) is Calc then just calculate the sizes 86 * Otherwise display the mount node on stdout 87 */ 88static void 89show_mti(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *twid) 90{ 91 switch (e) { 92 case Calc: 93 { 94 int mw = strlen(mt->mt_mountinfo); 95 int dw = strlen(mt->mt_directory); 96 int tw = strlen(mt->mt_type); 97 if (mw > *mwid) 98 *mwid = mw; 99 if (dw > *dwid) 100 *dwid = dw; 101 if (tw > *twid) 102 *twid = tw; 103 } 104 break; 105 106 case Full: 107 { 108 struct tm *tp = localtime((time_t *) ((voidp) &mt->mt_mounttime)); 109 printf("%-*.*s %-*.*s %-*.*s %s\n\t%-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%04d %02d:%02d:%02d\n", 110 *dwid, *dwid, 111 *mt->mt_directory ? mt->mt_directory : "/", /* XXX */ 112 *twid, *twid, 113 mt->mt_type, 114 *mwid, *mwid, 115 mt->mt_mountinfo, 116 mt->mt_mountpoint, 117 118 mt->mt_mountuid, 119 mt->mt_getattr, 120 mt->mt_lookup, 121 mt->mt_readdir, 122 mt->mt_readlink, 123 mt->mt_statfs, 124 125 tp->tm_mon + 1, tp->tm_mday, 126 tp->tm_year < 1900 ? tp->tm_year + 1900 : tp->tm_year, 127 tp->tm_hour, tp->tm_min, tp->tm_sec); 128 } 129 break; 130 131 case Stats: 132 { 133 struct tm *tp = localtime((time_t *) ((voidp) &mt->mt_mounttime)); 134 printf("%-*.*s %-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%04d\n", 135 *dwid, *dwid, 136 *mt->mt_directory ? mt->mt_directory : "/", /* XXX */ 137 138 mt->mt_mountuid, 139 mt->mt_getattr, 140 mt->mt_lookup, 141 mt->mt_readdir, 142 mt->mt_readlink, 143 mt->mt_statfs, 144 145 tp->tm_mon + 1, tp->tm_mday, 146 tp->tm_year < 1900 ? tp->tm_year + 1900 : tp->tm_year, 147 tp->tm_hour, tp->tm_min, tp->tm_sec); 148 } 149 break; 150 151 case Short: 152 { 153 printf("%-*.*s %-*.*s %-*.*s %s\n", 154 *dwid, *dwid, 155 *mt->mt_directory ? mt->mt_directory : "/", 156 *twid, *twid, 157 mt->mt_type, 158 *mwid, *mwid, 159 mt->mt_mountinfo, 160 mt->mt_mountpoint); 161 } 162 break; 163 164 default: 165 break; 166 } 167} 168 169 170/* 171 * Display a pwd data 172 */ 173static void 174show_pwd(amq_mount_tree *mt, char *path, size_t l, int *flag) 175{ 176 int len; 177 178 while (mt) { 179 len = strlen(mt->mt_mountpoint); 180 if (NSTREQ(path, mt->mt_mountpoint, len) && 181 !STREQ(mt->mt_directory, mt->mt_mountpoint)) { 182 char buf[MAXPATHLEN+1]; /* must be same size as 'path' */ 183 xstrlcpy(buf, mt->mt_directory, sizeof(buf)); 184 xstrlcat(buf, &path[len], sizeof(buf)); 185 xstrlcpy(path, buf, l); 186 *flag = 1; 187 } 188 show_pwd(mt->mt_next, path, l, flag); 189 mt = mt->mt_child; 190 } 191} 192 193 194/* 195 * Display a mount tree. 196 */ 197static void 198show_mt(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *pwid) 199{ 200 while (mt) { 201 show_mti(mt, e, mwid, dwid, pwid); 202 show_mt(mt->mt_next, e, mwid, dwid, pwid); 203 mt = mt->mt_child; 204 } 205} 206 207 208static void 209show_mi(amq_mount_info_list *ml, enum show_opt e, int *mwid, int *dwid, int *twid) 210{ 211 u_int i; 212 213 switch (e) { 214 215 case Calc: 216 { 217 for (i = 0; i < ml->amq_mount_info_list_len; i++) { 218 amq_mount_info *mi = &ml->amq_mount_info_list_val[i]; 219 int mw = strlen(mi->mi_mountinfo); 220 int dw = strlen(mi->mi_mountpt); 221 int tw = strlen(mi->mi_type); 222 if (mw > *mwid) 223 *mwid = mw; 224 if (dw > *dwid) 225 *dwid = dw; 226 if (tw > *twid) 227 *twid = tw; 228 } 229 } 230 break; 231 232 case Full: 233 { 234 for (i = 0; i < ml->amq_mount_info_list_len; i++) { 235 amq_mount_info *mi = &ml->amq_mount_info_list_val[i]; 236 printf("%-*.*s %-*.*s %-*.*s %-3d %s is %s", 237 *mwid, *mwid, mi->mi_mountinfo, 238 *dwid, *dwid, mi->mi_mountpt, 239 *twid, *twid, mi->mi_type, 240 mi->mi_refc, mi->mi_fserver, 241 mi->mi_up > 0 ? "up" : 242 mi->mi_up < 0 ? "starting" : "down"); 243 if (mi->mi_error > 0) { 244 printf(" (%s)", strerror(mi->mi_error)); 245 } else if (mi->mi_error < 0) { 246 fputs(" (in progress)", stdout); 247 } 248 fputc('\n', stdout); 249 } 250 } 251 break; 252 253 default: 254 break; 255 } 256} 257 258 259/* 260 * Display general mount statistics 261 */ 262static void 263show_ms(amq_mount_stats *ms) 264{ 265 printf("\ 266requests stale mount mount unmount\n\ 267deferred fhandles ok failed failed\n\ 268%-9d %-9d %-9d %-9d %-9d\n", 269 ms->as_drops, ms->as_stale, ms->as_mok, ms->as_merr, ms->as_uerr); 270} 271 272 273#if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) 274static char * 275cluster_server(void) 276{ 277 struct cct_entry *cp; 278 279 if (cnodeid() == 0) { 280 /* 281 * Not clustered 282 */ 283 return def_server; 284 } 285 while (cp = getccent()) 286 if (cp->cnode_type == 'r') 287 return cp->cnode_name; 288 289 return def_server; 290} 291#endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */ 292 293 294static void 295print_umnt_error(amq_sync_umnt *rv, const char *fs) 296{ 297 298 switch (rv->au_etype) { 299 case AMQ_UMNT_OK: 300 break; 301 case AMQ_UMNT_FAILED: 302 printf("unmount failed: %s\n", strerror(rv->au_errno)); 303 break; 304 case AMQ_UMNT_FORK: 305 if (rv->au_errno == 0) 306 printf("%s is not mounted\n", fs); 307 else 308 printf("falling back to asynchronous unmount: %s\n", 309 strerror(rv->au_errno)); 310 break; 311 case AMQ_UMNT_READ: 312 printf("pipe read error: %s\n", strerror(rv->au_errno)); 313 break; 314 case AMQ_UMNT_SERVER: 315 printf("amd server down\n"); 316 break; 317 case AMQ_UMNT_SIGNAL: 318 printf("got signal: %d\n", rv->au_signal); 319 break; 320 /* 321 * Omit default so the compiler can check for missing cases. 322 * 323 default: 324 break; 325 */ 326 } 327} 328 329 330static int 331amu_sync_umnt_to_retval(amq_sync_umnt *rv) 332{ 333 switch (rv->au_etype) { 334 case AMQ_UMNT_FORK: 335 if (rv->au_errno == 0) { 336 /* 337 * We allow this error so that things like: 338 * amq -uu /l/cd0d && eject cd0 339 * will work when /l/cd0d is not mounted. 340 * XXX - We still print an error message. 341 */ 342 return 0; 343 } 344 default: 345 return rv->au_etype; 346 } 347} 348 349 350static int 351clnt_failed(CLIENT *clnt, char *server) 352{ 353 fprintf(stderr, "%s: ", am_get_progname()); 354 clnt_perror(clnt, server); 355 return 1; 356} 357 358 359/* 360 * MAIN 361 */ 362int 363main(int argc, char *argv[]) 364{ 365 int opt_ch; 366 int errs = 0; 367 char *server; 368 struct sockaddr_in server_addr; 369 CLIENT *clnt = NULL; 370 struct hostent *hp; 371 int nodefault = 0; 372 struct timeval tv; 373 char *progname = NULL; 374 375 /* 376 * Compute program name 377 */ 378 if (argv[0]) { 379 progname = strrchr(argv[0], '/'); 380 if (progname && progname[1]) 381 progname++; 382 else 383 progname = argv[0]; 384 } 385 if (!progname) 386 progname = "amq"; 387 am_set_progname(progname); 388 389 /* 390 * Parse arguments 391 */ 392 while ((opt_ch = getopt(argc, argv, "Hfh:l:mqsuvx:D:pP:TUw")) != -1) 393 switch (opt_ch) { 394 case 'H': 395 goto show_usage; 396 break; 397 398 case 'f': 399 flush_flag = 1; 400 nodefault = 1; 401 break; 402 403 case 'h': 404 def_server = optarg; 405 break; 406 407 case 'l': 408 amq_logfile = optarg; 409 nodefault = 1; 410 break; 411 412 case 'm': 413 minfo_flag = 1; 414 nodefault = 1; 415 break; 416 417 case 'p': 418 getpid_flag = 1; 419 nodefault = 1; 420 break; 421 422 case 'q': 423 quiet_flag = 1; 424 nodefault = 1; 425 break; 426 427 case 's': 428 stats_flag = 1; 429 nodefault = 1; 430 break; 431 432 case 'u': 433 unmount_flag++; 434 nodefault = 1; 435 break; 436 437 case 'v': 438 getvers_flag = 1; 439 nodefault = 1; 440 break; 441 442 case 'x': 443 xlog_optstr = optarg; 444 nodefault = 1; 445 break; 446 447 case 'D': 448 debug_opts = optarg; 449 nodefault = 1; 450 break; 451 452 case 'P': 453 amd_program_number = atoi(optarg); 454 break; 455 456 case 'T': 457 use_tcp_flag = 1; 458 break; 459 460 case 'U': 461 use_udp_flag = 1; 462 break; 463 464 case 'w': 465 getpwd_flag = 1; 466 break; 467 468 default: 469 errs = 1; 470 break; 471 } 472 473 if (optind == argc) { 474 if (unmount_flag) 475 errs = 1; 476 } 477 if (errs) { 478 show_usage: 479 fprintf(stderr, "\ 480Usage: %s [-fmpqsvwHTU] [-h hostname] [-l log_file|\"syslog\"]\n\ 481\t[-x log_options] [-D debug_options]\n\ 482\t[-P program_number] [[-u[u]] directory ...]\n", 483 am_get_progname() 484 ); 485 exit(1); 486 } 487 488 489 /* set use_udp and use_tcp flags both to on if none are defined */ 490 if (!use_tcp_flag && !use_udp_flag) 491 use_tcp_flag = use_udp_flag = 1; 492 493#if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) 494 /* 495 * Figure out root server of cluster 496 */ 497 if (def_server == localhost) 498 server = cluster_server(); 499 else 500#endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */ 501 server = def_server; 502 503 /* 504 * Get address of server 505 */ 506 if ((hp = gethostbyname(server)) == 0 && !STREQ(server, localhost)) { 507 fprintf(stderr, "%s: Can't get address of %s\n", 508 am_get_progname(), server); 509 exit(1); 510 } 511 memset(&server_addr, 0, sizeof(server_addr)); 512 /* as per POSIX, sin_len need not be set (used internally by kernel) */ 513 server_addr.sin_family = AF_INET; 514 if (hp) { 515 memmove((voidp) &server_addr.sin_addr, (voidp) hp->h_addr, 516 sizeof(server_addr.sin_addr)); 517 } else { 518 /* fake "localhost" */ 519 server_addr.sin_addr.s_addr = htonl(0x7f000001); 520 } 521 522 /* 523 * Create RPC endpoint 524 */ 525 tv.tv_sec = 5; /* 5 seconds for timeout or per retry */ 526 tv.tv_usec = 0; 527 528 if (use_tcp_flag) /* try tcp first */ 529 clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "tcp"); 530 if (!clnt && use_udp_flag) { /* try udp next */ 531 clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "udp"); 532 /* if ok, set timeout (valid for connectionless transports only) */ 533 if (clnt) 534 clnt_control(clnt, CLSET_RETRY_TIMEOUT, (char *) &tv); 535 } 536 if (!clnt) { 537 fprintf(stderr, "%s: ", am_get_progname()); 538 clnt_pcreateerror(server); 539 exit(1); 540 } 541 542 /* 543 * Control debugging 544 */ 545 if (debug_opts) { 546 int *rc; 547 amq_setopt opt; 548 opt.as_opt = AMOPT_DEBUG; 549 opt.as_str = debug_opts; 550 rc = amqproc_setopt_1(&opt, clnt); 551 if (rc && *rc < 0) { 552 fprintf(stderr, "%s: daemon not compiled for debug\n", 553 am_get_progname()); 554 errs = 1; 555 } else if (!rc || *rc > 0) { 556 fprintf(stderr, "%s: debug setting for \"%s\" failed\n", 557 am_get_progname(), debug_opts); 558 errs = 1; 559 } 560 } 561 562 /* 563 * Control logging 564 */ 565 if (xlog_optstr) { 566 int *rc; 567 amq_setopt opt; 568 opt.as_opt = AMOPT_XLOG; 569 opt.as_str = xlog_optstr; 570 rc = amqproc_setopt_1(&opt, clnt); 571 if (!rc || *rc) { 572 fprintf(stderr, "%s: setting log level to \"%s\" failed\n", 573 am_get_progname(), xlog_optstr); 574 errs = 1; 575 } 576 } 577 578 /* 579 * Control log file 580 */ 581 if (amq_logfile) { 582 int *rc; 583 amq_setopt opt; 584 opt.as_opt = AMOPT_LOGFILE; 585 opt.as_str = amq_logfile; 586 rc = amqproc_setopt_1(&opt, clnt); 587 if (!rc || *rc) { 588 fprintf(stderr, "%s: setting logfile to \"%s\" failed\n", 589 am_get_progname(), amq_logfile); 590 errs = 1; 591 } 592 } 593 594 /* 595 * Flush map cache 596 */ 597 if (flush_flag) { 598 int *rc; 599 amq_setopt opt; 600 opt.as_opt = AMOPT_FLUSHMAPC; 601 opt.as_str = ""; 602 rc = amqproc_setopt_1(&opt, clnt); 603 if (!rc || *rc) { 604 fprintf(stderr, "%s: amd on %s cannot flush the map cache\n", 605 am_get_progname(), server); 606 errs = 1; 607 } 608 } 609 610 /* 611 * getpwd info 612 */ 613 if (getpwd_flag) { 614 char path[MAXPATHLEN+1]; 615 char *wd = getcwd(path, MAXPATHLEN+1); 616 amq_mount_tree_list *mlp = amqproc_export_1((voidp) 0, clnt); 617 amq_mount_tree_p mt; 618 u_int i; 619 int flag; 620 621 if (!wd) { 622 perror("getcwd"); 623 exit(1); 624 } 625 for (i = 0; mlp && i < mlp->amq_mount_tree_list_len; i++) { 626 mt = mlp->amq_mount_tree_list_val[i]; 627 while (1) { 628 flag = 0; 629 show_pwd(mt, path, sizeof(path), &flag); 630 if (!flag) { 631 printf("%s\n", path); 632 break; 633 } 634 } 635 } 636 exit(0); 637 } 638 639 /* 640 * Mount info 641 */ 642 if (minfo_flag) { 643 int dummy; 644 amq_mount_info_list *ml = amqproc_getmntfs_1(&dummy, clnt); 645 if (ml) { 646 int mwid = 0, dwid = 0, twid = 0; 647 show_mi(ml, Calc, &mwid, &dwid, &twid); 648 mwid++; 649 dwid++; 650 twid++; 651 show_mi(ml, Full, &mwid, &dwid, &twid); 652 653 } else { 654 fprintf(stderr, "%s: amd on %s cannot provide mount info\n", 655 am_get_progname(), server); 656 } 657 } 658 659 /* 660 * Get Version 661 */ 662 if (getvers_flag) { 663 amq_string *spp = amqproc_getvers_1((voidp) 0, clnt); 664 if (spp && *spp) { 665 fputs(*spp, stdout); 666 XFREE(*spp); 667 } else { 668 fprintf(stderr, "%s: failed to get version information\n", 669 am_get_progname()); 670 errs = 1; 671 } 672 } 673 674 /* 675 * Get PID of amd 676 */ 677 if (getpid_flag) { 678 int *ip = amqproc_getpid_1((voidp) 0, clnt); 679 if (ip && *ip) { 680 printf("%d\n", *ip); 681 } else { 682 fprintf(stderr, "%s: failed to get PID of amd\n", am_get_progname()); 683 errs = 1; 684 } 685 } 686 687 /* 688 * Apply required operation to all remaining arguments 689 */ 690 if (optind < argc) { 691 do { 692 char *fs = argv[optind++]; 693 if (unmount_flag > 1) { 694 amq_sync_umnt *sup; 695 /* 696 * Synchronous unmount request 697 */ 698 sup = amqproc_sync_umnt_1(&fs, clnt); 699 if (sup) { 700 if (quiet_flag == 0) 701 print_umnt_error(sup, fs); 702 errs = amu_sync_umnt_to_retval(sup); 703 } else { 704 errs = clnt_failed(clnt, server); 705 } 706 } else if (unmount_flag) { 707 /* 708 * Unmount request 709 */ 710 amqproc_umnt_1(&fs, clnt); 711 } else { 712 /* 713 * Stats request 714 */ 715 amq_mount_tree_p *mtp = amqproc_mnttree_1(&fs, clnt); 716 if (mtp) { 717 amq_mount_tree *mt = *mtp; 718 if (mt) { 719 int mwid = 0, dwid = 0, twid = 0; 720 show_mt(mt, Calc, &mwid, &dwid, &twid); 721 mwid++; 722 dwid++, twid++; 723 printf("%-*.*s Uid Getattr Lookup RdDir RdLnk Statfs Mounted@\n", 724 dwid, dwid, "What"); 725 show_mt(mt, Stats, &mwid, &dwid, &twid); 726 } else { 727 fprintf(stderr, "%s: %s not automounted\n", am_get_progname(), fs); 728 } 729 xdr_pri_free((XDRPROC_T_TYPE) xdr_amq_mount_tree_p, (caddr_t) mtp); 730 } else { 731 errs = clnt_failed(clnt, server); 732 } 733 } 734 } while (optind < argc); 735 736 } else if (unmount_flag) { 737 goto show_usage; 738 739 } else if (stats_flag) { 740 amq_mount_stats *ms = amqproc_stats_1((voidp) 0, clnt); 741 if (ms) { 742 show_ms(ms); 743 } else { 744 errs = clnt_failed(clnt, server); 745 } 746 747 } else if (!nodefault) { 748 amq_mount_tree_list *mlp = amqproc_export_1((voidp) 0, clnt); 749 if (mlp) { 750 enum show_opt e = Calc; 751 int mwid = 0, dwid = 0, pwid = 0; 752 753 while (e != ShowDone) { 754 u_int i; 755 for (i = 0; i < mlp->amq_mount_tree_list_len; i++) { 756 show_mt(mlp->amq_mount_tree_list_val[i], 757 e, &mwid, &dwid, &pwid); 758 } 759 mwid++; 760 dwid++, pwid++; 761 if (e == Calc) 762 e = Short; 763 else if (e == Short) 764 e = ShowDone; 765 } 766 767 } else { 768 errs = clnt_failed(clnt, server); 769 } 770 } 771 exit(errs); 772 return errs; /* should never reach here */ 773} 774