def diff(params, cache_only=None): def is_utf8(in_string): """Returns true if input is a valid UTF-8 string, False otherwise.""" if isinstance(in_string, UnicodeType): return True elif isinstance(in_string, StringType): if PY3: return True try: in_string.decode('utf-8') return True except UnicodeDecodeError: return False return False if cache_only: return (0, "no-ops for caching", {}) action_type = 'configfiles.diff' if not _local_permission_check(action_type): log_to_file(0, "permissions error: " + str(action_type)) return _perm_error(action_type) _init() files = params.get('files') or [] fp = file_utils.FileProcessor() missing_files = [] diffs = {} exists = hasattr(os.path, 'lexists') and os.path.lexists or os.path.exists for file in files: path = file['path'] if not exists(path): missing_files.append(path) continue diff = fp.diff(file) if diff: diffs[path] = diff extras = {} if missing_files: extras['missing_files'] = missing_files if diffs: for file in diffs.keys(): if not is_utf8(diffs[file]): diffs[file] = "%s: binary files differ" % file extras['diffs'] = diffs log_to_file( 0, "Files successfully compared: %s %s" % (format_file_string(files, create_key_list()), str(extras))) return 0, "Files successfully compared", extras
def diff(params, cache_only=None): def is_utf8(in_string): """Returns true if input is a valid UTF-8 string, False otherwise.""" if isinstance(in_string, UnicodeType): return True elif isinstance(in_string, StringType): try: in_string.decode('utf-8') return True except UnicodeDecodeError: return False return False if cache_only: return (0, "no-ops for caching", {}) action_type = 'configfiles.diff' if not _local_permission_check(action_type): log_to_file(0, "permissions error: " + str(action_type)) return _perm_error(action_type) _init() files = params.get('files') or [] fp = file_utils.FileProcessor() missing_files = [] diffs = {} exists = hasattr(os.path, 'lexists') and os.path.lexists or os.path.exists for file in files: path = file['path'] if not exists(path): missing_files.append(path) continue diff = fp.diff(file) if diff: diffs[path] = diff extras = {} if missing_files: extras['missing_files'] = missing_files if diffs: for file in diffs.keys(): if not is_utf8(diffs[file]): diffs[file] = "%s: binary files differ" % file extras['diffs'] = diffs log_to_file(0, "Files successfully diffed: %s %s" % (format_file_string(files, create_key_list()), str(extras))) return 0, "Files successfully diffed", extras
def mtime_upload(action_id, params, cache_only=None): if cache_only: return (0, "no-ops for caching", {}) action_type = 'configfiles.mtime_upload' if not _local_permission_check(action_type): log_to_file(0, "permissions error: " + str(action_type)) return _perm_error(action_type) _init() file_matches = [] now = time.time() upload_contents = None ignore_dirs = {'/proc': None, '/dev': None} if params['info']['import_contents'] == 'Y': upload_contents = 1 for to_ignore in params['ignore']: ignore_dirs[utils.normalize_path(to_ignore)] = 1 for search_path in params['search']: for dirname, dirs, names in os.walk(utils.normalize_path(search_path)): _visit_dir( { 'matches': file_matches, 'info': params['info'], 'ignore': ignore_dirs, 'now': now, }, dirname, names) if not file_matches: return 0, "No files found", {} r = rpc_cli_repository.ClientRepository() result = r.put_files(action_id, file_matches, upload_contents=upload_contents) formatted_result = format_result(result, file_matches) log_to_file(0, formatted_result) return formatted_result
def upload(action_id, params, cache_only=None): if cache_only: return (0, "no-ops for caching", {}) action_type = 'configfiles.upload' if not _local_permission_check(action_type): log_to_file(0, "permissions error: " + str(action_type)) return _perm_error(action_type) _init() files = params or [] r = rpc_cli_repository.ClientRepository() result = r.put_files(action_id, files) formatted_result = format_result(result, files) log_to_file(0, formatted_result) return formatted_result
def deploy(params, topdir=None, cache_only=None): if cache_only: return (0, "no-ops for caching", {}) action_type = 'configfiles.deploy' if not _local_permission_check(action_type): log_to_file(0, "permissions error: " + str(action_type)) return _perm_error(action_type) _init() files = params.get('files') or [] dep_trans = DeployTransaction(transaction_root=topdir, auto_rollback=0) for file in files: dep_trans.add(file) try: dep_trans.deploy() #5/3/05 wregglej - 135415 Adding stuff for missing user info except cfg_exceptions.UserNotFound, e: try: dep_trans.rollback() except FailedRollback: log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding some more exceptions to handle except cfg_exceptions.UserNotFound, f: log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {})
def mtime_upload(action_id, params, cache_only=None): if cache_only: return (0, "no-ops for caching", {}) action_type = 'configfiles.mtime_upload' if not _local_permission_check(action_type): log_to_file(0, "permissions error: " + str(action_type)) return _perm_error(action_type) _init() file_matches = [] now = time.time() upload_contents = None ignore_dirs = {'/proc':None, '/dev':None} if params['info']['import_contents'] == 'Y': upload_contents = 1 for to_ignore in params['ignore']: ignore_dirs[utils.normalize_path(to_ignore)] = 1 for search_path in params['search']: for dirname, dirs, names in os.walk(utils.normalize_path(search_path)): _visit_dir({ 'matches' : file_matches, 'info' : params['info'], 'ignore' : ignore_dirs, 'now' : now, }, dirname, names) if not file_matches: return 0, "No files found", {} r = rpc_cli_repository.ClientRepository() result = r.put_files(action_id, file_matches, upload_contents=upload_contents) formatted_result = format_result(result, file_matches) log_to_file(0, formatted_result) return formatted_result
def diff(params, cache_only=None): if cache_only: return (0, "no-ops for caching", {}) action_type = 'configfiles.diff' if not _local_permission_check(action_type): log_to_file(0, "permissions error: " + str(action_type)) return _perm_error(action_type) _init() files = params.get('files') or [] fp = file_utils.FileProcessor() missing_files = [] diffs = {} exists = hasattr(os.path, 'lexists') and os.path.lexists or os.path.exists for file in files: path = file['path'] if not exists(path): missing_files.append(path) continue if os.path.isdir(path): # We dont support dir diffs, ignore continue diff = fp.diff(file) diffs[path] = diff extras = {} if missing_files: extras['missing_files'] = missing_files if diffs: extras['diffs'] = diffs log_to_file(0, "Files successfully diffed: %s %s" % (format_file_string(files, create_key_list()), str(extras))) return 0, "Files successfully diffed", extras
os.close(pipe_read) # wait for the child to complete (somepid, exit_status) = os.waitpid(child_pid, 0) process_end = time.time() # Copy the output from the temporary file out_stream.seek(0, 0) extras['output'] = out_stream.read() out_stream.close() # Log script-output locally, unless we're asked not to if log_output: set_logfile(logfile_name) log_to_file(0, extras['output']) # since output can contain chars that won't make xmlrpc very happy, # base64 encode it... extras['base64enc'] = 1 extras['output'] = base64.encodestring(extras['output']) extras['return_code'] = exit_status # calculate start and end times in db's timespace extras['process_start'] = db_now + (process_start - now) extras['process_end'] = db_now + (process_end - now) for key in ('process_start', 'process_end'): extras[key] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(extras[key]))
"Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding some more exceptions to handle except cfg_exceptions.UserNotFound, f: log_to_file( 0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) return ( 50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound, f: log_to_file( 0, "Failed deployment and rollback, group '%s' could not be found" % (f[0], )) return ( 51, "Failed deployment and rollback, group '%s' could not be found" % (f[0], ), {}) else: log_to_file( 0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return ( 50, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) except cfg_exceptions.GroupNotFound, e:
try: dep_trans.deploy() #5/3/05 wregglej - 135415 Adding stuff for missing user info except cfg_exceptions.UserNotFound, e: try: dep_trans.rollback() except FailedRollback: log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding some more exceptions to handle except cfg_exceptions.UserNotFound, f: log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound, f: log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) else: log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) except cfg_exceptions.GroupNotFound, e: try: dep_trans.rollback() except FailedRollback: log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding some more exceptions to handle except cfg_exceptions.UserNotFound, f: log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ) ) return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group,
def run(action_id, params, cache_only=None): cfg = config.initUp2dateConfig() local_config.init('rhncfg-client', defaults=dict(cfg.items())) tempfile.tempdir = local_config.get('script_tmp_dir') logfile_name = local_config.get('script_log_file') log_output = local_config.get('script_log_file_enable') if log_output: # If we're going to log, make sure we can create the logfile _create_path(logfile_name) if cache_only: return (0, "no-ops for caching", {}) action_type = 'script.run' if not _local_permission_check(action_type): return _perm_error(action_type) extras = {'output': ''} script = params.get('script') if not script: return (1, "No script to execute", {}) username = params.get('username') groupname = params.get('groupname') if not username: return (1, "No username given to execute script as", {}) if not groupname: return (1, "No groupname given to execute script as", {}) timeout = params.get('timeout') if timeout: try: timeout = int(timeout) except ValueError: return (1, "Invalid timeout value", {}) else: timeout = None db_now = params.get('now') if not db_now: return (1, "'now' argument missing", {}) db_now = time.mktime(time.strptime(db_now, "%Y-%m-%d %H:%M:%S")) now = time.time() process_start = None process_end = None child_pid = None # determine uid/ugid for script ownership, uid also used for setuid... try: user_record = pwd.getpwnam(username) except KeyError: return 1, "No such user %s" % username, extras uid = user_record[2] ugid = user_record[3] # create the script on disk try: script_path = _create_script_file(script, uid=uid, gid=ugid) except OSError: e = sys.exc_info()[1] return 1, "Problem creating script file: %s" % e, extras # determine gid to run script as try: group_record = grp.getgrnam(groupname) except KeyError: return 1, "No such group %s" % groupname, extras run_as_gid = group_record[2] # create some pipes to communicate w/ the child process (pipe_read, pipe_write) = os.pipe() process_start = time.time() child_pid = os.fork() if not child_pid: # Parent doesn't write to child, so close that part os.close(pipe_read) # Redirect both stdout and stderr to the pipe os.dup2(pipe_write, sys.stdout.fileno()) os.dup2(pipe_write, sys.stderr.fileno()) # Close unnecessary file descriptors (including pipe since it's duped) for i in range(3, MAXFD): try: os.close(i) except: pass # all scripts initial working directory will be / # puts burden on script writer to ensure cwd is correct within the # script os.chdir('/') # the child process gets the desired uid/gid os.setgid(run_as_gid) groups = [ g.gr_gid for g in grp.getgrall() if username in g.gr_mem or username in g.gr_name ] os.setgroups(groups) os.setuid(uid) # give this its own process group (which happens to be equal to its # pid) os.setpgrp() # Finally, exec the script try: os.umask(int("022", 8)) os.execv(script_path, [ script_path, ]) finally: # This code can be reached only when script_path can not be # executed as otherwise execv never returns. # (The umask syscall always succeeds.) os._exit(1) # Parent doesn't write to child, so close that part os.close(pipe_write) output = None timed_out = None out_stream = tempfile.TemporaryFile() while 1: select_wait = None if timeout: elapsed = time.time() - process_start if elapsed >= timeout: timed_out = 1 # Send TERM to all processes in the child's process group # Send KILL after that, just to make sure the child died os.kill(-child_pid, signal.SIGTERM) time.sleep(2) os.kill(-child_pid, signal.SIGKILL) break select_wait = timeout - elapsed # XXX try-except here for interrupted system calls input_fds, output_fds, error_fds = select.select([pipe_read], [], [], select_wait) if error_fds: # when would this happen? os.close(pipe_read) return 1, "Fatal exceptional case", extras if not (pipe_read in input_fds): # Read timed out, should be caught in the next loop continue output = os.read(pipe_read, 4096) if not output: # End of file from the child break out_stream.write(output) os.close(pipe_read) # wait for the child to complete (somepid, exit_status) = os.waitpid(child_pid, 0) process_end = time.time() # Copy the output from the temporary file out_stream.seek(0, 0) extras['output'] = out_stream.read() out_stream.close() # Log script-output locally, unless we're asked not to if log_output: set_logfile(logfile_name) log_to_file(0, extras['output']) # since output can contain chars that won't make xmlrpc very happy, # base64 encode it... extras['base64enc'] = 1 extras['output'] = base64.encodestring(extras['output']) extras['return_code'] = exit_status # calculate start and end times in db's timespace extras['process_start'] = db_now + (process_start - now) extras['process_end'] = db_now + (process_end - now) for key in ('process_start', 'process_end'): extras[key] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(extras[key])) # clean up the script os.unlink(script_path) if timed_out: return 1, "Script killed, timeout of %s seconds exceeded" % timeout, extras if exit_status == 0: return 0, "Script executed", extras return 1, "Script failed", extras
os.close(pipe_read) # wait for the child to complete (somepid, exit_status) = os.waitpid(child_pid, 0) process_end = time.time() # Copy the output from the temporary file out_stream.seek(0, 0) extras['output'] = out_stream.read() out_stream.close() # Log script-output locally, unless we're asked not to if log_output : set_logfile(logfile_name) log_to_file(0, extras['output']) # since output can contain chars that won't make xmlrpc very happy, # base64 encode it... extras['base64enc'] = 1 extras['output'] = base64.encodestring(extras['output']) extras['return_code'] = exit_status # calculate start and end times in db's timespace extras['process_start'] = db_now + (process_start - now) extras['process_end'] = db_now + (process_end - now) for key in ('process_start', 'process_end'): extras[key] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(extras[key]))
def deploy(params, topdir=None, cache_only=None): if cache_only: return (0, "no-ops for caching", {}) action_type = 'configfiles.deploy' if not _local_permission_check(action_type): log_to_file(0, "permissions error: " + str(action_type)) return _perm_error(action_type) _init() files = params.get('files') or [] dep_trans = DeployTransaction(transaction_root=topdir, auto_rollback=0) for file in files: dep_trans.add(file) try: dep_trans.deploy() #5/3/05 wregglej - 135415 Adding stuff for missing user info except cfg_exceptions.UserNotFound: e = sys.exc_info()[1] try: dep_trans.rollback() except FailedRollback: log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding some more exceptions to handle except cfg_exceptions.UserNotFound: f = sys.exc_info()[1] log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound: f = sys.exc_info()[1] log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) else: log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) except cfg_exceptions.GroupNotFound: e = sys.exc_info()[1] try: dep_trans.rollback() except FailedRollback: log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding some more exceptions to handle except cfg_exceptions.UserNotFound: f = sys.exc_info()[1] log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ) ) return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound: f = sys.exc_info()[1] log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) else: log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (e[0], )) return (51, "Failed deployment and rollback, group '%s' could not be found" % (e[0], ), {}) except cfg_exceptions.FileEntryIsDirectory: e = sys.exc_info()[1] try: dep_trans.rollback() except FailedRollback: log_to_file(0, "Failed deployment and rollback, %s already exists as a directory" % (e[0], )) return (44, "Failed deployment and rollback, %s already exists as a directory" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding some more exceptions to handle except cfg_exceptions.UserNotFound: f = sys.exc_info()[1] log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound: f = sys.exc_info()[1] log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) else: log_to_file(0, "Failed deployment, %s already exists as a directory" % (e[0], )) return (45, "Failed deployment, %s already exists as a directory" % (e[0], ), {}) except cfg_exceptions.DirectoryEntryIsFile: e = sys.exc_info()[1] try: dep_trans.rollback() except FailedRollback: log_to_file(0, "Failed deployment and rollback, %s already exists as a file" % (e[0], )) return (46, "Failed deployment and rollback, %s already exists as a file" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding exceptions for missing user except cfg_exceptions.UserNotFound: f = sys.exc_info()[1] log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound: f = sys.exc_info()[1] log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) else: log_to_file(0, "Failed deployment, %s already exists as a file" % (e[0], )) return (47, "Failed deployment, %s already exists as a file" % (e[0], ), {}) except Exception: e = sys.exc_info()[1] print(e) try: dep_trans.rollback() except FailedRollback: e2 = sys.exc_info()[1] log_to_file(0, "Failed deployment, failed rollback: %s" % e2) return (48, "Failed deployment, failed rollback: %s" % e2, {}) #5/3/05 wregglej - 135415 Add exception handling for missing user. except cfg_exceptions.UserNotFound: f = sys.exc_info()[1] log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0])) return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0]), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound: f = sys.exc_info()[1] log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) else: log_to_file(0, "Failed deployment, rolled back: %s" % e) return (49, "Failed deployment, rolled back: %s" % e, {}) extras = {} log_to_file(0, "Files successfully deployed: %s %s" % (format_file_string(files, create_key_list()), str(extras))) return 0, "Files successfully deployed", extras
def deploy(params, topdir=None, cache_only=None): if cache_only: return (0, "no-ops for caching", {}) action_type = 'configfiles.deploy' if not _local_permission_check(action_type): log_to_file(0, "permissions error: " + str(action_type)) return _perm_error(action_type) _init() files = params.get('files') or [] dep_trans = DeployTransaction(transaction_root=topdir, auto_rollback=0) for file in files: dep_trans.add(file) try: dep_trans.deploy() #5/3/05 wregglej - 135415 Adding stuff for missing user info except cfg_exceptions.UserNotFound: e = sys.exc_info()[1] try: dep_trans.rollback() except FailedRollback: log_to_file( 0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return ( 44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding some more exceptions to handle except cfg_exceptions.UserNotFound: f = sys.exc_info()[1] log_to_file( 0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) return ( 50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound: f = sys.exc_info()[1] log_to_file( 0, "Failed deployment and rollback, group '%s' could not be found" % (f[0], )) return ( 51, "Failed deployment and rollback, group '%s' could not be found" % (f[0], ), {}) else: log_to_file( 0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return ( 50, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) except cfg_exceptions.GroupNotFound: e = sys.exc_info()[1] try: dep_trans.rollback() except FailedRollback: log_to_file( 0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) return ( 44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding some more exceptions to handle except cfg_exceptions.UserNotFound: f = sys.exc_info()[1] log_to_file( 0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) return ( 50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound: f = sys.exc_info()[1] log_to_file( 0, "Failed deployment and rollback, group '%s' could not be found" % (f[0], )) return ( 51, "Failed deployment and rollback, group '%s' could not be found" % (f[0], ), {}) else: log_to_file( 0, "Failed deployment and rollback, group '%s' could not be found" % (e[0], )) return ( 51, "Failed deployment and rollback, group '%s' could not be found" % (e[0], ), {}) except cfg_exceptions.FileEntryIsDirectory: e = sys.exc_info()[1] try: dep_trans.rollback() except FailedRollback: log_to_file( 0, "Failed deployment and rollback, %s already exists as a directory" % (e[0], )) return ( 44, "Failed deployment and rollback, %s already exists as a directory" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding some more exceptions to handle except cfg_exceptions.UserNotFound: f = sys.exc_info()[1] log_to_file( 0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) return ( 50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound: f = sys.exc_info()[1] log_to_file( 0, "Failed deployment and rollback, group '%s' could not be found" % (f[0], )) return ( 51, "Failed deployment and rollback, group '%s' could not be found" % (f[0], ), {}) else: log_to_file( 0, "Failed deployment, %s already exists as a directory" % (e[0], )) return (45, "Failed deployment, %s already exists as a directory" % (e[0], ), {}) except cfg_exceptions.DirectoryEntryIsFile: e = sys.exc_info()[1] try: dep_trans.rollback() except FailedRollback: log_to_file( 0, "Failed deployment and rollback, %s already exists as a file" % (e[0], )) return ( 46, "Failed deployment and rollback, %s already exists as a file" % (e[0], ), {}) #5/3/05 wregglej - 136415 Adding exceptions for missing user except cfg_exceptions.UserNotFound: f = sys.exc_info()[1] log_to_file( 0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) return ( 50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound: f = sys.exc_info()[1] log_to_file( 0, "Failed deployment and rollback, group '%s' could not be found" % (f[0], )) return ( 51, "Failed deployment and rollback, group '%s' could not be found" % (f[0], ), {}) else: log_to_file( 0, "Failed deployment, %s already exists as a file" % (e[0], )) return (47, "Failed deployment, %s already exists as a file" % (e[0], ), {}) except Exception: e = sys.exc_info()[1] print(e) try: dep_trans.rollback() except FailedRollback: e2 = sys.exc_info()[1] log_to_file(0, "Failed deployment, failed rollback: %s" % e2) return (48, "Failed deployment, failed rollback: %s" % e2, {}) #5/3/05 wregglej - 135415 Add exception handling for missing user. except cfg_exceptions.UserNotFound: f = sys.exc_info()[1] log_to_file( 0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0])) return ( 50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0]), {}) #5/5/05 wregglej - 136415 Adding exception handling for unknown group, except cfg_exceptions.GroupNotFound: f = sys.exc_info()[1] log_to_file( 0, "Failed deployment and rollback, group '%s' could not be found" % (f[0], )) return ( 51, "Failed deployment and rollback, group '%s' could not be found" % (f[0], ), {}) else: log_to_file(0, "Failed deployment, rolled back: %s" % e) return (49, "Failed deployment, rolled back: %s" % e, {}) extras = {} log_to_file( 0, "Files successfully deployed: %s %s" % (format_file_string(files, create_key_list()), str(extras))) return 0, "Files successfully deployed", extras
def run(action_id, params, cache_only=None): cfg = config.initUp2dateConfig() local_config.init('rhncfg-client', defaults=dict(cfg.items())) tempfile.tempdir = local_config.get('script_tmp_dir') logfile_name = local_config.get('script_log_file') log_output = local_config.get('script_log_file_enable') if log_output: # If we're going to log, make sure we can create the logfile _create_path(logfile_name) if cache_only: return (0, "no-ops for caching", {}) action_type = 'script.run' if not _local_permission_check(action_type): return _perm_error(action_type) extras = {'output':''} script = params.get('script') if not script: return (1, "No script to execute", {}) username = params.get('username') groupname = params.get('groupname') if not username: return (1, "No username given to execute script as", {}) if not groupname: return (1, "No groupname given to execute script as", {}) timeout = params.get('timeout') if timeout: try: timeout = int(timeout) except ValueError: return (1, "Invalid timeout value", {}) else: timeout = None db_now = params.get('now') if not db_now: return (1, "'now' argument missing", {}) db_now = time.mktime(time.strptime(db_now, "%Y-%m-%d %H:%M:%S")) now = time.time() process_start = None process_end = None child_pid = None # determine uid/ugid for script ownership, uid also used for setuid... try: user_record = pwd.getpwnam(username) except KeyError: return 1, "No such user %s" % username, extras uid = user_record[2] ugid = user_record[3] # create the script on disk try: script_path = _create_script_file(script, uid=uid, gid=ugid) except OSError: e = sys.exc_info()[1] return 1, "Problem creating script file: %s" % e, extras # determine gid to run script as try: group_record = grp.getgrnam(groupname) except KeyError: return 1, "No such group %s" % groupname, extras run_as_gid = group_record[2] # create some pipes to communicate w/ the child process (pipe_read, pipe_write) = os.pipe() process_start = time.time() child_pid = os.fork() if not child_pid: # Parent doesn't write to child, so close that part os.close(pipe_read) # Redirect both stdout and stderr to the pipe os.dup2(pipe_write, sys.stdout.fileno()) os.dup2(pipe_write, sys.stderr.fileno()) # Close unnecessary file descriptors (including pipe since it's duped) for i in range(3, MAXFD): try: os.close(i) except: pass # all scripts initial working directory will be / # puts burden on script writer to ensure cwd is correct within the # script os.chdir('/') # the child process gets the desired uid/gid os.setgid(run_as_gid) groups=[g.gr_gid for g in grp.getgrall() if username in g.gr_mem or username in g.gr_name] os.setgroups(groups) os.setuid(uid) # give this its own process group (which happens to be equal to its # pid) os.setpgrp() clean_env = {"PATH": "/sbin:/bin:/usr/sbin:/usr/bin", "TERM": "xterm"} # Finally, exec the script try: os.umask(int("022", 8)) os.execve(script_path, [script_path, ], clean_env) finally: # This code can be reached only when script_path can not be # executed as otherwise execv never returns. # (The umask syscall always succeeds.) os._exit(1) # Parent doesn't write to child, so close that part os.close(pipe_write) output = None timed_out = None out_stream = open('/var/lib/up2date/action.%s' % str(action_id), 'ab+', 0) while 1: select_wait = None if timeout: elapsed = time.time() - process_start if elapsed >= timeout: timed_out = 1 # Send TERM to all processes in the child's process group # Send KILL after that, just to make sure the child died os.kill(-child_pid, signal.SIGTERM) time.sleep(2) os.kill(-child_pid, signal.SIGKILL) break select_wait = timeout - elapsed try: input_fds, output_fds, error_fds = select.select([pipe_read], [], [], select_wait) except select.error: return 255, "Termination signal occurred during execution.", {} if error_fds: # when would this happen? os.close(pipe_read) return 1, "Fatal exceptional case", extras if not (pipe_read in input_fds): # Read timed out, should be caught in the next loop continue output = os.read(pipe_read, 4096) if not output: # End of file from the child break out_stream.write(output) os.close(pipe_read) # wait for the child to complete (somepid, exit_status) = os.waitpid(child_pid, 0) process_end = time.time() # Copy the output from the temporary file out_stream.seek(0, 0) extras['output'] = out_stream.read() out_stream.close() # Log script-output locally, unless we're asked not to if log_output : set_logfile(logfile_name) log_to_file(0, extras['output']) # since output can contain chars that won't make xmlrpc very happy, # base64 encode it... extras['base64enc'] = 1 extras['output'] = base64.encodestring(extras['output']) extras['return_code'] = exit_status # calculate start and end times in db's timespace extras['process_start'] = db_now + (process_start - now) extras['process_end'] = db_now + (process_end - now) for key in ('process_start', 'process_end'): extras[key] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(extras[key])) # clean up the script os.unlink(script_path) if timed_out: return 1, "Script killed, timeout of %s seconds exceeded" % timeout, extras if exit_status == 0: return 0, "Script executed", extras return 1, "Script failed", extras