diff options
Diffstat (limited to 'source3/smbd/conn_idle.c')
-rw-r--r-- | source3/smbd/conn_idle.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/source3/smbd/conn_idle.c b/source3/smbd/conn_idle.c new file mode 100644 index 0000000..870b2b7 --- /dev/null +++ b/source3/smbd/conn_idle.c @@ -0,0 +1,277 @@ +/* + Unix SMB/CIFS implementation. + Manage connections_struct structures + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Alexander Bokovoy 2002 + Copyright (C) Jeremy Allison 2010 + + 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 "includes.h" +#include "smbd/smbd.h" +#include "smbd/globals.h" +#include "rpc_server/rpc_pipes.h" +#include "lib/util/tevent_ntstatus.h" + +/**************************************************************************** + Update last used timestamps. +****************************************************************************/ + +static void conn_lastused_update(struct smbd_server_connection *sconn,time_t t) +{ + struct connection_struct *conn; + + for (conn=sconn->connections; conn; conn=conn->next) { + /* Update if connection wasn't idle. */ + if (conn->lastused != conn->lastused_count) { + conn->lastused = t; + conn->lastused_count = t; + } + } +} + +/**************************************************************************** + Idle inactive connections. +****************************************************************************/ + +bool conn_idle_all(struct smbd_server_connection *sconn, time_t t) +{ + int deadtime = lp_deadtime()*60; + struct connection_struct *conn; + + conn_lastused_update(sconn, t); + + if (deadtime <= 0) { + return false; + } + + for (conn=sconn->connections;conn;conn=conn->next) { + time_t age = t - conn->lastused; + + if (conn->num_files_open > 0 || age < deadtime) { + return false; + } + } + + return true; +} + +/**************************************************************************** + Forcibly unmount a share - async + All instances of the parameter 'sharename' share are unmounted. + The special sharename '*' forces unmount of all shares. +****************************************************************************/ + +static struct tevent_req *conn_force_tdis_send(connection_struct *conn); +static void conn_force_tdis_done(struct tevent_req *req); + +void conn_force_tdis( + struct smbd_server_connection *sconn, + bool (*check_fn)(struct connection_struct *conn, + void *private_data), + void *private_data) +{ + connection_struct *conn; + + /* SMB1 and SMB 2*/ + for (conn = sconn->connections; conn; conn = conn->next) { + struct smbXsrv_tcon *tcon; + bool do_close = false; + struct tevent_req *req; + + if (conn->tcon == NULL) { + continue; + } + tcon = conn->tcon; + + if (!NT_STATUS_IS_OK(tcon->status)) { + /* In the process of already being disconnected. */ + continue; + } + + do_close = check_fn(conn, private_data); + if (!do_close) { + continue; + } + + req = conn_force_tdis_send(conn); + if (req == NULL) { + DBG_WARNING("talloc_fail forcing async close of " + "share '%s'\n", + tcon->global->share_name); + continue; + } + + DBG_WARNING("Forcing close of " + "share '%s' (wire_id=0x%08x)\n", + tcon->global->share_name, + tcon->global->tcon_wire_id); + + tevent_req_set_callback(req, conn_force_tdis_done, conn); + } +} + +struct conn_force_tdis_state { + struct tevent_queue *wait_queue; +}; + +static void conn_force_tdis_wait_done(struct tevent_req *subreq); + +static struct tevent_req *conn_force_tdis_send(connection_struct *conn) +{ + struct tevent_req *req; + struct conn_force_tdis_state *state; + struct tevent_req *subreq; + files_struct *fsp; + + /* Create this off the NULL context. We must clean up on return. */ + req = tevent_req_create(NULL, &state, + struct conn_force_tdis_state); + if (req == NULL) { + return NULL; + } + state->wait_queue = tevent_queue_create(state, + "conn_force_tdis_wait_queue"); + if (tevent_req_nomem(state->wait_queue, req)) { + TALLOC_FREE(req); + return NULL; + } + + /* + * Make sure that no new request will be able to use this tcon. + * This ensures that once all outstanding fsp->aio_requests + * on this tcon are done, we are safe to close it. + */ + conn->tcon->status = NT_STATUS_NETWORK_NAME_DELETED; + + for (fsp = conn->sconn->files; fsp; fsp = fsp->next) { + if (fsp->conn != conn) { + continue; + } + /* + * Flag the file as close in progress. + * This will prevent any more IO being + * done on it. Not strictly needed, but + * doesn't hurt to flag it as closing. + */ + fsp->fsp_flags.closing = true; + + if (fsp->num_aio_requests > 0) { + /* + * Now wait until all aio requests on this fsp are + * finished. + * + * We don't set a callback, as we just want to block the + * wait queue and the talloc_free() of fsp->aio_request + * will remove the item from the wait queue. + */ + subreq = tevent_queue_wait_send(fsp->aio_requests, + conn->sconn->ev_ctx, + state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + TALLOC_FREE(req); + return NULL; + } + } + } + /* + * Now we add our own waiter to the end of the queue, + * this way we get notified when all pending requests are finished + * and reply to the outstanding SMB1 request. + */ + subreq = tevent_queue_wait_send(state, + conn->sconn->ev_ctx, + state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + TALLOC_FREE(req); + return NULL; + } + + tevent_req_set_callback(subreq, conn_force_tdis_wait_done, req); + return req; +} + +static void conn_force_tdis_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + + tevent_queue_wait_recv(subreq); + TALLOC_FREE(subreq); + tevent_req_done(req); +} + +static NTSTATUS conn_force_tdis_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +static void conn_force_tdis_done(struct tevent_req *req) +{ + connection_struct *conn = tevent_req_callback_data( + req, connection_struct); + NTSTATUS status; + uint64_t vuid = UID_FIELD_INVALID; + struct smbXsrv_tcon *tcon = conn->tcon; + struct smbd_server_connection *sconn = conn->sconn; + + status = conn_force_tdis_recv(req); + TALLOC_FREE(req); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("conn_force_tdis_recv of share '%s' " + "(wire_id=0x%08x) failed: %s\n", + tcon->global->share_name, + tcon->global->tcon_wire_id, + nt_errstr(status)); + return; + } + + if (conn->sconn->using_smb2) { + vuid = conn->vuid; + } + + DBG_WARNING("Closing " + "share '%s' (wire_id=0x%08x)\n", + tcon->global->share_name, + tcon->global->tcon_wire_id); + + conn = NULL; + status = smbXsrv_tcon_disconnect(tcon, vuid); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("smbXsrv_tcon_disconnect() of share '%s' " + "(wire_id=0x%08x) failed: %s\n", + tcon->global->share_name, + tcon->global->tcon_wire_id, + nt_errstr(status)); + return; + } + + TALLOC_FREE(tcon); + + /* + * As we've been awoken, we may have changed + * uid in the meantime. Ensure we're still root. + */ + change_to_root_user(); + /* + * Use 'false' in the last parameter (test) to force + * a full reload of services. Prevents + * reload_services caching the fact it's + * been called multiple times in a row. + * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=14604 + * for details. + */ + reload_services(sconn, conn_snum_used, false); +} |