3 Persistent database management routines for DHCPD... */
6 * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1995-2003 by Internet Software Consortium
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * Internet Systems Consortium, Inc.
23 * Redwood City, CA 94063
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
30 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
31 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
36 static char copyright
[] =
37 "$Id: db.c,v 1.80 2007/06/05 23:28:43 each Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n";
46 static int counting
= 0;
49 int lease_file_is_corrupt
= 0;
51 /* Write the specified lease to the current lease database file. */
53 int write_lease (lease
)
62 /* If the lease file is corrupt, don't try to write any more leases
63 until we've written a good lease file. */
64 if (lease_file_is_corrupt
)
65 if (!new_lease_file ())
71 fprintf (db_file
, "lease %s {", piaddr (lease
-> ip_addr
));
77 ((tval
= print_time(lease
->starts
)) == NULL
||
78 fprintf(db_file
, "\n starts %s", tval
) < 0))
82 ((tval
= print_time(lease
->ends
)) == NULL
||
83 fprintf(db_file
, "\n ends %s", tval
) < 0))
87 ((tval
= print_time(lease
->tstp
)) == NULL
||
88 fprintf(db_file
, "\n tstp %s", tval
) < 0))
92 ((tval
= print_time(lease
->tsfp
)) == NULL
||
93 fprintf(db_file
, "\n tsfp %s", tval
) < 0))
97 ((tval
= print_time(lease
->atsfp
)) == NULL
||
98 fprintf(db_file
, "\n atsfp %s", tval
) < 0))
102 ((tval
= print_time(lease
->cltt
)) == NULL
||
103 fprintf(db_file
, "\n cltt %s", tval
) < 0))
106 fprintf (db_file
, "\n binding state %s;",
107 ((lease
-> binding_state
> 0 &&
108 lease
-> binding_state
<= FTS_LAST
)
109 ? binding_state_names
[lease
-> binding_state
- 1]
112 if (lease
-> binding_state
!= lease
-> next_binding_state
)
113 fprintf (db_file
, "\n next binding state %s;",
114 ((lease
-> next_binding_state
> 0 &&
115 lease
-> next_binding_state
<= FTS_LAST
)
116 ? (binding_state_names
117 [lease
-> next_binding_state
- 1])
120 if (lease
->flags
& RESERVED_LEASE
)
121 fprintf(db_file
, "\n reserved;");
122 if (lease
->flags
& BOOTP_LEASE
)
123 fprintf(db_file
, "\n dynamic-bootp;");
125 /* If this lease is billed to a class and is still valid,
127 if (lease
-> billing_class
&& lease
-> ends
> cur_time
) {
128 if (!write_billing_class (lease
-> billing_class
)) {
129 log_error ("unable to write class %s",
130 lease
-> billing_class
-> name
);
135 if (lease
-> hardware_addr
.hlen
) {
137 fprintf (db_file
, "\n hardware %s %s;",
138 hardware_types
[lease
-> hardware_addr
.hbuf
[0]],
139 print_hw_addr (lease
-> hardware_addr
.hbuf
[0],
140 lease
-> hardware_addr
.hlen
- 1,
141 &lease
-> hardware_addr
.hbuf
[1]));
146 if (lease
-> uid_len
) {
148 s
= quotify_buf (lease
-> uid
, lease
-> uid_len
, MDL
);
151 fprintf (db_file
, "\n uid \"%s\";", s
);
158 if (lease
-> scope
) {
159 for (b
= lease
-> scope
-> bindings
; b
; b
= b
-> next
) {
162 if (b
-> value
-> type
== binding_data
) {
163 if (b
-> value
-> value
.data
.data
) {
164 s
= quotify_buf (b
-> value
-> value
.data
.data
,
165 b
-> value
-> value
.data
.len
, MDL
);
168 fprintf (db_file
, "\n set %s = \"%s\";",
176 } else if (b
-> value
-> type
== binding_numeric
) {
178 fprintf (db_file
, "\n set %s = %%%ld;",
179 b
-> name
, b
-> value
-> value
.intval
);
182 } else if (b
-> value
-> type
== binding_boolean
) {
184 fprintf (db_file
, "\n set %s = %s;",
186 b
-> value
-> value
.intval
? "true" : "false");
189 } else if (b
-> value
-> type
== binding_dns
) {
190 log_error ("%s: persistent dns values not supported.",
192 } else if (b
-> value
-> type
== binding_function
) {
193 log_error ("%s: persistent functions not supported.",
196 log_error ("%s: unknown binding type %d",
197 b
-> name
, b
-> value
-> type
);
201 if (lease
-> agent_options
) {
202 struct option_cache
*oc
;
203 struct data_string ds
;
206 memset (&ds
, 0, sizeof ds
);
207 for (p
= lease
-> agent_options
-> first
; p
; p
= p
-> cdr
) {
208 oc
= (struct option_cache
*)p
-> car
;
209 if (oc
-> data
.len
) {
211 fprintf (db_file
, "\n option agent.%s %s;",
212 oc
-> option
-> name
,
213 pretty_print_option (oc
-> option
, oc
-> data
.data
,
214 oc
-> data
.len
, 1, 1));
220 if (lease
-> client_hostname
&&
221 db_printable (lease
-> client_hostname
)) {
222 s
= quotify_string (lease
-> client_hostname
, MDL
);
225 fprintf (db_file
, "\n client-hostname \"%s\";", s
);
232 if (lease
-> on_expiry
) {
234 fprintf (db_file
, "\n on expiry%s {",
235 lease
-> on_expiry
== lease
-> on_release
236 ? " or release" : "");
239 write_statements (db_file
, lease
-> on_expiry
, 4);
241 fprintf (db_file
, "\n }");
243 if (lease
-> on_release
&& lease
-> on_release
!= lease
-> on_expiry
) {
245 fprintf (db_file
, "\n on release {");
248 write_statements (db_file
, lease
-> on_release
, 4);
250 fprintf (db_file
, "\n }");
253 fputs ("\n}\n", db_file
);
258 log_info ("write_lease: unable to write lease %s",
259 piaddr (lease
-> ip_addr
));
261 lease_file_is_corrupt
= 1;
265 int write_host (host
)
266 struct host_decl
*host
;
270 struct data_string ip_addrs
;
272 /* If the lease file is corrupt, don't try to write any more leases
273 until we've written a good lease file. */
274 if (lease_file_is_corrupt
)
275 if (!new_lease_file ())
278 if (!db_printable (host
-> name
))
285 fprintf (db_file
, "host %s {", host
-> name
);
290 if (host
-> flags
& HOST_DECL_DYNAMIC
) {
292 fprintf (db_file
, "\n dynamic;");
297 if (host
-> flags
& HOST_DECL_DELETED
) {
299 fprintf (db_file
, "\n deleted;");
303 if (host
-> interface
.hlen
) {
305 fprintf (db_file
, "\n hardware %s %s;",
306 hardware_types
[host
-> interface
.hbuf
[0]],
307 print_hw_addr (host
-> interface
.hbuf
[0],
308 host
-> interface
.hlen
- 1,
309 &host
-> interface
.hbuf
[1]));
314 if (host
-> client_identifier
.len
) {
317 if (db_printable_len (host
-> client_identifier
.data
,
318 host
-> client_identifier
.len
)) {
319 fprintf (db_file
, "\n uid \"%.*s\";",
320 (int)host
-> client_identifier
.len
,
321 host
-> client_identifier
.data
);
325 host
-> client_identifier
.data
[0]);
330 i
< host
-> client_identifier
.len
; i
++) {
332 fprintf (db_file
, ":%2.2x",
334 client_identifier
.data
[i
]);
343 memset (&ip_addrs
, 0, sizeof ip_addrs
);
344 if (host
-> fixed_addr
&&
345 evaluate_option_cache (&ip_addrs
, (struct packet
*)0,
347 (struct client_state
*)0,
348 (struct option_state
*)0,
349 (struct option_state
*)0,
351 host
-> fixed_addr
, MDL
)) {
354 fprintf (db_file
, "\n fixed-address ");
358 for (i
= 0; i
< ip_addrs
.len
- 3; i
+= 4) {
360 fprintf (db_file
, "%u.%u.%u.%u%s",
361 ip_addrs
.data
[i
] & 0xff,
362 ip_addrs
.data
[i
+ 1] & 0xff,
363 ip_addrs
.data
[i
+ 2] & 0xff,
364 ip_addrs
.data
[i
+ 3] & 0xff,
365 i
+ 7 < ip_addrs
.len
? "," : "");
371 fputc (';', db_file
);
377 if (host
-> named_group
) {
379 fprintf (db_file
, "\n group \"%s\";",
380 host
-> named_group
-> name
);
387 (!host
-> named_group
||
388 host
-> group
!= host
-> named_group
-> group
) &&
389 host
-> group
!= root_group
) {
391 write_statements (db_file
,
392 host
-> group
-> statements
, 8);
400 fputs ("\n}\n", db_file
);
405 log_info ("write_host: unable to write host %s",
407 lease_file_is_corrupt
= 1;
412 int write_group (group
)
413 struct group_object
*group
;
418 /* If the lease file is corrupt, don't try to write any more leases
419 until we've written a good lease file. */
420 if (lease_file_is_corrupt
)
421 if (!new_lease_file ())
424 if (!db_printable (group
-> name
))
431 fprintf (db_file
, "group %s {", group
-> name
);
436 if (group
-> flags
& GROUP_OBJECT_DYNAMIC
) {
438 fprintf (db_file
, "\n dynamic;");
443 if (group
-> flags
& GROUP_OBJECT_STATIC
) {
445 fprintf (db_file
, "\n static;");
450 if (group
-> flags
& GROUP_OBJECT_DELETED
) {
452 fprintf (db_file
, "\n deleted;");
456 if (group
-> group
) {
458 write_statements (db_file
,
459 group
-> group
-> statements
, 8);
467 fputs ("\n}\n", db_file
);
472 log_info ("write_group: unable to write group %s",
474 lease_file_is_corrupt
= 1;
480 * Write an IA_NA and the options it has.
483 write_ia_na(const struct ia_na
*ia_na
) {
484 struct iaaddr
*iaaddr
;
486 char addr_buf
[sizeof("ffff:ffff:ffff:ffff:ffff:ffff.255.255.255.255")];
487 const char *binding_state
;
493 * If the lease file is corrupt, don't try to write any more
494 * leases until we've written a good lease file.
496 if (lease_file_is_corrupt
) {
497 if (!new_lease_file()) {
507 s
= quotify_buf(ia_na
->iaid_duid
.data
, ia_na
->iaid_duid
.len
, MDL
);
511 fprintf_ret
= fprintf(db_file
, "ia-na \"%s\" {\n", s
);
513 if (fprintf_ret
< 0) {
516 for (i
=0; i
<ia_na
->num_iaaddr
; i
++) {
517 iaaddr
= ia_na
->iaaddr
[i
];
519 inet_ntop(AF_INET6
, &iaaddr
->addr
, addr_buf
, sizeof(addr_buf
));
520 if (fprintf(db_file
, " iaaddr %s {\n", addr_buf
) < 0) {
523 if ((iaaddr
->state
<= 0) || (iaaddr
->state
> FTS_LAST
)) {
524 log_fatal("Unknown ia_na state %d at %s:%d",
527 binding_state
= binding_state_names
[iaaddr
->state
-1];
528 if (fprintf(db_file
, " binding state %s;\n",
529 binding_state
) < 0) {
532 tval
= print_time(iaaddr
->valid_lifetime_end_time
);
536 if (fprintf(db_file
, " ends %s", tval
) < 0) {
539 if (fprintf(db_file
, "\n }\n") < 0) goto error_exit
;
541 if (fprintf(db_file
, "}\n\n") < 0) goto error_exit
;
547 log_info("write_ia_na: unable to write ia-na");
548 lease_file_is_corrupt
= 1;
554 * Put a copy of the server DUID in the leases file.
557 write_server_duid(void) {
558 struct data_string server_duid
;
563 * Only write the DUID if it's been set.
565 if (!server_duid_isset()) {
570 * If the lease file is corrupt, don't try to write any more
571 * leases until we've written a good lease file.
573 if (lease_file_is_corrupt
) {
574 if (!new_lease_file()) {
580 * Get a copy of our server DUID and convert to a quoted string.
582 memset(&server_duid
, 0, sizeof(server_duid
));
583 copy_server_duid(&server_duid
, MDL
);
584 s
= quotify_buf(server_duid
.data
, server_duid
.len
, MDL
);
585 data_string_forget(&server_duid
, MDL
);
591 * Write to the leases file.
593 fprintf_ret
= fprintf(db_file
, "server-duid \"%s\";\n\n", s
);
595 if (fprintf_ret
< 0) {
600 * Check if we actually managed to write.
606 log_info("write_server_duid: unable to write server-duid");
607 lease_file_is_corrupt
= 1;
612 #if defined (FAILOVER_PROTOCOL)
613 int write_failover_state (dhcp_failover_state_t
*state
)
619 if (lease_file_is_corrupt
)
620 if (!new_lease_file ())
624 fprintf (db_file
, "\nfailover peer \"%s\" state {", state
-> name
);
628 tval
= print_time(state
->me
.stos
);
630 fprintf(db_file
, "\n my state %s at %s",
631 (state
->me
.state
== startup
) ?
632 dhcp_failover_state_name_print(state
->saved_state
) :
633 dhcp_failover_state_name_print(state
->me
.state
),
637 tval
= print_time(state
->partner
.stos
);
639 fprintf(db_file
, "\n partner state %s at %s",
640 dhcp_failover_state_name_print(state
->partner
.state
),
644 if (state
-> i_am
== secondary
) {
646 fprintf (db_file
, "\n mclt %ld;",
647 (unsigned long)state
-> mclt
);
651 fprintf (db_file
, "\n}\n");
656 log_info ("write_failover_state: unable to write state %s",
658 lease_file_is_corrupt
= 1;
667 const unsigned char *s
;
670 for (i
= 0; s
[i
]; i
++)
671 if (!isascii (s
[i
]) || !isprint (s
[i
])
672 || s
[i
] == '"' || s
[i
] == '\\')
677 int db_printable_len (s
, len
)
678 const unsigned char *s
;
683 for (i
= 0; i
< len
; i
++)
684 if (!isascii (s
[i
]) || !isprint (s
[i
]) ||
685 s
[i
] == '"' || s
[i
] == '\\')
690 static int print_hash_string(FILE *fp
, struct class *class)
694 for (i
= 0 ; i
< class->hash_string
.len
; i
++)
695 if (!isascii(class->hash_string
.data
[i
]) ||
696 !isprint(class->hash_string
.data
[i
]))
699 if (i
== class->hash_string
.len
) {
700 if (fprintf(fp
, " \"%.*s\"", (int)class->hash_string
.len
,
701 class->hash_string
.data
) <= 0) {
702 log_error("Failure writing hash string: %m");
706 if (fprintf(fp
, " %2.2x", class->hash_string
.data
[0]) <= 0) {
707 log_error("Failure writing hash string: %m");
710 for (i
= 1 ; i
< class->hash_string
.len
; i
++) {
711 if (fprintf(fp
, ":%2.2x",
712 class->hash_string
.data
[i
]) <= 0) {
713 log_error("Failure writing hash string: %m");
724 write_named_billing_class(const void *key
, unsigned len
, void *object
)
726 const unsigned char *name
= key
;
727 struct class *class = object
;
729 if (class->flags
& CLASS_DECL_DYNAMIC
) {
731 if (class->superclass
== 0) {
732 if (fprintf(db_file
, "class \"%s\" {\n", name
) <= 0)
733 return ISC_R_IOERROR
;
735 if (fprintf(db_file
, "subclass \"%s\"",
736 class->superclass
->name
) <= 0)
737 return ISC_R_IOERROR
;
738 if (!print_hash_string(db_file
, class))
739 return ISC_R_IOERROR
;
740 if (fprintf(db_file
, " {\n") <= 0)
741 return ISC_R_IOERROR
;
744 if ((class->flags
& CLASS_DECL_DELETED
) != 0) {
745 if (fprintf(db_file
, " deleted;\n") <= 0)
746 return ISC_R_IOERROR
;
748 if (fprintf(db_file
, " dynamic;\n") <= 0)
749 return ISC_R_IOERROR
;
752 if (class->lease_limit
> 0) {
753 if (fprintf(db_file
, " lease limit %d;\n",
754 class->lease_limit
) <= 0)
755 return ISC_R_IOERROR
;
758 if (class->expr
!= 0) {
759 if (fprintf(db_file
, " match if ") <= 0)
760 return ISC_R_IOERROR
;
761 write_expression(db_file
, class->expr
, 5, 5, 0);
762 if (fprintf(db_file
, ";\n") <= 0)
763 return ISC_R_IOERROR
;
766 if (class->submatch
!= 0) {
767 if (class->spawning
) {
768 if (fprintf(db_file
, " spawn ") <= 0)
769 return ISC_R_IOERROR
;
771 if (fprintf(db_file
, " match ") <= 0)
772 return ISC_R_IOERROR
;
775 write_expression(db_file
, class->submatch
, 5, 5, 0);
776 if (fprintf(db_file
, ";\n") <= 0)
777 return ISC_R_IOERROR
;
780 if (class->statements
!= 0) {
781 write_statements(db_file
, class->statements
, 8);
784 /* XXXJAB this isn't right, but classes read in off the
785 leases file don't get the root group assigned to them
786 (due to clone_group() call). */
787 if (class->group
!= 0 && class->group
->authoritative
!= 0)
788 write_statements(db_file
, class->group
->statements
, 8);
790 if (fprintf(db_file
, "}\n\n") <= 0)
791 return ISC_R_IOERROR
;
794 if (class->hash
!= NULL
) { /* yep. recursive. god help us. */
795 /* XXX - cannot check error status of this...
796 * foo_hash_foreach returns a count of operations completed.
798 class_hash_foreach(class->hash
, write_named_billing_class
);
801 return ISC_R_SUCCESS
;
804 void write_billing_classes ()
806 struct collection
*lp
;
808 struct hash_bucket
*bp
;
811 for (lp
= collections
; lp
; lp
= lp
-> next
) {
812 for (cp
= lp
-> classes
; cp
; cp
= cp
-> nic
) {
813 if (cp
-> spawning
&& cp
-> hash
) {
814 class_hash_foreach (cp
-> hash
, write_named_billing_class
);
820 /* Write a spawned class to the database file. */
822 int write_billing_class (class)
828 if (lease_file_is_corrupt
)
829 if (!new_lease_file ())
832 if (!class -> superclass
) {
834 fprintf (db_file
, "\n billing class \"%s\";", class -> name
);
839 fprintf (db_file
, "\n billing subclass \"%s\"",
840 class -> superclass
-> name
);
844 print_hash_string(db_file
, class);
845 fprintf(db_file
, ";");
847 class -> dirty
= !errors
;
849 lease_file_is_corrupt
= 1;
853 /* Commit leases after a timeout. */
854 void commit_leases_timeout (void *foo
)
859 /* Commit any leases that have been written out... */
863 /* Commit any outstanding writes to the lease database file.
864 We need to do this even if we're rewriting the file below,
865 just in case the rewrite fails. */
866 if (fflush (db_file
) == EOF
) {
867 log_info ("commit_leases: unable to commit: %m");
870 if (fsync (fileno (db_file
)) < 0) {
871 log_info ("commit_leases: unable to commit: %m");
875 /* If we haven't rewritten the lease database in over an
876 hour, rewrite it now. (The length of time should probably
878 if (count
&& cur_time
- write_time
> 3600) {
880 write_time
= cur_time
;
886 void db_startup (testp
)
891 #if defined (TRACING)
892 if (!trace_playback ()) {
894 /* Read in the existing lease file... */
895 status
= read_conf_file (path_dhcpd_db
,
896 (struct group
*)0, 0, 1);
897 /* XXX ignore status? */
898 #if defined (TRACING)
902 #if defined (TRACING)
903 /* If we're playing back, there is no lease file, so we can't
904 append it, so we create one immediately (maybe this isn't
905 the best solution... */
906 if (trace_playback ()) {
911 db_file
= fopen (path_dhcpd_db
, "a");
913 log_fatal ("Can't open %s for append.", path_dhcpd_db
);
915 #if defined (TRACING)
916 if (trace_playback ())
917 write_time
= cur_time
;
924 #if defined(REPORT_HASH_PERFORMANCE)
925 log_info("Host HW hash: %s", host_hash_report(host_hw_addr_hash
));
926 log_info("Host UID hash: %s", host_hash_report(host_uid_hash
));
927 log_info("Lease IP hash: %s",
928 lease_ip_hash_report(lease_ip_addr_hash
));
929 log_info("Lease UID hash: %s", lease_id_hash_report(lease_uid_hash
));
930 log_info("Lease HW hash: %s",
931 lease_id_hash_report(lease_hw_addr_hash
));
935 int new_lease_file ()
938 char backfname
[512];
944 /* Make a temporary lease file... */
947 db_validity
= lease_file_is_corrupt
;
949 /* %Audit% Truncated filename causes panic. %2004.06.17,Safe%
950 * This should never happen since the path is a configuration
951 * variable from build-time or command-line. But if it should,
952 * either by malice or ignorance, we panic, since the potential
955 if (snprintf (newfname
, sizeof newfname
, "%s.%d",
956 path_dhcpd_db
, (int)t
) >= sizeof newfname
)
957 log_fatal("new_lease_file: lease file path too long");
959 db_fd
= open (newfname
, O_WRONLY
| O_TRUNC
| O_CREAT
, 0664);
961 log_error ("Can't create new lease file: %m");
964 if ((new_db_file
= fdopen(db_fd
, "w")) == NULL
) {
965 log_error("Can't fdopen new lease file: %m");
970 /* Close previous database, if any. */
973 db_file
= new_db_file
;
976 fprintf (db_file
, "# The format of this file is documented in the %s",
977 "dhcpd.leases(5) manual page.\n");
980 fprintf (db_file
, "# This lease file was written by isc-dhcp-%s\n\n",
985 /* At this point we have a new lease file that, so far, could not
986 * be described as either corrupt nor valid.
988 lease_file_is_corrupt
= 0;
990 /* Write out all the leases that we know of... */
992 if (!write_leases ())
995 #if defined (TRACING)
996 if (!trace_playback ()) {
998 /* %Audit% Truncated filename causes panic. %2004.06.17,Safe%
999 * This should never happen since the path is a configuration
1000 * variable from build-time or command-line. But if it should,
1001 * either by malice or ignorance, we panic, since the potential
1002 * for havoc is too high.
1004 if (snprintf (backfname
, sizeof backfname
, "%s~", path_dhcpd_db
)
1005 >= sizeof backfname
)
1006 log_fatal("new_lease_file: backup lease file path too long");
1008 /* Get the old database out of the way... */
1009 if (unlink (backfname
) < 0 && errno
!= ENOENT
) {
1010 log_error ("Can't remove old lease database backup %s: %m",
1014 if (link(path_dhcpd_db
, backfname
) < 0) {
1015 if (errno
== ENOENT
) {
1016 log_error("%s is missing - no lease db to backup.",
1019 log_error("Can't backup lease database %s to %s: %m",
1020 path_dhcpd_db
, backfname
);
1024 #if defined (TRACING)
1028 /* Move in the new file... */
1029 if (rename (newfname
, path_dhcpd_db
) < 0) {
1030 log_error ("Can't install new lease database %s to %s: %m",
1031 newfname
, path_dhcpd_db
);
1039 lease_file_is_corrupt
= db_validity
;
1045 int group_writer (struct group_object
*group
)
1047 if (!write_group (group
))
1049 if (!commit_leases ())