return result;
}
+static void smbd_sig_term_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ exit_server_cleanly("termination signal");
+}
+
+void smbd_setup_sig_term_handler(void)
+{
+ struct tevent_signal *se;
+
+ se = tevent_add_signal(smbd_event_context(),
+ smbd_event_context(),
+ SIGTERM, 0,
+ smbd_sig_term_handler,
+ NULL);
+ if (!se) {
+ exit_server("failed to setup SIGTERM handler");
+ }
+}
+
+static void smbd_sig_hup_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ change_to_root_user();
+ DEBUG(1,("Reloading services after SIGHUP\n"));
+ reload_services(False);
+}
+
+void smbd_setup_sig_hup_handler(void)
+{
+ struct tevent_signal *se;
+
+ se = tevent_add_signal(smbd_event_context(),
+ smbd_event_context(),
+ SIGHUP, 0,
+ smbd_sig_hup_handler,
+ NULL);
+ if (!se) {
+ exit_server("failed to setup SIGHUP handler");
+ }
+}
+
/****************************************************************************
Do all async processing in here. This includes kernel oplock messages, change
notify events etc.
****************************************************************************/
-static void async_processing(fd_set *pfds)
+static void async_processing(void)
{
DEBUG(10,("async_processing: Doing async processing.\n"));
process_aio_queue();
- process_kernel_oplocks(smbd_messaging_context(), pfds);
+ process_kernel_oplocks(smbd_messaging_context());
/* Do the aio check again after receive_local_message as it does a
select and may have eaten our signal. */
/* Is this till true? -- vl */
process_aio_queue();
-
- if (got_sig_term) {
- exit_server_cleanly("termination signal");
- }
-
- /* check for sighup processing */
- if (reload_after_sighup) {
- change_to_root_user();
- DEBUG(1,("Reloading services after SIGHUP\n"));
- reload_services(False);
- reload_after_sighup = 0;
- }
-}
-
-/****************************************************************************
- Add a fd to the set we will be select(2)ing on.
-****************************************************************************/
-
-static int select_on_fd(int fd, int maxfd, fd_set *fds)
-{
- if (fd != -1) {
- FD_SET(fd, fds);
- maxfd = MAX(maxfd, fd);
- }
-
- return maxfd;
}
/****************************************************************************
to.tv_sec = SMBD_SELECT_TIMEOUT;
to.tv_usec = 0;
- /*
- * Note that this call must be before processing any SMB
- * messages as we need to synchronously process any messages
- * we may have sent to ourselves from the previous SMB.
- */
- message_dispatch(smbd_messaging_context());
-
/*
* Setup the select fd sets.
*/
* This is hideously complex - *MUST* be simplified for 3.0 ! JRA.
*/
- if (oplock_message_waiting(&r_fds)) {
+ if (oplock_message_waiting()) {
DEBUG(10,("receive_message_or_smb: oplock_message is waiting.\n"));
- async_processing(&r_fds);
+ async_processing();
/*
* After async processing we must go and do the select again, as
* the state of the flag in fds for the server file descriptor is
&r_fds, &w_fds, &to, &maxfd);
}
- if (timeval_is_zero(&to)) {
- /* Process a timed event now... */
- if (run_events(smbd_event_context(), 0, NULL, NULL)) {
- return NT_STATUS_RETRY;
- }
+ /* Process a signal and timed events now... */
+ if (run_events(smbd_event_context(), 0, NULL, NULL)) {
+ return NT_STATUS_RETRY;
}
-
+
{
int sav;
START_PROFILE(smbd_idle);
- maxfd = select_on_fd(oplock_notify_fd(), maxfd, &r_fds);
-
selrtn = sys_select(maxfd+1,&r_fds,&w_fds,NULL,&to);
sav = errno;
is the best we can do until the oplock code knows more about
signals */
if (selrtn == -1 && errno == EINTR) {
- async_processing(&r_fds);
+ async_processing();
/*
* After async processing we must go and do the select again, as
* the state of the flag in fds for the server file descriptor is
return NT_STATUS_RETRY;
}
- /*
- * Ensure we process oplock break messages by preference.
- * This is IMPORTANT ! Otherwise we can starve other processes
- * sending us an oplock break message. JRA.
- */
-
- if (oplock_message_waiting(&r_fds)) {
- async_processing(&r_fds);
- /*
- * After async processing we must go and do the select again, as
- * the state of the flag in fds for the server file descriptor is
- * indeterminate - we may have done I/O on it in the oplock processing. JRA.
- */
- return NT_STATUS_RETRY;
- }
-
- /*
- * We've just woken up from a protentially long select sleep.
- * Ensure we process local messages as we need to synchronously
- * process any messages from other smbd's to avoid file rename race
- * conditions. This call is cheap if there are no messages waiting.
- * JRA.
- */
- message_dispatch(smbd_messaging_context());
-
- return NT_STATUS_OK;
+ /* should not be reached */
+ return NT_STATUS_INTERNAL_ERROR;
}
/*
return;
}
- process_kernel_oplocks(smbd_messaging_context(), NULL);
+ process_kernel_oplocks(smbd_messaging_context());
return;
}
set_current_user_info(
vuser->server_info->sanitized_username,
vuser->server_info->unix_name,
- pdb_get_fullname(vuser->server_info
- ->sam_account),
pdb_get_domain(vuser->server_info
->sam_account));
}
construct_reply_common(req, (char *)req->inbuf, outbuf);
}
-/****************************************************************************
- Construct a chained reply and add it to the already made reply
-****************************************************************************/
-
-#if 0
-
-void chain_reply(struct smb_request *req)
-{
- /*
- * Dirty little const_discard: We mess with req->inbuf, which is
- * declared as const. If maybe at some point this routine gets
- * rewritten, this const_discard could go away.
- */
- char *inbuf = CONST_DISCARD(char *, req->inbuf);
- int size = smb_len(req->inbuf)+4;
-
- int smb_com1, smb_com2 = CVAL(inbuf,smb_vwv0);
- unsigned smb_off2 = SVAL(inbuf,smb_vwv1);
- char *inbuf2;
- int outsize2;
- int new_size;
- char inbuf_saved[smb_wct];
- char *outbuf = (char *)req->outbuf;
- size_t outsize = smb_len(outbuf) + 4;
- size_t outsize_padded;
- size_t padding;
- size_t ofs, to_move;
-
- struct smb_request *req2;
- size_t caller_outputlen;
- char *caller_output;
-
- /* Maybe its not chained, or it's an error packet. */
- if (smb_com2 == 0xFF || SVAL(outbuf,smb_rcls) != 0) {
- SCVAL(outbuf,smb_vwv0,0xFF);
- return;
- }
-
- if (chain_size == 0) {
- /* this is the first part of the chain */
- orig_inbuf = inbuf;
- }
-
- /*
- * We need to save the output the caller added to the chain so that we
- * can splice it into the final output buffer later.
- */
-
- caller_outputlen = outsize - smb_wct;
-
- caller_output = (char *)memdup(outbuf + smb_wct, caller_outputlen);
-
- if (caller_output == NULL) {
- /* TODO: NT_STATUS_NO_MEMORY */
- smb_panic("could not dup outbuf");
- }
-
- /*
- * The original Win95 redirector dies on a reply to
- * a lockingX and read chain unless the chain reply is
- * 4 byte aligned. JRA.
- */
-
- outsize_padded = (outsize + 3) & ~3;
- padding = outsize_padded - outsize;
-
- /*
- * remember how much the caller added to the chain, only counting
- * stuff after the parameter words
- */
- chain_size += (outsize_padded - smb_wct);
-
- /*
- * work out pointers into the original packets. The
- * headers on these need to be filled in
- */
- inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct;
-
- /* remember the original command type */
- smb_com1 = CVAL(orig_inbuf,smb_com);
-
- /* save the data which will be overwritten by the new headers */
- memcpy(inbuf_saved,inbuf2,smb_wct);
-
- /* give the new packet the same header as the last part of the SMB */
- memmove(inbuf2,inbuf,smb_wct);
-
- /* create the in buffer */
- SCVAL(inbuf2,smb_com,smb_com2);
-
- /* work out the new size for the in buffer. */
- new_size = size - (inbuf2 - inbuf);
- if (new_size < 0) {
- DEBUG(0,("chain_reply: chain packet size incorrect "
- "(orig size = %d, offset = %d)\n",
- size, (int)(inbuf2 - inbuf) ));
- exit_server_cleanly("Bad chained packet");
- return;
- }
-
- /* And set it in the header. */
- smb_setlen(inbuf2, new_size - 4);
-
- DEBUG(3,("Chained message\n"));
- show_msg(inbuf2);
-
- if (!(req2 = talloc(talloc_tos(), struct smb_request))) {
- smb_panic("could not allocate smb_request");
- }
- init_smb_request(req2, (uint8 *)inbuf2,0, req->encrypted);
- req2->inbuf = (uint8_t *)inbuf2;
- req2->chain_fsp = req->chain_fsp;
-
- /* process the request */
- switch_message(smb_com2, req2, new_size);
-
- /*
- * We don't accept deferred operations in chained requests.
- */
- SMB_ASSERT(req2->outbuf != NULL);
- outsize2 = smb_len(req2->outbuf)+4;
-
- /*
- * Move away the new command output so that caller_output fits in,
- * copy in the caller_output saved above.
- */
-
- SMB_ASSERT(outsize_padded >= smb_wct);
-
- /*
- * "ofs" is the space we need for caller_output. Equal to
- * caller_outputlen plus the padding.
- */
-
- ofs = outsize_padded - smb_wct;
-
- /*
- * "to_move" is the amount of bytes the secondary routine gave us
- */
-
- to_move = outsize2 - smb_wct;
-
- if (to_move + ofs + smb_wct + chain_size > max_send) {
- smb_panic("replies too large -- would have to cut");
- }
-
- /*
- * In the "new" API "outbuf" is allocated via reply_outbuf, just for
- * the first request in the chain. So we have to re-allocate it. In
- * the "old" API the only outbuf ever used is the global OutBuffer
- * which is always large enough.
- */
-
- outbuf = TALLOC_REALLOC_ARRAY(NULL, outbuf, char,
- to_move + ofs + smb_wct);
- if (outbuf == NULL) {
- smb_panic("could not realloc outbuf");
- }
-
- req->outbuf = (uint8 *)outbuf;
-
- memmove(outbuf + smb_wct + ofs, req2->outbuf + smb_wct, to_move);
- memcpy(outbuf + smb_wct, caller_output, caller_outputlen);
-
- /*
- * copy the new reply header over the old one but preserve the smb_com
- * field
- */
- memmove(outbuf, req2->outbuf, smb_wct);
- SCVAL(outbuf, smb_com, smb_com1);
-
- /*
- * We've just copied in the whole "wct" area from the secondary
- * function. Fix up the chaining: com2 and the offset need to be
- * readjusted.
- */
-
- SCVAL(outbuf, smb_vwv0, smb_com2);
- SSVAL(outbuf, smb_vwv1, chain_size + smb_wct - 4);
-
- if (padding != 0) {
-
- /*
- * Due to padding we have some uninitialized bytes after the
- * caller's output
- */
-
- memset(outbuf + outsize, 0, padding);
- }
-
- smb_setlen(outbuf, outsize2 + caller_outputlen + padding - 4);
-
- /*
- * restore the saved data, being careful not to overwrite any data
- * from the reply header
- */
- memcpy(inbuf2,inbuf_saved,smb_wct);
-
- SAFE_FREE(caller_output);
- TALLOC_FREE(req2);
-
- /*
- * Reset the chain_size for our caller's offset calculations
- */
-
- chain_size -= (outsize_padded - smb_wct);
-
- return;
-}
-
-#else
-
/*
* How many bytes have we already accumulated up to the current wct field
* offset?
SCVAL(req->outbuf, smb_vwv0, 0xff);
}
+/****************************************************************************
+ Construct a chained reply and add it to the already made reply
+****************************************************************************/
+
void chain_reply(struct smb_request *req)
{
size_t smblen = smb_len(req->inbuf);
/*
* In req->chain_outbuf we collect all the replies. Start the
* chain by copying in the first reply.
+ *
+ * We do the realloc because later on we depend on
+ * talloc_get_size to determine the length of
+ * chain_outbuf. The reply_xxx routines might have
+ * over-allocated (reply_pipe_read_and_X used to be such an
+ * example).
*/
- req->chain_outbuf = req->outbuf;
+ req->chain_outbuf = TALLOC_REALLOC_ARRAY(
+ req, req->outbuf, uint8_t, smb_len(req->outbuf) + 4);
+ if (req->chain_outbuf == NULL) {
+ goto error;
+ }
req->outbuf = NULL;
} else {
if (!smb_splice_chain(&req->chain_outbuf,
}
}
-#endif
-
/****************************************************************************
Check if services need reloading.
****************************************************************************/
mypid = getpid();
}
- if (reload_after_sighup || (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK)) {
+ if (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK) {
reload_services(True);
- reload_after_sighup = False;
last_smb_conf_reload_time = t;
}
}
}
+
+/****************************************************************************
+received when we should release a specific IP
+****************************************************************************/
+static void release_ip(const char *ip, void *priv)
+{
+ char addr[INET6_ADDRSTRLEN];
+
+ if (strcmp(client_socket_addr(get_client_fd(),addr,sizeof(addr)), ip) == 0) {
+ /* we can't afford to do a clean exit - that involves
+ database writes, which would potentially mean we
+ are still running after the failover has finished -
+ we have to get rid of this process ID straight
+ away */
+ DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n",
+ ip));
+ /* note we must exit with non-zero status so the unclean handler gets
+ called in the parent, so that the brl database is tickled */
+ _exit(1);
+ }
+}
+
+static void msg_release_ip(struct messaging_context *msg_ctx, void *private_data,
+ uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
+{
+ release_ip((char *)data->data, NULL);
+}
+
+#ifdef CLUSTER_SUPPORT
+static int client_get_tcp_info(struct sockaddr_storage *server,
+ struct sockaddr_storage *client)
+{
+ socklen_t length;
+ if (server_fd == -1) {
+ return -1;
+ }
+ length = sizeof(*server);
+ if (getsockname(server_fd, (struct sockaddr *)server, &length) != 0) {
+ return -1;
+ }
+ length = sizeof(*client);
+ if (getpeername(server_fd, (struct sockaddr *)client, &length) != 0) {
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * Send keepalive packets to our client
+ */
+static bool keepalive_fn(const struct timeval *now, void *private_data)
+{
+ if (!send_keepalive(smbd_server_fd())) {
+ DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
+ return False;
+ }
+ return True;
+}
+
+/*
+ * Do the recurring check if we're idle
+ */
+static bool deadtime_fn(const struct timeval *now, void *private_data)
+{
+ if ((conn_num_open() == 0)
+ || (conn_idle_all(now->tv_sec))) {
+ DEBUG( 2, ( "Closing idle connection\n" ) );
+ messaging_send(smbd_messaging_context(), procid_self(),
+ MSG_SHUTDOWN, &data_blob_null);
+ return False;
+ }
+
+ return True;
+}
+
+/*
+ * Do the recurring log file and smb.conf reload checks.
+ */
+
+static bool housekeeping_fn(const struct timeval *now, void *private_data)
+{
+ change_to_root_user();
+
+ /* update printer queue caches if necessary */
+ update_monitored_printq_cache();
+
+ /* check if we need to reload services */
+ check_reload(time(NULL));
+
+ /* Change machine password if neccessary. */
+ attempt_machine_password_change();
+
+ /*
+ * Force a log file check.
+ */
+ force_check_log_size();
+ check_log_size();
+ return true;
+}
+
/****************************************************************************
Process commands from the client
****************************************************************************/
void smbd_process(void)
{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char remaddr[INET6_ADDRSTRLEN];
+
+ smbd_server_conn = talloc_zero(smbd_event_context(), struct smbd_server_connection);
+ if (!smbd_server_conn) {
+ exit_server("failed to create smbd_server_connection");
+ }
+
+ /* Ensure child is set to blocking mode */
+ set_blocking(smbd_server_fd(),True);
+
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), lp_socket_options());
+
+ /* this is needed so that we get decent entries
+ in smbstatus for port 445 connects */
+ set_remote_machine_name(get_peer_addr(smbd_server_fd(),
+ remaddr,
+ sizeof(remaddr)),
+ false);
+ reload_services(true);
+
/*
* Before the first packet, check the global hosts allow/ hosts deny
* parameters before doing any parsing of packets passed to us by the
exit_server_cleanly("connection denied");
}
- max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
+ static_init_rpc;
- smbd_server_conn = talloc_zero(smbd_event_context(), struct smbd_server_connection);
- if (!smbd_server_conn) {
- exit_server("failed to create smbd_server_connection");
+ init_modules();
+
+ if (!init_account_policy()) {
+ exit_server("Could not open account policy tdb.\n");
+ }
+
+ if (*lp_rootdir()) {
+ if (chroot(lp_rootdir()) != 0) {
+ DEBUG(0,("Failed changed root to %s\n", lp_rootdir()));
+ exit_server("Failed to chroot()");
+ }
+ DEBUG(0,("Changed root to %s\n", lp_rootdir()));
}
+
+ /* Setup oplocks */
+ if (!init_oplocks(smbd_messaging_context()))
+ exit_server("Failed to init oplocks");
+
+ /* Setup aio signal handler. */
+ initialize_async_io_handler();
+
+ /* register our message handlers */
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_SMB_FORCE_TDIS, msg_force_tdis);
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_SMB_RELEASE_IP, msg_release_ip);
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_SMB_CLOSE_FILE, msg_close_file);
+
+ if ((lp_keepalive() != 0)
+ && !(event_add_idle(smbd_event_context(), NULL,
+ timeval_set(lp_keepalive(), 0),
+ "keepalive", keepalive_fn,
+ NULL))) {
+ DEBUG(0, ("Could not add keepalive event\n"));
+ exit(1);
+ }
+
+ if (!(event_add_idle(smbd_event_context(), NULL,
+ timeval_set(IDLE_CLOSED_TIMEOUT, 0),
+ "deadtime", deadtime_fn, NULL))) {
+ DEBUG(0, ("Could not add deadtime event\n"));
+ exit(1);
+ }
+
+ if (!(event_add_idle(smbd_event_context(), NULL,
+ timeval_set(SMBD_SELECT_TIMEOUT, 0),
+ "housekeeping", housekeeping_fn, NULL))) {
+ DEBUG(0, ("Could not add housekeeping event\n"));
+ exit(1);
+ }
+
+#ifdef CLUSTER_SUPPORT
+
+ if (lp_clustering()) {
+ /*
+ * We need to tell ctdb about our client's TCP
+ * connection, so that for failover ctdbd can send
+ * tickle acks, triggering a reconnection by the
+ * client.
+ */
+
+ struct sockaddr_storage srv, clnt;
+
+ if (client_get_tcp_info(&srv, &clnt) == 0) {
+
+ NTSTATUS status;
+
+ status = ctdbd_register_ips(
+ messaging_ctdbd_connection(),
+ &srv, &clnt, release_ip, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("ctdbd_register_ips failed: %s\n",
+ nt_errstr(status)));
+ }
+ } else
+ {
+ DEBUG(0,("Unable to get tcp info for "
+ "CTDB_CONTROL_TCP_CLIENT: %s\n",
+ strerror(errno)));
+ }
+ }
+
+#endif
+
+ max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
+
smbd_server_conn->fde = event_add_fd(smbd_event_context(),
smbd_server_conn,
smbd_server_fd(),
exit_server("failed to create smbd_server_connection fde");
}
+ TALLOC_FREE(frame);
+
while (True) {
NTSTATUS status;
- TALLOC_CTX *frame = talloc_stackframe_pool(8192);
+
+ frame = talloc_stackframe_pool(8192);
errno = 0;
!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("smbd_server_connection_loop_once failed: %s,"
" exiting\n", nt_errstr(status)));
- return;
+ break;
}
TALLOC_FREE(frame);
}
+
+ exit_server_cleanly(NULL);
}