void test_modesense6_residuals(void) { struct scsi_task *ms_task = NULL; int ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of MODESENSE6 Residuals"); logging(LOG_VERBOSE, "MODESENSE6 command should not result in any " "residuals"); logging(LOG_VERBOSE, "Try a MODESENSE6 command with 4 bytes of " "transfer length and verify that we don't get residuals."); ret = modesense6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 4, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "[SUCCESS] All Pages fetched."); logging(LOG_VERBOSE, "Verify that we got at most 4 bytes of DATA-IN"); if (ms_task->datain.size > 4) { logging(LOG_NORMAL, "[FAILED] got more than 4 bytes of " "DATA-IN."); } else { logging(LOG_VERBOSE, "[SUCCESS] <= 4 bytes of DATA-IN " "received."); } CU_ASSERT_TRUE(ms_task->datain.size <= 4); logging(LOG_VERBOSE, "Verify residual overflow flag not set"); if (ms_task->residual_status == SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target set residual " "overflow flag"); } CU_ASSERT_NOT_EQUAL(ms_task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Try a MODESENSE6 command with 255 bytes of " "transfer length and verify that we get residuals if the target returns less than the requested amount of data."); scsi_free_scsi_task(ms_task); ret = modesense6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "[SUCCESS] All Pages fetched."); if (ms_task->datain.size == 255) { logging(LOG_VERBOSE, "We got all 255 bytes of data back " "from the target. Verify that underflow is not set."); if (ms_task->residual_status == SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target set residual " "underflow flag"); } else { logging(LOG_VERBOSE, "[SUCCESS] Residual underflow " "is not set"); } CU_ASSERT_NOT_EQUAL(ms_task->residual_status, SCSI_RESIDUAL_UNDERFLOW); } else { logging(LOG_VERBOSE, "We got less than the requested 255 bytes " "from the target. Verify that underflow is set."); if (ms_task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set " "residual underflow flag"); } else { logging(LOG_VERBOSE, "[SUCCESS] Residual underflow " "is set"); } CU_ASSERT_EQUAL(ms_task->residual_status, SCSI_RESIDUAL_UNDERFLOW); } scsi_free_scsi_task(ms_task); }
void test_read12_dpofua(void) { int ret, dpofua, usage_data_dpofua; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ12 DPO/FUA flags"); CHECK_FOR_SBC; logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); ret = modesense6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test READ12 with DPO==1"); if (dpofua) { ret = read12(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); } else { ret = read12(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); CU_ASSERT_EQUAL(ret, 0); } logging(LOG_VERBOSE, "Test READ12 with FUA==1"); if (dpofua) { ret = read12(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, NULL, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); } else { ret = read12(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); CU_ASSERT_EQUAL(ret, 0); } logging(LOG_VERBOSE, "Test READ12 with DPO==1 FUA==1"); if (dpofua) { ret = read12(sd, 0, block_size, block_size, 0, 1, 1, 0, 0, NULL, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); } else { ret = read12(sd, 0, block_size, block_size, 0, 1, 1, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); CU_ASSERT_EQUAL(ret, 0); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for READ12"); ret = report_supported_opcodes(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_READ12, 0, 65535, EXPECT_STATUS_GOOD); if (ret == -2) { logging(LOG_NORMAL, "REPORT_SUPPORTED_OPCODES not implemented. " "Skipping this part of the test"); return; } logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_NOT_EQUAL(rsoc, NULL); usage_data_dpofua = rsoc ? rsoc->cdb_usage_data[1] & 0x18 : -1; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the DPO/FUA flags " "are set in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpofua, 0x18); } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the DPO/FUA " "flags are clear in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpofua, 0x00); } scsi_free_scsi_task(rso_task); }
void test_reserve6_2initiators(void) { int ret; struct scsi_device sd2; memset(&sd2, 0, sizeof(sd2)); sd2.iscsi_url = sd->iscsi_url; sd2.iscsi_lun = sd->iscsi_lun; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test RESERVE6/RELEASE6 across two initiators"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This RESERVE6 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_NORMAL, "Take out a RESERVE6 from the first initiator"); ret = reserve6(sd); if (ret == -2) { logging(LOG_VERBOSE, "[SKIPPED] Target does not support RESERVE6. Skipping test"); CU_PASS("[SKIPPED] Target does not support RESERVE6. Skipping test"); return; } CU_ASSERT_EQUAL(ret, 0); logging(LOG_NORMAL, "Verify that the first initiator can re-RESERVE6 the same reservation"); ret = reserve6(sd); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Create a second connection to the target"); sd2.iscsi_ctx = iscsi_context_login(initiatorname2, sd2.iscsi_url, &sd2.iscsi_lun); if (sd2.iscsi_ctx == NULL) { logging(LOG_VERBOSE, "Failed to login to target"); return; } logging(LOG_NORMAL, "Try to take out a RESERVE6 from the second initiator"); ret = reserve6_conflict(&sd2); CU_ASSERT_EQUAL(ret, 0); logging(LOG_NORMAL, "Try to RELEASE from the second initiator. Should be a nop"); ret = release6(&sd2); CU_ASSERT_EQUAL(ret, 0); logging(LOG_NORMAL, "Test we can still send MODE SENSE from the first initiator"); ret = modesense6(sd, NULL, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_NORMAL, "MODE SENSE should fail from the second initiator"); ret = modesense6(&sd2, NULL, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_RESERVATION_CONFLICT); logging(LOG_NORMAL, "RESERVE6 from the second initiator should still fail"); ret = reserve6_conflict(&sd2); CU_ASSERT_EQUAL(ret, 0); logging(LOG_NORMAL, "RELEASE6 from the first initiator"); ret = release6(sd); CU_ASSERT_EQUAL(ret, 0); logging(LOG_NORMAL, "RESERVE6 from the second initiator should work now"); ret = reserve6(&sd2); CU_ASSERT_EQUAL(ret, 0); logging(LOG_NORMAL, "RELEASE6 from the second initiator"); ret = release6(&sd2); CU_ASSERT_EQUAL(ret, 0); iscsi_logout_sync(sd2.iscsi_ctx); iscsi_destroy_context(sd2.iscsi_ctx); }
void test_modesense6_control_swp(void) { struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_mode_page *page; unsigned char *buf = alloca(block_size); int ret; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of MODESENSE6 CONTROL SWP flag"); logging(LOG_VERBOSE, "Set SWP to enable write protect"); ret = set_swp(sd); if (ret == -2) { CU_PASS("[SKIPPED] Target does not support changing SWP"); return; } CU_ASSERT_EQUAL(ret, 0); if (ret) { goto finished; } logging(LOG_VERBOSE, "Read the CONTROL page back from the device"); ret = modesense6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_CONTROL, 0, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "[SUCCESS] CONTROL page fetched."); logging(LOG_VERBOSE, "Try to unmarshall the DATA-IN buffer."); ms = scsi_datain_unmarshall(ms_task); if (ms == NULL) { logging(LOG_NORMAL, "[FAILED] failed to unmarshall mode sense " "datain buffer"); CU_FAIL("[FAILED] Failed to unmarshall the data-in buffer."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] Unmarshalling successful."); for (page = ms->pages; page; page = page->next) { if (page->page_code == SCSI_MODEPAGE_CONTROL) { break; } } if(page == NULL) { logging(LOG_NORMAL, "[WARNING] CONTROL page was not returned." "All devices SHOULD implement this page."); } logging(LOG_VERBOSE, "Verify that the SWP bit is set"); if (page->control.swp == 0) { logging(LOG_NORMAL, "[FAILED] SWP bit is not set"); CU_FAIL("[FAILED] SWP is not set"); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] SWP was set successfully"); logging(LOG_VERBOSE, "Read a block from the now Read-Only device"); ret = read10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Try to write a block to the Read-Only device"); ret = write10(sd, 0, block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_WRITE_PROTECTED); CU_ASSERT_EQUAL(ret, 0); finished: if (ms_task != NULL) { scsi_free_scsi_task(ms_task); } logging(LOG_VERBOSE, "Clear SWP to disable write protect"); clear_swp(sd); }
int main(int argc, char *argv[]) { char *testname_re = NULL; CU_BasicRunMode mode = CU_BRM_VERBOSE; CU_ErrorAction error_action = CUEA_IGNORE; int res; struct scsi_readcapacity10 *rc10; struct scsi_task *inq_task = NULL; struct scsi_task *inq_lbp_task = NULL; struct scsi_task *inq_bdc_task = NULL; struct scsi_task *inq_bl_task = NULL; struct scsi_task *rc16_task = NULL; struct scsi_task *rsop_task = NULL; int full_size; int xml_mode = 0; static struct option long_opts[] = { { "help", no_argument, 0, '?' }, { "list", no_argument, 0, 'l' }, { "initiator-name", required_argument, 0, 'i' }, { "initiator-name-2", required_argument, 0, 'I' }, { "test", required_argument, 0, 't' }, { "dataloss", no_argument, 0, 'd' }, { "allow-sanitize", no_argument, 0, 'S' }, { "ignore", no_argument, 0, 'g' }, { "fail", no_argument, 0, 'f' }, { "abort", no_argument, 0, 'A' }, { "silent", no_argument, 0, 's' }, { "normal", no_argument, 0, 'n' }, { "verbose", no_argument, 0, 'v' }, { "xml", no_argument, 0, 'x' }, { "Verbose-scsi", no_argument, 0, 'V' }, { NULL, 0, 0, 0 } }; int i, c; int opt_idx = 0; while ((c = getopt_long(argc, argv, "?hli:I:t:sdgfAsSnvxV", long_opts, &opt_idx)) > 0) { switch (c) { case 'h': case '?': print_usage(); return 0; case 'l': list_all_tests(); return 0; case 'i': initiatorname1 = strdup(optarg); break; case 'I': initiatorname2 = strdup(optarg); break; case 't': testname_re = strdup(optarg); break; case 'd': data_loss++; break; case 'g': error_action = CUEA_IGNORE; /* default */ break; case 'f': error_action = CUEA_FAIL; break; case 'A': error_action = CUEA_ABORT; break; case 's': mode = CU_BRM_SILENT; break; case 'S': allow_sanitize = 1; break; case 'n': mode = CU_BRM_NORMAL; break; case 'v': mode = CU_BRM_VERBOSE; /* default */ break; case 'x': xml_mode = 1; break; case 'V': loglevel = LOG_VERBOSE; break; default: fprintf(stderr, "error: unknown option return: %c (option %s)\n", c, argv[optind]); return 1; } } /* parse all trailing arguments as device paths */ mp_num_sds = 0; while (optind < argc) { if (mp_num_sds >= MPATH_MAX_DEVS) { fprintf(stderr, "Too many multipath device URLs\n"); print_usage(); free(testname_re); return 10; } mp_sds[mp_num_sds] = malloc(sizeof(struct scsi_device)); memset(mp_sds[mp_num_sds], '\0', sizeof(struct scsi_device)); mp_sds[mp_num_sds]->sgio_fd = -1; if (!strncmp(argv[optind], "iscsi://", 8)) { mp_sds[mp_num_sds]->iscsi_url = strdup(argv[optind++]); #ifdef HAVE_SG_IO } else { mp_sds[mp_num_sds]->sgio_dev = strdup(argv[optind++]); #endif } mp_num_sds++; } /* So that we can override iscsi_queue_pdu in tests * and replace or mutate the blob that we are about to write to the * wire. * This allows such tests to do their mutates and then call out * to the real queueing function once they have modified the data. */ real_iscsi_queue_pdu = dlsym(RTLD_NEXT, "iscsi_queue_pdu"); if ((mp_num_sds == 0) || (mp_sds[0]->iscsi_url == NULL && mp_sds[0]->sgio_dev == NULL)) { #ifdef HAVE_SG_IO fprintf(stderr, "You must specify either an iSCSI URL or a device file\n"); #else fprintf(stderr, "You must specify an iSCSI URL\n"); #endif print_usage(); if (testname_re) free(testname_re); return 10; } /* sd remains an alias for the first device */ sd = mp_sds[0]; for (i = 0; i < mp_num_sds; i++) { res = connect_scsi_device(mp_sds[i], initiatorname1); if (res < 0) { fprintf(stderr, "Failed to connect to SCSI device %d\n", i); goto err_sds_free; } } if (mp_num_sds > 1) { /* check that all multipath sds identify as the same LU */ res = mpath_check_matching_ids(mp_num_sds, mp_sds); if (res < 0) { fprintf(stderr, "multipath devices don't match\n"); goto err_sds_free; } } /* * find the size of the LUN * All devices support readcapacity10 but only some support * readcapacity16 */ task = NULL; readcapacity10(sd, &task, 0, 0, EXPECT_STATUS_GOOD); if (task == NULL) { printf("Failed to send READCAPACITY10 command: %s\n", sd->error_str); goto err_sds_free; } if (task->status != SCSI_STATUS_GOOD) { printf("READCAPACITY10 command: failed with sense. %s\n", sd->error_str); scsi_free_scsi_task(task); goto err_sds_free; } rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { printf("failed to unmarshall READCAPACITY10 data.\n"); scsi_free_scsi_task(task); goto err_sds_free; } block_size = rc10->block_size; num_blocks = rc10->lba + 1; scsi_free_scsi_task(task); rc16_task = NULL; readcapacity16(sd, &rc16_task, 96, EXPECT_STATUS_GOOD); if (rc16_task == NULL) { printf("Failed to send READCAPACITY16 command: %s\n", sd->error_str); goto err_sds_free; } if (rc16_task->status == SCSI_STATUS_GOOD) { rc16 = scsi_datain_unmarshall(rc16_task); if (rc16 == NULL) { printf("failed to unmarshall READCAPACITY16 data. %s\n", sd->error_str); scsi_free_scsi_task(rc16_task); goto err_sds_free; } block_size = rc16->block_length; num_blocks = rc16->returned_lba + 1; lbppb = 1 << rc16->lbppbe; } inq_task = NULL; inquiry(sd, &inq_task, 0, 0, 64, EXPECT_STATUS_GOOD); if (inq_task == NULL || inq_task->status != SCSI_STATUS_GOOD) { printf("Inquiry command failed : %s\n", sd->error_str); goto err_sds_free; } full_size = scsi_datain_getfullsize(inq_task); if (full_size > inq_task->datain.size) { scsi_free_scsi_task(inq_task); /* we need more data for the full list */ inq_task = NULL; inquiry(sd, &inq_task, 0, 0, full_size, EXPECT_STATUS_GOOD); if (inq_task == NULL) { printf("Inquiry command failed : %s\n", sd->error_str); goto err_sds_free; } } inq = scsi_datain_unmarshall(inq_task); if (inq == NULL) { printf("failed to unmarshall inquiry datain blob\n"); scsi_free_scsi_task(inq_task); goto err_sds_free; } sbc3_support = 0; for (i = 0; i < 8; i++) { if (inq->version_descriptor[i] == 0x04C0) { sbc3_support = 1; } } /* try reading block limits vpd */ inq_bl_task = NULL; inquiry(sd, &inq_bl_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, 64, EXPECT_STATUS_GOOD); if (inq_bl_task && inq_bl_task->status != SCSI_STATUS_GOOD) { scsi_free_scsi_task(inq_bl_task); inq_bl_task = NULL; } if (inq_bl_task) { full_size = scsi_datain_getfullsize(inq_bl_task); if (full_size > inq_bl_task->datain.size) { scsi_free_scsi_task(inq_bl_task); inq_bl_task = NULL; inquiry(sd, &inq_bl_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, full_size, EXPECT_STATUS_GOOD); if (inq_bl_task == NULL) { printf("Inquiry command failed : %s\n", sd->error_str); goto err_sds_free; } } inq_bl = scsi_datain_unmarshall(inq_bl_task); if (inq_bl == NULL) { printf("failed to unmarshall inquiry datain blob\n"); goto err_sds_free; } } /* try reading block device characteristics vpd */ inquiry(sd, &inq_bdc_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS, 255, EXPECT_STATUS_GOOD); if (inq_bdc_task == NULL || inq_bdc_task->status != SCSI_STATUS_GOOD) { printf("Failed to read Block Device Characteristics page\n"); } else { inq_bdc = scsi_datain_unmarshall(inq_bdc_task); if (inq_bdc == NULL) { printf("failed to unmarshall inquiry datain blob\n"); goto err_sds_free; } } /* if thin provisioned we also need to read the VPD page for it */ if (rc16 && rc16->lbpme != 0){ inq_lbp_task = NULL; inquiry(sd, &inq_lbp_task, 1, SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING, 64, EXPECT_STATUS_GOOD); if (inq_lbp_task == NULL || inq_lbp_task->status != SCSI_STATUS_GOOD) { printf("Inquiry command failed : %s\n", sd->error_str); goto err_sds_free; } full_size = scsi_datain_getfullsize(inq_lbp_task); if (full_size > inq_lbp_task->datain.size) { scsi_free_scsi_task(inq_lbp_task); /* we need more data for the full list */ inq_lbp_task = NULL; inquiry(sd, &inq_lbp_task, 1, SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING, full_size, EXPECT_STATUS_GOOD); if (inq_lbp_task == NULL) { printf("Inquiry command failed : %s\n", sd->error_str); goto err_sds_free; } } inq_lbp = scsi_datain_unmarshall(inq_lbp_task); if (inq_lbp == NULL) { printf("failed to unmarshall inquiry datain blob\n"); goto err_sds_free; } } rsop_task = NULL; report_supported_opcodes(sd, &rsop_task, 1, SCSI_REPORT_SUPPORTING_OPS_ALL, 0, 0, 65535, EXPECT_STATUS_GOOD); if (rsop_task == NULL) { printf("Failed to send REPORT_SUPPORTED_OPCODES command: %s\n", sd->error_str); goto err_sds_free; } if (rsop_task->status == SCSI_STATUS_GOOD) { rsop = scsi_datain_unmarshall(rsop_task); if (rsop == NULL) { printf("failed to unmarshall REPORT_SUPPORTED_OPCODES data.\n"); scsi_free_scsi_task(rsop_task); rsop_task = NULL; } } /* check if the device is write protected or not */ task = NULL; modesense6(sd, &task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); if (task == NULL) { printf("Failed to send MODE_SENSE6 command: %s\n", sd->error_str); goto err_sds_free; } if (task->status == SCSI_STATUS_GOOD) { struct scsi_mode_sense *ms; ms = scsi_datain_unmarshall(task); if (ms == NULL) { printf("failed to unmarshall mode sense datain blob\n"); scsi_free_scsi_task(task); goto err_sds_free; } readonly = !!(ms->device_specific_parameter & 0x80); } scsi_free_scsi_task(task); if (maxsectbytes) { maximum_transfer_length = maxsectbytes / block_size; printf("Bus transfer size is limited to %d bytes. Clamping " "max transfers accordingly.\n", maxsectbytes); } if (CU_initialize_registry() != 0) { fprintf(stderr, "error: unable to initialize test registry\n"); goto err_sds_free; } if (CU_is_test_running()) { fprintf(stderr, "error: test suite(s) already running!?\n"); exit(1); } parse_and_add_tests(testname_re); if (testname_re) free(testname_re); CU_basic_set_mode(mode); CU_set_error_action(error_action); printf("\n"); /* * this actually runs the tests ... */ if (xml_mode) { CU_list_tests_to_file(); CU_automated_run_tests(); } else { res = CU_basic_run_tests(); printf("Tests completed with return value: %d\n", res); } CU_cleanup_registry(); if (inq_task != NULL) { scsi_free_scsi_task(inq_task); } if (inq_bl_task != NULL) { scsi_free_scsi_task(inq_bl_task); } if (inq_lbp_task != NULL) { scsi_free_scsi_task(inq_lbp_task); } if (inq_bdc_task != NULL) { scsi_free_scsi_task(inq_bdc_task); } if (rc16_task != NULL) { scsi_free_scsi_task(rc16_task); } if (rsop_task != NULL) { scsi_free_scsi_task(rsop_task); } for (i = 0; i < mp_num_sds; i++) { free_scsi_device(mp_sds[i]); } return 0; err_sds_free: for (i = 0; i < mp_num_sds; i++) { free_scsi_device(mp_sds[i]); } return -1; }