1/*
2   Unix SMB/CIFS implementation.
3   connection claim routines
4   Copyright (C) Andrew Tridgell 1998
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21#include "includes.h"
22
23static TDB_CONTEXT *tdb;
24
25/****************************************************************************
26 Return the connection tdb context (used for message send all).
27****************************************************************************/
28
29TDB_CONTEXT *conn_tdb_ctx(void)
30{
31	if (!tdb)
32		tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
33			       O_RDWR | O_CREAT, 0644);
34
35	return tdb;
36}
37
38static void make_conn_key(connection_struct *conn, const char *name, TDB_DATA *pkbuf, struct connections_key *pkey)
39{
40	ZERO_STRUCTP(pkey);
41	pkey->pid = sys_getpid();
42	pkey->cnum = conn?conn->cnum:-1;
43	fstrcpy(pkey->name, name);
44#ifdef DEVELOPER
45	/* valgrind fixer... */
46	{
47		size_t sl = strlen(pkey->name);
48		if (sizeof(fstring)-sl)
49			memset(&pkey->name[sl], '\0', sizeof(fstring)-sl);
50	}
51#endif
52
53	pkbuf->dptr = (char *)pkey;
54	pkbuf->dsize = sizeof(*pkey);
55}
56
57/****************************************************************************
58 Delete a connection record.
59****************************************************************************/
60
61BOOL yield_connection(connection_struct *conn, const char *name)
62{
63	struct connections_key key;
64	TDB_DATA kbuf;
65
66	if (!tdb)
67		return False;
68
69	DEBUG(3,("Yielding connection to %s\n",name));
70
71	make_conn_key(conn, name, &kbuf, &key);
72
73	if (tdb_delete(tdb, kbuf) != 0) {
74		int dbg_lvl = (!conn && (tdb_error(tdb) == TDB_ERR_NOEXIST)) ? 3 : 0;
75		DEBUG(dbg_lvl,("yield_connection: tdb_delete for name %s failed with error %s.\n",
76			name, tdb_errorstr(tdb) ));
77		return (False);
78	}
79
80	return(True);
81}
82
83struct count_stat {
84	pid_t mypid;
85	int curr_connections;
86	char *name;
87	BOOL Clear;
88};
89
90/****************************************************************************
91 Count the entries belonging to a service in the connection db.
92****************************************************************************/
93
94static int count_fn( TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *udp)
95{
96	struct connections_data crec;
97	struct count_stat *cs = (struct count_stat *)udp;
98
99	if (dbuf.dsize != sizeof(crec))
100		return 0;
101
102	memcpy(&crec, dbuf.dptr, sizeof(crec));
103
104	if (crec.cnum == -1)
105		return 0;
106
107	/* If the pid was not found delete the entry from connections.tdb */
108
109	if (cs->Clear && !process_exists(crec.pid) && (errno == ESRCH)) {
110		DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n",
111			(unsigned int)crec.pid, crec.cnum, crec.name));
112		if (tdb_delete(the_tdb, kbuf) != 0)
113			DEBUG(0,("count_fn: tdb_delete failed with error %s\n", tdb_errorstr(tdb) ));
114		return 0;
115	}
116
117	if (strequal(crec.name, cs->name))
118		cs->curr_connections++;
119
120	return 0;
121}
122
123/****************************************************************************
124 Claim an entry in the connections database.
125****************************************************************************/
126
127BOOL claim_connection(connection_struct *conn, const char *name,int max_connections,BOOL Clear, uint32 msg_flags)
128{
129	struct connections_key key;
130	struct connections_data crec;
131	TDB_DATA kbuf, dbuf;
132
133	if (!tdb)
134		tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
135			       O_RDWR | O_CREAT, 0644);
136
137	if (!tdb)
138		return False;
139
140	/*
141	 * Enforce the max connections parameter.
142	 */
143
144	if (max_connections > 0) {
145		struct count_stat cs;
146
147		cs.mypid = sys_getpid();
148		cs.curr_connections = 0;
149		cs.name = lp_servicename(SNUM(conn));
150		cs.Clear = Clear;
151
152		/*
153		 * This has a race condition, but locking the chain before hand is worse
154		 * as it leads to deadlock.
155		 */
156
157		if (tdb_traverse(tdb, count_fn, &cs) == -1) {
158			DEBUG(0,("claim_connection: traverse of connections.tdb failed with error %s.\n",
159				tdb_errorstr(tdb) ));
160			return False;
161		}
162
163		if (cs.curr_connections >= max_connections) {
164			DEBUG(1,("claim_connection: Max connections (%d) exceeded for %s\n",
165				max_connections, name ));
166			return False;
167		}
168	}
169
170	DEBUG(5,("claiming %s %d\n",name,max_connections));
171
172	make_conn_key(conn, name, &kbuf, &key);
173
174	/* fill in the crec */
175	ZERO_STRUCT(crec);
176	crec.magic = 0x280267;
177	crec.pid = sys_getpid();
178	crec.cnum = conn?conn->cnum:-1;
179	if (conn) {
180		crec.uid = conn->uid;
181		crec.gid = conn->gid;
182		safe_strcpy(crec.name,
183			    lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
184	}
185	crec.start = time(NULL);
186	crec.bcast_msg_flags = msg_flags;
187
188	safe_strcpy(crec.machine,get_remote_machine_name(),sizeof(crec.machine)-1);
189	safe_strcpy(crec.addr,conn?conn->client_address:client_addr(),sizeof(crec.addr)-1);
190
191	dbuf.dptr = (char *)&crec;
192	dbuf.dsize = sizeof(crec);
193
194	if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
195		DEBUG(0,("claim_connection: tdb_store failed with error %s.\n",
196			tdb_errorstr(tdb) ));
197		return False;
198	}
199
200	return True;
201}
202
203BOOL register_message_flags(BOOL doreg, uint32 msg_flags)
204{
205	struct connections_key key;
206	struct connections_data *pcrec;
207	TDB_DATA kbuf, dbuf;
208
209	if (!tdb)
210		return False;
211
212	DEBUG(10,("register_message_flags: %s flags 0x%x\n",
213		doreg ? "adding" : "removing",
214		(unsigned int)msg_flags ));
215
216	make_conn_key(NULL, "", &kbuf, &key);
217
218        dbuf = tdb_fetch(tdb, kbuf);
219        if (!dbuf.dptr) {
220		DEBUG(0,("register_message_flags: tdb_fetch failed\n"));
221		return False;
222	}
223
224	pcrec = (struct connections_data *)dbuf.dptr;
225	pcrec->bcast_msg_flags = msg_flags;
226	if (doreg)
227		pcrec->bcast_msg_flags |= msg_flags;
228	else
229		pcrec->bcast_msg_flags &= ~msg_flags;
230
231	if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
232		DEBUG(0,("register_message_flags: tdb_store failed with error %s.\n",
233			tdb_errorstr(tdb) ));
234		SAFE_FREE(dbuf.dptr);
235		return False;
236	}
237
238	DEBUG(10,("register_message_flags: new flags 0x%x\n",
239		(unsigned int)pcrec->bcast_msg_flags ));
240
241	SAFE_FREE(dbuf.dptr);
242	return True;
243}
244