Remote command through SMS

This case study is included in the evaluation version of the SMS Messaging Server

1. Background

SupportIT is an IT consultancy company supplying small and medium sized businesses with networking solutions and IT computer support. From Firewall and VPN installations to Microsoft upgrades and migrations, to a complete network audit and support service, SupportIT has the experience and expertise. Qualified and experienced Microsoft engineers are doing consultancy work. From storing Microsoft data from a failed server to upgrading and migrating Exchange servers to the latest versions.

This case study describes how SupportIT implemented an SMS-based solution to reboot servers and restart services from remote locations.

2. Problem Statement

The SupportIT staff members need a facility to reboot Windows computers or restart Windows services at any time, even when they are at a location where there is no network available.

Now, they need to make a phone call to a colleague and ask the person to reboot a machine or reboot a service. They want to be able to do this automatically via SMS.

3. Goals of the new System

Goals of the new system:

4. ActiveXperts SMS Messaging Server Solution

SupportIT uses a dedicated server for running ActiveXperts SMS Messaging Server. This system receives incoming SMS commands, checks the command for its syntax, checks if the command was sent by an SupportIT help desk member, and executes the reboot/restart command. In case a users sends a command that doesn't meet the syntax requirement, a 'how to use' message is returned.

SMS message format

The system handles SMS messages as follows:

SMS syntax Sample Explanation
REBOOT <server> REBOOT fileserver03 Reboot a server or workstation
SHUTDOWN <server> SHUTDOWN fileserver03 Shutdown a server or workstation
QUERYSVC <server> <service> QUERY webserver02 w3svc Query the state of a service on a particular server/workstation
STOPSVC <server> <service> STOPSVC webserver02 w3svc Stop a service on a particular server/workstation
STARTSVC <server> <service> STARTSVC webserver02 w3svc Start a service on a particular server/workstation
<any other message> Please help Unrecognized command. A Help message is replied

Hardware and Software

The system consists of the following:

A GSM/GPRS modem throughput, 10 messages per minute, is sufficient for this solution.
They selected a Falcom SAMBA GSM/GPRS modem, together with a SIM card including an SMS bundle of 5000 SMS messages. This SIM card is inserted into the Falcom SAMBA GSM/GPRS modem.
The SIM card is associated with the following GSM number: +3565099212.

Trigger

A Trigger is called when a new messages arrives in the system. SupportIT handles only one type of SMS messages: incoming reboot/restart commands. Therefore, only one trigger is required:

Enabled Description Condition Script
YES Process incoming commands To = '+3565099212' \Projects\SupportIT\Triggers\SupportIT.vbs

The trigger function is always called 'ProcessMessage' (mandatory name) and is always called by the SMS Messaging Server service with one parameter: the message ID of the message to be processed. The 'ProcessMessage' function in the 'SupportIT.vbs' file handles the following:

  - Set incoming message to 'processed', so it will not be processed again by the system
  - If syntax not matched                     => send an SMS reply to the sender 
  - Reboot/Shutdown command                   => Verify paramters; there should be one 
                                                 parameter: the computername
  - Query/Stop/Start service command          => Verify paramters; there should be two 
                                                 parameters: computername and servicename
  - Execute command   
 

SupportIT.vbs (full code)

 ' // ========================================================================
 ' // SupportIT.vbs
 ' // ------------------------------------------------------------------------
 ' // 
 ' // REBOOT server01
 ' // SHUTDOWN server01
 ' // QUERYSVC server01 messenger 
 ' // STARTSVC server01 messenger
 ' // STOPSVC server01 messenger
 ' // ========================================================================

 Option Explicit

 CONST STR_DEBUGFILE     = "\Sys\Tmp\Sample Project - SupportIT.txt"

 Const STR_USAGE         = "Usage: REBOOT/SHUTDOWN server | " & _
                           "QUERYSVC/STOPSVC/STARTSVC server service"

 Const CMD_UNKNOWN       = 0
 Const CMD_REBOOT        = 1
 Const CMD_SHUTDOWN      = 2
 Const CMD_QUERYSVC      = 3
 Const CMD_STARTSVC      = 4
 Const CMD_STOPSVC       = 5


 ' Declaration of global objects
 Dim g_objMessageDB, g_objDebugger, g_objConstants

 ' Creation of global objects
 Set g_objConstants      = CreateObject( "Axsms-messaging-server.Constants" )
 Set g_objMessageDB      = CreateObject( "Axsms-messaging-server.MessageDB" ) 
 Set g_objDebugger       = CreateObject( "ActiveXperts.VbDebugger" )

 ' Set Debug file - for troubleshooting purposes
 g_objDebugger.DebugFile = STR_DEBUGFILE
 g_objDebugger.Enabled	= False


 ' // ========================================================================
 ' // Function: ProcessMessage
 ' // ------------------------------------------------------------------------
 ' // ProcessMessage trigger function to process incoming messages
 ' // ========================================================================

 Function ProcessMessage( numMessageID )
   Dim objMessageIn
   Dim numCommand, strServer, strService, strReplyMessage
   Dim bResult

   g_objDebugger.WriteLine ">> ProcessMessage"

   ' Open the Message Database
   g_objMessageDB.Open
   If( g_objMessageDB.LastError <> 0 ) Then
      g_objDebugger.WriteLine "<< ProcessMessage,  unable to open database"
      Exit Function
   End If

   ' Retrieve the message that has just been received. If it fails then exit script 
   Set objMessageIn   = g_objMessageDB.FindFirstMessage( "ID = " & numMessageID ) 
   If g_objMessageDB.LastError <> 0 Then
      g_objMessageDB.Close
      g_objDebugger.WriteLine "<< ProcessMessage,  FindFirstMessage failed: " & _
          g_objMessageDB.LastError
      Exit Function
   End If

   ' Change Status to from Pending to Success. 
   objMessageIn.Status = g_objConstants.MESSAGESTATUS_SUCCESS
   g_objMessageDB.Save objMessageIn   

   g_objDebugger.WriteLine "Incoming message saved, result: [" & g_objMessageDB.LastError & "]"

   ' Retrieve Member's name
   bResult = GetCommand( objMessageIn.Body, numCommand, strServer, strService, strReplyMessage )
   If( bResult ) Then
      Select Case numCommand
         Case CMD_REBOOT
            Win32ShutDown strServer, True, strReplyMessage 
         Case CMD_SHUTDOWN
            Win32ShutDown strServer, False, strReplyMessage 
         Case CMD_QUERYSVC
            CheckServiceState strServer, strService, strReplyMessage 
         Case CMD_STARTSVC
            StartOrStopService strServer, strService, True, strReplyMessage 
         Case CMD_STOPSVC
            StartOrStopService strServer, strService, False, strReplyMessage 
         Case Else
            strReplyMessage = "Unknown."
     End Select
   End If
   
   ReplyMessage objMessageIn, strReplyMessage
 
   ' Close the Message Database
   g_objMessageDB.Close

   g_objDebugger.WriteLine "<< ProcessMessage"

 End Function


 ' // ========================================================================
 ' // Function: GetCommand
 ' // ========================================================================

 Function GetCommand( strBody, ByRef numCommand, ByRef strParam1, ByRef strParam2, _
      ByRef strErrorMessage )

   Dim arrMessage

   GetCommand   = False
   numCommand   = CMD_UNKNOWN
   strParam1    = ""
   strParam2    = ""
   strErrorMessage = ""

   arrMessage = Split( UCase( Trim( strBody ) ), " " )
   If( UBound( arrMessage ) < 1 ) Then
      GetCommand = False
      strErrorMessage = "Syntax error. " & STR_USAGE
      Exit Function
   End If
   
   Select Case UCase( arrMessage(0) )
      Case "REBOOT"
         numCommand = CMD_REBOOT
      Case "SHUTDOWN"
         numCommand = CMD_SHUTDOWN
      Case "QUERYSVC"
         numCommand = CMD_QUERYSVC
      Case "STARTSVC"
         numCommand = CMD_STARTSVC
      Case "STOPSVC"
         numCommand = CMD_STOPSVC
		Case Else
		   numCommand = CMD_UNKNOWN		
	End Select
	
   If( numCommand = CMD_UNKNOWN ) Then
      GetCommand = False
      strErrorMessage = "Syntax error. " & STR_USAGE
      Exit Function
   End If

   If( ( numCommand = CMD_REBOOT Or numCommand = CMD_SHUTDOWN ) And _
        UBound( arrMessage ) <> 1 ) Then
      GetCommand      = False
      strErrorMessage = "Parameter mismatch. " & STR_USAGE 
      Exit Function
   End If
     
   If(    ( numCommand = CMD_QUERYSVC Or numCommand = CMD_STARTSVC Or numCommand = CMD_STOPSVC ) 
      And UBound( arrMessage ) <> 2 ) Then
      GetCommand      = False
      strErrorMessage = "Parameter mismatch. " & STR_USAGE 
      Exit Function
   End If
   
   ' Assign strParam1 and optionally strParam2
   If( UBound( arrMessage ) >= 1 ) Then
      strParam1 = arrMessage( 1 )
   End If
   If( UBound( arrMessage ) >= 2 ) Then
      strParam2 = arrMessage( 2 )
   End If
 
   GetCommand = True

 End Function


 ' // ========================================================================
 ' // ReplyMessage
 ' // ------------------------------------------------------------------------
 ' // Auto reply to every incoming SMS message
 ' // ========================================================================

 Function ReplyMessage( objMessageIn, strResponse )

   Dim objMessageOut

   g_objDebugger.WriteLine ">> ReplyMessage"

   ' WRITE YOUR CODE HERE (for instance: forward message to an e-mail address, see below)
   Set objMessageOut = g_objMessageDB.Create
   If( g_objMessageDB.LastError = 0 ) Then
     objMessageOut.Direction = g_objConstants.MESSAGEDIRECTION_OUT
     objMessageOut.Status    = g_objConstants.MESSAGESTATUS_PENDING
     objMessageOut.Type      = objMessageIn.Type 
     objMessageOut.To        = objMessageIn.From
     objMessageOut.ChannelID = 0
     objMessageOut.BodyFormat= objMessageIn.BodyFormat
     objMessageOut.Body      = strResponse
     g_objMessageDB.Save objMessageOut
   End If

  g_objDebugger.WriteLine "<< ReplyMessage"

End Function


 '// ==================================================
 '// START/STOP A SERVICE AND ITS DEPENDENTS
 '// ==================================================
 Function StartOrStopService(strComputer, strService, bStart, ByRef strMessage )

 On Error Resume Next

   strMessage   = "Unknown result."
   StartOrStopService = False

   '// CONNECT TO WMI ON DESTINATION COMPUTER
   Dim objWMIService
   Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & _
      "\root\cimv2")
   If(Err.Number <> 0) Then
     StartOrStopService = False
     strMessage = "Unable to connect to WMI service on computer '" & strComputer & "'" 
     g_objDebugger.WriteLine DateTime() & " " & strMsg
     Exit Function 
   End If

   '// GET SERVICE COLLECTION FOR THIS SERVICE
   Dim colServiceList
   Set colServiceList = objWMIService.ExecQuery ("Select * from Win32_Service where Name='" & _
      strService & "'")
   If( colServiceList.Count = 0 ) Then
      StartOrStopService = False
      strMessage = "Unable to find service '" & strService & "' on computer '" & strComputer & "'"
      g_objDebugger.WriteLine DateTime() & " " & strMsg
      Exit Function			    
   End If
    
   '// START or STOP SERVICE
   Dim objService, errReturn
   For Each objService in colServiceList 
      If( Err.Number <> 0 ) Then
         StartOrStopService = False
         strMessage = "Unable to find service '" & strService & "' on computer '" & _
            strComputer & "'"
         g_objDebugger.WriteLine DateTime() & " " & strMsg
         Exit Function			    
      End If
		
      If( bStart ) Then
         objService.StartService()
         strMessage = "StartService successfully initiated."
      Else
         objService.StopService()
         strMessage = "StopService successfully initiated."
      End If
   Next
	
   CheckServiceState  = True

   '// CLOSE OBJECTS
   Set colServiceList = Nothing
   Set objWMIService  = Nothing
	
 End Function


 '// ==================================================
 '// CHECK SERVICE STATUS
 '// ==================================================
 Function CheckServiceState(strComputer, strService, ByRef strMessage )

 On Error Resume Next

   strMessage    = "Unknown result."
   CheckServiceState = False
   
   '// CONNECT TO WMI ON DESTINATION COMPUTER
   Dim objWMIService
   Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & _
      "\root\cimv2")
   If(Err.Number <> 0) Then
      CheckServiceState = False
      strMessage = "Failed to connect to WMI service on computer '" & strComputer & "'" 
      g_objDebugger.WriteLine DateTime() & " " & strMsg
      Exit Function 
   End If
   
   '// GET SERVICE COLLECTION FOR THIS SERVICE
   Dim colServiceList
   Set colServiceList = objWMIService.ExecQuery ("Select * from Win32_Service where Name='" & _
      strService & "'")
   If(colServiceList.Count = 0) Then
      CheckServiceState = False
      strMessage = "Failed to find service '" & strService & "' on computer '" & _
        strComputer & "'"
      g_objDebugger.WriteLine DateTime() & " " & strMsg
      Exit Function			    
   End If
   
   '// GET SERVICE STATE
   Dim objService
   For Each objService in colServiceList 
      If(Err.Number <> 0) Then
         CheckServiceState = False
         strMessage = "Failed to find service '" & strService & "' on computer '" & _
            strComputer & "'"
         g_objDebugger.WriteLine DateTime() & " " & strMsg
         Exit Function			    
      End If
      strMessage = "State of service '" & strService & "' on computer '" & strComputer & ": " & _
          objService.State
   Next
   
   CheckServiceState  = True
   
   '// CLOSE OBJECTS	
   Set colServiceList = Nothing
   Set objWMIService  = Nothing
	
 End Function


 '// ==================================================
 '// MANAGE SERVER (WIN32SHUTDOWN)
 '// ==================================================
 Function Win32ShutDown( strComputer, bReboot, ByRef strMessage )

 On Error Resume Next

   Win32ShutDown = False
   strMessage    = "Unknown result."
   
   '// CONNECT TO WMI ON DESTINATION COMPUTER
   Dim objWMIService
   Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & _
      "\root\cimv2")
   If( Err.Number <> 0) Then
      Win32ShutDown = False
      strMessage    = "Failed to connect to WMI service on computer '" & strComputer & "'" 
      g_objDebugger.WriteLine DateTime() & " " & strMsg
      Exit Function 
   End If
   
   '// GET OPERATING SYSTEM COLLECTION
   Dim colOperatingSystems
   Set colOperatingSystems = objWMIService.ExecQuery("Select * from Win32_OperatingSystem")
   If (colOperatingSystems.Count = 0) Then
      Win32ShutDown = False
      strMessage    = "Failed to query Win32 Operating System on computer '" & strComputer & "'."
      g_objDebugger.WriteLine DateTime() & " " & strMsg
      Exit Function			    
   End If
   
   '// SEND WIN32SHUTDOWN COMMAND
   Dim objOperatingSystem
   For Each objOperatingSystem in colOperatingSystems
      If (Err.Number <> 0) Then
         Win32ShutDown = False
         strMessage    = "Unable to retrieve Operating System Collection on computer '" & _
            strComputer & "'."
         g_objDebugger.WriteLine DateTime() & " " & strMsg
         Exit Function 
      End If
      
      If( bReboot ) Then
         objOperatingSystem.Win32Shutdown( 6 )
         strMessage = "Reboot of computer '" & strComputer & "' successfully initiated."
      Else
         objOperatingSystem.Win32Shutdown( 5 )
         strMessage = "Shutdown of computer '" & strComputer & "' successfully initiated."
      End If
   Next
   
   Win32ShutDown = True
	
 End Function