Executing an OpsMgr 2007 script manually may result in an empty console output


One of the cool feature of OpsMgr 2007 is that you can test a script outside the OpsMgr environment, simply running it from a command line by using CScript.exe (http://msdn.microsoft.com/en-us/library/bb437526.aspx). I use this feature often to debug my scripts while creating Management pack or for troubleshoot problems with Management pack not written by me. Sometimes this does not work and even if the script completes successfully no output is returned in console. So I started to investigate to understand the cause of the problem.

The first step was to identify which method or function is responsible for print out the the console output. I debugged a working script with Microsoft Script Debugger and I was able to identify that the console output is written by the MOMScriptAPI.Return method.

The second step was to debug that method to understand why in some case it doesn’t produce any output. With WinDbg I found that this method prepare the xml output that the script should write the the console and it put it in a variable as we can see below :

0:004> g
MOMScriptAPI+0x45bb:
574145bb 51              push    ecx this is the pointer to the address of the string that will be filled with the xml document (ecx = 0013ed68)
0:000> t
MOMScriptAPI+0x45bc:
574145bc 57              push    edi
0:000> p
MOMScriptAPI+0x45bd:
574145bd ff9088000000    call    dword ptr <Unloaded_elp.dll>+0x87 (00000088)[eax] ds:0023:5880a468={msxml6!DOMNode::get_xml (58865897)}

after the execution of the call to msxml6!DOMNode::get_xml we can dump the address of the string :

0:000> dc 0013ed68 (value of ecx at the time of ‘push ecx’ instruction)
0013ed68  001a6ffc 019a67f8 96cf5fb5 0013ed48 

and then we can dump the content of the string :

0:000> du 001a6ffc L 260
001a6ffc  <DataItem type=’System.DiscoveryData’ time=’2010-03-28T19:44:19.
001a707c  9712688+02:00′ sourceHealthServiceId=’EF2F97F2-72D7-D872-D2B5-C7
001a70fc  770F50B95F’><DiscoveryType>0</DiscoveryType><DiscoverySourceType
001a717c  >0</DiscoverySourceType><DiscoverySourceObjectId>{50BDB348-03CB-
001a71fc  418B-8F7B-5873DDF83C64}</DiscoverySourceObjectId><DiscoverySourc
001a727c  eManagedEntity>{F6DEC1E0-6526-47C9-A36C-673EF934184F}</Discovery
001a72fc  SourceManagedEntity><ClassInstances><ClassInstance TypeId='{7655
001a737c  ba11-dd69-45a0-8b04-8e6a2ed6f5df><Settings><Setting><Name>{678
001a73fc  f1531-9cf2-4b10-b20b-a09e99797ce7}</Name><Value>srvname.domain</

To write the string to the console, an handle to the stdout is retrieved using the API GetStdHandle with STD_OUTPUT_HANDLE (DWORD –11 = 0FFFFFFF5h) as parameter :

0:004> g
MOMScriptAPI+0x4a35:
57414a35 6af5            push    0FFFFFFF5h ( = STD_OUTPUT_HANDLE)])

0:000> t
MOMScriptAPI+0x4a37:
57414a37 ff15a8114157    call    dword ptr [MOMScriptAPI+0x11a8 (574111a8)] ds:0023:574111a8={kernel32!GetStdHandle (77e6b437)}

and then that handle retrieved is passed to the function WriteFile

0:000> kv
ChildEBP RetAddr  Args to Child             
0013ed9c 57414a44 00000007 01eb0048 0000d0b9 kernel32!WriteFile (FPO: [SEH])

where 00000007 is the handle retrieved by GetStdHandle, 01eb0048 is the address to the buffer to be written :

0:000> dc 01eb0048 l34
01eb0048  7461443c 65744961 7974206d 223d6570  <DataItem type=’
01eb0058  74737953 442e6d65 6f637369 79726576  System.Discovery
01eb0068  61746144 69742022 223d656d 30313032  Data’ time=’2010
01eb0078  2d33302d 31543832 34343a39 2e39313a  -03-28T19:44:19.
01eb0088  32313739 2b383836 303a3230 73202230  9712688+02:00′ s
01eb0098  6372756f 61654865 5368746c 69767265  ourceHealthServi
01eb00a8  64496563 4645223d 37394632 372d3246  ceId=’EF2F97F2-7
01eb00b8  2d374432 32373844 4232442d 37432d35  2D7-D872-D2B5-C7
01eb00c8  46303737 39423035 3e224635 7369443c  770F50B95F’><Dis
01eb00d8  65766f63 79547972 303e6570 69442f3c  coveryType>0</Di
01eb00e8  766f6373 54797265 3e657079 7369443c  scoveryType><Dis
01eb00f8  65766f63 6f537972 65637275 65707954  coverySourceType
01eb0108  2f3c303e 63736944 7265766f 756f5379  >0</DiscoverySou

and 0000d0b9 (= 53433) is the size of the buffer to be written.

internally the function WriteFile calls WriteConsole :

0:000> kv
ChildEBP RetAddr  Args to Child             
0013ed4c 77e720eb 00000007 01eb0048 0000d0b9 kernel32!WriteConsoleA (FPO: [5,0,0])
0013ed9c 57414a44 00000007 01eb0048 0000d0b9 kernel32!WriteFile+0x64 (FPO: [SEH])

the function WriteConsole fails with error code 8 :

MOMScriptAPI!DllGetClassObject+0x2ebd:
5741963c ff1544114157    call    dword ptr [MOMScriptAPI+0x1144 (57411144)] ds:0023:57411144={ntdll!RtlGetLastWin32Error (7c829e08)}

0:000> r eax
eax=00000008
0:000> !error eax
Error code: (Win32) 0x8 (8) – Not enough storage is available to process this command. (ERROR_NOT_ENOUGH_MEMORY)

This is confirmed by the OpsMgr trace log (TracingGuidsNative.log) :

[ModulesScript]    []    [Error]    []    [WriteScriptOutput]    [MOMScriptAPI_cpp240]Unable to write output, WriteFile returned 8(ERROR_NOT_ENOUGH_MEMORY)

According to MSDN WriteConsole could fail with the error ERROR_NOT_ENOUGH_MEMORY if the parameter nNumberOfCharsToWrite exceeds 64 KB. in my case this is not true, I found that a value of 0000d0b9 (= 52.18 K) is too much and a value of 0000cf9a is OK. The error is set inside the function WriteConsoleInternal after a call to CsrAllocateCaptureBuffer.

0013ec4c 77e4448f 00000008 00000007 00000000 kernel32!SetLastError (FPO: [1,0,0])
0013ed20 77e7210d 00000007 01eb0048 0000d0b9 kernel32!WriteConsoleInternal+0x113 (FPO: [SEH])
0013ed3c 77e720eb 00000007 01eb0048 0000d0b9 kernel32!WriteConsoleA+0x18 (FPO: [5,0,0])
0013ed8c 57414a44 00000007 01eb0048 0000d0b9 kernel32!WriteFile+0x64 (FPO: [SEH])

So the problem seems to be related with the size of the output to be written, I’m not sure that this limit is identical in every machine because I tested it only on a Windows Server 2003 SP2 x86. The limit should be 64K (according to MSDN) but in my test the limit seems near 52K, but for the moment I didn’t investigate more.

One possible solution to this problem could be to write a wrapper that call cscript and redirect stdout to a file. If stdout is redirected, WriteFile shoud write to the handle opened by the calling process and not to the console using WriteConsole. I will try this and I will write the result in a future post.

– Fabrizio

This posting is provided “AS IS” with no warranties, and confers no rights.

About these ads

,

  1. #1 by joel hendrickson on May 1, 2010 - 6:20 am

    I’ve been told that snapshot discoveries are limited to 64K — i wonder if this is the reason

    • #2 by Fabrizio Guaitolini on May 16, 2010 - 8:14 pm

      Hi joel, while I was testing and debugging this issue I’m quite sure that a discovery with an output of more than 64k was accepted and handled by OpsMgr (at least R2, I didn’t try older versions). I will check it out and I will post an article on this in case it is confirmed what you have been told.

  2. #3 by Jonathan Almquist on March 29, 2010 - 10:51 pm

    Thanks for writing this up. I always enjoy your posts. Can I call you the “mad scientist”?

  1. Executing an OpsMgr 2007 script manually may result in an empty console output (part 2) « Quae Nocent Docent

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 329 other followers

%d bloggers like this: