Package actions ::
Module script
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import os
17 import sys
18 import pwd
19 import grp
20 import time
21 import select
22 import signal
23 import tempfile
24 import base64
25
26 try:
27 MAXFD = os.sysconf("SC_OPEN_MAX")
28 except:
29 MAXFD = 256
30
31
32
33 from rhn.actions.configfiles import _local_permission_check, _perm_error
34 from config_common import local_config
35 from config_common.rhn_log import set_logfile, log_to_file
36
37 from up2date_client import config
38
39
40
41 __rhnexport__ = [
42 'run',
43 ]
44
45
46 ACTION_VERSION = 2
47
48
50
51 storageDir = tempfile.gettempdir()
52 script_path = os.path.join(storageDir, 'rhn-remote-script')
53
54
55 for i in range(2):
56 try:
57 fd = os.open(script_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, int("0700", 8))
58
59 break
60 except OSError:
61 e = sys.exc_info()[1]
62 if e.errno != 17:
63 raise
64
65 try:
66 os.unlink(script_path)
67 except OSError:
68 e = sys.exc_info()[1]
69 if e.errno != 2:
70 raise
71 else:
72
73 raise
74 sf = os.fdopen(fd, 'wb')
75 sf.write(script.encode("utf-8"))
76 sf.close()
77
78 if uid and gid:
79 os.chown(script_path, uid, gid)
80
81 return script_path
82
83
85 d = os.path.dirname(fpath)
86 if d and not os.path.exists(d):
87 os.makedirs(d, int("0700", 8))
88 return os.path.exists(d)
89
90 -def run(action_id, params, cache_only=None):
91
92 cfg = config.initUp2dateConfig()
93 local_config.init('rhncfg-client', defaults=dict(cfg.items()))
94
95 tempfile.tempdir = local_config.get('script_tmp_dir')
96
97 logfile_name = local_config.get('script_log_file')
98 log_output = local_config.get('script_log_file_enable')
99
100 if log_output:
101
102 _create_path(logfile_name)
103
104 if cache_only:
105 return (0, "no-ops for caching", {})
106
107 action_type = 'script.run'
108 if not _local_permission_check(action_type):
109 return _perm_error(action_type)
110
111
112 extras = {'output':''}
113 script = params.get('script')
114 if not script:
115 return (1, "No script to execute", {})
116
117 username = params.get('username')
118 groupname = params.get('groupname')
119
120 if not username:
121 return (1, "No username given to execute script as", {})
122
123 if not groupname:
124 return (1, "No groupname given to execute script as", {})
125
126 timeout = params.get('timeout')
127
128 if timeout:
129 try:
130 timeout = int(timeout)
131 except ValueError:
132 return (1, "Invalid timeout value", {})
133 else:
134 timeout = None
135
136 db_now = params.get('now')
137 if not db_now:
138 return (1, "'now' argument missing", {})
139 db_now = time.mktime(time.strptime(db_now, "%Y-%m-%d %H:%M:%S"))
140
141 now = time.time()
142 process_start = None
143 process_end = None
144
145 child_pid = None
146
147
148 try:
149 user_record = pwd.getpwnam(username)
150 except KeyError:
151 return 1, "No such user %s" % username, extras
152
153 uid = user_record[2]
154 ugid = user_record[3]
155
156
157
158 try:
159 script_path = _create_script_file(script, uid=uid, gid=ugid)
160 except OSError:
161 e = sys.exc_info()[1]
162 return 1, "Problem creating script file: %s" % e, extras
163
164
165 try:
166 group_record = grp.getgrnam(groupname)
167 except KeyError:
168 return 1, "No such group %s" % groupname, extras
169
170 run_as_gid = group_record[2]
171
172
173
174 (pipe_read, pipe_write) = os.pipe()
175
176 process_start = time.time()
177 child_pid = os.fork()
178
179 if not child_pid:
180
181 os.close(pipe_read)
182
183
184 os.dup2(pipe_write, sys.stdout.fileno())
185 os.dup2(pipe_write, sys.stderr.fileno())
186
187
188 for i in range(3, MAXFD):
189 try:
190 os.close(i)
191 except:
192 pass
193
194
195
196
197 os.chdir('/')
198
199
200 os.setgid(run_as_gid)
201 groups=[g.gr_gid for g in grp.getgrall() if username in g.gr_mem or username in g.gr_name]
202 os.setgroups(groups)
203 os.setuid(uid)
204
205
206
207 os.setpgrp()
208
209 clean_env = {"PATH": "/sbin:/bin:/usr/sbin:/usr/bin", "TERM": "xterm"}
210
211 try:
212 os.umask(int("022", 8))
213 os.execve(script_path, [script_path, ], clean_env)
214 finally:
215
216
217
218 os._exit(1)
219
220
221 os.close(pipe_write)
222
223 output = None
224 timed_out = None
225
226 out_stream = open('/var/lib/up2date/action.%s' % str(action_id), 'ab+', 0)
227
228 while 1:
229 select_wait = None
230
231 if timeout:
232 elapsed = time.time() - process_start
233
234 if elapsed >= timeout:
235 timed_out = 1
236
237
238 os.kill(-child_pid, signal.SIGTERM)
239 time.sleep(2)
240 os.kill(-child_pid, signal.SIGKILL)
241 break
242
243 select_wait = timeout - elapsed
244
245 try:
246 input_fds, output_fds, error_fds = select.select([pipe_read], [], [], select_wait)
247 except select.error:
248 return 255, "Termination signal occurred during execution.", {}
249
250 if error_fds:
251
252 os.close(pipe_read)
253 return 1, "Fatal exceptional case", extras
254
255 if not (pipe_read in input_fds):
256
257 continue
258
259 output = os.read(pipe_read, 4096)
260 if not output:
261
262 break
263
264 out_stream.write(output)
265
266 os.close(pipe_read)
267
268
269 (somepid, exit_status) = os.waitpid(child_pid, 0)
270 process_end = time.time()
271
272
273 out_stream.seek(0, 0)
274 extras['output'] = out_stream.read()
275 out_stream.close()
276
277
278 if log_output :
279 set_logfile(logfile_name)
280 log_to_file(0, extras['output'])
281
282
283
284 extras['base64enc'] = 1
285 extras['output'] = base64.encodestring(extras['output'])
286
287 extras['return_code'] = exit_status
288
289
290 extras['process_start'] = db_now + (process_start - now)
291 extras['process_end'] = db_now + (process_end - now)
292
293 for key in ('process_start', 'process_end'):
294 extras[key] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(extras[key]))
295
296
297 os.unlink(script_path)
298
299 if timed_out:
300 return 1, "Script killed, timeout of %s seconds exceeded" % timeout, extras
301
302 if exit_status == 0:
303 return 0, "Script executed", extras
304
305 return 1, "Script failed", extras
306