/*
Bacula® - The Network Backup Solution
- Copyright (C) 2003-2008 Free Software Foundation Europe e.V.
+ Copyright (C) 2003-2012 Free Software Foundation Europe e.V.
The main author of Bacula is Kern Sibbald, with contributions from
many others, a complete list can be found in the file AUTHORS.
This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
+ modify it under the terms of version three of the GNU Affero General Public
License as published by the Free Software Foundation and included
in the file LICENSE.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*
* Kern Sibbald, April MMIII
*
- * Version $Id$
*/
#include "bacula.h"
slot_list[i] = 0;
}
i = find_arg_with_value(ua, "slots");
+ if (i == -1) { /* not found */
+ i = find_arg_with_value(ua, "slot");
+ }
if (i > 0) {
/* scan slot list in ua->argv[i] */
char *p, *e, *h;
return true;
bail_out:
+ Dmsg1(100, "Problem with user selection ERR=%s\n", msg);
return false;
}
Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
}
slot_list[vl->Slot] = 0; /* clear Slot */
- memset(&mr, 0, sizeof(mr));
mr.Slot = vl->Slot;
mr.InChanger = 1;
- mr.StorageId = store.store->StorageId;
- /* Set InChanger to zero for this Slot */
+ mr.MediaId = 0; /* Force using VolumeName */
+ if (vl->VolName) {
+ bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
+ } else {
+ mr.VolumeName[0] = 0;
+ }
+ set_storageid_in_mr(store.store, &mr);
+ Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
+ mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
db_lock(ua->db);
+ /* Set InChanger to zero for this Slot */
db_make_inchanger_unique(ua->jcr, ua->db, &mr);
db_unlock(ua->db);
+ Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
+ mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
if (!vl->VolName) {
Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
continue;
}
- memset(&mr, 0, sizeof(mr));
- bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
db_lock(ua->db);
+ Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
+ mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
if (db_get_media_record(ua->jcr, ua->db, &mr)) {
+ Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
+ mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
+ /* If Slot, Inchanger, and StorageId have changed, update the Media record */
if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
mr.Slot = vl->Slot;
mr.InChanger = 1;
- mr.StorageId = store.store->StorageId;
if (have_enabled) {
mr.Enabled = Enabled;
}
+ set_storageid_in_mr(store.store, &mr);
if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
ua->error_msg("%s", db_strerror(ua->db));
} else {
}
db_unlock(ua->db);
}
- memset(&mr, 0, sizeof(mr));
+ mr.clear();
mr.InChanger = 1;
- mr.StorageId = store.store->StorageId;
+ set_storageid_in_mr(store.store, &mr);
db_lock(ua->db);
for (int i=1; i <= max_slots; i++) {
if (slot_list[i]) {
/* Check for oldvolume=name */
i = find_arg_with_value(ua, "oldvolume");
if (i >= 0) {
- memset(&omr, 0, sizeof(omr));
bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
if (db_get_media_record(ua->jcr, ua->db, &omr)) {
goto checkVol;
continue;
}
- memset(&mr, 0, sizeof(mr));
bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
/* If VolBytes are zero the Volume is not labeled */
if (db_get_media_record(ua->jcr, ua->db, &mr)) {
}
mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
}
- mr.StorageId = store.store->StorageId;
+ set_storageid_in_mr(store.store, &mr);
bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
if (!select_pool_dbr(ua, &pr)) {
goto bail_out;
}
- memset(&omr, 0, sizeof(omr));
/* Fire off the label requests */
for (vl=vol_list; vl; vl=vl->next) {
if (!vl->VolName || !slot_list[vl->Slot]) {
continue;
}
- memset(&mr, 0, sizeof(mr));
+ mr.clear();
bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
media_record_exists = false;
if (db_get_media_record(ua->jcr, ua->db, &mr)) {
vl->Slot, mr.VolumeName);
mr.Slot = vl->Slot;
mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
- mr.StorageId = store->StorageId;
+ set_storageid_in_mr(store, &mr);
if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
}
media_record_exists = true;
}
mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
- mr.StorageId = store->StorageId;
+ set_storageid_in_mr(store, &mr);
/*
* Deal with creating cleaning tape here. Normal tapes created in
* send_label_request() below
mr.VolBytes = 1; /* any bytes to indicate it exists */
bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
mr.MediaType[0] = 0;
- mr.StorageId = store->StorageId;
+ set_storageid_in_mr(store, &mr);
if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
ua->error_msg("%s", db_strerror(ua->db));
}
set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
mr.MediaType[0] = 0;
+ set_storageid_in_mr(store, &mr);
if (db_create_media_record(ua->jcr, ua->db, &mr)) {
ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"),
mr.VolumeName);
if (media_record_exists) { /* we update it */
mr->VolBytes = VolBytes;
mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
- mr->StorageId = ua->jcr->wstore->StorageId;
+ set_storageid_in_mr(ua->jcr->wstore, mr);
if (!db_update_media_record(ua->jcr, ua->db, mr)) {
ua->error_msg("%s", db_strerror(ua->db));
ok = false;
set_pool_dbr_defaults_in_media_dbr(mr, pr);
mr->VolBytes = VolBytes;
mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
- mr->StorageId = ua->jcr->wstore->StorageId;
mr->Enabled = 1;
+ set_storageid_in_mr(ua->jcr->wstore, mr);
if (db_create_media_record(ua->jcr, ua->db, mr)) {
ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
mr->VolumeName, mr->Slot);
vl->next = vol_list;
vol_list = vl;
} else {
- /* Add new entry to end of list */
+ vol_list_t *prev=vol_list;
+ /* Add new entry to the right place in the list */
for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
+ if (tvl->Slot > vl->Slot) {
+ /* no previous item, update vol_list directly */
+ if (prev == vol_list) {
+ vl->next = vol_list;
+ vol_list = vl;
+
+ } else { /* replace the previous pointer */
+ prev->next = vl;
+ vl->next = tvl;
+ }
+ break;
+ }
+ /* we are at the end */
if (!tvl->next) {
tvl->next = vl;
vl->next = NULL;
break;
}
+ prev = tvl;
}
}
}
return drives;
}
-
-
-
/*
* Check if this is a cleaning tape by comparing the Volume name
* with the Cleaning Prefix. If they match, this is a cleaning
strlen(ua->jcr->pool->cleaning_prefix)) == 0;
}
+static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
+{
+ char ed1[50], ed2[50], ed3[50];
+ POOL_DBR pr;
+ MEDIA_DBR mr;
+ /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
+ const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
+ const char *slot_api_empty_format="%c|%i||||||||\n";
+
+ if (is_volume_name_legal(NULL, vol_name)) {
+ bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
+ if (db_get_media_record(ua->jcr, ua->db, &mr)) {
+ memset(&pr, 0, sizeof(POOL_DBR));
+ pr.PoolId = mr.PoolId;
+ if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
+ strcpy(pr.Name, "?");
+ }
+ ua->send_msg(slot_api_full_format, type,
+ Slot, mr.Slot, mr.VolumeName,
+ edit_uint64(mr.VolBytes, ed1),
+ mr.VolStatus, mr.MediaType, pr.Name,
+ edit_uint64(mr.LastWritten, ed2),
+ edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
+
+ } else { /* Media unknown */
+ ua->send_msg(slot_api_full_format,
+ type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
+ "0", "0");
+
+ }
+ } else {
+ ua->send_msg(slot_api_empty_format, type, Slot);
+ }
+}
+
+/*
+ * Input (output of mxt-changer listall):
+ *
+ * Drive content: D:Drive num:F:Slot loaded:Volume Name
+ * D:0:F:2:vol2 or D:Drive num:E
+ * D:1:F:42:vol42
+ * D:3:E
+ *
+ * Slot content:
+ * S:1:F:vol1 S:Slot num:F:Volume Name
+ * S:2:E or S:Slot num:E
+ * S:3:F:vol4
+ *
+ * Import/Export tray slots:
+ * I:10:F:vol10 I:Slot num:F:Volume Name
+ * I:11:E or I:Slot num:E
+ * I:12:F:vol40
+ *
+ * If a drive is loaded, the slot *should* be empty
+ *
+ * Output:
+ *
+ * Drive list: D|Drive num|Slot loaded|Volume Name
+ * D|0|45|vol45
+ * D|1|42|vol42
+ * D|3||
+ *
+ * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
+ *
+ * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
+ * S|2||||||||
+ * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
+ *
+ * TODO: need to merge with status_slots()
+ */
+void status_content(UAContext *ua, STORE *store)
+{
+ int Slot, Drive;
+ char type;
+ char dev_name[MAX_NAME_LENGTH];
+ char vol_name[MAX_NAME_LENGTH];
+ BSOCK *sd;
+ vol_list_t *vl=NULL, *vol_list = NULL;
+
+ if (!(sd=open_sd_bsock(ua))) {
+ return;
+ }
+
+ if (!open_client_db(ua)) {
+ return;
+ }
+
+ bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
+ bash_spaces(dev_name);
+ /* Ask for autochanger list of volumes */
+ bnet_fsend(sd, NT_("autochanger listall %s \n"), dev_name);
+
+ /* Read and organize list of Drive, Slots and I/O Slots */
+ while (bnet_recv(sd) >= 0) {
+ strip_trailing_junk(sd->msg);
+
+ /* Check for returned SD messages */
+ if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
+ B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
+ sd->msg[4] == ' ') {
+ ua->send_msg("%s\n", sd->msg); /* pass them on to user */
+ continue;
+ }
+
+ Drive = Slot = -1;
+ *vol_name = 0;
+
+ if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
+ ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
+
+ /* we print information on the slot if we have a volume name */
+ if (*vol_name) {
+ /* Add Slot and VolumeName to list */
+ vl = (vol_list_t *)malloc(sizeof(vol_list_t));
+ vl->Slot = Slot;
+ vl->VolName = bstrdup(vol_name);
+ vl->next = vol_list;
+ vol_list = vl;
+ }
+
+ } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
+ ua->send_msg("D|%d||\n", Drive);
+
+ } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
+ content_send_info(ua, type, Slot, vol_name);
+
+ } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
+ /* type can be S (slot) or I (Import/Export slot) */
+ vol_list_t *prev=NULL;
+ for (vl = vol_list; vl; vl = vl->next) {
+ if (vl->Slot == Slot) {
+ bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
+
+ /* remove the node */
+ if (prev) {
+ prev->next = vl->next;
+ } else {
+ vol_list = vl->next;
+ }
+ free(vl->VolName);
+ free(vl);
+ break;
+ }
+ prev = vl;
+ }
+ content_send_info(ua, type, Slot, vol_name);
+
+ } else {
+ Dmsg1(10, "Discarding msg=%s\n", sd->msg);
+ }
+ }
+ close_sd_bsock(ua);
+}
/*
* Print slots from AutoChanger
MEDIA_DBR mr;
char *slot_list;
int max_slots;
- int drive;
int i=1;
- /* output format */
- const char *slot_api_empty_format="%i|||||\n";
- const char *slot_api_full_format="%i|%i|%s|%s|%s|%s|\n";
+ /* Slot | Volume | Status | MediaType | Pool */
const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
+ if (ua->api) {
+ status_content(ua, store_r);
+ return;
+ }
+
if (!open_client_db(ua)) {
return;
}
pm_strcpy(store.store_source, _("command line"));
set_wstorage(ua->jcr, &store);
- drive = get_storage_drive(ua, store.store);
+ get_storage_drive(ua, store.store);
max_slots = get_num_slots_from_SD(ua);
ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
goto bail_out;
}
- if (!ua->api) {
- ua->info_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
- ua->info_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
- }
+ ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
+ ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
/* Walk through the list getting the media records */
for (vl=vol_list; vl; vl=vl->next) {
if (!vl->VolName) {
Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
- if (!ua->api) {
- ua->info_msg(slot_hformat,
- vl->Slot, '*',
- "?", "?", "?", "?");
- } else {
- ua->info_msg(slot_api_empty_format, vl->Slot);
- }
+ ua->send_msg(slot_hformat,
+ vl->Slot, '*',
+ "?", "?", "?", "?");
continue;
}
/* Hope that slots are ordered */
for (; i < vl->Slot; i++) {
if (slot_list[i]) {
- if (!ua->api) {
- ua->info_msg(slot_hformat,
- i, ' ', "", "", "", "");
- } else {
- ua->info_msg(slot_api_empty_format, i);
- }
+ ua->send_msg(slot_hformat,
+ i, ' ', "", "", "", "");
slot_list[i]=0;
}
}
- memset(&mr, 0, sizeof(mr));
bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
db_lock(ua->db);
if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
strcpy(pr.Name, "?");
}
-
- if (!ua->api) {
- /* Print information */
- ua->info_msg(slot_hformat,
- vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
- mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
- } else {
- ua->info_msg(slot_api_full_format,
- vl->Slot, mr.Slot, mr.VolumeName, mr.VolStatus,
- mr.MediaType, pr.Name);
- }
-
db_unlock(ua->db);
+
+ /* Print information */
+ ua->send_msg(slot_hformat,
+ vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
+ mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
continue;
} else { /* TODO: get information from catalog */
- ua->info_msg(slot_hformat,
+ ua->send_msg(slot_hformat,
vl->Slot, '*',
mr.VolumeName, "?", "?", "?");
}
*/
for (; i <= max_slots; i++) {
if (slot_list[i]) {
- if (!ua->api) {
- ua->info_msg(slot_hformat,
- i, ' ', "", "", "", "");
- } else {
- ua->info_msg(slot_api_empty_format, i);
- }
+ ua->send_msg(slot_hformat,
+ i, ' ', "", "", "", "");
slot_list[i]=0;
}
}