/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
* All rights reserved
*/
#include <stddef.h>
#include <pthread.h>
#include <umem.h>
+#include <time.h>
#include <libzfs.h>
libzfs_handle_t *dedup_hdl;
} dedup_arg_t;
+typedef struct progress_arg {
+ zfs_handle_t *pa_zhp;
+ int pa_fd;
+ boolean_t pa_parsable;
+} progress_arg_t;
+
typedef struct dataref {
uint64_t ref_guid;
uint64_t ref_object;
char prevsnap[ZFS_MAXNAMELEN];
uint64_t prevsnap_obj;
boolean_t seenfrom, seento, replicate, doall, fromorigin;
- boolean_t verbose, dryrun, parsable;
+ boolean_t verbose, dryrun, parsable, progress;
int outfd;
boolean_t err;
nvlist_t *fss;
return (error);
}
+static void *
+send_progress_thread(void *arg)
+{
+ progress_arg_t *pa = arg;
+
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_handle_t *zhp = pa->pa_zhp;
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ unsigned long long bytes;
+ char buf[16];
+
+ time_t t;
+ struct tm *tm;
+
+ assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ if (!pa->pa_parsable)
+ (void) fprintf(stderr, "TIME SENT SNAPSHOT\n");
+
+ /*
+ * Print the progress from ZFS_IOC_SEND_PROGRESS every second.
+ */
+ for (;;) {
+ (void) sleep(1);
+
+ zc.zc_cookie = pa->pa_fd;
+ if (zfs_ioctl(hdl, ZFS_IOC_SEND_PROGRESS, &zc) != 0)
+ return ((void *)-1);
+
+ (void) time(&t);
+ 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);
+ } 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);
+ }
+ }
+}
+
static int
dump_snapshot(zfs_handle_t *zhp, void *arg)
{
send_dump_data_t *sdd = arg;
+ progress_arg_t pa = { 0 };
+ pthread_t tid;
+
char *thissnap;
int err;
boolean_t isfromsnap, istosnap, fromorigin;
}
if (!sdd->dryrun) {
+ /*
+ * If progress reporting is requested, spawn a new thread to
+ * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
+ */
+ if (sdd->progress) {
+ pa.pa_zhp = zhp;
+ pa.pa_fd = sdd->outfd;
+ pa.pa_parsable = sdd->parsable;
+
+ if ((err = pthread_create(&tid, NULL,
+ send_progress_thread, &pa))) {
+ zfs_close(zhp);
+ return (err);
+ }
+ }
+
err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
fromorigin, sdd->outfd, sdd->debugnv);
+
+ if (sdd->progress) {
+ (void) pthread_cancel(tid);
+ (void) pthread_join(tid, NULL);
+ }
}
(void) strcpy(sdd->prevsnap, thissnap);
avl_tree_t *fsavl = NULL;
static uint64_t holdseq;
int spa_version;
- boolean_t holdsnaps = B_FALSE;
pthread_t tid;
int pipefd[2];
dedup_arg_t dda = { 0 };
}
}
- if (!flags->dryrun && zfs_spa_version(zhp, &spa_version) == 0 &&
- spa_version >= SPA_VERSION_USERREFS &&
- (flags->doall || flags->replicate))
- holdsnaps = B_TRUE;
-
if (flags->dedup && !flags->dryrun) {
featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
DMU_BACKUP_FEATURE_DEDUPPROPS);
sdd.fsavl = fsavl;
sdd.verbose = flags->verbose;
sdd.parsable = flags->parsable;
+ sdd.progress = flags->progress;
sdd.dryrun = flags->dryrun;
sdd.filter_cb = filter_func;
sdd.filter_cb_arg = cb_arg;
if (debugnvp)
sdd.debugnv = *debugnvp;
- if (holdsnaps) {
+
+ /*
+ * Some flags require that we place user holds on the datasets that are
+ * being sent so they don't get destroyed during the send. We can skip
+ * this step if the pool is imported read-only since the datasets cannot
+ * be destroyed.
+ */
+ if (!flags->dryrun && !zpool_get_prop_int(zfs_get_pool_handle(zhp),
+ ZPOOL_PROP_READONLY, NULL) &&
+ zfs_spa_version(zhp, &spa_version) == 0 &&
+ spa_version >= SPA_VERSION_USERREFS &&
+ (flags->doall || flags->replicate)) {
++holdseq;
(void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
".send-%d-%llu", getpid(), (u_longlong_t)holdseq);