mirror of
https://github.com/iustin/mt-st.git
synced 2025-12-23 05:45:13 +00:00
Short of moving to autotools, this is the best that can be done: - move the version from hardcoded in the .c files, to a dynamically-built `version.h` file so that we only declare the version in one place - build a better dist file (.tar.gz) by explicitly selecting which files to copy, instead of unbounded recursion from the source directory - ensure that the files being copied to the archive have a sane user/group and mode - add a distcheck target that simply reuses the archive to build and run the programs, and then regenerate the archive from itself autotools would solve all this by default, but still feels too heavyweight for just two .c files. Additionall, drop the .lsm file. It seems mostly useless these days; I'll be happy to reinstate it however if anyone cares.
967 lines
24 KiB
C
967 lines
24 KiB
C
/* This program initializes Linux SCSI tape drives using the
|
|
inquiry data from the devices and a text database.
|
|
|
|
Maintained by Iustin Pop (iustin@k1024.org).
|
|
Copyright 1996-2008 by Kai Mäkisara (Kai.Makisara@kolumbus.fi).
|
|
Distribution of this program is allowed according to the
|
|
GNU Public Licence.
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <linux/major.h>
|
|
#include <scsi/sg.h>
|
|
|
|
#include "mtio.h"
|
|
#include "version.h"
|
|
|
|
#ifndef FALSE
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#endif
|
|
#define SKIP_WHITE(p) for ( ; *p == ' ' || *p == '\t'; p++)
|
|
|
|
typedef struct _modepar_tr {
|
|
int defined;
|
|
int blocksize;
|
|
int density;
|
|
int buffer_writes;
|
|
int async_writes;
|
|
int read_ahead;
|
|
int two_fm;
|
|
int compression;
|
|
int auto_lock;
|
|
int fast_eod;
|
|
int can_bsr;
|
|
int no_blklimits;
|
|
int can_partitions;
|
|
int scsi2logical;
|
|
int sysv;
|
|
int defs_for_writes;
|
|
} modepar_tr;
|
|
|
|
typedef struct _devdef_tr {
|
|
int do_rewind;
|
|
int drive_buffering;
|
|
int timeout;
|
|
int long_timeout;
|
|
int cleaning;
|
|
int nowait;
|
|
int sili;
|
|
modepar_tr modedefs[4];
|
|
} devdef_tr;
|
|
|
|
|
|
#define DEFMAX 2048
|
|
#define LINEMAX 256
|
|
|
|
#define MAX_TAPES 32
|
|
#define NBR_MODES 4
|
|
|
|
|
|
static int verbose = 0;
|
|
|
|
/* The device directories being searched */
|
|
typedef struct
|
|
{
|
|
char dir[PATH_MAX];
|
|
int selective_scan;
|
|
} devdir;
|
|
static devdir devdirs[] = { {"/dev/scsi", 0}, {"/dev", 1}, {"", 0}};
|
|
|
|
#define DEVFS_PATH "/dev/tapes"
|
|
#define DEVFS_TAPEFMT DEVFS_PATH "/tape%d"
|
|
|
|
/* The partial names of the tape devices being included in the
|
|
search in selective scan */
|
|
static char *tape_name_bases[] = {
|
|
"st", "nst", "rmt", "nrmt", "tape", NULL};
|
|
|
|
/* The list of standard definition files being searched */
|
|
static char *std_databases[] = {
|
|
"/etc/stinit.def",
|
|
NULL};
|
|
|
|
static FILE *
|
|
open_database(char *base)
|
|
{
|
|
int i;
|
|
FILE *f;
|
|
|
|
if (base != NULL) {
|
|
if ((f = fopen(base, "r")) == NULL)
|
|
fprintf(stderr, "stinit: Can't find SCSI tape database '%s'.\n",
|
|
base);
|
|
return f;
|
|
}
|
|
|
|
for (i=0; std_databases[i] != NULL; i++) {
|
|
if (verbose > 1)
|
|
fprintf(stderr, "Trying to open database '%s'.\n", std_databases[i]);
|
|
if ((f = fopen(std_databases[i], "r")) != NULL) {
|
|
if (verbose > 1)
|
|
fprintf(stderr, "Open succeeded.\n");
|
|
return f;
|
|
}
|
|
}
|
|
fprintf(stderr, "Can't find the tape characteristics database.\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static char *
|
|
find_string(char *s, char *target, char *buf, int buflen)
|
|
{
|
|
int have_arg;
|
|
char *cp, *cp2, c, *argp;
|
|
|
|
if (buf != NULL && buflen > 0)
|
|
*buf = '\0';
|
|
|
|
for ( ; *s != '\0'; ) {
|
|
SKIP_WHITE(s);
|
|
if (isalpha(*s)) {
|
|
for (cp=s ; isalnum(*cp) || *cp == '-'; cp++)
|
|
;
|
|
cp2 = cp;
|
|
SKIP_WHITE(cp);
|
|
if (*cp == '=') {
|
|
cp++;
|
|
SKIP_WHITE(cp);
|
|
if (*cp == '"') {
|
|
cp++;
|
|
for (cp2=cp; *cp2 != '"' && *cp2 != '\0'; cp2++)
|
|
;
|
|
}
|
|
else
|
|
for (cp2=cp+1; isalnum(*cp2) || *cp2 == '-'; cp2++)
|
|
;
|
|
if (*cp2 == '\0')
|
|
return NULL;
|
|
have_arg = TRUE;
|
|
argp = cp;
|
|
}
|
|
else {
|
|
have_arg = FALSE;
|
|
argp = "1";
|
|
}
|
|
|
|
if (!strncmp(target, s, strlen(target))) {
|
|
c = *cp2;
|
|
*cp2 = '\0';
|
|
if (buf == NULL)
|
|
buf = strdup(argp);
|
|
else {
|
|
if (strlen(argp) < (unsigned int)buflen)
|
|
strcpy(buf, argp);
|
|
else {
|
|
strncpy(buf, argp, buflen);
|
|
buf[buflen - 1] = '\0';
|
|
}
|
|
}
|
|
if (have_arg && c == '"')
|
|
cp2++;
|
|
else
|
|
*cp2 = c;
|
|
if (*cp2 != '\0')
|
|
memmove(s, cp2, strlen(cp2) + 1);
|
|
else
|
|
*s = '\0';
|
|
return buf;
|
|
}
|
|
s = cp2;
|
|
}
|
|
else
|
|
for ( ; *s != '\0' && *s != ' ' && *s != '\t'; s++)
|
|
;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int
|
|
num_arg(char *t)
|
|
{
|
|
int nbr;
|
|
char *tt;
|
|
|
|
nbr = strtol(t, &tt, 0);
|
|
if (t != tt) {
|
|
if (*tt == 'k')
|
|
nbr *= 1024;
|
|
else if (*tt == 'M')
|
|
nbr *= 1024 * 1024;
|
|
}
|
|
return nbr;
|
|
}
|
|
|
|
|
|
static int
|
|
next_block(FILE *dbf, char *buf, int buflen, int limiter)
|
|
{
|
|
int len;
|
|
char *cp, *bp;
|
|
static char lbuf[LINEMAX];
|
|
|
|
if (limiter == 0) { /* Restart */
|
|
rewind(dbf);
|
|
lbuf[0] = '\0';
|
|
return TRUE;
|
|
}
|
|
|
|
for (len = 0 ; ; ) {
|
|
bp = buf + len;
|
|
if ((cp = strchr(lbuf, limiter)) != NULL) {
|
|
*cp = '\0';
|
|
strcpy(bp, lbuf);
|
|
cp++;
|
|
SKIP_WHITE(cp);
|
|
memmove(lbuf, cp, strlen(cp) + 1);
|
|
return TRUE;
|
|
}
|
|
if (len + strlen(lbuf) >= DEFMAX) {
|
|
fprintf(stderr, "Too long definition: '%s'\n", buf);
|
|
return FALSE;
|
|
}
|
|
cp = lbuf;
|
|
SKIP_WHITE(cp);
|
|
strcpy(bp, cp);
|
|
strcat(bp, " ");
|
|
len += strlen(cp) + 1;
|
|
if (fgets(lbuf, LINEMAX, dbf) == NULL)
|
|
return FALSE;
|
|
if ((cp = strchr(lbuf, '#')) != NULL)
|
|
*cp = '\0';
|
|
else
|
|
lbuf[strlen(lbuf) - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
find_pars(FILE *dbf, char *company, char *product, char *rev, devdef_tr *defs,
|
|
int parse_only)
|
|
{
|
|
int i, mode, modes_defined, errors;
|
|
char line[LINEMAX], defstr[DEFMAX], comdef[DEFMAX];
|
|
char tmpcomp[LINEMAX], tmpprod[LINEMAX], tmprev[LINEMAX], *cp, c, *t;
|
|
char *nextdef, *curdef, *comptr;
|
|
static int call_nbr = 0;
|
|
|
|
call_nbr++;
|
|
defs->drive_buffering = (-1);
|
|
defs->timeout = (-1);
|
|
defs->long_timeout = (-1);
|
|
defs->cleaning = (-1);
|
|
defs->nowait = (-1);
|
|
defs->sili = (-1);
|
|
for (i=0; i < NBR_MODES; i++) {
|
|
defs->modedefs[i].defined = FALSE;
|
|
defs->modedefs[i].blocksize = (-1);
|
|
defs->modedefs[i].density = (-1);
|
|
defs->modedefs[i].buffer_writes = (-1);
|
|
defs->modedefs[i].async_writes = (-1);
|
|
defs->modedefs[i].read_ahead = (-1);
|
|
defs->modedefs[i].two_fm = (-1);
|
|
defs->modedefs[i].compression = (-1);
|
|
defs->modedefs[i].auto_lock = (-1);
|
|
defs->modedefs[i].fast_eod = (-1);
|
|
defs->modedefs[i].can_bsr = (-1);
|
|
defs->modedefs[i].no_blklimits = (-1);
|
|
defs->modedefs[i].can_partitions = (-1);
|
|
defs->modedefs[i].scsi2logical = (-1);
|
|
defs->modedefs[i].sysv = (-1);
|
|
defs->modedefs[i].defs_for_writes = (-1);
|
|
}
|
|
next_block(dbf, NULL, 0, 0);
|
|
|
|
/* Find matching inquiry block */
|
|
for (errors=0 ; ; ) {
|
|
|
|
if (!next_block(dbf, defstr, DEFMAX, '{'))
|
|
break;
|
|
|
|
find_string(defstr, "manuf", tmpcomp, LINEMAX);
|
|
find_string(defstr, "model", tmpprod, LINEMAX);
|
|
find_string(defstr, "rev", tmprev, LINEMAX);
|
|
|
|
if (!next_block(dbf, defstr, DEFMAX, '}')) {
|
|
fprintf(stderr,
|
|
"End of definition block not found for ('%s', '%s', '%s').\n",
|
|
tmpcomp, tmpprod, tmprev);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!parse_only) {
|
|
if (tmpcomp[0] != '\0' &&
|
|
strncmp(company, tmpcomp, strlen(tmpcomp)))
|
|
continue;
|
|
if (tmpprod[0] != '\0' &&
|
|
strncmp(product, tmpprod, strlen(tmpprod)))
|
|
continue;
|
|
if (tmprev[0] != '\0' &&
|
|
strncmp(rev, tmprev, strlen(tmprev)))
|
|
continue;
|
|
}
|
|
else if (verbose > 0)
|
|
printf("\nParsing modes for ('%s', '%s', '%s').\n",
|
|
tmpcomp, tmpprod, tmprev);
|
|
|
|
/* Block found, get the characteristics */
|
|
for (nextdef=defstr; *nextdef != '\0' &&
|
|
(*nextdef != 'm' || strncmp(nextdef, "mode", 2)); nextdef++)
|
|
;
|
|
c = *nextdef;
|
|
*nextdef = '\0';
|
|
strcpy(comdef, defstr);
|
|
*nextdef = c;
|
|
comptr = comdef;
|
|
SKIP_WHITE(comptr);
|
|
|
|
for ( ; *nextdef != '\0'; ) {
|
|
curdef = nextdef;
|
|
SKIP_WHITE(curdef);
|
|
for (nextdef++ ; *nextdef != '\0' &&
|
|
(*nextdef != 'm' || strncmp(nextdef, "mode", 2)); nextdef++)
|
|
;
|
|
c = *nextdef;
|
|
*nextdef = '\0';
|
|
|
|
mode = strtol(curdef + 4, &cp, 0) - 1;
|
|
if (mode < 0 || mode >= NBR_MODES) {
|
|
fprintf(stderr,
|
|
"Illegal mode for ('%s', '%s', '%s'):\n'%s'\n",
|
|
tmpcomp, tmpprod, tmprev, curdef);
|
|
*nextdef = c;
|
|
errors++;
|
|
continue;
|
|
}
|
|
|
|
strcpy(defstr, comptr);
|
|
strcat(defstr, cp);
|
|
*nextdef = c;
|
|
|
|
if (verbose > 1)
|
|
fprintf(stderr, "Mode %d definition: %s\n", mode + 1, defstr);
|
|
|
|
if ((t = find_string(defstr, "disab", line, LINEMAX)) != NULL &&
|
|
strtol(t, NULL, 0) != 0) {
|
|
defs->modedefs[mode].defined = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if ((t = find_string(defstr, "drive-", line, LINEMAX)) != NULL)
|
|
defs->drive_buffering = num_arg(t);
|
|
if ((t = find_string(defstr, "timeout", line, LINEMAX)) != NULL)
|
|
defs->timeout = num_arg(t);
|
|
if ((t = find_string(defstr, "long-time", line, LINEMAX)) != NULL)
|
|
defs->long_timeout = num_arg(t);
|
|
if ((t = find_string(defstr, "clean", line, LINEMAX)) != NULL)
|
|
defs->cleaning = num_arg(t);
|
|
if ((t = find_string(defstr, "no-w", line, LINEMAX)) != NULL)
|
|
defs->nowait = num_arg(t);
|
|
if ((t = find_string(defstr, "sili", line, LINEMAX)) != NULL)
|
|
defs->sili = num_arg(t);
|
|
|
|
defs->modedefs[mode].defined = TRUE;
|
|
if ((t = find_string(defstr, "block", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].blocksize = num_arg(t);
|
|
if ((t = find_string(defstr, "dens", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].density = num_arg(t);
|
|
if ((t = find_string(defstr, "buff", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].buffer_writes = num_arg(t);
|
|
if ((t = find_string(defstr, "async", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].async_writes = num_arg(t);
|
|
if ((t = find_string(defstr, "read", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].read_ahead = num_arg(t);
|
|
if ((t = find_string(defstr, "two", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].two_fm = num_arg(t);
|
|
if ((t = find_string(defstr, "comp", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].compression = num_arg(t);
|
|
if ((t = find_string(defstr, "auto", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].auto_lock = num_arg(t);
|
|
if ((t = find_string(defstr, "fast", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].fast_eod = num_arg(t);
|
|
if ((t = find_string(defstr, "can-b", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].can_bsr = num_arg(t);
|
|
if ((t = find_string(defstr, "noblk", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].no_blklimits = num_arg(t);
|
|
if ((t = find_string(defstr, "can-p", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].can_partitions = num_arg(t);
|
|
if ((t = find_string(defstr, "scsi2", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].scsi2logical = num_arg(t);
|
|
if ((t = find_string(defstr, "sysv", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].sysv = num_arg(t);
|
|
if ((t = find_string(defstr, "defs-for-w", line, LINEMAX)) != NULL)
|
|
defs->modedefs[mode].defs_for_writes = num_arg(t);
|
|
|
|
for (t=defstr; *t == ' ' || *t == '\t'; t++)
|
|
;
|
|
if (*t != '\0' && call_nbr <= 1) {
|
|
fprintf(stderr,
|
|
"Warning: errors in definition for ('%s', '%s', '%s'):\n%s\n",
|
|
tmpcomp, tmpprod, tmprev, defstr);
|
|
errors++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (parse_only) {
|
|
if (verbose > 0)
|
|
printf("\n");
|
|
printf("Definition parse completed. ");
|
|
if (errors) {
|
|
printf("Errors found!\n");
|
|
return FALSE;
|
|
}
|
|
else {
|
|
printf("No errors found.\n");
|
|
return TRUE;
|
|
}
|
|
}
|
|
else {
|
|
for (i=modes_defined=0; i < NBR_MODES; i++)
|
|
if (defs->modedefs[i].defined)
|
|
modes_defined++;
|
|
if (modes_defined == 0) {
|
|
fprintf(stderr,
|
|
"Warning: No modes in definition for ('%s', '%s', '%s').\n",
|
|
tmpcomp, tmpprod, tmprev);
|
|
errors++;
|
|
}
|
|
}
|
|
|
|
if (modes_defined)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static int sg_io_errcheck(struct sg_io_hdr *hdp)
|
|
{
|
|
if ((hdp->status & 0x7e) == 0 || hdp->host_status == 0 ||
|
|
hdp->driver_status == 0)
|
|
return 0;
|
|
return EIO;
|
|
}
|
|
|
|
|
|
#define INQUIRY 0x12
|
|
#define INQUIRY_CMDLEN 6
|
|
#define SENSE_BUFF_LEN 32
|
|
#define DEF_TIMEOUT 60000
|
|
|
|
#ifndef SCSI_IOCTL_SEND_COMMAND
|
|
#define SCSI_IOCTL_SEND_COMMAND 1
|
|
#endif
|
|
#define IOCTL_HEADER_LENGTH 8
|
|
|
|
static int
|
|
do_inquiry(char *tname, char *company, char *product, char *rev, int print_non_found)
|
|
{
|
|
int fn;
|
|
int result, *ip, i;
|
|
#define BUFLEN 256
|
|
unsigned char buffer[BUFLEN], *cmd, *inqptr;
|
|
struct sg_io_hdr io_hdr;
|
|
unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY, 0, 0, 0, 200, 0};
|
|
unsigned char sense_b[SENSE_BUFF_LEN];
|
|
|
|
if ((fn = open(tname, O_RDONLY | O_NONBLOCK)) < 0) {
|
|
if (print_non_found || verbose > 0) {
|
|
if (errno == ENXIO)
|
|
fprintf(stderr, "Device '%s' not found by kernel.\n", tname);
|
|
else
|
|
fprintf(stderr, "Can't open tape device '%s' (errno %d).\n",
|
|
tname, errno);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Try SG_IO first, if it is not supported, use SCSI_IOCTL_SEND_COMMAND */
|
|
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
|
|
io_hdr.interface_id = 'S';
|
|
io_hdr.cmd_len = sizeof(inqCmdBlk);
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
|
io_hdr.dxfer_len = 200;
|
|
io_hdr.dxferp = buffer;
|
|
io_hdr.cmdp = inqCmdBlk;
|
|
io_hdr.sbp = sense_b;
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
|
inqptr = buffer;
|
|
|
|
result = ioctl(fn, SG_IO, &io_hdr);
|
|
if (!result)
|
|
result = sg_io_errcheck(&io_hdr);
|
|
if (result) {
|
|
if (errno == ENOTTY || errno == EINVAL) {
|
|
memset(buffer, 0, BUFLEN);
|
|
ip = (int *)&(buffer[0]);
|
|
*ip = 0;
|
|
*(ip+1) = BUFLEN - 13;
|
|
|
|
cmd = &(buffer[8]);
|
|
cmd[0] = INQUIRY;
|
|
cmd[4] = 200;
|
|
|
|
result = ioctl(fn, SCSI_IOCTL_SEND_COMMAND, buffer);
|
|
inqptr = buffer + IOCTL_HEADER_LENGTH;
|
|
}
|
|
if (result) {
|
|
close(fn);
|
|
sprintf((char *)buffer,
|
|
"The SCSI INQUIRY for device '%s' failed (power off?)",
|
|
tname);
|
|
perror((char *)buffer);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
memcpy(company, inqptr + 8, 8);
|
|
for (i=8; i > 0 && company[i-1] == ' '; i--)
|
|
;
|
|
company[i] = '\0';
|
|
memcpy(product, inqptr + 16, 16);
|
|
for (i=16; i > 0 && product[i-1] == ' '; i--)
|
|
;
|
|
product[i] = '\0';
|
|
memcpy(rev, inqptr + 32, 4);
|
|
for (i=4; i > 0 && rev[i-1] == ' '; i--)
|
|
;
|
|
rev[i] = '\0';
|
|
|
|
close(fn);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static int
|
|
tapenum(char *name)
|
|
{
|
|
int dev;
|
|
struct dirent *dent;
|
|
DIR *dirp;
|
|
char tmpname[PATH_MAX];
|
|
const char *dn;
|
|
const devdir *dvd;
|
|
struct stat statbuf;
|
|
|
|
if (strchr(name, '/') != NULL) { /* Complete name */
|
|
if (stat(name, &statbuf) != 0) {
|
|
fprintf(stderr, "Can't stat '%s'.\n", name);
|
|
return (-1);
|
|
}
|
|
if (!S_ISCHR(statbuf.st_mode) ||
|
|
major(statbuf.st_rdev) != SCSI_TAPE_MAJOR)
|
|
return (-1);
|
|
dev = minor(statbuf.st_rdev) & 31;
|
|
return dev;
|
|
}
|
|
else { /* Search from the device directories */
|
|
for (dvd=devdirs; dvd->dir[0] != 0; dvd++) {
|
|
dn = dvd->dir;
|
|
if ((dirp = opendir(dn)) == NULL)
|
|
continue;
|
|
|
|
for ( ; (dent = readdir(dirp)) != NULL; )
|
|
if (!strcmp(dent->d_name, name)) {
|
|
strcpy(tmpname, dn);
|
|
strcat(tmpname, "/");
|
|
strcat(tmpname, dent->d_name);
|
|
if (stat(tmpname, &statbuf) != 0) {
|
|
fprintf(stderr, "Can't stat '%s'.\n", tmpname);
|
|
continue;
|
|
}
|
|
if (!S_ISCHR(statbuf.st_mode) ||
|
|
major(statbuf.st_rdev) != SCSI_TAPE_MAJOR)
|
|
continue;
|
|
dev = minor(statbuf.st_rdev) & 31;
|
|
closedir(dirp);
|
|
return dev;
|
|
}
|
|
closedir(dirp);
|
|
}
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
|
|
static int
|
|
accept_tape_name(char *name)
|
|
{
|
|
char **npp;
|
|
|
|
for (npp=tape_name_bases; *npp; npp++)
|
|
if (!strncmp(name, *npp, strlen(*npp)))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static int
|
|
find_devfiles(int tapeno, char **names)
|
|
{
|
|
int dev, mode, found;
|
|
int non_rew[NBR_MODES];
|
|
struct dirent *dent;
|
|
DIR *dirp;
|
|
char tmpname[PATH_MAX];
|
|
const char *dn;
|
|
static const devdir *dvd;
|
|
const devdir *dvp;
|
|
int tmpdevdirsindex = 0;
|
|
static devdir tmpdevdirs[MAX_TAPES + 1];
|
|
struct stat statbuf;
|
|
|
|
for (found=0; found < NBR_MODES; found++) {
|
|
*names[found] = '\0';
|
|
non_rew[found] = FALSE;
|
|
}
|
|
|
|
if (dvd == NULL && !stat(DEVFS_PATH, &statbuf)) {
|
|
if (S_ISDIR(statbuf.st_mode) && (dirp=opendir(DEVFS_PATH)) != NULL) {
|
|
/* Assume devfs, one directory for each tape */
|
|
for ( ; (dent = readdir(dirp)) != NULL; ) {
|
|
if (!strcmp (dent->d_name, ".")
|
|
|| !strcmp (dent->d_name, ".."))
|
|
continue;
|
|
snprintf(tmpdevdirs[tmpdevdirsindex].dir,
|
|
sizeof(tmpdevdirs[tmpdevdirsindex].dir),
|
|
"%s/%s", DEVFS_PATH, dent->d_name);
|
|
tmpdevdirs[tmpdevdirsindex].selective_scan = FALSE;
|
|
if (++tmpdevdirsindex == MAX_TAPES)
|
|
break;
|
|
}
|
|
tmpdevdirs[tmpdevdirsindex].dir[0] = 0;
|
|
closedir(dirp);
|
|
dvd = &tmpdevdirs[0];
|
|
}
|
|
}
|
|
if (dvd == NULL)
|
|
dvd = devdirs;
|
|
|
|
for (found=0, dvp=dvd; found < NBR_MODES && dvp->dir[0] != 0; dvp++) {
|
|
dn = dvp->dir;
|
|
if ((dirp = opendir(dn)) == NULL)
|
|
continue;
|
|
|
|
for ( ; (dent = readdir(dirp)) != NULL; ) {
|
|
if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, ".."))
|
|
continue;
|
|
/* Ignore non-tape devices to avoid loading all the modules */
|
|
if (dvp->selective_scan && !accept_tape_name(dent->d_name))
|
|
continue;
|
|
strcpy(tmpname, dn);
|
|
strcat(tmpname, "/");
|
|
strcat(tmpname, dent->d_name);
|
|
if (stat(tmpname, &statbuf) != 0) {
|
|
fprintf(stderr, "Can't stat '%s'.\n", tmpname);
|
|
continue;
|
|
}
|
|
if (!S_ISCHR(statbuf.st_mode) || major(statbuf.st_rdev) !=
|
|
SCSI_TAPE_MAJOR)
|
|
continue;
|
|
dev = minor(statbuf.st_rdev);
|
|
if ((dev & 31) != tapeno)
|
|
continue;
|
|
mode = (dev & 127) >> 5;
|
|
if (non_rew[mode])
|
|
continue;
|
|
if (*names[mode] == '\0')
|
|
found++;
|
|
strcpy(names[mode], tmpname);
|
|
non_rew[mode] = (dev & 128) != 0;
|
|
}
|
|
closedir(dirp);
|
|
}
|
|
|
|
return (found > 0);
|
|
}
|
|
|
|
|
|
static int
|
|
set_defs(devdef_tr *defs, char **fnames)
|
|
{
|
|
int i, tape;
|
|
int clear_set[2];
|
|
struct mtop op;
|
|
|
|
for (i=0; i < NBR_MODES; i++) {
|
|
if (*fnames[i] == '\0' || !defs->modedefs[i].defined)
|
|
continue;
|
|
|
|
if ((tape = open(fnames[i], O_RDONLY | O_NONBLOCK)) < 0) {
|
|
fprintf(stderr, "Can't open the tape device '%s' for mode %d.\n",
|
|
fnames[i], i);
|
|
return FALSE;
|
|
}
|
|
|
|
if (i == 0) {
|
|
if (defs->do_rewind) {
|
|
op.mt_op = MTREW;
|
|
op.mt_count = 1;
|
|
ioctl(tape, MTIOCTOP, &op); /* Don't worry about result */
|
|
}
|
|
|
|
if (defs->drive_buffering >= 0) {
|
|
op.mt_op = MTSETDRVBUFFER;
|
|
op.mt_count = MT_ST_DEF_DRVBUFFER | defs->drive_buffering;
|
|
if (ioctl(tape, MTIOCTOP, &op) != 0) {
|
|
fprintf(stderr, "Can't set drive buffering to %d.\n",
|
|
defs->drive_buffering);
|
|
}
|
|
}
|
|
|
|
if (defs->timeout >= 0) {
|
|
op.mt_op = MTSETDRVBUFFER;
|
|
op.mt_count = MT_ST_SET_TIMEOUT | defs->timeout;
|
|
if (ioctl(tape, MTIOCTOP, &op) != 0) {
|
|
fprintf(stderr, "Can't set device timeout %d s.\n",
|
|
defs->timeout);
|
|
}
|
|
}
|
|
|
|
if (defs->long_timeout >= 0) {
|
|
op.mt_op = MTSETDRVBUFFER;
|
|
op.mt_count = MT_ST_SET_LONG_TIMEOUT | defs->long_timeout;
|
|
if (ioctl(tape, MTIOCTOP, &op) != 0) {
|
|
fprintf(stderr, "Can't set device long timeout %d s.\n",
|
|
defs->long_timeout);
|
|
}
|
|
}
|
|
|
|
if (defs->cleaning >= 0) {
|
|
op.mt_op = MTSETDRVBUFFER;
|
|
op.mt_count = MT_ST_SET_CLN | defs->cleaning;
|
|
if (ioctl(tape, MTIOCTOP, &op) != 0) {
|
|
fprintf(stderr, "Can't set cleaning request parameter to %x\n",
|
|
defs->cleaning);
|
|
}
|
|
}
|
|
}
|
|
|
|
op.mt_op = MTSETDRVBUFFER;
|
|
|
|
clear_set[0] = clear_set[1] = 0;
|
|
if (defs->nowait >= 0)
|
|
clear_set[defs->nowait != 0] |= MT_ST_NOWAIT;
|
|
if (defs->sili >= 0)
|
|
clear_set[defs->sili != 0] |= MT_ST_SILI;
|
|
if (defs->modedefs[i].buffer_writes >= 0)
|
|
clear_set[defs->modedefs[i].buffer_writes != 0] |= MT_ST_BUFFER_WRITES;
|
|
if (defs->modedefs[i].async_writes >= 0)
|
|
clear_set[defs->modedefs[i].async_writes != 0] |= MT_ST_ASYNC_WRITES;
|
|
if (defs->modedefs[i].read_ahead >= 0)
|
|
clear_set[defs->modedefs[i].read_ahead != 0] |= MT_ST_READ_AHEAD;
|
|
if (defs->modedefs[i].two_fm >= 0)
|
|
clear_set[defs->modedefs[i].two_fm != 0] |= MT_ST_TWO_FM;
|
|
if (defs->modedefs[i].fast_eod >= 0)
|
|
clear_set[defs->modedefs[i].fast_eod != 0] |= MT_ST_FAST_MTEOM;
|
|
if (defs->modedefs[i].auto_lock >= 0)
|
|
clear_set[defs->modedefs[i].auto_lock != 0] |= MT_ST_AUTO_LOCK;
|
|
if (defs->modedefs[i].can_bsr >= 0)
|
|
clear_set[defs->modedefs[i].can_bsr != 0] |= MT_ST_CAN_BSR;
|
|
if (defs->modedefs[i].no_blklimits >= 0)
|
|
clear_set[defs->modedefs[i].no_blklimits != 0] |= MT_ST_NO_BLKLIMS;
|
|
if (defs->modedefs[i].can_partitions >= 0)
|
|
clear_set[defs->modedefs[i].can_partitions != 0] |= MT_ST_CAN_PARTITIONS;
|
|
if (defs->modedefs[i].scsi2logical >= 0)
|
|
clear_set[defs->modedefs[i].scsi2logical != 0] |= MT_ST_SCSI2LOGICAL;
|
|
if (defs->modedefs[i].sysv >= 0)
|
|
clear_set[defs->modedefs[i].sysv != 0] |= MT_ST_SYSV;
|
|
if (defs->modedefs[i].defs_for_writes >= 0)
|
|
clear_set[defs->modedefs[i].defs_for_writes != 0] |= MT_ST_DEF_WRITES;
|
|
|
|
if (clear_set[0] != 0) {
|
|
op.mt_count = MT_ST_CLEARBOOLEANS | clear_set[0];
|
|
if (ioctl(tape, MTIOCTOP, &op) != 0) {
|
|
fprintf(stderr, "Can't clear the tape options (bits 0x%x, mode %d).\n",
|
|
clear_set[0], i);
|
|
}
|
|
}
|
|
if (clear_set[1] != 0) {
|
|
op.mt_count = MT_ST_SETBOOLEANS | clear_set[1];
|
|
if (ioctl(tape, MTIOCTOP, &op) != 0) {
|
|
fprintf(stderr, "Can't set the tape options (bits 0x%x, mode %d).\n",
|
|
clear_set[1], i);
|
|
}
|
|
}
|
|
|
|
if (defs->modedefs[i].blocksize >= 0) {
|
|
op.mt_count = MT_ST_DEF_BLKSIZE | defs->modedefs[i].blocksize;
|
|
if (ioctl(tape, MTIOCTOP, &op) != 0) {
|
|
fprintf(stderr, "Can't set blocksize %d for mode %d.\n",
|
|
defs->modedefs[i].blocksize, i);
|
|
}
|
|
}
|
|
if (defs->modedefs[i].density >= 0) {
|
|
op.mt_count = MT_ST_DEF_DENSITY | defs->modedefs[i].density;
|
|
if (ioctl(tape, MTIOCTOP, &op) != 0) {
|
|
fprintf(stderr, "Can't set density %x for mode %d.\n",
|
|
defs->modedefs[i].density, i);
|
|
}
|
|
}
|
|
if (defs->modedefs[i].compression >= 0) {
|
|
op.mt_count = MT_ST_DEF_COMPRESSION | defs->modedefs[i].compression;
|
|
if (ioctl(tape, MTIOCTOP, &op) != 0) {
|
|
fprintf(stderr, "Can't set compression %d for mode %d.\n",
|
|
defs->modedefs[i].compression, i);
|
|
}
|
|
}
|
|
|
|
close(tape);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static int
|
|
define_tape(int tapeno, FILE *dbf, devdef_tr *defptr, int print_non_found)
|
|
{
|
|
int i;
|
|
char company[10], product[20], rev[5], *tname, *fnames[NBR_MODES];
|
|
|
|
if (verbose > 0)
|
|
printf("\nstinit, processing tape %d\n", tapeno);
|
|
|
|
if ((fnames[0] = calloc(NBR_MODES, PATH_MAX)) == NULL) {
|
|
fprintf(stderr, "Can't allocate name buffers.\n");
|
|
return FALSE;
|
|
}
|
|
for (i=1; i < NBR_MODES; i++)
|
|
fnames[i] = fnames[i-1] + PATH_MAX;
|
|
|
|
if (!find_devfiles(tapeno, fnames) ||
|
|
*fnames[0] == '\0') {
|
|
if (print_non_found)
|
|
fprintf(stderr, "Can't find any device files for tape %d.\n",
|
|
tapeno);
|
|
free(fnames[0]);
|
|
return FALSE;
|
|
}
|
|
if (verbose > 1)
|
|
for (i=0; i < NBR_MODES; i++)
|
|
printf("Mode %d, name '%s'\n", i + 1, fnames[i]);
|
|
|
|
tname = fnames[0];
|
|
if (!do_inquiry(tname, company, product, rev, print_non_found)) {
|
|
free(fnames[0]);
|
|
return FALSE;
|
|
}
|
|
if (verbose > 0)
|
|
printf("The manufacturer is '%s', product is '%s', and revision '%s'.\n",
|
|
company, product, rev);
|
|
|
|
if (!find_pars(dbf, company, product, rev, defptr, FALSE)) {
|
|
fprintf(stderr, "Can't find defaults for tape number %d.\n", tapeno);
|
|
free(fnames[0]);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!set_defs(defptr, fnames)) {
|
|
free(fnames[0]);
|
|
return FALSE;
|
|
}
|
|
|
|
free(fnames[0]);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static char
|
|
usage(int retval)
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: stinit [-h] [-v] [--version] [-f dbname] [-p] [-r] [drivename_or_number ...]\n");
|
|
exit(retval);
|
|
}
|
|
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
FILE *dbf = NULL;
|
|
int argn;
|
|
int tapeno, parse_only = FALSE;
|
|
char *dbname = NULL;
|
|
devdef_tr defs;
|
|
|
|
defs.do_rewind = FALSE;
|
|
for (argn=1; argn < argc && *argv[argn] == '-'; argn++) {
|
|
if (*(argv[argn] + 1) == 'v')
|
|
verbose++;
|
|
else if (*(argv[argn] + 1) == 'p')
|
|
parse_only = TRUE;
|
|
else if (*(argv[argn] + 1) == 'h')
|
|
usage(0);
|
|
else if (*(argv[argn] + 1) == 'r')
|
|
defs.do_rewind = TRUE;
|
|
else if (*(argv[argn] + 1) == 'f') {
|
|
argn += 1;
|
|
if (argn >= argc)
|
|
usage(1);
|
|
dbname = argv[argn];
|
|
}
|
|
else if (*(argv[argn] + 1) == '-' &&
|
|
*(argv[argn] + 2) == 'v') {
|
|
printf("stinit v. %s\n", VERSION);
|
|
exit(0);
|
|
break;
|
|
}
|
|
else
|
|
usage(1);
|
|
}
|
|
|
|
if ((dbf = open_database(dbname)) == NULL)
|
|
return 1;
|
|
|
|
if (parse_only) {
|
|
if (argc > argn)
|
|
fprintf(stderr, "Extra arguments on command line ignored.\n");
|
|
if (!find_pars(dbf, "xyz", "xyz", "xyz", &defs, TRUE))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
if (argc > argn) { /* Initialize specific drives */
|
|
for ( ; argn < argc; argn++) {
|
|
if (*argv[argn] == '-') {
|
|
usage(1);
|
|
return 1; /* Never executed but makes gcc happy */
|
|
}
|
|
else if (isdigit(*argv[argn]))
|
|
tapeno = strtol(argv[argn], NULL, 0);
|
|
else if ((tapeno = tapenum(argv[argn])) < 0) {
|
|
fprintf(stderr, "Can't find tape number for name '%s'.\n",
|
|
argv[argn]);
|
|
continue;
|
|
}
|
|
if (!define_tape(tapeno, dbf, &defs, TRUE))
|
|
fprintf(stderr, "Definition for '%s' failed.\n", argv[argn]);
|
|
}
|
|
}
|
|
else { /* Initialize all SCSI tapes */
|
|
for (tapeno=0; tapeno < MAX_TAPES; tapeno++)
|
|
if (!define_tape(tapeno, dbf, &defs, FALSE)) {
|
|
fprintf(stderr, "Initialized %d tape device%s.\n",
|
|
tapeno, (tapeno != 1 ? "s" : ""));
|
|
return 0; /* Process tapes until failure */
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|