diff options
Diffstat (limited to 'source3/smbd/process.c')
-rw-r--r-- | source3/smbd/process.c | 501 |
1 files changed, 298 insertions, 203 deletions
diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 95222d3f51e..7e94ffa173d 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); } |