Files
mt-st/stinit.c
Iustin Pop 34978b4019 Fix a 25-year old bug \o/
This is a trivial bug, but a bug nevertheless. It's present in the
first commit in this (synthetic) Git repository, and from my
investigations into very old versions - mt-st-0.5 from an old RedHat
version - it was already present in the `stinit.c` version from Apr
11, 1998, which makes it 25 years and 9 days old. This is a new record
for me, which I don't think I'll beat :)

The bug is trivial, in retrospect, and was found just by accident
because with the 1.6 release uploaded in Debian, the new test suite is
run on multiple architectures. While amd64/x86 doesn't trigger this
bug in neither '-O2' nor '-g' build, mips64el, arm64, and s390x do
show the bug in the optimised builds (and only in them) as follows:

amd64:

```
$ /sbin/stinit -v -v -v -p -f ./tests/data/bad-definition.data

Parsing modes for ('XYZ', 'UVW1', '').
Mode 1 definition:  blocksize=
Warning: errors in definition for ('XYZ', 'UVW1', ''):
 blocksize=

Definition parse completed. Errors found!
```

s390x:

```
$ ./stinit -p -v -v -v -f tests/data/bad-definition.data

Parsing modes for ('XYZ', 'UVW1', '').
Mode 1 definition:  blocksize=
Warning: errors in definition for ('XYZ', 'UVW1', ''):
 �H

Definition parse completed. Errors found!
```

And shelltest fails to parse that binary output, in 4/5 cases, which
by luck caused the builds to fail. From there it was a fun
investigation into trying to find why it behaves like that.

This bug is caught by the recently introduced MSAN build in the GitHub
CI, so will rely less on luck in the future.

I'm not 100% sure on the semantics of the fix, but it should only
affect this case (missing value to argument), so should be a well
localised fix.

Signed-off-by: Iustin Pop <iustin@k1024.org>
2023-04-20 23:18:23 +02:00

944 lines
30 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 <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/major.h>
#include <scsi/sg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.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 char usage(int retval) __attribute__((noreturn));
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 {
if (*cp == '\0')
return NULL;
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, size_t buflen, int limiter)
{
size_t 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) >= buflen) {
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], modebuf[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';) {
int tmpsize;
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;
}
if ((tmpsize = snprintf(modebuf, DEFMAX, "%s%s", comptr, cp)) >= DEFMAX) {
fprintf(stderr, "Definition too large for ('%s', '%s', '%s'): size %d, maximum is %d\n",
tmpcomp, tmpprod, tmprev, tmpsize, DEFMAX);
*nextdef = c;
errors++;
continue;
}
*nextdef = c;
if (verbose > 1)
fprintf(stderr, "Mode %d definition: %s\n", mode + 1, modebuf);
if ((t = find_string(modebuf, "disab", line, LINEMAX)) != NULL &&
strtol(t, NULL, 0) != 0) {
defs->modedefs[mode].defined = FALSE;
continue;
}
if ((t = find_string(modebuf, "drive-", line, LINEMAX)) != NULL)
defs->drive_buffering = num_arg(t);
if ((t = find_string(modebuf, "timeout", line, LINEMAX)) != NULL)
defs->timeout = num_arg(t);
if ((t = find_string(modebuf, "long-time", line, LINEMAX)) != NULL)
defs->long_timeout = num_arg(t);
if ((t = find_string(modebuf, "clean", line, LINEMAX)) != NULL)
defs->cleaning = num_arg(t);
if ((t = find_string(modebuf, "no-w", line, LINEMAX)) != NULL)
defs->nowait = num_arg(t);
if ((t = find_string(modebuf, "sili", line, LINEMAX)) != NULL)
defs->sili = num_arg(t);
defs->modedefs[mode].defined = TRUE;
if ((t = find_string(modebuf, "block", line, LINEMAX)) != NULL)
defs->modedefs[mode].blocksize = num_arg(t);
if ((t = find_string(modebuf, "dens", line, LINEMAX)) != NULL)
defs->modedefs[mode].density = num_arg(t);
if ((t = find_string(modebuf, "buff", line, LINEMAX)) != NULL)
defs->modedefs[mode].buffer_writes = num_arg(t);
if ((t = find_string(modebuf, "async", line, LINEMAX)) != NULL)
defs->modedefs[mode].async_writes = num_arg(t);
if ((t = find_string(modebuf, "read", line, LINEMAX)) != NULL)
defs->modedefs[mode].read_ahead = num_arg(t);
if ((t = find_string(modebuf, "two", line, LINEMAX)) != NULL)
defs->modedefs[mode].two_fm = num_arg(t);
if ((t = find_string(modebuf, "comp", line, LINEMAX)) != NULL)
defs->modedefs[mode].compression = num_arg(t);
if ((t = find_string(modebuf, "auto", line, LINEMAX)) != NULL)
defs->modedefs[mode].auto_lock = num_arg(t);
if ((t = find_string(modebuf, "fast", line, LINEMAX)) != NULL)
defs->modedefs[mode].fast_eod = num_arg(t);
if ((t = find_string(modebuf, "can-b", line, LINEMAX)) != NULL)
defs->modedefs[mode].can_bsr = num_arg(t);
if ((t = find_string(modebuf, "noblk", line, LINEMAX)) != NULL)
defs->modedefs[mode].no_blklimits = num_arg(t);
if ((t = find_string(modebuf, "can-p", line, LINEMAX)) != NULL)
defs->modedefs[mode].can_partitions = num_arg(t);
if ((t = find_string(modebuf, "scsi2", line, LINEMAX)) != NULL)
defs->modedefs[mode].scsi2logical = num_arg(t);
if ((t = find_string(modebuf, "sysv", line, LINEMAX)) != NULL)
defs->modedefs[mode].sysv = num_arg(t);
if ((t = find_string(modebuf, "defs-for-w", line, LINEMAX)) != NULL)
defs->modedefs[mode].defs_for_writes = num_arg(t);
for (t = modebuf; *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, modebuf);
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;
char *convp;
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);
} else if (isdigit(*argv[argn])) {
tapeno = strtol(argv[argn], &convp, 0);
if (*argv[argn] != '\0' && *convp != '\0') {
fprintf(stderr,
"Invalid tape device index '%s': don't "
"know how to parse '%s'\n",
argv[argn], convp);
continue;
}
} 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;
}