1 /* 2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sbin/mountctl/mountctl.c,v 1.2 2005/01/09 03:06:14 dillon Exp $ 35 */ 36 /* 37 * This utility implements the userland mountctl command which is used to 38 * manage high level journaling on mount points. 39 */ 40 41 #include <sys/types.h> 42 #include <sys/param.h> 43 #include <sys/ucred.h> 44 #include <sys/mount.h> 45 #include <sys/time.h> 46 #include <sys/mountctl.h> 47 #include <stdio.h> 48 #include <fcntl.h> 49 #include <string.h> 50 #include <unistd.h> 51 #include <errno.h> 52 53 static volatile void usage(void); 54 static void parse_option_keyword(const char *opt, 55 const char **wopt, const char **xopt); 56 static int64_t getsize(const char *str); 57 static const char *numtostr(int64_t num); 58 59 static int mountctl_scan(void (*func)(const char *, const char *, int, void *), 60 const char *keyword, const char *mountpt, int fd); 61 static void mountctl_list(const char *keyword, const char *mountpt, 62 int __unused fd, void *info); 63 static void mountctl_add(const char *keyword, const char *mountpt, int fd); 64 static void mountctl_delete(const char *keyword, const char *mountpt, 65 int __unused fd, void __unused *); 66 static void mountctl_modify(const char *keyword, const char *mountpt, int fd, void __unused *); 67 68 /* 69 * For all options 0 means unspecified, -1 means noOPT or nonOPT, and a 70 * positive number indicates enabling or execution of the option. 71 */ 72 static int freeze_opt; 73 static int start_opt; 74 static int close_opt; 75 static int abort_opt; 76 static int flush_opt; 77 static int reversable_opt; 78 static int twoway_opt; 79 static int64_t memfifo_opt; 80 static int64_t swapfifo_opt; 81 82 int 83 main(int ac, char **av) 84 { 85 int fd; 86 int ch; 87 int aopt = 0; 88 int dopt = 0; 89 int fopt = 0; 90 int lopt = 0; 91 int mopt = 0; 92 int mimplied = 0; 93 const char *wopt = NULL; 94 const char *xopt = NULL; 95 const char *keyword = NULL; 96 const char *mountpt = NULL; 97 char *tmp; 98 99 while ((ch = getopt(ac, av, "adflo:mw:x:ACFSZ")) != -1) { 100 switch(ch) { 101 case 'a': 102 aopt = 1; 103 if (aopt + dopt + lopt + mopt != 1) { 104 fprintf(stderr, "too many action options specified\n"); 105 usage(); 106 } 107 break; 108 case 'd': 109 dopt = 1; 110 if (aopt + dopt + lopt + mopt != 1) { 111 fprintf(stderr, "too many action options specified\n"); 112 usage(); 113 } 114 break; 115 case 'f': 116 fopt = 1; 117 break; 118 case 'l': 119 lopt = 1; 120 if (aopt + dopt + lopt + mopt != 1) { 121 fprintf(stderr, "too many action options specified\n"); 122 usage(); 123 } 124 break; 125 case 'o': 126 parse_option_keyword(optarg, &wopt, &xopt); 127 break; 128 case 'm': 129 mopt = 1; 130 if (aopt + dopt + lopt + mopt != 1) { 131 fprintf(stderr, "too many action options specified\n"); 132 usage(); 133 } 134 break; 135 case 'w': 136 wopt = optarg; 137 mimplied = 1; 138 break; 139 case 'x': 140 xopt = optarg; 141 mimplied = 1; 142 break; 143 case 'A': 144 mimplied = 1; 145 abort_opt = 1; 146 break; 147 case 'C': 148 mimplied = 1; 149 close_opt = 1; 150 break; 151 case 'F': 152 mimplied = 1; 153 flush_opt = 1; 154 break; 155 case 'S': 156 mimplied = 1; 157 start_opt = 1; 158 break; 159 case 'Z': 160 mimplied = 1; 161 freeze_opt = 1; 162 break; 163 default: 164 fprintf(stderr, "unknown option: -%c\n", optopt); 165 usage(); 166 } 167 } 168 ac -= optind; 169 av += optind; 170 171 /* 172 * Parse the keyword and/or mount point. 173 */ 174 switch(ac) { 175 case 0: 176 if (aopt) { 177 fprintf(stderr, "action requires a tag and/or mount " 178 "point to be specified\n"); 179 usage(); 180 } 181 break; 182 case 1: 183 if (av[0][0] == '/') { 184 mountpt = av[0]; 185 if ((keyword = strchr(mountpt, ':')) != NULL) { 186 ++keyword; 187 tmp = strdup(mountpt); 188 *strchr(tmp, ':') = 0; 189 mountpt = tmp; 190 } 191 } else { 192 keyword = av[0]; 193 } 194 break; 195 default: 196 fprintf(stderr, "unexpected extra arguments to command\n"); 197 usage(); 198 } 199 200 /* 201 * Additional sanity checks 202 */ 203 if (aopt + dopt + lopt + mopt + mimplied == 0) { 204 fprintf(stderr, "no action or implied action options were specified\n"); 205 usage(); 206 } 207 if (mimplied && aopt + dopt + lopt == 0) 208 mopt = 1; 209 if ((wopt || xopt) && !(aopt || mopt)) { 210 fprintf(stderr, "-w/-x/path/fd options may only be used with -m/-a\n"); 211 usage(); 212 } 213 if (aopt && (keyword == NULL || mountpt == NULL)) { 214 fprintf(stderr, "a keyword AND a mountpt must be specified " 215 "when adding a journal\n"); 216 usage(); 217 } 218 if (fopt == 0 && mopt + dopt && keyword == NULL && mountpt == NULL) { 219 fprintf(stderr, "a keyword, a mountpt, or both must be specified " 220 "when modifying or deleting a journal, unless " 221 "-f is also specified for safety\n"); 222 usage(); 223 } 224 225 /* 226 * Open the journaling file descriptor if required. 227 */ 228 if (wopt && xopt) { 229 fprintf(stderr, "you must specify only one of -w/-x/path/fd\n"); 230 exit(1); 231 } else if (wopt) { 232 if ((fd = open(wopt, O_RDWR|O_CREAT|O_APPEND, 0666)) < 0) { 233 fprintf(stderr, "unable to create %s: %s\n", wopt, strerror(errno)); 234 exit(1); 235 } 236 } else if (xopt) { 237 fd = strtol(xopt, NULL, 0); 238 } else if (aopt) { 239 fd = 1; /* stdout default for -a */ 240 } else { 241 fd = -1; 242 } 243 244 /* 245 * And finally execute the core command. 246 */ 247 if (lopt) 248 mountctl_scan(mountctl_list, keyword, mountpt, fd); 249 if (aopt) 250 mountctl_add(keyword, mountpt, fd); 251 if (dopt) { 252 ch = mountctl_scan(mountctl_delete, keyword, mountpt, -1); 253 if (ch) 254 printf("%d journals deleted\n", ch); 255 else 256 printf("Unable to locate any matching journals\n"); 257 } 258 if (mopt) { 259 ch = mountctl_scan(mountctl_modify, keyword, mountpt, fd); 260 if (ch) 261 printf("%d journals modified\n", ch); 262 else 263 printf("Unable to locate any matching journals\n"); 264 } 265 266 return(0); 267 } 268 269 static void 270 parse_option_keyword(const char *opt, const char **wopt, const char **xopt) 271 { 272 char *str = strdup(opt); 273 char *name; 274 char *val; 275 int negate; 276 int hasval; 277 int cannotnegate; 278 279 /* 280 * multiple comma delimited options may be specified. 281 */ 282 while ((name = strsep(&str, ",")) != NULL) { 283 /* 284 * some options have associated data. 285 */ 286 if ((val = strchr(name, '=')) != NULL) 287 *val++ = 0; 288 289 /* 290 * options beginning with 'no' or 'non' are negated. A positive 291 * number means not negated, a negative number means negated. 292 */ 293 negate = 1; 294 cannotnegate = 0; 295 hasval = 0; 296 if (strncmp(name, "non", 3) == 0) { 297 name += 3; 298 negate = -1; 299 } else if (strncmp(name, "no", 2) == 0) { 300 name += 2; 301 negate = -1; 302 } 303 304 /* 305 * Parse supported options 306 */ 307 if (strcmp(name, "reversable") == 0) { 308 reversable_opt = negate; 309 } else if (strcmp(name, "twoway") == 0) { 310 twoway_opt = negate; 311 } else if (strcmp(name, "memfifo") == 0) { 312 cannotnegate = 1; 313 hasval = 1; 314 if (val) { 315 if ((memfifo_opt = getsize(val)) == 0) 316 memfifo_opt = -1; 317 } 318 } else if (strcmp(name, "swapfifo") == 0) { 319 if (val) { 320 hasval = 1; 321 if ((swapfifo_opt = getsize(val)) == 0) 322 swapfifo_opt = -1; 323 } else if (negate < 0) { 324 swapfifo_opt = -1; 325 } else { 326 hasval = 1; /* force error */ 327 } 328 } else if (strcmp(name, "fd") == 0) { 329 cannotnegate = 1; 330 hasval = 1; 331 if (val) 332 *xopt = val; 333 } else if (strcmp(name, "path") == 0) { 334 cannotnegate = 1; 335 hasval = 1; 336 if (val) 337 *wopt = val; 338 } else if (strcmp(name, "freeze") == 0 || strcmp(name, "stop") == 0) { 339 if (negate < 0) 340 start_opt = -negate; 341 else 342 freeze_opt = negate; 343 } else if (strcmp(name, "start") == 0) { 344 if (negate < 0) 345 freeze_opt = -negate; 346 else 347 start_opt = negate; 348 } else if (strcmp(name, "close") == 0) { 349 close_opt = negate; 350 } else if (strcmp(name, "abort") == 0) { 351 abort_opt = negate; 352 } else if (strcmp(name, "flush") == 0) { 353 flush_opt = negate; 354 } else { 355 fprintf(stderr, "unknown option keyword: %s\n", name); 356 exit(1); 357 } 358 359 /* 360 * Sanity checks 361 */ 362 if (cannotnegate && negate < 0) { 363 fprintf(stderr, "option %s may not be negated\n", name); 364 exit(1); 365 } 366 if (hasval && val == NULL) { 367 fprintf(stderr, "option %s requires assigned data\n", name); 368 exit(1); 369 } 370 if (hasval == 0 && val) { 371 fprintf(stderr, "option %s does not take an assignment\n", name); 372 exit(1); 373 } 374 375 } 376 } 377 378 static int 379 mountctl_scan(void (*func)(const char *, const char *, int, void *), 380 const char *keyword, const char *mountpt, int fd) 381 { 382 struct statfs *sfs; 383 int count; 384 int calls; 385 int i; 386 struct mountctl_status_journal statreq; 387 struct mountctl_journal_ret_status rstat[4]; /* BIG */ 388 389 calls = 0; 390 if (mountpt) { 391 bzero(&statreq, sizeof(statreq)); 392 if (keyword) { 393 statreq.index = MC_JOURNAL_INDEX_ID; 394 count = strlen(keyword); 395 if (count > JIDMAX) 396 count = JIDMAX; 397 bcopy(keyword, statreq.id, count); 398 } else { 399 statreq.index = MC_JOURNAL_INDEX_ALL; 400 } 401 count = mountctl(mountpt, MOUNTCTL_STATUS_VFS_JOURNAL, -1, 402 &statreq, sizeof(statreq), &rstat, sizeof(rstat)); 403 if (count > 0 && rstat[0].recsize != sizeof(rstat[0])) { 404 fprintf(stderr, "Unable to access status, " 405 "structure size mismatch\n"); 406 exit(1); 407 } 408 if (count > 0) { 409 count /= sizeof(rstat[0]); 410 for (i = 0; i < count; ++i) { 411 func(rstat[i].id, mountpt, fd, &rstat[i]); 412 ++calls; 413 } 414 } 415 } else { 416 if ((count = getmntinfo(&sfs, MNT_WAIT)) > 0) { 417 for (i = 0; i < count; ++i) { 418 calls += mountctl_scan(func, keyword, sfs[i].f_mntonname, fd); 419 } 420 } else if (count < 0) { 421 /* XXX */ 422 } 423 } 424 return(calls); 425 } 426 427 static void 428 mountctl_list(const char *keyword, const char *mountpt, int __unused fd, void *info) 429 { 430 struct mountctl_journal_ret_status *rstat = info; 431 432 printf("%s:%s\n", mountpt, rstat->id[0] ? rstat->id : "<NOID>"); 433 printf(" membufsize=%s\n", numtostr(rstat->membufsize)); 434 printf(" membufused=%s\n", numtostr(rstat->membufused)); 435 printf(" membufiopend=%s\n", numtostr(rstat->membufiopend)); 436 printf(" total_bytes=%s\n", numtostr(rstat->bytessent)); 437 } 438 439 static void 440 mountctl_add(const char *keyword, const char *mountpt, int fd) 441 { 442 struct mountctl_install_journal joinfo; 443 int error; 444 445 bzero(&joinfo, sizeof(joinfo)); 446 snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword); 447 448 error = mountctl(mountpt, MOUNTCTL_INSTALL_VFS_JOURNAL, fd, 449 &joinfo, sizeof(joinfo), NULL, 0); 450 if (error == 0) { 451 fprintf(stderr, "%s:%s added\n", mountpt, joinfo.id); 452 } else { 453 fprintf(stderr, "%s:%s failed to add, error %s\n", mountpt, joinfo.id, strerror(errno)); 454 } 455 } 456 457 static void 458 mountctl_delete(const char *keyword, const char *mountpt, int __unused fd, void __unused *info) 459 { 460 struct mountctl_remove_journal joinfo; 461 int error; 462 463 bzero(&joinfo, sizeof(joinfo)); 464 snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword); 465 error = mountctl(mountpt, MOUNTCTL_REMOVE_VFS_JOURNAL, -1, 466 &joinfo, sizeof(joinfo), NULL, 0); 467 if (error == 0) { 468 fprintf(stderr, "%s:%s deleted\n", mountpt, joinfo.id); 469 } else { 470 fprintf(stderr, "%s:%s deletion failed, error %s\n", mountpt, joinfo.id, strerror(errno)); 471 } 472 } 473 474 static void 475 mountctl_modify(const char *keyword, const char *mountpt, int fd, void __unused *info) 476 { 477 fprintf(stderr, "modify not yet implemented\n"); 478 } 479 480 481 static volatile 482 void 483 usage(void) 484 { 485 printf( 486 " mountctl -l [tag/mountpt | mountpt:tag]\n" 487 " mountctl -a [-w output_path] [-x filedesc]\n" 488 " [-o option] [-o option ...] mountpt:tag\n" 489 " mountctl -d [tag/mountpt | mountpt:tag]\n" 490 " mountctl -m [-o option] [-o option ...] [tag/mountpt | mountpt:tag]\n" 491 " mountctl -FZSCA [tag/mountpt | mountpt:tag]\n" 492 ); 493 exit(1); 494 } 495 496 static 497 int64_t 498 getsize(const char *str) 499 { 500 const char *suffix; 501 int64_t val; 502 503 val = strtoll(str, &suffix, 0); 504 if (suffix) { 505 switch(*suffix) { 506 case 'b': 507 break; 508 case 't': 509 val *= 1024; 510 /* fall through */ 511 case 'g': 512 val *= 1024; 513 /* fall through */ 514 case 'm': 515 val *= 1024; 516 /* fall through */ 517 case 'k': 518 val *= 1024; 519 /* fall through */ 520 break; 521 default: 522 fprintf(stderr, "data value '%s' has unknown suffix\n", str); 523 exit(1); 524 } 525 } 526 return(val); 527 } 528 529 static 530 const char * 531 numtostr(int64_t num) 532 { 533 static char buf[64]; 534 int n; 535 double v = num; 536 537 if (num < 1024) 538 snprintf(buf, sizeof(buf), "%lld", num); 539 else if (num < 10 * 1024) 540 snprintf(buf, sizeof(buf), "%3.2fK", num / 1024.0); 541 else if (num < 1024 * 1024) 542 snprintf(buf, sizeof(buf), "%3.0fK", num / 1024.0); 543 else if (num < 10 * 1024 * 1024) 544 snprintf(buf, sizeof(buf), "%3.2fM", num / (1024.0 * 1024.0)); 545 else if (num < 1024 * 1024 * 1024) 546 snprintf(buf, sizeof(buf), "%3.0fM", num / (1024.0 * 1024.0)); 547 else if (num < 10LL * 1024 * 1024 * 1024) 548 snprintf(buf, sizeof(buf), "%3.2fG", num / (1024.0 * 1024.0 * 1024.0)); 549 else 550 snprintf(buf, sizeof(buf), "%3.0fG", num / (1024.0 * 1024.0 * 1024.0)); 551 return(buf); 552 } 553 554