user add/delete: add unit tests

This commit adds the test infrastructure such that we are able to
manipulate groups and users inside the unit test container.

To make it happen, the following changes are made:
1. executeCmd was moved to header
2. made the two userDelete and userAdd function virtual; this is needed
since on BMC the daemon is run as root by default; however on unit test
container, it's run as the current user on VMs. Thus in the unit test,
these two functions are overridden to dummy commands.

It can be shown that the unit test coverage increases a lot.

Tested: unit test pasesd

Signed-off-by: Nan Zhou <nanzhoumails@gmail.com>
Change-Id: I7be636d8acd487242831d1c74941e848e508b8f9
diff --git a/user_mgr.cpp b/user_mgr.cpp
index f2c500c..37116f6 100644
--- a/user_mgr.cpp
+++ b/user_mgr.cpp
@@ -31,8 +31,6 @@
 #include <unistd.h>
 
 #include <boost/algorithm/string/split.hpp>
-#include <boost/process/child.hpp>
-#include <boost/process/io.hpp>
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/elog.hpp>
 #include <phosphor-logging/log.hpp>
@@ -55,10 +53,8 @@
 {
 
 static constexpr const char* passwdFileName = "/etc/passwd";
-static constexpr size_t ipmiMaxUsers = 15;
 static constexpr size_t ipmiMaxUserNameLen = 16;
 static constexpr size_t systemMaxUserNameLen = 30;
-static constexpr size_t maxSystemUsers = 30;
 static constexpr const char* grpSsh = "ssh";
 static constexpr uint8_t minPasswdLength = 8;
 static constexpr int success = 0;
@@ -107,35 +103,6 @@
 
 using Argument = xyz::openbmc_project::Common::InvalidArgument;
 
-template <typename... ArgTypes>
-static std::vector<std::string> executeCmd(const char* path,
-                                           ArgTypes&&... tArgs)
-{
-    std::vector<std::string> stdOutput;
-    boost::process::ipstream stdOutStream;
-    boost::process::child execProg(path, const_cast<char*>(tArgs)...,
-                                   boost::process::std_out > stdOutStream);
-    std::string stdOutLine;
-
-    while (stdOutStream && std::getline(stdOutStream, stdOutLine) &&
-           !stdOutLine.empty())
-    {
-        stdOutput.emplace_back(stdOutLine);
-    }
-
-    execProg.wait();
-
-    int retCode = execProg.exit_code();
-    if (retCode)
-    {
-        log<level::ERR>("Command execution failed", entry("PATH=%s", path),
-                        entry("RETURN_CODE=%d", retCode));
-        elog<InternalFailure>();
-    }
-
-    return stdOutput;
-}
-
 std::string getCSVFromVector(std::span<const std::string> vec)
 {
     if (vec.empty())
@@ -316,12 +283,7 @@
     }
     try
     {
-        // set EXPIRE_DATE to 0 to disable user, PAM takes 0 as expire on
-        // 1970-01-01, that's an implementation-defined behavior
-        executeCmd("/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(),
-                   "-m", "-N", "-s",
-                   (sshRequested ? "/bin/sh" : "/bin/nologin"), "-e",
-                   (enabled ? "" : "1970-01-01"));
+        executeUserAdd(userName.c_str(), groups.c_str(), sshRequested, enabled);
     }
     catch (const InternalFailure& e)
     {
@@ -350,7 +312,7 @@
     throwForUserDoesNotExist(userName);
     try
     {
-        executeCmd("/usr/sbin/userdel", userName.c_str(), "-r");
+        executeUserDelete(userName.c_str());
     }
     catch (const InternalFailure& e)
     {
@@ -890,6 +852,12 @@
     return userList.size();
 }
 
+size_t UserMgr::getNonIpmiUsersCount()
+{
+    std::vector<std::string> ipmiUsers = getUsersInGroup("ipmi");
+    return usersList.size() - ipmiUsers.size();
+}
+
 bool UserMgr::isUserEnabled(const std::string& userName)
 {
     // All user management lock has to be based on /etc/shadow
@@ -1344,5 +1312,20 @@
     this->emit_object_added();
 }
 
+void UserMgr::executeUserAdd(const char* userName, const char* groups,
+                             bool sshRequested, bool enabled)
+{
+    // set EXPIRE_DATE to 0 to disable user, PAM takes 0 as expire on
+    // 1970-01-01, that's an implementation-defined behavior
+    executeCmd("/usr/sbin/useradd", userName, "-G", groups, "-m", "-N", "-s",
+               (sshRequested ? "/bin/sh" : "/bin/nologin"), "-e",
+               (enabled ? "" : "1970-01-01"));
+}
+
+void UserMgr::executeUserDelete(const char* userName)
+{
+    executeCmd("/usr/sbin/userdel", userName, "-r");
+}
+
 } // namespace user
 } // namespace phosphor