summaryrefslogtreecommitdiff
path: root/lib/chef/platform/service_helpers.rb
blob: dc0a808c0619f524a9e6a0a253287aab0ca13186 (plain)
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
#
# Author:: Lamont Granquist (<lamont@chef.io>)
# Copyright:: Copyright (c) 2014 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# XXX: mixing shellout into a mixin into classes has to be code smell
require 'chef/mixin/shell_out'
require 'chef/mixin/which'

class Chef
  class Platform
    class ServiceHelpers
      class << self

        include Chef::Mixin::ShellOut
        include Chef::Mixin::Which

        # This helper is mostly used to sort out the mess of different
        # linux mechanisms that can be used to start services.  It does
        # not necessarily need to linux-specific, but currently all our
        # other service providers are narrowly platform-specific with no
        # alternatives.
        #
        # NOTE: if a system has (for example) chkconfig installed then we
        # should report that chkconfig is installed.  The fact that a system
        # may also have systemd installed does not mean that we do not
        # report that systemd is also installed.  This module is purely for
        # discovery of all the alternatives, handling the priority of the
        # different services is NOT a design concern of this module.
        #
        def service_resource_providers
          service_resource_providers = []

          if ::File.exist?("/usr/sbin/update-rc.d")
            service_resource_providers << :debian
          end

          if ::File.exist?("/usr/sbin/invoke-rc.d")
            service_resource_providers << :invokercd
          end

          if ::File.exist?("/sbin/insserv")
            service_resource_providers << :insserv
          end

          # debian >= 6.0 has /etc/init but does not have upstart
          if ::File.exist?("/etc/init") && ::File.exist?("/sbin/start")
            service_resource_providers << :upstart
          end

          if ::File.exist?("/sbin/chkconfig")
            service_resource_providers << :redhat
          end

          if systemd_sanity_check?
            service_resource_providers << :systemd
          end

          service_resource_providers
        end

        def config_for_service(service_name)
          configs = []

          if ::File.exist?("/etc/init.d/#{service_name}")
            configs << :initd
          end

          if ::File.exist?("/etc/init/#{service_name}.conf")
            configs << :upstart
          end

          if ::File.exist?("/etc/xinetd.d/#{service_name}")
            configs << :xinetd
          end

          if ::File.exist?("/etc/rc.d/#{service_name}")
            configs << :etc_rcd
          end

          if ::File.exist?("/usr/local/etc/rc.d/#{service_name}")
            configs << :usr_local_etc_rcd
          end

          if systemd_sanity_check? && platform_has_systemd_unit?(service_name)
            configs << :systemd
          end

          configs
        end

        private

        def systemctl_path
          if @systemctl_path.nil?
            @systemctl_path = which("systemctl")
          end
          @systemctl_path
        end

        def systemd_sanity_check?
          systemctl_path && File.exist?("/proc/1/comm") && File.open("/proc/1/comm").gets.chomp == "systemd"
        end

        def extract_systemd_services(command)
          output = shell_out!(command).stdout
          # first line finds e.g. "sshd.service"
          services = []
          output.each_line do |line|
            fields = line.split
            services << fields[0] if fields[1] == "loaded" || fields[1] == "not-found"
          end
          # this splits off the suffix after the last dot to return "sshd"
          services += services.select {|s| s.match(/\.service$/) }.map { |s| s.sub(/(.*)\.service$/, '\1') }
        rescue Mixlib::ShellOut::ShellCommandFailed
          false
        end

        def platform_has_systemd_unit?(service_name)
          services = extract_systemd_services("#{systemctl_path} --all") +
            extract_systemd_services("#{systemctl_path} list-unit-files")
          services.include?(service_name)
        rescue Mixlib::ShellOut::ShellCommandFailed
          false
        end
      end
    end
  end
end