commit:     7909ed55ba45b8a94f4a29148e43953eb58aa184
Author:     Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Fri Dec  5 22:40:13 2014 +0000
Commit:     Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Sun Dec  7 22:57:07 2014 +0000
URL:        
http://sources.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=7909ed55

sync: allow overriding sync-user for the repository

---
 man/portage.5                    | 12 ++++++++++
 pym/portage/repository/config.py | 13 +++++++---
 pym/portage/sync/controller.py   | 51 +++++++++++++++++++++++++++++++++++++++-
 3 files changed, 72 insertions(+), 4 deletions(-)

diff --git a/man/portage.5 b/man/portage.5
index 8c3d389..4a02c64 100644
--- a/man/portage.5
+++ b/man/portage.5
@@ -934,6 +934,18 @@ ssh://ssh\[email protected]:22/\\${HOME}/portage\-storage
 .TP
 Note: For the ssh:// scheme, key\-based authentication might be of interest.
 .RE
+.TP
+.B sync\-user
+Specifies the credentials used to perform the synchronization.
+.br
+Syntax: [user][:group]
+.br
+If only user is provided, the primary group of the user will be used.
+If only group is provided, the current user will be preserved and only
+group id will be changed.
+.br
+This key takes precedence over FEATURES=userpriv. If user or group id
+is provided, Portage no longer uses owner of the directory.
 .RE
 
 .I Example:

diff --git a/pym/portage/repository/config.py b/pym/portage/repository/config.py
index 678cc68..f45684b 100644
--- a/pym/portage/repository/config.py
+++ b/pym/portage/repository/config.py
@@ -85,7 +85,7 @@ class RepoConfig(object):
                'main_repo', 'manifest_hashes', 'masters', 'missing_repo_name',
                'name', 'portage1_profiles', 'portage1_profiles_compat', 
'priority',
                'profile_formats', 'sign_commit', 'sign_manifest', 
'sync_cvs_repo',
-               'sync_type', 'sync_umask', 'sync_uri', 'thin_manifest',
+               'sync_type', 'sync_umask', 'sync_uri', 'sync_user', 
'thin_manifest',
                'update_changelog', 'user_location', '_eapis_banned',
                '_eapis_deprecated', '_masters_orig')
 
@@ -165,6 +165,11 @@ class RepoConfig(object):
                        sync_uri = sync_uri.strip()
                self.sync_uri = sync_uri or None
 
+               sync_user = repo_opts.get('sync-user')
+               if sync_user is not None:
+                       sync_user = sync_user.strip()
+               self.sync_user = sync_user or None
+
                auto_sync = repo_opts.get('auto-sync')
                if auto_sync is not None:
                        auto_sync = auto_sync.strip().lower()
@@ -385,6 +390,8 @@ class RepoConfig(object):
                        repo_msg.append(indent + "sync-umask: " + 
self.sync_umask)
                if self.sync_uri:
                        repo_msg.append(indent + "sync-uri: " + self.sync_uri)
+               if self.sync_user:
+                       repo_msg.append(indent + "sync-user: " + self.sync_user)
                if self.masters:
                        repo_msg.append(indent + "masters: " + " 
".join(master.name for master in self.masters))
                if self.priority is not None:
@@ -472,7 +479,7 @@ class RepoConfigLoader(object):
                                                # repos.conf is allowed to 
override.
                                                for k in ('aliases', 
'auto_sync', 'eclass_overrides',
                                                        'force', 'masters', 
'priority', 'sync_cvs_repo',
-                                                       'sync_type', 
'sync_umask', 'sync_uri',
+                                                       'sync_type', 
'sync_umask', 'sync_uri', 'sync_user',
                                                        ):
                                                        v = 
getattr(repos_conf_opts, k, None)
                                                        if v is not None:
@@ -923,7 +930,7 @@ class RepoConfigLoader(object):
        def config_string(self):
                str_or_int_keys = ("auto_sync", "format", "location",
                        "main_repo", "priority", "sync_cvs_repo",
-                       "sync_type", "sync_umask", "sync_uri")
+                       "sync_type", "sync_umask", "sync_uri", 'sync_user')
                str_tuple_keys = ("aliases", "eclass_overrides", "force")
                repo_config_tuple_keys = ("masters",)
                keys = str_or_int_keys + str_tuple_keys + repo_config_tuple_keys

diff --git a/pym/portage/sync/controller.py b/pym/portage/sync/controller.py
index 006300c..ab6eb21 100644
--- a/pym/portage/sync/controller.py
+++ b/pym/portage/sync/controller.py
@@ -6,6 +6,7 @@ from __future__ import print_function
 
 import sys
 import logging
+import grp
 import pwd
 
 import portage
@@ -200,7 +201,55 @@ class SyncManager(object):
                self.usersync_uid = None
                spawn_kwargs = {}
                spawn_kwargs["env"] = self.settings.environ()
-               if ('usersync' in self.settings.features and
+               if repo.sync_user is not None:
+                       def get_sync_user_data(sync_user):
+                               user = None
+                               group = None
+                               home = None
+
+                               spl = sync_user.split(':', 1)
+                               if spl[0]:
+                                       username = spl[0]
+                                       try:
+                                               try:
+                                                       pw = 
pwd.getpwnam(username)
+                                               except KeyError:
+                                                       pw = 
pwd.getpwuid(int(username))
+                                       except (ValueError, KeyError):
+                                               writemsg("!!! User '%s' invalid 
or does not exist\n"
+                                                               % username, 
noiselevel=-1)
+                                               return (user, group, home)
+                                       user = pw.pw_uid
+                                       group = pw.pw_gid
+                                       home = pw.pw_dir
+
+                               if len(spl) > 1:
+                                       groupname = spl[1]
+                                       try:
+                                               try:
+                                                       gp = 
grp.getgrnam(groupname)
+                                               except KeyError:
+                                                       pw = 
grp.getgrgid(int(groupname))
+                                       except (ValueError, KeyError):
+                                               writemsg("!!! Group '%s' 
invalid or does not exist\n"
+                                                               % groupname, 
noiselevel=-1)
+                                               return (user, group, home)
+
+                                       group = gp.gr_gid
+
+                               return (user, group, home)
+
+                       # user or user:group
+                       (uid, gid, home) = get_sync_user_data(repo.sync_user)
+                       if uid is not None:
+                               spawn_kwargs["uid"] = uid
+                               self.usersync_uid = uid
+                       if gid is not None:
+                               spawn_kwargs["gid"] = gid
+                               spawn_kwargs["groups"] = [gid]
+                       if home is not None:
+                               spawn_kwargs["env"]["HOME"] = home
+               elif ('usersync' in self.settings.features and
                        portage.data.secpass >= 2 and
                        (st.st_uid != os.getuid() and st.st_mode & 0o700 or
                        st.st_gid != os.getgid() and st.st_mode & 0o070)):

Reply via email to