diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 12a5cf6cf9b9..fc7c7de30af1 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -3986,7 +3986,7 @@ zfs_do_send(int argc, char **argv) }; /* check options */ - while ((c = getopt_long(argc, argv, ":i:I:RDpvnPLeht:cwb", long_options, + while ((c = getopt_long(argc, argv, ":i:I:RDpVvnPLeht:cwb", long_options, NULL)) != -1) { switch (c) { case 'i': @@ -4016,6 +4016,10 @@ zfs_do_send(int argc, char **argv) flags.parsable = B_TRUE; flags.verbose = B_TRUE; break; + case 'V': + flags.progress = B_TRUE; + flags.progressastitle = B_TRUE; + break; case 'v': if (flags.verbose) extraverbose = B_TRUE; diff --git a/include/libzfs.h b/include/libzfs.h index 65b06f7a806c..6f5e31139aa9 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -648,6 +648,9 @@ typedef struct sendflags { /* include snapshot holds in send stream */ boolean_t holds; + + /* show progress as process title(ie. -V) */ + boolean_t progressastitle; } sendflags_t; typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *); diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index 2aa0fd222514..35b3920b29a5 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -89,6 +89,8 @@ typedef struct progress_arg { zfs_handle_t *pa_zhp; int pa_fd; boolean_t pa_parsable; + boolean_t pa_astitle; + uint64_t pa_size; } progress_arg_t; typedef struct dataref { @@ -1034,6 +1036,7 @@ typedef struct send_dump_data { boolean_t seenfrom, seento, replicate, doall, fromorigin; boolean_t verbose, dryrun, parsable, progress, embed_data, std_out; boolean_t large_block, compress, raw, holds; + boolean_t progressastitle; int outfd; boolean_t err; nvlist_t *fss; @@ -1209,16 +1212,20 @@ send_progress_thread(void *arg) zfs_cmd_t zc = {"\0"}; zfs_handle_t *zhp = pa->pa_zhp; libzfs_handle_t *hdl = zhp->zfs_hdl; - unsigned long long bytes; + unsigned long long bytes, total; + int pct; char buf[16]; time_t t; struct tm *tm; (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - if (!pa->pa_parsable) + if (!pa->pa_parsable && !pa->pa_astitle) (void) fprintf(stderr, "TIME SENT SNAPSHOT\n"); + if (pa->pa_astitle) + total = pa->pa_size / 100; + /* * Print the progress from ZFS_IOC_SEND_PROGRESS every second. */ @@ -1233,15 +1240,27 @@ send_progress_thread(void *arg) tm = localtime(&t); bytes = zc.zc_cookie; - if (pa->pa_parsable) { - (void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n", - tm->tm_hour, tm->tm_min, tm->tm_sec, - bytes, zhp->zfs_name); + if (pa->pa_astitle) { + if (total > 0) { + pct = bytes / total; + } else + pct = 100; + if (pct > 100) + pct = 100; + + setproctitle("sending %s (%d%%: %llu/%llu)", + zhp->zfs_name, pct, bytes, pa->pa_size); } else { - zfs_nicebytes(bytes, buf, sizeof (buf)); - (void) fprintf(stderr, "%02d:%02d:%02d %5s %s\n", - tm->tm_hour, tm->tm_min, tm->tm_sec, - buf, zhp->zfs_name); + if (pa->pa_parsable) { + (void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n", + tm->tm_hour, tm->tm_min, tm->tm_sec, + bytes, zhp->zfs_name); + } else { + zfs_nicenum(bytes, buf, sizeof (buf)); + (void) fprintf(stderr, "%02d:%02d:%02d %5s %s\n", + tm->tm_hour, tm->tm_min, tm->tm_sec, + buf, zhp->zfs_name); + } } } } @@ -1407,6 +1426,8 @@ dump_snapshot(zfs_handle_t *zhp, void *arg) pa.pa_zhp = zhp; pa.pa_fd = sdd->outfd; pa.pa_parsable = sdd->parsable; + pa.pa_size = sdd->size; + pa.pa_astitle = sdd->progressastitle; if ((err = pthread_create(&tid, NULL, send_progress_thread, &pa)) != 0) { @@ -2014,6 +2035,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, sdd.verbose = flags->verbose; sdd.parsable = flags->parsable; sdd.progress = flags->progress; + sdd.progressastitle = flags->progressastitle; sdd.dryrun = flags->dryrun; sdd.large_block = flags->largeblock; sdd.embed_data = flags->embed_data; @@ -2053,7 +2075,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, sdd.snapholds = NULL; } - if (flags->verbose || sdd.snapholds != NULL) { + if (flags->progress || sdd.snapholds != NULL) { /* * Do a verbose no-op dry run to get all the verbose output * or to gather snapshot hold's before generating any data,