@ -139,7 +139,7 @@ from ansible.module_utils.pycompat24 import get_exception, literal_eval
from ansible . module_utils . six import ( PY2 , PY3 , b , binary_type , integer_types ,
iteritems , text_type , string_types )
from ansible . module_utils . six . moves import map , reduce
from ansible . module_utils . _text import to_native
from ansible . module_utils . _text import to_native , to_bytes , to_text
_NUMBERTYPES = tuple ( list ( integer_types ) + [ float ] )
@ -808,7 +808,7 @@ class AnsibleModule(object):
if not HAVE_SELINUX or not self . selinux_enabled ( ) :
return context
try :
ret = selinux . matchpathcon ( to_native ( path , ' strict ' ) , mode )
ret = selinux . matchpathcon ( to_native ( path , errors = ' strict ' ) , mode )
except OSError :
return context
if ret [ 0 ] == - 1 :
@ -823,7 +823,7 @@ class AnsibleModule(object):
if not HAVE_SELINUX or not self . selinux_enabled ( ) :
return context
try :
ret = selinux . lgetfilecon_raw ( to_native ( path , ' strict ' ) )
ret = selinux . lgetfilecon_raw ( to_native ( path , errors = ' strict ' ) )
except OSError :
e = get_exception ( )
if e . errno == errno . ENOENT :
@ -984,8 +984,9 @@ class AnsibleModule(object):
return changed
def set_mode_if_different ( self , path , mode , changed , diff = None ) :
path = os . path . expanduser ( path )
path_stat = os . lstat ( path )
b_path = to_bytes ( path )
b_path = os . path . expanduser ( b_path )
path_stat = os . lstat ( b_path )
if mode is None :
return changed
@ -1013,10 +1014,10 @@ class AnsibleModule(object):
if diff is not None :
if ' before ' not in diff :
diff [ ' before ' ] = { }
diff [ ' before ' ] [ ' mode ' ] = oct ( prev_mode )
diff [ ' before ' ] [ ' mode ' ] = ' 0 %03o ' % prev_mode
if ' after ' not in diff :
diff [ ' after ' ] = { }
diff [ ' after ' ] [ ' mode ' ] = oct ( mode )
diff [ ' after ' ] [ ' mode ' ] = ' 0 %03o ' % mode
if self . check_mode :
return True
@ -1024,22 +1025,22 @@ class AnsibleModule(object):
# every time
try :
if hasattr ( os , ' lchmod ' ) :
os . lchmod ( path, mode )
os . lchmod ( b_ path, mode )
else :
if not os . path . islink ( path) :
os . chmod ( path, mode )
if not os . path . islink ( b_ path) :
os . chmod ( b_ path, mode )
else :
# Attempt to set the perms of the symlink but be
# careful not to change the perms of the underlying
# file while trying
underlying_stat = os . stat ( path)
os . chmod ( path, mode )
new_underlying_stat = os . stat ( path)
underlying_stat = os . stat ( b_ path)
os . chmod ( b_ path, mode )
new_underlying_stat = os . stat ( b_ path)
if underlying_stat . st_mode != new_underlying_stat . st_mode :
os . chmod ( path, stat . S_IMODE ( underlying_stat . st_mode ) )
os . chmod ( b_ path, stat . S_IMODE ( underlying_stat . st_mode ) )
except OSError :
e = get_exception ( )
if os . path . islink ( path) and e . errno == errno . EPERM : # Can't set mode on symbolic links
if os . path . islink ( b_ path) and e . errno == errno . EPERM : # Can't set mode on symbolic links
pass
elif e . errno in ( errno . ENOENT , errno . ELOOP ) : # Can't set mode on broken symbolic links
pass
@ -1049,7 +1050,7 @@ class AnsibleModule(object):
e = get_exception ( )
self . fail_json ( path = path , msg = ' chmod failed ' , details = str ( e ) )
path_stat = os . lstat ( path)
path_stat = os . lstat ( b_ path)
new_mode = stat . S_IMODE ( path_stat . st_mode )
if new_mode != prev_mode :
@ -1902,11 +1903,13 @@ class AnsibleModule(object):
to work around limitations , corner cases and ensure selinux context is saved if possible '''
context = None
dest_stat = None
if os . path . exists ( dest ) :
b_src = to_bytes ( src )
b_dest = to_bytes ( dest )
if os . path . exists ( b_dest ) :
try :
dest_stat = os . stat ( dest )
os . chmod ( src, dest_stat . st_mode & PERM_BITS )
os . chown ( src, dest_stat . st_uid , dest_stat . st_gid )
dest_stat = os . stat ( b_ dest)
os . chmod ( b_ src, dest_stat . st_mode & PERM_BITS )
os . chown ( b_ src, dest_stat . st_uid , dest_stat . st_gid )
except OSError :
e = get_exception ( )
if e . errno != errno . EPERM :
@ -1917,7 +1920,7 @@ class AnsibleModule(object):
if self . selinux_enabled ( ) :
context = self . selinux_default_context ( dest )
creating = not os . path . exists ( dest)
creating = not os . path . exists ( b_ dest)
try :
login_name = os . getlogin ( )
@ -1933,7 +1936,7 @@ class AnsibleModule(object):
try :
# Optimistically try a rename, solves some corner cases and can avoid useless work, throws exception if not atomic.
os . rename ( src, dest)
os . rename ( b_ src, b_ dest)
except ( IOError , OSError ) :
e = get_exception ( )
if e . errno not in [ errno . EPERM , errno . EXDEV , errno . EACCES , errno . ETXTBSY ] :
@ -1941,67 +1944,77 @@ class AnsibleModule(object):
# and 26 (text file busy) which happens on vagrant synced folders and other 'exotic' non posix file systems
self . fail_json ( msg = ' Could not replace file: %s to %s : %s ' % ( src , dest , e ) )
else :
dest_dir = os . path . dirname ( dest )
dest_file = os . path . basename ( dest )
b_dest_dir = os . path . dirname ( b_dest )
# Converting from bytes so that if py3, it will be
# surrogateescaped. If py2, it wil be a noop. Converting
# from text strings could mangle filenames on py2)
native_dest_dir = to_native ( b_dest_dir )
native_suffix = to_native ( os . path . basename ( b_dest ) )
native_prefix = ' .ansible_tmp '
try :
tmp_dest = tempfile . NamedTemporaryFile (
prefix = " .ansible_tmp " , dir = dest_dir , suffix = dest_file )
tmp_dest _fd, tmp_dest_name = tempfile . mkstemp (
prefix = native_prefix , dir = native_dest_dir , suffix = native_suffix )
except ( OSError , IOError ) :
e = get_exception ( )
self . fail_json ( msg = ' The destination directory ( %s ) is not writable by the current user. Error was: %s ' % ( dest_dir , e ) )
self . fail_json ( msg = ' The destination directory ( %s ) is not writable by the current user. Error was: %s ' % ( os . path . dirname ( dest ) , e ) )
b_tmp_dest_name = to_bytes ( tmp_dest_name )
try : # leaves tmp file behind when sudo and not root
if switched_user and os . getuid ( ) != 0 :
# cleanup will happen by 'rm' of tempdir
# copy2 will preserve some metadata
shutil . copy2 ( src , tmp_dest . name )
else :
shutil . move ( src , tmp_dest . name )
if self . selinux_enabled ( ) :
self . set_context_if_different (
tmp_dest . name , context , False )
try :
try :
tmp_stat = os . stat ( tmp_dest . name )
if dest_stat and ( tmp_stat . st_uid != dest_stat . st_uid or tmp_stat . st_gid != dest_stat . st_gid ) :
os . chown ( tmp_dest . name , dest_stat . st_uid , dest_stat . st_gid )
except OSError :
e = get_exception ( )
if e . errno != errno . EPERM :
raise
os . rename ( tmp_dest . name , dest )
except ( shutil . Error , OSError , IOError ) :
e = get_exception ( )
# sadly there are some situations where we cannot ensure atomicity, but only if
# the user insists and we get the appropriate error we update the file unsafely
if unsafe_writes and e . errno == errno . EBUSY :
#TODO: issue warning that this is an unsafe operation, but doing it cause user insists
# close tmp file handle before file operations to prevent text file busy errors on vboxfs synced folders (windows host)
os . close ( tmp_dest_fd )
# leaves tmp file behind when sudo and not root
if switched_user and os . getuid ( ) != 0 :
# cleanup will happen by 'rm' of tempdir
# copy2 will preserve some metadata
shutil . copy2 ( b_src , b_tmp_dest_name )
else :
shutil . move ( b_src , b_tmp_dest_name )
if self . selinux_enabled ( ) :
self . set_context_if_different (
b_tmp_dest_name , context , False )
try :
try :
out_dest = open ( dest , ' wb ' )
in_src = open ( src , ' rb ' )
shutil . copyfileobj ( in_src , out_dest )
finally : # assuring closed files in 2.4 compatible way
if out_dest :
out_dest . close ( )
if in_src :
in_src . close ( )
except ( shutil . Error , OSError , IOError ) :
tmp_stat = os . stat ( b_tmp_dest_name )
if dest_stat and ( tmp_stat . st_uid != dest_stat . st_uid or tmp_stat . st_gid != dest_stat . st_gid ) :
os . chown ( b_tmp_dest_name , dest_stat . st_uid , dest_stat . st_gid )
except OSError :
e = get_exception ( )
self . fail_json ( msg = ' Could not write data to file ( %s ) from ( %s ): %s ' % ( dest , src , e ) )
else :
self . fail_json ( msg = ' Could not replace file: %s to %s : %s ' % ( src , dest , e ) )
self . cleanup ( tmp_dest . name )
if e . errno != errno . EPERM :
raise
os . rename ( b_tmp_dest_name , b_dest )
except ( shutil . Error , OSError , IOError ) :
e = get_exception ( )
# sadly there are some situations where we cannot ensure atomicity, but only if
# the user insists and we get the appropriate error we update the file unsafely
if unsafe_writes and e . errno == errno . EBUSY :
#TODO: issue warning that this is an unsafe operation, but doing it cause user insists
try :
try :
out_dest = open ( b_dest , ' wb ' )
in_src = open ( b_src , ' rb ' )
shutil . copyfileobj ( in_src , out_dest )
finally : # assuring closed files in 2.4 compatible way
if out_dest :
out_dest . close ( )
if in_src :
in_src . close ( )
except ( shutil . Error , OSError , IOError ) :
e = get_exception ( )
self . fail_json ( msg = ' Could not write data to file ( %s ) from ( %s ): %s ' % ( dest , src , e ) )
else :
self . fail_json ( msg = ' Could not replace file: %s to %s : %s ' % ( src , dest , e ) )
finally :
self . cleanup ( b_tmp_dest_name )
if creating :
# make sure the file has the correct permissions
# based on the current value of umask
umask = os . umask ( 0 )
os . umask ( umask )
os . chmod ( dest , DEFAULT_PERM & ~ umask )
os . chmod ( b_ dest, DEFAULT_PERM & ~ umask )
if switched_user :
os . chown ( dest , os . getuid ( ) , os . getgid ( ) )
os . chown ( b_ dest, os . getuid ( ) , os . getgid ( ) )
if self . selinux_enabled ( ) :
# rename might not preserve context
@ -2024,7 +2037,7 @@ class AnsibleModule(object):
: kw path_prefix : If given , additional path to find the command in .
This adds to the PATH environment vairable so helper commands in
the same directory can also be found
: kw cwd : i If given , working directory to run the command inside
: kw cwd : If given , working directory to run the command inside
: kw use_unsafe_shell : See ` args ` parameter . Default False
: kw prompt_regex : Regex string ( not a compiled regex ) which can be
used to detect prompts in the stdout which would otherwise cause
@ -2039,13 +2052,13 @@ class AnsibleModule(object):
shell = True
elif isinstance ( args , ( binary_type , text_type ) ) and use_unsafe_shell :
shell = True
elif isinstance ( args , string_types ) :
elif isinstance ( args , ( binary_type , text_type ) ) :
# On python2.6 and below, shlex has problems with text type
# On python3, shlex needs a text type.
if PY2 and isinstance ( args , text_type ) :
args = args. encode ( ' utf-8 ' )
elif PY3 and isinstance ( args , binary_type ) :
args = args. decode ( ' utf-8 ' , errors = ' surrogateescape ' )
if PY2 :
args = to_bytes( args )
elif PY3 :
args = to_text( args , errors = ' surrogateescape ' )
args = shlex . split ( args )
else :
msg = " Argument ' args ' to run_command must be list or string "
@ -2055,9 +2068,9 @@ class AnsibleModule(object):
if prompt_regex :
if isinstance ( prompt_regex , text_type ) :
if PY3 :
prompt_regex = prompt_regex. encode ( ' utf-8 ' , errors = ' surrogateescape ' )
prompt_regex = to_bytes( prompt_regex, errors = ' surrogateescape ' )
elif PY2 :
prompt_regex = prompt_regex. encode ( ' utf-8 ' )
prompt_regex = to_bytes( prompt_regex)
try :
prompt_re = re . compile ( prompt_regex , re . MULTILINE )
except re . error :
@ -2065,7 +2078,7 @@ class AnsibleModule(object):
# expand things like $HOME and ~
if not shell :
args = [ os . path . expand vars( os . path . expanduser ( x ) ) for x in args if x is not None ]
args = [ os . path . expand user( os . path . expandvars ( x ) ) for x in args if x is not None ]
rc = 0
msg = None