1    	/*
2    	    SSSD
3    	
4    	    LDAP Identity Cleanup Functions
5    	
6    	    Authors:
7    	        Simo Sorce <ssorce@redhat.com>
8    	
9    	    Copyright (C) 2009 Red Hat
10   	
11   	    This program is free software; you can redistribute it and/or modify
12   	    it under the terms of the GNU General Public License as published by
13   	    the Free Software Foundation; either version 3 of the License, or
14   	    (at your option) any later version.
15   	
16   	    This program is distributed in the hope that it will be useful,
17   	    but WITHOUT ANY WARRANTY; without even the implied warranty of
18   	    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   	    GNU General Public License for more details.
20   	
21   	    You should have received a copy of the GNU General Public License
22   	    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   	*/
24   	
25   	#include <errno.h>
26   	#include <time.h>
27   	#include <sys/time.h>
28   	
29   	#include "util/util.h"
30   	#include "util/find_uid.h"
31   	#include "db/sysdb.h"
32   	#include "providers/ldap/ldap_common.h"
33   	#include "providers/ldap/sdap_async.h"
34   	
35   	/* ==Cleanup-Task========================================================= */
36   	
37   	struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
38   	                                        struct tevent_context *ev,
39   	                                        struct sdap_id_ctx *ctx);
40   	static void ldap_id_cleanup_reschedule(struct tevent_req *req);
41   	
42   	static void ldap_id_cleanup_timeout(struct tevent_context *ev,
43   	                                      struct tevent_timer *te,
44   	                                      struct timeval tv, void *pvt);
45   	
46   	static void ldap_id_cleanup_timer(struct tevent_context *ev,
47   	                                  struct tevent_timer *tt,
48   	                                  struct timeval tv, void *pvt)
49   	{
50   	    struct sdap_id_ctx *ctx = talloc_get_type(pvt, struct sdap_id_ctx);
51   	    struct tevent_timer *timeout;
52   	    struct tevent_req *req;
53   	    int delay;
54   	
55   	    if (be_is_offline(ctx->be)) {
56   	        DEBUG(4, ("Backend is marked offline, retry later!\n"));
57   	        /* schedule starting from now, not the last run */
58   	        delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
59   	        tv = tevent_timeval_current_ofs(delay, 0);
60   	        ldap_id_cleanup_set_timer(ctx, tv);
61   	        return;
62   	    }
63   	
64   	    req = ldap_id_cleanup_send(ctx, ev, ctx);
65   	    if (!req) {
66   	        DEBUG(1, ("Failed to schedule cleanup, retrying later!\n"));
67   	        /* schedule starting from now, not the last run */
68   	        delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
69   	        tv = tevent_timeval_current_ofs(delay, 0);
70   	        ldap_id_cleanup_set_timer(ctx, tv);
71   	        return;
72   	    }
73   	    tevent_req_set_callback(req, ldap_id_cleanup_reschedule, ctx);
74   	
75   	    /* if cleanup takes so long, either we try to cleanup too
76   	     * frequently, or something went seriously wrong */
77   	    delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
78   	    tv = tevent_timeval_current_ofs(delay, 0);
Event returned_pointer: Pointer "timeout" returned by "_tevent_add_timer(ctx->be->ev, req, tv, ldap_id_cleanup_timeout, req, &"ldap_id_cleanup_timeout", &"providers/ldap/ldap_id_cleanup.c:79")" is never used.
79   	    timeout = tevent_add_timer(ctx->be->ev, req, tv,
80   	                               ldap_id_cleanup_timeout, req);
81   	    return;
82   	}
83   	
84   	static void ldap_id_cleanup_timeout(struct tevent_context *ev,
85   	                                      struct tevent_timer *te,
86   	                                      struct timeval tv, void *pvt)
87   	{
88   	    struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
89   	    struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
90   	                                                       struct sdap_id_ctx);
91   	    int delay;
92   	
93   	    delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
94   	    DEBUG(1, ("Cleanup timed out! Timeout too small? (%ds)!\n", delay));
95   	
96   	    tv = tevent_timeval_current_ofs(delay, 0);
97   	    ldap_id_cleanup_set_timer(ctx, tv);
98   	
99   	    talloc_zfree(req);
100  	}
101  	
102  	static void ldap_id_cleanup_reschedule(struct tevent_req *req)
103  	{
104  	    struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
105  	                                                       struct sdap_id_ctx);
106  	    enum tevent_req_state tstate;
107  	    uint64_t err;
108  	    struct timeval tv;
109  	    int delay;
110  	
111  	    if (tevent_req_is_error(req, &tstate, &err)) {
112  	        /* On error schedule starting from now, not the last run */
113  	        tv = tevent_timeval_current();
114  	    } else {
115  	        tv = ctx->last_purge;
116  	    }
117  	    talloc_zfree(req);
118  	
119  	    delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
120  	    tv = tevent_timeval_add(&tv, delay, 0);
121  	    ldap_id_cleanup_set_timer(ctx, tv);
122  	}
123  	
124  	
125  	
126  	int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
127  	{
128  	    struct tevent_timer *cleanup_task;
129  	
130  	    DEBUG(6, ("Scheduling next cleanup at %ld.%ld\n",
131  	              (long)tv.tv_sec, (long)tv.tv_usec));
132  	
133  	    cleanup_task = tevent_add_timer(ctx->be->ev, ctx,
134  	                                    tv, ldap_id_cleanup_timer, ctx);
135  	    if (!cleanup_task) {
136  	        DEBUG(0, ("FATAL: failed to setup cleanup task!\n"));
137  	        return EFAULT;
138  	    }
139  	
140  	    return EOK;
141  	}
142  	
143  	
144  	
145  	struct global_cleanup_state {
146  	    struct tevent_context *ev;
147  	    struct sdap_id_ctx *ctx;
148  	};
149  	
150  	static struct tevent_req *cleanup_users_send(TALLOC_CTX *memctx,
151  	                                             struct tevent_context *ev,
152  	                                             struct sdap_id_ctx *ctx);
153  	static void ldap_id_cleanup_users_done(struct tevent_req *subreq);
154  	static struct tevent_req *cleanup_groups_send(TALLOC_CTX *memctx,
155  	                                          struct tevent_context *ev,
156  	                                          struct sysdb_ctx *sysdb,
157  	                                          struct sss_domain_info *domain);
158  	static void ldap_id_cleanup_groups_done(struct tevent_req *subreq);
159  	
160  	struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
161  	                                        struct tevent_context *ev,
162  	                                        struct sdap_id_ctx *ctx)
163  	{
164  	    struct global_cleanup_state *state;
165  	    struct tevent_req *req, *subreq;
166  	
167  	    req = tevent_req_create(memctx, &state, struct global_cleanup_state);
168  	    if (!req) return NULL;
169  	
170  	    state->ev = ev;
171  	    state->ctx = ctx;
172  	
173  	    subreq = cleanup_users_send(state, ev, state->ctx);
174  	    if (!subreq) {
175  	        talloc_zfree(req);
176  	        return NULL;
177  	    }
178  	    tevent_req_set_callback(subreq, ldap_id_cleanup_users_done, req);
179  	
180  	    ctx->last_purge = tevent_timeval_current();
181  	
182  	    return req;
183  	}
184  	
185  	static void ldap_id_cleanup_users_done(struct tevent_req *subreq)
186  	{
187  	    struct tevent_req *req = tevent_req_callback_data(subreq,
188  	                                                      struct tevent_req);
189  	    struct global_cleanup_state *state = tevent_req_data(req,
190  	                                                 struct global_cleanup_state);
191  	    enum tevent_req_state tstate;
192  	    uint64_t err = 0;
193  	
194  	    if (tevent_req_is_error(subreq, &tstate, &err)) {
195  	        if (tstate != TEVENT_REQ_USER_ERROR) {
196  	            err = EIO;
197  	        }
198  	        if (err != ENOENT) {
199  	            goto fail;
200  	        }
201  	    }
202  	    talloc_zfree(subreq);
203  	
204  	    subreq = cleanup_groups_send(state, state->ev,
205  	                                 state->ctx->be->sysdb,
206  	                                 state->ctx->be->domain);
207  	    if (!subreq) {
208  	        err = ENOMEM;
209  	        goto fail;
210  	    }
211  	    tevent_req_set_callback(subreq, ldap_id_cleanup_groups_done, req);
212  	
213  	    return;
214  	
215  	fail:
216  	    DEBUG(1, ("Failed to cleanup users (%d [%s]), retrying later!\n",
217  	              (int)err, strerror(err)));
218  	    tevent_req_done(req);
219  	}
220  	
221  	static void ldap_id_cleanup_groups_done(struct tevent_req *subreq)
222  	{
223  	    struct tevent_req *req = tevent_req_callback_data(subreq,
224  	                                                      struct tevent_req);
225  	    enum tevent_req_state tstate;
226  	    uint64_t err;
227  	
228  	    if (tevent_req_is_error(subreq, &tstate, &err)) {
229  	        if (tstate != TEVENT_REQ_USER_ERROR) {
230  	            err = EIO;
231  	        }
232  	        if (err != ENOENT) {
233  	            goto fail;
234  	        }
235  	    }
236  	    talloc_zfree(subreq);
237  	
238  	    tevent_req_done(req);
239  	    return;
240  	
241  	fail:
242  	    DEBUG(1, ("Failed to cleanup groups (%d [%s]), retrying later!\n",
243  	              (int)err, strerror(err)));
244  	    tevent_req_done(req);
245  	}
246  	
247  	
248  	/* ==User-Cleanup-Process================================================= */
249  	
250  	struct cleanup_users_state {
251  	    struct tevent_context *ev;
252  	    struct sysdb_ctx *sysdb;
253  	    struct sss_domain_info *domain;
254  	    struct sdap_id_ctx *ctx;
255  	
256  	    struct sysdb_handle *handle;
257  	
258  	    hash_table_t *uid_table;
259  	
260  	    struct ldb_message **msgs;
261  	    size_t count;
262  	    int cur;
263  	};
264  	
265  	static void cleanup_users_process(struct tevent_req *subreq);
266  	static int cleanup_users_logged_in(hash_table_t *table,
267  	                                   const struct ldb_message *msg);
268  	static void cleanup_users_delete(struct tevent_req *req);
269  	static void cleanup_users_next(struct tevent_req *req);
270  	static void cleanup_users_delete_done(struct tevent_req *subreq);
271  	
272  	static struct tevent_req *cleanup_users_send(TALLOC_CTX *memctx,
273  	                                             struct tevent_context *ev,
274  	                                             struct sdap_id_ctx *ctx)
275  	{
276  	    struct tevent_req *req, *subreq;
277  	    struct cleanup_users_state *state;
278  	    static const char *attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL };
279  	    time_t now = time(NULL);
280  	    char *subfilter = NULL;
281  	    int account_cache_expiration;
282  	
283  	    req = tevent_req_create(memctx, &state, struct cleanup_users_state);
284  	    if (!req) {
285  	        return NULL;
286  	    }
287  	
288  	    state->ev = ev;
289  	    state->sysdb = ctx->be->sysdb;
290  	    state->domain = ctx->be->domain;
291  	    state->ctx  = ctx;
292  	    state->msgs = NULL;
293  	    state->count = 0;
294  	    state->cur = 0;
295  	
296  	    account_cache_expiration = dp_opt_get_int(state->ctx->opts->basic,
297  	                                           SDAP_ACCOUNT_CACHE_EXPIRATION);
298  	    DEBUG(9, ("Cache expiration is set to %d days\n",
299  	              account_cache_expiration));
300  	
301  	    if (account_cache_expiration > 0) {
302  	        subfilter = talloc_asprintf(state,
303  	                                    "(&(!(%s=0))(%s<=%ld)(|(!(%s=*))(%s<=%ld)))",
304  	                                    SYSDB_CACHE_EXPIRE,
305  	                                    SYSDB_CACHE_EXPIRE,
306  	                                    (long) now,
307  	                                    SYSDB_LAST_LOGIN,
308  	                                    SYSDB_LAST_LOGIN,
309  	                                    (long) (now - (account_cache_expiration * 86400)));
310  	    } else {
311  	        subfilter = talloc_asprintf(state,
312  	                                    "(&(!(%s=0))(%s<=%ld)(!(%s=*)))",
313  	                                    SYSDB_CACHE_EXPIRE,
314  	                                    SYSDB_CACHE_EXPIRE,
315  	                                    (long) now,
316  	                                    SYSDB_LAST_LOGIN);
317  	    }
318  	    if (!subfilter) {
319  	        DEBUG(2, ("Failed to build filter\n"));
320  	        talloc_zfree(req);
321  	        return NULL;
322  	    }
323  	
324  	    subreq = sysdb_search_users_send(state, state->ev,
325  	                                     state->sysdb, NULL,
326  	                                     state->domain, subfilter, attrs);
327  	    if (!subreq) {
328  	        DEBUG(2, ("Failed to send entry search\n"));
329  	        talloc_zfree(req);
330  	        return NULL;
331  	    }
332  	    tevent_req_set_callback(subreq, cleanup_users_process, req);
333  	    return req;
334  	}
335  	
336  	static void cleanup_users_process(struct tevent_req *subreq)
337  	{
338  	    struct tevent_req *req = tevent_req_callback_data(subreq,
339  	                                                      struct tevent_req);
340  	    struct cleanup_users_state *state = tevent_req_data(req,
341  	                                               struct cleanup_users_state);
342  	    int ret;
343  	
344  	    ret = sysdb_search_users_recv(subreq, state, &state->count, &state->msgs);
345  	    talloc_zfree(subreq);
346  	    if (ret) {
347  	        if (ret == ENOENT) {
348  	            tevent_req_done(req);
349  	            return;
350  	        }
351  	        tevent_req_error(req, ret);
352  	        return;
353  	    }
354  	
355  	    DEBUG(4, ("Found %d expired user entries!\n", state->count));
356  	
357  	    if (state->count == 0) {
358  	        tevent_req_done(req);
359  	    }
360  	
361  	    ret = get_uid_table(state, &state->uid_table);
362  	    /* get_uid_table returns ENOSYS on non-Linux platforms. We proceed with
363  	     * the cleanup in that case
364  	     */
365  	    if (ret != EOK && ret != ENOSYS) {
366  	        tevent_req_error(req, ret);
367  	        return;
368  	    }
369  	
370  	    cleanup_users_delete(req);
371  	}
372  	
373  	static void cleanup_users_delete(struct tevent_req *req)
374  	{
375  	    struct tevent_req *subreq;
376  	    struct cleanup_users_state *state = tevent_req_data(req,
377  	                                               struct cleanup_users_state);
378  	    const char *name;
379  	    int ret;
380  	
381  	    name = ldb_msg_find_attr_as_string(state->msgs[state->cur],
382  	                                      SYSDB_NAME, NULL);
383  	    if (!name) {
384  	        DEBUG(2, ("Entry %s has no Name Attribute ?!?\n",
385  	                  ldb_dn_get_linearized(state->msgs[state->cur]->dn)));
386  	        tevent_req_error(req, EFAULT);
387  	        return;
388  	    }
389  	
390  	    if (state->uid_table) {
391  	        ret = cleanup_users_logged_in(state->uid_table, state->msgs[state->cur]);
392  	        if (ret == EOK) {
393  	            /* If the user is logged in, proceed to the next one */
394  	            DEBUG(5, ("User %s is still logged in, keeping his data\n", name));
395  	            cleanup_users_next(req);
396  	            return;
397  	        } else if (ret != ENOENT) {
398  	            tevent_req_error(req, ret);
399  	            return;
400  	        }
401  	    }
402  	
403  	    /* If not logged in or cannot check the table, delete him */
404  	    DEBUG(9, ("About to delete user %s\n", name));
405  	    subreq = sysdb_delete_user_send(state, state->ev,
406  	                                    state->sysdb, NULL,
407  	                                    state->domain, name, 0);
408  	    if (!subreq) {
409  	        tevent_req_error(req, ENOMEM);
410  	        return;
411  	    }
412  	    tevent_req_set_callback(subreq, cleanup_users_delete_done, req);
413  	    return;
414  	}
415  	
416  	static int cleanup_users_logged_in(hash_table_t *table,
417  	                                   const struct ldb_message *msg)
418  	{
419  	    uid_t      uid;
420  	    hash_key_t key;
421  	    hash_value_t value;
422  	    int        ret;
423  	
424  	    uid = ldb_msg_find_attr_as_uint64(msg,
425  	                                      SYSDB_UIDNUM, 0);
426  	    if (!uid) {
427  	        DEBUG(2, ("Entry %s has no UID Attribute ?!?\n",
428  	                  ldb_dn_get_linearized(msg->dn)));
429  	        return EFAULT;
430  	    }
431  	
432  	    key.type = HASH_KEY_ULONG;
433  	    key.ul   = (unsigned long) uid;
434  	
435  	    ret = hash_lookup(table, &key, &value);
436  	    if (ret == HASH_SUCCESS) {
437  	        return EOK;
438  	    } else if (ret == HASH_ERROR_KEY_NOT_FOUND) {
439  	        return ENOENT;
440  	    }
441  	
442  	    return EIO;
443  	}
444  	
445  	static void cleanup_users_next(struct tevent_req *req)
446  	{
447  	    struct cleanup_users_state *state = tevent_req_data(req,
448  	                                               struct cleanup_users_state);
449  	
450  	    state->cur++;
451  	    if (state->cur < state->count) {
452  	        cleanup_users_delete(req);
453  	        return;
454  	    }
455  	
456  	    tevent_req_done(req);
457  	}
458  	
459  	static void cleanup_users_delete_done(struct tevent_req *subreq)
460  	{
461  	    struct tevent_req *req = tevent_req_callback_data(subreq,
462  	                                                      struct tevent_req);
463  	    int ret;
464  	
465  	    ret = sysdb_delete_user_recv(subreq);
466  	    talloc_zfree(subreq);
467  	    if (ret) {
468  	        DEBUG(2, ("User delete returned %d (%s)\n",
469  	                  ret, strerror(ret)));
470  	        tevent_req_error(req, ret);
471  	        return;
472  	    }
473  	
474  	    cleanup_users_next(req);
475  	}
476  	
477  	/* ==Group-Cleanup-Process================================================ */
478  	
479  	struct cleanup_groups_state {
480  	    struct tevent_context *ev;
481  	    struct sysdb_ctx *sysdb;
482  	    struct sss_domain_info *domain;
483  	
484  	    struct sysdb_handle *handle;
485  	
486  	    struct ldb_message **msgs;
487  	    size_t count;
488  	    int cur;
489  	};
490  	
491  	static void cleanup_groups_process(struct tevent_req *subreq);
492  	static void cleanup_groups_check_users(struct tevent_req *req);
493  	static void cleanup_groups_check_users_done(struct tevent_req *subreq);
494  	static void cleanup_groups_next(struct tevent_req *req);
495  	static void cleanup_groups_delete(struct tevent_req *req);
496  	static void cleanup_groups_delete_done(struct tevent_req *subreq);
497  	
498  	static struct tevent_req *cleanup_groups_send(TALLOC_CTX *memctx,
499  	                                              struct tevent_context *ev,
500  	                                              struct sysdb_ctx *sysdb,
501  	                                              struct sss_domain_info *domain)
502  	{
503  	    struct tevent_req *req, *subreq;
504  	    struct cleanup_groups_state *state;
505  	    static const char *attrs[] = { SYSDB_NAME, NULL };
506  	    time_t now = time(NULL);
507  	    char *subfilter;
508  	
509  	    req = tevent_req_create(memctx, &state, struct cleanup_groups_state);
510  	    if (!req) {
511  	        return NULL;
512  	    }
513  	
514  	    state->ev = ev;
515  	    state->sysdb = sysdb;
516  	    state->domain = domain;
517  	    state->msgs = NULL;
518  	    state->count = 0;
519  	    state->cur = 0;
520  	
521  	    subfilter = talloc_asprintf(state, "(&(!(%s=0))(%s<=%ld))",
522  	                                SYSDB_CACHE_EXPIRE,
523  	                                SYSDB_CACHE_EXPIRE, (long)now);
524  	    if (!subfilter) {
525  	        DEBUG(2, ("Failed to build filter\n"));
526  	        talloc_zfree(req);
527  	        return NULL;
528  	    }
529  	
530  	    subreq = sysdb_search_groups_send(state, state->ev,
531  	                                      state->sysdb, NULL,
532  	                                      state->domain, subfilter, attrs);
533  	    if (!subreq) {
534  	        DEBUG(2, ("Failed to send entry search\n"));
535  	        talloc_zfree(req);
536  	        return NULL;
537  	    }
538  	    tevent_req_set_callback(subreq, cleanup_groups_process, req);
539  	
540  	    return req;
541  	}
542  	
543  	static void cleanup_groups_process(struct tevent_req *subreq)
544  	{
545  	    struct tevent_req *req = tevent_req_callback_data(subreq,
546  	                                                      struct tevent_req);
547  	    struct cleanup_groups_state *state = tevent_req_data(req,
548  	                                               struct cleanup_groups_state);
549  	    int ret;
550  	
551  	    ret = sysdb_search_groups_recv(subreq, state, &state->count, &state->msgs);
552  	    talloc_zfree(subreq);
553  	    if (ret) {
554  	        if (ret == ENOENT) {
555  	            tevent_req_done(req);
556  	            return;
557  	        }
558  	        tevent_req_error(req, ret);
559  	        return;
560  	    }
561  	
562  	    DEBUG(4, ("Found %d expired group entries!\n", state->count));
563  	
564  	    if (state->count == 0) {
565  	        tevent_req_done(req);
566  	    }
567  	
568  	    cleanup_groups_check_users(req);
569  	}
570  	
571  	static void cleanup_groups_check_users(struct tevent_req *req)
572  	{
573  	    struct cleanup_groups_state *state = tevent_req_data(req,
574  	                                               struct cleanup_groups_state);
575  	    struct tevent_req *subreq;
576  	    const char *subfilter;
577  	    const char *dn;
578  	
579  	    dn = ldb_dn_get_linearized(state->msgs[state->cur]->dn);
580  	    if (!dn) {
581  	        tevent_req_error(req, EINVAL);
582  	        return;
583  	    }
584  	
585  	    subfilter = talloc_asprintf(state, "(%s=%s)",
586  	                                SYSDB_MEMBEROF, dn);
587  	    if (!subfilter) {
588  	        DEBUG(2, ("Failed to build filter\n"));
589  	        tevent_req_error(req, ENOMEM);
590  	    }
591  	
592  	    subreq = sysdb_search_users_send(state, state->ev,
593  	                                     state->sysdb, NULL,
594  	                                     state->domain, subfilter, NULL);
595  	    if (!subreq) {
596  	        DEBUG(2, ("Failed to send entry search\n"));
597  	        tevent_req_error(req, ENOMEM);
598  	    }
599  	    tevent_req_set_callback(subreq, cleanup_groups_check_users_done, req);
600  	}
601  	
602  	static void cleanup_groups_check_users_done(struct tevent_req *subreq)
603  	{
604  	    struct tevent_req *req = tevent_req_callback_data(subreq,
605  	                                                      struct tevent_req);
606  	    struct cleanup_groups_state *state = tevent_req_data(req,
607  	                                               struct cleanup_groups_state);
608  	    int ret;
609  	    struct ldb_message **msgs;
610  	    size_t count;
611  	
612  	    ret = sysdb_search_users_recv(subreq, state, &count, &msgs);
613  	    talloc_zfree(subreq);
614  	    if (ret != EOK) {
615  	        if (ret == ENOENT) {
616  	            cleanup_groups_delete(req);
617  	            return;
618  	        }
619  	        tevent_req_error(req, ret);
620  	        return;
621  	    }
622  	
623  	    cleanup_groups_next(req);
624  	}
625  	
626  	static void cleanup_groups_next(struct tevent_req *req)
627  	{
628  	    struct cleanup_groups_state *state = tevent_req_data(req,
629  	                                               struct cleanup_groups_state);
630  	
631  	    state->cur++;
632  	    if (state->cur < state->count) {
633  	        cleanup_groups_check_users(req);
634  	        return;
635  	    }
636  	
637  	    tevent_req_done(req);
638  	}
639  	
640  	static void cleanup_groups_delete(struct tevent_req *req)
641  	{
642  	    struct tevent_req *subreq;
643  	    struct cleanup_groups_state *state = tevent_req_data(req,
644  	                                               struct cleanup_groups_state);
645  	    const char *name;
646  	
647  	    name = ldb_msg_find_attr_as_string(state->msgs[state->cur],
648  	                                      SYSDB_NAME, NULL);
649  	    if (!name) {
650  	        DEBUG(2, ("Entry %s has no Name Attribute ?!?\n",
651  	                  ldb_dn_get_linearized(state->msgs[state->cur]->dn)));
652  	        tevent_req_error(req, EFAULT);
653  	        return;
654  	    }
655  	
656  	    DEBUG(8, ("About to delete group %s\n", name));
657  	    subreq = sysdb_delete_group_send(state, state->ev,
658  	                                     state->sysdb, NULL,
659  	                                     state->domain, name, 0);
660  	    if (!subreq) {
661  	        tevent_req_error(req, ENOMEM);
662  	        return;
663  	    }
664  	    tevent_req_set_callback(subreq, cleanup_groups_delete_done, req);
665  	}
666  	
667  	static void cleanup_groups_delete_done(struct tevent_req *subreq)
668  	{
669  	    struct tevent_req *req = tevent_req_callback_data(subreq,
670  	                                                      struct tevent_req);
671  	    int ret;
672  	
673  	    ret = sysdb_delete_group_recv(subreq);
674  	    talloc_zfree(subreq);
675  	    if (ret) {
676  	        DEBUG(2, ("Group delete returned %d (%s)\n", ret, strerror(ret)));
677  	        tevent_req_error(req, ret);
678  	        return;
679  	    }
680  	
681  	    cleanup_groups_next(req);
682  	}
683