/*
Bacula® - The Network Backup Solution
- Copyright (C) 2003-2007 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.
- Bacula® is a registered trademark of John Walker.
+ Bacula® is a registered trademark of Kern Sibbald.
The licensor of Bacula is the Free Software Foundation Europe
(FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
Switzerland, email:ftf@fsfeurope.org.
*
* 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);
bash_spaces(pr->Name);
if (relabel) {
bash_spaces(omr->VolumeName);
- bnet_fsend(sd, "relabel %s OldName=%s NewName=%s PoolName=%s "
+ sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s "
"MediaType=%s Slot=%d drive=%d",
dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
mr->MediaType, mr->Slot, drive);
ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
omr->VolumeName, mr->VolumeName);
} else {
- bnet_fsend(sd, "label %s VolumeName=%s PoolName=%s MediaType=%s "
+ sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s "
"Slot=%d drive=%d",
dev_name, mr->VolumeName, pr->Name, mr->MediaType,
mr->Slot, drive);
dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
}
- while (bnet_recv(sd) >= 0) {
+ while (sd->recv() >= 0) {
int dvd;
ua->send_msg("%s", sd->msg);
if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu DVD=%d ", &VolBytes,
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);
bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
bash_spaces(dev_name);
/* Ask for autochanger list of volumes */
- bnet_fsend(sd, NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
+ sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
Dmsg1(100, "Sent: %s", sd->msg);
/* Get Volume name in this Slot */
- while (bnet_recv(sd) >= 0) {
+ while (sd->recv() >= 0) {
ua->send_msg("%s", sd->msg);
Dmsg1(100, "Got: %s", sd->msg);
if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
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;
}
}
}
bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
bash_spaces(dev_name);
/* Ask for autochanger number of slots */
- bnet_fsend(sd, NT_("autochanger slots %s\n"), dev_name);
+ sd->fsend(NT_("autochanger slots %s\n"), dev_name);
- while (bnet_recv(sd) >= 0) {
+ while (sd->recv() >= 0) {
if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
break;
} else {
bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
bash_spaces(dev_name);
/* Ask for autochanger number of slots */
- bnet_fsend(sd, NT_("autochanger drives %s\n"), dev_name);
+ sd->fsend(NT_("autochanger drives %s\n"), dev_name);
- while (bnet_recv(sd) >= 0) {
+ while (sd->recv() >= 0) {
if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
break;
} else {
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
return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
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
+ */
+void status_slots(UAContext *ua, STORE *store_r)
+{
+ USTORE store;
+ POOL_DBR pr;
+ vol_list_t *vl, *vol_list = NULL;
+ MEDIA_DBR mr;
+ char *slot_list;
+ int max_slots;
+ int i=1;
+ /* 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;
+ }
+ store.store = store_r;
+
+ pm_strcpy(store.store_source, _("command line"));
+ set_wstorage(ua->jcr, &store);
+ get_storage_drive(ua, store.store);
+
+ max_slots = get_num_slots_from_SD(ua);
+
+ if (max_slots <= 0) {
+ ua->warning_msg(_("No slots in changer to scan.\n"));
+ return;
+ }
+ slot_list = (char *)malloc(max_slots+1);
+ if (!get_user_slot_list(ua, slot_list, max_slots)) {
+ free(slot_list);
+ return;
+ }
+
+ vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
+
+ if (!vol_list) {
+ ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
+ goto bail_out;
+ }
+ 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->Slot > max_slots) {
+ ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
+ vl->Slot, max_slots);
+ continue;
+ }
+ /* Check if user wants us to look at this slot */
+ if (!slot_list[vl->Slot]) {
+ Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
+ continue;
+ }
+
+ slot_list[vl->Slot] = 0; /* clear Slot */
+
+ if (!vl->VolName) {
+ Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
+ ua->send_msg(slot_hformat,
+ vl->Slot, '*',
+ "?", "?", "?", "?");
+ continue;
+ }
+
+ /* Hope that slots are ordered */
+ for (; i < vl->Slot; i++) {
+ if (slot_list[i]) {
+ ua->send_msg(slot_hformat,
+ i, ' ', "", "", "", "");
+ slot_list[i]=0;
+ }
+ }
+
+ 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)) {
+ memset(&pr, 0, sizeof(POOL_DBR));
+ pr.PoolId = mr.PoolId;
+ if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
+ strcpy(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->send_msg(slot_hformat,
+ vl->Slot, '*',
+ mr.VolumeName, "?", "?", "?");
+ }
+ db_unlock(ua->db);
+ }
+
+ /* Display the rest of the autochanger
+ */
+ for (; i <= max_slots; i++) {
+ if (slot_list[i]) {
+ ua->send_msg(slot_hformat,
+ i, ' ', "", "", "", "");
+ slot_list[i]=0;
+ }
+ }
+
+bail_out:
+
+ free_vol_list(vol_list);
+ free(slot_list);
+ close_sd_bsock(ua);
+
+ return;
+}