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