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