mariadb/storage/connect/tabwmi.cpp

853 lines
26 KiB
C++
Raw Normal View History

/***********************************************************************/
/* TABWMI: Author Olivier Bertrand -- PlugDB -- 2012 - 2013 */
/* TABWMI: Virtual table to get WMI information. */
/***********************************************************************/
#if !defined(WIN32)
#error This is a WIN32 only table type
#endif // !WIN32
#include "my_global.h"
#include <stdio.h>
#include "global.h"
#include "plgdbsem.h"
#include "mycat.h"
#include "reldef.h"
#include "xtable.h"
#include "colblk.h"
#include "filter.h"
//#include "xindex.h"
#include "tabwmi.h"
#include "valblk.h"
#include "plgcnx.h" // For DB types
#include "resource.h"
extern "C" int trace;
/* ------------------- Functions WMI Column info --------------------- */
/***********************************************************************/
/* Initialize WMI operations. */
/***********************************************************************/
PWMIUT InitWMI(PGLOBAL g, char *nsp, char *classname)
{
IWbemLocator *loc;
char *p;
HRESULT res;
PWMIUT wp = (PWMIUT)PlugSubAlloc(g, NULL, sizeof(WMIUTIL));
if (trace)
htrc("WMIColumns class %s space %s\n", SVP(classname), SVP(nsp));
/*********************************************************************/
/* Set default values for the namespace and class name. */
/*********************************************************************/
if (!nsp)
nsp = "root\\cimv2";
if (!classname) {
if (!stricmp(nsp, "root\\cimv2"))
classname = "ComputerSystemProduct";
else if (!stricmp(nsp, "root\\cli"))
classname = "Msft_CliAlias";
else {
strcpy(g->Message, "Missing class name");
return NULL;
} // endif classname
} // endif classname
/*********************************************************************/
/* Initialize WMI. */
/*********************************************************************/
//res = CoInitializeEx(NULL, COINIT_MULTITHREADED);
res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(res)) {
sprintf(g->Message, "Failed to initialize COM library. "
"Error code = %p", res);
return NULL;
} // endif res
#if 0 // irrelevant for a DLL
res = CoInitializeSecurity(NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, EOAC_NONE, NULL);
if (res != RPC_E_TOO_LATE && FAILED(res)) {
sprintf(g->Message, "Failed to initialize security. "
"Error code = %p", res);
CoUninitialize();
return NULL;
} // endif Res
#endif // 0
res = CoCreateInstance(CLSID_WbemLocator, NULL,
CLSCTX_INPROC_SERVER, IID_IWbemLocator,
(void**) &loc);
if (FAILED(res)) {
sprintf(g->Message, "Failed to create Locator. "
"Error code = %p", res);
CoUninitialize();
return NULL;
} // endif res
res = loc->ConnectServer(_bstr_t(nsp),
NULL, NULL, NULL, 0, NULL, NULL, &wp->Svc);
if (FAILED(res)) {
sprintf(g->Message, "Could not connect. Error code = %p", res);
loc->Release();
CoUninitialize();
return NULL;
} // endif res
loc->Release();
if (trace)
htrc("Successfully connected to namespace.\n");
/*********************************************************************/
/* Perform a full class object retrieval. */
/*********************************************************************/
p = (char*)PlugSubAlloc(g, NULL, strlen(classname) + 7);
if (strchr(classname, '_'))
strcpy(p, classname);
else
strcat(strcpy(p, "Win32_"), classname);
res = wp->Svc->GetObject(bstr_t(p), 0, 0, &wp->Cobj, 0);
if (FAILED(res)) {
sprintf(g->Message, "failed GetObject %s in %s\n", classname, nsp);
wp->Svc->Release();
wp->Svc = NULL; // MUST be set to NULL (why?)
return NULL;
} // endif res
return wp;
} // end of InitWMI
/***********************************************************************/
/* WMIColumns: constructs the result blocks containing the description */
/* of all the columns of a WMI table of a specified class. */
/***********************************************************************/
PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *cls, bool info)
{
static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING,
TYPE_INT, TYPE_INT, TYPE_SHORT};
static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME,
FLD_PREC, FLD_LENGTH, FLD_SCALE};
static unsigned int len, length[] = {0, 6, 8, 10, 10, 6};
int i = 0, n = 0, ncol = sizeof(buftyp) / sizeof(int);
int lng, typ, prec;
LONG low, upp;
BSTR propname;
VARIANT val;
CIMTYPE type;
HRESULT res;
PWMIUT wp;
SAFEARRAY *prnlist = NULL;
PQRYRES qrp = NULL;
PCOLRES crp;
if (!info) {
/*******************************************************************/
/* Initialize WMI if not done yet. */
/*******************************************************************/
if (!(wp = InitWMI(g, nsp, cls)))
return NULL;
/*******************************************************************/
/* Get the number of properties to return. */
/*******************************************************************/
res = wp->Cobj->Get(bstr_t("__Property_Count"), 0, &val, NULL, NULL);
if (FAILED(res)) {
sprintf(g->Message, "failed Get(__Property_Count) res=%d\n", res);
goto err;
} // endif res
if (!(n = val.lVal)) {
sprintf(g->Message, "Class %s in %s has no properties\n",
cls, nsp);
goto err;
} // endif res
/*******************************************************************/
/* Get max property name length. */
/*******************************************************************/
res = wp->Cobj->GetNames(NULL,
WBEM_FLAG_ALWAYS | WBEM_FLAG_NONSYSTEM_ONLY,
NULL, &prnlist);
if (FAILED(res)) {
sprintf(g->Message, "failed GetNames res=%d\n", res);
goto err;
} // endif res
res = SafeArrayGetLBound(prnlist, 1, &low);
res = SafeArrayGetUBound(prnlist, 1, &upp);
for (long i = low; i <= upp; i++) {
// Get this property name.
res = SafeArrayGetElement(prnlist, &i, &propname);
if (FAILED(res)) {
sprintf(g->Message, "failed GetArrayElement res=%d\n", res);
goto err;
} // endif res
len = (unsigned)SysStringLen(propname);
length[0] = max(length[0], len);
} // enfor i
res = SafeArrayDestroy(prnlist);
} else
length[0] = 128;
/*********************************************************************/
/* Allocate the structures used to refer to the result set. */
/*********************************************************************/
qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
buftyp, fldtyp, length, false, true);
if (info || !qrp)
return qrp;
/*********************************************************************/
/* Now get the results into blocks. */
/*********************************************************************/
res = wp->Cobj->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY);
if (FAILED(res)) {
sprintf(g->Message, "failed BeginEnumeration hr=%d\n", res);
qrp = NULL;
goto err;
} // endif hr
while (TRUE) {
res = wp->Cobj->Next(0, &propname, &val, &type, NULL);
if (FAILED(res)) {
sprintf(g->Message, "failed getting Next hr=%d\n", res);
qrp = NULL;
goto err;
} else if (res == WBEM_S_NO_MORE_DATA) {
VariantClear(&val);
break;
} // endif res
if (i >= n)
break; // Should never happen
else
prec = 0;
switch (type) {
case CIM_STRING:
typ = TYPE_STRING;
lng = 255;
prec = 1; // Case insensitive
break;
case CIM_SINT32:
case CIM_UINT32:
case CIM_BOOLEAN:
typ = TYPE_INT;
lng = 11;
break;
case CIM_SINT8:
case CIM_UINT8:
typ = TYPE_TINY;
lng = 4;
break;
case CIM_SINT16:
case CIM_UINT16:
typ = TYPE_SHORT;
lng = 6;
break;
case CIM_REAL64:
case CIM_REAL32:
prec = 2;
typ = TYPE_DOUBLE;
lng = 15;
break;
case CIM_SINT64:
case CIM_UINT64:
typ = TYPE_BIGINT;
lng = 20;
break;
case CIM_DATETIME:
typ = TYPE_DATE;
lng = 19;
break;
case CIM_CHAR16:
typ = TYPE_STRING;
lng = 16;
break;
case CIM_EMPTY:
typ = TYPE_STRING;
lng = 24; // ???
break;
default:
qrp->BadLines++;
goto suite;
} // endswitch type
crp = qrp->Colresp; // Column Name
crp->Kdata->SetValue(_com_util::ConvertBSTRToString(propname), i);
crp = crp->Next; // Data Type
crp->Kdata->SetValue(typ, i);
crp = crp->Next; // Type Name
crp->Kdata->SetValue(GetTypeName(typ), i);
crp = crp->Next; // Precision
crp->Kdata->SetValue(lng, i);
crp = crp->Next; // Length
crp->Kdata->SetValue(lng, i);
crp = crp->Next; // Scale (precision)
crp->Kdata->SetValue(prec, i);
i++;
suite:
SysFreeString(propname);
VariantClear(&val);
} // endfor i
qrp->Nblin = i;
err:
// Cleanup
wp->Cobj->Release();
wp->Svc->Release();
wp->Svc = NULL; // MUST be set to NULL (why?)
CoUninitialize();
/*********************************************************************/
/* Return the result pointer for use by GetData routines. */
/*********************************************************************/
return qrp;
} // end of WMIColumns
/* -------------- Implementation of the WMI classes ------------------ */
/***********************************************************************/
/* DefineAM: define specific AM values for WMI table. */
/***********************************************************************/
bool WMIDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
Nspace = Cat->GetStringCatInfo(g, "Namespace", "Root\\CimV2");
Wclass = Cat->GetStringCatInfo(g, "Class",
(!stricmp(Nspace, "root\\cimv2") ? "ComputerSystemProduct" :
!stricmp(Nspace, "root\\cli") ? "Msft_CliAlias" : ""));
if (!*Wclass) {
sprintf(g->Message, "Missing class name for %s", Nspace);
return true;
} else if (!strchr(Wclass, '_')) {
char *p = (char*)PlugSubAlloc(g, NULL, strlen(Wclass) + 7);
Wclass = strcat(strcpy(p, "Win32_"), Wclass);
} // endif Wclass
if (Catfunc == FNC_NO)
Ems = Cat->GetIntCatInfo("Estimate", 100);
return false;
} // end of DefineAM
/***********************************************************************/
/* GetTable: makes a new TDB of the proper type. */
/***********************************************************************/
PTDB WMIDEF::GetTable(PGLOBAL g, MODE m)
{
if (Catfunc == FNC_NO)
return new(g) TDBWMI(this);
else if (Catfunc == FNC_COL)
return new(g) TDBWCL(this);
sprintf(g->Message, "Bad catfunc %ud for WMI", Catfunc);
return NULL;
} // end of GetTable
/* ------------------------------------------------------------------- */
/***********************************************************************/
/* Implementation of the TDBWMI class. */
/***********************************************************************/
TDBWMI::TDBWMI(PWMIDEF tdp) : TDBASE(tdp)
{
Svc = NULL;
Enumerator = NULL;
ClsObj = NULL;
Nspace = tdp->Nspace;
Wclass = tdp->Wclass;
ObjPath = NULL;
Kvp = NULL;
Ems = tdp->Ems;
Kcol = NULL;
Vbp = NULL;
Init = false;
Done = false;
Res = 0;
Rc = 0;
N = -1;
} // end of TDBWMI constructor
/***********************************************************************/
/* Allocate WMI column description block. */
/***********************************************************************/
PCOL TDBWMI::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
{
PCOL colp;
colp = new(g) WMICOL(cdp, this, n);
if (cprec) {
colp->SetNext(cprec->GetNext());
cprec->SetNext(colp);
} else {
colp->SetNext(Columns);
Columns = colp;
} // endif cprec
return colp;
} // end of MakeCol
/***********************************************************************/
/* Initialize: Initialize WMI operations. */
/***********************************************************************/
bool TDBWMI::Initialize(PGLOBAL g)
{
if (Init)
return false;
// Initialize COM.
Res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(Res)) {
sprintf(g->Message, "Failed to initialize COM library. "
"Error code = %p", Res);
return true; // Program has failed.
} // endif Res
// Obtain the initial locator to Windows Management
// on a particular host computer.
IWbemLocator *loc; // Initial Windows Management locator
Res = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID*) &loc);
if (FAILED(Res)) {
sprintf(g->Message, "Failed to create Locator. "
"Error code = %p", Res);
CoUninitialize();
return true; // Program has failed.
} // endif Res
// Connect to the specified namespace with the
// current user and obtain pointer to Svc
// to make IWbemServices calls.
Res = loc->ConnectServer(_bstr_t(Nspace),
NULL, NULL,0, NULL, 0, 0, &Svc);
if (FAILED(Res)) {
sprintf(g->Message, "Could not connect. Error code = %p", Res);
loc->Release();
CoUninitialize();
return true; // Program has failed.
} // endif hres
loc->Release(); // Not used anymore
// Set the IWbemServices proxy so that impersonation
// of the user (client) occurs.
Res = CoSetProxyBlanket(Svc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
NULL, RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
if (FAILED(Res)) {
sprintf(g->Message, "Could not set proxy. Error code = 0x", Res);
Svc->Release();
CoUninitialize();
return true; // Program has failed.
} // endif Res
Init = true;
return false;
} // end of Initialize
/***********************************************************************/
/* Changes '\' into '\\' in the filter. */
/***********************************************************************/
void TDBWMI::DoubleSlash(PGLOBAL g)
{
if (To_Filter && strchr(To_Filter->Body, '\\')) {
char *body = To_Filter->Body;
char *buf = (char*)PlugSubAlloc(g, NULL, strlen(body) * 2);
int i = 0, k = 0;
do {
if (body[i] == '\\')
buf[k++] = '\\';
buf[k++] = body[i];
} while (body[i++]);
To_Filter->Body = buf;
} // endif To_Filter
} // end of DoubleSlash
/***********************************************************************/
/* MakeWQL: make the WQL statement use with WMI ExecQuery. */
/***********************************************************************/
char *TDBWMI::MakeWQL(PGLOBAL g)
{
char *colist, *wql/*, *pw = NULL*/;
int len, ncol = 0;
bool first = true, noloc = false;
PCOL colp;
// Normal WQL statement to retrieve results
for (colp = Columns; colp; colp = colp->GetNext())
if (!colp->IsSpecial() && (colp->GetColUse(U_P | U_J_EXT) || noloc))
ncol++;
if (ncol) {
colist = (char*)PlugSubAlloc(g, NULL, (NAM_LEN + 4) * ncol);
for (colp = Columns; colp; colp = colp->GetNext())
if (!colp->IsSpecial()) {
if (colp->GetResultType() == TYPE_DATE)
((DTVAL*)colp->GetValue())->SetFormat(g, "YYYYMMDDhhmmss", 19);
if (colp->GetColUse(U_P | U_J_EXT) || noloc) {
if (first) {
strcpy(colist, colp->GetName());
first = false;
} else
strcat(strcat(colist, ", "), colp->GetName());
} // endif ColUse
} // endif Special
} else {
// ncol == 0 can occur for queries such that sql count(*) from...
// for which we will count the rows from sql * from...
colist = (char*)PlugSubAlloc(g, NULL, 2);
strcpy(colist, "*");
} // endif ncol
// Below 14 is length of 'select ' + length of ' from ' + 1
len = (strlen(colist) + strlen(Wclass) + 14);
len += (To_Filter ? strlen(To_Filter->Body) + 7 : 0);
wql = (char*)PlugSubAlloc(g, NULL, len);
strcat(strcat(strcpy(wql, "SELECT "), colist), " FROM ");
strcat(wql, Wclass);
if (To_Filter)
strcat(strcat(wql, " WHERE "), To_Filter->Body);
return wql;
} // end of MakeWQL
/***********************************************************************/
/* GetWMIInfo: Get info for the WMI class. */
/***********************************************************************/
bool TDBWMI::GetWMIInfo(PGLOBAL g)
{
if (Done)
return false;
char *cmd = MakeWQL(g);
if (cmd == NULL) {
sprintf(g->Message, "Error making WQL statement");
Svc->Release();
CoUninitialize();
return true; // Program has failed.
} // endif cmd
// Query for Wclass in Nspace
Rc = Svc->ExecQuery(bstr_t("WQL"), bstr_t(cmd),
// WBEM_FLAG_BIDIRECTIONAL | WBEM_FLAG_RETURN_IMMEDIATELY,
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL, &Enumerator);
if (FAILED(Rc)) {
sprintf(g->Message, "Query %s failed. Error code = %p", cmd, Rc);
Svc->Release();
CoUninitialize();
return true; // Program has failed.
} // endif Rc
Done = true;
return false;
} // end of GetWMIInfo
/***********************************************************************/
/* WMI: Get the number returned instances. */
/***********************************************************************/
int TDBWMI::GetMaxSize(PGLOBAL g)
{
if (MaxSize < 0) {
/*******************************************************************/
/* Loop enumerating to get the count. This is prone to last a */
/* very long time for some classes such as DataFile, this is why */
/* we just return an estimated value that will be ajusted later. */
/*******************************************************************/
MaxSize = Ems;
#if 0
if (Initialize(g))
return -1;
else if (GetWMIInfo(g))
return -1;
else
MaxSize = 0;
PDBUSER dup = PlgGetUser(g);
while (Enumerator) {
Res = Enumerator->Next(WBEM_INFINITE, 1, &ClsObj, &Rc);
if (Rc == 0)
break;
MaxSize++;
} // endwile Enumerator
Res = Enumerator->Reset();
#endif // 0
} // endif MaxSize
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/
/* When making a Kindex, must provide the Key column info. */
/***********************************************************************/
int TDBWMI::GetRecpos(void)
{
if (!Kcol || !Vbp)
return N;
Kcol->Reset();
Kcol->Eval(NULL);
Vbp->SetValue(Kcol->GetValue(), N);
return N;
} // end of GetRecpos
/***********************************************************************/
/* WMI Access Method opening routine. */
/***********************************************************************/
bool TDBWMI::OpenDB(PGLOBAL g)
{
if (Use == USE_OPEN) {
/*******************************************************************/
/* Table already open. */
/*******************************************************************/
Res = Enumerator->Reset();
N = 0;
return false;
} // endif use
if (Mode != MODE_READ) {
/*******************************************************************/
/* WMI tables cannot be modified. */
/*******************************************************************/
strcpy(g->Message, "WMI tables are read only");
return true;
} // endif Mode
if (!To_Filter && !stricmp(Wclass, "CIM_Datafile")
&& !stricmp(Nspace, "root\\cimv2")) {
strcpy(g->Message,
"Would last forever when not filtered, use DIR table instead");
return true;
} else
DoubleSlash(g);
Use = USE_OPEN; // Do it now in case we are recursively called
/*********************************************************************/
/* Initialize the WMI processing. */
/*********************************************************************/
if (Initialize(g))
return true;
else
return GetWMIInfo(g);
} // end of OpenDB
/***********************************************************************/
/* Data Base read routine for WMI access method. */
/***********************************************************************/
int TDBWMI::ReadDB(PGLOBAL g)
{
Res = Enumerator->Next(WBEM_INFINITE, 1, &ClsObj, &Rc);
if (Rc == 0)
return RC_EF;
N++;
return RC_OK;
} // end of ReadDB
/***********************************************************************/
/* WriteDB: Data Base write routine for WMI access methods. */
/***********************************************************************/
int TDBWMI::WriteDB(PGLOBAL g)
{
strcpy(g->Message, "WMI tables are read only");
return RC_FX;
} // end of WriteDB
/***********************************************************************/
/* Data Base delete line routine for WMI access methods. */
/***********************************************************************/
int TDBWMI::DeleteDB(PGLOBAL g, int irc)
{
strcpy(g->Message, "Delete not enabled for WMI tables");
return RC_FX;
} // end of DeleteDB
/***********************************************************************/
/* Data Base close routine for WMI access method. */
/***********************************************************************/
void TDBWMI::CloseDB(PGLOBAL g)
{
// Cleanup
if (ClsObj)
ClsObj->Release();
if (Enumerator)
Enumerator->Release();
if (Svc)
Svc->Release();
CoUninitialize();
} // end of CloseDB
// ------------------------ WMICOL functions ----------------------------
/***********************************************************************/
/* WMICOL public constructor. */
/***********************************************************************/
WMICOL::WMICOL(PCOLDEF cdp, PTDB tdbp, int n)
: COLBLK(cdp, tdbp, n)
{
Tdbp = (PTDBWMI)tdbp;
VariantInit(&Prop);
Ctype = CIM_ILLEGAL;
Res = 0;
} // end of WMICOL constructor
#if 0
/***********************************************************************/
/* WMICOL constructor used for copying columns. */
/* tdbp is the pointer to the new table descriptor. */
/***********************************************************************/
WMICOL::WMICOL(WMICOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
{
} // end of WMICOL copy constructor
#endif // 0
/***********************************************************************/
/* Read the next WMI address elements. */
/***********************************************************************/
void WMICOL::ReadColumn(PGLOBAL g)
{
// Get the value of the Name property
Res = Tdbp->ClsObj->Get(_bstr_t(Name), 0, &Prop, &Ctype, 0);
switch (Prop.vt) {
case VT_EMPTY:
case VT_NULL:
case VT_VOID:
Value->Reset();
break;
case VT_BSTR:
Value->SetValue_psz(_com_util::ConvertBSTRToString(Prop.bstrVal));
break;
case VT_I4:
case VT_UI4:
Value->SetValue(Prop.lVal);
break;
case VT_I2:
case VT_UI2:
Value->SetValue(Prop.iVal);
break;
case VT_INT:
case VT_UINT:
Value->SetValue((int)Prop.intVal);
break;
case VT_BOOL:
Value->SetValue(((int)Prop.boolVal) ? 1 : 0);
break;
case VT_R8:
Value->SetValue(Prop.dblVal);
break;
case VT_R4:
Value->SetValue((double)Prop.fltVal);
break;
case VT_DATE:
switch (Value->GetType()) {
case TYPE_DATE:
{SYSTEMTIME stm;
struct tm ptm;
int rc = VariantTimeToSystemTime(Prop.date, &stm);
ptm.tm_year = stm.wYear;
ptm.tm_mon = stm.wMonth;
ptm.tm_mday = stm.wDay;
ptm.tm_hour = stm.wHour;
ptm.tm_min = stm.wMinute;
ptm.tm_sec = stm.wSecond;
((DTVAL*)Value)->MakeTime(&ptm);
}break;
case TYPE_STRING:
{SYSTEMTIME stm;
char buf[24];
int rc = VariantTimeToSystemTime(Prop.date, &stm);
sprintf(buf, "%02d/%02d/%d %02d:%02d:%02d",
stm.wDay, stm.wMonth, stm.wYear,
stm.wHour, stm.wMinute, stm.wSecond);
Value->SetValue_psz(buf);
}break;
default:
Value->SetValue((double)Prop.fltVal);
} // endswitch Type
break;
default:
// This will reset numeric column value
Value->SetValue_psz("Type not supported");
break;
} // endswitch vt
VariantClear(&Prop);
} // end of ReadColumn
/* ---------------------------TDBWCL class --------------------------- */
/***********************************************************************/
/* TDBWCL class constructor. */
/***********************************************************************/
TDBWCL::TDBWCL(PWMIDEF tdp) : TDBCAT(tdp)
{
Nsp = tdp->Nspace;
Cls = tdp->Wclass;
} // end of TDBWCL constructor
/***********************************************************************/
/* GetResult: Get the list of the WMI class properties. */
/***********************************************************************/
PQRYRES TDBWCL::GetResult(PGLOBAL g)
{
return WMIColumns(g, Nsp, Cls, false);
} // end of GetResult