File: windows.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (182 lines) | stat: -rw-r--r-- 7,816 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# Windows Service Control Manager (SCM) provider

Puppet::Type.type(:service).provide :windows, :parent => :service do

  desc <<-EOT
    Support for Windows Service Control Manager (SCM). This provider can
    start, stop, enable, and disable services, and the SCM provides working
    status methods for all services.

    Control of service groups (dependencies) is not yet supported, nor is running
    services as a specific user.
  EOT

  defaultfor :operatingsystem => :windows
  confine    :operatingsystem => :windows

  has_feature :refreshable, :configurable_timeout, :manages_logon_credentials

  def enable
    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: {startup_type: :SERVICE_AUTO_START})
  rescue => detail
    raise Puppet::Error.new(_("Cannot enable %{resource_name}, error was: %{detail}") % { resource_name: @resource[:name], detail: detail }, detail )
  end

  def disable
    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: {startup_type: :SERVICE_DISABLED})
  rescue => detail
    raise Puppet::Error.new(_("Cannot disable %{resource_name}, error was: %{detail}") % { resource_name: @resource[:name], detail: detail }, detail )
  end

  def manual_start
    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: {startup_type: :SERVICE_DEMAND_START})
  rescue => detail
    raise Puppet::Error.new(_("Cannot enable %{resource_name} for manual start, error was: %{detail}") % { resource_name: @resource[:name], detail: detail }, detail )
  end

  def delayed_start
    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: {startup_type: :SERVICE_AUTO_START, delayed: true})
  rescue => detail
    raise Puppet::Error.new(_("Cannot enable %{resource_name} for delayed start, error was: %{detail}") % { resource_name: @resource[:name], detail: detail }, detail )
  end

  def enabled?
    return :false unless Puppet::Util::Windows::Service.exists?(@resource[:name])

    start_type = Puppet::Util::Windows::Service.service_start_type(@resource[:name])
    debug("Service #{@resource[:name]} start type is #{start_type}")
    case start_type
      when :SERVICE_AUTO_START,
           :SERVICE_BOOT_START,
           :SERVICE_SYSTEM_START
        :true
      when :SERVICE_DEMAND_START
        :manual
      when :SERVICE_DELAYED_AUTO_START
        :delayed
      when :SERVICE_DISABLED
        :false
      else
        raise Puppet::Error.new(_("Unknown start type: %{start_type}") % { start_type: start_type })
    end
  rescue => detail
    raise Puppet::Error.new(_("Cannot get start type %{resource_name}, error was: %{detail}") % { resource_name: @resource[:name], detail: detail }, detail )
  end

  def start
    if status == :paused
      Puppet::Util::Windows::Service.resume(@resource[:name], timeout: @resource[:timeout])
      return
    end

    # status == :stopped here

    if enabled? == :false
      # If disabled and not managing enable, respect disabled and fail.
      if @resource[:enable].nil?
        raise Puppet::Error.new(_("Will not start disabled service %{resource_name} without managing enable. Specify 'enable => false' to override.") % { resource_name: @resource[:name] })
      # Otherwise start. If enable => false, we will later sync enable and
      # disable the service again.
      elsif @resource[:enable] == :true
        enable
      else
        manual_start
      end
    end
    Puppet::Util::Windows::Service.start(@resource[:name], timeout: @resource[:timeout])
  end

  def stop
    Puppet::Util::Windows::Service.stop(@resource[:name], timeout: @resource[:timeout])
  end

  def status
    return :stopped unless Puppet::Util::Windows::Service.exists?(@resource[:name])

    current_state = Puppet::Util::Windows::Service.service_state(@resource[:name])
    state = case current_state
      when :SERVICE_STOPPED,
           :SERVICE_STOP_PENDING
        :stopped
      when :SERVICE_PAUSED,
           :SERVICE_PAUSE_PENDING
        :paused
      when :SERVICE_RUNNING,
           :SERVICE_CONTINUE_PENDING,
           :SERVICE_START_PENDING
        :running
      else
        raise Puppet::Error.new(_("Unknown service state '%{current_state}' for service '%{resource_name}'") % { current_state: current_state, resource_name: @resource[:name] })
    end
    debug("Service #{@resource[:name]} is #{current_state}")
    state
  rescue => detail
    Puppet.warning("Status for service #{@resource[:name]} could not be retrieved: #{detail}")
    :stopped
  end

  def default_timeout
    Puppet::Util::Windows::Service::DEFAULT_TIMEOUT
  end

  # returns all providers for all existing services and startup state
  def self.instances
    services = []
    Puppet::Util::Windows::Service.services.each do |service_name, _|
      services.push(new(:name => service_name))
    end
    services
  end

  def logonaccount_insync?(current)
    @normalized_logon_account ||= normalize_logonaccount
    @resource[:logonaccount] = @normalized_logon_account

    insync = @resource[:logonaccount] == current
    self.logonpassword = @resource[:logonpassword] if insync
    insync
  end

  def logonaccount
    return unless Puppet::Util::Windows::Service.exists?(@resource[:name])
    Puppet::Util::Windows::Service.logon_account(@resource[:name])
  end

  def logonaccount=(value)
    validate_logon_credentials
    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: {logon_account: value, logon_password: @resource[:logonpassword]})
    restart if @resource[:ensure] == :running && [:running, :paused].include?(status)
  end

  def logonpassword=(value)
    validate_logon_credentials
    Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: {logon_password: value})
  end

  private

  def normalize_logonaccount
    logon_account = @resource[:logonaccount].sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")
    return 'LocalSystem' if Puppet::Util::Windows::User::localsystem?(logon_account)

    @logonaccount_information ||= Puppet::Util::Windows::SID.name_to_principal(logon_account)
    return logon_account unless @logonaccount_information
    return ".\\#{@logonaccount_information.account}" if @logonaccount_information.domain == Puppet::Util::Windows::ADSI.computer_name
    @logonaccount_information.domain_account
  end

  def validate_logon_credentials
    unless Puppet::Util::Windows::User::localsystem?(@normalized_logon_account)
      raise Puppet::Error.new("\"#{@normalized_logon_account}\" is not a valid account") unless @logonaccount_information && [:SidTypeUser, :SidTypeWellKnownGroup].include?(@logonaccount_information.account_type)

      user_rights = Puppet::Util::Windows::User::get_rights(@logonaccount_information.domain_account) unless Puppet::Util::Windows::User::default_system_account?(@normalized_logon_account)
      raise Puppet::Error.new("\"#{@normalized_logon_account}\" has the 'Log On As A Service' right set to denied.") if user_rights =~ /SeDenyServiceLogonRight/
      raise Puppet::Error.new("\"#{@normalized_logon_account}\" is missing the 'Log On As A Service' right.") unless user_rights.nil? || user_rights =~ /SeServiceLogonRight/
    end

    is_a_predefined_local_account = Puppet::Util::Windows::User::default_system_account?(@normalized_logon_account) || @normalized_logon_account == 'LocalSystem'
    account_info = @normalized_logon_account.split("\\")
    able_to_logon = Puppet::Util::Windows::User.password_is?(account_info[1], @resource[:logonpassword], account_info[0]) unless is_a_predefined_local_account
    raise Puppet::Error.new("The given password is invalid for user '#{@normalized_logon_account}'.") unless is_a_predefined_local_account || able_to_logon
  end
end