2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 #include <utils/debug.h>
21 #include "sql_attribute.h"
23 typedef struct private_sql_attribute_t private_sql_attribute_t
;
26 * private data of sql_attribute
28 struct private_sql_attribute_t
{
33 sql_attribute_t
public;
41 * whether to record lease history in lease table
47 * lookup/insert an identity
49 static u_int
get_identity(private_sql_attribute_t
*this, identification_t
*id
)
54 /* look for peer identity in the identities table */
55 e
= this->db
->query(this->db
,
56 "SELECT id FROM identities WHERE type = ? AND data = ?",
57 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
),
60 if (e
&& e
->enumerate(e
, &row
))
66 /* not found, insert new one */
67 if (this->db
->execute(this->db
, &row
,
68 "INSERT INTO identities (type, data) VALUES (?, ?)",
69 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
)) == 1)
77 * Lookup an attribute pool by name
79 static u_int
get_attr_pool(private_sql_attribute_t
*this, char *name
)
84 e
= this->db
->query(this->db
,
85 "SELECT id FROM attribute_pools WHERE name = ?",
86 DB_TEXT
, name
, DB_UINT
);
89 e
->enumerate(e
, &row
);
99 static u_int
get_pool(private_sql_attribute_t
*this, char *name
, u_int
*timeout
)
104 e
= this->db
->query(this->db
, "SELECT id, timeout FROM pools WHERE name = ?",
105 DB_TEXT
, name
, DB_UINT
, DB_UINT
);
106 if (e
&& e
->enumerate(e
, &pool
, timeout
))
116 * Look up an existing lease
118 static host_t
* check_lease(private_sql_attribute_t
*this, char *name
,
119 u_int pool
, u_int identity
)
126 time_t now
= time(NULL
);
128 e
= this->db
->query(this->db
,
129 "SELECT id, address FROM addresses "
130 "WHERE pool = ? AND identity = ? AND released != 0 LIMIT 1",
131 DB_UINT
, pool
, DB_UINT
, identity
, DB_UINT
, DB_BLOB
);
132 if (!e
|| !e
->enumerate(e
, &id
, &address
))
137 address
= chunk_clonea(address
);
140 if (this->db
->execute(this->db
, NULL
,
141 "UPDATE addresses SET acquired = ?, released = 0 "
142 "WHERE id = ? AND identity = ? AND released != 0",
143 DB_UINT
, now
, DB_UINT
, id
, DB_UINT
, identity
) > 0)
147 host
= host_create_from_chunk(AF_UNSPEC
, address
, 0);
150 DBG1(DBG_CFG
, "acquired existing lease for address %H in"
151 " pool '%s'", host
, name
);
160 * We check for unallocated addresses or expired leases. First we select an
161 * address as a candidate, but double check later on if it is still available
162 * during the update operation. This allows us to work without locking.
164 static host_t
* get_lease(private_sql_attribute_t
*this, char *name
,
165 u_int pool
, u_int timeout
, u_int identity
)
172 time_t now
= time(NULL
);
177 /* check for an expired lease */
178 e
= this->db
->query(this->db
,
179 "SELECT id, address FROM addresses "
180 "WHERE pool = ? AND released != 0 AND released < ? LIMIT 1",
181 DB_UINT
, pool
, DB_UINT
, now
- timeout
, DB_UINT
, DB_BLOB
);
185 /* with static leases, check for an unallocated address */
186 e
= this->db
->query(this->db
,
187 "SELECT id, address FROM addresses "
188 "WHERE pool = ? AND identity = 0 LIMIT 1",
189 DB_UINT
, pool
, DB_UINT
, DB_BLOB
);
193 if (!e
|| !e
->enumerate(e
, &id
, &address
))
198 address
= chunk_clonea(address
);
203 hits
= this->db
->execute(this->db
, NULL
,
204 "UPDATE addresses SET "
205 "acquired = ?, released = 0, identity = ? "
206 "WHERE id = ? AND released != 0 AND released < ?",
207 DB_UINT
, now
, DB_UINT
, identity
,
208 DB_UINT
, id
, DB_UINT
, now
- timeout
);
212 hits
= this->db
->execute(this->db
, NULL
,
213 "UPDATE addresses SET "
214 "acquired = ?, released = 0, identity = ? "
215 "WHERE id = ? AND identity = 0",
216 DB_UINT
, now
, DB_UINT
, identity
, DB_UINT
, id
);
222 host
= host_create_from_chunk(AF_UNSPEC
, address
, 0);
225 DBG1(DBG_CFG
, "acquired new lease for address %H in pool '%s'",
231 DBG1(DBG_CFG
, "no available address found in pool '%s'", name
);
235 METHOD(attribute_provider_t
, acquire_address
, host_t
*,
236 private_sql_attribute_t
*this, linked_list_t
*pools
, identification_t
*id
,
239 enumerator_t
*enumerator
;
240 host_t
*address
= NULL
;
241 u_int identity
, pool
, timeout
;
244 identity
= get_identity(this, id
);
247 /* check for an existing lease in all pools */
248 enumerator
= pools
->create_enumerator(pools
);
249 while (enumerator
->enumerate(enumerator
, &name
))
251 pool
= get_pool(this, name
, &timeout
);
254 address
= check_lease(this, name
, pool
, identity
);
261 enumerator
->destroy(enumerator
);
265 /* get an unallocated address or expired lease */
266 enumerator
= pools
->create_enumerator(pools
);
267 while (enumerator
->enumerate(enumerator
, &name
))
269 pool
= get_pool(this, name
, &timeout
);
272 address
= get_lease(this, name
, pool
, timeout
, identity
);
279 enumerator
->destroy(enumerator
);
285 METHOD(attribute_provider_t
, release_address
, bool,
286 private_sql_attribute_t
*this, linked_list_t
*pools
, host_t
*address
,
287 identification_t
*id
)
289 enumerator_t
*enumerator
;
291 time_t now
= time(NULL
);
295 enumerator
= pools
->create_enumerator(pools
);
296 while (enumerator
->enumerate(enumerator
, &name
))
298 pool
= get_pool(this, name
, &timeout
);
303 if (this->db
->execute(this->db
, NULL
,
304 "UPDATE addresses SET released = ? WHERE "
305 "pool = ? AND address = ?", DB_UINT
, time(NULL
),
306 DB_UINT
, pool
, DB_BLOB
, address
->get_address(address
)) > 0)
310 this->db
->execute(this->db
, NULL
,
311 "INSERT INTO leases (address, identity, acquired, released)"
312 " SELECT id, identity, acquired, ? FROM addresses "
313 " WHERE pool = ? AND address = ?",
314 DB_UINT
, now
, DB_UINT
, pool
,
315 DB_BLOB
, address
->get_address(address
));
321 enumerator
->destroy(enumerator
);
326 METHOD(attribute_provider_t
, create_attribute_enumerator
, enumerator_t
*,
327 private_sql_attribute_t
*this, linked_list_t
*pools
, identification_t
*id
,
330 enumerator_t
*attr_enumerator
= NULL
;
332 if (vips
->get_count(vips
))
334 enumerator_t
*pool_enumerator
;
338 this->db
->execute(this->db
, NULL
, "BEGIN EXCLUSIVE TRANSACTION");
340 /* in a first step check for attributes that match name and id */
343 u_int identity
= get_identity(this, id
);
345 pool_enumerator
= pools
->create_enumerator(pools
);
346 while (pool_enumerator
->enumerate(pool_enumerator
, &name
))
348 u_int attr_pool
= get_attr_pool(this, name
);
354 attr_enumerator
= this->db
->query(this->db
,
355 "SELECT count(*) FROM attributes "
356 "WHERE pool = ? AND identity = ?",
357 DB_UINT
, attr_pool
, DB_UINT
, identity
, DB_UINT
);
359 if (attr_enumerator
&&
360 attr_enumerator
->enumerate(attr_enumerator
, &count
) &&
363 attr_enumerator
->destroy(attr_enumerator
);
364 attr_enumerator
= this->db
->query(this->db
,
365 "SELECT type, value FROM attributes "
366 "WHERE pool = ? AND identity = ?", DB_UINT
,
367 attr_pool
, DB_UINT
, identity
, DB_INT
, DB_BLOB
);
370 DESTROY_IF(attr_enumerator
);
371 attr_enumerator
= NULL
;
373 pool_enumerator
->destroy(pool_enumerator
);
376 /* in a second step check for attributes that match name */
377 if (!attr_enumerator
)
379 pool_enumerator
= pools
->create_enumerator(pools
);
380 while (pool_enumerator
->enumerate(pool_enumerator
, &name
))
382 u_int attr_pool
= get_attr_pool(this, name
);
388 attr_enumerator
= this->db
->query(this->db
,
389 "SELECT count(*) FROM attributes "
390 "WHERE pool = ? AND identity = 0",
391 DB_UINT
, attr_pool
, DB_UINT
);
393 if (attr_enumerator
&&
394 attr_enumerator
->enumerate(attr_enumerator
, &count
) &&
397 attr_enumerator
->destroy(attr_enumerator
);
398 attr_enumerator
= this->db
->query(this->db
,
399 "SELECT type, value FROM attributes "
400 "WHERE pool = ? AND identity = 0",
401 DB_UINT
, attr_pool
, DB_INT
, DB_BLOB
);
404 DESTROY_IF(attr_enumerator
);
405 attr_enumerator
= NULL
;
407 pool_enumerator
->destroy(pool_enumerator
);
410 this->db
->execute(this->db
, NULL
, "END TRANSACTION");
412 /* lastly try to find global attributes */
413 if (!attr_enumerator
)
415 attr_enumerator
= this->db
->query(this->db
,
416 "SELECT type, value FROM attributes "
417 "WHERE pool = 0 AND identity = 0",
422 return (attr_enumerator ? attr_enumerator
: enumerator_create_empty());
425 METHOD(sql_attribute_t
, destroy
, void,
426 private_sql_attribute_t
*this)
434 sql_attribute_t
*sql_attribute_create(database_t
*db
)
436 private_sql_attribute_t
*this;
437 time_t now
= time(NULL
);
442 .acquire_address
= _acquire_address
,
443 .release_address
= _release_address
,
444 .create_attribute_enumerator
= _create_attribute_enumerator
,
449 .history
= lib
->settings
->get_bool(lib
->settings
,
450 "libhydra.plugins.attr-sql.lease_history", TRUE
),
453 /* close any "online" leases in the case we crashed */
456 this->db
->execute(this->db
, NULL
,
457 "INSERT INTO leases (address, identity, acquired, released)"
458 " SELECT id, identity, acquired, ? FROM addresses "
459 " WHERE released = 0", DB_UINT
, now
);
461 this->db
->execute(this->db
, NULL
,
462 "UPDATE addresses SET released = ? WHERE released = 0",
464 return &this->public;