Added utmp fix from David Lee <T.D.Lee@durham.ac.uk>.
[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         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                 ux.ut_syslen = strlen(host);
229                 pstrcpy(ux.ut_host, host);
230         }
231
232         pstrcpy(fname, dirname);
233         pstrcat(fname, "utmpx");
234         utmpxname(fname);
235         uxrc = pututxline(&ux);
236         if (uxrc == NULL) {
237                 DEBUG(2,("utmp_update: pututxline() failed\n"));
238                 return;
239         }
240
241         pstrcpy(fname, dirname);
242         pstrcat(fname, "wtmpx");
243         updwtmpx(fname, &ux);
244 #else
245         pstrcpy(fname, dirname);
246         pstrcat(fname, "utmp");
247
248         utmpname(fname);
249         pututline(u);
250
251         pstrcpy(fname, dirname);
252         pstrcat(fname, "wtmp");
253
254         /* *** OK.  Appending wtmp (as distinct from overwriting utmp) has
255         me baffled.  How is it to be done? *** */
256 #endif
257 }
258
259 static void utmp_yield(int pid, const connection_struct *conn)
260 {
261         struct utmp u;
262         pstring dirname;
263
264         if (! lp_utmp(SNUM(conn))) {
265                 DEBUG(2,("utmp_yield: lp_utmp() NULL\n"));
266                 return;
267         }
268
269         pstrcpy(dirname,lp_utmpdir());
270         trim_string(dirname,"","/");
271         pstrcat(dirname,"/");
272
273         DEBUG(2,("utmp_yield: dir:%s conn: user:%s cnum:%d i:%d\n",
274           dirname, conn->user, conn->cnum, conn->cnum));
275
276         memset((char *)&u, '\0', sizeof(struct utmp));
277         u.ut_type = DEAD_PROCESS;
278         u.ut_exit.e_termination = 0;
279         u.ut_exit.e_exit = 0;
280         if (utmp_fill(&u, conn, pid, conn->cnum) == 0) {
281                 utmp_update(dirname, &u, NULL);
282         }
283 }
284
285 static void utmp_claim(const struct connections_data *crec, const connection_struct *conn)
286 {
287         extern int Client;
288         struct utmp u;
289         pstring dirname;
290
291         if (conn == NULL) {
292                 DEBUG(2,("utmp_claim: conn NULL\n"));
293                 return;
294         }
295
296         if (! lp_utmp(SNUM(conn))) {
297                 DEBUG(2,("utmp_claim: lp_utmp() NULL\n"));
298                 return;
299         }
300
301         pstrcpy(dirname,lp_utmpdir());
302         trim_string(dirname,"","/");
303         pstrcat(dirname,"/");
304
305         DEBUG(2,("utmp_claim: dir:%s conn: user:%s cnum:%d i:%d\n",
306           dirname, conn->user, conn->cnum, conn->cnum));
307         DEBUG(2,("utmp_claim: crec: pid:%d, cnum:%d name:%s addr:%s mach:%s DNS:%s\n",
308           crec->pid, crec->cnum, crec->name, crec->addr, crec->machine, client_name(Client)));
309
310
311         memset((char *)&u, '\0', sizeof(struct utmp));
312         u.ut_type = USER_PROCESS;
313         if (utmp_fill(&u, conn, crec->pid, conn->cnum) == 0) {
314                 utmp_update(dirname, &u, crec->machine);
315         }
316 }
317
318 #endif