Skip to content
Snippets Groups Projects
Commit c0d7039b authored by Steven Murray's avatar Steven Murray Committed by Steven Murray
Browse files

Copied tape/sendscsicmd.c to rmc/rmc_send_scsi_cmd.c

parent 7ed1d80e
No related branches found
No related tags found
No related merge requests found
/*
* Copyright (C) 1996-2000 by CERN/IT/PDP/DM
* All rights reserved
*/
/* send_scsi_cmd - Send a SCSI command to a device */
/* return -5 if not supported on this platform (serrno = SEOPNOTSUP)
* -4 if SCSI error (serrno = EIO)
* -3 if CAM error (serrno = EIO)
* -2 if ioctl fails with errno (serrno = errno)
* -1 if open/stat fails with errno (message fully formatted)
* 0 if successful with no data transfer
* >0 number of bytes transferred
*/
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <linux/version.h>
#include <sys/param.h>
/* Impossible unless very very old kernels: */
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif
#include "/usr/include/scsi/sg.h"
#include <sys/stat.h>
#include "scsictl.h"
#include "serrno.h"
#include "Ctape.h"
static char tp_err_msgbuf[132];
static char *sk_msg[] = {
"No sense",
"Recovered error",
"Not ready",
"Medium error",
"Hardware error",
"Illegal request",
"Unit attention",
"Data protect",
"Blank check",
"Vendor unique",
"Copy aborted",
"Aborted command",
"Equal",
"Volume overflow",
"Miscompare",
"Reserved",
};
struct scsi_info {
int status;
char *text;
};
struct scsi_info scsi_codmsg[] = {
{ SCSI_STATUS_CHECK_CONDITION, "Check condition" },
{ SCSI_STATUS_BUSY, "Target busy" },
{ SCSI_STATUS_RESERVATION_CONFLICT, "Reservation conflict" },
{ 0xFF, NULL }
};
#define PROCBUFSZ 80
static void find_sgpath(char *sgpath, int maj, int min) {
/*
Find the sg device for a pair of major and minor device IDs
of a tape device. The match is done by
. identifying the tape's st device node
. getting the device's unique ID from sysfs
. searching the sg device with the same ID (in sysfs)
If no match is found, the returned sg path will be an empty
string.
*/
char systape[] = "/sys/class/scsi_tape";
char sysgen[] = "/sys/class/scsi_generic";
char syspath[256];
char tlink[256];
char glink[256];
int match = 0;
DIR *dir_tape, *dir_gen;
struct dirent *dirent;
char st_dev[64];
struct stat sbuf;
sgpath[0] = '\0';
/* find the st sysfs entry */
if (!(dir_tape = opendir(systape))) return;
while ((dirent = readdir(dir_tape))) {
if (0 == strcmp(".", dirent->d_name)) continue;
if (0 == strcmp("..", dirent->d_name)) continue;
sprintf(st_dev, "/dev/%s", dirent->d_name);
stat(st_dev, &sbuf);
if (maj == (int)major(sbuf.st_rdev) && min == (int)minor(sbuf.st_rdev)) {
sprintf(syspath, "%s/%s/device", systape, dirent->d_name);
match = 1;
break;
}
}
closedir(dir_tape);
if (0 == match) return;
memset(tlink, 0, 256);
readlink(syspath, tlink, 256);
/* find the corresponding sg sysfs entry */
if (!(dir_gen = opendir(sysgen))) return;
while ((dirent = readdir(dir_gen))) {
if (0 == strcmp(".", dirent->d_name)) continue;
if (0 == strcmp("..", dirent->d_name)) continue;
sprintf(syspath, "%s/%s/device", sysgen, dirent->d_name);
memset(glink, 0, 256);
readlink(syspath, glink, 256);
if (0 == strcmp(glink, tlink)) {
sprintf(sgpath, "/dev/%s", dirent->d_name);
goto out;
}
}
out:
closedir(dir_gen);
return;
}
int send_scsi_cmd (
const int tapefd,
const char *const path,
const int do_not_open,
const unsigned char *const cdb,
const int cdblen,
unsigned char *const buffer,
const int buflen,
char *const sense,
const int senselen,
const int timeout, /* in milliseconds */
const int flags,
int *const nb_sense_ret,
char **const msgaddr)
{
int fd;
FILE *fopen();
int n;
int resid = 0;
struct stat sbuf;
struct stat sbufa;
static char *sg_buffer;
static int sg_bufsiz = 0;
struct sg_header *sg_hd;
char sgpath[80];
int timeout_in_jiffies = 0;
int sg_big_buff_val = SG_BIG_BUFF;
int procfd, nbread;
char procbuf[PROCBUFSZ];
(void)senselen;
/* First the value in /proc of the max buffer size for the sg driver */
procfd = open("/proc/scsi/sg/def_reserved_size", O_RDONLY);
if (procfd >= 0) {
memset(procbuf, 0, PROCBUFSZ);
nbread = read(procfd, procbuf, PROCBUFSZ -1);
if (nbread > 0) {
long int tmp;
char *endptr = NULL;
tmp = strtol(procbuf, &endptr, 10);
if (endptr == NULL || *endptr == '\n') {
sg_big_buff_val = (int) tmp;
}
}
close(procfd);
}
if ((int)sizeof(struct sg_header) + cdblen + buflen > sg_big_buff_val) {
sprintf (tp_err_msgbuf, "blocksize too large (max %zd)\n",
sg_big_buff_val - sizeof(struct sg_header) - cdblen);
*msgaddr = tp_err_msgbuf;
serrno = EINVAL;
return (-1);
}
if ((int)sizeof(struct sg_header)+cdblen+buflen > sg_bufsiz) {
if (sg_bufsiz > 0) free (sg_buffer);
if ((sg_buffer = malloc (sizeof(struct sg_header)+cdblen+buflen)) == NULL) {
serrno = errno;
sprintf (tp_err_msgbuf, TP005);
*msgaddr = tp_err_msgbuf;
return (-1);
}
sg_bufsiz = sizeof(struct sg_header) + cdblen + buflen;
}
if (do_not_open) {
fd = tapefd;
strcpy (sgpath, path);
} else {
if (stat (path, &sbuf) < 0) {
serrno = errno;
snprintf (tp_err_msgbuf, sizeof(tp_err_msgbuf), TP042, path, "stat", strerror(errno));
tp_err_msgbuf[sizeof(tp_err_msgbuf) - 1] = '\0';
*msgaddr = tp_err_msgbuf;
return (-1);
}
/* get the major device ID of the sg devices ... */
if (stat ("/dev/sg0", &sbufa) < 0) {
serrno = errno;
snprintf (tp_err_msgbuf, sizeof(tp_err_msgbuf), TP042, "/dev/sg0", "stat", strerror(errno));
tp_err_msgbuf[sizeof(tp_err_msgbuf) - 1] = '\0';
*msgaddr = tp_err_msgbuf;
return (-1);
}
/* ... to detect links and use the path directly! */
if (major(sbuf.st_rdev) == major(sbufa.st_rdev)) {
strcpy (sgpath, path);
} else {
find_sgpath(sgpath, major(sbuf.st_rdev), minor(sbuf.st_rdev));
}
if ((fd = open (sgpath, O_RDWR)) < 0) {
serrno = errno;
snprintf (tp_err_msgbuf, sizeof(tp_err_msgbuf), TP042, sgpath, "open", strerror(errno));
tp_err_msgbuf[sizeof(tp_err_msgbuf) - 1] = '\0';
*msgaddr = tp_err_msgbuf;
return (-1);
}
}
/* set the sg timeout (in jiffies) */
timeout_in_jiffies = timeout * HZ / 1000;
ioctl (fd, SG_SET_TIMEOUT, &timeout_in_jiffies);
memset (sg_buffer, 0, sizeof(struct sg_header));
sg_hd = (struct sg_header *) sg_buffer;
sg_hd->reply_len = sizeof(struct sg_header) + ((flags & SCSI_IN) ? buflen : 0);
sg_hd->twelve_byte = cdblen == 12;
memcpy (sg_buffer+sizeof(struct sg_header), cdb, cdblen);
n = sizeof(struct sg_header) + cdblen;
if (buflen && (flags & SCSI_OUT)) {
memcpy (sg_buffer+n, buffer, buflen);
n+= buflen;
}
if (write (fd, sg_buffer, n) < 0) {
*msgaddr = (char *) strerror(errno);
serrno = errno;
snprintf (tp_err_msgbuf, sizeof(tp_err_msgbuf), TP042, sgpath, "write", *msgaddr);
tp_err_msgbuf[sizeof(tp_err_msgbuf) - 1] = '\0';
*msgaddr = tp_err_msgbuf;
if (! do_not_open) close (fd);
return (-2);
}
if ((n = read (fd, sg_buffer, sizeof(struct sg_header) +
((flags & SCSI_IN) ? buflen : 0))) < 0) {
*msgaddr = (char *) strerror(errno);
serrno = errno;
snprintf (tp_err_msgbuf, sizeof(tp_err_msgbuf), TP042, sgpath, "read", *msgaddr);
tp_err_msgbuf[sizeof(tp_err_msgbuf) - 1] = '\0';
*msgaddr = tp_err_msgbuf;
if (! do_not_open) close (fd);
return (-2);
}
if (! do_not_open) close (fd);
if (sg_hd->sense_buffer[0]) {
memcpy (sense, sg_hd->sense_buffer, sizeof(sg_hd->sense_buffer));
*nb_sense_ret = sizeof(sg_hd->sense_buffer);
}
if (sg_hd->sense_buffer[0] & 0x80) { /* valid */
resid = sg_hd->sense_buffer[3] << 24 | sg_hd->sense_buffer[4] << 16 |
sg_hd->sense_buffer[5] << 8 | sg_hd->sense_buffer[6];
}
if ((sg_hd->sense_buffer[0] & 0x70) &&
((sg_hd->sense_buffer[2] & 0xE0) == 0 ||
(sg_hd->sense_buffer[2] & 0xF) != 0)) {
char tmp_msgbuf[132];
snprintf (tmp_msgbuf, sizeof(tmp_msgbuf), "%s ASC=%X ASCQ=%X",
sk_msg[*(sense+2) & 0xF], *(sense+12), *(sense+13));
tmp_msgbuf[sizeof(tmp_msgbuf) - 1] = '\0';
serrno = EIO;
snprintf (tp_err_msgbuf, sizeof(tp_err_msgbuf), TP042, sgpath, "scsi", tmp_msgbuf);
tp_err_msgbuf[sizeof(tp_err_msgbuf) - 1] = '\0';
*msgaddr = tp_err_msgbuf;
return (-4);
} else if (sg_hd->result) {
*msgaddr = (char *) strerror(sg_hd->result);
serrno = sg_hd->result;
snprintf (tp_err_msgbuf, sizeof(tp_err_msgbuf), TP042, sgpath, "read", *msgaddr);
tp_err_msgbuf[sizeof(tp_err_msgbuf) - 1] = '\0';
*msgaddr = tp_err_msgbuf;
return (-2);
}
if (n)
n -= sizeof(struct sg_header) + resid;
if (n && (flags & SCSI_IN))
memcpy (buffer, sg_buffer+sizeof(struct sg_header), n);
return ((flags & SCSI_IN) ? n : buflen - resid);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment