1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-22 22:04:08 +03:00

r5104: - added support for task based servers. These are servers that within

themselves are run as a single process, but run as a child of the
  main process when smbd is run in the standard model, and run as part
  of the main process when in the single mode.

- rewrote the winbind template code to use the new task services. Also
  fixed the packet queueing

- got rid of event_context_merge() as it is no longer needed
This commit is contained in:
Andrew Tridgell 2005-01-30 02:55:30 +00:00 committed by Gerald (Jerry) Carter
parent 3c5a7650a9
commit 339964a596
12 changed files with 384 additions and 193 deletions

View File

@ -40,6 +40,7 @@ sub smb_build_main($)
"smb_server/config.mk",
"rpc_server/config.mk",
"ldap_server/config.mk",
"winbind/config.mk",
"libcli/auth/gensec.mk",
"libcli/auth/config.mk",
"libcli/ldap/config.mk",

View File

@ -154,5 +154,6 @@ struct nbt_name_status;
struct messaging_context;
struct stream_connection;
struct task_server;
struct model_ops;
struct stream_server_ops;

View File

@ -110,32 +110,6 @@ static void calc_maxfd(struct event_context *ev)
}
}
/*
move the event structures from ev2 into ev, upping the reference
count on ev. The event context ev2 is then destroyed.
this is used by modules that need to call on the events of a lower module
*/
struct event_context *event_context_merge(struct event_context *ev,
struct event_context *ev2)
{
DLIST_CONCATENATE(ev->fd_events, ev2->fd_events, struct fd_event *);
DLIST_CONCATENATE(ev->timed_events, ev2->timed_events, struct timed_event *);
DLIST_CONCATENATE(ev->loop_events, ev2->loop_events, struct loop_event *);
ev2->fd_events = NULL;
ev2->timed_events = NULL;
ev2->loop_events = NULL;
talloc_steal(ev->events, ev2->events);
event_context_destroy(ev2);
calc_maxfd(ev);
return ev;
}
/* to mark the ev->maxfd invalid
* this means we need to recalculate it
*/

View File

@ -45,7 +45,8 @@ REQUIRED_SUBSYSTEMS = \
[SUBSYSTEM::SERVER_SERVICE]
INIT_OBJ_FILES = \
smbd/service.o \
smbd/service_stream.o
smbd/service_stream.o \
smbd/service_task.o
REQUIRED_SUBSYSTEMS = \
MESSAGING
# End SUBSYSTEM SERVER

View File

@ -44,8 +44,14 @@ struct model_ops {
uint32_t , void *),
void *);
/* function to terminate a connection */
void (*terminate_connection)(struct event_context *, const char *reason);
/* function to create a task */
void (*new_task)(struct event_context *,
void (*)(struct event_context *, uint32_t, void *),
void *);
/* function to terminate a task */
void (*terminate)(struct event_context *, const char *reason);
};
/* this structure is used by modules to determine the size of some critical types */

View File

@ -59,17 +59,29 @@ static void single_accept_connection(struct event_context *ev,
new_conn(ev, sock2, socket_get_fd(sock), private);
}
/*
called to startup a new task
*/
static void single_new_task(struct event_context *ev,
void (*new_task)(struct event_context *, uint32_t, void *),
void *private)
{
static uint32_t taskid = 0x10000000;
new_task(ev, taskid++, private);
}
/* called when a connection goes down */
static void single_terminate_connection(struct event_context *ev, const char *reason)
/* called when a task goes down */
static void single_terminate(struct event_context *ev, const char *reason)
{
}
static const struct model_ops single_ops = {
.name = "single",
.model_init = single_model_init,
.new_task = single_new_task,
.accept_connection = single_accept_connection,
.terminate_connection = single_terminate_connection,
.terminate = single_terminate,
};
/*

View File

@ -104,11 +104,60 @@ static void standard_accept_connection(struct event_context *ev,
exit(0);
}
/* called when a connection goes down */
static void standard_terminate_connection(struct event_context *ev, const char *reason)
/*
called to create a new server task
*/
static void standard_new_task(struct event_context *ev,
void (*new_task)(struct event_context *, uint32_t , void *),
void *private)
{
DEBUG(2,("standard_terminate_connection: reason[%s]\n",reason));
pid_t pid;
struct event_context *ev2;
pid = fork();
if (pid != 0) {
/* parent or error code ... go back to the event loop */
return;
}
/* This is now the child code. We need a completely new event_context to work with */
ev2 = event_context_init(NULL);
/* the service has given us a private pointer that
encapsulates the context it needs for this new connection -
everything else will be freed */
talloc_steal(ev2, private);
/* this will free all the listening sockets and all state that
is not associated with this new connection */
talloc_free(ev);
/* tdb needs special fork handling */
if (tdb_reopen_all() == -1) {
DEBUG(0,("standard_accept_connection: tdb_reopen_all failed.\n"));
}
/* Ensure that the forked children do not expose identical random streams */
set_need_random_reseed();
/* setup this new connection */
new_task(ev2, getpid(), private);
/* we can't return to the top level here, as that event context is gone,
so we now process events in the new event context until there are no
more to process */
event_loop_wait(ev2);
talloc_free(ev2);
exit(0);
}
/* called when a task goes down */
static void standard_terminate(struct event_context *ev, const char *reason)
{
DEBUG(2,("standard_terminate: reason[%s]\n",reason));
/* this init_iconv() has the effect of freeing the iconv context memory,
which makes leak checking easier */
@ -128,7 +177,8 @@ static const struct model_ops standard_ops = {
.name = "standard",
.model_init = standard_model_init,
.accept_connection = standard_accept_connection,
.terminate_connection = standard_terminate_connection,
.new_task = standard_new_task,
.terminate = standard_terminate,
};
/*

View File

@ -105,10 +105,70 @@ static void thread_accept_connection(struct event_context *ev,
}
}
/* called when a SMB connection goes down */
static void thread_terminate_connection(struct event_context *event_ctx, const char *reason)
struct new_task_state {
struct event_context *ev;
void (*new_task)(struct event_context *, uint32_t , void *);
void *private;
};
static void *thread_task_fn(void *thread_parm)
{
DEBUG(10,("thread_terminate_connection: reason[%s]\n",reason));
struct new_task_state *new_task = talloc_get_type(thread_parm, struct new_task_state);
new_task->new_task(new_task->ev, pthread_self(), new_task->private);
/* run this connection from here */
event_loop_wait(new_task->ev);
talloc_free(new_task);
return NULL;
}
/*
called when a new task is needed
*/
static void thread_new_task(struct event_context *ev,
void (*new_task)(struct event_context *, uint32_t , void *),
void *private)
{
int rc;
pthread_t thread_id;
pthread_attr_t thread_attr;
struct new_task_state *state;
struct event_context *ev2;
ev2 = event_context_init(ev);
if (ev2 == NULL) return;
state = talloc(ev2, struct new_task_state);
if (state == NULL) {
talloc_free(ev2);
return;
}
state->new_task = new_task;
state->private = private;
state->ev = ev2;
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
rc = pthread_create(&thread_id, &thread_attr, thread_task_fn, state);
pthread_attr_destroy(&thread_attr);
if (rc == 0) {
DEBUG(4,("thread_new_task: created thread_id=%lu\n",
(unsigned long int)thread_id));
} else {
DEBUG(0,("thread_new_task: thread create failed rc=%d\n", rc));
talloc_free(ev2);
}
}
/* called when a task goes down */
static void thread_terminate(struct event_context *event_ctx, const char *reason)
{
DEBUG(10,("thread_terminate: reason[%s]\n",reason));
talloc_free(event_ctx);
@ -442,7 +502,8 @@ static const struct model_ops thread_ops = {
.name = "thread",
.model_init = thread_model_init,
.accept_connection = thread_accept_connection,
.terminate_connection = thread_terminate_connection,
.new_task = thread_new_task,
.terminate = thread_terminate,
};
/*

View File

@ -1,7 +1,7 @@
/*
Unix SMB/CIFS implementation.
hepler functions for stream based servers
helper functions for stream based servers
Copyright (C) Andrew Tridgell 2003-2005
Copyright (C) Stefan (metze) Metzmacher 2004
@ -54,7 +54,7 @@ void stream_terminate_connection(struct stream_connection *srv_conn, const char
struct event_ctx *event_ctx = srv_conn->event.ctx;
const struct model_ops *model_ops = srv_conn->model_ops;
talloc_free(srv_conn);
model_ops->terminate_connection(event_ctx, reason);
model_ops->terminate(event_ctx, reason);
}
/*

View File

@ -0,0 +1,90 @@
/*
Unix SMB/CIFS implementation.
helper functions for task based servers (nbtd, winbind etc)
Copyright (C) Andrew Tridgell 2005
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 2 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "process_model.h"
#include "events.h"
#include "smbd/service_task.h"
/*
terminate a task service
*/
void task_terminate(struct task_server *task, const char *reason)
{
struct event_ctx *event_ctx = task->event_ctx;
const struct model_ops *model_ops = task->model_ops;
talloc_free(task);
model_ops->terminate(event_ctx, reason);
}
/* used for the callback from the process model code */
struct task_state {
void (*task_init)(struct task_server *);
const struct model_ops *model_ops;
};
/*
called by the process model code when the new task starts up. This then calls
the server specific startup code
*/
static void task_server_callback(struct event_context *event_ctx, uint32_t server_id, void *private)
{
struct task_state *state = talloc_get_type(private, struct task_state);
struct task_server *task;
task = talloc(event_ctx, struct task_server);
if (task == NULL) return;
task->event_ctx = event_ctx;
task->model_ops = state->model_ops;
task->server_id = server_id;
task->msg_ctx = messaging_init(task, task->server_id, task->event_ctx);
if (!task->msg_ctx) {
task_terminate(task, "messaging_init() failed");
return;
}
state->task_init(task);
}
/*
startup a task based server
*/
NTSTATUS task_server_startup(struct event_context *event_ctx,
const struct model_ops *model_ops,
void (*task_init)(struct task_server *))
{
struct task_state *state;
state = talloc(event_ctx, struct task_state);
NT_STATUS_HAVE_NO_MEMORY(state);
state->task_init = task_init;
state->model_ops = model_ops;
model_ops->new_task(event_ctx, task_server_callback, state);
return NT_STATUS_OK;
}

View File

@ -0,0 +1,30 @@
/*
Unix SMB/CIFS implementation.
structures for task based servers
Copyright (C) Andrew Tridgell 2005
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 2 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
struct task_server {
struct event_context *event_ctx;
const struct model_ops *model_ops;
struct messaging_context *msg_ctx;
uint32_t server_id;
};

View File

@ -3,6 +3,7 @@
Main winbindd server routines
Copyright (C) Stefan Metzmacher 2005
Copyright (C) Andrew Tridgell 2005
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
@ -20,8 +21,10 @@
*/
#include "includes.h"
#include "dlinklist.h"
#include "events.h"
#include "system/time.h"
#include "smbd/service_task.h"
#include "smbd/service_stream.h"
#define WINBINDD_DIR "/tmp/.winbindd/"
#define WINBINDD_ECHO_SOCKET WINBINDD_DIR"echo"
@ -29,201 +32,163 @@
#define WINBINDD_ECHO_ADDR WINBINDD_ADDR_PREFIX"1"
#define WINBINDD_ECHO_PORT 55555
static void winbind_accept(struct server_connection *conn)
/*
state of an open winbind connection
*/
struct wbserver_connection {
DATA_BLOB blob;
struct send_queue {
struct send_queue *next, *prev;
DATA_BLOB blob;
} *queue;
};
/*
called when we get a new connection
*/
static void winbind_accept(struct stream_connection *conn)
{
DEBUG(10,("winbind_accept:\n"));
struct wbserver_connection *wbconn;
wbconn = talloc_zero(conn, struct wbserver_connection);
wbconn->blob = data_blob_talloc(wbconn, NULL, 1024);
conn->private = wbconn;
}
static DATA_BLOB tmp_blob;
static void winbind_recv(struct server_connection *conn, struct timeval t, uint16_t flags)
/*
receive some data on a winbind connection
*/
static void winbind_recv(struct stream_connection *conn, struct timeval t, uint16_t flags)
{
struct wbserver_connection *wbconn = talloc_get_type(conn->private, struct wbserver_connection);
NTSTATUS status;
size_t nread;
struct send_queue *q;
if (!tmp_blob.data) {
tmp_blob = data_blob_talloc(conn, NULL, 1024);
if (tmp_blob.data == NULL) {
return;
}
}
tmp_blob.length = 1024;
status = socket_recv(conn->socket, tmp_blob.data, tmp_blob.length, &nread, 0);
status = socket_recv(conn->socket, wbconn->blob.data, wbconn->blob.length, &nread, 0);
if (NT_STATUS_IS_ERR(status)) {
DEBUG(10,("socket_recv: %s\n",nt_errstr(status)));
talloc_free(tmp_blob.data);
server_terminate_connection(conn, "socket_recv: failed\n");
stream_terminate_connection(conn, "socket_recv: failed\n");
return;
}
tmp_blob.length = nread;
#if 0
DEBUG(0,("winbind_recv:\n"));
dump_data(0, tmp_blob.data, tmp_blob.length);
#endif
/* just reflect the data back down the socket */
q = talloc(wbconn, struct send_queue);
if (q == NULL) {
stream_terminate_connection(conn, "winbind_recv: out of memory\n");
return;
}
q->blob = data_blob_talloc(q, wbconn->blob.data, nread);
if (q->blob.data == NULL) {
stream_terminate_connection(conn, "winbind_recv: out of memory\n");
return;
}
DLIST_ADD_END(wbconn->queue, q, struct send_queue *);
conn->event.fde->flags |= EVENT_FD_WRITE;
}
static void winbind_send(struct server_connection *conn, struct timeval t, uint16_t flags)
/*
called when we can write to a connection
*/
static void winbind_send(struct stream_connection *conn, struct timeval t, uint16_t flags)
{
NTSTATUS status;
size_t sendlen;
struct wbserver_connection *wbconn = talloc_get_type(conn->private, struct wbserver_connection);
if (tmp_blob.length > 1 && tmp_blob.data[0] == (uint8_t)'q') {
while (wbconn->queue) {
struct send_queue *q = wbconn->queue;
NTSTATUS status;
size_t sendlen;
status = socket_send(conn->socket, &q->blob, &sendlen, 0);
if (NT_STATUS_IS_ERR(status)) {
DEBUG(10,("socket_send() %s\n",nt_errstr(status)));
stream_terminate_connection(conn, "socket_send: failed\n");
return;
}
if (!NT_STATUS_IS_OK(status)) {
return;
}
q->blob.length -= sendlen;
q->blob.data += sendlen;
if (q->blob.length == 0) {
DLIST_REMOVE(wbconn->queue, q);
talloc_free(q);
}
}
status = socket_send(conn->socket, &tmp_blob, &sendlen, 0);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10,("socket_send() %s\n",nt_errstr(status)));
server_terminate_connection(conn, "socket_send: failed\n");
return;
}
if (tmp_blob.length > 1 && tmp_blob.data[0] == (uint8_t)'q') {
server_terminate_connection(conn, "winbind_send: user quit\n");
return;
}
#if 0
DEBUG(0,("winbind_send:\n"));
dump_data(0, tmp_blob.data, tmp_blob.length);
#endif
tmp_blob.length -= sendlen;
if (tmp_blob.length == 0) {
conn->event.fde->flags &= ~EVENT_FD_WRITE;
}
conn->event.fde->flags &= ~EVENT_FD_WRITE;
}
static void winbind_idle(struct server_connection *conn, struct timeval t)
{
DEBUG(1,("winbind_idle: not implemented!\n"));
return;
}
static void winbind_close(struct server_connection *conn, const char *reason)
{
DEBUG(10,("winbind_close: %s\n", reason));
}
static int winbind_task_server_contect_destructor(void *ptr)
{
struct server_context *server = ptr;
server_service_shutdown(server, "exit");
return 0;
}
static void winbind_server_task_init(struct server_task *task)
{
const char *wb_task_service[] = { "winbind_task", NULL };
struct server_context *server;
DEBUG(1,("winbindsrv_task_init\n"));
server = server_service_startup("single", wb_task_service);
if (!server) {
DEBUG(0,("Starting Services (winbind_task) failed.\n"));
return;
}
task->task.private_data = talloc_steal(task, server);
task->event.ctx = event_context_merge(task->event.ctx, server->event.ctx);
server->event.ctx = talloc_reference(server, task->event.ctx);
talloc_set_destructor(server, winbind_task_server_contect_destructor);
/* wait for events */
event_loop_wait(task->event.ctx);
}
static const struct server_task_ops winbind_srver_task_ops = {
.name = "winbind_server_task",
.task_init = winbind_server_task_init
};
static const struct server_task_ops *winbind_srver_task_get_ops(void)
{
return &winbind_srver_task_ops;
}
static const struct server_stream_ops winbind_stream_ops = {
.name = "winbind",
.socket_init = NULL,
static const struct stream_server_ops winbind_stream_ops = {
.name = "smb",
.accept_connection = winbind_accept,
.recv_handler = winbind_recv,
.send_handler = winbind_send,
.idle_handler = winbind_idle,
.close_connection = winbind_close
};
static const struct server_stream_ops *winbind_get_stream_ops(void)
/*
startup the winbind task
*/
static void winbind_task_init(struct task_server *task)
{
return &winbind_stream_ops;
}
static void winbind_task_init(struct server_service *service)
{
struct server_stream_socket *stream_socket;
uint16_t port = 1;
const struct model_ops *model_ops;
NTSTATUS status;
DEBUG(1,("winbind_task_init\n"));
/* within the winbind task we want to be a single process, so
ask for the single process model ops and pass these to the
stream_setup_socket() call. */
model_ops = process_model_byname("single");
if (!model_ops) {
task_terminate(task, "Can't find 'single' process model_ops");
return;
}
/* Make sure the directory for NCALRPC exists */
if (!directory_exist(WINBINDD_DIR, NULL)) {
mkdir(WINBINDD_DIR, 0755);
}
stream_socket = service_setup_stream_socket(service, winbind_get_stream_ops(), "unix", WINBINDD_ECHO_SOCKET, &port);
if (!stream_socket) {
DEBUG(0,("service_setup_stream_socket(path=%s) failed\n",WINBINDD_ECHO_SOCKET));
status = stream_setup_socket(task->event_ctx, model_ops, &winbind_stream_ops,
"unix", WINBINDD_ECHO_SOCKET, &port, NULL);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
WINBINDD_ECHO_SOCKET, nt_errstr(status)));
task_terminate(task, "winbind Failed to find to ECHO unix socket");
return;
}
port = WINBINDD_ECHO_PORT;
stream_socket = service_setup_stream_socket(service, winbind_get_stream_ops(), "ipv4", WINBINDD_ECHO_ADDR, &port);
if (!stream_socket) {
DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) failed\n",WINBINDD_ECHO_ADDR, port));
status = stream_setup_socket(task->event_ctx, model_ops, &winbind_stream_ops,
"ipv4", WINBINDD_ECHO_ADDR, &port, NULL);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) failed - %s\n",
WINBINDD_ECHO_ADDR, port, nt_errstr(status)));
task_terminate(task, "winbind Failed to find to ECHO tcp socket");
return;
}
return;
}
static const struct server_service_ops winbind_task_ops = {
.name = "winbind_task",
.service_init = winbind_task_init,
};
const struct server_service_ops *winbind_task_get_ops(void)
/*
initialise the winbind server
*/
static NTSTATUS winbind_init(struct event_context *event_ctx, const struct model_ops *model_ops)
{
return &winbind_task_ops;
}
static void winbind_init(struct server_service *service)
{
DEBUG(1,("winbind_init\n"));
server_run_task(service, winbind_srver_task_get_ops());
return;
}
static const struct server_service_ops winbind_ops = {
.name = "winbind",
.service_init = winbind_init,
};
const struct server_service_ops *winbind_get_ops(void)
{
return &winbind_ops;
return task_server_startup(event_ctx, model_ops, winbind_task_init);
}
/*
register ourselves as a available server
*/
NTSTATUS server_service_winbind_init(void)
{
return NT_STATUS_OK;
return register_server_service("winbind", winbind_init);
}