; NSIS SERVICE LIBRARY - servicelib.nsh
; Version 1.0 - 09/15/2003
; Questions/Comments - dselkirk@hotmail.com
;
; Description:
;   Provides an interface to window services
;
; Inputs:
;   action	- systemlib action ie. create, delete, start, stop, pause, continue, installed, running, status
;   name	- name of service to manipulate
;   param	- action parameters; usage: var1=value1;var2=value2;...etc.
;
; Actions:
;   create	- creates a new windows service
;		Parameters:
;		  path	- path to service executable
;		  interact	- interact with the desktop ie. 1|0
;		  type	- set the service start type ie. 0|1|2|3|4
;                                   0 = boot start
;                                   1 = system start
;                                   2 = demand start
;                                   3 = auto start
;                                   4 = disabled (default)
;		  machine	- machine name where to install service
;		  user		- user that runs the service
;		  password	- password of the above user
;		  display     - service display name, defaults to service name
;		  desc	- service description
;
;   delete	- deletes a windows service
;   start	- start a stopped windows service
;   stop	- stops a running windows service
;   pause	- pauses a running windows service
;   continue	- continues a paused windows service
;   installed	- is the provided service installed
;		Parameters:
;		  action	- if true then invokes the specified action
;   running	- is the provided service running
;		Parameters:
;		  action	- if true then invokes the specified action
;   status	- check the status of the provided service
;
; Usage:
;   Method 1:
;     Push "action"
;     Push "name"
;     Push "param"
;     Call Service
;     Pop $0 ;response
;
;   Method 2:
;     !insertmacro SERVICE "action" "name" "param"
;
; History:
;		1.0 - 09/15/2003	- Initial release
;		1.1 - 09/16/2003	- Changed &l to i, thnx brainsucker
;		1.2 - 10/01/2003	- fixed domain name
;					- add desc paramater
;					- changed autostart to type with more options
;					- added dispplay for settings the service displayname
;					- added log for setting the eventlog
;					- added quick macro for the service macro

!ifndef SERVICELIB
  !define SERVICELIB

  !define SC_MANAGER_ALL_ACCESS 0x3F
  !define SERVICE_ALL_ACCESS 0xF01FF

  !define SERVICE_WIN32_OWN_PROCESS 0x10
  !define SERVICE_INTERACTIVE_PROCESS 0x100

  !define SERVICE_DISABLED 0x4
  !define SERVICE_DEMAND_START 0x3
  !define SERVICE_AUTO_START 0x2
  !define SERVICE_SYSTEM_START 0x1
  !define SERVICE_BOOT_START 0x0

  !define SERVICE_CONTROL_STOP  1
  !define SERVICE_CONTROL_PAUSE  2
  !define SERVICE_CONTROL_CONTINUE  3

  !define SERVICE_STOPPED 0x1
  !define SERVICE_START_PENDING 0x2
  !define SERVICE_STOP_PENDING 0x3
  !define SERVICE_RUNNING 0x4
  !define SERVICE_CONTINUE_PENDING 0x5
  !define SERVICE_PAUSE_PENDING 0x6
  !define SERVICE_PAUSED 0x7

  !define EVENTLOG_ERROR_TYPE 1
  !define EVENTLOG_WARNING_TYPE 2
  !define EVENTLOG_INFORMATION_TYPE 4

  !ifndef UN
    !define UN ""
  !endif

  !macro SERVICE ACTION NAME PARAM
    Push '${ACTION}'
    Push '${NAME}'
    Push '${PARAM}'
    Call ${UN}Service
  !macroend
  !define Service "!insertmacro SERVICE"

  !macro FUNC_GETPARAM
    Push $0
    Push $1
    Push $2
    Push $3
    Push $4
    Push $5
    Push $6
    Push $7
    Exch 8
    Pop $1 ;name
    Exch 8
    Pop $2 ;source
    StrCpy $0 ""
    StrLen $7 $2
    StrCpy $3 0
    lbl_loop:
      IntCmp $3 $7 0 0 lbl_done
      StrLen $4 "$1="
      StrCpy $5 $2 $4 $3
      StrCmp $5 "$1=" 0 lbl_next
      IntOp $5 $3 + $4
      StrCpy $3 $5
      lbl_loop2:
        IntCmp $3 $7 0 0 lbl_done
        StrCpy $6 $2 1 $3
        StrCmp $6 ";" 0 lbl_next2
        IntOp $6 $3 - $5
        StrCpy $0 $2 $6 $5
        Goto lbl_done
        lbl_next2:
        IntOp $3 $3 + 1
        Goto lbl_loop2
      lbl_next:
      IntOp $3 $3 + 1
      Goto lbl_loop
    lbl_done:
    Pop $5
    Pop $4
    Pop $3
    Pop $2
    Pop $1
    Exch 2
    Pop $6
    Pop $7
    Exch $0
  !macroend

  !macro CALL_GETPARAM VAR NAME DEFAULT LABEL
    Push $1
    Push ${NAME}
    Call ${UN}GETPARAM
    Pop $6
    StrCpy ${VAR} "${DEFAULT}"
    StrCmp $6 "" "${LABEL}" 0
    StrCpy ${VAR} $6
  !macroend

  !macro FUNC_SERVICE UN
    Push $0
    Push $1
    Push $2
    Push $3
    Push $4
    Push $5
    Push $6
    Push $7
    Exch 8
    Pop $1 ;param
    Exch 8
    Pop $2 ;name
    Exch 8
    Pop $3 ;action
    ;$0 return
    ;$4 OpenSCManager
    ;$5 OpenService


    StrCpy $0 "false"
    System::Call 'advapi32::OpenSCManagerA(n, n, i ${SC_MANAGER_ALL_ACCESS}) i.r4'
    IntCmp $4 0 lbl_done
    StrCmp $3 "create" lbl_create
    System::Call 'advapi32::OpenServiceA(i r4, t r2, i ${SERVICE_ALL_ACCESS}) i.r5'
    IntCmp $5 0 lbl_done

    lbl_select:
    StrCmp $3 "delete" lbl_delete
    StrCmp $3 "start" lbl_start
    StrCmp $3 "stop" lbl_stop
    StrCmp $3 "pause" lbl_pause
    StrCmp $3 "continue" lbl_continue
    StrCmp $3 "installed" lbl_installed
    StrCmp $3 "running" lbl_running
    StrCmp $3 "status" lbl_status
    Goto lbl_done

    ; create service
    lbl_create:
      Push $R1 ;machine
      Push $R2 ;user
      Push $R3 ;password
      Push $R4 ;interact
      Push $R5 ;autostart
      Push $R6 ;path
      Push $R7 ;displayname
      Push $R8 ;desc

      !insertmacro CALL_GETPARAM $R1 "machine" "n" "lbl_machine"
      lbl_machine:

      !insertmacro CALL_GETPARAM $R2 "user" "n" "lbl_user"
      lbl_user:

      !insertmacro CALL_GETPARAM $R3 "password" "n" "lbl_password"
      lbl_password:

      !insertmacro CALL_GETPARAM $R4 "interact" "${SERVICE_WIN32_OWN_PROCESS}" "lbl_interact"
        StrCpy $6 ${SERVICE_WIN32_OWN_PROCESS}
        IntCmp $R4 0 +2
        IntOp $R4 $6 | ${SERVICE_INTERACTIVE_PROCESS}
        StrCpy $R4 $6
      lbl_interact:

      !insertmacro CALL_GETPARAM $R5 "type" "0x3" "lbl_type"
        StrCmp $R5 0 0 +3
          StrCpy $R5 ${SERVICE_BOOT_START}
          Goto lbl_type
        StrCmp $R5 1 0 +3
          StrCpy $R5 ${SERVICE_SYSTEM_START}
          Goto lbl_type
        StrCmp $R5 2 0 +3
          StrCpy $R5 ${SERVICE_AUTO_START}
          Goto lbl_type
        StrCmp $R5 3 0 +3
          StrCpy $R5 ${SERVICE_DEMAND_START}
          Goto lbl_type
        StrCpy $R5 ${SERVICE_DISABLED}
      lbl_type:

      !insertmacro CALL_GETPARAM $R6 "path" "n" "lbl_path"
      lbl_path:

      !insertmacro CALL_GETPARAM $R7 "display" "$2" "lbl_display"
      lbl_display:

      !insertmacro CALL_GETPARAM $R8 "desc" "" "lbl_desc"
        WriteRegStr HKLM "SYSTEM\CurrentControlSet\Services\$2" "Description" "$R8"
      lbl_desc:

      !insertmacro CALL_GETPARAM $R9 "log" "0" "lbl_log"
        StrCmp $R9 "0" lbl_log
        WriteRegStr HKLM "SYSTEM\CurrentControlSet\Services\EventLog\Application\$2" "EventMessageFile" "$R6"
        StrCpy $R9 ${EVENTLOG_ERROR_TYPE}
        IntOp $R9 $R9 | ${EVENTLOG_WARNING_TYPE}
        IntOp $R9 $R9 | ${EVENTLOG_INFORMATION_TYPE}
        WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Services\EventLog\Application\$2" "TypesSupported" "$R9"
      lbl_log:

      ; System::Call 'advapi32::CreateServiceA(hSCManager, lpServiceName, lpDisplayName, dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl, lpBinaryPathName, lpLoadOrderGroup, lpdwTagId, lpDependencies, lp, lpPassword)'
      System::Call 'advapi32::CreateServiceA(i r4, t r2, t R7, i ${SERVICE_ALL_ACCESS}, i R4, i R5, i 0, t R6, n, n, R1, R2, R3) i.r6'
      StrCmp $R8 "" +2
      WriteRegStr HKLM "SYSTEM\CurrentControlSet\Services\$2" "Description" "$R8"
      Pop $R9
      Pop $R8
      Pop $R7
      Pop $R6
      Pop $R5
      Pop $R4
      Pop $R3
      Pop $R2
      Pop $R1
      StrCmp $6 0 lbl_done lbl_good

    ; delete service
    lbl_delete:
      System::Call 'advapi32::DeleteService(i r5) i.r6'
      StrCmp $6 0 lbl_done lbl_good

    ; start service
    lbl_start:
      System::Call 'advapi32::StartServiceA(i r5, i 0, i 0) i.r6'
      StrCmp $6 0 lbl_done lbl_good

    ; stop service
    lbl_stop:
      Push $R1
      System::Call '*(i,i,i,i,i,i,i) i.R1'
      System::Call 'advapi32::ControlService(i r5, i ${SERVICE_CONTROL_STOP}, i $R1) i'
      System::Free $R1
      Pop $R1
      StrCmp $6 0 lbl_done lbl_good

    ; pause service
    lbl_pause:
      Push $R1
      System::Call '*(i,i,i,i,i,i,i) i.R1'
      System::Call 'advapi32::ControlService(i r5, i ${SERVICE_CONTROL_PAUSE}, i $R1) i'
      System::Free $R1
      Pop $R1
      StrCmp $6 0 lbl_done lbl_good

    ; continue service
    lbl_continue:
      Push $R1
      System::Call '*(i,i,i,i,i,i,i) i.R1'
      System::Call 'advapi32::ControlService(i r5, i ${SERVICE_CONTROL_CONTINUE}, i $R1) i'
      System::Free $R1
      Pop $R1
      StrCmp $6 0 lbl_done lbl_good

    ; is installed
    lbl_installed:
      !insertmacro CALL_GETPARAM $7 "action" "" "lbl_good"
        StrCpy $3 $7
        Goto lbl_select

    ; is service running
    lbl_running:
      Push $R1
      System::Call '*(i,i,i,i,i,i,i) i.R1'
      System::Call 'advapi32::QueryServiceStatus(i r5, i $R1) i'
      System::Call '*$R1(i, i.r6)'
      System::Free $R1
      Pop $R1
      IntFmt $6 "0x%X" $6
      StrCmp $6 ${SERVICE_RUNNING} 0 lbl_done
      !insertmacro CALL_GETPARAM $7 "action" "" "lbl_good"
        StrCpy $3 $7
        Goto lbl_select

    lbl_status:
      Push $R1
      System::Call '*(i,i,i,i,i,i,i) i.R1'
      System::Call 'advapi32::QueryServiceStatus(i r5, i $R1) i'
      System::Call '*$R1(i, i .r6)'
      System::Free $R1
      Pop $R1
      IntFmt $6 "0x%X" $6
      StrCpy $0 "running"
      IntCmp $6 ${SERVICE_RUNNING} lbl_done
      StrCpy $0 "stopped"
      IntCmp $6 ${SERVICE_STOPPED} lbl_done
      StrCpy $0 "start_pending"
      IntCmp $6 ${SERVICE_START_PENDING} lbl_done
      StrCpy $0 "stop_pending"
      IntCmp $6 ${SERVICE_STOP_PENDING} lbl_done
      StrCpy $0 "running"
      IntCmp $6 ${SERVICE_RUNNING} lbl_done
      StrCpy $0 "continue_pending"
      IntCmp $6 ${SERVICE_CONTINUE_PENDING} lbl_done
      StrCpy $0 "pause_pending"
      IntCmp $6 ${SERVICE_PAUSE_PENDING} lbl_done
      StrCpy $0 "paused"
      IntCmp $6 ${SERVICE_PAUSED} lbl_done
      StrCpy $0 "unknown"

    lbl_good:
    StrCpy $0 "true"
    lbl_done:
    IntCmp $5 0 +2
    System::Call 'advapi32::CloseServiceHandle(i r5) n'
    IntCmp $4 0 +2
    System::Call 'advapi32::CloseServiceHandle(i r4) n'
    Pop $4
    Pop $3
    Pop $2
    Pop $1
    Exch 3
    Pop $5
    Pop $6
    Pop $7
    Exch $0
  !macroend

  Function Service
    !insertmacro FUNC_SERVICE ""
  FunctionEnd

  Function un.Service
    !insertmacro FUNC_SERVICE "un."
  FunctionEnd

  Function GetParam
    !insertmacro FUNC_GETPARAM
  FunctionEnd

  Function un.GetParam
    !insertmacro FUNC_GETPARAM
  FunctionEnd

!endif