Skip to content
Snippets Groups Projects
rmc_smcsubr.c 29.75 KiB
/*
 * Copyright (C) 1998-2003 by CERN/IT/PDP/DM
 * All rights reserved
 */
 

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#include "getconfent.h"
#include "rbtsubr_constants.h"
#include "rmc_constants.h"
#include "rmc_logit.h"
#include "rmc_send_scsi_cmd.h"
#include "rmc_sendrep.h"
#include "rmc_smcsubr.h"
#include "rmc_smcsubr2.h"
#include "scsictl.h"
#include "serrno.h"
#include "smc_constants.h"

#define	RBT_XTRA_PROC 10
#define PATH_CONF "/etc/cta/cta-smc.conf"
static struct smc_status smc_status;
static const char *smc_msgaddr;

static void save_error(
	const int rc,
	const int nb_sense,
	const char *const sense,
	const char *const msgaddr)
{
	smc_msgaddr = msgaddr;
	smc_status.rc = rc;
	smc_status.skvalid = 0;
	smc_status.save_errno = serrno;
	if (rc == -4 && nb_sense >= 14) {
		smc_status.asc = sense[12];
		smc_status.ascq = sense[13];
		smc_status.sensekey = sense[2] &0xF;
		smc_status.skvalid = 1;
	} else {
		smc_status.asc = 0;
		smc_status.ascq = 0;
		smc_status.sensekey = 0;
	}
}

static int vmatch (const char *const pattern, const char *const vid)
{
	const char *p;
	const char *v;

	for (p = pattern, v = vid; *p; p++, v++) {
		if (*v == 0 && *p != '*')
			return (1);
		switch (*p) {
		case '?':	/* match any single character */
			continue;
		case '*':
			if (*(++p) == 0)
				return (0); /* trailing * matches the rest */
			while (vmatch (p, v)) {
				if (*(++v) == 0)
					return (1);
			}
			return (0);
		default:
			if (*p != *v)
				return (1);
		}
	}
	return (*v != 0);
}

static int get_element_size(
	const int fd,
	const char *const rbtdev,
	const int type)
{
	unsigned char buf[128];
	unsigned char cdb[12];
	const char *msgaddr;
	int nb_sense_ret;
	int rc;
	char sense[MAXSENSE];
	int voltag = 0x10;
        int pause_mode = 1;
        int nretries = 0;

 	memset (cdb, 0, sizeof(cdb));
 	cdb[0] = 0xB8;		/* read element status */
 	cdb[1] = voltag + type; /* we request volume tag info and this type */
 	cdb[5] = 1;             /* we only need one element */
 	cdb[9] = 128;           /* limit for the report */

        /* IBM library in pause mode  */ 
        while (pause_mode && nretries <= 900) {
 	     rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 12, buf, 128,
 		  sense, 38, SCSI_IN, &nb_sense_ret, &msgaddr);
             if (rc < 0) {
               if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04)  && (sense[13] == -0X7B )) {
                 sleep(60);
                 pause_mode = 1;
               } else  {
                 pause_mode = 0; 
               }
             } else {
                pause_mode = 0;
             }
             nretries++;
        }

 	if (rc < 0) {
 		save_error (rc, nb_sense_ret, sense, msgaddr);
 		return (-1);
 	}
	return (buf[10] * 256 + buf[11]);
}

static int get_element_info(
	const char opcode,
	const int fd,
	const char *const rbtdev,
	const int type,
	const int start,
	const int nbelem,
	struct smc_element_info element_info[])
{
	int avail_elem;
	unsigned char cdb[12];
	unsigned char *data;
	int edl;
	int element_size;
	char func[16];
	int i;
	int len;
	const char *msgaddr;
	int nb_sense_ret;
	unsigned char *p;
	unsigned char *page_end, *page_start;
	unsigned char *q;
	int rc;
	char sense[MAXSENSE];
        int pause_mode = 1;
        int nretries = 0;
	int nbReportBytesRemaining = 0;
	int nbElementsInReport = 0;

	strncpy (func, "get_elem_info", sizeof(func));
	func[sizeof(func) - 1] = '\0';
	if (type) {
		element_size = get_element_size (fd, rbtdev, type);
		if (element_size < 0) return (-1);
	} else {
		element_size = get_element_size (fd, rbtdev, 1); /* transport */
		if (element_size < 0) return (-1);
		i = get_element_size (fd, rbtdev, 2);	/* slot */
		if (i < 0) return (-1);
		if (i > element_size) element_size = i;
		i = get_element_size (fd, rbtdev, 3);	/* port */
		if (i < 0) return (-1);
		if (i > element_size) element_size = i;
		i = get_element_size (fd, rbtdev, 4);	/* device */
		if (i < 0) return (-1);
		if (i > element_size) element_size = i;
	}
	len = nbelem * element_size + 8;
	if (type != 0 || nbelem == 1)
		len += 8;	/* one element header */
	else
		len += 32;	/* possibly four element headers */
	data = (unsigned char *)malloc (len);
	memset (cdb, 0, sizeof(cdb));
	cdb[0] = opcode;	/* read element status or request volume element address */
	cdb[1] = 0x10 + type;
	cdb[2] = start >> 8;
	cdb[3] = start & 0xFF;
	cdb[4] = nbelem >> 8;
	cdb[5] = nbelem & 0xFF;
	cdb[7] = len >> 16;
	cdb[8] = (len >> 8) & 0xFF;
	cdb[9] = len & 0xFF;

        /* IBM library in pause mode  */ 
        while (pause_mode && nretries <= 900) {
	     rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 12, data, len,
		  sense, 38, SCSI_IN, &nb_sense_ret, &msgaddr);
             if (rc < 0) {
               if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04)  && (sense[13] == -0X7B )) {
                 sleep(60);
                 pause_mode = 1;
               } else  {
                 pause_mode = 0;
               }
             } else {
                pause_mode = 0;
             }
             nretries++;
        }

	if (rc < 0) {
		save_error (rc, nb_sense_ret, sense, msgaddr);
		free (data);
		return (-1);
	}
	avail_elem = *(data+2) * 256 + *(data+3);
	nbReportBytesRemaining = *(data+5) * 256 * 256 + *(data+6) * 256 + *(data+7);
	i = 0;
	p = data + 8;			/* point after data header */
	while (i < avail_elem && 0 < nbReportBytesRemaining) {
		nbReportBytesRemaining -= 8;
		edl = *(p+2) * 256 + *(p+3);
		page_start = p + 8;	/* point after page header */
		page_end = page_start +
			(((*(p+5) * 256 + *(p+6)) * 256) + *(p+7));
		if (page_end > (data + len)) page_end = data + len;
		for (p = page_start; p < page_end && i < avail_elem; p += edl, i++) {
			nbElementsInReport++;
			nbReportBytesRemaining -= edl;
			element_info[i].element_address = *p * 256 + *(p+1);
			element_info[i].element_type = *(page_start-8);
			element_info[i].state = *(p+2);
			element_info[i].asc = *(p+4);
			element_info[i].ascq = *(p+5);
			element_info[i].flags = *(p+9);
			element_info[i].source_address = *(p+10) * 256 + *(p+11);
			if ((*(page_start-7) & 0x80) == 0 ||
			    (*(p+12) == '\0') || (*(p+12) == ' '))
				element_info[i].name[0] = '\0';
			else {
				q = (unsigned char *) strchr ((char *)p+12, ' ');
				if (q) {
					strncpy (element_info[i].name, (char *)p+12, q-p-12);
					element_info[i].name[q-p-12] = '\0';
				} else
					strcpy (element_info[i].name, (char *)p+12);
				if (strlen (element_info[i].name) > CA_MAXVIDLEN)
					element_info[i].name[CA_MAXVIDLEN] = '\0';
			}
		}
	}
	free (data);
	return (nbElementsInReport);
}

int smc_get_geometry(
	const int fd,
	const char *const rbtdev,
        struct robot_info *const robot_info)
{
	unsigned char buf[36];
	unsigned char cdb[6];
	char func[16];
	const char *msgaddr;
	int nb_sense_ret;
	int rc;
	char sense[MAXSENSE];
        int pause_mode = 1;        
        int nretries = 0;     

	strncpy(func, "get_geometry", sizeof(func));
	func[sizeof(func) - 1] = '\0';

	memset (cdb, 0, sizeof(cdb));
	cdb[0] = 0x12;		/* inquiry */
	cdb[4] = 36;

        /* IBM library in pause mode  */ 
        while (pause_mode && nretries <= 900) { 
	    rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 6, buf, 36,
		sense, 38, SCSI_IN, &nb_sense_ret, &msgaddr);
            if (rc < 0) {
              if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04)  && (sense[13] == -0X7B )) {
                 sleep(60);
                 pause_mode = 1;
              } else  {
                 pause_mode = 0;
              }
            } else {
                 pause_mode = 0;
            }
            nretries++;
        }

	if (rc < 0) {
		save_error (rc, nb_sense_ret, sense, msgaddr);
		return (-1);
	}
	memcpy (robot_info->inquiry, buf+8, 28);
	robot_info->inquiry[28] = '\0';
	memset (cdb, 0, sizeof(cdb));
	cdb[0] = 0x1A;		/* mode sense */
        cdb[1] = 0x08;          /* DBD bit - Disable block descriptors */
	cdb[2] = 0x1D;		/* element address assignment page */
	cdb[4] = 24;
        pause_mode = 1;
        nretries = 0;

        /* IBM library in pause mode  */ 
        while (pause_mode && nretries<=900) { 
	     rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 6, buf, 24,
		 sense, 38, SCSI_IN, &nb_sense_ret, &msgaddr);
             if (rc < 0) {
               if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04)  && (sense[13] == -0X7B )) {
                 sleep(60);
                 pause_mode = 1;
               } else  {
                 pause_mode = 0;
               }
             } else {
                 pause_mode = 0;
             }
             nretries++;
        }

	if (rc < 0) {
		save_error (rc, nb_sense_ret, sense, msgaddr);
		return (-1);
	}
	robot_info->transport_start = buf[6] * 256 + buf[7];
	robot_info->transport_count = buf[8] * 256 + buf[9];
	robot_info->slot_start = buf[10] * 256 + buf[11];
	robot_info->slot_count = buf[12] * 256 + buf[13];
	robot_info->port_start = buf[14] * 256 + buf[15];
	robot_info->port_count = buf[16] * 256 + buf[17];
	robot_info->device_start = buf[18] * 256 + buf[19];
	robot_info->device_count = buf[20] * 256 + buf[21];
	return (0);
}

int smc_read_elem_status(
	const int fd,
	const char *const rbtdev,
	const int type,
	const int start,
	const int nbelem,
	struct smc_element_info element_info[])
{
	char func[16];

	strncpy(func, "read_elem_statu", sizeof(func));
	func[sizeof(func) - 1] = '\0';

	return get_element_info (0xB8, fd, rbtdev, type, start, nbelem, element_info);
}

int smc_find_cartridgeWithoutSendVolumeTag (
	const int fd,
	const char *const rbtdev,
	const char *const find_template,
	const int type,
	const int start,
	const int nbelem,
	struct smc_element_info element_info[])
{
	static char err_msgbuf[132];
	int nbFound = 0;
	char func[16];
	int i;
	struct smc_element_info *inventory_info; 
	char *msgaddr;
	const int patternMatching = strchr (find_template, '*') || strchr (find_template, '?');
	struct robot_info robot_info;
	int tot_nbelem = 0;
	int nbElementsInReport = 0;

	strncpy(func, "findWithoutVT", sizeof(func));
	func[sizeof(func) - 1] = '\0';

	{
		const int smc_get_geometry_rc = smc_get_geometry (fd, rbtdev, &robot_info);
		if(smc_get_geometry_rc) {
			return smc_get_geometry_rc;
		}
	}

	tot_nbelem = robot_info.transport_count + robot_info.slot_count +
		robot_info.port_count + robot_info.device_count;

	if ((inventory_info = (struct smc_element_info *)malloc (tot_nbelem * sizeof(struct smc_element_info))) == NULL) {
		serrno = errno;
		sprintf (err_msgbuf, "malloc error: %s", strerror(errno));
		msgaddr = err_msgbuf;
		save_error (-1, 0, NULL, msgaddr);
		return (-1);
	}

	nbElementsInReport = smc_read_elem_status (fd, rbtdev, type, start, tot_nbelem, inventory_info);
	if(0 > nbElementsInReport) {
		free (inventory_info);
		return (nbElementsInReport);
	}
	for (i = 0 ; i < nbElementsInReport && nbFound < nbelem; i++) {
		if (inventory_info[i].state & 0x1) {
			if (patternMatching) {
				if (vmatch (find_template, inventory_info[i].name) == 0) {
					memcpy (&element_info[nbFound], &inventory_info[i],
						sizeof(struct smc_element_info));
					nbFound++;
				}
			} else {
				if (strcmp (find_template, inventory_info[i].name) == 0) {
					memcpy (element_info, &inventory_info[i],
						sizeof(struct smc_element_info));
					nbFound = 1;
					break;
				}
			}
		}
	}
	free (inventory_info);
	return (nbFound);
}

int smc_find_cartridge(
	const int fd,
	const char *const rbtdev,
	const char *const find_template,
	const int type,
	const int start,
	const int nbelem,
	struct smc_element_info element_info[])
{
	unsigned char cdb[12];
	char func[16];
	const char *msgaddr;
	int nb_sense_ret;
	char plist[40];
	int rc;
	char sense[MAXSENSE];
        int pause_mode = 1;
        int nretries = 0;
        char *smcLibraryType;

	strncpy(func, "findWithVT", sizeof(func));
	func[sizeof(func) - 1] = '\0';
        
        /* Skip the 0xB6 cdb command if the tape library is Spectra like */
        smcLibraryType = getconfent_fromfile(PATH_CONF,"SMC","LIBRARY_TYPE",0);
        if (NULL != smcLibraryType &&
            0 == strcasecmp(smcLibraryType,"SPECTRA")) {
          rc = smc_find_cartridgeWithoutSendVolumeTag (fd, rbtdev, find_template, type, start, nbelem,
                                    element_info);
          if (rc >= 0)
            return (rc);
          return (-1);  
        }

	memset (cdb, 0, sizeof(cdb));
	cdb[0] = 0xB6;		/* send volume tag */
	cdb[1] = type;
	cdb[2] = start >> 8;
	cdb[3] = start & 0xFF;
	cdb[5] = 5;
	cdb[9] = 40;
	memset (plist, 0, sizeof(plist));
	strcpy (plist, find_template);
   
       /* IBM library in pause mode  */ 
        while (pause_mode && nretries <= 900) {
          rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 12, (unsigned char*)plist, 40,
                           sense, 38, SCSI_OUT, &nb_sense_ret, &msgaddr);
            if (rc < 0) {
              if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04)  && (sense[13] == -0X7B )) {
                 sleep(60);
                 pause_mode = 1;
              } else  {
                 pause_mode = 0; 
              }
            } else {
                pause_mode = 0;
            }
            nretries++;
        }

	if (rc < 0) {
                save_error (rc, nb_sense_ret, sense, msgaddr);
		if (rc == -4 && nb_sense_ret >= 14 && (sense[2] & 0xF) == 5) {
			rc = smc_find_cartridgeWithoutSendVolumeTag (fd, rbtdev, find_template, type,
			    start, nbelem, element_info);
			if (rc >= 0)
				return (rc);
		}
		return (-1);
	}
	return get_element_info (0xB5, fd, rbtdev, type, start, nbelem, element_info);
}


/* SCSI 3 additional sense code and additional sense qualifier */
struct scsierr_codact {
	unsigned char sensekey;
	unsigned char asc;
	unsigned char ascq;
	short action;
	const char *txt;
};
static struct scsierr_codact scsierr_acttbl[] = {
    {0x02, 0x04, 0x00, RBT_FAST_RETRY, "Logical Unit Not Ready, Cause Not Reportable"},
    {0x02, 0x04, 0x01, RBT_FAST_RETRY, "Logical Unit Is In Process of Becoming Ready"},
    {0x02, 0x04, 0x02, RBT_NORETRY, "Logical Unit Not Ready, initialization required"},
    {0x02, 0x04, 0x03, RBT_NORETRY, "Logical Unit Not Ready, Manual Intervention Required"},
    {0x0B, 0x08, 0x00, RBT_NORETRY, "Logical Unit Communication Failure"},
    {0x0B, 0x08, 0x01, RBT_NORETRY, "Logical Unit Communication Time-out"},
    {0x05, 0x1A, 0x00, RBT_NORETRY, "Parameter List Length Error"},
    {0x05, 0x20, 0x00, RBT_NORETRY, "Invalid Command Operation Code"},
    {0x05, 0x21, 0x01, RBT_NORETRY, "Invalid Element Address"},
    {0x05, 0x24, 0x00, RBT_NORETRY, "Invalid field in CDB"},
    {0x05, 0x25, 0x00, RBT_NORETRY, "Logical Unit Not Supported"},
    {0x05, 0x26, 0x00, RBT_NORETRY, "Invalid field in Parameter List"},
    {0x05, 0x26, 0x01, RBT_NORETRY, "Parameter Not Supported"},
    {0x05, 0x26, 0x02, RBT_NORETRY, "Parameter Value Invalid"},
    {0x06, 0x28, 0x00, RBT_FAST_RETRY, "Not Ready to Ready Transition"},
    {0x06, 0x28, 0x01, RBT_FAST_RETRY, "Import or Export Element Accessed"},
    {0x06, 0x29, 0x00, RBT_FAST_RETRY, "Power On, Reset, or Bus Device Reset Occurred"},
    {0x06, 0x2A, 0x01, RBT_FAST_RETRY, "Mode Parameters Changed"},
    {0x05, 0x30, 0x00, RBT_NORETRY, "Incompatible Medium Installed"},
    {0x00, 0x30, 0x03, RBT_FAST_RETRY, "Cleaning Cartridge Installed"},
    {0x05, 0x39, 0x00, RBT_NORETRY, "Saving Parameters Not Supported"},
    {0x05, 0x3A, 0x00, RBT_XTRA_PROC, "Medium Not Present"},
    {0x05, 0x3B, 0x0D, RBT_XTRA_PROC, "Medium Destination Element Full"},
    {0x05, 0x3B, 0x0E, RBT_XTRA_PROC, "Medium Source Element Empty"},
    {0x04, 0x40, 0x01, RBT_NORETRY, "Hardware Error, General"},
    {0x04, 0x40, 0x02, RBT_NORETRY, "Hardware Error, Tape Transport"},
    {0x04, 0x40, 0x03, RBT_NORETRY, "Hardware Error, CAP"},
    {0x0B, 0x43, 0x00, RBT_NORETRY, "Message Error"},
    {0x02, 0x44, 0x00, RBT_NORETRY, "Internal Target Failure"},
    {0x0B, 0x45, 0x00, RBT_NORETRY, "Select or Reselect Failure"},
    {0x0B, 0x47, 0x00, RBT_NORETRY, "SCSI Parity Error"},
    {0x0B, 0x48, 0x00, RBT_NORETRY, "Initiator Detected Error"},
    {0x02, 0x4C, 0x00, RBT_NORETRY, "Logical Unit Failed Self-Configuration"},
    {0x05, 0x4E, 0x00, RBT_NORETRY, "Overlapped Commands Attempted"},
    {0x05, 0x53, 0x02, RBT_NORETRY, "Medium Removal Prevented"},
    {0x06, 0x54, 0x00, RBT_NORETRY, "SCSI To Host System Interface Failure"},
    {0x02, 0x5A, 0x01, RBT_NORETRY, "Operator Medium Removal Request"}
};

static const char* action_to_str(const short action) {
	switch(action) {
	case RBT_FAST_RETRY: return "RBT_FAST_RETRY";
	case RBT_NORETRY   : return "RBT_NORETRY";
	case RBT_XTRA_PROC : return "RBT_XTRA_PROC";
	default            : return "UNKNOWN";
	}
}

int smc_lasterror(
	struct smc_status *const smc_stat,
	const char **const msgaddr)
{
	unsigned int i;
	char func[16];
	strncpy (func, "lasterror", sizeof(func));
	func[sizeof(func) - 1] = '\0';

	rmc_logit(func, "Function entered:"
		" asc=%d ascq=%d save_errno=%d rc=%d sensekey=%d skvalid=%d\n",
		smc_status.asc, smc_status.ascq, smc_status.save_errno,
		smc_status.rc, smc_status.sensekey, smc_status.skvalid);

	smc_stat->rc = smc_status.rc;
	smc_stat->skvalid = smc_status.skvalid;
	*msgaddr = smc_msgaddr;
	if ((smc_status.rc == -1 || smc_status.rc == -2) &&
	    smc_status.save_errno == EBUSY)
		return (EBUSY);
	if (! smc_status.skvalid)
		return (RBT_NORETRY);
	smc_stat->sensekey = smc_status.sensekey;
	smc_stat->asc = smc_status.asc;
	smc_stat->ascq = smc_status.ascq;
	for (i = 0; i < sizeof(scsierr_acttbl)/sizeof(struct scsierr_codact); i++) {
		if (smc_status.asc == scsierr_acttbl[i].asc &&
		    smc_status.ascq == scsierr_acttbl[i].ascq &&
		    smc_status.sensekey == scsierr_acttbl[i].sensekey) {
			const char *const action_str =
				action_to_str(scsierr_acttbl[i].action);
			*msgaddr = scsierr_acttbl[i].txt;

			rmc_logit(func, "Entry found in scsierr_acttbl:"
				" action_str=%s\n", action_str);

			return (scsierr_acttbl[i].action);
		}
	}

	rmc_logit(func, "No matching entry in scsierr_acttbl\n");

	return (RBT_NORETRY);
}

int smc_move_medium(
	const int fd,
	const char *const rbtdev,
	const int from,
	const int to,
	const int invert)
{
	unsigned char cdb[12];
	char func[16];
	const char *msgaddr;
	int nb_sense_ret;
	int rc;
	char sense[MAXSENSE];
        int pause_mode = 1;
        int nretries = 0;         

	strncpy(func, "move_medium", sizeof(func));
	func[sizeof(func) - 1] = '\0';

	memset (cdb, 0, sizeof(cdb));
	cdb[0] = 0xA5;		/* move medium */
	cdb[4] = from >> 8;
	cdb[5] = from & 0xFF;
	cdb[6] = to >> 8;
	cdb[7] = to & 0xFF;
	cdb[10] = invert;
        
        while (pause_mode) {
	     rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 12, NULL, 0,
		sense, 38, SCSI_NONE, &nb_sense_ret, &msgaddr);
            if (rc < 0) {
              if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04)  && (sense[13] == -0X7B )) {
                 sleep(60);
                 pause_mode = 1;
              } else  {
                 pause_mode = 0;
              }
            } else {
                 pause_mode = 0;
            }
            nretries++;
        }

	if (rc < 0) {
		save_error (rc, nb_sense_ret, sense, msgaddr);
		return (-1);
	}
	return (0);
}

static int rmc_usrmsg(
	const int rpfd,
	const char *func,
	const char *const msg,
	...)
{
	va_list args;
	char prtbuf[RMC_PRTBUFSZ];
	const int save_errno = errno;

	va_start (args, msg);
	snprintf (prtbuf, sizeof(prtbuf), "%s: ", func);
	prtbuf[sizeof(prtbuf) - 1] = '\0';
	{
		const size_t nbBytesUsed = strlen (prtbuf);

		/* If there is still space in the print buffer */
		if(nbBytesUsed < (sizeof(prtbuf))) {
			const size_t nbBytesRemaining = sizeof(prtbuf) -
				nbBytesUsed;
			char *const p = prtbuf + nbBytesUsed;
			vsnprintf (p, nbBytesRemaining, msg, args);
			prtbuf[sizeof(prtbuf) - 1] = '\0';
		}
	}
	rmc_sendrep (rpfd, MSG_ERR, "%s", prtbuf);
	va_end (args);
	errno = save_errno;
	return (0);
}

int smc_dismount (
	const int rpfd,
	const int fd,
	const char *const loader,
	struct robot_info *const robot_info,
	const int drvord,
	const char *const vid)
{
	const unsigned int max_element_status_reads = 20;
	const unsigned int dismount_status_read_delay = 1; /* In seconds */
	unsigned int nb_element_status_reads = 0;
	int drive_not_unloaded = 1;
	struct smc_element_info drive_element_info;
	char func[16];
	const char *msgaddr = 0;
	struct smc_status smc_status;
 
	strncpy (func, "smc_dismount", sizeof(func));
	func[sizeof(func) - 1] = '\0';
	memset(&smc_status, '\0', sizeof(smc_status));

	/* IBM libraries sometimes disagree with the eject of their drives. */
	/* Sometimes the access bit of the result of Read Element Status    */
	/* (XB8) indicates the gripper cannot access the tape even though   */
	/* the eject was successful.  Reading the element status at a later */
	/* point in time eventually indicates the tape is accessible.       */
	while(drive_not_unloaded && nb_element_status_reads < max_element_status_reads) {
		if (0 > smc_read_elem_status (fd, loader, 4, robot_info->device_start+drvord,
		    	1, &drive_element_info)) {
			const int smc_error = smc_lasterror (&smc_status, &msgaddr);
			rmc_usrmsg ( rpfd, func, SR020, "read_elem_status", msgaddr);
			return (smc_error);
		}
		if (0 == (drive_element_info.state & 0x1)) {
			rmc_usrmsg ( rpfd, func, SR018, "demount", vid, drvord, "Medium Not Present");
			return (RBT_OK);
		}

		drive_not_unloaded = (0 == (drive_element_info.state & 0x8));
		if (drive_not_unloaded) {
			rmc_usrmsg ( rpfd, func, "read_elem_status of %s on drive %d detected Drive Not Unloaded\n", vid, drvord);
		}

		nb_element_status_reads++;

		if(nb_element_status_reads < max_element_status_reads) {
			sleep(dismount_status_read_delay);
		}
	}
	if(drive_not_unloaded) {
		rmc_usrmsg ( rpfd, func, SR018, "demount", vid, drvord, "Drive Not Unloaded");
		return (RBT_UNLD_DMNT);
	}

	if (*vid && strcmp (drive_element_info.name, vid)) {
		rmc_usrmsg ( rpfd, func, SR009, vid, drive_element_info.name);
		return (RBT_NORETRY);
	}
	if (0 > smc_move_medium (fd, loader, robot_info->device_start+drvord,
	    drive_element_info.source_address, (drive_element_info.flags & 0x40) ? 1 : 0)) {
		const int smc_error = smc_lasterror (&smc_status, &msgaddr);
		rmc_usrmsg ( rpfd, func, SR018, "demount", vid, drvord, msgaddr);
		return (smc_error);
	}
    /* check that the vid is in a slot before returning */
    while (1) {   
          struct smc_element_info vol_element_info;
          if (0 > smc_find_cartridge (fd, loader, drive_element_info.name, 0, 0, 1, &vol_element_info)) {
              const int smc_error = smc_lasterror (&smc_status, &msgaddr);
              rmc_usrmsg ( rpfd, func, SR017, "find_cartridge", drive_element_info.name, msgaddr);
              return (smc_error);
          }
         
          /* vid is in a storage slot */  
          if (vol_element_info.element_type == 2) break; 
          /* give time for the tape enter the slot */
          sleep (2);
    }

	return (0);
}

int smc_export (
	const int rpfd,
	const int fd,
	const char *const loader,
	struct robot_info *const robot_info,
	const char *const vid)
{
        struct smc_element_info element_info;
	char func[16];
	int i = 0;
        struct smc_element_info *impexp_info;
	const char *msgaddr = NULL;
	int nbelem = 0;
	struct smc_status smc_status;
 
	strncpy (func, "smc_export", sizeof(func));
	func[sizeof(func) - 1] = '\0';

	{
		const int smc_find_cartridge_rc = smc_find_cartridge (fd, loader, vid, 0, 0, 1, &element_info);
		if (0 > smc_find_cartridge_rc) {
			const int smc_lasterror_rc = smc_lasterror (&smc_status, &msgaddr);
			rmc_usrmsg ( rpfd, func, SR017, "find_cartridge", vid, msgaddr);
			return (smc_lasterror_rc);
		}
		if (0 == smc_find_cartridge_rc) {
			rmc_usrmsg ( rpfd, func, SR017, "export", vid, "volume not in library");
			return (RBT_NORETRY);
		}
	}
	if (element_info.element_type != 2) {
		rmc_usrmsg ( rpfd, func, SR017, "export", vid, "volume in use");
		return (RBT_SLOW_RETRY);
	}
	/* look for a free export slot */

	nbelem = robot_info->port_count;
	if ((impexp_info = (struct smc_element_info *)malloc (nbelem * sizeof(struct smc_element_info))) == NULL) {
		rmc_usrmsg ( rpfd, func, SR012);
		return (RBT_NORETRY);
	}

	{
		int foundAFreeExportSlot = 0;
		const int nbElementsInReport = smc_read_elem_status (fd, loader, 3, robot_info->port_start,
	    		nbelem, impexp_info);
		if (0 > nbElementsInReport) {
			const int smc_lasterror_rc = smc_lasterror (&smc_status, &msgaddr);
			rmc_usrmsg ( rpfd, func, SR020, "read_elem_status", msgaddr);
			free (impexp_info);
			return (smc_lasterror_rc);
		}
		for (i = 0; i < nbElementsInReport; i++) {
			if (((impexp_info+i)->state & 0x1) == 0) {
				foundAFreeExportSlot = 1;
				break;
			}
		}
		if (!foundAFreeExportSlot) {
			rmc_usrmsg ( rpfd, func, SR013);
			free (impexp_info);
			return (RBT_NORETRY);
		}
	}

	{
		const int smc_move_medium_rc = smc_move_medium (fd, loader, element_info.element_address,
	    		(impexp_info+i)->element_address, 0);
		if (0 > smc_move_medium_rc) {
			const int smc_lasterror_rc = smc_lasterror (&smc_status, &msgaddr);
			rmc_usrmsg ( rpfd, func, SR017, "export", vid, msgaddr);
			free (impexp_info);
			return (smc_lasterror_rc);
		}
	}

	free (impexp_info);
	return (0);
}

int smc_import (
	const int rpfd,
	const int fd,
	const char *const loader,
	struct robot_info *const robot_info,
	const char *const vid)
{
        int c;
	int device_start;
        struct smc_element_info *element_info;
	char func[16];
	int i, j;
	const char *msgaddr;
	int nbelem;
	int port_start;
	int slot_start;
	struct smc_status smc_status;
 
	strncpy (func, "smc_import", sizeof(func));
	func[sizeof(func) - 1] = '\0';

	nbelem = robot_info->transport_count + robot_info->slot_count +
		 robot_info->port_count + robot_info->device_count;
	if ((element_info = (struct smc_element_info *)malloc (nbelem * sizeof(struct smc_element_info))) == NULL) {
		rmc_usrmsg ( rpfd, func, SR012);
		return (RBT_NORETRY);
	}

	/* get inventory */

	if ((c = smc_read_elem_status (fd, loader, 0, 0, nbelem, element_info)) < 0) {
		c = smc_lasterror (&smc_status, &msgaddr);
		rmc_usrmsg ( rpfd, func, SR020, "read_elem_status", msgaddr);
		free (element_info);
		return (c);
	}
	for (i = 0; i < c; i++)
		if ((element_info+i)->element_type == 2) break;
	slot_start = i;
	for (i = 0; i < c; i++)
		if ((element_info+i)->element_type == 3) break;
	port_start = i;
	for (i = 0; i < c; i++)
		if ((element_info+i)->element_type == 4) break;
	device_start = i;

	/* mark home slots of cartridges currently on drives as non free */

	for (i = device_start; i < device_start+robot_info->device_count; i++) {
		if (((element_info+i)->state & 0x1) == 0) continue;
		for (j = slot_start; j < slot_start+robot_info->slot_count; j++)
			if ((element_info+i)->source_address ==
				(element_info+j)->element_address) break;
		(element_info+j)->state |= 1;
	}

	/* loop on all import slots */

	for (i = port_start; i < port_start+robot_info->port_count; i++) {
		if (*vid && strcmp (vid, (element_info+i)->name)) continue;
		if (*vid || (*vid == '\0' && ((element_info+i)->state & 2))) {

			/* find a free storage slot */

			for (j = slot_start; j < slot_start+robot_info->slot_count; j++)
				if (((element_info+j)->state & 0x1) == 0) break;
			if (j >= slot_start+robot_info->slot_count) {
				rmc_usrmsg ( rpfd, func, SR015);
				free (element_info);
				return (RBT_NORETRY);
			}

			if ((c = smc_move_medium (fd, loader, (element_info+i)->element_address,
			    (element_info+j)->element_address, 0)) < 0) {
				c = smc_lasterror (&smc_status, &msgaddr);
				rmc_usrmsg ( rpfd, func, SR017, "import",
				    (element_info+i)->name, msgaddr);
				free (element_info);
				return (c);
			}
			if (*vid || c) break;
			(element_info+j)->state |= 1;	/* dest slot is now full */
		}
	}
	free (element_info);
	return (c);
}

int smc_mount (
	const int rpfd,
	const int fd,
	const char *const loader,
	struct robot_info *const robot_info,
	const int drvord,
	const char *const vid,
	const int invert)
{
    int c;
    struct smc_element_info element_info;
	char func[16];
	const char *msgaddr;
	struct smc_status smc_status;
 
	strncpy (func, "smc_mount", sizeof(func));
	func[sizeof(func) - 1] = '\0';

	if ((c = smc_find_cartridge (fd, loader, vid, 0, 0, 1, &element_info)) < 0) {
		c = smc_lasterror (&smc_status, &msgaddr);
		rmc_usrmsg ( rpfd, func, SR017, "find_cartridge", vid, msgaddr);
		return (c);
	}
	if (c == 0) {
		rmc_usrmsg ( rpfd, func, SR018, "mount", vid, drvord, "volume not in library");
		return (RBT_NORETRY);
	}
	if (element_info.element_type != 2) {

                /* compare requested and replied vid   */
                rmc_usrmsg ( rpfd, func, "Asked for %s, got reply for %s\n", 
                        vid, element_info.name );

                /* detail on a tape's current location */
                switch (element_info.element_type) {

                case 1:
                        rmc_usrmsg ( rpfd, func, "Location: medium transport element (0x%x)\n", 
                                element_info.element_type );
                        break;
                case 2:                        
                        /* normal case: in its home slot, not possible inside the if */
                        break;
                case 3:
                        rmc_usrmsg ( rpfd, func, "Location: import/export element (0x%x)\n", 
                                element_info.element_type );
                        break;
                case 4:
                        rmc_usrmsg ( rpfd, func, "Location: data transfer element (0x%x)\n", 
                                element_info.element_type );
                        break;
                default:
                        rmc_usrmsg ( rpfd, func, "Location: unknown (0x%x)\n", 
                                element_info.element_type );
                }

                rmc_usrmsg ( rpfd, func, SR018, "mount", vid, drvord, "volume in use");
		return (RBT_SLOW_RETRY);
	}
	if ((c = smc_move_medium (fd, loader, element_info.element_address,
	    robot_info->device_start+drvord, invert)) < 0) {
		c = smc_lasterror (&smc_status, &msgaddr);
		rmc_usrmsg ( rpfd, func, SR018, "mount", vid, drvord, msgaddr);
		return (c);
	}
	return (0);
}