Quantcast
Channel: Windows Management Infrastructure Blog
Viewing all 66 articles
Browse latest View live

In-Band Management of IPMI with PowerShell

$
0
0

IPMI (Intelligent Platform Management Interface) is a specification defined by Intel and adopted by a number of companies for standardizing hardware management, particularly in server systems.  IPMI supports both in-band (from the operating system) and out-of-band (from the network direct to hardware) management.  The protocol IPMI uses for out-of-band management is not based on WS-Management and not the focus of this blog post.

Available since Windows 2003 R2, Microsoft has developed a generic IPMI Driver and IPMI Provider in Windows to enable management of BMCs (Base board Management Controller) that implement IPMI.  The IPMI Driver (IPMIdrv.sys) is essentially a light-weight “pass-through” driver which takes IPMI requests and returns IPMI responses.  The IPMI Provider (IPMIprv.dll) takes the heavy load of converting between IPMI and CIM.

The IPMI Provider only exposes a subset of the capabilities exposed by IPMI.  However, because of the IPMI Driver “pass-through” design, you can take advantage of this and send custom IPMI requests.  You can even do this remotely over WS-Management (via the OS) and that’s what I’ll talk about today.

Even though the IPMI Driver runs in the kernel, it exposes a User Mode (meaning Applications can call it) interface as a WMI class (Microsoft_IPMI) in the Root/WMI namespace.  It implements a dynamic method (meaning you execute the method against the instance) called RequestResponse() that we’ll use to send IPMI commands.  You will need to be in the administrators group to be able to call this method successfully.

I’ve published a PowerShell module (written in PowerShell script) on TechNet that can be used as a sample.  One of the main helper functions is this:

function Invoke-IPMIRequestResponse {
    [CmdletBinding(DefaultParametersetName="CimSession")]
    Param (
        [Parameter(ParameterSetName="CimSession",Position=0)]
        [Microsoft.Management.Infrastructure.CimSession] $CimSession,
        [byte]$Command,
        [byte]$LUN = $DefaultLUN,
        [byte[]]$RequestData,
        [byte]$ResponderAddress = $BMCResponderAddress)

    Process {
        $ErrorActionPreference = "SilentlyContinue"

        if ($CimSession -eq $null) {
            $CimSession = New-CimSession
        }

        $ipmi = Get-CimInstance -Namespace root/wmi -CimSession $CimSession Microsoft_IPMI
        $ErrorActionPreference = "Stop"

        if ($null -eq $ipmi) {
            Write-Error "Microsoft IPMI Driver not running on specified system"
        }

        $arguments = @{Command=$Command;LUN=$LUN;NetworkFunction= `
	$(Get-NetFn $command);RequestData=$RequestData;RequestDataSize= `
	[uint32]$RequestData.Length;ResponderAddress=$ResponderAddress}

        $out = Invoke-CimMethod -InputObject $ipmi -CimSession $CimSession RequestResponse -Arguments $arguments
        if ($out.CompletionCode -ne 0) {
             Write-Error ("IPMI Command failed (0x{0:x}): {1}" -f $out.CompletionCode, `
             (Convert-CompletionCodeToText $out.CompletionCode)) 

        }
        $out.ResponseData
    }
} 

This function takes a CimSession which is a new type we introduced in the Windows Management Framework 3.0 that makes it easy to manage remote management operations as well as using the new CIM cmdlets.  If a CimSession is not provided, this function works against the local machine. 

An IPMI request (as defined in the IPMI specification) is essentially split into: Command, LUN, RequestData, and ResponderAddress.  For the most part, LUN (Logical Unit Number) is usually 0 and ResponderAddress is usually 32 (meaning the BMC itself responds).  Command is the operation (for example, Get LAN Configuration) and RequestData is a byte-array specific to that Command.  Additional required data such as NetworkFunction (basically the Command category) is computed automatically.  I’m not going to go into details about IPMI itself, but people familiar with IPMI should be able to easily see how to make use of this function.  Here is an example of using the PowerShell module:

So what would you do with this?  On server systems, the BMC is typically on a different NIC than the host system, so the BMC would have a different network identity than the host system.  Given two IP Addresses and even two MAC Addresses, you would not be able to correlate that this BMC manages this particular host.  Using IPMI (and this PowerShell module), you can query the IP address of the BMC from the OS and thus be able to correlate the two together.  It’s also possible to store information from the OS into the BMC.  The example above, I made use of the IPMI Set System Info Parameter Command to store the OS name into the BMC.  This means that management software that talks to the BMC remotely would be able to determine what OS is installed and/or running on that system.

If you are using IPMI capable hardware today (in-band or out-of-band), I would appreciate it if you let me know what scenarios are you are trying to enable or are currently exercising.  In a future post, I’ll discuss leveraging DASH and SMASH to perform operations similar to what IPMI exposes remotely, but makes use of CIM and WS-Management.

Steve Lee
Principal Test Manager
Standards Based Management Team


Introducing - Standards Based Management in Windows Server "8"

$
0
0

We are excited to introduce the investments we have made in Windows Server “8” through this excellent blog post Standards based management in Windows Server 8 by Jeffrey Snover and Wojtek Kozaczynski.

It’s a very detailed overview covering “Why Standards based management matters” and “What we have done in Windows “8” to take cloud-management to a new level”. It talks about following
investment areas providing an excellent E2E picture

-       Simplified WMI provider development using MI API.

-       Standard compliant protocol, ready for large-scale distributed management.

-       Client development using MI .NET Client API.

-       PowerShell cmdlets to manage CIM end-points (CIM Cmdlets).

-       PowerShell cmdlets written as a CIM Provider (CIM-Based Cmdlets).

-       Management OData IIS extension.

 

In the next few weeks, we will take you through a deep dive of each of these areas. So tighten your seat belts and get ready for take for takeoff – it is going to be an exciting journey.

-Team 'Standards Based Management'

 

Announcing OMI - Open Management Infrastructure.

$
0
0
We are pleased to announce availability of a highly portable, small footprint, high performance CIM Object Manager called OMI (Open Management Infrastructure).
See the annoucement on Windows Server blog
Copying from the blog “The public availability of OMI means that you can now easily compile and implement a standards-based management service into any device or platform from a free open-source package. Our goals are to remove all obstacles that stand in the way of implementing standards-based management so that every device in the world can be managed in a clear, consistent, coherent way and to nurture spur a rich ecosystem of standards-based management products.”
 
OMI is available from https://collaboration.opengroup.org/omi/. Our goals for OMI are
  • A very small footprint
  • A provider generator model which makes the task of creating providers very easy
  • High portability to a wide variety of hardware and software
  • High performance
  • Support for WS-Management
We also announced collaboration with network device manufacturers Cisco and Arista to port OMI to their switches and implement standard DMTF CIM schema in their devices. Jeffrey Snover did a technology demonstration at TechEd Europe in which he used a common set of standards-based tools to manage a base-motherboard controller on a server, a Windows operating system, and an Arista switch running OMI. You can see recording of this session here  at 51:15
 Cheers,
SBM Team

Standards Based Management: DMTF Management Profiles

$
0
0

The investments we’ve made in Windows Server 2012 (and WMF3.0) for standards based management is built around WS-Man as the remoting protocol (HTTP is the transport) and CIM as the model.  By themselves, WS-Man and CIM are useful, but may not provide a consistent experience when managing a similar class of devices or management domain.  What I mean by this is that the DMTF has provided a very rich set of schema, however, different OEMs may implement different classes, methods, or properties so although they implement the CIM Schema, a management client does not have consistency/predictability and must handle all possible situations.

Management Profiles are a solution to this problem.  Management profiles as defined by the DMTF:

“A Profile, which may be specified in documents published by the DMTF or in specifications created by other organizations, defines the CIM model and associated behavior for a particular management domain (including the CIM classes, associations, indications, methods and properties). This provides a unified way of describing the management domain in CIM, which contributes to interoperability and helps with ease of use.

Each profile identifies unambiguously the classes, properties, methods and values that should be instantiated and manipulated to represent and manage a given domain.” –DMTF Management Profiles

What this means is that Profiles provide normative text on how to implement CIM Schema for a specific management domain so that the management client can expect certain behaviors predictably as long as the managed node claims conformance to a specific Profile (or set of Profiles).  It should be noted that Profiles are not required to be published/standardized by the DMTF.  OEMs are allowed to make custom extensions and expose them through OEM specific Profiles.

One of the key Profiles that make this all work is the Profile Registration Profile.  This specific Profile describes how a managed node publishes the Profiles it has implemented so that the initial discovery is a well known workflow:

1. Discover the Interop namespace

2. Find the specific CIM_RegisteredProfile you are interested in primarily by filtering against the RegisteredName (ie, Boot Control) and RegisteredVersion (ie, 1.0.0)

3. Perform a association traversal (which may cross namespaces) to the central class of that Profile (ie, CIM_ComputerSystem) via CIM_ElementConformsToProfile association class

4. Start managing based on details in the specific Management Profile

The two key benefits you get from this:

1. OEMs must implement an Interop namespace (there’s 4 possible allowed variations)

2. By using the cross-namespace association traversal, the OEM can choose whatever namespace(s) it wants to use for their implementation and the management client is oblivious to this implementation detail.

To show how this all comes together, my next blog post (I’m targeting next week) will detail a new PowerShell module I wrote (also to be published next week) that uses this workflow along with implementing use cases defined in specific Profiles exposed as higher level admin task abstraction cmdlets for out-of-band hardware management.

Steve Lee [MSFT]
Principal Test Manager
Standards Based Management

Standards Based Identity and Role Management using PowerShell

$
0
0

A year ago I published an article and PowerShell module to simplify managing Standards-Based devices (specifically CIM and WS-Man). The use cases focused around setting the boot order, changing power state, and getting software (firmware) and hardware inventory information. The module should work against any SMASH or DASH capable hardware that implements the specific Management Profiles used by the module.

DASH (Desktop and Mobile Architecture for System Hardware) specifically has a set of implementation requirements which include management of identities and roles.

The Simple Identity Management Profile (SIMP) allow you to do things such as creating accounts, removing accounts, and updating accounts. The Role Based Authorization Profile (RBAP) is used with SIMP to assign roles (you can think of it loosely as a collection of privileges) to an account. Both of these profiles are complex due to the breadth of capabilities being modeled and flexibility for implementations to be conformant. I’ve updated the Hardware Management module to enable use of some of the top use-cases while removing the complexities of understanding the profiles.

clip_image002

Here I show that I’ve imported the module and have already created CimSession instances to three hardware devices. The first one named BRCM is a HP desktop system that uses Broadcom’s TruManage DASH implementation. The second one is a desktop system using Intel’s vPro (aka AMT) implementation of DASH. The third one is a Dell PowerEdge server rack using Dell’s iDrac7 implementation of SMASH. I save all three sessions to a single $systems variable and get the power-state of all three systems. One thing you may notice is that not each system implements different parts of the same standard Profiles where certain properties and/or methods are optional or conditional. Specifically, you can see that two of the systems don’t return AvailablePowerStates information. So far, I haven’t done anything that uses the new SIMP and RBAP cmdlets. One thing you may notice if you used the previous version of the module is the CIM prefix added to the cmdlets based on feedback from the PowerShell team.

clip_image004

Here I’m enumerating the accounts that exist on the Broadcom TruManage and Intel vPro systems. I’m leaving out the Dell iDrac system only because there are lots of test accounts created on it. I already have an account named Steve on the Intel system, so let’s create one on the Broadcom system.

clip_image006

Account was created, but it’s not associated to any role (nor privileges) and is currently in an offline state.

clip_image008

First, let’s assign a role to the new account. I’ll enumerate all the roles supported by the TruManage system. One thing to note is that the current version of the RBAP doesn’t define a common way to modeling the administrator role. This means that unfortunately today, you’ll have to have specific knowledge about a particular implementation since you can’t simply query for the administrator role. On this particular system, I can see that it’s the first role in my array, so I use the Set-CIMRole to make my new Steve account an administrator.

clip_image010

After we enable the account, we can start using it. Here I create a new CIMSession instance using the newly created account and retrieve some software (firmware) inventory information on that system.

The updated module is available at the same TechNet location.

Steve Lee
Principal Test Manager
Windows Server Standards Based Management

Physical Computer System View Profile

$
0
0

Out-of-band management is a critical tool within a datacenter (also useful to small/medium businesses).  Essentially, it enables one to remotely manage a computer system independent of the operating system: system is powered off, operating system is not functioning, or operating system is not installed yet.  For Servers, there are really two standards adopted (some newer systems support both): IPMI and SMASH.  In my view, I can summarize these two as:

  1. IPMI
    1. de facto standard – widely available
    2. simpler for an OEM to implement – limited capabilities, but more consistent across OEMs
    3. fast binary protocol
  2. SMASH
    1. de jure standard – broad adoption
    2. rich capabilities – harder to implement and use, optional Profiles means less predictable capabilities across OEMs
    3. easy for OEM to extend – based on CIM model
    4. supports WS-Man protocol (although not mandatory) – manageable using generic CIM cmdlets

DASH is the equivalent of SMASH for desktops, although WS-Man is mandatory for DASH.  As you can see, each have some pros and some cons.  In an effort to bridge the gap between these two and provide a solution with some of the pros from both sides, the Physical Computer System View Profile (henceforth referred to as PCSV) was created and standardized within the DMTF.

I worked with representation from HP, Intel, Broadcom, and AMI to create a new management Profile that complements SMASH by providing more simplicity and predictability while still enabling richness and extensibility.  We focused our efforts on the top scenarios that customers cared about and created a view class that reused existing CIM schema from existing management Profiles: Computer System, Power Utilization Management, Sensors, Record Log, Boot Control, Software Inventory, System Memory, Physical Asset, OS Status, CPU, and Software Update.  Although many of the features of PCSV are optional, we made basic capabilities such as identity and setting the device state as mandatory.

image

This UML diagram taken from the PCSV Profile spec shows that the CIM_PhysicalComputerSystemView class is really a composition of properties and methods from existing CIM schema.  This means that OEMs that already implement these Profiles already have the data needed to populate the view class making it easier to implement.  For OEMs writing new implementations, they can decide whether they want the richness of implementing the full Profiles or only implement the PCSV Profile for targeted use cases.

As part of the Datacenter Abstraction Layer (DAL) initiative, one of the new cmdlets we introduced in Windows 8.1 and Windows Server 2012 R2 is the PCSVDevice cmdlets.  These cmdlets use PCSV as an abstraction to provide a single view whether you are targeting a device that implements IPMI, SMASH, or just PCSV. 

image

Here you can see that I used the same cmdlet against a Dell server using IPMI as well as Compaq desktop using WS-Man.  Although I’m only showing a subset of the information you can retrieve, the Get-PcsvDevice cmdlet is able to return all the same information as modeled in CIM_PhysicalComputerSystemView including asset information, firmware information, boot configuration, sensors, etc…

The Set-PcsvDeviceBootConfiguration cmdlet allows you to set a device’s one-time boot source.  For example, you would use this to do a PXE (Network) boot to install an OS image.  The Start/Restart/Stop-PCSVDevice cmdlets allow you to power-on, power-reset, and power-off the device.  More details about the PcsvDevice cmdlet is available in this PowerShell blog post.

In preparation for new PCSV enabled devices, I’ve also updated my HardwareManagement PowerShell module to use PCSV if implemented. 

image

In this example, I have a prototype implementation of PCSV (running on a PC).  This quick demo shows the benefit of using the view class where it is a single request/response operation to retrieve 36 sensors while using the full Sensors Profile implementation incurs a request/response for each batch of sensors which takes almost 3.5 times as long.

For completeness of supporting DASH, I also added network discovery capabilities by supporting RMCP Ping (also supported by IPMI devices):

image

The updated module is available at the same TechNet location.

Happy Holidays!

Steve Lee
Principal Test Manager
Windows Server Standards Based Management

Implementing MI Provider (4) - Generate Code (continute)

$
0
0

As described in Implementing MI Provider (3), Convert-MofToProvider.exe tool generates a set of code files, including schema.c, module.c, WMIAdapter.c, Provider.DEF, <class name>.h, and <class name>.c. There is one header file for each target class and all of it's ancestor class(es) if have. And one c file for each target class (specified by -ClassList parameter). Header files also get genrated for classes specified by -ExtraClass parameter.

 

<Class name>.h file(s)

Those header files are named with pattern like <class name>.h, which defines the type of <class name> class and a set of helper functions to manipulate the instance of that type. Take MSFT_WindowsProcess.h for example, first of all, it defines the structure of MSFT_WindowsProcess, represents the run-time type of windows process instance defined by MSFT_WindowsProcess schema.

typedef struct _MSFT_WindowsProcess /* extends CIM_Process */
{
MI_Instance __instance;
/* CIM_ManagedElement properties */
MI_ConstStringField InstanceID;
MI_ConstStringField Caption;
MI_ConstStringField Description;
MI_ConstStringField ElementName;
/* CIM_ManagedSystemElement properties */
MI_ConstDatetimeField InstallDate;
MI_ConstStringField Name;
MI_ConstUint16AField OperationalStatus;
MI_ConstStringAField StatusDescriptions;
MI_ConstStringField Status;
MI_ConstUint16Field HealthState;
MI_ConstUint16Field CommunicationStatus;
MI_ConstUint16Field DetailedStatus;
MI_ConstUint16Field OperatingStatus;
MI_ConstUint16Field PrimaryStatus;
/* CIM_LogicalElement properties */
/* CIM_EnabledLogicalElement properties */
MI_ConstUint16Field EnabledState;
MI_ConstStringField OtherEnabledState;
MI_ConstUint16Field RequestedState;
MI_ConstUint16Field EnabledDefault;
MI_ConstDatetimeField TimeOfLastStateChange;
MI_ConstUint16AField AvailableRequestedStates;
MI_ConstUint16Field TransitioningToState;
/* CIM_Process properties */
/*KEY*/ MI_ConstStringField CSCreationClassName;
/*KEY*/ MI_ConstStringField CSName;
/*KEY*/ MI_ConstStringField OSCreationClassName;
/*KEY*/ MI_ConstStringField OSName;
/*KEY*/ MI_ConstStringField CreationClassName;
/*KEY*/ MI_ConstStringField Handle;
MI_ConstUint32Field Priority;
MI_ConstUint16Field ExecutionState;
MI_ConstStringField OtherExecutionDescription;
MI_ConstDatetimeField CreationDate;
MI_ConstDatetimeField TerminationDate;
MI_ConstUint64Field KernelModeTime;
MI_ConstUint64Field UserModeTime;
MI_ConstUint64Field WorkingSetSize;
/* MSFT_WindowsProcess properties */
MI_ConstStringField CommandLine;
}
MSFT_WindowsProcess;

Then, the header file defines a set of functions to manipulate the instance of MSFT_WindowsProcess, following helper funtions set and clear CommandLine property value of the instance.

MI_INLINE MI_Result MI_CALL MSFT_WindowsProcess_Set_CommandLine(
_Inout_ MSFT_WindowsProcess* self,
_In_z_ const MI_Char* str)
{
return self->__instance.ft->SetElementAt(
(MI_Instance*)&self->__instance,
35,
(MI_Value*)&str,
MI_STRING,
0);
}

MI_INLINE MI_Result MI_CALL MSFT_WindowsProcess_SetPtr_CommandLine(
_Inout_ MSFT_WindowsProcess* self,
_In_z_ const MI_Char* str)
{
return self->__instance.ft->SetElementAt(
(MI_Instance*)&self->__instance,
35,
(MI_Value*)&str,
MI_STRING,
MI_FLAG_BORROW);
}

MI_INLINE MI_Result MI_CALL MSFT_WindowsProcess_Clear_CommandLine(
_Inout_ MSFT_WindowsProcess* self)
{
return self->__instance.ft->ClearElementAt(
(MI_Instance*)&self->__instance,
35);
}

 

<Class name>.c file(s)

Based on the type of target class, Convert-MofToProvider.exe generates different stub functions in c file. As mentioned, there are three type of classes, normal class, association class, and indication class.

For MSFT_WindowsProcess class, following are the generated stub functions,

#include <MI.h>
#include "MSFT_WindowsProcess.h"

void MI_CALL MSFT_WindowsProcess_Load(
_Outptr_result_maybenull_ MSFT_WindowsProcess_Self** self,
_In_opt_ MI_Module_Self* selfModule,
_In_ MI_Context* context)
{
MI_UNREFERENCED_PARAMETER(selfModule);

*self = NULL;
MI_Context_PostResult(context, MI_RESULT_OK);
}

void MI_CALL MSFT_WindowsProcess_Unload(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context)
{
MI_UNREFERENCED_PARAMETER(self);

MI_Context_PostResult(context, MI_RESULT_OK);
}

void MI_CALL MSFT_WindowsProcess_EnumerateInstances(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_opt_ const MI_PropertySet* propertySet,
_In_ MI_Boolean keysOnly,
_In_opt_ const MI_Filter* filter)
{
MI_Result result = MI_RESULT_OK;

MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(propertySet);
MI_UNREFERENCED_PARAMETER(filter);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_GetInstance(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_ const MSFT_WindowsProcess* instanceName,
_In_opt_ const MI_PropertySet* propertySet)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(instanceName);
MI_UNREFERENCED_PARAMETER(propertySet);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_CreateInstance(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_ const MSFT_WindowsProcess* newInstance)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(newInstance);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_ModifyInstance(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_ const MSFT_WindowsProcess* modifiedInstance,
_In_opt_ const MI_PropertySet* propertySet)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(propertySet);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_DeleteInstance(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_ const MSFT_WindowsProcess* instanceName)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_Invoke_RequestStateChange(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_opt_z_ const MI_Char* methodName,
_In_ const MSFT_WindowsProcess* instanceName,
_In_opt_ const MSFT_WindowsProcess_RequestStateChange* in)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(methodName);
MI_UNREFERENCED_PARAMETER(instanceName);
MI_UNREFERENCED_PARAMETER(in);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_Invoke_SetPriority(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_opt_z_ const MI_Char* methodName,
_In_ const MSFT_WindowsProcess* instanceName,
_In_opt_ const MSFT_WindowsProcess_SetPriority* in)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(methodName);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_Invoke_Create(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_opt_z_ const MI_Char* methodName,
_In_ const MSFT_WindowsProcess* instanceName,
_In_opt_ const MSFT_WindowsProcess_Create* in)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(methodName);
MI_UNREFERENCED_PARAMETER(instanceName);
MI_UNREFERENCED_PARAMETER(in);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

There are two categories of the generated methods insides one c file, intrinsic methods and extrinsic methods. Intrinsic methods are standard operations while extrinsic methods are methods defined within the class's schema (mof file).

Intrinsic Methods

Extrinsic methods

MSFT_WindowsProcess_EnumerateInstances

MSFT_WindowsProcess_Invoke_SetPriority

MSFT_WindowsProcess_GetInstance

MSFT_WindowsProcess_Invoke_RequestStateChange

MSFT_WindowsProcess_ModifyInstance

MSFT_WindowsProcess_Invoke_Create

MSFT_WindowsProcess_CreateInstance

 

MSFT_WindowsProcess_DeleteInstance

 

For each extrinsic method definition, there is also a type generated in the corresponding header file, for example, structure MSFT_WindowsProcess_Create was defined for Create method of MSFT_WindowsProcess class in MSFT_WindowsProcess.h file. Through this type, provider may access values of input parameters from client (who invoke the method) and output values of parameters to client.

For each class, there are two functions always generated within <class name>.c file, called load/unload functions. <class name>_Load function is invoked by CIMOM server to initialize the class before invoking any method, the class may initialize any resources which could live across class loading time and are used to during operations of the class. <class name>_Unload function is invoked by server to finalize the class, the class should finalize all resources initialized within load function.

 

Module.c 

Module.c file defines and implements entry function (MI_Main) of the MI provider. As shown below, there are three functions defined, Load, Unload, MI_Main.

Load function is used to initialize the provider wide resources, live during provider's life cycle. For example, inside Load function, a provider may initialize a critical section, which was used to protect shared data structures accessed by multiple threads simultaneously.

Unload function is used to finalize the provider resources. For example, the provider could delete the critical section inside Unload function.

MI_Main function exchanges information between the provider and its host. Usually provider manager calls MI_Main function of a MI provider to get the MI_Module information of a provider, which contains the flags, charsize, version, generator version, all supported schema, and load unload function pointer. Meanwhile, provider manager tells the provider server information by input parameter MI_Server. Provider could call MI_Server_GetVersion and MI_Server_GetSystemName to read current CIMOM server version and system name.

 #include <MI.h>

MI_EXTERN_C MI_SchemaDecl schemaDecl;

void MI_CALL Load(_Outptr_result_maybenull_ MI_Module_Self** self, _In_ struct _MI_Context* context)
{
*self = NULL;
MI_Context_PostResult(context, MI_RESULT_OK);
}

void MI_CALL Unload(_In_opt_ MI_Module_Self* self, _In_ struct _MI_Context* context)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(context);

MI_Context_PostResult(context, MI_RESULT_OK);
}

MI_EXTERN_C MI_EXPORT MI_Module* MI_MAIN_CALL MI_Main(_In_ MI_Server* server)
{
/* WARNING: THIS FUNCTION AUTOMATICALLY GENERATED. PLEASE DO NOT EDIT. */
static MI_Module module;
MI_EXTERN_C MI_Server* __mi_server;
__mi_server = server;
module.flags |= MI_MODULE_FLAG_DESCRIPTIONS;
module.flags |= MI_MODULE_FLAG_VALUES;
module.flags |= MI_MODULE_FLAG_BOOLEANS;
module.charSize = sizeof(MI_Char);
module.version = MI_VERSION;
module.generatorVersion = MI_MAKE_VERSION(1,0,0);
module.schemaDecl = &schemaDecl;
module.Load = Load;
module.Unload = Unload;
return &module;
}

 

 

Schema.c

schema.c file contains the schema of all related classes, and the schema of each class is represented by a set of embedded structures. Schema.c file defines two MI server functions as well. Schema file could be viewed as a binary format of the schema (mof) file. This file must *NOT* be modified, otherwise could break provider’s functionality. To change the schema, modify the original MOF (in this case sample.mof) and re-generate the code.

To explain the structure of the schema file, a set of snippets are copied below.

(1) schemaDecl is of type MI_SchemaDecl, which defines the schema structure of the provider, which contains all qualifiers declarations and all classes declarations.

 MI_SchemaDecl schemaDecl =
{
qualifierDecls, /* qualifierDecls */
MI_COUNT(qualifierDecls), /* numQualifierDecls */
classes, /* classDecls */
MI_COUNT(classes), /* classDecls */
};

(2) classes property is an array of MI_ClassDecl objects, which contains a list of all classes schema, which was named with <class name>_rtti. For example, MSFT_WindowsProcess_rtti is the schema of MSFT_WindowsProcess class.

 static MI_ClassDecl MI_CONST* MI_CONST classes[] =
{
&CIM_ConcreteJob_rtti,
&CIM_EnabledLogicalElement_rtti,
&CIM_Error_rtti,
&CIM_Job_rtti,
&CIM_LogicalElement_rtti,
&CIM_ManagedElement_rtti,
&CIM_ManagedSystemElement_rtti,
&CIM_Process_rtti,
&CIM_Service_rtti,
&CIM_ServiceProcess_rtti,
&MSFT_WindowsProcess_rtti,
&MSFT_WindowsServiceProcess_rtti,
};

(3) MSFT_WindowsProcess_rtti defines as following, which contains a set of qualifiers, a set of properties, a set of methods, a set of generated stub functions, and other information of the class.

 /* class MSFT_WindowsProcess */
MI_CONST MI_ClassDecl MSFT_WindowsProcess_rtti =
{
MI_FLAG_CLASS, /* flags */
0x006D7313, /* code */
MI_T("MSFT_WindowsProcess"), /* name */
MSFT_WindowsProcess_quals, /* qualifiers */
MI_COUNT(MSFT_WindowsProcess_quals), /* numQualifiers */
MSFT_WindowsProcess_props, /* properties */
MI_COUNT(MSFT_WindowsProcess_props), /* numProperties */
sizeof(MSFT_WindowsProcess), /* size */
MI_T("CIM_Process"), /* superClass */
&CIM_Process_rtti, /* superClassDecl */
MSFT_WindowsProcess_meths, /* methods */
MI_COUNT(MSFT_WindowsProcess_meths), /* numMethods */
&schemaDecl, /* schema */
&MSFT_WindowsProcess_funcs, /* functions */
NULL /* owningClass */
};

(4) MSFT_WindowsProcess_quals is an array of class qualifiers, and MSFT_WindowsProcess_ClassVersion_qual defines one of the qualifier as below.

 static MI_CONST MI_Char* MSFT_WindowsProcess_ClassVersion_qual_value = MI_T("1.0.0");

static MI_CONST MI_Qualifier MSFT_WindowsProcess_ClassVersion_qual =
{
MI_T("ClassVersion"),
MI_STRING,
MI_FLAG_ENABLEOVERRIDE|MI_FLAG_RESTRICTED,
&MSFT_WindowsProcess_ClassVersion_qual_value
};

static MI_Qualifier MI_CONST* MI_CONST MSFT_WindowsProcess_quals[] =
{
&MSFT_WindowsProcess_UMLPackagePath_qual,
&MSFT_WindowsProcess_Description_qual,
&MSFT_WindowsProcess_ClassVersion_qual,
};

(5) MSFT_WindowsProcess_props defines the array of all its properties, including the properties inherited from ancestor classes. One example of the property is MSFT_WindowsProcess_CommandLine_prop.

 static MI_PropertyDecl MI_CONST* MI_CONST MSFT_WindowsProcess_props[] =
{
&CIM_ManagedElement_InstanceID_prop,
&CIM_ManagedElement_Caption_prop,
&CIM_ManagedElement_Description_prop,
&CIM_ManagedElement_ElementName_prop,
&CIM_ManagedSystemElement_InstallDate_prop,
&CIM_Process_Name_prop,
&CIM_ManagedSystemElement_OperationalStatus_prop,
&CIM_ManagedSystemElement_StatusDescriptions_prop,
&CIM_ManagedSystemElement_Status_prop,
&CIM_ManagedSystemElement_HealthState_prop,
&CIM_ManagedSystemElement_CommunicationStatus_prop,
&CIM_ManagedSystemElement_DetailedStatus_prop,
&CIM_ManagedSystemElement_OperatingStatus_prop,
&CIM_ManagedSystemElement_PrimaryStatus_prop,
&CIM_EnabledLogicalElement_EnabledState_prop,
&CIM_EnabledLogicalElement_OtherEnabledState_prop,
&CIM_EnabledLogicalElement_RequestedState_prop,
&CIM_EnabledLogicalElement_EnabledDefault_prop,
&CIM_EnabledLogicalElement_TimeOfLastStateChange_prop,
&CIM_EnabledLogicalElement_AvailableRequestedStates_prop,
&CIM_EnabledLogicalElement_TransitioningToState_prop,
&CIM_Process_CSCreationClassName_prop,
&CIM_Process_CSName_prop,
&CIM_Process_OSCreationClassName_prop,
&CIM_Process_OSName_prop,
&CIM_Process_CreationClassName_prop,
&CIM_Process_Handle_prop,
&CIM_Process_Priority_prop,
&CIM_Process_ExecutionState_prop,
&CIM_Process_OtherExecutionDescription_prop,
&CIM_Process_CreationDate_prop,
&CIM_Process_TerminationDate_prop,
&CIM_Process_KernelModeTime_prop,
&CIM_Process_UserModeTime_prop,
&CIM_Process_WorkingSetSize_prop,
&MSFT_WindowsProcess_CommandLine_prop,
};
……
/* property MSFT_WindowsProcess.CommandLine */
static MI_CONST MI_PropertyDecl MSFT_WindowsProcess_CommandLine_prop =
{
MI_FLAG_PROPERTY|MI_FLAG_READONLY, /* flags */
0x0063650B, /* code */
MI_T("CommandLine"), /* name */
NULL, /* qualifiers */
0, /* numQualifiers */
MI_STRING, /* type */
NULL, /* className */
0, /* subscript */
offsetof(MSFT_WindowsProcess, CommandLine), /* offset */
MI_T("MSFT_WindowsProcess"), /* origin */
MI_T("MSFT_WindowsProcess"), /* propagator */
NULL,
};

(6) MSFT_WindowsProcess_meths defines an array of the schema of all extrinsic methods, while MSFT_WindowsProcess_funcs defines an array of function pointers, which are intrinsic methods.

Due to an extrinsic method might contain a set of input and output parameters, and a set off qualifiers for the method itself as well as all parameters, such that it requires a MI_MethodDecl structure to store all related information. Intrinsic methods do not have qualifiers and it has a fix set of parameters, such that is represented by a function pointer.

/* method MSFT_WindowsProcess.Create() */
MI_CONST MI_MethodDecl MSFT_WindowsProcess_Create_rtti =
{
MI_FLAG_METHOD|MI_FLAG_STATIC, /* flags */
0x00636506, /* code */
MI_T("Create"), /* name */
MSFT_WindowsProcess_Create_quals, /* qualifiers */
MI_COUNT(MSFT_WindowsProcess_Create_quals), /* numQualifiers */
MSFT_WindowsProcess_Create_params, /* parameters */
MI_COUNT(MSFT_WindowsProcess_Create_params), /* numParameters */
sizeof(MSFT_WindowsProcess_Create), /* size */
MI_UINT32, /* returnType */
MI_T("MSFT_WindowsProcess"), /* origin */
MI_T("MSFT_WindowsProcess"), /* propagator */
&schemaDecl, /* schema */
(MI_ProviderFT_Invoke)MSFT_WindowsProcess_Invoke_Create, /* method */
};

static MI_MethodDecl MI_CONST* MI_CONST MSFT_WindowsProcess_meths[] =
{
&MSFT_WindowsProcess_RequestStateChange_rtti,
&MSFT_WindowsProcess_SetPriority_rtti,
&MSFT_WindowsProcess_Create_rtti,
};
……

static MI_CONST MI_ProviderFT MSFT_WindowsProcess_funcs =
{
(MI_ProviderFT_Load)MSFT_WindowsProcess_Load,
(MI_ProviderFT_Unload)MSFT_WindowsProcess_Unload,
(MI_ProviderFT_GetInstance)MSFT_WindowsProcess_GetInstance,
(MI_ProviderFT_EnumerateInstances)MSFT_WindowsProcess_EnumerateInstances,
(MI_ProviderFT_CreateInstance)MSFT_WindowsProcess_CreateInstance,
(MI_ProviderFT_ModifyInstance)MSFT_WindowsProcess_ModifyInstance,
(MI_ProviderFT_DeleteInstance)MSFT_WindowsProcess_DeleteInstance,
(MI_ProviderFT_AssociatorInstances)NULL,
(MI_ProviderFT_ReferenceInstances)NULL,
(MI_ProviderFT_EnableIndications)NULL,
(MI_ProviderFT_DisableIndications)NULL,
(MI_ProviderFT_Subscribe)NULL,
(MI_ProviderFT_Unsubscribe)NULL,
(MI_ProviderFT_Invoke)NULL,
};

typedef void (MI_CALL *MI_ProviderFT_EnumerateInstances)(
_In_opt_ void* self,
_In_ MI_Context* context,
_In_z_ const MI_Char* nameSpace,
_In_z_ const MI_Char* className,
_In_opt_ const MI_PropertySet* propertySet,
MI_Boolean keysOnly,
_In_opt_ const MI_Filter* filter);

 

WMIAdapter.c

In order to host MI provider(s) within WMI, an adapter layer is necessary since WMI host process only supports COM based provider interface. WMIAdapter.c is a file to enable the MI provider running with WMI service. This file must *NOT* be modified.

 

Provider.DEF

Provider.DEF defines list of exported APIs from the provider DLL. Usually, this file name was specified in visual studio project linker options ‘/DEF:"Provider.DEF"’ (see following picture), in order to tell the linker to export a set of functions from target DLL.


 

Happy holidays! Next blog will discuss how to implement instance (normal) class.

 

Haowei Qin

Senior SDE

Standards Based Management

Implementing MI Provider (5) - Implement

$
0
0

As discussed in Implementing MI Provider (4) - Generate Code (continute)Convert-MofToProvider.exe tool generates <class name>.c file for each target class, which contains stub functions to place the business logic of each operation, such as enumerate/get/delete/modify/new/<extrinsic methods>.

This blog discusses how to implement stub functions of normal (instance) classes, while association and indication class will be discussed separately in future. Let's first introduce a set of frequently used MI APIs (when implementing MI provider) and then discuss how to implement the business logic of each generated stub function using those APIs.

MI Context API

The name of provider APIs are suffixed by MI_Context, which means the operation performed by the API was against the MI_Context object, while MI_Context defined as below, the MI_ContextFT structure definition could be found in mi.h from windows 8 SDK.

 typedef struct _MI_Context MI_Context;
struct _MI_Context
{
/* Function table */
const MI_ContextFT* ft;

/* Reserved for internal use */
ptrdiff_t reserved[3];
};

All MI_Context_* APIs are just a wrapper function that calls into one function inside MI_ContextFT structure.

MI_Context_PostInstance is used to post a MI_Instance back to server. Usually a MI_Instance could be constructed by calling generated constructor API defined in a class header file. Normally, provider calls into this API to delivery prepared instances. For example, client send an enumeration request to omi server, then the provider generates N number of instances, and should call this API N times to post them back to server.

MI_Context_PostError is used to post an error code with error message back to server.

MI_Context_PostCimError is used to post a CimError instance back to server.

MI_Context_PostResult is used to post the final operation result back to server.

NOTE: All Post APIs listed above are posting data to server, while server will deliver the data to client.

Any operation might post arbitrary number of instances back to server, however if without any specific clarification, all provider stub functions should call one of the following APIs once and only once (One exception is indication class's stub function EnableIndications, which may be covered by future blog).

  MI_Context_PostError

  MI_Context_PostCimError

  MI_Context_PostResult

MI_Context_GetLocalSession gets local MI_Session object, which can be used to communicate back to the local CIMOM server for local operations, for example a provider need to query instances of another class on the local machine. The provider must *NOT* call MI_Session_Close on the local session object since the life cycle of the session object is managed by the host process.

 

MI Powershell Semantics API

Powershell Semantics APIs are designed to enable MI provider to interact with Powershell Console efficiently, such as MI_Context_PromptUser, be used by MI provider to send a prompt message to the client application (powershell for example) to confirm whether to continue the operation or not.

 MI_Context_PromptUser

MI_Context_ShouldContinue

MI_Context_ShouldProcess

MI_Context_WriteCimError

MI_Context_WriteError

MI_Context_WriteProgress

MI_Context_WriteMessage

MI_Context_WriteVerbose

MI_Context_WriteWarning

MI_Context_WriteDebug

 

 

MI Client API

A MI provider may need MI client API to perform specific operation to a different CIM class, such as querying instances of another CIM class or invoking an extrinsic method of another CIM class. Following are several frequently used client APIs, for a complete list of APIs, please refer to windows 8 SDK mi.h

MI_Application_Initialize is used to initialize and create a MI_Application object.

MI_Application_Close is used to close a MI_Application object, which will cancel all sessions and operations related to this application object.

MI_Application_NewSession creates a new MI_Session object, which is used to kick off any operation towards the CIMOM server.

MI_Session_Close closes the session, all operations have to be closed before session close finished.

MI_Session_EnumerateInstances start an operation to enumerate instances of a target class under specific namespace from target server, which will return a MI_Operation object.

MI_Operation_GetInstance retrieves MI_Instance from a started MI_Operation object synchronously.

MI_Operation_Close closes the operation.

 

Taking MSFT_WindowsProcess class example, remaining of this blog explains the general process of implementing enumerate instance, get instance, modify instance, delete instance, create instance, and extrinsic method.

(1) Implement EnumerateInstances

Generally speaking, there are six steps to implement enumerate instance operation for a class,

1) Firstly construct an instance of the target class by invoking <class name>_Construct API,

2) Set properties value of the instance. One important note is that all key properties of the instance must be set, and the combination of the key properties value should be unique, which identifies the instance uniquely.

3) Post the instances back by invoking <class_name>_Post API.

4) Destruct the constructed instances by calling <class name>_Destruct API.

5) Post final result to client by calling one of the post API. Call MI_Context_PostResult if succeed.

6) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Sometime, it may take time to complete the enumerating operation if there are quite a few instances to be posted back to client, and it makes sense to use ps-semantic API MI_Context_WriteProgress to notify client the current percentage of the operation.

Following diagram is the implementation diagram of MSFT_WindowsProcess_EnumerateInstances, see example code as well. Refer to MI API sample for complete implementation.

NOTE: For any MI_Instance constructed successfully by calling generated API <class name>_Construct, it should be destructed always once and only once by calling <class name>_Destruct.
For any MI_Instance created by MI_Context_NewInstance or generated API such as MSFT_WindowsProcess_Clone, it should be deleted by calling MI_Instance_Delete once and only once.

 

 

void MI_CALL MSFT_WindowsProcess_EnumerateInstances(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_opt_ const MI_PropertySet* propertySet,
_In_ MI_Boolean keysOnly,
_In_opt_ const MI_Filter* filter)
{
MI_Result result = MI_RESULT_OK;

MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(propertySet);
MI_UNREFERENCED_PARAMETER(filter);

result = EnumerateProcesses(context, keysOnly);
if (result == MI_RESULT_ACCESS_DENIED)
{
MI_Context_PostError(context,
result,
MI_RESULT_TYPE_MI,
MI_T("Access denied. Please try to run client process with elevated priviledge."));
}
else if (result != MI_RESULT_OK)
{
MI_Context_PostError(context,
result,
MI_RESULT_TYPE_MI,
MI_T(""));
}
else
{
MI_Context_PostResult(context, result);
}
}

(2) Implement GetInstance

Get instance operation requires that all key properties of a MI_instance are sent to MI provider. There are several steps to implement get instance operation for a class,

1) Firstly check the key properties value of the given instance is valid or not.

2) If it is valid, then construct an instance of the target class by invoking <class name>_Construct API,

3) Set properties' value of the instance. One important note is that all key properties of the instance must be set, which are equal to the input instance, and the combination of the key properties value should be unique, which identifies the instance uniquely.

4) Post the instances back by invoking <class_name>_Post API.

5) Destruct the constructed instance by calling <class name>_Destruct API.

6) Post final result to client by calling MI_Context_PostResult API.

7) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Refer to MI API sample for complete implementation of MSFT_WindowsProcess_GetInstance.

NOTE: If a MI provider does not support Get instance operation, i.e., keep generated code <class name>_GetInstance unchanged (i.e., post MI_RESULT_NOT_SUPPORTED via one of the post API), then the get instance request will be redirected to <class name>_EnumerateInstance function, and returned instances from EnumerateInstances function will be filtered based on the key properties value of the incoming requested instance, and then CIMOM server post back result full instance back to client. But this behavior is inefficient and might cause performance issue in case the class has too many instances.

 

(3) Implement CreateInstance

Not all classes support CreateInstance operation. The confusing part of the intrinsic create method is that it is hard for client user to figure out which properties value are mandatory to create an instance of a target class, and the supported format of each properties value, especially for key properties, it kind of different for client user to specify a valid key value to create a new instance, sometime, the key properties value might be generated by MI provider automatically. So commonly, defining an extrinsic method to create instance makes more sense, the method could define a set of input parameters and might the format of each parameter through parameter qualifiers. One example is Create method of MSFT_WindowsProcess class. However, if a MI provider does support intrinsic CreateInstance operation, following are the steps to implement it,

1) Firstly check the properties’ value of the given instance is valid or not.

2) If it is valid, then construct an instance of the target class by invoking <class name>_Construct API,

3) Set all of the properties’ value of the instance. Some properties’ value might come from input instance; other might be calculated by the provider. One important note is that all key properties of the instance must be set, which are equal to the input instance, and the combination of the key properties value should be unique, which identifies the instance uniquely.

4) Post the newly created instance back by invoking <class_name>_Post API. This step is mandatory due to client relies on this to get the key values of the newly created instance.

5) Destruct the constructed instance by calling <class name>_Destruct API.

6) Post final result to client by calling MI_Context_PostResult API.

7) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Refer to MI API sample for complete implementation of MSFT_WindowsProcess_CreateInstance.

 

(4) Implement ModifyInstance

ModifyInstance operation faces the similar issue with CreateInstance due to it is hard for client user to figure out which property could be modified and which property could not. Someone might argue that Read and Write qualifiers are the flag to tell which property could be modified, but in reality, not all of the class schema definition follows Read/Write qualifiers strictly. If schema does define Read/Write qualifier appropriately, powershell console as a client does prevent modifying the value of Read only properties.

For ModifyInstance operation, it is also challenging to implement. Provider developer needs to figure out the set of properties to be modified, unfortunately there is no straight forward approach to tell the exactly list of properties to be modified, the PropertySet parameter of following method is always set to NULL in windows 8.

void MI_CALL MSFT_WindowsProcess_ModifyInstance(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_ const MSFT_WindowsProcess* modifiedInstance,
_In_opt_ const MI_PropertySet* propertySet)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(propertySet);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}
There is one workaround of the problem,
  • The provider could query the latest value of the instance.
  • Comparing the instance latest value with given instance to be modified, for any property that has a different value, it might need modification.
  • One important note is that if the property value is NULL, then it needs another technique to tell should the property be modified or not.

The following utility method could tell whether a specific property was explicitly modified by client application or not.

MI_Result MI_CALL MI_Instance_IsElementModified(
_In_ const MI_Instance* self,
_In_z_ const MI_Char* name,
_Out_opt_ MI_Boolean* modified)
{
MI_Uint32 flags = 0;
MI_Result r;
if (!modified)
{
return MI_RESULT_INVALID_PARAMETER;
}
r = MI_Instance_GetElement(self, name, NULL, NULL, &flags, NULL);
if (r == MI_RESULT_OK)
{
if ((flags & MI_FLAG_NOT_MODIFIED) == 0)
{
*modified = MI_TRUE;
}
else
{
*modified = MI_FALSE;
}
}
return r;
}
NOTE: MI_FLAG_NOT_MODIFIED is only applicable to top level property, which was modified by MI_Instance_SetElementAt or MI_Instance_SetElement APIs or equivalent MI.net APIs. Since a MI Instance could have embedded instance property, if a sub property value of an embedded instance property was modified, since there is no MI_Instance_SetElementAt or MI_Instance_SetElement API call to notify the parent instance of the change of the embedded property, then above utility method won’t work as expected for this property.

Given the above limitations, it might make more sense to define a specific extrinsic method to modify a specific property of an instance. One example is SetPriority method of MSFT_WindowsProcess class. However, some classes may still need to implement intrinsic ModifyInstance method following below steps,

1) Firstly check the key properties value of the given instance is valid or not.

2) If it is valid, before modifying the instance properties’ value, it might make sense to request confirmation from client application by calling MI_Context_PromptUser or …… to confirm that user really wants the modification.

3) If get positive answer from client user, then modify all of the requested non-key properties value of the instance by figuring out which properties are requested to be modified based on above techniques. How to modify the instance value is really depends on the provider’s business logic, it could modify the registry key; could store the value into file or database; could modify the value in memory cache, etc. There is NO need to post any instance back.

4) Post final result to client by calling MI_Context_PostResult API.

5) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Refer to MI API sample for complete implementation of MSFT_WindowsProcess_ModifyInstance. 

 

(5) Implement DeleteInstance 

DeleteInstance may have different meaning to different class, for MSFT_WindowsProcess class, it means kill the process. To implement delete instance,

1) Firstly check the key properties value of the given instance is valid or not.

2) If it is valid, then delete the instance. For MSFT_WindowsProcess class, it terminates the process corresponding to the given MI Instance.

3) Post final result to client by calling MI_Context_PostResult API.

4) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Refer to MI API sample for complete implementation of MSFT_WindowsProcess_DeleteInstance. 

 

(6) Implement Extrinsic Method

Extrinsic methods include instance method and static method. The difference between static and instance method is that static method has [static] qualifier defined; while instance method does not have. For static method, the parameter [instanceName] is always set to NULL, while instance method has a valid instanceName parameter to tell provider on which instance should the method be invoked.

 _In_ const MSFT_WindowsProcess* instanceName,

For any method, there is a structure defined for it, used to store all input and output parameters' value, and return value as well. There are several steps to implement method invoking operation for a class,

1) For instance method, firstly check the key properties value of the given instance is valid or not. If valid then continue,

2) Construct an instance of the target method by invoking <class name>_<method name>_Construct API,

3) Implement the method logic base on the input parameters and setting all of the output parameters and return value of the output instance. One important note is that ….

4) Post the instances back by invoking <class_name>_<method name>_Post API.

5) Destruct the constructed instance by calling <class name>_<method name>_Destruct API.

6) Post final result to client by calling MI_Context_PostResult API.

7) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Please look at MI API sample for an example of extrinsic method implementation: MSFT_WindowsProcess_Invoke_SetPriority.

 

Happy Holidays! Next blog will discuss how to build, register and debug MI provider.

 

Haowei Qin

Senior SDE

Standards Based Management

 

 


Implementing MI Provider (6) - Build, Register, and Debug

$
0
0

This blog discusses how to build, register and debug MI provider.

Build

To build MI provider, open visual studio 2012, create an empty Visual C++ project named “process”, set type to DLL, and add all generated files into the project.

  • Open project property dialog box, navigate to linker page, click input option, Set Module Definition File to “Provider.DEF”. This will ensure DLL exports the set of APIS defined in provider.DEF file. NOTE, make sure the LIBRARY section of Provider.DEF file was set to process.DLL.
  • Select your target platform, if you want the provider to run on X64 platform, please create “X64” platform through “Configuration Manager …” button, otherwise keep the default option is “Win32”, which means target platform is X86.
  • Optionally, go to “C/C++” -> “code generation” page, select “Multi-threaded Debug (/MTd)” or “Multi-threaded (/MT)” for runtime library to avoid the issue that target test machine is missing the runtime DLL requested by your provider DLL.

Refer to MI API sample for the sample project, open the solution file within Visual studio 2012, which integrates windows 8 SDK installation directory, by build all projects, you will be able to get all of the MI provider's binaries, for example process.DLL.

 

Register

(1) Tool

Register-CimProvider.exe is the tool used to register MI provider, shipped with windows 8, which can be found under %system32% folder. Following diagram shows the process of the registration.

On windows 8, MI provider is managed by WMI service. Register-CimProvider.exe tool helps to register MI providers into the system (WMI). The tool creates mof and mfl files on the fly based on the schema definition from the DLL as described above, and then compile the mof/mfl files into WMI repository. The tool registers the providers' DLL as a COM server as well.

To register process.DLL, copy the compiled process.DLL to a Windows 8 or Windows Server 2012 machine, and invoke the following command line from elevated commandline prompt (cmd.exe):

Register-CimProvider.exe -Namespace Root/Standardcimv2/sample -ProviderName process -Path process.dll -verbose -ForceUpdate

Upon that completes, process.dll should be successfully registered into system. By default, the provider is registered as a coupled provider, i.e, it runs in a Wmiprvse.exe process. And default hosting model value is NetworkServiceHost, which means the host process is running under NETWORK SERVICE account. process.mof will also be generated under current directory, which contains the detail registration schema of the provider.

(2) Provider Hosting Model

On windows 8, MI Provider's hosting model follows the same specification of WMI provider hosting model and security. Please refer following link for the details.

http://msdn.microsoft.com/en-us/library/windows/desktop/aa392783(v=vs.85).aspx

 

MI provider can be hosted as coupled provider and decoupled provider. As mentioned above, coupled provider will be hosted in wmiprvse.exe running under specific account which could be specified by -HostingModel argument, while a decoupled provider is running under any customized arbitrary process. WMI service controlled the lifecycle of a coupled provider, i.e., loading/unloading the provider time; while the lifecycle of a decoupled provider is completely controlled by its hosting process. MI API also provides a convenient API to host arbitrary MI provider within current process.

(3) Host Decoupled MI Provider

MI_Application_NewHostedProvider is the API to host provider as a decoupled provider under current process. Following is the pseudo code snippet of hosting a MI provider as a decoupled provider. Code snippet of hosting decoupled MI provider, (please refer to MI API sample dcuphost project for the complete implementation) 

 // load provider DLL
hProvider = LoadLibraryExW(“process.DLL”, NULL, 0);
// query MI_Main function from provider DLL
mi_main = (MI_MainFunction)GetProcAddress(hProvider, MI_MAIN_FUNCNAME);
// initialize MI_Application object
result = MI_Application_Initialize(0, NULL, NULL, &application);
// host the provider as decoupled provider
result = MI_Application_NewHostedProvider(&application,
pArgument->lpNamespace,
pArgument->lpProviderName,
mi_main,
NULL,
&hostedProvider);

Note that before hosting a MI provider as decoupled provider, it should be registered to the system as a decoupled provider firstly. Take process.dll for example, below is the command line to register it as a decoupled one,

Register-CimProvider.exe -Namespace Root/StandardCimV2/dcupsample -ProviderName process -Path C:\temp\provider.dll -Decoupled O:BAG:BAD:(A;;0x1;;;BA)(A;;0x1;;;NS) -Impersonation True

 

Test/Validate

To validate the MI provider is functioning or not, there are couple of approaches listed as of following, see MI API sample for the sample code.

Methodology

Description

Wbemtest.exe

Send request to provider via this UI tool.

Cim Cmdlets

A set of new cmdlets shipped with windows 8

Cim based cmdlets

Create cdxml to define a set of cim-based cmdlets to access the provider

Application

Using MI C API or MI.net API to build an application to verify the provider

 

Un-register

How to un-register the MI provider completely from system? Unfortunately, Register-CimProvider.exe tool has no option to un-register a MI provider from system. It providers an option to generate uninstall mof and (or) mfl file during the registration time. The option name is GenerateUnregistration.

Take process provider for example, if you run following command line, processUninstall.mof and process.mof will be generated under current directory.

Register-CimProvider.exe -Namespace Root/Standardcimv2/sample -ProviderName process -Path process.dll -verbose –ForceUpdate -GenerateUnregistration

processUninstall.mof contains instructions of unregistering the registered provider from system. It will delete registered non-abstract classes, related provider registration instances, and __Win32Porvider instance from system.

 #pragma classflags ("forceupdate")
#pragma namespace ( "\\\\.\\Root\\Standardcimv2\\sample")

#pragma deleteclass("MSFT_WindowsProcess",nofail)
#pragma deleteclass("MSFT_WindowsServiceProcess",nofail)
#pragma deleteinstance("__MethodProviderRegistration.Provider=\"\\\\\\\\.\\\\Root\\\\Standardcimv2\\\\sample:__Win32Provider.Name=\\\"process\\\"\"", nofail)
#pragma deleteinstance("__InstanceProviderRegistration.Provider=\"\\\\\\\\.\\\\Root\\\\Standardcimv2\\\\sample:__Win32Provider.Name=\\\"process\\\"\"", nofail)
#pragma deleteinstance("__Win32Provider.Name=\"process\"", nofail)

Notes regarding un-register MI provider,

Abstract classes registered by MI provider are not generated in Uninstall.mof. Because abstract classes might be referred by other providers under the same namespace, detecting that would cause complexity. As a MI provider developer, you can choose to delete all classes defined in the MI provider by adding deleteclass instructions for all classes into Uninstall.mof, as long as it can be guaranteed that there is no other provider be affected under the same namespace.

Registering MI provider will also create one registry key into the system. Process provider has the following registry key and value created,

 Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\CLSID\{EDCB2856-2E63-4F57-B3A1-F03CC7D9FEE3}]
@="process"

[HKEY_CLASSES_ROOT\CLSID\{EDCB2856-2E63-4F57-B3A1-F03CC7D9FEE3}\InprocServer32]
@="F:\\sdksrc\\book\\x64\\Debug\\process.dll"
"ThreadingModel"="Free"
To delete the registry key from system, run following command line,
reg delete HKEY_CLASSES_ROOT\CLSID\{EDCB2856-2E63-4F57- B3A1-F03CC7D9FEE3} /f

 

Debug

MI provider could be hosted in wmiprvse.exe process, or hosted as decoupled provider. Decoupled provider is relatively easy to debug since the host process has to start in order to host decoupled provider. By attaching to host process, you can debug the provider. This section focuses on debugging coupled provider, all break points and techniques are applicable to decoupled provider as well.

Normal debugging tools include visual studio and windbg. Following content introduces both techniques.

To debug a coupled provider, you need to

(1) Launch the provider into host process

(2) Attach to the host process

(3) set breakpoints and debug.

To launch MI provider, you may send an operation request to the MI provider (via powershell/cim cmdlets for example), the provider will be loaded into host process.

To attach to host process, you can run “tasklist –m <>.DLL” to tell which process is loading the provider. And use either windbg or visual studio 2012 attach to the process.

And then set breakpoints and you will be able to debug through. Following picture shows a snapshot of debugging EnumerateInstances method of MSFT_WindowsProcess class.

 

Debug provider Load/Class Load function

These two functions will be invoked once and only once, which were already called by the time attaching debugger to the host process. To debug these functions, debugger has to attach to host process before they get invoked. And following are the tricks,

(1) Register the provider as decoupled provider and start a host process to host the provider. This way you can attach the debugger tool to the host process to debug the provider load and class load function.

(2) Find another provider which shares the same host process with the target provider. Invoke another provider firstly will launch the host process. By attaching to the process, you will be able to set deferred breakpoints at Load and class Load function to debug.

For example, by default process.dll hosting model is NetworkServiceHost, which is the same with Cimwin32.dll. By invoking “wmic os” on any command line console, you will be able to see a wmiprvse.exe host process was launched running under NETWORKING SERVICE account. Now attach debugger to the process, and then let the process continue running. Then if invoke the process.dll provider by enumerate instances of MSFT_WindowsProcess class from powershell console, you will be able to see that process.dll is being launched in the same host process.

If you are not able to use trick 2, then try to use gflags.exe to set following debugger for image file “wmiprvse.exe”.

cdb.exe -server tcp:port=8080 -c "g"

Then invoke the provider from powershell console, now the host process will be launched but was blocked into cdb.exe, use windbg.exe to connect to the remote debugging server, and then set interested breakpoints before hit “g”.

windbg.exe -remote tcp:server=<server>,port=8080

Now you are able to debug Load and class Load functions.

Warning, For trick (2), you might block other host process from being launched, since cdb won’t be able to create debugger server on the same tcp port more than once.

 

 

Happy Holidays! I hope "Implementing MI Provider" blogs helps. Fell free to comment, ask questions, or email haoweiqin@outlook.com for deep discussions regarding CIM, WMI, etc.

 

Haowei Qin

Senior SDE

Standards Based Management

Performing Management tasks using Cimcmdlets - Processes

$
0
0

WMI provides a standardized system management infrastructure that can be leveraged by a number of different clients. WMI providers and the classes exposed by the providers help in various management tasks.

Our MSDN documentation lists a bunch of management scenarios and the corresponding Visual Basic samples, but it is much easier for an IT administrator to perform the same operations using PowerShell.

Customers have been requesting samples that will help them perform management tasks using PowerShell – therefore, in this series of blog posts we will be covering the PowerShell implementation of scenarios that are listed at the following MSDN page: http://msdn.microsoft.com/en-us/library/aa394585(v=vs.85).aspx

In this post we will be going over the scenarios specific to "Process Management" listed at : http://msdn.microsoft.com/en-us/library/aa394599(v=vs.85).aspx

 

Here are the corresponding PowerShell snippets:

1. Run an application in a hidden window:

PS:> $processStartupClass = Get-CimClass -ClassName Win32_ProcessStartup -Namespace root/cimv2

$processStartupInfo = New-CimInstance -cimclass $processStartupClass -Property @{ShowWindow =0} –Local 

PS:> $processClass = Get-CimClass -ClassName Win32_Process -Namespace root/cimv2

PS:> Invoke-CimMethod -CimClass $processClass -MethodName Create -Arguments @{commandline="notepad.exe"; ProcessStartupInformation = [CimInstance]$processStartupInfo}

 

2. Determine which scripts are running on the local computer:

 

PS:> $query = "SELECT * FROM Win32_Process WHERE Name = 'cscript.exe' OR Name = 'wscript.exe'"

PS:> $insts = Get-CimInstance -Query $query -Namespace root/cimv2

PS:> $insts | Format-Table –Property @(“Name”, “commandline”)

 

 

3. Find out the account name under which a process is running:

PS:> $query = "Select * from Win32_Process"

PS:> $insts = Get-CimInstance -Query $query -Namespace root/cimv2

PS:> # OR

PS:> $insts = Get-CimInstance –ClassName Win32_Process –Namespace root/cimv2

PS:> $insts | %{

                         Write-host   $_.CimInstanceProperties["Name"]

                          $owner = Invoke-CimMethod -InputObject $_ -MethodName GetOwner

                          $owner | Format-Table –Property @(“Domain”, “User”, “PSComputerName”)

                       }

 

4. Change the priority of a running process:

PS:> $query = "Select * from Win32_Process Where Name = 'Notepad.exe'"

PS:> $insts = Get-CimInstance -Query $query -Namespace root/cimv2

PS:> $aboveNormal = 32768

PS:> $insts | %{

    Invoke-CimMethod -InputObject $_ -MethodName SetPriority -Arguments @{Priority = [Uint32]$aboveNormal}

} 

 

5. Terminate a process using a script:

PS:> $query = "Select * from Win32_Process Where Name = 'Notepad.exe'"

PS:> Invoke-CimMethod -Query $query -MethodName Terminate

 

6. Determine how much processor time and memory each process is using:

$query = "Select * from win32_process"

$procs = Get-CimInstance -Query $query

# OR

$procs = Get-CimInstance –ClassName Win32_Process –Namespace root/cimv2

 

foreach($proc in $procs)

{

    $result = New-Object PSObject -Property @{

                                processorTime = ($proc.KernalModeTime + $proc.UserModeTime) / 10000000

                                Name = $proc.Name

                                ProcessID = $proc.ProcessId

                                WorkingSetSize = $proc.WorkingSetSize

                                PageFileUsage = $proc.PageFileUsage

                                PageFaults = $proc.PageFaults

                            }

                                                              

$result | Format-Table -Property @("Name", "ProcessID", "WorkingSetSize", "PageFileUsage", "PageFaults", "ProcessorTime")

}

 

 

7. Determine what applications are running on a remote computer:

$cimSession = New-CimSession remoteMachine –Credential $psCreds

$query = "Select * from Win32_Process"

$procs = Get-CimInstance -Query $query -CimSession $cimSession

# OR

$procs = Get-CimInstance –ClassName Win32_Process –Namespace root/cimv2

$procs |  Format-Table –Property @(“Name", “ProcessID", “ThreadCount", “PageFileUsage", “PageFaults", “WorkingSetSize")

 

As mentioned above, this blog series will cover various management scenarios. The next post will be about Computer Hardware Management scenarios listed at: http://msdn.microsoft.com/en-us/library/aa394587(v=vs.85).aspx

 

Thanks

Vaibhav Chugh [MSFT]

Standards Based Management

Performing Management tasks using Cimcmdlets [2] – Computer Hardware

$
0
0

As part of this blog series we are sharing PowerShell snippets for a few Computer Management tasks. In this post we will be going over the samples for Computer Hardware management.

The Visual Basic samples for Computer Hardware Management are at: http://msdn.microsoft.com/en-us/library/aa394587(v=vs.85).aspx

The corresponding PowerShell samples are below:

1. Determine how much free memory a computer has:

PS:> # Get Instance of Win32_OperatingSystem

PS:> $os = Get-CimInstance –ClassName Win32_OperatingSystem –Namespace root/cimv2

PS:> # Look for FreePhysicalMemory property

PS:> $os | Format-Table -Property FreePhysicalMemory

2. Determine whether a computer has a DVD drive:

# Look for instances of Win32_CDROMDrive

$insts = Get-CimInstance –ClassName Win32_CDROMDrive –Namespace root/cimv2

# Loop through them and print DeviceID, Description and Name

foreach($inst in $insts)

{

$inst | Format-Table -Property @("Name", "DeviceID", "Description")

}

3. Determine how much RAM is installed in a computer:

PS:> # Get instance of Win32_ComputerSystem

PS:> $inst = Get-CimInstance –ClassName Win32_ComputerSystem

PS:> # Print Name of the System and the Total Physical Memory

PS:> $inst | Format-Table -Property @("Name", "TotalPhysicalMemory")

4. Determine if a computer has more than one processor:

PS:> # Get instance of Win32_ComputerSystem

PS:> $inst = Get-CimInstance –ClassName Win32_ComputerSystem –Namespace root/cimv2

PS:> # Print Name of the system and Number of Processors

PS:> $inst | Format-Table -Property @("Name", "NumberOfProcessors")

5. Determine whether a computer has a PCMCIA slot:

# Use the Win32_PCMCIAController class and check the value of the Count property.

# If Count is 0, then the computer has no PCMCIA slots.

$insts = Get-CimInstance –ClassName Win32_PCMCIAController –Namespace root/cimv2

if($insts -ne $null)

{

$count = $insts.Count

if($count -eq $null)

{

$count = 1

}

Write-Host "Number of PCMCIA slots: $count"

}

else

{

Write-Host "Number of PCMCIA slots: 0"

}

6. Identify devices that are not working (those marked with an exclamation point icon in Device Manager)?

# Use the Win32_PnPEntity class

# Use the following clause in your WQL query. WHERE ConfigManagerErrorCode <> 0

$query = "Select * from Win32_PnPEntity WHERE ConfigManagerErrorCode <> 0"

$insts = Get-CimInstance -Query $query

# Loop through all such devices and print their information.

foreach($inst in $insts)

{

$inst | Format-Table -Property @("ClassGuid", "Description", "DeviceID", "Manufacturer", "Name", "PNPDeviceID", "Service")

}

7. Determine the properties of the mouse used on computer:

# Use the Win32_PointingDevice class.

# This returns the properties of all pointing devices, not just mouse devices.

$insts = Get-CimInstance –ClassName Win32_PointingDevice

# Loop though instances of Win32_PointingDevice and print the properties.

foreach($inst in $insts)

{

$inst | Format-Table –Property @(“Description", “DeviceID", “DeviceInterface", “DoubleSpeedThreshold", “Handedness", “HardwareType", “InfFileName", “InfSection", “Manufacturer", “Name", “NumberOfButtons", “PNPDeviceID", “PointingType", “QuadSpeedThreshold", “Resolution", “SampleRate", “Synch")

}

8. Determine the speed of a processor installed in a computer:

# Use the Win32_Processor class and check the value of the MaxClockSpeed property.

$insts = Get-CimInstance –ClassName Win32_Processor –Namespace root/cimv2

foreach($inst in $insts)

{

$inst | Format-Table –Property @(“ProcessorId", “MaxClockSpeed")

}

9. Determine whether a computer is a tower, a mini-tower, a laptop, and so on:

# Use the Win32_SystemEnclosure class and check the value of the ChassisType property.

$colChassis = Get-CimInstance –ClassName Win32_SystemEnclosure –Namespace root/cimv2

# Loop though the collection of chassis

foreach($chassis in $colChassis)

{

# Look though the Chassis Types

foreach($item in $chassis.ChassisTypes)

{

Write-Host "Chassis Type: $item"

}

}

10. Get the serial number and asset tag of a computer:

# Use the Win32_SystemEnclosure class, and the properties SerialNumber and SMBIOSAssetTag.

$sytemEnclosures = Get-CimInstance –ClassName Win32_SystemEnclosure

$sytemEnclosureCollection = @()

$sytemEnclosureCollection += $sytemEnclosures

foreach($systemEnclosure in $sytemEnclosureCollection)

{

$systemEnclosure | Format-Table –Property @(“PartNumber”, "SerialNumber”, “SMBIOSAssetTag")

}

11. Determine what kind of device is plugged into a USB port:

# Use the Win32_USBHub class and check the Description property.

# This property may have a value such as "Mass Storage Device" or "Printing Support".

$items = Get-CimInstance –ClassName Win32_USBHub –Namespace root/cimv2

foreach($item in $items)

{

$item | Format-Table –Property @(“DeviceID", “PNPDeviceID", “Description")

}

12. Determine how many tape drives are installed on a computer:

# Use the class Win32_TapeDrive class and then count the number of instances received.

# If Count = 0, then no tape drives are installed on the computer.

$insts = Get-CimInstance –ClassName Win32_TapeDrive

$count = 0

if($insts -ne $null)

{

$count = $insts.Count

if($count -eq $null)

{

$count = 1

}

}

Write-Host "Number of tape drives = $count"

We will be covering other computer management scenarios in our future posts. If you have any questions please feel free to send them to us.

Important links:

            Processes

Thanks

Vaibhav Chugh [MSFT]

Standards Based Management

Performing Management tasks using Cimcmdlets [3] – Operating System

$
0
0

As a part of the ongoing blog series “Performing Management tasks using Cimcmdlets” we are sharing PowerShell snippets for a few Computer Management tasks.

In this post we will be going over the PS snippets for Operating System Management. The corresponding Visual Basic samples are at: http://msdn.microsoft.com/en-us/library/aa394596(v=vs.85).aspx

1. Determine if a service pack has been installed on a computer:

PS:> # Get Instance of Win32_OperatingSystem

PS:> # Look for ServicePackMajorVersion and ServicePackMinorVersion properties

PS:> Get-CimInstance –ClassName Win32_OperatingSystem –Namespace root/cimv2 | Format-Table -Property @("ServicePackMajorVersion", "ServicePackMinorVersion")

2. Determine when the operating system was installed on a computer:

PS:> # Get Instance of Win32_OperatingSystem

PS:> # Look for InstallDate property

PS:> Get-CimInstance –ClassName Win32_OperatingSystem –Namespace root/cimv2 | Format-Table -Property @(" InstallDate ")

3. Determine which version of the Windows operating system is installed on a computer:

PS:> # Get Instance of Win32_OperatingSystem

PS:> # Look for Caption and Version properties

PS:> $os = Get-CimInstance –ClassName Win32_OperatingSystem –Namespace root/cimv2 | Format-Table -Property @("Caption", "Version")

4. Determine which folder is the Windows folder (%Windir%) on a computer:

PS:> # Get Instance of Win32_OperatingSystem

PS:> # Look for WindowsDirectory property

PS:> $os = Get-CimInstance –ClassName Win32_OperatingSystem –Namespace root/cimv2 | Format-Table -Property @("WindowsDirectory")

5. Determine what hotfixes have been installed on a computer:

PS:> # Get Instance of Win32_ QuickFixEngineering

PS:> # Look for HotFixID property

PS:> Get-CimInstance –ClassName Win32_QuickFixEngineering –Namespace root/cimv2 | Format-Table -Property @("HotFixID")

We will be covering other computer management scenarios in our future posts. If you have any questions please feel free to send them to us.

Important links:

              Processes

              Computer Hardware

Thanks

Vaibhav Chugh [MSFT]

Standards Based Management

Performing management tasks using CIM Cmdlets [4] – Files and Folders

$
0
0

 

As a part of the ongoing blog seriesPerforming Management tasks using CIM Cmdlets we are sharing PowerShell snippets for a few computer management tasks.

In this post, we will be going over the PowerShell snippets for file and folder management. The corresponding Visual Basic samples are in the MSDN article, WMI Tasks for Files and Folders.


  • Rename a file:

PS:> $fileName = 'C:\\Temp\\Test1.txt'

PS:> $newFileName = 'C:\\Temp\\Test1_NewName.txt'

PS:> $query = "SELECT * FROM CIM_DataFile WHERE Name = '$fileName'"

PS:> # Get Instance of CIM_DataFile where file name is C:\\Temp\\Test1.txt

PS:> $file = Get-CimInstance -Query $query -Namespace root/cimv2

PS:> # Change name of file Test1.txt to Test1_NewName.txt

PS:> Invoke-CimMethod -InputObject $file -MethodName 'Rename' –Arguments
@{FileName=$newFileName}

ReturnValue PSComputerName
----------- -------------- 0

 

  • Determine whether users have .MP3 files stored on their computer:

PS:> $query = "SELECT * FROM CIM_DataFile WHERE Extension = 'mp3'"

PS:> # Get Instances of CIM_DataFile with Extension property set to mp3

PS:> $mp3Files = Get-CimInstance -Query $query -Namespace root/cimv2

PS:> $mp3Files | Select Name

Name
----
c:\temp\test1.mp3
c:\temp\test2.mp3

 

  • Create shared folders on a computer:

PS:> # Create new share under C:\Temp called TestShare

PS:> Invoke-CimMethod -ClassName Win32_Share -Namespace root/cimv2 –MethodName
'Create' -Arguments @{Path='C:\Temp'; Name='TestShare'; Type=[uint32]'0';
MaximumAllowed=[uint32]'25'; Description='Public share for team'}

ReturnValue PSComputerName
----------- --------------
0

 

We will be covering other computer management scenarios in our future posts. If you have any questions, please feel free to send them to us, or post them in the Comments section.

 

Important links:

The complete list of tasks we are covering in this series is in the MSDN article, WMI Tasks for Scripts and Applications.

Links to previous posts:

Processes

Computer Hardware

Operating System

 

Thanks,

Milena Natanov [MSFT]

Standards-Based Management

Enumerating and getting WMI provider class instances with performance in mind

$
0
0

 

Performance of CIM_DataFile and Win32_Directory enumeration depends on the number of files or directories that are being enumerated, and, hence, can be very slow and can take hours.

In following example, we run the Get-CimInstance cmdlet to enumerate all the instances of the class CIM_DataFile. On a brand-new virtual machine, the operation took 38minutes.


PS:> Measure-Command { Get-CimInstance -ClassName CIM_DataFile -Namespace root/cimv2 }
Days : 0
Hours : 0
Minutes : 38
Seconds : 27

 

How performance can be improved

Here are few alternatives that can improve performance of your PowerShell script if it includes CIM_DataFile or Win32_Directory instanceenumeration. These methods can be used on any other class with a large number of instances.

 

Alternative 1: Enumerating instances using a WQL query

You can use a WQL query to narrow down enumeration to single instances. When you use WQL query, the Get-CimInstance cmdlet executes faster.

For example, the following query returns result immediately:


PS:> # Build a WQL query, which will return C:\ directory
PS:> $query = "Select * From Win32_Directory Where Name = 'C:'"
PS:> # Get Win32_Directory instance using WQL query
PS:> $instances = Get-CimInstance -Query $query -Namespace root/cimv2
PS:> $instances | Select Name
Name : c:

If WQL query uses the LIKE operator, expect the query to be as slow as Get-CimInstance without a WQL query.


PS:> # Build a WQL query, which will return all directories under C:\
PS:> $query = "Select * From Win32_Directory Where Name LIKE 'C:*'"
PS:> # Get Win32_Directory instances matching the query filter
PS:> Measure-Command { $instances = Get-CimInstance -Query $query -Namespace root/cimv2 }
Days : 0
Hours : 0
Minutes : 2
Seconds : 34

 

 

Alternative 2: Enumerating instances using Get-CimInstance with local instance

This method can only be used when the instance key property value is known.


PS:> # Get CIM_DataFile class
PS:> $class = Get-CimClass -ClassName CIM_DataFile -Namespace root/cimv2
PS:> # Create hash table containing instance’s key property and its value
PS:> $keyPropertyHash = @{}
PS:> $keyPropertyHash.Add('Name', 'C:\Temp\Test.txt')
PS:> # Create a new CIM instance in memory for a CIM_DataFile class with the key property @{ 'Name'='C:\Temp\Test.txt' }
PS:> $localInstance = New-CimInstance -CimClass $class -Property $keyPropertyHash –ClientOnly
PS:> #Get Test.txt file instance using $localInstance as an input object
PS:> $instance = Get-CimInstance –InputObject $localInstance
PS:> $instance | fl Name
Name : c:\temp\test.txt

 

Summary

When you’re enumerating instances in a class with large number of instances:

  • Use WQL queries if enumeration can be scoped down.
  • Run Get-CimInstance with the local instance if the key property value is known.
  • Avoid using queries with the LIKE operator if possible.
  • If you need full enumeration, plan for the time overhead that’s added to your script.

 

Important links:

Link to related post:

Files and Folders

 

Thanks,

Milena Natanov [MSFT]

Standards-Based Management

WS-Man (Web Services for Management) 1.2 Published

$
0
0

The DMTF recently published the Web Services for Management (WS-Man) standard version 1.2. This release of the WS-Man specification clarifies support for the latest encryption protocols, which has been required by organizations and governmental agencies such as the US Government NIST program.

WS-Man is a SOAP-based protocol that can be used with any transport, although it is primarily used with HTTP and HTTPS.  The older versions of the WS-Man specification made specific references to older versions of TLS (Transport Layer Security) that have proven to be insufficient.  The updated version more clearly decouples the protocol and the transport to ensure that interoperability is not dependent on specific versions of encryption algorithms, including TLS.  There is no functional change with the updated WS-Man 1.2 specification.  

Microsoft, an active member of the DMTF, contributed and edited the updated WS-Man specification. The Microsoft implementation of WS-Man is known as Windows Remote Management (WinRM), and has been a part of Windows since Windows 7 / Windows Server 2008 R2.  The WinRM implementation of WS-Man conforms with the latest NIST requirements described in NIST Special Publication 800 -52r1. WinRM leverages HTTP.SYS, which implements and enables configuration of the most recent and industry-standard secure transport protocol layer, as described in these two articles:

· Configuring WINRM for HTTPS

· The Configuring WinRM to Use HTTPS section of Configuration and Security

Note that if HTTPS is not used, WinRM still encrypts the payload over HTTP by default unless explicitly set to not use encryption.

WinRM has been, and should continue to be relied on in secure environments that are configured to meet the government and industry recommended cryptographic algorithms. Due to the lack of clarity in the previous releases of the WS-Man specification, some users were confused about whether or not WS-Man and WinRM supported recent and more secure implementations of the Transport-Layer Security (TLS), which it does. While this has not been an issue for the Microsoft implementation in WinRM, the WS-Man version 1.2 specification update has addressed that, and removed the confusing elements.

Steve Lee
Principal Engineering Manager
Windows Server

Keith Bankston
Senior Program Manager
Windows Server


Move to PowerShell for WinRM Management

$
0
0

As most people are aware, PowerShell provides two ways of managing the Windows Remote Management (WinRM) infrastructure: the WSMan Provider and the WS-Management Cmdlets. (As you are no doubt aware, WinRM is the Windows implementation of the WS-Management standard, which is why these cmdlets are named Ws-Management.) Together, the WSMan Provider and WS-Management cmdlets give administrators the ability to enable, configure, manage, and test WinRM easily from within PowerShell, and are the best way for building scripts to manage WinRM.

Prior to the release of the WS-Management cmdlets, the only way to perform some WinRM management tasks was to use the WinRM.VBS scripting interface and WinRM.CMD. However, with the proven success of the PowerShell cmdlets, it is time to move away from those older tools. No development is planned for either WinRM.CMD or WinRM.VBS, and while they are still a part of Windows (to support backwards compatibility), no new scripts or development should be based on them.  

This is a small part of the long-established strategy to move to PowerShell for management of the Windows platforms. PowerShell has become the Windows scripting choice for IT Professionals, and is being more broadly adopted by developers with the addition of Desired State Configuration.

The WS-Management Cmdlets and WSMan Provider give PowerShell a complete mechanism for both local and remote management of WinRM, CredSSP, and PowerShell Sessions that is unavailable from VBScript. For more information on these, you will find links to both the WS-Management cmdlets and the WS-Man provider here on TechNet. There is also a very good article titled “Understanding PowerShell Remote Management” that covers these topics quite well.

To reiterate the key point: while WinRM.VBS and WinRM.CMD will ship in Windows to support backwards compatibility, new scripts developed to manage WinRM should be created using the WSMan Provider and the PowerShell WS-Management cmdlets.

Implementing MI Provider (4) – Generate Code (continute)

$
0
0

As described in Implementing MI Provider (3), Convert-MofToProvider.exe tool generates a set of code files, including schema.c, module.c, WMIAdapter.c, Provider.DEF, <class name>.h, and <class name>.c. There is one header file for each target class and all of it’s ancestor class(es) if have. And one c file for each target class (specified by -ClassList parameter). Header files also get genrated for classes specified by –ExtraClass parameter.

 

<Class name>.h file(s)

Those header files are named with pattern like <class name>.h, which defines the type of <class name> class and a set of helper functions to manipulate the instance of that type. Take MSFT_WindowsProcess.h for example, first of all, it defines the structure of MSFT_WindowsProcess, represents the run-time type of windows process instance defined by MSFT_WindowsProcess schema.

typedef struct _MSFT_WindowsProcess /* extends CIM_Process */
{
MI_Instance __instance;
/* CIM_ManagedElement properties */
MI_ConstStringField InstanceID;
MI_ConstStringField Caption;
MI_ConstStringField Description;
MI_ConstStringField ElementName;
/* CIM_ManagedSystemElement properties */
MI_ConstDatetimeField InstallDate;
MI_ConstStringField Name;
MI_ConstUint16AField OperationalStatus;
MI_ConstStringAField StatusDescriptions;
MI_ConstStringField Status;
MI_ConstUint16Field HealthState;
MI_ConstUint16Field CommunicationStatus;
MI_ConstUint16Field DetailedStatus;
MI_ConstUint16Field OperatingStatus;
MI_ConstUint16Field PrimaryStatus;
/* CIM_LogicalElement properties */
/* CIM_EnabledLogicalElement properties */
MI_ConstUint16Field EnabledState;
MI_ConstStringField OtherEnabledState;
MI_ConstUint16Field RequestedState;
MI_ConstUint16Field EnabledDefault;
MI_ConstDatetimeField TimeOfLastStateChange;
MI_ConstUint16AField AvailableRequestedStates;
MI_ConstUint16Field TransitioningToState;
/* CIM_Process properties */
/*KEY*/ MI_ConstStringField CSCreationClassName;
/*KEY*/ MI_ConstStringField CSName;
/*KEY*/ MI_ConstStringField OSCreationClassName;
/*KEY*/ MI_ConstStringField OSName;
/*KEY*/ MI_ConstStringField CreationClassName;
/*KEY*/ MI_ConstStringField Handle;
MI_ConstUint32Field Priority;
MI_ConstUint16Field ExecutionState;
MI_ConstStringField OtherExecutionDescription;
MI_ConstDatetimeField CreationDate;
MI_ConstDatetimeField TerminationDate;
MI_ConstUint64Field KernelModeTime;
MI_ConstUint64Field UserModeTime;
MI_ConstUint64Field WorkingSetSize;
/* MSFT_WindowsProcess properties */
MI_ConstStringField CommandLine;
}
MSFT_WindowsProcess;

Then, the header file defines a set of functions to manipulate the instance of MSFT_WindowsProcess, following helper funtions set and clear CommandLine property value of the instance.

MI_INLINE MI_Result MI_CALL MSFT_WindowsProcess_Set_CommandLine(
_Inout_ MSFT_WindowsProcess* self,
_In_z_ const MI_Char* str)
{
return self->__instance.ft->SetElementAt(
(MI_Instance*)&self->__instance,
35,
(MI_Value*)&str,
MI_STRING,
0);
}

MI_INLINE MI_Result MI_CALL MSFT_WindowsProcess_SetPtr_CommandLine(
_Inout_ MSFT_WindowsProcess* self,
_In_z_ const MI_Char* str)
{
return self->__instance.ft->SetElementAt(
(MI_Instance*)&self->__instance,
35,
(MI_Value*)&str,
MI_STRING,
MI_FLAG_BORROW);
}

MI_INLINE MI_Result MI_CALL MSFT_WindowsProcess_Clear_CommandLine(
_Inout_ MSFT_WindowsProcess* self)
{
return self->__instance.ft->ClearElementAt(
(MI_Instance*)&self->__instance,
35);
}

 

<Class name>.c file(s)

Based on the type of target class, Convert-MofToProvider.exe generates different stub functions in c file. As mentioned, there are three type of classes, normal class, association class, and indication class.

For MSFT_WindowsProcess class, following are the generated stub functions,

#include <MI.h>
#include "MSFT_WindowsProcess.h"

void MI_CALL MSFT_WindowsProcess_Load(
_Outptr_result_maybenull_ MSFT_WindowsProcess_Self** self,
_In_opt_ MI_Module_Self* selfModule,
_In_ MI_Context* context)
{
MI_UNREFERENCED_PARAMETER(selfModule);

*self = NULL;
MI_Context_PostResult(context, MI_RESULT_OK);
}

void MI_CALL MSFT_WindowsProcess_Unload(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context)
{
MI_UNREFERENCED_PARAMETER(self);

MI_Context_PostResult(context, MI_RESULT_OK);
}

void MI_CALL MSFT_WindowsProcess_EnumerateInstances(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_opt_ const MI_PropertySet* propertySet,
_In_ MI_Boolean keysOnly,
_In_opt_ const MI_Filter* filter)
{
MI_Result result = MI_RESULT_OK;

MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(propertySet);
MI_UNREFERENCED_PARAMETER(filter);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_GetInstance(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_ const MSFT_WindowsProcess* instanceName,
_In_opt_ const MI_PropertySet* propertySet)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(instanceName);
MI_UNREFERENCED_PARAMETER(propertySet);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_CreateInstance(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_ const MSFT_WindowsProcess* newInstance)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(newInstance);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_ModifyInstance(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_ const MSFT_WindowsProcess* modifiedInstance,
_In_opt_ const MI_PropertySet* propertySet)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(propertySet);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_DeleteInstance(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_ const MSFT_WindowsProcess* instanceName)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_Invoke_RequestStateChange(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_opt_z_ const MI_Char* methodName,
_In_ const MSFT_WindowsProcess* instanceName,
_In_opt_ const MSFT_WindowsProcess_RequestStateChange* in)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(methodName);
MI_UNREFERENCED_PARAMETER(instanceName);
MI_UNREFERENCED_PARAMETER(in);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_Invoke_SetPriority(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_opt_z_ const MI_Char* methodName,
_In_ const MSFT_WindowsProcess* instanceName,
_In_opt_ const MSFT_WindowsProcess_SetPriority* in)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(methodName);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

void MI_CALL MSFT_WindowsProcess_Invoke_Create(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_opt_z_ const MI_Char* methodName,
_In_ const MSFT_WindowsProcess* instanceName,
_In_opt_ const MSFT_WindowsProcess_Create* in)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(methodName);
MI_UNREFERENCED_PARAMETER(instanceName);
MI_UNREFERENCED_PARAMETER(in);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}

There are two categories of the generated methods insides one c file, intrinsic methods and extrinsic methods. Intrinsic methods are standard operations while extrinsic methods are methods defined within the class’s schema (mof file).

Intrinsic Methods

Extrinsic methods

MSFT_WindowsProcess_EnumerateInstances

MSFT_WindowsProcess_Invoke_SetPriority

MSFT_WindowsProcess_GetInstance

MSFT_WindowsProcess_Invoke_RequestStateChange

MSFT_WindowsProcess_ModifyInstance

MSFT_WindowsProcess_Invoke_Create

MSFT_WindowsProcess_CreateInstance

 

MSFT_WindowsProcess_DeleteInstance

 

For each extrinsic method definition, there is also a type generated in the corresponding header file, for example, structure MSFT_WindowsProcess_Create was defined for Create method of MSFT_WindowsProcess class in MSFT_WindowsProcess.h file. Through this type, provider may access values of input parameters from client (who invoke the method) and output values of parameters to client.

For each class, there are two functions always generated within <class name>.c file, called load/unload functions. <class name>_Load function is invoked by CIMOM server to initialize the class before invoking any method, the class may initialize any resources which could live across class loading time and are used to during operations of the class. <class name>_Unload function is invoked by server to finalize the class, the class should finalize all resources initialized within load function.

 

Module.c 

Module.c file defines and implements entry function (MI_Main) of the MI provider. As shown below, there are three functions defined, Load, Unload, MI_Main.

Load function is used to initialize the provider wide resources, live during provider’s life cycle. For example, inside Load function, a provider may initialize a critical section, which was used to protect shared data structures accessed by multiple threads simultaneously.

Unload function is used to finalize the provider resources. For example, the provider could delete the critical section inside Unload function.

MI_Main function exchanges information between the provider and its host. Usually provider manager calls MI_Main function of a MI provider to get the MI_Module information of a provider, which contains the flags, charsize, version, generator version, all supported schema, and load unload function pointer. Meanwhile, provider manager tells the provider server information by input parameter MI_Server. Provider could call MI_Server_GetVersion and MI_Server_GetSystemName to read current CIMOM server version and system name.

 #include <MI.h>

MI_EXTERN_C MI_SchemaDecl schemaDecl;

void MI_CALL Load(_Outptr_result_maybenull_ MI_Module_Self** self, _In_ struct _MI_Context* context)
{
*self = NULL;
MI_Context_PostResult(context, MI_RESULT_OK);
}

void MI_CALL Unload(_In_opt_ MI_Module_Self* self, _In_ struct _MI_Context* context)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(context);

MI_Context_PostResult(context, MI_RESULT_OK);
}

MI_EXTERN_C MI_EXPORT MI_Module* MI_MAIN_CALL MI_Main(_In_ MI_Server* server)
{
/* WARNING: THIS FUNCTION AUTOMATICALLY GENERATED. PLEASE DO NOT EDIT. */
static MI_Module module;
MI_EXTERN_C MI_Server* __mi_server;
__mi_server = server;
module.flags |= MI_MODULE_FLAG_DESCRIPTIONS;
module.flags |= MI_MODULE_FLAG_VALUES;
module.flags |= MI_MODULE_FLAG_BOOLEANS;
module.charSize = sizeof(MI_Char);
module.version = MI_VERSION;
module.generatorVersion = MI_MAKE_VERSION(1,0,0);
module.schemaDecl = &schemaDecl;
module.Load = Load;
module.Unload = Unload;
return &module;
}

 

 

Schema.c

schema.c file contains the schema of all related classes, and the schema of each class is represented by a set of embedded structures. Schema.c file defines two MI server functions as well. Schema file could be viewed as a binary format of the schema (mof) file. This file must *NOT* be modified, otherwise could break provider’s functionality. To change the schema, modify the original MOF (in this case sample.mof) and re-generate the code.

To explain the structure of the schema file, a set of snippets are copied below.

(1) schemaDecl is of type MI_SchemaDecl, which defines the schema structure of the provider, which contains all qualifiers declarations and all classes declarations.

 MI_SchemaDecl schemaDecl =
{
qualifierDecls, /* qualifierDecls */
MI_COUNT(qualifierDecls), /* numQualifierDecls */
classes, /* classDecls */
MI_COUNT(classes), /* classDecls */
};

(2) classes property is an array of MI_ClassDecl objects, which contains a list of all classes schema, which was named with <class name>_rtti. For example, MSFT_WindowsProcess_rtti is the schema of MSFT_WindowsProcess class.

 static MI_ClassDecl MI_CONST* MI_CONST classes[] =
{
&CIM_ConcreteJob_rtti,
&CIM_EnabledLogicalElement_rtti,
&CIM_Error_rtti,
&CIM_Job_rtti,
&CIM_LogicalElement_rtti,
&CIM_ManagedElement_rtti,
&CIM_ManagedSystemElement_rtti,
&CIM_Process_rtti,
&CIM_Service_rtti,
&CIM_ServiceProcess_rtti,
&MSFT_WindowsProcess_rtti,
&MSFT_WindowsServiceProcess_rtti,
};

(3) MSFT_WindowsProcess_rtti defines as following, which contains a set of qualifiers, a set of properties, a set of methods, a set of generated stub functions, and other information of the class.

 /* class MSFT_WindowsProcess */
MI_CONST MI_ClassDecl MSFT_WindowsProcess_rtti =
{
MI_FLAG_CLASS, /* flags */
0x006D7313, /* code */
MI_T("MSFT_WindowsProcess"), /* name */
MSFT_WindowsProcess_quals, /* qualifiers */
MI_COUNT(MSFT_WindowsProcess_quals), /* numQualifiers */
MSFT_WindowsProcess_props, /* properties */
MI_COUNT(MSFT_WindowsProcess_props), /* numProperties */
sizeof(MSFT_WindowsProcess), /* size */
MI_T("CIM_Process"), /* superClass */
&CIM_Process_rtti, /* superClassDecl */
MSFT_WindowsProcess_meths, /* methods */
MI_COUNT(MSFT_WindowsProcess_meths), /* numMethods */
&schemaDecl, /* schema */
&MSFT_WindowsProcess_funcs, /* functions */
NULL /* owningClass */
};

(4) MSFT_WindowsProcess_quals is an array of class qualifiers, and MSFT_WindowsProcess_ClassVersion_qual defines one of the qualifier as below.

 static MI_CONST MI_Char* MSFT_WindowsProcess_ClassVersion_qual_value = MI_T("1.0.0");

static MI_CONST MI_Qualifier MSFT_WindowsProcess_ClassVersion_qual =
{
MI_T("ClassVersion"),
MI_STRING,
MI_FLAG_ENABLEOVERRIDE|MI_FLAG_RESTRICTED,
&MSFT_WindowsProcess_ClassVersion_qual_value
};

static MI_Qualifier MI_CONST* MI_CONST MSFT_WindowsProcess_quals[] =
{
&MSFT_WindowsProcess_UMLPackagePath_qual,
&MSFT_WindowsProcess_Description_qual,
&MSFT_WindowsProcess_ClassVersion_qual,
};

(5) MSFT_WindowsProcess_props defines the array of all its properties, including the properties inherited from ancestor classes. One example of the property is MSFT_WindowsProcess_CommandLine_prop.

 static MI_PropertyDecl MI_CONST* MI_CONST MSFT_WindowsProcess_props[] =
{
&CIM_ManagedElement_InstanceID_prop,
&CIM_ManagedElement_Caption_prop,
&CIM_ManagedElement_Description_prop,
&CIM_ManagedElement_ElementName_prop,
&CIM_ManagedSystemElement_InstallDate_prop,
&CIM_Process_Name_prop,
&CIM_ManagedSystemElement_OperationalStatus_prop,
&CIM_ManagedSystemElement_StatusDescriptions_prop,
&CIM_ManagedSystemElement_Status_prop,
&CIM_ManagedSystemElement_HealthState_prop,
&CIM_ManagedSystemElement_CommunicationStatus_prop,
&CIM_ManagedSystemElement_DetailedStatus_prop,
&CIM_ManagedSystemElement_OperatingStatus_prop,
&CIM_ManagedSystemElement_PrimaryStatus_prop,
&CIM_EnabledLogicalElement_EnabledState_prop,
&CIM_EnabledLogicalElement_OtherEnabledState_prop,
&CIM_EnabledLogicalElement_RequestedState_prop,
&CIM_EnabledLogicalElement_EnabledDefault_prop,
&CIM_EnabledLogicalElement_TimeOfLastStateChange_prop,
&CIM_EnabledLogicalElement_AvailableRequestedStates_prop,
&CIM_EnabledLogicalElement_TransitioningToState_prop,
&CIM_Process_CSCreationClassName_prop,
&CIM_Process_CSName_prop,
&CIM_Process_OSCreationClassName_prop,
&CIM_Process_OSName_prop,
&CIM_Process_CreationClassName_prop,
&CIM_Process_Handle_prop,
&CIM_Process_Priority_prop,
&CIM_Process_ExecutionState_prop,
&CIM_Process_OtherExecutionDescription_prop,
&CIM_Process_CreationDate_prop,
&CIM_Process_TerminationDate_prop,
&CIM_Process_KernelModeTime_prop,
&CIM_Process_UserModeTime_prop,
&CIM_Process_WorkingSetSize_prop,
&MSFT_WindowsProcess_CommandLine_prop,
};
……
/* property MSFT_WindowsProcess.CommandLine */
static MI_CONST MI_PropertyDecl MSFT_WindowsProcess_CommandLine_prop =
{
MI_FLAG_PROPERTY|MI_FLAG_READONLY, /* flags */
0x0063650B, /* code */
MI_T("CommandLine"), /* name */
NULL, /* qualifiers */
0, /* numQualifiers */
MI_STRING, /* type */
NULL, /* className */
0, /* subscript */
offsetof(MSFT_WindowsProcess, CommandLine), /* offset */
MI_T("MSFT_WindowsProcess"), /* origin */
MI_T("MSFT_WindowsProcess"), /* propagator */
NULL,
};

(6) MSFT_WindowsProcess_meths defines an array of the schema of all extrinsic methods, while MSFT_WindowsProcess_funcs defines an array of function pointers, which are intrinsic methods.

Due to an extrinsic method might contain a set of input and output parameters, and a set off qualifiers for the method itself as well as all parameters, such that it requires a MI_MethodDecl structure to store all related information. Intrinsic methods do not have qualifiers and it has a fix set of parameters, such that is represented by a function pointer.

/* method MSFT_WindowsProcess.Create() */
MI_CONST MI_MethodDecl MSFT_WindowsProcess_Create_rtti =
{
MI_FLAG_METHOD|MI_FLAG_STATIC, /* flags */
0x00636506, /* code */
MI_T("Create"), /* name */
MSFT_WindowsProcess_Create_quals, /* qualifiers */
MI_COUNT(MSFT_WindowsProcess_Create_quals), /* numQualifiers */
MSFT_WindowsProcess_Create_params, /* parameters */
MI_COUNT(MSFT_WindowsProcess_Create_params), /* numParameters */
sizeof(MSFT_WindowsProcess_Create), /* size */
MI_UINT32, /* returnType */
MI_T("MSFT_WindowsProcess"), /* origin */
MI_T("MSFT_WindowsProcess"), /* propagator */
&schemaDecl, /* schema */
(MI_ProviderFT_Invoke)MSFT_WindowsProcess_Invoke_Create, /* method */
};

static MI_MethodDecl MI_CONST* MI_CONST MSFT_WindowsProcess_meths[] =
{
&MSFT_WindowsProcess_RequestStateChange_rtti,
&MSFT_WindowsProcess_SetPriority_rtti,
&MSFT_WindowsProcess_Create_rtti,
};
……

static MI_CONST MI_ProviderFT MSFT_WindowsProcess_funcs =
{
(MI_ProviderFT_Load)MSFT_WindowsProcess_Load,
(MI_ProviderFT_Unload)MSFT_WindowsProcess_Unload,
(MI_ProviderFT_GetInstance)MSFT_WindowsProcess_GetInstance,
(MI_ProviderFT_EnumerateInstances)MSFT_WindowsProcess_EnumerateInstances,
(MI_ProviderFT_CreateInstance)MSFT_WindowsProcess_CreateInstance,
(MI_ProviderFT_ModifyInstance)MSFT_WindowsProcess_ModifyInstance,
(MI_ProviderFT_DeleteInstance)MSFT_WindowsProcess_DeleteInstance,
(MI_ProviderFT_AssociatorInstances)NULL,
(MI_ProviderFT_ReferenceInstances)NULL,
(MI_ProviderFT_EnableIndications)NULL,
(MI_ProviderFT_DisableIndications)NULL,
(MI_ProviderFT_Subscribe)NULL,
(MI_ProviderFT_Unsubscribe)NULL,
(MI_ProviderFT_Invoke)NULL,
};

typedef void (MI_CALL *MI_ProviderFT_EnumerateInstances)(
_In_opt_ void* self,
_In_ MI_Context* context,
_In_z_ const MI_Char* nameSpace,
_In_z_ const MI_Char* className,
_In_opt_ const MI_PropertySet* propertySet,
MI_Boolean keysOnly,
_In_opt_ const MI_Filter* filter);

 

WMIAdapter.c

In order to host MI provider(s) within WMI, an adapter layer is necessary since WMI host process only supports COM based provider interface. WMIAdapter.c is a file to enable the MI provider running with WMI service. This file must *NOT* be modified.

 

Provider.DEF

Provider.DEF defines list of exported APIs from the provider DLL. Usually, this file name was specified in visual studio project linker options ‘/DEF:”Provider.DEF”’ (see following picture), in order to tell the linker to export a set of functions from target DLL.


 

Happy holidays! Next blog will discuss how to implement instance (normal) class.

 

Haowei Qin

Senior SDE

Standards Based Management

Implementing MI Provider (5) – Implement

$
0
0

As discussed in Implementing MI Provider (4) – Generate Code (continute)Convert-MofToProvider.exe tool generates <class name>.c file for each target class, which contains stub functions to place the business logic of each operation, such as enumerate/get/delete/modify/new/<extrinsic methods>.

This blog discusses how to implement stub functions of normal (instance) classes, while association and indication class will be discussed separately in future. Let’s first introduce a set of frequently used MI APIs (when implementing MI provider) and then discuss how to implement the business logic of each generated stub function using those APIs.

MI Context API

The name of provider APIs are suffixed by MI_Context, which means the operation performed by the API was against the MI_Context object, while MI_Context defined as below, the MI_ContextFT structure definition could be found in mi.h from windows 8 SDK.

 typedef struct _MI_Context MI_Context;
struct _MI_Context
{
/* Function table */
const MI_ContextFT* ft;

/* Reserved for internal use */
ptrdiff_t reserved[3];
};

All MI_Context_* APIs are just a wrapper function that calls into one function inside MI_ContextFT structure.

MI_Context_PostInstance is used to post a MI_Instance back to server. Usually a MI_Instance could be constructed by calling generated constructor API defined in a class header file. Normally, provider calls into this API to delivery prepared instances. For example, client send an enumeration request to omi server, then the provider generates N number of instances, and should call this API N times to post them back to server.

MI_Context_PostError is used to post an error code with error message back to server.

MI_Context_PostCimError is used to post a CimError instance back to server.

MI_Context_PostResult is used to post the final operation result back to server.

NOTE: All Post APIs listed above are posting data to server, while server will deliver the data to client.

Any operation might post arbitrary number of instances back to server, however if without any specific clarification, all provider stub functions should call one of the following APIs once and only once (One exception is indication class’s stub function EnableIndications, which may be covered by future blog).

  MI_Context_PostError

  MI_Context_PostCimError

  MI_Context_PostResult

MI_Context_GetLocalSession gets local MI_Session object, which can be used to communicate back to the local CIMOM server for local operations, for example a provider need to query instances of another class on the local machine. The provider must *NOT* call MI_Session_Close on the local session object since the life cycle of the session object is managed by the host process.

 

MI Powershell Semantics API

Powershell Semantics APIs are designed to enable MI provider to interact with Powershell Console efficiently, such as MI_Context_PromptUser, be used by MI provider to send a prompt message to the client application (powershell for example) to confirm whether to continue the operation or not.

 MI_Context_PromptUser

MI_Context_ShouldContinue

MI_Context_ShouldProcess

MI_Context_WriteCimError

MI_Context_WriteError

MI_Context_WriteProgress

MI_Context_WriteMessage

MI_Context_WriteVerbose

MI_Context_WriteWarning

MI_Context_WriteDebug

 

 

MI Client API

A MI provider may need MI client API to perform specific operation to a different CIM class, such as querying instances of another CIM class or invoking an extrinsic method of another CIM class. Following are several frequently used client APIs, for a complete list of APIs, please refer to windows 8 SDK mi.h

MI_Application_Initialize is used to initialize and create a MI_Application object.

MI_Application_Close is used to close a MI_Application object, which will cancel all sessions and operations related to this application object.

MI_Application_NewSession creates a new MI_Session object, which is used to kick off any operation towards the CIMOM server.

MI_Session_Close closes the session, all operations have to be closed before session close finished.

MI_Session_EnumerateInstances start an operation to enumerate instances of a target class under specific namespace from target server, which will return a MI_Operation object.

MI_Operation_GetInstance retrieves MI_Instance from a started MI_Operation object synchronously.

MI_Operation_Close closes the operation.

 

Taking MSFT_WindowsProcess class example, remaining of this blog explains the general process of implementing enumerate instance, get instance, modify instance, delete instance, create instance, and extrinsic method.

(1) Implement EnumerateInstances

Generally speaking, there are six steps to implement enumerate instance operation for a class,

1) Firstly construct an instance of the target class by invoking <class name>_Construct API,

2) Set properties value of the instance. One important note is that all key properties of the instance must be set, and the combination of the key properties value should be unique, which identifies the instance uniquely.

3) Post the instances back by invoking <class_name>_Post API.

4) Destruct the constructed instances by calling <class name>_Destruct API.

5) Post final result to client by calling one of the post API. Call MI_Context_PostResult if succeed.

6) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Sometime, it may take time to complete the enumerating operation if there are quite a few instances to be posted back to client, and it makes sense to use ps-semantic API MI_Context_WriteProgress to notify client the current percentage of the operation.

Following diagram is the implementation diagram of MSFT_WindowsProcess_EnumerateInstances, see example code as well. Refer to MI API sample for complete implementation.

NOTE: For any MI_Instance constructed successfully by calling generated API <class name>_Construct, it should be destructed always once and only once by calling <class name>_Destruct.
For any MI_Instance created by MI_Context_NewInstance or generated API such as MSFT_WindowsProcess_Clone, it should be deleted by calling MI_Instance_Delete once and only once.

 

 

void MI_CALL MSFT_WindowsProcess_EnumerateInstances(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_opt_ const MI_PropertySet* propertySet,
_In_ MI_Boolean keysOnly,
_In_opt_ const MI_Filter* filter)
{
MI_Result result = MI_RESULT_OK;

MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(propertySet);
MI_UNREFERENCED_PARAMETER(filter);

result = EnumerateProcesses(context, keysOnly);
if (result == MI_RESULT_ACCESS_DENIED)
{
MI_Context_PostError(context,
result,
MI_RESULT_TYPE_MI,
MI_T("Access denied. Please try to run client process with elevated priviledge."));
}
else if (result != MI_RESULT_OK)
{
MI_Context_PostError(context,
result,
MI_RESULT_TYPE_MI,
MI_T(""));
}
else
{
MI_Context_PostResult(context, result);
}
}

(2) Implement GetInstance

Get instance operation requires that all key properties of a MI_instance are sent to MI provider. There are several steps to implement get instance operation for a class,

1) Firstly check the key properties value of the given instance is valid or not.

2) If it is valid, then construct an instance of the target class by invoking <class name>_Construct API,

3) Set properties’ value of the instance. One important note is that all key properties of the instance must be set, which are equal to the input instance, and the combination of the key properties value should be unique, which identifies the instance uniquely.

4) Post the instances back by invoking <class_name>_Post API.

5) Destruct the constructed instance by calling <class name>_Destruct API.

6) Post final result to client by calling MI_Context_PostResult API.

7) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Refer to MI API sample for complete implementation of MSFT_WindowsProcess_GetInstance.

NOTE: If a MI provider does not support Get instance operation, i.e., keep generated code <class name>_GetInstance unchanged (i.e., post MI_RESULT_NOT_SUPPORTED via one of the post API), then the get instance request will be redirected to <class name>_EnumerateInstance function, and returned instances from EnumerateInstances function will be filtered based on the key properties value of the incoming requested instance, and then CIMOM server post back result full instance back to client. But this behavior is inefficient and might cause performance issue in case the class has too many instances.

 

(3) Implement CreateInstance

Not all classes support CreateInstance operation. The confusing part of the intrinsic create method is that it is hard for client user to figure out which properties value are mandatory to create an instance of a target class, and the supported format of each properties value, especially for key properties, it kind of different for client user to specify a valid key value to create a new instance, sometime, the key properties value might be generated by MI provider automatically. So commonly, defining an extrinsic method to create instance makes more sense, the method could define a set of input parameters and might the format of each parameter through parameter qualifiers. One example is Create method of MSFT_WindowsProcess class. However, if a MI provider does support intrinsic CreateInstance operation, following are the steps to implement it,

1) Firstly check the properties’ value of the given instance is valid or not.

2) If it is valid, then construct an instance of the target class by invoking <class name>_Construct API,

3) Set all of the properties’ value of the instance. Some properties’ value might come from input instance; other might be calculated by the provider. One important note is that all key properties of the instance must be set, which are equal to the input instance, and the combination of the key properties value should be unique, which identifies the instance uniquely.

4) Post the newly created instance back by invoking <class_name>_Post API. This step is mandatory due to client relies on this to get the key values of the newly created instance.

5) Destruct the constructed instance by calling <class name>_Destruct API.

6) Post final result to client by calling MI_Context_PostResult API.

7) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Refer to MI API sample for complete implementation of MSFT_WindowsProcess_CreateInstance.

 

(4) Implement ModifyInstance

ModifyInstance operation faces the similar issue with CreateInstance due to it is hard for client user to figure out which property could be modified and which property could not. Someone might argue that Read and Write qualifiers are the flag to tell which property could be modified, but in reality, not all of the class schema definition follows Read/Write qualifiers strictly. If schema does define Read/Write qualifier appropriately, powershell console as a client does prevent modifying the value of Read only properties.

For ModifyInstance operation, it is also challenging to implement. Provider developer needs to figure out the set of properties to be modified, unfortunately there is no straight forward approach to tell the exactly list of properties to be modified, the PropertySet parameter of following method is always set to NULL in windows 8.

void MI_CALL MSFT_WindowsProcess_ModifyInstance(
_In_opt_ MSFT_WindowsProcess_Self* self,
_In_ MI_Context* context,
_In_opt_z_ const MI_Char* nameSpace,
_In_opt_z_ const MI_Char* className,
_In_ const MSFT_WindowsProcess* modifiedInstance,
_In_opt_ const MI_PropertySet* propertySet)
{
MI_UNREFERENCED_PARAMETER(self);
MI_UNREFERENCED_PARAMETER(nameSpace);
MI_UNREFERENCED_PARAMETER(className);
MI_UNREFERENCED_PARAMETER(propertySet);

MI_Context_PostResult(context, MI_RESULT_NOT_SUPPORTED);
}
There is one workaround of the problem,
  • The provider could query the latest value of the instance.
  • Comparing the instance latest value with given instance to be modified, for any property that has a different value, it might need modification.
  • One important note is that if the property value is NULL, then it needs another technique to tell should the property be modified or not.

The following utility method could tell whether a specific property was explicitly modified by client application or not.

MI_Result MI_CALL MI_Instance_IsElementModified(
_In_ const MI_Instance* self,
_In_z_ const MI_Char* name,
_Out_opt_ MI_Boolean* modified)
{
MI_Uint32 flags = 0;
MI_Result r;
if (!modified)
{
return MI_RESULT_INVALID_PARAMETER;
}
r = MI_Instance_GetElement(self, name, NULL, NULL, &flags, NULL);
if (r == MI_RESULT_OK)
{
if ((flags & MI_FLAG_NOT_MODIFIED) == 0)
{
*modified = MI_TRUE;
}
else
{
*modified = MI_FALSE;
}
}
return r;
}
NOTE: MI_FLAG_NOT_MODIFIED is only applicable to top level property, which was modified by MI_Instance_SetElementAt or MI_Instance_SetElement APIs or equivalent MI.net APIs. Since a MI Instance could have embedded instance property, if a sub property value of an embedded instance property was modified, since there is no MI_Instance_SetElementAt or MI_Instance_SetElement API call to notify the parent instance of the change of the embedded property, then above utility method won’t work as expected for this property.

Given the above limitations, it might make more sense to define a specific extrinsic method to modify a specific property of an instance. One example is SetPriority method of MSFT_WindowsProcess class. However, some classes may still need to implement intrinsic ModifyInstance method following below steps,

1) Firstly check the key properties value of the given instance is valid or not.

2) If it is valid, before modifying the instance properties’ value, it might make sense to request confirmation from client application by calling MI_Context_PromptUser or …… to confirm that user really wants the modification.

3) If get positive answer from client user, then modify all of the requested non-key properties value of the instance by figuring out which properties are requested to be modified based on above techniques. How to modify the instance value is really depends on the provider’s business logic, it could modify the registry key; could store the value into file or database; could modify the value in memory cache, etc. There is NO need to post any instance back.

4) Post final result to client by calling MI_Context_PostResult API.

5) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Refer to MI API sample for complete implementation of MSFT_WindowsProcess_ModifyInstance. 

 

(5) Implement DeleteInstance 

DeleteInstance may have different meaning to different class, for MSFT_WindowsProcess class, it means kill the process. To implement delete instance,

1) Firstly check the key properties value of the given instance is valid or not.

2) If it is valid, then delete the instance. For MSFT_WindowsProcess class, it terminates the process corresponding to the given MI Instance.

3) Post final result to client by calling MI_Context_PostResult API.

4) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Refer to MI API sample for complete implementation of MSFT_WindowsProcess_DeleteInstance. 

 

(6) Implement Extrinsic Method

Extrinsic methods include instance method and static method. The difference between static and instance method is that static method has [static] qualifier defined; while instance method does not have. For static method, the parameter [instanceName] is always set to NULL, while instance method has a valid instanceName parameter to tell provider on which instance should the method be invoked.

 _In_ const MSFT_WindowsProcess* instanceName,

For any method, there is a structure defined for it, used to store all input and output parameters’ value, and return value as well. There are several steps to implement method invoking operation for a class,

1) For instance method, firstly check the key properties value of the given instance is valid or not. If valid then continue,

2) Construct an instance of the target method by invoking <class name>_<method name>_Construct API,

3) Implement the method logic base on the input parameters and setting all of the output parameters and return value of the output instance. One important note is that ….

4) Post the instances back by invoking <class_name>_<method name>_Post API.

5) Destruct the constructed instance by calling <class name>_<method name>_Destruct API.

6) Post final result to client by calling MI_Context_PostResult API.

7) If there is any failure happened during above step which is considered blocking the current operation, provider should call MI_Context_PostError or MI_Context_PostCimError API to notify the client the final result.

Please look at MI API sample for an example of extrinsic method implementation: MSFT_WindowsProcess_Invoke_SetPriority.

 

Happy Holidays! Next blog will discuss how to build, register and debug MI provider.

 

Haowei Qin

Senior SDE

Standards Based Management

 

 

Implementing MI Provider (6) – Build, Register, and Debug

$
0
0

This blog discusses how to build, register and debug MI provider.

Build

To build MI provider, open visual studio 2012, create an empty Visual C++ project named “process”, set type to DLL, and add all generated files into the project.

  • Open project property dialog box, navigate to linker page, click input option, Set Module Definition File to “Provider.DEF”. This will ensure DLL exports the set of APIS defined in provider.DEF file. NOTE, make sure the LIBRARY section of Provider.DEF file was set to process.DLL.
  • Select your target platform, if you want the provider to run on X64 platform, please create “X64” platform through “Configuration Manager …” button, otherwise keep the default option is “Win32”, which means target platform is X86.
  • Optionally, go to “C/C++” -> “code generation” page, select “Multi-threaded Debug (/MTd)” or “Multi-threaded (/MT)” for runtime library to avoid the issue that target test machine is missing the runtime DLL requested by your provider DLL.

Refer to MI API sample for the sample project, open the solution file within Visual studio 2012, which integrates windows 8 SDK installation directory, by build all projects, you will be able to get all of the MI provider’s binaries, for example process.DLL.

 

Register

(1) Tool

Register-CimProvider.exe is the tool used to register MI provider, shipped with windows 8, which can be found under %system32% folder. Following diagram shows the process of the registration.

On windows 8, MI provider is managed by WMI service. Register-CimProvider.exe tool helps to register MI providers into the system (WMI). The tool creates mof and mfl files on the fly based on the schema definition from the DLL as described above, and then compile the mof/mfl files into WMI repository. The tool registers the providers’ DLL as a COM server as well.

To register process.DLL, copy the compiled process.DLL to a Windows 8 or Windows Server 2012 machine, and invoke the following command line from elevated commandline prompt (cmd.exe):

Register-CimProvider.exe -Namespace Root/Standardcimv2/sample -ProviderName process -Path process.dll -verbose -ForceUpdate

Upon that completes, process.dll should be successfully registered into system. By default, the provider is registered as a coupled provider, i.e, it runs in a Wmiprvse.exe process. And default hosting model value is NetworkServiceHost, which means the host process is running under NETWORK SERVICE account. process.mof will also be generated under current directory, which contains the detail registration schema of the provider.

(2) Provider Hosting Model

On windows 8, MI Provider’s hosting model follows the same specification of WMI provider hosting model and security. Please refer following link for the details.

http://msdn.microsoft.com/en-us/library/windows/desktop/aa392783(v=vs.85).aspx

 

MI provider can be hosted as coupled provider and decoupled provider. As mentioned above, coupled provider will be hosted in wmiprvse.exe running under specific account which could be specified by -HostingModel argument, while a decoupled provider is running under any customized arbitrary process. WMI service controlled the lifecycle of a coupled provider, i.e., loading/unloading the provider time; while the lifecycle of a decoupled provider is completely controlled by its hosting process. MI API also provides a convenient API to host arbitrary MI provider within current process.

(3) Host Decoupled MI Provider

MI_Application_NewHostedProvider is the API to host provider as a decoupled provider under current process. Following is the pseudo code snippet of hosting a MI provider as a decoupled provider. Code snippet of hosting decoupled MI provider, (please refer to MI API sample dcuphost project for the complete implementation) 

 // load provider DLL
hProvider = LoadLibraryExW(“process.DLL”, NULL, 0);
// query MI_Main function from provider DLL
mi_main = (MI_MainFunction)GetProcAddress(hProvider, MI_MAIN_FUNCNAME);
// initialize MI_Application object
result = MI_Application_Initialize(0, NULL, NULL, &application);
// host the provider as decoupled provider
result = MI_Application_NewHostedProvider(&application,
pArgument->lpNamespace,
pArgument->lpProviderName,
mi_main,
NULL,
&hostedProvider);

Note that before hosting a MI provider as decoupled provider, it should be registered to the system as a decoupled provider firstly. Take process.dll for example, below is the command line to register it as a decoupled one,

Register-CimProvider.exe -Namespace Root/StandardCimV2/dcupsample -ProviderName process -Path C:\temp\provider.dll -Decoupled O:BAG:BAD:(A;;0x1;;;BA)(A;;0x1;;;NS) -Impersonation True

 

Test/Validate

To validate the MI provider is functioning or not, there are couple of approaches listed as of following, see MI API sample for the sample code.

Methodology

Description

Wbemtest.exe

Send request to provider via this UI tool.

Cim Cmdlets

A set of new cmdlets shipped with windows 8

Cim based cmdlets

Create cdxml to define a set of cim-based cmdlets to access the provider

Application

Using MI C API or MI.net API to build an application to verify the provider

 

Un-register

How to un-register the MI provider completely from system? Unfortunately, Register-CimProvider.exe tool has no option to un-register a MI provider from system. It providers an option to generate uninstall mof and (or) mfl file during the registration time. The option name is GenerateUnregistration.

Take process provider for example, if you run following command line, processUninstall.mof and process.mof will be generated under current directory.

Register-CimProvider.exe -Namespace Root/Standardcimv2/sample -ProviderName process -Path process.dll -verbose –ForceUpdate -GenerateUnregistration

processUninstall.mof contains instructions of unregistering the registered provider from system. It will delete registered non-abstract classes, related provider registration instances, and __Win32Porvider instance from system.

 #pragma classflags ("forceupdate")
#pragma namespace ( "\\\\.\\Root\\Standardcimv2\\sample")

#pragma deleteclass("MSFT_WindowsProcess",nofail)
#pragma deleteclass("MSFT_WindowsServiceProcess",nofail)
#pragma deleteinstance("__MethodProviderRegistration.Provider=\"\\\\\\\\.\\\\Root\\\\Standardcimv2\\\\sample:__Win32Provider.Name=\\\"process\\\"\"", nofail)
#pragma deleteinstance("__InstanceProviderRegistration.Provider=\"\\\\\\\\.\\\\Root\\\\Standardcimv2\\\\sample:__Win32Provider.Name=\\\"process\\\"\"", nofail)
#pragma deleteinstance("__Win32Provider.Name=\"process\"", nofail)

Notes regarding un-register MI provider,

Abstract classes registered by MI provider are not generated in Uninstall.mof. Because abstract classes might be referred by other providers under the same namespace, detecting that would cause complexity. As a MI provider developer, you can choose to delete all classes defined in the MI provider by adding deleteclass instructions for all classes into Uninstall.mof, as long as it can be guaranteed that there is no other provider be affected under the same namespace.

Registering MI provider will also create one registry key into the system. Process provider has the following registry key and value created,

 Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\CLSID\{EDCB2856-2E63-4F57-B3A1-F03CC7D9FEE3}]
@="process"

[HKEY_CLASSES_ROOT\CLSID\{EDCB2856-2E63-4F57-B3A1-F03CC7D9FEE3}\InprocServer32]
@="F:\\sdksrc\\book\\x64\\Debug\\process.dll"
"ThreadingModel"="Free"
To delete the registry key from system, run following command line,
reg delete HKEY_CLASSES_ROOT\CLSID\{EDCB2856-2E63-4F57- B3A1-F03CC7D9FEE3} /f

 

Debug

MI provider could be hosted in wmiprvse.exe process, or hosted as decoupled provider. Decoupled provider is relatively easy to debug since the host process has to start in order to host decoupled provider. By attaching to host process, you can debug the provider. This section focuses on debugging coupled provider, all break points and techniques are applicable to decoupled provider as well.

Normal debugging tools include visual studio and windbg. Following content introduces both techniques.

To debug a coupled provider, you need to

(1) Launch the provider into host process

(2) Attach to the host process

(3) set breakpoints and debug.

To launch MI provider, you may send an operation request to the MI provider (via powershell/cim cmdlets for example), the provider will be loaded into host process.

To attach to host process, you can run “tasklist –m <>.DLL” to tell which process is loading the provider. And use either windbg or visual studio 2012 attach to the process.

And then set breakpoints and you will be able to debug through. Following picture shows a snapshot of debugging EnumerateInstances method of MSFT_WindowsProcess class.

 

Debug provider Load/Class Load function

These two functions will be invoked once and only once, which were already called by the time attaching debugger to the host process. To debug these functions, debugger has to attach to host process before they get invoked. And following are the tricks,

(1) Register the provider as decoupled provider and start a host process to host the provider. This way you can attach the debugger tool to the host process to debug the provider load and class load function.

(2) Find another provider which shares the same host process with the target provider. Invoke another provider firstly will launch the host process. By attaching to the process, you will be able to set deferred breakpoints at Load and class Load function to debug.

For example, by default process.dll hosting model is NetworkServiceHost, which is the same with Cimwin32.dll. By invoking “wmic os” on any command line console, you will be able to see a wmiprvse.exe host process was launched running under NETWORKING SERVICE account. Now attach debugger to the process, and then let the process continue running. Then if invoke the process.dll provider by enumerate instances of MSFT_WindowsProcess class from powershell console, you will be able to see that process.dll is being launched in the same host process.

If you are not able to use trick 2, then try to use gflags.exe to set following debugger for image file “wmiprvse.exe”.

cdb.exe -server tcp:port=8080 -c "g"

Then invoke the provider from powershell console, now the host process will be launched but was blocked into cdb.exe, use windbg.exe to connect to the remote debugging server, and then set interested breakpoints before hit “g”.

windbg.exe -remote tcp:server=<server>,port=8080

Now you are able to debug Load and class Load functions.

Warning, For trick (2), you might block other host process from being launched, since cdb won’t be able to create debugger server on the same tcp port more than once.

 

 

Happy Holidays! I hope “Implementing MI Provider” blogs helps. Fell free to comment, ask questions, or email haoweiqin@outlook.com for deep discussions regarding CIM, WMI, etc.

 

Haowei Qin

Senior SDE

Standards Based Management

Performing Management tasks using Cimcmdlets – Processes

$
0
0

WMI provides a standardized system management infrastructure that can be leveraged by a number of different clients. WMI providers and the classes exposed by the providers help in various management tasks.

Our MSDN documentation lists a bunch of management scenarios and the corresponding Visual Basic samples, but it is much easier for an IT administrator to perform the same operations using PowerShell.

Customers have been requesting samples that will help them perform management tasks using PowerShell – therefore, in this series of blog posts we will be covering the PowerShell implementation of scenarios that are listed at the following MSDN page: http://msdn.microsoft.com/en-us/library/aa394585(v=vs.85).aspx

In this post we will be going over the scenarios specific to “Process Management” listed at : http://msdn.microsoft.com/en-us/library/aa394599(v=vs.85).aspx

 

Here are the corresponding PowerShell snippets:

1. Run an application in a hidden window:

PS:> $processStartupClass = Get-CimClass -ClassName Win32_ProcessStartup -Namespace root/cimv2

$processStartupInfo = New-CimInstance -cimclass $processStartupClass -Property @{ShowWindow =0} –Local 

PS:> $processClass = Get-CimClass -ClassName Win32_Process -Namespace root/cimv2

PS:> Invoke-CimMethod -CimClass $processClass -MethodName Create -Arguments @{commandline=”notepad.exe”; ProcessStartupInformation = [CimInstance]$processStartupInfo}

 

2. Determine which scripts are running on the local computer:

 

PS:> $query = “SELECT * FROM Win32_Process WHERE Name = ‘cscript.exe’ OR Name = ‘wscript.exe'”

PS:> $insts = Get-CimInstance -Query $query -Namespace root/cimv2

PS:> $insts | Select Name, commandline

 

 

3. Find out the account name under which a process is running:

PS:> $query = “Select * from Win32_Process”

PS:> $insts = Get-CimInstance -Query $query -Namespace root/cimv2

PS:> # OR

PS:> $insts = Get-CimInstance –ClassName Win32_Process –Namespace root/cimv2

PS:> $insts | %{

                         Write-host   $_.CimInstanceProperties[“Name”]

                          $owner = Invoke-CimMethod -InputObject $_ -MethodName GetOwner

                          $owner | Select Domain, User, PSComputerName

                       }

 

4. Change the priority of a running process:

PS:> $query = “Select * from Win32_Process Where Name = ‘Notepad.exe'”

PS:> $insts = Get-CimInstance -Query $query -Namespace root/cimv2

PS:> $aboveNormal = 32768

PS:> $insts | %{

    Invoke-CimMethod -InputObject $_ -MethodName SetPriority -Arguments @{Priority = [Uint32]$aboveNormal}

} 

 

5. Terminate a process using a script:

PS:> $query = “Select * from Win32_Process Where Name = ‘Notepad.exe'”

PS:> Invoke-CimMethod -Query $query -MethodName Terminate

 

6. Determine how much processor time and memory each process is using:

$query = “Select * from win32_process”

$procs = Get-CimInstance -Query $query

# OR

$procs = Get-CimInstance –ClassName Win32_Process –Namespace root/cimv2

 

foreach($proc in $procs)

{

    $result = New-Object PSObject -Property @{

                                processorTime = ($proc.KernalModeTime + $proc.UserModeTime) / 10000000

                                Name = $proc.Name

                                ProcessID = $proc.ProcessId

                                WorkingSetSize = $proc.WorkingSetSize

                                PageFileUsage = $proc.PageFileUsage

                                PageFaults = $proc.PageFaults

                            }

                                                              

$result | Format-Table -Property @(“Name”, “ProcessID”, “WorkingSetSize”, “PageFileUsage”, “PageFaults”, “ProcessorTime”)

}

 

 

7. Determine what applications are running on a remote computer:

$cimSession = New-CimSession remoteMachine –Credential $psCreds

$query = “Select * from Win32_Process”

$procs = Get-CimInstance -Query $query -CimSession $cimSession

# OR

$procs = Get-CimInstance –ClassName Win32_Process –Namespace root/cimv2

$procs |  Format-Table –Property @(“Name”, “ProcessID”, “ThreadCount”, “PageFileUsage”, “PageFaults”, “WorkingSetSize”)

 

As mentioned above, this blog series will cover various management scenarios. The next post will be about Computer Hardware Management scenarios listed at: http://msdn.microsoft.com/en-us/library/aa394587(v=vs.85).aspx

 

Thanks

Vaibhav Chugh [MSFT]

Standards Based Management

Viewing all 66 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>