[master 1/2] Get rid of libuser. (#1255066)

dashea installerbot-noreply at redhat.com
Thu Aug 20 17:31:47 UTC 2015


From: David Shea <dshea at redhat.com>

The changes made to libuser for python3 support have made it difficult
to work with since it must now be imported from a UTF-8 locale, working
with it in the first place already involved a bunch of gross hacks, and
there's no reason why adding users and groups should need this
complicated of a library. Dump it and use the moderately less quirky
command-line tools.
---
 anaconda                |   6 -
 anaconda.spec.in        |   1 -
 pyanaconda/install.py   |   3 +-
 pyanaconda/kickstart.py |   6 +-
 pyanaconda/users.py     | 444 +++++++++++++++++++++---------------------------
 utils/handle-sshpw      |   4 -
 6 files changed, 200 insertions(+), 264 deletions(-)

diff --git a/anaconda b/anaconda
index 1ccad07..b57e7e9 100755
--- a/anaconda
+++ b/anaconda
@@ -386,8 +386,6 @@ def setupPythonPath():
     sys.path.extend(ADDON_PATHS)
 
 def setupEnvironment():
-    from pyanaconda.users import createLuserConf
-
     # This method is run before any threads are started, so this is the one
     # point where it's ok to modify the environment.
     # pylint: disable=environment-modify
@@ -416,10 +414,6 @@ def setupEnvironment():
     else:
         os.environ["DISPLAY"] = ":%s" % constants.X_DISPLAY_NUMBER
 
-    # Call createLuserConf now to setup $LIBUSER_CONF
-    # the config file can change later but the environment variable cannot
-    createLuserConf(iutil.getSysroot())
-
 def setupLoggingFromOpts(options):
     if (options.debug or options.updateSrc) and not options.loglevel:
         # debugging means debug logging if an explicit level hasn't been st
diff --git a/anaconda.spec.in b/anaconda.spec.in
index 10baf31..13fb99e 100644
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -94,7 +94,6 @@ Requires: python3-requests-ftp
 Requires: python3-kickstart >= %{pykickstartver}
 Requires: langtable-data >= %{langtablever}
 Requires: langtable-python3 >= %{langtablever}
-Requires: libuser-python3
 Requires: authconfig
 Requires: firewalld >= %{firewalldver}
 Requires: util-linux >= %{utillinuxver}
diff --git a/pyanaconda/install.py b/pyanaconda/install.py
index 43655c9..e0618fc 100644
--- a/pyanaconda/install.py
+++ b/pyanaconda/install.py
@@ -25,7 +25,7 @@
 from blivet.devices import BTRFSDevice
 from pyanaconda.bootloader import writeBootLoader
 from pyanaconda.progress import progress_report, progress_message, progress_step, progress_complete, progress_init
-from pyanaconda.users import createLuserConf, getPassAlgo, Users
+from pyanaconda.users import Users
 from pyanaconda import flags
 from pyanaconda import iutil
 from pyanaconda import timezone
@@ -90,7 +90,6 @@ def doConfiguration(storage, payload, ksdata, instClass):
 
     # Creating users and groups requires some pre-configuration.
     with progress_report(_("Creating users")):
-        createLuserConf(iutil.getSysroot(), algoname=getPassAlgo(ksdata.authconfig.authconfig))
         u = Users()
         ksdata.rootpw.execute(storage, ksdata, instClass, u)
         ksdata.group.execute(storage, ksdata, instClass, u)
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py
index a2b5899..0c72750 100644
--- a/pyanaconda/kickstart.py
+++ b/pyanaconda/kickstart.py
@@ -99,9 +99,6 @@ def run(self, chroot):
         else:
             scriptRoot = "/"
 
-        # Environment variables that cause problems for %post scripts
-        env_prune = ["LIBUSER_CONF"]
-
         (fd, path) = tempfile.mkstemp("", "ks-script-", scriptRoot + "/tmp")
 
         iutil.eintr_retry_call(os.write, fd, self.script.encode("utf-8"))
@@ -128,8 +125,7 @@ def run(self, chroot):
         with open(messages, "w") as fp:
             rc = iutil.execWithRedirect(self.interp, ["/tmp/%s" % os.path.basename(path)],
                                         stdout=fp,
-                                        root=scriptRoot,
-                                        env_prune=env_prune)
+                                        root=scriptRoot)
 
         if rc != 0:
             log.error("Error code %s running the kickstart script at line %s", rc, self.lineno)
diff --git a/pyanaconda/users.py b/pyanaconda/users.py
index 5eee4fc..9e8f03f 100644
--- a/pyanaconda/users.py
+++ b/pyanaconda/users.py
@@ -19,7 +19,6 @@
 # Author(s): Chris Lumens <clumens at redhat.com>
 #
 
-import libuser
 # Used for ascii_letters and digits constants
 import string # pylint: disable=deprecated-module
 import crypt
@@ -27,6 +26,9 @@
 import tempfile
 import os
 import os.path
+import subprocess
+import shutil
+from contextlib import contextmanager
 from pyanaconda import iutil
 import pwquality
 from pyanaconda.iutil import strip_accents
@@ -37,51 +39,6 @@
 import logging
 log = logging.getLogger("anaconda")
 
-def createLuserConf(instPath, algoname='sha512'):
-    """ Writes a libuser.conf for instPath.
-
-        This must be called before User() is instantiated the first time
-        so that libuser.admin will use the temporary config file.
-    """
-
-    # If LIBUSER_CONF is not set, create a new temporary file
-    if "LIBUSER_CONF" not in os.environ:
-        (fp, fn) = tempfile.mkstemp(prefix="libuser.")
-        log.info("created new libuser.conf at %s with instPath=\"%s\"", fn, instPath)
-        fd = os.fdopen(fp, "w")
-        # This is only ok if createLuserConf is first called before threads are started
-        os.environ["LIBUSER_CONF"] = fn # pylint: disable=environment-modify
-    else:
-        fn = os.environ["LIBUSER_CONF"]
-        log.info("Clearing libuser.conf at %s", fn)
-        fd = open(fn, "w")
-
-    buf = """
-[defaults]
-skeleton = %(instPath)s/etc/skel
-mailspooldir = %(instPath)s/var/mail
-crypt_style = %(algo)s
-modules = files shadow
-create_modules = files shadow
-[files]
-directory = %(instPath)s/etc
-[shadow]
-directory = %(instPath)s/etc
-""" % {"instPath": instPath, "algo": algoname}
-
-    # Import login.defs if installed
-    if os.path.exists(os.path.normpath(instPath + "/etc/login.defs")):
-        buf += """
-[import]
-login_defs = %(instPath)s/etc/login.defs
-""" % {"instPath": instPath}
-
-
-    fd.write(buf)
-    fd.close()
-
-    return fn
-
 def getPassAlgo(authconfigStr):
     """ Reads the auth string and returns a string indicating our desired
         password encoding algorithm.
@@ -198,39 +155,49 @@ def guess_username(fullname):
     username = strip_accents(username)
     return username
 
-class Users:
-    def __init__(self):
-        self.admin = libuser.admin()
+class Users(object):
+    def _getpwnam(self, user_name, root):
+        """Like pwd.getpwnam, but is able to use a different root.
 
-    def _prepareChroot(self, root):
-        # Unfortunately libuser doesn't have an API to operate on a
-        # chroot, so we hack it here by forking a child and calling
-        # chroot() in that child's context.
+           Also just returns the pwd structure as a list, because of laziness.
+        """
+        with open(root + "/etc/passwd", "r") as f:
+            for line in f:
+                fields = line.split(":")
+                if fields[0] == user_name:
+                    return fields
 
-        childpid = os.fork()
-        if not childpid:
-            if not root in ["", "/"]:
-                os.chroot(root)
-                os.chdir("/")
-                # This is ok because it's after a fork
-                del(os.environ["LIBUSER_CONF"]) # pylint: disable=environment-modify
+        return None
 
-            self.admin = libuser.admin()
+    def _groupExists(self, group_name, root):
+        """Returns whether a group with the given name already exists."""
+        with open(root + "/etc/group", "r") as f:
+            for line in f:
+                if line.split(":")[0] == group_name:
+                    return True
 
-        return childpid
+        return False
 
-    def _finishChroot(self, childpid):
-        assert childpid > 0
-        try:
-            status = iutil.eintr_retry_call(os.waitpid, childpid, 0)[1]
-        except OSError as e:
-            log.critical("exception from waitpid: %s %s", e.errno, e.strerror)
-            return False
+    @contextmanager
+    def _ensureLoginDefs(self, root):
+        """Runs a command after creating /etc/login.defs, if necessary.
 
-        if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0):
-            return True
+           groupadd and useradd need login.defs to exist in the chroot, and if
+           someone is doing a cloud image install or some kind of --nocore thing
+           it may not. An empty one is ok, though. If it's missing, create it,
+           run the command, then clean it up.
+        """
+        login_defs_path = root + '/etc/login.defs'
+        if not os.path.exists(login_defs_path):
+            open(login_defs_path, "w").close()
+            login_defs_created = True
         else:
-            return False
+            login_defs_created = False
+
+        yield
+
+        if login_defs_created:
+            os.unlink(login_defs_path)
 
     def createGroup(self, group_name, **kwargs):
         """Create a new user on the system with the given name.  Optional kwargs:
@@ -241,27 +208,25 @@ def createGroup(self, group_name, **kwargs):
                         in.  homedir will be interpreted relative to this.
                         Defaults to /mnt/sysimage.
         """
+        root = kwargs.get("root", iutil.getSysroot())
 
-        childpid = self._prepareChroot(kwargs.get("root", iutil.getSysroot()))
-        if childpid == 0:
-            if self.admin.lookupGroupByName(group_name):
-                log.error("Group %s already exists, not creating.", group_name)
-                os._exit(1)
-
-            groupEnt = self.admin.initGroup(group_name)
+        if self._groupExists(group_name, root):
+            raise ValueError("Group %s already exists" % group_name)
 
-            if kwargs.get("gid", -1) >= 0:
-                groupEnt.set(libuser.GIDNUMBER, kwargs["gid"])
+        args = ["-R", root]
+        if "gid" in kwargs:
+            args.extend(["-g", str(kwargs["gid"])])
 
-            try:
-                self.admin.addGroup(groupEnt)
-            except RuntimeError as e:
-                log.critical("Error when creating new group: %s", e)
-                os._exit(1)
+        args.append(group_name)
+        with self._ensureLoginDefs(root):
+            status = iutil.execWithRedirect("groupadd", args)
 
-            os._exit(0)
-        else:
-            return self._finishChroot(childpid)
+        if status == 4:
+            raise ValueError("GID %s already exists" % kwargs.get("gid", -1))
+        elif status == 9:
+            raise ValueError("Group %s already exists" % group_name)
+        elif status != 0:
+            raise OSError("Unable to create group %s: status=%s" % (group_name, status))
 
     def createUser(self, user_name, *args, **kwargs):
         """Create a new user on the system with the given name.  Optional kwargs:
@@ -286,179 +251,166 @@ def createUser(self, user_name, *args, **kwargs):
                         in.  homedir will be interpreted relative to this.
                         Defaults to /mnt/sysimage.
            shell     -- The shell for the new user.  If none is given, the
-                        libuser default is used.
+                        login.defs default is used.
            uid       -- The UID for the new user.  If none is given, the next
                         available one is used.
            gid       -- The GID for the new user.  If none is given, the next
                         available one is used.
+           mkmailspool -- Whether to create a /var/spool/mail file for the user.
+                        Defaults to True.
         """
-        childpid = self._prepareChroot(kwargs.get("root", iutil.getSysroot()))
-        if childpid == 0:
-            if self.admin.lookupUserByName(user_name):
-                log.error("User %s already exists, not creating.", user_name)
-                os._exit(1)
-
-            userEnt = self.admin.initUser(user_name)
-            groupEnt = self.admin.initGroup(user_name)
-
-            gid = kwargs.get("gid") or -1
-            if gid >= 0:
-                groupEnt.set(libuser.GIDNUMBER, kwargs["gid"])
-
-            grpLst = [grp for grp in map(self.admin.lookupGroupByName, kwargs.get("groups", [])) if grp]
-            userEnt.set(libuser.GIDNUMBER, [groupEnt.get(libuser.GIDNUMBER)[0]] +
-                        [grp.get(libuser.GIDNUMBER)[0] for grp in grpLst])
-
-            homedir = kwargs.get("homedir", None)
-            if not homedir:
-                homedir = "/home/" + user_name
-            # libuser expects the parent directory tree to exist.
-            parent_dir = iutil.parent_dir(homedir)
-            if parent_dir:
-                iutil.mkdirChain(parent_dir)
-            userEnt.set(libuser.HOMEDIRECTORY, homedir)
-
-            if kwargs.get("shell", False):
-                userEnt.set(libuser.LOGINSHELL, kwargs["shell"])
-
-            uid = kwargs.get("uid") or -1
-            if uid >= 0:
-                userEnt.set(libuser.UIDNUMBER, kwargs["uid"])
-
-            if kwargs.get("gecos", False):
-                userEnt.set(libuser.GECOS, kwargs["gecos"])
-
-            # need to create home directory for the user or does it already exist?
-            # userEnt.get returns lists (usually with a single item)
-            mk_homedir = not os.path.exists(userEnt.get(libuser.HOMEDIRECTORY)[0])
 
-            try:
-                self.admin.addUser(userEnt, mkmailspool=kwargs.get("mkmailspool", True),
-                                   mkhomedir=mk_homedir)
-            except RuntimeError as e:
-                log.critical("Error when creating new user: %s", e)
-                os._exit(1)
+        root = kwargs.get("root", iutil.getSysroot())
 
-            try:
-                self.admin.addGroup(groupEnt)
-            except RuntimeError as e:
-                log.critical("Error when creating new group: %s", e)
-                os._exit(1)
-
-            if not mk_homedir:
-                try:
-                    stats = os.stat(userEnt.get(libuser.HOMEDIRECTORY)[0])
-                    orig_uid = stats.st_uid
-                    orig_gid = stats.st_gid
-
-                    log.info("Home directory for the user %s already existed, "
-                             "fixing the owner and SELinux context.", user_name)
-                    # home directory already existed, change owner of it properly
-                    iutil.chown_dir_tree(userEnt.get(libuser.HOMEDIRECTORY)[0],
-                                         userEnt.get(libuser.UIDNUMBER)[0],
-                                         groupEnt.get(libuser.GIDNUMBER)[0],
-                                         orig_uid, orig_gid)
-                    iutil.execWithRedirect("restorecon",
-                                           ["-r", userEnt.get(libuser.HOMEDIRECTORY)[0]])
-                except OSError as e:
-                    log.critical("Unable to change owner of existing home directory: %s",
-                            os.strerror)
-                    os._exit(1)
-
-            pw = kwargs.get("password", False)
-            try:
-                if pw:
-                    if kwargs.get("isCrypted", False):
-                        password = kwargs["password"]
-                    else:
-                        password = cryptPassword(kwargs["password"], algo=kwargs.get("algo", None))
-                    self.admin.setpassUser(userEnt, password, True)
-                    userEnt.set(libuser.SHADOWLASTCHANGE, "")
-                    self.admin.modifyUser(userEnt)
-                elif pw == "":
-                    # Setup the account with *NO* password
-                    self.admin.unlockUser(userEnt)
-                    log.info("user account %s setup with no password", user_name)
-
-                if kwargs.get("lock", False):
-                    self.admin.lockUser(userEnt)
-                    log.info("user account %s locked", user_name)
-            # setpassUser raises SystemError on failure, while unlockUser and lockUser
-            # raise RuntimeError
-            except (RuntimeError, SystemError) as e:
-                log.critical("Unable to set password for new user: %s", e)
-                os._exit(1)
-
-            # Add the user to all the groups they should be part of.
-            grpLst.append(self.admin.lookupGroupByName(user_name))
-            try:
-                for grp in grpLst:
-                    grp.add(libuser.MEMBERNAME, user_name)
-                    self.admin.modifyGroup(grp)
-            except RuntimeError as e:
-                log.critical("Unable to add user to groups: %s", e)
-                os._exit(1)
-
-            os._exit(0)
-        else:
-            return self._finishChroot(childpid)
+        if self.checkUserExists(user_name, root):
+            raise ValueError("User %s already exists" % user_name)
 
-    def checkUserExists(self, username, root=None):
-        childpid = self._prepareChroot(root)
+        args = ["-R", root]
 
-        if childpid == 0:
-            if self.admin.lookupUserByName(username):
-                os._exit(0)
-            else:
-                os._exit(1)
+        # If a specific gid is requested, create the user group with that GID.
+        # Otherwise let useradd do it automatically.
+        if "gid" in kwargs:
+            self.createGroup(user_name, gid=kwargs['gid'], root=root)
+            args.extend(['-g', str(kwargs['gid'])])
+        else:
+            args.append('-U')
+
+        if "groups" in kwargs:
+            args.extend(['-G', ",".join(kwargs["groups"])])
+
+        homedir = kwargs.get("homedir", "/home/" + user_name)
+        # useradd expects the parent directory tree to exist.
+        parent_dir = iutil.parent_dir(root + homedir)
+        if parent_dir:
+            iutil.mkdirChain(parent_dir)
+        args.extend(["-d", homedir])
+
+        # Check whether the directory exists or if useradd should create it
+        mk_homedir = not os.path.exists(root + homedir)
+        if mk_homedir:
+            args.append("-m")
         else:
-            return self._finishChroot(childpid)
+            args.append("-M")
 
-    def setUserPassword(self, username, password, isCrypted, lock, algo=None):
-        user = self.admin.lookupUserByName(username)
+        if "shell" in kwargs:
+            args.extend(["-s", kwargs["shell"]])
 
-        if isCrypted:
-            self.admin.setpassUser(user, password, True)
+        if "uid" in kwargs:
+            args.extend(["-u", str(kwargs["uid"])])
+
+        if "gecos" in kwargs:
+            args.extend(["-c", kwargs["gecos"]])
+
+        # The mail spool is annoying. We can control where it is created, but
+        # not whether it is created. So, have useradd create it, maybe, in a
+        # temp directory, remove that directory, and then if we actually wanted
+        # a spool file just make an empty file in /var/spool/mail.
+        spool_dir = tempfile.mkdtemp()
+        args.extend(["-K", "MAIL_DIR=%s" % spool_dir])
+
+        args.append(user_name)
+        try:
+            with self._ensureLoginDefs(root):
+                status = iutil.execWithRedirect("useradd", args)
+
+            if status == 4:
+                raise ValueError("UID %s already exists" % kwargs.get("uid", -1))
+            elif status == 6:
+                raise ValueError("Invalid groups %s" % kwargs.get("groups", []))
+            elif status == 9:
+                raise ValueError("User %s already exists" % user_name)
+            elif status != 0:
+                raise OSError("Unable to create user %s: status=%s" % (user_name, status))
+        finally:
+            shutil.rmtree(spool_dir)
+
+        if kwargs.get("mkmailspool", True):
+            iutil.mkdirChain(root + "/var/spool/mail")
+            open(root + "/var/spool/mail/" + user_name, "w").close()
+
+        if not mk_homedir:
+            try:
+                stats = os.stat(root + homedir)
+                orig_uid = stats.st_uid
+                orig_gid = stats.st_gid
+
+                # Gett the UID and GID of the created user
+                pwent = self._getpwnam(user_name, root)
+
+                log.info("Home directory for the user %s already existed, "
+                         "fixing the owner and SELinux context.", user_name)
+                # home directory already existed, change owner of it properly
+                iutil.chown_dir_tree(root + homedir,
+                                     pwent[2], pwent[3],
+                                     orig_uid, orig_gid)
+                iutil.execWithRedirect("restorecon", ["-r", root + homedir])
+            except OSError as e:
+                log.critical("Unable to change owner of existing home directory: %s", e.strerror)
+                raise
+
+        pw = kwargs.get("password", "")
+        crypted = kwargs.get("isCrypted", False)
+        algo = kwargs.get("algo", None)
+        lock = kwargs.get("lock", False)
+
+        self.setUserPassword(user_name, pw, crypted, lock, algo, root)
+
+    def checkUserExists(self, username, root=None):
+        if self._getpwnam(username, root):
+            return True
+
+        return False
+
+    def setUserPassword(self, username, password, isCrypted, lock, algo=None, root="/"):
+        if password:
+            if not isCrypted:
+                password = cryptPassword(password, algo)
         else:
-            self.admin.setpassUser(user, cryptPassword(password, algo=algo), True)
+            log.info("user account %s setup with no password", username)
 
         if lock:
-            self.admin.lockUser(user)
+            password = "!" + password
+            log.info("user account %s locked", username)
 
-        user.set(libuser.SHADOWLASTCHANGE, "")
-        return self.admin.modifyUser(user)
+        proc = iutil.startProgram(["chpasswd", "-R", root, "-e"], stdin=subprocess.PIPE)
+        proc.communicate(("%s:%s\n" % (username, password)).encode("utf-8"))
+        if proc.returncode != 0:
+            raise OSError("Unable to set password for new user: status=%s" % proc.returncode)
 
-    def setRootPassword(self, password, isCrypted=False, isLocked=False, algo=None):
-        return self.setUserPassword("root", password, isCrypted, isLocked, algo)
+        # Reset sp_lstchg to an empty string. On systems with no rtc, this
+        # field can be set to 0, which has a special meaning that the password
+        # must be reset on the next login.
+        iutil.execWithRedirect("chage", ["-R", root, "-d", "", username])
+
+    def setRootPassword(self, password, isCrypted=False, isLocked=False, algo=None, root="/"):
+        return self.setUserPassword("root", password, isCrypted, isLocked, algo, root)
 
     def setUserSshKey(self, username, key, **kwargs):
-        childpid = self._prepareChroot(kwargs.get("root", iutil.getSysroot()))
-
-        if childpid == 0:
-            user = self.admin.lookupUserByName(username)
-            if not user:
-                log.error("setUserSshKey: user %s does not exist", username)
-                os._exit(1)
-
-            homedir = user.get(libuser.HOMEDIRECTORY)[0]
-            if not os.path.exists(homedir):
-                log.error("setUserSshKey: home directory for %s does not exist", username)
-                os._exit(1)
-
-            sshdir = os.path.join(homedir, ".ssh")
-            if not os.path.isdir(sshdir):
-                os.mkdir(sshdir, 0o700)
-                iutil.eintr_retry_call(os.chown, sshdir, user.get(libuser.UIDNUMBER)[0], user.get(libuser.GIDNUMBER)[0])
-
-            authfile = os.path.join(sshdir, "authorized_keys")
-            authfile_existed = os.path.exists(authfile)
-            with iutil.open_with_perm(authfile, "a", 0o600) as f:
-                f.write(key + "\n")
-
-            # Only change ownership if we created it
-            if not authfile_existed:
-                iutil.eintr_retry_call(os.chown, authfile, user.get(libuser.UIDNUMBER)[0], user.get(libuser.GIDNUMBER)[0])
-                iutil.execWithRedirect("restorecon", ["-r", sshdir])
-            os._exit(0)
-        else:
-            return self._finishChroot(childpid)
+        root = kwargs.get("root", iutil.getSysroot())
+
+        pwent = self._getpwnam(username, root)
+        if not pwent:
+            raise ValueError("setUserSshKey: user %s does not exist" % username)
+
+        homedir = root + pwent[5]
+        if not os.path.exists(homedir):
+            log.error("setUserSshKey: home directory for %s does not exist", username)
+            os._exit(1)
+
+        uid = pwent[2]
+        gid = pwent[3]
+
+        sshdir = os.path.join(homedir, ".ssh")
+        if not os.path.isdir(sshdir):
+            os.mkdir(sshdir, 0o700)
+            iutil.eintr_retry_call(os.chown, sshdir, int(uid), int(gid))
+
+        authfile = os.path.join(sshdir, "authorized_keys")
+        authfile_existed = os.path.exists(authfile)
+        with iutil.open_with_perm(authfile, "a", 0o600) as f:
+            f.write(key + "\n")
+
+        # Only change ownership if we created it
+        if not authfile_existed:
+            iutil.eintr_retry_call(os.chown, authfile, int(uid), int(gid))
+            iutil.execWithRedirect("restorecon", ["-r", sshdir])
diff --git a/utils/handle-sshpw b/utils/handle-sshpw
index f6d0688..7a6fd71 100755
--- a/utils/handle-sshpw
+++ b/utils/handle-sshpw
@@ -41,10 +41,6 @@ ksparser.registerSection(NullSection(handler, sectionOpen="%addon"))
 ksparser.registerSection(NullSection(handler, sectionOpen="%anaconda"))
 ksparser.readKickstart(ksfile)
 
-# we need to have a libuser.conf that points to the installer root for
-# sshpw, but after that we start sshd, we need one that points to the
-# install target.
-luserConf = users.createLuserConf(instPath="")
 u = users.Users()
 
 userdata = ksparser.handler.sshpw.dataList()


-- 
To view this commit on github, visit https://github.com/rhinstaller/anaconda/commit/1199fe25e02937fe8cef346f6950587a65d1619c


More information about the anaconda-patches mailing list