Использование привязок Win32-Ada: SNMP - PullRequest
1 голос
/ 04 июля 2019

Мой вопрос касается привязок Win32-Ada, предоставляемых пакетами:

Я хотел бы использовать следующую функцию, определенную в пакете Win32.Mgmtapi:

function SnmpMgrOidToStr
  (oid    : access Win32.Snmp.AsnObjectIdentifier;
   string : access Win32.LPSTR)
  return Win32.BOOL;

У меня есть доступ к переменной Variable_Binding типа Win32.Snmp.a_RFC1157VarBind_t.В пакете определены следующие четыре типа Win32.Snmp:

type AsnObjectIdentifier is record
   idLength : Win32.UINT;
   ids      : Win32.PUINT;
end record;

subtype AsnObjectName is AsnObjectIdentifier;

type RFC1157VarBind is record
   name  : AsnObjectName;
   value : AsnObjectSyntax;
end record;

type a_RFC1157VarBind_t is access all RFC1157VarBind;

Сокращенный пример кода:

with Win32;
with Win32.Mgmtapi;
with Win32.Snmp;

procedure Test is
begin
   -- [...]

   declare
      Variable_Binding : aliased Win32.Snmp.a_RFC1157VarBind_t := (...);

      OID_String : access Win32.LPSTR;

      Return_Value : Win32.BOOL;
   begin
      Return_Value := Win32.Mgmtapi.SnmpMgrOidToStr
        (oid    => Variable_Binding.name, -- type is not compatible
         string => OID_String);
   end;
end Test;

Мои три вопроса:

  1. ФункцияSnmpMgrOidToStr требуется тип доступа для параметра name.Компонент name, определенный в типе RFC1157VarBind, не совместим с этим типом доступа.Каковы мои варианты преобразования компонента name переменной Variable_Binding в тип доступа?
  2. Я предполагаю, что функция SnmpMgrOidToStr выделяет необходимую память для переменной OID_String типа Win32.LPSTR.Как преобразовать тип Win32.LPSTR в строку Ada (String / Unbounded_String)?
  3. Как освободить выделенную память после разговора в строку Ada?

Обновление:

Я добавил полный пример SNMP.Для этого требуется включенная и настроенная служба Windows SNMP.Выходные данные программы должны быть:

Opened session.
Converted string to object identifier.
Copied object identifier.
Sent request.
Type is: 4
WORKSTATION
system.sysName.0
Closed session.

Документация функции SnmpMgrRequest гласит:

Примечание Массив SnmpVarBind, на который указывает структура SnmpVarBindListдолжен быть выделен с использованием функции SnmpUtilMemAlloc.

И в соответствии с документацией функции SnmpUtilMemAlloc функция SnmpUtilMemFree должна использоваться для освобождениявыделенная память.

Я думаю, что я правильно распределяю память, используя этот вызов процедуры:

-- Allocate memory
-- Allocated memory is not freed properly
SnmpUtilMemAlloc (Win32.UINT (Win32.Snmp.RFC1157VarBind'Size * System.Storage_Unit));

Новые вопросы:

  1. Является ли этот вызов процедуры правильным?
  2. Как использовать функцию SnmpUtilMemFree для освобождения выделенной памяти?

В данный момент память не освобождается должным образом в соответствии с инструментом Dr.Память (версия 2.2.0-1):

Error #1: LEAK 1536 direct bytes 0x0418b850-0x0418be50 + 0 indirect bytes
# 0 replace_RtlAllocateHeap               [d:\drmemory_package\common\alloc_replace.c:3771]
# 1 KERNELBASE.dll!GlobalAlloc           +0x6d     (0x75404015 <KERNELBASE.dll+0x14015>)
# 2 snmpapi.dll!SnmpUtilMemAlloc         +0xf      (0x73db1ee8 <snmpapi.dll+0x1ee8>)
# 3 _ada_snmp_example                     [C:/Users/username/Desktop/SNMPExample/src/snmp_example.adb:123]
# 4 main                                  [C:\Users\username\Desktop\SNMPExample\obj/b__snmp_example.adb:259]

Полный исходный код:

with Ada.Text_IO;
with Ada.Unchecked_Conversion;
with Interfaces.C.Strings;
with System;

with Win32;
with Win32.Mgmtapi;
with Win32.Snmp;

procedure SNMP_Example
is
   -- Imported functions and procedures
   procedure SnmpUtilMemAlloc (nBytes : Win32.UINT);

   pragma Import (Stdcall, SnmpUtilMemAlloc, "SnmpUtilMemAlloc");

   procedure SnmpUtilMemFree (pMem : Win32.LPVOID);

   pragma Import (Stdcall, SnmpUtilMemFree, "SnmpUtilMemFree");

   function SnmpUtilOidCpy
     (DestObjId : access Win32.Snmp.AsnObjectIdentifier;
      SrcObjId  : access Win32.Snmp.AsnObjectIdentifier)
      return Win32.INT;

   pragma Import (Stdcall, SnmpUtilOidCpy, "SnmpUtilOidCpy");

   procedure SnmpUtilOidFree (Obj : access Win32.Snmp.AsnObjectIdentifier);

   pragma Import (Stdcall, SnmpUtilOidFree, "SnmpUtilOidFree");

   procedure SnmpUtilVarBindFree (VarBind : access Win32.Snmp.RFC1157VarBind);

   pragma Import (Stdcall, SnmpUtilVarBindFree, "SnmpUtilVarBindFree");

   procedure SnmpUtilVarBindListFree (VarBindList : access Win32.Snmp.RFC1157VarBindList);

   pragma Import (Stdcall, SnmpUtilVarBindListFree, "SnmpUtilVarBindListFree");

   -- Conversion related
   function To_LPVOID is new Ada.Unchecked_Conversion (Win32.LPSTR, Win32.LPVOID);

   function To_PCSTR is new Ada.Unchecked_Conversion (Win32.PBYTE, Win32.PCSTR);

   -- Linker options
   pragma Linker_Options ("-lmgmtapi");
   pragma Linker_Options ("-lsnmpapi");

   -- Connection related (Ada)
   IP_Address        : constant String  := "127.0.0.1";
   Community_String  : constant String  := "public";
   Timeout_MS        : constant Integer := 500;
   Number_Of_Retries : constant Integer := 1;

   -- Connection related (C)
   IP_Address_C       : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (IP_Address);
   Community_String_C : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (Community_String);

   -- Connection related (Win32)
   IP_Address_Win32        : constant Win32.LPSTR := Win32.To_PSTR (IP_Address_C);
   Community_String_Win32  : constant Win32.LPSTR := Win32.To_PSTR (Community_String_C);
   Timeout_MS_Win32        : constant Win32.INT := Win32.INT (Timeout_MS);
   Number_Of_Retries_Win32 : constant Win32.INT := Win32.INT (Number_Of_Retries);

   -- Comparison of types related
   use type Interfaces.C.int;
   use type Win32.BOOL;
   use type Win32.Mgmtapi.LPSNMP_MGR_SESSION;

   -- Session
   SNMP_Session : Win32.Mgmtapi.LPSNMP_MGR_SESSION;

   -- Custom types
   type Counter_Type is mod 2**32 - 1;
   type Gauge_Type is mod 2**32 - 1;
begin
   -- Open session
   SNMP_Session := Win32.Mgmtapi.SnmpMgrOpen
     (lpAgentAddress   => IP_Address_Win32,
      lpAgentCommunity => Community_String_Win32,
      nTimeOut         => Timeout_MS_Win32,
      nRetries         => Number_Of_Retries_Win32);

   if SNMP_Session = null then
      Ada.Text_IO.Put_Line ("Failed to open session.");
   else
      Ada.Text_IO.Put_Line ("Opened session.");

      Send_Request : declare
         -- Request related (Ada)
         SNMP_Object_Identifier_Ada : constant String := ".1.3.6.1.2.1.1.5.0";

         -- Request related (C)
         SNMP_Object_Identifier_C : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (SNMP_Object_Identifier_Ada);

         -- Request related (Win32)
         SNMP_Object_Identifier_Win32 : constant Win32.LPSTR := Win32.To_PSTR (SNMP_Object_Identifier_C);

         SNMP_Object_Identifier : aliased Win32.Snmp.AsnObjectIdentifier;

         Variable_Bindings       : aliased Win32.Snmp.RFC1157VarBindList;
         Variable_Bindings_Entry : aliased Win32.Snmp.RFC1157VarBind;

         Error_Status : aliased Win32.Snmp.AsnInteger;
         Error_Index  : aliased Win32.Snmp.AsnInteger;

         Return_Value_String_To_OID_Conversion : Win32.BOOL;
         Return_Value_OID_Copy                 : Win32.INT;
         Return_Value_Request                  : Win32.INT;
         Return_Value_OID_To_String_Conversion : Win32.BOOL;
      begin
         Return_Value_String_To_OID_Conversion := Win32.Mgmtapi.SnmpMgrStrToOid
           (string => SNMP_Object_Identifier_Win32,
            oid    => SNMP_Object_Identifier'Access);

         if Return_Value_String_To_OID_Conversion = 0 then
            Ada.Text_IO.Put_Line ("Failed to convert string to object identifier.");
         else
            Ada.Text_IO.Put_Line ("Converted string to object identifier.");

            -- Allocate memory
            -- Allocated memory is not freed properly
            SnmpUtilMemAlloc (Win32.UINT (Win32.Snmp.RFC1157VarBind'Size * System.Storage_Unit));

            Return_Value_OID_Copy := SnmpUtilOidCpy
              (DestObjId => Variable_Bindings_Entry.name'Unrestricted_Access,
               SrcObjId  => SNMP_Object_Identifier'Access);

            if Return_Value_OID_Copy = 0 then
               Ada.Text_IO.Put_Line ("Failed to copy object identifier.");
            else
               Ada.Text_IO.Put_Line ("Copied object identifier.");

               -- Construct variable bindings entry
               Variable_Bindings_Entry.value.asnType := Win32.Snmp.ASN_NULL;

               -- Construct variable bindings
               Variable_Bindings.len  := 1;
               Variable_Bindings.list := Variable_Bindings_Entry'Unchecked_Access;

               Return_Value_Request := Win32.Mgmtapi.SnmpMgrRequest
                 (session          => SNMP_Session,
                  requestType      => Win32.Snmp.ASN_RFC1157_GETREQUEST,
                  variableBindings => Variable_Bindings'Access,
                  errorStatus      => Error_Status'Access,
                  errorIndex       => Error_Index'Access);

               if Return_Value_Request = 0 then
                  Ada.Text_IO.Put_Line ("Failed to send request.");
               else
                  Ada.Text_IO.Put_Line ("Sent request.");

                  Ada.Text_IO.Put_Line ("Type is:" & Win32.BYTE'Image (Variable_Bindings.list.value.asnType));

                  case Variable_Bindings.list.value.asnType is
                     when Win32.Snmp.ASN_INTEGER =>
                        Ada.Text_IO.Put_Line (Integer'Image (Integer (Variable_Bindings.list.value.asnValue.number)));
                     when Win32.Snmp.ASN_OCTETSTRING =>
                        Ada.Text_IO.Put_Line (Interfaces.C.Strings.Value (Win32.To_Chars_Ptr (To_PCSTR (Variable_Bindings.list.value.asnValue.string.stream))));
                     when Win32.Snmp.ASN_RFC1155_COUNTER =>
                        Ada.Text_IO.Put_Line (Counter_Type'Image (Counter_Type (Variable_Bindings.list.value.asnValue.counter)));
                     when Win32.Snmp.ASN_RFC1155_GAUGE =>
                        Ada.Text_IO.Put_Line (Gauge_Type'Image (Gauge_Type (Variable_Bindings.list.value.asnValue.counter)));
                     when Win32.Snmp.ASN_RFC1155_TIMETICKS =>
                        Ada.Text_IO.Put_Line (Duration'Image (Duration (Variable_Bindings.list.value.asnValue.ticks) / 100));
                     when others =>
                        Ada.Text_IO.Put_Line ("Unsupported type.");
                  end case;

                  -- Convert object identifier to string
                  Convert_OID_To_String : declare
                     OID_String_Win32 : aliased Win32.LPSTR;
                  begin
                     Return_Value_OID_To_String_Conversion := Win32.Mgmtapi.SnmpMgrOidToStr
                       (oid    => Variable_Bindings.list.name'Unrestricted_Access,
                        string => OID_String_Win32'Unchecked_Access);

                     if Return_Value_OID_To_String_Conversion = 0 then
                        Ada.Text_IO.Put_Line ("Failed to convert object identifier to string.");
                     else
                        Ada.Text_IO.Put_Line (Interfaces.C.Strings.Value (Win32.To_Chars_Ptr (OID_String_Win32)));
                     end if;

                     SnmpUtilMemFree (To_LPVOID (OID_String_Win32));
                  end Convert_OID_To_String;

                  -- Free memory
                  SnmpUtilOidFree (SNMP_Object_Identifier'Access);

                  SnmpUtilVarBindFree (Variable_Bindings_Entry'Access);

                  SnmpUtilVarBindListFree (Variable_Bindings'Access);

                  Interfaces.C.Strings.Free (IP_Address_C);

                  Interfaces.C.Strings.Free (Community_String_C);

                  Interfaces.C.Strings.Free (SNMP_Object_Identifier_C);
               end if;
            end if;
         end if;
      end Send_Request;

      Close_Session : declare
         Return_Value : constant Win32.BOOL := Win32.Mgmtapi.SnmpMgrClose (SNMP_Session);
      begin
         if Return_Value = 0 then
            Ada.Text_IO.Put_Line ("Failed to close session.");
         else
            Ada.Text_IO.Put_Line ("Closed session.");
         end if;
      end Close_Session;
   end if;
end SNMP_Example;

1 Ответ

2 голосов
/ 04 июля 2019

К сожалению, я не могу создать минимальный рабочий пример для тестирования кода, поэтому я не могу гарантировать, что приведенный ниже код действительно будет работать:

test.adb

with Ada.Text_IO;
with Ada.Unchecked_Conversion;
with Interfaces.C.Strings;

with Win32;
with Win32.Mgmtapi;
with Win32.Snmp;

procedure Test is

   pragma Linker_Options ("-lMgmtapi");    --  for SnmpMgrOidToStr
   pragma Linker_Options ("-lSnmpapi");    --  for SnmpUtilMemFree

begin

   -- [...]

   declare

      --  Field "name" in type "RFC1157VarBind" is not aliased so you cannot
      --  use 'Unchecked_Access. I see two options here:
      --
      --    (1) Use 'Unrestricted_Access on the field "name".
      --    (2) Copy "VarBind_Ptr.name" into a new aliased variable and use 'Unchecked_Access.
      --
      --  I *assume* that the library behind "SnmpMgrOidToStr" will not "store"
      --  our pointer and dereferece it after we left this block of code (resulting
      --  in an invalid dereference). If this assumption holds, then we
      --  can use 'Unchecked_Access.
      --
      --  As "VarBind_Ptr" is already an access type, its field must also have a
      --  memory address (depite it not being defined as aliased). Hence, I
      --  think it's safe to use the very permissive 'Unrestricted_Access
      --  attribute here to obtain a reference.

      VarBind_Ptr  : Win32.Snmp.a_RFC1157VarBind_t;

      OID            : aliased Win32.Snmp.AsnObjectIdentifier := VarBind_Ptr.name;
      OID_String_Ptr : aliased Win32.LPSTR;

      Return_Value : Win32.BOOL;

   begin

      --  Option 1
      Return_Value := Win32.Mgmtapi.SnmpMgrOidToStr
        (oid    => VarBind_Ptr.name'Unrestricted_Access,
         string => OID_String_Ptr'Unchecked_Access);

      --  Option 2
      Return_Value := Win32.Mgmtapi.SnmpMgrOidToStr
        (oid    => OID'Unchecked_Access,
         string => OID_String_Ptr'Unchecked_Access);


      declare

         --  Win32.ads contains a convenience function "To_Chars_Ptr" to
         --  convert "Win32.LPSTR" to "Interfaces.C.Strings.chars_ptr". Hence,
         --  we can convert "OID_String_Ptr" to an Ada string via "chars_ptr":
         --
         --     Win32.LPSTR ---> chars_ptr ---> String
         --
         --  Note that the character array to which "Win32.LPSTR" points is, in
         --  this particular case, null terminated (see [1]). This might not
         --  always be the case [3].

         use Ada.Text_IO;
         use Interfaces.C.Strings;

      begin
         Put_Line (Value (Win32.To_Chars_Ptr (OID_String_Ptr)));
      end;


      declare

         --  The documentation [1] states that the string returned by
         --  "SnmpMgrOidToStr" should be freed using "SnmpUtilMemFree" [2]. This
         --  function seems to be missing in the win32ada library (?), so we
         --  need to import it ourselves.

         procedure SnmpUtilMemFree (pMem : Win32.LPVOID)
           with Import, Convention => Stdcall, Link_Name => "SnmpUtilMemFree";           

         function To_LPVOID is
            new Ada.Unchecked_Conversion (Win32.LPSTR, Win32.LPVOID);

      begin
         SnmpUtilMemFree (To_LPVOID (OID_String_Ptr));
      end;

   end;

end Test;

Ссылки:

  • MS Docs: SnmpMgrOidToStr [ 1 ]
  • MS Docs: SnmpUtilMemFree [ 2 ]
  • MS Docs: LPSTR [ 3 ]
...