2 Unix SMB/Netbios implementation.
4 connection claim routines
5 Copyright (C) Andrew Tridgell 1998
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 extern fstring remote_machine;
26 static TDB_CONTEXT *tdb;
28 extern int DEBUGLEVEL;
31 static void utmp_yield(pid_t pid, const connection_struct *conn);
32 static void utmp_claim(const struct connections_data *crec, const connection_struct *conn);
35 /****************************************************************************
36 delete a connection record
37 ****************************************************************************/
38 BOOL yield_connection(connection_struct *conn,char *name,int max_connections)
40 struct connections_key key;
43 if (!tdb) return False;
45 DEBUG(3,("Yielding connection to %s\n",name));
48 key.pid = sys_getpid();
49 if (conn) key.cnum = conn->cnum;
50 fstrcpy(key.name, name);
52 kbuf.dptr = (char *)&key;
53 kbuf.dsize = sizeof(key);
55 tdb_delete(tdb, kbuf);
59 utmp_yield(key.pid, conn);
66 /****************************************************************************
67 claim an entry in the connections database
68 ****************************************************************************/
69 BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOOL Clear)
71 struct connections_key key;
72 struct connections_data crec;
76 tdb = tdb_open(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST,
77 O_RDWR | O_CREAT, 0644);
79 if (!tdb) return False;
81 DEBUG(5,("claiming %s %d\n",name,max_connections));
84 key.pid = sys_getpid();
85 key.cnum = conn?conn->cnum:-1;
86 fstrcpy(key.name, name);
88 kbuf.dptr = (char *)&key;
89 kbuf.dsize = sizeof(key);
91 /* fill in the crec */
93 crec.magic = 0x280267;
94 crec.pid = sys_getpid();
95 crec.cnum = conn?conn->cnum:-1;
100 lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
102 crec.start = time(NULL);
104 StrnCpy(crec.machine,remote_machine,sizeof(crec.machine)-1);
105 StrnCpy(crec.addr,conn?conn->client_address:client_addr(),sizeof(crec.addr)-1);
107 dbuf.dptr = (char *)&crec;
108 dbuf.dsize = sizeof(crec);
110 if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) return False;
114 utmp_claim(&crec, conn);
122 /****************************************************************************
123 Reflect connection status in utmp/wtmp files.
124 T.D.Lee@durham.ac.uk September 1999
126 With grateful thanks since then to many who have helped port it to
127 different operating systems. The variety of OS quirks thereby
128 uncovered is amazing...
131 o Always attempt to use programmatic interface (pututline() etc.)
132 Indeed, at present only programmatic use is supported.
133 o The only currently supported programmatic interface to "wtmp{,x}"
134 is through "updwtmp*()" routines.
135 o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
136 o The HAVE_* items should identify supported features.
137 o If at all possible, avoid "if defined(MY-OS)" constructions.
139 OS observations and status:
140 Almost every OS seems to have its own quirks.
143 Tested on 2.6 and 2.7; should be OK on other flavours.
145 Apparently has utmpx.h but doesn't implement.
147 Has utmpx.h, but (e.g.) no "getutmpx()". (Is this like AIX ?)
149 utmpx.h seems not to set default filenames. non-x better.
151 Not tested. Appears to have "x".
153 Not tested. Appears to lack "x".
156 "updwtmp*()" routines seem absent, so no current wtmp* support.
157 Has "ut_addr": probably trivial to implement (although remember
158 that IPv6 is coming...).
161 No "putut*()" type of interface.
162 No "ut_type" and associated defines.
163 Write files directly. Alternatively use its login(3)/logout(3).
165 Not tested. Resembles FreeBSD, but no login()/logout().
168 Should "lastlog" files, if any, be updated?
169 BSD systems (SunOS 4, FreeBSD):
170 o Prominent mention on man pages.
171 System-V (e.g. Solaris 2):
172 o No mention on man pages, even under "man -k".
173 o Has a "/var/adm/lastlog" file, but pututxline() etc. seem
175 o Despite downplaying (above), nevertheless has <lastlog.h>.
176 So perhaps UN*X "lastlog" facility is intended for tty/terminal only?
179 Each connection requires a small number (starting at 0, working up)
180 to represent the line (unum). This must be unique within and across
183 The 4 byte 'ut_id' component is vital to distinguish connections,
184 of which there could be several hundered or even thousand.
185 Entries seem to be printable characters, with optional NULL pads.
187 We need to be distinct from other entries in utmp/wtmp.
189 Observed things: therefore avoid them. Add to this list please.
190 From Solaris 2.x (because that's what I have):
191 'sN' : run-levels; N: [0-9]
193 'CC' : arbitrary things; C: [a-z]
194 'rXNN' : rlogin; N: [0-9]; X: [0-9a-z]
195 'tXNN' : rlogin; N: [0-9]; X: [0-9a-z]
197 'ftpZ' : ftp (Z is the number 255, aka 0377, aka 0xff)
198 Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp",
199 but differences have been seen.
201 Arbitrarily I have chosen to use a distinctive 'SM' for the
204 The remaining two encode the "unum" (see above).
206 For "utmp consolidate" the suggestion was made to encode the pid into
207 those remaining two bytes (16 bits). But recent UNIX (e.g Solaris 8)
208 is migrating to pids > 16 bits, so we ought not to do this.
210 ****************************************************************************/
218 /* BSD systems: some may need lastlog.h (SunOS 4), some may not (FreeBSD) */
219 /* Some System-V systems (e.g. Solaris 2) declare this too. */
220 #ifdef HAVE_LASTLOG_H
224 /****************************************************************************
225 obtain/release a small number (0 upwards) unique within and across smbds
226 ****************************************************************************/
228 * Need a "small" number to represent this connection, unique within this
229 * smbd and across all smbds.
232 * Start at 0, hunt up for free, unique number "unum" by attempting to
233 * store it as a key in a tdb database:
234 * key: unum data: pid+conn
235 * Also store its inverse, ready for yield function:
236 * key: pid+conn data: unum
239 * Find key: pid+conn; data is unum; delete record
240 * Find key: unum ; delete record.
243 * The claim algorithm (a "for" loop attempting to store numbers in a tdb
244 * database) will be increasingly inefficient with larger numbers of
245 * connections. Is it possible to write a suitable primitive within tdb?
247 * However, by also storing the inverse key/data pair, we at least make
248 * the yield algorithm efficient.
251 static TDB_CONTEXT *tdb_utmp;
253 struct utmp_tdb_data {
258 static int utmp_claim_tdb(const connection_struct *conn)
260 struct utmp_tdb_data udata;
265 tdb_utmp = tdb_open(lock_path("utmp.tdb"), 0,
266 TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT, 0644);
268 if (!tdb_utmp) return(-1);
270 DEBUG(2,("utmp_claim_tdb: entered\n"));
273 udata.pid = sys_getpid();
274 udata.cnum = conn ? conn->cnum : -1;
276 dbuf.dptr = (char *) &udata;
277 dbuf.dsize = sizeof(udata);
279 /* The key is simply a number as close as possible to zero: find it */
281 /* stop loop when overflow +ve integers (a huge, busy machine!) */
282 for (i = 0; i >= 0 ; i++) {
283 kbuf.dptr = (char *) &i;
284 kbuf.dsize = sizeof(i);
286 if (tdb_store(tdb_utmp, kbuf, dbuf, TDB_INSERT) == 0) {
287 /* have successfully grabbed a free slot */
290 /* store the inverse for faster utmp_yield_tdb() */
291 tdb_store(tdb_utmp, dbuf, kbuf, TDB_INSERT);
293 break; /* Got it; escape */
296 if (slotnum < 0) { /* more connections than positive integers! */
297 DEBUG(2,("utmp_claim_tdb: failed\n"));
301 DEBUG(2,("utmp_claim_tdb: leaving with %d\n", slotnum));
306 static int utmp_yield_tdb(const connection_struct *conn)
308 struct utmp_tdb_data revkey;
316 DEBUG(2,("utmp_yield_tdb: entered\n"));
319 revkey.pid = sys_getpid();
320 revkey.cnum = conn ? conn->cnum : -1;
322 kbuf.dptr = (char *) &revkey;
323 kbuf.dsize = sizeof(revkey);
325 dbuf = tdb_fetch(tdb_utmp, kbuf);
326 if (dbuf.dptr == NULL) {
327 DEBUG(2,("utmp_yield_tdb: failed\n"));
328 return(-1); /* shouldn't happen */
331 /* Save our result */
332 slotnum = (int) dbuf.dptr;
335 tdb_delete(tdb_utmp, kbuf);
336 tdb_delete(tdb_utmp, dbuf);
339 DEBUG(2,("utmp_yield_tdb: leaving with %d\n", slotnum));
344 #if defined(HAVE_UT_UT_ID)
345 /****************************************************************************
346 encode the unique connection number into "ut_id"
347 ****************************************************************************/
348 static const char *ut_id_encstr =
349 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
353 ut_id_encode(int i, char *fourbyte)
361 * Encode remaining 2 bytes from 'i'.
362 * 'ut_id_encstr' is the character set on which modulo arithmetic is done.
363 * Example: digits would produce the base-10 numbers from '001'.
365 nbase = strlen(ut_id_encstr);
367 fourbyte[3] = ut_id_encstr[i % nbase];
369 fourbyte[2] = ut_id_encstr[i % nbase];
372 return(i); /* 0: good; else overflow */
374 #endif /* defined(HAVE_UT_UT_ID) */
378 * size small, e.g. Solaris: 12; FreeBSD: 8
379 * pattern conventions differ across systems.
380 * So take care in tweaking the template below.
381 * Arguably, this could be yet another smb.conf parameter.
383 static const char *ut_line_template =
384 #if defined(__FreeBSD__)
390 /****************************************************************************
391 Fill in a utmp (not utmpx) template
392 ****************************************************************************/
393 static int utmp_fill(struct utmp *u, const connection_struct *conn, pid_t pid,
396 #if defined(HAVE_UT_UT_TIME)
397 struct timeval timeval;
398 #endif /* defined(HAVE_UT_UT_TIME) */
399 char line_tmp[1024]; /* plenty big enough for slprintf() */
405 * Several (all?) systems seems to define one as the other.
406 * It is easier and clearer simply to let the following take its course,
407 * rather than to try to detect and optimise.
409 #if defined(HAVE_UT_UT_USER)
410 pstrcpy(u->ut_user, conn->user);
411 #endif /* defined(HAVE_UT_UT_USER) */
413 #if defined(HAVE_UT_UT_NAME)
414 pstrcpy(u->ut_name, conn->user);
415 #endif /* defined(HAVE_UT_UT_NAME) */
419 * If size limit proves troublesome, then perhaps use "ut_id_encode()".
421 * Temporary variable "line_tmp" avoids trouble:
422 * o with unwanted trailing NULL if ut_line full;
423 * o with overflow if ut_line would be more than full.
425 memset(line_tmp, '\0', sizeof(line_tmp));
426 slprintf(line_tmp, sizeof(line_tmp), (char *) ut_line_template, i);
427 line_len = strlen(line_tmp);
428 if (line_len <= sizeof(u->ut_line)) {
429 memcpy(u->ut_line, line_tmp, sizeof(u->ut_line));
432 DEBUG(1,("utmp_fill: ut_line exceeds field length(%d > %d)\n",
433 line_len, sizeof(u->ut_line)));
437 #if defined(HAVE_UT_UT_PID)
439 #endif /* defined(HAVE_UT_UT_PID) */
443 * Some have one, some the other. Many have both, but defined (aliased).
444 * It is easier and clearer simply to let the following take its course.
445 * But note that we do the more precise ut_tv as the final assignment.
447 #if defined(HAVE_UT_UT_TIME)
448 gettimeofday(&timeval, NULL);
449 u->ut_time = timeval.tv_sec;
450 #endif /* defined(HAVE_UT_UT_TIME) */
452 #if defined(HAVE_UT_UT_TV)
453 gettimeofday(&timeval, NULL);
455 #endif /* defined(HAVE_UT_UT_TV) */
457 #if defined(HAVE_UT_UT_HOST)
459 pstrcpy(u->ut_host, host);
461 #endif /* defined(HAVE_UT_UT_HOST) */
463 #if defined(HAVE_UT_UT_ADDR)
465 * "(unsigned long) ut_addr" apparently exists on at least HP-UX 10.20.
466 * Volunteer to implement, please ...
468 #endif /* defined(HAVE_UT_UT_ADDR) */
470 #if defined(HAVE_UT_UT_ID)
471 rc = ut_id_encode(i, u->ut_id);
472 #endif /* defined(HAVE_UT_UT_ID) */
477 /****************************************************************************
478 Default paths to various {u,w}tmp{,x} files
479 ****************************************************************************/
482 static const char *ux_pathname =
483 # if defined (UTMPX_FILE)
485 # elif defined (_UTMPX_FILE)
487 # elif defined (_PATH_UTMPX)
493 static const char *wx_pathname =
494 # if defined (WTMPX_FILE)
496 # elif defined (_WTMPX_FILE)
498 # elif defined (_PATH_WTMPX)
504 #endif /* HAVE_UTMPX_H */
506 static const char *ut_pathname =
507 # if defined (UTMP_FILE)
509 # elif defined (_UTMP_FILE)
511 # elif defined (_PATH_UTMP)
517 static const char *wt_pathname =
518 # if defined (WTMP_FILE)
520 # elif defined (_WTMP_FILE)
522 # elif defined (_PATH_WTMP)
528 /* BSD-like systems might want "lastlog" support. */
529 /* *** Not yet implemented */
530 #ifndef HAVE_PUTUTLINE /* see "pututline_my()" */
531 static const char *ll_pathname =
532 # if defined (_PATH_LASTLOG) /* what other names (if any?) */
536 # endif /* _PATH_LASTLOG */
537 #endif /* HAVE_PUTUTLINE */
540 * Get name of {u,w}tmp{,x} file.
541 * return: fname contains filename
542 * Possibly empty if this code not yet ported to this system.
544 * utmp{,x}: try "utmp dir", then default (a define)
545 * wtmp{,x}: try "wtmp dir", then "utmp dir", then default (a define)
547 static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default)
551 pstrcpy(dirname, "");
553 /* For w-files, first look for explicit "wtmp dir" */
554 if (uw_name[0] == 'w') {
555 pstrcpy(dirname,lp_wtmpdir());
556 trim_string(dirname,"","/");
559 /* For u-files and non-explicit w-dir, look for "utmp dir" */
560 if (dirname == 0 || strlen(dirname) == 0) {
561 pstrcpy(dirname,lp_utmpdir());
562 trim_string(dirname,"","/");
565 /* If explicit directory above, use it */
566 if (dirname != 0 && strlen(dirname) != 0) {
567 pstrcpy(fname, dirname);
569 pstrcat(fname, uw_name);
573 /* No explicit directory: attempt to use default paths */
574 if (strlen(uw_default) == 0) {
575 /* No explicit setting, no known default.
576 * Has it yet been ported to this OS?
578 DEBUG(2,("uw_pathname: unable to determine pathname\n"));
580 pstrcpy(fname, uw_default);
583 #ifndef HAVE_PUTUTLINE
584 /****************************************************************************
585 Update utmp file directly. No subroutine interface: probably a BSD system.
586 ****************************************************************************/
587 static void pututline_my(pstring uname, struct utmp *u, BOOL claim)
589 DEBUG(1,("pututline_my: not yet implemented\n"));
590 /* BSD implementor: may want to consider (or not) adjusting "lastlog" */
592 #endif /* HAVE_PUTUTLINE */
595 /****************************************************************************
596 Update wtmp file directly. No subroutine interface: probably a BSD system.
597 Credit: Michail Vidiassov <master@iaas.msu.ru>
598 ****************************************************************************/
599 static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim)
607 * may use empty ut_name to distinguish a logout record.
609 * May need "if defined(SUNOS4)" etc. around some of these,
610 * but try to avoid if possible.
613 * man page indicates ut_name and ut_host both NULL
615 * man page appears not to specify (hints non-NULL)
616 * A correspondent suggest at least ut_name should be NULL
618 memset((char *)&(u->ut_name), '\0', sizeof(u->ut_name));
619 memset((char *)&(u->ut_host), '\0', sizeof(u->ut_host));
621 /* Stolen from logwtmp function in libutil.
622 * May be more locking/blocking is needed?
624 if ((fd = open(wname, O_WRONLY|O_APPEND, 0)) < 0)
626 if (fstat(fd, &buf) == 0) {
627 if (write(fd, (char *)u, sizeof(struct utmp)) != sizeof(struct utmp))
628 (void) ftruncate(fd, buf.st_size);
632 #endif /* HAVE_UPDWTMP */
634 /****************************************************************************
635 Update via utmp/wtmp (not utmpx/wtmpx)
636 ****************************************************************************/
637 static void utmp_nox_update(struct utmp *u, pstring host, BOOL claim)
639 pstring uname, wname;
640 #if defined(PUTUTLINE_RETURNS_UTMP)
642 #endif /* PUTUTLINE_RETURNS_UTMP */
644 uw_pathname(uname, "utmp", ut_pathname);
645 DEBUG(2,("utmp_nox_update: uname:%s\n", uname));
647 #ifdef HAVE_PUTUTLINE
648 if (strlen(uname) != 0) {
652 # if defined(PUTUTLINE_RETURNS_UTMP)
657 DEBUG(2,("utmp_nox_update: pututline() failed\n"));
660 # else /* PUTUTLINE_RETURNS_UTMP */
664 # endif /* PUTUTLINE_RETURNS_UTMP */
666 #else /* HAVE_PUTUTLINE */
667 if (strlen(uname) != 0) {
668 pututline_my(uname, u, claim);
670 #endif /* HAVE_PUTUTLINE */
672 uw_pathname(wname, "wtmp", wt_pathname);
673 DEBUG(2,("utmp_nox_update: wname:%s\n", wname));
674 if (strlen(wname) != 0) {
678 * updwtmp() and the newer updwtmpx() may be unsymmetrical.
679 * At least one OS, Solaris 2.x declares the former in the
680 * "utmpx" (latter) file and context.
681 * In the Solaris case this is irrelevant: it has both and
682 * we always prefer the "x" case, so doesn't come here.
683 * But are there other systems, with no "x", which lack
687 updwtmp_my(wname, u, claim);
688 #endif /* HAVE_UPDWTMP */
692 /****************************************************************************
693 Update via utmpx/wtmpx (preferred) or via utmp/wtmp
694 ****************************************************************************/
695 static void utmp_update(struct utmp *u, pstring host, BOOL claim)
697 #if !defined(HAVE_UTMPX_H)
698 /* No utmpx stuff. Drop to non-x stuff */
699 utmp_nox_update(u, host, claim);
700 #elif !defined(HAVE_PUTUTXLINE)
701 /* Odd. Have utmpx.h but no "pututxline()". Drop to non-x stuff */
702 DEBUG(1,("utmp_update: have utmpx.h but no pututxline() function\n"));
703 utmp_nox_update(u, host, claim);
704 #elif !defined(HAVE_GETUTMPX)
705 /* Odd. Have utmpx.h but no "getutmpx()". Drop to non-x stuff */
706 DEBUG(1,("utmp_update: have utmpx.h but no getutmpx() function\n"));
707 utmp_nox_update(u, host, claim);
709 pstring uname, wname;
710 struct utmpx ux, *uxrc;
714 #if defined(HAVE_UX_UT_SYSLEN)
715 ux.ut_syslen = strlen(host) + 1; /* include end NULL */
716 #endif /* defined(HAVE_UX_UT_SYSLEN) */
717 pstrcpy(ux.ut_host, host);
720 uw_pathname(uname, "utmpx", ux_pathname);
721 uw_pathname(wname, "wtmpx", wx_pathname);
722 DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname));
724 * Check for either uname or wname being empty.
725 * Some systems, such as Redhat 6, have a "utmpx.h" which doesn't
726 * define default filenames.
727 * Also, our local installation has not provided an override.
728 * Drop to non-x method. (E.g. RH6 has good defaults in "utmp.h".)
730 if ((strlen(uname) == 0) || (strlen(wname) == 0)) {
731 utmp_nox_update(u, host, claim);
736 uxrc = pututxline(&ux);
739 DEBUG(2,("utmp_update: pututxline() failed\n"));
743 updwtmpx(wname, &ux);
745 /* Have utmpx.h but no "updwtmpx()". */
746 DEBUG(1,("utmp_update: no updwtmpx() function\n"));
747 #endif /* HAVE_UPDWTMPX */
749 #endif /* HAVE_UTMPX_H */
753 * "utmp consolidate": some background:
755 * In "utmp" files note every connection via this process.
756 * Argument "i" is simply a tty-like number we can use as-is.
758 * In "utmp" files, only note first open and final close. Keep:
759 * o count of open processes;
760 * o record value of first "i", to use as "i" in final close.
762 static int utmp_count = 0;
763 static int utmp_consolidate_conn_num;
765 /****************************************************************************
767 ****************************************************************************/
768 static void utmp_yield(pid_t pid, const connection_struct *conn)
773 if (! lp_utmp(SNUM(conn))) {
774 DEBUG(2,("utmp_yield: lp_utmp() NULL\n"));
778 i = utmp_yield_tdb(conn);
780 DEBUG(2,("utmp_yield: utmp_yield_tdb() failed\n"));
784 DEBUG(2,("utmp_yield: conn: user:%s cnum:%d i:%d (utmp_count:%d)\n",
785 conn->user, conn->cnum, i, utmp_count));
788 if (lp_utmp_consolidate()) {
789 if (utmp_count > 0) {
790 DEBUG(2,("utmp_yield: utmp consolidate: %d entries still open\n", utmp_count));
794 /* consolidate; final close: override conn_num */
795 conn_num = utmp_consolidate_conn_num;
799 memset((char *)&u, '\0', sizeof(struct utmp));
801 #if defined(HAVE_UT_UT_EXIT)
802 u.ut_exit.e_termination = 0;
803 u.ut_exit.e_exit = 0;
804 #endif /* defined(HAVE_UT_UT_EXIT) */
806 #if defined(HAVE_UT_UT_TYPE)
807 u.ut_type = DEAD_PROCESS;
808 #endif /* defined(HAVE_UT_UT_TYPE) */
810 if (utmp_fill(&u, conn, pid, conn_num, NULL) == 0) {
811 utmp_update(&u, NULL, False);
815 /****************************************************************************
817 ****************************************************************************/
818 static void utmp_claim(const struct connections_data *crec, const connection_struct *conn)
825 DEBUG(2,("utmp_claim: conn NULL\n"));
829 if (! lp_utmp(SNUM(conn))) {
830 DEBUG(2,("utmp_claim: lp_utmp() NULL\n"));
834 i = utmp_claim_tdb(conn);
836 DEBUG(2,("utmp_claim: utmp_claim_tdb() failed\n"));
840 pstrcpy(host, lp_utmp_hostname());
841 if (host == 0 || strlen(host) == 0) {
842 pstrcpy(host, crec->machine);
845 /* explicit "utmp host": expand for any "%" variables */
846 standard_sub_basic(host);
849 DEBUG(2,("utmp_claim: conn: user:%s cnum:%d i:%d (utmp_count:%d)\n",
850 conn->user, conn->cnum, i, utmp_count));
851 DEBUG(2,("utmp_claim: crec: pid:%d, cnum:%d name:%s addr:%s mach:%s DNS:%s host:%s\n",
852 crec->pid, crec->cnum, crec->name, crec->addr, crec->machine, client_name(), host));
855 if (lp_utmp_consolidate()) {
856 if (utmp_count > 1) {
857 DEBUG(2,("utmp_claim: utmp consolidate: %d entries already open\n", (utmp_count-1)));
861 /* consolidate; first open: keep record of "i" */
862 utmp_consolidate_conn_num = i;
866 memset((char *)&u, '\0', sizeof(struct utmp));
868 #if defined(HAVE_UT_UT_TYPE)
869 u.ut_type = USER_PROCESS;
870 #endif /* defined(HAVE_UT_UT_TYPE) */
872 if (utmp_fill(&u, conn, crec->pid, i, host) == 0) {
873 utmp_update(&u, host, True);
877 #endif /* WITH_UTMP */