More information on the tevent library is available at
https://tevent.samba.org/tevent_tutorial.html
We look at the code patch at the start of the smbd process. We start in main().
int main(int argc,const char *argv[]) { .. struct tevent_context *ev_ctx; .. /* * Initialize the event context. The event context needs to be * initialized before the messaging context, cause the messaging * context holds an event context. */ // This eventually returns tevent_context_init(NULL) ev_ctx = server_event_context(); if (ev_ctx == NULL) { exit(1); } .. //Read notes on this function below. if (!open_sockets_smbd(parent, ev_ctx, msg_ctx, ports)) exit_server("open_sockets_smbd() failed"); .. //Loop and wait for events. //This function is where the tevent contexts such as handling //incoming requests or reading new information on the socket. smbd_parent_loop(ev_ctx, parent); .. }
The function which opens sockets and adds the necessary tevents required to handle socket communication
static bool open_sockets_smbd(struct smbd_parent_context *parent, struct tevent_context *ev_ctx, struct messaging_context *msg_ctx, const char *smb_ports) { .. /* * If we fail to open any sockets * in this loop the parent-sockets == NULL * case below will prevent us from starting. */ (void)smbd_open_one_socket(parent, ev_ctx, &ss, port); .. } static bool smbd_open_one_socket(struct smbd_parent_context *parent, struct tevent_context *ev_ctx, const struct sockaddr_storage *ifss, uint16_t port) { .. s->fd = open_socket_in(SOCK_STREAM, port, parent->sockets == NULL ? 0 : 2, ifss, true); .. /* ready to listen */ set_socket_options(s->fd, "SO_KEEPALIVE"); set_socket_options(s->fd, lp_socket_options()); /* Set server socket to * non-blocking for the accept. */ set_blocking(s->fd, False); .. //This sets the tevent context for the parent sockets. //The handling function smbd_accept_connection() //is responsible for accepting new client connections. s->fde = tevent_add_fd(ev_ctx, s, s->fd, TEVENT_FD_READ, smbd_accept_connection, s); .. tevent_fd_set_close_fn(s->fde, smbd_open_socket_close_fn); .. }
On the parent processes, the process loops waiting for events to be handled.
int main(int argc,const char *argv[]) { .. smbd_parent_loop(ev_ctx, parent); .. }
A new incoming request triggers the tevent context for the fd which has the handling function set to
smbd_accept_connection().
static void smbd_accept_connection(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *private_data) { .. pid = fork(); //For child process if (pid == 0) { .. //Process the incoming request. smbd_process(ev, msg_ctx, fd, false); exit: exit_server_cleanly("end of child"); return; } //For the parent process ie. main smbd process. /* The parent doesn't need this socket */ close(fd); .. if (pid != 0) { add_child_pid(s->parent, pid); } .. }
void smbd_process(struct tevent_context *ev_ctx, struct messaging_context *msg_ctx, int sock_fd, bool interactive) { .. struct smbXsrv_client *client = NULL; .. struct smbXsrv_connection *xconn = NULL; .. //Create a new struct to store information about this client status = smbXsrv_client_create(ev_ctx, ev_ctx, msg_ctx, now, &client); .. //The connection information itself is stored in xconn status = smbd_add_connection(client, sock_fd, &xconn); .. //Loop and wait for new events. ret = tevent_loop_wait(ev_ctx); .. }
With multichannel support, we can have multiple connections connected to the same client. We do not consider multichannel in this document.
NTSTATUS smbd_add_connection(struct smbXsrv_client *client, int sock_fd, struct smbXsrv_connection **_xconn) { .. xconn = talloc_zero(client, struct smbXsrv_connection); .. xconn->transport.fde = tevent_add_fd(client->ev_ctx, xconn, sock_fd, TEVENT_FD_READ, smbd_server_connection_handler, xconn); .. /* for now we only have one connection */ DLIST_ADD_END(client->connections, xconn); xconn->client = client; .. }
The tevent handler for this socket is now changed to smbd_server_connection_handler().
The child smbd process is now attached to a single client connection. It loops in smbd_process()
void smbd_process(struct tevent_context *ev_ctx, struct messaging_context *msg_ctx, int sock_fd, bool interactive) { .. //Loop and wait for new events. ret = tevent_loop_wait(ev_ctx); .. }
Any new data now sent to this socket will trigger the event handler smbd_server_connection_handler().
static void smbd_server_connection_handler(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *private_data) { .. if (!NT_STATUS_IS_OK(xconn->transport.status)) { /* * we're not supposed to do any io */ TEVENT_FD_NOT_READABLE(xconn->transport.fde); TEVENT_FD_NOT_WRITEABLE(xconn->transport.fde); return; } .. if (flags & TEVENT_FD_WRITE) { smbd_server_connection_write_handler(xconn); return; } if (flags & TEVENT_FD_READ) { smbd_server_connection_read_handler(xconn, xconn->transport.sock); return; } } static void smbd_server_connection_read_handler( struct smbXsrv_connection *xconn, int fd) { .. status = receive_smb_talloc(mem_ctx, xconn, fd, (char **)(void *)&inbuf, 0, /* timeout */ &unread_bytes, &encrypted, &inbuf_len, &seqnum, !from_client /* trusted channel */); .. process_smb(xconn, inbuf, inbuf_len, unread_bytes, seqnum, encrypted, NULL); }