void
st_close (st_parameter_close *clp)
{
  close_status status;
  gfc_unit *u;
#if !HAVE_UNLINK_OPEN_FILE
  char * path;

  path = NULL;
#endif

  library_start (&clp->common);

  status = !(clp->common.flags & IOPARM_CLOSE_HAS_STATUS) ? CLOSE_UNSPECIFIED :
    find_option (&clp->common, clp->status, clp->status_len,
		 status_opt, "Bad STATUS parameter in CLOSE statement");

  if ((clp->common.flags & IOPARM_LIBRETURN_MASK) != IOPARM_LIBRETURN_OK)
  {
    library_end ();
    return;
  }

  u = find_unit (clp->common.unit);
  if (u != NULL)
    {
      if (u->flags.status == STATUS_SCRATCH)
	{
	  if (status == CLOSE_KEEP)
	    generate_error (&clp->common, LIBERROR_BAD_OPTION,
			    "Can't KEEP a scratch file on CLOSE");
#if !HAVE_UNLINK_OPEN_FILE
	  path = (char *) gfc_alloca (u->file_len + 1);
          unpack_filename (path, u->file, u->file_len);
#endif
	}
      else
	{
	  if (status == CLOSE_DELETE)
            {
#if HAVE_UNLINK_OPEN_FILE
	      delete_file (u);
#else
	      path = (char *) gfc_alloca (u->file_len + 1);
              unpack_filename (path, u->file, u->file_len);
#endif
            }
	}

      close_unit (u);

#if !HAVE_UNLINK_OPEN_FILE
      if (path != NULL)
        unlink (path);
#endif
    }

  /* CLOSE on unconnected unit is legal and a no-op: F95 std., 9.3.5. */ 
  library_end ();
}
Пример #2
0
static void
already_open (gfc_unit * u, unit_flags * flags)
{
  if (ioparm.file == NULL)
    {
      edit_modes (u, flags);
      return;
    }

  /* If the file is connected to something else, close it and open a
     new unit.  */

  if (!compare_file_filename (u->s, ioparm.file, ioparm.file_len))
    {
      if (close_unit (u))
	{
	  generate_error (ERROR_OS, "Error closing file in OPEN statement");
	  return;
	}

      new_unit (flags);
      return;
    }

  edit_modes (u, flags);
}
Пример #3
0
/**
 *   close up
 */
void unit_mgr_close() {
  int i;

  for (i = 0; i < unit_count; i++) {
    if (units[i].status == unit_loaded) {
      close_unit(i);
    }
  }
  unit_count = 0;
  if (units) {
    free(units);
  }
}
Пример #4
0
Файл: open.c Проект: delkon/gcc
gfc_unit *
new_unit (st_parameter_open *opp, gfc_unit *u, unit_flags * flags)
{
  gfc_unit *u2;
  stream *s;
  char tmpname[5 /* fort. */ + 10 /* digits of unit number */ + 1 /* 0 */];

  /* Change unspecifieds to defaults.  Leave (flags->action ==
     ACTION_UNSPECIFIED) alone so open_external() can set it based on
     what type of open actually works.  */

  if (flags->access == ACCESS_UNSPECIFIED)
    flags->access = ACCESS_SEQUENTIAL;

  if (flags->form == FORM_UNSPECIFIED)
    flags->form = (flags->access == ACCESS_SEQUENTIAL)
      ? FORM_FORMATTED : FORM_UNFORMATTED;

  if (flags->async == ASYNC_UNSPECIFIED)
    flags->async = ASYNC_NO;

  if (flags->status == STATUS_UNSPECIFIED)
    flags->status = STATUS_UNKNOWN;

  /* Checks.  */

  if (flags->delim == DELIM_UNSPECIFIED)
    flags->delim = DELIM_NONE;
  else
    {
      if (flags->form == FORM_UNFORMATTED)
	{
	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
			  "DELIM parameter conflicts with UNFORMATTED form in "
			  "OPEN statement");
	  goto fail;
	}
    }

  if (flags->blank == BLANK_UNSPECIFIED)
    flags->blank = BLANK_NULL;
  else
    {
      if (flags->form == FORM_UNFORMATTED)
	{
	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
			  "BLANK parameter conflicts with UNFORMATTED form in "
			  "OPEN statement");
	  goto fail;
	}
    }

  if (flags->pad == PAD_UNSPECIFIED)
    flags->pad = PAD_YES;
  else
    {
      if (flags->form == FORM_UNFORMATTED)
	{
	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
			  "PAD parameter conflicts with UNFORMATTED form in "
			  "OPEN statement");
	  goto fail;
	}
    }

  if (flags->decimal == DECIMAL_UNSPECIFIED)
    flags->decimal = DECIMAL_POINT;
  else
    {
      if (flags->form == FORM_UNFORMATTED)
	{
	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
			  "DECIMAL parameter conflicts with UNFORMATTED form "
			  "in OPEN statement");
	  goto fail;
	}
    }

  if (flags->encoding == ENCODING_UNSPECIFIED)
    flags->encoding = ENCODING_DEFAULT;
  else
    {
      if (flags->form == FORM_UNFORMATTED)
	{
	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
			  "ENCODING parameter conflicts with UNFORMATTED form in "
			  "OPEN statement");
	  goto fail;
	}
    }

  /* NB: the value for ROUND when it's not specified by the user does not
         have to be PROCESSOR_DEFINED; the standard says that it is
	 processor dependent, and requires that it is one of the
	 possible value (see F2003, 9.4.5.13).  */
  if (flags->round == ROUND_UNSPECIFIED)
    flags->round = ROUND_PROCDEFINED;
  else
    {
      if (flags->form == FORM_UNFORMATTED)
	{
	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
			  "ROUND parameter conflicts with UNFORMATTED form in "
			  "OPEN statement");
	  goto fail;
	}
    }

  if (flags->sign == SIGN_UNSPECIFIED)
    flags->sign = SIGN_PROCDEFINED;
  else
    {
      if (flags->form == FORM_UNFORMATTED)
	{
	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
			  "SIGN parameter conflicts with UNFORMATTED form in "
			  "OPEN statement");
	  goto fail;
	}
    }

  if (flags->position != POSITION_ASIS && flags->access == ACCESS_DIRECT)
   {
     generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
                     "ACCESS parameter conflicts with SEQUENTIAL access in "
                     "OPEN statement");
     goto fail;
   }
  else
   if (flags->position == POSITION_UNSPECIFIED)
     flags->position = POSITION_ASIS;

  if (flags->access == ACCESS_DIRECT
      && (opp->common.flags & IOPARM_OPEN_HAS_RECL_IN) == 0)
    {
      generate_error (&opp->common, LIBERROR_MISSING_OPTION,
		      "Missing RECL parameter in OPEN statement");
      goto fail;
    }

  if ((opp->common.flags & IOPARM_OPEN_HAS_RECL_IN) && opp->recl_in <= 0)
    {
      generate_error (&opp->common, LIBERROR_BAD_OPTION,
		      "RECL parameter is non-positive in OPEN statement");
      goto fail;
    }

  switch (flags->status)
    {
    case STATUS_SCRATCH:
      if ((opp->common.flags & IOPARM_OPEN_HAS_FILE) == 0)
	{
	  opp->file = NULL;
	  break;
	}

      generate_error (&opp->common, LIBERROR_BAD_OPTION,
		      "FILE parameter must not be present in OPEN statement");
      goto fail;

    case STATUS_OLD:
    case STATUS_NEW:
    case STATUS_REPLACE:
    case STATUS_UNKNOWN:
      if ((opp->common.flags & IOPARM_OPEN_HAS_FILE))
	break;

      opp->file = tmpname;
      opp->file_len = snprintf(opp->file, sizeof (tmpname), "fort.%d", 
			       (int) opp->common.unit);
      break;

    default:
      internal_error (&opp->common, "new_unit(): Bad status");
    }

  /* Make sure the file isn't already open someplace else.
     Do not error if opening file preconnected to stdin, stdout, stderr.  */

  u2 = NULL;
  if ((opp->common.flags & IOPARM_OPEN_HAS_FILE) != 0)
    u2 = find_file (opp->file, opp->file_len);
  if (u2 != NULL
      && (options.stdin_unit < 0 || u2->unit_number != options.stdin_unit)
      && (options.stdout_unit < 0 || u2->unit_number != options.stdout_unit)
      && (options.stderr_unit < 0 || u2->unit_number != options.stderr_unit))
    {
      unlock_unit (u2);
      generate_error (&opp->common, LIBERROR_ALREADY_OPEN, NULL);
      goto cleanup;
    }

  if (u2 != NULL)
    unlock_unit (u2);

  /* Open file.  */

  s = open_external (opp, flags);
  if (s == NULL)
    {
      char *path, *msg;
      size_t msglen;
      path = (char *) gfc_alloca (opp->file_len + 1);
      msglen = opp->file_len + 51;
      msg = (char *) gfc_alloca (msglen);
      unpack_filename (path, opp->file, opp->file_len);

      switch (errno)
	{
	case ENOENT: 
	  snprintf (msg, msglen, "File '%s' does not exist", path);
	  break;

	case EEXIST:
	  snprintf (msg, msglen, "File '%s' already exists", path);
	  break;

	case EACCES:
	  snprintf (msg, msglen, 
		    "Permission denied trying to open file '%s'", path);
	  break;

	case EISDIR:
	  snprintf (msg, msglen, "'%s' is a directory", path);
	  break;

	default:
	  msg = NULL;
	}

      generate_error (&opp->common, LIBERROR_OS, msg);
      goto cleanup;
    }

  if (flags->status == STATUS_NEW || flags->status == STATUS_REPLACE)
    flags->status = STATUS_OLD;

  /* Create the unit structure.  */

  u->file = xmalloc (opp->file_len);
  if (u->unit_number != opp->common.unit)
    internal_error (&opp->common, "Unit number changed");
  u->s = s;
  u->flags = *flags;
  u->read_bad = 0;
  u->endfile = NO_ENDFILE;
  u->last_record = 0;
  u->current_record = 0;
  u->mode = READING;
  u->maxrec = 0;
  u->bytes_left = 0;
  u->saved_pos = 0;

  if (flags->position == POSITION_APPEND)
    {
      if (sseek (u->s, 0, SEEK_END) < 0)
	generate_error (&opp->common, LIBERROR_OS, NULL);
      u->endfile = AT_ENDFILE;
    }

  /* Unspecified recl ends up with a processor dependent value.  */

  if ((opp->common.flags & IOPARM_OPEN_HAS_RECL_IN))
    {
      u->flags.has_recl = 1;
      u->recl = opp->recl_in;
      u->recl_subrecord = u->recl;
      u->bytes_left = u->recl;
    }
  else
    {
      u->flags.has_recl = 0;
      u->recl = max_offset;
      if (compile_options.max_subrecord_length)
	{
	  u->recl_subrecord = compile_options.max_subrecord_length;
	}
      else
	{
	  switch (compile_options.record_marker)
	    {
	    case 0:
	      /* Fall through */
	    case sizeof (GFC_INTEGER_4):
	      u->recl_subrecord = GFC_MAX_SUBRECORD_LENGTH;
	      break;

	    case sizeof (GFC_INTEGER_8):
	      u->recl_subrecord = max_offset - 16;
	      break;

	    default:
	      runtime_error ("Illegal value for record marker");
	      break;
	    }
	}
    }

  /* If the file is direct access, calculate the maximum record number
     via a division now instead of letting the multiplication overflow
     later.  */

  if (flags->access == ACCESS_DIRECT)
    u->maxrec = max_offset / u->recl;
  
  if (flags->access == ACCESS_STREAM)
    {
      u->maxrec = max_offset;
      u->recl = 1;
      u->bytes_left = 1;
      u->strm_pos = stell (u->s) + 1;
    }

  memmove (u->file, opp->file, opp->file_len);
  u->file_len = opp->file_len;

  /* Curiously, the standard requires that the
     position specifier be ignored for new files so a newly connected
     file starts out at the initial point.  We still need to figure
     out if the file is at the end or not.  */

  test_endfile (u);

  if (flags->status == STATUS_SCRATCH && opp->file != NULL)
    free (opp->file);
    
  if (flags->form == FORM_FORMATTED)
    {
      if ((opp->common.flags & IOPARM_OPEN_HAS_RECL_IN))
        fbuf_init (u, u->recl);
      else
        fbuf_init (u, 0);
    }
  else
    u->fbuf = NULL;

    
    
  return u;

 cleanup:

  /* Free memory associated with a temporary filename.  */

  if (flags->status == STATUS_SCRATCH && opp->file != NULL)
    free (opp->file);

 fail:

  close_unit (u);
  return NULL;
}
Пример #5
0
/*
 *	get_media - get the media mounted.
 *
 * entry   -
 *	 drive->mutex should be held.  This mutex
 *	 can be released during processing, but will be held
 *	 on return.
 *
 * returns -
 *	 0 - ok
 *	 1 - some sort of error, dispose of event
 *		Note: The catalog mutex is held on this condition.
 *	 2 - event was requeued.
 *	 -1 - same as 1 cept that the drive should be downed
 *
 *	 in all cases, dev_ent activity count will be incremented.
 */
int
get_media(
	library_t *library,
	drive_state_t *drive,
	robo_event_t *event,		/* the event (can be NULL) */
	struct CatalogEntry *ce)	/* catalog entry to be loaded */
{
	char *d_mess = drive->un->dis_mes[DIS_MES_NORM];
	dev_ent_t *un = drive->un;
	int status = 0;

	mutex_lock(&un->mutex);
	INC_ACTIVE(un);
	mutex_unlock(&un->mutex);

	/* is the media is already mounted */
	if (drive->status.b.full && (un->slot == ce->CeSlot)) {
		if (DBG_LVL(SAM_DBG_DEBUG))
			sam_syslog(LOG_DEBUG, "get_media:(%d)%s:%d.",
			    un->eq, __FILE__, __LINE__);
	} else {			/* get the media loaded */
		/*
		 * Make sure the source storage element has media in it.
		 * If the element is empty, external requests
		 * will be put back on the library's work list with the
		 * hope that it will be picked up later. Internal
		 * requests will return an error to the caller. If the in_use
		 * flag is not set, then the slot is
		 * "really" empty and the request will be disposed of.
		 */
		if (DBG_LVL(SAM_DBG_TMOVE))
			sam_syslog(LOG_DEBUG, "get_media:(%d)%s:%d.",
			    un->eq, __FILE__, __LINE__);

		if (!(ce->CeStatus & CES_occupied)) {
			/* Should this be put back on the library's list? */
			if ((ce->CeStatus & CES_inuse) && event != NULL &&
			    event->type != EVENT_TYPE_INTERNAL &&
			    !event->status.b.dont_reque) {
				event->next = NULL;
				add_to_end(library, event);
				/* Do not dispose of event */
				return (RET_GET_MEDIA_REQUEUED);
			}

			if (DBG_LVL(SAM_DBG_TMOVE))
				sam_syslog(LOG_DEBUG, "get_media:(%d)%s:%d.",
				    un->eq, __FILE__, __LINE__);
			return (RET_GET_MEDIA_DISPOSE);

		} else {
			status &= ~CES_occupied;
			(void) CatalogSetFieldByLoc(library->un->eq, ce->CeSlot,
			    0, CEF_Status, status, CES_occupied);
		}

		if (drive->status.b.full) {

			mutex_lock(&un->mutex);
			un->status.bits |= DVST_UNLOAD;
			mutex_unlock(&un->mutex);

			/*
			 * Save off what information we know about this volume
			 * before we spin down the drive and clear out the un.
			 */
			memmove(un->i.ViMtype, sam_mediatoa(un->type),
			    sizeof (un->i.ViMtype));
			memmove(un->i.ViVsn, un->vsn, sizeof (un->i.ViVsn));
			un->i.ViEq = un->fseq;
			un->i.ViSlot = un->slot;
			un->i.ViPart = 0;

			if (un->status.b.ready) {
				(void) spin_drive(drive, SPINDOWN, NOEJECT);
				sprintf(d_mess, "unloading %s", un->vsn);
			}

			mutex_lock(&un->mutex);
			close_unit(un, &drive->open_fd);
			un->status.bits = (DVST_REQUESTED | DVST_PRESENT) |
			    (un->status.bits & DVST_CLEANING);
			clear_un_fields(un);
			mutex_unlock(&un->mutex);

#if defined(USEDISMOUNT)
			sprintf(d_mess, "dismounting %s", un->vsn);
			if (dismount_media(library, drive) != MC_REQ_OK) {
				if (DBG_LVL(SAM_DBG_TMOVE))
					sam_syslog(LOG_DEBUG,
					    "get_media:(%d)%s:%d.",
					    un->eq, __FILE__, __LINE__);
				return (RET_GET_MEDIA_DOWN_DRIVE);
			}
#endif					/* defined(USEDISMOUNT) */


			/* clean up the old entries */
			if (*un->i.ViMtype != '\0')
				un->i.ViFlags |= VI_mtype;
			if (*un->i.ViVsn != '\0')
				un->i.ViFlags |= VI_vsn;
			CatalogVolumeUnloaded(&un->i, "");

			mutex_lock(&un->mutex);
			un->slot = ROBOT_NO_SLOT;
			un->mid = un->flip_mid = ROBOT_NO_SLOT;
			un->label_time = 0;
			mutex_unlock(&un->mutex);

			/* clear the drive information */
			drive->status.b.full = FALSE;
			drive->status.b.bar_code = FALSE;
		}
#if !defined(USEDISMOUNT)
		if (*un->i.ViVsn != '\0')
			sprintf(d_mess, "dismount %s/mount %s",
			    un->i.ViVsn, un->vsn);
		else
			sprintf(d_mess, "mount %s", ce->CeVsn);
#else
		sprintf(d_mess, "mount %s", ce->CeVsn);
#endif
		if (load_media(library, drive, ce, 0) != MC_REQ_OK) {
			/*
			 * Process error and return status to caller.
			 * Requeue the event if not internal otherwise
			 * return status to caller.
			 */
			req_comp_t err;
			int ret = RET_GET_MEDIA_DISPOSE;
			IBM_query_info_t *info;
			if (DBG_LVL(SAM_DBG_DEBUG))
				sam_syslog(LOG_DEBUG, "load of %s failed.",
				    ce->CeVsn);

			sprintf(d_mess, "mount of %s failed.", ce->CeVsn);
			if (DBG_LVL(SAM_DBG_TMOVE))
				sam_syslog(LOG_DEBUG, "get_media:(%d)%s:%d.",
				    un->eq, __FILE__, __LINE__);

			/*
			 * Attempt some sort of recovery:
			 * Check the state of the volume itself
			 */
			err = view_media(library, ce->CeBarCode, (void *)&info);
			if (err != MC_REQ_OK) {

				status |= CES_occupied;
				(void) CatalogSetFieldByLoc(library->un->eq,
				    ce->CeSlot, 0, CEF_Status, status, 0);

				/* If internal event, then dont requeue it */
				if (event == NULL ||
				    event->type == EVENT_TYPE_INTERNAL)
					ret = RET_GET_MEDIA_DISPOSE;
			} else if (info != NULL) {
				ushort_t vol_status, category;

				memcpy(&vol_status, &info->
				    data.expand_vol_data.volume_status[0],
				    sizeof (vol_status));
				memcpy(&category,
				    &info->data.expand_vol_data.cat_assigned[0],
				    sizeof (category));
				if (vol_status & MT_VLI)
					sam_syslog(LOG_INFO, "get_media(%d):"
					    " %s Present but inaccessible.",
					    LIBEQ, ce->CeVsn);
				if (vol_status & (MT_VM | MT_VQM | MT_VPM |
				    MT_VQD | MT_VPD))
					sam_syslog(LOG_INFO, "get_media(%d):"
					    " %s, Mounting/Dismounting "
					    " or queued.", LIBEQ, ce->CeVsn);
				if (vol_status & (MT_VQE | MT_VPE))
					sam_syslog(LOG_INFO, "get_media(%d):"
					    " %s, Ejecting.", LIBEQ,
					    ce->CeVsn);
				if (vol_status & MT_VMIS)
					sam_syslog(LOG_INFO, "get_media(%d):"
					    " %s, Misplaced.", LIBEQ,
					    ce->CeVsn);
				if (vol_status & MT_VUU)
					sam_syslog(LOG_INFO, "get_media(%d):"
					    " %s, Unreadable label.", LIBEQ,
					    ce->CeVsn);
				if (vol_status & MT_VMM)
					sam_syslog(LOG_INFO, "get_media(%d):"
					    " %s, Used during manual mode.",
					    LIBEQ, ce->CeVsn);
				if (vol_status & MT_VME)
					sam_syslog(LOG_INFO, "get_media(%d):"
					    " %s, Manually Ejected.",
					    LIBEQ, ce->CeVsn);
				if (category == MAN_EJECTED_CAT)
					set_media_category(library, ce->CeVsn,
					    MAN_EJECTED_CAT,
					    PURGE_VOL_CATEGORY);
				if (vol_status & (MT_VLI | MT_VQE | MT_VPE |
				    MT_VMIS | MT_VUU | MT_VME)) {
					ret = RET_GET_MEDIA_DISPOSE;
					sam_syslog(LOG_INFO,
					    "rec_cat(%d): %s, ce->CeSlot %d:"
					    " removed from catalog",
					    LIBEQ, ce->CeVsn, ce->CeSlot);
				}
				free(info);
			}
			return (ret);
		}
	}

	mutex_lock(&un->mutex);
	drive->status.b.full = TRUE;
	drive->status.b.valid = TRUE;
	memmove(un->vsn, ce->CeVsn, sizeof (un->vsn));
	un->slot = ce->CeSlot;
	un->mid = ce->CeMid;
	un->flip_mid = ROBOT_NO_SLOT;
	un->status.b.labeled = FALSE;
	un->status.b.ready = FALSE;
	drive->status.b.bar_code = TRUE;
	memcpy(drive->bar_code, ce->CeBarCode, BARCODE_LEN + 1);
	un->space = ce->CeSpace;
	switch (un->type & DT_CLASS_MASK) {
	case DT_OPTICAL:
		un->dt.od.ptoc_fwa = ce->m.CePtocFwa;
		break;

	case DT_TAPE:
		un->dt.tp.position = ce->m.CeLastPos;
		break;
	}
	mutex_unlock(&un->mutex);

	return (RET_GET_MEDIA_SUCCESS);
}
Пример #6
0
Файл: close.c Проект: kraj/gcc
void
st_close (st_parameter_close *clp)
{
  close_status status;
  gfc_unit *u;
#if !HAVE_UNLINK_OPEN_FILE
  char * path;

  path = NULL;
#endif

  library_start (&clp->common);

  status = !(clp->common.flags & IOPARM_CLOSE_HAS_STATUS) ? CLOSE_UNSPECIFIED :
    find_option (&clp->common, clp->status, clp->status_len,
		 status_opt, "Bad STATUS parameter in CLOSE statement");

  if ((clp->common.flags & IOPARM_LIBRETURN_MASK) != IOPARM_LIBRETURN_OK)
  {
    library_end ();
    return;
  }

  u = find_unit (clp->common.unit);
  if (u != NULL)
    {
      if (close_share (u) < 0)
	generate_error (&clp->common, LIBERROR_OS, "Problem in CLOSE");
      if (u->flags.status == STATUS_SCRATCH)
	{
	  if (status == CLOSE_KEEP)
	    generate_error (&clp->common, LIBERROR_BAD_OPTION,
			    "Can't KEEP a scratch file on CLOSE");
#if !HAVE_UNLINK_OPEN_FILE
	  path = strdup (u->filename);
#endif
	}
      else
	{
	  if (status == CLOSE_DELETE)
	    {
	      if (u->flags.readonly)
		generate_warning (&clp->common, "STATUS set to DELETE on CLOSE"
				  " but file protected by READONLY specifier");
	      else
		{
#if HAVE_UNLINK_OPEN_FILE
		  remove (u->filename);
#else
		  path = strdup (u->filename);
#endif
		}
	    }
	}

      close_unit (u);

#if !HAVE_UNLINK_OPEN_FILE
      if (path != NULL)
	{
	  remove (path);
	  free (path);
	}
#endif
    }

  /* CLOSE on unconnected unit is legal and a no-op: F95 std., 9.3.5. */ 
  library_end ();
}
Пример #7
0
/*
 * clean_3570 - attempt to load cleaning tape into 3570.
 */
void
clean_3570(
	drive_state_t *drive,
	robo_event_t *event,
	struct CatalogEntry *ce)
{
	int		retry;
	char	   *dev_name;
	char	   *d_mess = drive->un->dis_mes[DIS_MES_NORM];
	dev_ent_t	*un = drive->un;
	library_t	*library = drive->library;
	move_flags_t    move_flags;

	mutex_lock(&drive->mutex);
	move_flags.bits = 0;
	/*
	 * The 3570 does not return from the move until the cleaning cycle
	 * has completed.
	 */
	memccpy(d_mess, catgets(catfd, SET, 9030, "wait for cleaning cycle"),
	    '\0', DIS_MES_LEN);
	if (generic_get_media(library, drive, event, ce)) {
		memccpy(drive->un->dis_mes[DIS_MES_CRIT],
		    catgets(catfd, SET, 9029,
		    "unable to load cleaning cartridge, move failed"),
		    '\0', DIS_MES_LEN);

		DevLog(DL_ERR(5145), ce->CeSlot);
		down_drive(drive, SAM_STATE_CHANGE);
		drive->status.b.cln_inprog = FALSE;
		mutex_unlock(&drive->mutex);
		disp_of_event(library, event, EIO);
		return;
	}
	mutex_unlock(&drive->mutex);
	sleep(4);
	dev_name = samst_devname(un);
	mutex_lock(&un->mutex);
	drive->open_fd = open_unit(un, dev_name, 10);
	mutex_unlock(&un->mutex);
	free(dev_name);

	un->i.ViEq = un->fseq;
	un->i.ViSlot = un->slot;
	un->i.ViPart = 0;
	un->i.ViFlags = VI_cart;
	UpdateCatalog(drive->un, 0, CatalogVolumeLoaded);

	/* Wait for cleaning to finish */

	retry = 60;
	while (retry--) {
		sam_extended_sense_t *sense = (sam_extended_sense_t *)
		    SHM_REF_ADDR(un->sense);

		mutex_lock(&un->io_mutex);
		memset(sense, 0, sizeof (sam_extended_sense_t));
		if (scsi_cmd(drive->open_fd, un, SCMD_TEST_UNIT_READY, 20) ||
		    sense->es_key != 0) {
			/* If cleaning in progress */
			if (sense->es_key == 0x02 &&
			    sense->es_add_code == 0x30 &&
			    sense->es_qual_code == 0x03) {
				mutex_unlock(&un->io_mutex);
				sleep(30);
				continue;
			}
			if (sense->es_key == 0x06 &&
			    sense->es_add_code == 0x82 &&
			    sense->es_qual_code == 0x83)
				break;

			mutex_unlock(&un->io_mutex);
			sprintf(d_mess, "sense %x, %x, %x", sense->es_key,
			    sense->es_add_code, sense->es_qual_code);
			sleep(10);
		}
	}
	if (retry <= 0)
		DevLog(DL_ERR(5216));

	memccpy(d_mess, catgets(catfd, SET, 9034, "drive has been cleaned"),
	    '\0', DIS_MES_LEN);
	mutex_unlock(&un->io_mutex);
	mutex_lock(&un->mutex);
	close_unit(un, &drive->open_fd);
	mutex_unlock(&un->mutex);
	mutex_lock(&drive->mutex);
	move_flags.bits = 0;
	memccpy(d_mess,
	    catgets(catfd, SET, 9009, "waiting for media changer"),
	    '\0', DIS_MES_LEN);
	if (move_media(library, 0, drive->element, 0xff, 1, move_flags)) {
		memccpy(drive->un->dis_mes[DIS_MES_CRIT],
		    catgets(catfd, SET, 9032,
		    "unable to unload cleaning cartridge"),
		    '\0', DIS_MES_LEN);
		DevLog(DL_ERR(5147));
		drive->status.b.cln_inprog = FALSE;
		down_drive(drive, SAM_STATE_CHANGE);
		mutex_unlock(&drive->mutex);
		disp_of_event(library, event, EIO);
		return;
	}
	if (CatalogVolumeUnloaded(&un->i, "") == -1) {
		DevLog(DL_SYSERR(5336), ce->CeSlot);
	}
	drive->status.b.cln_inprog = FALSE;
	mutex_lock(&drive->un->mutex);
	drive->un->status.bits &= ~(DVST_CLEANING | DVST_REQUESTED);
	un->label_time = 0;
	mutex_unlock(&drive->un->mutex);
	mutex_unlock(&drive->mutex);
	disp_of_event(library, event, 0);
}
Пример #8
0
/*
 * audit - start auditing
 *
 *
 */
void
audit(
	drive_state_t *drive,	/* drive state pointer */
	const uint_t slot,		/* slot to audit */
	const int audit_eod)
{				/* flag to find eod during audit */
	int		part, err;
	uint_t	   myslot = 0;
	dev_ent_t	*un;
	sam_defaults_t *defaults;
	struct CatalogEntry ced;
	struct CatalogEntry *ce = &ced;
	int		skip_audit_eod = 0;

	defaults = GetDefaults();

	SANITY_CHECK(drive != (drive_state_t *)0);
	SANITY_CHECK(drive->library != (library_t *)0);
	SANITY_CHECK(drive->library->un != (dev_ent_t *)0);
	SANITY_CHECK(drive->library->un != drive->un);
	un = drive->un;

	if ((slot == ROBOT_NO_SLOT) &&
	    IS_GENERIC_API(drive->library->un->type)) {
		DevLog(DL_ERR(6004));
		return;
	}
	mutex_lock(&drive->mutex);

	if (drive->status.b.full) {
		mutex_lock(&un->mutex);
		un->status.b.requested = TRUE;
		mutex_unlock(&un->mutex);
		if (clear_drive(drive)) {
			mutex_lock(&un->mutex);
			un->status.b.requested = TRUE;
			mutex_unlock(&un->mutex);
			mutex_unlock(&drive->mutex);
			return;
		}
		if (drive->open_fd >= 0) {
			mutex_lock(&un->mutex);
			close_unit(un, &drive->open_fd);
			DEC_OPEN(un);
			mutex_unlock(&un->mutex);
		}
	}
	mutex_unlock(&drive->mutex);

	mutex_lock(&un->mutex);
	un->status.b.requested = TRUE;
	un->status.b.labeled = FALSE;
	un->status.b.ready = FALSE;
	mutex_unlock(&un->mutex);

	if (slot == ROBOT_NO_SLOT) {
		mutex_lock(&drive->library->mutex);
		drive->library->countdown--;
		drive->library->drives_auditing++;
		mutex_unlock(&drive->library->mutex);

		/*
		 * ok not to lock here wait for all drives to clear
		 */
		while (drive->library->countdown > 0)
			sleep(4);
	}
	for (;;) {

		mutex_lock(&drive->mutex);
		if (slot == ROBOT_NO_SLOT) {
			/* get the next slot number (s) */
			mutex_lock(&drive->library->mutex);
			myslot = drive->library->audit_index;
			if (myslot <= drive->library->range.storage_count) {
				drive->library->audit_index++;
				mutex_unlock(&drive->library->mutex);
			} else {
				/* No more slots to audit */
				mutex_unlock(&drive->library->mutex);
				mutex_lock(&drive->library->un->mutex);
				drive->library->un->status.b.mounted = TRUE;
				drive->library->un->status.b.audit = FALSE;
				drive->library->un->status.b.ready = TRUE;
				mutex_unlock(&drive->library->un->mutex);
				if (drive->status.b.full) {
					clear_drive(drive);
					if (drive->open_fd >= 0)
						mutex_lock(&un->mutex);
					close_unit(un, &drive->open_fd);
					mutex_unlock(&un->mutex);
				}
				mutex_lock(&un->mutex);
				un->status.b.requested = FALSE;
				mutex_unlock(&un->mutex);
				mutex_unlock(&drive->mutex);
				return;
			}
		} else {
			/* get specific slot */
			myslot = slot;
		}

		/*
		 * Should we audit this media? (is occupied, not cleaning and
		 * is a sam tape)
		 */
		if (drive->library->status.b.two_sided) {
			part = 1;
		} else {
			part = 0;
		}
		ce = CatalogGetCeByLoc(drive->library->un->eq,
			    myslot, part, &ced);
		if (ce == NULL ||
		    (!(ce->CeStatus & CES_occupied)) ||
		    (ce->CeStatus & CES_cleaning) ||
		    (ce->CeStatus & CES_non_sam)) {

			mutex_unlock(&drive->mutex);
			if (slot != ROBOT_NO_SLOT) {	/* only one slot */
				mutex_lock(&un->mutex);
				un->status.b.requested = FALSE;
				mutex_unlock(&un->mutex);
				return;
			}
			continue;
		}
		/*
		 * The following lines of code get a tape mounted, or if
		 * two-sided media, mounts the "A" side.
		 */
		err = get_media(drive->library, drive, NULL, ce);

		if (err) {
			mutex_lock(&un->mutex);
			un->status.b.requested = FALSE;
			DEC_ACTIVE(un);
			mutex_unlock(&un->mutex);
			mutex_unlock(&drive->mutex);
			return;
		}
		mutex_lock(&un->mutex);
		un->status.b.scanning = TRUE;
		mutex_unlock(&un->mutex);
		if (spin_drive(drive, SPINUP, NOEJECT)) {

			mutex_lock(&drive->un->mutex);
			drive->un->status.b.scanning &= ~DVST_SCANNING;
			drive->un->status.bits &= ~DVST_REQUESTED;
			mutex_unlock(&drive->un->mutex);

			if (un->state > DEV_ON) {
				clear_drive(drive);
				mutex_lock(&un->mutex);
				clear_driver_idle(drive, drive->open_fd);
				DEC_ACTIVE(un);
				close_unit(un, &drive->open_fd);
				mutex_unlock(&un->mutex);
				mutex_unlock(&drive->mutex);
			} else {
				mutex_lock(&un->mutex);
				clear_driver_idle(drive, drive->open_fd);
				DEC_ACTIVE(un);
				close_unit(un, &drive->open_fd);
				mutex_unlock(&un->mutex);
				mutex_unlock(&drive->mutex);
			}
			SendCustMsg(HERE, 9348);
			DevLog(DL_ERR(5218));
			return;
		}
		un->status.bits |= DVST_AUDIT;

		un->mid = ce->CeMid;
		un->status.b.labeled = FALSE;
		un->i.ViPart = ce->CePart;
		scan_a_device(un, drive->open_fd);
		if (drive->status.b.bar_code) {
			(void) CatalogSetStringByLoc(drive->library->un->eq,
			ce->CeSlot, ce->CePart,
			CEF_BarCode, (char *)drive->bar_code);
		}
		/*
		 * If the cleaning light came on while scanning, leave the
		 * audit bit set and unload the drive.
		 */
		if (un->status.bits & DVST_CLEANING) {
			mutex_lock(&un->mutex);
			un->mtime = 0;
			DEC_ACTIVE(un);
			close_unit(un, &drive->open_fd);
			un->status.b.requested = FALSE;
			mutex_unlock(&un->mutex);
			clear_drive(drive);
			mutex_unlock(&drive->mutex);
			return;
		} else {
			un->status.bits &= ~DVST_AUDIT;
		}

		mutex_lock(&un->mutex);
		/*
		 * This next check keeps us from auditing media that is not
		 * really labeled (label lie). I'm not sure why the un->mutex
		 * is held for this.
		 */
		if (!un->status.b.labeled &&
		    (ce->CeStatus & CES_bar_code) &&
		    (defaults->flags & DF_LABEL_BARCODE)) {
			int tmp;

			if (IS_TAPE(un)) {
				tmp = LEN_TAPE_VSN;
			} else {
				tmp = LEN_OPTIC_VSN;
			}
			vsn_from_barcode(un->vsn, ce->CeBarCode, defaults, tmp);
			un->status.b.labeled = TRUE;
			un->space = un->capacity;
			skip_audit_eod = 1;
		}
		if (IS_TAPE(un)) {
			if (un->status.b.labeled &&
			    audit_eod && !skip_audit_eod) {
				DevLog(DL_DETAIL(5074), un->vsn);
				mutex_unlock(&un->mutex);
				mutex_lock(&un->io_mutex);
				tape_append(drive->open_fd, un, NULL);
				mutex_unlock(&un->io_mutex);
				mutex_lock(&un->mutex);
			} else {
				if (!un->status.b.labeled) {
					un->space = un->capacity;
				} else {
					un->space = ce->CeSpace;
				}
			}
		}
		UpdateCatalog(un, 0, CatalogVolumeLoaded);

		/*
		 * Now do the "B" side if this is optical media.
		 * flip_and_scan calls CatalogVolumeLoaded so it is not done
		 * here.
		 */
		if (drive->library->status.b.two_sided && (ce->CePart == 1)) {
			mutex_unlock(&un->mutex);
			if (flip_and_scan(ce->CePart, drive)) {
				clear_drive(drive);
				mutex_lock(&un->mutex);
				un->status.b.requested = FALSE;
				clear_driver_idle(drive, drive->open_fd);
				close_unit(un, &drive->open_fd);
				DEC_ACTIVE(un);
				mutex_unlock(&un->mutex);
				mutex_unlock(&drive->mutex);
				return;
			}
			mutex_unlock(&drive->mutex);
		} else {
			mutex_unlock(&un->mutex);
			mutex_unlock(&drive->mutex);
		}

		mutex_lock(&un->mutex);
		close_unit(un, &drive->open_fd);
		DEC_ACTIVE(un);
		un->status.b.requested = TRUE;
		mutex_unlock(&un->mutex);

		if (slot != ROBOT_NO_SLOT) {	/* only one slot */
			mutex_lock(&un->mutex);
			un->status.b.requested = FALSE;
			mutex_unlock(&un->mutex);
			mutex_lock(&drive->library->un->mutex);
			drive->library->un->status.b.mounted = TRUE;
			mutex_unlock(&drive->library->un->mutex);
			return;
		}
	}
}
Пример #9
0
/*
 * clean - clean the drive.
 *
 */
void
clean(
	drive_state_t *drive,
	robo_event_t *event)
{
	dev_ent_t	*un;
	int		err, retry;
	uint32_t	access_count, status = 0;
	char	   *d_mess;
	char	   *l_mess;
	struct CatalogEntry ced;
	struct CatalogEntry *ce = &ced;
	library_t	*library;
	move_flags_t    move_flags;

	SANITY_CHECK(drive != (drive_state_t *)0);
	un = drive->un;
	SANITY_CHECK(un != (dev_ent_t *)0);
	library = (library_t *)drive->library;
	SANITY_CHECK(library != (library_t *)0);
	d_mess = drive->un->dis_mes[DIS_MES_NORM];
	l_mess = library->un->dis_mes[DIS_MES_NORM];
	mutex_lock(&drive->mutex);
	if (clear_drive(drive)) {
		mutex_lock(&drive->un->mutex);
		drive->un->status.bits |= DVST_CLEANING;
		mutex_unlock(&drive->un->mutex);
		drive->status.b.cln_inprog = FALSE;
		mutex_unlock(&drive->mutex);
		disp_of_event(library, event, ENOENT);
		return;
	}
	mutex_lock(&drive->un->mutex);
	drive->un->status.bits |= (DVST_REQUESTED | DVST_CLEANING);
	if (drive->un->open_count) {
		clear_driver_idle(drive, drive->open_fd);
		close_unit(drive->un, &drive->open_fd);
		DEC_OPEN(un);
	}
	mutex_unlock(&drive->un->mutex);
	mutex_unlock(&drive->mutex);

	DevLog(DL_ALL(5075));

	memccpy(d_mess, catgets(catfd, SET, 9025, "needs cleaning"),
	    '\0', DIS_MES_LEN);

	ce = CatalogGetCleaningVolume(library->un->eq, &ced);

	if (ce == NULL) {
		memccpy(l_mess,
		    catgets(catfd, SET, 9026,
	    "no cleaning cartridge available"),
		    '\0', DIS_MES_LEN);
		DevLog(DL_ERR(5141));
		SendCustMsg(HERE, 9347);
		mutex_lock(&drive->mutex);
		drive->status.b.cln_inprog = FALSE;
		down_drive(drive, SAM_STATE_CHANGE);
		mutex_lock(&drive->un->mutex);
		drive->un->status.bits &= ~DVST_REQUESTED;
		mutex_unlock(&drive->un->mutex);
		mutex_unlock(&drive->mutex);
		disp_of_event(library, event, EAGAIN);
		return;
	} else {

		status &= ~CES_occupied;
		(void) CatalogSetFieldByLoc(ce->CeEq, ce->CeSlot, ce->CePart,
		    CEF_Status, status, CES_occupied);


	}


	if (library->un->equ_type == DT_3570C) {
		clean_3570(drive, event, ce);
		return;
	}
	mutex_lock(&drive->mutex);

	if (IS_GENERIC_API(library->un->type)) {
		int		local_retry, d_errno, last_derrno = -1;
		api_errs_t	ret;
		char	   *tag = "load on clean";

		local_retry = 3;
		ret = API_ERR_TR;

		while (local_retry > 0) {
			if (aci_load_media(library, drive, ce, &d_errno) == 0)
				break;
			else {
				/* Error return on api call */
				if (d_errno == 0) {
					/*
					 * if call did not happen - error
					 * return but no error
					 */
					local_retry = -1;
					d_errno = EAMUCOMM;
				} else if ((last_derrno == -1) ||
				    (last_derrno != d_errno)) {
					/* Save error if repeated */
					last_derrno = d_errno;
					if (api_valid_error(library->un->type,
					    d_errno, library->un)) {
				/* Indentation for cstyle */
				if (library->un->slot != ROBOT_NO_SLOT) {
					DevLog(DL_DEBUG(6001),
					    library->un->slot, tag, d_errno,
					    d_errno, api_return_message(
					    library->un->type, d_errno));
				} else {
					DevLog(DL_DEBUG(6043), tag, d_errno,
					    d_errno, api_return_message(
					    library->un->type, d_errno));
				}

				local_retry = api_return_retry(
				    library->un->type, d_errno);
				ret = api_return_degree(
				    library->un->type, d_errno);
					} else {
						local_retry = -2;
					}
				}
				if (local_retry > 0) {
					/* delay before retrying */
					local_retry--;
					if (local_retry > 0)
						sleep(api_return_sleep(
						    library->un->type,
						    d_errno));
				}
			}
		}
		if (d_errno != EOK) {
			DevLog(DL_ERR(6036), ce->CeBarCode);
			memccpy(drive->un->dis_mes[DIS_MES_CRIT],
			    catgets(catfd, SET, 9029,
			"unable to load cleaning cartridge, move failed"),
			    '\0', DIS_MES_LEN);

			if (local_retry == -1) {
				/* The call didn't happen */
				DevLog(DL_ERR(6040), tag);
			} else if (local_retry == 0) {
				/* retries exceeded */
				DevLog(DL_ERR(6039), tag);
			} else {
				if (api_valid_error(drive->library->un->type,
				    d_errno, drive->library->un)) {
					if (drive->library->un->slot !=
					    ROBOT_NO_SLOT) {
						DevLog(DL_ERR(6001),
						    drive->library->un->slot,
						    tag, d_errno, d_errno,
						    api_return_message(
						    library->un->type,
						    d_errno));
					} else {
						DevLog(DL_ERR(6043), tag,
						    d_errno, d_errno,
						    api_return_message(
						    library->un->type,
						    d_errno));
					}
				}
			}

			if (ret == API_ERR_DD)
				down_drive(drive, SAM_STATE_CHANGE);
			else if (ret == API_ERR_DM)
				set_bad_media(un);
			else if (ret == API_ERR_DL)
				down_library(library, SAM_STATE_CHANGE);

			drive->status.b.cln_inprog = FALSE;
			mutex_lock(&drive->un->mutex);
			drive->un->status.bits &= ~(DVST_CLEANING |
			    DVST_REQUESTED);
			mutex_unlock(&drive->un->mutex);
			mutex_unlock(&drive->mutex);
			disp_of_event(library, event, EIO);
			return;
		}
	} else {
		move_flags.bits = 0;
		memccpy(d_mess,
		    catgets(catfd, SET, 9009, "waiting for media changer"),
		    '\0', DIS_MES_LEN);
		/*
		 * SPECTRA LOGIC NOTE:  In 3.3.1, the invert argument on this
		 * move was set to one.  This apparently was done for the
		 * spectra-logic robot which overloaded the invert argument
		 * to be the clean argument (See scsi_command case
		 * SCMD_MOVE_MEDIUM).  Unfortunately, the Qualstar robot,
		 * which is mapped to the spectra-logic, implements the
		 * mailbox control for the same bit that spectra-logic uses
		 * as clean.  Confused? Yep.
		 *
		 * Now to add to the confusion. Somewhere in the catalog
		 * rewrite, for reasons lost in the mists of time, the invert
		 * argument was set to zero.  This is inadverentately half
		 * the fix for snap 4966.  It simplifies things, ignoring any
		 * "special" cleaning logic and treating the cleaning tape as
		 * an ordinary load/unload. It seems to work.
		 *
		 * See #ifdef UNKNOWN_SPECTRA_LOGIC below for the other half of
		 * the fix.
		 */
		if (move_media(library, 0, ELEMENT_ADDRESS(library, ce->CeSlot),
		    drive->element, 0, move_flags)) {
			memccpy(drive->un->dis_mes[DIS_MES_CRIT],
			    catgets(catfd, SET, 9029,
			"unable to load cleaning cartridge, move failed"),
			    '\0', DIS_MES_LEN);

			DevLog(DL_ERR(5143));
			down_drive(drive, SAM_STATE_CHANGE);
			drive->status.b.cln_inprog = FALSE;
			mutex_unlock(&drive->mutex);
			disp_of_event(library, event, EIO);
			return;
		}
	}

	mutex_unlock(&drive->mutex);

	/*
	 * Log successful mount of cleaning tape
	 */
	DevLog(DL_ALL(10042), drive->un->eq);
	tapeclean_media(drive->un);

	/*
	 * move_media does not set up the un, so UpdateCatalog can't be
	 * called from here. Using generic_get_media instead of move_media
	 * leaves the drive hung up.
	 */
	status &= ~CES_occupied;
	(void) CatalogSetFieldByLoc(ce->CeEq, ce->CeSlot, ce->CePart,
	    CEF_Status, status, CES_occupied);
	access_count = ce->CeAccess;
	access_count--;
	(void) CatalogSetFieldByLoc(ce->CeEq, ce->CeSlot, ce->CePart,
	    CEF_Access, access_count, 0);
	(void) CatalogSetFieldByLoc(ce->CeEq, ce->CeSlot, ce->CePart,
	    CEF_MountTime, time(NULL), 0);

	retry = 7;
	err = 0;

	if (IS_GENERIC_API(library->un->equ_type)) {
		char	   *tag = "unload for clean";
		int		local_retry, d_errno, last_derrno;
		api_errs_t	ret;
		do {
			memccpy(d_mess,
			    catgets(catfd, SET, 9030,
			    "waiting for cleaning cycle"),
			    '\0', DIS_MES_LEN);
			sleep(3 * 60);	/* wait 3 minutes */
			tapeclean_media(drive->un);
			mutex_lock(&drive->mutex);
			memccpy(d_mess,
			    catgets(catfd, SET, 9031,
			    "attempt to unload cleaning cartridge"),
			    '\0', DIS_MES_LEN);

			local_retry = 3;
			ret = API_ERR_TR;
			last_derrno = -1;

			while (local_retry > 0) {
				/*
				 * vsn is not set, use aci_force_media()
				 * instead of aci_dismount_media()
				 */
				if (aci_force_media(library, drive,
				    &d_errno) == 0)
					break;
				else {
					/* Error return on api call */
					if (d_errno == 0) {
						/*
						 * if call did not happen -
						 * error return but no error
						 */
						local_retry = -1;
						d_errno = EAMUCOMM;
					} else if ((last_derrno == -1) ||
					    (last_derrno != d_errno)) {
						/* Save error if repeated */
						last_derrno = d_errno;
						if (api_valid_error(
						    drive->library->un->type,
						    d_errno,
						    drive->library->un)) {

						if (drive->library->un->slot !=
						    ROBOT_NO_SLOT) {
						DevLog(DL_DEBUG(6001),
						    drive->library->un->slot,
						    tag, d_errno, d_errno,
						    api_return_message(
						    library->un->type,
						    d_errno));
							} else {
						DevLog(DL_DEBUG(6043),
						    tag, d_errno, d_errno,
						    api_return_message(
						    library->un->type,
						    d_errno));
						}

						local_retry = api_return_retry(
						    library->un->type, d_errno);
						ret = api_return_degree(
						    library->un->type, d_errno);
						} else {
							local_retry = -2;
						}
					}
					if (local_retry > 0) {
						/* delay before retrying */
						local_retry--;
						if (local_retry > 0)
							sleep(api_return_sleep(
							    library->un->type,
							    d_errno));
					}
				}
			}
			if (d_errno != EOK) {
				DevLog(DL_ERR(6033), ce->CeBarCode);

				if (local_retry == -1) {
					/* The call didn't happen */
					DevLog(DL_ERR(6040), tag);
				} else if (local_retry == 0) {
					/* retries exceeded */
					DevLog(DL_ERR(6039), tag);
				} else {
					if (api_valid_error(
					    drive->library->un->type,
					    d_errno, drive->library->un))
						if (drive->library->un->slot !=
						    ROBOT_NO_SLOT) {
						DevLog(DL_ERR(6001),
						    drive->library->un->slot,
						    tag, d_errno, d_errno,
						    api_return_message(
						    library->un->type,
						    d_errno));
						} else {
						DevLog(DL_ERR(6043),
						    d_errno, d_errno,
						    api_return_message(
						    library->un->type,
						    d_errno));
						}
				}
				if (ret == API_ERR_DL)
					down_library(library, SAM_STATE_CHANGE);
				else if (ret == API_ERR_DD)
					down_drive(drive, SAM_STATE_CHANGE);
			}
			mutex_unlock(&drive->mutex);
		} while (err != 0 && retry-- != 0);
	} else {
		/*
		 * SPECTRA LOGIC NOTE:  Due to the removal of "special"
		 * cleaning code, we must unload the cleaning tape for all
		 * robot types.
		 *
		 * See the previous SPECTRA LOGIC NOTE:
		 */
#ifdef UNKNOWN_SPECTRA_LOGIC
		if (library->un->equ_type != DT_SPECLOG)
#endif
			do {
				memccpy(d_mess,
				    catgets(catfd, SET, 9030,
				    "wait for cleaning cycle"),
				    '\0', DIS_MES_LEN);
				sleep(3 * 60);	/* wait 3 minutes */
				tapeclean_media(drive->un);
				mutex_lock(&drive->mutex);
				DevLog(DL_DETAIL(5077));
				memccpy(d_mess,
				    catgets(catfd, SET, 9031,
				    "attempt to unload cleaning cartridge"),
				    '\0', DIS_MES_LEN);
				err = move_media(library, 0, drive->element,
				    ELEMENT_ADDRESS(library, ce->CeSlot),
				    0, move_flags);
				if (err) {
					DevLog(DL_ERR(5078), retry);
				} else {
					DevLog(DL_DETAIL(5079));
				}
				mutex_unlock(&drive->mutex);
			} while (err != 0 && retry-- != 0);
	}

	tapeclean_media(drive->un);

	if (err != 0) {
		DevLog(DL_ERR(5080));
		memccpy(drive->un->dis_mes[DIS_MES_CRIT],
		    catgets(catfd, SET, 9032,
		    "unable to unload cleaning cartridge"),
		    '\0', DIS_MES_LEN);
		mutex_lock(&drive->mutex);
		drive->status.b.cln_inprog = FALSE;
		down_drive(drive, SAM_STATE_CHANGE);
		mutex_unlock(&drive->mutex);
		disp_of_event(library, event, EIO);
		return;
	}
	status = CES_occupied;
	if (drive->un->status.b.bad_media) {
		/* cleaning media marked in catalog as bad */
		status |= CES_bad_media;

		/* reset bad media flag */
		drive->un->status.b.bad_media = 0;
	}
	(void) CatalogSetFieldByLoc(library->un->eq, ce->CeSlot, 0,
	    CEF_Status, status, 0);

	DevLog(DL_ALL(5334), access_count);

	if ((status & CES_bad_media) == 0) {
		memccpy(d_mess,
		    catgets(catfd, SET, 9034, "drive has been cleaned"),
		    '\0', DIS_MES_LEN);
	}
	mutex_lock(&drive->mutex);
	drive->status.b.cln_inprog = FALSE;
	mutex_lock(&drive->un->mutex);
	if ((status & CES_bad_media) == 0) {
		/* drive was cleaned */
		drive->un->status.bits &= ~(DVST_CLEANING | DVST_REQUESTED);
	} else {
		/* drive was not cleaned, it needs to still be cleaned */
		drive->un->status.bits &= ~(DVST_REQUESTED);
	}
	mutex_unlock(&drive->un->mutex);
	mutex_unlock(&drive->mutex);
	if (ce->CeAccess == 0 || (status & CES_bad_media)) {
		char	   *MES_9035 = catgets(catfd, SET, 9035,
		    "cleaning cartridge in slot %d has expired");

		char *mess = (char *)malloc_wait(
		    strlen(MES_9035) + 15, 2, 0);
		sprintf(mess, MES_9035, ce->CeSlot);
		memccpy(l_mess, mess, '\0', DIS_MES_LEN);
		free(mess);
		switch (library->un->type) {
		case DT_METD28:
		case DT_DLT2700:
		case DT_GRAUACI:
			DevLog(DL_ERR(5144), ce->CeSlot);
			break;

		default:
			schedule_export(library, ce->CeSlot);
			DevLog(DL_ERR(5145), ce->CeSlot);
			break;
		}
	} else if (tapeclean_drive(drive->un)) {
		memccpy(l_mess,
		    catgets(catfd, SET, 2983, "clean failed"),
		    '\0', DIS_MES_LEN);
		DevLog(DL_ERR(5364));
		mutex_lock(&drive->mutex);
		down_drive(drive, SAM_STATE_CHANGE);
		mutex_unlock(&drive->mutex);
		disp_of_event(library, event, EIO);
		return;
	}
	disp_of_event(library, event, 0);
}