Add two new features to chio(1):

- The ability to specify elements by volume tag instead of their actual
   physical location.  e.g., instead of:
	chio move slot 3 slot 4
   you would now use:
	chio move voltag FOO slot 4

 - The ability to return an element to its previous location, as specified
   by the source element.  e.g., instead of:
	chio move drive 0 slot 4
   you would now use:
	chio return drive 0
   or
	chio return voltag FOO

These features will obviously only work with changers that support volume
tags and/or source element IDs.  chio(1) should fail gracefully if the user
attempts to use these new features and the source element ID or volume tag
are not found.

PR:		bin/21178
Submitted by:	"C. Stephen Gunn" <csg@waterspout.com>
Reviewed by:	ken
This commit is contained in:
ken 2000-09-18 06:09:11 +00:00
parent 3dc41a81fd
commit 91f91457a7
2 changed files with 304 additions and 15 deletions

View File

@ -69,20 +69,27 @@ to the desired changer device.
.Pp
A medium changer apparatus is made up of
.Em elements .
There are four element types:
There are five element types:
.Em picker
(medium transport),
.Em slot
(storage),
.Em portal
(import/export), and
(import/export),
.Em drive
(data transfer). In this command description, the shorthand
(data transfer), and
.Em voltag
(select by volume identifier). The
.Em voltag
pseudo-element type allows the selection of tapes by their volume tag
(typically a barcode on the tape).
.Pp
In this command description, the shorthand
.Em ET
will be used to represent an element type, and
.Em EU
will be used to represent an element unit. For example, to represent
the first robotic arm in the changer, the
will be used to represent an element unit.
For example, to represent the first robotic arm in the changer, the
.Em ET
would be
.Dq picker
@ -137,6 +144,15 @@ Note that not all medium changers support the
.Ic exchange
operation; the changer must have multiple free pickers or emulate
multiple free pickers with transient storage.
.It Xo Nm return
.Ar <from ET> <from EU>
.Xc
Return the media unit to its source element.
This command will query the status of the specified media unit, and
will move it to the element specified in its source attribute.
This is a convenient way to return media from a drive or portal
to its previous element in the changer.
.Pp
.It Xo Nm position
.Ar <to ET> <to EU>
.Op Ar inv
@ -246,6 +262,11 @@ Element supports receiving media (importing) from an outside human operator.
.Bl -tag -width indent
.It Nm chio move slot 3 drive 0
Move the media in slot 3 (fourth slot) to drive 0 (first drive).
.It Nm chio move voltag VOLUME01 drive 0
Move the media with the barcode VOLUME01 to drive 0 (first drive).
.It Nm chio return drive 0
Remove the tape from drive 0 (first drive) and return it to its original
location in the rack.
.It Nm chio setpicker 2
Configure the changer to use picker 2 (third picker) for operations.
.El
@ -265,4 +286,7 @@ program and SCSI changer driver were written by
for And Communications, http://www.and.com/.
.br
Additional work by
.An Hans Huebner Aq hans@artcom.de
.An Hans Huebner
.Aq hans@artcom.de
and Steve Gunn
.Aq csg@waterspout.com

View File

@ -32,6 +32,7 @@
*/
/*
* Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr.
* Addidional Copyright (c) 2000, by C. Stephen Gunn, Waterspout Communications
*/
#ifndef lint
@ -64,6 +65,9 @@ static int parse_special __P((char *));
static int is_special __P((char *));
static const char *bits_to_string __P((int, const char *));
static void find_element __P((char *, u_int16_t *, u_int16_t *));
static struct changer_element_status *get_element_status __P((u_int16_t, u_int16_t));
static int do_move __P((char *, int, char **));
static int do_exchange __P((char *, int, char **));
static int do_position __P((char *, int, char **));
@ -72,14 +76,20 @@ static int do_getpicker __P((char *, int, char **));
static int do_setpicker __P((char *, int, char **));
static int do_status __P((char *, int, char **));
static int do_ielem __P((char *, int, char **));
static int do_return __P((char *, int, char **));
static int do_voltag __P((char *, int, char **));
#ifndef CHET_VT
#define CHET_VT 10 /* Completely Arbitrary */
#endif
/* Valid changer element types. */
const struct element_type elements[] = {
{ "drive", CHET_DT },
{ "picker", CHET_MT },
{ "portal", CHET_IE },
{ "slot", CHET_ST },
{ "voltag", CHET_VT }, /* Select tapes by barcode */
{ NULL, 0 },
};
@ -93,6 +103,7 @@ const struct changer_command commands[] = {
{ "position", do_position },
{ "setpicker", do_setpicker },
{ "status", do_status },
{ "return", do_return },
{ "voltag", do_voltag },
{ NULL, 0 },
};
@ -195,14 +206,24 @@ do_move(cname, argc, argv)
cmd.cm_fromtype = parse_element_type(*argv);
++argv; --argc;
/* <from EU> */
cmd.cm_fromunit = parse_element_unit(*argv);
/* Check for voltag virtual type */
if (CHET_VT == cmd.cm_fromtype) {
find_element(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit);
} else {
/* <from EU> */
cmd.cm_fromunit = parse_element_unit(*argv);
}
++argv; --argc;
/* <to ET> */
cmd.cm_totype = parse_element_type(*argv);
++argv; --argc;
/* Check for voltag virtual type, and report error */
if (CHET_VT == cmd.cm_totype)
errx(1,"%s: voltag only makes sense as an element source",
cname);
/* <to EU> */
cmd.cm_tounit = parse_element_unit(*argv);
++argv; --argc;
@ -266,16 +287,26 @@ do_exchange(cname, argc, argv)
cmd.ce_srctype = parse_element_type(*argv);
++argv; --argc;
/* <src EU> */
cmd.ce_srcunit = parse_element_unit(*argv);
/* Check for voltag virtual type */
if (CHET_VT == cmd.ce_srctype) {
find_element(*argv, &cmd.ce_srctype, &cmd.ce_srcunit);
} else {
/* <from EU> */
cmd.ce_srcunit = parse_element_unit(*argv);
}
++argv; --argc;
/* <dst1 ET> */
cmd.ce_fdsttype = parse_element_type(*argv);
++argv; --argc;
/* <dst1 EU> */
cmd.ce_fdstunit = parse_element_unit(*argv);
/* Check for voltag virtual type */
if (CHET_VT == cmd.ce_fdsttype) {
find_element(*argv, &cmd.ce_fdsttype, &cmd.ce_fdstunit);
} else {
/* <from EU> */
cmd.ce_fdstunit = parse_element_unit(*argv);
}
++argv; --argc;
/*
@ -293,6 +324,10 @@ do_exchange(cname, argc, argv)
cmd.ce_sdsttype = parse_element_type(*argv);
++argv; --argc;
if (CHET_VT == cmd.ce_sdsttype)
errx(1,"%s %s: voltag only makes sense as an element source",
cname, *argv);
/* <dst2 EU> */
cmd.ce_sdstunit = parse_element_unit(*argv);
++argv; --argc;
@ -652,9 +687,9 @@ do_status(cname, argc, argv)
cesr.cesr_element_base = base;
cesr.cesr_element_count = count;
/* Allocate storage for the status structures. */
cesr.cesr_element_status
= (struct changer_element_status *)
malloc(count * sizeof(struct changer_element_status));
cesr.cesr_element_status =
(struct changer_element_status *)
calloc(count, sizeof(struct changer_element_status));
if (!cesr.cesr_element_status)
errx(1, "can't allocate status storage");
@ -934,6 +969,236 @@ bits_to_string(v, cp)
return (buf);
}
/*
* do_return()
*
* Given an element reference, ask the changer/picker to move that
* element back to its source slot.
*/
static int
do_return(cname, argc, argv)
char *cname;
int argc;
char **argv;
{
struct changer_element_status *ces;
struct changer_move cmd;
u_int16_t type, element;
++argv; --argc;
if (argc < 2) {
warnx("%s: too few arguments", cname);
goto usage;
} else if (argc > 3) {
warnx("%s: too many arguments", cname);
goto usage;
}
type = parse_element_type(*argv);
++argv; --argc;
/* Handle voltag virtual Changer Element Type */
if (CHET_VT == type) {
find_element(*argv, &type, &element);
} else {
element = parse_element_unit(*argv);
}
++argv; --argc;
ces = get_element_status(type, element); /* Get the status */
if (NULL == ces)
errx(1, "%s: null element status pointer", cname);
if (!(ces->ces_flags & CES_SOURCE_VALID))
errx(1, "%s: no source information", cname);
(void) memset(&cmd, 0, sizeof(cmd));
cmd.cm_fromtype = type;
cmd.cm_fromunit = element;
cmd.cm_totype = ces->ces_source_type;
cmd.cm_tounit = ces->ces_source_addr;
if (ioctl(changer_fd, CHIOMOVE, &cmd) == -1)
err(1, "%s: CHIOMOVE", changer_name);
free(ces);
return(0);
usage:
(void) fprintf(stderr, "usage: %s %s "
"<from ET> <from EU>\n", __progname, cname);
return(1);
}
/*
* get_element_status()
*
* return a *cesr for the specified changer element. This
* routing will malloc()/calloc() the memory. The caller
* should free() it when done.
*/
static struct changer_element_status *
get_element_status(type, element)
u_int16_t type;
u_int16_t element;
{
struct changer_element_status_request cesr;
struct changer_element_status *ces;
ces = (struct changer_element_status *)
calloc(1, sizeof(struct changer_element_status));
if (NULL == ces)
errx(1, "can't allocate status storage");
(void)memset(&cesr, 0, sizeof(cesr));
cesr.cesr_element_type = type;
cesr.cesr_element_base = element;
cesr.cesr_element_count = 1; /* Only this one element */
cesr.cesr_flags |= CESR_VOLTAGS; /* Grab voltags as well */
cesr.cesr_element_status = ces;
if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
free(ces);
err(1, "%s: CHIOGSTATUS", changer_name);
/* NOTREACHED */
}
return ces;
}
/*
* find_element()
*
* Given a <voltag> find the chager element and unit, or exit
* with an error if it isn't found. We grab the changer status
* and iterate until we find a match, or crap out.
*/
static void
find_element(voltag, et, eu)
char *voltag;
u_int16_t *et;
u_int16_t *eu;
{
struct changer_params cp;
struct changer_element_status_request cesr;
struct changer_element_status *ch_ces, *ces;
int elem, total_elem, found = 0;
/*
* Get the changer parameters, we're interested in the counts
* for all types of elements to perform our search.
*/
if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp))
err(1, "%s: CHIOGPARAMS", changer_name);
/* Allocate some memory for the results */
total_elem = (cp.cp_nslots + cp.cp_ndrives
+ cp.cp_npickers + cp.cp_nportals);
ch_ces = (struct changer_element_status *)
calloc(total_elem, sizeof(struct changer_element_status));
if (NULL == ch_ces)
errx(1, "can't allocate status storage");
ces = ch_ces;
/* Read in the changer slots */
if (cp.cp_nslots > 0) {
cesr.cesr_element_type = CHET_ST;
cesr.cesr_element_base = 0;
cesr.cesr_element_count = cp.cp_nslots;
cesr.cesr_flags |= CESR_VOLTAGS;
cesr.cesr_element_status = ces;
if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
free(ch_ces);
err(1, "%s: CHIOGSTATUS", changer_name);
}
ces += cp.cp_nslots;
}
/* Read in the drive information */
if (cp.cp_ndrives > 0 ) {
(void) memset(&cesr, 0, sizeof(cesr));
cesr.cesr_element_type = CHET_DT;
cesr.cesr_element_base = 0;
cesr.cesr_element_count = cp.cp_ndrives;
cesr.cesr_flags |= CESR_VOLTAGS;
cesr.cesr_element_status = ces;
if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
free(ch_ces);
err(1, "%s: CHIOGSTATUS", changer_name);
}
ces += cp.cp_ndrives;
}
/* Read in the portal information */
if (cp.cp_nportals > 0 ) {
(void) memset(&cesr, 0, sizeof(cesr));
cesr.cesr_element_type = CHET_IE;
cesr.cesr_element_base = 0;
cesr.cesr_element_count = cp.cp_nportals;
cesr.cesr_flags |= CESR_VOLTAGS;
cesr.cesr_element_status = ces;
if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
free(ch_ces);
err(1, "%s: CHIOGSTATUS", changer_name);
}
ces += cp.cp_nportals;
}
/* Read in the picker information */
if (cp.cp_npickers > 0) {
(void) memset(&cesr, 0, sizeof(cesr));
cesr.cesr_element_type = CHET_MT;
cesr.cesr_element_base = 0;
cesr.cesr_element_count = cp.cp_npickers;
cesr.cesr_flags |= CESR_VOLTAGS;
cesr.cesr_element_status = ces;
if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
free(ch_ces);
err(1, "%s: CHIOGSTATUS", changer_name);
}
}
/*
* Now search the list the specified <voltag>
*/
for (elem = 0; elem <= total_elem; ++elem) {
ces = &ch_ces[elem];
/* Make sure we have a tape in this element */
if ((ces->ces_flags & (CES_STATUS_ACCESS|CES_STATUS_FULL))
!= (CES_STATUS_ACCESS|CES_STATUS_FULL))
continue;
/* Check to see if it is our target */
if (strcasecmp(voltag, ces->ces_pvoltag.cv_volid) == 0) {
*et = ces->ces_type;
*eu = ces->ces_addr;
++found;
break;
}
}
if (!found) {
errx(1, "%s: unable to locate voltag: %s", changer_name,
voltag);
}
free(ch_ces);
return;
}
static void
cleanup()