1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00

From Steve Danneman @ Isilon.

Attached is the companion patch to
(037b9689d9), which
made handling of WINBINDD_LIST_GROUPS asynchronous.

Because most all of the list_groups code was reusable, I abstracted it,
and implemented both list_groups and list_users on top of it.

On my large test domain a "wbinfo -u" call went from 70 seconds to 30
seconds with this patch.  Plus, the parent process is no longer blocked
from receiving new requests during that time.

Steven Danneman | Software Development Engineer
Isilon Systems    P +1-206-315-7500     F +1-206-315-7501
www.isilon.com
This commit is contained in:
Jeremy Allison 2008-06-26 14:02:39 -07:00
parent 384052f546
commit 5188f28611
7 changed files with 220 additions and 217 deletions

View File

@ -363,7 +363,12 @@ struct winbindd_tdc_domain {
uint32 trust_type;
};
/* Switch for listing users or groups */
enum ent_type {
LIST_USERS = 0,
LIST_GROUPS,
};
#include "winbindd/winbindd_proto.h"
#define WINBINDD_ESTABLISH_LOOP 30

View File

@ -453,8 +453,8 @@ enum winbindd_result winbindd_dual_lookupname(struct winbindd_domain *domain,
return WINBINDD_OK;
}
/* This is the first callback after enumerating groups from a domain */
static void listgroups_recv(TALLOC_CTX *mem_ctx, bool success,
/* This is the first callback after enumerating users/groups from a domain */
static void listent_recv(TALLOC_CTX *mem_ctx, bool success,
struct winbindd_response *response,
void *c, void *private_data)
{
@ -462,7 +462,7 @@ static void listgroups_recv(TALLOC_CTX *mem_ctx, bool success,
(void (*)(void *, bool, fstring, char*))c;
if (!success || response->result != WINBINDD_OK) {
DEBUG(5, ("list_groups() failed!\n"));
DEBUG(5, ("list_ent() failed!\n"));
cont(private_data, False, response->data.name.dom_name, NULL);
return;
}
@ -473,30 +473,95 @@ static void listgroups_recv(TALLOC_CTX *mem_ctx, bool success,
SAFE_FREE(response->extra_data.data);
}
/* Request the name of all groups in a single domain */
void winbindd_listgroups_async(TALLOC_CTX *mem_ctx,
/* Request the name of all users/groups in a single domain */
void winbindd_listent_async(TALLOC_CTX *mem_ctx,
struct winbindd_domain *domain,
void (*cont)(void *private_data, bool success,
fstring dom_name, char* extra_data),
void *private_data)
void *private_data, enum ent_type type)
{
struct winbindd_request request;
ZERO_STRUCT(request);
request.cmd = WINBINDD_LIST_GROUPS;
if (type == LIST_USERS)
request.cmd = WINBINDD_LIST_USERS;
else if (type == LIST_GROUPS)
request.cmd = WINBINDD_LIST_GROUPS;
do_async_domain(mem_ctx, domain, &request, listgroups_recv,
do_async_domain(mem_ctx, domain, &request, listent_recv,
(void *)cont, private_data);
}
enum winbindd_result winbindd_dual_list_users(struct winbindd_domain *domain,
struct winbindd_cli_state *state)
{
WINBIND_USERINFO *info;
NTSTATUS status;
struct winbindd_methods *methods;
uint32 num_entries = 0;
char *extra_data = NULL;
uint32_t extra_data_len = 0, i;
/* Must copy domain into response first for debugging in parent */
fstrcpy(state->response.data.name.dom_name, domain->name);
/* Query user info */
methods = domain->methods;
status = methods->query_user_list(domain, state->mem_ctx,
&num_entries, &info);
if (!NT_STATUS_IS_OK(status))
return WINBINDD_ERROR;
if (num_entries == 0)
return WINBINDD_OK;
/* Allocate some memory for extra data. Note that we limit
account names to sizeof(fstring) = 256 characters.
+1 for the ',' between group names */
extra_data = (char *)SMB_REALLOC(extra_data,
(sizeof(fstring) + 1) * num_entries);
if (!extra_data) {
DEBUG(0,("failed to enlarge buffer!\n"));
return WINBINDD_ERROR;
}
/* Pack user list into extra data fields */
for (i = 0; i < num_entries; i++) {
fstring acct_name, name;
if (info[i].acct_name == NULL)
fstrcpy(acct_name, "");
else
fstrcpy(acct_name, info[i].acct_name);
fill_domain_username(name, domain->name, acct_name, True);
/* Append to extra data */
memcpy(&extra_data[extra_data_len], name, strlen(name));
extra_data_len += strlen(name);
extra_data[extra_data_len++] = ',';
}
/* Assign extra_data fields in response structure */
if (extra_data) {
/* remove trailing ',' */
extra_data[extra_data_len - 1] = '\0';
state->response.extra_data.data = extra_data;
state->response.length += extra_data_len;
}
return WINBINDD_OK;
}
enum winbindd_result winbindd_dual_list_groups(struct winbindd_domain *domain,
struct winbindd_cli_state *state)
{
struct getent_state groups = {};
char *extra_data = NULL;
unsigned int extra_data_len = 0, i;
uint32_t extra_data_len = 0, i;
/* Must copy domain into response first for bookeeping in parent */
/* Must copy domain into response first for debugging in parent */
fstrcpy(state->response.data.name.dom_name, domain->name);
fstrcpy(groups.domain_name, domain->name);

View File

@ -49,6 +49,10 @@ static const struct winbindd_child_dispatch_table domain_dispatch_table[] = {
.name = "LOOKUPRIDS",
.struct_cmd = WINBINDD_LOOKUPRIDS,
.struct_fn = winbindd_dual_lookuprids,
},{
.name = "LIST_USERS",
.struct_cmd = WINBINDD_LIST_USERS,
.struct_fn = winbindd_dual_list_users,
},{
.name = "LIST_GROUPS",
.struct_cmd = WINBINDD_LIST_GROUPS,

View File

@ -1339,112 +1339,10 @@ void winbindd_getgrent(struct winbindd_cli_state *state)
request_error(state);
}
struct listgroups_state {
TALLOC_CTX *mem_ctx;
struct winbindd_cli_state *cli_state;
unsigned int domain_count;
char *extra_data;
unsigned int extra_data_len;
};
static void listgroups_recv(void *private_data, bool success, fstring dom_name,
char *extra_data);
/* List domain groups without mapping to unix ids */
void winbindd_list_groups(struct winbindd_cli_state *state)
{
struct winbindd_domain *domain;
const char *which_domain;
struct listgroups_state *groups_state;
DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
/* Ensure null termination */
state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
which_domain = state->request.domain_name;
/* Initialize listgroups_state */
groups_state = TALLOC_P(state->mem_ctx, struct listgroups_state);
if (groups_state == NULL) {
DEBUG(0, ("talloc failed\n"));
request_error(state);
return;
}
groups_state->mem_ctx = state->mem_ctx;
groups_state->cli_state = state;
groups_state->domain_count = 0;
groups_state->extra_data = NULL;
groups_state->extra_data_len = 0;
/* Must count the full list of expected domains before we request data
* from any of them. Otherwise it's possible for a connection to the
* first domain to fail, call listgroups_recv(), and return to the
* client without checking any other domains. */
for (domain = domain_list(); domain; domain = domain->next) {
/* if we have a domain name restricting the request and this
one in the list doesn't match, then just bypass the remainder
of the loop */
if ( *which_domain && !strequal(which_domain, domain->name) )
continue;
groups_state->domain_count++;
}
/* Make sure we're enumerating at least one domain */
if (!groups_state->domain_count) {
request_ok(state);
return;
}
/* Enumerate list of trusted domains and request group list from each */
for (domain = domain_list(); domain; domain = domain->next) {
if ( *which_domain && !strequal(which_domain, domain->name) )
continue;
winbindd_listgroups_async(state->mem_ctx, domain,
listgroups_recv, groups_state);
}
}
static void listgroups_recv(void *private_data, bool success, fstring dom_name,
char *extra_data)
{
/* extra_data comes to us as a '\0' terminated string of comma
separated groups */
struct listgroups_state *state = talloc_get_type_abort(
private_data, struct listgroups_state);
/* Append groups from one domain onto the whole list */
if (extra_data) {
DEBUG(5, ("listgroups_recv: %s returned groups.\n", dom_name));
if (!state->extra_data)
state->extra_data = talloc_asprintf(state->mem_ctx,
"%s", extra_data);
else
state->extra_data = talloc_asprintf_append_buffer(
state->extra_data,
",%s", extra_data);
/* Add one for the '\0' and each additional ',' */
state->extra_data_len += strlen(extra_data) + 1;
}
else {
DEBUG(5, ("listgroups_recv: %s returned no groups.\n",
dom_name));
}
if (--state->domain_count)
/* Still waiting for some child domains to return */
return;
/* Return list of all groups to the client */
if (state->extra_data) {
state->cli_state->response.extra_data.data =
SMB_STRDUP(state->extra_data);
state->cli_state->response.length += state->extra_data_len;
}
request_ok(state->cli_state);
winbindd_list_ent(state, LIST_GROUPS);
}
/* Get user supplementary groups. This is much quicker than trying to

View File

@ -97,6 +97,129 @@ enum winbindd_result winbindd_dual_check_machine_acct(struct winbindd_domain *do
return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
}
/* Helpers for listing user and group names */
const char *ent_type_strings[] = {"users",
"groups"};
static const char *get_ent_type_string(enum ent_type type)
{
return ent_type_strings[type];
}
struct listent_state {
TALLOC_CTX *mem_ctx;
struct winbindd_cli_state *cli_state;
enum ent_type type;
int domain_count;
char *extra_data;
uint32_t extra_data_len;
};
static void listent_recv(void *private_data, bool success, fstring dom_name,
char *extra_data);
/* List domain users/groups without mapping to unix ids */
void winbindd_list_ent(struct winbindd_cli_state *state, enum ent_type type)
{
struct winbindd_domain *domain;
const char *which_domain;
struct listent_state *ent_state;
DEBUG(3, ("[%5lu]: list %s\n", (unsigned long)state->pid,
get_ent_type_string(type)));
/* Ensure null termination */
state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
which_domain = state->request.domain_name;
/* Initialize listent_state */
ent_state = TALLOC_P(state->mem_ctx, struct listent_state);
if (ent_state == NULL) {
DEBUG(0, ("talloc failed\n"));
request_error(state);
return;
}
ent_state->mem_ctx = state->mem_ctx;
ent_state->cli_state = state;
ent_state->type = type;
ent_state->domain_count = 0;
ent_state->extra_data = NULL;
ent_state->extra_data_len = 0;
/* Must count the full list of expected domains before we request data
* from any of them. Otherwise it's possible for a connection to the
* first domain to fail, call listent_recv(), and return to the
* client without checking any other domains. */
for (domain = domain_list(); domain; domain = domain->next) {
/* if we have a domain name restricting the request and this
one in the list doesn't match, then just bypass the remainder
of the loop */
if ( *which_domain && !strequal(which_domain, domain->name) )
continue;
ent_state->domain_count++;
}
/* Make sure we're enumerating at least one domain */
if (!ent_state->domain_count) {
request_ok(state);
return;
}
/* Enumerate list of trusted domains and request user/group list from
* each */
for (domain = domain_list(); domain; domain = domain->next) {
if ( *which_domain && !strequal(which_domain, domain->name) )
continue;
winbindd_listent_async(state->mem_ctx, domain,
listent_recv, ent_state, type);
}
}
static void listent_recv(void *private_data, bool success, fstring dom_name,
char *extra_data)
{
/* extra_data comes to us as a '\0' terminated string of comma
separated users or groups */
struct listent_state *state = talloc_get_type_abort(
private_data, struct listent_state);
/* Append users/groups from one domain onto the whole list */
if (extra_data) {
DEBUG(5, ("listent_recv: %s returned %s.\n",
dom_name, get_ent_type_string(state->type)));
if (!state->extra_data)
state->extra_data = talloc_asprintf(state->mem_ctx,
"%s", extra_data);
else
state->extra_data = talloc_asprintf_append(
state->extra_data,
",%s", extra_data);
/* Add one for the '\0' and each additional ',' */
state->extra_data_len += strlen(extra_data) + 1;
}
else {
DEBUG(5, ("listent_recv: %s returned no %s.\n",
dom_name, get_ent_type_string(state->type)));
}
if (--state->domain_count)
/* Still waiting for some child domains to return */
return;
/* Return list of all users/groups to the client */
if (state->extra_data) {
state->cli_state->response.extra_data.data =
SMB_STRDUP(state->extra_data);
state->cli_state->response.length += state->extra_data_len;
}
request_ok(state->cli_state);
}
/* Constants and helper functions for determining domain trust types */
enum trust_type {

View File

@ -104,6 +104,15 @@ void winbindd_lookupname_async(TALLOC_CTX *mem_ctx,
void *private_data);
enum winbindd_result winbindd_dual_lookupname(struct winbindd_domain *domain,
struct winbindd_cli_state *state);
void winbindd_listent_async(TALLOC_CTX *mem_ctx,
struct winbindd_domain *domain,
void (*cont)(void *private_data, bool success,
fstring dom_name, char* extra_data),
void *private_data, enum ent_type type);
enum winbindd_result winbindd_dual_list_users(struct winbindd_domain *domain,
struct winbindd_cli_state *state);
enum winbindd_result winbindd_dual_list_groups(struct winbindd_domain *domain,
struct winbindd_cli_state *state);
bool print_sidlist(TALLOC_CTX *mem_ctx, const DOM_SID *sids,
size_t num_sids, char **result, ssize_t *len);
enum winbindd_result winbindd_dual_lookuprids(struct winbindd_domain *domain,
@ -133,16 +142,6 @@ void query_user_async(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
uint32 group_rid),
void *private_data);
void winbindd_listgroups_async(TALLOC_CTX *mem_ctx,
struct winbindd_domain *domain,
void (*cont)(void *private_data, bool success,
fstring dom_name, char* extra_data),
void *private_data);
enum winbindd_result winbindd_dual_list_groups(struct winbindd_domain *domain,
struct winbindd_cli_state *state);
/* The following definitions come from winbindd/winbindd_cache.c */
void winbindd_check_cache_size(time_t t);
@ -394,6 +393,7 @@ void winbindd_dsgetdcname(struct winbindd_cli_state *state);
void winbindd_check_machine_acct(struct winbindd_cli_state *state);
enum winbindd_result winbindd_dual_check_machine_acct(struct winbindd_domain *domain,
struct winbindd_cli_state *state);
void winbindd_list_ent(struct winbindd_cli_state *state, enum ent_type type);
void winbindd_list_trusted_domains(struct winbindd_cli_state *state);
enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain *domain,
struct winbindd_cli_state *state);

View File

@ -778,99 +778,7 @@ void winbindd_getpwent(struct winbindd_cli_state *state)
}
/* List domain users without mapping to unix ids */
void winbindd_list_users(struct winbindd_cli_state *state)
{
struct winbindd_domain *domain;
WINBIND_USERINFO *info;
const char *which_domain;
uint32 num_entries = 0, total_entries = 0;
char *extra_data = NULL;
int extra_data_len = 0;
enum winbindd_result rv = WINBINDD_ERROR;
DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
/* Ensure null termination */
state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
which_domain = state->request.domain_name;
/* Enumerate over trusted domains */
for (domain = domain_list(); domain; domain = domain->next) {
NTSTATUS status;
struct winbindd_methods *methods;
unsigned int i;
/* if we have a domain name restricting the request and this
one in the list doesn't match, then just bypass the remainder
of the loop */
if ( *which_domain && !strequal(which_domain, domain->name) )
continue;
methods = domain->methods;
/* Query display info */
status = methods->query_user_list(domain, state->mem_ctx,
&num_entries, &info);
if (!NT_STATUS_IS_OK(status)) {
continue;
}
if (num_entries == 0)
continue;
/* Allocate some memory for extra data */
total_entries += num_entries;
extra_data = (char *)SMB_REALLOC(
extra_data, sizeof(fstring) * total_entries);
if (!extra_data) {
DEBUG(0,("failed to enlarge buffer!\n"));
goto done;
}
/* Pack user list into extra data fields */
for (i = 0; i < num_entries; i++) {
fstring acct_name, name;
if (!info[i].acct_name) {
fstrcpy(acct_name, "");
} else {
fstrcpy(acct_name, info[i].acct_name);
}
fill_domain_username(name, domain->name, acct_name, True);
/* Append to extra data */
memcpy(&extra_data[extra_data_len], name,
strlen(name));
extra_data_len += strlen(name);
extra_data[extra_data_len++] = ',';
}
}
/* Assign extra_data fields in response structure */
if (extra_data) {
extra_data[extra_data_len - 1] = '\0';
state->response.extra_data.data = extra_data;
state->response.length += extra_data_len;
}
/* No domains responded but that's still OK so don't return an
error. */
rv = WINBINDD_OK;
done:
if (rv == WINBINDD_OK)
request_ok(state);
else
request_error(state);
winbindd_list_ent(state, LIST_USERS);
}