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