>From 49c798d880f0504755642efb0a83b0682e72caf4 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Fri, 14 Aug 2009 15:09:57 +0200
Subject: [PATCH 2/3] Decouple synchronous sysdb interface from tools

Instead of working directly with async code in tools, create synchronous
wrappers that could be used by tools and python bindings.

Also resolves many issues with code duplication in tools and thus fixes
ticket #87
---
 server/Makefile.am                 |    6 +-
 server/tools/common/sss_sync_ops.c |  814 ++++++++++++++++++++++++++++++++++++
 server/tools/common/sss_sync_ops.h |   68 +++
 server/tools/common/tools_util.c   |  336 +++++++++++++++
 server/tools/common/tools_util.h   |   81 ++++
 server/tools/sss_groupadd.c        |  107 +-----
 server/tools/sss_groupdel.c        |  115 +-----
 server/tools/sss_groupmod.c        |  267 +-----------
 server/tools/sss_useradd.c         |  262 ++----------
 server/tools/sss_userdel.c         |  118 +-----
 server/tools/sss_usermod.c         |  350 ++--------------
 server/tools/tools_util.c          |  373 -----------------
 server/tools/tools_util.h          |   99 -----
 13 files changed, 1426 insertions(+), 1570 deletions(-)
 create mode 100644 server/tools/common/sss_sync_ops.c
 create mode 100644 server/tools/common/sss_sync_ops.h
 create mode 100644 server/tools/common/tools_util.c
 create mode 100644 server/tools/common/tools_util.h
 delete mode 100644 server/tools/tools_util.c
 delete mode 100644 server/tools/tools_util.h

diff --git a/server/Makefile.am b/server/Makefile.am
index 12ec4b1..0a81ab4 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -169,7 +169,8 @@ SSSD_RESPONDER_OBJ = \
     responder/common/responder_packet.c
 
 SSSD_TOOLS_OBJ = \
-    tools/tools_util.c
+    tools/common/sss_sync_ops.c \
+    tools/common/tools_util.c
 
 SSSD_RESOLV_OBJ = \
     resolv/async_resolv.c
@@ -226,7 +227,8 @@ dist_noinst_HEADERS = \
     providers/krb5/krb5_auth.h \
     providers/ldap/sdap.h \
     providers/ldap/sdap_async.h \
-    tools/tools_util.h \
+    tools/common/tools_util.h \
+    tools/common/sss_sync_ops.h \
     krb5_plugin/sssd_krb5_locator_plugin.h \
     resolv/async_resolv.h \
     resolv/ares/ares_parse_srv_reply.h \
diff --git a/server/tools/common/sss_sync_ops.c b/server/tools/common/sss_sync_ops.c
new file mode 100644
index 0000000..e85838a
--- /dev/null
+++ b/server/tools/common/sss_sync_ops.c
@@ -0,0 +1,814 @@
+/*
+ * Authors:
+ *   Jakub Hrozek <jhrozek@redhat.com>
+ *
+ * Copyright (C) 2008  Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 only
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <tevent.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+
+#include "tools/common/sss_sync_ops.h"
+
+/* Default settings for user attributes */
+#define CONFDB_DFL_SECTION "config/user_defaults"
+
+#define DFL_SHELL_ATTR     "defaultShell"
+#define DFL_BASEDIR_ATTR   "baseDirectory"
+
+#define DFL_SHELL_VAL      "/bin/bash"
+#define DFL_BASEDIR_VAL    "/home"
+
+#define VAR_CHECK(var, val, attr, msg) do { \
+        if (var != (val)) { \
+            DEBUG(1, (msg" attribute: %s", attr)); \
+            return val; \
+        } \
+} while(0)
+
+typedef void (*sync_callback_fn)(struct tevent_req *);
+typedef struct ldb_dn* (*dn_fn)(struct sysdb_ctx *, void *, const char *, const char *);
+
+struct sync_ops_ctx {
+    struct tevent_context *ev;
+    struct sysdb_ctx *sysdb;
+    struct confdb_ctx *confdb;
+
+    struct sysdb_attrs *attrs;
+    dn_fn member_dn_fn;
+    int cur;
+
+    struct sysdb_handle *handle;
+    int error;
+    bool done;
+
+    struct ops_ctx *data;
+};
+
+/*
+ * common functions
+ */
+int setup_db(TALLOC_CTX *mem_ctx,
+             struct tevent_context *ev,
+             struct confdb_ctx **_confdb,
+             struct sysdb_ctx **_sysdb)
+{
+    struct confdb_ctx *confdb;
+    struct sysdb_ctx *sysdb;
+    char *confdb_path;
+    int ret;
+
+    /* Create the event context */
+    confdb_path = talloc_asprintf(mem_ctx, "%s/%s", DB_PATH, CONFDB_FILE);
+    if (confdb_path == NULL) {
+        return ENOMEM;
+    }
+
+    /* Connect to the conf db */
+    ret = confdb_init(mem_ctx, ev, &confdb, confdb_path);
+    if (ret != EOK) {
+        DEBUG(1, ("Could not initialize connection to the confdb"));
+        talloc_free(confdb_path);
+        return ret;
+    }
+
+    /* open sysdb at default path */
+    ret = sysdb_init(mem_ctx, ev, confdb, NULL, &sysdb);
+    if (ret != EOK) {
+        DEBUG(1, ("Could not initialize connection to the sysdb"));
+        talloc_free(confdb_path);
+        return ret;
+    }
+
+    talloc_free(confdb_path);
+    *_confdb = confdb;
+    *_sysdb = sysdb;
+    return EOK;
+}
+
+static int synchronous_transaction(TALLOC_CTX *mem_ctx,
+                                   sync_callback_fn cbc,
+                                   struct ops_ctx *data)
+{
+    int ret;
+    struct tevent_req *req;
+    struct sync_ops_ctx *octx = NULL;
+
+    octx = talloc_zero(mem_ctx, struct sync_ops_ctx);
+    if (octx == NULL) {
+        DEBUG(1, ("Could not create sync_ops context"));
+        return ENOMEM;
+    }
+
+    /* A private event context for the duration of the sync transaction */
+    octx->ev = tevent_context_init(mem_ctx);
+    if (octx->ev == NULL) {
+        DEBUG(1, ("Could not create event context"));
+        ret = EIO;
+        goto done;
+    }
+
+    ret = setup_db(octx, octx->ev, &octx->confdb, &octx->sysdb);
+    if (ret != EOK) {
+        DEBUG(1, ("Could not set up database\n"));
+        goto done;
+    }
+    octx->data = data;
+
+    req = sysdb_transaction_send(octx, octx->ev, octx->sysdb);
+    if (!req) {
+        DEBUG(1, ("Could not start transaction"));
+        ret = ENOMEM;
+        goto done;
+    }
+    tevent_req_set_callback(req, cbc, octx);
+
+    while (!octx->done) {
+        tevent_loop_once(octx->ev);
+    }
+    ret = octx->error;
+
+done:
+    talloc_free(octx);
+    return ret;
+}
+
+static void req_done(struct tevent_req *req)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(req, struct sync_ops_ctx);
+
+    octx->error = sysdb_transaction_commit_recv(req);
+    octx->done = true;
+}
+
+static void transaction_done(struct sync_ops_ctx *octx, int error)
+{
+    struct tevent_req *req;
+
+    if (error != EOK) {
+        goto fail;
+    }
+
+    req = sysdb_transaction_commit_send(octx, octx->ev, octx->handle);
+    if (!req) {
+        error = ENOMEM;
+        goto fail;
+    }
+    tevent_req_set_callback(req, req_done, octx);
+
+    return;
+
+fail:
+    /* free transaction */
+    talloc_zfree(octx->handle);
+
+    octx->error = error;
+    octx->done = true;
+}
+
+/*
+ * Generic add member to group
+ */
+static void add_to_groups_done(struct tevent_req *subreq);
+
+static void add_to_groups(struct sync_ops_ctx *octx)
+{
+    struct ldb_dn *parent_dn;
+    struct ldb_dn *member_dn;
+    struct tevent_req *subreq;
+
+    member_dn = octx->member_dn_fn(octx->sysdb, octx,
+                                   octx->data->domain->name,
+                                   octx->data->name);
+    if (!member_dn) {
+        return transaction_done(octx, ENOMEM);
+    }
+
+    parent_dn = sysdb_group_dn(octx->sysdb, octx,
+                               octx->data->domain->name,
+                               octx->data->addgroups[octx->cur]);
+    if (!parent_dn) {
+        return transaction_done(octx, ENOMEM);
+    }
+
+    subreq = sysdb_mod_group_member_send(octx,
+                                         octx->ev,
+                                         octx->handle,
+                                         member_dn,
+                                         parent_dn,
+                                         LDB_FLAG_MOD_ADD);
+    if (!subreq) {
+        return transaction_done(octx, ENOMEM);
+    }
+    tevent_req_set_callback(subreq, add_to_groups_done, octx);
+}
+
+static void add_to_groups_done(struct tevent_req *subreq)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(subreq,
+                                                         struct sync_ops_ctx);
+    int ret;
+
+    ret = sysdb_mod_group_member_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret) {
+        return transaction_done(octx, ret);
+    }
+
+    /* go on to next group */
+    octx->cur++;
+
+    /* check if we added all of them */
+    if (octx->data->addgroups[octx->cur] == NULL) {
+        return transaction_done(octx, EOK);
+    }
+
+    return add_to_groups(octx);
+}
+
+/*
+ * Generic remove member from group
+ */
+static void remove_from_groups_done(struct tevent_req *req);
+
+static void remove_from_groups(struct sync_ops_ctx *octx)
+{
+    struct ldb_dn *parent_dn;
+    struct ldb_dn *member_dn;
+    struct tevent_req *req;
+
+    member_dn = octx->member_dn_fn(octx->sysdb, octx,
+                                   octx->data->domain->name, octx->data->name);
+    if (!member_dn) {
+        return transaction_done(octx, ENOMEM);
+    }
+
+    parent_dn = sysdb_group_dn(octx->sysdb, octx,
+                               octx->data->domain->name,
+                               octx->data->rmgroups[octx->cur]);
+    if (!parent_dn) {
+        return transaction_done(octx, ENOMEM);
+    }
+
+    req = sysdb_mod_group_member_send(octx,
+                                      octx->ev,
+                                      octx->handle,
+                                      member_dn,
+                                      parent_dn,
+                                      LDB_FLAG_MOD_DELETE);
+    if (!req) {
+        return transaction_done(octx, ENOMEM);
+    }
+    tevent_req_set_callback(req, remove_from_groups_done, octx);
+}
+
+static void remove_from_groups_done(struct tevent_req *req)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(req,
+                                                         struct sync_ops_ctx);
+    int ret;
+
+    ret = sysdb_mod_group_member_recv(req);
+    if (ret) {
+        return transaction_done(octx, ret);
+    }
+    talloc_zfree(req);
+
+    /* go on to next group */
+    octx->cur++;
+
+    /* check if we removed all of them */
+    if (octx->data->rmgroups[octx->cur] == NULL) {
+        octx->cur = 0;
+        if (octx->data->addgroups != NULL) {
+            return add_to_groups(octx);
+        }
+        return transaction_done(octx, EOK);
+    }
+
+    return remove_from_groups(octx);
+}
+
+/*
+ * Add a user
+ */
+static void add_user_done(struct tevent_req *subreq);
+
+void add_user(struct tevent_req *req)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(req,
+                                                         struct sync_ops_ctx);
+    struct tevent_req *subreq;
+    int ret;
+
+    octx->member_dn_fn = sysdb_user_dn;
+
+    ret = sysdb_transaction_recv(req, octx, &octx->handle);
+    if (ret != EOK) {
+        return transaction_done(octx, ret);
+    }
+
+    subreq = sysdb_add_user_send(octx, octx->ev, octx->handle,
+                                 octx->data->domain, octx->data->name,
+                                 octx->data->uid, octx->data->gid,
+                                 octx->data->gecos, octx->data->home,
+                                 octx->data->shell, NULL);
+    if (!subreq) {
+        transaction_done(octx, ENOMEM);
+    }
+    tevent_req_set_callback(subreq, add_user_done, octx);
+}
+
+static void add_user_done(struct tevent_req *subreq)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(subreq,
+                                                         struct sync_ops_ctx);
+    int ret;
+
+    ret = sysdb_add_user_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret) {
+        return transaction_done(octx, ret);
+    }
+
+    if (octx->data->addgroups) {
+        return add_to_groups(octx);
+    }
+
+    return transaction_done(octx, ret);
+}
+
+/*
+ * Remove a user
+ */
+static void user_del_done(struct tevent_req *subreq);
+
+static void user_del(struct tevent_req *req)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(req,
+                                                         struct sync_ops_ctx);
+    struct tevent_req *subreq;
+    struct ldb_dn *user_dn;
+    int ret;
+
+    ret = sysdb_transaction_recv(req, octx, &octx->handle);
+    if (ret != EOK) {
+        return transaction_done(octx, ret);
+    }
+
+    user_dn = sysdb_user_dn(octx->sysdb, octx,
+                            octx->data->domain->name, octx->data->name);
+    if (!user_dn) {
+        DEBUG(1, ("Could not construct a user DN\n"));
+        return transaction_done(octx, ENOMEM);
+    }
+
+    subreq = sysdb_delete_entry_send(octx, octx->ev, octx->handle, user_dn, false);
+    if (!subreq)
+        return transaction_done(octx, ENOMEM);
+
+    tevent_req_set_callback(subreq, user_del_done, octx);
+}
+
+static void user_del_done(struct tevent_req *subreq)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(subreq,
+                                                         struct sync_ops_ctx);
+    int ret;
+
+    ret = sysdb_delete_entry_recv(subreq);
+
+    return transaction_done(octx, ret);
+}
+
+/*
+ * Modify a user
+ */
+static int usermod_build_attrs(TALLOC_CTX *mem_ctx,
+                               const char *gecos,
+                               const char *home,
+                               const char *shell,
+                               uid_t uid,
+                               gid_t gid,
+                               int lock,
+                               struct sysdb_attrs **_attrs)
+{
+    int ret;
+    struct sysdb_attrs *attrs;
+
+    attrs = sysdb_new_attrs(mem_ctx);
+    if (attrs == NULL) {
+        return ENOMEM;
+    }
+
+    if(shell) {
+        ret = sysdb_attrs_add_string(attrs,
+                                     SYSDB_SHELL,
+                                     shell);
+        VAR_CHECK(ret, EOK, SYSDB_SHELL,
+                  "Could not add attribute to changeset\n");
+    }
+
+    if(home) {
+        ret = sysdb_attrs_add_string(attrs,
+                                     SYSDB_HOMEDIR,
+                                     home);
+        VAR_CHECK(ret, EOK, SYSDB_HOMEDIR,
+                  "Could not add attribute to changeset\n");
+    }
+
+    if(gecos) {
+        ret = sysdb_attrs_add_string(attrs,
+                                     SYSDB_GECOS,
+                                     gecos);
+        VAR_CHECK(ret, EOK, SYSDB_GECOS,
+                  "Could not add attribute to changeset\n");
+    }
+
+    if(uid) {
+        ret = sysdb_attrs_add_long(attrs,
+                                   SYSDB_UIDNUM,
+                                   uid);
+        VAR_CHECK(ret, EOK, SYSDB_UIDNUM,
+                  "Could not add attribute to changeset\n");
+    }
+
+    if(gid) {
+        ret = sysdb_attrs_add_long(attrs,
+                                   SYSDB_GIDNUM,
+                                   gid);
+        VAR_CHECK(ret, EOK, SYSDB_GIDNUM,
+                  "Could not add attribute to changeset\n");
+    }
+
+    if(lock == DO_LOCK) {
+        ret = sysdb_attrs_add_string(attrs,
+                                     SYSDB_DISABLED,
+                                     "true");
+        VAR_CHECK(ret, EOK, SYSDB_DISABLED,
+                  "Could not add attribute to changeset\n");
+    }
+
+    if(lock == DO_UNLOCK) {
+        /* PAM code checks for 'false' value in SYSDB_DISABLED attribute */
+        ret = sysdb_attrs_add_string(attrs,
+                                     SYSDB_DISABLED,
+                                     "false");
+        VAR_CHECK(ret, EOK, SYSDB_DISABLED,
+                  "Could not add attribute to changeset\n");
+    }
+
+    *_attrs = attrs;
+    return EOK;
+}
+
+static void mod_user_attr_done(struct tevent_req *req);
+static void mod_user_cont(struct sync_ops_ctx *octx);
+
+static void mod_user(struct tevent_req *req)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(req,
+                                                         struct sync_ops_ctx);
+    struct tevent_req *subreq;
+    int ret;
+
+    octx->member_dn_fn = sysdb_user_dn;
+
+    ret = sysdb_transaction_recv(req, octx, &octx->handle);
+    if (ret != EOK) {
+        return transaction_done(octx, ret);
+    }
+
+    ret = usermod_build_attrs(octx,
+                              octx->data->gecos,
+                              octx->data->home,
+                              octx->data->shell,
+                              octx->data->uid,
+                              octx->data->gid,
+                              octx->data->lock,
+                              &octx->attrs);
+    if (ret != EOK) {
+        return transaction_done(octx, ret);
+    }
+
+    if (octx->attrs->num != 0) {
+        subreq = sysdb_set_user_attr_send(octx, octx->ev, octx->handle,
+                                          octx->data->domain, octx->data->name,
+                                          octx->attrs, SYSDB_MOD_REP);
+        if (!subreq) {
+            return transaction_done(octx, ENOMEM);
+        }
+        tevent_req_set_callback(subreq, mod_user_attr_done, octx);
+        return;
+    }
+
+    return mod_user_cont(octx);
+}
+
+static void mod_user_attr_done(struct tevent_req *subreq)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(subreq,
+                                                         struct sync_ops_ctx);
+    int ret;
+
+    ret = sysdb_set_user_attr_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        return transaction_done(octx, ret);
+    }
+
+    mod_user_cont(octx);
+}
+
+static void mod_user_cont(struct sync_ops_ctx *octx)
+{
+    if (octx->data->rmgroups != NULL) {
+        return remove_from_groups(octx);
+    }
+
+    if (octx->data->addgroups != NULL) {
+        return add_to_groups(octx);
+    }
+
+    return transaction_done(octx, EOK);
+}
+
+/*
+ * Add a group
+ */
+static void add_group_done(struct tevent_req *subreq);
+
+static void add_group(struct tevent_req *req)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(req,
+                                                         struct sync_ops_ctx);
+    struct tevent_req *subreq;
+    int ret;
+
+    ret = sysdb_transaction_recv(req, octx, &octx->handle);
+    if (ret != EOK) {
+        return transaction_done(octx, ret);
+    }
+
+    subreq = sysdb_add_group_send(octx, octx->ev, octx->handle,
+                                  octx->data->domain, octx->data->name,
+                                  octx->data->gid, NULL);
+    if (!subreq) {
+        transaction_done(octx, ENOMEM);
+    }
+    tevent_req_set_callback(subreq, add_group_done, octx);
+}
+
+static void add_group_done(struct tevent_req *subreq)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(subreq,
+                                                         struct sync_ops_ctx);
+    int ret;
+
+    ret = sysdb_add_group_recv(subreq);
+    talloc_zfree(subreq);
+
+    return transaction_done(octx, ret);
+}
+
+/*
+ * Delete a group
+ */
+static void group_del_done(struct tevent_req *subreq);
+
+static void group_del(struct tevent_req *req)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(req,
+                                                         struct sync_ops_ctx);
+    struct tevent_req *subreq;
+    struct ldb_dn *group_dn;
+    int ret;
+
+    ret = sysdb_transaction_recv(req, octx, &octx->handle);
+    if (ret != EOK) {
+        return transaction_done(octx, ret);
+    }
+
+    group_dn = sysdb_group_dn(octx->sysdb, octx,
+                              octx->data->domain->name, octx->data->name);
+    if (group_dn == NULL) {
+        DEBUG(1, ("Could not construct a group DN\n"));
+        return transaction_done(octx, ENOMEM);
+    }
+
+    subreq = sysdb_delete_entry_send(octx, octx->ev, octx->handle, group_dn, false);
+    if (!subreq)
+        return transaction_done(octx, ENOMEM);
+
+    tevent_req_set_callback(subreq, group_del_done, octx);
+}
+
+static void group_del_done(struct tevent_req *subreq)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(subreq,
+                                                         struct sync_ops_ctx);
+    int ret;
+
+    ret = sysdb_delete_entry_recv(subreq);
+
+    return transaction_done(octx, ret);
+}
+
+/*
+ * Modify a group
+ */
+static void mod_group_attr_done(struct tevent_req *req);
+static void mod_group_cont(struct sync_ops_ctx *octx);
+
+static void mod_group(struct tevent_req *req)
+{
+    struct sync_ops_ctx *octx;
+    struct tevent_req *subreq;
+    struct sysdb_attrs *attrs;
+    int ret;
+
+    octx = tevent_req_callback_data(req, struct sync_ops_ctx);
+    octx->member_dn_fn = sysdb_group_dn;
+
+    ret = sysdb_transaction_recv(req, octx, &octx->handle);
+    if (ret != EOK) {
+        return transaction_done(octx, ret);
+    }
+
+    if (octx->data->gid != 0) {
+        attrs = sysdb_new_attrs(NULL);
+        if (!attrs) {
+            transaction_done(octx, ENOMEM);
+        }
+        ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, octx->data->gid);
+        if (ret) {
+            transaction_done(octx, ret);
+        }
+
+        subreq = sysdb_set_group_attr_send(octx, octx->ev, octx->handle,
+                                           octx->data->domain, octx->data->name,
+                                           attrs, SYSDB_MOD_REP);
+        if (!subreq) {
+            return transaction_done(octx, ENOMEM);
+        }
+        tevent_req_set_callback(subreq, mod_group_attr_done, octx);
+        return;
+    }
+
+    return mod_group_cont(octx);
+}
+
+static void mod_group_attr_done(struct tevent_req *subreq)
+{
+    struct sync_ops_ctx *octx = tevent_req_callback_data(subreq,
+                                                         struct sync_ops_ctx);
+    int ret;
+
+    ret = sysdb_set_group_attr_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        return transaction_done(octx, ret);
+    }
+
+    mod_group_cont(octx);
+}
+
+static void mod_group_cont(struct sync_ops_ctx *octx)
+{
+    if (octx->data->rmgroups != NULL) {
+        return remove_from_groups(octx);
+    }
+
+    if (octx->data->addgroups != NULL) {
+        return add_to_groups(octx);
+    }
+
+    return transaction_done(octx, EOK);
+}
+
+/*
+ * Default values for add operations
+ */
+int useradd_defaults(TALLOC_CTX *mem_ctx,
+                     struct confdb_ctx *confdb,
+                     struct ops_ctx *data,
+                     const char *gecos,
+                     const char *homedir,
+                     const char *shell)
+{
+    int ret;
+    char *basedir = NULL;
+    char *dfl_shell = NULL;
+
+    data->gecos = talloc_strdup(mem_ctx, gecos ? gecos : data->name);
+    if (!data->gecos) {
+        return ENOMEM;
+    }
+
+    if (homedir) {
+        data->home = talloc_strdup(data, homedir);
+    } else {
+        ret = confdb_get_string(confdb, mem_ctx,
+                                CONFDB_DFL_SECTION, DFL_BASEDIR_ATTR,
+                                DFL_BASEDIR_VAL, &basedir);
+        if (ret != EOK) {
+            goto done;
+        }
+        data->home = talloc_asprintf(mem_ctx, "%s/%s", basedir, data->name);
+        if (!data->home) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+    if (!data->home) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    if (!shell) {
+        ret = confdb_get_string(confdb, mem_ctx,
+                                CONFDB_DFL_SECTION, DFL_SHELL_ATTR,
+                                DFL_SHELL_VAL, &dfl_shell);
+        if (ret != EOK) {
+            goto done;
+        }
+        shell = dfl_shell;
+    }
+    data->shell = talloc_strdup(mem_ctx, shell);
+    if (!data->shell) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = EOK;
+done:
+    talloc_free(dfl_shell);
+    talloc_free(basedir);
+    return ret;
+}
+
+/*
+ * Public interface for adding users
+ */
+int useradd(TALLOC_CTX *mem_ctx, struct ops_ctx *data)
+{
+    return synchronous_transaction(mem_ctx, add_user, data);
+}
+
+/*
+ * Public interface for deleting users
+ */
+int userdel(TALLOC_CTX *mem_ctx, struct ops_ctx *data)
+{
+    return synchronous_transaction(mem_ctx, user_del, data);
+}
+
+/*
+ * Public interface for modifying users
+ */
+int usermod(TALLOC_CTX *mem_ctx, struct ops_ctx *data)
+{
+    return synchronous_transaction(mem_ctx, mod_user, data);
+}
+
+/*
+ * Public interface for adding groups
+ */
+int groupadd(TALLOC_CTX *mem_ctx, struct ops_ctx *data)
+{
+    return synchronous_transaction(mem_ctx, add_group, data);
+}
+
+/*
+ * Public interface for deleting groups
+ */
+int groupdel(TALLOC_CTX *mem_ctx, struct ops_ctx *data)
+{
+    return synchronous_transaction(mem_ctx, group_del, data);
+}
+
+/*
+ * Public interface for modifying groups
+ */
+int groupmod(TALLOC_CTX *mem_ctx, struct ops_ctx *data)
+{
+    return synchronous_transaction(mem_ctx, mod_group, data);
+}
+
diff --git a/server/tools/common/sss_sync_ops.h b/server/tools/common/sss_sync_ops.h
new file mode 100644
index 0000000..4107986
--- /dev/null
+++ b/server/tools/common/sss_sync_ops.h
@@ -0,0 +1,68 @@
+/*
+ * Authors:
+ *   Jakub Hrozek <jhrozek@redhat.com>
+ *
+ * Copyright (C) 2009  Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 only
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SSS_OPS_H__
+#define __SSS_OPS_H__
+
+#include <stdbool.h>
+
+#define DO_LOCK     1
+#define DO_UNLOCK   2
+
+struct ops_ctx {
+    struct sss_domain_info *domain;
+
+    char *name;
+    uid_t uid;
+    gid_t gid;
+    char *gecos;
+    char *home;
+    char *shell;
+    int   lock;
+
+    char **addgroups;
+    char **rmgroups;
+};
+
+/* connect to confdb and sysdb using ev event context */
+int setup_db(TALLOC_CTX *mem_ctx,
+             struct tevent_context *ev,
+             struct confdb_ctx **_confdb,
+             struct sysdb_ctx **_sysdb);
+
+/* default values for add operations */
+int useradd_defaults(TALLOC_CTX *mem_ctx,
+                     struct confdb_ctx *confdb,
+                     struct ops_ctx *data,
+                     const char *gecos,
+                     const char *homedir,
+                     const char *shell);
+
+/* synchronous operations */
+int useradd(TALLOC_CTX *mem_ctx, struct ops_ctx *data);
+int userdel(TALLOC_CTX *mem_ctx, struct ops_ctx *data);
+int usermod(TALLOC_CTX *mem_ctx, struct ops_ctx *data);
+
+int groupadd(TALLOC_CTX *mem_ctx, struct ops_ctx *data);
+int groupdel(TALLOC_CTX *mem_ctx, struct ops_ctx *data);
+int groupmod(TALLOC_CTX *mem_ctx, struct ops_ctx *data);
+#endif /* __SSS_OPS_H__ */
+
diff --git a/server/tools/common/tools_util.c b/server/tools/common/tools_util.c
new file mode 100644
index 0000000..df9af9f
--- /dev/null
+++ b/server/tools/common/tools_util.c
@@ -0,0 +1,336 @@
+/*
+   SSSD
+
+   tools_utils.c
+
+   Copyright (C) Jakub Hrozek <jhrozek@redhat.com>        2009
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <tevent.h>
+#include <popt.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "db/sysdb.h"
+#include "tools/common/tools_util.h"
+
+/*
+ * Returns:
+ * 0 = yes, local domain proxying to files
+ * -1 = no, other type of domain
+ * > 0 = error code
+ */
+static int is_domain_local_legacy(struct tools_ctx *ctx, struct sss_domain_info *dom)
+{
+    char *libname = NULL;
+    char *conf_path = NULL;
+    int ret = -1;
+
+    /* Is there a better way to find out? Having LEGACYLOCAL as reserved would help */
+    conf_path = talloc_asprintf(ctx, "config/domains/%s", dom->name);
+    if (conf_path == NULL ) {
+        return ENOMEM;
+    }
+
+    ret = confdb_get_string(ctx->confdb, ctx, conf_path,
+                            "libName", NULL, &libname);
+    if (ret != EOK) {
+        talloc_free(conf_path);
+        return ret;
+    }
+    if (libname == NULL) {
+        talloc_free(conf_path);
+        return -1;
+    }
+
+    if (strcasecmp(libname, "files") == 0) {
+        talloc_free(conf_path);
+        talloc_free(libname);
+        return EOK;
+    }
+
+    talloc_free(conf_path);
+    talloc_free(libname);
+    return -1;
+}
+
+enum id_domain get_domain_type(struct tools_ctx *ctx,
+                               struct sss_domain_info *dom)
+{
+    if (dom == NULL) {
+        return ID_OUTSIDE;
+    }
+
+    if (strcasecmp(dom->provider, "local") == 0) {
+        return ID_IN_LOCAL;
+    } else if (strcasecmp(dom->provider, "files") == 0 ||
+               is_domain_local_legacy(ctx, dom) == 0) {
+        return ID_IN_LEGACY_LOCAL;
+    }
+
+    return ID_IN_OTHER;
+}
+
+struct sss_domain_info *get_local_domain(struct sss_domain_info *domains)
+{
+    struct sss_domain_info *dom = NULL;
+
+    /* No ID specified, find LOCAL */
+    for (dom = domains; dom; dom = dom->next) {
+        if (strcasecmp(dom->provider, "local") == 0) {
+            break;
+        }
+    }
+
+    return dom;
+}
+
+int get_domain_by_id(struct sss_domain_info *domains,
+                     uint32_t id,
+                     struct sss_domain_info **_dom)
+{
+    struct sss_domain_info *dom = NULL;
+    int ret = EOK;
+
+    if (id) {
+        for (dom = domains; dom; dom = dom->next) {
+            if (id >= dom->id_min &&
+                (dom->id_max == 0 || id <= dom->id_max)) {
+                break;
+            }
+        }
+    }
+
+    if (dom == NULL && id == 0) {
+        dom = get_local_domain(domains);
+        if (dom == NULL) {
+            DEBUG(1, ("Cannot find local domain info\n"));
+            ret = ENOENT;
+        }
+    }
+
+    *_dom = dom;
+    return ret;
+}
+
+int domain_lookup(struct ops_ctx *ops, struct sss_domain_info *domains)
+{
+    int ret;
+    struct sss_domain_info *dom = NULL;
+
+    if (!ops) {
+        return EIO;
+    }
+
+    ret = get_domain_by_id(domains, ops->uid, &dom);
+    if (ret != EOK) {
+        return ret;
+    }
+    /* If found domain by id check conflict against domain in ops */
+    if (dom && ops->domain && ops->domain != dom) {
+        return EINVAL;
+    }
+    if (ops->domain == NULL && dom) {
+        ops->domain = dom;
+    }
+
+    return EOK;
+}
+
+/*
+ * Print poptUsage as well as our error message
+ */
+void usage(poptContext pc, const char *error)
+{
+    poptPrintUsage(pc, stderr, 0);
+    if (error) fprintf(stderr, "%s", error);
+}
+
+int parse_groups(TALLOC_CTX *mem_ctx, const char *optstr, char ***_out)
+{
+    char **out;
+    char *orig, *n, *o;
+    char delim = ',';
+    unsigned int tokens = 1;
+    int i;
+
+    orig = talloc_strdup(mem_ctx, optstr);
+    if (!orig) return ENOMEM;
+
+    n = orig;
+    tokens = 1;
+    while ((n = strchr(n, delim))) {
+        n++;
+        tokens++;
+    }
+
+    out = talloc_array(mem_ctx, char *, tokens+1);
+    if (!out) {
+        talloc_free(orig);
+        return ENOMEM;
+    }
+
+    n = o = orig;
+    for (i = 0; i < tokens; i++) {
+        o = n;
+        n = strchr(n, delim);
+        if (!n) {
+            break;
+        }
+        *n = '\0';
+        n++;
+        out[i] = talloc_strdup(out, o);
+    }
+    out[tokens-1] = talloc_strdup(out, o);
+    out[tokens] = NULL;
+
+    talloc_free(orig);
+    *_out = out;
+    return EOK;
+}
+
+int parse_name_domain(struct tools_ctx *tctx,
+                      const char *fullname)
+{
+    int ret;
+    char *domain = NULL;
+    struct sss_domain_info *dom;
+
+    ret = sss_parse_name(tctx, tctx->snctx, fullname, &domain, &tctx->octx->name);
+    if (ret != EOK) {
+        DEBUG(0, ("Cannot parse full name\n"));
+        return ret;
+    }
+    DEBUG(5, ("Parsed username: %s\n", tctx->octx->name));
+
+    if (domain) {
+        DEBUG(5, ("Parsed domain: %s\n", domain));
+
+        /* Got string domain name, find corresponding sss_domain_info */
+        for (dom = tctx->domains; dom; dom = dom->next) {
+            if (strcasecmp(dom->name, domain) == 0) {
+                DEBUG(6, ("Found sss_domain_info for given domain name\n"));
+                tctx->octx->domain = dom;
+                break;
+            }
+        }
+        if (tctx->octx->domain == NULL) {
+            DEBUG(0, ("Invalid domain %s specified in FQDN\n", domain));
+            return EINVAL;
+        }
+    }
+
+    return EOK;
+}
+
+int run_command(const char *command)
+{
+    int ret;
+
+    ret = system(command);
+    if (ret != EXIT_SUCCESS) {
+        if (ret == -1) {
+            DEBUG(1, ("system(3) failed\n"));
+        } else {
+            DEBUG(1, ("Could not exec '%s', return code: %d\n",
+                      command, WEXITSTATUS(ret)));
+        }
+    }
+
+    return ret;
+}
+
+int set_locale(void)
+{
+    char *c;
+
+    c = setlocale(LC_ALL, "");
+    if (c == NULL) {
+        return EIO;
+    }
+
+    errno = 0;
+    c = bindtextdomain(PACKAGE, LOCALEDIR);
+    if (c == NULL) {
+        return errno;
+    }
+
+    errno = 0;
+    c = textdomain(PACKAGE);
+    if (c == NULL) {
+        return errno;
+    }
+
+    return EOK;
+}
+
+int init_sss_tools(struct tools_ctx **_tctx)
+{
+    int ret;
+    struct tools_ctx *tctx;
+    struct ops_ctx *octx;
+
+    tctx = talloc_zero(NULL, struct tools_ctx);
+    if (tctx == NULL) {
+        DEBUG(1, ("Could not allocate memory for tools context"));
+        return ENOMEM;
+    }
+
+    tctx->ev = tevent_context_init(tctx);
+    if (tctx->ev == NULL) {
+        DEBUG(1, ("Could not create event context"));
+        return EIO;
+    }
+
+    /* Connect to the database */
+    ret = setup_db(tctx, tctx->ev, &tctx->confdb, &tctx->sysdb);
+    if (ret != EOK) {
+        DEBUG(1, ("Could not set up database\n"));
+        goto fini;
+    }
+
+    ret = confdb_get_domains(tctx->confdb, tctx, &tctx->domains);
+    if (ret != EOK) {
+        DEBUG(1, ("Could not get domains"));
+        goto fini;
+    }
+
+    ret = sss_names_init(tctx, tctx->confdb, &tctx->snctx);
+    if (ret != EOK) {
+        DEBUG(1, ("Could not set up parsing\n"));
+        goto fini;
+    }
+
+    octx = talloc_zero(tctx, struct ops_ctx);
+    if (octx == NULL) {
+        DEBUG(1, ("Could not allocate memory for data context\n"));
+        ERROR("Out of memory\n");
+        ret = ENOMEM;
+        goto fini;
+    }
+    tctx->octx = octx;
+
+    *_tctx = tctx;
+    ret = EOK;
+fini:
+    return ret;
+}
+
diff --git a/server/tools/common/tools_util.h b/server/tools/common/tools_util.h
new file mode 100644
index 0000000..b6c413d
--- /dev/null
+++ b/server/tools/common/tools_util.h
@@ -0,0 +1,81 @@
+#ifndef __TOOLS_UTIL_H__
+#define __TOOLS_UTIL_H__
+
+#include "util/sssd-i18n.h"
+#include "tools/common/sss_sync_ops.h"
+
+#define UID_NOT_SET 0
+#define GID_NOT_SET 0
+
+#define APPEND_PARAM(str, param, val) do { \
+    if (val) { \
+        str = talloc_asprintf_append(str, param, val); \
+        if (str == NULL) { \
+            return ENOMEM; \
+        } \
+    } \
+} while(0)
+
+#define APPEND_STRING(str, val) do { \
+    str = talloc_asprintf_append(str, "%s ", val); \
+    if (str == NULL) { \
+        return ENOMEM; \
+    } \
+} while(0)
+
+#define CHECK_ROOT(val, prg_name) do { \
+    val = getuid(); \
+    if (val != 0) { \
+        DEBUG(1, ("Running under %d, must be root\n", val)); \
+        ERROR("%s must be run as root\n", prg_name); \
+        val = EXIT_FAILURE; \
+        goto fini; \
+    } \
+} while(0)
+
+enum id_domain {
+    ID_IN_LOCAL = 0,
+    ID_IN_LEGACY_LOCAL,
+    ID_IN_OTHER,
+    ID_OUTSIDE,
+    ID_ERROR
+};
+
+struct tools_ctx {
+    struct tevent_context *ev;
+    struct confdb_ctx *confdb;
+    struct sysdb_ctx *sysdb;
+
+    struct sss_names_ctx *snctx;
+    struct sss_domain_info *domains;
+
+    struct ops_ctx *octx;
+    int error;
+};
+
+int init_sss_tools(struct tools_ctx **_tctx);
+
+void usage(poptContext pc, const char *error);
+
+int parse_groups(TALLOC_CTX *mem_ctx, const char *optstr, char ***_out);
+
+struct sss_domain_info *get_local_domain(struct sss_domain_info *domains);
+
+enum id_domain get_domain_type(struct tools_ctx *ctx,
+                               struct sss_domain_info *dom);
+
+int get_domain_by_id(struct sss_domain_info *domains,
+                     uint32_t id,
+                     struct sss_domain_info **_dom);
+
+int domain_lookup(struct ops_ctx *ops,
+                  struct sss_domain_info *domains);
+
+int run_command(const char *command);
+
+int set_locale(void);
+
+int parse_name_domain(struct tools_ctx *tctx,
+                      const char *fullname);
+
+#endif  /* __TOOLS_UTIL_H__ */
diff --git a/server/tools/sss_groupadd.c b/server/tools/sss_groupadd.c
index ab313fb..c601e5c 100644
--- a/server/tools/sss_groupadd.c
+++ b/server/tools/sss_groupadd.c
@@ -28,7 +28,7 @@
 
 #include "util/util.h"
 #include "db/sysdb.h"
-#include "tools/tools_util.h"
+#include "tools/common/tools_util.h"
 
 #ifndef GROUPADD
 #define GROUPADD SHADOW_UTILS_PATH"/groupadd "
@@ -42,72 +42,6 @@
 #define GROUPADD_GROUPNAME "%s "
 #endif
 
-static void add_group_req_done(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-
-    data->error = sysdb_transaction_commit_recv(req);
-    data->done = true;
-}
-
-static void add_group_terminate(struct ops_ctx *data, int error)
-{
-    struct tevent_req *req;
-
-    if (error != EOK) {
-        goto fail;
-    }
-
-    req = sysdb_transaction_commit_send(data, data->ev, data->handle);
-    if (!req) {
-        error = ENOMEM;
-        goto fail;
-    }
-    tevent_req_set_callback(req, add_group_req_done, data);
-
-    return;
-
-fail:
-    /* free transaction */
-    talloc_zfree(data->handle);
-
-    data->error = error;
-    data->done = true;
-}
-
-static void add_group_done(struct tevent_req *subreq);
-
-static void add_group(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-    struct tevent_req *subreq;
-    int ret;
-
-    ret = sysdb_transaction_recv(req, data, &data->handle);
-    if (ret != EOK) {
-        return add_group_terminate(data, ret);
-    }
-
-    subreq = sysdb_add_group_send(data, data->ev, data->handle,
-                                  data->domain, data->name,
-                                  data->gid, NULL);
-    if (!subreq) {
-        add_group_terminate(data, ENOMEM);
-    }
-    tevent_req_set_callback(subreq, add_group_done, data);
-}
-
-static void add_group_done(struct tevent_req *subreq)
-{
-    struct ops_ctx *data = tevent_req_callback_data(subreq, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_add_group_recv(subreq);
-    talloc_zfree(subreq);
-
-    return add_group_terminate(data, ret);
-}
-
 static int groupadd_legacy(struct ops_ctx *ctx)
 {
     int ret = EOK;
@@ -141,8 +75,7 @@ int main(int argc, const char **argv)
         POPT_TABLEEND
     };
     poptContext pc = NULL;
-    struct tevent_req *req;
-    struct ops_ctx *data = NULL;
+    struct tools_ctx *tctx = NULL;
     int ret = EXIT_SUCCESS;
     const char *pc_groupname = NULL;
 
@@ -177,7 +110,7 @@ int main(int argc, const char **argv)
 
     CHECK_ROOT(ret, debug_prg_name);
 
-    ret = init_sss_tools(&data);
+    ret = init_sss_tools(&tctx);
     if(ret != EOK) {
         DEBUG(1, ("init_sss_tools failed (%d): %s\n", ret, strerror(ret)));
         ERROR("Error initializing the tools\n");
@@ -185,20 +118,20 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    ret = parse_name_domain(data, pc_groupname);
+    ret = parse_name_domain(tctx, pc_groupname);
     if (ret != EOK) {
         ret = EXIT_FAILURE;
         goto fini;
     }
 
-    data->gid = pc_gid;
+    tctx->octx->gid = pc_gid;
 
-    ret = domain_lookup(data);
+    ret = domain_lookup(tctx->octx,tctx->domains);
     if (ret) {
         switch (ret) {
             case EINVAL:
                 ERROR("Selected domain %s conflicts with selected GID %llu\n",
-                        data->domain->name, (unsigned long long int) data->uid);
+                        tctx->octx->domain->name, (unsigned long long int) tctx->octx->uid);
                 break;
 
             default:
@@ -209,13 +142,13 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    ret = get_domain_type(data->ctx, data->domain);
+    ret = get_domain_type(tctx, tctx->octx->domain);
     switch (ret) {
         case ID_IN_LOCAL:
             break;
 
         case ID_IN_LEGACY_LOCAL:
-            ret = groupadd_legacy(data);
+            ret = groupadd_legacy(tctx->octx);
             if(ret != EOK) {
                 ERROR("Cannot add group to domain using the legacy tools\n");
             }
@@ -227,7 +160,7 @@ int main(int argc, const char **argv)
             goto fini;
 
         case ID_IN_OTHER:
-            DEBUG(1, ("Cannot add group to domain %s\n", data->domain->name));
+            DEBUG(1, ("Cannot add group to domain %s\n", tctx->octx->domain->name));
             ERROR("Unsupported domain type");
             ret = EXIT_FAILURE;
             goto fini;
@@ -239,22 +172,8 @@ int main(int argc, const char **argv)
             goto fini;
     }
 
-    /* add_group */
-    req = sysdb_transaction_send(data, data->ev, data->ctx->sysdb);
-    if (!req) {
-        DEBUG(1, ("Could not start transaction (%d)[%s]\n", ret, strerror(ret)));
-        ERROR("Transaction error. Could not add group.\n");
-        ret = EXIT_FAILURE;
-        goto fini;
-    }
-    tevent_req_set_callback(req, add_group, data);
-
-    while (!data->done) {
-        tevent_loop_once(data->ev);
-    }
-
-    if (data->error) {
-        ret = data->error;
+    ret = groupadd(tctx, tctx->octx);
+    if (ret) {
         switch (ret) {
             case EEXIST:
                 ERROR("A group with the same name or GID already exists\n");
@@ -271,7 +190,7 @@ int main(int argc, const char **argv)
 
     ret = EXIT_SUCCESS;
 fini:
-    talloc_free(data);
+    talloc_free(tctx);
     poptFreeContext(pc);
     exit(ret);
 }
diff --git a/server/tools/sss_groupdel.c b/server/tools/sss_groupdel.c
index 17c1bfd..efccb88 100644
--- a/server/tools/sss_groupdel.c
+++ b/server/tools/sss_groupdel.c
@@ -28,7 +28,7 @@
 
 #include "db/sysdb.h"
 #include "util/util.h"
-#include "tools/tools_util.h"
+#include "tools/common/tools_util.h"
 
 #ifndef GROUPDEL
 #define GROUPDEL SHADOW_UTILS_PATH"/groupdel "
@@ -39,79 +39,6 @@
 #endif
 
 
-static void groupdel_req_done(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-
-    data->error = sysdb_transaction_commit_recv(req);
-    data->done = true;
-}
-
-/* sysdb callback */
-static void groupdel_done(void *pvt, int error, struct ldb_result *ignore)
-{
-    struct ops_ctx *data = talloc_get_type(pvt, struct ops_ctx);
-    struct tevent_req *req;
-
-    if (error != EOK) {
-        goto fail;
-    }
-
-    req = sysdb_transaction_commit_send(data, data->ev, data->handle);
-    if (!req) {
-        error = ENOMEM;
-        goto fail;
-    }
-    tevent_req_set_callback(req, groupdel_req_done, data);
-
-    return;
-
-fail:
-    /* free transaction */
-    talloc_zfree(data->handle);
-
-    data->error = error;
-    data->done = true;
-}
-
-static void group_del_done(struct tevent_req *subreq);
-
-static void group_del(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-    struct tevent_req *subreq;
-    struct ldb_dn *group_dn;
-    int ret;
-
-    ret = sysdb_transaction_recv(req, data, &data->handle);
-    if (ret != EOK) {
-        return groupdel_done(data, ret, NULL);
-    }
-
-    group_dn = sysdb_group_dn(data->ctx->sysdb, data,
-                              data->domain->name, data->name);
-    if (group_dn == NULL) {
-        DEBUG(1, ("Could not construct a group DN\n"));
-        return groupdel_done(data, ENOMEM, NULL);
-    }
-
-    subreq = sysdb_delete_entry_send(data, data->ev, data->handle, group_dn, false);
-    if (!subreq)
-        return groupdel_done(data, ENOMEM, NULL);
-
-    tevent_req_set_callback(subreq, group_del_done, data);
-}
-
-static void group_del_done(struct tevent_req *subreq)
-{
-    struct ops_ctx *data = tevent_req_callback_data(subreq, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_delete_entry_recv(subreq);
-
-    return groupdel_done(data, ret, NULL);
-}
-
 static int groupdel_legacy(struct ops_ctx *ctx)
 {
     int ret = EOK;
@@ -130,11 +57,10 @@ int main(int argc, const char **argv)
 {
     int ret = EXIT_SUCCESS;
     int pc_debug = 0;
-    struct ops_ctx *data = NULL;
-    struct tevent_req *req;
     struct group *grp_info;
     const char *pc_groupname = NULL;
     enum id_domain domain_type;
+    struct tools_ctx *tctx = NULL;
 
     poptContext pc = NULL;
     struct poptOption long_options[] = {
@@ -174,7 +100,7 @@ int main(int argc, const char **argv)
 
     CHECK_ROOT(ret, debug_prg_name);
 
-    ret = init_sss_tools(&data);
+    ret = init_sss_tools(&tctx);
     if(ret != EOK) {
         DEBUG(1, ("init_sss_tools failed (%d): %s\n", ret, strerror(ret)));
         ERROR("Error initializing the tools\n");
@@ -182,24 +108,24 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    ret = parse_name_domain(data, pc_groupname);
+    ret = parse_name_domain(tctx, pc_groupname);
     if (ret != EOK) {
         ret = EXIT_FAILURE;
         goto fini;
     }
 
     /* arguments processed, go on to actual work */
-    grp_info = getgrnam(data->name);
+    grp_info = getgrnam(tctx->octx->name);
     if (grp_info) {
-        data->gid = grp_info->gr_gid;
+        tctx->octx->gid = grp_info->gr_gid;
     }
 
-    ret = domain_lookup(data);
+    ret = domain_lookup(tctx->octx,tctx->domains);
     if (ret) {
         switch (ret) {
             case EINVAL:
                 ERROR("Selected domain %s conflicts with selected GID %llu\n",
-                        data->domain->name, (unsigned long long int) data->uid);
+                        tctx->octx->domain->name, (unsigned long long int) tctx->octx->uid);
                 break;
 
             default:
@@ -210,13 +136,13 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    domain_type = get_domain_type(data->ctx, data->domain);
+    domain_type = get_domain_type(tctx, tctx->octx->domain);
     switch (domain_type) {
         case ID_IN_LOCAL:
             break;
 
         case ID_IN_LEGACY_LOCAL:
-            ret = groupdel_legacy(data);
+            ret = groupdel_legacy(tctx->octx);
             if(ret != EOK) {
                 ERROR("Cannot delete group from domain using the legacy tools\n");
                 ret = EXIT_FAILURE;
@@ -230,7 +156,7 @@ int main(int argc, const char **argv)
             goto fini;
 
         case ID_IN_OTHER:
-            DEBUG(1, ("Cannot remove group from domain %s\n", data->domain->name));
+            DEBUG(1, ("Cannot remove group from domain %s\n", tctx->octx->domain->name));
             ERROR("Unsupported domain type\n");
             ret = EXIT_FAILURE;
             goto fini;
@@ -243,21 +169,8 @@ int main(int argc, const char **argv)
     }
 
     /* groupdel */
-    req = sysdb_transaction_send(data, data->ev, data->ctx->sysdb);
-    if (!req) {
-        DEBUG(1, ("Could not start transaction (%d)[%s]\n", ret, strerror(ret)));
-        ERROR("Transaction error. Could not remove group.\n");
-        ret = EXIT_FAILURE;
-        goto fini;
-    }
-    tevent_req_set_callback(req, group_del, data);
-
-    while (!data->done) {
-        tevent_loop_once(data->ev);
-    }
-
-    if (data->error) {
-        ret = data->error;
+    ret = groupdel(tctx, tctx->octx);
+    if (ret) {
         DEBUG(1, ("sysdb operation failed (%d)[%s]\n", ret, strerror(ret)));
         switch (ret) {
             case ENOENT:
@@ -281,7 +194,7 @@ int main(int argc, const char **argv)
     ret = EXIT_SUCCESS;
 
 fini:
-    talloc_free(data);
+    talloc_free(tctx);
     poptFreeContext(pc);
     exit(ret);
 }
diff --git a/server/tools/sss_groupmod.c b/server/tools/sss_groupmod.c
index 8dfd157..02a3a61 100644
--- a/server/tools/sss_groupmod.c
+++ b/server/tools/sss_groupmod.c
@@ -29,7 +29,7 @@
 
 #include "util/util.h"
 #include "db/sysdb.h"
-#include "tools/tools_util.h"
+#include "tools/common/tools_util.h"
 
 #ifndef GROUPMOD
 #define GROUPMOD SHADOW_UTILS_PATH"/groupmod "
@@ -43,221 +43,6 @@
 #define GROUPMOD_GROUPNAME "%s "
 #endif
 
-static void mod_group_req_done(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-
-    data->error = sysdb_transaction_commit_recv(req);
-    data->done = true;
-}
-
-static void mod_group_done(struct ops_ctx *data, int error)
-{
-    struct tevent_req *req;
-
-    if (error != EOK) {
-        goto fail;
-    }
-
-    req = sysdb_transaction_commit_send(data, data->ev, data->handle);
-    if (!req) {
-        error = ENOMEM;
-        goto fail;
-    }
-    tevent_req_set_callback(req, mod_group_req_done, data);
-
-    return;
-
-fail:
-    /* free transaction */
-    talloc_zfree(data->handle);
-
-    data->error = error;
-    data->done = true;
-}
-
-static void mod_group_attr_done(struct tevent_req *req);
-static void mod_group_cont(struct ops_ctx *data);
-static void remove_from_groups(struct ops_ctx *data);
-static void remove_from_groups_done(struct tevent_req *req);
-static void add_to_groups(struct ops_ctx *data);
-static void add_to_groups_done(struct tevent_req *req);
-
-static void mod_group(struct tevent_req *req)
-{
-    struct ops_ctx *data;
-    struct tevent_req *subreq;
-    struct sysdb_attrs *attrs;
-    int ret;
-
-    data = tevent_req_callback_data(req, struct ops_ctx);
-
-    ret = sysdb_transaction_recv(req, data, &data->handle);
-    if (ret != EOK) {
-        return mod_group_done(data, ret);
-    }
-    talloc_zfree(req);
-
-    if (data->gid != 0) {
-        attrs = sysdb_new_attrs(data);
-        if (!attrs) {
-            mod_group_done(data, ENOMEM);
-        }
-        ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, data->gid);
-        if (ret) {
-            mod_group_done(data, ret);
-        }
-
-        subreq = sysdb_set_group_attr_send(data, data->ev, data->handle,
-                                           data->domain, data->name,
-                                           attrs, SYSDB_MOD_REP);
-        if (!subreq) {
-            return mod_group_done(data, ENOMEM);
-        }
-        tevent_req_set_callback(subreq, mod_group_attr_done, data);
-        return;
-    }
-
-    return mod_group_cont(data);
-}
-
-static void mod_group_attr_done(struct tevent_req *subreq)
-{
-    struct ops_ctx *data = tevent_req_callback_data(subreq, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_set_group_attr_recv(subreq);
-    talloc_zfree(subreq);
-    if (ret != EOK) {
-        return mod_group_done(data, ret);
-    }
-
-    mod_group_cont(data);
-}
-
-static void mod_group_cont(struct ops_ctx *data)
-{
-    if (data->rmgroups != NULL) {
-        return remove_from_groups(data);
-    }
-
-    if (data->addgroups != NULL) {
-        return add_to_groups(data);
-    }
-
-    return mod_group_done(data, EOK);
-}
-
-static void remove_from_groups(struct ops_ctx *data)
-{
-    struct ldb_dn *parent_dn;
-    struct ldb_dn *member_dn;
-    struct tevent_req *req;
-
-    parent_dn = sysdb_group_dn(data->ctx->sysdb, data,
-                               data->domain->name, data->name);
-    if (!parent_dn) {
-        return mod_group_done(data, ENOMEM);
-    }
-
-    member_dn = sysdb_group_dn(data->ctx->sysdb, data,
-                               data->domain->name,
-                               data->rmgroups[data->cur]);
-    if (!member_dn) {
-        return mod_group_done(data, ENOMEM);
-    }
-
-    req = sysdb_mod_group_member_send(data,
-                                      data->ev,
-                                      data->handle,
-                                      member_dn,
-                                      parent_dn,
-                                      LDB_FLAG_MOD_DELETE);
-    if (!req) {
-        return mod_group_done(data, ENOMEM);
-    }
-    tevent_req_set_callback(req, remove_from_groups_done, data);
-}
-
-static void remove_from_groups_done(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_mod_group_member_recv(req);
-    if (ret) {
-        return mod_group_done(data, ret);
-    }
-    talloc_zfree(req);
-
-    /* go on to next group */
-    data->cur++;
-
-    /* check if we added all of them */
-    if (data->rmgroups[data->cur] == NULL) {
-        data->cur = 0;
-        if (data->addgroups != NULL) {
-            return remove_from_groups(data);
-        }
-        return mod_group_done(data, EOK);
-    }
-
-    return remove_from_groups(data);
-}
-
-static void add_to_groups(struct ops_ctx *data)
-{
-    struct ldb_dn *parent_dn;
-    struct ldb_dn *member_dn;
-    struct tevent_req *req;
-
-    parent_dn = sysdb_group_dn(data->ctx->sysdb, data,
-                               data->domain->name, data->name);
-    if (!parent_dn) {
-        return mod_group_done(data, ENOMEM);
-    }
-
-    member_dn = sysdb_group_dn(data->ctx->sysdb, data,
-                               data->domain->name,
-                               data->addgroups[data->cur]);
-    if (!member_dn) {
-        return mod_group_done(data, ENOMEM);
-    }
-
-    req = sysdb_mod_group_member_send(data,
-                                      data->ev,
-                                      data->handle,
-                                      member_dn,
-                                      parent_dn,
-                                      LDB_FLAG_MOD_ADD);
-    if (!req) {
-        return mod_group_done(data, ENOMEM);
-    }
-    tevent_req_set_callback(req, add_to_groups_done, data);
-}
-
-static void add_to_groups_done(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_mod_group_member_recv(req);
-    if (ret) {
-        return mod_group_done(data, ret);
-    }
-    talloc_zfree(req);
-
-    /* go on to next group */
-    data->cur++;
-
-    /* check if we added all of them */
-    if (data->addgroups[data->cur] == NULL) {
-        return mod_group_done(data, EOK);
-    }
-
-    return add_to_groups(data);
-}
-
 static int groupmod_legacy(struct tools_ctx *tools_ctx,
                            struct ops_ctx *ctx,
                            struct sss_domain_info *old_domain)
@@ -275,7 +60,7 @@ static int groupmod_legacy(struct tools_ctx *tools_ctx,
     }
 
     if (ctx->gid) {
-        ret = get_domain_by_id(tools_ctx, ctx->gid, &dom);
+        ret = get_domain_by_id(tools_ctx->domains, ctx->gid, &dom);
         if (ret != EOK) {
             ERROR("Cannot get domain info\n");
             talloc_free(command);
@@ -315,8 +100,7 @@ int main(int argc, const char **argv)
         POPT_TABLEEND
     };
     poptContext pc = NULL;
-    struct ops_ctx *data = NULL;
-    struct tevent_req *req;
+    struct tools_ctx *tctx = NULL;
     char *addgroups = NULL, *rmgroups = NULL;
     int ret;
     struct group *grp_info;
@@ -372,7 +156,7 @@ int main(int argc, const char **argv)
 
     CHECK_ROOT(ret, debug_prg_name);
 
-    ret = init_sss_tools(&data);
+    ret = init_sss_tools(&tctx);
     if (ret != EOK) {
         DEBUG(1, ("init_sss_tools failed (%d): %s\n", ret, strerror(ret)));
         ERROR("Error initializing the tools\n");
@@ -380,18 +164,16 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    ret = parse_name_domain(data, pc_groupname);
+    ret = parse_name_domain(tctx, pc_groupname);
     if (ret != EOK) {
         ret = EXIT_FAILURE;
         goto fini;
     }
 
-    data->gid = pc_gid;
+    tctx->octx->gid = pc_gid;
 
     if (addgroups) {
-        ret = parse_groups(data,
-                addgroups,
-                &data->addgroups);
+        ret = parse_groups(tctx, addgroups, &tctx->octx->addgroups);
         if (ret != EOK) {
             DEBUG(1, ("Cannot parse groups to add the group to\n"));
             ERROR("Internal error while parsing parameters\n");
@@ -400,9 +182,7 @@ int main(int argc, const char **argv)
     }
 
     if (rmgroups) {
-        ret = parse_groups(data,
-                rmgroups,
-                &data->rmgroups);
+        ret = parse_groups(tctx, rmgroups, &tctx->octx->rmgroups);
         if (ret != EOK) {
             DEBUG(1, ("Cannot parse groups to remove the group from\n"));
             ERROR("Internal error while parsing parameters\n");
@@ -411,17 +191,17 @@ int main(int argc, const char **argv)
     }
 
     /* arguments processed, go on to actual work */
-    grp_info = getgrnam(data->name);
+    grp_info = getgrnam(tctx->octx->name);
     if (grp_info) {
        old_gid = grp_info->gr_gid;
     }
 
-    ret = domain_lookup(data);
+    ret = domain_lookup(tctx->octx, tctx->domains);
     if (ret) {
         switch (ret) {
             case EINVAL:
                 ERROR("Selected domain %s conflicts with selected GID %llu\n",
-                        data->domain->name, (unsigned long long int) data->uid);
+                        tctx->octx->domain->name, (unsigned long long int) tctx->octx->uid);
                 break;
 
             default:
@@ -432,13 +212,13 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    ret = get_domain_type(data->ctx, data->domain);
+    ret = get_domain_type(tctx, tctx->octx->domain);
     switch (ret) {
         case ID_IN_LOCAL:
             break;
 
         case ID_IN_LEGACY_LOCAL:
-            ret = groupmod_legacy(data->ctx, data, data->domain);
+            ret = groupmod_legacy(tctx, tctx->octx, tctx->octx->domain);
             if(ret != EOK) {
                 ERROR("Cannot delete group from domain using the legacy tools\n");
             }
@@ -450,7 +230,7 @@ int main(int argc, const char **argv)
             goto fini;
 
         case ID_IN_OTHER:
-            DEBUG(1, ("Cannot modify group from domain %s\n", data->domain->name));
+            DEBUG(1, ("Cannot modify group from domain %s\n", tctx->octx->domain->name));
             ERROR("Unsupported domain type\n");
             ret = EXIT_FAILURE;
             goto fini;
@@ -462,21 +242,8 @@ int main(int argc, const char **argv)
             goto fini;
     }
 
-    req = sysdb_transaction_send(data, data->ev, data->ctx->sysdb);
-    if (!req) {
-        DEBUG(1, ("Could not start transaction (%d)[%s]\n", ret, strerror(ret)));
-        ERROR("Transaction error. Could not modify group.\n");
-        ret = EXIT_FAILURE;
-        goto fini;
-    }
-    tevent_req_set_callback(req, mod_group, data);
-
-    while (!data->done) {
-        tevent_loop_once(data->ctx->ev);
-    }
-
-    if (data->error) {
-        ret = data->error;
+    ret = groupmod(tctx, tctx->octx);
+    if (ret) {
         DEBUG(1, ("sysdb operation failed (%d)[%s]\n", ret, strerror(ret)));
         switch (ret) {
             case ENOENT:
@@ -502,6 +269,6 @@ fini:
     free(addgroups);
     free(rmgroups);
     poptFreeContext(pc);
-    talloc_free(data);
+    talloc_free(tctx);
     exit(ret);
 }
diff --git a/server/tools/sss_useradd.c b/server/tools/sss_useradd.c
index 2d7ee2e..09eeb91 100644
--- a/server/tools/sss_useradd.c
+++ b/server/tools/sss_useradd.c
@@ -29,7 +29,7 @@
 
 #include "util/util.h"
 #include "db/sysdb.h"
-#include "tools/tools_util.h"
+#include "tools/common/tools_util.h"
 #include "util/sssd-i18n.h"
 
 /* Define default command strings if not redefined by user */
@@ -73,38 +73,29 @@
 #define USERADD_USERNAME "%s "
 #endif
 
-/* Default settings for user attributes */
-#define CONFDB_DFL_SECTION "config/user_defaults"
-
-#define DFL_SHELL_ATTR     "defaultShell"
-#define DFL_BASEDIR_ATTR   "baseDirectory"
-
-#define DFL_SHELL_VAL      "/bin/bash"
-#define DFL_BASEDIR_VAL    "/home"
-
 static void get_gid_callback(void *ptr, int error, struct ldb_result *res)
 {
-    struct ops_ctx *data = talloc_get_type(ptr, struct ops_ctx);
+    struct tools_ctx *tctx = talloc_get_type(ptr, struct tools_ctx);
 
     if (error) {
-        data->error = error;
+        tctx->error = error;
         return;
     }
 
     switch (res->count) {
     case 0:
-        data->error = ENOENT;
+        tctx->error = ENOENT;
         break;
 
     case 1:
-        data->gid = ldb_msg_find_attr_as_uint(res->msgs[0], SYSDB_GIDNUM, 0);
-        if (data->gid == 0) {
-            data->error = ERANGE;
+        tctx->octx->gid = ldb_msg_find_attr_as_uint(res->msgs[0], SYSDB_GIDNUM, 0);
+        if (tctx->octx->gid == 0) {
+            tctx->error = ERANGE;
         }
         break;
 
     default:
-        data->error = EFAULT;
+        tctx->error = EFAULT;
         break;
     }
 }
@@ -113,32 +104,32 @@ static void get_gid_callback(void *ptr, int error, struct ldb_result *res)
  * is given, returns that as integer (rationale: shadow-utils)
  * On error, returns -EINVAL
  */
-static int get_gid(struct ops_ctx *data, const char *groupname)
+static int get_gid(struct tools_ctx *tctx, const char *groupname)
 {
     char *end_ptr;
     int ret;
 
     errno = 0;
-    data->gid = strtoul(groupname, &end_ptr, 10);
+    tctx->octx->gid = strtoul(groupname, &end_ptr, 10);
     if (groupname == '\0' || *end_ptr != '\0' ||
-        errno != 0 || data->gid == 0) {
+        errno != 0 || tctx->octx->gid == 0) {
         /* Does not look like a gid - find the group name */
 
-        ret = sysdb_getgrnam(data, data->ctx->sysdb,
-                             data->domain, groupname,
-                             get_gid_callback, data);
+        ret = sysdb_getgrnam(tctx->octx, tctx->sysdb,
+                             tctx->octx->domain, groupname,
+                             get_gid_callback, tctx);
         if (ret != EOK) {
             DEBUG(1, ("sysdb_getgrnam failed: %d\n", ret));
             goto done;
         }
 
-        data->error = EOK;
-        data->gid = 0;
-        while ((data->error == EOK) && (data->gid == 0)) {
-            tevent_loop_once(data->ctx->ev);
+        tctx->error = EOK;
+        tctx->octx->gid = 0;
+        while ((tctx->error == EOK) && (tctx->octx->gid == 0)) {
+            tevent_loop_once(tctx->ev);
         }
 
-        if (data->error) {
+        if (tctx->error) {
             DEBUG(1, ("sysdb_getgrnam failed: %d\n", ret));
             goto done;
         }
@@ -148,133 +139,6 @@ done:
     return ret;
 }
 
-static void add_user_req_done(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-
-    data->error = sysdb_transaction_commit_recv(req);
-    data->done = true;
-}
-
-static void add_user_terminate(struct ops_ctx *data, int error)
-{
-    struct tevent_req *req;
-
-    if (error != EOK) {
-        goto fail;
-    }
-
-    req = sysdb_transaction_commit_send(data, data->ev, data->handle);
-    if (!req) {
-        error = ENOMEM;
-        goto fail;
-    }
-    tevent_req_set_callback(req, add_user_req_done, data);
-
-    return;
-
-fail:
-    /* free transaction */
-    talloc_zfree(data->handle);
-
-    data->error = error;
-    data->done = true;
-}
-
-static void add_user_done(struct tevent_req *subreq);
-static void add_to_groups(struct ops_ctx *data);
-static void add_to_groups_done(struct tevent_req *req);
-
-static void add_user(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-    struct tevent_req *subreq;
-    int ret;
-
-    ret = sysdb_transaction_recv(req, data, &data->handle);
-    if (ret != EOK) {
-        return add_user_terminate(data, ret);
-    }
-
-    subreq = sysdb_add_user_send(data, data->ev, data->handle,
-                                 data->domain, data->name,
-                                 data->uid, data->gid,
-                                 data->gecos, data->home,
-                                 data->shell, NULL);
-    if (!subreq) {
-        add_user_terminate(data, ENOMEM);
-    }
-    tevent_req_set_callback(subreq, add_user_done, data);
-}
-
-static void add_user_done(struct tevent_req *subreq)
-{
-    struct ops_ctx *data = tevent_req_callback_data(subreq, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_add_user_recv(subreq);
-    talloc_zfree(subreq);
-    if (ret) {
-        return add_user_terminate(data, ret);
-    }
-
-    if (data->groups) {
-        return add_to_groups(data);
-    }
-
-    return add_user_terminate(data, ret);
-}
-
-static void add_to_groups(struct ops_ctx *data)
-{
-    struct ldb_dn *parent_dn;
-    struct ldb_dn *member_dn;
-    struct tevent_req *subreq;
-
-    member_dn = sysdb_user_dn(data->ctx->sysdb, data,
-                              data->domain->name, data->name);
-    if (!member_dn) {
-        return add_user_terminate(data, ENOMEM);
-    }
-
-    parent_dn = sysdb_group_dn(data->ctx->sysdb, data,
-                              data->domain->name,
-                              data->groups[data->cur]);
-    if (!parent_dn) {
-        return add_user_terminate(data, ENOMEM);
-    }
-
-    subreq = sysdb_mod_group_member_send(data, data->ev, data->handle,
-                                         member_dn, parent_dn,
-                                         LDB_FLAG_MOD_ADD);
-    if (!subreq) {
-        return add_user_terminate(data, ENOMEM);
-    }
-    tevent_req_set_callback(subreq, add_to_groups_done, data);
-}
-
-static void add_to_groups_done(struct tevent_req *subreq)
-{
-    struct ops_ctx *data = tevent_req_callback_data(subreq, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_mod_group_member_recv(subreq);
-    talloc_zfree(subreq);
-    if (ret) {
-        return add_user_terminate(data, ret);
-    }
-
-    /* go on to next group */
-    data->cur++;
-
-    /* check if we added all of them */
-    if (data->groups[data->cur] == NULL) {
-        return add_user_terminate(data, EOK);
-    }
-
-    return add_to_groups(data);
-}
-
 static int useradd_legacy(struct ops_ctx *ctx, char *grouplist)
 {
     int ret = EOK;
@@ -316,7 +180,6 @@ int main(int argc, const char **argv)
     const char *pc_gecos = NULL;
     const char *pc_home = NULL;
     char *pc_shell = NULL;
-    char *basedir = NULL;
     int pc_debug = 0;
     const char *pc_username = NULL;
     struct poptOption long_options[] = {
@@ -332,8 +195,7 @@ int main(int argc, const char **argv)
     };
     poptContext pc = NULL;
     struct sss_domain_info *dom = NULL;
-    struct ops_ctx *data = NULL;
-    struct tevent_req *req;
+    struct tools_ctx *tctx = NULL;
     char *groups = NULL;
     int ret;
 
@@ -378,7 +240,7 @@ int main(int argc, const char **argv)
 
     CHECK_ROOT(ret, debug_prg_name);
 
-    ret = init_sss_tools(&data);
+    ret = init_sss_tools(&tctx);
     if (ret != EOK) {
         DEBUG(1, ("init_sss_tools failed (%d): %s\n", ret, strerror(ret)));
         ERROR("Error initializing the tools\n");
@@ -386,14 +248,14 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    ret = parse_name_domain(data, pc_username);
+    ret = parse_name_domain(tctx, pc_username);
     if (ret != EOK) {
         ret = EXIT_FAILURE;
         goto fini;
     }
 
     if (groups) {
-        ret = parse_groups(data, groups, &data->groups);
+        ret = parse_groups(tctx, groups, &tctx->octx->addgroups);
         if (ret != EOK) {
             DEBUG(1, ("Cannot parse groups to add the user to\n"));
             ERROR("Internal error while parsing parameters\n");
@@ -403,7 +265,7 @@ int main(int argc, const char **argv)
 
     /* Same as shadow-utils useradd, -g can specify gid or group name */
     if (pc_group != NULL) {
-        ret = get_gid(data, pc_group);
+        ret = get_gid(tctx, pc_group);
         if (ret != EOK) {
             ERROR("Cannot get group information for the user\n");
             ret = EXIT_FAILURE;
@@ -411,64 +273,27 @@ int main(int argc, const char **argv)
         }
     }
 
-    data->uid = pc_uid;
+    tctx->octx->uid = pc_uid;
 
     /*
      * Fills in defaults for ops_ctx user did not specify.
-     * FIXME - Should this originate from the confdb or another config?
      */
-    if (!pc_gecos) {
-        pc_gecos = data->name;
-    }
-    data->gecos = talloc_strdup(data, pc_gecos);
-    if (!data->gecos) {
-        ret = EXIT_FAILURE;
-        goto fini;
-    }
-
-    if (pc_home) {
-        data->home = talloc_strdup(data, pc_home);
-    } else {
-        ret = confdb_get_string(data->ctx->confdb, data,
-                                CONFDB_DFL_SECTION, DFL_BASEDIR_ATTR,
-                                DFL_BASEDIR_VAL, &basedir);
-        if (ret != EOK) {
-            ret = EXIT_FAILURE;
-            goto fini;
-        }
-        data->home = talloc_asprintf(data, "%s/%s", basedir, data->name);
-        if (!data->home) {
-            ret = EXIT_FAILURE;
-            goto fini;
-        }
-    }
-    if (!data->home) {
-        ret = EXIT_FAILURE;
-        goto fini;
-    }
-
-    if (!pc_shell) {
-        ret = confdb_get_string(data->ctx->confdb, data,
-                                CONFDB_DFL_SECTION, DFL_SHELL_ATTR,
-                                DFL_SHELL_VAL, &pc_shell);
-        if (ret != EOK) {
-            ret = EXIT_FAILURE;
-            goto fini;
-        }
-    }
-    data->shell = talloc_strdup(data, pc_shell);
-    if (!data->shell) {
+    ret = useradd_defaults(tctx, tctx->confdb, tctx->octx,
+                           pc_gecos, pc_home, pc_shell);
+    if (ret != EOK) {
+        ERROR("Cannot set default values\n");
         ret = EXIT_FAILURE;
         goto fini;
     }
 
     /* arguments processed, go on to actual work */
-    ret = domain_lookup(data);
+    ret = domain_lookup(tctx->octx, tctx->domains);
     if (ret) {
         switch (ret) {
             case EINVAL:
                 ERROR("Selected domain %s conflicts with selected UID %llu\n",
-                        data->domain->name, (unsigned long long int) data->uid);
+                        tctx->octx->domain->name,
+                        (unsigned long long int) tctx->octx->uid);
                 break;
 
             default:
@@ -479,13 +304,13 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    ret = get_domain_type(data->ctx, data->domain);
+    ret = get_domain_type(tctx, tctx->octx->domain);
     switch (ret) {
         case ID_IN_LOCAL:
             break;
 
         case ID_IN_LEGACY_LOCAL:
-            ret = useradd_legacy(data, groups);
+            ret = useradd_legacy(tctx->octx, groups);
             if(ret != EOK) {
                 ERROR("Cannot add user to domain using the legacy tools\n");
             }
@@ -510,21 +335,8 @@ int main(int argc, const char **argv)
     }
 
     /* useradd */
-    req = sysdb_transaction_send(data, data->ctx->ev, data->ctx->sysdb);
-    if (!req) {
-        DEBUG(1, ("Could not start transaction (%d)[%s]\n", ret, strerror(ret)));
-        ERROR("Transaction error. Could not modify user.\n");
-        ret = EXIT_FAILURE;
-        goto fini;
-    }
-    tevent_req_set_callback(req, add_user, data);
-
-    while (!data->done) {
-        tevent_loop_once(data->ctx->ev);
-    }
-
-    if (data->error) {
-        ret = data->error;
+    ret = useradd(tctx, tctx->octx);
+    if (ret) {
         switch (ret) {
             case EEXIST:
                 ERROR("A user with the same name or UID already exists\n");
@@ -544,7 +356,7 @@ int main(int argc, const char **argv)
 
 fini:
     poptFreeContext(pc);
-    talloc_free(data);
+    talloc_free(tctx);
     free(groups);
     exit(ret);
 }
diff --git a/server/tools/sss_userdel.c b/server/tools/sss_userdel.c
index fea12e1..0ab9147 100644
--- a/server/tools/sss_userdel.c
+++ b/server/tools/sss_userdel.c
@@ -28,7 +28,7 @@
 
 #include "db/sysdb.h"
 #include "util/util.h"
-#include "tools/tools_util.h"
+#include "tools/common/tools_util.h"
 
 #ifndef USERDEL
 #define USERDEL SHADOW_UTILS_PATH"/userdel "
@@ -38,81 +38,6 @@
 #define USERDEL_USERNAME    "%s "
 #endif
 
-static void userdel_req_done(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-
-    data->error = sysdb_transaction_commit_recv(req);
-    data->done = true;
-}
-
-/* sysdb callback */
-static void userdel_done(void *pvt, int error, struct ldb_result *ignore)
-{
-    struct ops_ctx *data = talloc_get_type(pvt, struct ops_ctx);
-    struct tevent_req *req;
-
-    if (error != EOK) {
-        goto fail;
-    }
-
-    req = sysdb_transaction_commit_send(data, data->ev, data->handle);
-    if (!req) {
-        error = ENOMEM;
-        goto fail;
-    }
-    tevent_req_set_callback(req, userdel_req_done, data);
-
-    return;
-
-fail:
-    /* free transaction */
-    talloc_zfree(data->handle);
-
-    data->error = error;
-    data->done = true;
-}
-
-static void user_del_done(struct tevent_req *subreq);
-
-static void user_del(struct tevent_req *req)
-{
-    struct ops_ctx *data;
-    struct tevent_req *subreq;
-    struct ldb_dn *user_dn;
-    int ret;
-
-    data = tevent_req_callback_data(req, struct ops_ctx);
-
-    ret = sysdb_transaction_recv(req, data, &data->handle);
-    if (ret != EOK) {
-        return userdel_done(data, ret, NULL);
-    }
-
-    user_dn = sysdb_user_dn(data->ctx->sysdb, data,
-                            data->domain->name, data->name);
-    if (!user_dn) {
-        DEBUG(1, ("Could not construct a user DN\n"));
-        return userdel_done(data, ENOMEM, NULL);
-    }
-
-    subreq = sysdb_delete_entry_send(data, data->ev, data->handle, user_dn, false);
-    if (!subreq)
-        return userdel_done(data, ENOMEM, NULL);
-
-    tevent_req_set_callback(subreq, user_del_done, data);
-}
-
-static void user_del_done(struct tevent_req *subreq)
-{
-    struct ops_ctx *data = tevent_req_callback_data(subreq, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_delete_entry_recv(subreq);
-
-    return userdel_done(data, ret, NULL);
-}
-
 static int userdel_legacy(struct ops_ctx *ctx)
 {
     int ret = EOK;
@@ -130,9 +55,7 @@ static int userdel_legacy(struct ops_ctx *ctx)
 int main(int argc, const char **argv)
 {
     int ret = EXIT_SUCCESS;
-    struct ops_ctx *data = NULL;
-    struct tevent_req *req;
-    struct sss_domain_info *dom;
+    struct tools_ctx *tctx = NULL;
     struct passwd *pwd_info;
     const char *pc_username = NULL;
     enum id_domain domain_type;
@@ -176,7 +99,7 @@ int main(int argc, const char **argv)
 
     CHECK_ROOT(ret, debug_prg_name);
 
-    ret = init_sss_tools(&data);
+    ret = init_sss_tools(&tctx);
     if(ret != EOK) {
         DEBUG(1, ("init_sss_tools failed (%d): %s\n", ret, strerror(ret)));
         ERROR("Error initializing the tools\n");
@@ -184,24 +107,24 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    ret = parse_name_domain(data, pc_username);
+    ret = parse_name_domain(tctx, pc_username);
     if (ret != EOK) {
         ret = EXIT_FAILURE;
         goto fini;
     }
 
     /* arguments processed, go on to actual work */
-    pwd_info = getpwnam(data->name);
+    pwd_info = getpwnam(tctx->octx->name);
     if (pwd_info) {
-        data->uid = pwd_info->pw_uid;
+        tctx->octx->uid = pwd_info->pw_uid;
     }
 
-    ret = domain_lookup(data);
+    ret = domain_lookup(tctx->octx,tctx->domains);
     if (ret) {
         switch (ret) {
             case EINVAL:
                 ERROR("Selected domain %s conflicts with selected UID %llu\n",
-                        data->domain->name, (unsigned long long int) data->uid);
+                        tctx->octx->domain->name, (unsigned long long int) tctx->octx->uid);
                 break;
 
             default:
@@ -212,13 +135,13 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    domain_type = get_domain_type(data->ctx, data->domain);
+    domain_type = get_domain_type(tctx, tctx->octx->domain);
     switch (domain_type) {
         case ID_IN_LOCAL:
             break;
 
         case ID_IN_LEGACY_LOCAL:
-            ret = userdel_legacy(data);
+            ret = userdel_legacy(tctx->octx);
             if(ret != EOK) {
                 ERROR("Cannot delete user from domain using the legacy tools\n");
                 ret = EXIT_FAILURE;
@@ -232,7 +155,7 @@ int main(int argc, const char **argv)
             goto fini;
 
         case ID_IN_OTHER:
-            DEBUG(1, ("Cannot remove user from domain %s\n", data->domain->name));
+            DEBUG(1, ("Cannot remove user from domain %s\n", tctx->octx->domain->name));
             ERROR("Unsupported domain type\n");
             ret = EXIT_FAILURE;
             goto fini;
@@ -245,21 +168,8 @@ int main(int argc, const char **argv)
     }
 
     /* userdel */
-    req = sysdb_transaction_send(data, data->ev, data->ctx->sysdb);
-    if (!req) {
-        DEBUG(1, ("Could not start transaction (%d)[%s]\n", ret, strerror(ret)));
-        ERROR("Transaction error. Could not remove user.\n");
-        ret = EXIT_FAILURE;
-        goto fini;
-    }
-    tevent_req_set_callback(req, user_del, data);
-
-    while (!data->done) {
-        tevent_loop_once(data->ctx->ev);
-    }
-
-    if (data->error) {
-        ret = data->error;
+    ret = userdel(tctx, tctx->octx);
+    if (ret) {
         DEBUG(1, ("sysdb operation failed (%d)[%s]\n", ret, strerror(ret)));
         switch (ret) {
             case ENOENT:
@@ -283,7 +193,7 @@ int main(int argc, const char **argv)
     ret = EXIT_SUCCESS;
 
 fini:
-    talloc_free(data);
+    talloc_free(tctx);
     poptFreeContext(pc);
     exit(ret);
 }
diff --git a/server/tools/sss_usermod.c b/server/tools/sss_usermod.c
index 126252c..cb9b324 100644
--- a/server/tools/sss_usermod.c
+++ b/server/tools/sss_usermod.c
@@ -29,19 +29,7 @@
 
 #include "util/util.h"
 #include "db/sysdb.h"
-#include "tools/tools_util.h"
-
-#define DO_LOCK     1
-#define DO_UNLOCK   2
-
-#define VAR_CHECK(var, val, attr, msg) do { \
-        if (var != (val)) { \
-            DEBUG(1, (msg" attribute: %s", attr)); \
-            ERROR(msg); \
-            var = EXIT_FAILURE; \
-            goto fini; \
-        } \
-} while(0)
+#include "tools/common/tools_util.h"
 
 /* Define default command strings if not redefined by user */
 #ifndef USERMOD
@@ -80,210 +68,6 @@
 #define USERMOD_USERNAME "%s"
 #endif
 
-static void mod_user_req_done(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-
-    data->error = sysdb_transaction_commit_recv(req);
-    data->done = true;
-}
-
-static void mod_user_done(struct ops_ctx *data, int error)
-{
-    struct tevent_req *req;
-
-    if (error != EOK) {
-        goto fail;
-    }
-
-    req = sysdb_transaction_commit_send(data, data->ev, data->handle);
-    if (!req) {
-        error = ENOMEM;
-        goto fail;
-    }
-    tevent_req_set_callback(req, mod_user_req_done, data);
-
-    return;
-
-fail:
-    /* free transaction */
-    talloc_zfree(data->handle);
-
-    data->error = error;
-    data->done = true;
-}
-
-static void mod_user_attr_done(struct tevent_req *req);
-static void mod_user_cont(struct ops_ctx *data);
-static void remove_from_groups(struct ops_ctx *data);
-static void remove_from_groups_done(struct tevent_req *req);
-static void add_to_groups(struct ops_ctx *data);
-static void add_to_groups_done(struct tevent_req *req);
-
-static void mod_user(struct tevent_req *req)
-{
-    struct ops_ctx *data;
-    struct tevent_req *subreq;
-    int ret;
-
-    data = tevent_req_callback_data(req, struct ops_ctx);
-
-    ret = sysdb_transaction_recv(req, data, &data->handle);
-    if (ret != EOK) {
-        return mod_user_done(data, ret);
-    }
-
-    if (data->attrs->num != 0) {
-        subreq = sysdb_set_user_attr_send(data, data->ev, data->handle,
-                                          data->domain, data->name,
-                                          data->attrs, SYSDB_MOD_REP);
-        if (!subreq) {
-            return mod_user_done(data, ENOMEM);
-        }
-        tevent_req_set_callback(subreq, mod_user_attr_done, data);
-        return;
-    }
-
-    return mod_user_cont(data);
-}
-
-static void mod_user_attr_done(struct tevent_req *subreq)
-{
-    struct ops_ctx *data = tevent_req_callback_data(subreq, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_set_user_attr_recv(subreq);
-    talloc_zfree(subreq);
-    if (ret != EOK) {
-        return mod_user_done(data, ret);
-    }
-
-    mod_user_cont(data);
-}
-
-static void mod_user_cont(struct ops_ctx *data)
-{
-    if (data->rmgroups != NULL) {
-        return remove_from_groups(data);
-    }
-
-    if (data->addgroups != NULL) {
-        return add_to_groups(data);
-    }
-
-    return mod_user_done(data, EOK);
-}
-
-static void remove_from_groups(struct ops_ctx *data)
-{
-    struct ldb_dn *parent_dn;
-    struct ldb_dn *member_dn;
-    struct tevent_req *req;
-
-    member_dn = sysdb_user_dn(data->ctx->sysdb, data,
-                              data->domain->name, data->name);
-    if (!member_dn) {
-        return mod_user_done(data, ENOMEM);
-    }
-
-    parent_dn = sysdb_group_dn(data->ctx->sysdb, data,
-                               data->domain->name,
-                               data->rmgroups[data->cur]);
-    if (!parent_dn) {
-        return mod_user_done(data, ENOMEM);
-    }
-
-    req = sysdb_mod_group_member_send(data,
-                                      data->ev,
-                                      data->handle,
-                                      member_dn,
-                                      parent_dn,
-                                      LDB_FLAG_MOD_DELETE);
-    if (!req) {
-        return mod_user_done(data, ENOMEM);
-    }
-    tevent_req_set_callback(req, remove_from_groups_done, data);
-}
-
-static void remove_from_groups_done(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_mod_group_member_recv(req);
-    if (ret) {
-        return mod_user_done(data, ret);
-    }
-    talloc_zfree(req);
-
-    /* go on to next group */
-    data->cur++;
-
-    /* check if we added all of them */
-    if (data->rmgroups[data->cur] == NULL) {
-        data->cur = 0;
-        if (data->addgroups != NULL) {
-            return remove_from_groups(data);
-        }
-        return mod_user_done(data, EOK);
-    }
-
-    return remove_from_groups(data);
-}
-
-static void add_to_groups(struct ops_ctx *data)
-{
-    struct ldb_dn *parent_dn;
-    struct ldb_dn *member_dn;
-    struct tevent_req *req;
-
-    member_dn = sysdb_user_dn(data->ctx->sysdb, data,
-                              data->domain->name, data->name);
-    if (!member_dn) {
-        return mod_user_done(data, ENOMEM);
-    }
-
-    parent_dn = sysdb_group_dn(data->ctx->sysdb, data,
-                               data->domain->name,
-                               data->addgroups[data->cur]);
-    if (!parent_dn) {
-        return mod_user_done(data, ENOMEM);
-    }
-
-    req = sysdb_mod_group_member_send(data,
-                                      data->ev,
-                                      data->handle,
-                                      member_dn,
-                                      parent_dn,
-                                      LDB_FLAG_MOD_ADD);
-    if (!req) {
-        return mod_user_done(data, ENOMEM);
-    }
-    tevent_req_set_callback(req, add_to_groups_done, data);
-}
-
-static void add_to_groups_done(struct tevent_req *req)
-{
-    struct ops_ctx *data = tevent_req_callback_data(req, struct ops_ctx);
-    int ret;
-
-    ret = sysdb_mod_group_member_recv(req);
-    if (ret) {
-        return mod_user_done(data, ret);
-    }
-    talloc_zfree(req);
-
-    /* go on to next group */
-    data->cur++;
-
-    /* check if we added all of them */
-    if (data->addgroups[data->cur] == NULL) {
-        return mod_user_done(data, EOK);
-    }
-
-    return add_to_groups(data);
-}
-
 static int usermod_legacy(struct tools_ctx *tools_ctx, struct ops_ctx *ctx,
                           uid_t uid, gid_t gid,
                           const char *gecos, const char *home,
@@ -297,7 +81,7 @@ static int usermod_legacy(struct tools_ctx *tools_ctx, struct ops_ctx *ctx,
     APPEND_STRING(command, USERMOD);
 
     if (uid) {
-        ret = get_domain_by_id(tools_ctx, ctx->gid, &dom);
+        ret = get_domain_by_id(tools_ctx->domains, ctx->gid, &dom);
         if (ret != EOK) {
             ERROR("Cannot get domain info\n");
             talloc_free(command);
@@ -313,7 +97,7 @@ static int usermod_legacy(struct tools_ctx *tools_ctx, struct ops_ctx *ctx,
     }
 
     if (gid) {
-        ret = get_domain_by_id(tools_ctx, ctx->gid, &dom);
+        ret = get_domain_by_id(tools_ctx->domains, ctx->gid, &dom);
         if (ret != EOK) {
             ERROR("Cannot get domain info\n");
             talloc_free(command);
@@ -353,9 +137,9 @@ int main(int argc, const char **argv)
     int pc_lock = 0;
     uid_t pc_uid = 0;
     gid_t pc_gid = 0;
-    const char *pc_gecos = NULL;
-    const char *pc_home = NULL;
-    const char *pc_shell = NULL;
+    char *pc_gecos = NULL;
+    char *pc_home = NULL;
+    char *pc_shell = NULL;
     int pc_debug = 0;
     struct poptOption long_options[] = {
         POPT_AUTOHELP
@@ -372,13 +156,12 @@ int main(int argc, const char **argv)
         POPT_TABLEEND
     };
     poptContext pc = NULL;
-    struct ops_ctx *data = NULL;
-    struct tevent_req *req;
     char *addgroups = NULL, *rmgroups = NULL;
     int ret;
     struct passwd *pwd_info;
     uid_t old_uid = 0;
     const char *pc_username = NULL;
+    struct tools_ctx *tctx = NULL;
 
     debug_prg_name = argv[0];
 
@@ -437,7 +220,7 @@ int main(int argc, const char **argv)
 
     CHECK_ROOT(ret, debug_prg_name);
 
-    ret = init_sss_tools(&data);
+    ret = init_sss_tools(&tctx);
     if (ret != EOK) {
         DEBUG(1, ("init_sss_tools failed (%d): %s\n", ret, strerror(ret)));
         ERROR("Error initializing the tools\n");
@@ -445,23 +228,14 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    data->attrs = sysdb_new_attrs(data->ctx);
-    if (data->attrs == NULL) {
-        DEBUG(1, ("Could not allocate memory for sysdb_attrs context\n"));
-        ERROR("Out of memory\n");
-        return ENOMEM;
-    }
-
-    ret = parse_name_domain(data, pc_username);
+    ret = parse_name_domain(tctx, pc_username);
     if (ret != EOK) {
         ret = EXIT_FAILURE;
         goto fini;
     }
 
     if (addgroups) {
-        ret = parse_groups(data,
-                           addgroups,
-                           &data->addgroups);
+        ret = parse_groups(tctx, addgroups, &tctx->octx->addgroups);
         if (ret != EOK) {
             DEBUG(1, ("Cannot parse groups to add the user to\n"));
             ERROR("Internal error while parsing parameters\n");
@@ -470,9 +244,7 @@ int main(int argc, const char **argv)
     }
 
     if (rmgroups) {
-        ret = parse_groups(data,
-                           rmgroups,
-                           &data->rmgroups);
+        ret = parse_groups(tctx, rmgroups, &tctx->octx->rmgroups);
         if (ret != EOK) {
             DEBUG(1, ("Cannot parse groups to remove the user from\n"));
             ERROR("Internal error while parsing parameters\n");
@@ -480,17 +252,17 @@ int main(int argc, const char **argv)
         }
     }
 
-    pwd_info = getpwnam(data->name);
+    pwd_info = getpwnam(tctx->octx->name);
     if (pwd_info) {
         old_uid = pwd_info->pw_uid;
     }
 
-    ret = domain_lookup(data);
+    ret = domain_lookup(tctx->octx, tctx->domains);
     if (ret) {
         switch (ret) {
             case EINVAL:
                 ERROR("Selected domain %s conflicts with selected UID %llu\n",
-                        data->domain->name, (unsigned long long int) data->uid);
+                        tctx->octx->domain->name, (unsigned long long int) tctx->octx->uid);
                 break;
 
             default:
@@ -501,14 +273,14 @@ int main(int argc, const char **argv)
         goto fini;
     }
 
-    ret = get_domain_type(data->ctx, data->domain);
+    ret = get_domain_type(tctx, tctx->octx->domain);
     switch (ret) {
         case ID_IN_LOCAL:
             break;
 
         case ID_IN_LEGACY_LOCAL:
-            ret = usermod_legacy(data->ctx, data, pc_uid, pc_gid, pc_gecos,
-                                 pc_home, pc_shell, pc_lock, data->domain);
+            ret = usermod_legacy(tctx, tctx->octx, pc_uid, pc_gid, pc_gecos,
+                                 pc_home, pc_shell, pc_lock, tctx->octx->domain);
             if(ret != EOK) {
                 ERROR("Cannot delete user from domain using the legacy tools\n");
             }
@@ -520,7 +292,7 @@ int main(int argc, const char **argv)
             goto fini;
 
         case ID_IN_OTHER:
-            DEBUG(1, ("Cannot modify user from domain %s\n", data->domain->name));
+            DEBUG(1, ("Cannot modify user from domain %s\n", tctx->octx->domain->name));
             ERROR("Unsupported domain type\n");
             ret = EXIT_FAILURE;
             goto fini;
@@ -532,82 +304,16 @@ int main(int argc, const char **argv)
             goto fini;
     }
 
-    /* add parameters to changeset */
-    /* FIXME - might want to do this via attr:pc_var mapping in a loop */
-
-    if(pc_shell) {
-        ret = sysdb_attrs_add_string(data->attrs,
-                                     SYSDB_SHELL,
-                                     pc_shell);
-        VAR_CHECK(ret, EOK, SYSDB_SHELL,
-                  "Could not add attribute to changeset\n");
-    }
-
-    if(pc_home) {
-        ret = sysdb_attrs_add_string(data->attrs,
-                                     SYSDB_HOMEDIR,
-                                     pc_home);
-        VAR_CHECK(ret, EOK, SYSDB_HOMEDIR,
-                  "Could not add attribute to changeset\n");
-    }
-
-    if(pc_gecos) {
-        ret = sysdb_attrs_add_string(data->attrs,
-                                     SYSDB_GECOS,
-                                     pc_gecos);
-        VAR_CHECK(ret, EOK, SYSDB_GECOS,
-                  "Could not add attribute to changeset\n");
-    }
-
-    if(pc_uid) {
-        ret = sysdb_attrs_add_long(data->attrs,
-                                   SYSDB_UIDNUM,
-                                   pc_uid);
-        VAR_CHECK(ret, EOK, SYSDB_UIDNUM,
-                  "Could not add attribute to changeset\n");
-    }
-
-    if(pc_gid) {
-        ret = sysdb_attrs_add_long(data->attrs,
-                                   SYSDB_GIDNUM,
-                                   pc_gid);
-        VAR_CHECK(ret, EOK, SYSDB_GIDNUM,
-                  "Could not add attribute to changeset\n");
-    }
-
-    if(pc_lock == DO_LOCK) {
-        ret = sysdb_attrs_add_string(data->attrs,
-                                     SYSDB_DISABLED,
-                                     "true");
-        VAR_CHECK(ret, EOK, SYSDB_DISABLED,
-                  "Could not add attribute to changeset\n");
-    }
-
-    if(pc_lock == DO_UNLOCK) {
-        /* PAM code checks for 'false' value in SYSDB_DISABLED attribute */
-        ret = sysdb_attrs_add_string(data->attrs,
-                                     SYSDB_DISABLED,
-                                     "false");
-        VAR_CHECK(ret, EOK, SYSDB_DISABLED,
-                  "Could not add attribute to changeset\n");
-    }
-
-    req = sysdb_transaction_send(data, data->ev, data->ctx->sysdb);
-    if (!req) {
-        DEBUG(1, ("Could not start transaction (%d)[%s]\n", ret, strerror(ret)));
-        ERROR("Transaction error. Could not modify user.\n");
-        ret = EXIT_FAILURE;
-        goto fini;
-    }
-    tevent_req_set_callback(req, mod_user, data);
-
-    while (!data->done) {
-        tevent_loop_once(data->ctx->ev);
-    }
-
-    if (data->error) {
-        ret = data->error;
+    tctx->octx->gecos = pc_gecos;
+    tctx->octx->home = pc_home;
+    tctx->octx->shell = pc_shell;
+    tctx->octx->uid = pc_uid;
+    tctx->octx->gid = pc_gid;
+    tctx->octx->lock = pc_lock;
 
+    /* usermod */
+    ret = usermod(tctx, tctx->octx);
+    if (ret) {
         DEBUG(1, ("sysdb operation failed (%d)[%s]\n", ret, strerror(ret)));
         switch (ret) {
             case ENOENT:
@@ -633,6 +339,6 @@ fini:
     free(addgroups);
     free(rmgroups);
     poptFreeContext(pc);
-    talloc_free(data);
+    talloc_free(tctx);
     exit(ret);
 }
diff --git a/server/tools/tools_util.c b/server/tools/tools_util.c
deleted file mode 100644
index ef97e84..0000000
--- a/server/tools/tools_util.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
-   SSSD
-
-   tools_utils.c
-
-   Copyright (C) Jakub Hrozek <jhrozek@redhat.com>        2009
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <talloc.h>
-#include <tevent.h>
-#include <popt.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include "util/util.h"
-#include "confdb/confdb.h"
-#include "db/sysdb.h"
-#include "tools/tools_util.h"
-
-/*
- * Returns:
- * 0 = yes, local domain proxying to files
- * -1 = no, other type of domain
- * > 0 = error code
- */
-static int is_domain_local_legacy(struct tools_ctx *ctx, struct sss_domain_info *dom)
-{
-    char *libname = NULL;
-    char *conf_path = NULL;
-    int ret = -1;
-
-    /* Is there a better way to find out? Having LEGACYLOCAL as reserved would help */
-    conf_path = talloc_asprintf(ctx, "config/domains/%s", dom->name);
-    if (conf_path == NULL ) {
-        return ENOMEM;
-    }
-
-    ret = confdb_get_string(ctx->confdb, ctx, conf_path,
-                            "libName", NULL, &libname);
-    if (ret != EOK) {
-        talloc_free(conf_path);
-        return ret;
-    }
-    if (libname == NULL) {
-        talloc_free(conf_path);
-        return -1;
-    }
-
-    if (strcasecmp(libname, "files") == 0) {
-        talloc_free(conf_path);
-        talloc_free(libname);
-        return EOK;
-    }
-
-    talloc_free(conf_path);
-    talloc_free(libname);
-    return -1;
-}
-
-enum id_domain get_domain_type(struct tools_ctx *ctx,
-                               struct sss_domain_info *dom)
-{
-    if (dom == NULL) {
-        return ID_OUTSIDE;
-    }
-
-    if (strcasecmp(dom->provider, "local") == 0) {
-        return ID_IN_LOCAL;
-    } else if (strcasecmp(dom->provider, "files") == 0 ||
-               is_domain_local_legacy(ctx, dom) == 0) {
-        return ID_IN_LEGACY_LOCAL;
-    }
-
-    return ID_IN_OTHER;
-}
-
-static struct sss_domain_info *get_local_domain(struct tools_ctx *ctx)
-{
-    struct sss_domain_info *dom = NULL;
-
-    /* No ID specified, find LOCAL */
-    for (dom = ctx->domains; dom; dom = dom->next) {
-        if (strcasecmp(dom->provider, "local") == 0) {
-            break;
-        }
-    }
-
-    return dom;
-}
-
-int get_domain_by_id(struct tools_ctx *ctx,
-                     uint32_t id,
-                     struct sss_domain_info **_dom)
-{
-    struct sss_domain_info *dom = NULL;
-    int ret = EOK;
-
-    if (id) {
-        for (dom = ctx->domains; dom; dom = dom->next) {
-            if (id >= dom->id_min &&
-                (dom->id_max == 0 || id <= dom->id_max)) {
-                break;
-            }
-        }
-    }
-
-    if (dom == NULL && id == 0) {
-        dom = get_local_domain(ctx);
-        if (dom == NULL) {
-            DEBUG(1, ("Cannot find local domain info\n"));
-            ret = ENOENT;
-        }
-    }
-
-    *_dom = dom;
-    return ret;
-}
-
-int domain_lookup(struct ops_ctx *ops)
-{
-    int ret;
-    struct sss_domain_info *dom = NULL;
-
-    if (!ops) {
-        return EIO;
-    }
-
-    ret = get_domain_by_id(ops->ctx, ops->uid, &dom);
-    if (ret != EOK) {
-        return ret;
-    }
-    /* If found domain by id check conflict against domain in ops */
-    if (dom && ops->domain && ops->domain != dom) {
-        return EINVAL;
-    }
-    if (ops->domain == NULL && dom) {
-        ops->domain = dom;
-    }
-
-    return EOK;
-}
-
-int setup_db(struct tools_ctx **tools_ctx)
-{
-    char *confdb_path;
-    struct tools_ctx *ctx;
-    int ret;
-
-    ctx = talloc_zero(NULL, struct tools_ctx);
-    if (ctx == NULL) {
-        DEBUG(1, ("Could not allocate memory for tools context"));
-        return ENOMEM;
-    }
-
-    /* Create the event context */
-    ctx->ev = tevent_context_init(ctx);
-    if (ctx->ev == NULL) {
-        DEBUG(1, ("Could not create event context"));
-        talloc_free(ctx);
-        return EIO;
-    }
-
-    confdb_path = talloc_asprintf(ctx, "%s/%s", DB_PATH, CONFDB_FILE);
-    if (confdb_path == NULL) {
-        talloc_free(ctx);
-        return ENOMEM;
-    }
-
-    /* Connect to the conf db */
-    ret = confdb_init(ctx, ctx->ev, &ctx->confdb, confdb_path);
-    if (ret != EOK) {
-        DEBUG(1, ("Could not initialize connection to the confdb"));
-        talloc_free(ctx);
-        return ret;
-    }
-
-    ret = confdb_get_domains(ctx->confdb, ctx, &ctx->domains);
-    if (ret != EOK) {
-        DEBUG(1, ("Could not get domains"));
-        talloc_free(ctx);
-        return ret;
-    }
-
-    /* open sysdb at default path */
-    ret = sysdb_init(ctx, ctx->ev, ctx->confdb, NULL, &ctx->sysdb);
-    if (ret != EOK) {
-        DEBUG(1, ("Could not initialize connection to the sysdb"));
-        talloc_free(ctx);
-        return ret;
-    }
-
-    talloc_free(confdb_path);
-    *tools_ctx = ctx;
-    return EOK;
-}
-
-/*
- * Print poptUsage as well as our error message
- */
-void usage(poptContext pc, const char *error)
-{
-    poptPrintUsage(pc, stderr, 0);
-    if (error) fprintf(stderr, "%s", error);
-}
-
-int parse_groups(TALLOC_CTX *mem_ctx, const char *optstr, char ***_out)
-{
-    char **out;
-    char *orig, *n, *o;
-    char delim = ',';
-    unsigned int tokens = 1;
-    int i;
-
-    orig = talloc_strdup(mem_ctx, optstr);
-    if (!orig) return ENOMEM;
-
-    n = orig;
-    tokens = 1;
-    while ((n = strchr(n, delim))) {
-        n++;
-        tokens++;
-    }
-
-    out = talloc_array(mem_ctx, char *, tokens+1);
-    if (!out) {
-        talloc_free(orig);
-        return ENOMEM;
-    }
-
-    n = o = orig;
-    for (i = 0; i < tokens; i++) {
-        o = n;
-        n = strchr(n, delim);
-        if (!n) {
-            break;
-        }
-        *n = '\0';
-        n++;
-        out[i] = talloc_strdup(out, o);
-    }
-    out[tokens-1] = talloc_strdup(out, o);
-    out[tokens] = NULL;
-
-    talloc_free(orig);
-    *_out = out;
-    return EOK;
-}
-
-int parse_name_domain(struct ops_ctx *octx,
-                      const char *fullname)
-{
-    int ret;
-    char *domain = NULL;
-    struct sss_domain_info *dom;
-
-    ret = sss_parse_name(octx, octx->ctx->snctx, fullname, &domain, &octx->name);
-    if (ret != EOK) {
-        DEBUG(0, ("Cannot parse full name\n"));
-        return ret;
-    }
-    DEBUG(5, ("Parsed username: %s\n", octx->name));
-
-    if (domain) {
-        DEBUG(5, ("Parsed domain: %s\n", domain));
-
-        /* Got string domain name, find corresponding sss_domain_info */
-        for (dom = octx->ctx->domains; dom; dom = dom->next) {
-            if (strcasecmp(dom->name, domain) == 0) {
-                DEBUG(6, ("Found sss_domain_info for given domain name\n"));
-                octx->domain = dom;
-                break;
-            }
-        }
-        if (octx->domain == NULL) {
-            DEBUG(0, ("Invalid domain %s specified in FQDN\n", domain));
-            return EINVAL;
-        }
-    }
-
-    return EOK;
-}
-
-int run_command(const char *command)
-{
-    int ret;
-
-    ret = system(command);
-    if (ret != EXIT_SUCCESS) {
-        if (ret == -1) {
-            DEBUG(1, ("system(3) failed\n"));
-        } else {
-            DEBUG(1, ("Could not exec '%s', return code: %d\n",
-                      command, WEXITSTATUS(ret)));
-        }
-    }
-
-    return ret;
-}
-
-int set_locale(void)
-{
-    char *c;
-
-    c = setlocale(LC_ALL, "");
-    if (c == NULL) {
-        return EIO;
-    }
-
-    errno = 0;
-    c = bindtextdomain(PACKAGE, LOCALEDIR);
-    if (c == NULL) {
-        return errno;
-    }
-
-    errno = 0;
-    c = textdomain(PACKAGE);
-    if (c == NULL) {
-        return errno;
-    }
-
-    return EOK;
-}
-
-int init_sss_tools(struct ops_ctx **_octx)
-{
-    int ret;
-    struct tools_ctx *tctx;
-    struct ops_ctx *octx;
-
-    /* Connect to the database */
-    ret = setup_db(&tctx);
-    if (ret != EOK) {
-        DEBUG(1, ("Could not set up database\n"));
-        ret = EXIT_FAILURE;
-        goto fini;
-    }
-
-    ret = sss_names_init(tctx, tctx->confdb, &tctx->snctx);
-    if (ret != EOK) {
-        DEBUG(1, ("Could not set up parsing\n"));
-        goto fini;
-    }
-
-    octx = talloc_zero(tctx, struct ops_ctx);
-    if (octx == NULL) {
-        DEBUG(1, ("Could not allocate memory for data context\n"));
-        ERROR("Out of memory\n");
-        ret = ENOMEM;
-        goto fini;
-    }
-    octx->ctx = tctx;
-
-    *_octx = octx;
-    ret = EOK;
-fini:
-    return ret;
-}
-
diff --git a/server/tools/tools_util.h b/server/tools/tools_util.h
deleted file mode 100644
index 616608f..0000000
--- a/server/tools/tools_util.h
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef __TOOLS_UTIL_H__
-#define __TOOLS_UTIL_H__
-
-#include "util/sssd-i18n.h"
-
-#define UID_NOT_SET 0
-#define GID_NOT_SET 0
-
-#define APPEND_PARAM(str, param, val) do { \
-    if (val) { \
-        str = talloc_asprintf_append(str, param, val); \
-        if (str == NULL) { \
-            return ENOMEM; \
-        } \
-    } \
-} while(0)
-
-#define APPEND_STRING(str, val) do { \
-    str = talloc_asprintf_append(str, "%s ", val); \
-    if (str == NULL) { \
-        return ENOMEM; \
-    } \
-} while(0)
-
-#define CHECK_ROOT(val, prg_name) do { \
-    val = getuid(); \
-    if (val != 0) { \
-        DEBUG(1, ("Running under %d, must be root\n", val)); \
-        ERROR("%s must be run as root\n", prg_name); \
-        val = EXIT_FAILURE; \
-        goto fini; \
-    } \
-} while(0)
-
-enum id_domain {
-    ID_IN_LOCAL = 0,
-    ID_IN_LEGACY_LOCAL,
-    ID_IN_OTHER,
-    ID_OUTSIDE,
-    ID_ERROR
-};
-
-struct tools_ctx {
-    struct tevent_context *ev;
-    struct confdb_ctx *confdb;
-    struct sysdb_ctx *sysdb;
-    struct sss_names_ctx *snctx;
-
-    struct sss_domain_info *domains;
-};
-
-struct ops_ctx {
-    struct tools_ctx *ctx;
-    struct tevent_context *ev;
-    struct sss_domain_info *domain;
-
-    char *name;
-    uid_t uid;
-    gid_t gid;
-    char *gecos;
-    char *home;
-    char *shell;
-    struct sysdb_attrs *attrs;
-
-    char **addgroups;
-    char **rmgroups;
-    char **groups;
-    int cur;
-
-    struct sysdb_handle *handle;
-    int error;
-    bool done;
-};
-
-int init_sss_tools(struct ops_ctx **_octx);
-
-int setup_db(struct tools_ctx **ctx);
-
-void usage(poptContext pc, const char *error);
-
-int parse_groups(TALLOC_CTX *mem_ctx, const char *optstr, char ***_out);
-
-enum id_domain get_domain_type(struct tools_ctx *ctx,
-                               struct sss_domain_info *dom);
-
-int get_domain_by_id(struct tools_ctx *ctx,
-                     uint32_t id,
-                     struct sss_domain_info **_dom);
-
-int domain_lookup(struct ops_ctx *ops);
-
-int run_command(const char *command);
-
-int set_locale(void);
-
-int parse_name_domain(struct ops_ctx *octx,
-                      const char *fullname);
-
-#endif  /* __TOOLS_UTIL_H__ */
-- 
1.6.2.5

