Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

# This code is part of Ansible, but is an independent component. 

# This particular file snippet, and this file snippet only, is BSD licensed. 

# Modules you write using this snippet, which is embedded dynamically by Ansible 

# still belong to the author of the module, and may assign their own license 

# to the complete work. 

# 

# Copyright (c) Ansible Inc, 2016 

# All rights reserved. 

# 

# Redistribution and use in source and binary forms, with or without modification, 

# are permitted provided that the following conditions are met: 

# 

# * Redistributions of source code must retain the above copyright 

# notice, this list of conditions and the following disclaimer. 

# * Redistributions in binary form must reproduce the above copyright notice, 

# this list of conditions and the following disclaimer in the documentation 

# and/or other materials provided with the distribution. 

# 

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 

# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 

# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 

# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 

# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 

# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 

# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 

# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 

# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

 

import glob 

import os 

import pickle 

import platform 

import select 

import shlex 

import subprocess 

import traceback 

 

from ansible.module_utils.six import PY2, b 

from ansible.module_utils._text import to_bytes, to_text 

 

 

def sysv_is_enabled(name): 

''' 

This function will check if the service name supplied 

is enabled in any of the sysv runlevels 

 

:arg name: name of the service to test for 

''' 

return bool(glob.glob('/etc/rc?.d/S??%s' % name)) 

 

 

def get_sysv_script(name): 

''' 

This function will return the expected path for an init script 

corresponding to the service name supplied. 

 

:arg name: name or path of the service to test for 

''' 

if name.startswith('/'): 

result = name 

else: 

result = '/etc/init.d/%s' % name 

 

return result 

 

 

def sysv_exists(name): 

''' 

This function will return True or False depending on 

the existence of an init script corresponding to the service name supplied. 

 

:arg name: name of the service to test for 

''' 

return os.path.exists(get_sysv_script(name)) 

 

 

def fail_if_missing(module, found, service, msg=''): 

''' 

This function will return an error or exit gracefully depending on check mode status 

and if the service is missing or not. 

 

:arg module: is an AnsibleModule object, used for it's utility methods 

:arg found: boolean indicating if services was found or not 

:arg service: name of service 

:kw msg: extra info to append to error/success msg when missing 

''' 

if not found: 

if module.check_mode: 

module.exit_json(msg="Service %s not found on %s, assuming it will exist on full run" % (service, msg), changed=True) 

else: 

module.fail_json(msg='Could not find the requested service %s: %s' % (service, msg)) 

 

 

def fork_process(): 

''' 

This function performs the double fork process to detach from the 

parent process and execute. 

''' 

pid = os.fork() 

 

if pid == 0: 

# Set stdin/stdout/stderr to /dev/null 

fd = os.open(os.devnull, os.O_RDWR) 

 

# clone stdin/out/err 

for num in range(3): 

107 ↛ 106line 107 didn't jump to line 106, because the condition on line 107 was never false if fd != num: 

os.dup2(fd, num) 

 

# close otherwise 

111 ↛ 115line 111 didn't jump to line 115, because the condition on line 111 was never false if fd not in range(3): 

os.close(fd) 

 

# Make us a daemon 

pid = os.fork() 

 

# end if not in child 

118 ↛ 119line 118 didn't jump to line 119, because the condition on line 118 was never true if pid > 0: 

os._exit(0) 

 

# get new process session and detach 

sid = os.setsid() 

123 ↛ 124line 123 didn't jump to line 124, because the condition on line 123 was never true if sid == -1: 

raise Exception("Unable to detach session while daemonizing") 

 

# avoid possible problems with cwd being removed 

os.chdir("/") 

 

pid = os.fork() 

130 ↛ 131line 130 didn't jump to line 131, because the condition on line 130 was never true if pid > 0: 

os._exit(0) 

 

return pid 

 

 

def daemonize(module, cmd): 

''' 

Execute a command while detaching as a daemon, returns rc, stdout, and stderr. 

 

:arg module: is an AnsibleModule object, used for it's utility methods 

:arg cmd: is a list or string representing the command and options to run 

 

This is complex because daemonization is hard for people. 

What we do is daemonize a part of this module, the daemon runs the command, 

picks up the return code and output, and returns it to the main process. 

''' 

 

# init some vars 

chunk = 4096 # FIXME: pass in as arg? 

errors = 'surrogate_or_strict' 

 

# start it! 

try: 

pipe = os.pipe() 

pid = fork_process() 

except OSError: 

module.fail_json(msg="Error while attempting to fork: %s", exception=traceback.format_exc()) 

except Exception as exc: 

module.fail_json(msg=to_text(exc), exception=traceback.format_exc()) 

 

# we don't do any locking as this should be a unique module/process 

if pid == 0: 

os.close(pipe[0]) 

 

# if command is string deal with py2 vs py3 conversions for shlex 

if not isinstance(cmd, list): 

if PY2: 

cmd = shlex.split(to_bytes(cmd, errors=errors)) 

else: 

cmd = shlex.split(to_text(cmd, errors=errors)) 

 

# make sure we always use byte strings 

run_cmd = [] 

for c in cmd: 

run_cmd.append(to_bytes(c, errors=errors)) 

 

# execute the command in forked process 

p = subprocess.Popen(run_cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=lambda: os.close(pipe[1])) 

fds = [p.stdout, p.stderr] 

 

# loop reading output till its done 

output = {p.stdout: b(""), p.sterr: b("")} 

while fds: 

rfd, wfd, efd = select.select(fds, [], fds, 1) 

if (rfd + wfd + efd) or p.poll(): 

for out in fds: 

if out in rfd: 

data = os.read(out.fileno(), chunk) 

if not data: 

fds.remove(out) 

output[out] += b(data) 

 

# even after fds close, we might want to wait for pid to die 

p.wait() 

 

# Return a pickled data of parent 

return_data = pickle.dumps([p.returncode, to_text(output[p.stdout]), to_text(output[p.stderr])], protocol=pickle.HIGHEST_PROTOCOL) 

os.write(pipe[1], to_bytes(return_data, errors=errors)) 

 

# clean up 

os.close(pipe[1]) 

os._exit(0) 

 

elif pid == -1: 

module.fail_json(msg="Unable to fork, no exception thrown, probably due to lack of resources, check logs.") 

 

else: 

# in parent 

os.close(pipe[1]) 

os.waitpid(pid, 0) 

 

# Grab response data after child finishes 

return_data = b("") 

while True: 

rfd, wfd, efd = select.select([pipe[0]], [], [pipe[0]]) 

if pipe[0] in rfd: 

data = os.read(pipe[0], chunk) 

if not data: 

break 

return_data += b(data) 

 

# Note: no need to specify encoding on py3 as this module sends the 

# pickle to itself (thus same python interpreter so we aren't mixing 

# py2 and py3) 

return pickle.loads(to_bytes(return_data, errors=errors)) 

 

 

def check_ps(module, pattern): 

 

# Set ps flags 

if platform.system() == 'SunOS': 

psflags = '-ef' 

else: 

psflags = 'auxww' 

 

# Find ps binary 

psbin = module.get_bin_path('ps', True) 

 

(rc, out, err) = module.run_command('%s %s' % (psbin, psflags)) 

# If rc is 0, set running as appropriate 

if rc == 0: 

for line in out.split('\n'): 

if pattern in line: 

return True 

return False