1 /* $NetBSD: amq.c,v 1.1.1.1 2008/09/19 20:07:17 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2007 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 */ 57 static int flush_flag; 58 static int minfo_flag; 59 static int getpid_flag; 60 static int unmount_flag; 61 static int stats_flag; 62 static int getvers_flag; 63 static u_long amd_program_number = AMQ_PROGRAM; 64 static int use_tcp_flag, use_udp_flag; 65 static int getpwd_flag; 66 static char *debug_opts; 67 static char *amq_logfile; 68 static char *xlog_optstr; 69 static char localhost[] = "localhost"; 70 static char *def_server = localhost; 71 72 /* externals */ 73 extern int optind; 74 extern char *optarg; 75 76 /* structures */ 77 enum show_opt { 78 Full, Stats, Calc, Short, ShowDone 79 }; 80 81 82 /* 83 * If (e) is Calc then just calculate the sizes 84 * Otherwise display the mount node on stdout 85 */ 86 static void 87 show_mti(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *twid) 88 { 89 switch (e) { 90 case Calc: 91 { 92 int mw = strlen(mt->mt_mountinfo); 93 int dw = strlen(mt->mt_directory); 94 int tw = strlen(mt->mt_type); 95 if (mw > *mwid) 96 *mwid = mw; 97 if (dw > *dwid) 98 *dwid = dw; 99 if (tw > *twid) 100 *twid = tw; 101 } 102 break; 103 104 case Full: 105 { 106 struct tm *tp = localtime((time_t *) ((voidp) &mt->mt_mounttime)); 107 printf("%-*.*s %-*.*s %-*.*s %s\n\t%-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%04d %02d:%02d:%02d\n", 108 *dwid, *dwid, 109 *mt->mt_directory ? mt->mt_directory : "/", /* XXX */ 110 *twid, *twid, 111 mt->mt_type, 112 *mwid, *mwid, 113 mt->mt_mountinfo, 114 mt->mt_mountpoint, 115 116 mt->mt_mountuid, 117 mt->mt_getattr, 118 mt->mt_lookup, 119 mt->mt_readdir, 120 mt->mt_readlink, 121 mt->mt_statfs, 122 123 tp->tm_mon + 1, tp->tm_mday, 124 tp->tm_year < 1900 ? tp->tm_year + 1900 : tp->tm_year, 125 tp->tm_hour, tp->tm_min, tp->tm_sec); 126 } 127 break; 128 129 case Stats: 130 { 131 struct tm *tp = localtime((time_t *) ((voidp) &mt->mt_mounttime)); 132 printf("%-*.*s %-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%04d\n", 133 *dwid, *dwid, 134 *mt->mt_directory ? mt->mt_directory : "/", /* XXX */ 135 136 mt->mt_mountuid, 137 mt->mt_getattr, 138 mt->mt_lookup, 139 mt->mt_readdir, 140 mt->mt_readlink, 141 mt->mt_statfs, 142 143 tp->tm_mon + 1, tp->tm_mday, 144 tp->tm_year < 1900 ? tp->tm_year + 1900 : tp->tm_year, 145 tp->tm_hour, tp->tm_min, tp->tm_sec); 146 } 147 break; 148 149 case Short: 150 { 151 printf("%-*.*s %-*.*s %-*.*s %s\n", 152 *dwid, *dwid, 153 *mt->mt_directory ? mt->mt_directory : "/", 154 *twid, *twid, 155 mt->mt_type, 156 *mwid, *mwid, 157 mt->mt_mountinfo, 158 mt->mt_mountpoint); 159 } 160 break; 161 162 default: 163 break; 164 } 165 } 166 167 168 /* 169 * Display a pwd data 170 */ 171 static void 172 show_pwd(amq_mount_tree *mt, char *path, size_t l, int *flag) 173 { 174 int len; 175 176 while (mt) { 177 len = strlen(mt->mt_mountpoint); 178 if (NSTREQ(path, mt->mt_mountpoint, len) && 179 !STREQ(mt->mt_directory, mt->mt_mountpoint)) { 180 char buf[MAXPATHLEN+1]; /* must be same size as 'path' */ 181 xstrlcpy(buf, mt->mt_directory, sizeof(buf)); 182 xstrlcat(buf, &path[len], sizeof(buf)); 183 xstrlcpy(path, buf, l); 184 *flag = 1; 185 } 186 show_pwd(mt->mt_next, path, l, flag); 187 mt = mt->mt_child; 188 } 189 } 190 191 192 /* 193 * Display a mount tree. 194 */ 195 static void 196 show_mt(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *pwid) 197 { 198 while (mt) { 199 show_mti(mt, e, mwid, dwid, pwid); 200 show_mt(mt->mt_next, e, mwid, dwid, pwid); 201 mt = mt->mt_child; 202 } 203 } 204 205 206 static void 207 show_mi(amq_mount_info_list *ml, enum show_opt e, int *mwid, int *dwid, int *twid) 208 { 209 u_int i; 210 211 switch (e) { 212 213 case Calc: 214 { 215 for (i = 0; i < ml->amq_mount_info_list_len; i++) { 216 amq_mount_info *mi = &ml->amq_mount_info_list_val[i]; 217 int mw = strlen(mi->mi_mountinfo); 218 int dw = strlen(mi->mi_mountpt); 219 int tw = strlen(mi->mi_type); 220 if (mw > *mwid) 221 *mwid = mw; 222 if (dw > *dwid) 223 *dwid = dw; 224 if (tw > *twid) 225 *twid = tw; 226 } 227 } 228 break; 229 230 case Full: 231 { 232 for (i = 0; i < ml->amq_mount_info_list_len; i++) { 233 amq_mount_info *mi = &ml->amq_mount_info_list_val[i]; 234 printf("%-*.*s %-*.*s %-*.*s %-3d %s is %s", 235 *mwid, *mwid, mi->mi_mountinfo, 236 *dwid, *dwid, mi->mi_mountpt, 237 *twid, *twid, mi->mi_type, 238 mi->mi_refc, mi->mi_fserver, 239 mi->mi_up > 0 ? "up" : 240 mi->mi_up < 0 ? "starting" : "down"); 241 if (mi->mi_error > 0) { 242 printf(" (%s)", strerror(mi->mi_error)); 243 } else if (mi->mi_error < 0) { 244 fputs(" (in progress)", stdout); 245 } 246 fputc('\n', stdout); 247 } 248 } 249 break; 250 251 default: 252 break; 253 } 254 } 255 256 257 /* 258 * Display general mount statistics 259 */ 260 static void 261 show_ms(amq_mount_stats *ms) 262 { 263 printf("\ 264 requests stale mount mount unmount\n\ 265 deferred fhandles ok failed failed\n\ 266 %-9d %-9d %-9d %-9d %-9d\n", 267 ms->as_drops, ms->as_stale, ms->as_mok, ms->as_merr, ms->as_uerr); 268 } 269 270 271 #if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) 272 static char * 273 cluster_server(void) 274 { 275 struct cct_entry *cp; 276 277 if (cnodeid() == 0) { 278 /* 279 * Not clustered 280 */ 281 return def_server; 282 } 283 while (cp = getccent()) 284 if (cp->cnode_type == 'r') 285 return cp->cnode_name; 286 287 return def_server; 288 } 289 #endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */ 290 291 292 /* 293 * MAIN 294 */ 295 int 296 main(int argc, char *argv[]) 297 { 298 int opt_ch; 299 int errs = 0; 300 char *server; 301 struct sockaddr_in server_addr; 302 CLIENT *clnt = NULL; 303 struct hostent *hp; 304 int nodefault = 0; 305 struct timeval tv; 306 char *progname = NULL; 307 308 /* 309 * Compute program name 310 */ 311 if (argv[0]) { 312 progname = strrchr(argv[0], '/'); 313 if (progname && progname[1]) 314 progname++; 315 else 316 progname = argv[0]; 317 } 318 if (!progname) 319 progname = "amq"; 320 am_set_progname(progname); 321 322 /* 323 * Parse arguments 324 */ 325 while ((opt_ch = getopt(argc, argv, "Hfh:l:msuvx:D:pP:TUw")) != -1) 326 switch (opt_ch) { 327 case 'H': 328 goto show_usage; 329 break; 330 331 case 'f': 332 flush_flag = 1; 333 nodefault = 1; 334 break; 335 336 case 'h': 337 def_server = optarg; 338 break; 339 340 case 'l': 341 amq_logfile = optarg; 342 nodefault = 1; 343 break; 344 345 case 'm': 346 minfo_flag = 1; 347 nodefault = 1; 348 break; 349 350 case 'p': 351 getpid_flag = 1; 352 nodefault = 1; 353 break; 354 355 case 's': 356 stats_flag = 1; 357 nodefault = 1; 358 break; 359 360 case 'u': 361 unmount_flag = 1; 362 nodefault = 1; 363 break; 364 365 case 'v': 366 getvers_flag = 1; 367 nodefault = 1; 368 break; 369 370 case 'x': 371 xlog_optstr = optarg; 372 nodefault = 1; 373 break; 374 375 case 'D': 376 debug_opts = optarg; 377 nodefault = 1; 378 break; 379 380 case 'P': 381 amd_program_number = atoi(optarg); 382 break; 383 384 case 'T': 385 use_tcp_flag = 1; 386 break; 387 388 case 'U': 389 use_udp_flag = 1; 390 break; 391 392 case 'w': 393 getpwd_flag = 1; 394 break; 395 396 default: 397 errs = 1; 398 break; 399 } 400 401 if (optind == argc) { 402 if (unmount_flag) 403 errs = 1; 404 } 405 if (errs) { 406 show_usage: 407 fprintf(stderr, "\ 408 Usage: %s [-fmpsvwHTU] [-h hostname] [-l log_file|\"syslog\"]\n\ 409 \t[-x log_options] [-D debug_options]\n\ 410 \t[-P program_number] [[-u] directory ...]\n", 411 am_get_progname() 412 ); 413 exit(1); 414 } 415 416 417 /* set use_udp and use_tcp flags both to on if none are defined */ 418 if (!use_tcp_flag && !use_udp_flag) 419 use_tcp_flag = use_udp_flag = 1; 420 421 #if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) 422 /* 423 * Figure out root server of cluster 424 */ 425 if (def_server == localhost) 426 server = cluster_server(); 427 else 428 #endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */ 429 server = def_server; 430 431 /* 432 * Get address of server 433 */ 434 if ((hp = gethostbyname(server)) == 0 && !STREQ(server, localhost)) { 435 fprintf(stderr, "%s: Can't get address of %s\n", 436 am_get_progname(), server); 437 exit(1); 438 } 439 memset(&server_addr, 0, sizeof(server_addr)); 440 /* as per POSIX, sin_len need not be set (used internally by kernel) */ 441 server_addr.sin_family = AF_INET; 442 if (hp) { 443 memmove((voidp) &server_addr.sin_addr, (voidp) hp->h_addr, 444 sizeof(server_addr.sin_addr)); 445 } else { 446 /* fake "localhost" */ 447 server_addr.sin_addr.s_addr = htonl(0x7f000001); 448 } 449 450 /* 451 * Create RPC endpoint 452 */ 453 tv.tv_sec = 5; /* 5 seconds for timeout or per retry */ 454 tv.tv_usec = 0; 455 456 if (use_tcp_flag) /* try tcp first */ 457 clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "tcp"); 458 if (!clnt && use_udp_flag) { /* try udp next */ 459 clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "udp"); 460 /* if ok, set timeout (valid for connectionless transports only) */ 461 if (clnt) 462 clnt_control(clnt, CLSET_RETRY_TIMEOUT, (char *) &tv); 463 } 464 if (!clnt) { 465 fprintf(stderr, "%s: ", am_get_progname()); 466 clnt_pcreateerror(server); 467 exit(1); 468 } 469 470 /* 471 * Control debugging 472 */ 473 if (debug_opts) { 474 int *rc; 475 amq_setopt opt; 476 opt.as_opt = AMOPT_DEBUG; 477 opt.as_str = debug_opts; 478 rc = amqproc_setopt_1(&opt, clnt); 479 if (rc && *rc < 0) { 480 fprintf(stderr, "%s: daemon not compiled for debug\n", 481 am_get_progname()); 482 errs = 1; 483 } else if (!rc || *rc > 0) { 484 fprintf(stderr, "%s: debug setting for \"%s\" failed\n", 485 am_get_progname(), debug_opts); 486 errs = 1; 487 } 488 } 489 490 /* 491 * Control logging 492 */ 493 if (xlog_optstr) { 494 int *rc; 495 amq_setopt opt; 496 opt.as_opt = AMOPT_XLOG; 497 opt.as_str = xlog_optstr; 498 rc = amqproc_setopt_1(&opt, clnt); 499 if (!rc || *rc) { 500 fprintf(stderr, "%s: setting log level to \"%s\" failed\n", 501 am_get_progname(), xlog_optstr); 502 errs = 1; 503 } 504 } 505 506 /* 507 * Control log file 508 */ 509 if (amq_logfile) { 510 int *rc; 511 amq_setopt opt; 512 opt.as_opt = AMOPT_LOGFILE; 513 opt.as_str = amq_logfile; 514 rc = amqproc_setopt_1(&opt, clnt); 515 if (!rc || *rc) { 516 fprintf(stderr, "%s: setting logfile to \"%s\" failed\n", 517 am_get_progname(), amq_logfile); 518 errs = 1; 519 } 520 } 521 522 /* 523 * Flush map cache 524 */ 525 if (flush_flag) { 526 int *rc; 527 amq_setopt opt; 528 opt.as_opt = AMOPT_FLUSHMAPC; 529 opt.as_str = ""; 530 rc = amqproc_setopt_1(&opt, clnt); 531 if (!rc || *rc) { 532 fprintf(stderr, "%s: amd on %s cannot flush the map cache\n", 533 am_get_progname(), server); 534 errs = 1; 535 } 536 } 537 538 /* 539 * getpwd info 540 */ 541 if (getpwd_flag) { 542 char path[MAXPATHLEN+1]; 543 char *wd = getcwd(path, MAXPATHLEN+1); 544 amq_mount_tree_list *mlp = amqproc_export_1((voidp) 0, clnt); 545 amq_mount_tree_p mt; 546 u_int i; 547 int flag; 548 549 if (!wd) { 550 perror("getcwd"); 551 exit(1); 552 } 553 for (i = 0; mlp && i < mlp->amq_mount_tree_list_len; i++) { 554 mt = mlp->amq_mount_tree_list_val[i]; 555 while (1) { 556 flag = 0; 557 show_pwd(mt, path, sizeof(path), &flag); 558 if (!flag) { 559 printf("%s\n", path); 560 break; 561 } 562 } 563 } 564 exit(0); 565 } 566 567 /* 568 * Mount info 569 */ 570 if (minfo_flag) { 571 int dummy; 572 amq_mount_info_list *ml = amqproc_getmntfs_1(&dummy, clnt); 573 if (ml) { 574 int mwid = 0, dwid = 0, twid = 0; 575 show_mi(ml, Calc, &mwid, &dwid, &twid); 576 mwid++; 577 dwid++; 578 twid++; 579 show_mi(ml, Full, &mwid, &dwid, &twid); 580 581 } else { 582 fprintf(stderr, "%s: amd on %s cannot provide mount info\n", 583 am_get_progname(), server); 584 } 585 } 586 587 /* 588 * Get Version 589 */ 590 if (getvers_flag) { 591 amq_string *spp = amqproc_getvers_1((voidp) 0, clnt); 592 if (spp && *spp) { 593 fputs(*spp, stdout); 594 XFREE(*spp); 595 } else { 596 fprintf(stderr, "%s: failed to get version information\n", 597 am_get_progname()); 598 errs = 1; 599 } 600 } 601 602 /* 603 * Get PID of amd 604 */ 605 if (getpid_flag) { 606 int *ip = amqproc_getpid_1((voidp) 0, clnt); 607 if (ip && *ip) { 608 printf("%d\n", *ip); 609 } else { 610 fprintf(stderr, "%s: failed to get PID of amd\n", am_get_progname()); 611 errs = 1; 612 } 613 } 614 615 /* 616 * Apply required operation to all remaining arguments 617 */ 618 if (optind < argc) { 619 do { 620 char *fs = argv[optind++]; 621 if (unmount_flag) { 622 /* 623 * Unmount request 624 */ 625 amqproc_umnt_1(&fs, clnt); 626 } else { 627 /* 628 * Stats request 629 */ 630 amq_mount_tree_p *mtp = amqproc_mnttree_1(&fs, clnt); 631 if (mtp) { 632 amq_mount_tree *mt = *mtp; 633 if (mt) { 634 int mwid = 0, dwid = 0, twid = 0; 635 show_mt(mt, Calc, &mwid, &dwid, &twid); 636 mwid++; 637 dwid++, twid++; 638 printf("%-*.*s Uid Getattr Lookup RdDir RdLnk Statfs Mounted@\n", 639 dwid, dwid, "What"); 640 show_mt(mt, Stats, &mwid, &dwid, &twid); 641 } else { 642 fprintf(stderr, "%s: %s not automounted\n", am_get_progname(), fs); 643 } 644 xdr_pri_free((XDRPROC_T_TYPE) xdr_amq_mount_tree_p, (caddr_t) mtp); 645 } else { 646 fprintf(stderr, "%s: ", am_get_progname()); 647 clnt_perror(clnt, server); 648 errs = 1; 649 } 650 } 651 } while (optind < argc); 652 653 } else if (unmount_flag) { 654 goto show_usage; 655 656 } else if (stats_flag) { 657 amq_mount_stats *ms = amqproc_stats_1((voidp) 0, clnt); 658 if (ms) { 659 show_ms(ms); 660 } else { 661 fprintf(stderr, "%s: ", am_get_progname()); 662 clnt_perror(clnt, server); 663 errs = 1; 664 } 665 666 } else if (!nodefault) { 667 amq_mount_tree_list *mlp = amqproc_export_1((voidp) 0, clnt); 668 if (mlp) { 669 enum show_opt e = Calc; 670 int mwid = 0, dwid = 0, pwid = 0; 671 672 while (e != ShowDone) { 673 u_int i; 674 for (i = 0; i < mlp->amq_mount_tree_list_len; i++) { 675 show_mt(mlp->amq_mount_tree_list_val[i], 676 e, &mwid, &dwid, &pwid); 677 } 678 mwid++; 679 dwid++, pwid++; 680 if (e == Calc) 681 e = Short; 682 else if (e == Short) 683 e = ShowDone; 684 } 685 686 } else { 687 fprintf(stderr, "%s: ", am_get_progname()); 688 clnt_perror(clnt, server); 689 errs = 1; 690 } 691 } 692 exit(errs); 693 return errs; /* should never reach here */ 694 } 695