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