CREATE TABLE Media (
MediaId INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
VolumeName TINYBLOB NOT NULL,
- Slot INTEGER NOT NULL,
+ Slot INTEGER NOT NULL DEFAULT 0,
PoolId INTEGER UNSIGNED NOT NULL REFERENCES Pool,
MediaType TINYBLOB NOT NULL,
FirstWritten DATETIME NOT NULL,
db_lock(mdb);
Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,\
-VolBytes,VolMounts,VolErrors,VolWrites,VolMaxBytes,VolCapacityBytes \
+VolBytes,VolMounts,VolErrors,VolWrites,VolMaxBytes,VolCapacityBytes,Slot \
FROM Media WHERE PoolId=%d AND MediaType=\"%s\" AND VolStatus=\"%s\" \
ORDER BY MediaId", mr->PoolId, mr->MediaType, mr->VolStatus);
mr->VolWrites = atoi(row[8]);
mr->VolMaxBytes = (uint64_t)strtod(row[9], NULL);
mr->VolCapacityBytes = (uint64_t)strtod(row[10], NULL);
+ mr->Slot = atoi(row[11]);
sql_free_result(mdb);
if (mr->MediaId != 0) { /* find by id */
Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,\
VolBytes,VolMounts,VolErrors,VolWrites,VolMaxBytes,VolCapacityBytes,\
-MediaType,VolStatus,PoolId,VolRetention,Recycle \
+MediaType,VolStatus,PoolId,VolRetention,Recycle,Slot \
FROM Media WHERE MediaId=%d", mr->MediaId);
} else { /* find by name */
Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,\
VolBytes,VolMounts,VolErrors,VolWrites,VolMaxBytes,VolCapacityBytes,\
-MediaType,VolStatus,PoolId,VolRetention,Recycle \
+MediaType,VolStatus,PoolId,VolRetention,Recycle,Slot \
FROM Media WHERE VolumeName=\"%s\"", mr->VolumeName);
}
mr->PoolId = atoi(row[13]);
mr->VolRetention = (btime_t)strtod(row[14], NULL);
mr->Recycle = atoi(row[15]);
+ mr->Slot = atoi(row[16]);
stat = mr->MediaId;
}
} else {
bnet_fsend(bs, OK_media, mr.VolumeName, mr.VolJobs,
mr.VolFiles, mr.VolBlocks, mr.VolBytes, mr.VolMounts, mr.VolErrors,
mr.VolWrites, mr.VolMaxBytes, mr.VolCapacityBytes,
- mr.VolStatus);
+ mr.VolStatus, mr.Slot);
} else {
Dmsg4(100, "get_media_record PoolId=%d wanted %d, Status=%s, \
MediaType=%s\n", mr.PoolId, jcr->PoolId, mr.VolStatus, mr.MediaType);
}
strcpy(mr.VolumeName, ua->cmd);
}
- mr.MediaId = 0;
- if (!db_get_media_record(ua->db, &mr)) {
- bsendmsg(ua, _("Volume record for %s not found.\n"), mr.VolumeName);
- return 0;
- }
for (int done=0; !done; ) {
+ mr.MediaId = 0;
+ if (!db_get_media_record(ua->db, &mr)) {
+ bsendmsg(ua, _("Volume record for %s not found.\n"), mr.VolumeName);
+ return 0;
+ }
start_prompt(ua, _("Parameters to modify:\n"));
add_prompt(ua, _("Volume Status"));
add_prompt(ua, _("Volume Retention Period"));
add_prompt(ua, _("Recycle Flag"));
+ add_prompt(ua, _("Slot"));
add_prompt(ua, _("Done"));
switch (do_prompt(ua, _("Select paramter to modify"), NULL)) {
case 0: /* Volume Status */
}
free_pool_memory(query);
break;
+
+ case 3: /* Slot */
+ int slot;
+ bsendmsg(ua, _("Current value is: %d\n"), mr.Slot);
+ if (!get_cmd(ua, _("Enter new Slot: "))) {
+ return 0;
+ }
+ slot = atoi(ua->cmd);
+ if (slot < 0) {
+ bsendmsg(ua, _("Invalid slot, it must be 0 or greater\n"));
+ break;
+ } else if (pr.MaxVols > 0 && slot >(int)pr.MaxVols) {
+ bsendmsg(ua, _("Invalid slot, it must be between 0 and %d\n"),
+ pr.MaxVols);
+ break;
+ }
+ query = get_pool_memory(PM_MESSAGE);
+ Mmsg(&query, "UPDATE Media SET Slot=%d WHERE MediaId=%d",
+ slot, mr.MediaId);
+ if (!db_sql_query(ua->db, query, NULL, NULL)) {
+ bsendmsg(ua, "%s", db_strerror(ua->db));
+ } else {
+ bsendmsg(ua, "New value is: %d\n", slot);
+ }
+ free_pool_memory(query);
+ break;
+
default: /* Done or error */
- return 0;
+ bsendmsg(ua, "Selection done.\n");
+ return 1;
}
}
return 1;
char *edit_btime (btime_t val, char *buf);
void jobstatus_to_ascii (int JobStatus, char *msg, int maxlen);
void add_str_to_pool_mem (POOLMEM **base, char **msg, char *str);
+int run_program (char *prog, int wait, POOLMEM *results);
/*
#endif
}
+
+#define MAX_ARGV 100
+static char *bargv[MAX_ARGV];
+static int bargc;
+static void build_argc_argv(char *cmd);
+
+int run_program(char *prog, int wait, POOLMEM *results)
+{
+ int stat = ETIME;
+ int chldstatus = 0;
+ pid_t pid1, pid2;
+ int pfd[2];
+ int i;
+
+
+ build_argc_argv(prog);
+#ifdef lots_of_debug
+ printf("argc=%d\n", bargc);
+ for (i=0; i<bargc; i++) {
+ printf("argc=%d argv=%s\n", i, bargv[i]);
+ }
+#endif
+
+ if (results && pipe(pfd) == -1) {
+ return errno;
+ }
+ /* Start worker process */
+ switch (pid1 = fork()) {
+ case -1:
+ break;
+
+ case 0: /* child */
+// printf("execl of %s\n", prog);
+ if (results) {
+ close(1); dup(pfd[1]); /* attach pipes to stdin and stdout */
+ close(2); dup(pfd[1]);
+ }
+ execvp(bargv[0], bargv);
+ exit(errno); /* shouldn't get here */
+
+ default: /* parent */
+ /* start timer process */
+ switch (pid2=fork()) {
+ case -1:
+ break;
+ case 0: /* child 2 */
+ /* Time the worker process */
+ sleep(wait);
+ if (kill(pid1, SIGTERM) == 0) { /* time expired kill it */
+ exit(0);
+ }
+ sleep(3);
+ kill(pid1, SIGKILL);
+ exit(0);
+ default: /* parent */
+ int i;
+ if (results) {
+ i = read(pfd[0], results, sizeof_pool_memory(results) - 1);
+ if (--i < 0) {
+ i = 0;
+ }
+ results[i] = 0; /* set end of string */
+ }
+ /* wait for worker child to exit */
+ for ( ;; ) {
+ pid_t wpid;
+ wpid = waitpid(pid1, &chldstatus, 0);
+ if (wpid == pid1 || (errno != EINTR)) {
+ break;
+ }
+ }
+ if (WIFEXITED(chldstatus))
+ stat = WEXITSTATUS(chldstatus);
+
+ kill(pid2, SIGKILL); /* kill off timer process */
+ waitpid(pid2, &chldstatus, 0); /* reap timer process */
+ if (results) {
+ close(pfd[0]); /* close pipe */
+ close(pfd[1]);
+ }
+ break;
+ }
+ break;
+ }
+ return stat;
+}
+
+/*
+ * Build argc and argv from a string
+ */
+static void build_argc_argv(char *cmd)
+{
+ int i, quote;
+ char *p, *q;
+
+ bargc = 0;
+ for (i=0; i<MAX_ARGV; i++)
+ bargv[i] = NULL;
+
+ p = cmd;
+ quote = 0;
+ while (*p && (*p == ' ' || *p == '\t'))
+ p++;
+ if (*p == '\"') {
+ quote = 1;
+ p++;
+ }
+ if (*p) {
+ while (*p && bargc < MAX_ARGV) {
+ q = p;
+ if (quote) {
+ while (*q && *q != '\"')
+ q++;
+ quote = 0;
+ } else {
+ while (*q && *q != ' ')
+ q++;
+ }
+ if (*q)
+ *(q++) = '\0';
+ bargv[bargc++] = p;
+ p = q;
+ while (*p && (*p == ' ' || *p == '\t'))
+ p++;
+ if (*p == '\"') {
+ quote = 1;
+ p++;
+ }
+ }
+ }
+}
unbash_spaces(vol->VolCatName);
strcpy(jcr->VolumeName, vol->VolCatName); /* set desired VolumeName */
- Dmsg1(200, "Got Volume=%s\n", vol->VolCatName);
+ Dmsg2(030, "do_reqest_vol_info got slot=%d Volume=%s\n",
+ vol->Slot, vol->VolCatName);
return 1;
}
dev->dev_name, strerror(dev->dev_errno));
return 0;
}
- Dmsg1(000, "Offlined device %s\n", dev->dev_name);
+ Dmsg1(100, "Offlined device %s\n", dev->dev_name);
return 1;
}
/* Forward referenced functions */
static int mount_next_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *label_blk, int release);
+static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd);
extern char my_name[];
extern int debug_level;
if (!dev_is_tape(dev)) {
close_dev(dev);
}
- /******FIXME**** send read volume info to director */
+ /******FIXME**** send read volume usage statistics to director */
} else if (dev->num_writers > 0) {
dev->num_writers--;
*/
static int mount_next_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release)
{
- int recycle, ask;
+ int recycle, ask, retry = 0;
Dmsg0(90, "Enter mount_next_volume()\n");
mount_next_vol:
+ if (retry++ > 5) {
+ Mmsg0(&dev->errmsg, _("Too many retries.\n"));
+ return 0;
+ }
if (job_cancelled(jcr)) {
Mmsg0(&dev->errmsg, _("Job cancelled.\n"));
return 0;
jcr->VolFirstFile = 0; /* first update of Vol FileIndex */
for ( ;; ) {
+ int slot = jcr->VolCatInfo.Slot;
+
+ /*
+ * Handle autoloaders here. If we cannot autoload it, we
+ * will fall through to ask the sysop.
+ */
+ if (dev->capabilities && CAP_AUTOCHANGER && slot <= 0) {
+ if (dir_find_next_appendable_volume(jcr)) {
+ slot = jcr->VolCatInfo.Slot;
+ }
+ }
+ Dmsg1(100, "Want changer slot=%d\n", slot);
+
+ if (slot > 0 && jcr->device->changer_name && jcr->device->changer_command) {
+ uint32_t timeout = jcr->device->changer_timeout;
+ POOLMEM *changer, *results;
+ int status, loaded;
+
+ results = get_pool_memory(PM_MESSAGE);
+ changer = get_pool_memory(PM_FNAME);
+ /* Find out what is loaded */
+ changer = edit_device_codes(jcr, changer, jcr->device->changer_command,
+ "loaded");
+ status = run_program(changer, timeout, results);
+ if (status == 0) {
+ loaded = atoi(results);
+ } else {
+ loaded = -1; /* force unload */
+ }
+ Dmsg1(100, "loaded=%s\n", results);
- Dmsg1(000, "Have changer. Dev=%s\n", NPRT(jcr->device->changer_name));
- if (ask) {
- if (dev->capabilities && CAP_AUTOCHANGER) {
- Dmsg1(000, "Have changer. Dev=%s\n", NPRT(jcr->device->changer_name));
- /*** ****FIXME**** add changer code here */
+ /* If bad status or tape we want is not loaded, load it. */
+ if (status != 0 || loaded != slot) {
+ if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
+ offline_dev(dev);
+ }
+ /* We are going to load a new tape, so close the device */
+ force_close_dev(dev);
+ if (loaded != 0) { /* must unload drive */
+ Dmsg0(100, "Doing changer unload.\n");
+ changer = edit_device_codes(jcr, changer,
+ jcr->device->changer_command, "unload");
+ status = run_program(changer, timeout, NULL);
+ Dmsg1(100, "unload status=%d\n", status);
+ }
+ /*
+ * Load the desired cassette
+ */
+ Dmsg1(100, "Doing changer load slot %d\n", slot);
+ changer = edit_device_codes(jcr, changer,
+ jcr->device->changer_command, "load");
+ status = run_program(changer, timeout, NULL);
+ Dmsg2(100, "load slot %d status=%d\n", slot, status);
}
- if (!dir_ask_sysop_to_mount_next_volume(jcr, dev)) {
- return 0; /* error return */
+ free_pool_memory(changer);
+ free_pool_memory(results);
+ Dmsg1(100, "After changer, status=%d\n", status);
+ if (status == 0) { /* did we succeed? */
+ ask = 0; /* yes, got vol, no need to ask sysop */
}
}
+
+
+ if (ask && !dir_ask_sysop_to_mount_next_volume(jcr, dev)) {
+ return 0; /* error return */
+ }
Dmsg1(200, "want vol=%s\n", jcr->VolumeName);
/* Open device */
/*
- * Edit codes into ChangerDevice
+ * Edit codes into ChangerCommand
* %% = %
* %a = archive device name
* %c = changer device name
* cmd = command string (load, unload, ...)
*
*/
-char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd)
+static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd)
{
- char *p, *o, *str;
+ char *p, *o;
+ const char *str;
char add[20];
Dmsg1(200, "edit_job_codes: %s\n", imsg);
str = jcr->device->dev->dev_name;
break;
case 'c':
- str = jcr->device->changer_name;
+ str = NPRT(jcr->device->changer_name);
break;
case 'o':
- str = cmd;
+ str = NPRT(cmd);
break;
case 's':
- sprintf(add, "%d", jcr->device->dev->VolCatInfo.Slot - 1);
+ sprintf(add, "%d", jcr->VolCatInfo.Slot - 1);
str = add;
break;
case 'S':
- sprintf(add, "%d", jcr->device->dev->VolCatInfo.Slot);
+ sprintf(add, "%d", jcr->VolCatInfo.Slot);
str = add;
break;
case 'j': /* Job name */
str = add;
}
Dmsg1(200, "add_str %s\n", str);
- add_str_to_pool_mem(&omsg, &o, str);
+ add_str_to_pool_mem(&omsg, &o, (char *)str);
*o = 0;
Dmsg1(200, "omsg=%s\n", omsg);
}
{"alwaysopen", store_yesno, ITEM(res_dev.cap_bits), CAP_ALWAYSOPEN, ITEM_DEFAULT, 1},
{"autochanger", store_yesno, ITEM(res_dev.cap_bits), CAP_AUTOCHANGER, 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},
+ {"changertimeout", store_pint, ITEM(res_dev.changer_timeout), 0, ITEM_DEFAULT, 60},
{"offlineonunmount", store_yesno, ITEM(res_dev.cap_bits), CAP_OFFLINEUNMOUNT, ITEM_DEFAULT, 1},
{"maximumrewindwait", store_pint, ITEM(res_dev.max_rewind_wait), 0, ITEM_DEFAULT, 5 * 60},
{"minimumblocksize", store_pint, ITEM(res_dev.min_block_size), 0, 0, 0},
free(res->res_dev.device_name);
if (res->res_dev.changer_name)
free(res->res_dev.changer_name);
+ if (res->res_dev.changer_command)
+ free(res->res_dev.changer_command);
break;
case R_MSGS:
if (res->res_msgs.mail_cmd)
char *media_type; /* User assigned media type */
char *device_name; /* Archive device name */
char *changer_name; /* Changer device name */
+ char *changer_command; /* Changer command -- external program */
int cap_bits; /* Capabilities of this device */
+ uint32_t changer_timeout; /* Changer timeout */
uint32_t max_rewind_wait; /* maximum secs to wait for rewind */
uint32_t min_block_size; /* min block size */
uint32_t max_block_size; /* max block size */
/* */
#define VERSION "1.23"
#define VSTRING "1"
-#define DATE "15 July 2002"
-#define LSMDATE "15Jul02"
+#define DATE "16 July 2002"
+#define LSMDATE "16Jul02"
/* Debug flags */
#define DEBUG 1