#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
+#include <sys/time.h>
#include <rt_names.h>
#include "version.h"
struct cmd_dev_flash_status_ctx {
struct dl *dl;
+ struct timespec time_of_last_status;
+ uint64_t status_msg_timeout;
+ size_t elapsed_time_msg_len;
char *last_msg;
char *last_component;
uint8_t not_first:1,
return str1 ? 1 : -1;
}
+static void cmd_dev_flash_clear_elapsed_time(struct cmd_dev_flash_status_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < ctx->elapsed_time_msg_len; i++)
+ pr_out_tty("\b \b");
+
+ ctx->elapsed_time_msg_len = 0;
+}
+
static int cmd_dev_flash_status_cb(const struct nlmsghdr *nlh, void *data)
{
struct cmd_dev_flash_status_ctx *ctx = data;
const char *bus_name;
const char *dev_name;
+ cmd_dev_flash_clear_elapsed_time(ctx);
+
if (genl->cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS &&
genl->cmd != DEVLINK_CMD_FLASH_UPDATE_END)
return MNL_CB_STOP;
done = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]);
if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL])
total = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]);
+ if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TIMEOUT])
+ ctx->status_msg_timeout = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TIMEOUT]);
+ else
+ ctx->status_msg_timeout = 0;
if (!nullstrcmp(msg, ctx->last_msg) &&
!nullstrcmp(component, ctx->last_component) &&
ctx->last_pc && ctx->not_first) {
pr_out_tty("\b\b\b\b\b"); /* clean percentage */
} else {
+ /* only update the last status timestamp if the message changed */
+ clock_gettime(CLOCK_MONOTONIC, &ctx->time_of_last_status);
+
if (ctx->not_first)
pr_out("\n");
if (component) {
return MNL_CB_STOP;
}
+static void cmd_dev_flash_time_elapsed(struct cmd_dev_flash_status_ctx *ctx)
+{
+ struct timespec now, res;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ res.tv_sec = now.tv_sec - ctx->time_of_last_status.tv_sec;
+ res.tv_nsec = now.tv_nsec - ctx->time_of_last_status.tv_nsec;
+ if (res.tv_nsec < 0) {
+ res.tv_sec--;
+ res.tv_nsec += 1000000000L;
+ }
+
+ /* Only begin displaying an elapsed time message if we've waited a few
+ * seconds with no response, or the status message included a timeout
+ * value.
+ */
+ if (res.tv_sec > 2 || ctx->status_msg_timeout) {
+ uint64_t elapsed_m, elapsed_s;
+ char msg[128];
+ size_t len;
+
+ /* clear the last elapsed time message, if we have one */
+ cmd_dev_flash_clear_elapsed_time(ctx);
+
+ elapsed_m = res.tv_sec / 60;
+ elapsed_s = res.tv_sec % 60;
+
+ /**
+ * If we've elapsed a few seconds without receiving any status
+ * notification from the device, we display a time elapsed
+ * message. This has a few possible formats:
+ *
+ * 1) just time elapsed, when no timeout was provided
+ * " ( Xm Ys )"
+ * 2) time elapsed out of a timeout that came from the device
+ * driver via DEVLINK_CMD_FLASH_UPDATE_STATUS_TIMEOUT
+ * " ( Xm Ys : Am Ys)"
+ * 3) time elapsed if we still receive no status after
+ * reaching the provided timeout.
+ * " ( Xm Ys : timeout reached )"
+ */
+ if (!ctx->status_msg_timeout) {
+ len = snprintf(msg, sizeof(msg),
+ " ( %lum %lus )", elapsed_m, elapsed_s);
+ } else if (res.tv_sec <= ctx->status_msg_timeout) {
+ uint64_t timeout_m, timeout_s;
+
+ timeout_m = ctx->status_msg_timeout / 60;
+ timeout_s = ctx->status_msg_timeout % 60;
+
+ len = snprintf(msg, sizeof(msg),
+ " ( %lum %lus : %lum %lus )",
+ elapsed_m, elapsed_s, timeout_m, timeout_s);
+ } else {
+ len = snprintf(msg, sizeof(msg),
+ " ( %lum %lus : timeout reached )", elapsed_m, elapsed_s);
+ }
+
+ ctx->elapsed_time_msg_len = len;
+
+ pr_out_tty("%s", msg);
+ fflush(stdout);
+ }
+}
+
static int cmd_dev_flash_fds_process(struct cmd_dev_flash_status_ctx *ctx,
struct mnlg_socket *nlg_ntf,
int pipe_r)
{
int nlfd = mnlg_socket_get_fd(nlg_ntf);
+ struct timeval timeout;
fd_set fds[3];
int fdmax;
int i;
if (nlfd >= fdmax)
fdmax = nlfd + 1;
- while (select(fdmax, &fds[0], &fds[1], &fds[2], NULL) < 0) {
+ /* select only for a short while (1/10th of a second) in order to
+ * allow periodically updating the screen with an elapsed time
+ * indicator.
+ */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 100000;
+
+ while (select(fdmax, &fds[0], &fds[1], &fds[2], &timeout) < 0) {
if (errno == EINTR)
continue;
pr_err("select() failed\n");
return err2;
ctx->flash_done = 1;
}
+ cmd_dev_flash_time_elapsed(ctx);
return 0;
}
}
close(pipe_w);
+ /* initialize starting time to allow comparison for when to begin
+ * displaying a time elapsed message.
+ */
+ clock_gettime(CLOCK_MONOTONIC, &ctx.time_of_last_status);
+
do {
err = cmd_dev_flash_fds_process(&ctx, nlg_ntf, pipe_r);
if (err)
free(dl);
}
-static int dl_batch(struct dl *dl, const char *name, bool force)
+static int dl_batch_cmd(int argc, char *argv[], void *data)
{
- char *line = NULL;
- size_t len = 0;
- int ret = EXIT_SUCCESS;
-
- if (name && strcmp(name, "-") != 0) {
- if (freopen(name, "r", stdin) == NULL) {
- fprintf(stderr,
- "Cannot open file \"%s\" for reading: %s\n",
- name, strerror(errno));
- return EXIT_FAILURE;
- }
- }
-
- cmdlineno = 0;
- while (getcmdline(&line, &len, stdin) != -1) {
- char *largv[100];
- int largc;
-
- largc = makeargs(line, largv, 100);
- if (!largc)
- continue; /* blank line */
-
- if (dl_cmd(dl, largc, largv)) {
- fprintf(stderr, "Command failed %s:%d\n",
- name, cmdlineno);
- ret = EXIT_FAILURE;
- if (!force)
- break;
- }
- }
+ struct dl *dl = data;
- if (line)
- free(line);
+ return dl_cmd(dl, argc, argv);
+}
- return ret;
+static int dl_batch(struct dl *dl, const char *name, bool force)
+{
+ return do_batch(name, force, dl_batch_cmd, dl);
}
int main(int argc, char **argv)