cdrtools/scgcheck/scgcheck.c
2025-06-15 04:19:58 +08:00

656 lines
17 KiB
C
Raw Permalink Blame History

/* @(#)scgcheck.c 1.22 16/01/24 Copyright 1998-2016 J. Schilling */
#include <schily/mconfig.h>
#ifndef lint
static UConst char sccsid[] =
"@(#)scgcheck.c 1.22 16/01/24 Copyright 1998-2016 J. Schilling";
#endif
/*
* Copyright (c) 1998-2016 J. Schilling
*
* Warning: This program has been written to verify the correctness
* of the upper layer interface from the library "libscg". If you
* modify code from the program "scgcheck", you must change the
* name of the program.
*/
/*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* See the file CDDL.Schily.txt in this distribution for details.
* A copy of the CDDL is also available via the Internet at
* http://www.opensource.org/licenses/cddl1.txt
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file CDDL.Schily.txt from this distribution.
*/
#include <schily/stdio.h>
#include <schily/stdlib.h>
#include <schily/unistd.h>
#include <schily/string.h>
#include <schily/schily.h>
#include <schily/nlsdefs.h>
#include <schily/standard.h>
#include <schily/utypes.h>
#include <schily/btorder.h>
#include <scg/scgcmd.h>
#include <scg/scsidefs.h>
#include <scg/scsireg.h>
#include <scg/scsitransp.h>
#include "scsi_scan.h"
#include "cdrecord.h"
#include "scgcheck.h"
#include "version.h"
LOCAL void usage __PR((int ret));
EXPORT int main __PR((int ac, char *av[]));
LOCAL SCSI *doopen __PR((char *dev));
LOCAL void checkversion __PR((SCSI *scgp));
LOCAL void getbuf __PR((SCSI *scgp));
LOCAL void scg_openerr __PR((char *errstr));
LOCAL int find_drive __PR((SCSI *scgp, char *sdevname, int flags));
EXPORT void flushit __PR((void));
EXPORT int countopen __PR((void));
EXPORT int chkprint __PR((const char *, ...)) __printflike__(1, 2);
EXPORT int chkgetline __PR((char *, int));
char *dev;
char *scgopts;
int debug; /* print debug messages */
int kdebug; /* kernel debug messages */
int scsi_verbose; /* SCSI verbose flag */
int lverbose; /* local verbose flag */
int silent; /* SCSI silent flag */
int deftimeout = 40; /* default SCSI timeout */
int xdebug; /* extended debug flag */
BOOL autotest;
char *buf; /* The transfer buffer */
long bufsize; /* The size of the transfer buffer */
FILE *logfile;
char unavail[] = "<data unavaiable>";
char scgc_version[] = VERSION;
int basefds;
#define BUF_SIZE (126*1024)
#define MAX_BUF_SIZE (16*1024*1024)
LOCAL void
usage(ret)
int ret;
{
error(_("Usage:\tscgcheck [options]\n"));
error(_("Options:\n"));
error(_("\t-version print version information and exit\n"));
error(_("\tdev=target SCSI target to use\n"));
error(_("\tscgopts=spec SCSI options for libscg\n"));
error(_("\ttimeout=# set the default SCSI command timeout to #.\n"));
error(_("\tdebug=#,-d Set to # or increment misc debug level\n"));
error(_("\tkdebug=#,kd=# do Kernel debugging\n"));
error(_("\t-verbose,-v increment general verbose level by one\n"));
error(_("\t-Verbose,-V increment SCSI command transport verbose level by one\n"));
error(_("\t-silent,-s do not print status of failed SCSI commands\n"));
error(_("\tf=filename Name of file to write log data to.\n"));
error(_("\t-auto try to do a fully automated test\n"));
error("\n");
exit(ret);
}
char opts[] = "debug#,d+,kdebug#,kd#,timeout#,verbose+,v+,Verbose+,V+,silent,s,x+,xd#,help,h,version,dev*,scgopts*,f*,auto";
EXPORT int
main(ac, av)
int ac;
char *av[];
{
int cac;
char * const *cav;
SCSI *scgp = NULL;
char device[128];
char abuf[2];
int ret;
int fcount;
BOOL help = FALSE;
BOOL pversion = FALSE;
char *filename = "check.log";
#if defined(USE_NLS)
char *dir;
#endif
save_args(ac, av);
#if defined(USE_NLS)
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "scgcheck" /* Use this only if it weren't */
#endif
dir = searchfileinpath("share/locale", F_OK,
SIP_ANY_FILE|SIP_NO_PATH, NULL);
if (dir)
(void) bindtextdomain(TEXT_DOMAIN, dir);
else
#if defined(PROTOTYPES) && defined(INS_BASE)
(void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
#else
(void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
#endif
(void) textdomain(TEXT_DOMAIN);
#endif
cac = --ac;
cav = ++av;
if (getallargs(&cac, &cav, opts,
&debug, &debug,
&kdebug, &kdebug,
&deftimeout,
&lverbose, &lverbose,
&scsi_verbose, &scsi_verbose,
&silent, &silent,
&xdebug, &xdebug,
&help, &help, &pversion,
&dev, &scgopts,
&filename, &autotest) < 0) {
errmsgno(EX_BAD, _("Bad flag: %s.\n"), cav[0]);
usage(EX_BAD);
}
if (help)
usage(0);
if (pversion) {
printf(_("scgcheck %s (%s-%s-%s) Copyright (C) 1998-2016 %s\n"),
scgc_version,
HOST_CPU, HOST_VENDOR, HOST_OS,
_("Joerg Schilling"));
exit(0);
}
fcount = 0;
cac = ac;
cav = av;
while (getfiles(&cac, &cav, opts) > 0) {
fcount++;
cac--;
cav++;
}
if (fcount > 0)
comerrno(EX_BAD, _("Bad argument(s).\n"));
/*error("dev: '%s'\n", dev);*/
logfile = fileopen(filename, "wct");
if (logfile == NULL)
comerr(_("Cannot open logfile.\n"));
chkprint(_("Scgcheck %s (%s-%s-%s) SCSI user level transport library ABI checker.\n\
Copyright (C) 1998-2016 %s\n"),
scgc_version,
HOST_CPU, HOST_VENDOR, HOST_OS,
_("Joerg Schilling"));
/*
* Call scg_remote() to force loading the remote SCSI transport library
* code that is located in librscg instead of the dummy remote routines
* that are located inside libscg.
*/
scg_remote();
basefds = countopen();
if (xdebug)
error(_("nopen: %d\n"), basefds);
chkprint(_("**********> Checking whether your implementation supports to scan the SCSI bus.\n"));
chkprint(_("Trying to open device: '%s'.\n"), dev);
scgp = doopen(dev);
if (xdebug) {
error(_("nopen: %d\n"), countopen());
error(_("Scanopen opened %d new files.\n"), countopen() - basefds);
}
device[0] = '\0';
if (scgp == NULL) do {
error(_("SCSI open failed...\n"));
if (!scg_yes(_("Retry with different device name? ")))
break;
error(_("Enter SCSI device name for bus scanning [%s]: "), device);
flushit();
(void) getline(device, sizeof (device));
if (device[0] == '\0')
strcpy(device, "0,6,0");
chkprint(_("Trying to open device: '%s'.\n"), device);
scgp = doopen(device);
} while (scgp == NULL);
if (scgp) {
checkversion(scgp);
getbuf(scgp);
ret = select_target(scgp, stdout);
select_target(scgp, logfile);
if (ret < 1) {
chkprint(_("----------> SCSI scan bus test: found NO TARGETS\n"));
} else {
chkprint(_("----------> SCSI scan bus test PASSED\n"));
}
} else {
chkprint(_("----------> SCSI scan bus test FAILED\n"));
}
if (xdebug)
error(_("nopen: %d\n"), countopen());
chkprint(_("For the next test we need to open a single SCSI device.\n"));
chkprint(_("Best results will be obtained if you specify a modern CD-ROM drive.\n"));
if (scgp) { /* Scanbus works / may work */
int i;
i = find_drive(scgp, dev, 0);
if (i < 0) {
scg_openerr("");
/* NOTREACHED */
}
snprintf(device, sizeof (device),
"%s%s%d,%d,%d",
dev?dev:"", dev?(dev[strlen(dev)-1] == ':'?"":":"):"",
scg_scsibus(scgp), scg_target(scgp), scg_lun(scgp));
scg_close(scgp);
scgp = NULL;
}
if (device[0] == '\0')
strcpy(device, "0,6,0");
do {
char Device[128];
error(_("Enter SCSI device name [%s]: "), device);
(void) chkgetline(Device, sizeof (Device));
if (Device[0] != '\0')
strcpy(device, Device);
chkprint(_("Trying to open device: '%s'.\n"), device);
scgp = doopen(device);
if (scgp) {
checkversion(scgp);
getbuf(scgp);
}
/*
* XXX hier mu<6D> getestet werden ob das Ger<65>t brauchbar f<>r die folgenden Tests ist.
*/
} while (scgp == NULL);
if (xdebug)
error(_("nopen: %d\n"), countopen());
/*
* First try to check which type of SCSI device we
* have.
*/
scgp->silent++;
(void) unit_ready(scgp); /* eat up unit attention */
scgp->silent--;
getdev(scgp, TRUE);
printinq(scgp, logfile);
printf(_("Ready to start test for second SCSI open? Enter <CR> to continue: "));
(void) chkgetline(abuf, sizeof (abuf));
#define CHECK_SECOND_OPEN
#ifdef CHECK_SECOND_OPEN
if (!streql(abuf, "n")) {
SCSI *scgp2 = NULL;
int oldopen = countopen();
BOOL second_ok = TRUE;
scgp->silent++;
ret = inquiry(scgp, buf, sizeof (struct scsi_inquiry));
scgp->silent--;
if (xdebug)
error(_("ret: %d key: %d\n"), ret, scg_sense_key(scgp));
if (ret >= 0 || scgp->scmd->error == SCG_RETRYABLE) {
chkprint(_("First SCSI open OK - device usable\n"));
chkprint(_("**********> Checking for second SCSI open.\n"));
if ((scgp2 = doopen(device)) != NULL) {
/*
* XXX Separates getbuf() fuer scgp2?
*/
chkprint(_("Second SCSI open for same device succeeded, %d additional file descriptor(s) used.\n"),
countopen() - oldopen);
scgp->silent++;
ret = inquiry(scgp, buf, sizeof (struct scsi_inquiry));
scgp->silent--;
if (ret >= 0 || scgp->scmd->error == SCG_RETRYABLE) {
chkprint(_("Second SCSI open is usable\n"));
}
chkprint(_("Closing second SCSI.\n"));
scg_close(scgp2);
scgp2 = NULL;
chkprint(_("Checking first SCSI.\n"));
scgp->silent++;
ret = inquiry(scgp, buf, sizeof (struct scsi_inquiry));
scgp->silent--;
if (ret >= 0 || scgp->scmd->error == SCG_RETRYABLE) {
second_ok = TRUE;
chkprint(_("First SCSI open is still usable\n"));
chkprint(_("----------> Second SCSI open test PASSED.\n"));
} else if (ret < 0 && scgp->scmd->error == SCG_FATAL) {
second_ok = FALSE;
chkprint(_("First SCSI open does not work anymore.\n"));
chkprint(_("----------> Second SCSI open test FAILED.\n"));
} else {
second_ok = FALSE;
chkprint(_("First SCSI open has strange problems.\n"));
chkprint(_("----------> Second SCSI open test FAILED.\n"));
}
} else {
second_ok = FALSE;
chkprint(_("Cannot open same SCSI device a second time.\n"));
chkprint(_("----------> Second SCSI open test FAILED.\n"));
}
} else {
second_ok = FALSE;
chkprint(_("First SCSI open is not usable\n"));
chkprint(_("----------> Second SCSI open test FAILED.\n"));
}
if (xdebug > 1)
error("scgp %p scgp2 %p\n", scgp, scgp2);
if (scgp2)
scg_close(scgp2);
if (scgp) {
scgp->silent++;
ret = inquiry(scgp, buf, sizeof (struct scsi_inquiry));
scgp->silent--;
if (ret >= 0 || scgp->scmd->error == SCG_RETRYABLE) {
chkprint(_("First SCSI open is still usable\n"));
} else {
scg_freebuf(scgp);
scg_close(scgp);
scgp = doopen(device);
getbuf(scgp);
if (xdebug > 1)
error("scgp %p\n", scgp);
}
}
}
#endif /* CHECK_SECOND_OPEN */
printf(_("Ready to start test for succeeded command? Enter <CR> to continue: "));
(void) chkgetline(abuf, sizeof (abuf));
chkprint(_("**********> Checking for succeeded SCSI command.\n"));
scgp->verbose++;
ret = inquiry(scgp, buf, sizeof (struct scsi_inquiry));
scg_vsetup(scgp);
scg_errfflush(scgp, logfile);
scgp->verbose--;
scg_fprbytes(logfile, _("Inquiry Data :"), (Uchar *)buf,
sizeof (struct scsi_inquiry) - scg_getresid(scgp));
if (ret >= 0 && !scg_cmd_err(scgp)) {
chkprint(_("----------> SCSI succeeded command test PASSED\n"));
} else {
chkprint(_("----------> SCSI succeeded command test FAILED\n"));
}
sensetest(scgp);
if (!autotest)
chkprint(_("----------> SCSI status byte test NOT YET READY\n"));
/*
* scan OK
* work OK
* fail OK
* sense data/count OK
* SCSI status
* dma resid
* ->error GOOD/FAIL/timeout/noselect
* ??
*
* reset
*/
dmaresid(scgp);
chkprint(_("----------> SCSI transport code test NOT YET READY\n"));
return (0);
}
LOCAL SCSI *
doopen(sdevname)
char *sdevname;
{
SCSI *scgp;
char errstr[128];
if ((scgp = scg_open(sdevname, errstr, sizeof (errstr), debug, lverbose)) == (SCSI *)0) {
errmsg(_("%s%sCannot open SCSI driver.\n"), errstr, errstr[0]?". ":"");
fprintf(logfile, _("%s. %s%sCannot open SCSI driver.\n"),
errmsgstr(geterrno()), errstr, errstr[0]?". ":"");
errmsgno(EX_BAD, _("For possible targets try 'cdrecord -scanbus'. Make sure you are root.\n"));
return (scgp);
}
scg_settimeout(scgp, deftimeout);
if (scgopts) {
int i = scg_opts(scgp, scgopts);
if (i <= 0)
exit(i < 0 ? EX_BAD : 0);
}
scgp->verbose = scsi_verbose;
scgp->silent = silent;
scgp->debug = debug;
scgp->kdebug = kdebug;
scgp->cap->c_bsize = 2048;
return (scgp);
}
LOCAL void
checkversion(scgp)
SCSI *scgp;
{
char *vers;
char *auth;
/*
* Warning: If you modify this section of code, you must
* change the name of the program.
*/
vers = scg_version(0, SCG_VERSION);
auth = scg_version(0, SCG_AUTHOR);
chkprint(_("Using libscg version '%s-%s'\n"), auth, vers);
if (auth == 0 || strcmp("schily", auth) != 0) {
errmsgno(EX_BAD,
_("Warning: using inofficial version of libscg (%s-%s '%s').\n"),
auth, vers, scg_version(0, SCG_SCCS_ID));
}
vers = scg_version(scgp, SCG_VERSION);
auth = scg_version(scgp, SCG_AUTHOR);
if (lverbose > 1)
error(_("Using libscg transport code version '%s-%s'\n"), auth, vers);
fprintf(logfile, _("Using libscg transport code version '%s-%s'\n"), auth, vers);
if (auth == 0 || strcmp("schily", auth) != 0) {
errmsgno(EX_BAD,
_("Warning: using inofficial libscg transport code version (%s-%s '%s').\n"),
auth, vers, scg_version(scgp, SCG_SCCS_ID));
}
vers = scg_version(scgp, SCG_KVERSION);
if (vers == NULL)
vers = unavail;
fprintf(logfile, _("Using kernel transport code version '%s'\n"), vers);
vers = scg_version(scgp, SCG_RVERSION);
auth = scg_version(scgp, SCG_RAUTHOR);
if (lverbose > 1 && vers && auth)
error(_("Using remote transport code version '%s-%s'\n"), auth, vers);
if (auth != 0 && strcmp("schily", auth) != 0) {
errmsgno(EX_BAD,
_("Warning: using inofficial remote transport code version (%s-%s '%s').\n"),
auth, vers, scg_version(scgp, SCG_RSCCS_ID));
}
if (auth == NULL)
auth = unavail;
if (vers == NULL)
vers = unavail;
fprintf(logfile, _("Using remote transport code version '%s-%s'\n"), auth, vers);
}
LOCAL void
getbuf(scgp)
SCSI *scgp;
{
bufsize = scg_bufsize(scgp, MAX_BUF_SIZE);
chkprint(_("Max DMA buffer size: %ld\n"), bufsize);
seterrno(0);
if ((buf = scg_getbuf(scgp, bufsize)) == NULL) {
errmsg(_("Cannot get SCSI buffer (%ld bytes).\n"), bufsize);
fprintf(logfile, _("%s. Cannot get SCSI buffer (%ld bytes).\n"),
errmsgstr(geterrno()), bufsize);
} else {
scg_freebuf(scgp);
}
bufsize = scg_bufsize(scgp, BUF_SIZE);
if (debug)
error(_("SCSI buffer size: %ld\n"), bufsize);
if ((buf = scg_getbuf(scgp, bufsize)) == NULL)
comerr(_("Cannot get SCSI I/O buffer.\n"));
}
LOCAL void
scg_openerr(errstr)
char *errstr;
{
errmsg(_("%s%sCannot open or use SCSI driver.\n"), errstr, errstr[0]?". ":"");
errmsgno(EX_BAD, _("For possible targets try 'cdrecord -scanbus'.%s\n"),
geteuid() ? _(" Make sure you are root."):"");
errmsgno(EX_BAD, _("For possible transport specifiers try 'cdrecord dev=help'.\n"));
exit(EX_BAD);
}
LOCAL int
find_drive(scgp, sdevname, flags)
SCSI *scgp;
char *sdevname;
int flags;
{
int ntarget;
int type = INQ_ROMD;
if ((flags & F_MSINFO) == 0)
error(_("No target specified, trying to find one...\n"));
ntarget = find_target(scgp, type, -1);
if (ntarget < 0)
return (ntarget);
if (ntarget == 1) {
/*
* Simple case, exactly one CD-ROM found.
*/
find_target(scgp, type, 1);
} else if (ntarget <= 0 && (ntarget = find_target(scgp, type = INQ_WORM, -1)) == 1) {
/*
* Exactly one CD-ROM acting as WORM found.
*/
find_target(scgp, type, 1);
} else if (ntarget <= 0) {
/*
* No single CD-ROM or WORM found.
*/
errmsgno(EX_BAD, _("No CD/DVD/BD-Recorder target found.\n"));
errmsgno(EX_BAD, _("Your platform may not allow to scan for SCSI devices.\n"));
comerrno(EX_BAD, _("Call 'cdrecord dev=help' or ask your sysadmin for possible targets.\n"));
} else {
errmsgno(EX_BAD, _("Too many CD/DVD/BD-Recorder targets found.\n"));
#ifdef nonono
select_target(scgp, stdout);
comerrno(EX_BAD, _("Select a target from the list above and use 'cdrecord dev=%s%sb,t,l'.\n"),
sdevname?sdevname:"",
sdevname?(sdevname[strlen(sdevname)-1] == ':'?"":":"):"");
#endif /* nonono */
find_target(scgp, type, 1);
}
if ((flags & F_MSINFO) == 0)
error(_("Using dev=%s%s%d,%d,%d.\n"),
sdevname?sdevname:"",
sdevname?(sdevname[strlen(sdevname)-1] == ':'?"":":"):"",
scg_scsibus(scgp), scg_target(scgp), scg_lun(scgp));
return (ntarget);
}
EXPORT void
flushit()
{
flush();
fflush(logfile);
}
/*--------------------------------------------------------------------------*/
#include <schily/fcntl.h>
int
countopen()
{
int nopen = 0;
int i;
#ifdef F_GETFD /* XXX how to count open fd on MSC */
for (i = 0; i < 1000; i++) {
if (fcntl(i, F_GETFD, 0) >= 0)
nopen++;
}
#endif
return (nopen);
}
/*--------------------------------------------------------------------------*/
#include <schily/varargs.h>
/* VARARGS1 */
#ifdef PROTOTYPES
EXPORT int
chkprint(const char *fmt, ...)
#else
EXPORT int
chkprint(fmt, va_alist)
char *fmt;
va_dcl
#endif
{
va_list args;
int ret;
#ifdef PROTOTYPES
va_start(args, fmt);
#else
va_start(args);
#endif
ret = js_fprintf(stdout, "%r", fmt, args);
va_end(args);
if (ret < 0)
return (ret);
#ifdef PROTOTYPES
va_start(args, fmt);
#else
va_start(args);
#endif
ret = js_fprintf(logfile, "%r", fmt, args);
va_end(args);
return (ret);
}
EXPORT int
chkgetline(lbuf, len)
char *lbuf;
int len;
{
flushit();
if (autotest) {
printf("\n");
flush();
if (len > 0)
lbuf[0] = '\0';
return (0);
}
return (getline(lbuf, len));
}