[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 aa49f8cf7cf3527e03ef7b1a019436944a64ce4b..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.
+****************************************************************************/
+
+BOOL yield_connection(connection_struct *conn, const char *name)
+{
+       struct db_record *rec;
+       NTSTATUS status;
+
+       DEBUG(3,("Yielding connection to %s\n",name));
 
-extern fstring remote_machine;
-static TDB_CONTEXT *tdb;
+       if (!(rec = connections_fetch_entry(NULL, conn, name))) {
+               DEBUG(0, ("connections_fetch_entry failed\n"));
+               return False;
+       }
 
-extern int DEBUGLEVEL;
+       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);
+}
 
-#ifdef WITH_UTMP
-static void utmp_yield(pid_t pid, const connection_struct *conn);
-static void utmp_claim(const struct connections_data *crec, const connection_struct *conn);
-#endif
+struct count_stat {
+       pid_t mypid;
+       int curr_connections;
+       const char *name;
+       BOOL Clear;
+};
 
 /****************************************************************************
-delete a connection record
+ 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 connections_key key;
-       TDB_DATA kbuf;
+       struct count_stat *cs = (struct count_stat *)udp;
+       if (crec->cnum == -1) {
+               return 0;
+       }
 
-       if (!tdb) return False;
+       /* If the pid was not found delete the entry from connections.tdb */
 
-       DEBUG(3,("Yielding connection to %s\n",name));
+       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));
 
-       ZERO_STRUCT(key);
-       key.pid = sys_getpid();
-       if (conn) key.cnum = conn->cnum;
-       fstrcpy(key.name, name);
+               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;
+       }
 
-       kbuf.dptr = (char *)&key;
-       kbuf.dsize = sizeof(key);
+       if (strequal(crec->servicename, cs->name))
+               cs->curr_connections++;
 
-       tdb_delete(tdb, kbuf);
+       return 0;
+}
 
-#ifdef WITH_UTMP
-       if(conn)
-               utmp_yield(key.pid, conn);
-#endif
+/****************************************************************************
+ Claim an entry in the connections database.
+****************************************************************************/
 
-       return(True);
-}
+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;
+       }
 
+       return cs.curr_connections;
+}
 
 /****************************************************************************
-claim an entry in the connections database
+ Claim an entry in the connections database.
 ****************************************************************************/
-BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOOL Clear)
+
+BOOL claim_connection(connection_struct *conn, const char *name,
+                     uint32 msg_flags)
 {
-       struct connections_key key;
+       struct db_record *rec;
        struct connections_data crec;
-       TDB_DATA kbuf, dbuf;
+       TDB_DATA dbuf;
+       NTSTATUS status;
 
-       if (max_connections <= 0)
-               return(True);
+       DEBUG(5,("claiming [%s]\n", name));
 
-       if (!tdb) {
-               tdb = tdb_open(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST, 
-                              O_RDWR | O_CREAT, 0644);
+       if (!(rec = connections_fetch_entry(NULL, conn, name))) {
+               DEBUG(0, ("connections_fetch_entry failed\n"));
+               return False;
        }
-       if (!tdb) return False;
-
-       DEBUG(5,("claiming %s %d\n",name,max_connections));
-
-       ZERO_STRUCT(key);
-       key.pid = sys_getpid();
-       key.cnum = conn?conn->cnum:-1;
-       fstrcpy(key.name, name);
-
-       kbuf.dptr = (char *)&key;
-       kbuf.dsize = sizeof(key);
 
        /* fill in the crec */
        ZERO_STRUCT(crec);
        crec.magic = 0x280267;
-       crec.pid = sys_getpid();
+       crec.pid = procid_self();
        crec.cnum = conn?conn->cnum:-1;
        if (conn) {
                crec.uid = conn->uid;
                crec.gid = conn->gid;
-               StrnCpy(crec.name,
-                       lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
+               strlcpy(crec.servicename, lp_servicename(SNUM(conn)),
+                       sizeof(crec.servicename));
        }
        crec.start = time(NULL);
+       crec.bcast_msg_flags = msg_flags;
        
-       StrnCpy(crec.machine,remote_machine,sizeof(crec.machine)-1);
-       StrnCpy(crec.addr,conn?conn->client_address:client_addr(),sizeof(crec.addr)-1);
+       strlcpy(crec.machine,get_remote_machine_name(),sizeof(crec.machine));
+       strlcpy(crec.addr,conn?conn->client_address:client_addr(),
+               sizeof(crec.addr));
 
-       dbuf.dptr = (char *)&crec;
+       dbuf.dptr = (uint8 *)&crec;
        dbuf.dsize = sizeof(crec);
 
-       if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) return False;
+       status = rec->store(rec, dbuf, TDB_REPLACE);
 
-#ifdef WITH_UTMP
-       if (conn)
-           utmp_claim(&crec, conn);
-#endif
+       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;
+       }
 
        return True;
 }
 
-#ifdef WITH_UTMP
-
-/****************************************************************************
-Reflect connection status in utmp/wtmp files.
-       T.D.Lee@durham.ac.uk  September 1999
-
-Hints for porting:
-       o Always attempt to use programmatic interface (pututline() etc.)
-       o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
-
-OS status:
-       Solaris 2.x:  Tested on 2.6 and 2.7; should be OK on other flavours.
-               T.D.Lee@durham.ac.uk
-       HPUX 9.x:  Not tested.  Appears not to have "x".
-       IRIX 6.5:  Not tested.  Appears to have "x".
-
-Notes:
-       The 4 byte 'ut_id' component is vital to distinguish connections,
-       of which there could be several hundered or even thousand.
-       Entries seem to be printable characters, with optional NULL pads.
-
-       We need to be distinct from other entries in utmp/wtmp.
-
-       Observed things: therefore avoid them.  Add to this list please.
-       From Solaris 2.x (because that's what I have):
-               'sN'    : run-levels; N: [0-9]
-               'co'    : console
-               'CC'    : arbitrary things;  C: [a-z]
-               'rXNN'  : rlogin;  N: [0-9]; X: [0-9a-z]
-               'tXNN'  : rlogin;  N: [0-9]; X: [0-9a-z]
-               '/NNN'  : Solaris CDE
-               'ftpZ'  : ftp (Z is the number 255, aka 0377, aka 0xff)
-       Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp",
-       but differences have been seen.
-
-       Arbitrarily I have chosen to use a distinctive 'SM' for the
-       first two bytes.
-
-       The remaining two encode the connection number used in samba locking
-       functions "claim_connection() and "yield_connection()".  This seems
-       to be a "nicely behaved" number: starting from 0 then working up
-       looking for an available slot.
-
-****************************************************************************/
-
-#include <utmp.h>
-
-#ifdef HAVE_UTMPX_H
-#include <utmpx.h>
-#endif
-
-static const char *ut_id_encstr =
-       "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
-static
-int
-ut_id_encode(int i, char *fourbyte)
+BOOL register_message_flags(BOOL doreg, uint32 msg_flags)
 {
-       int nbase;
+       struct db_record *rec;
+       struct connections_data *pcrec;
+       NTSTATUS status;
 
-       fourbyte[0] = 'S';
-       fourbyte[1] = 'M';
+       DEBUG(10,("register_message_flags: %s flags 0x%x\n",
+               doreg ? "adding" : "removing",
+               (unsigned int)msg_flags ));
 
-/*
- * Encode remaining 2 bytes from 'i'.
- * 'ut_id_encstr' is the character set on which modulo arithmetic is done.
- * Example: digits would produce the base-10 numbers from '001'.
- */
-       nbase = strlen(ut_id_encstr);
+       if (!(rec = connections_fetch_entry(NULL, NULL, ""))) {
+               DEBUG(0, ("connections_fetch_entry failed\n"));
+               return False;
+       }
 
-       fourbyte[3] = ut_id_encstr[i % nbase];
-       i /= nbase;
-       fourbyte[2] = ut_id_encstr[i % nbase];
-       i /= nbase;
+       if (rec->value.dsize != sizeof(struct connections_data)) {
+               DEBUG(0,("register_message_flags: Got wrong record size\n"));
+               TALLOC_FREE(rec);
+               return False;
+       }
 
-       return(i);      /* 0: good; else overflow */
-}
+       pcrec = (struct connections_data *)rec->value.dptr;
+       if (doreg)
+               pcrec->bcast_msg_flags |= msg_flags;
+       else
+               pcrec->bcast_msg_flags &= ~msg_flags;
 
-static int utmp_fill(struct utmp *u, const connection_struct *conn, pid_t pid, int i)
-{
-       struct timeval timeval;
-       int rc;
+       status = rec->store(rec, rec->value, TDB_REPLACE);
 
-       pstrcpy(u->ut_user, conn->user);
-       rc = ut_id_encode(i, u->ut_id);
-       slprintf(u->ut_line, 12, "smb/%d", i);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("register_message_flags: tdb_store failed: %s.\n",
+                        nt_errstr(status)));
+               TALLOC_FREE(rec);
+               return False;
+       }
 
-       u->ut_pid = pid;
+       DEBUG(10,("register_message_flags: new flags 0x%x\n",
+               (unsigned int)pcrec->bcast_msg_flags ));
 
-       gettimeofday(&timeval, NULL);
-       u->ut_time = timeval.tv_sec;
+       TALLOC_FREE(rec);
 
-       return(rc);
+       return True;
 }
 
-/* Default path (if possible) */
-#ifdef HAVE_UTMPX_H
-
-# ifdef UTMPX_FILE
-static char *ut_pathname = UTMPX_FILE;
-# else
-static char *ut_pathname = "";
-# endif
-# ifdef WTMPX_FILE
-static char *wt_pathname = WTMPX_FILE;
-# else
-static char *wt_pathname = "";
-# endif
-
-#else  /* HAVE_UTMPX_H */
-
-# ifdef UTMP_FILE
-static char *ut_pathname = UTMP_FILE;
-# else
-static char *ut_pathname = "";
-# endif
-# ifdef WTMP_FILE
-static char *wt_pathname = WTMP_FILE;
-# else
-static char *wt_pathname = "";
-# endif
-
-#endif /* HAVE_UTMPX_H */
-
-static void uw_pathname(pstring fname, const char *uw_name)
-{
-       pstring dirname;
-
-       pstrcpy(dirname,lp_utmpdir());
-       trim_string(dirname,"","/");
-
-       /* Given directory: use it */
-       if (dirname != 0 && strlen(dirname) != 0) {
-               pstrcpy(fname, dirname);
-               pstrcat(fname, "/");
-               pstrcat(fname, uw_name);
-               return;
-       }
-
-       /* No given directory: attempt to use default paths */
-       if (uw_name[0] == 'u') {
-               pstrcpy(fname, ut_pathname);
-               return;
-       }
+/*********************************************************************
+*********************************************************************/
 
-       if (uw_name[0] == 'w') {
-               pstrcpy(fname, wt_pathname);
-               return;
+static TDB_DATA* make_pipe_rec_key( struct pipe_open_rec *prec )
+{
+       TDB_DATA *kbuf = NULL;
+       fstring key_string;
+       
+       if ( !prec )
+               return NULL;
+       
+       if ( (kbuf = TALLOC_P(prec, TDB_DATA)) == NULL ) {
+               return NULL;
        }
-
-       pstrcpy(fname, "");
+       
+       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 utmp_update(const struct utmp *u, const char *host)
+/*********************************************************************
+*********************************************************************/
+
+static void fill_pipe_open_rec( struct pipe_open_rec *prec, smb_np_struct *p )
 {
-       pstring fname;
+       prec->pid = pid_to_procid(sys_getpid());
+       prec->pnum = p->pnum;
+       prec->uid = geteuid();
+       fstrcpy( prec->name, p->name );
 
-#ifdef HAVE_UTMPX_H
-       struct utmpx ux, *uxrc;
+       return;
+}
 
-       getutmpx(u, &ux);
-       if (host) {
-#if defined(HAVE_UX_UT_SYSLEN)
-               ux.ut_syslen = strlen(host);
-#endif /* defined(HAVE_UX_UT_SYSLEN) */
-               pstrcpy(ux.ut_host, host);
-       }
+/*********************************************************************
+*********************************************************************/
 
-       uw_pathname(fname, "utmpx");
-       DEBUG(2,("utmp_update: fname:%s\n", fname));
-       if (strlen(fname) != 0) {
-               utmpxname(fname);
+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;
        }
-       uxrc = pututxline(&ux);
-       if (uxrc == NULL) {
-               DEBUG(2,("utmp_update: pututxline() failed\n"));
-               return;
+       
+       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);
 
-       uw_pathname(fname, "wtmpx");
-       DEBUG(2,("utmp_update: fname:%s\n", fname));
-       if (strlen(fname) != 0) {
-               updwtmpx(fname, &ux);
-       }
-#else
-       uw_pathname(fname, "utmp");
-       DEBUG(2,("utmp_update: fname:%s\n", fname));
-       if (strlen(fname) != 0) {
-               utmpname(fname);
+       if (!(dbrec = connections_fetch_record(prec, *key))) {
+               DEBUG(0, ("connections_fetch_record failed\n"));
+               goto done;
        }
-       pututline(u);
 
-       uw_pathname(fname, "wtmp");
-
-       /* *** Hmmm.  Appending wtmp (as distinct from overwriting utmp) has
-       me baffled.  How is it to be done? *** */
-#endif
+       ret = NT_STATUS_IS_OK(dbrec->store(dbrec, data, TDB_REPLACE));
+       
+done:
+       TALLOC_FREE( prec );    
+       return ret;
 }
 
-static void utmp_yield(pid_t pid, const connection_struct *conn)
-{
-       struct utmp u;
-
-       if (! lp_utmp(SNUM(conn))) {
-               DEBUG(2,("utmp_yield: lp_utmp() NULL\n"));
-               return;
-       }
-
-       DEBUG(2,("utmp_yield: conn: user:%s cnum:%d\n",
-                conn->user, conn->cnum));
+/*********************************************************************
+*********************************************************************/
 
-       memset((char *)&u, '\0', sizeof(struct utmp));
-       u.ut_type = DEAD_PROCESS;
-       u.ut_exit.e_termination = 0;
-       u.ut_exit.e_exit = 0;
-       if (utmp_fill(&u, conn, pid, conn->cnum) == 0) {
-               utmp_update(&u, NULL);
-       }
-}
-
-static void utmp_claim(const struct connect_record *crec, const connection_struct *conn)
+BOOL delete_pipe_opendb( smb_np_struct *p )
 {
-       struct utmp u;
-
-       if (conn == NULL) {
-               DEBUG(2,("utmp_claim: conn NULL\n"));
-               return;
+       struct db_record *dbrec;
+       struct pipe_open_rec *prec;
+       TDB_DATA *key;
+       BOOL ret = False;
+       
+       if ( (prec = TALLOC_P( NULL, struct pipe_open_rec)) == NULL ) {
+               DEBUG(0,("store_pipe_opendb: talloc failed!\n"));
+               return False;
        }
-
-       if (! lp_utmp(SNUM(conn))) {
-               DEBUG(2,("utmp_claim: lp_utmp() NULL\n"));
-               return;
+       
+       fill_pipe_open_rec( prec, p );
+       if ( (key = make_pipe_rec_key( prec )) == NULL ) {
+               goto done;
        }
-
-       DEBUG(2,("utmp_claim: conn: user:%s cnum:%d i:%d\n",
-         conn->user, conn->cnum, i));
-       DEBUG(2,("utmp_claim: crec: pid:%d, cnum:%d name:%s addr:%s mach:%s DNS:%s\n",
-         crec->pid, crec->cnum, crec->name, crec->addr, crec->machine, client_name()));
-
-
-       memset((char *)&u, '\0', sizeof(struct utmp));
-       u.ut_type = USER_PROCESS;
-       if (utmp_fill(&u, conn, crec->pid, conn->cnum) == 0) {
-               utmp_update(&u, crec->machine);
+       
+       if (!(dbrec = connections_fetch_record(prec, *key))) {
+               DEBUG(0, ("connections_fetch_record failed\n"));
+               goto done;
        }
-}
 
-#endif
+       ret = NT_STATUS_IS_OK(dbrec->delete_rec(dbrec));
+       
+done:
+       TALLOC_FREE( prec );
+       return ret;
+}