blob: 104cd2c12152374964401eb5af025bb007667fea [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2003-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include <pthread.h>
#include "platform_defs.h"
#include "command.h"
#include "input.h"
#include "init.h"
#include "io.h"
char *progname;
int exitcode;
int expert;
static int idlethread;
size_t pagesize;
struct timeval stopwatch;
static void
usage(void)
{
fprintf(stderr,
_("Usage: %s [-adfinrRstVx] [-m mode] [-p prog] [[-c|-C] cmd]... file\n"),
progname);
exit(1);
}
void
init_cvtnum(
size_t *blocksize,
size_t *sectsize)
{
if (!file || (file->flags & IO_FOREIGN)) {
*blocksize = 4096;
*sectsize = 512;
} else {
*blocksize = file->geom.blocksize;
*sectsize = file->geom.sectsize;
}
}
static void
init_commands(void)
{
attr_init();
bmap_init();
bulkstat_init();
copy_range_init();
cowextsize_init();
encrypt_init();
fadvise_init();
fiemap_init();
file_init();
flink_init();
freeze_init();
fsmap_init();
fsuuid_init();
fsync_init();
getrusage_init();
help_init();
imap_init();
inject_init();
label_init();
log_writes_init();
madvise_init();
mincore_init();
mmap_init();
open_init();
parent_init();
pread_init();
prealloc_init();
pwrite_init();
quit_init();
readdir_init();
reflink_init();
repair_init();
resblks_init();
scrub_init();
seek_init();
sendfile_init();
shutdown_init();
stat_init();
swapext_init();
sync_init();
sync_range_init();
truncate_init();
utimes_init();
crc32cselftest_init();
}
/*
* This allows xfs_io commands specified on the command line to be run on every
* open file in the file table. Commands that should not be iterated across all
* open files need to specify CMD_FLAG_ONESHOT in their command flags.
*/
static int
filetable_iterator(
int index)
{
if (index >= filecount)
return 0;
file = &filetable[index++];
return index;
}
static int
init_check_command(
const cmdinfo_t *ct)
{
if (!file && !(ct->flags & CMD_NOFILE_OK)) {
fprintf(stderr, _("no files are open, try 'help open'\n"));
return 0;
}
if (!mapping && !(ct->flags & CMD_NOMAP_OK)) {
fprintf(stderr, _("no mapped regions, try 'help mmap'\n"));
return 0;
}
if (file && !(ct->flags & CMD_FOREIGN_OK) &&
(file->flags & IO_FOREIGN)) {
fprintf(stderr,
_("foreign file active, %s command is for XFS filesystems only\n"),
ct->name);
return 0;
}
return 1;
}
static void
init(
int argc,
char **argv)
{
int c, flags = 0;
char *sp;
mode_t mode = 0600;
struct xfs_fsop_geom geometry = { 0 };
struct fs_path fsp;
progname = basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
pagesize = getpagesize();
gettimeofday(&stopwatch, NULL);
fs_table_initialise(0, NULL, 0, NULL);
while ((c = getopt(argc, argv, "ac:C:dFfiLm:p:PnrRstTVx")) != EOF) {
switch (c) {
case 'a':
flags |= IO_APPEND;
break;
case 'c':
add_user_command(optarg);
break;
case 'C':
add_oneshot_user_command(optarg);
break;
case 'd':
flags |= IO_DIRECT;
break;
case 'F':
/* Ignored / deprecated now, handled automatically */
break;
case 'f':
flags |= IO_CREAT;
break;
case 'i':
idlethread = 1;
break;
case 'm':
mode = strtoul(optarg, &sp, 0);
if (!sp || sp == optarg) {
fprintf(stderr, _("non-numeric mode -- %s\n"),
optarg);
exit(1);
}
break;
case 'n':
flags |= IO_NONBLOCK;
break;
case 'p':
progname = optarg;
break;
case 'r':
flags |= IO_READONLY;
break;
case 's':
flags |= IO_OSYNC;
break;
case 't':
flags |= IO_TRUNC;
break;
case 'P':
flags |= IO_PATH;
break;
case 'L':
flags |= IO_NOFOLLOW;
break;
case 'R':
flags |= IO_REALTIME;
break;
case 'T':
flags |= IO_TMPFILE;
break;
case 'x':
expert = 1;
break;
case 'V':
printf(_("%s version %s\n"), progname, VERSION);
exit(0);
default:
usage();
}
}
while (optind < argc) {
c = openfile(argv[optind], &geometry, flags, mode, &fsp);
if (c < 0)
exit(1);
if (!platform_test_xfs_fd(c))
flags |= IO_FOREIGN;
if (addfile(argv[optind], c, &geometry, flags, &fsp) < 0)
exit(1);
optind++;
}
init_commands();
add_command_iterator(filetable_iterator);
add_check_command(init_check_command);
}
/*
* The purpose of this idle thread is to test io from a multi threaded process.
* With single threaded process, the file table is not shared and file structs
* are not reference counted. Spawning an idle thread can help detecting file
* struct reference leaks.
*/
static void *
idle_loop(void *arg)
{
for (;;)
pause();
return NULL;
}
static void
start_idle_thread(void)
{
pthread_t t;
if (pthread_create(&t, NULL, idle_loop, NULL)) {
fprintf(stderr, "Error creating idle thread\n");
exit(1);
}
}
int
main(
int argc,
char **argv)
{
init(argc, argv);
if (idlethread)
start_idle_thread();
command_loop();
return exitcode;
}