//
// CamCategory.c++
//
// Base class for Categories which use the cam service.
//
//
// Copyright (c) 1998, 2000 Silicon Graphics, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2.1 of the GNU Lesser General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it would be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// Further, this software is distributed without any warranty that it is
// free of the rightful claim of any third person regarding infringement
// or the like. Any license provided herein, whether implied or
// otherwise, applies only to this software file. Patent licenses, if
// any, provided herein do not apply to combinations of this program
// with other software, or any other product whatsoever.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307,
// USA.
//
// Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
// Mountain View, CA 94043, or http://www.sgi.com/
//
// For further information regarding this notice, see:
// http://oss.sgi.com/projects/GenInfo/NoticeExplan/
//
#ident "1.1"
#include <string.h>
#include <sysadm/AppContext.h>
#include <sysadm/Log.h>
#include <fsmgr/CamCategory.h>
#include <ctype.h>
#include <ci_clikeys.h>
#include <sysadm/format.h>
#include <sysadm/i18n.h>
#include <unistd.h>
#include <sys/param.h>
namespace fsmgr {
using namespace sysadm;
//
// These are here for populating CamCategory::_typeTable, because
// _typeTable is a DictionaryOf and it needs pointers to its
// elements.
//
const Attribute::EType CamCategory::STRING = Attribute::STRING;
const Attribute::EType CamCategory::LONG = Attribute::LONG;
const Attribute::EType CamCategory::BOOLEAN = Attribute::BOOLEAN;
const Attribute::EType CamCategory::DOUBLE = Attribute::DOUBLE;
DictionaryOf<const Attribute::EType> CamCategory::_typeTable;
//
// Table of invisible Attributes keys. These are the keys of
// Attributes that should not go into log files, such as passwords.
//
DictionaryOf<String> CamCategory::_invisibleAttrs;
//
// CamCategory constructor.
//
CamCategory::CamCategory(const String& selector, cam_category_t category)
: Category(selector), _numCategories(0), _numInventories(0),
_itemCache(NULL), _handle(NULL), _inputId(-1)
{
initTypeTable();
addCategory(category);
}
//
// CamCategory destructor.
//
CamCategory::~CamCategory()
{
if (_inputId != -1) {
AppContext::getAppContext().unregisterMonitor(_inputId);
}
if (_handle != NULL) {
cam_close(_handle);
}
if (_itemCache != NULL) {
for (int ii = 0; ii < _numCategories; ii++) {
RemoveAndDestroyContentsOf(&_itemCache[ii]);
}
delete [] _itemCache;
}
}
#ifdef CAM_WAR
//
// This is a workaround for bugs 626229, 626513
//
void CamCategory::addItem(const Item& newItem)
{
ConstIteratorOver<Item> iter(getItems());
Item* item;
while ((item = iter()) != NULL) {
if (item->getSelector() == newItem.getSelector()) {
Log::debug("CamCategory", "Mapping add to change for %s",
(const char*)newItem.getSelector());
changeItem(newItem);
return;
}
}
Category::addItem(newItem);
}
#endif
//
// void CamCategory::addCategory(cam_category_t category)
//
// Description:
// Add a category to be monitored.
//
// Parameters:
// category cam category to be monitored.
//
void CamCategory::addCategory(cam_category_t category)
{
assert(_numCategories < MAX_CATEGORIES);
// Illegal to call addCategory() after calling startMonitor().
assert(!isMonitoring());
_categories[_numCategories++] = category;
}
//
// void CamCategory::startMonitor()
//
// Description:
// Called when it's time to start monitoring. Set up our
// connection with libcam.
//
void CamCategory::startMonitor()
{
Log::trace(getSelector(), "startMonitor()");
assert(_numCategories > 0);
cam_error_t error;
_handle = cam_open(NULL, &error);
if (_handle == NULL) {
char buf[Log::MAXLEN];
char hostname[MAXHOSTNAMELEN];
if (gethostname(hostname, MAXHOSTNAMELEN) != 0) {
strcpy(hostname, "server");
}
switch (error) {
case cam_error_no_server:
SaStringFormat(buf, sizeof buf,
i18n("Unable to connect to CAM daemon on %s. "
"Try running \"/etc/rc.d/init.d/fs_cluster start\" "
"on %s as root."),
hostname,
hostname);
break;
default:
SaStringFormat(buf, sizeof buf,
i18n("Unable to open connection with CAM. "
"Error: %d."),
error);
}
// If the call to notifyError(buf) is present below, the GUI
// displays the error message and terminates. If it's removed, the
// AppContext::exit() will cause the "connection lost - retry?"
// dialog to be shown. The notifyError() was removed because cad
// was dying too often, and was put back as part of a fix for
// bug 767083.
notifyError(buf);
Log::fatal(getSelector(), buf);
AppContext::getAppContext().exit(1);
return;
}
_itemCache = new DictionaryOf<Item>[_numCategories];
// Register categories in reverse order so we get inventories from
// status categories first -- if they come at all. This is a
// workaround for bug 631445.
for (int ii = _numCategories - 1; ii >= 0; ii--) {
if (cam_register(_handle, _categories[ii]) == -1) {
char buf[Log::MAXLEN];
error = cam_error(_handle);
// FilesystemCategory registers cam_category_none
// intentionally & expects it to fail; see the comment in
// category/filesystem/FilesystemCategory.c++.
if (_categories[ii] == cam_category_none) {
continue;
}
switch (error) {
case cam_error_license:
SaStringFormat(buf, sizeof buf,
i18n("No CXFS license found. See the CXFS "
"Administration Guide for more "
"information."));
break;
default:
SaStringFormat(buf, sizeof buf,
i18n("Unable to register category %d. "
"Error: %d."),
_categories[ii],
error);
}
// See notifyError() comment above.
notifyError(buf);
Log::fatal(getSelector(), buf);
AppContext::getAppContext().exit(1);
return;
}
}
_inputId = AppContext::getAppContext().
registerMonitor(cam_get_select_fd(_handle),
handleInput, this, AppContext::INPUT);
}
#ifdef CAM_WAR
//
// bool CamCategory::checkCategory(cam_category_t category)
//
// Description:
// Check to see if "category" is one of the categories we're
// monitoring. This is to work around a bug in libcam that
// causes us to get events for categories we did not register
// for.
//
// Parameters:
// category cam category to check.
//
// Returns:
// true if this is a category we're registered for, false
// otherwise.
//
bool CamCategory::checkCategory(cam_category_t category)
{
for (int ii = 0; ii < _numCategories; ii++) {
if (_categories[ii] == category) {
return true;
}
}
Log::error(getSelector(), "Got an event for foreign category %d",
category);
return false;
}
#endif
//
// void CamCategory::handleInput(void* clientData, int /*id*/, int /*fd*/)
//
// Description:
// Called when we receive some input from libcam's file
// descriptor. Get a cam_event and parse it into a Category
// notification.
//
// Parameters:
// clientData CamCategory* (this is a static method)
//
void CamCategory::handleInput(void* clientData, int /*id*/, int /*fd*/)
{
CamCategory* self = (CamCategory*)clientData;
Log::trace(self->getSelector(), "handleInput()");
const cam_event_t* event = cam_get_event(self->_handle);
if (event == NULL) {
cam_error_t error = cam_error(self->_handle);
if (error != cam_error_again) {
char buf[Log::MAXLEN];
SaStringFormat(buf, sizeof buf,
i18n("Unable to get CAM event. Error: %d."), error);
self->notifyError(buf);
Log::fatal(self->getSelector(), buf);
AppContext::getAppContext().exit(1);
}
return;
}
if ((event->valid & CAM_EV_ACTION) == 0) {
cam_free_event(self->_handle, event);
return;
}
switch (event->action) {
default:
case cam_action_none:
break;
case cam_action_add:
self->handleAdded(*event);
break;
case cam_action_inventory:
self->handleInventory(*event);
break;
case cam_action_modify:
self->handleModify(*event);
break;
case cam_action_remove:
self->handleRemove(*event);
break;
}
cam_free_event(self->_handle, event);
}
//
// void CamCategory::addOrChangeObject(const cam_object_t& obj)
//
// Description:
// Deal appropriately with an object that libcam told us about.
// If we already know about the object; change it. Otherwise,
// add it.
//
// Parameters:
// obj object to add or change.
//
void CamCategory::addOrChangeObject(const cam_object_t& obj)
{
String selector(getSelectorFromObject(obj));
bool alreadyExisted = _itemCache[0].lookupByKey(selector);
Item* item(createItemFromObject(obj));
if (item == NULL) {
return;
}
modifyItemOnCamEvent(item);
// Don't actually add the Item unless it was from category 0. If
// it wasn't from category 0 but we have a corresponding Item in
// category 0, that means we've already added an object with this
// selector and so we need to do a change instead.
//
// If it's from a category other than category 0 and there is no
// corresponding Item in category 0, we will wait until we get an
// add for this Item in category 0 before adding to our Category.
if (alreadyExisted) {
CamCategory::changeItem(*item);
} else if (_itemCache[0].lookupByKey(selector)) {
CamCategory::addItem(*item);
}
delete item;
}
//
// void CamCategory::handleAdded(const cam_event_t& event)
//
// Description:
// Handle a libcam added action.
//
// Parameters:
// event A libcam added action.
//
void CamCategory::handleAdded(const cam_event_t& event)
{
Log::trace(getSelector(), "handleAdded()");
if ((event.valid & CAM_EV_OBJECT) == 0) {
return;
}
#ifdef CAM_WAR
if (!checkCategory(event.data.object->category)) {
return;
}
#endif
addOrChangeObject(*event.data.object);
}
//
// void CamCategory::handleInventory(const cam_event_t& event)
//
// Description:
// Handle a libcam inventory action.
//
// Parameters:
// event A libcam inventory action.
//
void CamCategory::handleInventory(const cam_event_t& event)
{
Log::trace(getSelector(), "handleInventory()");
#ifdef CAM_WAR
if (!checkCategory(event.data.inventory->category)) {
return;
}
#endif
if ((event.valid & CAM_EV_INVENTORY) != CAM_EV_INVENTORY
|| (event.data.inventory->valid & CAM_IV_ALL) != CAM_IV_ALL) {
return;
}
if (event.data.inventory->num_objects > 0) {
beginBlockChanges();
for (int ii = 0; ii < event.data.inventory->num_objects; ii++) {
addOrChangeObject(*event.data.inventory->objects[ii]);
}
endBlockChanges();
}
#ifdef CAM_WAR
if (event.data.inventory->num_objects == 0) {
Log::debug("CamCategory",
"Received inventory of zero (0) objects on category %d",
event.data.inventory->category);
beginBlockChanges();
// find category
for (int ii = 0; ii < _numCategories; ii++) {
if (_categories[ii] == event.data.inventory->category) {
DictionaryOfIterator<Item> iter(&_itemCache[ii]);
Item* item;
while ((item = iter()) != NULL) {
String selector(item->getSelector());
Log::debug("CamCategory",
"Clearing object %s in category %d",
(const char*)selector,
event.data.inventory->category);
Item toChange(getSelector(), selector);
bool removeIt = false;
for (int jj = 0; jj < _numCategories; jj++) {
if (_categories[jj] == event.data.inventory->category) {
delete _itemCache[jj].remove(selector);
if (jj == 0) {
removeIt = true;
}
} else {
copyAttrs(_itemCache[jj].lookupByKey(selector),
toChange);
}
}
// Don't remove the Item unless this remove notification is
// coming from category 0. If this notification is coming
// from a category other than category 0, and we have an
// Item in category 0, then change the Item in the
// Category. This will effectively strip off the
// attributes corresponding to the cam object we're getting
// removal notification for.
if (removeIt) {
removeItem(selector);
} else if (_itemCache[0].lookupByKey(selector) != NULL) {
modifyItemOnCamEvent(&toChange);
Log::debug("CamCategory",
"Cleared object %s",
(const char*)toChange.toString());
changeItem(toChange);
}
}
}
}
endBlockChanges();
}
#endif
if (!hasExistsEnded()
&& event.data.inventory->category == _categories[0]) {
endExists();
}
}
//
// void CamCategory::handleModify(const cam_event_t& event)
//
// Description:
// Handle a libcam modify action.
//
// Parameters:
// event a libcam modify action.
//
void CamCategory::handleModify(const cam_event_t& event)
{
Log::trace(getSelector(), "handleModify()");
if ((event.valid & CAM_EV_OBJECT) == 0) {
return;
}
#ifdef CAM_WAR
if (!checkCategory(event.data.object->category)) {
return;
}
#endif
Item* item(createItemFromObject(*event.data.object));
if (item == NULL) {
return;
}
modifyItemOnCamEvent(item);
// Don't change it unless we've already added it. We won't have
// added it unless it's in the cache for category 0.
if (_itemCache[0].lookupByKey(item->getSelector()) != NULL) {
changeItem(*item);
}
delete item;
}
//
// void CamCategory::handleRemove(const cam_event_t& event)
//
// Description:
// Handle a libcam remove action.
//
// Parameters:
// event a libcam remove action.
//
void CamCategory::handleRemove(const cam_event_t& event)
{
Log::trace(getSelector(), "handleRemove()");
if ((event.valid & CAM_EV_OBJECT) == 0
|| (event.data.object->valid & CAM_OV_NAME) != CAM_OV_NAME) {
return;
}
#ifdef CAM_WAR
if (!checkCategory(event.data.object->category)) {
return;
}
#endif
String selector(getSelectorFromObject(*event.data.object));
Item toChange(getSelector(), selector);
bool removeIt = false;
for (int ii = 0; ii < _numCategories; ii++) {
if (_categories[ii] == event.data.object->category) {
delete _itemCache[ii].remove(selector);
if (ii == 0) {
removeIt = true;
}
} else {
copyAttrs(_itemCache[ii].lookupByKey(selector), toChange);
}
}
// Don't remove the Item unless this remove notification is coming
// from category 0. If this notification is coming from a
// category other than category 0, and we have an Item in category
// 0, then change the Item in the Category. This will effectively
// strip off the attributes corresponding to the cam object we're
// getting removal notification for.
if (removeIt) {
removeItem(selector);
} else if (_itemCache[0].lookupByKey(selector) != NULL) {
modifyItemOnCamEvent(&toChange);
changeItem(toChange);
}
}
//
// void CamCategory::modifyItemOnCamEvent(Item* item)
//
// Description:
// Create any attributes that need to be derived from the original object.
// This is called before calling Category::addItem and
// Category::changeItem.
//
// Parameters:
// item Item for which to derive any additional attributes
//
void CamCategory::modifyItemOnCamEvent(Item*)
{
// by default there are no additional attributes
}
//
// Item* CamCategory::createItemFromObject(const cam_object_t& obj)
//
// Description:
// Create an Item that corresponds to "obj".
//
// Parameters:
// obj cam_object to create an Item for.
//
// Returns:
// An Item for "obj".
//
Item* CamCategory::createItemFromObject(const cam_object_t& obj)
{
#define OBJECT_MASK (CAM_OV_NAME | CAM_OV_NUM_INFO | CAM_OV_INFO | \
CAM_OV_CATEGORY)
if ((obj.valid & OBJECT_MASK) != OBJECT_MASK) {
return NULL;
}
String selector(getSelectorFromObject(obj));
Item* cacheItem = new Item(getSelector(), selector);
for (int i = 0; i < obj.num_info; i++) {
if ((obj.info[i]->valid & CAM_OIV_ALL) == CAM_OIV_ALL) {
const char* key = obj.info[i]->key;
const Attribute::EType* type = _typeTable.lookupByKey(String(key));
if (type == NULL) {
// Maybe this is a key like _KEY_1, in which case we
// remove the digits at the end and try again.
if (isdigit(key[strlen(key)-1])) {
char * keycopy = strdup(key);
int pos = strlen(keycopy) -1;
while (isdigit(keycopy[pos])) {
keycopy[pos--] = '\0';
}
type = _typeTable.lookupByKey(String(keycopy));
free(keycopy);
if (type == NULL) {
type = &STRING;
}
} else {
type = &STRING;
}
}
cacheItem->setAttr(Attribute(key, *type, obj.info[i]->value));
if (_invisibleAttrs.lookupByKey(String(key)) != NULL) {
cacheItem->setAttrVisible(key, false);
}
}
}
Item* item = new Item(*cacheItem);
for (int ii = 0; ii < _numCategories; ii++) {
if (_categories[ii] == obj.category) {
delete _itemCache[ii].remove(selector);
// Note that at this point _itemCache owns cacheItem, so
// that we should not delete it here.
_itemCache[ii].add(cacheItem, new String(selector));
} else {
copyAttrs(_itemCache[ii].lookupByKey(selector), *item);
}
}
return item;
}
//
// String CamCategory::getSelectorFromObject(const cam_object_t& obj)
//
// Description:
// Called to get the selector from an object.
//
// Parameters:
// obj Object to get selector from.
//
// Returns:
// Selector for obj.
//
String CamCategory::getSelectorFromObject(const cam_object_t& obj)
{
return obj.name;
}
//
// void CamCategory::copyAttrs(const Item* source, Item& dest)
//
// Description:
// Copy Attributes from one item to another.
//
// Parameters:
// source Source of Attributes to copy.
// dest Destination of Attributes to copy.
//
void CamCategory::copyAttrs(const Item* source, Item& dest)
{
if (source == NULL) {
return;
}
CollectionOf<Attribute> attrs(source->copyAttrList());
IteratorOver<Attribute> iter(&attrs);
Attribute* attr;
while ((attr = iter()) != NULL) {
dest.setAttr(*attr);
}
}
//
// void CamCategory::initTypeTable()
//
// Description:
// Populate our lookup table that maps attribute names to types.
// By default, Attributes are Strings. The table below maps
// attribute keys (defined in ci_clikeys.h) to other types. This
// table is used in createItemFromObject().
//
void CamCategory::initTypeTable()
{
if (_typeTable.getSize() == 0) {
_typeTable.add(&LONG, new String(CICLI_NODEID));
_typeTable.add(&LONG, new String(CICLI_NUM_ACTIONS));
_typeTable.add(&LONG, new String(CICLI_NUM_CLUSTERS));
_typeTable.add(&LONG, new String(CICLI_NUM_CTRLNETS));
_typeTable.add(&LONG, new String(CICLI_NUM_DEPENDENCIES));
_typeTable.add(&LONG, new String(CICLI_NUM_FAILOVER_ATTRS));
_typeTable.add(&LONG, new String(CICLI_NUM_FAILOVER_SCRIPTS));
_typeTable.add(&LONG, new String(CICLI_NUM_IN_FAILOVER_AFD));
_typeTable.add(&LONG, new String(CICLI_NUM_LOG_FILES));
_typeTable.add(&LONG, new String(CICLI_NUM_LOG_GROUPS));
_typeTable.add(&LONG, new String(CICLI_NUM_LOG_SUBSYSTEMS));
_typeTable.add(&LONG, new String(CICLI_NUM_MACHINES));
_typeTable.add(&LONG, new String(CICLI_NUM_RESOURCE_KEYS));
_typeTable.add(&LONG, new String(CICLI_NUM_RESOURCE_TYPES));
_typeTable.add(&LONG, new String(CICLI_PRIORITY_));
_typeTable.add(&BOOLEAN, new String(CICLI_HB_));
_typeTable.add(&BOOLEAN, new String(CICLI_CTRL_));
_typeTable.add(&LONG, new String(CICLI_ACT_MONINTERVAL));
_typeTable.add(&LONG, new String(CICLI_ACT_STARTMONTIME));
_typeTable.add(&LONG, new String(CICLI_ACT_MAXEXECTIME));
_typeTable.add(&LONG, new String(CICLI_RT_ORDER));
_typeTable.add(&LONG, new String(CICLI_RT_RESTART_COUNT));
_typeTable.add(&LONG, new String(CICLI_RT_RESTART_MODE));
_typeTable.add(&LONG, new String(CICLI_CLUSTER_STATUS));
_typeTable.add(&BOOLEAN, new String(CICLI_CLUSTER_NODE_INERROR));
_typeTable.add(&BOOLEAN, new String(CICLI_CLUSTER_RG_INERROR));
_typeTable.add(&LONG, new String(CICLI_MACHINE_STATUS));
_typeTable.add(&LONG, new String(CICLI_RESOURCE_STATE));
_typeTable.add(&LONG, new String(CICLI_RESOURCE_ERROR));
_typeTable.add(&LONG, new String(CICLI_RG_STATUS_STATE));
_typeTable.add(&LONG, new String(CICLI_RG_ERROR));
_typeTable.add(&LONG, new String(CICLI_NODE_TIMEOUT));
_typeTable.add(&LONG, new String(CICLI_HEARTBEAT_PERIOD));
_typeTable.add(&BOOLEAN, new String(CICLI_CRS_RUN_PWRFAIL));
_typeTable.add(&BOOLEAN, new String(CICLI_LOCALHOST));
_typeTable.add(&LONG, new String(CICLI_CMS_WAIT_FOR_ALL_TIMEOUT));
// CXFS types
//
_typeTable.add(&BOOLEAN, new String(CICLI_CELL_IS_CELLULAR));
_typeTable.add(&BOOLEAN, new String(CICLI_CELL_IS_FAILSAFE));
_typeTable.add(&LONG, new String(CICLI_CELL_NUM_INTERCONNECTS));
_typeTable.add(&LONG, new String(CICLI_CELL_NUM_TARGETS_));
_typeTable.add(&LONG, new String(CICLI_CELL_WEIGHT));
_typeTable.add(&BOOLEAN, new String(CICLI_CLUSTER_IS_CELLULAR));
_typeTable.add(&BOOLEAN, new String(CICLI_CLUSTER_IS_FAILSAFE));
_typeTable.add(&BOOLEAN, new String(CICLI_CLUSTER_FS_FORCE_));
_typeTable.add(&LONG, new String(CICLI_CLUSTER_ID));
_typeTable.add(&LONG, new String(CICLI_CLUSTER_NUM_FILESYSTEMS));
_typeTable.add(&LONG, new String(CICLI_CLUSTER_FS_NUM_SERVERS_));
}
if (_invisibleAttrs.getSize() == 0) {
String* invisibleAttr = new String(CICLI_SYSCTRL_PASSWD);
_invisibleAttrs.add(invisibleAttr, invisibleAttr);
}
}
} // namespace fsmgr