Changes from APPLIANCE_HEAD:
[samba.git] / source3 / smbd / connection.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    connection claim routines
5    Copyright (C) Andrew Tridgell 1998
6    
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.
11    
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.
16    
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.
20 */
21
22 #include "includes.h"
23
24
25 extern fstring remote_machine;
26 static TDB_CONTEXT *tdb;
27
28 extern int DEBUGLEVEL;
29
30 #ifdef WITH_UTMP
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);
33 #endif
34
35 /****************************************************************************
36  Return the connection tdb context (used for message send all).
37 ****************************************************************************/
38
39 TDB_CONTEXT *conn_tdb_ctx(void)
40 {
41         return tdb;
42 }
43
44 /****************************************************************************
45 delete a connection record
46 ****************************************************************************/
47 BOOL yield_connection(connection_struct *conn,char *name,int max_connections)
48 {
49         struct connections_key key;
50         TDB_DATA kbuf;
51
52         if (!tdb) return False;
53
54         DEBUG(3,("Yielding connection to %s\n",name));
55
56         ZERO_STRUCT(key);
57         key.pid = sys_getpid();
58         key.cnum = conn?conn->cnum:-1;
59         fstrcpy(key.name, name);
60         dos_to_unix(key.name, True);           /* Convert key to unix-codepage */
61
62         kbuf.dptr = (char *)&key;
63         kbuf.dsize = sizeof(key);
64
65         tdb_delete(tdb, kbuf);
66
67 #ifdef WITH_UTMP
68         if(conn)
69                 utmp_yield(key.pid, conn);
70 #endif
71
72         return(True);
73 }
74
75
76 /****************************************************************************
77 claim an entry in the connections database
78 ****************************************************************************/
79 BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOOL Clear)
80 {
81         struct connections_key key;
82         struct connections_data crec;
83         TDB_DATA kbuf, dbuf;
84
85         if (!tdb) {
86                 tdb = tdb_open(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST, 
87                                O_RDWR | O_CREAT, 0644);
88         }
89         if (!tdb) return False;
90
91         DEBUG(5,("claiming %s %d\n",name,max_connections));
92
93         ZERO_STRUCT(key);
94         key.pid = sys_getpid();
95         key.cnum = conn?conn->cnum:-1;
96         fstrcpy(key.name, name);
97         dos_to_unix(key.name, True);           /* Convert key to unix-codepage */
98
99         kbuf.dptr = (char *)&key;
100         kbuf.dsize = sizeof(key);
101
102         /* fill in the crec */
103         ZERO_STRUCT(crec);
104         crec.magic = 0x280267;
105         crec.pid = sys_getpid();
106         crec.cnum = conn?conn->cnum:-1;
107         if (conn) {
108                 crec.uid = conn->uid;
109                 crec.gid = conn->gid;
110                 StrnCpy(crec.name,
111                         lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
112         }
113         crec.start = time(NULL);
114         
115         StrnCpy(crec.machine,remote_machine,sizeof(crec.machine)-1);
116         StrnCpy(crec.addr,conn?conn->client_address:client_addr(),sizeof(crec.addr)-1);
117
118         dbuf.dptr = (char *)&crec;
119         dbuf.dsize = sizeof(crec);
120
121         if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) return False;
122
123 #ifdef WITH_UTMP
124         if (conn)
125             utmp_claim(&crec, conn);
126 #endif
127
128         return True;
129 }
130
131 #ifdef WITH_UTMP
132
133 /****************************************************************************
134 Reflect connection status in utmp/wtmp files.
135         T.D.Lee@durham.ac.uk  September 1999
136
137         With grateful thanks since then to many who have helped port it to
138         different operating systems.  The variety of OS quirks thereby
139         uncovered is amazing...
140
141 Hints for porting:
142         o  Always attempt to use programmatic interface (pututline() etc.)
143            Indeed, at present only programmatic use is supported.
144         o  The only currently supported programmatic interface to "wtmp{,x}"
145            is through "updwtmp*()" routines.
146         o  The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
147         o  The HAVE_* items should identify supported features.
148         o  If at all possible, avoid "if defined(MY-OS)" constructions.
149
150 OS observations and status:
151         Almost every OS seems to have its own quirks.
152
153         Solaris 2.x:
154                 Tested on 2.6 and 2.7; should be OK on other flavours.
155         AIX:
156                 Apparently has utmpx.h but doesn't implement.
157         OSF:
158                 Has utmpx.h, but (e.g.) no "getutmpx()".  (Is this like AIX ?)
159         Redhat 6:
160                 utmpx.h seems not to set default filenames.  non-x better.
161         IRIX 6.5:
162                 Not tested.  Appears to have "x".
163         HP-UX 9.x:
164                 Not tested.  Appears to lack "x".
165         HP-UX 10.x:
166                 Not tested.
167                 "updwtmp*()" routines seem absent, so no current wtmp* support.
168                 Has "ut_addr": probably trivial to implement (although remember
169                 that IPv6 is coming...).
170
171         FreeBSD:
172                 No "putut*()" type of interface.
173                 No "ut_type" and associated defines. 
174                 Write files directly.  Alternatively use its login(3)/logout(3).
175         SunOS 4:
176                 Not tested.  Resembles FreeBSD, but no login()/logout().
177
178 lastlog:
179         Should "lastlog" files, if any, be updated?
180         BSD systems (SunOS 4, FreeBSD):
181                 o  Prominent mention on man pages.
182         System-V (e.g. Solaris 2):
183                 o  No mention on man pages, even under "man -k".
184                 o  Has a "/var/adm/lastlog" file, but pututxline() etc. seem
185                    not to touch it.
186                 o  Despite downplaying (above), nevertheless has <lastlog.h>.
187         So perhaps UN*X "lastlog" facility is intended for tty/terminal only?
188
189 Notes:
190         Each connection requires a small number (starting at 0, working up)
191         to represent the line (unum).  This must be unique within and across
192         all smbd processes.
193
194         The 4 byte 'ut_id' component is vital to distinguish connections,
195         of which there could be several hundered or even thousand.
196         Entries seem to be printable characters, with optional NULL pads.
197
198         We need to be distinct from other entries in utmp/wtmp.
199
200         Observed things: therefore avoid them.  Add to this list please.
201         From Solaris 2.x (because that's what I have):
202                 'sN'    : run-levels; N: [0-9]
203                 'co'    : console
204                 'CC'    : arbitrary things;  C: [a-z]
205                 'rXNN'  : rlogin;  N: [0-9]; X: [0-9a-z]
206                 'tXNN'  : rlogin;  N: [0-9]; X: [0-9a-z]
207                 '/NNN'  : Solaris CDE
208                 'ftpZ'  : ftp (Z is the number 255, aka 0377, aka 0xff)
209         Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp",
210         but differences have been seen.
211
212         Arbitrarily I have chosen to use a distinctive 'SM' for the
213         first two bytes.
214
215         The remaining two encode the "unum" (see above).
216
217         For "utmp consolidate" the suggestion was made to encode the pid into
218         those remaining two bytes (16 bits).  But recent UNIX (e.g Solaris 8)
219         is migrating to pids > 16 bits, so we ought not to do this.
220
221 ****************************************************************************/
222
223 #include <utmp.h>
224
225 #ifdef HAVE_UTMPX_H
226 #include <utmpx.h>
227 #endif
228
229 /* BSD systems: some may need lastlog.h (SunOS 4), some may not (FreeBSD) */
230 /* Some System-V systems (e.g. Solaris 2) declare this too. */
231 #ifdef HAVE_LASTLOG_H
232 #include <lastlog.h>
233 #endif
234
235 /****************************************************************************
236 obtain/release a small number (0 upwards) unique within and across smbds
237 ****************************************************************************/
238 /*
239  * Need a "small" number to represent this connection, unique within this
240  * smbd and across all smbds.
241  *
242  * claim:
243  *      Start at 0, hunt up for free, unique number "unum" by attempting to
244  *      store it as a key in a tdb database:
245  *              key: unum               data: pid+conn  
246  *      Also store its inverse, ready for yield function:
247  *              key: pid+conn           data: unum
248  *
249  * yield:
250  *      Find key: pid+conn; data is unum;  delete record
251  *      Find key: unum ; delete record.
252  *
253  * Comment:
254  *      The claim algorithm (a "for" loop attempting to store numbers in a tdb
255  *      database) will be increasingly inefficient with larger numbers of
256  *      connections.  Is it possible to write a suitable primitive within tdb?
257  *
258  *      However, by also storing the inverse key/data pair, we at least make
259  *      the yield algorithm efficient.
260  */
261
262 static TDB_CONTEXT *tdb_utmp;
263
264 struct utmp_tdb_data {
265         pid_t pid;
266         int cnum;
267 };
268
269 static int utmp_claim_tdb(const connection_struct *conn)
270 {
271         struct utmp_tdb_data udata;
272         int i, slotnum;
273         TDB_DATA kbuf, dbuf;
274
275         if (!tdb_utmp) {
276                 tdb_utmp = tdb_open(lock_path("utmp.tdb"), 0,
277                                 TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT, 0644);
278         }
279         if (!tdb_utmp) return(-1);
280
281         DEBUG(2,("utmp_claim_tdb: entered\n"));
282
283         ZERO_STRUCT(udata);
284         udata.pid = sys_getpid();
285         udata.cnum = conn ? conn->cnum : -1;
286
287         dbuf.dptr = (char *) &udata;
288         dbuf.dsize = sizeof(udata);
289
290         /* The key is simply a number as close as possible to zero: find it */
291         slotnum = -1;
292         /* stop loop when overflow +ve integers (a huge, busy machine!) */
293         for (i = 0; i >= 0 ; i++) {
294                 kbuf.dptr = (char *) &i;
295                 kbuf.dsize = sizeof(i);
296
297                 if (tdb_store(tdb_utmp, kbuf, dbuf, TDB_INSERT) == 0) {
298                         /* have successfully grabbed a free slot */
299                         slotnum = i;
300
301                         /* store the inverse for faster utmp_yield_tdb() */
302                         tdb_store(tdb_utmp, dbuf, kbuf, TDB_INSERT);
303
304                         break;  /* Got it; escape */
305                 }
306         }
307         if (slotnum < 0) {      /* more connections than positive integers! */
308                 DEBUG(2,("utmp_claim_tdb: failed\n"));
309                 return(-1);
310         }
311
312         DEBUG(2,("utmp_claim_tdb: leaving with %d\n", slotnum));
313
314         return(slotnum);
315 }
316
317 static int utmp_yield_tdb(const connection_struct *conn)
318 {
319         struct utmp_tdb_data revkey;
320         int i, slotnum;
321         TDB_DATA kbuf, dbuf;
322
323         if (!tdb_utmp) {
324                 return(-1);
325         }
326
327         DEBUG(2,("utmp_yield_tdb: entered\n"));
328
329         ZERO_STRUCT(revkey);
330         revkey.pid = sys_getpid();
331         revkey.cnum = conn ? conn->cnum : -1;
332
333         kbuf.dptr = (char *) &revkey;
334         kbuf.dsize = sizeof(revkey);
335
336         dbuf = tdb_fetch(tdb_utmp, kbuf);
337         if (dbuf.dptr == NULL) {
338                 DEBUG(2,("utmp_yield_tdb: failed\n"));
339                 return(-1);             /* shouldn't happen */
340         }
341
342         /* Save our result */
343         slotnum = (int) dbuf.dptr;
344
345         /* Tidy up */
346         tdb_delete(tdb_utmp, kbuf);
347         tdb_delete(tdb_utmp, dbuf);
348
349         free(dbuf.dptr);
350         DEBUG(2,("utmp_yield_tdb: leaving with %d\n", slotnum));
351
352         return(slotnum);
353 }
354
355 #if defined(HAVE_UT_UT_ID)
356 /****************************************************************************
357 encode the unique connection number into "ut_id"
358 ****************************************************************************/
359 static const char *ut_id_encstr =
360         "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
361
362 static
363 int
364 ut_id_encode(int i, char *fourbyte)
365 {
366         int nbase;
367
368         fourbyte[0] = 'S';
369         fourbyte[1] = 'M';
370
371 /*
372  * Encode remaining 2 bytes from 'i'.
373  * 'ut_id_encstr' is the character set on which modulo arithmetic is done.
374  * Example: digits would produce the base-10 numbers from '001'.
375  */
376         nbase = strlen(ut_id_encstr);
377
378         fourbyte[3] = ut_id_encstr[i % nbase];
379         i /= nbase;
380         fourbyte[2] = ut_id_encstr[i % nbase];
381         i /= nbase;
382
383         return(i);      /* 0: good; else overflow */
384 }
385 #endif /* defined(HAVE_UT_UT_ID) */
386
387 /*
388  * ut_line:
389  *      size small, e.g. Solaris: 12;  FreeBSD: 8
390  *      pattern conventions differ across systems.
391  * So take care in tweaking the template below.
392  * Arguably, this could be yet another smb.conf parameter.
393  */
394 static const char *ut_line_template =
395 #if defined(__FreeBSD__)
396         "smb%d" ;
397 #else
398         "smb/%d" ;
399 #endif
400
401 /****************************************************************************
402 Fill in a utmp (not utmpx) template
403 ****************************************************************************/
404 static int utmp_fill(struct utmp *u, const connection_struct *conn, pid_t pid,
405   int i, pstring host)
406 {
407 #if defined(HAVE_UT_UT_TIME)
408         struct timeval timeval;
409 #endif /* defined(HAVE_UT_UT_TIME) */
410         char line_tmp[1024];    /* plenty big enough for slprintf() */
411         int line_len;
412         int rc = 0;
413
414 /*
415  * ut_name, ut_user:
416  *      Several (all?) systems seems to define one as the other.
417  *      It is easier and clearer simply to let the following take its course,
418  *      rather than to try to detect and optimise.
419  */
420 #if defined(HAVE_UT_UT_USER)
421         pstrcpy(u->ut_user, conn->user);
422 #endif /* defined(HAVE_UT_UT_USER) */
423
424 #if defined(HAVE_UT_UT_NAME)
425         pstrcpy(u->ut_name, conn->user);
426 #endif /* defined(HAVE_UT_UT_NAME) */
427
428 /*
429  * ut_line:
430  *      If size limit proves troublesome, then perhaps use "ut_id_encode()".
431  *
432  * Temporary variable "line_tmp" avoids trouble:
433  * o  with unwanted trailing NULL if ut_line full;
434  * o  with overflow if ut_line would be more than full.
435  */
436         memset(line_tmp, '\0', sizeof(line_tmp));
437         slprintf(line_tmp, sizeof(line_tmp), (char *) ut_line_template, i);
438         line_len = strlen(line_tmp);
439         if (line_len <= sizeof(u->ut_line)) {
440                 memcpy(u->ut_line, line_tmp, sizeof(u->ut_line));
441         }
442         else {
443                 DEBUG(1,("utmp_fill: ut_line exceeds field length(%d > %d)\n",
444                   line_len, sizeof(u->ut_line)));
445                 return(1);
446         }
447
448 #if defined(HAVE_UT_UT_PID)
449         u->ut_pid = pid;
450 #endif /* defined(HAVE_UT_UT_PID) */
451
452 /*
453  * ut_time, ut_tv: 
454  *      Some have one, some the other.  Many have both, but defined (aliased).
455  *      It is easier and clearer simply to let the following take its course.
456  *      But note that we do the more precise ut_tv as the final assignment.
457  */
458 #if defined(HAVE_UT_UT_TIME)
459         gettimeofday(&timeval, NULL);
460         u->ut_time = timeval.tv_sec;
461 #endif /* defined(HAVE_UT_UT_TIME) */
462
463 #if defined(HAVE_UT_UT_TV)
464         gettimeofday(&timeval, NULL);
465         u->ut_tv = timeval;
466 #endif /* defined(HAVE_UT_UT_TV) */
467
468 #if defined(HAVE_UT_UT_HOST)
469         if (host) {
470                 pstrcpy(u->ut_host, host);
471         }
472 #endif /* defined(HAVE_UT_UT_HOST) */
473
474 #if defined(HAVE_UT_UT_ADDR)
475         /*
476          * "(unsigned long) ut_addr" apparently exists on at least HP-UX 10.20.
477          * Volunteer to implement, please ...
478          */
479 #endif /* defined(HAVE_UT_UT_ADDR) */
480
481 #if defined(HAVE_UT_UT_ID)
482         rc = ut_id_encode(i, u->ut_id);
483 #endif /* defined(HAVE_UT_UT_ID) */
484
485         return(rc);
486 }
487
488 /****************************************************************************
489 Default paths to various {u,w}tmp{,x} files
490 ****************************************************************************/
491 #ifdef  HAVE_UTMPX_H
492
493 static const char *ux_pathname =
494 # if defined (UTMPX_FILE)
495         UTMPX_FILE ;
496 # elif defined (_UTMPX_FILE)
497         _UTMPX_FILE ;
498 # elif defined (_PATH_UTMPX)
499         _PATH_UTMPX ;
500 # else
501         "" ;
502 # endif
503
504 static const char *wx_pathname =
505 # if defined (WTMPX_FILE)
506         WTMPX_FILE ;
507 # elif defined (_WTMPX_FILE)
508         _WTMPX_FILE ;
509 # elif defined (_PATH_WTMPX)
510         _PATH_WTMPX ;
511 # else
512         "" ;
513 # endif
514
515 #endif  /* HAVE_UTMPX_H */
516
517 static const char *ut_pathname =
518 # if defined (UTMP_FILE)
519         UTMP_FILE ;
520 # elif defined (_UTMP_FILE)
521         _UTMP_FILE ;
522 # elif defined (_PATH_UTMP)
523         _PATH_UTMP ;
524 # else
525         "" ;
526 # endif
527
528 static const char *wt_pathname =
529 # if defined (WTMP_FILE)
530         WTMP_FILE ;
531 # elif defined (_WTMP_FILE)
532         _WTMP_FILE ;
533 # elif defined (_PATH_WTMP)
534         _PATH_WTMP ;
535 # else
536         "" ;
537 # endif
538
539 /* BSD-like systems might want "lastlog" support. */
540 /* *** Not yet implemented */
541 #ifndef HAVE_PUTUTLINE          /* see "pututline_my()" */
542 static const char *ll_pathname =
543 # if defined (_PATH_LASTLOG)    /* what other names (if any?) */
544         _PATH_LASTLOG ;
545 # else
546         "" ;
547 # endif /* _PATH_LASTLOG */
548 #endif  /* HAVE_PUTUTLINE */
549
550 /*
551  * Get name of {u,w}tmp{,x} file.
552  *      return: fname contains filename
553  *              Possibly empty if this code not yet ported to this system.
554  *
555  * utmp{,x}:  try "utmp dir", then default (a define)
556  * wtmp{,x}:  try "wtmp dir", then "utmp dir", then default (a define)
557  */
558 static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default)
559 {
560         pstring dirname;
561
562         pstrcpy(dirname, "");
563
564         /* For w-files, first look for explicit "wtmp dir" */
565         if (uw_name[0] == 'w') {
566                 pstrcpy(dirname,lp_wtmpdir());
567                 trim_string(dirname,"","/");
568         }
569
570         /* For u-files and non-explicit w-dir, look for "utmp dir" */
571         if (dirname == 0 || strlen(dirname) == 0) {
572                 pstrcpy(dirname,lp_utmpdir());
573                 trim_string(dirname,"","/");
574         }
575
576         /* If explicit directory above, use it */
577         if (dirname != 0 && strlen(dirname) != 0) {
578                 pstrcpy(fname, dirname);
579                 pstrcat(fname, "/");
580                 pstrcat(fname, uw_name);
581                 return;
582         }
583
584         /* No explicit directory: attempt to use default paths */
585         if (strlen(uw_default) == 0) {
586                 /* No explicit setting, no known default.
587                  * Has it yet been ported to this OS?
588                  */
589                 DEBUG(2,("uw_pathname: unable to determine pathname\n"));
590         }
591         pstrcpy(fname, uw_default);
592 }
593
594 #ifndef HAVE_PUTUTLINE
595 /****************************************************************************
596 Update utmp file directly.  No subroutine interface: probably a BSD system.
597 ****************************************************************************/
598 static void pututline_my(pstring uname, struct utmp *u, BOOL claim)
599 {
600         DEBUG(1,("pututline_my: not yet implemented\n"));
601         /* BSD implementor: may want to consider (or not) adjusting "lastlog" */
602 }
603 #endif /* HAVE_PUTUTLINE */
604
605 #ifndef HAVE_UPDWTMP
606 /****************************************************************************
607 Update wtmp file directly.  No subroutine interface: probably a BSD system.
608 Credit: Michail Vidiassov <master@iaas.msu.ru>
609 ****************************************************************************/
610 static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim)
611 {
612         int fd;
613         struct stat buf;
614
615         if (! claim) {
616                 /*
617                  * BSD-like systems:
618                  *      may use empty ut_name to distinguish a logout record.
619                  *
620                  * May need "if defined(SUNOS4)" etc. around some of these,
621                  * but try to avoid if possible.
622                  *
623                  * SunOS 4:
624                  *      man page indicates ut_name and ut_host both NULL
625                  * FreeBSD 4.0:
626                  *      man page appears not to specify (hints non-NULL)
627                  *      A correspondent suggest at least ut_name should be NULL
628                  */
629                 memset((char *)&(u->ut_name), '\0', sizeof(u->ut_name));
630                 memset((char *)&(u->ut_host), '\0', sizeof(u->ut_host));
631         }
632         /* Stolen from logwtmp function in libutil.
633          * May be more locking/blocking is needed?
634          */
635         if ((fd = open(wname, O_WRONLY|O_APPEND, 0)) < 0)
636                 return;
637         if (fstat(fd, &buf) == 0) {
638                 if (write(fd, (char *)u, sizeof(struct utmp)) != sizeof(struct utmp))
639                 (void) ftruncate(fd, buf.st_size);
640         }
641         (void) close(fd);
642 }
643 #endif /* HAVE_UPDWTMP */
644
645 /****************************************************************************
646 Update via utmp/wtmp (not utmpx/wtmpx)
647 ****************************************************************************/
648 static void utmp_nox_update(struct utmp *u, pstring host, BOOL claim)
649 {
650         pstring uname, wname;
651 #if defined(PUTUTLINE_RETURNS_UTMP)
652         struct utmp *urc;
653 #endif /* PUTUTLINE_RETURNS_UTMP */
654
655         uw_pathname(uname, "utmp", ut_pathname);
656         DEBUG(2,("utmp_nox_update: uname:%s\n", uname));
657
658 #ifdef HAVE_PUTUTLINE
659         if (strlen(uname) != 0) {
660                 utmpname(uname);
661         }
662
663 # if defined(PUTUTLINE_RETURNS_UTMP)
664         setutent();
665         urc = pututline(u);
666         endutent();
667         if (urc == NULL) {
668                 DEBUG(2,("utmp_nox_update: pututline() failed\n"));
669                 return;
670         }
671 # else  /* PUTUTLINE_RETURNS_UTMP */
672         setutent();
673         pututline(u);
674         endutent();
675 # endif /* PUTUTLINE_RETURNS_UTMP */
676
677 #else   /* HAVE_PUTUTLINE */
678         if (strlen(uname) != 0) {
679                 pututline_my(uname, u, claim);
680         }
681 #endif /* HAVE_PUTUTLINE */
682
683         uw_pathname(wname, "wtmp", wt_pathname);
684         DEBUG(2,("utmp_nox_update: wname:%s\n", wname));
685         if (strlen(wname) != 0) {
686 #ifdef HAVE_UPDWTMP
687                 updwtmp(wname, u);
688                 /*
689                  * updwtmp() and the newer updwtmpx() may be unsymmetrical.
690                  * At least one OS, Solaris 2.x declares the former in the
691                  * "utmpx" (latter) file and context.
692                  * In the Solaris case this is irrelevant: it has both and
693                  * we always prefer the "x" case, so doesn't come here.
694                  * But are there other systems, with no "x", which lack
695                  * updwtmp() perhaps?
696                  */
697 #else
698                 updwtmp_my(wname, u, claim);
699 #endif /* HAVE_UPDWTMP */
700         }
701 }
702
703 /****************************************************************************
704 Update via utmpx/wtmpx (preferred) or via utmp/wtmp
705 ****************************************************************************/
706 static void utmp_update(struct utmp *u, pstring host, BOOL claim)
707 {
708 #if !defined(HAVE_UTMPX_H)
709         /* No utmpx stuff.  Drop to non-x stuff */
710         utmp_nox_update(u, host, claim);
711 #elif !defined(HAVE_PUTUTXLINE)
712         /* Odd.  Have utmpx.h but no "pututxline()".  Drop to non-x stuff */
713         DEBUG(1,("utmp_update: have utmpx.h but no pututxline() function\n"));
714         utmp_nox_update(u, host, claim);
715 #elif !defined(HAVE_GETUTMPX)
716         /* Odd.  Have utmpx.h but no "getutmpx()".  Drop to non-x stuff */
717         DEBUG(1,("utmp_update: have utmpx.h but no getutmpx() function\n"));
718         utmp_nox_update(u, host, claim);
719 #else
720         pstring uname, wname;
721         struct utmpx ux, *uxrc;
722
723         getutmpx(u, &ux);
724         if (host) {
725 #if defined(HAVE_UX_UT_SYSLEN)
726                 ux.ut_syslen = strlen(host) + 1;        /* include end NULL */
727 #endif /* defined(HAVE_UX_UT_SYSLEN) */
728                 pstrcpy(ux.ut_host, host);
729         }
730
731         uw_pathname(uname, "utmpx", ux_pathname);
732         uw_pathname(wname, "wtmpx", wx_pathname);
733         DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname));
734         /*
735          * Check for either uname or wname being empty.
736          * Some systems, such as Redhat 6, have a "utmpx.h" which doesn't
737          * define default filenames.
738          * Also, our local installation has not provided an override.
739          * Drop to non-x method.  (E.g. RH6 has good defaults in "utmp.h".)
740          */
741         if ((strlen(uname) == 0) || (strlen(wname) == 0)) {
742                 utmp_nox_update(u, host, claim);
743         }
744         else {
745                 utmpxname(uname);
746                 setutxent();
747                 uxrc = pututxline(&ux);
748                 endutxent();
749                 if (uxrc == NULL) {
750                         DEBUG(2,("utmp_update: pututxline() failed\n"));
751                         return;
752                 }
753 #ifdef HAVE_UPDWTMPX
754                 updwtmpx(wname, &ux);
755 #else
756                 /* Have utmpx.h but no "updwtmpx()". */
757                 DEBUG(1,("utmp_update: no updwtmpx() function\n"));
758 #endif /* HAVE_UPDWTMPX */
759         }
760 #endif /* HAVE_UTMPX_H */
761 }
762
763 /*
764  * "utmp consolidate": some background:
765  *      False (default):
766  *              In "utmp" files note every connection via this process.
767  *              Argument "i" is simply a tty-like number we can use as-is.
768  *      True:
769  *              In "utmp" files, only note first open and final close.  Keep:
770  *              o  count of open processes;
771  *              o  record value of first "i", to use as "i" in final close.
772  */
773 static int utmp_count = 0;
774 static int utmp_consolidate_conn_num;
775
776 /****************************************************************************
777 close a connection
778 ****************************************************************************/
779 static void utmp_yield(pid_t pid, const connection_struct *conn)
780 {
781         struct utmp u;
782         int conn_num, i;
783
784         if (! lp_utmp(SNUM(conn))) {
785                 DEBUG(2,("utmp_yield: lp_utmp() NULL\n"));
786                 return;
787         }
788
789         i = utmp_yield_tdb(conn);
790         if (i < 0) {
791                 DEBUG(2,("utmp_yield: utmp_yield_tdb() failed\n"));
792                 return;
793         }
794         conn_num = i;
795         DEBUG(2,("utmp_yield: conn: user:%s cnum:%d i:%d (utmp_count:%d)\n",
796           conn->user, conn->cnum, i, utmp_count));
797
798         utmp_count -= 1;
799         if (lp_utmp_consolidate()) {
800                 if (utmp_count > 0) {
801                         DEBUG(2,("utmp_yield: utmp consolidate: %d entries still open\n", utmp_count));
802                         return;
803                 }
804                 else {
805                         /* consolidate; final close: override conn_num  */
806                         conn_num = utmp_consolidate_conn_num;
807                 }
808         }
809
810         memset((char *)&u, '\0', sizeof(struct utmp));
811
812 #if defined(HAVE_UT_UT_EXIT)
813         u.ut_exit.e_termination = 0;
814         u.ut_exit.e_exit = 0;
815 #endif  /* defined(HAVE_UT_UT_EXIT) */
816
817 #if defined(HAVE_UT_UT_TYPE)
818         u.ut_type = DEAD_PROCESS;
819 #endif  /* defined(HAVE_UT_UT_TYPE) */
820
821         if (utmp_fill(&u, conn, pid, conn_num, NULL) == 0) {
822                 utmp_update(&u, NULL, False);
823         }
824 }
825
826 /****************************************************************************
827 open a connection
828 ****************************************************************************/
829 static void utmp_claim(const struct connections_data *crec, const connection_struct *conn)
830 {
831         struct utmp u;
832         pstring host;
833         int i;
834
835         if (conn == NULL) {
836                 DEBUG(2,("utmp_claim: conn NULL\n"));
837                 return;
838         }
839
840         if (! lp_utmp(SNUM(conn))) {
841                 DEBUG(2,("utmp_claim: lp_utmp() NULL\n"));
842                 return;
843         }
844
845         i = utmp_claim_tdb(conn);
846         if (i < 0) {
847                 DEBUG(2,("utmp_claim: utmp_claim_tdb() failed\n"));
848                 return;
849         }
850
851         pstrcpy(host, lp_utmp_hostname());
852         if (host == 0 || strlen(host) == 0) {
853                 pstrcpy(host, crec->machine);
854         }
855         else {
856                 /* explicit "utmp host": expand for any "%" variables */
857                 standard_sub_basic(host);
858         }
859
860         DEBUG(2,("utmp_claim: conn: user:%s cnum:%d i:%d (utmp_count:%d)\n",
861           conn->user, conn->cnum, i, utmp_count));
862         DEBUG(2,("utmp_claim: crec: pid:%d, cnum:%d name:%s addr:%s mach:%s DNS:%s host:%s\n",
863           crec->pid, crec->cnum, crec->name, crec->addr, crec->machine, client_name(), host));
864
865         utmp_count += 1;
866         if (lp_utmp_consolidate()) {
867                 if (utmp_count > 1) {
868                         DEBUG(2,("utmp_claim: utmp consolidate: %d entries already open\n", (utmp_count-1)));
869                         return;
870                 }
871                 else {
872                         /* consolidate; first open: keep record of "i" */
873                         utmp_consolidate_conn_num = i;
874                 }
875         }
876
877         memset((char *)&u, '\0', sizeof(struct utmp));
878
879 #if defined(HAVE_UT_UT_TYPE)
880         u.ut_type = USER_PROCESS;
881 #endif  /* defined(HAVE_UT_UT_TYPE) */
882
883         if (utmp_fill(&u, conn, crec->pid, i, host) == 0) {
884                 utmp_update(&u, host, True);
885         }
886 }
887
888 #endif  /* WITH_UTMP */