return purged;
}
+static BSOCK *open_sd_bsock(UAContext *ua)
+{
+ STORE *store = ua->jcr->wstore;
+
+ if (!ua->jcr->store_bsock) {
+ ua->send_msg(_("Connecting to Storage daemon %s at %s:%d ...\n"),
+ store->name(), store->address, store->SDport);
+ if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
+ ua->error_msg(_("Failed to connect to Storage daemon.\n"));
+ return NULL;
+ }
+ }
+ return ua->jcr->store_bsock;
+}
+
/*
* IF volume status is Append, Full, Used, or Error, mark it Purged
* Purged volumes can then be recycled (if enabled).
if (!db_update_media_record(jcr, ua->db, mr)) {
return false;
}
+
+ /* Send the command to truncate the volume after purge. If this feature
+ * is disabled for the specific device, this will be a no-op. */
+ BSOCK *sd;
+ if ((sd=open_sd_bsock(ua)) != NULL) {
+ sd->fsend("truncate_on_purge %s vol=%s", ua->jcr->wstore->dev_name(), mr->VolumeName);
+
+ while (sd->recv() >= 0)
+ ua->send_msg("%s", sd->msg);
+
+ sd->signal(BNET_TERMINATE);
+ sd->close();
+ ua->jcr->store_bsock = NULL;
+ }
+
pm_strcpy(jcr->VolumeName, mr->VolumeName);
generate_job_event(jcr, "VolumePurged");
generate_plugin_event(jcr, bEventVolumePurged);
dev->max_spool_size = device->max_spool_size;
dev->drive_index = device->drive_index;
dev->autoselect = device->autoselect;
+ dev->truncate_on_purge = device->truncate_on_purge;
dev->dev_type = device->dev_type;
dev->init_backend();
if (dev->is_tape()) { /* No parts on tapes */
int openmode; /* parameter passed to open_dev (useful to reopen the device) */
int dev_type; /* device type */
bool autoselect; /* Autoselect in autochanger */
+ bool truncate_on_purge; /* Truncate this volume when it gets purged? */
bool initiated; /* set when init_dev() called */
int label_type; /* Bacula/ANSI/IBM label types */
uint32_t drive_index; /* Autochanger drive index (base 0) */
static bool cancel_cmd(JCR *cjcr);
static bool mount_cmd(JCR *jcr);
static bool unmount_cmd(JCR *jcr);
+static bool truncate_on_purge_cmd(JCR *jcr);
static bool bootstrap_cmd(JCR *jcr);
static bool changer_cmd(JCR *sjcr);
static bool do_label(JCR *jcr, int relabel);
{"status", status_cmd, 1},
{".status", qstatus_cmd, 1},
{"unmount", unmount_cmd, 0},
+ {"truncate_on_purge", truncate_on_purge_cmd, 0},
{"use storage=", use_cmd, 0},
{"run", run_cmd, 0},
// {"query", query_cmd, 0},
return true;
}
+/*
+ * The truncate command will recycle a volume. The director can call this
+ * after purging a volume so that disk space will not be wasted. Only useful
+ * for File Storage, of course.
+ *
+ */
+static bool truncate_on_purge_cmd(JCR *jcr)
+{
+ POOL_MEM devname;
+ POOL_MEM volumename;
+ BSOCK *dir = jcr->dir_bsock;
+ DEVICE *dev;
+ DCR *dcr;
+ int drive;
+
+ if (sscanf(dir->msg, "truncate_on_purge %127s vol=%s", devname.c_str(), volumename.c_str()) != 2) {
+ dir->fsend(_("3916 Error scanning truncate_on_purge command\n"));
+ goto done;
+ }
+
+ /* FIXME: autochanger, drive = 0? how can we avoid that? we only work on files */
+ if ((dcr = find_device(jcr, devname, 0)) == NULL) {
+ dir->fsend(_("3999 Device \"%s\" not found or could not be opened.\n"), devname.c_str());
+ goto done;
+ }
+
+ dev = dcr->dev;
+
+ if (!dev->truncate_on_purge) {
+ dir->fsend(_("3919 Truncate on purge not enabled, skipping\n"));
+ goto done;
+ }
+
+ /* Store the VolumeName for opening and re-labeling the volume */
+ bstrncpy(dcr->VolumeName, volumename.c_str(), sizeof(dcr->VolumeName));
+ bstrncpy(dev->VolHdr.VolumeName, volumename.c_str(), sizeof(dev->VolHdr.VolumeName));
+
+ /* Re-write the label with the recycle flag */
+ if (rewrite_volume_label(dcr, true))
+ dir->fsend(_("3917 Volume recycled\n"));
+ else dir->fsend(_("3918 Recycle failed\n"));
+
+done:
+ dir->signal(BNET_EOD);
+ return true;
+}
+
/*
* Release command from Director. This rewinds the device and if
* configured does a offline and ensures that Bacula will
len = Mmsg(msg, _("Device parameters:\n"));
sendit(msg, len, sp);
+ len = Mmsg(msg, _("truncate on purge: %d\n"), dev->device->truncate_on_purge);
+ sendit(msg, len, sp);
+
len = Mmsg(msg, _("Archive name: %s Device name: %s\n"), dev->archive_name(),
dev->name());
sendit(msg, len, sp);
{"offlineonunmount", store_bit, ITEM(res_dev.cap_bits), CAP_OFFLINEUNMOUNT, ITEM_DEFAULT, 0},
{"blockchecksum", store_bit, ITEM(res_dev.cap_bits), CAP_BLOCKCHECKSUM, ITEM_DEFAULT, 1},
{"autoselect", store_bool, ITEM(res_dev.autoselect), 1, ITEM_DEFAULT, 1},
+ {"truncateonpurge", store_bool, ITEM(res_dev.truncate_on_purge), 1, ITEM_DEFAULT, 0},
{"changerdevice", store_strname,ITEM(res_dev.changer_name), 0, 0, 0},
{"changercommand", store_strname,ITEM(res_dev.changer_command), 0, 0, 0},
{"alertcommand", store_strname,ITEM(res_dev.alert_command), 0, 0, 0},
uint32_t dev_type; /* device type */
uint32_t label_type; /* label type */
bool autoselect; /* Automatically select from AutoChanger */
+ bool truncate_on_purge; /* Truncate this volume when it gets purged? */
uint32_t drive_index; /* Autochanger drive index */
uint32_t cap_bits; /* Capabilities of this device */
utime_t max_changer_wait; /* Changer timeout */