mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Fix the following incorrect help text line: -T, --file-timeout (null) Stop reading file after SEC seconds Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
480 lines
11 KiB
C
480 lines
11 KiB
C
/*
|
|
* dump2tar - tool to dump files and command output into a tar archive
|
|
*
|
|
* Command line interface
|
|
*
|
|
* Copyright IBM Corp. 2016, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "lib/util_opt.h"
|
|
#include "lib/util_prg.h"
|
|
|
|
#include "dump.h"
|
|
#include "global.h"
|
|
#include "idcache.h"
|
|
#include "misc.h"
|
|
#include "strarray.h"
|
|
|
|
#define MIN_BUFFER_SIZE 4096
|
|
|
|
#define OPT_NOSHORT_BASE 256
|
|
|
|
#define OPT_DEREFERENCE (OPT_NOSHORT_BASE + 0)
|
|
#define OPT_NORECURSION (OPT_NOSHORT_BASE + 1)
|
|
#define OPT_EXCLUDETYPE (OPT_NOSHORT_BASE + 2)
|
|
|
|
/* Program description */
|
|
static const struct util_prg dump2tar_prg = {
|
|
.desc = "Use dump2tar to create a tar archive from the contents "
|
|
"of arbitrary files.\nIt works even when the size of actual "
|
|
"file content is not known beforehand,\nsuch as with FIFOs, "
|
|
"character devices or certain Linux debugfs or sysfs files.\n"
|
|
"\nYou can also add files under different names and add "
|
|
"command output using the\nformat described in section SPECS "
|
|
"below. When no additional options are\nspecified, the "
|
|
"resulting archive is written to the standard output stream\n"
|
|
"in uncompressed tar format.",
|
|
.args = "SPECS",
|
|
.copyright_vec = {
|
|
{ "IBM Corp.", 2016, 2017 },
|
|
UTIL_PRG_COPYRIGHT_END
|
|
},
|
|
};
|
|
|
|
/* Definition of command line options */
|
|
static struct util_opt dump2tar_opts[] = {
|
|
UTIL_OPT_SECTION("OUTPUT OPTIONS"),
|
|
{
|
|
.option = { "output-file", required_argument, NULL, 'o' },
|
|
.argument = "FILE",
|
|
.desc = "Write archive to FILE (default: standard output)",
|
|
},
|
|
#ifdef HAVE_ZLIB
|
|
{
|
|
.option = { "gzip", no_argument, NULL, 'z' },
|
|
.desc = "Write a gzip compressed archive",
|
|
},
|
|
#endif /* HAVE_ZLIB */
|
|
{
|
|
.option = { "max-size", required_argument, NULL, 'm' },
|
|
.argument = "N",
|
|
.desc = "Stop adding files when archive size exceeds N bytes",
|
|
},
|
|
{
|
|
.option = { "timeout", required_argument, NULL, 't' },
|
|
.argument = "SEC",
|
|
.desc = "Stop adding files after SEC seconds",
|
|
},
|
|
{
|
|
.option = { "no-eof", no_argument, NULL, 131 },
|
|
.desc = "Do not write an end-of-file marker",
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
},
|
|
{
|
|
.option = { "add-cmd-status", no_argument, NULL, 132 },
|
|
.desc = "Add status of commands as separate file",
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
},
|
|
{
|
|
.option = { "append", no_argument, NULL, 133 },
|
|
.desc = "Append output to end of file",
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
},
|
|
|
|
UTIL_OPT_SECTION("INPUT OPTIONS"),
|
|
{
|
|
.option = { "files-from", required_argument, NULL, 'F' },
|
|
.argument = "FILE",
|
|
.desc = "Read filenames from FILE (- for standard input)",
|
|
},
|
|
{
|
|
.option = { "ignore-failed-read", no_argument, NULL, 'i' },
|
|
.desc = "Continue after read errors",
|
|
},
|
|
{
|
|
.option = { "buffer-size", required_argument, NULL, 'b' },
|
|
.argument = "N",
|
|
.desc = "Read data in chunks of N byte (default: 16384)",
|
|
},
|
|
{
|
|
.option = { "file-timeout", required_argument, NULL, 'T' },
|
|
.desc = "Stop reading file after SEC seconds",
|
|
.argument = "SEC",
|
|
},
|
|
{
|
|
.option = { "file-max-size", required_argument, NULL, 'M' },
|
|
.argument = "N",
|
|
.desc = "Stop reading file after N bytes",
|
|
},
|
|
{
|
|
.option = { "jobs", required_argument, NULL, 'j' },
|
|
.argument = "N",
|
|
.desc = "Read N files in parallel (default: 1)",
|
|
},
|
|
{
|
|
.option = { "jobs-per-cpu", required_argument, NULL, 'J' },
|
|
.argument = "N",
|
|
.desc = "Read N files per CPU in parallel",
|
|
},
|
|
{
|
|
.option = { "exclude", required_argument, NULL, 'x' },
|
|
.argument = "PATTERN",
|
|
.desc = "Don't add files matching PATTERN",
|
|
},
|
|
{
|
|
.option = { "exclude-from", required_argument, NULL, 'X' },
|
|
.argument = "FILE",
|
|
.desc = "Don't add files matching patterns in FILE",
|
|
},
|
|
{
|
|
.option = { "exclude-type", required_argument, NULL,
|
|
OPT_EXCLUDETYPE },
|
|
.argument = "TYPE",
|
|
.desc = "Don't add files of specified TYPE (one of: fdcbpls)",
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
},
|
|
{
|
|
.option = { "dereference", no_argument, NULL, OPT_DEREFERENCE },
|
|
.desc = "Add link targets instead of links",
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
},
|
|
{
|
|
.option = { "no-recursion", no_argument, NULL,
|
|
OPT_NORECURSION },
|
|
.desc = "Don't add files from sub-directories",
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
},
|
|
|
|
UTIL_OPT_SECTION("MISC OPTIONS"),
|
|
UTIL_OPT_HELP,
|
|
UTIL_OPT_VERSION,
|
|
{
|
|
.option = { "verbose", no_argument, NULL, 'V' },
|
|
.desc = "Print additional informational output",
|
|
},
|
|
{
|
|
.option = { "quiet", no_argument, NULL, 'q' },
|
|
.desc = "Suppress printing of informational output",
|
|
},
|
|
UTIL_OPT_END,
|
|
};
|
|
|
|
/* Split buffer size specification in @arg into two numbers to be stored in
|
|
* @from_ptr and @to_ptr. Return %EXIT_OK on success. */
|
|
static int parse_buffer_size(char *arg, size_t *from_ptr, size_t *to_ptr)
|
|
{
|
|
char *err;
|
|
unsigned long from, to;
|
|
|
|
if (!*arg) {
|
|
mwarnx("Empty buffer size specified");
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
from = strtoul(arg, &err, 10);
|
|
|
|
if (*err == '-')
|
|
to = strtoul(err + 1, &err, 10);
|
|
else
|
|
to = *to_ptr;
|
|
|
|
if (*err) {
|
|
mwarnx("Invalid buffer size: %s", arg);
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
if (from < MIN_BUFFER_SIZE || to < MIN_BUFFER_SIZE) {
|
|
mwarnx("Buffer size too low (minimum %u)", MIN_BUFFER_SIZE);
|
|
return EXIT_USAGE;
|
|
}
|
|
|
|
if (to < from)
|
|
to = from;
|
|
|
|
*from_ptr = from;
|
|
*to_ptr = to;
|
|
|
|
return EXIT_OK;
|
|
}
|
|
|
|
static void parse_and_add_spec(struct dump_opts *opts, const char *spec)
|
|
{
|
|
char *op, *s, *inname, *outname = NULL;
|
|
bool is_cmd = false;
|
|
|
|
s = mstrdup(spec);
|
|
op = strstr(s, "|=");
|
|
if (op)
|
|
is_cmd = true;
|
|
else
|
|
op = strstr(s, ":=");
|
|
|
|
if (op) {
|
|
*op = 0;
|
|
inname = op + 2;
|
|
outname = s;
|
|
} else {
|
|
inname = s;
|
|
}
|
|
dump_opts_add_spec(opts, inname, outname, is_cmd);
|
|
free(s);
|
|
}
|
|
|
|
static int add_specs_from_file(struct dump_opts *opts, const char *filename)
|
|
{
|
|
FILE *fd;
|
|
char *line = NULL;
|
|
size_t line_size;
|
|
int rc = EXIT_RUNTIME;
|
|
bool need_close = false, parse_spec = true;
|
|
|
|
if (strcmp(filename, "-") == 0)
|
|
fd = stdin;
|
|
else {
|
|
fd = fopen(filename, "r");
|
|
if (!fd) {
|
|
mwarn("%s: Cannot open file", filename);
|
|
goto out;
|
|
}
|
|
need_close = true;
|
|
}
|
|
|
|
while ((getline(&line, &line_size, fd) != -1)) {
|
|
chomp(line, "\n");
|
|
if (line[0] == 0)
|
|
continue;
|
|
if (parse_spec && strcmp(line, "--") == 0) {
|
|
/* After a line containing --, no more := or |= specs
|
|
* are expected */
|
|
parse_spec = false;
|
|
continue;
|
|
}
|
|
if (parse_spec)
|
|
parse_and_add_spec(opts, line);
|
|
else
|
|
dump_opts_add_spec(opts, line, NULL, false);
|
|
}
|
|
|
|
if (ferror(fd))
|
|
mwarn("%s: Cannot read file", filename);
|
|
else
|
|
rc = EXIT_OK;
|
|
|
|
out:
|
|
if (need_close)
|
|
fclose(fd);
|
|
free(line);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void print_help(void)
|
|
{
|
|
static const struct {
|
|
const char *name;
|
|
const char *desc;
|
|
} specs[] = {
|
|
{ "PATH", "Add file or directory at PATH" },
|
|
{ "NEWPATH:=PATH", "Add file or directory at PATH as NEWPATH" },
|
|
{ "NEWPATH|=CMDLINE", "Add output of command line CMDLINE as "
|
|
"NEWPATH" },
|
|
{ NULL, NULL },
|
|
};
|
|
int i;
|
|
|
|
util_prg_print_help();
|
|
printf("SPECS\n");
|
|
for (i = 0; specs[i].name; i++)
|
|
util_opt_print_indented(specs[i].name, specs[i].desc);
|
|
printf("\n");
|
|
util_opt_print_help();
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int rc = EXIT_USAGE, opt;
|
|
long i;
|
|
struct dump_opts *opts;
|
|
|
|
if (getenv("DUMP2TAR_DEBUG"))
|
|
global_debug = true;
|
|
|
|
util_prg_init(&dump2tar_prg);
|
|
util_opt_init(dump2tar_opts, "-");
|
|
misc_init();
|
|
|
|
opts = dump_opts_new();
|
|
opterr = 0;
|
|
while ((opt = util_opt_getopt_long(argc, argv)) != -1) {
|
|
switch (opt) {
|
|
case 'h': /* --help */
|
|
print_help();
|
|
rc = EXIT_OK;
|
|
goto out;
|
|
case 'v': /* --version */
|
|
util_prg_print_version();
|
|
rc = EXIT_OK;
|
|
goto out;
|
|
case 'V': /* --verbose */
|
|
global_verbose = true;
|
|
global_quiet = false;
|
|
opts->verbose = true;
|
|
opts->quiet = false;
|
|
break;
|
|
case 'q': /* --quiet */
|
|
global_quiet = true;
|
|
global_verbose = false;
|
|
opts->quiet = true;
|
|
opts->verbose = false;
|
|
break;
|
|
case 'i': /* --ignore-failed-read */
|
|
opts->ignore_failed_read = true;
|
|
break;
|
|
case 'j': /* --jobs N */
|
|
opts->jobs = atoi(optarg);
|
|
if (opts->jobs < 1) {
|
|
mwarnx("Invalid number of jobs: %s", optarg);
|
|
goto out;
|
|
}
|
|
break;
|
|
case 'J': /* --jobs-per-cpu N */
|
|
opts->jobs_per_cpu = atoi(optarg);
|
|
if (opts->jobs_per_cpu < 1) {
|
|
mwarnx("Invalid number of jobs: %s", optarg);
|
|
goto out;
|
|
}
|
|
break;
|
|
case 'b': /* --buffer-size N */
|
|
if (parse_buffer_size(optarg, &opts->read_chunk_size,
|
|
&opts->max_buffer_size))
|
|
goto out;
|
|
break;
|
|
case 'x': /* --exclude PATTERN */
|
|
add_str_to_strarray(&opts->exclude, optarg);
|
|
break;
|
|
case 'X': /* --exclude-from FILE */
|
|
if (add_file_to_strarray(&opts->exclude, optarg))
|
|
goto out;
|
|
break;
|
|
case 'F': /* --files-from FILE */
|
|
if (add_specs_from_file(opts, optarg))
|
|
goto out;
|
|
break;
|
|
case 'o': /* --output-file FILE */
|
|
if (opts->output_file) {
|
|
mwarnx("Output file specified multiple times");
|
|
goto out;
|
|
}
|
|
opts->output_file = optarg;
|
|
break;
|
|
case OPT_DEREFERENCE: /* --dereference */
|
|
opts->dereference = true;
|
|
break;
|
|
case OPT_NORECURSION: /* --no-recursion */
|
|
opts->recursive = false;
|
|
break;
|
|
case OPT_EXCLUDETYPE: /* --exclude-type TYPE */
|
|
for (i = 0; optarg[i]; i++) {
|
|
if (dump_opts_set_type_excluded(opts,
|
|
optarg[i]))
|
|
break;
|
|
|
|
}
|
|
if (optarg[i]) {
|
|
mwarnx("Unrecognized file type: %c", optarg[i]);
|
|
goto out;
|
|
}
|
|
break;
|
|
case 131: /* --no-eof */
|
|
opts->no_eof = true;
|
|
break;
|
|
case 132: /* --add-cmd-status */
|
|
opts->add_cmd_status = true;
|
|
break;
|
|
case 133: /* --append */
|
|
opts->append = true;
|
|
break;
|
|
case 't': /* --timeout VALUE */
|
|
opts->timeout = atoi(optarg);
|
|
if (opts->timeout < 1) {
|
|
mwarnx("Invalid timeout value: %s", optarg);
|
|
goto out;
|
|
}
|
|
break;
|
|
case 'T': /* --file-timeout VALUE */
|
|
opts->file_timeout = atoi(optarg);
|
|
if (opts->file_timeout < 1) {
|
|
mwarnx("Invalid timeout value: %s", optarg);
|
|
goto out;
|
|
}
|
|
break;
|
|
case 'm': /* --max-size N */
|
|
opts->max_size = atol(optarg);
|
|
if (opts->max_size < 2) {
|
|
mwarnx("Invalid maximum size: %s", optarg);
|
|
goto out;
|
|
}
|
|
break;
|
|
case 'M': /* --file-max-size N */
|
|
opts->file_max_size = atol(optarg);
|
|
if (opts->file_max_size < 2) {
|
|
mwarnx("Invalid maximum size: %s", optarg);
|
|
goto out;
|
|
}
|
|
break;
|
|
case 'z': /* --gzip */
|
|
opts->gzip = true;
|
|
break;
|
|
case 1: /* Filename specification or unrecognized option */
|
|
if (optarg[0] == '-') {
|
|
mwarnx("Invalid option '%s'", optarg);
|
|
goto out;
|
|
}
|
|
parse_and_add_spec(opts, optarg);
|
|
break;
|
|
case '?': /* Unrecognized option */
|
|
if (optopt)
|
|
mwarnx("Invalid option '-%c'", optopt);
|
|
else
|
|
mwarnx("Invalid option '%s'", argv[optind - 1]);
|
|
goto out;
|
|
case ':': /* Missing argument */
|
|
mwarnx("Option '%s' requires an argument",
|
|
argv[optind - 1]);
|
|
goto out;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (optind >= argc && opts->num_specs == 0) {
|
|
mwarnx("Please specify files to dump");
|
|
goto out;
|
|
}
|
|
|
|
for (i = optind; i < argc; i++)
|
|
dump_opts_add_spec(opts, argv[i], NULL, false);
|
|
|
|
rc = dump_to_tar(opts);
|
|
|
|
out:
|
|
idcache_cleanup();
|
|
misc_cleanup();
|
|
dump_opts_free(opts);
|
|
|
|
if (rc == EXIT_USAGE)
|
|
util_prg_print_parse_error();
|
|
|
|
return rc;
|
|
}
|