lease file -> XML?
Phil Benchoff
benchoff at vt.edu
Mon Aug 30 14:38:20 UTC 2004
Below is a README and current version of the patch I posted about last week.
In addition to the replies that were posted on dhcp-hackers, I did receive
replies from two other folks who expressed some interest.
This patch is used to dump an XML version of some things in the leases file
and I am using it to get the status of the running server. If there is
interest in getting this into the distribution, I will try to get the
patch into reasonable shape to do so.
Phil
------------------------------------------------------------
README file for patches to output lease file in XML.
Description
This patch adds a command-line option (-lx) to ISC dhcpd. The -lx option
causes dhcpd to output an XML version of the lease file to stdout.
Caveats
- This patch is not useful for the dhcpd server itself. I suggest not
changing the dhcpd you use in production and just building a stand-alone
dhcpd to use to parse the lease file.
NOTE: DUE TO A SEGFAULT I HAVEN'T HAD TIME TO DEAL WITH, THE PATCHED
VERSION CAN'T BE USED AS A SERVER.
- Do not run this as a user that can write to the lease database.
- The XML output should be well-formed, but I have not written a formal
specification and don't plan to at the moment.
- I have only tested it on Linux so far. I suspect there are issues
with the printing of longs that may pop up on 64-bit platforms.
- Not all elements of leases are output.
- There is no code to output group and host declarations at the moment.
- I use tab stops at 4-character spacing. I'll clean up my indenting to
match the rest of the code if this patch goes anywhere.
Patching
In the root of the ISC DHCP distribution:
patch --verbose -b -p1 < dhcp-3.0-xml-parser-patch
Compilation
Add "--copts -DVT_PARSER_ONLY" to whatever options you use with the
configure command. Be sure you see "-DVT_PARSER_ONLY" on the CC commands
as the program compiles. "-lx" should appear in the usage output of the
dhcpd program.
Installation
This patch does not add any functionality to the dhcpd server and I suggest
using the dhcpd produced here as a stand-alone program and not changing the
dhcpd you actually use as a server.
Usage
Just add the -lx and -q options to dhcpd. If you normally use -cf, you
will need it here too.
TODO
Soon:
- create or find a function properly escape CDATA
- fix output so only XML goes to stdout, errors to stderr, and don't
do any syslogging.
- make sure exit code is 0 for success and non-zero for errors
- host and group data: either output as XML or be sure it is bypassed
Maybe:
- command-line options for IP address range, binding states, and
hardware addresses.
- develop XML schema/DTD or whatever
=========================================================================
Under the Hood
=========================================================================
This patch was developed on ISC dhcp-3.0.1 with no other patches.
All code should be encapsulated in #ifdef VT_PARSER_ONLY/#endif.
The code path is similar to what is used with the -T option (test
lease file, lftest global) except that expire_all_pools() and
write_leases() are called. write_xxx() functions in db.c are modified
to output XML conditional on lfxml.
server/dhcpd.c:
- add lfxml global that keeps track of whether we should output XML
- add -lf command line option
- add !lfxml to the conditional for starting ICMP support. This will
probably be removed since lftest is set with -lx.
- add -lf to the usage
server/mdb.c:
- add extern lfxml declaration
- changed write_leases() to output XML
- comment out call to pool_timer() in exipre_all_pools()
server/db.c:
- add extern lfxml declaration
- db_startup() function changed to call expire_all_pools(), set db_file
to stdout, and call write_leases() if lfxml is set.
- write_(lease|failover_state|<etc>) functions changed to output XML
- new_lease_file() changed to call log_fatal() if lfxml is set.
(This needs to be handled better.)
Phil Benchoff, benchoff at vt.edu
$Id: README.xml,v 1.5 2004/08/30 13:06:17 benchoff Exp $
------------------------------------------------------------
--- dhcp-3.0.1-OEM/server/dhcpd.c 2004-07-09 20:11:18.000000000 -0400
+++ dhcp-3.0.1+vt_parser/server/dhcpd.c 2004-08-30 08:33:21.000000000 -0400
@@ -153,6 +153,11 @@
int dhcp_max_agent_option_packet_length = DHCP_MTU_MAX;
+#ifdef VT_PARSER_ONLY
+/* Keeps track of -lx option */
+int lfxml = 0;
+#endif
+
static omapi_auth_key_t *omapi_key = (omapi_auth_key_t *)0;
int omapi_port;
@@ -317,6 +322,16 @@
cftest = 1;
lftest = 1;
log_perror = -1;
+#ifdef VT_PARSER_ONLY
+ } else if (!strcmp (argv [i], "-lx")) {
+ /* output lease file XML */
+ cftest = 1;
+ lftest = 1;
+ lfxml = 1;
+#ifndef DEBUG
+ daemon = 0;
+#endif
+#endif /* VTPARSER_ONLY */
} else if (!strcmp (argv [i], "-q")) {
quiet = 1;
quiet_interface_discovery = 1;
@@ -875,7 +890,11 @@
log_info (copyright);
log_info (arr);
+#ifdef VT_PARSER_ONLY
+ log_fatal ("Usage: dhcpd [-p <UDP port #>] [-d] [-f]%s%s%s%s%s",
+#else
log_fatal ("Usage: dhcpd [-p <UDP port #>] [-d] [-f]%s%s%s%s",
+#endif
"\n [-cf config-file] [-lf lease-file]",
#if defined (TRACING)
"\n [-tf trace-output-file]",
@@ -883,7 +902,12 @@
#else
"", "",
#endif /* TRACING */
- "\n [-t] [-T] [-s server] [if0 [...ifN]]");
+ "\n [-t] [-T] [-s server] [if0 [...ifN]]",
+#ifdef VT_PARSER_ONLY
+ "\n [-lx]");
+#else
+ "");
+#endif
}
void lease_pinged (from, packet, length)
--- dhcp-3.0.1-OEM/server/mdb.c 2004-06-10 13:59:56.000000000 -0400
+++ dhcp-3.0.1+vt_parser/server/mdb.c 2004-08-30 08:41:48.000000000 -0400
@@ -40,6 +40,10 @@
#include "dhcpd.h"
#include "omapip/hash.h"
+#ifdef VT_PARSER_ONLY
+extern int lfxml;
+#endif
+
struct subnet *subnets;
struct shared_network *shared_networks;
host_hash_t *host_hw_addr_hash;
@@ -1738,6 +1742,12 @@
int num_written;
struct lease **lptr [5];
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fputs("<dhcp_server>\n",stdout);
+ }
+#endif
+
/* Write all the dynamically-created group declarations. */
if (group_name_hash) {
num_written = 0;
@@ -1795,10 +1805,25 @@
#if defined (FAILOVER_PROTOCOL)
/* Write all the failover states. */
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fputs(" <failover_states>\n",stdout);
+ }
+#endif
if (!dhcp_failover_write_all_states ())
return 0;
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fputs(" </failover_states>\n",stdout);
+ }
+#endif
#endif
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fputs(" <leases>\n",stdout);
+ }
+#endif
/* Write all the leases. */
num_written = 0;
for (s = shared_networks; s; s = s -> next) {
@@ -1825,7 +1850,18 @@
}
}
}
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fputs(" </leases>\n",stdout);
+ }
+#endif
log_info ("Wrote %d leases to leases file.", num_written);
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fputs("</dhcp_server>\n",stdout);
+ return 0;
+ }
+#endif
if (!commit_leases ())
return 0;
return 1;
@@ -1973,7 +2009,18 @@
expiry routine on the pool. */
for (s = shared_networks; s; s = s -> next) {
for (p = s -> pools; p; p = p -> next) {
+#ifdef VT_PARSER_ONLY
+/*
+ * XXX N3PB
+ * Segfaults in here sometimes. This doesn't seem to be
+ * critical to getting a reasolable XML output. Until
+ * this is taken care of, the binary generated can't
+ * be used as a real DHCP server.
+#endif
pool_timer (p);
+#ifdef VT_PARSER_ONLY
+ */
+#endif
p -> lease_count = 0;
p -> free_leases = 0;
--- dhcp-3.0.1-OEM/server/db.c 2004-06-17 16:54:40.000000000 -0400
+++ dhcp-3.0.1+vt_parser/server/db.c 2004-08-30 08:39:00.000000000 -0400
@@ -48,6 +48,10 @@
TIME write_time;
int lease_file_is_corrupt = 0;
+#ifdef VT_PARSER_ONLY
+extern int lfxml;
+#endif
+
/* Write the specified lease to the current lease database file. */
int write_lease (lease)
@@ -69,7 +73,23 @@
if (counting)
++count;
errno = 0;
- fprintf (db_file, "lease %s {", piaddr (lease -> ip_addr));
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ /*
+ * lease->ip_addr is 16 bytes.
+ * use ISC function to convert to dotted-decimal string
+ * convert with inet_addr
+ * print with ntohl
+ */
+ fprintf (stdout," <lease id=\"%u\">\n",ntohl(inet_addr(piaddr (lease -> ip_addr))));
+ fprintf (stdout," <address family=\"inet\" ip_integer=\"%u\">%s</address>\n",
+ ntohl(inet_addr(piaddr (lease -> ip_addr))),piaddr (lease -> ip_addr));
+ } else {
+#endif
+ fprintf (db_file, "lease %s {", piaddr (lease -> ip_addr));
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno) {
++errors;
}
@@ -88,7 +108,15 @@
} else
strcpy (tbuf, "never;");
errno = 0;
- fprintf (db_file, "\n starts %s", tbuf);
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf(stdout," <start time=\"%u\">%s</start>\n",lease -> starts,tbuf);
+ } else {
+#endif
+ fprintf (db_file, "\n starts %s", tbuf);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno) {
++errors;
}
@@ -105,7 +133,15 @@
} else
strcpy (tbuf, "never;");
errno = 0;
- fprintf (db_file, "\n ends %s", tbuf);
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf(stdout," <end time=\"%u\">%s</end>\n",lease -> ends,tbuf);
+ } else {
+#endif
+ fprintf (db_file, "\n ends %s", tbuf);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno) {
++errors;
}
@@ -114,10 +150,18 @@
if (lease -> tstp) {
t = gmtime (&lease -> tstp);
errno = 0;
- fprintf (db_file, "\n tstp %d %d/%02d/%02d %02d:%02d:%02d;",
- t -> tm_wday, t -> tm_year + 1900,
- t -> tm_mon + 1, t -> tm_mday,
- t -> tm_hour, t -> tm_min, t -> tm_sec);
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ } else {
+#endif
+ fprintf (db_file, "\n tstp %d %d/%02d/%02d %02d:%02d:%02d;",
+ t -> tm_wday, t -> tm_year + 1900,
+ t -> tm_mon + 1, t -> tm_mday,
+ t -> tm_hour, t -> tm_min, t -> tm_sec);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
+
if (errno) {
++errors;
}
@@ -125,10 +169,17 @@
if (lease -> tsfp) {
t = gmtime (&lease -> tsfp);
errno = 0;
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ } else {
+#endif
fprintf (db_file, "\n tsfp %d %d/%02d/%02d %02d:%02d:%02d;",
t -> tm_wday, t -> tm_year + 1900,
t -> tm_mon + 1, t -> tm_mday,
t -> tm_hour, t -> tm_min, t -> tm_sec);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno) {
++errors;
}
@@ -136,10 +187,17 @@
if (lease -> cltt) {
t = gmtime (&lease -> cltt);
errno = 0;
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ } else {
+#endif
fprintf (db_file, "\n cltt %d %d/%02d/%02d %02d:%02d:%02d;",
t -> tm_wday, t -> tm_year + 1900,
t -> tm_mon + 1, t -> tm_mday,
t -> tm_hour, t -> tm_min, t -> tm_sec);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno) {
++errors;
}
@@ -147,45 +205,106 @@
if (lease -> binding_state == FTS_ACTIVE &&
(lease -> flags & BOOTP_LEASE)) {
- fprintf (db_file, "\n binding state bootp;\n");
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf (stdout," <binding_state type=\"current\" state=\"%s\" />\n","bootp");
+ } else {
+#endif
+ fprintf (db_file, "\n binding state bootp;\n");
+#ifdef VT_PARSER_ONLY
+ }
+#endif
} else {
- fprintf (db_file, "\n binding state %s;",
- ((lease -> binding_state > 0 &&
- lease -> binding_state <= FTS_LAST)
- ? binding_state_names [lease -> binding_state - 1]
- : "abandoned"));
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf (stdout," <binding_state type=\"current\" state=\"%s\" />\n",
+ ((lease -> binding_state > 0 &&
+ lease -> binding_state <= FTS_LAST)
+ ? binding_state_names [lease -> binding_state - 1]
+ : "abandoned"));
+ } else {
+#endif
+ fprintf (db_file, "\n binding state %s;",
+ ((lease -> binding_state > 0 &&
+ lease -> binding_state <= FTS_LAST)
+ ? binding_state_names [lease -> binding_state - 1]
+ : "abandoned"));
+#ifdef VT_PARSER_ONLY
+ }
+#endif
}
if (lease -> binding_state != lease -> next_binding_state) {
if (lease -> next_binding_state == FTS_ACTIVE &&
- (lease -> flags & BOOTP_LEASE))
- fprintf (db_file, "\n next binding state bootp;\n");
- else
- fprintf (db_file, "\n next binding state %s;",
- ((lease -> next_binding_state > 0 &&
- lease -> next_binding_state <= FTS_LAST)
- ? (binding_state_names
- [lease -> next_binding_state - 1])
- : "abandoned"));
+ (lease -> flags & BOOTP_LEASE)) {
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf (stdout," <binding_state type=\"next\" state=\"%s\" />\n","bootp");
+ } else {
+#endif
+ fprintf (db_file, "\n next binding state bootp;\n");
+#ifdef VT_PARSER_ONLY
+ }
+#endif
+ } else {
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf (stdout," <binding_state type=\"next\" state=\"%s\" />\n",
+ ((lease -> next_binding_state > 0 &&
+ lease -> next_binding_state <= FTS_LAST)
+ ? (binding_state_names
+ [lease -> next_binding_state - 1])
+ : "abandoned"));
+ } else {
+#endif
+ fprintf (db_file, "\n next binding state %s;",
+ ((lease -> next_binding_state > 0 &&
+ lease -> next_binding_state <= FTS_LAST)
+ ? (binding_state_names
+ [lease -> next_binding_state - 1])
+ : "abandoned"));
+#ifdef VT_PARSER_ONLY
+ }
+#endif
+ }
}
/* If this lease is billed to a class and is still valid,
write it out. */
if (lease -> billing_class && lease -> ends > cur_time) {
- if (!write_billing_class (lease -> billing_class)) {
- log_error ("unable to write class %s",
- lease -> billing_class -> name);
- ++errors;
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ } else {
+#endif
+ if (!write_billing_class (lease -> billing_class)) {
+ log_error ("unable to write class %s",
+ lease -> billing_class -> name);
+ ++errors;
+ }
+#ifdef VT_PARSER_ONLY
}
+#endif
}
if (lease -> hardware_addr.hlen) {
errno = 0;
- fprintf (db_file, "\n hardware %s %s;",
- hardware_types [lease -> hardware_addr.hbuf [0]],
- print_hw_addr (lease -> hardware_addr.hbuf [0],
- lease -> hardware_addr.hlen - 1,
- &lease -> hardware_addr.hbuf [1]));
+#ifdef VT_PARSER_ONLY
+ if (lfxml) {
+ fprintf (stdout," <hardware_address type=\"%s\" address=\"%s\" />\n",
+ hardware_types [lease -> hardware_addr.hbuf [0]],
+ print_hw_addr (lease -> hardware_addr.hbuf [0],
+ lease -> hardware_addr.hlen - 1,
+ &lease -> hardware_addr.hbuf [1]));
+ } else {
+#endif
+ fprintf (db_file, "\n hardware %s %s;",
+ hardware_types [lease -> hardware_addr.hbuf [0]],
+ print_hw_addr (lease -> hardware_addr.hbuf [0],
+ lease -> hardware_addr.hlen - 1,
+ &lease -> hardware_addr.hbuf [1]));
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno) {
++errors;
}
@@ -194,13 +313,25 @@
int i;
s = quotify_buf (lease -> uid, lease -> uid_len, MDL);
if (s) {
- fprintf (db_file, "\n uid \"%s\";", s);
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ /* XXX N3PB escape cdata */
+ } else {
+#endif
+ fprintf (db_file, "\n uid \"%s\";", s);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno)
++errors;
dfree (s, MDL);
} else
++errors;
}
+#ifdef VT_PARSER_ONLY
+if ( !lfxml ) {
+ /* XXX N3PB Just skip this whole section for now. */
+#endif
if (lease -> scope) {
for (b = lease -> scope -> bindings; b; b = b -> next) {
if (!b -> value)
@@ -295,8 +426,20 @@
/* XXX */
fprintf (db_file, "\n }");
}
+#ifdef VT_PARSER_ONLY
+}
+#endif
+
errno = 0;
- fputs ("\n}\n", db_file);
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fputs(" </lease>\n", stdout);
+ } else {
+#endif
+ fputs ("\n}\n", db_file);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno) {
++errors;
}
@@ -533,42 +676,89 @@
return 0;
errno = 0;
- fprintf (db_file, "\nfailover peer \"%s\" state {", state -> name);
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf (stdout," <failover_state partner=\"%s\">\n",state->name);
+ } else {
+#endif
+ fprintf (db_file, "\nfailover peer \"%s\" state {", state -> name);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno)
++errors;
t = gmtime (&state -> me.stos);
errno = 0;
- fprintf (db_file, "\n my state %s at %d %d/%02d/%02d %02d:%02d:%02d;",
- /* Never record our state as "startup"! */
- (state -> me.state == startup
- ? dhcp_failover_state_name_print (state -> saved_state)
- : dhcp_failover_state_name_print (state -> me.state)),
- t -> tm_wday, t -> tm_year + 1900,
- t -> tm_mon + 1, t -> tm_mday,
- t -> tm_hour, t -> tm_min, t -> tm_sec);
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf (stdout," <my_state state=\"%s\" time=\"%u\"/>\n",
+ (state -> me.state == startup
+ ? dhcp_failover_state_name_print (state -> saved_state)
+ : dhcp_failover_state_name_print (state -> me.state)),
+ state -> me.stos);
+ } else {
+#endif
+ fprintf (db_file, "\n my state %s at %d %d/%02d/%02d %02d:%02d:%02d;",
+ /* Never record our state as "startup"! */
+ (state -> me.state == startup
+ ? dhcp_failover_state_name_print (state -> saved_state)
+ : dhcp_failover_state_name_print (state -> me.state)),
+ t -> tm_wday, t -> tm_year + 1900,
+ t -> tm_mon + 1, t -> tm_mday,
+ t -> tm_hour, t -> tm_min, t -> tm_sec);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno)
++errors;
t = gmtime (&state -> partner.stos);
errno = 0;
- fprintf (db_file,
- "\n partner state %s at %d %d/%02d/%02d %02d:%02d:%02d;",
- dhcp_failover_state_name_print (state -> partner.state),
- t -> tm_wday, t -> tm_year + 1900,
- t -> tm_mon + 1, t -> tm_mday,
- t -> tm_hour, t -> tm_min, t -> tm_sec);
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf (stdout," <partner_state state=\"%s\" time=\"%u\"/>\n",
+ dhcp_failover_state_name_print (state -> partner.state),
+ state -> partner.stos);
+ } else {
+#endif
+ fprintf (db_file,
+ "\n partner state %s at %d %d/%02d/%02d %02d:%02d:%02d;",
+ dhcp_failover_state_name_print (state -> partner.state),
+ t -> tm_wday, t -> tm_year + 1900,
+ t -> tm_mon + 1, t -> tm_mday,
+ t -> tm_hour, t -> tm_min, t -> tm_sec);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno)
++errors;
if (state -> i_am == secondary) {
errno = 0;
- fprintf (db_file, "\n mclt %ld;",
- (unsigned long)state -> mclt);
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf (stdout, " <mclt seconds=\"%ld\"/>\n",
+ (unsigned long)state -> mclt);
+ } else {
+#endif
+ fprintf (db_file, "\n mclt %ld;",
+ (unsigned long)state -> mclt);
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno)
++errors;
}
- fprintf (db_file, "\n}\n");
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ fprintf (stdout," </failover_state>\n");
+ } else {
+#endif
+ fprintf (db_file, "\n}\n");
+#ifdef VT_PARSER_ONLY
+ }
+#endif
if (errno)
++errors;
@@ -758,6 +948,18 @@
GET_TIME (&write_time);
new_lease_file ();
}
+#ifdef VT_PARSER_ONLY
+ else {
+ if ( lfxml ) {
+ /* expire_all_pools ends up calling write_lease sometimes. */
+ lfxml = 0;
+ expire_all_pools();
+ lfxml = 1;
+ db_file = stdout;
+ write_leases();
+ }
+ }
+#endif
}
int new_lease_file ()
@@ -767,6 +969,11 @@
TIME t;
int db_fd;
+#ifdef VT_PARSER_ONLY
+ if ( lfxml ) {
+ log_fatal("new_lease_file() called in parser only mode.");
+ }
+#endif
/* If we already have an open database, close it. */
if (db_file) {
fclose (db_file);
More information about the dhcp-hackers
mailing list