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