Add --progress-json parameter to utilities.

Progress data can now be printed out in json format
suitable for machine processing.
This commit is contained in:
Ondrej Kozina
2022-02-15 12:33:59 +01:00
parent 6852c49d0c
commit 3cd5d83ee9
13 changed files with 188 additions and 42 deletions

View File

@@ -1201,6 +1201,25 @@ the passphrase verification for \fIluksFormat\fR.
.B "\-\-progress-frequency <seconds>"
Print separate line every <seconds> with wipe progress.
.TP
.B "\-\-progress-json"
Prints progress data in json format suitable mostly for machine processing.
It prints separate line every half second (or based on \fI\-\-progress\-frequency\fR value).
The json output looks as follows during progress (except it's compact single line):
.EX
{
"device":"/dev/sda" // backing device or file
"device_bytes":"8192", // bytes of I/O so far
"device_size":"44040192", // total bytes of I/O to go
"speed":"126877696", // calculated speed in bytes per second (based on progress so far)
"eta_ms":"2520012" // estimated time to finish an operation in milliseconds
"time_ms":"5561235" // total time spent in IO operation in milliseconds
}
.EE
Note on numbers in json output: Due to json parsers limitations all numbers are represented in a string format
due to need of full 64bit unsigned integers.
.TP
.B "\-\-timeout, \-t <number of seconds>"
The number of seconds to wait before timeout on passphrase input
via terminal. It is relevant every time a passphrase is asked,

View File

@@ -21,7 +21,7 @@ Formats <device> (calculates space and dm-integrity superblock and wipes the dev
\fB<options>\fR can be [\-\-data\-device, \-\-batch\-mode, \-\-no\-wipe, \-\-journal\-size,
\-\-interleave\-sectors, \-\-tag\-size, \-\-integrity, \-\-integrity\-key\-size,
\-\-integrity\-key\-file, \-\-sector\-size, \-\-progress-frequency]
\-\-integrity\-key\-file, \-\-sector\-size, \-\-progress\-frequency, \-\-progress\-json]
.PP
\fIopen\fR <device> <name>
@@ -72,6 +72,24 @@ Do not ask for confirmation.
.B "\-\-progress-frequency <seconds>"
Print separate line every <seconds> with wipe progress.
.TP
.B "\-\-progress-json"
Prints wipe progress data in json format suitable mostly for machine processing.
It prints separate line every half second (or based on \fI\-\-progress\-frequency\fR value).
The json output looks as follows during wipe progress (except it's compact single line):
.EX
{
"device":"/dev/sda" // backing device or file
"device_bytes":"8192", // bytes wiped so far
"device_size":"44040192", // total bytes to wipe
"speed":"126877696", // calculated speed in bytes per second (based on progress so far)
"eta_ms":"2520012" // estimated time to finish wipe in milliseconds
"time_ms":"5561235" // total time spent wiping device in milliseconds
}
.EE
Note on numbers in json output: Due to json parsers limitations all numbers are represented in a string format
due to need of full 64bit unsigned integers.
.TP
.B "\-\-no\-wipe"
Do not wipe the device after format. A device that is not initially wiped will contain invalid checksums.
.TP

View File

@@ -1179,11 +1179,14 @@ static int _wipe_data_device(struct crypt_device *cd)
{
char tmp_name[64], tmp_path[128], tmp_uuid[40];
uuid_t tmp_uuid_bin;
int r;
int r = -EINVAL;
char *backing_file = NULL;
struct tools_progress_params prog_parms = {
.frequency = ARG_UINT32(OPT_PROGRESS_FREQUENCY_ID),
.batch_mode = ARG_SET(OPT_BATCH_MODE_ID),
.interrupt_message = _("\nWipe interrupted.")
.json_output = ARG_SET(OPT_PROGRESS_JSON_ID),
.interrupt_message = _("\nWipe interrupted."),
.device = tools_get_device_name(crypt_get_device_name(cd), &backing_file)
};
if (!ARG_SET(OPT_BATCH_MODE_ID))
@@ -1195,14 +1198,14 @@ static int _wipe_data_device(struct crypt_device *cd)
uuid_generate(tmp_uuid_bin);
uuid_unparse(tmp_uuid_bin, tmp_uuid);
if (snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid) < 0)
return -EINVAL;
goto out;
if (snprintf(tmp_path, sizeof(tmp_path), "%s/%s", crypt_get_dir(), tmp_name) < 0)
return -EINVAL;
goto out;
r = crypt_activate_by_volume_key(cd, tmp_name, NULL, 0,
CRYPT_ACTIVATE_PRIVATE | CRYPT_ACTIVATE_NO_JOURNAL);
if (r < 0)
return r;
goto out;
/* Wipe the device */
set_int_handler(0);
@@ -1212,6 +1215,8 @@ static int _wipe_data_device(struct crypt_device *cd)
log_err(_("Cannot deactivate temporary device %s."), tmp_path);
set_int_block(0);
out:
free(backing_file);
return r;
}

View File

@@ -96,10 +96,13 @@ struct tools_progress_params {
struct timeval end_time;
uint64_t start_offset;
bool batch_mode;
bool json_output;
const char *interrupt_message;
const char *device;
};
int tools_progress(uint64_t size, uint64_t offset, void *usrptr);
const char *tools_get_device_name(const char *device, char **r_backing_file);
int tools_read_mk(const char *file, char **key, int keysize);
int tools_write_mk(const char *file, const char *key, int keysize);

View File

@@ -135,6 +135,8 @@ ARG(OPT_PERSISTENT, '\0', POPT_ARG_NONE, N_("Set activation flags persistent for
ARG(OPT_PRIORITY, '\0', POPT_ARG_STRING, N_("Keyslot priority: ignore, normal, prefer"), NULL, CRYPT_ARG_STRING, {}, OPT_PRIORITY_ACTIONS)
ARG(OPT_PROGRESS_JSON, '\0', POPT_ARG_NONE, N_("Print progress data in json format (suitable for machine processing)"), NULL, CRYPT_ARG_BOOL, {}, OPT_PROGRESS_JSON_ACTIONS)
ARG(OPT_PROGRESS_FREQUENCY, '\0', POPT_ARG_STRING, N_("Progress line update (in seconds)"), N_("secs"), CRYPT_ARG_UINT32, {}, {})
ARG(OPT_READONLY, 'r', POPT_ARG_NONE, N_("Create a readonly mapping"), NULL, CRYPT_ARG_BOOL, {}, {})

View File

@@ -76,6 +76,7 @@
#define OPT_PBKDF_FORCE_ITERATIONS_ACTIONS { FORMAT_ACTION, ADDKEY_ACTION, CHANGEKEY_ACTION, CONVERTKEY_ACTION, REENCRYPT_ACTION }
#define OPT_PERSISTENT_ACTIONS { OPEN_ACTION }
#define OPT_PRIORITY_ACTIONS { CONFIG_ACTION }
#define OPT_PROGRESS_JSON_ACTIONS { FORMAT_ACTION, REENCRYPT_ACTION }
#define OPT_REFRESH_ACTIONS { OPEN_ACTION }
#define OPT_SECTOR_SIZE_ACTIONS { OPEN_ACTION, REENCRYPT_ACTION, FORMAT_ACTION }
#define OPT_SERIALIZE_MEMORY_HARD_PBKDF_ACTIONS { OPEN_ACTION }

View File

@@ -80,11 +80,14 @@ static int _wipe_data_device(struct crypt_device *cd, const char *integrity_key)
{
char tmp_name[64], tmp_path[128], tmp_uuid[40];
uuid_t tmp_uuid_bin;
int r;
int r = -EINVAL;
char *backing_file = NULL;
struct tools_progress_params prog_parms = {
.frequency = ARG_UINT32(OPT_PROGRESS_FREQUENCY_ID),
.batch_mode = ARG_SET(OPT_BATCH_MODE_ID),
.interrupt_message = _("\nWipe interrupted.")
.json_output = ARG_SET(OPT_PROGRESS_JSON_ID),
.interrupt_message = _("\nWipe interrupted."),
.device = tools_get_device_name(crypt_get_device_name(cd), &backing_file)
};
if (!ARG_SET(OPT_BATCH_MODE_ID))
@@ -96,14 +99,14 @@ static int _wipe_data_device(struct crypt_device *cd, const char *integrity_key)
uuid_generate(tmp_uuid_bin);
uuid_unparse(tmp_uuid_bin, tmp_uuid);
if (snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid) < 0)
return -EINVAL;
goto out;
if (snprintf(tmp_path, sizeof(tmp_path), "%s/%s", crypt_get_dir(), tmp_name) < 0)
return -EINVAL;
goto out;
r = crypt_activate_by_volume_key(cd, tmp_name, integrity_key,
ARG_UINT32(OPT_INTEGRITY_KEY_SIZE_ID), CRYPT_ACTIVATE_PRIVATE | CRYPT_ACTIVATE_NO_JOURNAL);
if (r < 0)
return r;
goto out;
/* Wipe the device */
set_int_handler(0);
@@ -113,6 +116,8 @@ static int _wipe_data_device(struct crypt_device *cd, const char *integrity_key)
log_err(_("Cannot deactivate temporary device %s."), tmp_path);
set_int_block(0);
out:
free(backing_file);
return r;
}

View File

@@ -77,6 +77,8 @@ ARG(OPT_NO_WIPE, '\0', POPT_ARG_NONE, N_("Do not wipe device after format"), NUL
ARG(OPT_PROGRESS_FREQUENCY, '\0', POPT_ARG_STRING, N_("Progress line update (in seconds)"), N_("secs"), CRYPT_ARG_UINT32, {}, {})
ARG(OPT_PROGRESS_JSON, '\0', POPT_ARG_NONE, N_("Print wipe progress data in json format (suitable for machine processing)"), NULL, CRYPT_ARG_BOOL, {}, OPT_PROGRESS_JSON_ACTIONS)
ARG(OPT_INTEGRITY_BITMAP_MODE, 'B', POPT_ARG_NONE, N_("Use bitmap to track changes and disable journal for integrity device"), NULL, CRYPT_ARG_BOOL, {}, {})
ARG(OPT_INTEGRITY_RECALCULATE, '\0', POPT_ARG_NONE, N_("Recalculate initial tags automatically."), NULL, CRYPT_ARG_BOOL, {}, OPT_INTEGRITY_RECALCULATE_ACTIONS)

View File

@@ -37,6 +37,7 @@
#define OPT_JOURNAL_SIZE_ACTIONS { FORMAT_ACTION }
#define OPT_NO_WIPE_ACTIONS { FORMAT_ACTION }
#define OPT_INTERLEAVE_SECTORS_ACTIONS { FORMAT_ACTION }
#define OPT_PROGRESS_JSON_ACTIONS { FORMAT_ACTION }
#define OPT_SECTOR_SIZE_ACTIONS { FORMAT_ACTION }
#define OPT_TAG_SIZE_ACTIONS { FORMAT_ACTION }

View File

@@ -118,6 +118,7 @@
#define OPT_PERSISTENT "persistent"
#define OPT_PLUGIN "plugin"
#define OPT_PRIORITY "priority"
#define OPT_PROGRESS_JSON "progress-json"
#define OPT_PROGRESS_FREQUENCY "progress-frequency"
#define OPT_READONLY "readonly"
#define OPT_REDUCE_DEVICE_SIZE "reduce-device-size"

View File

@@ -224,17 +224,59 @@ static void tools_time_progress(uint64_t device_size, uint64_t bytes, struct too
fflush(stdout);
}
static void log_progress_json(const char *device, uint64_t bytes, uint64_t device_size, uint64_t eta, uint64_t uib, uint64_t time_spent)
{
int r;
char json[PATH_MAX+256];
r = snprintf(json, sizeof(json) - 1,
"{\"device\":\"%s\","
"\"device_bytes\":\"%" PRIu64 "\"," /* in bytes */
"\"device_size\":\"%" PRIu64 "\"," /* in bytes */
"\"speed\":\"%" PRIu64 "\"," /* in bytes per second */
"\"eta_ms\":\"%" PRIu64 "\"," /* in milliseconds */
"\"time_ms\":\"%" PRIu64 "\"}\n", /* in milliseconds */
device, bytes, device_size, uib, eta, time_spent);
if (r < 0 || (size_t)r >= sizeof(json) - 1)
return;
log_std(json);
}
static void tools_time_progress_json(uint64_t device_size, uint64_t bytes, struct tools_progress_params *parms)
{
double tdiff, uib;
bool final = (bytes == device_size);
if (!calculate_tdiff(final, bytes, parms, &tdiff))
return;
uib = (double)(bytes - parms->start_offset) / tdiff;
log_progress_json(parms->device,
bytes,
device_size,
final ? UINT64_C(0) : (uint64_t)((device_size / uib - tdiff) * 1E3),
(uint64_t)uib,
(uint64_t)(tdiff * 1E3));
fflush(stdout);
}
int tools_progress(uint64_t size, uint64_t offset, void *usrptr)
{
int r = 0;
struct tools_progress_params *parms = (struct tools_progress_params *)usrptr;
if (parms && !parms->batch_mode)
if (parms && parms->json_output)
tools_time_progress_json(size, offset, parms);
else if (parms && !parms->batch_mode)
tools_time_progress(size, offset, parms);
check_signal(&r);
if (r) {
if (!parms || !parms->frequency)
if (!parms || (!parms->frequency && !parms->json_output))
tools_clear_line();
if (parms && parms->interrupt_message)
log_err("%s", parms->interrupt_message);
@@ -242,3 +284,18 @@ int tools_progress(uint64_t size, uint64_t offset, void *usrptr)
return r;
}
const char *tools_get_device_name(const char *device, char **r_backing_file)
{
char *bfile;
assert(r_backing_file);
bfile = crypt_loop_backing_file(device);
if (bfile) {
*r_backing_file = bfile;
return bfile;
}
return device;
}

View File

@@ -849,12 +849,15 @@ static int luks2_reencrypt_in_progress(struct crypt_device *cd)
static int encrypt_luks2(int action_argc, const char **action_argv)
{
enum device_status_info dev_st;
int r = 0;
int r = -EINVAL;
struct crypt_device *cd = NULL;
char *backing_file = NULL;
struct tools_progress_params prog_parms = {
.frequency = ARG_UINT32(OPT_PROGRESS_FREQUENCY_ID),
.batch_mode = ARG_SET(OPT_BATCH_MODE_ID),
.interrupt_message = _("\nReencryption interrupted.")
.json_output = ARG_SET(OPT_PROGRESS_JSON_ID),
.interrupt_message = _("\nReencryption interrupted."),
.device = tools_get_device_name(crypt_get_device_name(cd), &backing_file)
};
if (ARG_SET(OPT_RESUME_ONLY_ID)) {
@@ -864,7 +867,7 @@ static int encrypt_luks2(int action_argc, const char **action_argv)
dev_st = load_luks(&cd, CRYPT_LUKS2, ARG_STR(OPT_HEADER_ID), action_argv[0]);
if (dev_st != DEVICE_LUKS2)
return -EINVAL;
goto out;
r = luks2_reencrypt_in_progress(cd);
if (r < 0)
@@ -886,38 +889,44 @@ static int encrypt_luks2(int action_argc, const char **action_argv)
}
out:
crypt_free(cd);
free(backing_file);
return r;
}
static int decrypt_luks2(struct crypt_device *cd, int action_argc, const char **action_argv)
{
int r = 0;
int r = -EINVAL;
char *backing_file = NULL;
struct tools_progress_params prog_parms = {
.frequency = ARG_UINT32(OPT_PROGRESS_FREQUENCY_ID),
.batch_mode = ARG_SET(OPT_BATCH_MODE_ID),
.interrupt_message = _("\nReencryption interrupted.")
.json_output = ARG_SET(OPT_PROGRESS_JSON_ID),
.interrupt_message = _("\nReencryption interrupted."),
.device = tools_get_device_name(crypt_get_device_name(cd), &backing_file)
};
if (!ARG_SET(OPT_HEADER_ID)) {
log_err(_("LUKS2 decryption requires option --header."));
return -EINVAL;
goto out;
}
r = luks2_reencrypt_in_progress(cd);
if (r < 0) /* error */
return r;
goto out;
if (r > 0) { /* in progress */
if (ARG_SET(OPT_INIT_ONLY_ID)) {
log_err(_("LUKS2 reencryption already initialized. Aborting operation."));
return -EINVAL;
r = -EINVAL;
goto out;
}
r = action_reencrypt_load(cd, action_argv[0]);
} else {
if (ARG_SET(OPT_RESUME_ONLY_ID)) {
log_err(_("Device reencryption not in progress."));
return -EINVAL;
r = -EINVAL;
goto out;
}
r = action_decrypt_luks2(cd, action_argv[0]);
}
@@ -927,32 +936,39 @@ static int decrypt_luks2(struct crypt_device *cd, int action_argc, const char **
r = crypt_reencrypt_run(cd, tools_progress, &prog_parms);
}
out:
free(backing_file);
return r;
}
static int reencrypt_luks2(struct crypt_device *cd, int action_argc, const char **action_argv)
{
int r;
char *backing_file = NULL;
struct tools_progress_params prog_parms = {
.frequency = ARG_UINT32(OPT_PROGRESS_FREQUENCY_ID),
.batch_mode = ARG_SET(OPT_BATCH_MODE_ID),
.interrupt_message = _("\nReencryption interrupted.")
.json_output = ARG_SET(OPT_PROGRESS_JSON_ID),
.interrupt_message = _("\nReencryption interrupted."),
.device = tools_get_device_name(crypt_get_device_name(cd), &backing_file)
};
r = luks2_reencrypt_in_progress(cd);
if (r < 0) /* error */
return r;
goto out;
if (r > 0) { /* in progress */
if (ARG_SET(OPT_INIT_ONLY_ID)) {
log_err(_("LUKS2 reencryption already initialized. Aborting operation."));
return -EINVAL;
r = -EINVAL;
goto out;
}
r = action_reencrypt_load(cd, action_argv[0]);
} else {
if (ARG_SET(OPT_RESUME_ONLY_ID)) {
log_err(_("Device reencryption not in progress."));
return -EINVAL;
r = -EINVAL;
goto out;
}
r = action_reencrypt_luks2(cd, action_argv[0]);
}
@@ -962,6 +978,8 @@ static int reencrypt_luks2(struct crypt_device *cd, int action_argc, const char
r = crypt_reencrypt_run(cd, tools_progress, &prog_parms);
}
out:
free(backing_file);
return r;
}

View File

@@ -717,10 +717,14 @@ static int copy_data_forward(struct reenc_ctx *rc, int fd_old, int fd_new,
size_t block_size, void *buf, uint64_t *bytes)
{
ssize_t s1, s2;
int r = -EIO;
char *backing_file = NULL;
struct tools_progress_params prog_parms = {
.frequency = ARG_UINT32(OPT_PROGRESS_FREQUENCY_ID),
.batch_mode = ARG_SET(OPT_BATCH_MODE_ID),
.interrupt_message = _("\nReencryption interrupted.")
.json_output = ARG_SET(OPT_PROGRESS_JSON_ID),
.interrupt_message = _("\nReencryption interrupted."),
.device = tools_get_device_name(rc->device, &backing_file)
};
log_dbg("Reencrypting in forward direction.");
@@ -728,7 +732,7 @@ static int copy_data_forward(struct reenc_ctx *rc, int fd_old, int fd_new,
if (lseek64(fd_old, rc->device_offset, SEEK_SET) < 0 ||
lseek64(fd_new, rc->device_offset, SEEK_SET) < 0) {
log_err(_("Cannot seek to device offset."));
return -EIO;
goto out;
}
rc->resume_bytes = *bytes = rc->device_offset;
@@ -736,7 +740,7 @@ static int copy_data_forward(struct reenc_ctx *rc, int fd_old, int fd_new,
tools_progress(rc->device_size, *bytes, &prog_parms);
if (write_log(rc) < 0)
return -EIO;
goto out;
while (!quit && rc->device_offset < rc->device_size) {
if ((rc->device_size - rc->device_offset) < (uint64_t)block_size)
@@ -746,7 +750,7 @@ static int copy_data_forward(struct reenc_ctx *rc, int fd_old, int fd_new,
(rc->device_offset + s1) != rc->device_size)) {
log_dbg("Read error, expecting %zu, got %zd.",
block_size, s1);
return -EIO;
goto out;
}
/* If device_size is forced, never write more than limit */
@@ -757,16 +761,16 @@ static int copy_data_forward(struct reenc_ctx *rc, int fd_old, int fd_new,
if (s2 < 0) {
log_dbg("Write error, expecting %zu, got %zd.",
block_size, s2);
return -EIO;
goto out;
}
rc->device_offset += s1;
if (ARG_SET(OPT_WRITE_LOG_ID) && write_log(rc) < 0)
return -EIO;
goto out;
if (ARG_SET(OPT_USE_FSYNC_ID) && fsync(fd_new) < 0) {
log_dbg("Write error, fsync.");
return -EIO;
goto out;
}
*bytes += (uint64_t)s2;
@@ -774,7 +778,10 @@ static int copy_data_forward(struct reenc_ctx *rc, int fd_old, int fd_new,
tools_progress(rc->device_size, *bytes, &prog_parms);
}
return quit ? -EAGAIN : 0;
r = 0;
out:
free(backing_file);
return quit ? -EAGAIN : r;
}
static int copy_data_backward(struct reenc_ctx *rc, int fd_old, int fd_new,
@@ -782,10 +789,14 @@ static int copy_data_backward(struct reenc_ctx *rc, int fd_old, int fd_new,
{
ssize_t s1, s2, working_block;
off64_t working_offset;
int r = -EIO;
char *backing_file = NULL;
struct tools_progress_params prog_parms = {
.frequency = ARG_UINT32(OPT_PROGRESS_FREQUENCY_ID),
.batch_mode = ARG_SET(OPT_BATCH_MODE_ID),
.interrupt_message = _("\nReencryption interrupted.")
.json_output = ARG_SET(OPT_PROGRESS_JSON_ID),
.interrupt_message = _("\nReencryption interrupted."),
.device = tools_get_device_name(rc->device, &backing_file)
};
log_dbg("Reencrypting in backward direction.");
@@ -802,7 +813,7 @@ static int copy_data_backward(struct reenc_ctx *rc, int fd_old, int fd_new,
tools_progress(rc->device_size, *bytes, &prog_parms);
if (write_log(rc) < 0)
return -EIO;
goto out;
/* dirty the device during ENCRYPT mode */
rc->stained = 1;
@@ -819,30 +830,30 @@ static int copy_data_backward(struct reenc_ctx *rc, int fd_old, int fd_new,
if (lseek64(fd_old, working_offset, SEEK_SET) < 0 ||
lseek64(fd_new, working_offset, SEEK_SET) < 0) {
log_err(_("Cannot seek to device offset."));
return -EIO;
goto out;
}
s1 = read_buf(fd_old, buf, working_block);
if (s1 < 0 || (s1 != working_block)) {
log_dbg("Read error, expecting %zu, got %zd.",
block_size, s1);
return -EIO;
goto out;
}
s2 = write(fd_new, buf, working_block);
if (s2 < 0) {
log_dbg("Write error, expecting %zu, got %zd.",
block_size, s2);
return -EIO;
goto out;
}
rc->device_offset -= s1;
if (ARG_SET(OPT_WRITE_LOG_ID) && write_log(rc) < 0)
return -EIO;
goto out;
if (ARG_SET(OPT_USE_FSYNC_ID) && fsync(fd_new) < 0) {
log_dbg("Write error, fsync.");
return -EIO;
goto out;
}
*bytes += (uint64_t)s2;
@@ -850,7 +861,10 @@ static int copy_data_backward(struct reenc_ctx *rc, int fd_old, int fd_new,
tools_progress(rc->device_size, *bytes, &prog_parms);
}
return quit ? -EAGAIN : 0;
r = 0;
out:
free(backing_file);
return quit ? -EAGAIN : r;
}
static void zero_rest_of_device(int fd, size_t block_size, void *buf,