[GLUE] Rsync SAMBA_3_2_0 SVN r25598 in order to create the v3-2-test branch.
[sfrench/samba-autobuild/.git] / source3 / smbd / connection.c
index af74e40f6a5608ed69a5a6a244cdbbef90fe5eff..65b7c352c550368ed1fbeb13d9525f574706889d 100644 (file)
@@ -1,12 +1,11 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 1.9.
+   Unix SMB/CIFS implementation.
    connection claim routines
    Copyright (C) Andrew Tridgell 1998
    
    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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
+/****************************************************************************
+ Delete a connection record.
+****************************************************************************/
 
-extern fstring remote_machine;
+BOOL yield_connection(connection_struct *conn, const char *name)
+{
+       struct db_record *rec;
+       NTSTATUS status;
 
-extern int DEBUGLEVEL;
+       DEBUG(3,("Yielding connection to %s\n",name));
+
+       if (!(rec = connections_fetch_entry(NULL, conn, name))) {
+               DEBUG(0, ("connections_fetch_entry failed\n"));
+               return False;
+       }
+
+       status = rec->delete_rec(rec);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG( NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND) ? 3 : 0,
+                      ("deleting connection record returned %s\n",
+                       nt_errstr(status)));
+       }
+
+       TALLOC_FREE(rec);
+       return NT_STATUS_IS_OK(status);
+}
+
+struct count_stat {
+       pid_t mypid;
+       int curr_connections;
+       const char *name;
+       BOOL Clear;
+};
 
 /****************************************************************************
-simple routines to do connection counting
+ Count the entries belonging to a service in the connection db.
 ****************************************************************************/
-BOOL yield_connection(connection_struct *conn,char *name,int max_connections)
+
+static int count_fn(struct db_record *rec,
+                   const struct connections_key *ckey,
+                   const struct connections_data *crec,
+                   void *udp)
 {
-       struct connect_record crec;
-       pstring fname;
-       int fd;
-       int mypid = getpid();
-       int i;
+       struct count_stat *cs = (struct count_stat *)udp;
+       if (crec->cnum == -1) {
+               return 0;
+       }
 
-       DEBUG(3,("Yielding connection to %s\n",name));
+       /* If the pid was not found delete the entry from connections.tdb */
 
-       if (max_connections <= 0)
-               return(True);
+       if (cs->Clear && !process_exists(crec->pid) && (errno == ESRCH)) {
+               NTSTATUS status;
+               DEBUG(2,("pid %s doesn't exist - deleting connections %d [%s]\n",
+                        procid_str_static(&crec->pid), crec->cnum,
+                        crec->servicename));
 
-       bzero(&crec,sizeof(crec));
+               status = rec->delete_rec(rec);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("count_fn: tdb_delete failed with error %s\n",
+                                nt_errstr(status)));
+               }
+               return 0;
+       }
 
-       pstrcpy(fname,lp_lockdir());
-       trim_string(fname,"","/");
+       if (strequal(crec->servicename, cs->name))
+               cs->curr_connections++;
 
-       pstrcat(fname,"/");
-       pstrcat(fname,name);
-       pstrcat(fname,".LCK");
+       return 0;
+}
 
-       fd = open(fname,O_RDWR);
-       if (fd == -1) {
-               DEBUG(2,("Couldn't open lock file %s (%s)\n",fname,strerror(errno)));
-               return(False);
+/****************************************************************************
+ Claim an entry in the connections database.
+****************************************************************************/
+
+int count_current_connections( const char *sharename, BOOL clear  )
+{
+       struct count_stat cs;
+
+       cs.mypid = sys_getpid();
+       cs.curr_connections = 0;
+       cs.name = sharename;
+       cs.Clear = clear;
+
+       /*
+        * This has a race condition, but locking the chain before hand is worse
+        * as it leads to deadlock.
+        */
+
+       if (connections_forall(count_fn, &cs) == -1) {
+               DEBUG(0,("count_current_connections: traverse of "
+                        "connections.tdb failed\n"));
+               return False;
        }
 
-       if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_WRLCK)==False) {
-               DEBUG(0,("ERROR: can't get lock on %s\n", fname));
+       return cs.curr_connections;
+}
+
+/****************************************************************************
+ Claim an entry in the connections database.
+****************************************************************************/
+
+BOOL claim_connection(connection_struct *conn, const char *name,
+                     uint32 msg_flags)
+{
+       struct db_record *rec;
+       struct connections_data crec;
+       TDB_DATA dbuf;
+       NTSTATUS status;
+
+       DEBUG(5,("claiming [%s]\n", name));
+
+       if (!(rec = connections_fetch_entry(NULL, conn, name))) {
+               DEBUG(0, ("connections_fetch_entry failed\n"));
                return False;
        }
 
-       /* find the right spot */
-       for (i=0;i<max_connections;i++) {
-               if (read(fd, &crec,sizeof(crec)) != sizeof(crec)) {
-                       DEBUG(2,("Entry not found in lock file %s\n",fname));
-                       if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) {
-                               DEBUG(0,("ERROR: can't release lock on %s\n", fname));
-                       }
-                       close(fd);
-                       return(False);
-               }
-               if (crec.pid == mypid && crec.cnum == conn->cnum)
-                       break;
+       /* fill in the crec */
+       ZERO_STRUCT(crec);
+       crec.magic = 0x280267;
+       crec.pid = procid_self();
+       crec.cnum = conn?conn->cnum:-1;
+       if (conn) {
+               crec.uid = conn->uid;
+               crec.gid = conn->gid;
+               strlcpy(crec.servicename, lp_servicename(SNUM(conn)),
+                       sizeof(crec.servicename));
        }
+       crec.start = time(NULL);
+       crec.bcast_msg_flags = msg_flags;
+       
+       strlcpy(crec.machine,get_remote_machine_name(),sizeof(crec.machine));
+       strlcpy(crec.addr,conn?conn->client_address:client_addr(),
+               sizeof(crec.addr));
 
-       if (crec.pid != mypid || crec.cnum != conn->cnum) {
-               if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) {
-                       DEBUG(0,("ERROR: can't release lock on %s\n", fname));
-               }
-               close(fd);
-               DEBUG(2,("Entry not found in lock file %s\n",fname));
-               return(False);
-       }
-
-       bzero((void *)&crec,sizeof(crec));
-  
-       /* remove our mark */
-       if (sys_lseek(fd,i*sizeof(crec),SEEK_SET) != i*sizeof(crec) ||
-           write(fd, &crec,sizeof(crec)) != sizeof(crec)) {
-               DEBUG(2,("Couldn't update lock file %s (%s)\n",fname,strerror(errno)));
-               if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) {
-                       DEBUG(0,("ERROR: can't release lock on %s\n", fname));
-               }
-               close(fd);
-               return(False);
+       dbuf.dptr = (uint8 *)&crec;
+       dbuf.dsize = sizeof(crec);
+
+       status = rec->store(rec, dbuf, TDB_REPLACE);
+
+       TALLOC_FREE(rec);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("claim_connection: tdb_store failed with error %s.\n",
+                        nt_errstr(status)));
+               return False;
        }
 
-       if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) {
-               DEBUG(0,("ERROR: can't release lock on %s\n", fname));
+       return True;
+}
+
+BOOL register_message_flags(BOOL doreg, uint32 msg_flags)
+{
+       struct db_record *rec;
+       struct connections_data *pcrec;
+       NTSTATUS status;
+
+       DEBUG(10,("register_message_flags: %s flags 0x%x\n",
+               doreg ? "adding" : "removing",
+               (unsigned int)msg_flags ));
+
+       if (!(rec = connections_fetch_entry(NULL, NULL, ""))) {
+               DEBUG(0, ("connections_fetch_entry failed\n"));
+               return False;
        }
 
-       DEBUG(3,("Yield successful\n"));
+       if (rec->value.dsize != sizeof(struct connections_data)) {
+               DEBUG(0,("register_message_flags: Got wrong record size\n"));
+               TALLOC_FREE(rec);
+               return False;
+       }
 
-       close(fd);
-       return(True);
+       pcrec = (struct connections_data *)rec->value.dptr;
+       if (doreg)
+               pcrec->bcast_msg_flags |= msg_flags;
+       else
+               pcrec->bcast_msg_flags &= ~msg_flags;
+
+       status = rec->store(rec, rec->value, TDB_REPLACE);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("register_message_flags: tdb_store failed: %s.\n",
+                        nt_errstr(status)));
+               TALLOC_FREE(rec);
+               return False;
+       }
+
+       DEBUG(10,("register_message_flags: new flags 0x%x\n",
+               (unsigned int)pcrec->bcast_msg_flags ));
+
+       TALLOC_FREE(rec);
+
+       return True;
 }
 
+/*********************************************************************
+*********************************************************************/
 
-/****************************************************************************
-simple routines to do connection counting
-****************************************************************************/
-BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOOL Clear)
+static TDB_DATA* make_pipe_rec_key( struct pipe_open_rec *prec )
 {
-       extern int Client;
-       struct connect_record crec;
-       pstring fname;
-       int fd=-1;
-       int i,foundi= -1;
-       int total_recs;
+       TDB_DATA *kbuf = NULL;
+       fstring key_string;
        
-       if (max_connections <= 0)
-               return(True);
+       if ( !prec )
+               return NULL;
        
-       DEBUG(5,("trying claim %s %s %d\n",lp_lockdir(),name,max_connections));
-       
-       pstrcpy(fname,lp_lockdir());
-       trim_string(fname,"","/");
-       
-       if (!directory_exist(fname,NULL))
-               mkdir(fname,0755);
-       
-       pstrcat(fname,"/");
-       pstrcat(fname,name);
-       pstrcat(fname,".LCK");
-       
-       if (!file_exist(fname,NULL)) {
-               fd = open(fname,O_RDWR|O_CREAT|O_EXCL, 0644);
+       if ( (kbuf = TALLOC_P(prec, TDB_DATA)) == NULL ) {
+               return NULL;
        }
+       
+       snprintf( key_string, sizeof(key_string), "%s/%d/%d",
+               prec->name, procid_to_pid(&prec->pid), prec->pnum );
+               
+       *kbuf = string_term_tdb_data(talloc_strdup(prec, key_string));
+       if (kbuf->dptr == NULL )
+               return NULL;
+
+       return kbuf;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static void fill_pipe_open_rec( struct pipe_open_rec *prec, smb_np_struct *p )
+{
+       prec->pid = pid_to_procid(sys_getpid());
+       prec->pnum = p->pnum;
+       prec->uid = geteuid();
+       fstrcpy( prec->name, p->name );
+
+       return;
+}
 
-       if (fd == -1) {
-               fd = open(fname,O_RDWR);
+/*********************************************************************
+*********************************************************************/
+
+BOOL store_pipe_opendb( smb_np_struct *p )
+{
+       struct db_record *dbrec;
+       struct pipe_open_rec *prec;
+       TDB_DATA *key;
+       TDB_DATA data;
+       BOOL ret = False;
+       
+       if ( (prec = TALLOC_P( NULL, struct pipe_open_rec)) == NULL ) {
+               DEBUG(0,("store_pipe_opendb: talloc failed!\n"));
+               return False;
        }
        
-       if (fd == -1) {
-               DEBUG(1,("couldn't open lock file %s\n",fname));
-               return(False);
+       fill_pipe_open_rec( prec, p );
+       if ( (key = make_pipe_rec_key( prec )) == NULL ) {
+               goto done;
        }
+       
+       data.dptr = (uint8 *)prec;
+       data.dsize = sizeof(struct pipe_open_rec);
 
-       if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_WRLCK)==False) {
-               DEBUG(0,("ERROR: can't get lock on %s\n", fname));
-               return False;
+       if (!(dbrec = connections_fetch_record(prec, *key))) {
+               DEBUG(0, ("connections_fetch_record failed\n"));
+               goto done;
        }
 
-       total_recs = file_size(fname) / sizeof(crec);
-                       
-       /* find a free spot */
-       for (i=0;i<max_connections;i++) {
-               if (i>=total_recs || 
-                   sys_lseek(fd,i*sizeof(crec),SEEK_SET) != i*sizeof(crec) ||
-                   read(fd,&crec,sizeof(crec)) != sizeof(crec)) {
-                       if (foundi < 0) foundi = i;
-                       break;
-               }
-               
-               if (Clear && crec.pid && !process_exists(crec.pid)) {
-                       if(sys_lseek(fd,i*sizeof(crec),SEEK_SET) != i*sizeof(crec)) {
-              DEBUG(0,("claim_connection: ERROR: sys_lseek failed to seek \
-to %d\n", i*sizeof(crec) ));
-              continue;
-            }
-                       bzero((void *)&crec,sizeof(crec));
-                       write(fd, &crec,sizeof(crec));
-                       if (foundi < 0) foundi = i;
-                       continue;
-               }
-               if (foundi < 0 && (!crec.pid || !process_exists(crec.pid))) {
-                       foundi=i;
-                       if (!Clear) break;
-               }
-       }  
+       ret = NT_STATUS_IS_OK(dbrec->store(dbrec, data, TDB_REPLACE));
        
-       if (foundi < 0) {
-               DEBUG(3,("no free locks in %s\n",fname));
-               if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) {
-                       DEBUG(0,("ERROR: can't release lock on %s\n", fname));
-               }
-               close(fd);
-               return(False);
-       }      
+done:
+       TALLOC_FREE( prec );    
+       return ret;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+BOOL delete_pipe_opendb( smb_np_struct *p )
+{
+       struct db_record *dbrec;
+       struct pipe_open_rec *prec;
+       TDB_DATA *key;
+       BOOL ret = False;
        
-       /* fill in the crec */
-       bzero((void *)&crec,sizeof(crec));
-       crec.magic = 0x280267;
-       crec.pid = getpid();
-       if (conn) {
-               crec.cnum = conn->cnum;
-               crec.uid = conn->uid;
-               crec.gid = conn->gid;
-               StrnCpy(crec.name,
-                       lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
-       } else {
-               crec.cnum = -1;
+       if ( (prec = TALLOC_P( NULL, struct pipe_open_rec)) == NULL ) {
+               DEBUG(0,("store_pipe_opendb: talloc failed!\n"));
+               return False;
        }
-       crec.start = time(NULL);
        
-       StrnCpy(crec.machine,remote_machine,sizeof(crec.machine)-1);
-       StrnCpy(crec.addr,client_addr(Client),sizeof(crec.addr)-1);
+       fill_pipe_open_rec( prec, p );
+       if ( (key = make_pipe_rec_key( prec )) == NULL ) {
+               goto done;
+       }
        
-       /* make our mark */
-       if (sys_lseek(fd,foundi*sizeof(crec),SEEK_SET) != foundi*sizeof(crec) ||
-           write(fd, &crec,sizeof(crec)) != sizeof(crec)) {
-               if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) {
-                       DEBUG(0,("ERROR: can't release lock on %s\n", fname));
-               }
-               close(fd);
-               return(False);
+       if (!(dbrec = connections_fetch_record(prec, *key))) {
+               DEBUG(0, ("connections_fetch_record failed\n"));
+               goto done;
        }
 
-       if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) {
-               DEBUG(0,("ERROR: can't release lock on %s\n", fname));
-       }
+       ret = NT_STATUS_IS_OK(dbrec->delete_rec(dbrec));
        
-       close(fd);
-       return(True);
+done:
+       TALLOC_FREE( prec );
+       return ret;
 }