/*************************************** try-3494lib.c ********************************** Sample program to illustrate how to retrieve information from 3494 robotic tape library utilizing the IBM atldd (automated tape library device driver) interface. By this means you may query the 3494 Library Manager for various information, and implement monitoring on a polling or event wait basis. (The 3494 also provides limited SNMP support whereby a waiting monitoring facility may receive spontaneous alerts from the 3494. See the various 3494 manuals and redbooks for information on usage. The receiver may be a commercial network monitoring application: they almost universally utilize SNMP.) COMPILER: Any standard C compiler COMPILING: cc -o try-3494lib try-3494lib.c -lc -ls INVOCATION: ./try-3494lib NOTES: - Communication with the 3494 Library Manager requires use of the mtlibio.h header file, installed with the atldd device driver. That file has continually changed in structure over the years, making for numerous hassles in compensating for problems and dealing with some amazingly dumb design changes. Earlier versions of the file included dual sets of definitions, one defined by character arrays and another by short/int, selected via MT_PACKED #define. Later versions (mid-2000) dropped the duality such that all fields are defined in terms of character arrays - which is ridiculous in forcing the programmer to go through contortions to use the values as numerics. For example, 2-char fields must be recast as "*(unsigned short *)" to use them as numerics. REFERENCES: "IBM TotalStorage Tape Device Drivers: Programming Reference" (GC35-0346) (a renaming of the earlier manual "IBM SCSI Tape Drive, Medium Changer, and Library Device Drivers: Programming Reference" (WB2107)) Available at ftp://ftp.storsys.ibm.com/devdrvr/Doc/ or ftp://ftp.software.ibm.com/storage/devdrvr/Doc/ as files: IBM_TotalStorage_tape_IUG.ps or IBM_TotalStorage_tape_IUG.pdf IBM_TotalStorage_tape_PROGREF.ps or IBM_TotalStorage_tape_PROGREF.pdf ACKNOWLEDGEMENTS: - Thanks to Francis Dequenne of ECMWF, Berkshire UK for contributing Library Event Wait sample code. HISTORY: 1996/08/01 Created. Richard Sims, Boston University Office of Information Technology 2001/04/20 Accommodate character-array definition of values. RBS ******************************************************************************************/ /* Include files, from /usr/include... */ #define _ALL_SOURCE /* For ANSI compilation (as in using 'c89' cmd to * compile, instead of 'cc'). */ #include /* Contains errno. */ #include /* For open(). */ #include /* Defines stdin, stdout, stderr. */ #include #include #include #include #include /* Defines IOCPARM_MASK. */ #define _POSIX_SOURCE /* Assure getting STDIN_* in unistd.h. */ #include /* Defines STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO. */ #include /* This is the 3494 lib header file, as planted by atldd. */ /********************************** Global variables ************************************/ char *Pgm_Name; /* The basename of this program, as obtained via argv[0]. */ /****************************** timestamp() ********************************** Return the current date-time, in format YYYY/MM/DD HH:MM:SS INVOCATION: timestamp() RETURNS: Date-time string of form "YYYY/MM/DD HH:MM:SS". Note that you can just get time time portion by referencing timestamp()+11. HISTORY: 1996/10/02 Written by Richard Sims *******************************************************************************/ #define FUNCTION_NAME "timestamp" #define RETURNED_VALUE_TYPE char * #include /* For timeval struct. */ #include /* For tm struct. */ char * timestamp() { /*___________________________Function storage_____________________________*/ /* Define variables as static to maximize performance. */ static time_t current_time_in_seconds_since_1970; static struct tm *tm_struct_ptr; static char timestamp_string[20]; /* Room for YYYY/MM/DD HH:MM:SS\0 */ /*___________________________Function logic_______________________________*/ time(¤t_time_in_seconds_since_1970); tm_struct_ptr = localtime(¤t_time_in_seconds_since_1970); sprintf(timestamp_string,"%04u/%02u/%02u %02u:%02u:%02u", tm_struct_ptr->tm_year+1900, tm_struct_ptr->tm_mon+1, tm_struct_ptr->tm_mday, tm_struct_ptr->tm_hour, tm_struct_ptr->tm_min, tm_struct_ptr->tm_sec ); return(timestamp_string); } #undef FUNCTION_NAME #undef RETURNED_VALUE_TYPE /******************************************************* main() *************************************************************/ main(int argc, /* Number of arguments passed. */ char * argv[], /* An array of pointers to the character strings which comprise the arguments. */ char * envp[] /* An array of pointers to the character strings which comprise the environmental parameters. */ ) { /*_______________________________________________Storage________________________________________________________*/ auto int i, n; auto int mtlib_fd; /* Magnetic tape library file descriptor, for open(). */ auto int rc; /* Return code from a library call. */ static char * char_ptr; /* Pointer into char portion of output. */ struct mtlqarg mtlqarg_struct; struct mtlewarg mtlewarg_struct; char volser[7]; /* Used during displaying of a volser. */ /*______________________________________Preliminaries___________________________________________*/ Pgm_Name = ((char_ptr = strrchr(argv[0], '/')) != NULL) ? ++char_ptr : argv[0]; /* Get basename of invoked program name. */ /*______________________Process invocation parameters______________________*/ /* None. */ /*________________________Establish access to library____________________________*/ if ((mtlib_fd = open("/dev/lmcp0", O_RDONLY)) == -1) { /* The open() failed. * Condition "errno 2: No such file or directory" means that the * library manager control point is not installed on this system. * (It need only be installed on ADSM server systems.) */ fprintf(stderr, "Unable to open 3494 library /dev/lmcp0 - quitting.\n" " Errno %d: %s\n", errno, strerror(errno)); exit(EXIT_FAILURE); } /* The library is open for reading. */ /*__________________________________________Obtain library status____________________________________________*/ /* This is a demonstration of one method to get information from the 3494 library: by polling it. This is in * contrast to the later event wait method. Polling is more flexible and more suitable for a general monitor * in that you aren't blocked waiting for some event to occur. */ puts("\n================================ One-time Library Status report ================================"); mtlqarg_struct.sub_cmd = MT_QLD; /* We do Query Library Data. */ if ((rc = ioctl(mtlib_fd, MTIOCLQ, &mtlqarg_struct)) != 0) { fprintf(stderr, "3494 library query failed - quitting.\n" " Errno %d: %s\n", errno, strerror(errno)); switch((unsigned int)mtlqarg_struct.mtlqret.cc) { case MTCC_COMPLETE: puts("Complete - No error"); break; case MTCC_COMPLETE_VISION: puts("Complete - Vision not operational"); break; case MTCC_COMPLETE_NOTREAD: puts("Complete - VOLSER not readable"); break; case MTCC_COMPLETE_CAT: puts("Complete - Cat. assign. not changed "); break; case MTCC_CANCEL_PROGREQ: puts("Cancelled - Program request"); break; case MTCC_CANCEL_ORDERSEQ: puts("Cancelled - Order sequence"); break; case MTCC_CANCEL_MANMODE: puts("Cancelled - Manual mode"); break; case MTCC_FAILED_HARDWARE: puts("Failed - Hardware"); break; case MTCC_FAILED_VISION: puts("Failed - Vision not operational"); break; case MTCC_FAILED_NOTREAD: puts("Failed - VOLSER not readable"); break; case MTCC_FAILED_INACC: puts("Failed - Volume inaccessible"); break; case MTCC_FAILED_MISPLACED: puts("Failed - Volume misplaced"); break; case MTCC_FAILED_CATEMPTY: puts("Failed - Category empty"); break; case MTCC_FAILED_MANEJECT: puts("Failed - Volume manually ejected"); break; case MTCC_FAILED_INVENTORY: puts("Failed - Vol no longer in inventory"); break; case MTCC_FAILED_NOTAVAIL: puts("Failed - Device no longer available"); break; case MTCC_FAILED_LOADFAIL: puts("Failed - Permanent Load failure "); break; case MTCC_FAILED_DAMAGED: puts("Failed - Load Failure-Cartridge damaged"); break; case MTCC_COMPLETE_DEMOUNT: puts("Complete - Demount"); break; case MTCC_NO_LMCP: puts("LMCP is not configured"); break; case MTCC_NOT_CMDPORT_LMCP: puts("Device is not command-port LMCP"); break; case MTCC_NO_DEV: puts("Device is not configured"); break; case MTCC_NO_DEVLIB: puts("Device is not in library"); break; case MTCC_NO_MEM: puts("Not enough memory"); break; case MTCC_DEV_INUSE: puts("Device is in use"); break; case MTCC_IO_FAILED: puts("I/O failed"); break; case MTCC_DEV_INVALID: puts("Device is invalid"); break; case MTCC_NOT_NTFPORT_LMCP: puts("Device not notification-port LMCP"); break; case MTCC_INVALID_SUBCMD: puts("Invalid subcmd parameter"); break; case MTCC_LIB_NOT_CONFIG: puts("No library device is configured"); break; case MTCC_INTERNAL_ERROR: puts("Internal error"); break; case MTCC_INVALID_CANCELTYPE: puts("Invalid cancel type"); break; case MTCC_NOT_LMCP: puts("Not an LMCP device"); break; case MTCC_LIB_OFFLINE: puts("Library offline to host"); break; case MTCC_DRIVE_UNLOAD: puts("Volume not unloaded from drive"); break; case MTCC_COMMAND_TIMEOUT: puts("Device driver command timeout"); break; case MTCC_UNDEFINED: puts("Undefined completion code"); break; default: printf("Unknown condition code %d\n", mtlqarg_struct.mtlqret.cc); break; } /* Go on to close the file. */ } else { /*__________________________Library status retrieval succeeded - report_______________________________*/ printf("Type of information queried: "); /* Message will be completed below. */ switch(mtlqarg_struct.mtlqret.info.info_type) { case MT_QVD: puts("Query Volume Data"); break; case MT_QLD: puts("Query Library Data"); break; case MT_QSD: puts("Query Statistical Data"); break; case MT_QID: puts("Query inventory data"); break; case MT_QCID: puts("Query category inventory data"); break; case MT_QDD: puts("Query device data"); break; case MT_QEVD: puts("Query Expanded volume data"); break; case MT_QIVCD: puts("Query Inventory Volume Count Data"); break; case MT_QRCL: puts("Query Reserved Category List"); break; case MT_QCAL: puts("Query Category Attribute List"); break; default: puts("Query type unknown"); break; } /* Library state is a bitmask, so multiple simultaneous conditions may apply. */ printf("Library state:"); /* Line to be completed below. */ if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_AOS) printf(" Automated Operational State;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_POS) printf(" Paused Operational State;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_MMOS) printf(" Manual Mode Operational State;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_DO) printf(" Degraded Operation;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_SEIO) printf(" Safety Enclose Interlock Open;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_VSNO) printf(" Vision system Non-Operational;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_OL) printf(" Offline;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_IR) printf(" Intervention Required;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_CK1) printf(" Library Manager Check 1 Condition;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_SCF) printf(" All Storage Cells Full;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_OCV) printf(" Out of Cleaner Volumes;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_DWD) printf(" Dual Write Disabled;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_EA) printf(" Environmental Alert -- smoke detected;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_MMMOS) printf(" Managed Manual Mode Operational State;"); if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & MT_LIB_SWITCH) printf(" Library Manager switchover in progress;"); putchar('\n'); /* Finish the line. */ /* Also report any unrecognized state values. Do this by looking for any bits we don't know about. */ if (*(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state & ( (MT_LIB_AOS /* 0x8000 */ | MT_LIB_POS /* 0x4000 */ | MT_LIB_MMOS /* 0x2000 */ | MT_LIB_DO /* 0x1000 */ | MT_LIB_SEIO /* 0x0800 */ | MT_LIB_VSNO /* 0x0400 */ | MT_LIB_OL /* 0x0200 */ | MT_LIB_IR /* 0x0100 */ | MT_LIB_CK1 /* 0x0080 */ | MT_LIB_SCF /* 0x0040 */ | MT_LIB_OCV /* 0x0020 */ | MT_LIB_DWD /* 0x0010 */ | MT_LIB_EA /* 0x0008 */ | MT_LIB_MMMOS /* 0x0004 */ | MT_LIB_SWITCH /* 0x0002 */) ^ 0xffff) ) { printf("3494 library check returned unknown library state number 0x%04x\n", *(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.op_state); } /* Report number of available cleaner cycles. */ printf("Available 3590 cleaner cycles = %u\n", *(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.avail_3590_cleaner_cycles); /* Report number of input/output stations: bit0-3 input, bit4-7 out. * Expect it to yield numbers: 1, 1. */ printf("Number of input stations = %u; output stations = %u\n", (mtlqarg_struct.mtlqret.info.data.library_data.inout_no & 0xF0) >> 4, (mtlqarg_struct.mtlqret.info.data.library_data.inout_no & 0x0F) ); printf("Number of cartridge positions in each convenience station = %u\n", mtlqarg_struct.mtlqret.info.data.library_data.conven_cap); /* Accessor format */ /* --------------- */ /* bit 0:1 Accessor State */ /* 0 Not installed */ /* 1 Not operational */ /* 2 Not available */ /* 3 Available */ /* */ /* bit 2:3 Gripper 0 state */ /* 0 Not installed */ /* 1 Not operational */ /* 2 Not available */ /* 3 Available */ /* */ /* bit 4 Gripper 0 vision system */ /* operational */ /* */ /* bit 5:6 Gripper 1 state */ /* 0 Not installed */ /* 1 Not operational */ /* 2 Not available */ /* 3 Available */ /* */ /* bit 7 Gripper 1 vision system */ /* operational */ switch((mtlqarg_struct.mtlqret.info.data.library_data.acces0_stat & 0xc0) >> 6) { case 0: puts("Accessor 0 not installed."); break; case 1: puts("Accessor 0 not operational."); break; case 2: puts("Accessor 0 not available."); break; case 3: printf("Accessor 0 available (acces0_stat = %#x).\n", mtlqarg_struct.mtlqret.info.data.library_data.acces0_stat); /* Note that with a dual gripper, there is only 1 vision system, so bit 4 * governs both: bit 7 is not meaningful after all. */ switch((mtlqarg_struct.mtlqret.info.data.library_data.acces0_stat & 0x08) >> 3) { case 0: puts(" Gripper 0 vision system not operational."); break; case 1: puts(" Gripper 0 vision system operational."); break; } /* Bits 2:3 Gripper 0 state. */ switch((mtlqarg_struct.mtlqret.info.data.library_data.acces0_stat & 0x30) >> 4) { case 0: puts(" Gripper 0 not installed."); break; case 1: puts(" Gripper 0 installed but not operational!"); break; case 2: puts(" Gripper 0 installed but not available!"); break; case 3: puts(" Gripper 0 installed and available."); break; } /* Bits 5:6 Gripper 1 state. */ switch((mtlqarg_struct.mtlqret.info.data.library_data.acces0_stat & 0x06) >> 1) { case 0: puts(" Gripper 1 not installed."); break; case 1: puts(" Gripper 1 installed but not operational!"); break; case 2: puts(" Gripper 1 installed but not available!"); break; case 3: puts(" Gripper 1 installed and available."); break; } break; } /*_________________________________Report number of cells in library__________________________________*/ switch(n = sizeof(mtlqarg_struct.mtlqret.info.data.library_data.num_cells)) { case 1: printf("Number of storage cells in library = %u\n", *(unsigned char *)mtlqarg_struct.mtlqret.info.data.library_data.num_cells); break; case 2: printf("Number of storage cells in library = %u\n", *(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.num_cells); break; case 4: printf("Number of storage cells in library = %u\n", *(unsigned int *)mtlqarg_struct.mtlqret.info.data.library_data.num_cells); break; default: printf("Size of mtlibio.h num_cells is wacky (%u).", n); break; } /*___________________________Report number of available cells in library_____________________________*/ switch(n = sizeof(mtlqarg_struct.mtlqret.info.data.library_data.avail_cells)) { case 1: printf("Number of available cells in library = %u\n", *(unsigned char *)mtlqarg_struct.mtlqret.info.data.library_data.avail_cells); break; case 2: printf("Number of available cells in library = %u\n", *(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.avail_cells); break; case 4: printf("Number of available cells in library = %u\n", *(unsigned int *)mtlqarg_struct.mtlqret.info.data.library_data.avail_cells); break; default: printf("Size of mtlibio.h avail_cells is wacky (%u).", n); break; } /*____________________________Report number of subsystems in library_________________________________*/ switch(n = sizeof(mtlqarg_struct.mtlqret.info.data.library_data.num_subsys)) { case 1: printf("Number of subsystems in library = %u\n", *(unsigned char *)mtlqarg_struct.mtlqret.info.data.library_data.num_subsys); break; case 2: printf("Number of subsystems in library = %u\n", *(unsigned short *)mtlqarg_struct.mtlqret.info.data.library_data.num_subsys); break; case 4: printf("Number of subsystems in library = %u\n", *(unsigned int *)mtlqarg_struct.mtlqret.info.data.library_data.num_subsys); break; default: printf("Size of mtlibio.h num_subsys is wacky (%u).", n); break; } printf("Primary library mgr installed? %s\n", mtlqarg_struct.mtlqret.info.data.library_data.comp_avail_status[0] & 0x80 ? "Yes" : "No"); printf("Primary library mgr available? %s\n", mtlqarg_struct.mtlqret.info.data.library_data.comp_avail_status[0] & 0x40 ? "Yes" : "No"); printf("Secondary library mgr installed? %s\n", mtlqarg_struct.mtlqret.info.data.library_data.comp_avail_status[0] & 0x20 ? "Yes" : "No"); printf("Secondary library mgr available? %s\n", mtlqarg_struct.mtlqret.info.data.library_data.comp_avail_status[0] & 0x10 ? "Yes" : "No"); printf("Primary hard drive installed? %s\n", mtlqarg_struct.mtlqret.info.data.library_data.comp_avail_status[0] & 0x08 ? "Yes" : "No"); printf("Primary hard drive available? %s\n", mtlqarg_struct.mtlqret.info.data.library_data.comp_avail_status[0] & 0x04 ? "Yes" : "No"); printf("Secondary hard drive installed? %s\n", mtlqarg_struct.mtlqret.info.data.library_data.comp_avail_status[0] & 0x02 ? "Yes" : "No"); printf("Secondary hard drive available? %s\n", mtlqarg_struct.mtlqret.info.data.library_data.comp_avail_status[0] & 0x01 ? "Yes" : "No"); /*___________________________Report the state of the Input/Output Station___________________________*/ printf("Input/Output Station Status = %#x: Input is %s; Output is %s; Bulk I/O is %s and is %s;\n", mtlqarg_struct.mtlqret.info.data.library_data.in_out_status, mtlqarg_struct.mtlqret.info.data.library_data.in_out_status & 0x80 ? "empty" : "populated", mtlqarg_struct.mtlqret.info.data.library_data.in_out_status & 0x20 ? "empty" : mtlqarg_struct.mtlqret.info.data.library_data.in_out_status & 0x10 ? "full" : "partially filled", mtlqarg_struct.mtlqret.info.data.library_data.in_out_status & 0x04 ? "allowed" : "not allowed", mtlqarg_struct.mtlqret.info.data.library_data.in_out_status & 0x02 ? "full" : "not full"); /*___________________________Look for cartridges in the Convenience I/O passthrough___________________________*/ /* The Library manager will spontaneously cause exhausted cleaning cartridges to be output there. * The Operator Panel will have its Output Mode light light when there is a cartridge there. * The Operator Station (LCD) will have a System Status saying "Convenience I/O: Volumes present". * Note: A full Convenience I/O passthrough results in a Intervention Required (Int Req) condition. */ printf("Convenience Output status = %#0x:\n", mtlqarg_struct.mtlqret.info.data.library_data.in_out_status); /* The in_out_status value is a negative: it is set if the device is empty... */ if (mtlqarg_struct.mtlqret.info.data.library_data.in_out_status & 0x10) /* Bit 3 = all conv output full */ { /* The Convenience I/O is full... */ puts(" The Convenience I/O passthrough is full of cartridges"); } else if ((mtlqarg_struct.mtlqret.info.data.library_data.in_out_status & 0x20) == 0) /* Bit 2 = all conv output empty */ { /* The Convenience I/O is *not* all empty... */ puts(" There is at least one cartridge in the Convenience I/O passthrough"); } else { puts(" There are no cartridges in the Convenience I/O passthrough"); } /* We don't use the Bulk I/O area inside the first door, so won't check for it. */ } /*_______________________________________Demonstrate Library Event Wait_________________________________________________*/ /* In contrast to the periodic polling of the above sample logic, the logic here will issue MTIOCLEW, which is a * Magnetic Tape I/O Call Library Event Wait, which returns only after some event has occurred. That is, your program * is blocked from continuing until some library event occurs. * Note that MTIOCLEW is the basis of the library driver-provided 'mtevent' command. * In a real monitoring program, one might enclose the following logic in a while() loop, and ostensibly have it in a * child process of an active monitoring process. */ puts("\n================================ Demonstrate Library Event Wait ================================"); #define LEW_TIMEOUT_SECS 9999 while (1) { /*_______________________________________________Local variables______________________________________________________*/ time_t loop_start_time; time_t event_time; unsigned int state; /* Short variable name for operational state captured from very long name. */ loop_start_time = time((time_t *) 0); /* When this loop started. */ mtlewarg_struct.subcmd = LEWTIME; /* Wait for something to happen... */ mtlewarg_struct.timeout = LEW_TIMEOUT_SECS; /* up to this many seconds. */ printf("Will wait up to %u seconds for an event. Do Ctrl-c (cancel) to terminate..\n", LEW_TIMEOUT_SECS); rc = ioctl(mtlib_fd, MTIOCLEW, &mtlewarg_struct); /* Conclusion of the operation will yield a return code, and possibly an errno and condition code. */ event_time = time((time_t *) 0); /* When the event wait ended. */ printf("%s Event occurred after waiting %u seconds.\n", timestamp(), event_time - loop_start_time); if (rc != 0) { fprintf(stderr, "3494 library Library Event Wait failed - return code %d;\n" " Errno %d: %s\n", rc, errno, strerror(errno)); switch((unsigned int)mtlewarg_struct.mtlewret.cc) { case MTCC_COMPLETE: puts("Complete - No error"); break; case MTCC_COMPLETE_VISION: puts("Complete - Vision not operational"); break; case MTCC_COMPLETE_NOTREAD: puts("Complete - VOLSER not readable"); break; case MTCC_COMPLETE_CAT: puts("Complete - Cat. assign. not changed "); break; case MTCC_CANCEL_PROGREQ: puts("Cancelled - Program request"); break; case MTCC_CANCEL_ORDERSEQ: puts("Cancelled - Order sequence"); break; case MTCC_CANCEL_MANMODE: puts("Cancelled - Manual mode"); break; case MTCC_FAILED_HARDWARE: puts("Failed - Hardware"); break; case MTCC_FAILED_VISION: puts("Failed - Vision not operational"); break; case MTCC_FAILED_NOTREAD: puts("Failed - VOLSER not readable"); break; case MTCC_FAILED_INACC: puts("Failed - Volume inaccessible"); break; case MTCC_FAILED_MISPLACED: puts("Failed - Volume misplaced"); break; case MTCC_FAILED_CATEMPTY: puts("Failed - Category empty"); break; case MTCC_FAILED_MANEJECT: puts("Failed - Volume manually ejected"); break; case MTCC_FAILED_INVENTORY: puts("Failed - Vol no longer in inventory"); break; case MTCC_FAILED_NOTAVAIL: puts("Failed - Device no longer available"); break; case MTCC_FAILED_LOADFAIL: puts("Failed - Permanent Load failure "); break; case MTCC_FAILED_DAMAGED: puts("Failed - Load Failure-Cartridge damaged"); break; case MTCC_COMPLETE_DEMOUNT: puts("Complete - Demount"); break; case MTCC_NO_LMCP: puts("LMCP is not configured"); break; case MTCC_NOT_CMDPORT_LMCP: puts("Device is not command-port LMCP"); break; case MTCC_NO_DEV: puts("Device is not configured"); break; case MTCC_NO_DEVLIB: puts("Device is not in library"); break; case MTCC_NO_MEM: puts("Not enough memory"); break; case MTCC_DEV_INUSE: puts("Device is in use"); break; case MTCC_IO_FAILED: puts("I/O failed"); break; case MTCC_DEV_INVALID: puts("Device is invalid"); break; case MTCC_NOT_NTFPORT_LMCP: puts("Device not notification-port LMCP"); break; case MTCC_INVALID_SUBCMD: puts("Invalid subcmd parameter"); break; case MTCC_LIB_NOT_CONFIG: puts("No library device is configured"); break; case MTCC_INTERNAL_ERROR: puts("Internal error"); break; case MTCC_INVALID_CANCELTYPE: puts("Invalid cancel type"); break; case MTCC_NOT_LMCP: puts("Not an LMCP device"); break; case MTCC_LIB_OFFLINE: puts("Library offline to host"); break; case MTCC_DRIVE_UNLOAD: puts("Volume not unloaded from drive"); break; case MTCC_COMMAND_TIMEOUT: puts("Device driver command timeout"); break; case MTCC_UNDEFINED: puts("Undefined completion code"); break; default: printf("Unknown condition code %d\n", mtlewarg_struct.mtlewret.cc); break; } /* Go on to close the file. */ } else { /*__________________________Library Event Wait popped - report_______________________________*/ if (mtlewarg_struct.mtlewret.msg_type == NO_MSG) { puts("Nothing to report"); goto LEW_DONE; } else if (mtlewarg_struct.mtlewret.msg_type == UNSOL_ATTN_MSG) { puts("Unsolicited attention message"); } else if (mtlewarg_struct.mtlewret.msg_type == DELAY_RESP_MSG) { puts("Delayed response message"); /* This is the normal result of an event occurring. */ } switch(mtlewarg_struct.mtlewret.lib_event) { case MT_NTF_ERA60: puts("Library attachment facility equipment check"); break; case MT_NTF_ERA62: puts("Library manager offline to subsystem"); break; case MT_NTF_ERA63: puts("Control Unit and Library incompatible"); break; case MT_NTF_ERA64: puts("Volser in use"); break; case MT_NTF_ERA65: puts("Volume reserved"); break; case MT_NTF_ERA66: puts("Volser not in library"); break; case MT_NTF_ERA67: puts("Library category empty"); break; case MT_NTF_ERA68: puts("Library order equence check"); break; case MT_NTF_ERA69: puts("Output stations full"); break; case MT_NTF_ERA6B: puts("Misplaced volume"); break; case MT_NTF_ERA6C: puts("Misplaced volume found"); break; case MT_NTF_ERA6D: puts("Drive not unloaded"); break; case MT_NTF_ERA6E: puts("Inaccessible volume restored"); break; case MT_NTF_ERA6F: puts("Library vision failure"); break; case MT_NTF_ERA70: puts("Library manager equipment check"); break; case MT_NTF_ERA71: puts("Library equipment check"); break; case MT_NTF_ERA72: puts("Manual mode"); break; case MT_NTF_ERA73: puts("Library intervention required"); break; case MT_NTF_ERA74: puts("Informational data"); break; case MT_NTF_ERA75: puts("Inaccessible volume"); break; case MT_NTF_ERA76: puts("All cells full"); break; case MT_NTF_ERA77: puts("Duplicate volser ejected"); break; case MT_NTF_ERA78: puts("Duplicate volser in input station"); break; case MT_NTF_ERA79: puts("Unreadable or invalid volser in input station"); break; case MT_NTF_ERA7A: puts("Read library stats"); break; case MT_NTF_ERA7B: puts("Library volume manually ejected"); break; case MT_NTF_ERA7C: puts("No cleaner volumes left"); break; case MT_NTF_ERA7F: puts("Category is in use"); break; case MT_NTF_ERA80: puts("Unexpected Volume ejected"); break; case MT_NTF_ERA81: puts("I/O station door open"); break; case MT_NTF_ERA82: puts("Library manager program exception"); break; case MT_NTF_ERA83: puts("Library drive exception"); break; case MT_NTF_ERA84: puts("Library drive failure"); break; case MT_NTF_ERA85: puts("Library environmental alert"); break; case MT_NTF_ERA86: puts("All categories reserved"); break; case MT_NTF_ERA87: puts("Duplicate volume add requested"); break; case MT_NTF_ERA88: puts("Damaged Volume ejected"); break; case MT_NTF_ATTN_CSC: puts("Category state change"); break; case MT_NTF_ATTN_LMOM: puts("Library manager operator message"); break; case MT_NTF_ATTN_IOSSC: puts("I/O station state change"); break; case MT_NTF_ATTN_OSC: /* Transfer the state value to a var with a much less awkward name: */ switch(n = sizeof(mtlewarg_struct.mtlewret.msg_info.type_spec_msg_info.operational_state_chg.operational_state)) { case 1: state = *(unsigned char *)mtlewarg_struct.mtlewret.msg_info.type_spec_msg_info.operational_state_chg.operational_state; break; case 2: state = *(unsigned short *)mtlewarg_struct.mtlewret.msg_info.type_spec_msg_info.operational_state_chg.operational_state; break; case 4: state = *(unsigned int *)mtlewarg_struct.mtlewret.msg_info.type_spec_msg_info.operational_state_chg.operational_state; break; default: printf("Size of mtlibio.h operational_state is wacky (%u).", n); break; } printf("Operational state change, to: %s\n", state & MT_LIB_AOS ? " - Automated Operational State" : state & MT_LIB_POS ? " - Paused Operational State" : state & MT_LIB_MMOS ? " - Manual Mode Operational State" : state & MT_LIB_DO ? " - Degraded Operation" : state & MT_LIB_SEIO ? " - Safety Enclose Interlock Open" : state & MT_LIB_VSNO ? " - Vision system Non-Operational" : state & MT_LIB_OL ? " - Offline" : state & MT_LIB_IR ? " - Intervention Required" : state & MT_LIB_CK1 ? " - Library Manager Check 1 Condition" : state & MT_LIB_SCF ? " - All Storage Cells Full" : state & MT_LIB_OCV ? " - Out of Cleaner Volumes" : state & MT_LIB_DWD ? " - Dual Write Disabled" : state & MT_LIB_EA ? " - Environmental Alert -- smoke detected" : state & MT_LIB_MMMOS ? " - Managed Manual Mode Operational State" : " - Unknown Library condition"); break; case MT_NTF_ATTN_DAC: puts("Device availability change"); break; case MT_NTF_ATTN_DCC: puts("Device category change"); break; case MT_NTF_ATTN_VE: volser[0] = '\0'; strncat(volser, mtlewarg_struct.mtlewret.msg_info.type_spec_msg_info.volume_exception.volser, 6); printf("Volume exception: %6s\n", volser); break; case MT_NTF_DEL_MC: volser[0] = '\0'; strncat(volser, mtlewarg_struct.mtlewret.msg_info.type_spec_msg_info.volume_exception.volser, 6); printf("Mount complete: %6s\n", volser); break; case MT_NTF_DEL_DC: volser[0] = '\0'; strncat(volser, mtlewarg_struct.mtlewret.msg_info.type_spec_msg_info.volume_exception.volser, 6); printf("Demount complete: %6s\n", volser); break; case MT_NTF_DEL_AC: volser[0]='\0'; strncat(volser, mtlewarg_struct.mtlewret.msg_info.type_spec_msg_info.typeA_del_resp_msg.op_volser, 6); printf("Audit complete: Expected volser: %6s", volser); volser[0] = '\0'; strncat(volser, mtlewarg_struct.mtlewret.msg_info.type_spec_msg_info.typeA_del_resp_msg.read_volser, 6); printf(" Read volser: %6s\n", volser); break; case MT_NTF_DEL_EC: volser[0] = '\0'; strncat(volser, mtlewarg_struct.mtlewret.msg_info.type_spec_msg_info.volume_exception.volser, 6); printf("Eject complete: %6s\n", volser); break; case MT_NTF_TIMEOUT: puts("Timeout"); break; default: printf("Unknown library event code: %#0x\n", mtlewarg_struct.mtlewret.lib_event); break; } fflush(stdout); } LEW_DONE: ; } /* bottom of endless while-loop to await library events */ /*_______________________Done with library - close and exit__________________________*/ rc = close(mtlib_fd); exit(EXIT_SUCCESS); } /* end of main() */