#!/usr/bin/perl
## -----------------------------------------------------------------------
##   This utility can talk to Intel(r) AMT(r) managed machines.
##
##   Copyright 2007 Gerd Hoffmann <kraxel@redhat.com>
##   Copyright 2010-2012 Stanislav V. Zankov
##
##   This program is free software; you can redistribute it and/or modify
##   it under the terms of the GNU General Public License as published by
##   the Free Software Foundation; either version 2 of the License, or
##   (at your option) any later version.
##
##   This program is distributed in the hope that it will be useful,
##   but WITHOUT ANY WARRANTY; without even the implied warranty of
##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##   GNU General Public License for more details.
##
##   You should have received a copy of the GNU General Public License along
##   with this program; if not, write to the Free Software Foundation, Inc.,
##   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
##
## -----------------------------------------------------------------------

use strict;
use warnings;
use Time::Zone;
use Digest::MD5 qw(md5_base64);
use MIME::Base64;
use Math::BigInt;

my $param_force = my $param_quiet = my $param_hex =
  my $param_num = my $usage_header_flag = my $request_amt_ver = 0;
my $amt_debug = (defined($ENV{'AMT_DEBUG'}) ? 1 : 0);
my ( $amt_version, $amt_timeout, $amt_cmnd );

my $schemabase = "http://schemas.intel.com/platform/client";

my $amt_host = shift;
my $amt_port = "16992";

while( defined($amt_host)  &&  $amt_host =~ m/^-/) {
    $param_force = 1 if ($amt_host =~ m/^-(-force|f)$/i);
    $param_quiet = 1 if ($amt_host =~ m/^-(-quiet|q)$/i);
    $param_hex = 1   if ($amt_host =~ m/^-(-hex|x)$/i);
    $param_num = 1   if ($amt_host =~ m/^-(-num(eric)?|n)$/i);
    $amt_debug |= 1  if ($amt_host =~ m/^-(-debug|d)$/i);
    $amt_debug |= 2  if ($amt_host =~ m/^-d(-debug|d)$/i);

    $amt_host = shift;
}

$main::amt_user = ( defined($ENV{'AMT_USER'}) ? $ENV{'AMT_USER'} : "admin" );
$main::amt_pass = $ENV{'AMT_PASSWORD'};

$amt_cmnd = shift;

if( defined($ENV{'AMT_VERSION'}) ) {
    $amt_version = $ENV{'AMT_VERSION'};
    if( $amt_version !~ m/^(\d+).(\d+)/ ) {
	print "  ERROR: 'AMT_VERSION' wrong value. Example: 2.21\n";
	exit -1;
    }
}

if( defined($ENV{'AMT_TIMEOUT'}) ) {
    $amt_timeout = $ENV{'AMT_TIMEOUT'};
    if( $amt_timeout !~ m/^\d+$/ ) {
	print "  ERROR: 'AMT_TIMEOUT' wrong value: $amt_timeout.\n";
	exit -1;
    }
}

sub soap_fault;
sub soap_debug;

eval "use SOAP::Lite autotype => 0, on_fault => \\&soap_fault".
    ($amt_debug ? ", +trace => [ transport => \\&soap_debug ], readable => 1" : "")."; 1"
  or die;


#############################################################################
# data

my @ps = ("S0", "S1", "S2", "S3", "S4", "S5 (soft-off)", "S4/S5", "G3 Off", "S1/S2/S3",
	  "G1 Sleep", "S5 by PwrButt", "Legacy ON", "Legacy OFF", "Unknown" );
my %rctrl_cmd = (
	"reset"		=> 0x10,
	"powerup"	=> 0x11,
	"powerdown"	=> 0x12,
	"powercycle"	=> 0x13,
	"setbootopt"	=> 0x21
);

my %rctrl_cmd_spec = (	# SpecialCommand
	"nop"		=> 0x00,
	"pxe"		=> 0x01,
	"hd"		=> 0x02,
	"hdsafe"	=> 0x03,
	"diag"		=> 0x04,
	"cd"		=> 0x05
);
my %rctrl_cmd_spec_intel_par = (	# SpecialCommandParameter for Intel
	"iderflop"   => 0x01,
	"idercd"     => 0x101,
	"reflash"    => 0x04,
	"biossetup"  => 0x08,
	"biospause"  => 0x10
);
my %rctrl_cmd_par_oem = (	# OEMParameters
	"sol"	=> 0x01
);
my %rctrl_b_opt = (		# -BootOptions-
	"lckpwr"	=> 0x0002,	# LockPowerButton
	"lckrst"	=> 0x0004,	# LockResetButton
	"lckslp"	=> 0x0040,	# LockSleepButton
	"lckkbd"	=> 0x0020,	# LockKeyboard
	"pwdbps"	=> 0x0800,	# UserPasswordBypass
	"evnt"		=> 0x1000,	# ForceProgressEvents
	"vdef"		=> 0x0000,	# VerbosityDefault
	"vqut"		=> 0x2000,	# VerbosityQuiet
	"vvrb"		=> 0x4000,	# VerbosityVerbose
	"vbln"		=> 0x6000,	# VerbosityBlank
	"cfgres"	=> 0x8000	# ConfigurationDataReset
);

# incomplete list
my %pt_status_desc = (
	0x0  =>  "success",
	0x1  =>  "internal error",
	0x2  =>  "not Ready",
	0x3  =>  "invalid pt mode",
	0xc  =>  "invalid name",
	0xf  =>  "invalid byte count",
	0x10 =>  "not permitted",
	0x17 =>  "max limit reached",
	0x18 =>  "invalid auth type",
	0x19 =>  "authentication failure",
	0x1a =>  "invalid dhcp mode",
	0x1b =>  "invalid ip address",
	0x1c =>  "invalid domain name",
	0x20 =>  "invalid provisioning state",
	0x22 =>  "invalid time",
	0x23 =>  "invalid index",
	0x24 =>  "invalid parameter",
	0x25 =>  "invalid netmask",
	0x26 =>  "flash write limit exceeded",
	0x400 => "disabled by policy",
	0x800 => "network if error base",
	0x801 => "unsupported oem number",
	0x802 => "unsupported boot option",
	0x803 => "invalid command",
	0x804 => "invalid special command",
	0x805 => "invalid handle",
	0x806 => "invalid password",
	0x807 => "invalid realm",
	0x808 => "storage acl entry in use",
	0x809 => "data missing",
	0x80a => "duplicate",
	0x80b => "eventlog frozen",
	0x80c => "pki missing_keys",
	0x80d => "pki generating keys",
	0x80e => "invalid key",
	0x80f => "invalid cert",
	0x810 => "cert key not match",
	0x811 => "max kerb domain reached",
	0x812 => "unsupported",
	0x813 => "invalid priority",
	0x814 => "not found",
	0x815 => "invalid credentials",
	0x816 => "invalid passphrase",
	0x818 => "no association",
	0x81b => "audit fail",
	0x81c => "blocking component"
);
my %pt_status = (
	"PT_STATUS_SUCCESS"			=> 0x0,
	"PT_STATUS_INTERNAL_ERROR"		=> 0x1,
	"PT_STATUS_NOT_READY"			=> 0x2,
	"PT_STATUS_INVALID_PT_MODE"		=> 0x3,
	"PT_STATUS_INVALID_NAME"		=> 0xC,
	"PT_STATUS_INVALID_BYTE_COUNT"		=> 0xF,
	"PT_STATUS_NOT_PERMITTED"		=> 0x10,
	"PT_STATUS_MAX_LIMIT_REACHED"		=> 0x17,
	"PT_STATUS_INVALID_AUTH_TYPE"		=> 0x18,
	"PT_STATUS_AUTHENTICATION_FAILURE"	=> 0x19,
	"PT_STATUS_INVALID_DHCP_MODE"		=> 0x1A,
	"PT_STATUS_INVALID_IP_ADDRESS"		=> 0x1B,
	"PT_STATUS_INVALID_DOMAIN_NAME"		=> 0x1C,
	"PT_STATUS_INVALID_PROVISIONING_STATE"	=> 0x20,
	"PT_STATUS_INVALID_TIME"		=> 0x22,
	"PT_STATUS_INVALID_INDEX"		=> 0x23,
	"PT_STATUS_INVALID_PARAMETER"		=> 0x24,
	"PT_STATUS_INVALID_NETMASK"		=> 0x25,
	"PT_STATUS_FLASH_WRITE_LIMIT_EXCEEDED"	=> 0x26,
	"PT_STATUS_DISABLED_BY_POLICY"		=> 0x400,
	"PT_STATUS_NETWORK_IF_ERROR_BASE"	=> 0x800,
	"PT_STATUS_UNSUPPORTED_OEM_NUMBER"	=> 0x801,
	"PT_STATUS_UNSUPPORTED_BOOT_OPTION"	=> 0x802,
	"PT_STATUS_INVALID_COMMAND"		=> 0x803,
	"PT_STATUS_INVALID_SPECIAL_COMMAND"	=> 0x804,
	"PT_STATUS_INVALID_HANDLE"		=> 0x805,
	"PT_STATUS_INVALID_PASSWORD"		=> 0x806,
	"PT_STATUS_INVALID_REALM"		=> 0x807,
	"PT_STATUS_STORAGE_ACL_ENTRY_IN_USE"	=> 0x808,
	"PT_STATUS_DATA_MISSING"		=> 0x809,
	"PT_STATUS_DUPLICATE"			=> 0x80A,
	"PT_STATUS_EVENTLOG_FROZEN"		=> 0x80B,
	"PT_STATUS_PKI_MISSING_KEYS"		=> 0x80C,
	"PT_STATUS_PKI_GENERATING_KEYS"		=> 0x80D,
	"PT_STATUS_INVALID_KEY"			=> 0x80E,
	"PT_STATUS_INVALID_CERT"		=> 0x80F,
	"PT_STATUS_CERT_KEY_NOT_MATCH"		=> 0x810,
	"PT_STATUS_MAX_KERB_DOMAIN_REACHED"	=> 0x811,
	"PT_STATUS_UNSUPPORTED"			=> 0x812,
	"PT_STATUS_INVALID_PRIORITY"		=> 0x813,
	"PT_STATUS_NOT_FOUND"			=> 0x814,
	"PT_STATUS_INVALID_CREDENTIALS"		=> 0x815,
	"PT_STATUS_INVALID_PASSPHRASE"		=> 0x816,
	"PT_STATUS_NO_ASSOCIATION"		=> 0x818,
	"PT_STATUS_AUDIT_FAIL"			=> 0x81B,
	"PT_STATUS_BLOCKING_COMPONENT"		=> 0x81C
);

my %provis_state = (
    "ProvisioningStatePre"  => 'Pre Provisioning (Factory Mode)',
    "ProvisioningStateIn"   => 'In Provisioning (Setup Mode)',
    "ProvisioningStatePost" => 'Post Provisioning (Operational Mode)'
);

my %pwd_model = (
    "PasswordModelCoupled"      => 'Coupled (the password of the network and the local interfaces are identical)',
    "PasswordModelSeperate"     => 'Separate (the password of the network and the local interfaces are separate)',
    "PasswordModelSeperateHash" => 'Separate-Hash password model'
);

my %me_capabilities = (
    0 => "CommonServices",
    1 => "AMT",
    2 => "SAL",
    3 => "Reserved3",
    4 => "Reserved4"
);

my %net_dhcp_modes = (
    0 => "Invalid",
    1 => "Disabled",
    2 => "Enabled"
);

my %net_dhcp_modes_code = (
    "DhcpModeInvalid"  => 0,
    "DhcpModeDisabled" => 1,
    "DhcpModeEnabled"  => 2
);

my %acl_access_permission = (
    "local"   => "LocalAccessPermission",
    "network" => "NetworkAccessPermission",
    "any"     => "AnyAccessPermission"
);

my %user_acl_realm = (
    0  =>  "Invalid",
    1  =>  "RCS",
    2  =>  "Redirection",
    3  =>  "PTAdministration",
    4  =>  "HardwareAsset",
    5  =>  "RemoteControl",
    6  =>  "Storage",
    7  =>  "EventManager",
    8  =>  "StorageAdmin",
    9  =>  "AgentPresenceLocal",
    10 =>  "AgentPresenceRemote",
    11 =>  "CircuitBreaker",
    12 =>  "NetworkTime",
    13 =>  "GeneralInfo",
    14 =>  "FirmwareUpdate",
    15 =>  "EIT",
    16 =>  "LocalUN",
    17 =>  "EndpointAccessControl",
    18 =>  "EndpointAccessControlAdmin",
    19 =>  "EventLogReader",
    20 =>  "SecurityAuditLog",
    21 =>  "UserAccessControl",
    22 =>  "Reserved1",
    23 =>  "Reserved2"
);
my %user_acl_realm_descr = (
    1  =>  "Used by Remote Connectivity provisioning",
    2  =>  "Redirection interfaces (SOL/IDER)",
    3  =>  "Controls access to all iAMT exposed interfaces",
    22 =>  "Reserved for future use",
    23 =>  "Reserved for future use"
);
my @user_acl_realm = (
    "Invalid",
    "RCS",
    "Redirection",
    "PTAdministration",
    "HardwareAsset",
    "RemoteControl",
    "Storage",
    "EventManager",
    "StorageAdmin",
    "AgentPresenceLocal",
    "AgentPresenceRemote",
    "CircuitBreaker",
    "NetworkTime",
    "GeneralInfo",
    "FirmwareUpdate",
    "EIT",
    "LocalUN",
    "EndpointAccessControl",
    "EndpointAccessControlAdmin",
    "EventLogReader",
    "SecurityAuditLog",
    "UserAccessControl",
    "Reserved1",
    "Reserved2"
);
my %user_acl_realm_codes = (
    "rcs"                        => 1,
    "redirection"                => 2,
    "ptadministration"           => 3,
    "hardwareasset"              => 4,
    "remotecontrol"              => 5,
    "storage"                    => 6,
    "eventmanager"               => 7,
    "storageadmin"               => 8,
    "agentpresencelocal"         => 9,
    "agentpresenceremote"        => 10,
    "circuitbreaker"             => 11,
    "networktime"                => 12,
    "generalinfo"                => 13,
    "firmwareupdate"             => 14,
    "eit"                        => 15,
    "localun"                    => 16,
    "endpointaccesscontrol"      => 17,
    "endpointaccesscontroladmin" => 18,
    "eventlogreader"             => 19,
    "securityauditlog"           => 20,
    "useraccesscontrol"          => 21,
    "reserved1"                  => 22,
    "reserved2"                  => 23
);

my %hwasset_type_names = (
    1  => "All",
    2  => "BIOS",
    3  => "ComputerSystem",
    4  => "Baseboard",
    5  => "Processor",
    6  => "MemoryModule",
    7  => "FRU",
    8  => "MediaDevice",
    9  => "PortableBattery",
    10 => "VproVerificationTable",
    11 => "AmtInformation"
);

my %hwasset_type_codes = (
    "bios" => 2,
    "computersystem" => 3,
    "baseboard" => 4,
    "processor" => 5,
    "memorymodule" => 6,
    "fru" => 7,
    "mediadevice" => 8,
    "portablebattery" => 9,
    "vproverificationtable" => 10,
    "amtinformation" => 11
);

my %hwasset_bios_characteristics = (
    0x1		=> "Reserved_1",
    0x2		=> "Reserved_2",
    0x4		=> "Unknown",
    0x8		=> "BIOS characteristics not supported",
    0x10	=> "ISA",
    0x20	=> "MCA",
    0x40	=> "EISA",
    0x80	=> "PCI",
    0x100	=> "PC_card",
    0x200	=> "PnP",
    0x400	=> "APM",
    0x800	=> "BIOS upgradeable",
    0x1000	=> "BIOS shadowing allowed",
    0x2000	=> "VL VESA",
    0x4000	=> "ESCD",
    0x8000	=> "Boot from CD",
    0x10000	=> "Selectable boot",
    0x20000	=> "BIOS ROM socketed",
    0x40000	=> "Boot from PC card",
    0x80000	=> "EDD spec",
    0x100000	=> "int13h floppy for NEC 9800",
    0x200000	=> "int13h floppy for Toshiba",
    0x400000	=> "int13h 5.25.in 360 kb floppy",
    0x800000	=> "int13h 5.25 in 1.2 mb floppy",
    0x1000000	=> "int13h 3.5 in 720 kb floppy",
    0x2000000	=> "int13h 3.5 in 2.88 mb floppy",
    0x4000000	=> "int5h print screen services",
    0x8000000	=> "int9h 8042 keyboard services",
    0x10000000	=> "int14h serial services",
    0x20000000	=> "int17h printer services",
    0x40000000	=> "int10h CGA and mono video",
    0x80000000	=> "NEC PC 98"
);

my %hwasset_cpu_family = (
    0x1 => "Other",
    0x2 => "Unknown",
    20 => "Intel(R) Celeron(R) M processor",
    21 => "Intel(R) Pentium(R) 4 HT processor",
    40 => "Intel(R) Core(TM) Duo processor",
    41 => "Intel(R) Core(TM) Duo mobile processor",
    42 => "Intel(R) Core(TM) Solo mobile processor",
    43 => "Intel(R) Atom(TM) processor",
    130 => "Itanium(TM) Processor",
    161 => "Quad-Core Intel(R) Xeon(R) processor 3200 Series",
    162 => "Dual-Core Intel(R) Xeon(R) processor 3000 Series",
    163 => "Quad-Core Intel(R) Xeon(R) processor 5300 Series",
    164 => "Dual-Core Intel(R) Xeon(R) processor 5100 Series",
    165 => "Dual-Core Intel(R) Xeon(R) processor 5000 Series",
    166 => "Dual-Core Intel(R) Xeon(R) processor LV",
    167 => "Dual-Core Intel(R) Xeon(R) processor ULV",
    168 => "Dual-Core Intel(R) Xeon(R) processor 7100 Series",
    169 => "Quad-Core Intel(R) Xeon(R) processor 5400 Series",
    170 => "Quad-Core Intel(R) Xeon(R) processor",
    171 => "Dual-Core Intel(R) Xeon(R) processor 5200 Series",
    172 => "Dual-Core Intel(R) Xeon(R) processor 7200 Series",
    173 => "Quad-Core Intel(R) Xeon(R) processor 7300 Series",
    174 => "Quad-Core Intel(R) Xeon(R) processor 7400 Series",
    175 => "Multi-Core Intel(R) Xeon(R) processor 7400 Series",
    176	=> "Pentium(R) III Xeon(TM)",
    177	=> "Pentium(R) III Processor with Intel(R) SpeedStep(TM) Technology",
    178	=> "Pentium(R) 4",
    179	=> "Intel(R) Xeon(TM)",
    181	=> "Intel(R) Xeon(TM) processor MP",
    184 => "Intel(R) Itanium(R) 2 processor",
    185 => "Intel(R) Pentium(R) M processor",
    186 => "Intel(R) Celeron(R) D processor",
    187 => "Intel(R) Pentium(R) D processor",
    188 => "Intel(R) Pentium Processor Extreme Edition",
    189 => "Intel(R) Core(TM) Solo Processor",
    190 => "Intel(R) Core(TM)2 Duo Processor",
    191 => "Intel(R) Core(TM) 2 Duo Processor",
    192 => "Intel(R) Core(TM)2 Solo processor",
    193 => "Intel(R) Core(TM)2 Extreme processor",
    194 => "Intel(R) Core(TM)2 Quad processor",
    195 => "Intel(R) Core(TM)2 Extreme mobile processor",
    196 => "Intel(R) Core(TM)2 Duo mobile processor",
    197 => "Intel(R) Core(TM)2 Solo mobile processor",
    198 => "Intel(R) Core(TM) i7 processor",
    199 => "Dual-Core Intel(R) Celeron(R) processor",
    205	=> "Intel(R) Core(TM) i5 processor",
    206	=> "Intel(R) Core(TM) i3 processor",
    214 => "Multi-Core Intel(R) Xeon(R) processor",
    215 => "Dual-Core Intel(R) Xeon(R) processor 3xxx Series",
    216 => "Quad-Core Intel(R) Xeon(R) processor 3xxx Series",
    218 => "Dual-Core Intel(R) Xeon(R) processor 5xxx Series",
    219 => "Quad-Core Intel(R) Xeon(R) processor 5xxx Series",
    221 => "Dual-Core Intel(R) Xeon(R) processor 7xxx Series",
    222 => "Quad-Core Intel(R) Xeon(R) processor 7xxx Series",
    223 => "Multi-Core Intel(R) Xeon(R) processor 7xxx Series",
    224	=> "Multi-Core Intel(R) Xeon(R) processor 3400 Series"
);

my %hwasset_cpu_upg_info = (
    0x1 => "Other",
    0x2 => "Unknown",
    0x3 => "Daughterboard",
    0x4 => "ZIF socket",
    0x5 => "Replaceable Piggyback",
    0x6 => "Not upgradeable",
    0x7 => "LIF socket",
    0x8 => "Slot 1",
    0x9 => "Slot 2",
    0xA => "Socket 370",
    0xC => "Slot M",
    0xD => "Socket 423",
    0xF => "Socket 478",
     16 => "Socket 754",
     17 => "Socket 940",
     18 => "Socket 939",
     19 => "Socket mPGA604",
     20 => "Socket LGA771",
     21 => "Socket LGA775",
     22 => "Socket S1",
     23 => "Socket AM2",
     24 => "Socket F (1207)",
     25 => "Socket LGA1366",
     29 => "Socket LGA1156",
     30 => "Socket LGA1567",
     31 => "Socket PGA988A",
     32 => "Socket BGA1288"
);

my %hwasset_mem_form_fact = (
    1 => "Other",
    2 => "Unknown",
    3 => "SIMM",
    4 => "SIP",
    5 => "Chip",
    6 => "DIP",
    7 => "ZIP",
    8 => "Proprietary Card",
    9 => "DIMM",
    10 => "TSOP",
    11 => "Row of chips",
    12 => "RIMM",
    13 => "SODIMM",
    14 => "SRIMM",
    15 => "FB-DIMM"
);

my %hwasset_mem_type = (
    1 => "Other",
    2 => "Unknown",
    3 => "DRAM",
    4 => "EDRAM",
    5 => "VRAM",
    6 => "SRAM",
    7 => "RAM",
    8 => "ROM",
    9 => "FLASH",
    10 => "EEPROM",
    11 => "FEPROM",
    12 => "EPROM",
    13 => "CDRAM",
    14 => "3DRAM",
    15 => "SDRAM",
    16 => "SGRAM",
    17 => "RDRAM",
    18 => "DDR",
    19 => "DDR2",
    20 => "DDR2 FB-DIMM",
    24 => "DDR3",
    25 => "FBD2"
);

my %hwasset_mem_type_det = (
    0x1		=> "Reserved1",
    0x2		=> "Other",
    0x4		=> "Unknown",
    0x8		=> "FastPaged",
    0x10	=> "StaticColumn",
    0x20	=> "PseudoStatic",
    0x40	=> "RAMBUS",
    0x80	=> "Synchronous",
    0x100	=> "CMOS",
    0x200	=> "EDO",
    0x400	=> "WindowDRAM",
    0x800	=> "CacheDRAM",
    0x1000	=> "NonVolatile",
    0x2000	=> "Reserved2",
    0x4000	=> "Reserved3",
    0x8000	=> "Reserved4"
);

my %hwasset_media_cap82 = (
    0x1		=> "SMART feature set",
    0x2		=> "Security Mode feature set",
    0x4		=> "Removable Media feature set",
    0x8		=> "Mandatory Power Management feature set",
    0x10	=> "PACKET command feature set",
    0x20	=> "Write cache",
    0x40	=> "Look-ahead",
    0x80	=> "Release interrupt",
    0x100	=> "SERVICE interrupt",
    0x200	=> "DEVICE RESET command",
    0x400	=> "Host Protected Area feature set",
    0x800	=> "WRITE VERIFY command (Obsolete)",
    0x1000	=> "WRITE BUFFER command",
    0x2000	=> "READ BUFFER command",
    0x4000	=> "NOP command",
    0x8000	=> "IDENTIFY DEVICE DMA command (Obsolete)"
);
my %hwasset_media_cap83 = (
    0x1		=> "DOWNLOAD MICROCODE command",
    0x2		=> "READ/WRITE DMA QUEUED",
    0x4		=> "CFA feature set",
    0x8		=> "Advanced Power Management feature set",
    0x10	=> "Removable Media Status Notification feature set",
    0x20	=> "Power-Up In Standby feature set",
    0x40	=> "SET FEATURES subcommand required to spinup after power-up",
    0x80	=> "cap_word83_bit7",
    0x100	=> "SET MAX security extension",
    0x200	=> "Automatic Acoustic Management feature set",
    0x400	=> "48-bit Address feature set",
    0x800	=> "Device Configuration Overlay feature set",
    0x1000	=> "Mandatory FLUSH CACHE command",
    0x2000	=> "FLUSH CACHE EXT command",
#    0x4000	=> "Shall be set to one",
    0x8000	=> "Shall be cleared to zero"
);
my %hwasset_media_cap84 = (
    0x1		=> "SMART error logging",
    0x2		=> "SMART self-test",
    0x4		=> "Media serial number",
    0x8		=> "Media Card Pass Through Command feature set",
    0x10	=> "Streaming feature set",
    0x20	=> "General Purpose Logging feature set",
    0x40	=> "WRITE DMA FUA EXT and WRITE MULTIPLE FUA EXT",
    0x80	=> "WRITE DMA QUEUED FUA EXT command",
    0x100	=> "World wide name",
    0x200	=> "URG bit supported for READ STREAM DMA/PIO",
    0x400	=> "URG bit supported for WRITE STREAM DMA/PIO",
    0x800	=> "Time-limited R/W feature set",
    0x1000	=> "Time-limited R/W feature set R/W continuous enabled",
    0x2000	=> "cap_word84_bit13",
#    0x4000	=> "Shall be set to one",
    0x8000	=> "Shall be cleared to zero"
);

my %auditable_app_id = (
    16	=> "Security Admin",
    17	=> "Remote Control Operation",
    18	=> "Redirection Manager",
    19	=> "Firmware Update Manager",
    20	=> "Security Audit Log",
    21	=> "Network Time",
    22	=> "Network Administration",
    23	=> "Storage Administration",
    24	=> "Event Manager",
    25	=> "Circuit Breaker Manager",
    26	=> "Agent Presence Manager",
    27	=> "Wireless Configuration",
    28	=> "EAC"
);
my %audit_event_id = (
  16 => {
    0 =>  "AMT Provisioning Started",
    1 =>  "AMT Provisioning Completed",
    2 =>  "ACL Entry Added",
    3 =>  "ACL Entry Modified",
    4 =>  "ACL Entry Removed",
    5 =>  "ACL Access with invalid credentials",
    6 =>  "ACL Entry Enabled",
    7 =>  "TLS State Changed",
    8 =>  "TLS Server Certificate Set",
    9 =>  "TLS Server Certificate Removed",
    10 => "TLS Trusted Root Certificate Added",
    11 => "TLS Trusted Root Certificate Removed",
    12 => "TLS Pre-shared Key Set",
    13 => "Kerberos Settings Modified",
    14 => "Kerberos Master Key Modified",
    15 => "Flash Wear-Out Counters Reset",
    16 => "Power Package Modified",
    17 => "Set realm Authentication mode"
  },
  17 => {
    0 => "Performed Power-Up",
    1 => "Performed Power-Down",
    2 => "Performed Power-Cycle",
    3 => "Performed Reset",
    4 => "Boot Options"
  },
  18 => {
    0 => "IDE-R Session Opened",
    1 => "IDE-R Session Closed",
    2 => "IDE-R Enabled",
    3 => "IDE-R Disabled",
    4 => "SoL Session Opened",
    5 => "SoL Session Closed",
    6 => "SoL Enabled",
    7 => "SoL Disabled"
  },
  19 => {
    0 => "Firmware Updated",
    1 => "Firmware Update Failed"
  },
  20 => {
    0 => "Security Audit Log Cleared",
    1 => "Security Audit policy modified",
    2 => "Security Audit Log Disabled",
    3 => "Security Audit Log Enabled",
    4 => "Security Audit Log Exported",
    5 => "Security Audit Log Recovered (AMT 5.1+)"
  },
  21 => {
    0 => "AMT Time Set"
  },
  22 => {
    0 => "TCP/IP Parameters Set",
    1 => "Host Name Set",
    2 => "Domain Name Set",
    3 => "VLAN Parameters Set",
    4 => "Link Policy Set"
  },
  23 => {
    0 => "Global Storage Attributes Set",
    1 => "Storage EACL Modified",
    2 => "Storage FPACL Modified",
    3 => "Storage Write Operation"
  },
  24 => {
    0 => "Alert Subscribed",
    1 => "Alert Unsubscribed",
    2 => "Event Log Cleared",
    3 => "Event Log Frozen"
  },
  25 => {
    0 => "CB Filter Added",
    1 => "CB Filter Removed",
    2 => "CB Policy Added",
    3 => "CB Policy Remove",
    4 => "CB Default Policy Set",
    5 => "CB Heuristics Option Set",
    6 => "CB Heuristics State Cleared"
  },
  26 => {
    0 => "Agent Watchdog Added",
    1 => "Agent Watchdog Removed",
    2 => "Agent Watchdog Action set"
  },
  27 => {
    0 => "Wireless profile added",
    1 => "Wireless profile removed",
    2 => "Wireless profile updated"
  },
  28 => {
    0 => "EAC Posture Signer SET",
    1 => "EAC Enabled",
    2 => "EAC Disabled",
    3 => "EAC Posture State update",
    4 => "EAC Set Options"
  }
);

my %audit_storage_policy_get = (
    0 => "No Roll Over",
    1 => "Roll Over",
    2 => "Restricted Roll Over"
);
my %audit_storage_policy_set = (
    "no_ro" => 0,
    "rollover" => 1,
    "restrict_ro" => 2
);

my %signing_mechanism = (
    0 => "RSA_SHA-1",
    1 => "RSA_SHA-256",
    2 => "RSA_SHA-384"
);

my %audit_lock_type = (
    "lock"	=> 0,
    "unprovis"	=> 1,
    "unlock"	=> 2
);

my %provisioning_modes = (
    "Current"		=> 0,
    "Enterprise"	=> 1,
    "SmallBusiness"	=> 2,
    "RemoteConnectivity" => 3
);
my %provisioning_codes = (
    0 => "Current",
    1 => "Enterprise",
    2 => "SmallBusiness",
    3 => "RemoteConnectivity"
);

my %acl_realm_types = (
    0 => 'Invalid',
    1 => 'RCS',
    2 => 'Redirection',
    3 => 'PTAdministration',
    4 => 'HardwareAsset',
    5 => 'RemoteControl',
    6 => 'Storage',
    7 => 'EventManager',
    8 => 'StorageAdmin',
    9 => 'AgentPresenceLocal',
    10 => 'AgentPresenceRemote',
    11 => 'CircuitBreaker',
    12 => 'NetworkTime',
    13 => 'GeneralInfo',
    14 => 'FirmwareUpdate',
    15 => 'EIT',
    16 => 'LocalUN',
    17 => 'EndpointAccessControl',
    18 => 'EndpointAccessControlAdmin',
    19 => 'EventLogReader',
    20 => 'SecurityAuditLog',
    21 => 'UserAccessControl',
    22 => 'Reserved1',
    23 => 'Reserved2'
);
my %acl_realm_codes = (
	"invalid" => 0,
	"rcs" => 1,
	"redirection" => 2,
	"ptadministration" => 3,
	"hardwareasset" => 4,
	"remotecontrol" => 5,
	"storage" => 6,
	"eventmanager" => 7,
	"storageadmin" => 8,
	"agentpresencelocal" => 9,
	"agentpresenceremote" => 10,
	"circuitbreaker" => 11,
	"networktime" => 12,
	"generalinfo" => 13,
	"firmwareupdate" => 14,
	"eit" => 15,
	"localun" => 16,
	"endpointaccesscontrol" => 17,
	"endpointaccesscontroladmin" => 18,
	"eventlogreader" => 19,
	"securityauditlog" => 20,
	"useraccesscontrol" => 21,
	"reserved1" => 22,
	"reserved2" => 23
);
my %sensor_entity_id_codes = (
	0 => "match all",
	1 => "Other",
	2 => "Unknown (unspecified)",
	3 => "Processor",
	4 => "Disk or disk bay",
	5 => "Peripheral bay",
	6 => "System management module",
	7 => "System board",
	8 => "Memory module",
	9 => "Processor module",
	10 => "Power supply",
	11 => "Add-in card",
	12 => "Front panel board",
	13 => "Back panel board",
	14 => "Power system board",
	15 => "Drive backplane",
	16 => "System internal expansion board",
	17 => "Other system board",
	18 => "Processor board",
	19 => "Reserved",
	20 => "Power unit / power domain",
	21 => "Power module / converter",
	22 => "Power management / power distribution board",
	23 => "Chassis back panel board",
	24 => "System chassis",
	25 => "Sub-chassis",
	26 => "Other chassis board",
	27 => "Disk Drive Bay",
	28 => "Peripheral Bay",
	29 => "Device Bay",
	30 => "Fan / cooling device",
	31 => "Cooling unit",
	32 => "Cable / interconnect",
	33 => "Memory device",
	34 => "System Management Software",
	35 => "BIOS",
	36 => "Operating System",
	37 => "System Bus",
	38 => "Group (for use with Entity Association)"
);
my %sensor_type_desc = (
	0x00 => "reserved",
	0x01 => "Temperature",
	0x02 => "Voltage",
	0x03 => "Current",
	0x04 => "Fan",
	0x05 => "Physical Security (Chassis Intrusion)",
	0x06 => "Platform Security Violation Attempt",
	0x07 => "Processor",
	0x08 => "Power Supply",
	0x09 => "Power Unit",
	0x0A => "Cooling Device",
	0x0B => "Other Units-based Sensor (per units given in SDR)",
	0x0C => "Memory",
	0x0D => "Drive Slot (Bay)",
	0x0E => "POST Memory Resize",
	0x0F => "POST Progress/Error",
	0x10 => "Event Logging Disabled",
	0x11 => "Watchdog 1",
	0x12 => "System Event",
	0x13 => "Critical Interrupt",
	0x14 => "Button",
	0x15 => "Module / Board",
	0x16 => "Microcontroller / Coprocessor",
	0x17 => "Add-in Card",
	0x18 => "Chassis",
	0x19 => "Chip Set",
	0x1A => "Other FRU",
	0x1B => "Cable / Interconnect",
	0x1C => "Terminator",
	0x1D => "System Boot Initiated",
	0x1E => "Boot Error",
	0x1F => "OS Boot",
	0x20 => "OS Critical Stop",
	0x21 => "Slot / Connector",
	0x22 => "System ACPI Power State",
	0x23 => "Watchdog 2",
	0x24 => "Platform Alert",
	0x25 => "Entity Presence",
	0x26 => "Monitor ASIC / IC",
	0x27 => "LAN",
	0x28 => "Management Subsystem Health",
	0x29 => "Battery",
	0x2A => "Session Audit",
	0x2B => "Version Change",
	0x2C => "FRU State"
);
my %event_severity_desc = (
	0x00 => "unspecified",
	0x01 => "Monitor",
	0x02 => "Information",
	0x04 => "OK (return to OK condition)",
	0x08 => "Non-critical condition (warning)",
	0x10 => "Critical condition",
	0x20 => "Non-recoverable condition"
);
my %event_severity_code = (
	"unspecified"	=> 0x00,
	"monitor"	=> 0x01,
	"inform"	=> 0x02,
	"ok"		=> 0x04,
	"warning"	=> 0x08,
	"critical"	=> 0x10,
	"non-recover"	=> 0x20
);
my %event_desc;
sub init_event_desc_var;

#######################################################################################
# SOAP-related

my ( $nas, $sas, $rcs, $gis, $nts, $ems, $has, $als, $rds, $uas, $ras );

#--------------------------------------------------------------------------------------
sub SOAP::Transport::HTTP::Client::get_basic_credentials {
    return $main::amt_user => $main::amt_pass;
}

#--------------------------------------------------------------------------------------
sub soap_init() {
    my $proxybase = "http://$amt_host:$amt_port";

    $amt_host =~ m/^([a-z0-9_.-]+)(:([0-9]{1,5}))?$/i;
    if( defined($3) ) {
	$amt_host = $1;
	$amt_port = $3;
    }

    $nas = SOAP::Lite->new(
		proxy      => "$proxybase/NetworkAdministrationService",
		default_ns => "$schemabase/NetworkAdministration/2004/01");
    $sas = SOAP::Lite->new(
		proxy      => "$proxybase/SecurityAdministrationService",
		default_ns => "$schemabase/SecurityAdministration/2004/01");
    $rcs = SOAP::Lite->new(
		proxy      => "$proxybase/RemoteControlService",
		default_ns => "$schemabase/RemoteControl/2004/01");
    $gis = SOAP::Lite->new(
		proxy      => "$proxybase/GeneralInfoService",
		default_ns => "$schemabase/GeneralInfo/2004/01" );
    $nts = SOAP::Lite->new(
		proxy      => "$proxybase/NetworkTimeService",
		default_ns => "$schemabase/NetworkTime/2004/01");
    $ems = SOAP::Lite->new(
		proxy      => "$proxybase/EventManagerService",
		default_ns => "$schemabase/EventManager/2004/01");
    $uas = SOAP::Lite->new(
		proxy      => "$proxybase/UserAccessControlService",
		default_ns => "$schemabase/UserAccessControl/2008/01");
    $has = SOAP::Lite->new(
		proxy      => "$proxybase/HardwareAssetService",
		default_ns => "$schemabase/HardwareAsset/2004/01");
    $als = SOAP::Lite->new(
		proxy      => "$proxybase/AuditLogService",
		default_ns => "$schemabase/AuditLog/2008/01");
    $rds = SOAP::Lite->new(
		proxy      => "$proxybase/RedirectionService",
		default_ns => "$schemabase/Redirection/2004/01");
    $ras = SOAP::Lite->new(
		proxy      => "$proxybase/RemoteAccessAdminService",	# + else 404
		default_ns => "$schemabase/RemoteAccessAdmin/2008/01");

    if( defined($amt_timeout) ) {
	foreach my $s ( $nas, $sas, $rcs, $gis, $nts, $ems, $has, $als, $rds, $uas, $ras ) {
	    $s->transport->timeout( $amt_timeout );
	    $s->proxy->timeout( $amt_timeout );
	}
    }
}
#--------------------------------------------------------------------------------------
sub soap_fault {
    my( $soap, $res ) = @_;

    if( ref $res ) {
	print "\nRequest FAULT. Code='", $res->faultcode, "'. Details: '", $res->faultdetail,
		"'. Fault string: '". $res->faultstring. "'.\n\n";
    } else {
	print "\n  FAULT: ", $soap->transport->status, "\n";
    }
    print "    Use AMT_VERSION to skip version check fails.\n"
      if( $request_amt_ver );
    exit 1;
}

#--------------------------------------------------------------------------------------
sub soap_debug {
    my ($in) = @_;
    my $resp = ref($in) eq "HTTP::Response";

    if( ($amt_debug & 2  && $resp ) ||
	($amt_debug & 1  && ref($in) eq "HTTP::Request") ) {
	print STDERR "\n--\n", $in->content, ($resp ? "\n" : ""), "--\n";
    }
}


#############################################################################
# functions

sub print_usage_header
{
    print STDERR <<EOF
 amttool 1.7.4   Can talk to Intel AMT managed devices (SOAP-based version).

 Usage: amttool [<params>]  <hostname>[:<port>]  <command> [help|<cmd_params>]
 Parameters (before the hostname): --force|-f, --quiet|-q, --debug|-d, --ddebug|-dd

EOF
  if( ! $usage_header_flag++ );
}
#--------------------------------------------------------------------------------------
sub usage()
{
    print_usage_header();
    print STDERR <<EOF;
 <command>:
   help     - detailed commands help (except remote control)
   info     - general,audit,remote info (default command when only hostname given)
   net      - iAMT device network administration
   time     - get/sync iAMT device time
   user     - access control management for an admin
   uuser    - user access control (set own passwd, del special permissions)
   hwasset  - prints hardware asset data
   audit    - Access Monitor/Audit (AMT ver. 4.0+)
   event    - platform events: configure, view log, subscribe
   security - some commands from Security Administration interface
   pwr_save - power saving management
   redirect - IDE-Redirection settings and log
   rem_control - remote power and boot control and info

 Use 'amttool help' to get the full detailed help.

 Environment variables:
   AMT_USER - AMT username ('admin' if not set)
   AMT_PASSWORD - AMT Password
   AMT_VERSION  - to skip AMT version check (no access to GeneralInfo realm, etc.)
   AMT_TIMEOUT - set connection timeout
   HTTP_proxy - link to proxy in format: http://[proxy_user:pass@]<proxy_addr>:<port>

EOF
#   rem_access  - administer remote access parameters
}
#--------------------------------------------------------------------------------------
sub get_user_confirm() {
    return 1 if( $param_force );

    print " [y/N] ? ";
    my $reply = <stdin>;
    return 1 if( $reply =~ m/^(y|yes)$/i);

    printf "canceled.\n";
    return 0;
}
#--------------------------------------------------------------------------------------
sub print_pt_error_str($) {
    my $rc = shift;

    printf "error 0x%x - ", $rc;
    if( !defined($pt_status_desc{$rc}) ) {
	print "unknown status code.\n";
    } else {
	print $pt_status_desc{$rc}, ".\n";
    }
}
#--------------------------------------------------------------------------------------
sub print_err_result($) {
    my $ret = shift;

    return 0  if( $ret->result == $pt_status{'PT_STATUS_SUCCESS'} );

    print_pt_error_str( $ret->result );
    return 1;
}
#--------------------------------------------------------------------------------------
sub print_commit_warn {
    my $ret = shift;

    if( defined($ret) && $ret->result == $pt_status{'PT_STATUS_SUCCESS'}) {
	print " * Use 'sucurity commit' to save changes!\n\n";
    }
}
#--------------------------------------------------------------------------------------
sub print_result($) {
    my $ret = shift;

    print "done.\n" if( !print_err_result( $ret ) );
    return $ret->result;
}
#--------------------------------------------------------------------------------------
sub print_paramsout($) {
    my $ret = shift;
    my @paramsout = $ret->paramsout;
    print "params: ", join(", ", @paramsout), "\n";
}
#--------------------------------------------------------------------------------------
sub print_hash {
    my $hash = shift;
    my $in = shift;
    my $wi = shift;

    foreach my $item (sort keys %{$hash}) {
	if( ref($hash->{$item}) eq "HASH") {
#		printf "%*s%s\n", $in, "", $item;
		next;
	}
	printf "%*s%-*s%s\n", $in, "", $wi, $item, $hash->{$item};
    }
}
#--------------------------------------------------------------------------------------
sub conv_uuid_txt_to_base64 {
    my $guid = shift;

    $guid =~ m/^(..)(..)(..)(..)-(..)(..)-(..)(..)-(..)(..)-(..)(..)(..)(..)(..)(..)$/;

    my @guid = ( $4,$3,$2,$1, $6,$5, $8,$7, $9,$10,$11,$12,$13,$14,$15,$16 );
    for my $i ( 0..15 ) {
	$guid[$i] = hex($guid[$i]);
    }
    return encode_base64( pack "C16", @guid );
}
#--------------------------------------------------------------------------------------
sub print_uuid {
    my ($q) = @_;
    my @n;
    if( ref($q) eq "HASH" ) {
	@n = @{ $q->{'Byte'} };
    } else {
	@n = @_;
    }
    printf "%02x%02x%02x%02x-%02x%02x-%02x%02x-", $n[3], $n[2], $n[1], $n[0],
	    $n[5], $n[4], $n[7], $n[6];
    printf "%02x%02x-", $n[8], $n[9];
    for my $i ( 10 .. 15 ) {
	printf "%02x", $n[$i]; }
}
#--------------------------------------------------------------------------------------
sub conv_ipv4_to_txt {
    my $ipv4 = shift;

    my $addr = sprintf("%d.%d.%d.%d",
		$ipv4 / 256 / 256 / 256,
		$ipv4 / 256 / 256 % 256,
		$ipv4 / 256 % 256,
		$ipv4 % 256);

    return $addr;
}
#--------------------------------------------------------------------------------------
sub print_date_time {
	my $time = shift;
	my $in = shift;

	my @tmp = localtime( $time );
	printf "%*s%02s/%02s/%s %02s:%02s", $in, "",
		$tmp[3], $tmp[4]+1, $tmp[5]+1900, $tmp[2], $tmp[1];
}
#--------------------------------------------------------------------------------------
sub print_hash_ipv4 {
    my $hash = shift;
    my $in = shift;
    my $wi = shift;

    foreach my $item (sort keys %{$hash})
    {
	printf "%*s%-*s", $in, "", $wi, $item;
	my $val = $hash->{$item};
	if( $val =~ m/[0-9]+/ ) {
	    printf conv_ipv4_to_txt( $val )."\n";
	} else {
	    printf "$val\n";
	}
    }
}
#--------------------------------------------------------------------------------------
sub print_newline($) {
    my $cmd = shift;
    if( $cmd eq "help" ) {
	print STDERR "\n"
    } else {
	print "\n"
    }
}

#-----------------------------------------------------------------------------------------
sub do_soap {
    my $soap = shift;
    my $name = shift;
    my @args = @_;

    my $method = SOAP::Data ->name($name)
			    ->attr( { xmlns => $soap->ns } );
    my $ret = $soap->call( $method, @args );

    return print_result($ret);
}
#-----------------------------------------------------------------------------------------
sub check_amt_version {
    my ( $major, $minor, $verb ) = @_;

    if( ! defined($amt_version) ) {
	$request_amt_ver = 1;
	$amt_version = $gis->GetCoreVersion()->paramsout;
	$request_amt_ver = 0;
    }

    $amt_version =~ m/^(\d+).(\d+)/;
    return 1  if $1 > $major;
    return 1  if $1 == $major && $2 >= $minor;

    if( $verb ) {
	print "$verb: " if( $verb !~ m/^v(erb)?$/i);
	print "version mismatch (need $major.$minor+, have $amt_version)\n";
    }
    return 0;
}
#======================================================================================

sub cmnd_rem_control_info {
    my $cmd = shift;
    $cmd = ( defined($cmd) ? lc($cmd) : "" );

    if( $cmd eq "help" ) {
	print_usage_header();
	print STDERR "    rem_control info\n\tprints the remote control capabilities\n";
	return 0;
    }

    my $ret = $rcs->GetSystemPowerState();
    return $ret->result  if( print_err_result($ret) );

    my $val = $ret->valueof('//SystemPowerState');
    print "# Remote Control Info :: AMT Remote Control\n",
	"  Powerstate:    \t", $ps[ $val & 0x0f ],
	"\n  Watchdog Expired:\t", ( $val & 0x100 ? "Yes" : "No" ),
	"\n  Power Source:  \t",   ( $val & 0x200 ? "DC" : "AC" );

    $ret = $rcs->GetRemoteControlCapabilities();
    return $ret->result  if( print_err_result($ret) );

    $val = $ret->valueof('//SystemCapabilitiesSupported');
    print "\n  Remote Control Capabilities:\n",
	"    Control Commands Supported\t",
	$val & 0x01 ? "powerCycle " : "",
	$val & 0x02 ? "powerDown "  : "",
	$val & 0x04 ? "powerUp "    : "",
	$val & 0x08 ? "reset"       : "",
	"\n    IanaOemNumber\t\t", $ret->valueof('//IanaOemNumber');

    $val = $ret->valueof('//SpecialCommandsSupported');
    print "\n    Special Commands Supported\t",
	$val & 0x0100 ? "PXE-boot "         : "",
	$val & 0x0200 ? "HD-boot "          : "",
	$val & 0x0400 ? "HD-boot-safemode " : "",
	$val & 0x0800 ? "Diag-boot "        : "",
	$val & 0x1000 ? "CD-boot"           : "";

    $val = $ret->valueof('//OemDefinedCapabilities');
    print "\n    Special Commands (Oem)\t",
	$val & 0x01 ? "IDER "        : "",
	$val & 0x02 ? "SOL "         : "",
	$val & 0x04 ? "BiosReflash " : "",
	$val & 0x08 ? "BiosSetup "   : "",
	$val & 0x10 ? "BiosPause "   : "";
    if( check_amt_version(2,0) ) {
	my $term = $val & 0xff00;
	print "",
		$val & 0x020 ? "NoFloppyBoot " : "",
		$val & 0x040 ? "NoCDBoot "     : "",
		$term == 0x1 ? "TerminalEmulationVT52 "    : "",
		$term == 0x2 ? "TerminalEmulationVT100+ "  : "",
		$term == 0x3 ? "TerminalEmulationVT-UTF8 " : "",
		$term == 0x4 ? "TerminalEmulationPC-ANSI"  : "";
    }

    $val = $ret->valueof('//SystemFirmwareCapabilities');
    print "\n    SystemFirmwareCapabilities\t",
	$val & 0x0002 ? "LockPowerButton "        : "",
	$val & 0x0004 ? "LockResetButton "        : "",
	$val & 0x0040 ? "LockSleepButton "        : "",
	$val & 0x0020 ? "LockKeyboard "           : "",
	$val & 0x0800 ? "UserPasswordBypass "     : "",
	$val & 0x1000 ? "ForcedProgressEvents "   : "",
	$val & 0x8000 ? "ConfigurationDataReset " : "",
	$val & 0x2000 ? "VerbosityVerbose "       : "",
	$val & 0x4000 ? "VerbosityQuiet "         : "",
	$val & 0x0001 ? "VerbosityScreenBlank"    : "",
	"\n";
}

#--------------------------------------------------------------------------------------
sub cmnd_rem_control_power {
    my $cmd = shift;
    my @params  = @_;
    my ( @args, $spec );
    my $sol = my $run_sol = my $boot_opts = my $spec_int= 0;

    $cmd = lc $cmd;

    if( lc($cmd) eq "help" || !defined( $rctrl_cmd{$cmd}) ) {
	print_usage_header();
	print STDERR <<EOF;
    rem_control <remote_control_command>
	managing the power and booting states
	<remote_control_command>:
	  powerdown
	  (powerup | powercycle | reset) [<boot_param>] [<boot_options>]
	  setbootopt <boot_param> [<boot_options>]
	<boot_param>:
	  pxe|hd|hdsafe|cd|diag
	  [iderflop|idercd] [biossetup] [biospause] [reflash] [sol]
	sol - serial-over-lan (use 'amtterm' utility for access)
	<boot_options>:
	  lckpwr - lock power button     vdef - verbosity: default
	  lckrst - lock reset button     vvrb - verbosity: verbose
	  lckslp - lock sleep button     vqut - verbosity: quiet
	  lckkbd - lock keyboard         vbln - verbosity: screen blank
	  pwdbps - user password bypass  evnt - forced progress events
	  cfgres - config. data reset
EOF
	return 0;
    }

    foreach my $par ( @params ) {
	my $p = lc $par;
	if( $p =~ m/^sol$/ ) { $sol = 1; }
	elsif( $p =~ m/^runsol$/ ) { $run_sol = 1; $sol = 1; }
	elsif( defined $rctrl_cmd_spec{$p} ) { $spec = $rctrl_cmd_spec{ $p }; }
	elsif( defined $rctrl_b_opt{$p} ) { $boot_opts += $rctrl_b_opt{ $p }; }
	elsif( ( !defined($spec) || $spec == 0xC1 )
		 && defined $rctrl_cmd_spec_intel_par{$p} ) {
		$spec_int |= $rctrl_cmd_spec_intel_par{ $p };
		$spec = 0xC1;
	    }
	else {
	    print "  WARN: unknown parameter '$par', ignored.\n";
	    next
	}
	push @args, $par;
    }

    print "  '$cmd' ", ( @args ? "with '".join(' ', @args)."' " : "" );

    return -1  if( !get_user_confirm() );

    @args = ();
    push @args, SOAP::Data->name( 'Command' => $rctrl_cmd{$cmd} );
    push @args, SOAP::Data->name( 'IanaOemNumber' => "343" );

    if( $cmd ne "powerdown"  &&  defined($spec) ) {
	push @args, SOAP::Data->name( 'SpecialCommand' => $spec );
	push @args, SOAP::Data->name( 'SpecialCommandParameter' => $spec_int )
	  if( defined($spec) && $spec == 0xC1 && $spec_int > 0 );
	push @args, SOAP::Data->name( 'BootOptions' => $boot_opts )
	  if( $boot_opts );
	push @args, SOAP::Data->name( 'OEMparameters' => $rctrl_cmd_par_oem{"sol"} )
	  if( $sol && $spec == 0xC1 );
    }

    my $ret = do_soap( $rcs, "RemoteControl", @args );

    if( $run_sol  &&  !$ret ) {
	sleep 1;
	system( 'amtterm -q -u '.$main::amt_user." ".$amt_host." ".$amt_port );
    }
    return $ret;
}

#--------------------------------------------------------------------------------------
sub cmnd_rem_control_alarm {
    my $cmd = shift;
    my $set = shift;
    my $state = shift;
    my @params = @_;
    my ( @args, $ret );

    $state = ( defined($state) ? lc($state) : "" );

    if( lc($cmd) eq "help" ||
	defined($set) && ( lc($set) ne "set" || $state !~m/^on|off$/
	    || $state eq "on" && !@params || $state eq "off" && @params ) ) {
	print_usage_header();
	print STDERR "    rem_control alarm [set ( off | on <start_time> [<interval>] ) ]\n",
			"\t<start_time> format: [<year><month><day>-]<hour>:<minute>\n",
			"\t<interval> format: <days>[-<hour>:<minute>], <days> max 65535\n";
	return 0;
    }

    return -2  if( !check_amt_version(5,1, "  The AMT Wake-up Alarm doesn't supported" ));

$rcs->uri("$schemabase/RemoteControl/2009/01");

$rcs->SetAlarmWakeUp;

    if( ! defined($set) ) {
#	$ret = $rcs->uri("$schemabase/RemoteControl/2008/01")->GetAlarmWakeUp;
	$ret = $rcs->GetAlarmWakeUp;
	return $ret->result  if( print_err_result($ret) );

	print "  Wake-up Alarm: ";
	if( $ret->valueof('//State') == 0 ) {
	    print "Stopped";
	    return 0;
	}
	my $val = $ret->valueof('//NextTime');
	print "Armed\n    Next Time (Y/M/D):\t",
		$val->{'Date'}->{'Years'}, "/",
		$val->{'Date'}->{'Months'}, "/",
		$val->{'Date'}->{'Days'}, " ",
		$val->{'Time'}->{'Hours'}, ":",
		$val->{'Time'}->{'Minutes'}, "\n";

	if( $val = $ret->valueof('//Interval') ) {
	    print "    Interval (D/H:M):\t",
		$val->{'Date'}->{'Days'}, "/",
		$val->{'Time'}->{'Hours'}, ":",
		$val->{'Time'}->{'Minutes'}, "\n";
	}
	return 0;
    }

    print "  Set Wake-up Alarm to '", ucfirst($state), "' ";
    return -1 if( ! get_user_confirm() );

    push @args, SOAP::Data->name( "Command" => ($state eq "on" ? 1 : 0) );
#   push @args, SOAP::Data->name( "Command" => 0 )->uri("$schemabase/RemoteControl/2004/01");
#    push @args, SOAP::Data->name( "Command" => "ClearAlarmWake" );
    return do_soap( $rcs, "SetAlarmWakeUp", @args );
}

#-------------------------------------------------------------------------------------
sub cmnd_rem_control {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = ( defined($cmd) ? lc($cmd) : "" );

    if( $cmd eq "help" || !@params ) {
	print_usage_header();
	$cmd = "help";
	@params = ( "help" );
    } else {
	print "## '$amt_host' :: AMT Remote Control\n";
    }

    if( $cmd =~ m/^info|help$/ ) {
	$ret = cmnd_rem_control_info( @params );
    }
    if( $cmd =~ m/^reset|power(up|down|cycle)|setbootopt|help$/ ) {
	$ret = cmnd_rem_control_power( @params );
    }
    if( $cmd =~ m/^alarm|helpp$/ ) {
	$ret = cmnd_rem_control_alarm( @params );
    }

    print_newline( $cmd );
    return $ret  if( $ret >= -2);

    print "  Unknown command: rem_control $cmd\n";
    return -4;
}
#======================================================================================


#-------------------------------------------------------------------------------------
sub cmnd_rem_access_uiface {
    my ( $cmd, $if, $set, $state ) = @_;
    my %if_type_code = ( "bios" => 0, "os" => 1 );
    my %if_type_desc = ( 0 => "bios", 1 => "os" );
    my ( $ret, $arg );

    $if = ( defined($if) ? lc($if) : "" );

    if( lc($cmd) eq "help" || $if !~ m/^bios|os|all$/ ) {
	print_usage_header();
	print STDERR "    rem_access uiface (bios|os|all) [set (on|off)]\n",
		"\tget/set status for user initiated interfaces\n";
	return 0;
    }

    if( !$set ) {
	print "  User Initiated Interface State:\n";

	foreach my $i ( $if eq "all" ? sort keys %if_type_desc : $if_type_code{ $if } ) {
	    $arg = SOAP::Data->name( "AAAAAAASource" => $i );
#$ras->EnumerateRemoteAccessPolicies;
	    $ret = $ras->GetUserInitiatedInterfaceConfiguration;
#	    $ret = $ras->GetUserInitiatedInterfaceConfiguration( $arg );
#	    $ret = $ras->GetUserInitiatedInterface( $arg );
	    return $ret->result  if( print_err_result($ret) );

	    print "    ", uc( $if_type_desc{ $i } ), "\t", $ret->valueof('//IsEnabled'), "\n";
	}
    }

    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_rem_access {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = lc($cmd);

    if( $cmd eq "help" || !@params ) {
	print_usage_header();
	$cmd = "help";
	@params = ( "help" );
    } else {
	print "## '$amt_host' :: AMT Remote Access\n";
	return -2  if( !check_amt_version(4,0, "  Remote Access doesn't supported" ));
    }

    if( $cmd =~ m/^uiface|help$/ ) {
	$ret = cmnd_rem_access_uiface( @params );
    }

    print_newline( $cmd );
    return $ret  if( $ret >= -2);

    print "  Unknown command: rem_access $cmd\n";
    return -4;
}
#======================================================================================

#--------------------------------------------------------------------------------------
sub cmnd_info_gen_ver_only()
{
    check_amt_version(2,0);
    print "$amt_version";
    return 0;
}

#--------------------------------------------------------------------------------------
sub cmnd_info_gen_ver()
{
    check_amt_version(2,0);
    print "  AMT version:  $amt_version\n";

    return  if( ! check_amt_version(2,0) );

    my @ret = $gis->GetCodeVersions()->paramsout;
    my ( $bios, $desc, $ver );
    print "  Code Versions:\n";

    foreach my $i (@ret) {
	if( ref($i) ne "HASH" ) {
	    $bios = $i;
	    next;
	}
	$desc = $i->{'Description'};
	$ver = $i->{'Version'};
	printf "    %-18s\t", $desc.":";

	if( lc($desc) ne "sku" ) {
	    print $ver, "\n";
	    next;
	}

	if( !check_amt_version(2,5) ) {
		print "", ($ver == 0 ? "AMT + ASF + QST SKU" : ""),
			($ver == 1 ? "ASF + FSC SKU" : ""),
			($ver == 2 ? "FSC SKU" : ""), "\n";
	} else {
		print "", ($ver & 0x2 ? "iQST " : ""),
			($ver & 0x4 ? "ASF " : ""),
			($ver & 0x8 ? "Intel_AMT_Pro " : "");
		print "", ($ver & 0x10 ? "Intel_Standard_Manageability " : ""),
			($ver & 0x20 ? "TPM " : ""),
			($ver & 0x40 ? "Reserved " : ""),
			($ver & 0x80 ? "Finger_Print_Sensor " : ""),
			($ver & 0x8000 ? "L3_Manageability_Upgrade" : "")
		  if( check_amt_version(5,0) );
		print "\n";
	}
    }
    print "  BIOS version:  \t$bios\n";
}

#--------------------------------------------------------------------------------------
sub cmnd_info_gen_provis()
{
    my $ret = $gis->GetProvisioningMode()->paramsout;
    print "  Provisioning mode:  ", $provisioning_codes{$ret};

    if( check_amt_version(2,0) ) {
	$ret = $gis->GetProvisioningState()->paramsout;
	print "\n  Provisioning state:  ", $provis_state{$ret};

	$ret = $gis->GetConfigServerInfo();
	print "\n  Config Server (IP:port):  ",
		$ret->valueof('//Ip'), ":", $ret->valueof('//Port');
    }
}

#--------------------------------------------------------------------------------------
sub cmnd_info_gen_net() {
    my $ret = $gis->GetVlanParameters();
    print "\n  VLan",
	($ret->valueof('//VlanMode') eq "true" ? " Tag:  ".$ret->valueof('//VlanTag') : ":  Disabled");

    if( check_amt_version(2,0) )
    {
	$ret = $gis->GetNetworkState()->paramsout;
	print "\n  Network State:  ", (lc($ret) eq "true" ? "Enabled" : "Disabled");

	my @ret = $gis->GetEnabledInterfaces()->paramsout;
	print "\n  Enabled Interfaces:  ". join(", ", @ret);
    }
}

#--------------------------------------------------------------------------------------
sub cmnd_info_gen_passwd()
{
    return  if( !check_amt_version(2,0) );

    my $ret = $gis->GetPasswordModel()->paramsout;
    print "\n  Password model:  ", $pwd_model{$ret};

    $ret = $gis->GetAdminAclEntryStatus()->paramsout;
    print "\n  Local 'admin' password:  ",
	    ( $ret eq "true" ? "never changed" : "changed from default");

    $ret = $gis->GetAdminNetAclEntryStatus()->paramsout;
    print "\n  Remote 'admin' password:  ",
	    ( $ret eq "true" ? "never changed" : "changed from default");
}

#--------------------------------------------------------------------------------------
sub cmnd_info_gen_security()
{
    return  if( !check_amt_version(2,0) );

    my @ret = $gis->GetSecurityParameters()->paramsout;
    print "\n  Security parameters:",
	"\n    Enterprise Mode:  ", ucfirst $ret[0],
	"\n    TLS Enabled:  ", ucfirst $ret[1],
	"\n    HwCrypto Enabled:  ", ucfirst $ret[2],
	"\n    Provisioning State:  ", $provis_state{$ret[3]},
	"\n    Network Interface Enabled:  ", ucfirst $ret[4],
	"\n    SOL Enabled:  ", ucfirst $ret[5],
	"\n    IDER Enabled:  ", ucfirst $ret[6],
	"\n    FWUpdate Enabled:  ", ucfirst $ret[7],
	"\n    Link Is Up:  ", ucfirst $ret[8];
}

#--------------------------------------------------------------------------------------
sub cmnd_info_gen_ider()
{
    return  if( !check_amt_version(2,0) );

    my @ret = $gis->GetIderSessionLog()->paramsout;
    print "\n  IDE-R Sessions Log:", (@ret ? "" : "  Empty"), "\n";
    my $tz = tz_local_offset();

    foreach my $item (@ret) {
	print_date_time( $item->{'TimeStamp'}-$tz, 3 );
	print " - ", conv_ipv4_to_txt( $item->{'ConsoleAddress'} ),
		":", $item->{'Port'}, "\n";
    }
}

#--------------------------------------------------------------------------------------
sub cmnd_info_gen_me()
{
    return  if( ! check_amt_version(5,0) );

    my @ret = $gis->GetMeCapabilities()->valueof('//Data');
    print "  ME Capabilities:\n"
      if( @ret);

    foreach my $i ( @ret ) {
	printf "    %-15s\t%s\t(ver.: %d.%d)\n", $me_capabilities{ $i->{'Capability'} },
		( $i->{'State'} ? "Enabled " : "Disabled"),
		$i->{'Version'}->{'Major'}, $i->{'Version'}->{'Minor'};
    }
}

#--------------------------------------------------------------------------------------
sub cmnd_info_gen_uuid()
{
    return  if( ! check_amt_version(4,0) );

    print "  System UUID:  ";
    print_uuid  $gis->GetUuid()->valueof('//UUID');
    print "\n";
}

#--------------------------------------------------------------------------------------
sub cmnd_info_general {
    my ( $cmd, $sub_cmd ) = @_;

    $cmd = ( defined($cmd) ? lc($cmd) : "" );
    $sub_cmd = ( defined($sub_cmd) ? lc($sub_cmd) : "" );

    if( $cmd eq "help" || $sub_cmd eq "help"
	|| $sub_cmd !~ /^(ver|ver_only|provis|net|passwd|security|ider|me|uuid)?$/ ) {
	print_usage_header();
	print STDERR "    info gen[eral] [<command>]\n",
	    "\t<command> - (ver|ver_only|provis|net|passwd|security|ider|me|uuid)\n",
	    "\tall info if <details> not given\n";
	return 0;
    }

    return cmnd_info_gen_ver_only   if( $sub_cmd eq "ver_only" );

    my $ret = $gis->GetHostName()->paramsout;
    print "# General Info :: AMT General Info\n  Hostname:  $ret\n";

    if( $sub_cmd =~ m/^(ver)?$/ ) {
	$ret = cmnd_info_gen_ver;  }
    if( $sub_cmd =~ m/^(provis)?$/ ) {
	$ret = cmnd_info_gen_provis;  }
    if( $sub_cmd =~ m/^(net)?$/ ) {
	$ret = cmnd_info_gen_net;  }
    if( $sub_cmd =~ m/^(passwd)?$/ ) {
	$ret = cmnd_info_gen_passwd;  }
    if( $sub_cmd =~ m/^(security)?$/ ) {
	$ret = cmnd_info_gen_security;  }
    if( $sub_cmd =~ m/^(ider)?$/ ) {
	$ret = cmnd_info_gen_ider;  }
    if( $sub_cmd =~ m/^(me)?$/ ) {
	$ret = cmnd_info_gen_me;  }
    if( $sub_cmd =~ m/^(uuid)?$/ ) {
	$ret = cmnd_info_gen_uuid;
    }
    return $ret;
}

#-----------------------------------------------------------------------------------------
sub cmnd_info_audit_policy {
    my (@ret, $ret, $cnt, $arg, $a_id, $e_id, $f_id, $app_id, $n);

    $ret = $gis->GetAuditPolicy;
    return $ret->result  if( print_err_result($ret) );

    @ret = $ret->paramsout->{'AuditPolicyRecords'};

    print "  Audit Policy:\n    ";
    $cnt = scalar(@{$ret[0]}) - 1;

    # log header
    foreach my $item (sort keys %{$ret[0][0]}) {
	$n = 0;
	if( $item =~ m/AppId/i ) {
	    $a_id = $item;
	    $n = 25;
	} elsif( $item =~ m/EventId/i ) {
	    $e_id = $item;
	    $n = 38;
	} elsif( $item =~ m/flag/i ) {
	    $f_id = $item;
	}
	printf "%-*s".($n ? "\t" : ""), ($param_num ? 4 : $n), $item;
    }
    print "\n";

    # log rows
    for my $i ( 0 .. $cnt ) {
	print "   ";
	$n = 0;
	foreach my $item (sort keys %{$ret[0][$i]}) {
	    my $val = $ret[0][$i]->{$item};
	    if( $param_num ) {
		print "", ($n ? "\t" : "     "), $val;
		$n = 1;
		next;
	    }
	    if( $a_id eq $item  &&  defined($auditable_app_id{$val}) ) {
		printf "  %-24s\t", $auditable_app_id{$val};
		$app_id= $val;
		next;
	    }
	    if( $e_id eq $item  &&  defined($audit_event_id{$app_id}{$val}) ) {
		printf "%-38s", $audit_event_id{$app_id}{$val};
		next;
	    }
	    if( $f_id eq $item ) {
		print "\t",( $val == 0 ? "-" : "" ),
			   ( $val == 1 ? "Critical" : "" ),
			   ( $val >  1 ? "Unknown [$val]" : "" );
		next;
	    }
	    print "\t$val";
	}
	print "\n";
    }
}

#-----------------------------------------------------------------------------------------
sub cmnd_info_audit_log_status
{
    my $ret = $gis->GetAuditLogStatus();
    return $ret->result  if( print_err_result($ret) );

    my @ret = $ret->paramsout;
    print "  Audit Log Status:\n",
	"    PercentageFree:\t", $ret[0],
	"\n    NumberOfRecords:\t", $ret[1],
	"\n    TimeOfLastRecord:\t";
    print_date_time( $ret[2]-tz_local_offset(), 0 );
    print "\n    State:\t\t",
	($ret[3] & 0x01 ? "Disabled " : ""),
	($ret[3] & 0x02 ? "Locked " : ""),
	($ret[3] & 0x04 ? "Almost_Full " : ""),
	($ret[3] & 0x08 ? "FULL! " : ""),
	($ret[3] & 0x10 ? "NoKey" : ""),
	($ret[3] ==   0 ? "Enabled" : "");
    print "\n    MaxAllowedAuditors:\t", $ret[4], "\n";
}

#-----------------------------------------------------------------------------------------
sub cmnd_info_audit {
    my ( $cmd, $sub_cmd ) = @_;
    my $ret;

    $cmd = ( defined($cmd) ? lc($cmd) : "" );
    $sub_cmd = ( defined($sub_cmd) ? lc($sub_cmd) : "" );

    if( $cmd eq "help" || $sub_cmd eq "help"
	|| $sub_cmd !~ /^(log_status|policy)?$/ ) {
	print STDERR "    info audit [help|policy|log_status]\n",
		"\tall the info if the parameter not given\n",
		"\tGlobal parameter '--num[eric]|-n' forces numeric ID codes output\n";
	return 0;
    }

    return -2  if( !check_amt_version(4,0, ($cmd eq 'audit' ? "  Audit doesn't supported" : "") ));

    print "# Audit Info :: AMT General Info\n";

    if( $sub_cmd =~ m/^(policy)?$/ ) {
	$ret = cmnd_info_audit_policy; }
    if( $sub_cmd =~ m/^(log_status)?$/ ) {
	$ret = cmnd_info_audit_log_status;
    }
    return $ret;
}

#--------------------------------------------------------------------------------------
sub cmnd_info {
    my @params = @_;
    my $cmd = shift;

    $cmd = ( defined($cmd) ? lc($cmd) : "" );

    if( $cmd eq "help" || $cmd !~ m/^((gen(eral)?)|(rem(ote)?)|(aud(it)?))?$/ ) {
	print_usage_header();
	print STDERR "    info [<iface> [<details>]]\n",
	    "\t<iface> - (gen[eral]|rem[ote]|aud[it]), all info if omited\n",
	    "\t<details> - use 'info <iface> help' command\n\n";
	return 0;
    }
    print "## '$amt_host'\n"
      if( defined($params[1]) && lc( $params[1] ) ne "ver_only" );

    if( $cmd =~ m/^(gen(eral)?)?$/ ) {
	cmnd_info_general( @params ); }
    if( $cmd =~ m/^(aud(it)?)?$/ ) {
	cmnd_info_audit( @params ); }
    if( $cmd =~ m/^(rem(ote)?)?$/ ) {
	cmnd_rem_control_info;
    }
    print_newline( $cmd );
    return 0;
}
#======================================================================================


#=====================================================================================
sub cmnd_time {
    my $cmd = shift;
    my @params = @_;

    if( defined($cmd) &&  lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    time [set]\n\tget time from AMT device, with 'set' sync AMT-dev time with local\n\n";
	return 0;
    }

    print "## Host '$amt_host' :: AMT ";

    my ($ta0, $tm1, $tm2, $ret );
    my $tz = tz_local_offset();

    if( check_amt_version(2,0) ) {
	print "Network Time\n";
	$ta0 = $nts->GetLowAccuracyTimeSynch()->valueof('//Ta0');
    } else {
	print "Event Manager\n";
	$ta0 = $ems->GetEventLogTimestampClock()->valueof('//Time');;
    }
    $tm1 = time();

    print "  Time on the Intel AMT device: ";
    print_date_time $ta0-$tz, 0;

    print "\n  Time on the local machine:    ";
    print_date_time $tm1, 0;
    print "\n\n";

    return 0 if( !defined($cmd) || $cmd ne "set");

    print "Are you sure to synchronize AMT time with the local";
    return -1 if !get_user_confirm();

    print "Syncing: ";
    my @args;

    if( check_amt_version(2,0)) {
	push  @args, SOAP::Data->name('Ta0' => $ta0);
	push  @args, SOAP::Data->name('Tm1' => $tm1+$tz);
	$tm2 = time()+$tz;
	push  @args, SOAP::Data->name('Tm2' => $tm2);
	$ret = do_soap($nts, "SetHighAccuracyTimeSynch", @args);
    } else {
	$tm2 = time();
	push  @args, SOAP::Data->name('Time' => $tm2);
	$ret = do_soap($ems, "SetEventLogTimestampClock", @args);
    }
    return $ret;
}

#=====================================================================================
sub cmnd_net_info {
    my $cmd = shift;
    my ($arg, $ret, @if, %v);

    if( lc($cmd) eq "help") {
	print_usage_header();
	print STDERR "    net info\n";
	return 0;
    }

    if( check_amt_version(2,5) )
    {
	$ret = $nas->EnumerateInterfaces;
	@if = $ret->paramsout;

	foreach my $if( @if) {
	    $arg = SOAP::Data->name('InterfaceHandle' => $if );
	    $ret = $nas->GetInterfaceSettings($arg);

	    printf "  Network Interface Handle%4s\n", $if;

	    %v = %{ $ret->paramsout };
	    printf "%*s%-*s ", 4, "", 24, "Description";
	    printf $v{'HardwareAddressDescription'} if( defined( $v{'HardwareAddressDescription'} ));
	    printf $v{'HardwareDescription'} if( defined( $v{'HardwareDescription'} ));
	    printf "\n%*s%-*s %s\n", 4, "", 24, "MAC Address", $v{'MACAddress'};
	    printf "%*s%-*s %s\n", 4, "", 24, "Interface Mode", $v{'InterfaceMode'};

	    my $pol = $v{'LinkPolicy'};
	    printf "%*s%-*s %s ", 4, "", 24, "Link Policy", $pol;
	    if( $pol > 0 ) {
		my $s0 = ($pol & 0x01) || ($pol & 0x10);
		my $sx = ($pol & 0x0E) || ($pol & 0xE0);
		my $ac = ($pol & 0x01) || ($pol & 0x0E);
		my $dc = ($pol & 0x10) || ($pol & 0xE0);
		printf "(link operational if ME is, ".
			"power state %s%s%s on %s%s%s)\n", 
			( $s0 ) ? "S0" : "",
			( $s0 && $sx ) ? "/" : "",
			( $sx ) ? "Sx" : "",
			( $ac ) ? "AC" : "",
			( $ac && $dc ) ? "/" : "",
			( $dc ) ? "DC" : "";
	    }

	    printf "%4s%-24s %s\n", "", "DHCP",
			(($v{'DhcpEnabled'} eq "false") ? "Disabled" : "Enabled");

	    %v = %{ $ret->paramsout->{'IPv4Parameters'} };
	    printf "%7s%-21s %s\n", "", "IP/Mask",
			conv_ipv4_to_txt( $v{'LocalAddress'} )." / ".
			conv_ipv4_to_txt( $v{'SubnetMask'} );
	    printf "%7s%-21s %s\n", "", "Gateway",
			conv_ipv4_to_txt($v{'DefaultGatewayAddress'} );
	    printf "%7s%-21s %s\n", "", "DNS",
			conv_ipv4_to_txt( $v{'PrimaryDnsAddress'} )." ".
			conv_ipv4_to_txt( $v{'SecondaryDnsAddress'} );
	}
    } else {
	# AMT prior to v. 2.5+
	$ret = $nas->GetTcpIpParameters();

	return $ret->result  if( print_err_result($ret) );

	@if = $ret->paramsout;
	%v = %{$if[1]};
	printf "  Network Interface%-10sWired0\n", 10, "";
	printf "%4s%-24s %s\n", "", "DHCP", $net_dhcp_modes{ $if[0] };

	printf "%7s%-21s %s\n", "", "IP/Mask",
		conv_ipv4_to_txt( $v{'LocalAddress'} )." / ".
		conv_ipv4_to_txt( $v{'SubnetMask'} );
	printf "%7s%-21s %s\n", "", "Gateway",
		conv_ipv4_to_txt($v{'DefaultGatewayAddress'} );
	printf "%7s%-21s %s\n", "", "DNS",
		conv_ipv4_to_txt( $v{'PrimaryDnsAddress'} )." ".
		conv_ipv4_to_txt( $v{'SecondaryDnsAddress'} );
#	printf "%*s%-*s %s\n", 7, "", 21, "DomainName", $v{'DomainName'};
    }
    return 0;
}

#--------------------------------------------------------------------------------------
sub ipv4_addr($$) {
    my ( $name, $ipv4 ) = @_;

    $ipv4 =~ m/(\d+).(\d+).(\d+).(\d+)/ or die "parse ipv4 address: $ipv4";
    my $num = $1 * 256 * 256 * 256 +
		$2 * 256 * 256 +
		$3 * 256 +
		$4;
    printf STDERR "ipv4 %-24s: %-16s -> %u\n", $name, $ipv4, $num
	if( $amt_debug & 0x1 );
    return SOAP::Data->name( $name => $num );
}

#======================================================================================
sub cmnd_net_conf {
    my $cmd = shift;
    my @prms = @_;

    my $if   = shift;
    my $link = shift;
    my $ip   = shift;
    my $mask = shift;
    my $gw   = shift;
    my $dns1 = shift;
    my $dns2 = shift;

    my ($mode, $ret, @ifdesc, @ipv4, @args);

    $cmd = lc($cmd);

    if( $cmd eq "help" || !defined($if) || !defined($link) ||
			(defined($ip) && !defined($mask)) ) {
	print_usage_header();
	print STDERR "    net conf <if> <policy> [<ip> <mask> [<gw> [<dns1> [<dns2>]]]]\n";
	
	if( $cmd ne "help" ) {
	    print STDERR "\t<if> - the interface handle (any digit for AMT prior to v. 2.5+)\n".
	    "\t<policy> - the link policy (any digit for AMT prior to v. 2.5+)\n".
	    "\t<ip> <mask> - the IPv4 address\n\n".
	    "\tWhen no IP configuration is specified the tool tries to configure AMT\n".
	    "\tin shared_mac_address mode with dhcp, otherwise\n".
	    "\tin separate_mac_address mode with static IP address.\n\n".
	    "\tDefaults: <dns1> - gateway address, <dns2> - 0.0.0.0.\n\n";
	} else {
	    print STDERR "\t'net conf' for details\n";
	}
	return 0;
    }

    # build common arguments struct
    if( defined($ip) ) {
	$mode = "SEPARATE_MAC_ADDRESS";
	$gw   = "0.0.0.0" if !defined($gw);
	$dns1 = $gw       if !defined($dns1);
	$dns2 = "0.0.0.0" if !defined($dns2);
	push @ipv4, ipv4_addr("LocalAddress", $ip);
	push @ipv4, ipv4_addr("SubnetMask", $mask);
	push @ipv4, ipv4_addr("DefaultGatewayAddress", $gw);
	push @ipv4, ipv4_addr("PrimaryDnsAddress", $dns1);
	push @ipv4, ipv4_addr("SecondaryDnsAddress", $dns2);
    } else {
	$mode = "SHARED_MAC_ADDRESS";
	# no ip info -- use DHCP
    }

    print "  Setting IP configuration: ";

    return -1  if( !get_user_confirm() );

    if( check_amt_version(2,5) )
    {
	push @ifdesc, SOAP::Data->name( 'InterfaceMode' => $mode);
	push @ifdesc, SOAP::Data->name( 'LinkPolicy'    => $link);
	push @ifdesc, SOAP::Data->name( 'IPv4Parameters' => \SOAP::Data->value(@ipv4) )
	  if @ipv4;

	push @args, SOAP::Data->name( 'InterfaceHandle' => $if);
	push @args, SOAP::Data->name( 'InterfaceDescriptor' => \SOAP::Data->value(@ifdesc) );

	$ret = $nas->SetInterfaceSettings( @args );
    } else {
	# AMT prior v. 2.5
	# DomainName in StaticIPv4Parameters
	if( @ipv4 )
	{
	    push @args, SOAP::Data->name("DhcpMode" => $net_dhcp_modes_code{'DhcpModeDisabled'});
	    push @ipv4, SOAP::Data->name("DomainName", $nas->GetDomainName()->paramsout );
	    push @args, SOAP::Data->name("StaticIPv4Parameters" => \SOAP::Data->value(@ipv4));
	} else {
	    push @args, SOAP::Data->name("DhcpMode" => $net_dhcp_modes_code{'DhcpModeEnabled'});
	}
	$ret = $nas->SetTcpIpParameters( @args );
    }
    print_result $ret;
    print_commit_warn $ret;
    return $ret->result;
}

#-------------------------------------------------------------------------------------
sub cmnd_net_ping {
    my ( $cmd, $state ) = @_;
    my ( $arg, $ret );

    $cmd = lc($cmd);

    if( $cmd eq "help" || ( defined($state) && $state !~ m/^(on|off)$/i) ) {
	print_usage_header();
	print STDERR "    net ping [on|off]\n";
	return 0;
    }

    print "  Ping response";

    if( !$state ) {
	$ret = $nas->GetPingResponse()->valueof('//enabled');
	print "  ", (($ret eq "false") ? "Disabled" : "Enabled"), "\n";
	return 0;
    }

    print " set to ", ($state eq "on" ? "Enabled" : "Disabled"), ": ";

    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'enabled' => ($state eq "on" ? 1 : 0) );
    return do_soap( $nas, "SetPingResponse", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_net_hostname {
    my ( $cmd, $name ) = @_;
    my ( $arg, $ret );

    $cmd = lc($cmd);

    if( $cmd eq "help") {
	print_usage_header();
	print STDERR "    net host[name] <host_name>\n";
	return 0;
    }

    print "  Hostname";

    if( ! $name ) {
	$ret = $nas->GetHostName()->valueof('//HostName');
	printf ":  $ret\n\n";
	return 0;
    }

    if( $name !~ m/^[a-z0-9_-]+$/i ) {
	printf "\n  Error. Wrong hostname: '$name'\n";
	return -2;
    }

    print "  set to  '$name'";

    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'HostName' => $name );
    $ret = $nas->SetHostName( $arg );

    print_result $ret ;
    print_commit_warn $ret;
    return $ret->result;
}

#-------------------------------------------------------------------------------------
sub cmnd_net_domain_name {
    my ( $cmd, $name ) = @_;
    my ( $arg, $ret );

    $cmd = lc($cmd);

    if( $cmd eq "help") {
	print_usage_header();
	print STDERR "    net domain[name] (<domain_name>|-)\n";
	return 0;
    }

    print "  Domainname";

    if( ! $name ) {
	$ret = $nas->GetDomainName()->valueof('//DomainName');
	print ": ", ($ret ? "not defined." : $ret), "\n";
	return 0;
    }

    if( $name !~ m/^([a-z0-9_-]+\.)*[a-z0-9_-]+$/i ) {
	print "\n  Error. Wrong domain name: $name\n";
	return -2;
    }

    if( $name eq "-" ) {
	$name = "";
	print " reset to empty string: ";
    }else {
	print " set to '$name': ";
    }

    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'DomainName' => $name );
    $ret = $nas->SetDomainName( $arg );

    print_result $ret;
    print_commit_warn $ret;
    return $ret->result;
}

#-------------------------------------------------------------------------------------
sub cmnd_net_vlan {
    my ( $cmd, $p1, $p2, $p3 ) = @_;
    my ( @arg, $ret, $tag );

    $cmd = lc($cmd);

    if( $cmd eq "help" || defined($p1) &&
	  ( ( $p1 ne "set" && $p1 !~ m/^[0-9]+$/ ) ||
	    ( $p1 eq "set" && (!defined($p2) || $p2 !~ m/^[0-9]+$/)) ||
	    ( defined($p2) && ( ( $p2 ne "set" && $p2 !~ m/^[0-9]+$/ ) ||
		$p2 eq "set" && (!defined($p3) || $p3 !~ m/^[0-9]+$/)) )
	  ) ) {
	print_usage_header();
	print STDERR "    net vlan [<iface>] [set <vlan_tag>]\n",
		"\tNOT supported by AMT ver. 2.5, 2.6, 4.x, 6.x+\n",
		"\tsome AMT ver. hangs with or without <iface>\n",
		"\t<vlan_tag>=1-4095 to enable and 0 to disable\n";
	return 0;
    }

    if( (!defined($p1) || $p1 ne "set") && !defined($p2) ) {
	print "  V-LAN";
	if( defined($p1) ) {
	    print " for interface ", $p1;
	    my $arg = SOAP::Data->name( 'InterfaceHandle' => $p1 );
	    $ret = $nas->GetVlanParameters( $arg );
	} else {
	    $ret = $nas->GetVlanParameters( ); }
	print "", ($ret->valueof('//VlanMode') eq "true" ? " Tag: ".$ret->valueof('//VlanTag') : ": Disabled"),
	    "\n\n";
	return $ret->result;
    }

    $tag = ($p1 eq "set" ? $p2 : $p3);
    if( $tag < 0 || $tag > 4095 ) {
	print "  Error. VLAN Tag number can be 1-4095 and 0 to disable.\n\n";
	return -2;
    }

    print "  Set VLAN Tag #", $tag, ($p1 ne "set" ? " for interface ".$p1 : ""), ": ";
    return -1  if( !get_user_confirm() );

    push @arg, SOAP::Data->name( 'InterfaceHandle' => $p1 ) if( $p1 ne "set" );
    push @arg, SOAP::Data->name( 'VlanMode' => $tag && 1 );
    push @arg, SOAP::Data->name( 'VlanTag'  => $tag );
    $ret = $nas->SetVlanParameters( @arg );

    print_result $ret;
    print_commit_warn $ret;
    return $ret->result;
}

#--------------------------------------------------------------------------------------
sub cmnd_net {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = lc($cmd);

    if( $cmd eq "help" || !@params ) {
	print_usage_header();
	$cmd = "help";
	@params = ( $cmd );
    } else {
	if( $cmd =~ m/^(commit(changes)?)$/ ) {
	    $ret = cmnd_security_commit(@params);
	} else {
	    printf "## '$amt_host' :: AMT Net Administration (PTAdministration realm)\n";
	}
    }

    if( $cmd =~ m/^(host(name)?|help)$/ ) {
	$ret = cmnd_net_hostname(@params);
    }
    if( $cmd =~ m/^(domain(name)?|help)$/ ) {
	$ret = cmnd_net_domain_name(@params);
    }
    if( $cmd =~ m/^(ping|help)$/ ) {
	$ret = cmnd_net_ping(@params);
    }
    if( $cmd =~ m/^(info|help)$/ ) {
	$ret = cmnd_net_info(@params);
    }
    if( $cmd =~ m/^(conf(ig)?|help)$/ ) {
	$ret = cmnd_net_conf(@params);
    }
    if( $cmd =~ m/^(vlan|help)$/ ) {
	$ret = cmnd_net_vlan(@params);
    }

    print_newline( $cmd );
    return $ret   if( $ret > -3);

    print " Unknown command: net $cmd\n";
    return -4;
}
#=====================================================================================


#-------------------------------------------------------------------------------------
sub get_user_handle($) {
    my $srch_name = shift;
    my ( @usr, $ret, $arg, $totl, $name );
    my $start = 1;

    return if( !defined($srch_name));

    do {
	$arg = SOAP::Data->name( 'StartIndex' => $start );
	$ret = $sas->EnumerateUserAclEntries( $arg );
	return $ret->result  if( print_err_result($ret) );

	$totl = $ret->valueof('//TotalCount');
	return  if( $totl == 0  &&  $ret->valueof('//HandleCount') == 0 );

	if( $totl == 1 ) {
	    @usr = $ret->valueof('//Handles')->{'Handle'}
	} else {
	    @usr = @{ $ret->valueof('//Handles')->{'Handle'}} }

	foreach my $usr_hndl (@usr) {
	    $arg = SOAP::Data->name( 'Handle' => $usr_hndl );

	    if( check_amt_version(2,0) ) {
		# AMT v. 2.x+
		$ret = ( $sas eq $uas ) ? $sas->GetUserAclEntry( $arg ) : $sas->GetUserAclEntryEx( $arg );
		return  if( print_err_result($ret) );
		$ret = $ret->paramsout;

		if( defined($ret->{'DigestUser'}) ) {
		    $name = $ret->{'DigestUser'}->{'Username'};
		} else {
		    $name = decode_base64( $ret->{'KerberosUser'}->{'Sid'} );
		}
	    } else {
		# AMT 1.x
		$name = $sas->GetUserAclEntry( $arg )->paramsout->{'Username'};
	    }
	    return $usr_hndl   if( $srch_name eq $name );
	    $start++;
	}
    } while ( $totl > $start );
}

#-------------------------------------------------------------------------------------
sub create_realms_arr( @ ) {
    my @txt_realms = @_;
    my @realms;

    foreach my $i ( @txt_realms ) {
	if( defined( $user_acl_realm_codes{ lc($i) } )) {
	    push @realms, SOAP::Data->name( "Realm" =>
			$user_acl_realm_codes{ lc($i) } )
			->uri("$schemabase/SecurityAdministration/2004/01");
	} elsif( $i =~ m/^[0-9]{1,2}$/ &&  $i > 0  &&  $i < scalar @user_acl_realm ) {
	    push @realms, SOAP::Data->name("Realms" => $i );
	} else {
	    print "  ERROR. Wrong realm '$i'. 'user realm' for the list.\n";
	    return;
	}
    }
    return @realms;
}

#-------------------------------------------------------------------------------------
sub print_user_details( $ ) {
    my $usr_hndl = shift;
    my ( $name, $perm, $ret, $val, @realms );

    my $arg = SOAP::Data->name( 'Handle' => $usr_hndl );

    if( check_amt_version(2,0) ) {
	# AMT v. 2.x+
	$ret = ( $sas eq $uas ) ? $sas->GetUserAclEntry( $arg ) : $sas->GetUserAclEntryEx( $arg );
	return $ret->result  if( print_err_result($ret) );
	$val = $ret->paramsout;

	if( defined($val->{'DigestUser'}) ) {
	    $name = $val->{'DigestUser'}->{'Username'};
	    printf "%4s%-22s", "", $name;
	} else {
	    printf "%4sKerbSID: %-18s", "",
			decode_base64($val->{'KerberosUser'}->{'Sid'});
	}
	if( ref($val->{'Realms'}) eq "HASH" ) {
	    if( ref( $val->{'Realms'}->{'Realm'} ) eq "ARRAY" ) {
		$perm = scalar @{$val->{'Realms'}->{'Realm'}};
		@realms = @{$val->{'Realms'}->{'Realm'}};
	    } else {
		$perm = 1;
		$realms[0] = $val->{'Realms'}->{'Realm'};
	    }
	} else {
	    $perm = 0;
	    @realms = ();
	}
	printf "%2d realm%-4s", $perm, (($perm == 1) ? "" : "s");
	printf "%-25s", $val->{'AccessPermission'};

	if( check_amt_version(3,0)  &&  $sas ne $uas ) {
	    $ret = $sas->GetAclEnabledState( $arg );
	    return $ret->result  if( print_err_result($ret) );

	    $val = $ret->valueof('//Enabled');
	    printf "%-9s", ( ($val eq "false") ? "Disabled" : "Enabled" );
	}
	if( $name =~ /^\$\$/ ) {
	    printf "<system account>";
	}
	print "\n";
    } else {
	# AMT 1.x
	$ret = $sas->GetUserAclEntry( $arg );
	print "   ", $ret->paramsout->{'Username'}, "\n";
    }
    return @realms;
}

#-------------------------------------------------------------------------------------
sub prep_user_acl_entry( @ ) {
    my $amt2   = shift;
    my $name   = shift;
    my $access = shift;
    my @realms_arr = @_;
    my @args;

    if( $amt2 ) {    # AMT 2.0+
	my @dguser;

	push @dguser, SOAP::Data->name( "Username" => $name );

	if( $sas ne $uas  ||  defined( $ENV{'AMT_NEW_PASSWORD'}) ) {
	    my $hash = md5_base64( $name, ":",
		$sas->GetDigestRealm()->paramsout, ":",
		$ENV{'AMT_NEW_PASSWORD'} );
	    push @dguser, SOAP::Data->name( "DigestPassword" => $hash );
	}

	push @args, SOAP::Data->name( "DigestUser" =>  \SOAP::Data->value( @dguser ) );
	if( $sas ne $uas ) {
	    $access = $acl_access_permission{$access}
	      if defined($acl_access_permission{$access});
	    push( @args, SOAP::Data->name( "AccessPermission" => $access ));
	}
    } else {      # AMT 1.x
	push @args, SOAP::Data->name( "Username" => $name );
	push @args, SOAP::Data->name( "Password" => $ENV{'AMT_NEW_PASSWORD'});
    }
    push @args, SOAP::Data->name("Realms" =>  \SOAP::Data->value( @realms_arr ) )
      if( @realms_arr );

    return @args;
}

#-------------------------------------------------------------------------------------
sub cmnd_user_add {
    my $cmd  = shift;
    my $name = shift;
    my @realms = @_;
    my ( @realms_arr, $access );

    if( lc($cmd) eq "help" || !defined($name) || !@realms) {
	print_usage_header();
	print STDERR "    user add <username> [<access>] <realms>\n".
		"\t<access> - (network|local|any) only for AMT 2.0+\n".
		"\t  defaults to 'network'\n".
		"\t<realms> - space separated list of realms\n".
		"\t  or their codes ('net realm' for the list)\n".
		"\tpassword is passed via AMT_NEW_PASSWORD environment variable\n";
	return 0;
    }

    my $amt2 = check_amt_version(2,0);

    if( $amt2 ) {
	$access = shift;
	$access = lc $access;
	if( defined($acl_access_permission{ $access }) ) {
	    $access = $acl_access_permission{ $access };
	    @realms = @_;
	} else {
	    $access = $acl_access_permission{ 'network' };
	}
    }

    if( !defined( $ENV{'AMT_NEW_PASSWORD'})  ) {
	print "  ERROR: No password via AMT_NEW_PASSWORD environment variable.\n";
	return $pt_status{"PT_STATUS_INVALID_PASSWORD"};
    }
    if( ! @realms ) {
	print "  ERROR: No realms.\n";
	return $pt_status{"PT_STATUS_INVALID_REALM"};
    }

    @realms_arr = create_realms_arr( @realms );
    return $pt_status{"PT_STATUS_INVALID_REALM"}
      if( ! @realms_arr );

    print "  Add new user '$name' ";
    return -1  if( ! get_user_confirm() );

    my @args = prep_user_acl_entry( $amt2, $name, $access, @realms_arr );

    return do_soap( $sas, "AddUserAclEntry".( $amt2 ? "Ex" : "" ),
		SOAP::Data->name( "Entry".( $amt2 ? "Ex" : "" ), \SOAP::Data->value(@args) ));
}

#-------------------------------------------------------------------------------------
sub cmnd_user_update {
    my $cmd    = shift;
    my $name   = shift;
    my @realms = @_;
    my ( @realms_arr, $access, $ret );

    if( $cmd eq "help" || !defined($name) ) {
	print_usage_header();
	print STDERR "    ",
		($sas ne $uas) ? "user upd[ate] <username> [<access>] [<realms>]\n".
		 "\tupdates user's password and, if specified access and/or realms\n"
		: "uuser upd[ate] <username> [<realms>]\n".
		 "\tsets a user's password if it given via the AMT_NEW_PASSWORD envir.var.\n".
		 "\t<realms> used to remove special realms (like audit)\n";
	return 0;
    }

    if( $sas ne $uas  &&  !defined( $ENV{'AMT_NEW_PASSWORD'}) ) {
	print "  ERROR: No password via AMT_NEW_PASSWORD environment variable.\n";
	return $pt_status{"PT_STATUS_INVALID_PASSWORD"};
    }

    my $amt2 = check_amt_version(2,0);
    if( @realms  &&  $amt2  &&  $sas ne $uas ) {
	$access = shift;
	$access = lc $access;
	@realms = @_
	    if( defined($access) &&
		defined( $acl_access_permission{$access} ));
    }

    my $hndl = get_user_handle($name);
    if( $hndl !~ m/[0-9]+/ ) {
	print " ERROR: Unknown username '$name'.\n";
	return $pt_status{"PT_STATUS_INVALID_NAME"};
    }

    if( $sas ne $uas  &&
	  ( ! @realms  ||  $amt2 && !defined( $acl_access_permission{$access}) )) {
	# get values of unchanged parameters
	my $arg = SOAP::Data->name( 'Handle' => $hndl );

	if( $amt2 ) {    # AMT v. 2.x+
	    $ret = $sas->GetUserAclEntryEx( $arg );
	    return $ret->result  if( print_err_result($ret) );
	    $ret = $ret->paramsout;

	    $access = $ret->{'AccessPermission'}
	      if( !defined($access) || !defined( $acl_access_permission{$access}) );
	} else { 
	    # AMT 1.x
	    $ret = $sas->GetUserAclEntry( $arg );
	    return $ret->result  if( print_err_result($ret) );
	    $ret = $ret->paramsout;
	}
    }

    if( @realms ) {
	    @realms_arr = create_realms_arr( @realms );
	    return $pt_status{"PT_STATUS_INVALID_REALM"}
	      if( ! @realms_arr );
    } elsif( $sas ne $uas ) {
	foreach my $i ( $ret->{'Realms'}->{'Realm'} ) {
	    push  @realms_arr,  SOAP::Data->name( "Realm" => $i );
	}
    }

    print "  Update user '$name' ";
    return -1  if( !get_user_confirm() );

    my @args;
    my @entry = prep_user_acl_entry( $amt2, $name, $access, @realms_arr );
    push @args, SOAP::Data->name( "Handle"  => $hndl );

    push @args, SOAP::Data->name( "Entry".( $amt2 && $sas ne $uas ? "Ex" : "" ),
			\SOAP::Data->value(@entry) );
    return do_soap( $sas, "UpdateUserAclEntry".( $amt2 && $sas ne $uas ? "Ex" : "" ), @args );
}

#-------------------------------------------------------------------------------------
sub cmnd_user_list {
    my $cmd = shift;
    my ( $totl, $perm, $name );
    my $start = 1;

    if( lc($cmd) eq "help") {
	print_usage_header();
	print STDERR "    ", ($sas ne $uas ? "" : "u" ), "user list\n".
		"\tprints users list and their info (except Primary Admin)\n";
	return 0;
    }

    do {
	my $arg = SOAP::Data->name( 'StartIndex' => $start );
	my $ret = $sas->EnumerateUserAclEntries( $arg );
	return $ret->result  if( print_err_result($ret) );

	$totl = $ret->valueof('//TotalCount');

	print "  Registered $totl AMT user account",
		($totl == 1 ? "" : "s"), ($totl ? ":" : "."), "\n"
	  if( $start == 1 );

	return 0  if( $totl == 0  &&  $ret->valueof('//HandleCount') == 0 );

	my @arr = $totl == 1 ? $ret->valueof('//Handles')->{'Handle'} : @{ $ret->valueof('//Handles')->{'Handle'} };
	foreach my $usr_hndl ( @arr ) {
	    print_user_details( $usr_hndl );
	    $start++;
	}
    } while( $totl > $start );
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_user_info {
    my $cmd  = shift;
    my $name = shift;

    if( lc($cmd) eq "help" || !defined($name) ) {
	print_usage_header();
	print STDERR "    ", ($sas ne $uas ? "" : "u" ), "user info <username>\n";
	return 0;
    }

    my $hndl = get_user_handle($name);

    if( $hndl !~ m/[0-9]+/ ) {
	printf " Error: unknown username '%s'.\n", $name;
	return $pt_status{"PT_STATUS_INVALID_NAME"};
    }

    print "  User '$name' detailed info:\n";

    my @realms = print_user_details( $hndl );
    return 0  if( ! @realms );

    print "    Realms: ";
    do {
	my $i = pop @realms;
	print $user_acl_realm{$i}, (@realms ? ", " : "\n");
    } while( @realms );

    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_user_admin {
    my $cmd = shift;
    my $ret;

    if( lc($cmd) eq "help") {
	print_usage_header();
	print STDERR "    user admin\n".
		"\tprints Primary Admin username\n";
	return 0;
    }

    $ret = $sas->GetAdminAclEntry();
    return $ret->result  if( print_err_result($ret) );

    print "  Primary Admin username: ", $ret->paramsout, "\n";
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_user_adminupd {
    my $cmd  = shift;
    my $name = shift;
    my (@args, $arg, $ret);

    $name = $main::amt_user if( !defined($name));

    if( lc($cmd) eq "help" || $name eq "help" ||
	    !defined($ENV{'AMT_NEW_PASSWORD'}) ) {
	print_usage_header();
	print STDERR "    user adminupd[ate] [<username>]\n".
		"\tchange Primary Admin password and/or username\n".
		"\tpassword is passed via AMT_NEW_PASSWORD environment variable\n";
	return 0;
    }

    print "  Change the PrimaryAdmin's ",
		( ($name ne $main::amt_user) ? "username and " : "" ),
		 "password: ";

    return -1 if( ! get_user_confirm() );

    push @args, SOAP::Data->name("Username" => $name );

    if( check_amt_version(2,0) ) {
	# AMT 2.0+
	my $hash = md5_base64( $name, ":",
		$sas->GetDigestRealm()->paramsout, ":",
		$ENV{'AMT_NEW_PASSWORD'} );

	push @args, SOAP::Data->name( "DigestPassword" => $hash );

	return do_soap($sas, "SetAdminAclEntryEx",
			SOAP::Data->name( "EntryEx", \SOAP::Data->value(@args) ));
    } else {
	# AMT 1.x
	push @args, SOAP::Data->name( "Password" => $ENV{'AMT_NEW_PASSWORD'});

	return do_soap($sas, "SetAdminAclEntry",
			SOAP::Data->name( "Entry", \SOAP::Data->value(@args) ));
    }
}

#-------------------------------------------------------------------------------------
sub cmnd_user_enable {
    my $cmd  = shift;
    my $name = shift;
    my ( @args, $ret );

    $cmd = lc($cmd);

    if( $cmd eq "help" ) {
	print_usage_header();
	print STDERR "    user (en|dis)[able] <username>\n";
	return 0;
    }

    return -2  if( !check_amt_version(3,0, " Enable/Disable a User ACL feature is supported in iAMT v. 3.0+") );

    my $hndl = get_user_handle($name);

    if( $hndl !~ m/[0-9]+/ ) {
	printf "  Error: unknown username '%s'.\n", $name;
	return $pt_status{"PT_STATUS_INVALID_NAME"};
    }

    my $enabled = ($cmd =~ m/^en(able)?$/) ? "true" : "false";
    print "  ", ($enabled eq "true" ? "En" : "Dis"), "able user '$name' ";
    return -1 if( ! get_user_confirm() );

    push @args, SOAP::Data->name("Handle"  => $hndl );
    push @args, SOAP::Data->name("Enabled" => $enabled );

    return do_soap( $sas, "SetAclEnabledState", @args );
}

#-------------------------------------------------------------------------------------
sub cmnd_user_del {
    my $cmd  = shift;
    my $name = shift;
    my ( $arg, $ret );

    $cmd = lc($cmd);

    if( $cmd eq "help" || !defined($name)) {
	print_usage_header();
	print STDERR "    user del <username>\n";
	return 0;
    }

    my $hndl = get_user_handle($name);

    if( $hndl !~ m/[0-9]+/ ) {
	print " Error: unknown username '$name'.\n";
	return $pt_status{"PT_STATUS_INVALID_NAME"};
    }

    print "  DELETE user '$name' ";
    return -1 if( ! get_user_confirm() );

    $arg = SOAP::Data->name("Handle" => $hndl);
    return do_soap( $sas, "RemoveUserAclEntry", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_user_realms {
    my $cmd = shift;

    if( lc($cmd) eq "help") {
	print_usage_header();
	print STDERR "    ", ($sas ne $uas ? "" : "u" ), "user realm[list]\n",
		"\tprints the user Access Control List realms\n";
	return 0;
    }

    print "\n User Access Control List realms:";

    for my $i ( 0 .. scalar( @user_acl_realm )-1 ) {
	next if( !$i );
	printf "\n    %2d  ->  $user_acl_realm[$i]", $i;
	print "  -  ", $user_acl_realm_descr{$i}
	    if( defined($user_acl_realm_descr{$i}) );
    }
    print "\n";
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_user {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = lc($cmd);

    if( $cmd eq "help" || !@params ) {
	print_usage_header();
	$cmd = "help";
	@params = ( $cmd );
    } else {
	print "## '$amt_host' :: AMT Security Administration (PTAdministration realm)\n";
    }

    if( $cmd =~ m/^(list|help)$/ ) {
	$ret = cmnd_user_list(@params);
    }
    if( $cmd =~ m/^(info|help)$/ ) {
	$ret = cmnd_user_info(@params);
    }
    if( $cmd =~ m/^(realm(list)?|help)$/ ) {
	$ret = cmnd_user_realms(@params);
    }
    if( $cmd =~ m/^(add|help)$/ ) {
	$ret = cmnd_user_add(@params);
    }
    if( $cmd =~ m/^(upd(ate)?|help)$/ ) {
	$ret = cmnd_user_update(@params);
    }
    if( $cmd =~ m/^(del|help)$/ ) {
	$ret = cmnd_user_del(@params);
    }
    if( $cmd =~ m/^((en|dis)(able)?|help)$/ ) {
	$ret = cmnd_user_enable(@params);
    }
    if( $cmd =~ m/^(admin|help)$/ ) {
	$ret = cmnd_user_admin(@params);
    }
    if( $cmd =~ m/^(adminupd(ate)?|help)$/ ) {
	$ret = cmnd_user_adminupd(@params);
    }

    print_newline( $cmd );
    return $ret  if( $ret >= -2);

    print " Unknown command: user $cmd\n";
    return -4;
}
#=====================================================================================

#-------------------------------------------------------------------------------------
sub cmnd_uuser {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = lc($cmd);

    if( $cmd eq "help" || !@params ) {
	print_usage_header();
	$cmd = "help";
	@params = ( $cmd );
    } else {
	return -22  if( !check_amt_version(4,0, "  User Access Control doesn't supported") );
	print "## '$amt_host' :: AMT User Access Control\n";
    }

    $sas = $uas;
    if( $cmd =~ m/^(list|help)$/ ) {
	$ret = cmnd_user_list(@params);
    }
    if( $cmd =~ m/^(info|help)$/ ) {
	$ret = cmnd_user_info(@params);
    }
    if( $cmd =~ m/^(upd(ate)?|help)$/ ) {
	$ret = cmnd_user_update(@params);
    }
    if( $cmd =~ m/^(realm(list)?|help)$/ ) {
	$ret = cmnd_user_realms(@params);
    }

    print_newline( $cmd );
    return $ret  if( $ret >= -2 );

    print " Unknown command: user $cmd\n";
    return -4;
}
#=====================================================================================


#-------------------------------------------------------------------------------------
sub cmnd_hwasset_types {
    my $cmd = shift;
    my (@assets, $ret, $cnt);

    $cmd = lc($cmd);
    if( $cmd eq "help") {
	print_usage_header();
	print STDERR "    hwasset [list]\n".
		"\tenumerates the asset types supported by the iAMT device\n";
	return 0;
    }

    $ret = $has->EnumerateAssetTypes();
    return $ret->result  if( print_err_result($ret) );

    $cnt  = ($ret->paramsout)[0];
    print "  Asset types supported ($cnt total):\n";

    return 0  if( !$cnt );

    @assets = ($cnt == 1) ? ($ret->paramsout)[1]->{'AssetType'} :
			@{ ($ret->paramsout)[1]->{'AssetType'} };

    foreach my $type ( @assets ) {
	if( !defined($hwasset_type_names{$type}) ) {
	    print "    unknown asset code ($type).\n";
	} else {
	    print "    ", $hwasset_type_names{$type}, "\n";
	}
    }
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print {
    my $cmd = shift;
    my @params = @_;
    my $all = 0;
    my (@ret, @data, $ret, $cnt, $arg, $type_code);

    $cmd = lc($cmd);

    if( $cmd eq "help") {
	print_usage_header();
	print STDERR "    hwasset data <asset_type>\n".
		"\tprints hardware asset data for a given asset type\n".
		"\t<asset_type> - case insensitive, supported by the device ('hwasset list') or 'All'\n";
	return 0;
    }

    if( lc($params[0]) eq "all" ) {
	$all = 1;
	@params = ();
	push @params, ( sort keys %hwasset_type_codes );
    }

    foreach my $type ( @params ) {

	$type_code = $hwasset_type_codes{ lc($type) };
	if( !defined( $type_code )) {
	    print " Unknown asset type '$type' given.\n";
	    next;
	}
	$type = $hwasset_type_names{ $type_code };

	$arg = SOAP::Data->name( 'AssetType' => $type_code );
	$ret = $has->GetAssetData( $arg );

	if( !defined($ret->result) ) {
	    print " Warning. Requested asset type '$type' is not supported.\n"
	      if( !$all );
	    next;
	}
	print " Data for the asset '$type' ";

	if( $ret->result != $pt_status{'PT_STATUS_SUCCESS'} ) {
	    if( $ret->result == $pt_status{'PT_STATUS_MAX_LIMIT_REACHED'} ) {
		print " -- Warning.  The platform contains additional asset data information that\n".
			"\twas not captured by the Intel AMT device due to container space limitation.\n";
		next;
	    }
	    elsif( $ret->result == $pt_status{'PT_STATUS_INVALID_PARAMETER'} ) {
		print " -- Warning. Asset type is not supported.\n"
		  if( !$all );
		next;
	    }
	    else {
		print_result($ret);
		return $ret->result;
	    }
	}
	$cnt = ($ret->paramsout)[0];
	if( $cnt ) {
	    print "($cnt item", ($cnt-1 ? "s" : ""), "):\n";
	} else {
	    print "is't supported by the device.\n\n";
	    next;
	}

	my $asset_data = (($ret->paramsout)[1])->{'AssetData'};
	foreach my $item ( ($cnt>1 ? @{ $asset_data} : $asset_data) ) {
	    if( $param_hex ) {
		my $t = $item->{'AssetSize'};
		print "  Data size, bytes: $t\n  Asset data:";
		foreach my $byte ( unpack "C[$t]", decode_base64( $item->{'AssetData'} )) {
		    printf " %02x", $byte;
		}
		print "\n";
	    } else {
		cmnd_hwasset_print_bios( $item ) if( $hwasset_type_codes{'bios'} == $type_code );
		cmnd_hwasset_print_computer( $item ) if( $hwasset_type_codes{'computersystem'} == $type_code );
		cmnd_hwasset_print_baseboard( $item ) if( $hwasset_type_codes{'baseboard'} == $type_code );
		cmnd_hwasset_print_processor( $item ) if( $hwasset_type_codes{'processor'} == $type_code );
		cmnd_hwasset_print_memory( $item ) if( $hwasset_type_codes{'memorymodule'} == $type_code );
		cmnd_hwasset_print_fru( $item ) if( $hwasset_type_codes{'fru'} == $type_code );
		cmnd_hwasset_print_mediadevice( $item ) if( $hwasset_type_codes{'mediadevice'} == $type_code );
		cmnd_hwasset_print_port_batt( $item ) if( $hwasset_type_codes{'portablebattery'} == $type_code );
		cmnd_hwasset_print_vpro( $item ) if( $hwasset_type_codes{'vproverificationtable'} == $type_code );
		cmnd_hwasset_print_amt( $item ) if( $hwasset_type_codes{'amtinformation'} == $type_code );
	    }
	}
    }
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print_bios {
    my @data = @_;

    my ( $struct_ver, $vendor, $ver, $rel_date, $padd, $charact ) =
	unpack "L Z65 Z65 Z65 C L", decode_base64( $data[0]->{'AssetData'} );
    if( $padd != 0 ) {
	printf "   Error. Something's going wrong, received data has failed checking.\n";
	return;
    }

    printf "  (data struct.ver. %d.%d)\n", $struct_ver & 0xFFFF, $struct_ver >> 16;
    print "   Vendor:\t '$vendor'\n",
	"   Version:\t '$ver'\n",
	"   Release date: '$rel_date'\n",
	"   BIOS characteristics: ";
    foreach my $i ( sort {$a <=> $b} keys %hwasset_bios_characteristics ) {
	print "'", $hwasset_bios_characteristics{$i}, "' "
	  if( $charact & $i );
    }
    print "\n";
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print_computer {
    my @data = @_;

    my ( $struct_ver, $manufact, $product, $ver, $ser_num, $uuid1, @uuid ) =
	unpack "L Z65 Z65 Z65 Z65 L S[2] H[4] H[12]", decode_base64( $data[0]->{'AssetData'} );

    printf "  (data struct.ver. %d.%d)\n", $struct_ver & 0xFFFF, $struct_ver >> 16;
    print "   Manufacturer: '$manufact'\n",
	"   Product:\t '$product'\n",
	"   Version:\t '$ver'\n",
	"   Serial numb.: '$ser_num'\n";
    printf "   UUID:\t %x-%x-%x-%s-%s\n", uc($uuid1), $uuid[0], $uuid[1], $uuid[2], $uuid[3], $uuid[4];
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print_baseboard {
    my @data = @_;

    my ( $struct_ver, $manufact, $product, $ver, $ser_num, $a_tag, $repl, $padd ) =
	unpack "L Z65 Z65 Z65 Z65 Z65 C S", decode_base64( $data[0]->{'AssetData'} );

    if( $padd != 0 ) {
	printf "   Error. Something's going wrong, received data has failed checking.\n";
	return 15;
    }
    printf "  (data struct.ver. %d.%d)\n", $struct_ver & 0xFFFF, $struct_ver >> 16;
    print "   Manufacturer: '$manufact'\n",
	"   Product:\t '$product'\n",
	"   Version:\t '$ver'\n",
	"   Serial numb.: '$ser_num'\n",
	"   Asset tag:\t '$a_tag'\n",
	"   Replaceable:  ", ($repl ? "yes" : "no"), "\n";
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print_processor {
    my @data = @_;

    my ( $struct_ver, $id1, $id2, $max_speed, $speed, $status, $type, $fam,
	$upg_info, $socket_pop, $socket_desig, $manufact, $ver ) =
	unpack "L L L S S C C C C C Z65 Z65 Z65", decode_base64( $data[0]->{'AssetData'} );

    printf "  (data struct.ver. %d.%d)\n", $struct_ver & 0xFFFF, $struct_ver >> 16;
    printf "   ID:\t\t\t0x%08x%08x\n", $id2, $id1;
    print "   Max Socket Speed:\t$max_speed MHz\n",
	"   Current Speed:\t$speed MHz\n";

    print "   Processor Status:\t",
		($status == 0x0 ? "Unknown" : ""),
		($status == 0x1 ? "Enabled" : ""),
		($status == 0x2 ? "Disabled by user" : ""),
		($status == 0x3 ? "Disabled by BIOS" : ""),
		($status == 0x4 ? "Waiting to be enabled" : ""),
		($status == 0x7 ? "Other" : ""), "\n";
    print "   Processor Type:\t",
		($type == 0x1 ? "Other" : ""),
		($type == 0x2 ? "Unknown" : ""),
		($type == 0x3 ? "Central" : ""),
		($type == 0x4 ? "Math" : ""),
		($type == 0x5 ? "DSP" : ""),
		($type == 0x6 ? "Video" : ""),
	"\n   Socket Populated:\t".($socket_pop ? "yes" : "no"),
	"\n   Processor family:\t";
    defined( $hwasset_cpu_family{$fam}) ? printf "'%s'", $hwasset_cpu_family{$fam} : printf "[0x%x]", $fam;

    print "\n   Upgrade Information:\t";
    defined( $hwasset_cpu_upg_info{$upg_info}) ? printf "'%s'", $hwasset_cpu_upg_info{$upg_info} : printf "[0x%x]", $upg_info;

    print "\n   Socket Designation:  '$socket_desig'\n",
	"   Manufacturer:\t'$manufact'\n",
	"   Version:\t\t'$ver'\n";
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print_memory {
    my @data = @_;

    my ( $struct_ver, $size, $f_fact, $type, $type_det, $speed, $manufact, $ser_num, $a_tag, $part_num ) =
	unpack "L S C C S S Z65 Z65 Z65 Z65", decode_base64( $data[0]->{'AssetData'} );

    if( $size == 0 ) {
	printf "  (* No memory device in the socket *)\n";
	return;
    }

    printf "  (data struct.ver. %d.%d)\n", $struct_ver & 0xFFFF, $struct_ver >> 16;
    print "   Size:\t ";
    if( $size == 0xFFFF) {
	print "Unknown\n";
    } else {
	if( $size & 0x8000 ) {
	    printf "%d Kb\n", ($size & 0x7FFF);
	} else {
	    print "$size Mb\n";
	}
    }
    print "   Form Factor:  ";
    defined( $hwasset_mem_form_fact{$f_fact}) ? printf "'%s'", $hwasset_mem_form_fact{$f_fact} : printf "[0x%x]", $f_fact;

    print "\n   Memory Type:  ";
    defined( $hwasset_mem_type{$type}) ? printf "'%s'", $hwasset_mem_type{$type} : printf "[0x%x]", $type;

    print "\n   Memory Type Details:";
    my $div='';
    foreach my $i ( sort {$a <=> $b} keys %hwasset_mem_type_det ) {
	print $div." '".$hwasset_mem_type_det{$i}."'"
	    if( $type_det & $i );
	$div = ',';
    }

    print "\n   Speed:\t ", ($speed ? "$speed MHz" : "unknown"),
	"\n   Manufacturer: '$manufact'\n",
	"   Serial numb.: '$ser_num'\n",
	"   Asset Tag:\t '$a_tag'\n",
	"   Part Number:\t '$part_num'\n";
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print_fru {
    my @data = @_;

    my( $struct_ver, $vend_id, $dev_id, $rev_id, $prog_if, $sub_cl,
	$base_cl, $sub_vend_id, $subsyst_id, $dev_loc, $padd ) =
	unpack "L S S C C C C S S S S", decode_base64( $data[0]->{'AssetData'} );

    if( $padd != 0 ) {
	print "   Error. Something's going wrong, received data has failed checking.\n";
	return;
    }

    printf "  (data struct.ver. %d.%d)\n", $struct_ver & 0xFFFF, $struct_ver >> 16;

    printf "   Vendor ID:\t0x%x\n", $vend_id;
    printf "   Device ID:\t0x%x\n", $dev_id;
    printf "   RevisionID:\t0x%x\n", $rev_id;
    printf "   Progr.Iface: 0x%x\n", $prog_if;
    printf "   Subclass:\t0x%x\n", $sub_cl;
    printf "   Base Class:\t0x%x\n", $base_cl;
    printf "   SubVendorID: 0x%x\n", $sub_vend_id;
    printf "   SubSystemID: 0x%x\n", $subsyst_id;
    printf "   DevLocation: 0x%x\n", $dev_loc;
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print_mediadevice {
    my @data = @_;

    my ( $struct_ver, $model, $serial, $cap84, $cap83, $cap82, $max_size0, $max_size1, $padd ) =
	unpack "L Z40 Z20 S3 L2 S", decode_base64( $data[0]->{'AssetData'} );

    if( $padd != 0 ) {
	printf "   Error. Something's going wrong, received data has failed checking.\n";
	return 15;
    }
    printf "  (data struct.ver. %d.%d)\n", $struct_ver & 0xFFFF, $struct_ver >> 16;
    print "    Model Number:  '$model'\n",
	"    Serial Number: '$serial'\n",
	"    Max MediaSize: ",
	(Math::BigInt->new($max_size1)->blsft(32)->badd($max_size0)),
	" bytes\n";

    print "    Capabilities:";
    my $div='';
    foreach my $i ( sort {$a <=> $b} keys %hwasset_media_cap82 ) {
	printf $div." '".$hwasset_media_cap82{$i}."'"
	  if( $cap82 & $i );
	$div = ',';
    }
    $div='';
    foreach my $i ( sort {$a <=> $b} keys %hwasset_media_cap83 ) {
	printf $div." '".$hwasset_media_cap83{$i}."'"
	  if( $cap83 & $i );
	$div = ',';
    }
    $div='';
    foreach my $i ( sort {$a <=> $b} keys %hwasset_media_cap84 ) {
	printf $div." '".$hwasset_media_cap84{$i}."'"
	  if( $cap84 & $i );
	$div = ',';
    }
    print "\n";
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print_port_batt {
    my @data = @_;

    my ( $struct_ver, $oem, $sbds_date, $des_cap, $des_volt, $sbds_serial, $des_cap_mltpl, $dev_chem,
	 $max_err, $serial, $sbds_ver, $locat, $manufact, $man_date, $dev_name, $sbds_dev_chem ) =
	unpack "L L L S S S C C C Z65 Z65 Z65 Z65 Z65 Z65 Z65", decode_base64( $data[0]->{'AssetData'} );

    printf "  (data struct.ver. %d.%d)\n", $struct_ver & 0xFFFF, $struct_ver >> 16;
    printf "   OEM Specific Info: 0x%x\n", $oem;
    print "   Design Capacity:  ", ($des_cap ? ($des_cap*$des_cap_mltpl)." mWatt-hours" : "unknown"),
	"\n   Design Voltage:   ", ($des_volt ? "$des_volt mVolts" : "unknown"),
	"\n   Device Chemistry:  ";
    if( $dev_chem == 0x02 ) {
	print $sbds_dev_chem;
    } else {
	print   ($dev_chem==0x01 ? "Other" : "" ).
		($dev_chem==0x03 ? "Lead Acid" : "" ).
		($dev_chem==0x04 ? "Nickel Cadmium" : "" ).
		($dev_chem==0x05 ? "Nickel metal hydride" : "" ).
		($dev_chem==0x06 ? "Lithium-ion" : "" ).
		($dev_chem==0x07 ? "Zinc air" : "" ).
		($dev_chem==0x08 ? "Lithium Polymer" : "" );
    }

    printf "\n   Max Error In Battery Data: $max_err\%\n",
	"   Location:         '$locat'\n",
	"   Manufacturer:     '$manufact'\n",
	"   Device Name:      '$dev_name'\n",
	"   Manufacture Date: ";
    if( defined($man_date) ) {
	print "'$man_date'\n";
    } else {
	printf "%4d/%02d/%02d (YYYY/MM/DD)\n", (($sbds_date&0xFE00) >>9)+ 1980, ($sbds_date&0x1E0)>>5, $sbds_date&0x1F;
    }
    print "   SerialNumber:     ";
    if( defined($serial) ) {
	print "'$serial'\n";
    } else {
	printf "0x%x\n", $sbds_serial;
    }
    print "   SBDS Ver. Number: '$sbds_ver'\n";
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print_vpro {
    my @data = @_;

    my ( $struct_ver, $cpu, $mch0, $mch1, $ich0, $ich1, $me0, $me1, $me2,
	 $tpm, $net0, $net1, $net2, $bios, $struct_id, $rsrvd  ) =
	unpack "L16", decode_base64( $data[0]->{'AssetData'} );

    if( $struct_id != 0x6F725076 ) {
	printf "  Received data check failed (wrong StructureIdentifier field).\n";
	return;
    }

    printf "  (data struct.ver. %d.%d)\n", $struct_ver & 0xFFFF, $struct_ver >> 16;

    print "   CPU:".
	" VMX=".($cpu & 0x1 ? "Enabled" : "Disabled").
	" SMX=".($cpu & 0x2 ? "Enabled" : "Disabled").
	" LT/TXT=";
    if( $cpu & 0x4 ) {
	printf ($cpu & 0x8 ? "Enabled" : "Disabled");
    }else{
	print "NOT_Capable";
    }

    print " VT-x=";
    if( $cpu & 0x10 ) {
	printf ($cpu & 0x20 ? "Enabled" : "Disabled");
    }else{
	print "NOT_Capable";
    }
    my $mask = 0x40;
    for my $i ( 6..31 ) {
	printf " Bit_".$i."=Enabled"
	  if( $cpu & $mask);
	$mask <<= 1;
    }

    printf "\n   MCH:".
	 "\tPCI Bus 0x%02x / Dev 0x%02x / Func 0x%02x\n", $mch0 & 0xFF00, $mch0 & 0xF8, $mch0 & 0x7;
    printf "\tDev Identification Number (DID): 0x%04x\n", ($mch0 & 0xFFFF0000) >>16;
    print "\tCapabilities: VT-d=";
    if( $mch1 & 0x1 ) {
	printf ($mch1 & 0x2 ? "Enabled" : "Disabled");
    }else{
	print "NOT_Capable";
    }
    print " TXT=";
    if( $mch1 & 0x1 ) {
	printf ($mch1 & 0x2 ? "Enabled" : "Disabled");
    }else{
	print "NOT_Capable";
    }
    $mask = 0x4;
    for my $i ( 36..63 ) {
	print " Bit_".$i."=Enabled"
	  if( $mch1 & $mask );
	$mask <<= 1;
    }

    printf "\n   ICH:".
	 "\tPCI Bus 0x%02x / Dev 0x%02x / Func 0x%02x\n", $ich0 & 0xFF00, $ich0 & 0xF8, $ich0 & 0x7;
    printf "\tDev Identification Number (DID): 0x%04x\n", ($ich0 & 0xFFFF0000) >>16;

    printf "   ME:  ".($me0 & 0x1 ? "Enabled" : "Disabled").
	"\n\tIntel_QST_FW=".($me0 & 0x2 ? "" : "NOT_")."Supported".
	" Intel_ASF_FW=".($me0 & 0x4 ? "" : "NOT_")."Supported".
	" Intel_AMT_FW=".($me0 & 0x8 ? "" : "NOT_")."Supported";
    my $div="\n\t";
    $mask = 0x10;
    for my $i ( 4..31 ) {
	printf $div."Bit_".$i."=Enabled"
	  if( $me0 & $mask );
	$mask <<= 1;
	$div=" ";
    }
    printf "\n\tME FW ver. %d.%d hotfix %d build %d\n",
	($me1&0xFFFF0000) >>16, $me1&0xFFFF, ($me2&0xFFFF0000) >>16, $me2&0xFFFF;

    printf "   TPM: ".($tpm & 0x2 ? "Enabled" : "Disabled").
	"\n\tTPM on board = ".($tpm & 0x1 ? "" : "NOT_")."Supported";
    if( $tpm & 0x1 ) {
	$div = "\n\tReserved bits: ";
	$mask = 0x4;
	for my $i ( 2..15 ) {
	    printf $div."Bit_".$i."=Enabled"
	      if( $tpm & $mask);
	    $mask <<= 1;
	    $div=" ";
	}
	printf "\n\tTPM designed to TCG spec ver. %d.%d",
	    ($tpm&0xFF0000) >>16, ($tpm&0xFF000000) >>24;
    }

    printf "\n   Network Devices:\n".
	 "\tWired NIC - PCI Bus 0x%02x / Dev 0x%02x / Func 0x%02x / ", $net0 & 0xFF00, $net0 & 0xF8, $net0 & 0x7;
    printf "DID 0x%04x\n", ($net0 & 0xFFFF0000) >>16;

    print "   BIOS";
    $div=" ";
    if( $bios & 0x1F ) {
	printf " supports setup screen for (can be editable):".
	    ($bios & 0x1 ? " VT-x" : "").
	    ($bios & 0x2 ? " VT-d" : "").
	    ($bios & 0x4 ? " TXT" : "").
	    ($bios & 0x8 ? " TXT" : "").
	    ($bios & 0x10 ? " ME" : "")."\n";
	$div = "\t";
    }
    if( $bios & 0x20 ) {
	my $t = ($bios&0x380) >>7;
	printf $div."supports VA extensions (ACPI Op region) with maximum ver. ".
	    ($t == 0 ? "2.6" : "").
	    ($t == 1 ? "3.0" : "").
	    ($t >  1 ? "higher than 3.0" : "")."\n";
    }
    print $div."SPI Flash has Platform Data region reserved.\n"
      if( $bios & 0x40 );
    print $div."(There is data in reserved bits).\n"
      if( $bios & 0xFFFFFE00 );
    print "   Reserved bytes (for future expansion) contains data.\n"
      if( $rsrvd );
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset_print_amt {
    my @data = @_;

    my ( $struct_ver, $supp, $enab, $ider, $sol, $net, $ext_data, $oem0, $oem1, $oem2, $oem3, $rsrvd ) =
	unpack "L C10 S", decode_base64( $data[0]->{'AssetData'} );

    printf "  (data struct.ver. %d.%d)\n", $struct_ver & 0xFFFF, $struct_ver >> 16;
    print "   AMT: ";
    if( $supp == 0 ) {
	print "NOT ";
#	return;
    }
    print "Supported, ", ($enab == 1 ? "Enabled" : "Disabled"), "\n",
	"   IDE-R: ", ($ider == 1 ? "Enabled" : "Disabled"), "\n",
	"   SOL: ", ($sol == 1 ? "Enabled" : "Disabled"), "\n",
	"   Network: ", ($net == 1 ? "Enabled" : "Disabled"), "\n";
    printf "   OEM capabilities:\n\tOEM-Specific data: 0x%02x\n", $ext_data;
    print "\tSupports:".
	    ($oem0 & 0x1  ? " IDE-R" : "").
	    ($oem0 & 0x2  ? " SOL" : "").
	    ($oem0 & 0x4  ? " BiosReflash" : "").
	    ($oem0 & 0x8  ? " BiosSetupScreen" : "").
	    ($oem0 & 0x10 ? " BiosPause" : "").
	    ($oem0 & 0x20 ? " No": " ")."FloppyBoot".
	    ($oem0 & 0x40 ? " No": " ")."CDBoot".
	    ($oem0 & 0x80 ? " ReservedBit": "")."\n";
    print "\tTerminal Emulation: ".
	    ($oem1 == 1  ? "VT52 (Basic monochrome)" : "").
	    ($oem1 == 2  ? "VT100+ (Adds function keys F5 to F14)" : "").
	    ($oem1 == 3  ? "VT-UTF8" : "").
	    ($oem1 == 4  ? "PC-ANSI" : "").
	    ($oem1  > 4  ? "unknown type" : "")."\n";
    printf "\t3rd Byte: 0x%02x\n", $oem2;
    printf "\t4th Byte: 0x%02x\n", $oem3;
    printf "   Reserved Bytes: 0x%04x\n", $rsrvd if( $rsrvd );
}

#-------------------------------------------------------------------------------------
sub cmnd_hwasset {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = lc($cmd);

    if( $cmd eq "help" ) {
	print_usage_header();
	@params = ( "help" );
    } else {
	print "## '$amt_host' :: AMT Hardware Asset\n";
    }

    if( $cmd =~ m/^((list)?|help)$/ ) {
	$ret = cmnd_hwasset_types(@params);
    }
    if( $cmd =~ m/^(data|help)$/ ) {
	$ret = cmnd_hwasset_print(@params);
    }

    print_newline( $cmd );
    return $ret  if( $ret >= -2);

    print "  Unknown command: hwasset $cmd\n";
    return -4;
}
#=====================================================================================


#-----------------------------------------------------------------------------------------
sub cmnd_audit_log {
    my ($cmd, $filt_app, $filt_evnt ) = @_;

    if( lc($cmd) eq "help" || (defined($filt_app) && ($filt_app !~ m/^[0-9]+$/)) ||
	 (defined($filt_evnt) && ($filt_evnt !~ m/^[0-9]+$/)) ) {
	print STDERR "    audit log [<audited_app_id> [<event_id]]\n",
		"\tprints logged events, <IDs> - numeric, optional, to filter log output\n",
		"\tExtendedData is dumped only in numeric mode\n";
	return 0;
    }

    my( $app_id, $ev_id, $init_type, $init_data, $time,	$mc_loc, $naddr_len, $naddr, $edata_len, @edata,
	$len, @tmp, $str, $ret, $cnt, $arg);

    my $got_cnt = 0;
    my $tot_cnt = 1;

    while( $got_cnt < $tot_cnt ) {
	$arg = SOAP::Data->name( 'StartIndex' => $got_cnt+1 );
	$ret = $gis->ReadAuditLogRecords( $arg );
	return $ret->result if( print_err_result($ret) );

	$tot_cnt = $ret->valueof( '//TotalRecordCount' );
	$got_cnt += $tot_cnt;

	print "\n Audit Log contains ".$tot_cnt." record".($tot_cnt != 1 ? "s" : "").".\n"
	    if( !$got_cnt );
	return 0 if( !$tot_cnt );

	if( !$got_cnt && !$param_hex ) {
	    print " Log filtered with: AuditedAppID=$filt_app".
		    ( defined($filt_evnt) ? ", EventID=$filt_evnt" : "" )."\n\n"
		if( defined($filt_app) );

	    printf " %-*s\t%-*s\tInitiator\tTime            ".
		    "\tMCLocType\tNetAddress".($param_num ? "\tExtendedData\n" : "\n"),
		    ($param_num ? 1 : 24), ($param_num ? "AuditedAppID" : "Auditable Application" ),
		    ($param_num ? 1 : 38), "Event".($param_num ? "ID" : "" );
	}

	foreach my $row ( $ret->valueof( '//EventRecords' ) )
	{
	    my $log_str = decode_base64( $row );
	    if( $param_hex ) {
		foreach my $byte ( unpack "C255",$log_str ) {
		    printf " %02x", $byte;
		}
		    print "\n";
		next;
	    }

	    ( $app_id, $ev_id, $init_type, @tmp ) = unpack "n n C6", $log_str;

	    if( defined($filt_app) ) {
		next if( $filt_app != $app_id ||
			( defined($filt_evnt) && $filt_evnt != $ev_id) );
	    }

	    printf " %-*s\t%-*s\t",
		($param_num ? 7 : 24), (defined($auditable_app_id{$app_id}) && !$param_num ?
					$auditable_app_id{$app_id} : $app_id),
		($param_num ? 2 : 38), (defined( $audit_event_id{$app_id}{$ev_id}) && !$param_num ?
					$audit_event_id{$app_id}{$ev_id} : $ev_id);
	    if( $init_type == 0) {
		$len = $tmp[0];
		( $init_type, $str ) = unpack "a6 Z[$len]", $log_str;
		$len++;
	    } elsif( $init_type == 1) {
		$str = "<Kerberos SID>";
		$len = 5 + $tmp[4];
	    } else {
		$str = "Local";
		$len = 0;
	    }
	    printf "%-9s\t", $str;
	    $len += 5;

	    ( $init_type, $time, $mc_loc, $naddr_len, $edata_len ) =
		unpack "a[$len] N C3", $log_str;
	    $time -= tz_local_offset();
	    print_date_time $time, 0;
	    printf "\t%-8s\t", ($mc_loc==0 ? "IPv4" : "").
		    ($mc_loc==1 ? "IPv6" : "").($mc_loc==2 ? "None" : "");
	    $len += 6;

	    if( $naddr_len ) {
		( $init_type, $naddr, $edata_len ) = unpack "a[$len] Z[$naddr_len] C", $log_str;
		printf "%-10s", ($naddr_len ? $naddr : "");
	    }

	    if( $param_num ) {
		print "\t";

		if( $edata_len ) {
		    $len += $naddr_len + 1;
		    ( $init_type, @edata ) = unpack "a[$len] a[$edata_len]", $log_str;
		    for my $b ( 0 .. scalar(@edata)-1 ) {
			printf " %02x", $edata[$b];
		    }
		} else {
		    print "<none>" }
	    }
	    print "\n";
	}
    }
    print "\n";
}

#-----------------------------------------------------------------------------------------
sub cmnd_audit_idlist {
    my $cmd = shift;

    if( lc($cmd) eq "help") {
	print STDERR "    audit id[list]\n\tlist of known event names & IDs\n";
	return 0;
    }

    printf "  %-30s\tAuditedAppID\tEventID\n", "Event Name";

    foreach my $app_id (sort {$a <=> $b} keys %audit_event_id ) {
	printf " = %s =\n", $auditable_app_id{$app_id};
	foreach my $ev_id ( sort {$a <=> $b} keys %{ $audit_event_id{$app_id} } ) {
		printf "  %-38s\t%d\t%d\n", $audit_event_id{$app_id}{$ev_id}, $app_id, $ev_id;
	}
    }
}

#-----------------------------------------------------------------------------------------
sub cmnd_audit_enable {
    my ( $cmd, $state ) = @_;
    $state = lc($state) if( defined($state) );

    if( lc($cmd) eq "help" || !defined($state) ||
	 ($state ne "true" && $state ne "false" )) {
	print STDERR "    audit enable (true|false)\n";
	return 0;
    }

    $state = ($state eq "true" ? 1 : 0 );

    print "  The audit log cannot be enabled prior to setting the signature key and certificate.\n"
      if( $state );
    print "  Setting Audit State to '", ( $state ? "Enabled" : "Disabled" ), "': ";
    return -1 if( ! get_user_confirm() );

    my $arg = SOAP::Data->name( 'Enable' => $state );
    return do_soap $als, "EnableAuditing", $arg;
}

#-----------------------------------------------------------------------------------------
sub cmnd_audit_clear_log {
    my $cmd = shift;

    if( lc($cmd) eq "help" ) {
	print STDERR "    audit clear[log]\n\tclears the contents of the audit log\n";
	return 0;
    }

    print "  May only be called when the audit log service is in a LOCKED STATE!\n\n".
	  "  Clear the Audit Log: ";
    return -1 if( ! get_user_confirm() );

    return do_soap $als, "ClearAuditLog";
}

#-----------------------------------------------------------------------------------------
sub cmnd_audit_set_policy {
    my ( $cmd, $state, $app_id, $evnt_id, $flag ) = @_;
    $state = lc( $state ) if( defined($state) );
    $flag = lc( $flag ) if( defined($flag) );

    if( (oc($cmd) eq "help") || #!defined($state) || !defined($app_id) || !defined($evnt_id) ||
	( $state ne "enable" && $state ne "disable") ||
	( $app_id !~ m/^[0-9]+$/) || ($evnt_id !~ m/^[0-9]+$/) ||
	( $flag ne "critical" && $flag ne "") ) {
	print STDERR "    audit policy (enable|disable) <audAppId> <eventId> [critical]\n".
		"\tset a new audit policy; IDs - numeric values\n";
	return 0;
    }

    printf " Set the Audit log Policy to < $state $app_id-$evnt_id >".
	    ($flag ne "" ? " $flag: " : ": ");
    return -1 if( ! get_user_confirm() );

    my ( @arg, @policy );

    push @policy, SOAP::Data->name( 'AuditedAppID' => $app_id );
    push @policy, SOAP::Data->name( 'EventId'      => $evnt_id );
    push @policy, SOAP::Data->name( 'Flag'         => ($flag eq "critical" ? 1 : 0) );

    push @arg, SOAP::Data->name( 'Enable' => ($state eq "enable" ? 1 : 0 ) );
    push @arg, SOAP::Data->name( 'AuditPolicy' => \SOAP::Data->value(@policy) );

    return do_soap $als, "SetAuditPolicy", @arg;
}

#-----------------------------------------------------------------------------------------
sub cmnd_audit_storage {
    my ( $cmd, $set, $policy, $days ) = @_;
    my ( $ret );
    $set = lc( $set ) if( defined($set) );
    $policy = lc( $policy ) if( defined($policy) );
    my $pol_r = $policy eq 'restrict_ro';

    if( (lc($cmd) eq "help") || ( $set ne "set" && $set ne "") ||
	( $set eq "set" && ( !defined $audit_storage_policy_set{ $policy } ||
	  ( $pol_r && (!defined($days) || $days < 1 || $days > 100) ) )) ) {
	print STDERR "    audit storage [set <policy> [<days>]]\n".
		"\tget/set the audit log records storage policy (AMT 5.1+)\n".
		"\t<policy>=rollover,no_ro,restrict_ro. <days>=1-100 (restricted rollover only)\n";
	return 0;
    }

    return -2 if( !check_amt_version(5,1, "  AuditLogStoragePolicy doesn't supported") );

    if( $set ne "set" ) {
	$ret = $als->GetAuditLogStoragePolicy;
	return $ret->result if( print_err_result($ret) );

	my @po = $ret->paramsout;
	printf "  Audit Log Storage policy in use: %s\n", $audit_storage_policy_get{ $po[0] };
	printf "  Minimal days to keep any record: %d\n", $po[1]
	    if( $po[0] == $audit_storage_policy_set{ 'restrict_ro' } );
	printf "\n";
	return 0;
    }

    my $pol_code = $audit_storage_policy_set{ $policy };
    printf "  Set the Audit Log Storage Policy to '".$audit_storage_policy_get{$pol_code}."'".
	($pol_r ? " with minimum $days day".($days==1 ? "":"s")." to keep: " : ": ");
    return -1 if( ! get_user_confirm() );

    my @arg;
    push @arg, SOAP::Data->name( 'StoragePolicy' => $pol_code );
    push @arg, SOAP::Data->name( 'DaysToKeep'    => $days )
	if( $pol_r );

    return do_soap $als, "SetAuditLogStoragePolicy", @arg;
}

#-----------------------------------------------------------------------------------------
sub cmnd_audit_signature {
    my $cmd = shift;

    if( lc($cmd) eq "help" ) {
	print STDERR "    audit sign[ature]\n\treturns the audit log signature and related info\n";
	return 0;
    }

    printf "  Audit Log Signature and Log related information\n";

    my $arg = SOAP::Data->name( 'SigningMechanism' => 0 );
    my $ret = $als->ExportAuditLogSignature( $arg );
    if( $ret->result != $pt_status{'PT_STATUS_SUCCESS'} ) {
	print "    Check is Audit Log Enabled.\n    ";
	print_result $ret;
	print "\n";
	return $ret->result;
    }

    my $sign = $ret->valueof('//AuditSignature');
    my $tz = tz_local_offset();

    print "    Audit Log Records:\t ", $sign->{'TotalRecordCount'}, "\n",
	"    Start Log Time:\t ";
    print_date_time $sign->{'StartLogTime'}-$tz, 0;
    print "\n    End Log Time:\t ";
    print_date_time $sign->{'EndLogTime'}-$tz, 0;

    print "\n    System UUID:\t ";
    print_uuid $sign->{'UUID'};
    print "\n    System FQDN:\t ", $sign->{'FQDN'};

    print "\n    Signature generated: ";
    print_date_time $sign->{'GenerationTime'}-$tz, 0;

    print "\n    Signature Mechanism: ", $signing_mechanism{ $sign->{'SignatureMechanism'} },
	    "\n    Signature Data (BASE64 encoded):\n\t",
	    join( "\n\t", split( '\n', encode_base64( $sign->{'Signature'} )) );

    my @len = ( @{ $sign->{'LengthOfCertificates'} }, 0, 0, 0, 0, 0 );
    my @certs = unpack "a[$len[0]] a[$len[1]] a[$len[2]] a[$len[3]] a[$len[4]]",
			$sign->{'Certificates'};
    foreach my $c ( @certs ) {
	next if( $c eq "" );
	print "\n    Certificate Data (BASE64 encoded):\n\t",
        join( "\n\t", split( '\n', encode_base64( $c )) );

    }
    print "\n";
}

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub sub_audit_parse_pem {
    my ( $fname, $beg_str, $end_str ) = @_;
    my $buf;

    open( FILE, $fname ) || die "  Error opening file '$fname'.\n\n";
    read( FILE, $buf, 10000);
    close( FILE );

    $buf =~ m/-----${beg_str}-----\n(.*)\n-----${end_str}-----\n/s;
    return $1
	if( defined($1) );

    printf "  Failed file '$fname' contents check.\n\n";
    return "-1";
}

#-----------------------------------------------------------------------------------------
sub cmnd_audit_set_key {
    my ( $cmd, $k_file, $c_file0, $c_file1, $c_file2, $c_file3, $c_file4 ) = @_;

    if( lc($cmd) eq "help" || $k_file eq "" || $c_file0 eq "" ) {
	print STDERR "    audit [set]key <keys_file> <cert_file_1> [<cert_file_2> ... [<cert_file_5]]\n".
		"\tsets the key and certificate needed to sign the audit log\n".
		"\tKey Pair RSA 2048 bit. SHA1 signing. Certs: up to 5 X.509, total max length 4100 bytes\n";
	return 0;
    }

    my @flist = ( $c_file0 );
    my $f;

    foreach $f ( $c_file1, $c_file2, $c_file3, $c_file4 ) {
	push @flist, $f if( defined($f) );
    }
    foreach $f ( $k_file, @flist ) {
	if( ! -e $f ) {
	    printf "  Error: the file '$f' isn't exist.\n\n";
	    return -1;
	}
	if( ! -f $f ) {
	    printf "  Error: the file '$f' is a special type of file.\n\n";
	    return -1;
	}
	if( ! -r $f ) {
	    printf "  Error: the file '$f' isn't readable.\n\n";
	    return -1;
	}
    }

    my ( $arg, @key_cert, $keys, @cert_len, $cert );

    $keys = sub_audit_parse_pem( $k_file, "BEGIN RSA PRIVATE KEY", "END RSA PRIVATE KEY" );
    return -1 if( $keys eq "-1" );
    $keys =~ s/\n//sg;

    foreach $f ( @flist ) {

	$arg = sub_audit_parse_pem( $f, "BEGIN CERTIFICATE", "END CERTIFICATE" );
	return -1 if( $arg eq "-1" );
	$arg =~ s/\n//sg;
	$cert .= $arg;
	push @cert_len, length( decode_base64($arg) );
    }

    printf "  Setting the key and certificate to sign the audit log: ";
    return -1 if( !get_user_confirm() );

    push @key_cert, SOAP::Data->name( 'SignatureMechanism' => 0 );
    push @key_cert, SOAP::Data->name( 'SigningKey' => 
	    \SOAP::Data->name( 'RSAKeyPair' => 
		\SOAP::Data->name( 'DERKey' => $keys )->uri("$schemabase/CertStore/2006/01")
	    )->uri("$schemabase/CertStore/2006/01") );

    push @key_cert, SOAP::Data->name( 'LengthOfCertificates' => @cert_len );
    push @key_cert, SOAP::Data->name( 'Certificates' => $cert );

    $arg = SOAP::Data->name( 'KeyAndCert' => \SOAP::Data->value(@key_cert) );
    return do_soap $als, "SetSigningKeyingMaterial", $arg;
}

#-----------------------------------------------------------------------------------------
sub cmnd_audit_lock {
    my ( $cmd, $type, $time, $handle ) = @_;
    $type = lc( $type ) if( defined($type) );

    print "\n  Warn: <handle> is required to UnLock operation!\n\n"
	if( $type eq "unlock" && !defined($handle) );

    if( (lc($cmd) eq "help") || 
	  !defined( $audit_lock_type{$type} ) ||
	( $time !~ m/^[0-9]+$/ ) || $time<1 || $time>300  ||
	( $type eq "unlock" && !defined($handle) ) ||
	( defined($handle) && $handle !~ m/^[0-9]+$/) ) {
	print STDERR "    audit lock <type> <timeout> [<handle>]\n".
		"\tlog lock/unlock and allows audit unprovisioning\n".
		"\t<type>=lock,unprovis,unlock   <timeout>=1-300 seconds\n".
		"\t<handle> needed when log is already locked, received when locking\n";
	return 0;
    }

    print " Set Audit Lock state to ",
	    ($type eq "unlock" ? "UN" : "" ), "LOCKed",
	    ($type eq "lock" ? " within $time second".($time==1 ? "":"s") : "" ),
	    ($type eq "unprovis" ? " with ENABLED UNPROVISioning" : "" ), ": ";
    return -1 if( ! get_user_confirm() );

    my ( @arg, $ret );

    push @arg, SOAP::Data->name( 'LockTimeoutInSeconds' => $time );
    push @arg, SOAP::Data->name( 'Flag'   => $audit_lock_type{$type} );
    push @arg, SOAP::Data->name( 'Handle' => $handle )
	if( defined($handle) );

    $ret = $als->LockAuditLog( @arg );
    print_result $ret;

    print "   Received handle: ",
	$ret->valueof('//Handle'),
	"\n   Use it for additional 'lock' requests.\n\n"
      if( defined( $ret->paramsout ) );

    return $ret->result;
}

#-------------------------------------------------------------------------------------
sub cmnd_audit {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = lc($cmd);

    if( $cmd eq "help" || !@params) {
	print_usage_header();
	print STDERR "  Audit-specific global parameter '--num[eric]|-n' forces numeric ID codes output\n",
	       "    Service supported in AMT ver.4.0 and later releases\n";
	$cmd = "help";
	@params = ( $cmd );
    } else {
	return -22  if( !check_amt_version(4,0, "  Audit doesn't supported") );
	return cmnd_info( "audit" )
	    if( $cmd eq "info" );
	print "## '$amt_host' :: AMT Access Monitor (Audit)\n";
    }

    if( $cmd =~ m/^help$/ ) {
	$ret = cmnd_info_audit(@params);
    }
    if( $cmd =~ m/^(log|help)$/ ) {
	$ret = cmnd_audit_log(@params);
    }
    if( $cmd =~ m/^(id(list)?|help)$/ ) {
	$ret = cmnd_audit_idlist(@params);
    }
    if( $cmd =~ m/^(enable|help)$/ ) {
	$ret = cmnd_audit_enable(@params);
    }
    if( $cmd =~ m/^(clear(log)?|help)$/ ) {
	$ret = cmnd_audit_clear_log(@params);
    }
    if( $cmd =~ m/^(policy|help)$/ ) {
	$ret = cmnd_audit_set_policy(@params);
    }
    if( $cmd =~ m/^(storage|help)$/ ) {
	$ret = cmnd_audit_storage(@params);
    }
    if( $cmd =~ m/^(sign(ature)?|help)$/ ) {
	$ret = cmnd_audit_signature(@params);
    }
    if( $cmd =~ m/^((set)?key|help)$/ ) {
	$ret = cmnd_audit_set_key(@params);
    }
    if( $cmd =~ m/^(lock|help)$/ ) {
	$ret = cmnd_audit_lock(@params);
    }

    print_newline( $cmd );

    if( $ret >= -2 ) {
	return $ret;
    } else {
	print " Unknown command: audit $cmd\n\n";
	return -4;
    }
}
#=====================================================================================

#-------------------------------------------------------------------------------------
sub cmnd_security_commit {
    my $cmd  = shift;

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    security commit[changes]\n",
		"\tCommits (save to flash) pending configuration made to the AMT with commands:\n",
		"\tnet vlan, net config, net hostName, net domainName\n";
	return 0;
    }

    print "  Commit net commands pending configuration: ";

    return -1  if( !get_user_confirm() );
    return do_soap( $sas, "CommitChanges" );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_digest {
    my $cmd  = shift;
    my @prms = @_;
    my $ret;

    if( lc($cmd) eq "help") {
	print_usage_header();
	print STDERR "    security digest[realm]\n";
	return 0;
    }

    return -2  if( !check_amt_version(2,0, "  Command not supported") );

    $ret = $sas->GetDigestRealm();
    return $ret->result  if( print_err_result($ret) );

    print "  Device Digest Authentication Realm: '", $ret->valueof('//DigestRealm'), "'\n\n";
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_security_ifaces {
    my ( $cmd, @list ) = @_;
    my ($ret, $arg);
    my %ifaces = ( "webui" => "WebUI", "serialoverlan" => "SerialOverLAN", "ideredirection" => "IdeRedirection" );

    for my $i (1 .. scalar(@list) ) {
	$list[$i-1] = lc( $list[$i-1] );
	$list[$i-1] = ( defined($ifaces{$list[$i-1]}) ? $ifaces{ $list[$i-1] } : "-");
    }

    if( lc($cmd) eq "help" || defined($list[0]) && $list[0] eq "-"
	|| defined($list[1]) && $list[1] eq "-"
	|| defined($list[2]) && $list[2] eq "-" )
    {
	print_usage_header();
	print STDERR "    security i[nter]faces [<enabled_ifaces_list>]\n",
		"\tGet/Set enabled interfaces of the Intel AMT device\n",
		"\t<enabled_ifaces_list> elements: ",
		join( ' ', values %ifaces),"\n";
	return 0;
    }

    return -2  if( !check_amt_version(2,0, "  Command not supported") );

    if( !defined($list[0]) ) {
	print "  Enabled interfaces:  ";

	$ret = $sas->GetEnabledInterfaces();
	return $ret->result  if( print_err_result($ret) );
	print join '  ', $ret->valueof('//EnabledInterfaces'), "\n";
	return 0;
    }

    print "  Set Enabled Interfaces to  '", join( ' ', @list), "': ";
    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'EnabledInterfaces' => @list );
    return do_soap( $sas, "SetEnabledInterfaces", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_vpn_routing {
    my $cmd = shift;
    my $state = shift;
    my ($ret, $arg);

    $state = lc($state) if( defined($state) );

    if( lc($cmd) eq "help" || $state !~ m/^o(n|ff)$/ ) {
	print_usage_header();
	print STDERR "    security vpn_routing <on|off>\n";
	return 0;
    }

    return -2  if( !check_amt_version(2,5, "  Command not supported") );

    print "  Set VPN Routing of AMT traffic to '", ucfirst($state), "': " ;
    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'Enable' => ($state eq "on" ? "true" : "false") );
    return do_soap( $sas, "EnableVpnRouting", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_env_detect {
    my $cmd = shift;
    my $set = shift;
    my @params = @_;
    my ($ret, $arg, @arg);

    $set = "" if( ! defined($set) );

    if( lc($cmd) eq "help" || $set ne "" && lc($set) ne "set" ) {
	print_usage_header();
	print STDERR "    security env[_detect] [set [<domains> [circut_breaker_policy]]]\n",
		"\tget/set the enterprise environment definition\n",
		"\t<domains> - space separated domains list (max 5, 64 bytes each)\n";
	return 0;
    }

    return -2  if( !check_amt_version(2,5, "  Command not supported") );

    if( $set eq "" ) {
	$ret = $sas->GetEnvironmentDetection();
	return $ret->result  if( print_err_result($ret) );
	$arg = $ret->valueof('//Params');
	print "  Environment Detection Settings: ";

	if( $arg->{'LocalDomains'} eq "" ) {
	    print " Disabled\n";
	} else {
	    my $d = $arg->{'LocalDomains'}->{'values'};
	    @arg = ( ref($d) eq "ARRAY") ? @{ $d } : ( $d );
	    print "\n    LocalDomains: ", join( '  ',@arg ), "\n";
	}

	print "  Circuit Breaker Policy Handle: ",
		$arg->{'ExternalCircuitBreakerPolicy'}, "\n"
	    if( defined($arg->{'ExternalCircuitBreakerPolicy'}) );
	return 0;
    }

    print "  Set Environment Detection Settings to";
    if( ! @params ) {
	print " 'Disabled': ";
	$arg[0] = SOAP::Data->name( 'LocalDomains' => "" );
    } else {
	my $dom_num = scalar(@params) - 1;
	$dom_num-- if( $params[$dom_num] =~ m/^[0-9]+$/ );

	$arg[0] = SOAP::Data->name( 'LocalDomains' => 
		    \SOAP::Data->name( 'values' => @params[0..$dom_num] ));
	$arg[1] = SOAP::Data->name( 'ExternalCircuitBreakerPolicy' => $params[$dom_num+1] );

	print "\n\tLocalDomains: ", join('  ', @params[0..$dom_num]);
	print "\n\tCircuit Breaker Policy Handle: ", $params[$dom_num+1]
	    if( defined($params[$dom_num+1]) );
	print "\n\t";
    }

    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'Params' => \SOAP::Data->value( @arg) );
    return do_soap( $sas, "SetEnvironmentDetection", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_realm {
    my $cmd   = shift;
    my @list1 = @_;
    my $set   = shift;
    my $mode  = shift;
    my @list2 = @_;
    my ( $ret, $arg, @arg, $i );
    my %modes = ( "noauth" => "NoAuth", "auth" => "Auth", "disable" => "Disable" );

    $set  = lc($set)  if( defined($set) );
    $mode = lc($mode) if( defined($mode) );

    if( lc($cmd) eq "help" || @list1 &&
	(   $set ne "set" && $set ne "all" && !defined( $acl_realm_codes{ lc($set) })
	 || $set eq "all" && @list1 != 1
	 || $set eq "set" && ( ! defined($modes{$mode})
	    || ! defined($acl_realm_codes{ lc($list2[0]) }) && $list2[0] ne "all"
	    || ! @list2 || $list2[0] eq "all" && @list2 != 1) 
	 )) {
	print_usage_header();
	print STDERR "    security realm [set <mode>] [<realms>|all]\n",
		"\tget/set authentication mode for the given realms\n",
		"\trun without parameters to get the modes and realms list\n";
	return 0;
    }

    if( !@list1 ) {
	print "  Supported Authentication Modes (case insensitive):\n\t", join( ' ', values %modes );
	print "\n  GET command supports realms:\n\tall - full realms list";
	foreach my $i ( sort {$a <=> $b} keys %acl_realm_types ) {
	    next if( !$i );
	    print "\n\t", $acl_realm_types{ $i };
	}
	print "\n  SET command supports realms only (case insensitive):\n\t",
		$acl_realm_types{ 16 }, "\n\t", $acl_realm_types{ 17 }, 
		"\n\tYou should try 'security realm set Auth all' to found out the supported realms\n";
	return 0;
    }

    foreach $i ( $set ne "set" ? @list1 : @list2 ) {
	next if( $i eq "all"  || defined($acl_realm_codes{ lc($i) }) );

	print "    warning: wrong realm '$i'. See 'security realm help'\n";
	return $pt_status{"PT_STATUS_INVALID_REALM"};
    }

    return -2  if( !check_amt_version(2,5, "  Command not supported") );

    if( $set ne "set" ) {
	@list1 = (sort values %acl_realm_types) if( $set eq "all" );
	print "  The Authentication mode for the realm", (@list1 == 1 ? "" : "s"), ":\n";
	foreach $i ( @list1 ) {
	    next if( $i eq "all" );
	    $arg = SOAP::Data->name( 'Realm' => $acl_realm_codes{ lc($i) } );
	    $ret = $sas->GetRealmAuthOptions( $arg );
	    return $ret->result  if( print_err_result($ret) );
	    printf "    %-*s\t%s\n", 25, $i, $ret->valueof('//HTTPAuthOption');
	}
	return 0;
    }

    @list2 = (sort values %acl_realm_types) if( $list2[0] eq "all" );

    print "  Set the Auth mode '$modes{$mode}' to the realm", (@list2==1 ? " ":"s:\n");
    print "\t", join(' ', @list2), ": " if( !$param_force );

    return -1  if( !get_user_confirm() );
#    print "\n"

    $arg[1] = SOAP::Data->name( 'HTTPAuthOption' => $modes{$mode} );
    foreach $i ( @list2 ) {
	next  if( $i eq "all" );
	printf "    %-*s\t", 25, $i if( @list2 != 1 );
	$arg[0] = SOAP::Data->name( 'Realm' => $acl_realm_codes{ lc($i) } );
	$ret = do_soap( $sas, "SetRealmAuthOptions", @arg );
    }
    return $ret;
}

#-------------------------------------------------------------------------------------
sub cmnd_security_flash_wear_out {
    my $cmd  = shift;

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    security flash_wear_out\n",
		"\tresets the wear-out protection to the initial state\n";
	return 0;
    }

    print
    "\n  This command resets the wear-out protection to the initial state for all protected flash sectors.\n",
    "  At the initial state, each sector is granted sufficient Erase or Write Operations for the device \n",
    "  to work correctly. This operation should be used only in rare occasions such as during setup and \n",
    "  configuration or following contamination from a malicious application that caused a denial-of-service \n",
    "  (DoS) attack by consuming the entire credit for flash erase operations.\n",
    "\n  In AMT release 7.0 and later this command does not reset the the wear-out protection.",
    "\n\n\tDo you REALLY want to continue ??? ";

    return -1  if( !get_user_confirm() );
    return do_soap( $sas, "ResetFlashWearOutProtection" );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_zero_touch {
    my $cmd = shift;
    my $state = shift;
    my ($ret, $arg);

    $state = ( defined($state) ? lc($state) : "" );

    if( lc($cmd) eq "help" || $state !~ m/^(o(n|ff))?$/ ) {
	print_usage_header();
	print STDERR "    security zero_touch [<on|off>]\n";
	return 0;
    }

    return -2  if( !check_amt_version(2,2, "  Command not supported") );

    if( !$state ) {
	$ret = $sas->GetZeroTouchConfigurationMode();
	return $ret->result  if( print_err_result($ret) );
	$state  = $ret->valueof('//Enabled');

	print "  Zero Touch Configuration Mode: ",
		(lc($state) eq "true" ? "Enabled" : "Disabled"), "\n";
	return 0;
    }

    print "  Set Zero Touch Configuration Mode to '", ucfirst($state), "': ";
    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'Enabled' => ($state eq "on" ? "true" : "false") );
    return do_soap( $sas, "SetZeroTouchConfigurationMode", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_audit_provis {
    my $cmd = shift;
    my $state = shift;
    my ($ret, $arg);

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    security audit_provis\n",
		"\tinfo of the last device's set up (command not tested at all)\n";
	return 0;
    }

    return -2  if( !check_amt_version(2,2, "  Command not supported") );

    print "  Provisioning Audit Record: ";

    $ret = $sas->GetProvisioningAuditRecord();
    if( $ret->result ) {
	if( $ret->result == 0x809 ) {
	    print "not set  --  " ;
	}
	print_err_result($ret);
	return $ret->result;
    }
    $arg = $ret->valueof('//ProvisioningAuditRecord');

    print "\n\tProvisioning TLS Mode: ",
	( $arg->{'ProvisioningTLSMode'} == 0 ? "NOT_READY" : "" ),
	( $arg->{'ProvisioningTLSMode'} == 1 ? "PSK" : "" ),
	( $arg->{'ProvisioningTLSMode'} == 2 ? "PKI" : "" );
    print "\n\tSecure DNS: ", $arg->{'SecureDNS'},
	    "\n\tHost Initiated: ", $arg->{'HostInitiated'};
    print "\n\tProv Server FQDN: ", $arg->{'ProvServerFQDN'}
	if( defined($arg->{'ProvServerFQDN'}) );
    print "\n\tSelected Hash Type: ",
	( $arg->{'SelectedHashType'} == 0 ? "UNDEFINED_ALGORITHM" : "" ),
	( $arg->{'SelectedHashType'} == 1 ? "SHA_1_160" : "" ),
	( $arg->{'SelectedHashType'} == 2 ? "SHA_2_256" : "" ),
	( $arg->{'SelectedHashType'} == 3 ? "SHA_2_384" : "" );
    print "\n\tSelected Hash Data (BASE64 encoded):\n\t",
	join( "\n\t", split( '\n', encode_base64( $arg->{'SelectedHashData'} )) );

    my @certs = ( scalar($arg->{'CaCertSerials'}) > 1 ) ? @{ $arg->{'CaCertSerials'} } : ( $arg->{'CaCertSerials'} );
    foreach my $c ( @certs ) {
	print "\n    CA Certificate Serial (BASE64 encoded):\n\t",
	    join( "\n\t", split( '\n', encode_base64( $c )) );
    }

    print "\n\tAdditional Ca Serial Nums: ", $arg->{'AdditionalCaSerialNums'}
	if( defined($arg->{'AdditionalCaSerialNums'}) );
    print "\n\tIs Oem Default: ", $arg->{'IsOemDefault'};
    print "\n\tIs Time Valid: ", $arg->{'IsTimeValid'};

    print "\n\tProvis. Server's IP: ",
	    conv_ipv4_to_txt( $arg->{'ProvServerIP'} );

    print "\n\tTls Start Time: ";
    print_date_time $arg->{'TlsStartTime'} - tz_local_offset(), 0;

    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_security_provis_pid {
    my $cmd = shift;
    my ($ret, $arg);

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    security provis_pid\n";
	return 0;
    }

    return -2  if( !check_amt_version(2,2, "  Command not supported") );

    $ret = $sas->GetProvisioningPID();
    return $ret->result  if( print_err_result($ret) );
    $arg = $ret->valueof('//PID');

    print "  The PID from a PID/PPS pair ";

    if( $arg eq "" ) {
	print " is NOT set.\n";
	return 0;
    }

    print "(BASE64 encoded):\n\t",
	join( "\n\t", split( '\n', encode_base64($arg)) );
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_security_cfg_serv_fqdn {
    my $cmd = shift;
    my $fqdn = shift;
    my ($ret, $arg);

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    security cfg_serv_fqdn [<FQDN>]\n";
	return 0;
    }

    return -2  if( !check_amt_version(2,2, "  Command not supported") );

    if( ! defined($fqdn) ) {
	$ret = $sas->GetConfigurationServerFQDN();
	return $ret->result  if( print_err_result($ret) );
	$fqdn = $ret->valueof('//fqdn');

	print "  Configuration Server's FQDN: ",
		( $fqdn ? ": ".$fqdn : "NOT set."), "\n";
	return 0;
    }

    if( $fqdn !~ m/^([a-z0-9_-]+\.)*[a-z0-9_-]+$/i ) {
	printf "\n  Error. Wrong domain name: $fqdn\n";
        return -2;
    }

    print "  Set Configuration Server's FQDN to '", $fqdn, "': ";
    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'fqdn' => $fqdn );
    return do_soap( $sas, "SetConfigurationServerFQDN", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_cred_cache {
    my $cmd = shift;
    my $state = shift;
    my ($ret, $arg);

    $state = ( defined($state) ? lc($state) : "" );

    if( lc($cmd) eq "help" || $state !~ m/^(o(n|ff))?$/ ) {
	print_usage_header();
	print STDERR "    security cred_cache [on|off]\n",
		"\tget/set state of credentials cache for Kerberos\n";
	return 0;
    }

    return -2  if( !check_amt_version(3,2, "  Command not supported") );

    if( !$state ) {
	$ret = $sas->GetCredentialCacheState();
	return $ret->result  if( print_err_result($ret) );
	$state  = $ret->valueof('//Enabled');

	print "  Credential Cache State: ",
		(lc($state) eq "true" ? "Enabled" : "Disabled"), "\n";
	return 0;
    }

    print "  Set Credential Cache State to '", ucfirst($state), "': ";
    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'Enabled' => ($state eq "on" ? "true" : "false") );
    return do_soap( $sas, "SetCredentialCacheState", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_provis_mode {
    my ( $cmd, $mode )  = @_;
    my ( $ret, $arg );

    if( lc($cmd) eq "help" || defined($mode) && !defined($provisioning_modes{$mode}) ) {
	print_usage_header();
	print STDERR "    security provisioning [$provisioning_codes{1}|$provisioning_codes{2}]\n",
		"\tget/set provisioning mode\n";
	return 0;
    }

    if( !defined( $mode )) {
	$ret = $sas->GetProvisioningMode();
	return $ret->result  if( print_err_result($ret) );

	$mode  = $ret->valueof('//ProvisioningMode');
	print "  Current provisioning mode: ",
		( $mode == 1 ? "Enterprise" : "SmallBusiness" ), "\n";
	return 0;
    }

    print "  Deprecated in AMT 4.0 and later.\n\n"  if( check_amt_version(4,0) );
    print "  Set provisioning mode to ", $mode, ": ";

    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'ProvisioningMode' => $provisioning_modes{$mode} );
    return do_soap( $sas, "Unprovision", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_unprovision {
    my ( $cmd, $mode )  = @_;

    if( lc($cmd) eq "help" || !defined($provisioning_modes{$mode}) ) {
	print_usage_header();
	print STDERR "    security unprovision <mode>\n",
		"\tresets device configuration to default factory settings\n",
		"\t<mode> one of: ", join( ' ', sort keys %provisioning_modes ), "\n";
	return 0;
    }

    print "\n\tThis command resets the AMT device configuration to default factory settings.\n",
	"\tIf Audit Log is active, you must first lock it with 'audit lock unprovis <timeout>'.\n",
	"\n\tDo you REALLY want to continue ??? ";

    return -1  if( !get_user_confirm() );

    my $arg = SOAP::Data->name( 'ProvisioningMode' => $provisioning_modes{$mode} );
    return do_soap( $sas, "Unprovision", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_partial_unprovision {
    my $cmd = shift;

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    security partial_unprovision\n",
		"\tset to 'security unprovision Enterprise' with some settings unchanged\n";
	return 0;
    }
    return -2  if( !check_amt_version(2,0, "  Partial Unprovision doesn't supported") );

    print "\n\tThis command resets the AMT device configuration to Enterprise unprovision mode\n",
	"\twith unchanged settings: admin name & pass, tls/psk keys, host_name, domain_name,\n",
	"\tprovisioning server, Zero Touch configuration.\n",
	"\tIf Audit Log is active, you must first lock it with 'audit lock unprovis <timeout>'.\n",
	"\n\tDo you REALLY want to continue ??? ";

    return -1  if( !get_user_confirm() );

    return do_soap( $sas, "PartialUnprovision" );
}

#-------------------------------------------------------------------------------------
sub cmnd_security_templ {
    my $cmd  = shift;
    my @prms = @_;

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    security commit[changes]\n",
		"\tnet vlan, net config, net hostName, net domainName\n";
	return 0;
    }

    print "  Commit net commands pending configuration: ";

    return -1  if( !get_user_confirm() );
    return do_soap( $sas, "CommitChanges" );
}

#-------------------------------------------------------------------------------------
sub cmnd_security {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = lc($cmd) if( defined($cmd) );

    if( $cmd eq "help" || !@params) {
	print_usage_header();
	$cmd = "help";
	@params = ( $cmd );
    } else {
	print "## '$amt_host' :: AMT Security Administration (PTAdministration realm)\n";
    }

    if( $cmd =~ m/^(commit(changes)?|help)$/ ) {
	$ret = cmnd_security_commit(@params);
    }
    if( $cmd =~ m/^(i(nter)?faces|help)$/ ) {
	$ret = cmnd_security_ifaces(@params);
    }
    if( $cmd =~ m/^(realm|help)$/ ) {
	$ret = cmnd_security_realm(@params);
    }
    if( $cmd =~ m/^(vpn_routing|help)$/ ) {
	$ret = cmnd_security_vpn_routing(@params);
    }
    if( $cmd =~ m/^(env(_detect)?|help)$/ ) {
	$ret = cmnd_security_env_detect(@params);
    }
    if( $cmd =~ m/^(digest(realm)?|help)$/ ) {
	$ret = cmnd_security_digest(@params);
    }
    if( $cmd =~ m/^(zero_touch|help)$/ ) {
	$ret = cmnd_security_zero_touch(@params);
    }
    if( $cmd =~ m/^(audit_provis|help)$/ ) {
	$ret = cmnd_security_audit_provis(@params);
    }
    if( $cmd =~ m/^(provis_pid|help)$/ ) {
	$ret = cmnd_security_provis_pid(@params);
    }
    if( $cmd =~ m/^(cfg_serv_fqdn|help)$/ ) {
	$ret = cmnd_security_cfg_serv_fqdn(@params);
    }
    if( $cmd =~ m/^(cred_cache|help)$/ ) {
	$ret = cmnd_security_cred_cache(@params);
    }
    if( $cmd =~ m/^(provisioning|help)$/ ) {
	$ret = cmnd_security_provis_mode(@params);
    }
    if( $cmd =~ m/^(partial_unprovision|help)$/ ) {
	$ret = cmnd_security_partial_unprovision(@params);
    }
    if( $cmd =~ m/^(unprovision|help)$/ ) {
	$ret = cmnd_security_unprovision(@params);
    }
    if( $cmd =~ m/^(flash_wear_out|help)$/ ) {
	$ret = cmnd_security_flash_wear_out(@params);
    }

    print_newline( $cmd );
    return $ret  if( $ret >= -2 );

    print " Unknown command: security $cmd\n\n";
    return -4;
}
#=====================================================================================


#-------------------------------------------------------------------------------------
sub cmnd_redirect_listener {
    my ( $cmd, $state ) = @_;
    my ( $ret, $arg );

    $state = ( defined($state) ? lc($state) : "" );

    if( lc($cmd) eq "help" || $state !~ m/^(on|off)?$/) {
	print_usage_header();
	print STDERR "    redirect listener [on|off]\n",
		"\tget/set redirection listener state\n";
	return 0;
    }

    if( $state eq "" ) {
	$ret = $rds->GetRedirectionListenerState();
	return $ret->result  if( print_err_result($ret) );
	
	print "   Redirection service listener: ",
		( lc($ret->valueof('//Enabled')) eq "true" ? "Enabled":"Disabled" ),
		"\n";
	return 0;
    }

    print "  Set Redirection service listener to '",
	($state eq "on" ? "Enabled":"Disabled" ), "' : ";
    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'Enabled' => ($state eq "on" ? "true":"false") );
    return do_soap( $rds, "SetRedirectionListenerState", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_redirect_log {
    my $cmd  = shift;
    my ( $ret, $num, @log );

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    redirect log\n";
	return 0;
    }


    $ret = $rds->GetIderSessionLog();
    return $ret->result  if( print_err_result($ret) );

    @log = $ret->valueof('//LogData');
    $num = scalar( @log );
    print "  IDE-R session log contains ",
	    $num, " record", ($num==1 ? "" : "s" ), ".\n";

    foreach my $r ( @log ) {
	print "    ";
	print_date_time( ($r->{'TimeStamp'} - tz_local_offset() ), 0 );
	print "\t", conv_ipv4_to_txt( $r->{'ConsoleAddress'} ),
		":", $r->{'Port'}, "\n";
    }
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_redirect {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = lc($cmd);

    if( $cmd eq "help" || !@params) {
	print_usage_header();
	$cmd = "help";
	@params = ( $cmd );
    } else {
	return -22  if( !check_amt_version(2,0, "  Redirection service doesn't supported") );
	print "## '$amt_host' :: AMT Redirection\n";
    }

    if( $cmd =~ m/^(listener|help)$/ ) {
	$ret = cmnd_redirect_listener(@params);
    }
    if( $cmd =~ m/^(log|help)$/ ) {
	$ret = cmnd_redirect_log(@params);
    }

    print_newline( $cmd );
    return $ret  if( $ret >= -2 );

    print " Unknown command: redirect $cmd\n\n";
    return -4;
}
#=====================================================================================

#-------------------------------------------------------------------------------------
sub print_pwr_pkg_descr {
    my $r = shift;

    my $arg = SOAP::Data->name( 'PolicyGUID', $r );
    my $ret = $sas->GetPowerPackage( $arg );
    return $ret->result  if( print_err_result($ret) );

    print $ret->valueof('//PolicyDescriptor');
    return 0;
}
#-------------------------------------
sub print_pwr_pkg_guid_descr {
    my $r = shift;

    print "    ";
    print_uuid ( unpack "C16", decode_base64( $r ) );
    print "\t";
    my $ret = print_pwr_pkg_descr( $r );
    print "\n";
    return $ret;
}

#-------------------------------------------------------------------------------------
sub cmnd_power_pkg_list {
    my $cmd  = shift;
    my ( $ret, $num, @list, $arg );

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    pwr_save pkg_list\n\tlist all supported power packages GUIDs and descriptions\n";
	return 0;
    }

    $ret = $sas->EnumeratePowerPackages();
    return $ret->result  if( print_err_result($ret) );

    @list = $ret->valueof('//PolicyGUID');
    $num = scalar( @list );

    print "  The device supports ", $num,
	    " power package", ($num==1 ? "" : "s" ), ($num ? ":" : "." ), "\n";

    foreach my $r ( @list ) {
	print_pwr_pkg_guid_descr $r;
    }
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_power_pkg_active {
    my ( $cmd, $set, $guid ) = @_;
    my ( $ret, $arg );
    $set = lc($set);

    if( lc($cmd) eq "help" || $set !~ m/^(set)?$/ ) {
	print_usage_header();
	print STDERR "    pwr_save pkg_active [set <guid>]\n",
		"\tget/set the active power package\n";
	return 0;
    }

    if( $set eq "" ) {
	$ret = $sas->GetActivePowerPackage();
	return $ret->result  if( print_err_result($ret) );
	
	print "  The Active Power Package:\n";
	print_pwr_pkg_guid_descr $ret->valueof('//PolicyGUID');
	return 0;
    }

    if( $guid !~ /^[[:xdigit:]]{8}(-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12}$/ ) {
	print "  ERROR: Wrong GUID given\n";
	return 0;
    }

    print "  Set The Active Power Package to GUID ", $guid, " : ";
    return -1  if( !get_user_confirm() );

    $arg = SOAP::Data->name( 'PolicyGUID' => conv_uuid_txt_to_base64($guid) );
    return do_soap( $sas, "SetActivePowerPackage", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_power_pkg_policy {
    my ( $cmd, $set, $time ) = @_;

    if( lc($cmd) eq "help"
	 || defined($set) && ( lc($set) ne "set" || $time !~ /^[0-9]+$/ ) ) {
	print_usage_header();
	print STDERR "    pwr_save pkg_policy [set <timeout>]\n",
		"\tget/set the global power policy\n";
	return 0;
    }

    if( !defined($set) ) {
	my $ret = $sas->GetGlobalPowerPolicy();
	return $ret->result  if( print_err_result($ret) );

	print "  The Current Global Power Policy:\n\tIdle Wake Timeout\t",
		$ret->valueof('//GlobalPowerPolicy')->{'IdleWakeTimeout'}, "\n";
	return 0;
    }

    print "  Set The Global Power Policy -> Idle Wake Timeout = $time : ";
    return -1  if( !get_user_confirm() );

    my $arg = SOAP::Data->name( 'GlobalPowerPolicy' => 
		\SOAP::Data->name( 'IdleWakeTimeout' => $time ) );
    return do_soap( $sas, "SetGlobalPowerPolicy", $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_power_options {
    my ( $cmd, $set, $ac, $time ) = @_;
    my ( $ret, $arg, @arg );

    if( lc($cmd) eq "help" || defined($set)
	  && ( lc($set) ne "set" || $ac !~ /^[0345]$/ || defined($time) && $time !~ /^[0-9]+$/ )) {
	print_usage_header();
	print STDERR "    pwr_save options [set <active_ac> [<sleep_time>]]\n",
		"\t<active_ac> - the highest AC state AMT operates: 0,3,4,5\n",
		"\t<sleep_time> - wake-on-net access sleep timer\n";
	return 0;
    }

    if( !defined($set) ) {
	$ret = $sas->GetPowerSavingOptions();
	return $ret->result  if( print_err_result($ret) );
	
	print "  The Power Saving Settings:",
		"\n\tActive State AC:\t\t",  $ret->valueof('//ActiveStateAC'), "\n";

	$arg = $ret->valueof('//WakeOnNetAccessThresholdAC');
	print "\tWake On Net Access Threshold AC:\t",  $arg, "\n"
	    if( defined($arg) && $arg ne "" );

	$arg = $ret->valueof('//WakeOnNetAccessSleepTimer');
	print "\tWakeOnNet Access Sleep Timer:\t",  $arg, "\n"
	    if( defined($arg) && $arg ne "" );
	return 0;
    }

    print "  Set The Power Saving Settings to:\n",
	"\tAMT Active State AC\t\tPowerStateS$ac\n";
    print "\tWakeOnNet Access Sleep Timer\t$time\n"
	if( defined($time) );
    print "    ";
    return -1  if( !get_user_confirm() );

    push @arg, SOAP::Data->name( 'ActiveStateAC' => "PowerStateS".$ac );
    push @arg, SOAP::Data->name( 'WakeOnNetAccessSleepTimer' => $time )
	if( defined($time) );
    $ret = $sas->SetPowerSavingOptions( @arg );

    print_result $ret;
    print_commit_warn $ret
	if( check_amt_version(2,1) );
    return $ret->result;
}

#-------------------------------------------------------------------------------------
sub cmnd_power {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = lc($cmd);

    if( $cmd eq "help" || !@params) {
	print_usage_header();
	$cmd = "help";
	@params = ( $cmd );
    } else {
	if( $cmd =~ m/^pkg_/ ) {
	    return -22  if( !check_amt_version(2,5, "  Power Packages doesn't supported") );
	} else {
	    return -22  if( !check_amt_version(2,0, "  Power Saving features doesn't supported") );
	    if( check_amt_version(6, 0)) {
		print "  ERROR: This function is not supported in AMT 6.0+, use the power package functions!\n\n";
		return -22;
	    }
	    print "* WARNING: This function is deprecated, use the power package functions!\n*\n"
		if( check_amt_version(2, 5));
	}
	print "## '$amt_host' :: AMT Security Administration (PTAdministration realm)\n";
    }

    if( $cmd =~ m/^(pkg_list|help)$/ ) {
	$ret = cmnd_power_pkg_list(@params);
    }
    if( $cmd =~ m/^(pkg_active|help)$/ ) {
	$ret = cmnd_power_pkg_active(@params);
    }
    if( $cmd =~ m/^(pkg_policy|help)$/ ) {
	$ret = cmnd_power_pkg_policy(@params);
    }
    if( $cmd =~ m/^(options|help)$/ ) {
	$ret = cmnd_power_options(@params);
    }

    print_newline( $cmd );
    return $ret  if( $ret >= -2 );

    print " Unknown command: pwr_save $cmd\n\n";
    return -4;
}
#=====================================================================================

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_hex {
#   return sprintf("0x%X", $_[0]);
    return (defined $_[1]) ? sprintf("0x%X", $_[0]) : sprintf("[0x%X]", $_[0] );
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_sensor_entity_desc($) {
    my $id = shift;
    my $idt = get_hex( $id );

    return $idt." ".$sensor_entity_id_codes{ $id }  if( defined $sensor_entity_id_codes{ $id } );

    return "$idt Chassis-specific (system specific)"
      if( $id >= 0x90 && $id <= 0xAF );

    return "$idt Board-set specific (system specific)"
      if( $id >= 0xB0 && $id <= 0xCF );

    return "$idt OEM System Integrator defined"
      if( $id >= 0xD0 && $id <= 0xFF );

    return "$idt Reserved value";
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_sensor_type_desc {
    my $id = shift;
    my $idt = (defined $_[0]) ? "" : get_hex( $id )." ";

    return $idt.$sensor_type_desc{ $id }  if( defined $sensor_type_desc{ $id } );

    return $idt."OEM Reserved - User notification"
      if( $id == 0xC0 );
    return $idt."OEM Reserved"
      if( $id > 0xC0 && $id <= 0xFF );

    return $idt."Reserved";
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_sensor_number_desc($) {
    my $id = shift;
    my $idt = get_hex( $id );

    return "$idt (unspecified)"
      if( $id == 0x0 || $id == 0xFF );
    return "$idt";
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_event_source_type_desc($) {
	my $id = shift;
	my $idt = get_hex( $id );

	return "$idt Platform Firmware (e.g. BIOS)"	if( $id >= 0x0 && $id <= 0x7 );
	return "$idt SMI Handler"			if( $id >= 0x8 && $id <= 0xF );
	return "$idt ISV System Management Software" if( $id >= 0x10 && $id <= 0x17 );
	return "$idt Alert ASIC"		if( $id >= 0x18 && $id <= 0x1F );
	return "$idt IPMI"			if( $id >= 0x20 && $id <= 0x27 );
	return "$idt BIOS Vendor"		if( $id >= 0x28 && $id <= 0x2F );
	return "$idt System Board Set Vendor"	if( $id >= 0x30 && $id <= 0x3F );
	return "$idt System Integrator"		if( $id >= 0x38 && $id <= 0x3F );
	return "$idt Third Party Add-in"	if( $id >= 0x40 && $id <= 0x47 );
	return "$idt OSV"			if( $id >= 0x48 && $id <= 0x4F );
	return "$idt NIC"			if( $id >= 0x50 && $id <= 0x57 );
	return "$idt System Management Card"	if( $id >= 0x58 && $id <= 0x5F );
	return "$idt ASF 1.0"			if( $id == 0x68 );
	return "$idt Reserved for ASF x.x"	if( $id >= 0x69 && $id < 0x6F );
	return "$idt Sensor Specific"		if( $id == 0x6F );
	return "$idt Unspecified"		if( $id == 0xFF );
	return "$idt Unknown";
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_event_severity_desc($) {
    my $id = shift;

#    return $idt." ".$event_severity_desc{ $id }  if( defined $event_severity_desc{ $id } );
    return $event_severity_desc{ $id }  if( defined $event_severity_desc{ $id } );
    my $idt = get_hex( $id );
    return "$idt Unknown";
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_event_type_desc {
	my $id = shift;
	my $idt = (defined $_[0]) ? "" : get_hex( $id )." ";

	return $idt."Unspecified (Sensor Class: N/A)"		if( $id == 0x0 );
	return $idt."Threshold (Sensor Class: Threshold)"	if( $id == 0x1 );
	return $idt."Generic (Sensor Class: Discrete)"		if( $id >= 0x2 && $id <= 0xC );
	return $idt."Sensor-specific (Sensor Class: Discrete)"	if( $id == 0x6F );
	return $idt."OEM (Sensor Class: OEM Discrete - User notification)"  if( $id == 0x70 );
	return $idt."OEM (Sensor Class: OEM Discrete)"		if( $id > 0x70 && $id <= 0x7F );
	return $idt."Reserved";
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_event_desc {
    my $l = shift;
    my $known = shift;
    my ( @d, @str, $filt );

    my $e_tto = my $k = sprintf "%X;%X;%X", $l->{'EventSensorType'}, $l->{'EventType'}, $l->{'EventOffset'};

    if( defined( $l->{'EventData'} )) {
	@d = @{ $l->{'EventData'}->{'Byte'} };

	my $dat;
	foreach my $i ( @d ) {
	    $dat .= sprintf( ";%X" , $i );
	}
	$k .= $dat
	  if( $dat ne ";40;0;0;0;0;0;0;0" );
    } elsif( $l->{'EventType'} == 0xff && $l->{'EventOffset'} != 0xF ) {
	# 
	$filt = sprintf "%X;[[:xdigit:]]{1,2};%X", $l->{'EventSensorType'}, $l->{'EventOffset'};
	@str = grep( /^$filt$/i, keys %event_desc );

	return $event_desc{ $str[0] }  if( scalar @str == 1 );
    }

    return $event_desc{ $k }  if( defined $event_desc{$k} );

    @str = grep( /^$e_tto;.*\*/i, keys %event_desc );
    if( ! @str ) {
	# check for Generic Event Type Codes
	$k = sprintf "0;%X;%X", $l->{'EventType'}, $l->{'EventOffset'};
	return "'".get_sensor_type_desc( $l->{'EventSensorType'}, 0 )."' - ".$event_desc{$k}
	  if( defined $event_desc{$k} );
    } else {
	# check for *-mask
	return $event_desc{$e_tto.";*"}  if( defined $event_desc{$e_tto.";*"} );
	for my $s ( @str ) {
	    $filt = $s;
	    $filt =~ s/\*/[[:xdigit:];]+/;
	    return $event_desc{$s}  if( $k =~ m/^$filt$/ );
	}
    }

    return "<Unknown event descr.: sensor '".
	get_sensor_type_desc( $l->{'EventSensorType'}, 0 ).
	"', event type '".get_event_type_desc( $l->{'EventType'}, 0 )."'>"
      if( !defined $known );
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub print_sensor_details( $ ) {
    my $hndl = shift;
    my ( $ret, $val );

    my $arg = SOAP::Data->name( 'Handle' => $hndl );

    $ret = $ems->GetSensorAttributes( $arg );
    return $ret->result   if( print_err_result($ret) );
    $val = $ret->valueof('//Attributes');

    print "    Index          \t", $val->{'Index'},
	"\n    Assertion Event\t",	ucfirst( $val->{'AssertionEvent'} ),
	"\n    Deassertion Event\t",	ucfirst( $val->{'DeassertionEvent'} ),
	"\n    Legacy Sensor\t",	ucfirst( $val->{'IsLegacySensor'} ),
	"\n    Event Sensor Type\t", get_sensor_type_desc( $val->{'EventSensorType'} ),
	"\n    Event Type   \t", get_event_type_desc( $val->{'EventType'} ),
	"\n    Event Offset\t", get_hex( $val->{'EventOffset'} ),
	"\n    Event Source Type\t", get_event_source_type_desc( $val->{'EventSourceType'} ),
	"\n    Event Severity\t", get_event_severity_desc( $val->{'EventSeverity'} ),
	"\n    Device Address\t", get_hex( $val->{'DeviceAddress'} ),
	"\n    Sensor Number\t", get_sensor_number_desc( $val->{'SensorNumber'} ),
	"\n    Entity       \t", get_sensor_entity_desc( $val->{'Entity'} ),
	"\n    Entity Instance\t", get_hex( $val->{'EntityInstance'} ),
	($val->{'EntityInstance'} ? "" : " match all"), "\n";

    $arg = get_event_desc( $val, 1 );
    print "    Event Description\t".$arg."\n"   if( $arg );
}

#-------------------------------------------------------------------------------------
sub cmnd_event_sensors_list {
    my $cmd = shift;
    my $totl;
    my $start = 1;

    if( lc($cmd) eq "help") {
	print_usage_header();
	print STDERR "    event sensors_list\n";
	return 0;
    }

    init_event_desc_var;

    do {
	my $arg = SOAP::Data->name( 'StartIndex' => $start );
	my $ret = $ems->EnumerateSensors( $arg );
	return $ret->result  if( print_err_result($ret) );

	$totl = $ret->valueof('//TotalCount');

	print "  Registered $totl logical sensor",
		($totl == 1 ? "" : "s"), ($totl ? ":" : "."), "\n"
	  if( $start == 1 );

	return 0  if( $totl == 0  &&  $ret->valueof('//HandleCount') == 0 );

	my @arr = $totl == 1 ? $ret->valueof('//Handles')->{'SensorHandle'} :
			    @{ $ret->valueof('//Handles')->{'SensorHandle'} };
	foreach my $hndl ( @arr ) {
	    print "   Sensor\n";
	    print_sensor_details( $hndl );
	    $start++;
	}
    } while( $totl > $start );
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_event_log_status {
    my $cmd = shift;

    if( lc($cmd) eq "help") {
	print_usage_header();
	print STDERR "    event log_status\n";
	return 0;
    }

    my $ret = $ems->GetEventLogStatus;
    return $ret->result  if( print_err_result($ret) );

    my $free = $ret->valueof('//NumberOfFreeRecords');
    my $rec  = $ret->valueof('//NumberOfRecords');
    print "  Event Log Status",
	"\n    Free Records\t", $free, " (", $free*100/($free+$rec),
	"%)\n    Records Total\t", $free+$rec,
	"\n    Time Stamp (D/M/Y)\t";
    print_date_time $ret->valueof('//Time') - tz_local_offset(), 0 ;
    print "\n    Log Frozen  \t", ucfirst( $ret->valueof('//IsFrozen') ), "\n";
}

#-------------------------------------------------------------------------------------
sub cmnd_event_log_clear {
    my $cmd = shift;

    if( lc($cmd) eq "help") {
	print_usage_header();
	print STDERR "    event log_clear\n";
	return 0;
    }

    print "  Clear ALL records from the Event Log ";
    return -1 if( ! get_user_confirm() );

    return do_soap $ems, "ClearEventLog";
}

#-------------------------------------------------------------------------------------
sub cmnd_event_log_freeze {
    my ( $cmd, $state ) = @_;

    $state = lc( $state ) if( defined($state) );

    if( lc($cmd) eq "help" || $state !~ m/^on|off$/ ) {
	print_usage_header();
	print STDERR "    event log_freeze (on|off)\n";
	return 0;
    }

    print "  Set the Event Log Freeze State to '", ucfirst($state), "' ";
    return -1  if( ! get_user_confirm() );

    my $arg = SOAP::Data->name( 'NewState' => ($state eq "on" ? "true" : "false") );
    return do_soap $ems, "FreezeEventLog", $arg;
}

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub print_event_log_record($) {
    my $rec = shift;

    print_date_time $rec->{'TimeStamp'} - tz_local_offset(), 0;

    my $arg = get_event_desc( $rec );

    if( $param_num ||  !$arg ) {
	print "\t", get_hex( $rec->{'EventSensorType'}, 0 ),
	    "\t", get_hex( $rec->{'EventType'}, 0 ),
	    "\t", get_hex( $rec->{'EventOffset'}, 0 ),
	    "\t", get_hex( $rec->{'EventSourceType'}, 0 ),
	    "\t", get_hex( $rec->{'EventSeverity'}, 0 ),
	    "\t", get_hex( $rec->{'DeviceAddress'}, 0 ),
	    "\t", get_hex( $rec->{'SensorNumber'}, 0 ),
	    "\t", get_hex( $rec->{'Entity'}, 0 ),
	    "\t", get_hex( $rec->{'EntityInstance'}, 0 ),
	    "\t", get_hex( @{$rec->{'EventData'}->{'Byte'}}[0], 0 ),
	     " ", get_hex( @{$rec->{'EventData'}->{'Byte'}}[1], 0 ),
	     " ", get_hex( @{$rec->{'EventData'}->{'Byte'}}[2], 0 ),
	     " ", get_hex( @{$rec->{'EventData'}->{'Byte'}}[3], 0 ),
	     " ", get_hex( @{$rec->{'EventData'}->{'Byte'}}[4], 0 ),
	     " ", get_hex( @{$rec->{'EventData'}->{'Byte'}}[5], 0 ),
	     " ", get_hex( @{$rec->{'EventData'}->{'Byte'}}[6], 0 ),
	     " ", get_hex( @{$rec->{'EventData'}->{'Byte'}}[7], 0 );
    } else {
	print "\t".$arg;
    }
    print "\n";
}
#-------------------------------------------------------------------------------------
sub cmnd_event_log_list {
    my ( $cmd, $show_nmb ) = @_;
    my ( $totl, $arg, $ret );
    my $start = 1;
    $show_nmb = 0   if( ! defined($show_nmb) );

    if( lc($cmd) eq "help" || $show_nmb !~ m/^[0-9]*$/) {
	print_usage_header();
	print STDERR "    event log_list [<last_rows_number>]\n";
	return 0;
    }

    init_event_desc_var;

    do {
	$arg = SOAP::Data->name( 'StartIndex' => $start );
	$ret = $ems->ReadEventLogRecords( $arg );
	return $ret->result  if( print_err_result($ret) );

	$totl = $ret->valueof('//TotalRecordCount');

	 if( $start == 1 ) {
	    print "  Total $totl Event Log Record",
		($totl == 1 ? "" : "s"), ($totl && !$show_nmb ? ":" : "."), "\n";
	    print "  Last ", $show_nmb, " row",
		($show_nmb == 1 ? "" : "s"), ":\n"
	      if( $show_nmb && $totl );
	    print "#   Time Stamp   \tEvSens\tEvent\tEvent\tEvent\tEvent\tDevice\tSensor\tEntity\tEntity\tEvent Data\n",
		  "#                \tType\tType\tOffset\tSrcTyp\tSeverit\tAddress\tNumber\t      \tInstance\t\n"
	      if( $param_num );
	}

	return 0  if( $totl == 0  &&  $ret->valueof('//RecordsReturned') == 0 );

	my @arr = $totl == 1 ? $ret->valueof('//EventRecords')->{'EventLogRecord'} :
			    @{ $ret->valueof('//EventRecords')->{'EventLogRecord'} };
	foreach my $rec ( @arr ) {
	    print_event_log_record( $rec );
	    $start++;
	    return 0   if( $show_nmb  &&  $start > $show_nmb );
	}
    } while( $totl > $start );
    return 0;
}

#-------------------------------------------------------------------------------------
sub cmnd_event_alert_community {
    my ( $cmd, $set, $comm_str ) = @_;
    my ( $ret, @arg, @str );

    if( lc($cmd) eq "help" || defined($set)
	  && ( lc($set) ne "set" || !defined($comm_str) || length($comm_str) > 16 )) {
	print_usage_header();
	print STDERR "    event alert_community [set <community>]\n",
		"\tget/set the default community string used in PET alerts (for iAMT ver. pre 2.5)\n",
		"\tcommunity string: max 16 printable chars\n";
	return 0;
    }

    if( !defined($set) ) {
	$ret = $ems->GetAlertCommunityString;
	return $ret->result  if( print_err_result($ret) );

	my $len = $ret->valueof('//Length');
	my $comm = $ret->valueof('//CommunityString');
	print "  Current Community String: ",
	    (! $len ) ? "is not set (empty)." : "'".
		($len == 1 ? chr($comm->{'Byte'}) : join('', map( chr, @{ $comm->{'Byte'}} )) )."'",
	    "\n";
	return 0;
    }

    print "  Set the Community String to '$comm_str': ";
    return -1  if( ! get_user_confirm() );

    @str = map( ord, split( '', $comm_str ));

    push @arg, SOAP::Data->name( 'Length' => scalar @str );
    push @arg, SOAP::Data->name( 'CommunityString' =>
		\SOAP::Data->name( 'Byte', SOAP::Data->value(@str) ));

    return do_soap $ems, "SetAlertCommunityString", @arg;
}

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_filter_configuration_desc($) {
    my $b = shift;
    my $str;

    return (($b & 0x80) == 0 ? "Disabled, " : "Enabled, " ).
	( ($b & 0x60) == 0    ? "SoftwareConfigurable" : "" ).
	( ($b & 0x60) == 0x20 ? "Reserved1" : "" ).
	( ($b & 0x60) == 0x40 ? "ManufacturerPreConfigured" : "" ).
	( ($b & 0x60) == 0x60 ? "Reserved2" : "" );
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_filter_action_desc($) {
	my $b = shift;
	my $str;

	return ( ($b & 0x80) == 0 ? "DoNot" : "" )."LogEvent, ".
	    ( ($b & 0x1) == 0 ? "No" : "" )."Alert";
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub get_filter_details($) {
	my $hndl = shift;
	my $arg = SOAP::Data->name( 'EventFilterHandle' => $hndl );

	my $ret = $ems->GetEventFilter( $arg );
	return $ret->result   if( print_err_result($ret) );

	return $ret->valueof('//EventFilter');
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub print_filter_details($) {
	my $v = shift;
	my $i;

	return  if( ref($v) ne "HASH");

	print "\n    Configuration:\t", get_filter_configuration_desc( $v->{'FilterConfiguration'} )
	  if( defined( $v->{'FilterConfiguration'} ));
	print "\n    Action:     \t", get_filter_action_desc( $v->{'FilterAction'} )
	  if( defined( $v->{'FilterAction'} ));
	print "\n    AlertSubscriptionPolicyID: ", $v->{'AlertSubscriptionPolicyID'}
	  if( defined( $v->{'AlertSubscriptionPolicyID'} ));

	$i = $v->{'EventSensorType'};
	print "\n    EventSensorType:\t", ($i ==0xFF) ? "[0xFF] match all" : get_sensor_type_desc($i)
	  if( defined( $i ));
	$i = $v->{'EventType'};
	print "\n    EventType:   \t", ($i ==0xFF) ? "[0xFF] match all" :  get_event_type_desc($i)
	  if( defined( $i ));
	$i = $v->{'EventOffset'};
	print "\n    EventOffset:\t", get_hex( $i ), ($i ==0xF) ? " match all" : ""
	  if( defined( $i ));
	$i = $v->{'EventSourceType'};
	print "\n    EventSourceType:\t", ($i ==0xFF) ? "[0xFF] match all" : get_event_source_type_desc($i)
	  if( defined( $i ));
	$i = $v->{'EventSeverity'};
	print "\n    EventSeverity:\t", ($i ==0xFF) ? "[0xFF] match all" : get_event_severity_desc($i)
	  if( defined( $i ));
	$i = $v->{'DeviceAddress'};
	print "\n    DeviceAddress:\t", get_hex( $i ), ($i ==0xFF) ? " match all" : ""
	  if( defined( $i ));
	$i = $v->{'SensorNumber'};
	print "\n    SensorNumber:\t", ($i ==0xFF) ? "[0xFF] match all" : get_sensor_number_desc($i)
	  if( defined( $i ));
	$i = $v->{'Entity'};
	print "\n    Entity:      \t", ($i ==0xFF) ? "[0xFF] match all" : get_sensor_entity_desc($i)
	  if( defined( $i ));
	$i = $v->{'EntityInstance'};
	print "\n    EntityInstance:\t", get_hex( $i ), ($i ==0) ? " match all" : ""
	  if( defined( $i ));
	if( defined $v->{'EventSensorType'} &&
		defined $v->{'EventType'}  &&  defined $v->{'EventOffset'} ) {
	    $i = get_event_desc( $v, 1 );
	    print "\n    Event Description:\t$i"
	      if( $i );
	}
	print "\n";
}

#-------------------------------------------------------------------------------------
sub cmnd_event_filter_list {
    my $cmd = shift;
    my $pol_id = shift;
    my ( $totl, @arg );
    my $start = 1;

    if( lc($cmd) eq "help" || defined($pol_id) && $pol_id !~ m/^[0-9]+$/ ) {
	print_usage_header();
	print STDERR "    event filter_list [<alert_subscription_policy_id>]\n";
	return 0;
    }
    init_event_desc_var;

    do {
	push @arg, SOAP::Data->name( 'StartIndex' => $start );
	push @arg, SOAP::Data->name( 'AlertSubscriptionPolicyID' => $pol_id )
	  if( defined($pol_id) );

	my $ret = $ems->EnumerateEventFilters( @arg );
	return $ret->result  if( print_err_result($ret) );

	$totl = $ret->valueof('//TotalEventFilterCount');

	if( $start == 1 ) {
	    print "  Registered $totl Event Filter", ($totl == 1 ? "" : "s");
	    print " (for the Policy ID #$pol_id)"
	      if( defined $pol_id );
	    print "", ($totl ? ":" : "."), "\n";
	}

	return 0  if( $totl == 0  &&  $ret->valueof('//FiltersReturned') == 0 );

	my @arr = ($totl == 1) ? $ret->valueof('//Filters')->{'EventFilterHandle'} :
			    @{ $ret->valueof('//Filters')->{'EventFilterHandle'} };
	foreach my $hndl ( @arr ) {
	    print "   Filter [handle $hndl]:";
	    print_filter_details get_filter_details( $hndl );
	    $start++;
	}
    } while( $totl > $start );
    return 0;
}

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub check_num {
	my ( $str, $abr, $ret ) = @_;
	return 0  if( $str !~ /^$abr:(.*)$/i );

	if( $1 =~ /^([[:xdigit:]]+)h$/ ) {
	    ${$ret} = hex($1);
	} elsif( $1 =~ /^([[:digit:]]+)$/ ) {
	    ${$ret} = $1;
	} else {
	    print "  WARN: wrong value for parameter '$str'.\n";
	    return 0;
	}
	return 1;
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub parse_filter_params {
    my $v = shift;
    my $p = shift;
    my @params = @_;
    my $i;

    for ( @params ) {
	if( check_num($_,"est",\$i ))	{ $v->{'EventSensorType'}=$i; next }
	if( check_num($_,"et", \$i ))	{ $v->{'EventType'}=$i; next }
	if( check_num($_,"eo", \$i ))	{ $v->{'EventOffset'}=$i; next }
	if( check_num($_,"st", \$i ))	{ $v->{'EventSourceType'}=$i; next }
	if( check_num($_,"dev",\$i ))	{ $v->{'DeviceAddress'}=$i; next }
	if( check_num($_,"sn", \$i ))	{ $v->{'SensorNumber'}=$i; next }
	if( check_num($_,"e",  \$i ))	{ $v->{'Entity'}=$i; next }
	if( check_num($_,"ei", \$i ))	{ $v->{'EntityInstance'}=$i; next }

	if( /^s:(.*)$/i ) {
		$i=lc($1);
		if( defined $event_severity_code{$i} ) {
		    $v->{'EventSeverity'} = $event_severity_code{$i};
		    next
		}
	}
	if( /^(en|dis)(able)?$/i ) {
		$v->{'FilterConfiguration'} = (lc($1) eq "en" ? 0x80 : 0);
		next
	}
	if( /^(not?)?log$/i ) {
		$v->{'FilterAction'} = 0  if( !defined( $v->{'FilterAction'} ));
		if( defined $1 ) {
		    $v->{'FilterAction'} &= 0x7F;
		} else {
		    $v->{'FilterAction'} |= 0x80;
		}
		next
	}
	if( /^(not?)?alert$/i ) {
		$v->{'FilterAction'} = 0  if( !defined( $v->{'FilterAction'} ));
		if( defined $1 ) {
		    $v->{'FilterAction'} &= 0xFE;
		} else {
		    $v->{'FilterAction'} |= 0x01;
		}
		next
	}
	if( /^([0-9]+)$/ ) {
	    if( defined($v->{'AlertSubscriptionPolicyID'} )) {
		print "  ERR: parameters contains duplicate for AlertSubscriptionPolicyID\n";
		return 1
	    }
	    $v->{'AlertSubscriptionPolicyID'}=$1;
	    next
	}
	if( $p eq "desc" && /^d:([[:xdigit:]]+h?(-[[:xdigit:]]+h?){7})$/i ) {
	    for my $i ( split( '-', $1 ) ) {
		push @{ $v->{'EventData'}->{'Byte'} }, ($i=~/([[:xdigit:]]+)h/ ? hex($1) : $i );
	    }
	    next
	}
	print "  ERR: wrong parameter '$_'.\n";
	return 1
    }

    if( !defined($v->{'AlertSubscriptionPolicyID'}) && $p eq "add") {
	print "  ERR: parameters should contains AlertSubscriptionPolicyID value!\n";
	return 1;
    }
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub set_new_filter_def_params {
    my $v = shift;

    for ( qw/DeviceAddress EventSensorType EventType EventSourceType SensorNumber/ ) {
	$v->{$_}=0xFF  if( !defined $v->{$_}) }

    for ( qw/FilterConfiguration FilterAction EventSeverity Entity EntityInstance/ ) {
	$v->{$_}=0  if( !defined $v->{$_}) }

    $v->{'EventOffset'}=0xF  if( !defined $v->{'EventOffset'} );
}
#-------------------------------------------------------------------------------------
sub cmnd_event_filter_add {
    my $cmd = shift;
    my @params = @_;
    my ( $ret, @arg, @str, %params );

    if( lc($cmd) eq "help" || !@params ) {
	print_usage_header();
	print STDERR "    event filter_add <filter_parameters>\n",
		"\t<filter_parameters>: [(en|dis)[able]] [[Not]Log] [[Not]Alert]\n",
		"\t\t<alert_subscription_policy_id> [est:<num>] [et:<num>] [eo:<num>]\n",
		"\t\t[st:<num>] [s:<severity>] [dev:<num>] [sn:<num>] [e:<num>] [ei:<num>]\n",
		"\t<num> - number: decimal or hex (with 'h' suffix)\n",
		"\test=EventSensorType et=EventType eo=EventOffset st=EventSourceType\n",
		"\tdev=DeviceAddress sn=SensorNumber e=Entity ei=EntityInstance\n",
		"\t<severity> one of: ", join( ' ', sort keys %event_severity_code ),
		"\n\tdefaults: Disable NotLog NotAlert, <num>='match all' values, severity: Unspecified\n";
	return 0;
    }

    return -1  if( parse_filter_params( \%params, "add", @params ));

    print "  Add the New Event Filter with the parameters:";

    set_new_filter_def_params \%params;
    print_filter_details \%params;

    print "\t";
    return -1  if( ! get_user_confirm() );

    for ( keys %params ) {
	push @arg, SOAP::Data->name( "$_" => $params{$_} );
    }
    my $arg = SOAP::Data->name( 'EventFilter' => \SOAP::Data->value( @arg ) );
    $ret = do_soap $ems, "AddEventFilter", $arg;

    print "\n  NOTE: Maximum number of filters - 16 (AMT pre 2.5), 17 (AMT ver. 2.6+).\n"
      if( $ret == $pt_status{'PT_STATUS_MAX_LIMIT_REACHED'});

    return $ret;
}

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub set_update_filter_full_params {
    my $hndl= shift;
    my $v = shift;
    my $orig;

    for( qw/DeviceAddress EventSensorType EventType EventSourceType SensorNumber EventOffset
	FilterConfiguration FilterAction EventSeverity Entity EntityInstance AlertSubscriptionPolicyID/ )
    {
	next  if( defined $v->{$_});

	$orig = get_filter_details( $hndl )
	  if( !defined $orig );
	return 1  if( ref($orig) ne "HASH");

	$v->{$_} = $orig->{$_};
    }
    return 0;
}
#-------------------------------------------------------------------------------------
sub cmnd_event_filter_update {
    my $cmd = shift;
    my $hndl= shift;
    my @params = @_;
    my ( @arg, @s_arg, %params );

    if( lc($cmd) eq "help" || !@params || !defined($hndl) || $hndl !~ m/^[0-9]+$/ ) {
	print_usage_header();
	print STDERR "    event filter_update <filter_handle> <filter_parameters>\n",
		"\t<filter_parameters> format like for the 'event filter_add' with exceptions:\n",
		"\t<alert_subscription_policy_id> is optional\n",
		"\tunspecified parameters remains unchanged\n";
	return 0;
    }

    return -1  if( parse_filter_params( \%params, "upd", @params ));

    print "  Update the Event Filter (handle: $hndl) with the parameters change:";
    print_filter_details \%params;

    return -1  if( set_update_filter_full_params $hndl, \%params );

    print "\t";
    return -1  if( ! get_user_confirm() );

    for ( keys %params ) {
	push @s_arg, SOAP::Data->name( "$_" => $params{$_} );
    }

    push @arg, SOAP::Data->name( 'EventFilterHandle' => $hndl );
    push @arg, SOAP::Data->name( 'EventFilter' => \SOAP::Data->value( @s_arg ) );
    return do_soap $ems, "UpdateEventFilter", @arg;
}

#-------------------------------------------------------------------------------------
sub cmnd_event_filter_remove {
    my ( $cmd, $hndl ) = @_;

    if( lc($cmd) eq "help" || !defined($hndl) || $hndl !~ m/^[0-9]+$/ ) {
	print_usage_header();
	print STDERR "    event filter remove <filter_handle>\n";
	return 0;
    }

    print "  Remove the Event Filter with the handle '$hndl': ";
    return -1  if( ! get_user_confirm() );

    my $arg = SOAP::Data->name( 'EventFilterHandle' => $hndl );
    return do_soap $ems, "RemoveEventFilter", $arg;
}

#-------------------------------------------------------------------------------------
sub cmnd_event_description_get {
    my $cmd = shift;
    my @params = @_;
    my %params;

    if( lc($cmd) eq "help" || !@params ) {
	print_usage_header();
	print STDERR "    event desc[ription]_get est:<num> et:<num> eo:<num> [d:<event_data>]\n",
		"\test=EventSensorType et=EventType eo=EventOffset\n",
		"\t<num> - number: decimal or hex (with 'h' suffix)\n",
		"\t<event_data> - <num>-...-<num> (8 bytes)\n";
	return 0;
    }

    return -1  if( parse_filter_params( \%params, "desc", @params ));

    init_event_desc_var;
    print "Event: ", get_event_desc( \%params ), "\n";
}

#-------------------------------------------------------------------------------------
sub cmnd_event_description_list {
    my $cmd = shift;
    my ( $ret, @arg, @str, %params );

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    event desc[ription]_list\n",
		"\tlist of the known event descriptions with it's codes\n";
	return 0;
    }
    init_event_desc_var;
    print "# Event Code                \tEvent Desription\n",
	"# (code format: EventSensorType;EventType;EventOffset[;EventData]\n";
    for ( sort keys %event_desc ) {
	printf " %-28s\t%s\n", $_, $event_desc{$_};
    }
}

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub print_alert_subscr_details_deprec($) {
	my $hndl = shift;

	my $arg = SOAP::Data->name( 'SubscriptionID' => $hndl );
	my $ret = $ems->GetAlertSubscription( $arg );
	return $ret->result  if( print_err_result($ret) );

	$arg = $ret->valueof('//SubscriptionInfo');
	print "     Alert Subscription Policy ID:\t", $arg->{'AlertSubscriptionPolicyID'},
	    "\n     Destination IP Address:    \t", conv_ipv4_to_txt( $arg->{'DestinationAddress'} ),
	    "\n";
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub print_alert_subscr_details_new($) {
	my $hndl = shift;

	my $arg = SOAP::Data->name( 'SubscriptionHandle' => $hndl );
	my $ret = $ems->GetGeneralAlertSubscription( $arg );
	return $ret->result  if( print_err_result($ret) );

	$arg = $ret->valueof('//SubscriptionInfo');
	print "     Policy ID:   \t", $arg->{'PolicyID'};

	if( defined( $arg->{'AlertSubscriptionSNMP'}) ) {
	    print "\n     Subscription Type:\tSNMP\n     Dest. Address:\t",
		values %{ $arg->{'AlertSubscriptionSNMP'}->{'Address'} };

	    print "\n     Community String:\t",
		$arg->{'AlertSubscriptionSNMP'}->{'CommunityString'}
	      if( defined($arg->{'AlertSubscriptionSNMP'}->{'CommunityString'}) );
	}
	else {
	    print "\n     Subscription Type:\tSOAP\n     Dest. Address:\t",
		$arg->{'AlertSubscriptionSOAP'}->{'Address'};

	    print "\n     User Credentials:\t",
		$arg->{'AlertSubscriptionSOAP'}->{'UserCredentials'}->{'Username'}
	      if( defined($arg->{'AlertSubscriptionSOAP'}->{'UserCredentials'}) );

	    print "\n     Alert Auth Options:\t",
		join( ' ', $arg->{'AlertSubscriptionSOAP'}->{'AlertAuthOptions'} )
	      if( defined($arg->{'AlertSubscriptionSOAP'}->{'AlertAuthOptions'}) );
	}
	print "\n";
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub alert_subs_list_new($) {
	my $pol_id = shift;
	my ( $arg, $ret, $totl );

	if( defined($pol_id) ) {
	    $arg = SOAP::Data->name( 'PolicyID' => $pol_id );
	    $ret = $ems->EnumerateGeneralAlertSubscriptions( $arg );
	} else {
	    $ret = $ems->EnumerateGeneralAlertSubscriptions;
	}

	if( print_err_result($ret) ) {
	    print "  Try to use deprecated command version by setting AMT_VERSION=2.4 (envir.variable)\n"
	      if( $ret->result == $pt_status{'PT_STATUS_INTERNAL_ERROR'} );
	    return $ret->result;
	}

	my @arr = $ret->valueof('//SubscriptionHandle');

	if( ! @arr ) {
	    print "  No Registered Alert Subscriptions",
		(defined $pol_id ? " (for PolicyID = $pol_id)" : ""), ".\n";
	    return 0;
	}

	$totl = scalar @arr;
	print "  Registered $totl Alert Subscription",
		(defined $pol_id ? " (for PolicyID = $pol_id)" : ""),
		($totl == 1 ? "" : "s"), ($totl ? ":" : "."), "\n";

	foreach my $hndl ( @arr ) {
	    print "   Alert Subscription [handle $hndl]:\n";
	    print_alert_subscr_details_new( $hndl );
	}
	return 0;
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub alert_subs_list_deprec($) {
    my $pol_id = shift;
    my ( $totl, @arg, $ret );
    my $start = 1;

    do {
	push @arg, SOAP::Data->name( 'StartIndex' => $start );
	push @arg, SOAP::Data->name( 'AlertSubscriptionPolicyID' => $pol_id )
	  if( defined($pol_id) );
	$ret = $ems->EnumerateAlertSubscriptions( @arg );
	return $ret->result  if( print_err_result($ret) );

	$totl = $ret->valueof('//TotalSubscriptionCount');

	print "  Registered $totl Alert Subscription",
		(defined $pol_id ? " (for PolicyID=$pol_id)" : ""),
		($totl == 1 ? "" : "s"), ($totl ? ":" : "."), "\n"
	  if( $start == 1 );

	return 0  if( $totl == 0  &&  $ret->valueof('//SubscriptionsReturned') == 0 );

	my @arr = ($totl == 1) ? $ret->valueof('//SubscriptionHandles')->{'AlertSubscriptionHandle'} :
			    @{ $ret->valueof('//SubscriptionHandles')->{'AlertSubscriptionHandle'} };
	foreach my $hndl ( @arr ) {
	    print "   Alert Subscription [handle $hndl]:\n";
	    print_alert_subscr_details_deprec( $hndl );
	    $start++;
	}
    } while( $totl > $start );
    return 0;
}
#-------------------------------------------------------------------------------------
sub cmnd_event_alert_subs_list {
    my $cmd = shift;
    my $pol_id = shift;

    if( lc($cmd) eq "help" || defined($pol_id) && $pol_id !~ m/^[0-9]+$/ ) {
	print_usage_header();
	print STDERR "    event alert_subs_list [<alert_subscription_policy_id>]\n";
	return 0;
    }

    if( check_amt_version(2,5) ) {
	return alert_subs_list_new( $pol_id ); }
    else {
	return alert_subs_list_deprec( $pol_id ); }
}

#-------------------------------------------------------------------------------------
sub cmnd_event_alert_subs_cancel {
    my ( $cmd, $hndl ) = @_;

    if( lc($cmd) eq "help" || !defined($hndl) || $hndl !~ m/^[0-9]+$/ ) {
	print_usage_header();
	print STDERR "    event alert_subs_cancel <subscription_handle>\n";
	return 0;
    }

    print "  Cancel the Subscription to Alert with the handle '$hndl': ";
    return -1  if( ! get_user_confirm() );

    my $arg = SOAP::Data->name( 'SubscriptionHandle' => $hndl );

    return do_soap $ems, "CancelAlertSubscription", $arg;
}

#-------------------------------------------------------------------------------------
sub cmnd_event_alert_pol_list {
    my $cmd = shift;
    my ( $totl, @arg );
    my $start = 1;

    if( lc($cmd) eq "help" ) {
	print_usage_header();
	print STDERR "    event alert_pol_list\n";
	return 0;
    }

    do {
	push @arg, SOAP::Data->name( 'StartIndex' => $start );

	my $ret = $ems->EnumerateAlertPolicies( @arg );
	return $ret->result  if( print_err_result($ret) );

	$totl = $ret->valueof('//TotalPolicyCount');

	print "  Alert Policy handles list ($totl total)", ($totl ? ": " : ".\n" )
	  if( $start == 1 );

	return 0  if( $totl == 0  &&  $ret->valueof('//PoliciesReturned') == 0 );
	$start += $ret->valueof('//PoliciesReturned');

	my @arr = ($totl == 1) ? $ret->valueof('//PolicyHandles')->{'AlertSubscriptionPolicyID'} :
			    @{ $ret->valueof('//PolicyHandles')->{'AlertSubscriptionPolicyID'} };
	print join ' ', sort {$a <=> $b} @arr;
    } while( $totl > $start );
    print "\n";
    return 0;
}

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub subscribe_for_alert_new {
    my ($pol_id, $addr, $comm_str ) = @_;
    my ( @arg, @sub_arg );

    if( $addr =~ m/^(\d+\.){3}\d+$/ ) {
	push @sub_arg, SOAP::Data->name( 'Address' => 
			\SOAP::Data->name( 'IPv4Address' => $addr )
			  ->uri("$schemabase/Common/2006/01") );
    } else {
	push @sub_arg, SOAP::Data->name( 'Address' => 
			\SOAP::Data->name( 'IPv6Address' => $addr )
			  ->uri("$schemabase/Common/2006/01") );
    }
    push @sub_arg, SOAP::Data->name( 'CommunityString' => $comm_str )
      if( defined($comm_str) );

    push @arg, SOAP::Data->name( 'PolicyID' => $pol_id );
    push @arg, SOAP::Data->name( 'AlertSubscriptionSNMP' => \SOAP::Data->value( @sub_arg ) );
    my  $arg = SOAP::Data->name( 'SubscriptionInfo' => \SOAP::Data->value( @arg ));

    return do_soap $ems, "SubscribeForGeneralAlert", $arg;
}
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub subscribe_for_alert_deprec {
    my ($pol_id, $ipv4 ) = @_;
    my @arg;

    push @arg, SOAP::Data->name( 'AlertSubscriptionPolicyID' => $pol_id );
    push @arg, ipv4_addr( 'DestinationAddress', $ipv4 );
    my  $arg = SOAP::Data->name( 'SubscriptionInfo' => \SOAP::Data->value( @arg ));

    return do_soap $ems, "SubscribeForAlert", $arg;
}
#-------------------------------------------------------------------------------------
sub cmnd_event_alert_subscribe {
    my ( $cmd, $hndl, $addr, $comm_str ) = @_;
    my ( $ret, @arg, @str );

    if( lc($cmd) eq "help" || !defined($hndl) || $hndl !~ m/^[0-9]+$/
	|| !defined($addr) || defined($comm_str) && length($comm_str) > 16 ) {
	print_usage_header();
	print STDERR "    event alert_subscribe <alert_subscription_policy_id> <address> [<community>]\n",
		"\t<address> - ip_v4 address only for the devices with iAMT ver. pre 2.5\n",
		"\t<address> - ip_v4 or ip_v6 for the devices with iAMT ver. 2.5+\n",
		"\t<community> for iAMT pre 2.5 ver. ignored, use 'alert_community' command\n",
		"\t<community> only for iAMT 2.5+, max 16 printable chars\n";
	return 0;
    }

    if( check_amt_version(2,5) ) {
	if( $addr !~ m/^([[:xdigit:]]{0,4}:){7}[[:xdigit:]]{0,4}$/
	    && $addr !~ m/^[0-9]{1,3}(\.[0-9]{1,3}){3}$/ ) {
	    print "  Error in IP address parameter.\n";
	    return -1;
	}
    } else {
	if( $addr !~ m/^[0-9]{1,3}(\.[0-9]{1,3}){3}$/ ) {
	    print "  Error in IPv4 address parameter.\n";
	    return -1;
	}
	print "  WARN: CommunityString Ignored (AMT ver. $amt_version)\n"
	  if( defined($comm_str) );
    }

    print "  Subscribe for Alert with Policy_ID '$hndl' to address '$addr' ";
    return -1  if( ! get_user_confirm() );

    if( check_amt_version(2,5) ) {
	$ret = subscribe_for_alert_new( $hndl, $addr, $comm_str ); }
    else {
	$ret = subscribe_for_alert_deprec( $hndl, $addr ); }

    print "\n  NOTE: Maximum User subscriptions - 8 (AMT pre 2.5), 16 (up to 14 SNMP for AMT 2.5+).\n"
      if( $ret == $pt_status{'PT_STATUS_MAX_LIMIT_REACHED'});

    return $ret;
}

#-------------------------------------------------------------------------------------
sub cmnd_event {
    my @params = @_;
    my $cmd = shift;
    my $ret = -3;

    $cmd = lc($cmd);

    if( $cmd eq "help" || !@params) {
	print_usage_header();
	print STDERR "  Global parameter '--num[eric]|-n' forces numeric data output\n";

	$cmd = "help";
	@params = ( $cmd );
    } else {
	return cmnd_event_description_get(@params)   if( $cmd =~ m/^desc(ription)_get?$/ );
	return cmnd_event_description_list(@params)  if( $cmd =~ m/^desc(ription)_list?$/ );
	print "## '$amt_host' :: AMT Event Manager\n";
    }

    if( $cmd =~ m/^sensors_list|help$/ ) {
	$ret = cmnd_event_sensors_list(@params);
    }
    if( $cmd =~ m/^log_status|help$/ ) {
	$ret = cmnd_event_log_status(@params);
    }
    if( $cmd =~ m/^log_clear|help$/ ) {
	$ret = cmnd_event_log_clear(@params);
    }
    if( $cmd =~ m/^log_freeze|help$/ ) {
	$ret = cmnd_event_log_freeze(@params);
    }
    if( $cmd =~ m/^log_list|help$/ ) {
	$ret = cmnd_event_log_list(@params);
    }
    if( $cmd =~ m/^help$/ ) {
	$ret = cmnd_event_description_get(@params);
	$ret = cmnd_event_description_list(@params);
    }
    if( $cmd =~ m/^filter_list|help$/ ) {
	$ret = cmnd_event_filter_list(@params);
    }
    if( $cmd =~ m/^filter_add|help$/ ) {
	$ret = cmnd_event_filter_add(@params);
    }
    if( $cmd =~ m/^filter_update|help$/ ) {
	$ret = cmnd_event_filter_update(@params);
    }
    if( $cmd =~ m/^filter_remove|help$/ ) {
	$ret = cmnd_event_filter_remove(@params);
    }
    if( $cmd =~ m/^alert_subs_list|help$/ ) {
	$ret = cmnd_event_alert_subs_list(@params);
    }
    if( $cmd =~ m/^alert_subscribe|help$/ ) {
	$ret = cmnd_event_alert_subscribe(@params);
    }
    if( $cmd =~ m/^alert_subs_cancel|help$/ ) {
	$ret = cmnd_event_alert_subs_cancel(@params);
    }
    if( $cmd =~ m/^alert_community|help$/ ) {
	$ret = cmnd_event_alert_community(@params);
    }
    if( $cmd =~ m/^alert_pol_list|help$/ ) {
	$ret = cmnd_event_alert_pol_list(@params);
    }

    print_newline( $cmd );
    return $ret  if( $ret >= -2 );

    print " Unknown command: event $cmd\n\n";
    return -4;
}
#=====================================================================================



######################################################################################
# main

if( !defined($amt_host)) {
    usage();
    exit 0;
}

soap_init;

my $ret;

if( defined($amt_cmnd) )
{
    $amt_cmnd = lc($amt_cmnd);

    # params backward compatibility
    if( $amt_cmnd eq "netinfo") {
	$amt_cmnd = "net";
	@ARGV = ("info", @ARGV);
    } elsif( $amt_cmnd eq "netconf") {
	$amt_cmnd = "net";
	@ARGV = ("conf", @ARGV);
    } elsif( $amt_cmnd =~ m/^(reset|power(up|down|cycle)|setbootopt)$/) {
	@ARGV = ( $amt_cmnd, @ARGV );
	$amt_cmnd = "rem_control";
    } elsif( $amt_cmnd eq "help" ) {
	@ARGV = ( "help" );
    }
} else {
    if( lc($amt_host) eq "help" ) {
	$amt_cmnd = "help";
	@ARGV = ( "help" );
    } else {
	$amt_cmnd = "info";
    }
}

if( $amt_cmnd =~ m/^rem_control|help$/) {
	$ret = cmnd_rem_control(@ARGV);
}
if( $amt_cmnd =~ m/^info|help$/) {
	$ret = cmnd_info(@ARGV);
}
if( $amt_cmnd =~ m/^net|help$/) {
	$ret = cmnd_net(@ARGV);
}
if( $amt_cmnd =~ m/^user|help$/) {
	$ret = cmnd_user(@ARGV);
}
if( $amt_cmnd =~ m/^uuser|help$/) {
	$ret = cmnd_uuser(@ARGV);
}
if( $amt_cmnd =~ m/^time|help$/) {
	$ret = cmnd_time(@ARGV);
}
if( $amt_cmnd =~ m/^hwasset|help$/) {
	$ret = cmnd_hwasset(@ARGV);
}
if( $amt_cmnd =~ m/^audit|help$/) {
	$ret = cmnd_audit(@ARGV);
}
if( $amt_cmnd =~ m/^security|help$/) {
	$ret = cmnd_security(@ARGV);
}
if( $amt_cmnd =~ m/^redirect|help$/) {
	$ret = cmnd_redirect(@ARGV);
}
if( $amt_cmnd =~ m/^pwr_save|help$/) {
	$ret = cmnd_power(@ARGV);
}
if( $amt_cmnd =~ m/^rem_access|helpp$/) {
	$ret = cmnd_rem_access(@ARGV);
}
if( $amt_cmnd =~ m/^event|help$/) {
	$ret = cmnd_event(@ARGV);
}
if( !defined($ret)) {
	usage();
	$ret = -1;
}

exit $ret;


#=====================================================================================
sub init_event_desc_var()
{
    %event_desc = (
	"0;1;0" =>	"Lower Non-Critical, sensor Going Low",
	"0;1;1" =>	"Lower Non-Critical, sensor Going High",
	"0;1;2" =>	"Lower Critical, sensor Going Low",
	"0;1;3" =>	"Lower Critical, sensor Going High",
	"0;1;4" =>	"Lower Non-Recoverable, sensor Going Low",
	"0;1;5" =>	"Lower Non-Recoverable, sensor Going High",
	"0;1;6" =>	"Upper Non-Critical, sensor Going Low",
	"0;1;7" =>	"Upper Non-Critical, sensor Going High",
	"0;1;8" =>	"Upper Critical, sensor Going Low",
	"0;1;9" =>	"Upper Critical, sensor Going High",
	"0;1;A" =>	"Upper Non-Recoverable, sensor Going Low",
	"0;1;B" =>	"Upper Non-Recoverable, sensor Going High",
	"0;2;0" =>	"Sensor transition to Idle",
	"0;2;1" =>	"Sensor transition to Active",
	"0;2;2" =>	"Sensor transition to Busy",
	"0;3;0" =>	"Sensor state De-asserted",
	"0;3;1" =>	"Sensor state Asserted",
	"0;4;0" =>	"Sensor predictive failure De-asserted",
	"0;4;1" =>	"Sensor predictive failure Asserted",
	"0;5;0" =>	"Sensor limit Not Exceeded",
	"0;5;1" =>	"Sensor limit Exceeded",
	"0;6;0" =>	"Sensor performance Met",
	"0;6;1" =>	"Sensor performance Lags",
	"0;7;0" =>	"Sensor transition to OK",
	"0;7;1" =>	"Sensor transition to Non-Critical from OK",
	"0;7;2" =>	"Sensor transition to Critical from Less Severe",
	"0;7;3" =>	"Sensor transition to Non-Recoverable from Less Severe",
	"0;7;4" =>	"Sensor transition to Non-critical from More Severe",
	"0;7;5" =>	"Sensor transition to Critical from Non-Recoverable",
	"0;7;6" =>	"Sensor transition to Non-Recoverable",
	"0;7;7" =>	"Sensor Monitor",
	"0;7;8" =>	"Sensor Informational",
	"0;8;0" =>	"Device Removed",
	"0;8;1" =>	"Device Inserted",
	"0;9;0" =>	"Device Disabled",
	"0;9;1" =>	"Device Enabled",
	"0;A;0" =>	"Sensor transitioned to Running",
	"0;A;1" =>	"Sensor transitioned to In Test",
	"0;A;2" =>	"Sensor transitioned to Power off",
	"0;A;3" =>	"Sensor transitioned to On-line",
	"0;A;4" =>	"Sensor transitioned to Off-line",
	"0;A;5" =>	"Sensor transitioned to Off-duty",
	"0;A;6" =>	"Sensor transitioned to a Degraded state",
	"0;A;7" =>	"Sensor transitioned to Power Save state",
	"0;A;8" =>	"Sensor install Error",
	"0;B;0" =>	"Redundancy Restored",
	"0;B;1" =>	"Redundancy Lost (insufficient)",
	"0;B;2" =>	"Redundancy Lost (sufficient)",
	"0;C;0" =>	"Sensor indicates device transitioned to D0 power",
	"0;C;1" =>	"Sensor indicates device transitioned to D1 power",
	"0;C;2" =>	"Sensor indicates device transitioned to D2 power",
	"0;C;3" =>	"Sensor indicates device transitioned to D3 power",
    "5;6F;0" =>	"Physical Security - Chassis Open",
	"5;6F;1" =>	"Physical Security - Drive Bay Open",
	"5;6F;2" =>	"Physical Security - I/O Card Area Open",
	"5;6F;3" =>	"Physical Security - Processor Area Open",
	"5;6F;4" =>	"Physical Security - LAN Disconnected",
	"5;6F;5" =>	"Physical Security - Docking Permission De-asserted",
	"5;6F;6" =>	"Physical Security - Fan Area Open",
	"5;6F;80" =>	"Physical Security - Chassis Closed",
	"5;6F;81" =>	"Physical Security - Drive Bay Closed",
	"5;6F;82" =>	"Physical Security - I/O Card Area Closed",
	"5;6F;83" =>	"Physical Security - Processor Area Closed",
	"5;6F;85" =>	"Physical Security - Docking Permission Asserted",
	"5;6F;86" =>	"Physical Security - Fan Area Closed",
    "6;6F;0" =>	"Platform Security - Secure Mode (Front Panel Lockout) Violation",
	"6;6F;1" =>	"Platform Security - Pre-boot user password Violation",
	"6;6F;2" =>	"Platform Security - Pre-boot Setup password Violation",
	"6;6F;3" =>	"Platform Security - Network Boot Password Violation",
	"6;6F;4" =>	"Platform Security - Password Violation",
	"6;6F;5" =>	"Platform Security - Out-of-band Access Password Violation",
	"6;6F;5;AA;*" =>	"Platform Security - Out-of-band Access Password Violation",
	"6;6F;A;AA;0;1;*" =>	"CircuitBreaker Heuristics notif - Slow threshold trespassed - Blocked all Transmit ports",
	"6;6F;A;AA;10;1;*" =>	"CircuitBreaker Heuristics notif - Fast threshold trespassed - Blocked all Transmit ports",
	"6;6F;A;AA;20;1;*" =>	"CircuitBreaker Heuristics notif - Factory defined threshold trespassed - Blocked all Transmit ports",
	"6;6F;A;AA;30;1;*" =>	"CircuitBreaker Heuristics notif - Encounter timeout expired (system clean) - Blocked all Transmit ports",
	"6;6F;A;AA;0;2;*" =>	"CircuitBreaker Heuristics notif - Slow threshold trespassed - Blocked offending port (TCP)",
	"6;6F;A;AA;10;2;*" =>	"CircuitBreaker Heuristics notif - Fast threshold trespassed - Blocked offending port (TCP)",
	"6;6F;A;AA;20;2;*" =>	"CircuitBreaker Heuristics notif - Factory defined threshold trespassed - Blocked offending port (TCP)",
	"6;6F;A;AA;30;2;*" =>	"CircuitBreaker Heuristics notif - Encounter timeout expired (system clean) - Blocked offending port (TCP)",
	"6;6F;A;AA;0;4;*" =>	"CircuitBreaker Heuristics notif - Slow threshold trespassed - Blocked offending port (UDP)",
	"6;6F;A;AA;10;4;*" =>	"CircuitBreaker Heuristics notif - Fast threshold trespassed - Blocked offending port (UDP)",
	"6;6F;A;AA;20;4;*" =>	"CircuitBreaker Heuristics notif - Factory defined threshold trespassed - Blocked offending port (UDP)",
	"6;6F;A;AA;30;4;*" =>	"CircuitBreaker Heuristics notif - Encounter timeout expired (system clean) - Blocked offending port (UDP)",
	"6;6F;A;AA;0;8;*" =>	"CircuitBreaker Heuristics notif - Slow threshold trespassed - Enabled preconfigured CB policy",
	"6;6F;A;AA;10;8;*" =>	"CircuitBreaker Heuristics notif - Fast threshold trespassed - Enabled preconfigured CB policy",
	"6;6F;A;AA;20;8;*" =>	"CircuitBreaker Heuristics notif - Factory defined threshold trespassed - Enabled preconfigured CB policy",
	"6;6F;A;AA;30;8;*" =>	"CircuitBreaker Heuristics notif - Encounter timeout expired (system clean) - Enabled preconfigured CB policy",
	"6;6F;A;AA;0;10;*" =>	"CircuitBreaker Heuristics notif - Slow threshold trespassed - Removed restricting filters",
	"6;6F;A;AA;10;10;*" =>	"CircuitBreaker Heuristics notif - Fast threshold trespassed - Removed restricting filters",
	"6;6F;A;AA;20;10;*" =>	"CircuitBreaker Heuristics notif - Factory defined threshold trespassed - Removed restricting filters",
	"6;6F;A;AA;30;10;*" =>	"CircuitBreaker Heuristics notif - Encounter timeout expired (system clean) - Removed restricting filters",
    "7;1;3;0;0;0;0;0;0;0;0" =>	"Processor - Failed - FRB2/Hang in POST",
	"7;1;4;0;0;0;0;0;0;0;0" =>	"Processor - Failed - FRB3/CPU didn't start",
	"7;6F;0" =>	"Processor - Failed - IERR",
	"7;6F;1" =>	"Processor - Over-Temperature Condition Detected",
	"7;6F;2" =>	"Processor - Failed - FRB1/BIST",
	"7;6F;3" =>	"Processor - Failed - FRB2/Hang in POST",
	"7;6F;4" =>	"Processor - Failed - FRB3 (CPU didn't start)",
	"7;6F;5" =>	"Processor - Configuration Mismatch",
	"7;6F;6" =>	"Processor - SM BIOS `Uncorrectable CPU-complex Error'",
	"7;6F;7" =>	"Processor - Added",
	"7;6F;8" =>	"Processor - Disabled",
	"7;6F;9" =>	"Processor - Terminator Detected",
	"7;6F;A" =>	"Processor - Degraded (Automatically Throttled)",
	"7;6F;B" =>	"Machine Check Exception (Uncorrectable)",
	"7;6F;C" =>	"Correctable Machine Check Error",
	"7;6F;81" =>	"Processor - Over-Temperature Condition Removed",
	"7;6F;87" =>	"Processor - Removed",
	"7;6F;88" =>	"Processor - Enabled",
	"7;6F;8A" =>	"Processor - Not Degraded",
    "8;6F;0" =>	"Power supply - Added",
	"8;6F;1" =>	"Power supply - Failed",
	"8;6F;2" =>	"Power supply - Failure predicted",
	"8;6F;3" =>	"Power supply - Input Lost",
	"8;6F;4" =>	"Power supply - Input Lost or Out of Range",
	"8;6F;5" =>	"Power supply - Input Out of Range, but present",
	"8;6F;6" =>	"Power supply - Configuration Error",
	"8;6F;80" =>	"Power supply - Removed",
	"8;6F;83" =>	"Power supply - Input Normal",
    "9;6F;0" =>	"Power unit - Disabled",
	"9;6F;1" =>	"Power unit - Power Cycled",
	"9;6F;2" =>	"Power unit - 240VA Power Down Error",
	"9;6F;3" =>	"Power unit - Interlock Power Down Error",
	"9;6F;4" =>	"Power unit - AC Power Lost",
	"9;6F;5" =>	"Power unit - Soft power control Failed",
	"9;6F;6" =>	"Power unit - Failed",
	"9;6F;7" =>	"Power unit - Predictive Failure",
	"9;6F;80" =>	"Power unit - Enabled",
    "C;6F;0" =>	"Memory - Corrected Error detected",
	"C;6F;1" =>	"Memory - Uncorrectable Error detected",
	"C;6F;2" =>	"Memory - Parity Error detected",
	"C;6F;3" =>	"Memory - Scrub Failure",
	"C;6F;4" =>	"Memory - Disabled",
	"C;6F;5" =>	"Memory - Logging Limit Reached",
	"C;6F;6" =>	"Memory - Added",
	"C;6F;8" =>	"Memory - Double chip Sparing Initiated",
	"C;6F;9" =>	"Memory - Automatically Throttled",
	"C;6F;A" =>	"Memory - Critical Over-Temperature Detected",
	"C;6F;84" =>	"Memory - Enabled",
	"C;6F;85" =>	"Memory - Logging Limit Removed",
	"C;6F;86" =>	"Memory - Removed",
	"C;6F;88" =>	"Memory - Double chip Sparing Concluded",
	"C;6F;8A" =>	"Memory - Critical Over-Temperature Removed",
    "D;6F;0" =>	"Drive - Added",
	"D;6F;1" =>	"Drive - Disabled due to Fault",
	"D;6F;2" =>	"Drive - Failure Predicted",
	"D;6F;3" =>	"Drive - Hot Spare Enabled",
	"D;6F;4" =>	"Drive - Consistency Check Begun",
	"D;6F;5" =>	"Drive - Array Critical",
	"D;6F;6" =>	"Drive - Failed Array",
	"D;6F;7" =>	"Drive - Rebuild/Remap in Progress",
	"D;6F;8" =>	"Drive - Rebuild/Remap Aborted",
	"D;6F;80" =>	"Drive - Removed",
	"D;6F;81" =>	"Drive - Enabled",
	"D;6F;83" =>	"Drive - Hot Spare Disabled",
	"D;6F;84" =>	"Drive - Consistency Check Completed",
	"D;6F;86" =>	"Drive - Failed Array Restored",
	"D;6F;87" =>	"Drive - Rebuild Completed",
    "F;6F;0" =>	"System Firmware Error (POST Error) [category]",
	"F;6F;0;40;1;0;0;0;0;0;0" =>	"POST Err - No Memory detected (no physically installed)",
	"F;6F;0;40;2;0;0;0;0;0;0" =>	"POST Err - Memory Unrecoverable Failure",
	"F;6F;0;40;3;0;0;0;0;0;0" =>	"POST Err - HardDisk/ATAPI/IDE device Unrecoverable Failure",
	"F;6F;0;40;4;0;0;0;0;0;0" =>	"POST Err - Motherboard Unrecoverable Failure",
	"F;6F;0;40;5;0;0;0;0;0;0" =>	"POST Err - Diskette subsystem Unrecoverable Failure",
	"F;6F;0;40;6;0;0;0;0;0;0" =>	"POST Err - SMART Alert detected",
	"F;6F;0;40;7;0;0;0;0;0;0" =>	"POST Err - Keyboard Unrecoverable Failure",
	"F;6F;0;40;8;0;0;0;0;0;0" =>	"POST Err - Boot device Unrecoverable Failure",
	"F;6F;0;40;9;0;0;0;0;0;0" =>	"POST Err - Video controller Unrecoverable Failure",
	"F;6F;0;40;A;0;0;0;0;0;0" =>	"POST Err - No Video device detected",
	"F;6F;0;40;B;0;0;0;0;0;0" =>	"POST Err - Firmware (BIOS) ROM Corruption detected",
	"F;6F;0;40;C;0;0;0;0;0;0" =>	"POST Err - CPU voltage mismatch Failure",
	"F;6F;0;40;D;0;0;0;0;0;0" =>	"POST Err - CPU speed matching Failure",
	 "F;1;1;40;1;0;0;0;0;0;0" =>	"System Firmware Hang",
	 "F;6F;1" =>	"System Firmware Hang [category]",
#	"F;6F;1;40;1;0;0;0;0;0;0" =>	"SysFirmware Hang - No Memory detected (no physically installed)",
#	"F;6F;1;40;2;0;0;0;0;0;0" =>	"SysFirmware Hang - Storage subsystem Failure",
#	"F;6F;1;40;3;0;0;0;0;0;0" =>	"SysFirmware Hang - HardDisk/ATAPI/IDE device Unrecoverable Failure",
#	"F;6F;1;40;4;0;0;0;0;0;0" =>	"SysFirmware Hang - Motherboard Unrecoverable Failure",
#	"F;6F;1;40;5;0;0;0;0;0;0" =>	"SysFirmware Hang - Diskette subsystem Unrecoverable Failure",
#	"F;6F;1;40;6;0;0;0;0;0;0" =>	"SysFirmware Hang - USB subsystem Failure",
#	"F;6F;1;40;7;0;0;0;0;0;0" =>	"SysFirmware Hang - PCI Configuration Failure",
#	"F;6F;1;40;8;0;0;0;0;0;0" =>	"SysFirmware Hang - Boot device Unrecoverable Failure",
#	"F;6F;1;40;9;0;0;0;0;0;0" =>	"SysFirmware Hang - Video subsystem Failure",
	"F;6F;1;40;1;0;0;0;0;0;0" =>	"SysFirmware Hang - Memory Initialization",
	"F;6F;1;40;2;0;0;0;0;0;0" =>	"SysFirmware Hang - HD Initialization",
	"F;6F;1;40;3;0;0;0;0;0;0" =>	"SysFirmware Hang - Secondary processor(s) Initialization",
	"F;6F;1;40;4;0;0;0;0;0;0" =>	"SysFirmware Hang - User Authentication",
	"F;6F;1;40;5;0;0;0;0;0;0" =>	"SysFirmware Hang - User-initiated system setup",
	"F;6F;1;40;6;0;0;0;0;0;0" =>	"SysFirmware Hang - USB resource configuration",
	"F;6F;1;40;7;0;0;0;0;0;0" =>	"SysFirmware Hang - PCI resource configuration",
	"F;6F;1;40;8;0;0;0;0;0;0" =>	"SysFirmware Hang - Option ROM initialization",
	"F;6F;1;40;9;0;0;0;0;0;0" =>	"SysFirmware Hang - Video initialization",
	"F;6F;1;40;A;0;0;0;0;0;0" =>	"SysFirmware Hang - Cache initialization",
	"F;6F;1;40;B;0;0;0;0;0;0" =>	"SysFirmware Hang - SM Bus initialization",
	"F;6F;1;40;C;0;0;0;0;0;0" =>	"SysFirmware Hang - Keyboard controller initialization",
	"F;6F;1;40;D;0;0;0;0;0;0" =>	"SysFirmware Hang - Embedded controller/Management controller initialization",
	"F;6F;1;40;E;0;0;0;0;0;0" =>	"SysFirmware Hang - Docking station Attachment",
	"F;6F;1;40;F;0;0;0;0;0;0" =>	"SysFirmware Hang - Docking station Enable",
	"F;6F;1;40;10;0;0;0;0;0;0" =>	"SysFirmware Hang - Docking station Ejection",
	"F;6F;1;40;11;0;0;0;0;0;0" =>	"SysFirmware Hang - Docking station Disable",
	"F;6F;1;40;12;0;0;0;0;0;0" =>	"SysFirmware Hang - Calling operating system wake-up vector",
	"F;6F;1;40;13;0;0;0;0;0;0" =>	"SysFirmware Hang - Start OS boot process",
	"F;6F;1;40;14;0;0;0;0;0;0" =>	"SysFirmware Hang - Motherboard initialization",
	"F;6F;1;40;16;0;0;0;0;0;0" =>	"SysFirmware Hang - Floppy initialization",
	"F;6F;1;40;17;0;0;0;0;0;0" =>	"SysFirmware Hang - Keyboard Test",
	"F;6F;1;40;18;0;0;0;0;0;0" =>	"SysFirmware Hang - Pointing device test",
	"F;6F;1;40;19;0;0;0;0;0;0" =>	"SysFirmware Hang - Primary processor initialization",
	 "F;6F;2" =>	"System Firmware Progress [category] (Entry)",
	"F;6F;2;40;1;0;0;0;0;0;0" =>	"SysFirmware Progress - Memory Initialization (Entry)",
	"F;6F;2;40;2;0;0;0;0;0;0" =>	"SysFirmware Progress - HD Initialization (Entry)",
	"F;6F;2;40;3;0;0;0;0;0;0" =>	"SysFirmware Progress - Secondary processor(s) Initialization (Entry)",
	"F;6F;2;40;4;0;0;0;0;0;0" =>	"SysFirmware Progress - User Authentication",
	"F;6F;2;40;5;0;0;0;0;0;0" =>	"SysFirmware Progress - User-initiated system setup (Entry)",
	"F;6F;2;40;6;0;0;0;0;0;0" =>	"SysFirmware Progress - USB resource configuration (Entry)",
	"F;6F;2;40;7;0;0;0;0;0;0" =>	"SysFirmware Progress - PCI resource configuration (Entry)",
	"F;6F;2;40;8;0;0;0;0;0;0" =>	"SysFirmware Progress - Option ROM initialization (Entry)",
	"F;6F;2;40;9;0;0;0;0;0;0" =>	"SysFirmware Progress - Video initialization (Entry)",
	"F;6F;2;40;A;0;0;0;0;0;0" =>	"SysFirmware Progress - Cache initialization (Entry)",
	"F;6F;2;40;B;0;0;0;0;0;0" =>	"SysFirmware Progress - SM Bus initialization (Entry)",
	"F;6F;2;40;C;0;0;0;0;0;0" =>	"SysFirmware Progress - Keyboard controller initialization (Entry)",
	"F;6F;2;40;D;0;0;0;0;0;0" =>	"SysFirmware Progress - Embedded controller/Management controller initialization (Entry)",
	"F;6F;2;40;E;0;0;0;0;0;0" =>	"SysFirmware Progress - Docking station Attachment (Entry)",
	"F;6F;2;40;F;0;0;0;0;0;0" =>	"SysFirmware Progress - Docking station Enable (Entry)",
	"F;6F;2;40;10;0;0;0;0;0;0" =>	"SysFirmware Progress - Docking station Ejection (Entry)",
	"F;6F;2;40;11;0;0;0;0;0;0" =>	"SysFirmware Progress - Docking station Disable (Entry)",
	"F;6F;2;40;12;0;0;0;0;0;0" =>	"SysFirmware Progress - Calling operating system wake-up vector (Entry)",
	"F;6F;2;40;13;0;0;0;0;0;0" =>	"SysFirmware Progress - Start OS boot process",
	"F;6F;2;40;14;0;0;0;0;0;0" =>	"SysFirmware Progress - Motherboard initialization (Entry)",
	"F;6F;2;40;16;0;0;0;0;0;0" =>	"SysFirmware Progress - Floppy initialization (Entry)",
	"F;6F;2;40;17;0;0;0;0;0;0" =>	"SysFirmware Progress - Keyboard Test (Entry)",
	"F;6F;2;40;18;0;0;0;0;0;0" =>	"SysFirmware Progress - Pointing device test (Entry)",
	"F;6F;2;40;19;0;0;0;0;0;0" =>	"SysFirmware Progress - Primary processor initialization (Entry)",
	 "F;6F;82" =>	"System Firmware Progress [category] (Exit)",
	"F;6F;82;40;1;0;0;0;0;0;0" =>	"SysFirmware Progress - Memory Initialization (Exit)",
	"F;6F;82;40;2;0;0;0;0;0;0" =>	"SysFirmware Progress - HD Initialization (Exit)",
	"F;6F;82;40;3;0;0;0;0;0;0" =>	"SysFirmware Progress - Secondary processor(s) Initialization (Exit)",
	"F;6F;82;40;4;0;0;0;0;0;0" =>	"SysFirmware Progress - User Authentication (Exit)",
	"F;6F;82;40;5;0;0;0;0;0;0" =>	"SysFirmware Progress - User-initiated system setup (Exit)",
	"F;6F;82;40;6;0;0;0;0;0;0" =>	"SysFirmware Progress - USB resource Configuration (Exit)",
	"F;6F;82;40;7;0;0;0;0;0;0" =>	"SysFirmware Progress - PCI resource Configuration (Exit)",
	"F;6F;82;40;8;0;0;0;0;0;0" =>	"SysFirmware Progress - Option ROM Initialization (Exit)",
	"F;6F;82;40;9;0;0;0;0;0;0" =>	"SysFirmware Progress - Video Initialization (Exit)",
	"F;6F;82;40;A;0;0;0;0;0;0" =>	"SysFirmware Progress - Cache Initialization (Exit)",
	"F;6F;82;40;B;0;0;0;0;0;0" =>	"SysFirmware Progress - SM Bus initialization (Exit)",
	"F;6F;82;40;C;0;0;0;0;0;0" =>	"SysFirmware Progress - Keyboard controller Initialization (Exit)",
	"F;6F;82;40;D;0;0;0;0;0;0" =>	"SysFirmware Progress - Embedded controller/Management controller initialization (Exit)",
	"F;6F;82;40;E;0;0;0;0;0;0" =>	"SysFirmware Progress - Docking station Attachment (Exit)",
	"F;6F;82;40;F;0;0;0;0;0;0" =>	"SysFirmware Progress - Dock Enable (Exit)",
	"F;6F;82;40;10;0;0;0;0;0;0" =>	"SysFirmware Progress - Docking station Ejection (Exit)",
	"F;6F;82;40;11;0;0;0;0;0;0" =>	"SysFirmware Progress - Dock Disable (Exit)",
	"F;6F;82;40;12;0;0;0;0;0;0" =>	"SysFirmware Progress - Call OS Wake vector (Exit)",
	"F;6F;82;40;13;0;0;0;0;0;0" =>	"SysFirmware Progress - Start OS boot process (Exit)",
	"F;6F;82;40;14;0;0;0;0;0;0" =>	"SysFirmware Progress - Motherboard Initialization (Exit)",
	"F;6F;82;40;16;0;0;0;0;0;0" =>	"SysFirmware Progress - Floppy initialization (Exit)",
	"F;6F;82;40;17;0;0;0;0;0;0" =>	"SysFirmware Progress - Keyboard Test (Exit)",
	"F;6F;82;40;18;0;0;0;0;0;0" =>	"SysFirmware Progress - Pointing device Test (Exit)",
	"F;6F;82;40;19;0;0;0;0;0;0" =>	"SysFirmware Progress - Primary processor initialization (Exit)",
	 "F;71;5;AA;AC;AC;0;0" =>	"Host WakeUp Notification - Host power state S0",
	"F;71;5;AA;AC;AC;1;0" =>	"Host WakeUp Notification - Host power state S1",
	"F;71;5;AA;AC;AC;2;0" =>	"Host WakeUp Notification - Host power state is reserved",
	"F;71;5;AA;AC;AC;3;0" =>	"Host WakeUp Notification - Host power state S3",
	"F;71;5;AA;AC;AC;4;0" =>	"Host WakeUp Notification - Host power state S4",
	"F;71;5;AA;AC;AC;5;0" =>	"Host WakeUp Notification - Host power state S5",
	"F;71;5;AA;AC;AC;6;0" =>	"Host WakeUp Notification - Host power state is unknown",
    "10;6F;0" =>	"Logging - Correctable Memory Error Logging Disabled",
	"10;6F;1" =>	"Logging - Event 'Type','Offset' Logging Disabled",
	"10;6F;2" =>	"Logging - Log Cleared",
	"10;6F;3" =>	"Logging - All Event Logging Disabled",
	"10;6F;4" =>	"Logging - Log Full",
	"10;6F;5" =>	"Logging - Log Almost Full",
	"10;6F;6" =>	"Logging - Install Error (Correctable Machine Check Error Logging Disabled)",
	"10;6F;80" =>	"Logging - Correctable Memory Error Logging Enabled",
	"10;6F;81" =>	"Logging - Event 'Type','Offset' Logging Enabled",
	"10;6F;83" =>	"Logging - All Event Logging Enabled",
	"10;6F;84" =>	"Logging - Log No Longer full",
    "11;6F;0" =>	"Watchdog (BIOS) - Reset",
	"11;6F;1" =>	"Watchdog (OS) - Reset",
	"11;6F;2" =>	"Watchdog (OS) - Shut Down",
	"11;6F;3" =>	"Watchdog (OS) - Power Down",
	"11;6F;4" =>	"Watchdog (OS) - Power Cycle",
	"11;6F;5" =>	"Watchdog (OS) - NMI/Diagnostic Interrupt",
	"11;6F;6" =>	"Watchdog (OS) - Expired, status only",
	"11;6F;7" =>	"Watchdog (OS) - Pre-timeout Interrupt, non-NMI",
    "12;6F;0" =>	"SysEvent - System Reconfigured",
	"12;6F;1" =>	"SysEvent - OEM System Boot Event",
	"12;6F;2" =>	"SysEvent - Unknown System Hardware Failure",
	"12;6F;3" =>	"SysEvent - Auxilary Log Entry Event",
	"12;6F;4" =>	"SysEvent - PEF Action Executed",
	"12;6F;5" =>	"SysEvent - Timestamp Clock Synch",
	 "12;A;0;AA;70;18;B0;A1;2F;29;1" =>	"Agent Presence - Not started",
	"12;A;0;AA;70;18;B0;A1;2F;29;2" =>	"Agent Presence - Stopped",
	"12;A;0;AA;70;18;B0;A1;2F;29;4" =>	"Agent Presence - Running",
	"12;A;0;AA;70;18;B0;A1;2F;29;8" =>	"Agent Presence - Expired",
	"12;A;0;AA;70;18;B0;A1;2F;29;10" =>	"Agent Presence - Suspended",
	"12;A;0;AA;*;1" =>	"Agent Presence - Not started",
	"12;A;0;AA;*;2" =>	"Agent Presence - Stopped",
	"12;A;0;AA;*;4" =>	"Agent Presence - Running",
	"12;A;0;AA;*;8" =>	"Agent Presence - Expired",
	"12;A;0;AA;*;10" =>	"Agent Presence - Suspended",
    "13;6F;0" =>	"Critical Interrupt - Front Panel NMI/Diagnostic Interrupt",
	"13;6F;1" =>	"Critical Interrupt - Bus Timeout",
	"13;6F;2" =>	"Critical Interrupt - I/O Channel Check NMI",
	"13;6F;3" =>	"Critical Interrupt - Software NMI",
	"13;6F;4" =>	"Critical Interrupt - PCI PERR",
	"13;6F;5" =>	"Critical Interrupt - PCI SERR",
	"13;6F;6" =>	"Critical Interrupt - EISA Fail Safe Timeout",
	"13;6F;7" =>	"Critical Interrupt - Bus Correctable Error",
	"13;6F;8" =>	"Critical Interrupt - Bus Uncorrectable Error",
	"13;6F;9" =>	"Critical Interrupt - Fatal NMI",
	"13;6F;A" =>	"Critical Interrupt - Bus Fatal Error",
	"13;6F;B" =>	"Critical Interrupt - Bus Degraded",
	"13;6F;83" =>	"Critical Interrupt - Software NMI Recovered",
	"13;6F;8B" =>	"Critical Interrupt - Bus No Longer Degraded",
    "14;6F;0" =>	"Button - Power Button Pressed",
	"14;6F;1" =>	"Button - Sleep Button Pressed",
	"14;6F;2" =>	"Button - Reset Button Pressed",
	"14;6F;3" =>	"Button - FRU Latch Opened",
	"14;6F;4" =>	"Button - FRU Service Request",
	"14;6F;80" =>	"Button - Power Button Released",
	"14;6F;81" =>	"Button - Sleep Button Released",
	"14;6F;82" =>	"Button - Reset Button Released",
	"14;6F;83" =>	"Button - FRU Latch Closed",
	"14;6F;84" =>	"Button - FRU Service Request completed",
    "19;6F;0" =>	"ChipSet - Soft Power Control Failure",
	"19;6F;1" =>	"ChipSet - Thermal Trip",
    "1B;6F;0" =>	"Cable/Interconnect - Connected",
	"1B;6F;1" =>	"Cable/Interconnect - Configuration Error",
	"1B;6F;80" =>	"Cable/Interconnect - Disconnected",
	"1B;6F;81" =>	"Cable/Interconnect - Configuration Repaired",
    "1D;6F;0" =>	"System Boot/Restart - by Power On",
	"1D;6F;1" =>	"System Boot/Restart - by Hard Power Cycle/Reset",
	"1D;6F;2" =>	"System Boot/Restart - by Soft Power Cycle/Reset",
	"1D;6F;3" =>	"System Boot/Restart - PXE Boot Requested",
	"1D;6F;4" =>	"System Boot/Restart - Diagnostics Boot Requested",
	"1D;6F;5" =>	"System Boot/Restart - Hard Reset (by OS/Run-time software)",
	"1D;6F;6" =>	"System Boot/Restart - Warm Reset (by OS/Run-time software)",
	"1D;6F;7" =>	"System Boot/Restart - System Restart Requested",
	"1D;6F;87" =>	"System Boot/Restart - System Restarted",
    "1E;6F;0" =>	"Boot Error - No Bootable Media",
	"1E;6F;1" =>	"Boot Error - Non-bootable Media",
	"1E;6F;2" =>	"Boot Error - PXE Server Not Found",
	"1E;6F;3" =>	"Boot Error - Invalid boot sector",
	"1E;6F;4" =>	"Boot Error - User-timeout on boot",
    "1F;6F;0" =>	"OS Boot - from Floppy",
	"1F;6F;1" =>	"OS Boot - from Local Drive Completed",
	"1F;6F;2" =>	"OS Boot - PXE Boot Completed",
	"1F;6F;3" =>	"OS Boot - Diags Boot Completed",
	"1F;6F;4" =>	"OS Boot - Completed",
	"1F;6F;5" =>	"OS Boot - ROM Boot Completed",
	"1F;6F;6" =>	"OS Boot - Boot Completed",
    "20;6F;0" =>	"OS Critical Stop During OS Load/Initialization",
	"20;6F;1" =>	"OS Run-time Critical Stop (`core dump',`blue screen',etc.)",
	"20;6F;1;0;0;0;0;0;0;0;0" =>	"OS Run-time Critical Stop (`core dump',`blue screen',etc.)",
	"20;6F;2" =>	"OS Graceful Stop",
	"20;6F;3" =>	"OS Graceful Shutdown Begun",
	"20;6F;4" =>	"OS Stop/Shutdown - Soft Shutdown initiated by PEF",
	"20;6F;5" =>	"OS Stop/Shutdown - Agent Not Responding",
	"20;6F;83" =>	"OS Graceful Shutdown Completed",
	"20;6F;85" =>	"OS Stop/Shutdown - Agent Responding",
    "21;6F;0" =>	"Slot/Connector - Fault status Asserted",
	"21;6F;1" =>	"Slot/Connector - Identify status Asserted",
	"21;6F;2" =>	"Slot/Connector - Device Installed/Attached",
	"21;6F;3" =>	"Slot/Connector - Ready for Device Installation",
	"21;6F;4" =>	"Slot/Connector - Ready for Device Removal",
	"21;6F;5" =>	"Slot/Connector - Soft Power is Off",
	"21;6F;6" =>	"Slot/Connector - Device Removal Request",
	"21;6F;7" =>	"Slot/Connector - Interlock Asserted",
	"21;6F;8" =>	"Slot/Connector - Slot Disabled",
	"21;6F;9" =>	"Slot/Connector - Slot Holds Spare Device",
	"21;6F;80" =>	"Slot/Connector - Fault status De-asserted",
	"21;6F;81" =>	"Slot/Connector - Identify status De-asserted",
	"21;6F;82" =>	"Slot/Connector - Device Empty",
	"21;6F;85" =>	"Slot/Connector - Power is On",
	"21;6F;87" =>	"Slot/Connector - Interlock De-asserted",
	"21;6F;88" =>	"Slot/Connector - Slot Enabled",
	"21;6F;89" =>	"Slot/Connector - Slot No Longer holds Spare",
    "22;6F;0" =>	"Power State - ON [S0/G0, working]",
	"22;6F;1" =>	"Power State - S1 Sleep - Light",
	"22;6F;2" =>	"Power State - S2 Sleep",
	"22;6F;3" =>	"Power State - S3 Standby",
	"22;6F;4" =>	"Power State - S4 Hibernate/Suspend-To-Disk, Soft-Off",
	"22;6F;5" =>	"Power State - S5/G2 Soft-Off",
	"22;6F;6" =>	"Power State - S4/S5 Soft-Off",
	"22;6F;7" =>	"Power State - G3 Hard-Off",
	"22;6F;8" =>	"Power State - Sleep in an S1,S2 or S3",
	"22;6F;9" =>	"Power State - Sleep - G1",
	"22;6F;A" =>	"Power State - S5 entered by override",
	"22;6F;B" =>	"Power State - Legacy ON state",
	"22;6F;C" =>	"Power State - Legacy OFF state",
	"22;6F;E" =>	"Power State - Unknown",
    "23;6F;0" =>	"Watchdog Timer Expired [category]",
	"23;6F;0;40;2;0;0;0;0;0;0" =>	"Watchdog (BIOS/POST) - Timer Expired (no action, no interrupt)",
	"23;6F;0;40;4;0;0;0;0;0;0" =>	"Watchdog (SMS/OS) - Timer Expired (no action, no interrupt)",
	"23;6F;0;40;6;0;0;0;0;0;0" =>	"Watchdog - Timer Expired (no action, no interrupt)",
	"23;6F;1" =>	"Watchdog - Reboot",
	"23;6F;2" =>	"Watchdog - Poweroff",
	"23;6F;3" =>	"Watchdog - Power Cycle",
	"23;6F;8" =>	"Watchdog - Timer interrupt occurred",
    "24;6F;0" =>	"Platform Alert - Generated Page",
	"24;6F;1" =>	"Platform Alert - Generated LAN Alert",
	"24;6F;2" =>	"Platform Alert - Event Trap Generated",
	"24;6F;3" =>	"Platform Alert - Generated SNMP Trap, OEM format",
	"24;A;1;AA;*" =>	"Circuit Breaker Event",
    "25;6F;0" =>	"'Entity' Present",
	"25;6F;2" =>	"'Entity' Disabled",
	"25;6F;80" =>	"'Entity' Absent",
	"25;6F;82" =>	"'Entity' Enabled",
    "26;1;0" =>	"NetAdapter State Tampered - the host software is trying to sabotage the link",
	"26;1;0;40;1;0;0;0;0;0;0" =>	"NetAdapter State Tampered - the host software tried to sabotage the link 5 times/hour",
	"26;1;0;40;2;0;0;0;0;0;0" =>	"NetAdapter State Tampered - the iAMT was not able to maintain it's Protected Clock",
	"26;1;0;AA;2;*" =>	"Firmware/Software Update/Change: Success (no active application update)",
	"26;1;0;AA;3;*" =>	"Firmware Update Event: Partial (no active application update)",
	"26;1;0;AA;4;*" =>	"Firmware Update Event: Failure (no active application update)",
	"26;1;0;AA;42;*" =>	"Firmware/Software Update/Change: Success (active application A)",
	"26;1;0;AA;43;*" =>	"Firmware Update Event: Partial (active application A)",
	"26;1;0;AA;44;*" =>	"Firmware Update Event: Failure (active application A)",
	"26;1;0;AA;62;*" =>	"Firmware/Software Update/Change: Success (active application B)",
	"26;1;0;AA;63;*" =>	"Firmware Update Event: Partial (active application B)",
	"26;1;0;AA;64;*" =>	"Firmware Update Event: Failure (active application B)",
	"26;6F;6;AA;0;0;0;0;0;0;0" =>	"Firmware Reset",
    "27;A;3" =>	"LAN - Connected",
	"27;A;3;*" =>	"LAN - Connected",
	"27;6F;0" =>	"LAN - Heartbeat Lost",
	"27;6F;1" =>	"LAN - Heartbeat",
	"27;6F;80" =>	"LAN - Heartbeat Detected",
    "28;6F;0" =>	"Management Subsys Health - Sensor Unavailable or Degraded",
	"28;6F;1" =>	"Management Subsys Health - Controller Unavailable or Degraded",
	"28;6F;2" =>	"Management Subsys Health - Management Controller Off-line",
	"28;6F;3" =>	"Management Subsys Health - Management Controller Disabled",
	"28;6F;4" =>	"Management Subsys Health - Sensor Failed",
	"28;6F;5" =>	"Management Subsys Health - FRU Failed",
	"28;6F;81" =>	"Management Subsys Health - Controller returned from Degraded/Unavailable",
	"28;6F;82" =>	"Management Subsys Health - Management Controller Enabled",
	"28;6F;84" =>	"Management Subsys Health - Sensor returned from Degraded/Unavailable/Failure",
    "29;6F;0" =>	"Battery - level is Critically Low",
	"29;6F;1" =>	"Battery - Failed",
	"29;6F;2" =>	"Battery - Added",
	"29;6F;80" =>	"Battery - level No Longer critically low",
	"29;6F;82" =>	"Battery - Removed",
    "2A;6F;0" =>	"Session Audit - Activated",
	"2A;6F;1" =>	"Session Audit - Deactivated",
	"2A;6F;2" =>	"Session Audit - Invalid Username or Password",
	"2A;6F;3" =>	"Session Audit - Invalid password disable",
	"2A;6F;80" =>	"Session Audit - Deactivated",
    "2B;6F;0" =>	"Version Change - Hardware Changed (for associated 'Entity')",
	"2B;6F;1" =>	"Version Change - Firmware or Software Changed (for associated 'Entity')",
	"2B;6F;2" =>	"Version Change - Hardware Incompatibility (for associated 'Entity')",
	"2B;6F;3" =>	"Version Change - Firmware or Software Incompatibility (for associated 'Entity')",
	"2B;6F;4" =>	"Version Change - Invalid/Unsupported Hardware Version of 'Entity'",
	"2B;6F;5" =>	"Version Change - Invalid/Unsupported Firmware/Software Version of 'Entity'",
	"2B;6F;6" =>	"Version Change - Successful Hardware Change (for associated 'Entity')",
    "2C;6F;0" =>	"FRU - Not Installed",
	"2C;6F;1" =>	"FRU - Inactive (in standby or `hot spare' state)",
	"2C;6F;2" =>	"FRU - Activation Requested",
	"2C;6F;3" =>	"FRU - Activation in Progress",
	"2C;6F;4" =>	"FRU - Active",
	"2C;6F;5" =>	"FRU - Deactivation Requested",
	"2C;6F;6" =>	"FRU - Deactivation in Progress",
	"2C;6F;7" =>	"FRU - Communication Lost",
	"2C;6F;80" =>	"FRU - Installed",
    "C0;70;0;AA;0;0;0;0;0;0;0" =>	"AMT General Notification",
	"C0;70;0;AA;10;0;0;0;0;0;0" =>	"AMT CircuitBreaker Notification - CB Drop TX filter hit",
	"C0;70;0;AA;10;1;0;0;0;0;0" =>	"AMT CircuitBreaker Notification - CB Rate Limit TX filter hit",
	"C0;70;0;AA;10;2;0;0;0;0;0" =>	"AMT CircuitBreaker Notification - CB Drop RX filter hit",
	"C0;70;0;AA;10;3;0;0;0;0;0" =>	"AMT CircuitBreaker Notification - CB Rate Limit RX filter hit",
	"C0;70;0;AA;20;0;0;0;0;0;0" =>	"AMT EAC Notification",
	"C0;70;0;AA;30;0;0;0;0;0;0" =>	"AMT Remote Diagnostic (Remote Redirection - SOL session Started)",
	"C0;70;0;AA;30;1;0;0;0;0;0" =>	"AMT Remote Diagnostic (Remote Redirection - SOL session Stopped)",
	"C0;70;0;AA;30;2;0;0;0;0;0" =>	"AMT Remote Diagnostic (Remote Redirection - IDE-R session Started)",
	"C0;70;0;AA;30;3;0;0;0;0;0" =>	"AMT Remote Diagnostic (Remote Redirection - IDE-R session Stopped)",
	"C0;70;0;AA;40;2;0;0;0;0;0" =>	"AMT WLAN notification - Host profile mismatch. Management Interface ignored",
	"C0;70;0;AA;40;3;0;0;0;0;0" =>	"AMT WLAN notification - Management device overrides host Radio",
	"C0;70;0;AA;40;4;0;0;0;0;0" =>	"AMT WLAN notification - Host profile security mismatch",
	"C0;70;0;AA;40;5;0;0;0;0;0" =>	"AMT WLAN notification - Management device relinquishes control over host Radio",
	"C0;70;0;AA;40;6;0;0;0;0;0" =>	"AMT WLAN notification",
    "C1;71;0;AA;10;0;0;0;0;0;0" =>	"AMT Notification - Auditor - Storage is 50% full",
	"C1;71;0;AA;10;1;0;0;0;0;0" =>	"AMT Notification - Auditor - Storage is 75% full",
	"C1;71;0;AA;10;2;0;0;0;0;0" =>	"AMT Notification - Auditor - Storage is 85% full",
	"C1;71;0;AA;10;3;0;0;0;0;0" =>	"AMT Notification - Auditor - Storage is 95% full",
	"C1;71;0;AA;10;4;0;0;0;0;0" =>	"AMT Notification - Auditor - Storage is full",
	"C1;71;0;AA;20;0;0;0;0;0;0" =>	"AMT Notification - Diagnostic - Certificate error (General certificate error)",
	"C1;71;0;AA;20;0;1;0;0;0;0" =>	"AMT Notification - Diagnostic - Certificate error (Certificate expired)",
	"C1;71;0;AA;20;0;2;0;0;0;0" =>	"AMT Notification - Diagnostic - Certificate error (Missing trusted root certificate)",
	"C1;71;0;AA;20;0;3;0;0;0;0" =>	"AMT Notification - Diagnostic - Certificate error (Failed to validate certificate chain)",
	"C1;71;0;AA;20;0;4;0;0;0;0" =>	"AMT Notification - Diagnostic - Certificate error (Certificate revoked)",
	"C1;71;0;AA;20;0;5;0;0;0;0" =>	"AMT Notification - Diagnostic - Certificate error (RSA exponent too big)",
	"C1;71;0;AA;20;0;6;0;0;0;0" =>	"AMT Notification - Diagnostic - Certificate error (RSA modulus too big)",
	"C1;71;0;AA;20;0;7;0;0;0;0" =>	"AMT Notification - Diagnostic - Certificate error (Unsupported digest)",
	"C1;71;0;AA;20;0;8;0;0;0;0" =>	"AMT Notification - Diagnostic - Certificate error (Distinguished name too long)",
	"C1;71;0;AA;20;0;9;0;0;0;0" =>	"AMT Notification - Diagnostic - Certificate error (Key usage missing)",
	"C1;71;0;AA;20;1;0;0;0;0;0" =>	"AMT Notification - Diagnostic - TLS Handshake error - General SSL handshake error",
	"C1;71;0;AA;20;2;0;0;0;0;0" =>	"AMT Notification - Diagnostic - 802.1X error - General 802.1X error",
	"C1;71;0;AA;20;3;0;0;0;0;0" =>	"AMT Notification - Diagnostic - EAC error - General NAC error",
	"C1;71;0;AA;20;3;1;0;0;0;0" =>	"AMT Notification - Diagnostic - EAC error - Attempt to get a posture while NAC is disabled in iAMT dev",
	"C1;71;0;AA;20;3;2;0;0;0;0" =>	"AMT Notification - Diagnostic - EAC error - Attempt to get posture of unsupported type",
	"C1;71;0;AA;30;0;0;0;0;0;0" =>	"AMT Notification - User Initiated - user requests a remote connection"
    );
}
