1
0
Files
irix-657m-src/eoe/cmd/failover/foconfig.c
2022-09-29 17:59:04 +03:00

713 lines
18 KiB
C

#ident "cmd/failover/foconfig.c: $Revision: 1.5 $"
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <invent.h>
#include <string.h>
#include <dslib.h>
#include <sys/scsi.h>
#include <sys/dkio.h>
#include <sys/failover.h>
#define MAX_NAME_LENGTH 2048
#define CONFIGURATION_FILE_NAME "/etc/failover.conf"
int ps_scan ( char *string, char *argv[], int pointers);
vertex_hdl_t fo_get_lun_vhdl (char *canonical);
int fo_do_inquiry (char *canonical, char *inqdata, size_t length);
void fo_invalidate_candidates (int *candidate_table);
char *program_name=NULL;
int vflag=0;
void
usage(void)
{
fprintf (stderr,"%s [-d] [-f configuration_file] [[-v][-v]...]\n",program_name);
exit(1);
}
int
same_device(char *name1, char *name2)
{
char *tmpstr1;
char *tmpstr2;
int rc;
if (strncmp(name1, "target", 6) == 0)
return !strcmp(name1, name2);
/*
* Compare "node/<NUMBER>/" part of name.
*/
tmpstr1 = strstr(name1, "port");
tmpstr2 = strstr(name2, "port");
if (tmpstr1 == NULL || tmpstr2 == NULL)
return 0;
*tmpstr1 = *tmpstr2 = '\0';
rc = strcmp(name1, name2);
*tmpstr1 = *tmpstr2 = 'p';
if (rc)
return 0;
/*
* Move past "/port" in name
*/
tmpstr1 = strchr(tmpstr1, '/');
tmpstr2 = strchr(tmpstr2, '/');
if (tmpstr1 == NULL || tmpstr2 == NULL)
return 0;
/*
* Move to "/lun" in name
*/
tmpstr1++; tmpstr2++;
tmpstr1 = strchr(tmpstr1, '/');
tmpstr2 = strchr(tmpstr2, '/');
if (tmpstr1 == NULL || tmpstr2 == NULL)
return 0;
/*
* Compare rest of name.
*/
return !strcmp(tmpstr1, tmpstr2);
}
char *
get_input_line (char *buf, int buf_size, FILE *stream)
{
char *rv;
char *b=buf;
int space = buf_size;
int length;
/* fgets returns either NULL or the first argument */
while ((rv = fgets(b,space,stream)) == b) {
length = strlen(b);
if (*(b+length-2) != '\\') {
break;
}
*(b+length-1) = ' '; /* get rid of newline */
*(b+length-2) = ' '; /* get rid of backslash */
space -= length;
b += length;
}
return (rv ? buf : rv);
}
main (int argc, char *argv[])
{
char *input_file_name = CONFIGURATION_FILE_NAME;
FILE *input_file=NULL;
char *input_vector[128]; /* array of 128 pointers to hold token pointers */
int input_elements=sizeof(input_vector)/sizeof(char *);
int input_tokens;
char input_line[MAX_NAME_LENGTH]; /* input line buffer */
char input_copy[MAX_NAME_LENGTH]; /* input line copy */
char fo_canonical_name[MAX_FO_PATHS][MAX_NAME_LENGTH];
char fo_inquiry_data[MAX_FO_PATHS][SCSI_INQUIRY_LEN+1]; /* leave room for NULL */
size_t fo_inquiry_data_size = SCSI_INQUIRY_LEN+1;
size_t fo_inquiry_vendor_size = SCSI_DEVICE_NAME_SIZE;
int fo_inquiry_data_valid[MAX_FO_PATHS];
int fo_canonical_name_length = MAX_NAME_LENGTH;
char *fo_device_name[MAX_FO_PATHS];
char *fo_instance_name;
char *fo_candidate_name[MAX_FO_PATHS];
int fo_is_candidate[MAX_FO_PATHS];
vertex_hdl_t fo_lun_vhdl[MAX_FO_PATHS];
struct user_fo_generic_info fgi;
int volume_fd;
int status;
int i,j,k;
int c;
int dry_run=0;
int disable_disabled=0;
int keep_partitions=1;
extern char *optarg;
extern int optind;
/*
** Process command line options.
**
** Sets: c - character of current argument
** input_file_name - as specified for "-f"
** vflag - verbose output flag
** dry_run - from "-d"
** program_name - from argv[0]
*/
program_name = argv[0];
while ((c = getopt(argc,argv,"Ddf:kv")) != -1) {
switch (c) {
case 'D':
disable_disabled=1;
break;
case 'd':
dry_run = 1;
break;
case 'f': /* input file override */
input_file_name = optarg;
break;
case 'k':
keep_partitions = 1;
break;
case 'v': /* verbose output flag */
vflag++;
break;
default:
usage();
}
}
/*
** Open the failover configuration file.
**
** Input: vflag - verbose output flag
** program_name - as invoked
** input_file_name - from command line or default
**
** Exit: input_file - FILE handle
*/
if (vflag > 1) printf ("%s: opening %s.\n",program_name,input_file_name);
input_file = fopen (input_file_name, "r");
if (!input_file) {
fprintf (stderr,"%s: Unable to open failover configuration file %s.\n",
program_name,input_file_name);
exit(2);
}
if (vflag > 1) printf ("%s: Successfully opened %s.\n",program_name,input_file_name);
/*
** Read a line of the input file and process it. Repeat.
**
** Input: input_file - FILE handle
**
** Sets: input_line - as read from file
*/
if (vflag>1) printf ("%s: processing %s.\n",program_name,input_file_name);
while (get_input_line(input_line,sizeof(input_line),input_file) == input_line) {
strcpy (input_copy, input_line);
for (i=0; i<sizeof(input_copy); i++) {
if (input_copy[i] == '\n') {
input_copy[i] = '\0';
break;
}
}
if (vflag) printf ("Processing: %s\n",input_copy);
/*
** Initialize array elements.
**
** Sets: i - index into each array
** fgi
** fo_instance_name
** fo_lun_vhdl[]
** fo_device_name[]
** fo_is_candidate[]
** fo_canonical_name[][]
** fo_candidate_name[]
** fo_inquiry_data_valid[]
*/
bzero (&fgi,sizeof(fgi));
fo_instance_name = NULL;
for (i=0; i<MAX_FO_PATHS; i++) {
fo_lun_vhdl[i] = 0;
fo_device_name[i] = NULL;
fo_is_candidate[i] = 0;
fo_canonical_name[i][0] = NULL;
fo_candidate_name[i] = NULL;
fo_inquiry_data_valid[i] = 0;
}
/*
** Tokenize each line, extract elements.
**
** Input: input_line - line from file
** input_elements - elements in input_vector[] array
**
** Sets: input_tokens - number of tokens in each line
** input_vector - initialized by ps_scan()
** fo_device_name - pointer to each potential device
** fo_instance_name - first non-comment token
** i - index into input_vector
** j - index into fo_device_name[]
**
** Exit: j - number of potential devices
** fo_device_name[] - strings from input line
** fo_instance_name - name of fo instance
*/
input_tokens = ps_scan (input_line, input_vector, input_elements);
for (i=0, j=0; i<input_tokens && j<MAX_FO_PATHS; i++) { /* skips tokenless lines */
char *vector = input_vector[i];
if (*vector == '#') {
if (i == 0 && (strcmp(vector,"#dryrun") == 0)) {
dry_run = 1;
}
else if (i == 0 && (strcmp(vector,"#verbose") == 0)) {
vflag++;
}
else if (i == 0 && !disable_disabled && (strcmp(vector,"#disable") == 0)) {
printf ("%s: disable directive encountered. Exiting.\n",program_name);
exit(1);
}
break; /* comment line */
}
if (!fo_instance_name) {
if (*vector == 's' && *(vector+1) == 'c') {
printf ("%s: skipping line \'%s\': group name should not begin with \'sc\'.\n",
program_name, input_copy);
break;
}
fo_instance_name = input_vector[i];
}
else {
fo_device_name[j++] = input_vector[i];
}
}
if (!j) continue;
/*
** Validate each potential instance of failover devices.
** Get the canonical name of device and the lun vertex handle.
**
** Input: j - number of device names
** fo_canonical_name_length - max length of canonical path
** fo_device_name - pointer to each potential device
**
** Sets: i - index into fo_lun_vhdl[], fo_canonical_name[][0], fo_device_name[]
** fo_lun_vhdl[] - lun vertex handle associated with fo_device_name
** fo_canonical_name[] - set by fo_get_lun_vhdl() - absolute path
** fo_is_candidate[] - as appropriate
**
** Exit: j - number of potential devices
** fo_is_candidate[] - potential candidates for failover
*/
for (i=0; i<j; i++) {
int length;
char *status;
char path[MAX_NAME_LENGTH+10];
strcpy (path, "/hw/scsi/");
strcat (path, fo_device_name[i]);
length = fo_canonical_name_length; /* gets trashed by call! */
status = filename_to_devname(path,
&fo_canonical_name[i][0],
&length);
if (!status) {
fo_is_candidate[i] = 0;
if (vflag==1) printf ("%s: %s does not exist in system.\n",
program_name, fo_device_name[i]);
else if (vflag>1) printf ("%s: %s: Unable to convert filename %s to canonical name.\n",
program_name, strerror(errno), fo_device_name[i]);
continue;
}
fo_lun_vhdl[i] = fo_get_lun_vhdl (&fo_canonical_name[i][0]);
if (fo_lun_vhdl[i] == -1) {
fo_is_candidate[i] = 0;
if (vflag==1) printf ("%s: %s does not exist in system.\n",
program_name, fo_device_name[i]);
else if (vflag>1) printf ("%s: Unable to get lun vertex handle for %s.\n",
program_name, &fo_canonical_name[i][0]);
}
else {
fo_is_candidate[i] = 1;
if (vflag>1) {
printf ("%s: %s -> %s (lvh %d).\n",
program_name,
fo_device_name[i],
&fo_canonical_name[i][0],
fo_lun_vhdl[i]
);
}
}
}
/*
** Examine paths to acertain that they all point to the same target and lun.
** Add those that match to the fo_candidate_name[] array. Get inquiry data
** for potential candidates, and if valid, compare. Inquiry data not being
** valid is sufficient to cause device to not be a candidate.
** Valid inquiry that doesn't compare will cause no devices to be considered for failover.
**
** Input: fo_canonical_name[][] - canonical name
** fo_device_name[] - pointer to each potential device
** fo_is_candidate[] - device is valid so far
** j - number of potential devices
**
** Sets: i - index into fo_canonical_name[], fo_device_name[], fo_inquiry_data[][],
** fo_inquiry_data_valid[], fo_is_candidate[]
** fo_candidate_name[] - pointer to devices with matching target/lun
** fo_inquiry_data[][] - inquiry data returned from dksc ioctl
** fo_inquiry_data_valid[] - flag indicated inquiry data is valid
** fo_is_candidate[] - flag indicated device is candidate for failover
*/
{
char *scsi_ctlr;
char *target, *first_target=NULL;
int first_target_index;
int status;
for (i=0; i<j; i++) {
if (!fo_is_candidate[i]) continue;
scsi_ctlr = strstr (&fo_canonical_name[i][0],"scsi_ctlr");
if (scsi_ctlr == NULL) { /* not found */
fo_is_candidate[i] = 0;
if (vflag) printf ("%s: %s -> %s has no scsi controller.\n",
program_name, fo_device_name[i],&fo_canonical_name[i][0]);
}
else {
target = strstr (scsi_ctlr,"target");
if (target == NULL)
target = strstr(scsi_ctlr, "node");
if (target) {
bzero (&fo_inquiry_data[i][0],fo_inquiry_data_size);
if (!first_target) {
/*
** perform inquiry for first_target here
*/
fo_inquiry_data_valid[i] =
fo_do_inquiry (&fo_canonical_name[i][0],
&fo_inquiry_data[i][0],
fo_inquiry_data_size);
if (!fo_inquiry_data_valid[i]) {
if (vflag) {
printf ("%s: %s: Could not get inquiry data. Skipping instance.\n",
program_name,fo_instance_name);
}
fo_invalidate_candidates(fo_is_candidate);
break;
}
first_target = target;
first_target_index = i;
fo_candidate_name[i] = &fo_canonical_name[i][0];
}
else if (same_device(target, first_target)) { /* match */
/*
** perform inquiry here
*/
fo_inquiry_data_valid[i] =
fo_do_inquiry (&fo_canonical_name[i][0],
&fo_inquiry_data[i][0],
fo_inquiry_data_size);
if (!fo_inquiry_data_valid[i] ||
bcmp(&fo_inquiry_data[i][8],
&fo_inquiry_data[first_target_index][8],
fo_inquiry_vendor_size) != 0) {
fo_invalidate_candidates(fo_is_candidate);
if (vflag>1) {
printf ("%s: different inquiry data for %s and %s, skipping.\n",
program_name,
fo_device_name[first_target_index],
fo_device_name[i]);
printf ("%s: %s: %s.\n",
program_name,
fo_device_name[first_target_index],
&fo_inquiry_data[first_target_index][0]);
printf ("%s: %s: %s.\n",
program_name,
fo_device_name[i],
&fo_inquiry_data[i][0]);
}
break;
}
fo_candidate_name[i] = &fo_canonical_name[i][0];
}
else {
if (vflag) printf ("%s: %s and %s refer to different devices.\n",
program_name,
fo_device_name[first_target_index],
fo_device_name[i]);
fo_invalidate_candidates(fo_is_candidate);
break;
}
}
else {
if (vflag) printf ("%s: Unable to locate target in string %s.\n",
program_name,scsi_ctlr);
fo_invalidate_candidates(fo_is_candidate);
break;
}
}
}
}
for (i=0; i<j; i++) {
if (vflag && fo_is_candidate[i])
printf ("%s: candidate: %s -> %s\n",
program_name,
fo_device_name[i],
fo_candidate_name[i]);
}
/*
** Build the scsi_fo_generic_info table and call failover.
**
** Input: fo_canonical_name[][] - canonical name
** fo_is_candidate[] - device is valid so far
** fo_lun_vhdl[]
** fo_inquiry_data[][] - inquiry data returned from dksc ioctl
** fo_instance_name
** j - number of potential devices
**
** Sets: i - index into fo_canonical_name[], fo_inquiry_data[][],
** fo_is_candidate[], fo_lun_vhdl[]
** k - index into fgi_lun_vhdl[], fgi_inq_data[]
** fgi - fo_generic_info table initialized
** volume_fd - file descriptor of an associated volume/char
*/
for (volume_fd=-1, k=0, i=0; i<j; i++) {
if (fo_is_candidate[i]) {
fgi.fgi_lun_vhdl[k] = fo_lun_vhdl[i];
bcopy (&fo_inquiry_data[i][0],&fgi.fgi_inq_data[k][0],SCSI_INQUIRY_LEN);
k++;
if (volume_fd < 0) volume_fd = fo_open_volume (&fo_canonical_name[i][0]);
}
}
strncpy (fgi.fgi_instance_name,fo_instance_name,sizeof(fgi.fgi_instance_name)-1);
fgi.fgi_keep_partitions = keep_partitions;
if (!k) {
printf ("%s: %s: No candidates.\n",program_name, fo_instance_name);
}
else if (volume_fd < 0) { /* Couldn't open a dksc node for any member */
printf ("%s: %s: Cannot establish failover instance, cannot open any volume device.\n",
program_name,fo_instance_name);
}
else if (dry_run) {
printf ("%s: dry run: fgi_instance_name %s.\n",program_name,fgi.fgi_instance_name);
printf ("%s: dry run: fgi_lun_vhdl(s) ",program_name);
for (i=0; i<j; i++) printf ("%d ",fgi.fgi_lun_vhdl[i]);
printf ("\n");
for (i=0; i<j; i++) {
printf ("%s: dry run: inq string: %s.\n",program_name,&fgi.fgi_inq_data[i][8]);
}
close (volume_fd);
}
else {
status = ioctl (volume_fd,DIOCFOUPDATE,&fgi);
if (status < 0) {
printf ("%s: Failover instance not established for instance %s.\n",
program_name,fo_instance_name);
}
close (volume_fd);
}
}
}
void
fo_invalidate_candidates (int *candidate_table)
{
int i;
for (i=0; i<MAX_FO_PATHS; i++) *candidate_table++ = 0;
}
int
fo_open_volume (char *canonical)
{
int volume_fd=-1;
int found_lun=0;
char *lun, *lun_digit, *lun_slash;
char volume_name[MAX_NAME_LENGTH];
strcpy (volume_name, canonical);
lun = strstr (volume_name,"lun");
if (lun) {
lun_digit = strpbrk (lun,"0123456789");
if (lun_digit) {
found_lun = 1;
lun_slash = strpbrk (lun_digit,"/");
if (lun_slash) {
*lun_slash = NULL;
}
}
}
if (found_lun) {
strcat (volume_name, "/disk/volume/char");
volume_fd = open (volume_name, O_RDONLY);
}
return volume_fd;
}
int
fo_send_inquiry_command (struct dsreq *dsp, uchar_t *data, long datalen, int vu, int neg)
{
int fd;
int status;
fillg0cmd(dsp, (uchar_t *)CMDBUF(dsp), G0_INQU, 0, 0, 0, B1(datalen), B1(vu<<6));
filldsreq(dsp, data, datalen, DSRQ_READ|DSRQ_SENSE|neg);
dsp->ds_time = 1000 * 30; /* 90 seconds */
return doscsireq (getfd(dsp), dsp);
}
int
fo_do_inquiry (char *canonical, char *inqdata, size_t length)
{
int status = 0;
int volume_fd;
struct dsreq *dsp;
uchar_t *inqbuf = (uchar_t *)malloc(length);
bzero (inqbuf,length);
dsp = dsopen (canonical, O_RDONLY);
if (!dsp) {
printf ("dsopen of %s failure.\n",canonical);
}
else {
if ( fo_send_inquiry_command (dsp, inqbuf, length, 0, 0) ) {
printf ("inquiry failure for %s.\n", canonical);
}
else {
status = 1; /* status ok */
bcopy (inqbuf, inqdata, length);
}
}
dsclose (dsp);
return status;
}
vertex_hdl_t
fo_get_lun_vhdl (char *canonical)
{
char lun_name[MAX_NAME_LENGTH];
int i, j, found_lun=0;
char *lun, *lun_digit, *lun_slash;
vertex_hdl_t lvh = -1;
struct stat stat_buffer;
strcpy (lun_name, canonical);
lun = strstr (lun_name,"lun");
if (lun) {
lun_digit = strpbrk (lun,"0123456789");
if (lun_digit) {
found_lun = 1;
lun_slash = strpbrk (lun_digit,"/");
if (lun_slash) {
*lun_slash = NULL;
}
}
}
if (!found_lun) {
if (vflag) printf ("%s: Unable to locate lun for specified device: %s.\n",
program_name,canonical);
}
else {
if (stat(lun_name,&stat_buffer) >= 0) {
lvh = (vertex_hdl_t) stat_buffer.st_ino;
}
}
return lvh;
}
/********************************************************************************
* *
* ps_scan - Tokenize a null-terminated string. *
* *
* string - null terminated string to tokenize *
* argv - array of pointers to receive token addresses *
* pointers - number of pointers in argv array *
* *
* RETURNS: number of tokens encountered. *
* *
* Note: input string is modified by inserting NULL string terminators at the *
* end of each token. *
* *
* Borrowed from Cray Research, Inc., Giga-Ring software! *
* *
********************************************************************************/
int
ps_scan ( char *string, char *argv[], int pointers)
{
int argc = 0;
enum {SPACE, WORD} state = SPACE;
int i;
for (i=0; *string && argc<pointers; string++)
switch (state) {
case SPACE:
if (!isspace (*string)) {
argv[argc++] = string;
state = WORD;
}
break;
case WORD:
if (isspace (*string)) {
*string = '\0';
state = SPACE;
}
break;
}
return (argc);
}