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