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