1 /* 2 * Copyright (c) 1990 Jan-Simon Pendry 3 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jan-Simon Pendry at Imperial College, London. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * from: @(#)amq.c 8.1 (Berkeley) 6/7/93 35 * $Id: amq.c,v 1.12 2007/02/18 08:34:38 jmc Exp $ 36 */ 37 38 /* 39 * Automounter query tool 40 */ 41 42 #ifndef lint 43 char copyright[] = "\ 44 @(#)Copyright (c) 1990 Jan-Simon Pendry\n\ 45 @(#)Copyright (c) 1990 Imperial College of Science, Technology & Medicine\n\ 46 @(#)Copyright (c) 1990, 1993\n\ 47 The Regents of the University of California. All rights reserved.\n"; 48 #endif /* not lint */ 49 50 #ifndef lint 51 static char rcsid[] = "$Id: amq.c,v 1.12 2007/02/18 08:34:38 jmc Exp $"; 52 static char sccsid[] = "@(#)amq.c 8.1 (Berkeley) 6/7/93"; 53 #endif /* not lint */ 54 55 #include "am.h" 56 #include "amq.h" 57 #include <stdio.h> 58 #include <fcntl.h> 59 #include <netdb.h> 60 #include <unistd.h> 61 62 static int privsock(int); 63 64 static int flush_flag; 65 static int minfo_flag; 66 static int unmount_flag; 67 static int stats_flag; 68 static int getvers_flag; 69 static char *debug_opts; 70 static char *logfile; 71 static char *mount_map; 72 static char *xlog_optstr; 73 static char localhost[] = "localhost"; 74 static char *def_server = localhost; 75 76 extern int optind; 77 extern char *optarg; 78 79 static struct timeval tmo = { 10, 0 }; 80 #define TIMEOUT tmo 81 82 enum show_opt { Full, Stats, Calc, Short, ShowDone }; 83 84 /* 85 * If (e) is Calc then just calculate the sizes 86 * Otherwise display the mount node on stdout 87 */ 88 static void 89 show_mti(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, 90 int *twid) 91 { 92 switch (e) { 93 case Calc: { 94 int mw = strlen(mt->mt_mountinfo); 95 int dw = strlen(mt->mt_directory); 96 int tw = strlen(mt->mt_type); 97 98 if (mw > *mwid) 99 *mwid = mw; 100 if (dw > *dwid) 101 *dwid = dw; 102 if (tw > *twid) 103 *twid = tw; 104 break; 105 } 106 107 case Full: { 108 struct tm *tp = localtime((time_t *) &mt->mt_mounttime); 109 110 printf("%-*.*s %-*.*s %-*.*s %s\n\t%-5d %-7d %-6d" 111 " %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%02d\n", 112 *dwid, *dwid, *mt->mt_directory ? mt->mt_directory : "/", 113 *twid, *twid, mt->mt_type, *mwid, *mwid, 114 mt->mt_mountinfo, mt->mt_mountpoint, mt->mt_mountuid, 115 mt->mt_getattr, mt->mt_lookup, mt->mt_readdir, 116 mt->mt_readlink, mt->mt_statfs, 117 tp->tm_year > 99 ? tp->tm_year - 100 : tp->tm_year, 118 tp->tm_mon+1, tp->tm_mday, 119 tp->tm_hour, tp->tm_min, tp->tm_sec); 120 break; 121 } 122 123 case Stats: { 124 struct tm *tp = localtime((time_t *) &mt->mt_mounttime); 125 126 printf("%-*.*s %-5d %-7d %-6d %-7d %-7d %-6d" 127 " %02d/%02d/%02d %02d:%02d:%02d\n", 128 *dwid, *dwid, *mt->mt_directory ? mt->mt_directory : "/", 129 mt->mt_mountuid, mt->mt_getattr, mt->mt_lookup, 130 mt->mt_readdir, mt->mt_readlink, mt->mt_statfs, 131 tp->tm_year > 99 ? tp->tm_year - 100 : tp->tm_year, 132 tp->tm_mon+1, tp->tm_mday, 133 tp->tm_hour, tp->tm_min, tp->tm_sec); 134 break; 135 } 136 137 case Short: { 138 printf("%-*.*s %-*.*s %-*.*s %s\n", 139 *dwid, *dwid, *mt->mt_directory ? mt->mt_directory : "/", 140 *twid, *twid, mt->mt_type, *mwid, *mwid, 141 mt->mt_mountinfo, mt->mt_mountpoint); 142 break; 143 } 144 } 145 } 146 147 /* 148 * Display a mount tree. 149 */ 150 static void 151 show_mt(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, 152 int *pwid) 153 { 154 while (mt) { 155 show_mti(mt, e, mwid, dwid, pwid); 156 show_mt(mt->mt_next, e, mwid, dwid, pwid); 157 mt = mt->mt_child; 158 } 159 } 160 161 static void 162 show_mi(amq_mount_info_list *ml, enum show_opt e, int *mwid, 163 int *dwid, int *twid) 164 { 165 int i; 166 167 switch (e) { 168 case Calc: { 169 for (i = 0; i < ml->amq_mount_info_list_len; i++) { 170 amq_mount_info *mi = &ml->amq_mount_info_list_val[i]; 171 int mw = strlen(mi->mi_mountinfo); 172 int dw = strlen(mi->mi_mountpt); 173 int tw = strlen(mi->mi_type); 174 175 if (mw > *mwid) 176 *mwid = mw; 177 if (dw > *dwid) 178 *dwid = dw; 179 if (tw > *twid) 180 *twid = tw; 181 } 182 break; 183 } 184 185 case Full: { 186 for (i = 0; i < ml->amq_mount_info_list_len; i++) { 187 amq_mount_info *mi = &ml->amq_mount_info_list_val[i]; 188 printf("%-*.*s %-*.*s %-*.*s %-3d %s is %s", 189 *mwid, *mwid, mi->mi_mountinfo, 190 *dwid, *dwid, mi->mi_mountpt, 191 *twid, *twid, mi->mi_type, 192 mi->mi_refc, mi->mi_fserver, 193 mi->mi_up > 0 ? "up" : 194 mi->mi_up < 0 ? "starting" : "down"); 195 if (mi->mi_error > 0) { 196 #ifdef HAS_STRERROR 197 printf(" (%s)", strerror(mi->mi_error)); 198 #else 199 extern char *sys_errlist[]; 200 extern int sys_nerr; 201 202 if (mi->mi_error < sys_nerr) 203 printf(" (%s)", sys_errlist[mi->mi_error]); 204 else 205 printf(" (Error %d)", mi->mi_error); 206 #endif 207 } else if (mi->mi_error < 0) { 208 fputs(" (in progress)", stdout); 209 } 210 fputc('\n', stdout); 211 } 212 break; 213 } 214 } 215 } 216 217 /* 218 * Display general mount statistics 219 */ 220 static void 221 show_ms(amq_mount_stats *ms) 222 { 223 printf("requests stale mount mount unmount\n" 224 "deferred fhandles ok failed failed\n" 225 "%-9d %-9d %-9d %-9d %-9d\n", 226 ms->as_drops, ms->as_stale, ms->as_mok, ms->as_merr, ms->as_uerr); 227 } 228 229 static bool_t 230 xdr_pri_free(xdrproc_t xdr_args, void *args_ptr) 231 { 232 XDR xdr; 233 234 xdr.x_op = XDR_FREE; 235 return ((*xdr_args)(&xdr, args_ptr)); 236 } 237 238 /* 239 * MAIN 240 */ 241 int 242 main(int argc, char *argv[]) 243 { 244 int nodefault = 0, opt_ch, errs = 0, s; 245 struct sockaddr_in server_addr; 246 struct hostent *hp; 247 CLIENT *clnt; 248 char *server; 249 250 /* 251 * Parse arguments 252 */ 253 while ((opt_ch = getopt(argc, argv, "fh:l:msuvx:D:M:")) != -1) 254 switch (opt_ch) { 255 case 'f': 256 flush_flag = 1; 257 nodefault = 1; 258 break; 259 260 case 'h': 261 def_server = optarg; 262 break; 263 264 case 'l': 265 logfile = optarg; 266 nodefault = 1; 267 break; 268 269 case 'm': 270 minfo_flag = 1; 271 nodefault = 1; 272 break; 273 274 case 's': 275 stats_flag = 1; 276 nodefault = 1; 277 break; 278 279 case 'u': 280 unmount_flag = 1; 281 nodefault = 1; 282 break; 283 284 case 'v': 285 getvers_flag = 1; 286 nodefault = 1; 287 break; 288 289 case 'x': 290 xlog_optstr = optarg; 291 nodefault = 1; 292 break; 293 294 case 'D': 295 debug_opts = optarg; 296 nodefault = 1; 297 break; 298 299 case 'M': 300 mount_map = optarg; 301 nodefault = 1; 302 break; 303 304 default: 305 errs = 1; 306 break; 307 } 308 309 if (optind == argc) { 310 if (unmount_flag) 311 errs = 1; 312 } 313 314 if (errs) { 315 show_usage: 316 fprintf(stderr, "usage: %s [-fmsuv] [-h hostname] " 317 "[directory ...]\n", __progname); 318 exit(1); 319 } 320 321 server = def_server; 322 323 /* 324 * Get address of server 325 */ 326 if ((hp = gethostbyname(server)) == 0 && strcmp(server, localhost) != 0) { 327 fprintf(stderr, "%s: Can't get address of %s\n", __progname, server); 328 exit(1); 329 } 330 bzero(&server_addr, sizeof server_addr); 331 server_addr.sin_family = AF_INET; 332 if (hp) { 333 bcopy((void *)hp->h_addr, (void *)&server_addr.sin_addr, 334 sizeof(server_addr.sin_addr)); 335 } else { 336 /* fake "localhost" */ 337 server_addr.sin_addr.s_addr = htonl(0x7f000001); 338 } 339 340 /* 341 * Create RPC endpoint 342 */ 343 s = privsock(SOCK_STREAM); 344 clnt = clnttcp_create(&server_addr, AMQ_PROGRAM, AMQ_VERSION, &s, 0, 0); 345 if (clnt == 0) { 346 close(s); 347 s = privsock(SOCK_DGRAM); 348 clnt = clntudp_create(&server_addr, AMQ_PROGRAM, 349 AMQ_VERSION, TIMEOUT, &s); 350 } 351 if (clnt == 0) { 352 fprintf(stderr, "%s: ", __progname); 353 clnt_pcreateerror(server); 354 exit(1); 355 } 356 357 /* 358 * Control debugging 359 */ 360 if (debug_opts) { 361 int *rc; 362 amq_setopt opt; 363 opt.as_opt = AMOPT_DEBUG; 364 opt.as_str = debug_opts; 365 rc = amqproc_setopt_1(&opt, clnt); 366 if (rc && *rc < 0) { 367 fprintf(stderr, 368 "%s: daemon not compiled for debug", __progname); 369 errs = 1; 370 } else if (!rc || *rc > 0) { 371 fprintf(stderr, 372 "%s: debug setting for \"%s\" failed\n", 373 __progname, debug_opts); 374 errs = 1; 375 } 376 } 377 378 /* 379 * Control logging 380 */ 381 if (xlog_optstr) { 382 int *rc; 383 amq_setopt opt; 384 opt.as_opt = AMOPT_XLOG; 385 opt.as_str = xlog_optstr; 386 rc = amqproc_setopt_1(&opt, clnt); 387 if (!rc || *rc) { 388 fprintf(stderr, "%s: setting log level to \"%s\" failed\n", 389 __progname, xlog_optstr); 390 errs = 1; 391 } 392 } 393 394 /* 395 * Control log file 396 */ 397 if (logfile) { 398 int *rc; 399 amq_setopt opt; 400 opt.as_opt = AMOPT_LOGFILE; 401 opt.as_str = logfile; 402 rc = amqproc_setopt_1(&opt, clnt); 403 if (!rc || *rc) { 404 fprintf(stderr, "%s: setting logfile to \"%s\" failed\n", 405 __progname, logfile); 406 errs = 1; 407 } 408 } 409 410 /* 411 * Flush map cache 412 */ 413 if (flush_flag) { 414 int *rc; 415 amq_setopt opt; 416 opt.as_opt = AMOPT_FLUSHMAPC; 417 opt.as_str = ""; 418 rc = amqproc_setopt_1(&opt, clnt); 419 if (!rc || *rc) { 420 fprintf(stderr, 421 "%s: amd on %s cannot flush the map cache\n", 422 __progname, server); 423 errs = 1; 424 } 425 } 426 427 /* 428 * Mount info 429 */ 430 if (minfo_flag) { 431 int dummy; 432 amq_mount_info_list *ml = amqproc_getmntfs_1(&dummy, clnt); 433 if (ml) { 434 int mwid = 0, dwid = 0, twid = 0; 435 show_mi(ml, Calc, &mwid, &dwid, &twid); 436 mwid++; dwid++; twid++; 437 show_mi(ml, Full, &mwid, &dwid, &twid); 438 } else { 439 fprintf(stderr, "%s: amd on %s cannot provide mount info\n", 440 __progname, server); 441 } 442 } 443 444 /* 445 * Mount map 446 */ 447 if (mount_map) { 448 int *rc; 449 do { 450 rc = amqproc_mount_1(&mount_map, clnt); 451 } while (rc && *rc < 0); 452 if (!rc || *rc > 0) { 453 if (rc) 454 errno = *rc; 455 else 456 errno = ETIMEDOUT; 457 fprintf(stderr, "%s: could not start new ", __progname); 458 perror("autmount point"); 459 } 460 } 461 462 /* 463 * Get Version 464 */ 465 if (getvers_flag) { 466 amq_string *spp = amqproc_getvers_1((void *)0, clnt); 467 if (spp && *spp) { 468 printf("%s.\n", *spp); 469 free(*spp); 470 } else { 471 fprintf(stderr, "%s: failed to get version information\n", 472 __progname); 473 errs = 1; 474 } 475 } 476 477 /* 478 * Apply required operation to all remaining arguments 479 */ 480 if (optind < argc) { 481 do { 482 char *fs = argv[optind++]; 483 if (unmount_flag) { 484 /* 485 * Unmount request 486 */ 487 amqproc_umnt_1(&fs, clnt); 488 } else { 489 /* 490 * Stats request 491 */ 492 amq_mount_tree_p *mtp = amqproc_mnttree_1(&fs, clnt); 493 if (mtp) { 494 amq_mount_tree *mt = *mtp; 495 if (mt) { 496 int mwid = 0, dwid = 0, twid = 0; 497 498 show_mt(mt, Calc, &mwid, &dwid, &twid); 499 mwid++; 500 dwid++; 501 twid++; 502 503 printf("%-*.*s Uid Getattr " 504 "Lookup RdDir RdLnk " 505 "Statfs Mounted@\n", 506 dwid, dwid, "What"); 507 show_mt(mt, Stats, &mwid, &dwid, &twid); 508 } else { 509 fprintf(stderr, 510 "%s: %s not automounted\n", 511 __progname, fs); 512 } 513 xdr_pri_free(xdr_amq_mount_tree_p, mtp); 514 } else { 515 fprintf(stderr, "%s: ", __progname); 516 clnt_perror(clnt, server); 517 errs = 1; 518 } 519 } 520 } while (optind < argc); 521 } else if (unmount_flag) { 522 goto show_usage; 523 } else if (stats_flag) { 524 amq_mount_stats *ms = amqproc_stats_1((void *)0, clnt); 525 if (ms) { 526 show_ms(ms); 527 } else { 528 fprintf(stderr, "%s: ", __progname); 529 clnt_perror(clnt, server); 530 errs = 1; 531 } 532 } else if (!nodefault) { 533 amq_mount_tree_list *mlp = amqproc_export_1((void *)0, clnt); 534 if (mlp) { 535 enum show_opt e = Calc; 536 int mwid = 0, dwid = 0, pwid = 0; 537 538 while (e != ShowDone) { 539 int i; 540 541 for (i = 0; i < mlp->amq_mount_tree_list_len; i++) { 542 show_mt(mlp->amq_mount_tree_list_val[i], 543 e, &mwid, &dwid, &pwid); 544 } 545 mwid++; 546 dwid++; 547 pwid++; 548 if (e == Calc) 549 e = Short; 550 else if (e == Short) 551 e = ShowDone; 552 } 553 } else { 554 fprintf(stderr, "%s: ", __progname); 555 clnt_perror(clnt, server); 556 errs = 1; 557 } 558 } 559 560 exit(errs); 561 } 562 563 /* 564 * udpresport creates a datagram socket and attempts to bind it to a 565 * secure port. 566 * returns: The bound socket, or -1 to indicate an error. 567 */ 568 static int 569 inetresport(int ty) 570 { 571 struct sockaddr_in addr; 572 int alport, sock; 573 574 /* Use internet address family */ 575 addr.sin_family = AF_INET; 576 addr.sin_addr.s_addr = INADDR_ANY; 577 if ((sock = socket(AF_INET, ty, 0)) < 0) 578 return -1; 579 for (alport = IPPORT_RESERVED-1; alport > IPPORT_RESERVED/2 + 1; alport--) { 580 addr.sin_port = htons((u_short)alport); 581 if (bind(sock, (struct sockaddr *)&addr, sizeof (addr)) >= 0) 582 return sock; 583 if (errno != EADDRINUSE) { 584 close(sock); 585 return -1; 586 } 587 } 588 close(sock); 589 errno = EAGAIN; 590 return -1; 591 } 592 593 /* 594 * Privsock() calls inetresport() to attempt to bind a socket to a secure 595 * port. If inetresport() fails, privsock returns a magic socket number which 596 * indicates to RPC that it should make its own socket. 597 * returns: A privileged socket # or RPC_ANYSOCK. 598 */ 599 static int 600 privsock(int ty) 601 { 602 int sock = inetresport(ty); 603 604 if (sock < 0) { 605 errno = 0; 606 /* Couldn't get a secure port, let RPC make an insecure one */ 607 sock = RPC_ANYSOCK; 608 } 609 return sock; 610 } 611 612 #ifdef DEBUG 613 void 614 xfree(char *f, char *l, void *p) 615 { 616 free(p); 617 } 618 #endif /* DEBUG */ 619