summaryrefslogtreecommitdiff
path: root/source3/smbd/connection.c
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2000-10-28 19:38:39 +0000
committerJeremy Allison <jra@samba.org>2000-10-28 19:38:39 +0000
commit1cb444057a7894ca97706fb65a756e5fdb635f81 (patch)
treee251df5cf04952b32aafa3db1c75e09b0f742f8f /source3/smbd/connection.c
parent234792321c07aa59103fb7502534e6dba0ca4c08 (diff)
downloadsamba-1cb444057a7894ca97706fb65a756e5fdb635f81.tar.gz
David Lee's utmp patch (finally). Thanks David !
Jeremy. (This used to be commit b809a2d0c81c54e917ccc0c99b3e70ea8d7ceab1)
Diffstat (limited to 'source3/smbd/connection.c')
-rw-r--r--source3/smbd/connection.c678
1 files changed, 594 insertions, 84 deletions
diff --git a/source3/smbd/connection.c b/source3/smbd/connection.c
index bc014c300d6..6e088f9f8f5 100644
--- a/source3/smbd/connection.c
+++ b/source3/smbd/connection.c
@@ -126,17 +126,63 @@ BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOO
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.
+ With grateful thanks since then to many who have helped port it to
+ different operating systems. The variety of OS quirks thereby
+ uncovered is amazing...
-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".
+Hints for porting:
+ o Always attempt to use programmatic interface (pututline() etc.)
+ Indeed, at present only programmatic use is supported.
+ o The only currently supported programmatic interface to "wtmp{,x}"
+ is through "updwtmp*()" routines.
+ o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
+ o The HAVE_* items should identify supported features.
+ o If at all possible, avoid "if defined(MY-OS)" constructions.
+
+OS observations and status:
+ Almost every OS seems to have its own quirks.
+
+ Solaris 2.x:
+ Tested on 2.6 and 2.7; should be OK on other flavours.
+ AIX:
+ Apparently has utmpx.h but doesn't implement.
+ OSF:
+ Has utmpx.h, but (e.g.) no "getutmpx()". (Is this like AIX ?)
+ Redhat 6:
+ utmpx.h seems not to set default filenames. non-x better.
+ IRIX 6.5:
+ Not tested. Appears to have "x".
+ HP-UX 9.x:
+ Not tested. Appears to lack "x".
+ HP-UX 10.x:
+ Not tested.
+ "updwtmp*()" routines seem absent, so no current wtmp* support.
+ Has "ut_addr": probably trivial to implement (although remember
+ that IPv6 is coming...).
+
+ FreeBSD:
+ No "putut*()" type of interface.
+ No "ut_type" and associated defines.
+ Write files directly. Alternatively use its login(3)/logout(3).
+ SunOS 4:
+ Not tested. Resembles FreeBSD, but no login()/logout().
+
+lastlog:
+ Should "lastlog" files, if any, be updated?
+ BSD systems (SunOS 4, FreeBSD):
+ o Prominent mention on man pages.
+ System-V (e.g. Solaris 2):
+ o No mention on man pages, even under "man -k".
+ o Has a "/var/adm/lastlog" file, but pututxline() etc. seem
+ not to touch it.
+ o Despite downplaying (above), nevertheless has <lastlog.h>.
+ So perhaps UN*X "lastlog" facility is intended for tty/terminal only?
Notes:
+ Each connection requires a small number (starting at 0, working up)
+ to represent the line (unum). This must be unique within and across
+ all smbd processes.
+
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.
@@ -158,10 +204,11 @@ Notes:
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.
+ The remaining two encode the "unum" (see above).
+
+ For "utmp consolidate" the suggestion was made to encode the pid into
+ those remaining two bytes (16 bits). But recent UNIX (e.g Solaris 8)
+ is migrating to pids > 16 bits, so we ought not to do this.
****************************************************************************/
@@ -171,6 +218,136 @@ Notes:
#include <utmpx.h>
#endif
+/* BSD systems: some may need lastlog.h (SunOS 4), some may not (FreeBSD) */
+/* Some System-V systems (e.g. Solaris 2) declare this too. */
+#ifdef HAVE_LASTLOG_H
+#include <lastlog.h>
+#endif
+
+/****************************************************************************
+obtain/release a small number (0 upwards) unique within and across smbds
+****************************************************************************/
+/*
+ * Need a "small" number to represent this connection, unique within this
+ * smbd and across all smbds.
+ *
+ * claim:
+ * Start at 0, hunt up for free, unique number "unum" by attempting to
+ * store it as a key in a tdb database:
+ * key: unum data: pid+conn
+ * Also store its inverse, ready for yield function:
+ * key: pid+conn data: unum
+ *
+ * yield:
+ * Find key: pid+conn; data is unum; delete record
+ * Find key: unum ; delete record.
+ *
+ * Comment:
+ * The claim algorithm (a "for" loop attempting to store numbers in a tdb
+ * database) will be increasingly inefficient with larger numbers of
+ * connections. Is it possible to write a suitable primitive within tdb?
+ *
+ * However, by also storing the inverse key/data pair, we at least make
+ * the yield algorithm efficient.
+ */
+
+static TDB_CONTEXT *tdb_utmp;
+
+struct utmp_tdb_data {
+ pid_t pid;
+ int cnum;
+};
+
+static int utmp_claim_tdb(const connection_struct *conn)
+{
+ struct utmp_tdb_data udata;
+ int i, slotnum;
+ TDB_DATA kbuf, dbuf;
+
+ if (!tdb_utmp) {
+ tdb_utmp = tdb_open(lock_path("utmp.tdb"), 0,
+ TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT, 0644);
+ }
+ if (!tdb_utmp) return(-1);
+
+ DEBUG(2,("utmp_claim_tdb: entered\n"));
+
+ ZERO_STRUCT(udata);
+ udata.pid = sys_getpid();
+ udata.cnum = conn ? conn->cnum : -1;
+
+ dbuf.dptr = (char *) &udata;
+ dbuf.dsize = sizeof(udata);
+
+ /* The key is simply a number as close as possible to zero: find it */
+ slotnum = -1;
+ /* stop loop when overflow +ve integers (a huge, busy machine!) */
+ for (i = 0; i >= 0 ; i++) {
+ kbuf.dptr = (char *) &i;
+ kbuf.dsize = sizeof(i);
+
+ if (tdb_store(tdb_utmp, kbuf, dbuf, TDB_INSERT) == 0) {
+ /* have successfully grabbed a free slot */
+ slotnum = i;
+
+ /* store the inverse for faster utmp_yield_tdb() */
+ tdb_store(tdb_utmp, dbuf, kbuf, TDB_INSERT);
+
+ break; /* Got it; escape */
+ }
+ }
+ if (slotnum < 0) { /* more connections than positive integers! */
+ DEBUG(2,("utmp_claim_tdb: failed\n"));
+ return(-1);
+ }
+
+ DEBUG(2,("utmp_claim_tdb: leaving with %d\n", slotnum));
+
+ return(slotnum);
+}
+
+static int utmp_yield_tdb(const connection_struct *conn)
+{
+ struct utmp_tdb_data revkey;
+ int i, slotnum;
+ TDB_DATA kbuf, dbuf;
+
+ if (!tdb_utmp) {
+ return(-1);
+ }
+
+ DEBUG(2,("utmp_yield_tdb: entered\n"));
+
+ ZERO_STRUCT(revkey);
+ revkey.pid = sys_getpid();
+ revkey.cnum = conn ? conn->cnum : -1;
+
+ kbuf.dptr = (char *) &revkey;
+ kbuf.dsize = sizeof(revkey);
+
+ dbuf = tdb_fetch(tdb_utmp, kbuf);
+ if (dbuf.dptr == NULL) {
+ DEBUG(2,("utmp_yield_tdb: failed\n"));
+ return(-1); /* shouldn't happen */
+ }
+
+ /* Save our result */
+ slotnum = (int) dbuf.dptr;
+
+ /* Tidy up */
+ tdb_delete(tdb_utmp, kbuf);
+ tdb_delete(tdb_utmp, dbuf);
+
+ free(dbuf.dptr);
+ DEBUG(2,("utmp_yield_tdb: leaving with %d\n", slotnum));
+
+ return(slotnum);
+}
+
+#if defined(HAVE_UT_UT_ID)
+/****************************************************************************
+encode the unique connection number into "ut_id"
+****************************************************************************/
static const char *ut_id_encstr =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -197,61 +374,198 @@ ut_id_encode(int i, char *fourbyte)
return(i); /* 0: good; else overflow */
}
+#endif /* defined(HAVE_UT_UT_ID) */
-static int utmp_fill(struct utmp *u, const connection_struct *conn, pid_t pid, int i)
+/*
+ * ut_line:
+ * size small, e.g. Solaris: 12; FreeBSD: 8
+ * pattern conventions differ across systems.
+ * So take care in tweaking the template below.
+ * Arguably, this could be yet another smb.conf parameter.
+ */
+static const char *ut_line_template =
+#if defined(__FreeBSD__)
+ "smb%d" ;
+#else
+ "smb/%d" ;
+#endif
+
+/****************************************************************************
+Fill in a utmp (not utmpx) template
+****************************************************************************/
+static int utmp_fill(struct utmp *u, const connection_struct *conn, pid_t pid,
+ int i, pstring host)
{
+#if defined(HAVE_UT_UT_TIME)
struct timeval timeval;
- int rc;
+#endif /* defined(HAVE_UT_UT_TIME) */
+ char line_tmp[1024]; /* plenty big enough for slprintf() */
+ int line_len;
+ int rc = 0;
+/*
+ * ut_name, ut_user:
+ * Several (all?) systems seems to define one as the other.
+ * It is easier and clearer simply to let the following take its course,
+ * rather than to try to detect and optimise.
+ */
+#if defined(HAVE_UT_UT_USER)
pstrcpy(u->ut_user, conn->user);
- rc = ut_id_encode(i, u->ut_id);
- slprintf(u->ut_line, 12, "smb/%d", i);
+#endif /* defined(HAVE_UT_UT_USER) */
+#if defined(HAVE_UT_UT_NAME)
+ pstrcpy(u->ut_name, conn->user);
+#endif /* defined(HAVE_UT_UT_NAME) */
+
+/*
+ * ut_line:
+ * If size limit proves troublesome, then perhaps use "ut_id_encode()".
+ *
+ * Temporary variable "line_tmp" avoids trouble:
+ * o with unwanted trailing NULL if ut_line full;
+ * o with overflow if ut_line would be more than full.
+ */
+ memset(line_tmp, '\0', sizeof(line_tmp));
+ slprintf(line_tmp, sizeof(line_tmp), (char *) ut_line_template, i);
+ line_len = strlen(line_tmp);
+ if (line_len <= sizeof(u->ut_line)) {
+ memcpy(u->ut_line, line_tmp, sizeof(u->ut_line));
+ }
+ else {
+ DEBUG(1,("utmp_fill: ut_line exceeds field length(%d > %d)\n",
+ line_len, sizeof(u->ut_line)));
+ return(1);
+ }
+
+#if defined(HAVE_UT_UT_PID)
u->ut_pid = pid;
+#endif /* defined(HAVE_UT_UT_PID) */
+/*
+ * ut_time, ut_tv:
+ * Some have one, some the other. Many have both, but defined (aliased).
+ * It is easier and clearer simply to let the following take its course.
+ * But note that we do the more precise ut_tv as the final assignment.
+ */
+#if defined(HAVE_UT_UT_TIME)
gettimeofday(&timeval, NULL);
u->ut_time = timeval.tv_sec;
+#endif /* defined(HAVE_UT_UT_TIME) */
+
+#if defined(HAVE_UT_UT_TV)
+ gettimeofday(&timeval, NULL);
+ u->ut_tv = timeval;
+#endif /* defined(HAVE_UT_UT_TV) */
+
+#if defined(HAVE_UT_UT_HOST)
+ if (host) {
+ pstrcpy(u->ut_host, host);
+ }
+#endif /* defined(HAVE_UT_UT_HOST) */
+
+#if defined(HAVE_UT_UT_ADDR)
+ /*
+ * "(unsigned long) ut_addr" apparently exists on at least HP-UX 10.20.
+ * Volunteer to implement, please ...
+ */
+#endif /* defined(HAVE_UT_UT_ADDR) */
+
+#if defined(HAVE_UT_UT_ID)
+ rc = ut_id_encode(i, u->ut_id);
+#endif /* defined(HAVE_UT_UT_ID) */
return(rc);
}
-/* Default path (if possible) */
+/****************************************************************************
+Default paths to various {u,w}tmp{,x} files
+****************************************************************************/
#ifdef HAVE_UTMPX_H
-# ifdef UTMPX_FILE
-static char *ut_pathname = UTMPX_FILE;
+static const char *ux_pathname =
+# if defined (UTMPX_FILE)
+ UTMPX_FILE ;
+# elif defined (_UTMPX_FILE)
+ _UTMPX_FILE ;
+# elif defined (_PATH_UTMPX)
+ _PATH_UTMPX ;
# else
-static char *ut_pathname = "";
+ "" ;
# endif
-# ifdef WTMPX_FILE
-static char *wt_pathname = WTMPX_FILE;
+
+static const char *wx_pathname =
+# if defined (WTMPX_FILE)
+ WTMPX_FILE ;
+# elif defined (_WTMPX_FILE)
+ _WTMPX_FILE ;
+# elif defined (_PATH_WTMPX)
+ _PATH_WTMPX ;
# else
-static char *wt_pathname = "";
+ "" ;
# endif
-#else /* HAVE_UTMPX_H */
+#endif /* HAVE_UTMPX_H */
-# ifdef UTMP_FILE
-static char *ut_pathname = UTMP_FILE;
+static const char *ut_pathname =
+# if defined (UTMP_FILE)
+ UTMP_FILE ;
+# elif defined (_UTMP_FILE)
+ _UTMP_FILE ;
+# elif defined (_PATH_UTMP)
+ _PATH_UTMP ;
# else
-static char *ut_pathname = "";
+ "" ;
# endif
-# ifdef WTMP_FILE
-static char *wt_pathname = WTMP_FILE;
+
+static const char *wt_pathname =
+# if defined (WTMP_FILE)
+ WTMP_FILE ;
+# elif defined (_WTMP_FILE)
+ _WTMP_FILE ;
+# elif defined (_PATH_WTMP)
+ _PATH_WTMP ;
# else
-static char *wt_pathname = "";
+ "" ;
# endif
-#endif /* HAVE_UTMPX_H */
+/* BSD-like systems might want "lastlog" support. */
+/* *** Not yet implemented */
+#ifndef HAVE_PUTUTLINE /* see "pututline_my()" */
+static const char *ll_pathname =
+# if defined (_PATH_LASTLOG) /* what other names (if any?) */
+ _PATH_LASTLOG ;
+# else
+ "" ;
+# endif /* _PATH_LASTLOG */
+#endif /* HAVE_PUTUTLINE */
-static void uw_pathname(pstring fname, const char *uw_name)
+/*
+ * Get name of {u,w}tmp{,x} file.
+ * return: fname contains filename
+ * Possibly empty if this code not yet ported to this system.
+ *
+ * utmp{,x}: try "utmp dir", then default (a define)
+ * wtmp{,x}: try "wtmp dir", then "utmp dir", then default (a define)
+ */
+static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default)
{
pstring dirname;
- pstrcpy(dirname,lp_utmpdir());
- trim_string(dirname,"","/");
+ pstrcpy(dirname, "");
+
+ /* For w-files, first look for explicit "wtmp dir" */
+ if (uw_name[0] == 'w') {
+ pstrcpy(dirname,lp_wtmpdir());
+ trim_string(dirname,"","/");
+ }
- /* Given directory: use it */
+ /* For u-files and non-explicit w-dir, look for "utmp dir" */
+ if (dirname == 0 || strlen(dirname) == 0) {
+ pstrcpy(dirname,lp_utmpdir());
+ trim_string(dirname,"","/");
+ }
+
+ /* If explicit directory above, use it */
if (dirname != 0 && strlen(dirname) != 0) {
pstrcpy(fname, dirname);
pstrcat(fname, "/");
@@ -259,90 +573,256 @@ static void uw_pathname(pstring fname, const char *uw_name)
return;
}
- /* No given directory: attempt to use default paths */
- if (uw_name[0] == 'u') {
- pstrcpy(fname, ut_pathname);
+ /* No explicit directory: attempt to use default paths */
+ if (strlen(uw_default) == 0) {
+ /* No explicit setting, no known default.
+ * Has it yet been ported to this OS?
+ */
+ DEBUG(2,("uw_pathname: unable to determine pathname\n"));
+ }
+ pstrcpy(fname, uw_default);
+}
+
+#ifndef HAVE_PUTUTLINE
+/****************************************************************************
+Update utmp file directly. No subroutine interface: probably a BSD system.
+****************************************************************************/
+static void pututline_my(pstring uname, struct utmp *u, BOOL claim)
+{
+ DEBUG(1,("pututline_my: not yet implemented\n"));
+ /* BSD implementor: may want to consider (or not) adjusting "lastlog" */
+}
+#endif /* HAVE_PUTUTLINE */
+
+#ifndef HAVE_UPDWTMP
+/****************************************************************************
+Update wtmp file directly. No subroutine interface: probably a BSD system.
+Credit: Michail Vidiassov <master@iaas.msu.ru>
+****************************************************************************/
+static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim)
+{
+ int fd;
+ struct stat buf;
+
+ if (! claim) {
+ /*
+ * BSD-like systems:
+ * may use empty ut_name to distinguish a logout record.
+ *
+ * May need "if defined(SUNOS4)" etc. around some of these,
+ * but try to avoid if possible.
+ *
+ * SunOS 4:
+ * man page indicates ut_name and ut_host both NULL
+ * FreeBSD 4.0:
+ * man page appears not to specify (hints non-NULL)
+ * A correspondent suggest at least ut_name should be NULL
+ */
+ memset((char *)&(u->ut_name), '\0', sizeof(u->ut_name));
+ memset((char *)&(u->ut_host), '\0', sizeof(u->ut_host));
+ }
+ /* Stolen from logwtmp function in libutil.
+ * May be more locking/blocking is needed?
+ */
+ if ((fd = open(wname, O_WRONLY|O_APPEND, 0)) < 0)
return;
+ if (fstat(fd, &buf) == 0) {
+ if (write(fd, (char *)u, sizeof(struct utmp)) != sizeof(struct utmp))
+ (void) ftruncate(fd, buf.st_size);
}
+ (void) close(fd);
+}
+#endif /* HAVE_UPDWTMP */
- if (uw_name[0] == 'w') {
- pstrcpy(fname, wt_pathname);
+/****************************************************************************
+Update via utmp/wtmp (not utmpx/wtmpx)
+****************************************************************************/
+static void utmp_nox_update(struct utmp *u, pstring host, BOOL claim)
+{
+ pstring uname, wname;
+#if defined(PUTUTLINE_RETURNS_UTMP)
+ struct utmp *urc;
+#endif /* PUTUTLINE_RETURNS_UTMP */
+
+ uw_pathname(uname, "utmp", ut_pathname);
+ DEBUG(2,("utmp_nox_update: uname:%s\n", uname));
+
+#ifdef HAVE_PUTUTLINE
+ if (strlen(uname) != 0) {
+ utmpname(uname);
+ }
+
+# if defined(PUTUTLINE_RETURNS_UTMP)
+ setutent();
+ urc = pututline(u);
+ endutent();
+ if (urc == NULL) {
+ DEBUG(2,("utmp_nox_update: pututline() failed\n"));
return;
}
+# else /* PUTUTLINE_RETURNS_UTMP */
+ setutent();
+ pututline(u);
+ endutent();
+# endif /* PUTUTLINE_RETURNS_UTMP */
- pstrcpy(fname, "");
+#else /* HAVE_PUTUTLINE */
+ if (strlen(uname) != 0) {
+ pututline_my(uname, u, claim);
+ }
+#endif /* HAVE_PUTUTLINE */
+
+ uw_pathname(wname, "wtmp", wt_pathname);
+ DEBUG(2,("utmp_nox_update: wname:%s\n", wname));
+ if (strlen(wname) != 0) {
+#ifdef HAVE_UPDWTMP
+ updwtmp(wname, u);
+ /*
+ * updwtmp() and the newer updwtmpx() may be unsymmetrical.
+ * At least one OS, Solaris 2.x declares the former in the
+ * "utmpx" (latter) file and context.
+ * In the Solaris case this is irrelevant: it has both and
+ * we always prefer the "x" case, so doesn't come here.
+ * But are there other systems, with no "x", which lack
+ * updwtmp() perhaps?
+ */
+#else
+ updwtmp_my(wname, u, claim);
+#endif /* HAVE_UPDWTMP */
+ }
}
-static void utmp_update(const struct utmp *u, const char *host)
+/****************************************************************************
+Update via utmpx/wtmpx (preferred) or via utmp/wtmp
+****************************************************************************/
+static void utmp_update(struct utmp *u, pstring host, BOOL claim)
{
- pstring fname;
-
-#ifdef HAVE_UTMPX_H
+#if !defined(HAVE_UTMPX_H)
+ /* No utmpx stuff. Drop to non-x stuff */
+ utmp_nox_update(u, host, claim);
+#elif !defined(HAVE_PUTUTXLINE)
+ /* Odd. Have utmpx.h but no "pututxline()". Drop to non-x stuff */
+ DEBUG(1,("utmp_update: have utmpx.h but no pututxline() function\n"));
+ utmp_nox_update(u, host, claim);
+#elif !defined(HAVE_GETUTMPX)
+ /* Odd. Have utmpx.h but no "getutmpx()". Drop to non-x stuff */
+ DEBUG(1,("utmp_update: have utmpx.h but no getutmpx() function\n"));
+ utmp_nox_update(u, host, claim);
+#else
+ pstring uname, wname;
struct utmpx ux, *uxrc;
getutmpx(u, &ux);
if (host) {
#if defined(HAVE_UX_UT_SYSLEN)
- ux.ut_syslen = strlen(host);
+ ux.ut_syslen = strlen(host) + 1; /* include end NULL */
#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);
- }
- uxrc = pututxline(&ux);
- if (uxrc == NULL) {
- DEBUG(2,("utmp_update: pututxline() failed\n"));
- return;
- }
-
- uw_pathname(fname, "wtmpx");
- DEBUG(2,("utmp_update: fname:%s\n", fname));
- if (strlen(fname) != 0) {
- updwtmpx(fname, &ux);
+ uw_pathname(uname, "utmpx", ux_pathname);
+ uw_pathname(wname, "wtmpx", wx_pathname);
+ DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname));
+ /*
+ * Check for either uname or wname being empty.
+ * Some systems, such as Redhat 6, have a "utmpx.h" which doesn't
+ * define default filenames.
+ * Also, our local installation has not provided an override.
+ * Drop to non-x method. (E.g. RH6 has good defaults in "utmp.h".)
+ */
+ if ((strlen(uname) == 0) || (strlen(wname) == 0)) {
+ utmp_nox_update(u, host, claim);
}
+ else {
+ utmpxname(uname);
+ setutxent();
+ uxrc = pututxline(&ux);
+ endutxent();
+ if (uxrc == NULL) {
+ DEBUG(2,("utmp_update: pututxline() failed\n"));
+ return;
+ }
+#ifdef HAVE_UPDWTMPX
+ updwtmpx(wname, &ux);
#else
- uw_pathname(fname, "utmp");
- DEBUG(2,("utmp_update: fname:%s\n", fname));
- if (strlen(fname) != 0) {
- utmpname(fname);
+ /* Have utmpx.h but no "updwtmpx()". */
+ DEBUG(1,("utmp_update: no updwtmpx() function\n"));
+#endif /* HAVE_UPDWTMPX */
}
- pututline(u);
-
- uw_pathname(fname, "wtmp");
-
- /* *** Hmmm. Appending wtmp (as distinct from overwriting utmp) has
- me baffled. How is it to be done? *** */
-#endif
+#endif /* HAVE_UTMPX_H */
}
+/*
+ * "utmp consolidate": some background:
+ * False (default):
+ * In "utmp" files note every connection via this process.
+ * Argument "i" is simply a tty-like number we can use as-is.
+ * True:
+ * In "utmp" files, only note first open and final close. Keep:
+ * o count of open processes;
+ * o record value of first "i", to use as "i" in final close.
+ */
+static int utmp_count = 0;
+static int utmp_consolidate_conn_num;
+
+/****************************************************************************
+close a connection
+****************************************************************************/
static void utmp_yield(pid_t pid, const connection_struct *conn)
{
struct utmp u;
+ int conn_num, i;
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));
+ i = utmp_yield_tdb(conn);
+ if (i < 0) {
+ DEBUG(2,("utmp_yield: utmp_yield_tdb() failed\n"));
+ return;
+ }
+ conn_num = i;
+ DEBUG(2,("utmp_yield: conn: user:%s cnum:%d i:%d (utmp_count:%d)\n",
+ conn->user, conn->cnum, i, utmp_count));
+
+ utmp_count -= 1;
+ if (lp_utmp_consolidate()) {
+ if (utmp_count > 0) {
+ DEBUG(2,("utmp_yield: utmp consolidate: %d entries still open\n", utmp_count));
+ return;
+ }
+ else {
+ /* consolidate; final close: override conn_num */
+ conn_num = utmp_consolidate_conn_num;
+ }
+ }
memset((char *)&u, '\0', sizeof(struct utmp));
- u.ut_type = DEAD_PROCESS;
+
+#if defined(HAVE_UT_UT_EXIT)
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);
+#endif /* defined(HAVE_UT_UT_EXIT) */
+
+#if defined(HAVE_UT_UT_TYPE)
+ u.ut_type = DEAD_PROCESS;
+#endif /* defined(HAVE_UT_UT_TYPE) */
+
+ if (utmp_fill(&u, conn, pid, conn_num, NULL) == 0) {
+ utmp_update(&u, NULL, False);
}
}
+/****************************************************************************
+open a connection
+****************************************************************************/
static void utmp_claim(const struct connections_data *crec, const connection_struct *conn)
{
struct utmp u;
+ pstring host;
+ int i;
if (conn == NULL) {
DEBUG(2,("utmp_claim: conn NULL\n"));
@@ -354,17 +834,47 @@ static void utmp_claim(const struct connections_data *crec, const connection_str
return;
}
- DEBUG(2,("utmp_claim: conn: user:%s cnum:%d\n",
- conn->user, conn->cnum));
- 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()));
+ i = utmp_claim_tdb(conn);
+ if (i < 0) {
+ DEBUG(2,("utmp_claim: utmp_claim_tdb() failed\n"));
+ return;
+ }
+ pstrcpy(host, lp_utmp_hostname());
+ if (host == 0 || strlen(host) == 0) {
+ pstrcpy(host, crec->machine);
+ }
+ else {
+ /* explicit "utmp host": expand for any "%" variables */
+ standard_sub_basic(host);
+ }
+
+ DEBUG(2,("utmp_claim: conn: user:%s cnum:%d i:%d (utmp_count:%d)\n",
+ conn->user, conn->cnum, i, utmp_count));
+ DEBUG(2,("utmp_claim: crec: pid:%d, cnum:%d name:%s addr:%s mach:%s DNS:%s host:%s\n",
+ crec->pid, crec->cnum, crec->name, crec->addr, crec->machine, client_name(), host));
+
+ utmp_count += 1;
+ if (lp_utmp_consolidate()) {
+ if (utmp_count > 1) {
+ DEBUG(2,("utmp_claim: utmp consolidate: %d entries already open\n", (utmp_count-1)));
+ return;
+ }
+ else {
+ /* consolidate; first open: keep record of "i" */
+ utmp_consolidate_conn_num = i;
+ }
+ }
memset((char *)&u, '\0', sizeof(struct utmp));
+
+#if defined(HAVE_UT_UT_TYPE)
u.ut_type = USER_PROCESS;
- if (utmp_fill(&u, conn, crec->pid, conn->cnum) == 0) {
- utmp_update(&u, crec->machine);
+#endif /* defined(HAVE_UT_UT_TYPE) */
+
+ if (utmp_fill(&u, conn, crec->pid, i, host) == 0) {
+ utmp_update(&u, host, True);
}
}
-#endif
+#endif /* WITH_UTMP */