/****************************************************************************** @ Copyright 2006 Hewlett-Packard Development Company, L.P. Confidential computer software. Valid license from HP required for possession, use or copying. Consistent with FAR 12.211 and 12.212, Commercial Computer Software, Computer Software Documentation, and Technical Data for Commercial Items are licensed to the U.S. Government under vendor’s standard commercial license. The information contained herein is subject to change without notice. The only warranties for HP products and services are set forth in the express warranty statements accompanying such products and services. Nothing herein should be construed as constituting an additional warranty. HP shall not be liable for technical or editorial errors or omissions contained herein. ******************************************************************************* FACILITY: Reliable Transaction Router Application template to manage group partition failover AUTHOR: Hewlett-Packard development company DESCRIPTION: MONGROUP is intended to give us the ability to Group Channel together. This application acts as a Partition Failover Manager, which controls failover of partitions which are grouped by user. The application reads an input ("input.txt ) file and prepares a consolidated information table which contains information such as facility name, partition name, group name, krid(key range ID) and partition state. The application is coded to scan the partition status using rtr_request_info() call at random time interval to avoid scanning of group partitions status at about the same time on all backend nodes. The random time interval is basically consists of two components (1) base timer value of 65 seconds + (2) Random number Based on the partition status, the application makes decisions. On OpenVMS: build using: $ cc /include= MONGROUP.C $ cc /include= REQ_INFO.C $ link MONGROUP,REQ_INFO,sys$input/opt sys$share:librtr/share ^Z ******************************************************************************/ # include # include # include # include # include "MONGROUP.h" # include # include # include extern get_gr_table(int, char **); extern void display_proc_id_table(void); extern void display_Current_list(void); void create_server_proc(void); /*** * Function : Modname_Extractor * * Description : Extract module name, mainly used in dumping information * * Return code : None * ***/ void Modname_Extractor(char mod_name[100]) { int i=0,j; char fullname[100]; memset(fullname,'\0',100); strcpy(fullname,__FILE__); while(fullname[i] != ']') i++; j=0; while(fullname[i] != '.') mod_name[j++] = fullname[++i]; mod_name[--j] = '\0'; } /*** * Function : nolead * * Description : This Function removes leading white spaces. * * Return code : Pointer to New String. * ***/ static char * nolead( char * str) { char * p; int ch; p = str; if (*p) { while ((ch = ((int)*p)) != 0) /* skip over leading spaces */ { if (isspace((unsigned char)ch)) { p++; } else { break; } } /* left shift starting from first non-space or eos */ if (p != str) { strcpy(str,p); } } return(str); } /*** * Function : Generate_Random_Number * * Description : This Function Generates Random Number * based on System Time and returns last * two digits of that Random Number. * * Return code : Last two digits of Random Number. * ***/ int Generate_Random_Number(void) { int rand_number; time_t t1; (void) time(&t1); srand48((long) t1); /* use time in seconds to set seed */ rand_number = lrand48(); rand_number = rand_number % 50; return(rand_number+15); } /*** * Function : notrail * * Description : This function removes trailing spaces. * * Return code : Pointer to string without trailing spaces. * ***/ static char * notrail( char * str ) { char * p; int ch; int len; if ((len = strlen(str)) != 0) { p = str + --len; while (p >= str) /* skip over trailing spaces */ { ch = (int)*p; if (isspace((unsigned char)ch)) { p--; } else { break; } } p++; *p = '\0'; } return(str); } /*** * Function : strtoken * * Description : Parse a string to extract the next token. * * Return code : pointer to character following the token. * ***/ char *strtoken( char *string, char *token ) { char delimiter; int base; while ( (*string == ' ') || (*string == '\t') || (*string == '\n') ) string++; if( ! *string ) { *token = '\0'; return string; } if ( (*string == '\"') || (*string == '\'') || (*string == '`') ) delimiter = *string++; else delimiter = '\0'; while ( *string != delimiter ) { base = 0; switch( *string ) { case '\\' : string++; switch( *string ) { case 'b' : *token++ = '\b'; string++; break; case 'f' : *token++ = '\f'; string++; break; case 'n' : *token++ = '\n'; string++; break; case 'r' : *token++ = '\r'; string++; break; case 't' : *token++ = '\t'; string++; break; case '\\': *token++ = '\\'; string++; break; case '\0': *token = '\\'; return string; case '\'': *token++ = '\''; string++; break; case 'x' : case 'X' : base = 16; case '0' : if ( ! base ) base = 8; case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : if ( ! base ) base = 10; { int loop, num; for ( loop = 0, num = 0; loop < 4 && isdigit(*string); loop++ ) num = num * base + ( *string++ - '0' ); *token++ = (char) num; break; } case 'E' : case 'e' : *token++ = '\033'; /* 033 is ESC */ string++; break; default: *token++ = *string++; break; } break; case ' ' : case '\t' : case '\n' : if ( ! delimiter ) { *token = '\0'; return string; } break; default : *token++ = *string++; break; } } /* end of while */ *token = '\0'; if ( *string ) string++; /* at the last delimiter */ return string; } /*** * Function : addrow * * Description : This Function reads information from input.txt file * and creates table containing information facility * name, partition name and group name. * * Return code : Void * ***/ /* *************** Create Link List *************** */ void addrow(char *fac_name, char *part_name,char *gr_name) { GROUP_INFO *newnode; newnode = (GROUP_INFO *)malloc(sizeof(GROUP_INFO)); memset(newnode,0,sizeof(GROUP_INFO)); strcpy (newnode->facility_name,fac_name); strcpy (newnode->partition_name,part_name); strcpy (newnode->group_name,gr_name); if (Head == NULL) { Head = newnode; Tail = newnode; newnode->pNext = NULL; } else { Tail->pNext = newnode; newnode->pNext = NULL; Tail = newnode; } } /*** * Function : display_list * * Description : This Function display information supplied by user * for the creation of group. * * Return code : Void * ***/ /* ****************** Display List ******************* */ void display_list(void) { GROUP_INFO *newnode; newnode = Head; while(newnode != NULL) { if(debug) dumplog_p3("Facility = %s,Part = %s, Group = %s",newnode->facility_name,newnode->partition_name, newnode->group_name); newnode = newnode ->pNext; } } /*** * Function : kill_application * * Description : This Function manages failover of group of partitions * based on the current states of partitions in * group. * * Return code : Void * ***/ /* ****************** Kill Application ******************* */ void kill_application(void) { char command[100] = ""; int j; char *arguments[3]; int number_of_arguments = 3; CURRENT_GROUP_INFO *newnode1, *newnode2, *newnode3,*newnode4,*newnode5; SERVER_PROC_INFO *SerInfoNode; KILL_PROC_FLAG *killnode; START_PROC_INFO *scriptnode; Ser_Proc_Head = NULL; Ser_Proc_Tail = NULL; newnode1 = Current_Gr_Head; /* ******** Create Per Partition Processes Table ********* * * Supply parameters to req_info.c file which internally uses * rtr_request_info function to get information from RTR based on * itemlist provided in servinfo.txt * */ get_table_flag = 0; arguments[0] = "vr\0"; arguments[1] = "servinfo.txt\0"; arguments[2] = "*\0"; get_gr_table(number_of_arguments,arguments); display_proc_id_table(); display_Current_list(); /* Scan throgh the table which contains information such as * facility name, group name, partition name, paritition state. * Based on the states of parititions in one group decision will * be taken. * * In this application we consider two partition state combinations, * * COMBINATIONS : * * Noserver - Active : Kill all the processes related to all partitions * in that particular group where such combination * is observed. * * Active - Standby : Set active standby flag for the group where * such combination is observed first time. If * application finds any such combination next time * it will kill all the processes related to all * partitions in that particular group where such * combination is observed. * * Noserver - Noserver : User can customize this section based on his needs. * * Noserver - Standby : User can customize this section based on his needs. * */ while(newnode1 != NULL) { if(newnode1->DirtyGrFlag == 1) { if(debug) dumplog_p1("Group %s is already processed, Go to next Group",newnode1->group_name); goto NextRow; } newnode2 = newnode1->pNext; while(newnode2 != NULL) { if( (!strcmp(newnode1->facility_name , newnode2->facility_name)) && (!strcmp(newnode1->group_name , newnode2->group_name)) && (strcmp(newnode1->partition_state,newnode2->partition_state) != 0) ) { if ( ( (!strcmp(newnode1->partition_state,"rsc_ps_no_servers")) && (!strcmp(newnode2->partition_state,"rsc_ps_active")) ) || ( (!strcmp(newnode1->partition_state,"rsc_ps_active")) && (!strcmp(newnode2->partition_state,"rsc_ps_no_servers")))) { newnode3 = Current_Gr_Head; while(newnode3 != NULL) { if(!strcmp(newnode3->group_name,newnode1->group_name)) { SerInfoNode = Ser_Proc_Head; while(SerInfoNode != NULL) { if((newnode3->partition_id == SerInfoNode->partition_id) && (!strcmp(newnode3->facility_name,SerInfoNode->facility_name))) { /* Kill apllication Since we have Active No Srever */ memset(command,'\0',sizeof(command)); # ifdef VMS strcat(command,"stop/id="); # else strcat(command,"kill "); # endif strcat(command,SerInfoNode->server_proc_id); if(debug) dumplog_p1("Killing %s", command); system(command); } SerInfoNode = SerInfoNode ->pNext; } } newnode3 = newnode3->pNext; } if(debug) dumplog_p0("Executing Startup Script to Start Your Application"); /* Restart all processes for the Specific group */ scriptnode = Start_Script_Head; while(scriptnode != NULL) { if(!strcmp(scriptnode->group_name,newnode2->group_name)) { if(debug) dumplog_p1("Restarting %s procedure",scriptnode->start_script_name); system(scriptnode->start_script_name); } scriptnode = scriptnode->pNext; } newnode5 = Current_Gr_Head; while(newnode5 != NULL) { if(!strcmp(newnode1->group_name,newnode5->group_name)) newnode5->DirtyGrFlag = 1; newnode5 = newnode5->pNext; } goto NextRow; } if ( ( (!strcmp(newnode1->partition_state,"rsc_ps_active")) && (!strcmp(newnode2->partition_state,"rsc_ps_standby")) ) || ( (!strcmp(newnode1->partition_state,"rsc_ps_standby")) && (!strcmp(newnode2->partition_state,"rsc_ps_active")) ) ) { if((newnode1->ASFlag == 1) && (newnode2->ASFlag == 1)) { goto NextRow; } if(debug) dumplog_p0("In Active - Standby"); killnode = Kill_Flag_Head; while(killnode != NULL) { /* One of the group Must Match Otherwise Error */ if(debug) dumplog_p1("Group Name : %s",newnode1->group_name); if(!strcmp(killnode->group_name,newnode1->group_name)) { if(killnode->Kill_Active_Standby == 0) { killnode->Kill_Active_Standby = 1; newnode4 = Current_Gr_Head; while(newnode4 != NULL) { if(!strcmp(newnode1->group_name,newnode4->group_name)) newnode4->ASFlag = 1; newnode4 = newnode4->pNext; } goto NextRow; } if(killnode->Kill_Active_Standby == 1) { newnode3 = Current_Gr_Head; while(newnode3 != NULL) { if(!strcmp(newnode3->group_name,newnode1->group_name)) { SerInfoNode = Ser_Proc_Head; while(SerInfoNode != NULL) { if((newnode3->partition_id == SerInfoNode->partition_id) && (!strcmp(newnode1->facility_name,SerInfoNode->facility_name))) { /* Kill apllication Since we have Active No Srever */ memset(command,'\0',sizeof(command)); # ifdef VMS strcat(command,"stop/id="); # else strcat(command,"kill "); # endif strcat(command,SerInfoNode->server_proc_id); if(debug) dumplog_p1("Killing %s", command); system(command); } SerInfoNode = SerInfoNode ->pNext; } } newnode3 = newnode3->pNext; } killnode->Kill_Active_Standby = 0; sleep(10); if(debug) dumplog_p0("Executing Startup Script to Start Your Application"); /* Restart all processes for the Specific group */ scriptnode = Start_Script_Head; while(scriptnode != NULL) { if(!strcmp(scriptnode->group_name,newnode2->group_name)) { if(debug) dumplog_p1("Restarting %s procedure",scriptnode->start_script_name); system(scriptnode->start_script_name); } scriptnode = scriptnode->pNext; } } } killnode = killnode ->pNext; } newnode5 = Current_Gr_Head; while(newnode5 != NULL) { if(!strcmp(newnode1->group_name,newnode5->group_name)) newnode5->DirtyGrFlag = 1; newnode5 = newnode5->pNext; } goto NextRow; } } newnode2 = newnode2->pNext; } NextRow : newnode1 = newnode1->pNext; } } /*** * Function : create_server_proc * * Description : This Function restarts all the processes related to * all the partitions in all groups. This function uses * processes information supplied by user in input.txt file * * Return code : Void * ***/ void create_server_proc(void) { START_PROC_INFO *scriptnode; scriptnode = Start_Script_Head; while(scriptnode != NULL) { system(scriptnode->start_script_name); scriptnode = scriptnode->pNext; } if(debug) dumplog_p0("Completed Execution"); } /*** * Function : create_list * * Description : This Function creates tabuler information of groups * from input.txt file in the form of link list. * * Return code : On Successful completion 0 * ***/ int create_list() { FILE *fp; char *getline,*getline1; char *tmp_fac_name; char *tmp_part_name; char *tmp_gr_name; char *find_char; char *arguments[3]; int number_of_arguments = 3; GROUP_INFO *newnode; int status; Head = NULL; Tail = NULL; Current_Gr_Head = NULL; Current_Gr_Tail = NULL; getline = malloc(Max_Getline_Size*sizeof(char)); tmp_fac_name = malloc( Max_Fac_Size*sizeof(char)); tmp_part_name = malloc(Max_Part_Size*sizeof(char)); tmp_gr_name = malloc(Max_Group_Size*sizeof(char)); memset(tmp_fac_name,'\0', Max_Fac_Size*sizeof(char)); memset(tmp_part_name,'\0',Max_Part_Size*sizeof(char)); memset(tmp_gr_name,'\0',Max_Group_Size*sizeof(char)); getline1 = getline; if( NULL == (fp = fopen("input.txt","r"))) { printf("\n Unable to Open File"); exit(-1); } fgets(getline,Max_Getline_Size,fp); while(!feof(fp)) { tmp_fac_name = nolead(tmp_fac_name); /* ******** Parse a string to extract the next token ******** */ if(getline[0] != '!') { getline = strtoken(getline,tmp_fac_name); getline = strtoken(getline,tmp_part_name); getline = strtoken(getline,tmp_gr_name); //addrow(tmp_fac_name,tmp_part_name,tmp_gr_name); /* Add New row to the Group Info Table */ newnode = (GROUP_INFO *)malloc(sizeof(GROUP_INFO)); strcpy (newnode->facility_name,tmp_fac_name); strcpy (newnode->partition_name,tmp_part_name); strcpy (newnode->group_name,tmp_gr_name); if (Head == NULL) { Head = newnode; Tail = newnode; newnode->pNext = NULL; } else { Tail->pNext = newnode; newnode->pNext = NULL; Tail = newnode; } } getline = getline1; memset(getline,'\0',Max_Getline_Size); fgets(getline,Max_Getline_Size,fp); } display_list(); arguments[0] = "vr\0"; arguments[1] = "backendinfo.txt\0"; arguments[2] = "*\0"; get_gr_table(number_of_arguments,arguments); fclose(fp); free(tmp_fac_name); free(tmp_part_name); free(tmp_gr_name); free(getline1); return 0; } /*** * Function : initialize_kill_flag * * Description : This Function initializes kill flag to zero for * all grouops. This flag is used by application * while taking decision of killing processes related * to all partitions in group. * * Return code : Void * ***/ void initialize_kill_flag(void) { FILE *fl; char tmp_buffer[100]; char *getline,*getline1; int insert_flag; KILL_PROC_FLAG *newnode,*newnode2; Kill_Flag_Head = NULL; Kill_Flag_Tail = NULL; getline = malloc(Max_Getline_Size*sizeof(char)); if( NULL == (fl = fopen("input.txt","r"))) { printf("\n Unable to Open File"); exit(-1); } memset(getline,'\0',Max_Getline_Size); fgets(getline,Max_Getline_Size,fl); getline1 = getline; getline = nolead(getline); while(!feof(fl)) { /* ******** Parse a string to extract the next token ******** */ if(getline[0] != '!') { getline = strtoken(getline,tmp_buffer); getline = strtoken(getline,tmp_buffer); memset(tmp_buffer,'\0',100); getline = strtoken(getline,tmp_buffer); if (Kill_Flag_Head == NULL) { newnode = (KILL_PROC_FLAG *)malloc(sizeof(KILL_PROC_FLAG)); strncpy (newnode->group_name,tmp_buffer,strlen(tmp_buffer)); newnode->Kill_Active_Standby = 0; Kill_Flag_Head = newnode; Kill_Flag_Tail = newnode; newnode->pNext = NULL; } else { newnode2 = Kill_Flag_Head; insert_flag = 0; while(newnode2 != NULL) { if(!strcmp(tmp_buffer,newnode2->group_name)) { insert_flag = 1; break; } newnode2 = newnode2->pNext; } if(insert_flag == 0) { newnode = (KILL_PROC_FLAG *)malloc(sizeof(KILL_PROC_FLAG)); strncpy (newnode->group_name,tmp_buffer,strlen(tmp_buffer)); newnode->Kill_Active_Standby = 0; Kill_Flag_Tail->pNext = newnode; newnode->pNext = NULL; Kill_Flag_Tail = newnode; } } } getline = getline1; memset(getline,'\0',Max_Getline_Size); fgets(getline,Max_Getline_Size,fl); getline = nolead(getline); } fclose(fl); free(getline1); } /*** * Function : display_kill_flag * * Description : This Function display kill flag information * of all groups. * * Return code : Void * ***/ void display_kill_flag(void) { KILL_PROC_FLAG *newnode; newnode = Kill_Flag_Head; while(newnode != NULL) { if(debug) dumplog_p2("Group = %s, Flag = %d",newnode->group_name,newnode->Kill_Active_Standby); newnode = newnode->pNext; } } /*** * Function : initialize_process_restart * * Description : This Function initializes process restart * information. This information is used while * restarting processes in any particular group. * Process restart information is provided by user * in the input.txt file. * * Return code : Void * ***/ void initialize_process_restart(void) { FILE *fl; char tmp_group_name[Max_Group_Size]; char tmp_part_name[Max_Part_Size]; char tmp_start_script_name[Max_Start_Script_Name]; char tmp_buffer[100]; char *getline1; char *getline; int insert_flag; START_PROC_INFO *newnode,*newnode2; Start_Script_Head = NULL; Start_Script_Tail = NULL; getline = malloc(Max_Getline_Size*sizeof(char)); getline1 = getline; if( NULL == (fl = fopen("input.txt","r"))) { printf("\n Unable to Open File"); exit(-1); } memset(getline,'\0',1024); fgets(getline,Max_Getline_Size,fl); getline = nolead(getline); while(!feof(fl)) { /* ******** Parse a string to extract the next token ******** */ if(getline[0] != '!') { getline = strtoken(getline,tmp_buffer); memset(tmp_part_name,'\0',Max_Part_Size); getline = strtoken(getline,tmp_part_name); memset(tmp_group_name,'\0',Max_Group_Size); getline = strtoken(getline,tmp_group_name); memset(tmp_start_script_name,'\0',Max_Start_Script_Name); getline = strtoken(getline,tmp_start_script_name); if (Start_Script_Head == NULL) { newnode = (START_PROC_INFO *)malloc(sizeof(START_PROC_INFO)); strncpy (newnode->group_name,tmp_group_name,strlen(tmp_group_name)); strncpy (newnode->partition_name,tmp_part_name,strlen(tmp_part_name)); strncpy (newnode->start_script_name,tmp_start_script_name,strlen(tmp_start_script_name)); Start_Script_Head = newnode; Start_Script_Tail = newnode; newnode->pNext = NULL; } else { newnode2 = Start_Script_Head; insert_flag = 0; while(newnode2 != NULL) { if(!strcmp(tmp_group_name,newnode2->group_name)) if(!strcmp(tmp_part_name,newnode2->partition_name)) insert_flag = 1; newnode2 = newnode2->pNext; } if(insert_flag == 0) { newnode = (START_PROC_INFO *)malloc(sizeof(START_PROC_INFO)); strncpy (newnode->group_name,tmp_group_name,strlen(tmp_group_name)); strncpy (newnode->partition_name,tmp_part_name,strlen(tmp_part_name)); strncpy (newnode->start_script_name,tmp_start_script_name,strlen(tmp_start_script_name)); Start_Script_Tail->pNext = newnode; newnode->pNext = NULL; Start_Script_Tail = newnode; } } } getline = getline1; memset(getline,'\0',1024); fgets(getline,Max_Getline_Size,fl); getline = nolead(getline); } free (getline1); fclose(fl); } /*** * Function : display_initialize_process * * Description : This Function displays process restart information * of all groups. * * Return code : Void * ***/ void display_initialize_process(void) { START_PROC_INFO *newnode; newnode = Start_Script_Head; while(newnode != NULL) { if(debug) dumplog_p3("Group = %s, Part = %s , Script = %s",newnode->group_name,newnode->partition_name,newnode->start_script_name); newnode = newnode->pNext; } } /*** * Function : void clean_tables * * Description : This Function deallocates all the memory used by * various tables. * * Return code : Void * ***/ void clean_tables(void) { GROUP_INFO *freenode1, *freenode2; CURRENT_GROUP_INFO *freenode_current_gr1,*freenode_current_gr2; SERVER_PROC_INFO *freenode_serv_proc_info1,*freenode_serv_proc_info2; freenode1 = Head; freenode_current_gr1 = Current_Gr_Head; freenode_serv_proc_info1 = Ser_Proc_Head; while(freenode1 != NULL) { freenode2 = freenode1; freenode1 = freenode1->pNext; free(freenode2); } while(freenode_current_gr1 != NULL) { freenode_current_gr2 = freenode_current_gr1; freenode_current_gr1 = freenode_current_gr1->pNext; free(freenode_current_gr2); } while(freenode_serv_proc_info1 != NULL) { freenode_serv_proc_info2 = freenode_serv_proc_info1; freenode_serv_proc_info1 = freenode_serv_proc_info1->pNext; free(freenode_serv_proc_info2); } Head = NULL; Current_Gr_Head = NULL; Ser_Proc_Head = NULL; Tail = NULL; Current_Gr_Tail = NULL; Ser_Proc_Tail = NULL; } int main(int argc, char **argv) { int rand1; int no_of_arg; debug=0; for (no_of_arg = 1; no_of_arg < argc; no_of_arg++) { /* Check for DEBUG TRACE */ if((!strcmp(argv[1], "debug")) || (!strcmp(argv[1], "DEBUG"))); { debug = 1; } } Ser_Proc_Head = NULL; Ser_Proc_Tail = NULL; initialize_kill_flag(); display_kill_flag(); initialize_process_restart(); display_initialize_process(); /* Channel Group Manager application starts all the processes * associated with all the partition in any group at the start. */ create_server_proc(); sleep(10); while(1) { get_table_flag = 1; create_list(); get_table_flag = 0; kill_application(); clean_tables(); rand1 = Generate_Random_Number(); if(debug) dumplog_p1("Waiting for 65 + %d",rand1); sleep(65 + rand1); } return 0; }