/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* */ /* */ /* Licensed Materials - Property of IBM */ /* */ /* (C) COPYRIGHT International Business Machines Corp. 1998,2021 */ /* All Rights Reserved */ /* */ /* US Government Users Restricted Rights - Use, duplication or */ /* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* */ /* IBM_PROLOG_END_TAG */ static const char *sccsid = "@(#)52 1.12 src/rsct/pgs/samples/Sample_Subscribe.C, gssamples, rsct_rady, radys003a 6/6/21 12:23:55"; #if !defined(_HAGSD_COPYRIGHT_H) #define _HAGSD_COPYRIGHT_H static const char copyright[] = "Licensed Materials - Property of IBM\n\ (C) COPYRIGHT International Business Machines Corp. 1998,2001.\n\ All Rights Reserved.\n\ US Government Users Restricted Rights - Use, duplication or \n\ disclosure restricted by GSA ADP Schedule Contract with IBM Corp.\n"; #endif /*********************************************************************/ /* * Name: Sample_Subscribe.C * * This program's purpose in life is quite simple: it wants to take * as input a group name and some optional flags. It will connect to * Group Services, subscribe to the given group, get the desired data, * then sit and wait for further notifications, or unsubscribe and exit. * * For usage syntax and help info, see the WriteHelpInfo() function. * * While this may not seem too exciting, it does do certain things that * can make your life a bit easier. Group Services maintains the * provider membership list in "age" order - the first provider to join * the group is at the front, the last is at the end. This is very * helpful from a programmatic point, because a group can use this * feature to easily "elect" a group leader, if they so choose. It is * the provider who has been around the longest. * * However, in certain cases for human eyes this is troublesome. An * easy example is that you have 100 nodes (or, 500) and expect to have * one provider on each node. But, using lssrc Group Services states * that there are 99 providers in the group! Who is missing? If you * use "sample_test" to subscribe, it will always print the membership * list in 'raw' format - the same order in which Group Services * maintains it. Which makes it very difficult to figure out from * which node is a provider missing. * * Therefore, this program offers three options in how to display the * membership list: * 'raw' -- straight printout in same order as the list is presented * to us by Group Services. * 'ordered' - sorted by node number, lowest first. * 'tabular' - table printout, displaying each "frame" and the providers * on each node in that "frame". * * One more note, regarding the group's state value. The program will * atempt to print it, but many group state values may be unprintable * characters (e.g., array of ints). Thus, the program will display it * three ways: * - as characters, replacing non-printable characters with '.'. (All * characters are assumed to be single-byte.) * - if the total length of the group state value is an integral value, * print as an array of ints. * - display the hex equivalents of the entire group state value. */ /*********************************************************************/ #if defined(__linux__) || defined(__INTERIX) || defined(__sun) #define NFDS(a) a #endif /*********************************************************************/ /* * Include the classes used by this program. */ /*********************************************************************/ #include "Sample_Subscribe.h" // Common declarations. #include "Sample_Subscription.h" // Handle the subscriptions extern "C" unsigned int sleep (unsigned int Seconds); Subscription *subscription; // The subscription to our group. char *outputFileName; // Name of the output file. ofstream outputFile; // Output stream, if not cout. // Callback functions. GSSAMPLE_EXTERN_C void DelayedErrorCB(const ha_gs_delayed_error_notification_t *_note); GSSAMPLE_EXTERN_C void SubscriptionCB(const ha_gs_subscription_notification_t *_note); /*********************************************************************/ /* * Write out help info. */ /*********************************************************************/ void WriteHelpInfo(void) { cerr << "Sample_Subscribe group_name < [-a] [-m] [-j] [-l] [-s] > [-r|-o] [-p]" << endl; cerr << " [-c flag] [-d domain] [-u subsystem] [-f output_file] [-t] [-h]" << endl; cerr << "group_name -- required, is the group name for subscription." << endl; cerr << endl; cerr << " Subscription controls:" << endl; cerr << " At least one of the following must be given." << endl; cerr << "-a -- if given, subscribe for membership changes, with deltas" << endl; cerr << "-m -- if given, subscribe for membership changes, no deltas" << endl; cerr << "-j -- if given, subscribe for membership join change deltas" << endl; cerr << "-l -- if given, subscribe for membership leave change deltas" << endl; cerr << "-s -- if given, subscribe for group state value changes" << endl; cerr << " Specifying -a is equivalent (and overrides) specifying '-m -j -l'." << endl; cerr << endl; cerr << "-p -- if given, be persistent, remain connected and print" << endl; cerr << " out notifications (use Ctrl-C, or kill signal to stop" << endl; cerr << " in this case.) If not given, disconnect and exit" << endl; cerr << " after first notification." << endl; cerr << endl; cerr << "-c flag -- if given, when we receive a subscription notification" << endl; cerr << " that includes any optional \"special\" data, print out" << endl; cerr << " the contents of the special data. The flag controls " << endl; cerr << " types of special data to print. Flag values:" << endl; cerr << " 1 -- print out the \"adapter death array\" if given." << endl; cerr << " 2 -- print out the \"current alias array\" if given." << endl; cerr << " 4 -- print out the \"changing alias array\" if given." << endl; cerr << " To display multiple of these, specify a flag value with" << endl; cerr << " one or more of the above values added together." << endl; cerr << " If -c is not given, then print only the flags that specify what" << endl; cerr << " special data was received, but not the full contents of the data." << endl; cerr << endl; cerr << " Display controls:" << endl; cerr << " If neither -r nor -o are given, display a tabular format" << endl; cerr << " translating node numbers to frame/node number pairs." << endl; cerr << "-r -- if given, do \"raw\" display, use node numbers when" << endl; cerr << " displaying membership, display in order given by" << endl; cerr << " Group Services in the notification." << endl; cerr << "-o -- if given, sort the output in node number order, lowest" << endl; cerr << " first." << endl; cerr << endl; cerr << " Domain control:" << endl; cerr << " You can specify the Group Service domain to which the program " << endl; cerr << " connects by setting HA_SYSPAR_NAME in your environment to the " << endl; cerr << " name of the domain (SP partition name), or by using the -d flag." << endl; cerr << " If neither is set, the program exits with an error." << endl; cerr << "-d domain -- specifies the Group Services domain. If this is given," << endl; cerr << " any setting in HA_SYSPAR_NAME is overridden." << endl; cerr << endl; cerr << " Group Services subsystem control:" << endl; cerr << " By default, connect to the SP Realm (PSSP) Group Services daemon." << endl; cerr << " You can override this by specifying the subsystem name, either" << endl; cerr << " setting PGSD_SUBSYS in your environment or using this flag." << endl; cerr << "-u subsys -- specify the Group Services subsystem. \"hags\" connects" << endl; cerr << " to the PSSP daemon (default). \"grpsvcs\" connects to" << endl; cerr << " the HA daemon (if HACMP/ES is installed and running.)" << endl; cerr << " If given, this overrides PGSD_SUBSYS, if it is set." << endl; cerr << endl; cerr << " Connect to test daemon:" << endl; cerr << "-t -- ignore any settings of HA_SYSPAR_NAME and PGSD_SUBSYS." << endl; cerr << endl; cerr << "-f output_file -- specifies a file for output. If not given, use" << endl; cerr << " stdout." << endl; cerr << endl; cerr << "-h -- display this info, and exit." << endl; cerr.flush(); return; } /*********************************************************************/ /* * Redirect cout into the specified file. This allows us to simply use * "cout" everywhere. */ /*********************************************************************/ int RedirectCout(char *_outputFileName) { int _rc = 0; outputFile.open( _outputFileName, ios::out ); if (outputFile.good()) { //cout = *outputFile; cerr << "log file is opened." << endl; } else { cerr << "ERROR: could not open log file: " << _outputFileName << "." << endl; _rc = -1; } return(_rc); } /*********************************************************************/ /* * Basically just parse input args, connect to Group Services, and rip * into it! */ /*********************************************************************/ int main(int argc, char ** argv) { /*********************************************************************/ /* * This is the file descriptor of the socket that will connect this * program to Group Services. It will be initialized when we call * ha_gs_init(). */ ha_gs_descriptor_t socketFD; /*********************************************************************/ /* * This defines the "default" responsiveness settings used for the * ha_gs_init() call. */ ha_gs_responsiveness_t auto_responsiveness = {HA_GS_NO_RESPONSIVENESS, 0, 0, (char *)0, 0}; /*********************************************************************/ /* * These variables represent various data types used to send and/or * receive data to/from Group Services. Refer to the IBM High * Availability Group Services manual for full descriptions. */ ha_gs_socket_ctrl_t socket_ctrl = HA_GS_SOCKET_NO_SIGNAL; ha_gs_rc_t rc; /*********************************************************************/ /* * These variables are used to control the subscription setup. */ int subscriptionControl; // For what to control? char *subscriptionGroupName; // Group name to subscribe to. Subscription::providerDisplayType displayType; // Type of display for right now. Subscription::specialDisplayType displaySpecial; // Display special data. int persistentConnexion; // Stay connected? const char *hagsDomain; // Name of the GS domain. const char *hagsSubsys; // Hags subsystem. int hagsTestConn; // Ignore domain and subsystem? /*********************************************************************/ /* * These variables are used to control the select() system call. Please * refer to the AIX documentation for a full descriptions of select(). */ int select_rc; int highestDescriptor; int howMany; int commandFD; fd_set socketsForSelect; /* Maintain all registered sockets in mask. */ fd_set socketSelectMask; /* Used for the actual select. */ struct timeval nextJob; /* Wait time for select. */ /*********************************************************************/ /* * Set defaults. */ displayType = Subscription::kTable; // Assume we want pretty display. displaySpecial = Subscription::kShort; // Just print out flag values. persistentConnexion = 0; // Do not stay connected. subscriptionControl = Subscription::kNothing; // Nothing yet specified. subscriptionGroupName = NULL; // Group name. outputFileName = NULL; // Using cout for now. hagsDomain = NULL; // No domain yet specified. hagsSubsys = NULL; // No subsystem yet specified. hagsTestConn = 0; // Do not connect to a test domain. /*********************************************************************/ /* * Verify command-line arguments, if any. */ if (2 > argc) { cerr << "ERROR: Must specify a group name for subscription!" << endl; WriteHelpInfo(); exit(-10); } else { if (!strcmp( argv[1], "-h")) { WriteHelpInfo(); exit(0); } subscriptionGroupName = argv[1]; } for(int argCtr = 2; argCtr < argc; argCtr++) { if(!strcmp( argv[argCtr], "-r")) { displayType = Subscription::kRaw; } else if(!strcmp( argv[argCtr], "-o")) { displayType = Subscription::kOrdered; } else if(!strcmp( argv[argCtr], "-p")) { persistentConnexion = 1; } else if(!strcmp( argv[argCtr], "-h")) { WriteHelpInfo(); exit(0); } else if(!strcmp( argv[argCtr], "-a")) { subscriptionControl |= Subscription::kAllMembership; } else if(!strcmp( argv[argCtr], "-m")) { subscriptionControl |= Subscription::kMembership; } else if(!strcmp( argv[argCtr], "-j")) { subscriptionControl |= Subscription::kJoins; } else if(!strcmp( argv[argCtr], "-l")) { subscriptionControl |= Subscription::kLeaves; } else if(!strcmp( argv[argCtr], "-s")) { subscriptionControl |= Subscription::kState; } else if(!strcmp( argv[argCtr], "-c")) { displaySpecial = (Subscription::specialDisplayType)atoi(argv[++argCtr]); } else if(!strcmp( argv[argCtr], "-f")) { outputFileName = argv[++argCtr]; } else if(!strcmp( argv[argCtr], "-d")) { hagsDomain = argv[++argCtr]; } else if(!strcmp( argv[argCtr], "-u")) { hagsSubsys = argv[++argCtr]; } else if(!strcmp( argv[argCtr], "-t")) { hagsTestConn = 1; } else { cerr << "Invalid flag [" << argv[argCtr] << "] given." << endl; WriteHelpInfo(); exit(-20); } } /*********************************************************************/ /* * Ensure user has specified a domain for us to connect to, unless -t * was given, in which case simply let the environment do whatever it * wants to do! Normal assumption in such a case is that PGSD_SUPP_SOCK * is set with the actual socket path. */ if (!hagsTestConn) { if (NULL == hagsDomain) { // Nothing given via -d. if (NULL == (hagsDomain = getenv("HA_DOMAIN_NAME"))) { if (NULL == (hagsDomain = getenv("HA_SYSPAR_NAME"))) { outputFile << "**PeerDomain is assumed. " << "If it is HACMP or PSSP, " << "HA_DOMAIN_NAME or HA_SYSPAR_NAME must be specified." << endl; hagsDomain = "PeerDomain"; } } } else { char *hagsDomain2 = new char[strlen("HA_SYSPAR_NAME=") + strlen(hagsDomain) + 2]; sprintf(hagsDomain2, "HA_SYSPAR_NAME=%s", hagsDomain); putenv(hagsDomain2); } if (NULL != hagsSubsys) { char *hagsSubsys2 = new char[strlen("PGSD_SUBSYS=") + strlen(hagsSubsys) + 2]; sprintf(hagsSubsys2, "PGSD_SUBSYS=%s", hagsSubsys); putenv(hagsSubsys2); } else { hagsSubsys = "cthags"; /* peer domain */ } } /*********************************************************************/ /* * Set up structures based on parameters. */ if (!Subscription::Valid(subscriptionControl)) { cerr << "ERROR: Must specify valid subscription type!" << endl; WriteHelpInfo(); exit(-2); } if (NULL != outputFileName) { if (-1 == RedirectCout(outputFileName)) { exit(-3); } } /*********************************************************************/ /* * Establish defaults for select(). */ nextJob.tv_sec = 3600; /* Wait one hour for select. */ nextJob.tv_usec = 0; highestDescriptor = 0; FD_ZERO(&socketsForSelect); /* No sockets yet. */ socketFD = -1; /* No socket connection to Group Services yet. */ /*********************************************************************/ /* * Processing as follows: * - init with Group Services, try 10 times, if unsuccessful, display * error and exit. * - once initted, subscribe, and wait in select. * - if get error (e.g., NO GROUP) display error and exit. * - get subscription data, display it. * - if not persistent, exit. * - if persistent, wait in select, display each notification as it * arrives, until we get killed. */ for (int _tries = 9; 0 <= _tries; _tries--) { // Only need delayed error callback, since we use NO_RESPONSIVENESS. rc = ha_gs_init(&socketFD, socket_ctrl, &auto_responsiveness, NULL, NULL, DelayedErrorCB, NULL); if (HA_GS_OK == rc) { outputFile << "Connected to Group Services [subsys: " << hagsSubsys << " domain: " << hagsDomain << "]." << endl; FD_SET(socketFD, &socketsForSelect); if (socketFD > highestDescriptor) { highestDescriptor = socketFD; } break; } else if (HA_GS_CONNECT_FAILED == rc) { if (0 < _tries) { cerr << "Could not connect to Group Services [subsys: " << hagsSubsys << " domain: " << hagsDomain << "]." << endl; cerr << " Attempt " << _tries << " more time"; if (1 == _tries) { cerr << "." << endl; } else { cerr << "s." << endl; } sleep(10); } else { cerr << "Could not connect to Group Services [subsys: " << hagsSubsys << "domain: " << hagsDomain << "]." << endl; cerr << " No more attempts! Giving up." << endl; exit(-4); } } else { cerr << "Fatal error connecting to Group Services [subsys: " << hagsSubsys << "domain: " << hagsDomain << "]." << endl; cerr << " Error code [" << WriteAnRC(rc) << "]" << endl; exit(rc); } } // for(init tries) /*********************************************************************/ /* * Set up the subscription. Must be connected to get here. */ subscription = new Subscription(subscriptionGroupName, subscriptionControl, persistentConnexion, displayType, displaySpecial, SubscriptionCB); if (subscription->Bad()) { cerr << "ERROR: Could not establish subscription to [" << subscriptionGroupName << "]. Give up and exit." << endl; exit(subscription->ErrorCode()); } /*********************************************************************/ /* * Sit in select processing. */ while(1) { /*********************************************************************/ /* * Set up and call select(). */ /* * Load all desired file descriptors into the select mask, then * call select. We use "nextJob" as a timer, to pop out of select() * if no input arrives (default time 3600 seconds). */ nextJob.tv_sec = 3600; /* Wait one hour for select. */ nextJob.tv_usec = 0; memcpy(&socketSelectMask, &socketsForSelect, sizeof(socketsForSelect)); select_rc = select(highestDescriptor + 1, &socketSelectMask, 0, 0, &nextJob); if (select_rc < 0) { /* * If rc < 0, then an error occured. If select was interrupted, * then no worries, just start select() over. Any other error, * give up. */ if (errno == EINTR) { cerr << "Got EINTR during the select. Continue." << endl; continue; } else { cerr << "ERROR: Error [" << strerror(errno) << "] on select. Give up and exit." << endl; exit(errno); } } else if (0 < (howMany = NFDS(select_rc))) { /* * Input has arrived on one or more of our file descriptors. * We normally expect to have 1 -- the socket to Group Services. */ if (1 < howMany) { cerr << "ERROR: Input on more than one sockets?? Have [" << howMany << "]. Give up!" << endl; exit(howMany); } rc = ha_gs_dispatch(HA_GS_NON_BLOCKING); if (HA_GS_OK != rc) { cerr << "ERROR: Bad news, bad return code from dispatch[" << WriteAnRC(rc) << "]. Give up and exit." << endl; exit(rc); } // If not persistent, we are done. The dispatch call was either // the initial subscription notification, or an error. In either // case, time to leave unless we really have to stick around. if (!subscription->Persistent()) { delete subscription; // Kill the subscription. break; } } else { /* * Select() simply timed out. Display something to show * that the program is alive, then just restart select(). */ outputFile << "."; } outputFile.flush(); // Ensure output goes out. } // while(1) outputFile << "Thank you for using our services. Please call again." << endl; exit(0); } // main() /*********************************************************************/ /* * Handle a delayed error notification. Essentially just pass through * to the established subscription object. */ /*********************************************************************/ GSSAMPLE_EXTERN_C void DelayedErrorCB(const ha_gs_delayed_error_notification_t *_note) { subscription->HandleDelayedError(_note); return; } /*********************************************************************/ /* * Handle a subscription notification. Essentially just pass through * to the established subscription object. */ /*********************************************************************/ GSSAMPLE_EXTERN_C void SubscriptionCB(const ha_gs_subscription_notification_t *_note) { subscription->HandleNotification(_note); return; } /*********************************************************************/ /* * Utility to return the "name" of a Group Services error code. */ /*********************************************************************/ GSSAMPLE_EXTERN_C const char *WriteAnRC(ha_gs_rc_t _pRC) { switch(_pRC) { case HA_GS_OK: return("HA_GS_OK"); case HA_GS_NOT_OK: return("HA_GS_NOT_OK"); case HA_GS_EXISTS: return("HA_GS_EXISTS"); case HA_GS_NO_INIT: return("HA_GS_NO_INIT"); case HA_GS_NAME_TOO_LONG: return("HA_GS_NAME_TOO_LONG"); case HA_GS_NO_MEMORY: return("HA_GS_NO_MEMORY"); case HA_GS_NOT_A_MEMBER: return("HA_GS_NOT_A_MEMBER"); case HA_GS_BAD_CLIENT_TOKEN: return("HA_GS_BAD_CLIENT_TOKEN"); case HA_GS_BAD_MEMBER_TOKEN: return("HA_GS_BAD_MEMBER_TOKEN"); case HA_GS_BAD_PARAMETER: return("HA_GS_BAD_PARAMETER"); case HA_GS_UNKNOWN_GROUP: return("HA_GS_UNKNOWN_GROUP"); case HA_GS_INVALID_GROUP: return("HA_GS_INVALID_GROUP"); case HA_GS_NO_SOURCE_GROUP_PROVIDER: return("HA_GS_NO_SOURCE_GROUP_PROVIDER"); case HA_GS_BAD_GROUP_ATTRIBUTES: return("HA_GS_BAD_GROUP_ATTRIBUTES"); case HA_GS_WRONG_OLD_STATE: return("HA_GS_WRONG_OLD_STATE"); case HA_GS_DUPLICATE_INSTANCE_NUMBER: return("HA_GS_DUPLICATE_INSTANCE_NUMBER"); case HA_GS_COLLIDE: return("HA_GS_COLLIDE"); case HA_GS_SOCK_CREATE_FAILED: return("HA_GS_SOCK_CREATE_FAILED"); case HA_GS_SOCK_INIT_FAILED: return("HA_GS_SOCK_INIT_FAILED"); case HA_GS_CONNECT_FAILED: return("HA_GS_CONNECT_FAILED"); case HA_GS_VOTE_NOT_EXPECTED: return("HA_GS_VOTE_NOT_EXPECTED"); case HA_GS_NOT_SUPPORTED: return("HA_GS_NOT_SUPPORTED"); case HA_GS_INVALID_SOURCE_GROUP: return("HA_GS_INVALID_SOURCE_GROUP"); #ifndef SAMPLE_22 case HA_GS_UNKNOWN_PROVIDER: return("HA_GS_UNKNOWN_PROVIDER"); case HA_GS_INVALID_DEACTIVATE_PHASE: return("HA_GS_INVALID_DEACTIVATE_PHASE"); case HA_GS_PROVIDER_APPEARS_TWICE: return("HA_GS_PROVIDER_APPEARS_TWICE"); #endif /* !SAMPLE_22 */ default: return("Unknown error code"); } } /*********************************************************************/ /* * Write a time stamp. */ /*********************************************************************/ GSSAMPLE_EXTERN_C const char *WriteTheTime(void) { struct timeval current_time; struct timezone tz; static char tod[32]; /* extract part of string. */ char *cTod; gettimeofday(¤t_time, &tz); /* Grab month/day/time only. */ cTod = ctime(¤t_time.tv_sec); memcpy(tod, cTod+4, 16); tod[15] = '\0'; return(tod); } /*********************************************************************/ /* * Determine how many characters are needed to display the given * provider ID. Calculation is: * (length of instance) + (length of node) + 2 [for " " and "/"]. */ /*********************************************************************/ int DisplayLen(short _val) { int _len = 0; short _abs = abs(_val); if (10 > _abs) _len = 1; else if (100 > _abs) _len = 2; else if (1000 > _abs) _len = 3; else if (10000 > _abs) _len = 4; else _len = 5; if (0 > _val) _len++; return(_len); } int PrintLength(short _inst, short _node) { int _len = DisplayLen(_inst) + DisplayLen(_node); return(_len + 2); }