acconfig.h configure configure.in: Added check for UT_SYSLEN for utmp code.
[sfrench/samba-autobuild/.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 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 = 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         extern int Client;
75
76         if (max_connections <= 0)
77                 return(True);
78
79         if (!tdb) {
80                 tdb = tdb_open(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST, 
81                                O_RDWR | O_CREAT, 0644);
82         }
83         if (!tdb) return False;
84
85         DEBUG(5,("claiming %s %d\n",name,max_connections));
86
87         ZERO_STRUCT(key);
88         key.pid = getpid();
89         key.cnum = conn?conn->cnum:-1;
90         fstrcpy(key.name, name);
91
92         kbuf.dptr = (char *)&key;
93         kbuf.dsize = sizeof(key);
94
95         /* fill in the crec */
96         ZERO_STRUCT(crec);
97         crec.magic = 0x280267;
98         crec.pid = getpid();
99         crec.cnum = conn?conn->cnum:-1;
100         if (conn) {
101                 crec.uid = conn->uid;
102                 crec.gid = conn->gid;
103                 StrnCpy(crec.name,
104                         lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
105         }
106         crec.start = time(NULL);
107         
108         StrnCpy(crec.machine,remote_machine,sizeof(crec.machine)-1);
109         StrnCpy(crec.addr,conn?conn->client_address:client_addr(Client),sizeof(crec.addr)-1);
110
111         dbuf.dptr = (char *)&crec;
112         dbuf.dsize = sizeof(crec);
113
114         if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) return False;
115
116 #ifdef WITH_UTMP
117         if (conn)
118             utmp_claim(&crec, conn);
119 #endif
120
121         return True;
122 }
123
124 #ifdef WITH_UTMP
125
126 /****************************************************************************
127 Reflect connection status in utmp/wtmp files.
128         T.D.Lee@durham.ac.uk  September 1999
129
130 Hints for porting:
131         o Always attempt to use programmatic interface (pututline() etc.)
132         o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
133
134 OS status:
135         Solaris 2.x:  Tested on 2.6 and 2.7; should be OK on other flavours.
136                 T.D.Lee@durham.ac.uk
137         HPUX 9.x:  Not tested.  Appears not to have "x".
138         IRIX 6.5:  Not tested.  Appears to have "x".
139
140 Notes:
141         The 4 byte 'ut_id' component is vital to distinguish connections,
142         of which there could be several hundered or even thousand.
143         Entries seem to be printable characters, with optional NULL pads.
144
145         We need to be distinct from other entries in utmp/wtmp.
146
147         Observed things: therefore avoid them.  Add to this list please.
148         From Solaris 2.x (because that's what I have):
149                 'sN'    : run-levels; N: [0-9]
150                 'co'    : console
151                 'CC'    : arbitrary things;  C: [a-z]
152                 'rXNN'  : rlogin;  N: [0-9]; X: [0-9a-z]
153                 'tXNN'  : rlogin;  N: [0-9]; X: [0-9a-z]
154                 '/NNN'  : Solaris CDE
155                 'ftpZ'  : ftp (Z is the number 255, aka 0377, aka 0xff)
156         Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp",
157         but differences have been seen.
158
159         Arbitrarily I have chosen to use a distinctive 'SM' for the
160         first two bytes.
161
162         The remaining two encode the connection number used in samba locking
163         functions "claim_connection() and "yield_connection()".  This seems
164         to be a "nicely behaved" number: starting from 0 then working up
165         looking for an available slot.
166
167 ****************************************************************************/
168
169 #include <utmp.h>
170
171 #ifdef HAVE_UTMPX_H
172 #include <utmpx.h>
173 #endif
174
175 static const char *ut_id_encstr =
176         "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
177
178 static
179 int
180 ut_id_encode(int i, char *fourbyte)
181 {
182         int nbase;
183
184         fourbyte[0] = 'S';
185         fourbyte[1] = 'M';
186
187 /*
188  * Encode remaining 2 bytes from 'i'.
189  * 'ut_id_encstr' is the character set on which modulo arithmetic is done.
190  * Example: digits would produce the base-10 numbers from '001'.
191  */
192         nbase = strlen(ut_id_encstr);
193
194         fourbyte[3] = ut_id_encstr[i % nbase];
195         i /= nbase;
196         fourbyte[2] = ut_id_encstr[i % nbase];
197         i /= nbase;
198
199         return(i);      /* 0: good; else overflow */
200 }
201
202 static int utmp_fill(struct utmp *u, const connection_struct *conn, pid_t pid, int i)
203 {
204         struct timeval timeval;
205         int rc;
206
207         pstrcpy(u->ut_user, conn->user);
208         rc = ut_id_encode(i, u->ut_id);
209         slprintf(u->ut_line, 12, "smb/%d", i);
210
211         u->ut_pid = pid;
212
213         gettimeofday(&timeval, NULL);
214         u->ut_time = timeval.tv_sec;
215
216         return(rc);
217 }
218
219 static void utmp_update(const pstring dirname, const struct utmp *u, const char *host)
220 {
221         pstring fname;
222
223 #ifdef HAVE_UTMPX_H
224         struct utmpx ux, *uxrc;
225
226         getutmpx(u, &ux);
227         if (host) {
228 #if defined(HAVE_UX_UT_SYSLEN)
229                 ux.ut_syslen = strlen(host);
230 #endif /* defined(HAVE_UX_UT_SYSLEN) */
231                 pstrcpy(ux.ut_host, host);
232         }
233
234         pstrcpy(fname, dirname);
235         pstrcat(fname, "utmpx");
236         utmpxname(fname);
237         uxrc = pututxline(&ux);
238         if (uxrc == NULL) {
239                 DEBUG(2,("utmp_update: pututxline() failed\n"));
240                 return;
241         }
242
243         pstrcpy(fname, dirname);
244         pstrcat(fname, "wtmpx");
245         updwtmpx(fname, &ux);
246 #else
247         pstrcpy(fname, dirname);
248         pstrcat(fname, "utmp");
249
250         utmpname(fname);
251         pututline(u);
252
253         pstrcpy(fname, dirname);
254         pstrcat(fname, "wtmp");
255
256         /* *** OK.  Appending wtmp (as distinct from overwriting utmp) has
257         me baffled.  How is it to be done? *** */
258 #endif
259 }
260
261 static void utmp_yield(int pid, const connection_struct *conn)
262 {
263         struct utmp u;
264         pstring dirname;
265
266         if (! lp_utmp(SNUM(conn))) {
267                 DEBUG(2,("utmp_yield: lp_utmp() NULL\n"));
268                 return;
269         }
270
271         pstrcpy(dirname,lp_utmpdir());
272         trim_string(dirname,"","/");
273         pstrcat(dirname,"/");
274
275         DEBUG(2,("utmp_yield: dir:%s conn: user:%s cnum:%d i:%d\n",
276           dirname, conn->user, conn->cnum, conn->cnum));
277
278         memset((char *)&u, '\0', sizeof(struct utmp));
279         u.ut_type = DEAD_PROCESS;
280         u.ut_exit.e_termination = 0;
281         u.ut_exit.e_exit = 0;
282         if (utmp_fill(&u, conn, pid, conn->cnum) == 0) {
283                 utmp_update(dirname, &u, NULL);
284         }
285 }
286
287 static void utmp_claim(const struct connections_data *crec, const connection_struct *conn)
288 {
289         extern int Client;
290         struct utmp u;
291         pstring dirname;
292
293         if (conn == NULL) {
294                 DEBUG(2,("utmp_claim: conn NULL\n"));
295                 return;
296         }
297
298         if (! lp_utmp(SNUM(conn))) {
299                 DEBUG(2,("utmp_claim: lp_utmp() NULL\n"));
300                 return;
301         }
302
303         pstrcpy(dirname,lp_utmpdir());
304         trim_string(dirname,"","/");
305         pstrcat(dirname,"/");
306
307         DEBUG(2,("utmp_claim: dir:%s conn: user:%s cnum:%d i:%d\n",
308           dirname, conn->user, conn->cnum, conn->cnum));
309         DEBUG(2,("utmp_claim: crec: pid:%d, cnum:%d name:%s addr:%s mach:%s DNS:%s\n",
310           crec->pid, crec->cnum, crec->name, crec->addr, crec->machine, client_name(Client)));
311
312
313         memset((char *)&u, '\0', sizeof(struct utmp));
314         u.ut_type = USER_PROCESS;
315         if (utmp_fill(&u, conn, crec->pid, conn->cnum) == 0) {
316                 utmp_update(dirname, &u, crec->machine);
317         }
318 }
319
320 #endif