[haiku-commits] BRANCH HaikuPM-github.package-management [519bb60] in src: bin/multiuser servers/registrar

  • From: HaikuPM-github.package-management <community@xxxxxxxxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Wed, 18 Sep 2013 16:45:35 +0200 (CEST)

added 12 changesets to branch 'refs/remotes/HaikuPM-github/package-management'
old head: 7d09a8dc6788de70bc30da84e6feb9b006c46bfb
new head: 519bb60aef35f6e7371dc0ccf0a9c989036ae9b4
overview: https://github.com/haiku/HaikuPM/compare/7d09a8d...519bb60

----------------------------------------------------------------------------

9a85313: X86PagingStructuresPAE: Zero fPageDirPointerTable in constructor
  
  ... and use it as a guard in the destructor. Fixes crash when running
  out of memory and Init() is not called.

c13744f: useradd: Improve usage text

222fb7a: getgrgid_r()/getgrname_r(): Fix group not found return value

fb8a9c4: getpw{nam,uid}[_r]: Fix return value behavior
  
  ... when the user is not found.

de15b85: getgr{nam,gid}[_r](): Fix retrieving group members

0e9e586: useradd: Support specifying group by name

e9d9ac7: Add userdel

82a064b: useradd: small style fix

032ea9a: useradd: Create the new user with a locked password

6c346b8: passwd: Add option -d to delete a user's password

dc3be29: Enable -Werror in src/bin/multiuser

519bb60: Add group{add,del,mod}

                                    [ Ingo Weinhold <ingo_weinhold@xxxxxx> ]

----------------------------------------------------------------------------

16 files changed, 959 insertions(+), 115 deletions(-)
build/jam/ArchitectureRules                      |   1 +
build/jam/images/HaikuImage                      |   4 +-
build/jam/images/HaikuImageBootstrap             |   4 +-
headers/private/app/RegistrarDefs.h              |   2 +
src/bin/multiuser/Jamfile                        |  10 +-
src/bin/multiuser/groupadd.cpp                   | 109 ++++++
src/bin/multiuser/groupdel.cpp                   |  98 ++++++
src/bin/multiuser/groupmod.cpp                   | 159 +++++++++
src/bin/multiuser/passwd.cpp                     | 116 +++++--
src/bin/multiuser/useradd.cpp                    | 118 ++++---
src/bin/multiuser/userdel.cpp                    |  98 ++++++
src/servers/registrar/AuthenticationManager.cpp  | 332 +++++++++++++++++--
src/servers/registrar/AuthenticationManager.h    |   4 +
.../x86/paging/pae/X86PagingStructuresPAE.cpp    |   8 +-
src/system/libroot/posix/grp.cpp                 |   9 +-
src/system/libroot/posix/pwd.cpp                 |   2 +-

############################################################################

Commit:      9a85313bc68fde2a79e17e51726bab55eb3d85ac
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Wed Sep 18 14:12:51 2013 UTC

X86PagingStructuresPAE: Zero fPageDirPointerTable in constructor

... and use it as a guard in the destructor. Fixes crash when running
out of memory and Init() is not called.

----------------------------------------------------------------------------

diff --git a/src/system/kernel/arch/x86/paging/pae/X86PagingStructuresPAE.cpp 
b/src/system/kernel/arch/x86/paging/pae/X86PagingStructuresPAE.cpp
index efffa60..99e3f1d 100644
--- a/src/system/kernel/arch/x86/paging/pae/X86PagingStructuresPAE.cpp
+++ b/src/system/kernel/arch/x86/paging/pae/X86PagingStructuresPAE.cpp
@@ -20,6 +20,8 @@
 
 
 X86PagingStructuresPAE::X86PagingStructuresPAE()
+       :
+       fPageDirPointerTable(NULL)
 {
        memset(fVirtualPageDirs, 0, sizeof(fVirtualPageDirs));
 }
@@ -32,8 +34,10 @@ X86PagingStructuresPAE::~X86PagingStructuresPAE()
                // There's one contiguous allocation for 0 and 1.
 
        // free the PDPT page
-       X86PagingMethodPAE::Method()->Free32BitPage(fPageDirPointerTable,
-               pgdir_phys, fPageDirPointerTableHandle);
+       if (fPageDirPointerTable != NULL) {
+               
X86PagingMethodPAE::Method()->Free32BitPage(fPageDirPointerTable,
+                       pgdir_phys, fPageDirPointerTableHandle);
+       }
 }
 
 

############################################################################

Commit:      c13744f4c0fc1f8b597c7a349af9218473525be2
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Mon Sep 16 09:23:22 2013 UTC

useradd: Improve usage text

----------------------------------------------------------------------------

diff --git a/src/bin/multiuser/useradd.cpp b/src/bin/multiuser/useradd.cpp
index 4522084..febe75a 100644
--- a/src/bin/multiuser/useradd.cpp
+++ b/src/bin/multiuser/useradd.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008, Ingo Weinhold, ingo_weinhold@xxxxxx. All Rights Reserved.
+ * Copyright 2008-2013, Ingo Weinhold, ingo_weinhold@xxxxxx.
  * Distributed under the terms of the MIT License.
  */
 
@@ -30,9 +30,27 @@ extern const char *__progname;
 
 
 static const char* kUsage =
-"Usage: %s [ -d <home> ] [ -e <expiration> ] [ -f <inactive> ] [ -g <gid> ]\n"
-"          [ -s <shell> ] [ -n <real name> ]\n"
-;
+       "Usage: %s [ <options> ] <user name>\n"
+       "Creates a new user <user name>.\n"
+       "\n"
+       "Options:\n"
+       "  -d <home>\n"
+       "    Specifies the home directory for the new user.\n"
+       "  -e <expiration>\n"
+       "    Specifies the expiration date for the new user's account.\n"
+       "  -f <inactive>\n"
+       "    Specifies the number of days after the expiration of the new 
user's "
+                       "password\n"
+       "    until the account expires.\n"
+       "  -g <gid>\n"
+       "    Specifies the new user's primary group by ID or name.\n"
+       "  -h, --help\n"
+       "    Print usage info.\n"
+       "  -s <shell>\n"
+       "    Specifies the new user's login shell.\n"
+       "  -n <real name>\n"
+       "    Specifies the new user's real name.\n"
+       ;
 
 static void
 print_usage_and_exit(bool error)
@@ -84,6 +102,7 @@ main(int argc, const char* const* argv)
 
                        case 'g':
                                gid = atoi(optarg);
+// TODO: Support name!
                                break;
 
                        case 'h':

############################################################################

Commit:      222fb7a91ab71cda2bc8d7dd58227c7fd213b677
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Mon Sep 16 10:14:34 2013 UTC

getgrgid_r()/getgrname_r(): Fix group not found return value

----------------------------------------------------------------------------

diff --git a/src/system/libroot/posix/grp.cpp b/src/system/libroot/posix/grp.cpp
index f8faac4..2a4882e 100644
--- a/src/system/libroot/posix/grp.cpp
+++ b/src/system/libroot/posix/grp.cpp
@@ -49,8 +49,9 @@ query_group_entry(const char* name, gid_t _gid, struct group 
*group,
        KMessage reply;
        status_t error = 
BPrivate::send_authentication_request_to_registrar(message,
                reply);
-       if (error != B_OK)
-               return error;
+       if (error != B_OK) {
+               return error == ENOENT ? B_OK : error;
+       }
 
        int32 gid;
        const char* password;

############################################################################

Commit:      fb8a9c47106f9b5beaaa76416b314accc5d4562e
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Wed Sep 18 14:15:41 2013 UTC

getpw{nam,uid}[_r]: Fix return value behavior

... when the user is not found.

----------------------------------------------------------------------------

diff --git a/src/system/libroot/posix/pwd.cpp b/src/system/libroot/posix/pwd.cpp
index 57839ba..7d84451 100644
--- a/src/system/libroot/posix/pwd.cpp
+++ b/src/system/libroot/posix/pwd.cpp
@@ -50,7 +50,7 @@ query_passwd_entry(const char* name, uid_t _uid, struct 
passwd *passwd,
        status_t error = 
BPrivate::send_authentication_request_to_registrar(message,
                reply);
        if (error != B_OK)
-               return error;
+               return error == ENOENT ? B_OK : error;
 
        int32 uid;
        int32 gid;

############################################################################

Commit:      de15b85e5c58a30265a5017bb16a2d3028f1eb70
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Wed Sep 18 14:17:03 2013 UTC

getgr{nam,gid}[_r](): Fix retrieving group members

----------------------------------------------------------------------------

diff --git a/src/system/libroot/posix/grp.cpp b/src/system/libroot/posix/grp.cpp
index 2a4882e..f04f589 100644
--- a/src/system/libroot/posix/grp.cpp
+++ b/src/system/libroot/posix/grp.cpp
@@ -64,8 +64,8 @@ query_group_entry(const char* name, gid_t _gid, struct group 
*group,
 
        const char* members[MAX_GROUP_MEMBER_COUNT];
        int memberCount = 0;
-       for (int memberCount = 0; memberCount < MAX_GROUP_MEMBER_COUNT;) {
-               if (reply.FindString("members", members + memberCount) != B_OK)
+       for (int32 index = 0; memberCount < MAX_GROUP_MEMBER_COUNT; index++) {
+               if (reply.FindString("members", index, members + memberCount) 
!= B_OK)
                        break;
                memberCount++;
        }

############################################################################

Commit:      0e9e586cacc38965a67a67d9f40a4f6bf4d7bda6
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Mon Sep 16 10:15:52 2013 UTC

useradd: Support specifying group by name

----------------------------------------------------------------------------

diff --git a/src/bin/multiuser/useradd.cpp b/src/bin/multiuser/useradd.cpp
index febe75a..8bee29e 100644
--- a/src/bin/multiuser/useradd.cpp
+++ b/src/bin/multiuser/useradd.cpp
@@ -66,7 +66,7 @@ main(int argc, const char* const* argv)
        const char* home = "/boot/home";
        int expiration = 99999;
        int inactive = -1;
-       gid_t gid = 100;
+       const char* group = NULL;
        const char* shell = "/bin/sh";
        const char* realName = "";
 
@@ -101,9 +101,10 @@ main(int argc, const char* const* argv)
                                break;
 
                        case 'g':
-                               gid = atoi(optarg);
-// TODO: Support name!
+                       {
+                               group = optarg;
                                break;
+                       }
 
                        case 'h':
                                print_usage_and_exit(false);
@@ -139,6 +140,57 @@ main(int argc, const char* const* argv)
                exit(1);
        }
 
+       // get group ID
+       gid_t gid = 100;
+       if (group != NULL) {
+               char* end;
+               gid = strtol(group, &end, 0);
+               if (*end == '\0') {
+                       // seems to be a number
+                       if (gid < 1) {
+                               fprintf(stderr, "Error: Invalid group ID 
\"%s\".\n",
+                                       group);
+                               exit(1);
+                       }
+               } else {
+                       // must be a group name -- get it
+                       char* buffer = NULL;
+                       ssize_t bufferSize = sysconf(_SC_GETGR_R_SIZE_MAX);
+                       if (bufferSize <= 0)
+                               bufferSize = 256;
+                       for (;;) {
+                               buffer = (char*)realloc(buffer, bufferSize);
+                               if (buffer == NULL) {
+                                       fprintf(stderr, "Error: Out of 
memory!\n");
+                                       exit(1);
+                               }
+
+                               struct group groupBuffer;
+                               struct group* groupFound;
+                               int error = getgrnam_r(group, &groupBuffer, 
buffer, bufferSize,
+                                       &groupFound);
+                               if (error == ERANGE) {
+                                       bufferSize *= 2;
+                                       continue;
+                               }
+
+                               if (error != 0) {
+                                       fprintf(stderr, "Error: Failed to get 
info for group \"%s\".\n",
+                                               group);
+                                       exit(1);
+                               }
+                               if (groupFound == NULL) {
+                                       fprintf(stderr, "Error: Specified group 
\"%s\" doesn't "
+                                               "exist.\n", group);
+                                       exit(1);
+                               }
+
+                               gid = groupFound->gr_gid;
+                               break;
+                       }
+               }
+       }
+
        // read password
        char password[LINE_MAX];
        if (read_password("password for user: ", password, sizeof(password),

############################################################################

Commit:      e9d9ac713ffa301240e7a2e113bb0d5bb70082f8
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Mon Sep 16 13:49:36 2013 UTC

Add userdel

----------------------------------------------------------------------------

diff --git a/build/jam/images/HaikuImage b/build/jam/images/HaikuImage
index fc8429a..a58a7f8 100644
--- a/build/jam/images/HaikuImage
+++ b/build/jam/images/HaikuImage
@@ -37,7 +37,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures
        tac tail tcpdump tcptester tee telnet telnetd test timeout top touch
        tr traceroute translate trash true truncate tsort tty
        uname unchop unexpand unmount uniq unlink unshar unzip unzipsfx
-       <bin>updatedb uptime urlwrapper useradd uudecode uuencode
+       <bin>updatedb uptime urlwrapper useradd userdel uudecode uuencode
        vdir version vmstat
        waitfor watch wc wget whoami writembr@x86 xargs xres
        yes
diff --git a/build/jam/images/HaikuImageBootstrap 
b/build/jam/images/HaikuImageBootstrap
index 1cf9810..71e5cca 100644
--- a/build/jam/images/HaikuImageBootstrap
+++ b/build/jam/images/HaikuImageBootstrap
@@ -37,7 +37,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures
        tac tail tcpdump tcptester tee telnet telnetd test timeout top touch
        tr traceroute trash true truncate tsort tty
        uname unchop unexpand unmount uniq unlink unshar unzip unzipsfx
-       <bin>updatedb uptime urlwrapper useradd uudecode uuencode
+       <bin>updatedb uptime urlwrapper useradd userdel uudecode uuencode
        vdir version vmstat
        waitfor watch wc wget whoami writembr@x86 xargs xres
        yes
diff --git a/headers/private/app/RegistrarDefs.h 
b/headers/private/app/RegistrarDefs.h
index c5144e2..ea3d20a 100644
--- a/headers/private/app/RegistrarDefs.h
+++ b/headers/private/app/RegistrarDefs.h
@@ -121,7 +121,9 @@ enum {
        B_REG_GET_GROUP                                                 = 
'rggr',
        B_REG_GET_USER_GROUPS                                   = 'rgug',
        B_REG_UPDATE_USER                                               = 
'ruus',
+       B_REG_DELETE_USER                                               = 
'rdus',
        B_REG_UPDATE_GROUP                                              = 
'rugr',
+       B_REG_DELETE_GROUP                                              = 
'rdgr',
 };
 
 // B_REG_MIME_SET_PARAM "which" constants
diff --git a/src/bin/multiuser/Jamfile b/src/bin/multiuser/Jamfile
index 6a4bc30..24f3232 100644
--- a/src/bin/multiuser/Jamfile
+++ b/src/bin/multiuser/Jamfile
@@ -15,5 +15,7 @@ BinCommand passwd : passwd.cpp : libmultiuser_utils.a ;
 
 BinCommand useradd : useradd.cpp : libmultiuser_utils.a ;
 
+BinCommand userdel : userdel.cpp ;
+
 # set set-uid bit on passwd
 MODE on passwd = 04755 ;
diff --git a/src/bin/multiuser/userdel.cpp b/src/bin/multiuser/userdel.cpp
new file mode 100644
index 0000000..c31cff0
--- /dev/null
+++ b/src/bin/multiuser/userdel.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx.
+ * Distributed under the terms of the MIT License.
+ */
+
+#include <getopt.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <OS.h>
+
+#include <RegistrarDefs.h>
+#include <user_group.h>
+#include <util/KMessage.h>
+
+#include "multiuser_utils.h"
+
+
+extern const char *__progname;
+
+
+static const char* kUsage =
+       "Usage: %s [ <options> ] <user name>\n"
+       "Deletes the specified user.\n"
+       "\n"
+       "Options:\n"
+       "  -h, --help\n"
+       "    Print usage info.\n"
+       ;
+
+static void
+print_usage_and_exit(bool error)
+{
+       fprintf(error ? stderr : stdout, kUsage, __progname);
+       exit(error ? 1 : 0);
+}
+
+
+int
+main(int argc, const char* const* argv)
+{
+       while (true) {
+               static struct option sLongOptions[] = {
+                       { "help", no_argument, 0, 'h' },
+                       { 0, 0, 0, 0 }
+               };
+
+               opterr = 0; // don't print errors
+               int c = getopt_long(argc, (char**)argv, "h", sLongOptions, 
NULL);
+               if (c == -1)
+                       break;
+
+       
+               switch (c) {
+                       case 'h':
+                               print_usage_and_exit(false);
+                               break;
+
+                       default:
+                               print_usage_and_exit(true);
+                               break;
+               }
+       }
+
+       if (optind != argc - 1)
+               print_usage_and_exit(true);
+
+       const char* user = argv[optind];
+
+       if (geteuid() != 0) {
+               fprintf(stderr, "Error: Only root may delete users.\n");
+               exit(1);
+       }
+
+       if (getpwnam(user) == NULL) {
+               fprintf(stderr, "Error: User \"%s\" doesn't exists.\n", user);
+               exit(1);
+       }
+
+       // prepare request for the registrar
+       KMessage message(BPrivate::B_REG_DELETE_USER);
+       if (message.AddString("name", user) != B_OK) {
+               fprintf(stderr, "Error: Out of memory!\n");
+               exit(1);
+       }
+
+       // send the request
+       KMessage reply;
+       status_t error = send_authentication_request_to_registrar(message, 
reply);
+       if (error != B_OK) {
+               fprintf(stderr, "Error: Failed to delete user: %s\n", 
strerror(error));
+               exit(1);
+       }
+
+       return 0;
+}
diff --git a/src/servers/registrar/AuthenticationManager.cpp 
b/src/servers/registrar/AuthenticationManager.cpp
index d841ad7..a3fe86a 100644
--- a/src/servers/registrar/AuthenticationManager.cpp
+++ b/src/servers/registrar/AuthenticationManager.cpp
@@ -435,6 +435,12 @@ public:
                return B_OK;
        }
 
+       void RemoveUser(User* user)
+       {
+               fUsersByID.erase(fUsersByID.find(user->UID()));
+               fUsersByName.erase(fUsersByName.find(user->Name()));
+       }
+
        User* UserByID(uid_t uid) const
        {
                map<uid_t, User*>::const_iterator it = fUsersByID.find(uid);
@@ -721,7 +727,7 @@ AuthenticationManager::_RequestThread()
                                if (error == B_OK) {
                                        message.SendReply(fPasswdDBReply, -1, 
-1, 0, registrarTeam);
                                } else {
-                                       fPasswdDBReply->SetTo(1);
+                                       _InvalidatePasswdDBReply();
                                        KMessage reply(error);
                                        message.SendReply(&reply, -1, -1, 0, 
registrarTeam);
                                }
@@ -752,7 +758,7 @@ AuthenticationManager::_RequestThread()
                                if (error == B_OK) {
                                        message.SendReply(fGroupDBReply, -1, 
-1, 0, registrarTeam);
                                } else {
-                                       fGroupDBReply->SetTo(1);
+                                       _InvalidateGroupDBReply();
                                        KMessage reply(error);
                                        message.SendReply(&reply, -1, -1, 0, 
registrarTeam);
                                }
@@ -789,7 +795,7 @@ AuthenticationManager::_RequestThread()
                                        message.SendReply(fShadowPwdDBReply, 
-1, -1, 0,
                                                registrarTeam);
                                } else {
-                                       fShadowPwdDBReply->SetTo(1);
+                                       _InvalidateShadowPwdDBReply();
                                        KMessage reply(error);
                                        message.SendReply(&reply, -1, -1, 0, 
registrarTeam);
                                }
@@ -915,7 +921,7 @@ AuthenticationManager::_RequestThread()
                                        error = B_BAD_VALUE;
                                }
 
-                               // only can change anything
+                               // only root can change anything
                                if (error == B_OK && !isRoot)
                                        error = EPERM;
 
@@ -951,8 +957,8 @@ AuthenticationManager::_RequestThread()
                                                if (error == B_OK) {
                                                        fUserDB->AddUser(user);
                                                        fUserDB->WriteToDisk();
-                                                       
fPasswdDBReply->SetTo(1);
-                                                       
fShadowPwdDBReply->SetTo(1);
+                                                       
_InvalidatePasswdDBReply();
+                                                       
_InvalidateShadowPwdDBReply();
                                                }
                                        } catch (...) {
                                                error = B_NO_MEMORY;
@@ -971,13 +977,59 @@ AuthenticationManager::_RequestThread()
 
                                break;
                        }
+
+                       case B_REG_DELETE_USER:
+                       {
+                               // find user
+                               User* user = NULL;
+                               int32 uid;
+                               const char* name;
+
+                               if (message.FindInt32("uid", &uid) == B_OK) {
+                                       user = fUserDB->UserByID(uid);
+                               } else if (message.FindString("name", &name) == 
B_OK) {
+                                       user = fUserDB->UserByName(name);
+                               } else {
+                                       error = B_BAD_VALUE;
+                               }
+
+                               if (error == B_OK && user == NULL)
+                                       error = ENOENT;
+
+                               // only root can change anything
+                               if (error == B_OK && !isRoot)
+                                       error = EPERM;
+
+                               // apply the change
+                               if (error == B_OK) {
+                                       fUserDB->RemoveUser(user);
+                                       fUserDB->WriteToDisk();
+                                       _InvalidatePasswdDBReply();
+                                       _InvalidateShadowPwdDBReply();
+                               }
+
+                               // send reply
+                               KMessage reply;
+                               reply.SetWhat(error);
+                               message.SendReply(&reply, -1, -1, 0, 
registrarTeam);
+
+                               break;
+                       }
+
                        case B_REG_UPDATE_GROUP:
                                debug_printf("B_REG_UPDATE_GROUP done: 
currently unsupported!\n");
                                break;
+
+                       case B_REG_DELETE_GROUP:
+                       {
+                               debug_printf(
+                                       "B_REG_DELETE_GROUP done: currently 
unsupported!\n");
+                               break;
+                       }
+
                        default:
                                debug_printf("REG: invalid message: %" B_PRIu32 
"\n",
                                        message.What());
-
                }
        }
 }
@@ -1132,3 +1184,24 @@ AuthenticationManager::_InitShadowPwdDB()
 
        return B_OK;
 }
+
+
+void
+AuthenticationManager::_InvalidatePasswdDBReply()
+{
+       fPasswdDBReply->SetTo(1);
+}
+
+
+void
+AuthenticationManager::_InvalidateGroupDBReply()
+{
+       fGroupDBReply->SetTo(1);
+}
+
+
+void
+AuthenticationManager::_InvalidateShadowPwdDBReply()
+{
+       fShadowPwdDBReply->SetTo(1);
+}
diff --git a/src/servers/registrar/AuthenticationManager.h 
b/src/servers/registrar/AuthenticationManager.h
index ef43500..849cd52 100644
--- a/src/servers/registrar/AuthenticationManager.h
+++ b/src/servers/registrar/AuthenticationManager.h
@@ -35,6 +35,10 @@ private:
                        status_t                        _InitGroupDB();
                        status_t                        _InitShadowPwdDB();
 
+                       void                            
_InvalidatePasswdDBReply();
+                       void                            
_InvalidateGroupDBReply();
+                       void                            
_InvalidateShadowPwdDBReply();
+
 private:
                        port_id                         fRequestPort;
                        thread_id                       fRequestThread;

############################################################################

Commit:      82a064b57d73dded1109fa41be971d63b237fdc2
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Mon Sep 16 14:33:26 2013 UTC

useradd: small style fix

----------------------------------------------------------------------------

diff --git a/src/bin/multiuser/useradd.cpp b/src/bin/multiuser/useradd.cpp
index 8bee29e..1900cfb 100644
--- a/src/bin/multiuser/useradd.cpp
+++ b/src/bin/multiuser/useradd.cpp
@@ -175,8 +175,8 @@ main(int argc, const char* const* argv)
                                }
 
                                if (error != 0) {
-                                       fprintf(stderr, "Error: Failed to get 
info for group \"%s\".\n",
-                                               group);
+                                       fprintf(stderr, "Error: Failed to get 
info for group "
+                                               "\"%s\".\n", group);
                                        exit(1);
                                }
                                if (groupFound == NULL) {

############################################################################

Commit:      032ea9a485abba103a82f0b782546dda8ff8d758
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Mon Sep 16 16:00:48 2013 UTC

useradd: Create the new user with a locked password

----------------------------------------------------------------------------

diff --git a/src/bin/multiuser/Jamfile b/src/bin/multiuser/Jamfile
index 24f3232..f234bd7 100644
--- a/src/bin/multiuser/Jamfile
+++ b/src/bin/multiuser/Jamfile
@@ -13,7 +13,7 @@ BinCommand login : login.cpp : libmultiuser_utils.a ;
 
 BinCommand passwd : passwd.cpp : libmultiuser_utils.a ;
 
-BinCommand useradd : useradd.cpp : libmultiuser_utils.a ;
+BinCommand useradd : useradd.cpp ;
 
 BinCommand userdel : userdel.cpp ;
 
diff --git a/src/bin/multiuser/useradd.cpp b/src/bin/multiuser/useradd.cpp
index 1900cfb..476e6fa 100644
--- a/src/bin/multiuser/useradd.cpp
+++ b/src/bin/multiuser/useradd.cpp
@@ -130,7 +130,7 @@ main(int argc, const char* const* argv)
        const char* user = argv[optind];
 
        if (geteuid() != 0) {
-               fprintf(stderr, "Error: You need to be root.\n");
+               fprintf(stderr, "Error: Only root may add users.\n");
                exit(1);
        }
 
@@ -191,41 +191,6 @@ main(int argc, const char* const* argv)
                }
        }
 
-       // read password
-       char password[LINE_MAX];
-       if (read_password("password for user: ", password, sizeof(password),
-                       false) != B_OK) {
-               exit(1);
-       }
-
-       if (strlen(password) >= MAX_SHADOW_PWD_PASSWORD_LEN) {
-               fprintf(stderr, "Error: The password is too long.\n");
-               exit(1);
-       }
-
-       // read password again
-       char repeatedPassword[LINE_MAX];
-       if (read_password("repeat password: ", repeatedPassword,
-                       sizeof(repeatedPassword), false) != B_OK) {
-               exit(1);
-       }
-
-       // passwords need to match
-       if (strcmp(password, repeatedPassword) != 0) {
-               fprintf(stderr, "Error: passwords don't match\n");
-               exit(1);
-       }
-
-       memset(repeatedPassword, 0, sizeof(repeatedPassword));
-
-       // crypt it
-       char* encryptedPassword;
-       if (strlen(password) > 0) {
-               encryptedPassword = crypt(password, user);
-               memset(password, 0, sizeof(password));
-       } else
-               encryptedPassword = password;
-
        // find an unused UID
        uid_t uid = 1000;
        while (getpwuid(uid) != NULL)
@@ -240,7 +205,7 @@ main(int argc, const char* const* argv)
                || message.AddString("home", home) != B_OK
                || message.AddString("shell", shell) != B_OK
                || message.AddString("real name", realName) != B_OK
-               || message.AddString("shadow password", encryptedPassword) != 
B_OK
+               || message.AddString("shadow password", "!") != B_OK
                || message.AddInt32("last changed", time(NULL)) != B_OK
                || message.AddInt32("min", min) != B_OK
                || message.AddInt32("max", max) != B_OK

############################################################################

Commit:      6c346b88e1134f09024879a85f358ea4992023dd
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Mon Sep 16 16:01:07 2013 UTC

passwd: Add option -d to delete a user's password

----------------------------------------------------------------------------

diff --git a/src/bin/multiuser/passwd.cpp b/src/bin/multiuser/passwd.cpp
index 58d8620..bd12bec 100644
--- a/src/bin/multiuser/passwd.cpp
+++ b/src/bin/multiuser/passwd.cpp
@@ -1,9 +1,10 @@
 /*
- * Copyright 2008, Ingo Weinhold, ingo_weinhold@xxxxxx. All Rights Reserved.
+ * Copyright 2008-2013, Ingo Weinhold, ingo_weinhold@xxxxxx.
  * Distributed under the terms of the MIT License.
  */
 
 #include <errno.h>
+#include <getopt.h>
 #include <pwd.h>
 #include <shadow.h>
 #include <stdio.h>
@@ -28,8 +29,15 @@ extern const char *__progname;
 
 
 static const char* kUsage =
-"Usage: %s [ <user name> ]\n"
-;
+       "Usage: %s [ <options> ] [ <user name> ]\n"
+       "Deletes the specified user.\n"
+       "\n"
+       "Options:\n"
+       "  -d\n"
+       "    Delete the password for the specified user.\n"
+       "  -h, --help\n"
+       "    Print usage info.\n"
+       ;
 
 static void
 print_usage_and_exit(bool error)
@@ -42,12 +50,39 @@ print_usage_and_exit(bool error)
 int
 main(int argc, const char* const* argv)
 {
-       if (argc > 2)
+       bool deletePassword = false;;
+
+       while (true) {
+               static struct option sLongOptions[] = {
+                       { "help", no_argument, 0, 'h' },
+                       { 0, 0, 0, 0 }
+               };
+
+               opterr = 0; // don't print errors
+               int c = getopt_long(argc, (char**)argv, "dh", sLongOptions, 
NULL);
+               if (c == -1)
+                       break;
+
+       
+               switch (c) {
+                       case 'd':
+                               deletePassword = true;
+                               break;
+
+                       case 'h':
+                               print_usage_and_exit(false);
+                               break;
+
+                       default:
+                               print_usage_and_exit(true);
+                               break;
+               }
+       }
+
+       if (optind + 1 < argc)
                print_usage_and_exit(true);
 
-       const char* user = NULL;
-       if (argc == 2)
-               user = argv[1];
+       const char* user = optind < argc ? argv[optind] : NULL;
 
        if (geteuid() != 0) {
                fprintf(stderr, "Error: You need to be root.\n");
@@ -57,6 +92,18 @@ main(int argc, const char* const* argv)
        // this is a set-uid tool -- get the real UID
        uid_t uid = getuid();
 
+       if (deletePassword) {
+               if (uid != 0) {
+                       fprintf(stderr, "Error: Only root can delete users' 
passwords.\n");
+                       exit(1);
+               }
+
+               if (user == NULL) {
+                       fprintf(stderr, "Error: A user must be specified.\n");
+                       exit(1);
+               }
+       }
+
        // get the passwd entry
        struct passwd* passwd;
        if (user != NULL) {
@@ -90,40 +137,43 @@ main(int argc, const char* const* argv)
                }
        }
 
-       // read new password
        char password[LINE_MAX];
-       if (read_password("new password: ", password, sizeof(password), false)
-                       != B_OK) {
-               exit(1);
-       }
+       char* encryptedPassword;
 
-       if (strlen(password) >= MAX_SHADOW_PWD_PASSWORD_LEN) {
-               fprintf(stderr, "Error: The password is too long.\n");
-               exit(1);
-       }
+       if (deletePassword) {
+               password[0] = '\0';
+               encryptedPassword = password;
+       } else {
+               // read new password
+               if (read_password("new password: ", password, sizeof(password), 
false)
+                               != B_OK) {
+                       exit(1);
+               }
 
-       // read password again
-       char repeatedPassword[LINE_MAX];
-       if (read_password("repeat new password: ", repeatedPassword,
-                       sizeof(repeatedPassword), false) != B_OK) {
-               exit(1);
-       }
+               if (strlen(password) >= MAX_SHADOW_PWD_PASSWORD_LEN) {
+                       fprintf(stderr, "Error: The password is too long.\n");
+                       exit(1);
+               }
 
-       // passwords need to match
-       if (strcmp(password, repeatedPassword) != 0) {
-               fprintf(stderr, "Error: passwords don't match\n");
-               exit(1);
-       }
+               // read password again
+               char repeatedPassword[LINE_MAX];
+               if (read_password("repeat new password: ", repeatedPassword,
+                               sizeof(repeatedPassword), false) != B_OK) {
+                       exit(1);
+               }
+
+               // passwords need to match
+               if (strcmp(password, repeatedPassword) != 0) {
+                       fprintf(stderr, "Error: passwords don't match\n");
+                       exit(1);
+               }
 
-       memset(repeatedPassword, 0, sizeof(repeatedPassword));
+               memset(repeatedPassword, 0, sizeof(repeatedPassword));
 
-       // crypt it
-       char* encryptedPassword;
-       if (strlen(password) > 0) {
+               // crypt it
                encryptedPassword = crypt(password, user);
                memset(password, 0, sizeof(password));
-       } else
-               encryptedPassword = password;
+       }
 
        // prepare request for the registrar
        KMessage message(BPrivate::B_REG_UPDATE_USER);

############################################################################

Commit:      dc3be296140d4b76f0de2630dd86e85f1ffbc8c5
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Wed Sep 18 14:10:38 2013 UTC

Enable -Werror in src/bin/multiuser

----------------------------------------------------------------------------

diff --git a/build/jam/ArchitectureRules b/build/jam/ArchitectureRules
index 1181446..216ca71 100644
--- a/build/jam/ArchitectureRules
+++ b/build/jam/ArchitectureRules
@@ -627,6 +627,7 @@ rule ArchitectureSetupWarnings architecture
        EnableWerror src add-ons translators tiff ;
 #      EnableWerror src add-ons translators wonderbrush ;
        EnableWerror src add-ons print ;
+       EnableWerror src bin multiuser ;
        EnableWerror src bin package ;
        EnableWerror src bin package_repo ;
        EnableWerror src bin pkgman ;

############################################################################

Commit:      519bb60aef35f6e7371dc0ccf0a9c989036ae9b4
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Wed Sep 18 14:28:58 2013 UTC

Add group{add,del,mod}

----------------------------------------------------------------------------

diff --git a/build/jam/images/HaikuImage b/build/jam/images/HaikuImage
index a58a7f8..6ea1d85 100644
--- a/build/jam/images/HaikuImage
+++ b/build/jam/images/HaikuImage
@@ -14,7 +14,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures
        echo eject env error expand expr
        factor false fdinfo ffm filepanel find finddir FirstBootPrompt fmt fold
        fortune frcode ftp ftpd funzip fwcontrol
-       gawk gdb@x86 getlimits groups gzip gzexe
+       gawk gdb@x86 getlimits groupadd groupdel groupmod groups gzip gzexe
        hd head hey hostname
        id ident ifconfig <bin>install installsound iroster isvolume
        ideinfo@ide idestatus@ide
diff --git a/build/jam/images/HaikuImageBootstrap 
b/build/jam/images/HaikuImageBootstrap
index 71e5cca..ebf7d14 100644
--- a/build/jam/images/HaikuImageBootstrap
+++ b/build/jam/images/HaikuImageBootstrap
@@ -14,7 +14,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures
        echo eject env error expand expr
        factor false fdinfo ffm filepanel find finddir fmt fold
        fortune frcode ftp ftpd funzip
-       gawk gdb@x86 getlimits groups gzip gzexe
+       gawk gdb@x86 getlimits groupadd groupdel groupmod groups gzip gzexe
        hd head hey hostname
        id ident ifconfig <bin>install isvolume
        ideinfo@ide idestatus@ide
diff --git a/src/bin/multiuser/Jamfile b/src/bin/multiuser/Jamfile
index f234bd7..c79da87 100644
--- a/src/bin/multiuser/Jamfile
+++ b/src/bin/multiuser/Jamfile
@@ -17,5 +17,11 @@ BinCommand useradd : useradd.cpp ;
 
 BinCommand userdel : userdel.cpp ;
 
+BinCommand groupadd : groupadd.cpp ;
+
+BinCommand groupdel : groupdel.cpp ;
+
+BinCommand groupmod : groupmod.cpp : $(TARGET_LIBSTDC++) ;
+
 # set set-uid bit on passwd
 MODE on passwd = 04755 ;
diff --git a/src/bin/multiuser/groupadd.cpp b/src/bin/multiuser/groupadd.cpp
new file mode 100644
index 0000000..55e7bed
--- /dev/null
+++ b/src/bin/multiuser/groupadd.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx.
+ * Distributed under the terms of the MIT License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <OS.h>
+
+#include <RegistrarDefs.h>
+#include <user_group.h>
+#include <util/KMessage.h>
+
+#include "multiuser_utils.h"
+
+
+extern const char *__progname;
+
+
+static const char* kUsage =
+       "Usage: %s [ <options> ] <group name>\n"
+       "Creates a new group <group name>.\n"
+       "\n"
+       "Options:\n"
+       "  -h, --help\n"
+       "    Print usage info.\n"
+       ;
+
+static void
+print_usage_and_exit(bool error)
+{
+       fprintf(error ? stderr : stdout, kUsage, __progname);
+       exit(error ? 1 : 0);
+}
+
+
+int
+main(int argc, const char* const* argv)
+{
+       while (true) {
+               static struct option sLongOptions[] = {
+                       { "help", no_argument, 0, 'h' },
+                       { 0, 0, 0, 0 }
+               };
+
+               opterr = 0; // don't print errors
+               int c = getopt_long(argc, (char**)argv, "h", sLongOptions, 
NULL);
+               if (c == -1)
+                       break;
+
+       
+               switch (c) {
+                       case 'h':
+                               print_usage_and_exit(false);
+                               break;
+
+                       default:
+                               print_usage_and_exit(true);
+                               break;
+               }
+       }
+
+       if (optind != argc - 1)
+               print_usage_and_exit(true);
+
+       const char* group = argv[optind];
+
+       if (geteuid() != 0) {
+               fprintf(stderr, "Error: Only root may add groups.\n");
+               exit(1);
+       }
+
+       // check, if group already exists
+       if (getgrnam(group) != NULL) {
+               fprintf(stderr, "Error: Group \"%s\" already exists.\n", group);
+               exit(1);
+       }
+
+       // find an unused GID
+       gid_t gid = 100;
+       while (getgrgid(gid) != NULL)
+               gid++;
+
+       // prepare request for the registrar
+       KMessage message(BPrivate::B_REG_UPDATE_GROUP);
+       if (message.AddInt32("gid", gid) != B_OK
+               || message.AddString("name", group) != B_OK
+               || message.AddString("password", "x") != B_OK
+               || message.AddBool("add group", true) != B_OK) {
+               fprintf(stderr, "Error: Out of memory!\n");
+               exit(1);
+       }
+
+       // send the request
+       KMessage reply;
+       status_t error = send_authentication_request_to_registrar(message, 
reply);
+       if (error != B_OK) {
+               fprintf(stderr, "Error: Failed to create group: %s\n", 
strerror(error));
+               exit(1);
+       }
+
+       return 0;
+}
diff --git a/src/bin/multiuser/groupdel.cpp b/src/bin/multiuser/groupdel.cpp
new file mode 100644
index 0000000..ab8d5ce
--- /dev/null
+++ b/src/bin/multiuser/groupdel.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx.
+ * Distributed under the terms of the MIT License.
+ */
+
+#include <getopt.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <OS.h>
+
+#include <RegistrarDefs.h>
+#include <user_group.h>
+#include <util/KMessage.h>
+
+#include "multiuser_utils.h"
+
+
+extern const char *__progname;
+
+
+static const char* kUsage =
+       "Usage: %s [ <options> ] <group name>\n"
+       "Deletes the specified group.\n"
+       "\n"
+       "Options:\n"
+       "  -h, --help\n"
+       "    Print usage info.\n"
+       ;
+
+static void
+print_usage_and_exit(bool error)
+{
+       fprintf(error ? stderr : stdout, kUsage, __progname);
+       exit(error ? 1 : 0);
+}
+
+
+int
+main(int argc, const char* const* argv)
+{
+       while (true) {
+               static struct option sLongOptions[] = {
+                       { "help", no_argument, 0, 'h' },
+                       { 0, 0, 0, 0 }
+               };
+
+               opterr = 0; // don't print errors
+               int c = getopt_long(argc, (char**)argv, "h", sLongOptions, 
NULL);
+               if (c == -1)
+                       break;
+
+       
+               switch (c) {
+                       case 'h':
+                               print_usage_and_exit(false);
+                               break;
+
+                       default:
+                               print_usage_and_exit(true);
+                               break;
+               }
+       }
+
+       if (optind != argc - 1)
+               print_usage_and_exit(true);
+
+       const char* group = argv[optind];
+
+       if (geteuid() != 0) {
+               fprintf(stderr, "Error: Only root may delete groups.\n");
+               exit(1);
+       }
+
+       if (getgrnam(group) == NULL) {
+               fprintf(stderr, "Error: Group \"%s\" doesn't exists.\n", group);
+               exit(1);
+       }
+
+       // prepare request for the registrar
+       KMessage message(BPrivate::B_REG_DELETE_GROUP);
+       if (message.AddString("name", group) != B_OK) {
+               fprintf(stderr, "Error: Out of memory!\n");
+               exit(1);
+       }
+
+       // send the request
+       KMessage reply;
+       status_t error = send_authentication_request_to_registrar(message, 
reply);
+       if (error != B_OK) {
+               fprintf(stderr, "Error: Failed to delete group: %s\n", 
strerror(error));
+               exit(1);
+       }
+
+       return 0;
+}
diff --git a/src/bin/multiuser/groupmod.cpp b/src/bin/multiuser/groupmod.cpp
new file mode 100644
index 0000000..2fbb47d
--- /dev/null
+++ b/src/bin/multiuser/groupmod.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx.
+ * Distributed under the terms of the MIT License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+
+#include <OS.h>
+
+#include <RegistrarDefs.h>
+#include <user_group.h>
+#include <util/KMessage.h>
+
+#include "multiuser_utils.h"
+
+
+extern const char *__progname;
+
+
+static const char* kUsage =
+       "Usage: %s [ <options> ] <group name>\n"
+       "Creates a new group <group name>.\n"
+       "\n"
+       "Options:\n"
+       "  -A, --add-user <user>\n"
+       "    Add the user <user> to the group.\n"
+       "  -h, --help\n"
+       "    Print usage info.\n"
+       "  -R, --remove-user <user>\n"
+       "    Remove the user <user> from the group.\n"
+       ;
+
+static void
+print_usage_and_exit(bool error)
+{
+       fprintf(error ? stderr : stdout, kUsage, __progname);
+       exit(error ? 1 : 0);
+}
+
+
+int
+main(int argc, const char* const* argv)
+{
+       typedef std::set<std::string> StringSet;
+
+       StringSet usersToAdd;
+       StringSet usersToRemove;
+
+       while (true) {
+               static struct option sLongOptions[] = {
+                       { "add-user", required_argument, 0, 'A' },
+                       { "help", no_argument, 0, 'h' },
+                       { "remove-user", required_argument, 0, 'A' },
+                       { 0, 0, 0, 0 }
+               };
+
+               opterr = 0; // don't print errors
+               int c = getopt_long(argc, (char**)argv, "A:hR:", sLongOptions, 
NULL);
+               if (c == -1)
+                       break;
+
+       
+               switch (c) {
+                       case 'A':
+                               usersToAdd.insert(optarg);
+                               break;
+
+                       case 'h':
+                               print_usage_and_exit(false);
+                               break;
+
+                       case 'R':
+                               usersToRemove.insert(optarg);
+                               break;
+
+                       default:
+                               print_usage_and_exit(true);
+                               break;
+               }
+       }
+
+       if (optind != argc - 1)
+               print_usage_and_exit(true);
+
+       const char* group = argv[optind];
+
+       if (geteuid() != 0) {
+               fprintf(stderr, "Error: Only root may modify groups.\n");
+               exit(1);
+       }
+
+       // get the group
+       struct group* groupInfo = getgrnam(group);
+       if (groupInfo == NULL) {
+               fprintf(stderr, "Error: Group \"%s\" doesn't exist.\n", group);
+               exit(1);
+       }
+
+       // check, if anything needs to be done
+       if (usersToAdd.empty() && usersToRemove.empty()) {
+               fprintf(stderr, "Error: No modification specified.\n");
+               exit(1);
+       }
+
+       // prepare request for the registrar
+       KMessage message(BPrivate::B_REG_UPDATE_GROUP);
+       if (message.AddInt32("gid", groupInfo->gr_gid) != B_OK
+               || message.AddString("name", group) != B_OK
+               || message.AddString("password", groupInfo->gr_passwd) != B_OK
+               || message.AddBool("add group", false) != B_OK) {
+               fprintf(stderr, "Error: Out of memory!\n");
+               exit(1);
+       }
+
+       for (int32 i = 0; const char* user = groupInfo->gr_mem[i]; i++) {
+               if (usersToRemove.erase(user) > 0)
+                       continue;
+
+               usersToAdd.insert(user);
+       }
+
+       if (!usersToRemove.empty()) {
+               fprintf(stderr, "Error: \"%s\" is not a member of group 
\"%s\"\n",
+                       usersToRemove.begin()->c_str(), group);
+               exit(1);
+       }
+
+       // If the group doesn't have any more members, insert an empty string 
as an
+       // indicator for the registrar to remove all members.
+       if (usersToAdd.empty())
+               usersToAdd.insert("");
+
+       for (StringSet::const_iterator it = usersToAdd.begin();
+               it != usersToAdd.end(); ++it) {
+               if (message.AddString("members", it->c_str()) != B_OK) {
+                       fprintf(stderr, "Error: Out of memory!\n");
+                       exit(1);
+               }
+       }
+
+       // send the request
+       KMessage reply;
+       status_t error = send_authentication_request_to_registrar(message, 
reply);
+       if (error != B_OK) {
+               fprintf(stderr, "Error: Failed to create group: %s\n", 
strerror(error));
+               exit(1);
+       }
+
+       return 0;
+}
diff --git a/src/servers/registrar/AuthenticationManager.cpp 
b/src/servers/registrar/AuthenticationManager.cpp
index a3fe86a..57742db 100644
--- a/src/servers/registrar/AuthenticationManager.cpp
+++ b/src/servers/registrar/AuthenticationManager.cpp
@@ -12,9 +12,11 @@
 
 #include <map>
 #include <new>
+#include <set>
 #include <string>
 
 #include <DataIO.h>
+#include <StringList.h>
 
 #include <AutoDeleter.h>
 #include <RegistrarDefs.h>
@@ -30,6 +32,9 @@ using std::string;
 using namespace BPrivate;
 
 
+typedef std::set<std::string> StringSet;
+
+
 class AuthenticationManager::FlatStore {
 public:
        FlatStore()
@@ -337,22 +342,29 @@ private:
 
 class AuthenticationManager::Group {
 public:
+       Group()
+               :
+               fGID(0),
+               fName(),
+               fPassword(),
+               fMembers()
+       {
+       }
+
        Group(const char* name, const char* password, gid_t gid,
                const char* const* members, int memberCount)
                :
                fGID(gid),
                fName(name),
                fPassword(password),
-               fMembers(new string[memberCount]),
-               fMemberCount(memberCount)
+               fMembers()
        {
                for (int i = 0; i < memberCount; i++)
-                       fMembers[i] = members[i];
+                       fMembers.insert(members[i]);
        }
 
        ~Group()
        {
-               delete[] fMembers;
        }
 
        const string& Name() const      { return fName; }
@@ -360,12 +372,40 @@ public:
 
        bool HasMember(const char* name)
        {
-               for (int i = 0; i < fMemberCount; i++) {
-                       if (fMembers[i] == name)
-                               return true;
+               try {
+                       return fMembers.find(name) != fMembers.end();
+               } catch (...) {
+                       return false;
                }
+       }
 
-               return false;
+       bool MemberRemoved(const std::string& name)
+       {
+               return fMembers.erase(name) > 0;
+       }
+
+       void UpdateFromMessage(const KMessage& message)
+       {
+               int32 intValue;
+               if (message.FindInt32("gid", &intValue) == B_OK)
+                       fGID = intValue;
+
+               const char* stringValue;
+               if (message.FindString("name", &stringValue) == B_OK)
+                       fName = stringValue;
+
+               if (message.FindString("password", &stringValue) == B_OK)
+                       fPassword = stringValue;
+
+               if (message.FindString("members", &stringValue) == B_OK) {
+                       fMembers.clear();
+                       for (int32 i = 0;
+                               (stringValue = message.GetString("members", i, 
NULL)) != NULL;
+                               i++) {
+                               if (stringValue != NULL && *stringValue != '\0')
+                                       fMembers.insert(stringValue);
+                       }
+               }
        }
 
        group* WriteFlatGroup(FlatStore& store) const
@@ -373,15 +413,18 @@ public:
                struct group group;
 
                char* members[MAX_GROUP_MEMBER_COUNT + 1];
-               for (int i = 0; i < fMemberCount; i++)
-                       members[i] = store.AppendString(fMembers[i].c_str());
-               members[fMemberCount] = (char*)-1;
+               int32 count = 0;
+               for (StringSet::const_iterator it = fMembers.begin();
+                       it != fMembers.end(); ++it) {
+                       members[count++] = store.AppendString(it->c_str());
+               }
+               members[count] = (char*)-1;
 
                group.gr_gid = fGID;
                group.gr_name = store.AppendString(fName);
                group.gr_passwd = store.AppendString(fPassword);
                group.gr_mem = (char**)store.AppendData(members,
-                       sizeof(char*) * (fMemberCount + 1), true);
+                       sizeof(char*) * (count + 1), true);
 
                return store.AppendData(group);
        }
@@ -396,22 +439,34 @@ public:
                        return error;
                }
 
-               for (int i = 0; i < fMemberCount; i++) {
-                       if ((error = message.AddString("members", 
fMembers[i].c_str()))
-                                       != B_OK) {
+               for (StringSet::const_iterator it = fMembers.begin();
+                       it != fMembers.end(); ++it) {
+                       if ((error = message.AddString("members", it->c_str())) 
!= B_OK)
                                return error;
-                       }
                }
 
                return B_OK;
        }
 
+       void WriteGroupLine(FILE* file)
+       {
+               fprintf(file, "%s:%s:%d:",
+                       fName.c_str(), fPassword.c_str(), (int)fGID);
+               for (StringSet::const_iterator it = fMembers.begin();
+                       it != fMembers.end(); ++it) {
+                       if (it == fMembers.begin())
+                               fprintf(file, "%s", it->c_str());
+                       else
+                               fprintf(file, ",%s", it->c_str());
+               }
+               fputs("\n", file);
+       }
+
 private:
-       gid_t   fGID;
-       string  fName;
-       string  fPassword;
-       string* fMembers;
-       int             fMemberCount;
+       gid_t           fGID;
+       string          fName;
+       string          fPassword;
+       StringSet       fMembers;
 };
 
 
@@ -555,6 +610,23 @@ public:
                return B_OK;
        }
 
+       void RemoveGroup(Group* group)
+       {
+               fGroupsByID.erase(fGroupsByID.find(group->GID()));
+               fGroupsByName.erase(fGroupsByName.find(group->Name()));
+       }
+
+       bool UserRemoved(const std::string& user)
+       {
+               bool changed = false;
+               for (map<gid_t, Group*>::const_iterator it = 
fGroupsByID.begin();
+                        it != fGroupsByID.end(); ++it) {
+                       Group* group = it->second;
+                       changed |= group->MemberRemoved(user);
+               }
+               return changed;
+       }
+
        Group* GroupByID(gid_t gid) const
        {
                map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid);
@@ -605,6 +677,31 @@ public:
                return count;
        }
 
+       void WriteToDisk()
+       {
+               // rename the old files
+               string groupBackup(kGroupFile);
+               groupBackup += ".old";
+
+               rename(kGroupFile, groupBackup.c_str());
+                       // Don't check errors. We can't do anything anyway.
+
+               // open file
+               FILE* groupFile = fopen(kGroupFile, "w");
+               if (groupFile == NULL) {
+                       debug_printf("REG: Failed to open group file \"%s\" for 
"
+                               "writing: %s\n", kGroupFile, strerror(errno));
+               }
+               CObjectDeleter<FILE, int> _1(groupFile, fclose);
+
+               // write groups
+               for (map<gid_t, Group*>::const_iterator it = 
fGroupsByID.begin();
+                       it != fGroupsByID.end(); ++it) {
+                       Group* group = it->second;
+                       group->WriteGroupLine(groupFile);
+               }
+       }
+
 private:
        map<uid_t, Group*>      fGroupsByID;
        map<string, Group*>     fGroupsByName;
@@ -1002,10 +1099,17 @@ AuthenticationManager::_RequestThread()
 
                                // apply the change
                                if (error == B_OK) {
+                                       std::string userName = user->Name();
+
                                        fUserDB->RemoveUser(user);
                                        fUserDB->WriteToDisk();
                                        _InvalidatePasswdDBReply();
                                        _InvalidateShadowPwdDBReply();
+
+                                       if (fGroupDB->UserRemoved(userName)) {
+                                               fGroupDB->WriteToDisk();
+                                               _InvalidateGroupDBReply();
+                                       }
                                }
 
                                // send reply
@@ -1017,13 +1121,110 @@ AuthenticationManager::_RequestThread()
                        }
 
                        case B_REG_UPDATE_GROUP:
-                               debug_printf("B_REG_UPDATE_GROUP done: 
currently unsupported!\n");
+                       {
+                               // find group
+                               Group* group = NULL;
+                               int32 gid;
+                               const char* name;
+
+                               if (message.FindInt32("gid", &gid) == B_OK) {
+                                       group = fGroupDB->GroupByID(gid);
+                               } else if (message.FindString("name", &name) == 
B_OK) {
+                                       group = fGroupDB->GroupByName(name);
+                               } else {
+                                       error = B_BAD_VALUE;
+                               }
+
+                               // only root can change anything
+                               if (error == B_OK && !isRoot)
+                                       error = EPERM;
+
+                               // check addGroup vs. existing group
+                               bool addGroup = message.GetBool("add group", 
false);
+                               if (error == B_OK) {
+                                       if (addGroup) {
+                                               if (group != NULL)
+                                                       error = EEXIST;
+                                       } else if (group == NULL)
+                                               error = ENOENT;
+                               }
+
+                               // apply all changes
+                               if (error == B_OK) {
+                                       // clone the group object and update it 
from the message
+                                       Group* oldGroup = group;
+                                       group = NULL;
+                                       try {
+                                               group = (oldGroup != NULL ? new 
Group(*oldGroup)
+                                                       : new Group);
+                                               
group->UpdateFromMessage(message);
+
+                                               // gid and name should remain 
the same
+                                               if (oldGroup != NULL) {
+                                                       if (oldGroup->GID() != 
group->GID()
+                                                               || 
oldGroup->Name() != group->Name()) {
+                                                               error = 
B_BAD_VALUE;
+                                                       }
+                                               }
+
+                                               // replace the old group and 
write DBs to disk
+                                               if (error == B_OK) {
+                                                       
fGroupDB->AddGroup(group);
+                                                       fGroupDB->WriteToDisk();
+                                                       
_InvalidateGroupDBReply();
+                                               }
+                                       } catch (...) {
+                                               error = B_NO_MEMORY;
+                                       }
+
+                                       if (error == B_OK)
+                                               delete oldGroup;
+                                       else
+                                               delete group;
+                               }
+
+                               // send reply
+                               KMessage reply;
+                               reply.SetWhat(error);
+                               message.SendReply(&reply, -1, -1, 0, 
registrarTeam);
+
                                break;
+                       }
 
                        case B_REG_DELETE_GROUP:
                        {
-                               debug_printf(
-                                       "B_REG_DELETE_GROUP done: currently 
unsupported!\n");
+                               // find group
+                               Group* group = NULL;
+                               int32 gid;
+                               const char* name;
+
+                               if (message.FindInt32("gid", &gid) == B_OK) {
+                                       group = fGroupDB->GroupByID(gid);
+                               } else if (message.FindString("name", &name) == 
B_OK) {
+                                       group = fGroupDB->GroupByName(name);
+                               } else {
+                                       error = B_BAD_VALUE;
+                               }
+
+                               if (error == B_OK && group == NULL)
+                                       error = ENOENT;
+
+                               // only root can change anything
+                               if (error == B_OK && !isRoot)
+                                       error = EPERM;
+
+                               // apply the change
+                               if (error == B_OK) {
+                                       fGroupDB->RemoveGroup(group);
+                                       fGroupDB->WriteToDisk();
+                                       _InvalidateGroupDBReply();
+                               }
+
+                               // send reply
+                               KMessage reply;
+                               reply.SetWhat(error);
+                               message.SendReply(&reply, -1, -1, 0, 
registrarTeam);
+
                                break;
                        }
 


Other related posts:

  • » [haiku-commits] BRANCH HaikuPM-github.package-management [519bb60] in src: bin/multiuser servers/registrar - HaikuPM-github . package-management