summaryrefslogtreecommitdiff
path: root/source3/smbd/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/process.c')
-rw-r--r--source3/smbd/process.c501
1 files changed, 298 insertions, 203 deletions
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index 95222d3f51..7e94ffa173 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -23,7 +23,7 @@
extern int DEBUGLEVEL;
-time_t smb_last_time=(time_t)0;
+struct timeval smb_last_time;
char *InBuffer = NULL;
char *OutBuffer = NULL;
@@ -48,7 +48,7 @@ extern char *last_inbuf;
extern char *InBuffer;
extern char *OutBuffer;
extern int smb_read_error;
-extern BOOL reload_after_sighup;
+extern VOLATILE SIG_ATOMIC_T reload_after_sighup;
extern BOOL global_machine_password_needs_changing;
extern fstring global_myworkgroup;
extern pstring global_myname;
@@ -173,7 +173,7 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len,
to.tv_sec = timeout / 1000;
to.tv_usec = (timeout % 1000) * 1000;
- selrtn = sys_select(MAX(maxfd,Client)+1,&fds,NULL, timeout>0?&to:NULL);
+ selrtn = sys_select(MAX(maxfd,Client)+1,&fds,timeout>0?&to:NULL);
/* Check if error */
if(selrtn == -1) {
@@ -231,6 +231,52 @@ BOOL receive_next_smb(char *inbuf, int bufsize, int timeout)
return ret;
}
+/****************************************************************************
+ We're terminating and have closed all our files/connections etc.
+ If there are any pending local messages we need to respond to them
+ before termination so that other smbds don't think we just died whilst
+ holding oplocks.
+****************************************************************************/
+
+void respond_to_all_remaining_local_messages(void)
+{
+ char buffer[1024];
+ fd_set fds;
+
+ /*
+ * Assert we have no exclusive open oplocks.
+ */
+
+ if(get_number_of_exclusive_open_oplocks()) {
+ DEBUG(0,("respond_to_all_remaining_local_messages: PANIC : we have %d exclusive oplocks.\n",
+ get_number_of_exclusive_open_oplocks() ));
+ return;
+ }
+
+ /*
+ * Setup the select read fd set.
+ */
+
+ FD_ZERO(&fds);
+ if(!setup_oplock_select_set(&fds))
+ return;
+
+ /*
+ * Keep doing receive_local_message with a 1 ms timeout until
+ * we have no more messages.
+ */
+
+ while(receive_local_message(&fds, buffer, sizeof(buffer), 1)) {
+ /* Deal with oplock break requests from other smbd's. */
+ process_local_message(buffer, sizeof(buffer));
+
+ FD_ZERO(&fds);
+ (void)setup_oplock_select_set(&fds);
+ }
+
+ return;
+}
+
/*
These flags determine some of the permissions required to do an operation
@@ -283,8 +329,8 @@ struct smb_message_struct
{SMBunlink,"SMBunlink",reply_unlink,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK},
{SMBread,"SMBread",reply_read,AS_USER},
- {SMBwrite,"SMBwrite",reply_write,AS_USER | CAN_IPC},
- {SMBclose,"SMBclose",reply_close,AS_USER | CAN_IPC},
+ {SMBwrite,"SMBwrite",reply_write,AS_USER | CAN_IPC },
+ {SMBclose,"SMBclose",reply_close,AS_USER | CAN_IPC },
{SMBmkdir,"SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE},
{SMBrmdir,"SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE},
{SMBdskattr,"SMBdskattr",reply_dskattr,AS_USER},
@@ -319,9 +365,9 @@ struct smb_message_struct
{SMBwriteBmpx,"SMBwriteBmpx",reply_writebmpx,AS_USER},
{SMBwriteBs,"SMBwriteBs",reply_writebs,AS_USER},
{SMBwritec,"SMBwritec",NULL,AS_USER},
- {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE},
- {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER},
- {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC},
+ {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE },
+ {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER },
+ {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK},
{SMBtranss,"SMBtranss",NULL,AS_USER | CAN_IPC},
{SMBioctls,"SMBioctls",NULL,AS_USER},
{SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
@@ -330,7 +376,7 @@ struct smb_message_struct
{SMBopenX,"SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
{SMBreadX,"SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
{SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC },
- {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER},
+ {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER },
{SMBffirst,"SMBffirst",reply_search,AS_USER},
{SMBfunique,"SMBfunique",reply_search,AS_USER},
@@ -339,14 +385,14 @@ struct smb_message_struct
/* LANMAN2.0 PROTOCOL FOLLOWS */
{SMBfindnclose, "SMBfindnclose", reply_findnclose, AS_USER},
{SMBfindclose, "SMBfindclose", reply_findclose,AS_USER},
- {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER | CAN_IPC},
+ {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER | QUEUE_IN_OPLOCK },
{SMBtranss2, "SMBtranss2", reply_transs2, AS_USER},
/* NT PROTOCOL FOLLOWS */
{SMBntcreateX, "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
- {SMBnttrans, "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC },
+ {SMBnttrans, "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK},
{SMBnttranss, "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
- {SMBntcancel, "SMBntcancel", reply_ntcancel, AS_USER },
+ {SMBntcancel, "SMBntcancel", reply_ntcancel, 0 },
/* messaging routines */
{SMBsends,"SMBsends",reply_sends,AS_GUEST},
@@ -368,14 +414,14 @@ do a switch on the message type, and return the response size
****************************************************************************/
static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize)
{
- static int pid= -1;
+ static pid_t pid= (pid_t)-1;
int outsize = 0;
static int num_smb_messages =
sizeof(smb_messages) / sizeof(struct smb_message_struct);
int match;
extern int Client;
- if (pid == -1)
+ if (pid == (pid_t)-1)
pid = getpid();
errno = 0;
@@ -399,21 +445,25 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize
}
else
{
- DEBUG(3,("switch message %s (pid %d)\n",smb_messages[match].name,pid));
+ DEBUG(3,("switch message %s (pid %d)\n",smb_messages[match].name,(int)pid));
- if(global_oplock_break && (smb_messages[match].flags & QUEUE_IN_OPLOCK))
+ if(global_oplock_break)
{
- /*
- * Queue this message as we are the process of an oplock break.
- */
+ int flags = smb_messages[match].flags;
- DEBUG( 2, ( "switch_message: queueing message due to being in " ) );
- DEBUGADD( 2, ( "oplock break state.\n" ) );
+ if(flags & QUEUE_IN_OPLOCK)
+ {
+ /*
+ * Queue this message as we are the process of an oplock break.
+ */
- push_oplock_pending_smb_message( inbuf, size );
- return -1;
- }
+ DEBUG( 2, ( "switch_message: queueing message due to being in " ) );
+ DEBUGADD( 2, ( "oplock break state.\n" ) );
+ push_oplock_pending_smb_message( inbuf, size );
+ return -1;
+ }
+ }
if (smb_messages[match].fn)
{
int flags = smb_messages[match].flags;
@@ -508,7 +558,7 @@ static int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
int msg_type = CVAL(inbuf,0);
extern int chain_size;
- smb_last_time = time(NULL);
+ GetTimeOfDay(&smb_last_time);
chain_size = 0;
file_chain_reset();
@@ -640,7 +690,7 @@ char *smb_fn_name(int type)
void construct_reply_common(char *inbuf,char *outbuf)
{
- bzero(outbuf,smb_size);
+ memset(outbuf,'\0',smb_size);
set_message(outbuf,0,0,True);
CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com);
@@ -687,6 +737,14 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
orig_outbuf = 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 = (outsize + 3) & ~3;
+
/* we need to tell the client where the next part of the reply will be */
SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf));
CVAL(outbuf,smb_vwv0) = smb_com2;
@@ -741,234 +799,271 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
}
/****************************************************************************
- process commands from the client
+ Setup the needed select timeout.
****************************************************************************/
-void smbd_process(void)
+
+static int setup_select_timeout(void)
{
- extern int Client;
- extern int ClientPort;
+ int change_notify_timeout = lp_change_notify_timeout() * 1000;
+ int select_timeout;
- InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
- OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
- if ((InBuffer == NULL) || (OutBuffer == NULL))
- return;
+ /*
+ * Increase the select timeout back to SMBD_SELECT_TIMEOUT if we
+ * have removed any blocking locks. JRA.
+ */
- InBuffer += SMB_ALIGNMENT;
- OutBuffer += SMB_ALIGNMENT;
+ select_timeout = blocking_locks_pending() ? SMBD_SELECT_TIMEOUT_WITH_PENDING_LOCKS*1000 :
+ SMBD_SELECT_TIMEOUT*1000;
+
+ if (change_notifies_pending())
+ select_timeout = MIN(select_timeout, change_notify_timeout);
+
+ return select_timeout;
+}
+
+/****************************************************************************
+ Check if services need reloading.
+****************************************************************************/
+
+void check_reload(int t)
+{
+ static time_t last_smb_conf_reload_time = 0;
+
+ if(last_smb_conf_reload_time == 0)
+ last_smb_conf_reload_time = t;
-#if PRIME_NMBD
- DEBUG(3,("priming nmbd\n"));
+ if (reload_after_sighup || (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK))
{
- struct in_addr ip;
- ip = *interpret_addr2("localhost");
- if (zero_ip(ip)) ip = *interpret_addr2("127.0.0.1");
- *OutBuffer = 0;
- send_one_packet(OutBuffer,1,ip,NMB_PORT,SOCK_DGRAM);
+ reload_services(True);
+ reload_after_sighup = False;
+ last_smb_conf_reload_time = t;
}
-#endif
+}
+/****************************************************************************
+ Process any timeout housekeeping. Return False if the caler should exit.
+****************************************************************************/
- max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
+static BOOL timeout_processing(int deadtime, int *select_timeout, time_t *last_timeout_processing_time)
+{
+ extern int Client;
+ static time_t last_keepalive_sent_time = 0;
+ static time_t last_idle_closed_check = 0;
+ time_t t;
+ BOOL allidle = True;
+ extern int keepalive;
- /* re-initialise the timezone */
- TimeInit();
+ if (smb_read_error == READ_EOF)
+ {
+ DEBUG(3,("end of file from client\n"));
+ return False;
+ }
- /* if connection on port 445, fake session setup... */
- if(ClientPort == 445)
+ if (smb_read_error == READ_ERROR)
{
- extern fstring remote_machine;
- extern fstring local_machine;
+ DEBUG(3,("receive_smb error (%s) exiting\n",
+ strerror(errno)));
+ return False;
+ }
- fstrcpy(remote_machine, dns_to_netbios_name(client_name(Client)));
- fstrcpy(local_machine, global_myname);
- remote_machine[15] = 0;
- local_machine[15] = 0;
- strlower(remote_machine);
- strlower(local_machine);
+ *last_timeout_processing_time = t = time(NULL);
- DEBUG(2, ("smbd_process(): faking session setup\n"
- "client_name: %s my_name: %s\n", remote_machine, local_machine));
+ if(last_keepalive_sent_time == 0)
+ last_keepalive_sent_time = t;
- add_session_user(remote_machine);
+ if(last_idle_closed_check == 0)
+ last_idle_closed_check = t;
- reload_services(True);
- reopen_logs();
+ /* become root again if waiting */
+ unbecome_user();
- if(lp_status(-1)) {
- claim_connection(NULL,"STATUS.",MAXSTATUS,True);
- }
+ /* check if we need to reload services */
+ check_reload(t);
+
+ /* automatic timeout if all connections are closed */
+ if (conn_num_open()==0 && (t - last_idle_closed_check) >= IDLE_CLOSED_TIMEOUT)
+ {
+ DEBUG( 2, ( "Closing idle connection\n" ) );
+ return False;
}
+ else
+ last_idle_closed_check = t;
- while (True)
+ if (keepalive && (t - last_keepalive_sent_time)>keepalive)
{
- int deadtime = lp_deadtime()*60;
- int counter;
- int last_keepalive=0;
- int service_load_counter = 0;
- BOOL got_smb = False;
+ struct cli_state *cli = server_client();
+ if (!send_keepalive(Client)) {
+ DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
+ return False;
+ }
+ /* also send a keepalive to the password server if its still
+ connected */
+ if (cli && cli->initialised)
+ send_keepalive(cli->fd);
+ last_keepalive_sent_time = t;
+ }
- if (deadtime <= 0)
- deadtime = DEFAULT_SMBD_TIMEOUT;
+ /* check for connection timeouts */
+ allidle = conn_idle_all(t, deadtime);
-#if USE_READ_PREDICTION
- if (lp_readprediction())
- do_read_prediction();
-#endif
+ if (allidle && conn_num_open()>0) {
+ DEBUG(2,("Closing idle connection 2.\n"));
+ return False;
+ }
- errno = 0;
+ if(global_machine_password_needs_changing)
+ {
+ unsigned char trust_passwd_hash[16];
+ time_t lct;
+ pstring remote_machine_list;
+
+ /*
+ * We're in domain level security, and the code that
+ * read the machine password flagged that the machine
+ * password needs changing.
+ */
+
+ /*
+ * First, open the machine password file with an exclusive lock.
+ */
+
+ if(!trust_password_lock( global_myworkgroup, global_myname, True)) {
+ DEBUG(0,("process: unable to open the machine account password file for \
+machine %s in domain %s.\n", global_myname, global_myworkgroup ));
+ return True;
+ }
- for (counter=SMBD_SELECT_LOOP;
- !receive_message_or_smb(InBuffer,BUFFER_SIZE,
- SMBD_SELECT_LOOP*1000,&got_smb);
- counter += SMBD_SELECT_LOOP)
- {
- time_t t;
- BOOL allidle = True;
- extern int keepalive;
+ if(!get_trust_account_password( trust_passwd_hash, &lct)) {
+ DEBUG(0,("process: unable to read the machine account password for \
+machine %s in domain %s.\n", global_myname, global_myworkgroup ));
+ trust_password_unlock();
+ return True;
+ }
- if (counter > 365 * 3600) /* big number of seconds. */
- {
- counter = 0;
- service_load_counter = 0;
- }
+ /*
+ * Make sure someone else hasn't already done this.
+ */
- if (smb_read_error == READ_EOF)
- {
- DEBUG(3,("end of file from client\n"));
- return;
- }
+ if(t < lct + lp_machine_password_timeout()) {
+ trust_password_unlock();
+ global_machine_password_needs_changing = False;
+ return True;
+ }
- if (smb_read_error == READ_ERROR)
- {
- DEBUG(3,("receive_smb error (%s) exiting\n",
- strerror(errno)));
- return;
- }
+ pstrcpy(remote_machine_list, lp_passwordserver());
- t = time(NULL);
+ change_trust_account_password( global_myworkgroup, remote_machine_list);
+ trust_password_unlock();
+ global_machine_password_needs_changing = False;
+ }
- /* become root again if waiting */
- unbecome_user();
+ /*
+ * Check to see if we have any blocking locks
+ * outstanding on the queue.
+ */
+ process_blocking_lock_queue(t);
- /* check for smb.conf reload */
- if (counter >= service_load_counter + SMBD_RELOAD_CHECK)
- {
- service_load_counter = counter;
+ /*
+ * Check to see if we have any change notifies
+ * outstanding on the queue.
+ */
+ process_pending_change_notify_queue(t);
- /* reload services, if files have changed. */
- reload_services(True);
- }
+ /*
+ * Modify the select timeout depending upon
+ * what we have remaining in our queues.
+ */
- /*
- * If reload_after_sighup == True then we got a SIGHUP
- * and are being asked to reload. Fix from <branko.cibej@hermes.si>
- */
+ *select_timeout = setup_select_timeout();
- if (reload_after_sighup)
- {
- DEBUG(0,("Reloading services after SIGHUP\n"));
- reload_services(False);
- reload_after_sighup = False;
- /*
- * Use this as an excuse to print some stats.
- */
- print_stat_cache_statistics();
- }
+ return True;
+}
- /* automatic timeout if all connections are closed */
- if (conn_num_open()==0 && counter >= IDLE_CLOSED_TIMEOUT)
- {
- DEBUG( 2, ( "Closing idle connection\n" ) );
- return;
- }
+/****************************************************************************
+ process commands from the client
+****************************************************************************/
- if (keepalive && (counter-last_keepalive)>keepalive)
- {
- struct cli_state *cli = server_client();
- if (!send_keepalive(Client)) {
- DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
- return;
- }
- /* also send a keepalive to the password server if its still
- connected */
- if (cli && cli->initialised)
- send_keepalive(cli->fd);
- last_keepalive = counter;
- }
+void smbd_process(void)
+{
+ extern int smb_echo_count;
+ time_t last_timeout_processing_time = time(NULL);
+ unsigned int num_smbs = 0;
+
+ InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ if ((InBuffer == NULL) || (OutBuffer == NULL))
+ return;
- /* check for connection timeouts */
- allidle = conn_idle_all(t, deadtime);
+ InBuffer += SMB_ALIGNMENT;
+ OutBuffer += SMB_ALIGNMENT;
- if (allidle && conn_num_open()>0) {
- DEBUG(2,("Closing idle connection 2.\n"));
- return;
- }
+ max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
- if(global_machine_password_needs_changing)
- {
- unsigned char trust_passwd_hash[16];
- time_t lct;
- pstring remote_machine_list;
- int sec_chan = SEC_CHAN_WKSTA;
-
- /*
- * We're in domain level security, and the code that
- * read the machine password flagged that the machine
- * password needs changing.
- */
+ /* re-initialise the timezone */
+ TimeInit();
- /*
- * First, open the machine password file with an exclusive lock.
- */
+ while (True)
+ {
+ int deadtime = lp_deadtime()*60;
+ BOOL got_smb = False;
+ int select_timeout = setup_select_timeout();
- if(!trust_password_lock( global_myworkgroup, global_myname, True)) {
- DEBUG(0,("process: unable to open the machine account password file for \
-machine %s in domain %s.\n", global_myname, global_myworkgroup ));
- continue;
- }
+ if (deadtime <= 0)
+ deadtime = DEFAULT_SMBD_TIMEOUT;
- if(!get_trust_account_password( trust_passwd_hash, &lct)) {
- DEBUG(0,("process: unable to read the machine account password for \
-machine %s in domain %s.\n", global_myname, global_myworkgroup ));
- trust_password_unlock();
- continue;
- }
+#if USE_READ_PREDICTION
+ if (lp_readprediction())
+ do_read_prediction();
+#endif
- /*
- * Make sure someone else hasn't already done this.
- */
+ errno = 0;
- if(t < lct + lp_machine_password_timeout()) {
- trust_password_unlock();
- global_machine_password_needs_changing = False;
- continue;
- }
+ while(!receive_message_or_smb(InBuffer,BUFFER_SIZE,select_timeout,&got_smb))
+ {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ }
- pstrcpy(remote_machine_list, lp_passwordserver());
- if (lp_server_role() == ROLE_DOMAIN_BDC)
- sec_chan = SEC_CHAN_BDC;
+ if(got_smb) {
+ /*
+ * Ensure we do timeout processing if the SMB we just got was
+ * only an echo request. This allows us to set the select
+ * timeout in 'receive_message_or_smb()' to any value we like
+ * without worrying that the client will send echo requests
+ * faster than the select timeout, thus starving out the
+ * essential processing (change notify, blocking locks) that
+ * the timeout code does. JRA.
+ */
+ int num_echos = smb_echo_count;
+
+ process_smb(InBuffer, OutBuffer);
- change_trust_account_password(global_myworkgroup, remote_machine_list,
- sec_chan);
- trust_password_unlock();
- global_machine_password_needs_changing = False;
+ if(smb_echo_count != num_echos) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
}
- /*
- * Check to see if we have any blocking locks
- * outstanding on the queue.
- */
- process_blocking_lock_queue(t);
+ num_smbs++;
/*
- * Check to see if we have any change notifies
- * outstanding on the queue.
+ * If we are getting smb requests in a constant stream
+ * with no echos, make sure we attempt timeout processing
+ * every select_timeout milliseconds - but only check for this
+ * every 200 smb requests.
*/
- process_pending_change_notify_queue(t);
- }
- if(got_smb)
- process_smb(InBuffer, OutBuffer);
+ if((num_smbs % 200) == 0) {
+ time_t new_check_time = time(NULL);
+ if(last_timeout_processing_time - new_check_time >= (select_timeout/1000)) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ last_timeout_processing_time = new_check_time; /* Reset time. */
+ }
+ }
+ }
else
process_local_message(InBuffer, BUFFER_SIZE);
}