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 (); }
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); }
/** * 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); } }
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; }
/* * 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); }
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 (); }
/* * 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); }
/* * 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; } } }
/* * 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); }