def xtermTitleReset(): global default_xterm_title if default_xterm_title is None: prompt_command = os.environ.get('PROMPT_COMMAND') if prompt_command == "": default_xterm_title = "" elif prompt_command is not None: if dotitles and \ 'TERM' in os.environ and \ _legal_terms_re.match(os.environ['TERM']) is not None and \ sys.__stderr__.isatty(): from portage.process import find_binary, spawn shell = os.environ.get("SHELL") if not shell or not os.access(shell, os.EX_OK): shell = find_binary("sh") if shell: spawn([shell, "-c", prompt_command], env=os.environ, fd_pipes={ 0: portage._get_stdin().fileno(), 1: sys.__stderr__.fileno(), 2: sys.__stderr__.fileno() }) else: os.system(prompt_command) return else: pwd = os.environ.get('PWD','') home = os.environ.get('HOME', '') if home != '' and pwd.startswith(home): pwd = '~' + pwd[len(home):] default_xterm_title = '\x1b]0;%s@%s:%s\x07' % ( os.environ.get('LOGNAME', ''), os.environ.get('HOSTNAME', '').split('.', 1)[0], pwd) xtermTitle(default_xterm_title, raw=True)
def portage_func(func, args, exit_status=0): # we don't care about the output of the programs, # just their exit value and the state of $D global env f = open('/dev/null', 'wb') fd_pipes = {0:0,1:f.fileno(),2:f.fileno()} spawn([func] + args.split(), env=env, fd_pipes=fd_pipes) f.close()
def portage_func(func, args, exit_status=0): # we don't care about the output of the programs, # just their exit value and the state of $D global env f = open('/dev/null', 'wb') fd_pipes = {0:0,1:f.fileno(),2:f.fileno()} def pre_exec(): os.chdir(env['S']) spawn([func] + args.split(), env=env, fd_pipes=fd_pipes, pre_exec=pre_exec) f.close()
def set_term_size(lines, columns, fd): """ Set the number of lines and columns for the tty that is connected to fd. For portability, this simply calls `stty rows $lines columns $columns`. """ from portage.process import spawn cmd = ["stty", "rows", str(lines), "columns", str(columns)] try: spawn(cmd, env=os.environ, fd_pipes={0:fd}) except CommandNotFound: writemsg(_("portage: stty: command not found\n"), noiselevel=-1)
def file_get(baseurl=None, dest=None, conn=None, fcmd=None, filename=None, fcmd_vars=None): """Takes a base url to connect to and read from. URI should be in the form <proto>://[user[:pass]@]<site>[:port]<path>""" if not fcmd: warnings.warn( "Use of portage.getbinpkg.file_get() without the fcmd " "parameter is deprecated", DeprecationWarning, stacklevel=2, ) return file_get_lib(baseurl, dest, conn) variables = {} if fcmd_vars is not None: variables.update(fcmd_vars) if "DISTDIR" not in variables: if dest is None: raise portage.exception.MissingParameter( _("%s is missing required '%s' key") % ("fcmd_vars", "DISTDIR")) variables["DISTDIR"] = dest if "URI" not in variables: if baseurl is None: raise portage.exception.MissingParameter( _("%s is missing required '%s' key") % ("fcmd_vars", "URI")) variables["URI"] = baseurl if "FILE" not in variables: if filename is None: filename = os.path.basename(variables["URI"]) variables["FILE"] = filename from portage.util import varexpand from portage.process import spawn myfetch = portage.util.shlex_split(fcmd) myfetch = [varexpand(x, mydict=variables) for x in myfetch] fd_pipes = { 0: portage._get_stdin().fileno(), 1: sys.__stdout__.fileno(), 2: sys.__stdout__.fileno(), } sys.__stdout__.flush() sys.__stderr__.flush() retval = spawn(myfetch, env=os.environ.copy(), fd_pipes=fd_pipes) if retval != os.EX_OK: sys.stderr.write(_("Fetcher exited with a failure condition.\n")) return 0 return 1
def start(self, settings): """ Start the SOCKSv5 server. @param settings: Portage settings instance (used to determine paths) @type settings: portage.config """ try: import asyncio # NOQA except ImportError: raise NotImplementedError('SOCKSv5 proxy requires asyncio module') tmpdir = os.path.join(settings['PORTAGE_TMPDIR'], 'portage') ensure_dirs_kwargs = {} if portage.secpass >= 1: ensure_dirs_kwargs['gid'] = portage_gid ensure_dirs_kwargs['mode'] = 0o70 ensure_dirs_kwargs['mask'] = 0 portage.util.ensure_dirs(tmpdir, **ensure_dirs_kwargs) self.socket_path = os.path.join(tmpdir, '.portage.%d.net.sock' % os.getpid()) server_bin = os.path.join(settings['PORTAGE_BIN_PATH'], 'socks5-server.py') spawn_kwargs = {} # The portage_uid check solves EPERM failures in Travis CI. if portage.data.secpass > 1 and os.geteuid() != portage_uid: spawn_kwargs.update( uid=portage_uid, gid=portage_gid, groups=userpriv_groups, umask=0o077) self._pids = spawn([_python_interpreter, server_bin, self.socket_path], returnpid=True, **spawn_kwargs)
def file_get(baseurl, dest, conn=None, fcmd=None): """(baseurl,dest,fcmd=) -- Takes a base url to connect to and read from. URI should be in the form <proto>://[user[:pass]@]<site>[:port]<path>""" if not fcmd: return file_get_lib(baseurl, dest, conn) variables = { "DISTDIR": dest, "URI": baseurl, "FILE": os.path.basename(baseurl) } from portage.util import varexpand from portage.process import spawn myfetch = portage.util.shlex_split(fcmd) myfetch = [varexpand(x, mydict=variables) for x in myfetch] fd_pipes = { 0: sys.stdin.fileno(), 1: sys.stdout.fileno(), 2: sys.stdout.fileno() } retval = spawn(myfetch, env=os.environ.copy(), fd_pipes=fd_pipes) if retval != os.EX_OK: sys.stderr.write(_("Fetcher exited with a failure condition.\n")) return 0 return 1
def file_get(baseurl,dest,conn=None,fcmd=None,filename=None): """(baseurl,dest,fcmd=) -- Takes a base url to connect to and read from. URI should be in the form <proto>://[user[:pass]@]<site>[:port]<path>""" if not fcmd: warnings.warn("Use of portage.getbinpkg.file_get() without the fcmd " "parameter is deprecated", DeprecationWarning, stacklevel=2) return file_get_lib(baseurl,dest,conn) if not filename: filename = os.path.basename(baseurl) variables = { "DISTDIR": dest, "URI": baseurl, "FILE": filename } from portage.util import varexpand from portage.process import spawn myfetch = portage.util.shlex_split(fcmd) myfetch = [varexpand(x, mydict=variables) for x in myfetch] fd_pipes= { 0:sys.__stdin__.fileno(), 1:sys.__stdout__.fileno(), 2:sys.__stdout__.fileno() } sys.__stdout__.flush() sys.__stderr__.flush() retval = spawn(myfetch, env=os.environ.copy(), fd_pipes=fd_pipes) if retval != os.EX_OK: sys.stderr.write(_("Fetcher exited with a failure condition.\n")) return 0 return 1
def file_get(baseurl,dest,conn=None,fcmd=None): """(baseurl,dest,fcmd=) -- Takes a base url to connect to and read from. URI should be in the form <proto>://[user[:pass]@]<site>[:port]<path>""" if not fcmd: return file_get_lib(baseurl,dest,conn) variables = { "DISTDIR": dest, "URI": baseurl, "FILE": os.path.basename(baseurl) } from portage.util import varexpand from portage.process import spawn myfetch = portage.util.shlex_split(fcmd) myfetch = [varexpand(x, mydict=variables) for x in myfetch] fd_pipes= { 0:sys.stdin.fileno(), 1:sys.stdout.fileno(), 2:sys.stdout.fileno() } retval = spawn(myfetch, env=os.environ.copy(), fd_pipes=fd_pipes) if retval != os.EX_OK: sys.stderr.write(_("Fetcher exited with a failure condition.\n")) return 0 return 1
def update_index(self, mymanifests, myupdates): '''Update the vcs's modified index if it is needed @param mymanifests: manifest files updated @param myupdates: other files updated''' # It's not safe to use the git commit -a option since there might # be some modified files elsewhere in the working tree that the # user doesn't want to commit. Therefore, call git update-index # in order to ensure that the index is updated with the latest # versions of all new and modified files in the relevant portion # of the working tree. myfiles = mymanifests + myupdates myfiles.sort() update_index_cmd = ["git", "update-index"] update_index_cmd.extend(f.lstrip("./") for f in myfiles) if self.options.pretend: print("(%s)" % (" ".join(update_index_cmd),)) else: retval = spawn(update_index_cmd, env=os.environ) if retval != os.EX_OK: writemsg_level( "!!! Exiting on %s (shell) " "error code: %s\n" % (self.vcs_settings.vcs, retval), level=logging.ERROR, noiselevel=-1) sys.exit(retval)
async def watch_pid(): with asyncio.get_child_watcher() as watcher: pids = spawn([true_binary], returnpid=True) watcher.add_child_handler(pids[0], callback, *args_tuple) self.assertEqual((await future), (pids[0], os.EX_OK, args_tuple))
def start(self, settings): """ Start the SOCKSv5 server. @param settings: Portage settings instance (used to determine paths) @type settings: portage.config """ try: import asyncio # NOQA except ImportError: raise NotImplementedError('SOCKSv5 proxy requires asyncio module') self.socket_path = os.path.join(settings['PORTAGE_TMPDIR'], '.portage.%d.net.sock' % os.getpid()) server_bin = os.path.join(settings['PORTAGE_BIN_PATH'], 'socks5-server.py') spawn_kwargs = {} # The portage_uid check solves EPERM failures in Travis CI. if portage.data.secpass > 1 and os.geteuid() != portage_uid: spawn_kwargs.update(uid=portage_uid, gid=portage_gid, groups=userpriv_groups, umask=0o077) self._pids = spawn([_python_interpreter, server_bin, self.socket_path], returnpid=True, **spawn_kwargs)
def testChildWatcher(self): true_binary = find_binary("true") self.assertNotEqual(true_binary, None) initial_policy = asyncio.get_event_loop_policy() if not isinstance(initial_policy, DefaultEventLoopPolicy): asyncio.set_event_loop_policy(DefaultEventLoopPolicy()) try: try: asyncio.set_child_watcher(None) except NotImplementedError: pass else: self.assertTrue(False) args_tuple = ('hello', 'world') loop = asyncio.get_event_loop() future = loop.create_future() def callback(pid, returncode, *args): future.set_result((pid, returncode, args)) with asyncio.get_child_watcher() as watcher: pids = spawn([true_binary], returnpid=True) watcher.add_child_handler(pids[0], callback, *args_tuple) self.assertEqual( loop.run_until_complete(future), (pids[0], os.EX_OK, args_tuple)) finally: asyncio.set_event_loop_policy(initial_policy)
def xtermTitleReset(): global default_xterm_title if default_xterm_title is None: prompt_command = os.environ.get("PROMPT_COMMAND") if prompt_command == "": default_xterm_title = "" elif prompt_command is not None: if ( dotitles and "TERM" in os.environ and _legal_terms_re.match(os.environ["TERM"]) is not None and sys.__stderr__.isatty() ): from portage.process import find_binary, spawn shell = os.environ.get("SHELL") if not shell or not os.access(shell, os.EX_OK): shell = find_binary("sh") if shell: spawn( [shell, "-c", prompt_command], env=os.environ, fd_pipes={ 0: portage._get_stdin().fileno(), 1: sys.__stderr__.fileno(), 2: sys.__stderr__.fileno(), }, ) else: os.system(prompt_command) return else: pwd = os.environ.get("PWD", "") home = os.environ.get("HOME", "") if home != "" and pwd.startswith(home): pwd = "~" + pwd[len(home) :] default_xterm_title = "\x1b]0;%s@%s:%s\x07" % ( os.environ.get("LOGNAME", ""), os.environ.get("HOSTNAME", "").split(".", 1)[0], pwd, ) xtermTitle(default_xterm_title, raw=True)
def add_manifest(self, mymanifests, myheaders, myupdates, myremoved, commitmessage): myfiles = mymanifests[:] # If there are no header (SVN/CVS keywords) changes in # the files, this Manifest commit must include the # other (yet uncommitted) files. if not myheaders: myfiles += myupdates myfiles += myremoved myfiles.sort() fd, commitmessagefile = tempfile.mkstemp(".repoman.msg") mymsg = os.fdopen(fd, "wb") mymsg.write(_unicode_encode(commitmessage)) mymsg.close() commit_cmd = [] if self.options.pretend and self.vcs_settings.vcs is None: # substitute a bogus value for pretend output commit_cmd.append("cvs") else: commit_cmd.append(self.vcs_settings.vcs) commit_cmd.extend(self.vcs_settings.vcs_global_opts) commit_cmd.append("commit") commit_cmd.extend(self.vcs_settings.vcs_local_opts) if self.vcs_settings.vcs == "hg": commit_cmd.extend(["--logfile", commitmessagefile]) commit_cmd.extend(myfiles) else: commit_cmd.extend(["-F", commitmessagefile]) commit_cmd.extend(f.lstrip("./") for f in myfiles) try: if self.options.pretend: print("(%s)" % (" ".join(commit_cmd),)) else: retval = spawn(commit_cmd, env=self.repo_settings.commit_env) if retval != os.EX_OK: if self.repo_settings.repo_config.sign_commit and self.vcs_settings.vcs == 'git' and \ not git_supports_gpg_sign(): # Inform user that newer git is needed (bug #403323). logging.error( "Git >=1.7.9 is required for signed commits!") writemsg_level( "!!! Exiting on %s (shell) " "error code: %s\n" % (self.vcs_settings.vcs, retval), level=logging.ERROR, noiselevel=-1) sys.exit(retval) finally: try: os.unlink(commitmessagefile) except OSError: pass
def file_get(baseurl=None, dest=None, conn=None, fcmd=None, filename=None, fcmd_vars=None): """Takes a base url to connect to and read from. URI should be in the form <proto>://[user[:pass]@]<site>[:port]<path>""" if not fcmd: warnings.warn("Use of portage.getbinpkg.file_get() without the fcmd " "parameter is deprecated", DeprecationWarning, stacklevel=2) return file_get_lib(baseurl, dest, conn) variables = {} if fcmd_vars is not None: variables.update(fcmd_vars) if "DISTDIR" not in variables: if dest is None: raise portage.exception.MissingParameter( _("%s is missing required '%s' key") % ("fcmd_vars", "DISTDIR")) variables["DISTDIR"] = dest if "URI" not in variables: if baseurl is None: raise portage.exception.MissingParameter( _("%s is missing required '%s' key") % ("fcmd_vars", "URI")) variables["URI"] = baseurl if "FILE" not in variables: if filename is None: filename = os.path.basename(variables["URI"]) variables["FILE"] = filename from portage.util import varexpand from portage.process import spawn myfetch = portage.util.shlex_split(fcmd) myfetch = [varexpand(x, mydict=variables) for x in myfetch] fd_pipes = { 0: portage._get_stdin().fileno(), 1: sys.__stdout__.fileno(), 2: sys.__stdout__.fileno() } sys.__stdout__.flush() sys.__stderr__.flush() retval = spawn(myfetch, env=os.environ.copy(), fd_pipes=fd_pipes) if retval != os.EX_OK: sys.stderr.write(_("Fetcher exited with a failure condition.\n")) return 0 return 1
def priming_commit(self, myupdates, myremoved, commitmessage): myfiles = myupdates + myremoved fd, commitmessagefile = tempfile.mkstemp(".repoman.msg") mymsg = os.fdopen(fd, "wb") mymsg.write(_unicode_encode(commitmessage)) mymsg.close() separator = '-' * 78 print() print(green("Using commit message:")) print(green(separator)) print(commitmessage) print(green(separator)) print() # Having a leading ./ prefix on file paths can trigger a bug in # the cvs server when committing files to multiple directories, # so strip the prefix. myfiles = [f.lstrip("./") for f in myfiles] commit_cmd = [self.vcs_settings.vcs] commit_cmd.extend(self.vcs_settings.vcs_global_opts) commit_cmd.append("commit") commit_cmd.extend(self.vcs_settings.vcs_local_opts) commit_cmd.extend(["-F", commitmessagefile]) commit_cmd.extend(myfiles) try: if self.options.pretend: print("(%s)" % (" ".join(commit_cmd),)) else: retval = spawn(commit_cmd, env=self.repo_settings.commit_env) if retval != os.EX_OK: writemsg_level( "!!! Exiting on %s (shell) " "error code: %s\n" % (self.vcs_settings.vcs, retval), level=logging.ERROR, noiselevel=-1) sys.exit(retval) finally: try: os.unlink(commitmessagefile) except OSError: pass
def commit(self, myfiles, commitmessagefile): '''Hg commit function @param commitfiles: list of files to commit @param commitmessagefile: file containing the commit message @returns: The sub-command exit value or 0 ''' commit_cmd = [] commit_cmd.append(self.vcs) commit_cmd.extend(self.vcs_settings.vcs_global_opts) commit_cmd.append("commit") commit_cmd.extend(self.vcs_settings.vcs_local_opts) commit_cmd.extend(["--logfile", commitmessagefile]) commit_cmd.extend(myfiles) if self.options.pretend: print("(%s)" % (" ".join(commit_cmd),)) return 0 else: retval = spawn(commit_cmd, env=self.repo_settings.commit_env) return retval
def commit(self, myfiles, commitmessagefile): """Hg commit function @param commitfiles: list of files to commit @param commitmessagefile: file containing the commit message @returns: The sub-command exit value or 0 """ commit_cmd = [] commit_cmd.append(self.vcs) commit_cmd.extend(self.vcs_settings.vcs_global_opts) commit_cmd.append("commit") commit_cmd.extend(self.vcs_settings.vcs_local_opts) commit_cmd.extend(["--logfile", commitmessagefile]) commit_cmd.extend(myfiles) if self.options.pretend: print("(%s)" % (" ".join(commit_cmd), )) return 0 else: retval = spawn(commit_cmd, env=self.repo_settings.commit_env) return retval
def start(self, settings): """ Start the SOCKSv5 server. @param settings: Portage settings instance (used to determine paths) @type settings: portage.config """ try: import asyncio # NOQA except ImportError: raise NotImplementedError("SOCKSv5 proxy requires asyncio module") self.socket_path = os.path.join(settings["PORTAGE_TMPDIR"], ".portage.%d.net.sock" % os.getpid()) server_bin = os.path.join(settings["PORTAGE_BIN_PATH"], "socks5-server.py") self._pids = spawn( [_python_interpreter, server_bin, self.socket_path], returnpid=True, uid=portage_uid, gid=portage_gid, groups=userpriv_groups, umask=0o077, )
def start(self, settings): """ Start the SOCKSv5 server. @param settings: Portage settings instance (used to determine paths) @type settings: portage.config """ try: import asyncio # NOQA except ImportError: raise NotImplementedError('SOCKSv5 proxy requires asyncio module') self.socket_path = os.path.join(settings['PORTAGE_TMPDIR'], '.portage.%d.net.sock' % os.getpid()) server_bin = os.path.join(settings['PORTAGE_BIN_PATH'], 'socks5-server.py') self._pids = spawn([_python_interpreter, server_bin, self.socket_path], returnpid=True, uid=portage_uid, gid=portage_gid, groups=userpriv_groups, umask=0o077)
def testChildWatcher(self): true_binary = find_binary("true") self.assertNotEqual(true_binary, None) initial_policy = asyncio.get_event_loop_policy() if not isinstance(initial_policy, DefaultEventLoopPolicy): asyncio.set_event_loop_policy(DefaultEventLoopPolicy()) loop = None try: try: asyncio.set_child_watcher(None) except NotImplementedError: pass else: self.assertTrue(False) args_tuple = ('hello', 'world') loop = asyncio._wrap_loop() future = loop.create_future() def callback(pid, returncode, *args): future.set_result((pid, returncode, args)) with asyncio.get_child_watcher() as watcher: pids = spawn([true_binary], returnpid=True) watcher.add_child_handler(pids[0], callback, *args_tuple) self.assertEqual( loop.run_until_complete(future), (pids[0], os.EX_OK, args_tuple)) finally: asyncio.set_event_loop_policy(initial_policy) if loop not in (None, global_event_loop()): loop.close() self.assertFalse(global_event_loop().is_closed())
def movefile(src, dest, newmtime=None, sstat=None, mysettings=None, hardlink_candidates=None, encoding=_encodings['fs']): """moves a file from src to dest, preserving all permissions and attributes; mtime will be preserved even when moving across filesystems. Returns mtime as integer on success and None on failure. mtime is expressed in seconds in Python <3.3 and nanoseconds in Python >=3.3. Move is atomic.""" if mysettings is None: mysettings = portage.settings src_bytes = _unicode_encode(src, encoding=encoding, errors='strict') dest_bytes = _unicode_encode(dest, encoding=encoding, errors='strict') xattr_enabled = "xattr" in mysettings.features selinux_enabled = mysettings.selinux_enabled() if selinux_enabled: selinux = _unicode_module_wrapper(_selinux, encoding=encoding) _copyfile = selinux.copyfile _rename = selinux.rename else: _copyfile = _shutil.copyfile _rename = _os.rename lchown = _unicode_func_wrapper(portage.data.lchown, encoding=encoding) os = _unicode_module_wrapper(_os, encoding=encoding, overrides=_os_overrides) try: if not sstat: sstat = os.lstat(src) except SystemExit as e: raise except Exception as e: writemsg("!!! %s\n" % _("Stating source file failed... movefile()"), noiselevel=-1) writemsg("!!! %s\n" % (e,), noiselevel=-1) return None destexists = 1 try: dstat = os.lstat(dest) except (OSError, IOError): dstat = os.lstat(os.path.dirname(dest)) destexists = 0 if bsd_chflags: if destexists and dstat.st_flags != 0: bsd_chflags.lchflags(dest, 0) # Use normal stat/chflags for the parent since we want to # follow any symlinks to the real parent directory. pflags = os.stat(os.path.dirname(dest)).st_flags if pflags != 0: bsd_chflags.chflags(os.path.dirname(dest), 0) if destexists: if stat.S_ISLNK(dstat[stat.ST_MODE]): try: os.unlink(dest) destexists = 0 except SystemExit as e: raise except Exception as e: pass if stat.S_ISLNK(sstat[stat.ST_MODE]): try: target = os.readlink(src) if mysettings and "D" in mysettings and \ target.startswith(mysettings["D"]): target = target[len(mysettings["D"])-1:] if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): os.unlink(dest) try: if selinux_enabled: selinux.symlink(target, dest, src) else: os.symlink(target, dest) except OSError as e: # Some programs will create symlinks automatically, so we have # to tolerate these links being recreated during the merge # process. In any case, if the link is pointing at the right # place, we're in good shape. if e.errno not in (errno.ENOENT, errno.EEXIST) or \ target != os.readlink(dest): raise lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID]) try: _os.unlink(src_bytes) except OSError: pass if sys.hexversion >= 0x3030000: try: os.utime(dest, ns=(sstat.st_mtime_ns, sstat.st_mtime_ns), follow_symlinks=False) except NotImplementedError: # utimensat() and lutimes() missing in libc. return os.stat(dest, follow_symlinks=False).st_mtime_ns else: return sstat.st_mtime_ns else: # utime() in Python <3.3 only works on the target of a symlink, so it's not # possible to preserve mtime on symlinks. return os.lstat(dest)[stat.ST_MTIME] except SystemExit as e: raise except Exception as e: writemsg("!!! %s\n" % _("failed to properly create symlink:"), noiselevel=-1) writemsg("!!! %s -> %s\n" % (dest, target), noiselevel=-1) writemsg("!!! %s\n" % (e,), noiselevel=-1) return None hardlinked = False # Since identical files might be merged to multiple filesystems, # so os.link() calls might fail for some paths, so try them all. # For atomic replacement, first create the link as a temp file # and them use os.rename() to replace the destination. if hardlink_candidates: head, tail = os.path.split(dest) hardlink_tmp = os.path.join(head, ".%s._portage_merge_.%s" % \ (tail, os.getpid())) try: os.unlink(hardlink_tmp) except OSError as e: if e.errno != errno.ENOENT: writemsg(_("!!! Failed to remove hardlink temp file: %s\n") % \ (hardlink_tmp,), noiselevel=-1) writemsg("!!! %s\n" % (e,), noiselevel=-1) return None del e for hardlink_src in hardlink_candidates: try: os.link(hardlink_src, hardlink_tmp) except OSError: continue else: try: os.rename(hardlink_tmp, dest) except OSError as e: writemsg(_("!!! Failed to rename %s to %s\n") % \ (hardlink_tmp, dest), noiselevel=-1) writemsg("!!! %s\n" % (e,), noiselevel=-1) return None hardlinked = True try: _os.unlink(src_bytes) except OSError: pass break renamefailed = 1 if hardlinked: renamefailed = False if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev): try: if selinux_enabled: selinux.rename(src, dest) else: os.rename(src, dest) renamefailed = 0 except OSError as e: if e.errno != errno.EXDEV: # Some random error. writemsg("!!! %s\n" % _("Failed to move %(src)s to %(dest)s") % {"src": src, "dest": dest}, noiselevel=-1) writemsg("!!! %s\n" % (e,), noiselevel=-1) return None # Invalid cross-device-link 'bind' mounted or actually Cross-Device if renamefailed: if stat.S_ISREG(sstat[stat.ST_MODE]): dest_tmp = dest + "#new" dest_tmp_bytes = _unicode_encode(dest_tmp, encoding=encoding, errors='strict') try: # For safety copy then move it over. _copyfile(src_bytes, dest_tmp_bytes) if xattr_enabled: try: _copyxattr(src_bytes, dest_tmp_bytes, exclude=mysettings.get("PORTAGE_XATTR_EXCLUDE", "")) except SystemExit: raise except: msg = _("Failed to copy extended attributes. " "In order to avoid this error, set " "FEATURES=\"-xattr\" in make.conf.") msg = textwrap.wrap(msg, 65) for line in msg: writemsg("!!! %s\n" % (line,), noiselevel=-1) raise _apply_stat(sstat, dest_tmp_bytes) _rename(dest_tmp_bytes, dest_bytes) _os.unlink(src_bytes) except SystemExit as e: raise except Exception as e: writemsg("!!! %s\n" % _('copy %(src)s -> %(dest)s failed.') % {"src": src, "dest": dest}, noiselevel=-1) writemsg("!!! %s\n" % (e,), noiselevel=-1) return None else: #we don't yet handle special, so we need to fall back to /bin/mv a = spawn([MOVE_BINARY, '-f', src, dest], env=os.environ) if a != os.EX_OK: writemsg(_("!!! Failed to move special file:\n"), noiselevel=-1) writemsg(_("!!! '%(src)s' to '%(dest)s'\n") % \ {"src": _unicode_decode(src, encoding=encoding), "dest": _unicode_decode(dest, encoding=encoding)}, noiselevel=-1) writemsg("!!! %s\n" % a, noiselevel=-1) return None # failure # In Python <3.3 always use stat_obj[stat.ST_MTIME] for the integral timestamp # which is returned, since the stat_obj.st_mtime float attribute rounds *up* # if the nanosecond part of the timestamp is 999999881 ns or greater. try: if hardlinked: if sys.hexversion >= 0x3030000: newmtime = os.stat(dest).st_mtime_ns else: newmtime = os.stat(dest)[stat.ST_MTIME] else: # Note: It is not possible to preserve nanosecond precision # (supported in POSIX.1-2008 via utimensat) with the IEEE 754 # double precision float which only has a 53 bit significand. if newmtime is not None: if sys.hexversion >= 0x3030000: os.utime(dest, ns=(newmtime, newmtime)) else: os.utime(dest, (newmtime, newmtime)) else: if sys.hexversion >= 0x3030000: newmtime = sstat.st_mtime_ns else: newmtime = sstat[stat.ST_MTIME] if renamefailed: if sys.hexversion >= 0x3030000: # If rename succeeded then timestamps are automatically # preserved with complete precision because the source # and destination inodes are the same. Otherwise, manually # update timestamps with nanosecond precision. os.utime(dest, ns=(newmtime, newmtime)) else: # If rename succeeded then timestamps are automatically # preserved with complete precision because the source # and destination inodes are the same. Otherwise, round # down to the nearest whole second since python's float # st_mtime cannot be used to preserve the st_mtim.tv_nsec # field with complete precision. Note that we have to use # stat_obj[stat.ST_MTIME] here because the float # stat_obj.st_mtime rounds *up* sometimes. os.utime(dest, (newmtime, newmtime)) except OSError: # The utime can fail here with EPERM even though the move succeeded. # Instead of failing, use stat to return the mtime if possible. try: if sys.hexversion >= 0x3030000: newmtime = os.stat(dest).st_mtime_ns else: newmtime = os.stat(dest)[stat.ST_MTIME] except OSError as e: writemsg(_("!!! Failed to stat in movefile()\n"), noiselevel=-1) writemsg("!!! %s\n" % dest, noiselevel=-1) writemsg("!!! %s\n" % str(e), noiselevel=-1) return None if bsd_chflags: # Restore the flags we saved before moving if pflags: bsd_chflags.chflags(os.path.dirname(dest), pflags) return newmtime
def movefile(src, dest, newmtime=None, sstat=None, mysettings=None, hardlink_candidates=None, encoding=_encodings['fs']): """moves a file from src to dest, preserving all permissions and attributes; mtime will be preserved even when moving across filesystems. Returns true on success and false on failure. Move is atomic.""" #print "movefile("+str(src)+","+str(dest)+","+str(newmtime)+","+str(sstat)+")" if mysettings is None: mysettings = portage.settings selinux_enabled = mysettings.selinux_enabled() if selinux_enabled: selinux = _unicode_module_wrapper(_selinux, encoding=encoding) lchown = _unicode_func_wrapper(portage.data.lchown, encoding=encoding) os = _unicode_module_wrapper(_os, encoding=encoding, overrides=_os_overrides) shutil = _unicode_module_wrapper(_shutil, encoding=encoding) try: if not sstat: sstat=os.lstat(src) except SystemExit as e: raise except Exception as e: print(_("!!! Stating source file failed... movefile()")) print("!!!",e) return None destexists=1 try: dstat=os.lstat(dest) except (OSError, IOError): dstat=os.lstat(os.path.dirname(dest)) destexists=0 if bsd_chflags: if destexists and dstat.st_flags != 0: bsd_chflags.lchflags(dest, 0) # Use normal stat/chflags for the parent since we want to # follow any symlinks to the real parent directory. pflags = os.stat(os.path.dirname(dest)).st_flags if pflags != 0: bsd_chflags.chflags(os.path.dirname(dest), 0) if destexists: if stat.S_ISLNK(dstat[stat.ST_MODE]): try: os.unlink(dest) destexists=0 except SystemExit as e: raise except Exception as e: pass if stat.S_ISLNK(sstat[stat.ST_MODE]): try: target=os.readlink(src) if mysettings and mysettings["D"]: if target.find(mysettings["D"])==0: target=target[len(mysettings["D"]):] if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): os.unlink(dest) if selinux_enabled: selinux.symlink(target, dest, src) else: os.symlink(target,dest) lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) # utime() only works on the target of a symlink, so it's not # possible to perserve mtime on symlinks. return os.lstat(dest)[stat.ST_MTIME] except SystemExit as e: raise except Exception as e: print(_("!!! failed to properly create symlink:")) print("!!!",dest,"->",target) print("!!!",e) return None hardlinked = False # Since identical files might be merged to multiple filesystems, # so os.link() calls might fail for some paths, so try them all. # For atomic replacement, first create the link as a temp file # and them use os.rename() to replace the destination. if hardlink_candidates: head, tail = os.path.split(dest) hardlink_tmp = os.path.join(head, ".%s._portage_merge_.%s" % \ (tail, os.getpid())) try: os.unlink(hardlink_tmp) except OSError as e: if e.errno != errno.ENOENT: writemsg(_("!!! Failed to remove hardlink temp file: %s\n") % \ (hardlink_tmp,), noiselevel=-1) writemsg("!!! %s\n" % (e,), noiselevel=-1) return None del e for hardlink_src in hardlink_candidates: try: os.link(hardlink_src, hardlink_tmp) except OSError: continue else: try: os.rename(hardlink_tmp, dest) except OSError as e: writemsg(_("!!! Failed to rename %s to %s\n") % \ (hardlink_tmp, dest), noiselevel=-1) writemsg("!!! %s\n" % (e,), noiselevel=-1) return None hardlinked = True break renamefailed=1 if hardlinked: renamefailed = False if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev): try: if selinux_enabled: selinux.rename(src, dest) else: os.rename(src,dest) renamefailed=0 except OSError as e: if e.errno != errno.EXDEV: # Some random error. print(_("!!! Failed to move %(src)s to %(dest)s") % {"src": src, "dest": dest}) print("!!!",e) return None # Invalid cross-device-link 'bind' mounted or actually Cross-Device if renamefailed: didcopy=0 if stat.S_ISREG(sstat[stat.ST_MODE]): try: # For safety copy then move it over. if selinux_enabled: selinux.copyfile(src, dest + "#new") selinux.rename(dest + "#new", dest) else: shutil.copyfile(src,dest+"#new") os.rename(dest+"#new",dest) didcopy=1 except SystemExit as e: raise except Exception as e: print(_('!!! copy %(src)s -> %(dest)s failed.') % {"src": src, "dest": dest}) print("!!!",e) return None else: #we don't yet handle special, so we need to fall back to /bin/mv a = spawn([MOVE_BINARY, '-f', src, dest], env=os.environ) if a != os.EX_OK: writemsg(_("!!! Failed to move special file:\n"), noiselevel=-1) writemsg(_("!!! '%(src)s' to '%(dest)s'\n") % \ {"src": _unicode_decode(src, encoding=encoding), "dest": _unicode_decode(dest, encoding=encoding)}, noiselevel=-1) writemsg("!!! %s\n" % a, noiselevel=-1) return None # failure try: if didcopy: if stat.S_ISLNK(sstat[stat.ST_MODE]): lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) else: os.chown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown os.unlink(src) except SystemExit as e: raise except Exception as e: print(_("!!! Failed to chown/chmod/unlink in movefile()")) print("!!!",dest) print("!!!",e) return None # Always use stat_obj[stat.ST_MTIME] for the integral timestamp which # is returned, since the stat_obj.st_mtime float attribute rounds *up* # if the nanosecond part of the timestamp is 999999881 ns or greater. try: if hardlinked: newmtime = os.stat(dest)[stat.ST_MTIME] else: # Note: It is not possible to preserve nanosecond precision # (supported in POSIX.1-2008 via utimensat) with the IEEE 754 # double precision float which only has a 53 bit significand. if newmtime is not None: os.utime(dest, (newmtime, newmtime)) else: newmtime = sstat[stat.ST_MTIME] if renamefailed: # If rename succeeded then timestamps are automatically # preserved with complete precision because the source # and destination inode are the same. Otherwise, round # down to the nearest whole second since python's float # st_mtime cannot be used to preserve the st_mtim.tv_nsec # field with complete precision. Note that we have to use # stat_obj[stat.ST_MTIME] here because the float # stat_obj.st_mtime rounds *up* sometimes. os.utime(dest, (newmtime, newmtime)) except OSError: # The utime can fail here with EPERM even though the move succeeded. # Instead of failing, use stat to return the mtime if possible. try: newmtime = os.stat(dest)[stat.ST_MTIME] except OSError as e: writemsg(_("!!! Failed to stat in movefile()\n"), noiselevel=-1) writemsg("!!! %s\n" % dest, noiselevel=-1) writemsg("!!! %s\n" % str(e), noiselevel=-1) return None if bsd_chflags: # Restore the flags we saved before moving if pflags: bsd_chflags.chflags(os.path.dirname(dest), pflags) return newmtime
def movefile(src, dest, newmtime=None, sstat=None, mysettings=None, hardlink_candidates=None, encoding=_encodings['fs']): """moves a file from src to dest, preserving all permissions and attributes; mtime will be preserved even when moving across filesystems. Returns mtime as integer on success and None on failure. mtime is expressed in seconds in Python <3.3 and nanoseconds in Python >=3.3. Move is atomic.""" if mysettings is None: mysettings = portage.settings src_bytes = _unicode_encode(src, encoding=encoding, errors='strict') dest_bytes = _unicode_encode(dest, encoding=encoding, errors='strict') xattr_enabled = "xattr" in mysettings.features selinux_enabled = mysettings.selinux_enabled() if selinux_enabled: selinux = _unicode_module_wrapper(_selinux, encoding=encoding) _copyfile = selinux.copyfile _rename = selinux.rename else: _copyfile = _shutil.copyfile _rename = _os.rename lchown = _unicode_func_wrapper(portage.data.lchown, encoding=encoding) os = _unicode_module_wrapper(_os, encoding=encoding, overrides=_os_overrides) try: if not sstat: sstat = os.lstat(src) except SystemExit as e: raise except Exception as e: writemsg("!!! %s\n" % _("Stating source file failed... movefile()"), noiselevel=-1) writemsg("!!! %s\n" % (e, ), noiselevel=-1) return None destexists = 1 try: dstat = os.lstat(dest) except (OSError, IOError): dstat = os.lstat(os.path.dirname(dest)) destexists = 0 if bsd_chflags: if destexists and dstat.st_flags != 0: bsd_chflags.lchflags(dest, 0) # Use normal stat/chflags for the parent since we want to # follow any symlinks to the real parent directory. pflags = os.stat(os.path.dirname(dest)).st_flags if pflags != 0: bsd_chflags.chflags(os.path.dirname(dest), 0) if destexists: if stat.S_ISLNK(dstat[stat.ST_MODE]): try: os.unlink(dest) destexists = 0 except SystemExit as e: raise except Exception as e: pass if stat.S_ISLNK(sstat[stat.ST_MODE]): try: target = os.readlink(src) if mysettings and "D" in mysettings and \ target.startswith(mysettings["D"]): target = target[len(mysettings["D"]) - 1:] if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): os.unlink(dest) try: if selinux_enabled: selinux.symlink(target, dest, src) else: os.symlink(target, dest) except OSError as e: # Some programs will create symlinks automatically, so we have # to tolerate these links being recreated during the merge # process. In any case, if the link is pointing at the right # place, we're in good shape. if e.errno not in (errno.ENOENT, errno.EEXIST) or \ target != os.readlink(dest): raise lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID]) try: _os.unlink(src_bytes) except OSError: pass if sys.hexversion >= 0x3030000: try: os.utime(dest, ns=(sstat.st_mtime_ns, sstat.st_mtime_ns), follow_symlinks=False) except NotImplementedError: # utimensat() and lutimes() missing in libc. return os.stat(dest, follow_symlinks=False).st_mtime_ns else: return sstat.st_mtime_ns else: # utime() in Python <3.3 only works on the target of a symlink, so it's not # possible to preserve mtime on symlinks. return os.lstat(dest)[stat.ST_MTIME] except SystemExit as e: raise except Exception as e: writemsg("!!! %s\n" % _("failed to properly create symlink:"), noiselevel=-1) writemsg("!!! %s -> %s\n" % (dest, target), noiselevel=-1) writemsg("!!! %s\n" % (e, ), noiselevel=-1) return None hardlinked = False # Since identical files might be merged to multiple filesystems, # so os.link() calls might fail for some paths, so try them all. # For atomic replacement, first create the link as a temp file # and them use os.rename() to replace the destination. if hardlink_candidates: head, tail = os.path.split(dest) hardlink_tmp = os.path.join(head, ".%s._portage_merge_.%s" % \ (tail, os.getpid())) try: os.unlink(hardlink_tmp) except OSError as e: if e.errno != errno.ENOENT: writemsg(_("!!! Failed to remove hardlink temp file: %s\n") % \ (hardlink_tmp,), noiselevel=-1) writemsg("!!! %s\n" % (e, ), noiselevel=-1) return None del e for hardlink_src in hardlink_candidates: try: os.link(hardlink_src, hardlink_tmp) except OSError: continue else: try: os.rename(hardlink_tmp, dest) except OSError as e: writemsg(_("!!! Failed to rename %s to %s\n") % \ (hardlink_tmp, dest), noiselevel=-1) writemsg("!!! %s\n" % (e, ), noiselevel=-1) return None hardlinked = True try: _os.unlink(src_bytes) except OSError: pass break renamefailed = 1 if hardlinked: renamefailed = False if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev): try: if selinux_enabled: selinux.rename(src, dest) else: os.rename(src, dest) renamefailed = 0 except OSError as e: if e.errno != errno.EXDEV: # Some random error. writemsg("!!! %s\n" % _("Failed to move %(src)s to %(dest)s") % { "src": src, "dest": dest }, noiselevel=-1) writemsg("!!! %s\n" % (e, ), noiselevel=-1) return None # Invalid cross-device-link 'bind' mounted or actually Cross-Device if renamefailed: if stat.S_ISREG(sstat[stat.ST_MODE]): dest_tmp = dest + "#new" dest_tmp_bytes = _unicode_encode(dest_tmp, encoding=encoding, errors='strict') try: # For safety copy then move it over. _copyfile(src_bytes, dest_tmp_bytes) if xattr_enabled: try: _copyxattr(src_bytes, dest_tmp_bytes, exclude=mysettings.get( "PORTAGE_XATTR_EXCLUDE", "security.* system.nfs4_acl")) except SystemExit: raise except: msg = _("Failed to copy extended attributes. " "In order to avoid this error, set " "FEATURES=\"-xattr\" in make.conf.") msg = textwrap.wrap(msg, 65) for line in msg: writemsg("!!! %s\n" % (line, ), noiselevel=-1) raise _apply_stat(sstat, dest_tmp_bytes) _rename(dest_tmp_bytes, dest_bytes) _os.unlink(src_bytes) except SystemExit as e: raise except Exception as e: writemsg("!!! %s\n" % _('copy %(src)s -> %(dest)s failed.') % { "src": src, "dest": dest }, noiselevel=-1) writemsg("!!! %s\n" % (e, ), noiselevel=-1) return None else: #we don't yet handle special, so we need to fall back to /bin/mv a = spawn([MOVE_BINARY, '-f', src, dest], env=os.environ) if a != os.EX_OK: writemsg(_("!!! Failed to move special file:\n"), noiselevel=-1) writemsg(_("!!! '%(src)s' to '%(dest)s'\n") % \ {"src": _unicode_decode(src, encoding=encoding), "dest": _unicode_decode(dest, encoding=encoding)}, noiselevel=-1) writemsg("!!! %s\n" % a, noiselevel=-1) return None # failure # In Python <3.3 always use stat_obj[stat.ST_MTIME] for the integral timestamp # which is returned, since the stat_obj.st_mtime float attribute rounds *up* # if the nanosecond part of the timestamp is 999999881 ns or greater. try: if hardlinked: if sys.hexversion >= 0x3030000: newmtime = os.stat(dest).st_mtime_ns else: newmtime = os.stat(dest)[stat.ST_MTIME] else: # Note: It is not possible to preserve nanosecond precision # (supported in POSIX.1-2008 via utimensat) with the IEEE 754 # double precision float which only has a 53 bit significand. if newmtime is not None: if sys.hexversion >= 0x3030000: os.utime(dest, ns=(newmtime, newmtime)) else: os.utime(dest, (newmtime, newmtime)) else: if sys.hexversion >= 0x3030000: newmtime = sstat.st_mtime_ns else: newmtime = sstat[stat.ST_MTIME] if renamefailed: if sys.hexversion >= 0x3030000: # If rename succeeded then timestamps are automatically # preserved with complete precision because the source # and destination inodes are the same. Otherwise, manually # update timestamps with nanosecond precision. os.utime(dest, ns=(newmtime, newmtime)) else: # If rename succeeded then timestamps are automatically # preserved with complete precision because the source # and destination inodes are the same. Otherwise, round # down to the nearest whole second since python's float # st_mtime cannot be used to preserve the st_mtim.tv_nsec # field with complete precision. Note that we have to use # stat_obj[stat.ST_MTIME] here because the float # stat_obj.st_mtime rounds *up* sometimes. os.utime(dest, (newmtime, newmtime)) except OSError: # The utime can fail here with EPERM even though the move succeeded. # Instead of failing, use stat to return the mtime if possible. try: if sys.hexversion >= 0x3030000: newmtime = os.stat(dest).st_mtime_ns else: newmtime = os.stat(dest)[stat.ST_MTIME] except OSError as e: writemsg(_("!!! Failed to stat in movefile()\n"), noiselevel=-1) writemsg("!!! %s\n" % dest, noiselevel=-1) writemsg("!!! %s\n" % str(e), noiselevel=-1) return None if bsd_chflags: # Restore the flags we saved before moving if pflags: bsd_chflags.chflags(os.path.dirname(dest), pflags) return newmtime
def perform(self, qa_output): myunadded, mydeleted = self._vcs_unadded() myautoadd = self._vcs_autoadd(myunadded) self._vcs_deleted(mydeleted) changes = self.get_vcs_changed(mydeleted) mynew, mychanged, myremoved, no_expansion, expansion = changes # Manifests need to be regenerated after all other commits, so don't commit # them now even if they have changed. mymanifests = set() myupdates = set() for f in mychanged + mynew: if "Manifest" == os.path.basename(f): mymanifests.add(f) else: myupdates.add(f) myupdates.difference_update(myremoved) myupdates = list(myupdates) mymanifests = list(mymanifests) myheaders = [] commitmessage = self.options.commitmsg if self.options.commitmsgfile: try: f = io.open( _unicode_encode( self.options.commitmsgfile, encoding=_encodings['fs'], errors='strict'), mode='r', encoding=_encodings['content'], errors='replace') commitmessage = f.read() f.close() del f except (IOError, OSError) as e: if e.errno == errno.ENOENT: portage.writemsg( "!!! File Not Found:" " --commitmsgfile='%s'\n" % self.options.commitmsgfile) else: raise if not commitmessage or not commitmessage.strip(): commitmessage = self.get_new_commit_message(qa_output) commitmessage = commitmessage.rstrip() myupdates, broken_changelog_manifests = self.changelogs( myupdates, mymanifests, myremoved, mychanged, myautoadd, mynew, commitmessage) commit_footer = self.get_commit_footer() commitmessage += commit_footer print("* %s files being committed..." % green(str(len(myupdates))), end=' ') if self.vcs_settings.vcs not in ('cvs', 'svn'): # With git, bzr and hg, there's never any keyword expansion, so # there's no need to regenerate manifests and all files will be # committed in one big commit at the end. print() elif not self.repo_settings.repo_config.thin_manifest: self.thick_manifest(myupdates, myheaders, no_expansion, expansion) logging.info("myupdates: %s", myupdates) logging.info("myheaders: %s", myheaders) uq = UserQuery(self.options) if self.options.ask and uq.query('Commit changes?', True) != 'Yes': print("* aborting commit.") sys.exit(128 + signal.SIGINT) # Handle the case where committed files have keywords which # will change and need a priming commit before the Manifest # can be committed. if (myupdates or myremoved) and myheaders: self.priming_commit(myupdates, myremoved, commitmessage) # When files are removed and re-added, the cvs server will put /Attic/ # inside the $Header path. This code detects the problem and corrects it # so that the Manifest will generate correctly. See bug #169500. # Use binary mode in order to avoid potential character encoding issues. self.clear_attic(myheaders) if self.scanner.repolevel == 1: utilities.repoman_sez( "\"You're rather crazy... " "doing the entire repository.\"\n") if self.vcs_settings.vcs in ('cvs', 'svn') and (myupdates or myremoved): for x in sorted(vcs_files_to_cps( chain(myupdates, myremoved, mymanifests), self.scanner.repolevel, self.scanner.reposplit, self.scanner.categories)): self.repoman_settings["O"] = os.path.join(self.repo_settings.repodir, x) digestgen(mysettings=self.repoman_settings, myportdb=self.repo_settings.portdb) elif broken_changelog_manifests: for x in broken_changelog_manifests: self.repoman_settings["O"] = os.path.join(self.repo_settings.repodir, x) digestgen(mysettings=self.repoman_settings, myportdb=self.repo_settings.portdb) if self.repo_settings.sign_manifests: self.sign_manifest(myupdates, myremoved, mymanifests) if self.vcs_settings.vcs == 'git': # It's not safe to use the git commit -a option since there might # be some modified files elsewhere in the working tree that the # user doesn't want to commit. Therefore, call git update-index # in order to ensure that the index is updated with the latest # versions of all new and modified files in the relevant portion # of the working tree. myfiles = mymanifests + myupdates myfiles.sort() update_index_cmd = ["git", "update-index"] update_index_cmd.extend(f.lstrip("./") for f in myfiles) if self.options.pretend: print("(%s)" % (" ".join(update_index_cmd),)) else: retval = spawn(update_index_cmd, env=os.environ) if retval != os.EX_OK: writemsg_level( "!!! Exiting on %s (shell) " "error code: %s\n" % (self.vcs_settings.vcs, retval), level=logging.ERROR, noiselevel=-1) sys.exit(retval) self.add_manifest(mymanifests, myheaders, myupdates, myremoved, commitmessage) if self.options.quiet: return print() if self.vcs_settings.vcs: print("Commit complete.") else: print( "repoman was too scared" " by not seeing any familiar version control file" " that he forgot to commit anything") utilities.repoman_sez( "\"If everyone were like you, I'd be out of business!\"\n") return