fixed the problem with messages not getting through
[samba.git] / source / 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 delete a connection record
37 ****************************************************************************/
38 BOOL yield_connection(connection_struct *conn,char *name,int max_connections)
39 {
40         struct connections_key key;
41         TDB_DATA kbuf;
42
43         if (!tdb) return False;
44
45         DEBUG(3,("Yielding connection to %s\n",name));
46
47         ZERO_STRUCT(key);
48         key.pid = sys_getpid();
49         if (conn) key.cnum = conn->cnum;
50         fstrcpy(key.name, name);
51
52         kbuf.dptr = (char *)&key;
53         kbuf.dsize = sizeof(key);
54
55         tdb_delete(tdb, kbuf);
56
57 #ifdef WITH_UTMP
58         if(conn)
59                 utmp_yield(key.pid, conn);
60 #endif
61
62         return(True);
63 }
64
65
66 /****************************************************************************
67 claim an entry in the connections database
68 ****************************************************************************/
69 BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOOL Clear)
70 {
71         struct connections_key key;
72         struct connections_data crec;
73         TDB_DATA kbuf, dbuf;
74
75         if (!tdb) {
76                 tdb = tdb_open(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST, 
77                                O_RDWR | O_CREAT, 0644);
78         }
79         if (!tdb) return False;
80
81         DEBUG(5,("claiming %s %d\n",name,max_connections));
82
83         ZERO_STRUCT(key);
84         key.pid = sys_getpid();
85         key.cnum = conn?conn->cnum:-1;
86         fstrcpy(key.name, name);
87
88         kbuf.dptr = (char *)&key;
89         kbuf.dsize = sizeof(key);
90
91         /* fill in the crec */
92         ZERO_STRUCT(crec);
93         crec.magic = 0x280267;
94         crec.pid = sys_getpid();
95         crec.cnum = conn?conn->cnum:-1;
96         if (conn) {
97                 crec.uid = conn->uid;
98                 crec.gid = conn->gid;
99                 StrnCpy(crec.name,
100                         lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
101         }
102         crec.start = time(NULL);
103         
104         StrnCpy(crec.machine,remote_machine,sizeof(crec.machine)-1);
105         StrnCpy(crec.addr,conn?conn->client_address:client_addr(),sizeof(crec.addr)-1);
106
107         dbuf.dptr = (char *)&crec;
108         dbuf.dsize = sizeof(crec);
109
110         if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) return False;
111
112 #ifdef WITH_UTMP
113         if (conn)
114             utmp_claim(&crec, conn);
115 #endif
116
117         return True;
118 }
119
120 #ifdef WITH_UTMP
121
122 /****************************************************************************
123 Reflect connection status in utmp/wtmp files.
124         T.D.Lee@durham.ac.uk  September 1999
125
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...
129
130 Hints for porting:
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.
138
139 OS observations and status:
140         Almost every OS seems to have its own quirks.
141
142         Solaris 2.x:
143                 Tested on 2.6 and 2.7; should be OK on other flavours.
144         AIX:
145                 Apparently has utmpx.h but doesn't implement.
146         OSF:
147                 Has utmpx.h, but (e.g.) no "getutmpx()".  (Is this like AIX ?)
148         Redhat 6:
149                 utmpx.h seems not to set default filenames.  non-x better.
150         IRIX 6.5:
151                 Not tested.  Appears to have "x".
152         HP-UX 9.x:
153                 Not tested.  Appears to lack "x".
154         HP-UX 10.x:
155                 Not tested.
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...).
159
160         FreeBSD:
161                 No "putut*()" type of interface.
162                 No "ut_type" and associated defines. 
163                 Write files directly.  Alternatively use its login(3)/logout(3).
164         SunOS 4:
165                 Not tested.  Resembles FreeBSD, but no login()/logout().
166
167 lastlog:
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
174                    not to touch it.
175                 o  Despite downplaying (above), nevertheless has <lastlog.h>.
176         So perhaps UN*X "lastlog" facility is intended for tty/terminal only?
177
178 Notes:
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
181         all smbd processes.
182
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.
186
187         We need to be distinct from other entries in utmp/wtmp.
188
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]
192                 'co'    : console
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]
196                 '/NNN'  : Solaris CDE
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.
200
201         Arbitrarily I have chosen to use a distinctive 'SM' for the
202         first two bytes.
203
204         The remaining two encode the "unum" (see above).
205
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.
209
210 ****************************************************************************/
211
212 #include <utmp.h>
213
214 #ifdef HAVE_UTMPX_H
215 #include <utmpx.h>
216 #endif
217
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
221 #include <lastlog.h>
222 #endif
223
224 /****************************************************************************
225 obtain/release a small number (0 upwards) unique within and across smbds
226 ****************************************************************************/
227 /*
228  * Need a "small" number to represent this connection, unique within this
229  * smbd and across all smbds.
230  *
231  * claim:
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
237  *
238  * yield:
239  *      Find key: pid+conn; data is unum;  delete record
240  *      Find key: unum ; delete record.
241  *
242  * Comment:
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?
246  *
247  *      However, by also storing the inverse key/data pair, we at least make
248  *      the yield algorithm efficient.
249  */
250
251 static TDB_CONTEXT *tdb_utmp;
252
253 struct utmp_tdb_data {
254         pid_t pid;
255         int cnum;
256 };
257
258 static int utmp_claim_tdb(const connection_struct *conn)
259 {
260         struct utmp_tdb_data udata;
261         int i, slotnum;
262         TDB_DATA kbuf, dbuf;
263
264         if (!tdb_utmp) {
265                 tdb_utmp = tdb_open(lock_path("utmp.tdb"), 0,
266                                 TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT, 0644);
267         }
268         if (!tdb_utmp) return(-1);
269
270         DEBUG(2,("utmp_claim_tdb: entered\n"));
271
272         ZERO_STRUCT(udata);
273         udata.pid = sys_getpid();
274         udata.cnum = conn ? conn->cnum : -1;
275
276         dbuf.dptr = (char *) &udata;
277         dbuf.dsize = sizeof(udata);
278
279         /* The key is simply a number as close as possible to zero: find it */
280         slotnum = -1;
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);
285
286                 if (tdb_store(tdb_utmp, kbuf, dbuf, TDB_INSERT) == 0) {
287                         /* have successfully grabbed a free slot */
288                         slotnum = i;
289
290                         /* store the inverse for faster utmp_yield_tdb() */
291                         tdb_store(tdb_utmp, dbuf, kbuf, TDB_INSERT);
292
293                         break;  /* Got it; escape */
294                 }
295         }
296         if (slotnum < 0) {      /* more connections than positive integers! */
297                 DEBUG(2,("utmp_claim_tdb: failed\n"));
298                 return(-1);
299         }
300
301         DEBUG(2,("utmp_claim_tdb: leaving with %d\n", slotnum));
302
303         return(slotnum);
304 }
305
306 static int utmp_yield_tdb(const connection_struct *conn)
307 {
308         struct utmp_tdb_data revkey;
309         int i, slotnum;
310         TDB_DATA kbuf, dbuf;
311
312         if (!tdb_utmp) {
313                 return(-1);
314         }
315
316         DEBUG(2,("utmp_yield_tdb: entered\n"));
317
318         ZERO_STRUCT(revkey);
319         revkey.pid = sys_getpid();
320         revkey.cnum = conn ? conn->cnum : -1;
321
322         kbuf.dptr = (char *) &revkey;
323         kbuf.dsize = sizeof(revkey);
324
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 */
329         }
330
331         /* Save our result */
332         slotnum = (int) dbuf.dptr;
333
334         /* Tidy up */
335         tdb_delete(tdb_utmp, kbuf);
336         tdb_delete(tdb_utmp, dbuf);
337
338         free(dbuf.dptr);
339         DEBUG(2,("utmp_yield_tdb: leaving with %d\n", slotnum));
340
341         return(slotnum);
342 }
343
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";
350
351 static
352 int
353 ut_id_encode(int i, char *fourbyte)
354 {
355         int nbase;
356
357         fourbyte[0] = 'S';
358         fourbyte[1] = 'M';
359
360 /*
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'.
364  */
365         nbase = strlen(ut_id_encstr);
366
367         fourbyte[3] = ut_id_encstr[i % nbase];
368         i /= nbase;
369         fourbyte[2] = ut_id_encstr[i % nbase];
370         i /= nbase;
371
372         return(i);      /* 0: good; else overflow */
373 }
374 #endif /* defined(HAVE_UT_UT_ID) */
375
376 /*
377  * ut_line:
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.
382  */
383 static const char *ut_line_template =
384 #if defined(__FreeBSD__)
385         "smb%d" ;
386 #else
387         "smb/%d" ;
388 #endif
389
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,
394   int i, pstring host)
395 {
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() */
400         int line_len;
401         int rc = 0;
402
403 /*
404  * ut_name, ut_user:
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.
408  */
409 #if defined(HAVE_UT_UT_USER)
410         pstrcpy(u->ut_user, conn->user);
411 #endif /* defined(HAVE_UT_UT_USER) */
412
413 #if defined(HAVE_UT_UT_NAME)
414         pstrcpy(u->ut_name, conn->user);
415 #endif /* defined(HAVE_UT_UT_NAME) */
416
417 /*
418  * ut_line:
419  *      If size limit proves troublesome, then perhaps use "ut_id_encode()".
420  *
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.
424  */
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));
430         }
431         else {
432                 DEBUG(1,("utmp_fill: ut_line exceeds field length(%d > %d)\n",
433                   line_len, sizeof(u->ut_line)));
434                 return(1);
435         }
436
437 #if defined(HAVE_UT_UT_PID)
438         u->ut_pid = pid;
439 #endif /* defined(HAVE_UT_UT_PID) */
440
441 /*
442  * ut_time, ut_tv: 
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.
446  */
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) */
451
452 #if defined(HAVE_UT_UT_TV)
453         gettimeofday(&timeval, NULL);
454         u->ut_tv = timeval;
455 #endif /* defined(HAVE_UT_UT_TV) */
456
457 #if defined(HAVE_UT_UT_HOST)
458         if (host) {
459                 pstrcpy(u->ut_host, host);
460         }
461 #endif /* defined(HAVE_UT_UT_HOST) */
462
463 #if defined(HAVE_UT_UT_ADDR)
464         /*
465          * "(unsigned long) ut_addr" apparently exists on at least HP-UX 10.20.
466          * Volunteer to implement, please ...
467          */
468 #endif /* defined(HAVE_UT_UT_ADDR) */
469
470 #if defined(HAVE_UT_UT_ID)
471         rc = ut_id_encode(i, u->ut_id);
472 #endif /* defined(HAVE_UT_UT_ID) */
473
474         return(rc);
475 }
476
477 /****************************************************************************
478 Default paths to various {u,w}tmp{,x} files
479 ****************************************************************************/
480 #ifdef  HAVE_UTMPX_H
481
482 static const char *ux_pathname =
483 # if defined (UTMPX_FILE)
484         UTMPX_FILE ;
485 # elif defined (_UTMPX_FILE)
486         _UTMPX_FILE ;
487 # elif defined (_PATH_UTMPX)
488         _PATH_UTMPX ;
489 # else
490         "" ;
491 # endif
492
493 static const char *wx_pathname =
494 # if defined (WTMPX_FILE)
495         WTMPX_FILE ;
496 # elif defined (_WTMPX_FILE)
497         _WTMPX_FILE ;
498 # elif defined (_PATH_WTMPX)
499         _PATH_WTMPX ;
500 # else
501         "" ;
502 # endif
503
504 #endif  /* HAVE_UTMPX_H */
505
506 static const char *ut_pathname =
507 # if defined (UTMP_FILE)
508         UTMP_FILE ;
509 # elif defined (_UTMP_FILE)
510         _UTMP_FILE ;
511 # elif defined (_PATH_UTMP)
512         _PATH_UTMP ;
513 # else
514         "" ;
515 # endif
516
517 static const char *wt_pathname =
518 # if defined (WTMP_FILE)
519         WTMP_FILE ;
520 # elif defined (_WTMP_FILE)
521         _WTMP_FILE ;
522 # elif defined (_PATH_WTMP)
523         _PATH_WTMP ;
524 # else
525         "" ;
526 # endif
527
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?) */
533         _PATH_LASTLOG ;
534 # else
535         "" ;
536 # endif /* _PATH_LASTLOG */
537 #endif  /* HAVE_PUTUTLINE */
538
539 /*
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.
543  *
544  * utmp{,x}:  try "utmp dir", then default (a define)
545  * wtmp{,x}:  try "wtmp dir", then "utmp dir", then default (a define)
546  */
547 static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default)
548 {
549         pstring dirname;
550
551         pstrcpy(dirname, "");
552
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,"","/");
557         }
558
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,"","/");
563         }
564
565         /* If explicit directory above, use it */
566         if (dirname != 0 && strlen(dirname) != 0) {
567                 pstrcpy(fname, dirname);
568                 pstrcat(fname, "/");
569                 pstrcat(fname, uw_name);
570                 return;
571         }
572
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?
577                  */
578                 DEBUG(2,("uw_pathname: unable to determine pathname\n"));
579         }
580         pstrcpy(fname, uw_default);
581 }
582
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)
588 {
589         DEBUG(1,("pututline_my: not yet implemented\n"));
590         /* BSD implementor: may want to consider (or not) adjusting "lastlog" */
591 }
592 #endif /* HAVE_PUTUTLINE */
593
594 #ifndef HAVE_UPDWTMP
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)
600 {
601         int fd;
602         struct stat buf;
603
604         if (! claim) {
605                 /*
606                  * BSD-like systems:
607                  *      may use empty ut_name to distinguish a logout record.
608                  *
609                  * May need "if defined(SUNOS4)" etc. around some of these,
610                  * but try to avoid if possible.
611                  *
612                  * SunOS 4:
613                  *      man page indicates ut_name and ut_host both NULL
614                  * FreeBSD 4.0:
615                  *      man page appears not to specify (hints non-NULL)
616                  *      A correspondent suggest at least ut_name should be NULL
617                  */
618                 memset((char *)&(u->ut_name), '\0', sizeof(u->ut_name));
619                 memset((char *)&(u->ut_host), '\0', sizeof(u->ut_host));
620         }
621         /* Stolen from logwtmp function in libutil.
622          * May be more locking/blocking is needed?
623          */
624         if ((fd = open(wname, O_WRONLY|O_APPEND, 0)) < 0)
625                 return;
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);
629         }
630         (void) close(fd);
631 }
632 #endif /* HAVE_UPDWTMP */
633
634 /****************************************************************************
635 Update via utmp/wtmp (not utmpx/wtmpx)
636 ****************************************************************************/
637 static void utmp_nox_update(struct utmp *u, pstring host, BOOL claim)
638 {
639         pstring uname, wname;
640 #if defined(PUTUTLINE_RETURNS_UTMP)
641         struct utmp *urc;
642 #endif /* PUTUTLINE_RETURNS_UTMP */
643
644         uw_pathname(uname, "utmp", ut_pathname);
645         DEBUG(2,("utmp_nox_update: uname:%s\n", uname));
646
647 #ifdef HAVE_PUTUTLINE
648         if (strlen(uname) != 0) {
649                 utmpname(uname);
650         }
651
652 # if defined(PUTUTLINE_RETURNS_UTMP)
653         setutent();
654         urc = pututline(u);
655         endutent();
656         if (urc == NULL) {
657                 DEBUG(2,("utmp_nox_update: pututline() failed\n"));
658                 return;
659         }
660 # else  /* PUTUTLINE_RETURNS_UTMP */
661         setutent();
662         pututline(u);
663         endutent();
664 # endif /* PUTUTLINE_RETURNS_UTMP */
665
666 #else   /* HAVE_PUTUTLINE */
667         if (strlen(uname) != 0) {
668                 pututline_my(uname, u, claim);
669         }
670 #endif /* HAVE_PUTUTLINE */
671
672         uw_pathname(wname, "wtmp", wt_pathname);
673         DEBUG(2,("utmp_nox_update: wname:%s\n", wname));
674         if (strlen(wname) != 0) {
675 #ifdef HAVE_UPDWTMP
676                 updwtmp(wname, u);
677                 /*
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
684                  * updwtmp() perhaps?
685                  */
686 #else
687                 updwtmp_my(wname, u, claim);
688 #endif /* HAVE_UPDWTMP */
689         }
690 }
691
692 /****************************************************************************
693 Update via utmpx/wtmpx (preferred) or via utmp/wtmp
694 ****************************************************************************/
695 static void utmp_update(struct utmp *u, pstring host, BOOL claim)
696 {
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);
708 #else
709         pstring uname, wname;
710         struct utmpx ux, *uxrc;
711
712         getutmpx(u, &ux);
713         if (host) {
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);
718         }
719
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));
723         /*
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".)
729          */
730         if ((strlen(uname) == 0) || (strlen(wname) == 0)) {
731                 utmp_nox_update(u, host, claim);
732         }
733         else {
734                 utmpxname(uname);
735                 setutxent();
736                 uxrc = pututxline(&ux);
737                 endutxent();
738                 if (uxrc == NULL) {
739                         DEBUG(2,("utmp_update: pututxline() failed\n"));
740                         return;
741                 }
742 #ifdef HAVE_UPDWTMPX
743                 updwtmpx(wname, &ux);
744 #else
745                 /* Have utmpx.h but no "updwtmpx()". */
746                 DEBUG(1,("utmp_update: no updwtmpx() function\n"));
747 #endif /* HAVE_UPDWTMPX */
748         }
749 #endif /* HAVE_UTMPX_H */
750 }
751
752 /*
753  * "utmp consolidate": some background:
754  *      False (default):
755  *              In "utmp" files note every connection via this process.
756  *              Argument "i" is simply a tty-like number we can use as-is.
757  *      True:
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.
761  */
762 static int utmp_count = 0;
763 static int utmp_consolidate_conn_num;
764
765 /****************************************************************************
766 close a connection
767 ****************************************************************************/
768 static void utmp_yield(pid_t pid, const connection_struct *conn)
769 {
770         struct utmp u;
771         int conn_num, i;
772
773         if (! lp_utmp(SNUM(conn))) {
774                 DEBUG(2,("utmp_yield: lp_utmp() NULL\n"));
775                 return;
776         }
777
778         i = utmp_yield_tdb(conn);
779         if (i < 0) {
780                 DEBUG(2,("utmp_yield: utmp_yield_tdb() failed\n"));
781                 return;
782         }
783         conn_num = i;
784         DEBUG(2,("utmp_yield: conn: user:%s cnum:%d i:%d (utmp_count:%d)\n",
785           conn->user, conn->cnum, i, utmp_count));
786
787         utmp_count -= 1;
788         if (lp_utmp_consolidate()) {
789                 if (utmp_count > 0) {
790                         DEBUG(2,("utmp_yield: utmp consolidate: %d entries still open\n", utmp_count));
791                         return;
792                 }
793                 else {
794                         /* consolidate; final close: override conn_num  */
795                         conn_num = utmp_consolidate_conn_num;
796                 }
797         }
798
799         memset((char *)&u, '\0', sizeof(struct utmp));
800
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) */
805
806 #if defined(HAVE_UT_UT_TYPE)
807         u.ut_type = DEAD_PROCESS;
808 #endif  /* defined(HAVE_UT_UT_TYPE) */
809
810         if (utmp_fill(&u, conn, pid, conn_num, NULL) == 0) {
811                 utmp_update(&u, NULL, False);
812         }
813 }
814
815 /****************************************************************************
816 open a connection
817 ****************************************************************************/
818 static void utmp_claim(const struct connections_data *crec, const connection_struct *conn)
819 {
820         struct utmp u;
821         pstring host;
822         int i;
823
824         if (conn == NULL) {
825                 DEBUG(2,("utmp_claim: conn NULL\n"));
826                 return;
827         }
828
829         if (! lp_utmp(SNUM(conn))) {
830                 DEBUG(2,("utmp_claim: lp_utmp() NULL\n"));
831                 return;
832         }
833
834         i = utmp_claim_tdb(conn);
835         if (i < 0) {
836                 DEBUG(2,("utmp_claim: utmp_claim_tdb() failed\n"));
837                 return;
838         }
839
840         pstrcpy(host, lp_utmp_hostname());
841         if (host == 0 || strlen(host) == 0) {
842                 pstrcpy(host, crec->machine);
843         }
844         else {
845                 /* explicit "utmp host": expand for any "%" variables */
846                 standard_sub_basic(host);
847         }
848
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));
853
854         utmp_count += 1;
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)));
858                         return;
859                 }
860                 else {
861                         /* consolidate; first open: keep record of "i" */
862                         utmp_consolidate_conn_num = i;
863                 }
864         }
865
866         memset((char *)&u, '\0', sizeof(struct utmp));
867
868 #if defined(HAVE_UT_UT_TYPE)
869         u.ut_type = USER_PROCESS;
870 #endif  /* defined(HAVE_UT_UT_TYPE) */
871
872         if (utmp_fill(&u, conn, crec->pid, i, host) == 0) {
873                 utmp_update(&u, host, True);
874         }
875 }
876
877 #endif  /* WITH_UTMP */