finally got sick of the "extern int Client" code and the stupid
[kai/samba.git] / source3 / smbd / connection.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    connection claim routines
5    Copyright (C) Andrew Tridgell 1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24
25 extern fstring remote_machine;
26 static TDB_CONTEXT *tdb;
27
28 extern int DEBUGLEVEL;
29
30 #ifdef WITH_UTMP
31 static void utmp_yield(pid_t pid, const connection_struct *conn);
32 static void utmp_claim(const struct connections_data *crec, const connection_struct *conn);
33 #endif
34
35 /****************************************************************************
36 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
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 = 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 = 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 Hints for porting:
130         o Always attempt to use programmatic interface (pututline() etc.)
131         o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
132
133 OS status:
134         Solaris 2.x:  Tested on 2.6 and 2.7; should be OK on other flavours.
135                 T.D.Lee@durham.ac.uk
136         HPUX 9.x:  Not tested.  Appears not to have "x".
137         IRIX 6.5:  Not tested.  Appears to have "x".
138
139 Notes:
140         The 4 byte 'ut_id' component is vital to distinguish connections,
141         of which there could be several hundered or even thousand.
142         Entries seem to be printable characters, with optional NULL pads.
143
144         We need to be distinct from other entries in utmp/wtmp.
145
146         Observed things: therefore avoid them.  Add to this list please.
147         From Solaris 2.x (because that's what I have):
148                 'sN'    : run-levels; N: [0-9]
149                 'co'    : console
150                 'CC'    : arbitrary things;  C: [a-z]
151                 'rXNN'  : rlogin;  N: [0-9]; X: [0-9a-z]
152                 'tXNN'  : rlogin;  N: [0-9]; X: [0-9a-z]
153                 '/NNN'  : Solaris CDE
154                 'ftpZ'  : ftp (Z is the number 255, aka 0377, aka 0xff)
155         Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp",
156         but differences have been seen.
157
158         Arbitrarily I have chosen to use a distinctive 'SM' for the
159         first two bytes.
160
161         The remaining two encode the connection number used in samba locking
162         functions "claim_connection() and "yield_connection()".  This seems
163         to be a "nicely behaved" number: starting from 0 then working up
164         looking for an available slot.
165
166 ****************************************************************************/
167
168 #include <utmp.h>
169
170 #ifdef HAVE_UTMPX_H
171 #include <utmpx.h>
172 #endif
173
174 static const char *ut_id_encstr =
175         "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
176
177 static
178 int
179 ut_id_encode(int i, char *fourbyte)
180 {
181         int nbase;
182
183         fourbyte[0] = 'S';
184         fourbyte[1] = 'M';
185
186 /*
187  * Encode remaining 2 bytes from 'i'.
188  * 'ut_id_encstr' is the character set on which modulo arithmetic is done.
189  * Example: digits would produce the base-10 numbers from '001'.
190  */
191         nbase = strlen(ut_id_encstr);
192
193         fourbyte[3] = ut_id_encstr[i % nbase];
194         i /= nbase;
195         fourbyte[2] = ut_id_encstr[i % nbase];
196         i /= nbase;
197
198         return(i);      /* 0: good; else overflow */
199 }
200
201 static int utmp_fill(struct utmp *u, const connection_struct *conn, pid_t pid, int i)
202 {
203         struct timeval timeval;
204         int rc;
205
206         pstrcpy(u->ut_user, conn->user);
207         rc = ut_id_encode(i, u->ut_id);
208         slprintf(u->ut_line, 12, "smb/%d", i);
209
210         u->ut_pid = pid;
211
212         gettimeofday(&timeval, NULL);
213         u->ut_time = timeval.tv_sec;
214
215         return(rc);
216 }
217
218 /* Default path (if possible) */
219 #ifdef  HAVE_UTMPX_H
220
221 # ifdef UTMPX_FILE
222 static char *ut_pathname = UTMPX_FILE;
223 # else
224 static char *ut_pathname = "";
225 # endif
226 # ifdef WTMPX_FILE
227 static char *wt_pathname = WTMPX_FILE;
228 # else
229 static char *wt_pathname = "";
230 # endif
231
232 #else   /* HAVE_UTMPX_H */
233
234 # ifdef UTMP_FILE
235 static char *ut_pathname = UTMP_FILE;
236 # else
237 static char *ut_pathname = "";
238 # endif
239 # ifdef WTMP_FILE
240 static char *wt_pathname = WTMP_FILE;
241 # else
242 static char *wt_pathname = "";
243 # endif
244
245 #endif  /* HAVE_UTMPX_H */
246
247 static void uw_pathname(pstring fname, const char *uw_name)
248 {
249         pstring dirname;
250
251         pstrcpy(dirname,lp_utmpdir());
252         trim_string(dirname,"","/");
253
254         /* Given directory: use it */
255         if (dirname != 0 && strlen(dirname) != 0) {
256                 pstrcpy(fname, dirname);
257                 pstrcat(fname, "/");
258                 pstrcat(fname, uw_name);
259                 return;
260         }
261
262         /* No given directory: attempt to use default paths */
263         if (uw_name[0] == 'u') {
264                 pstrcpy(fname, ut_pathname);
265                 return;
266         }
267
268         if (uw_name[0] == 'w') {
269                 pstrcpy(fname, wt_pathname);
270                 return;
271         }
272
273         pstrcpy(fname, "");
274 }
275
276 static void utmp_update(const struct utmp *u, const char *host)
277 {
278         pstring fname;
279
280 #ifdef HAVE_UTMPX_H
281         struct utmpx ux, *uxrc;
282
283         getutmpx(u, &ux);
284         if (host) {
285 #if defined(HAVE_UX_UT_SYSLEN)
286                 ux.ut_syslen = strlen(host);
287 #endif /* defined(HAVE_UX_UT_SYSLEN) */
288                 pstrcpy(ux.ut_host, host);
289         }
290
291         uw_pathname(fname, "utmpx");
292         DEBUG(2,("utmp_update: fname:%s\n", fname));
293         if (strlen(fname) != 0) {
294                 utmpxname(fname);
295         }
296         uxrc = pututxline(&ux);
297         if (uxrc == NULL) {
298                 DEBUG(2,("utmp_update: pututxline() failed\n"));
299                 return;
300         }
301
302         uw_pathname(fname, "wtmpx");
303         DEBUG(2,("utmp_update: fname:%s\n", fname));
304         if (strlen(fname) != 0) {
305                 updwtmpx(fname, &ux);
306         }
307 #else
308         uw_pathname(fname, "utmp");
309         DEBUG(2,("utmp_update: fname:%s\n", fname));
310         if (strlen(fname) != 0) {
311                 utmpname(fname);
312         }
313         pututline(u);
314
315         uw_pathname(fname, "wtmp");
316
317         /* *** Hmmm.  Appending wtmp (as distinct from overwriting utmp) has
318         me baffled.  How is it to be done? *** */
319 #endif
320 }
321
322 static void utmp_yield(pid_t pid, const connection_struct *conn)
323 {
324         struct utmp u;
325
326         if (! lp_utmp(SNUM(conn))) {
327                 DEBUG(2,("utmp_yield: lp_utmp() NULL\n"));
328                 return;
329         }
330
331         DEBUG(2,("utmp_yield: conn: user:%s cnum:%d\n",
332                  conn->user, conn->cnum));
333
334         memset((char *)&u, '\0', sizeof(struct utmp));
335         u.ut_type = DEAD_PROCESS;
336         u.ut_exit.e_termination = 0;
337         u.ut_exit.e_exit = 0;
338         if (utmp_fill(&u, conn, pid, conn->cnum) == 0) {
339                 utmp_update(&u, NULL);
340         }
341 }
342
343 static void utmp_claim(const struct connect_record *crec, const connection_struct *conn)
344 {
345         struct utmp u;
346
347         if (conn == NULL) {
348                 DEBUG(2,("utmp_claim: conn NULL\n"));
349                 return;
350         }
351
352         if (! lp_utmp(SNUM(conn))) {
353                 DEBUG(2,("utmp_claim: lp_utmp() NULL\n"));
354                 return;
355         }
356
357         DEBUG(2,("utmp_claim: conn: user:%s cnum:%d i:%d\n",
358           conn->user, conn->cnum, i));
359         DEBUG(2,("utmp_claim: crec: pid:%d, cnum:%d name:%s addr:%s mach:%s DNS:%s\n",
360           crec->pid, crec->cnum, crec->name, crec->addr, crec->machine, client_name()));
361
362
363         memset((char *)&u, '\0', sizeof(struct utmp));
364         u.ut_type = USER_PROCESS;
365         if (utmp_fill(&u, conn, crec->pid, conn->cnum) == 0) {
366                 utmp_update(&u, crec->machine);
367         }
368 }
369
370 #endif