3 Persistent database management routines for DHCPD... */
6 * Copyright (c) 2004-2006 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.77 2007/05/19 18:47:15 dhankins Exp $ Copyright (c) 2004-2006 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
);
150 fprintf (db_file
, "\n uid \"%s\";", s
);
157 if (lease
-> scope
) {
158 for (b
= lease
-> scope
-> bindings
; b
; b
= b
-> next
) {
161 if (b
-> value
-> type
== binding_data
) {
162 if (b
-> value
-> value
.data
.data
) {
163 s
= quotify_buf (b
-> value
-> value
.data
.data
,
164 b
-> value
-> value
.data
.len
, MDL
);
167 fprintf (db_file
, "\n set %s = \"%s\";",
175 } else if (b
-> value
-> type
== binding_numeric
) {
177 fprintf (db_file
, "\n set %s = %%%ld;",
178 b
-> name
, b
-> value
-> value
.intval
);
181 } else if (b
-> value
-> type
== binding_boolean
) {
183 fprintf (db_file
, "\n set %s = %s;",
185 b
-> value
-> value
.intval
? "true" : "false");
188 } else if (b
-> value
-> type
== binding_dns
) {
189 log_error ("%s: persistent dns values not supported.",
191 } else if (b
-> value
-> type
== binding_function
) {
192 log_error ("%s: persistent functions not supported.",
195 log_error ("%s: unknown binding type %d",
196 b
-> name
, b
-> value
-> type
);
200 if (lease
-> agent_options
) {
201 struct option_cache
*oc
;
202 struct data_string ds
;
205 memset (&ds
, 0, sizeof ds
);
206 for (p
= lease
-> agent_options
-> first
; p
; p
= p
-> cdr
) {
207 oc
= (struct option_cache
*)p
-> car
;
208 if (oc
-> data
.len
) {
210 fprintf (db_file
, "\n option agent.%s %s;",
211 oc
-> option
-> name
,
212 pretty_print_option (oc
-> option
, oc
-> data
.data
,
213 oc
-> data
.len
, 1, 1));
219 if (lease
-> client_hostname
&&
220 db_printable (lease
-> client_hostname
)) {
221 s
= quotify_string (lease
-> client_hostname
, MDL
);
224 fprintf (db_file
, "\n client-hostname \"%s\";", s
);
231 if (lease
-> on_expiry
) {
233 fprintf (db_file
, "\n on expiry%s {",
234 lease
-> on_expiry
== lease
-> on_release
235 ? " or release" : "");
238 write_statements (db_file
, lease
-> on_expiry
, 4);
240 fprintf (db_file
, "\n }");
242 if (lease
-> on_release
&& lease
-> on_release
!= lease
-> on_expiry
) {
244 fprintf (db_file
, "\n on release {");
247 write_statements (db_file
, lease
-> on_release
, 4);
249 fprintf (db_file
, "\n }");
252 fputs ("\n}\n", db_file
);
257 log_info ("write_lease: unable to write lease %s",
258 piaddr (lease
-> ip_addr
));
260 lease_file_is_corrupt
= 1;
264 int write_host (host
)
265 struct host_decl
*host
;
269 struct data_string ip_addrs
;
271 /* If the lease file is corrupt, don't try to write any more leases
272 until we've written a good lease file. */
273 if (lease_file_is_corrupt
)
274 if (!new_lease_file ())
277 if (!db_printable (host
-> name
))
284 fprintf (db_file
, "host %s {", host
-> name
);
289 if (host
-> flags
& HOST_DECL_DYNAMIC
) {
291 fprintf (db_file
, "\n dynamic;");
296 if (host
-> flags
& HOST_DECL_DELETED
) {
298 fprintf (db_file
, "\n deleted;");
302 if (host
-> interface
.hlen
) {
304 fprintf (db_file
, "\n hardware %s %s;",
305 hardware_types
[host
-> interface
.hbuf
[0]],
306 print_hw_addr (host
-> interface
.hbuf
[0],
307 host
-> interface
.hlen
- 1,
308 &host
-> interface
.hbuf
[1]));
313 if (host
-> client_identifier
.len
) {
316 if (db_printable_len (host
-> client_identifier
.data
,
317 host
-> client_identifier
.len
)) {
318 fprintf (db_file
, "\n uid \"%.*s\";",
319 (int)host
-> client_identifier
.len
,
320 host
-> client_identifier
.data
);
324 host
-> client_identifier
.data
[0]);
329 i
< host
-> client_identifier
.len
; i
++) {
331 fprintf (db_file
, ":%2.2x",
333 client_identifier
.data
[i
]);
342 memset (&ip_addrs
, 0, sizeof ip_addrs
);
343 if (host
-> fixed_addr
&&
344 evaluate_option_cache (&ip_addrs
, (struct packet
*)0,
346 (struct client_state
*)0,
347 (struct option_state
*)0,
348 (struct option_state
*)0,
350 host
-> fixed_addr
, MDL
)) {
353 fprintf (db_file
, "\n fixed-address ");
357 for (i
= 0; i
< ip_addrs
.len
- 3; i
+= 4) {
359 fprintf (db_file
, "%u.%u.%u.%u%s",
360 ip_addrs
.data
[i
] & 0xff,
361 ip_addrs
.data
[i
+ 1] & 0xff,
362 ip_addrs
.data
[i
+ 2] & 0xff,
363 ip_addrs
.data
[i
+ 3] & 0xff,
364 i
+ 7 < ip_addrs
.len
? "," : "");
370 fputc (';', db_file
);
376 if (host
-> named_group
) {
378 fprintf (db_file
, "\n group \"%s\";",
379 host
-> named_group
-> name
);
386 (!host
-> named_group
||
387 host
-> group
!= host
-> named_group
-> group
) &&
388 host
-> group
!= root_group
) {
390 write_statements (db_file
,
391 host
-> group
-> statements
, 8);
399 fputs ("\n}\n", db_file
);
404 log_info ("write_host: unable to write host %s",
406 lease_file_is_corrupt
= 1;
411 int write_group (group
)
412 struct group_object
*group
;
417 /* If the lease file is corrupt, don't try to write any more leases
418 until we've written a good lease file. */
419 if (lease_file_is_corrupt
)
420 if (!new_lease_file ())
423 if (!db_printable (group
-> name
))
430 fprintf (db_file
, "group %s {", group
-> name
);
435 if (group
-> flags
& GROUP_OBJECT_DYNAMIC
) {
437 fprintf (db_file
, "\n dynamic;");
442 if (group
-> flags
& GROUP_OBJECT_STATIC
) {
444 fprintf (db_file
, "\n static;");
449 if (group
-> flags
& GROUP_OBJECT_DELETED
) {
451 fprintf (db_file
, "\n deleted;");
455 if (group
-> group
) {
457 write_statements (db_file
,
458 group
-> group
-> statements
, 8);
466 fputs ("\n}\n", db_file
);
471 log_info ("write_group: unable to write group %s",
473 lease_file_is_corrupt
= 1;
479 * Write an IA_NA and the options it has.
482 write_ia_na(const struct ia_na
*ia_na
) {
483 struct iaaddr
*iaaddr
;
485 char addr_buf
[sizeof("ffff:ffff:ffff:ffff:ffff:ffff.255.255.255.255")];
486 const char *binding_state
;
492 * If the lease file is corrupt, don't try to write any more
493 * leases until we've written a good lease file.
495 if (lease_file_is_corrupt
) {
496 if (!new_lease_file()) {
506 s
= quotify_buf(ia_na
->iaid_duid
.data
, ia_na
->iaid_duid
.len
, MDL
);
510 fprintf_ret
= fprintf(db_file
, "ia-na \"%s\" {\n", s
);
512 if (fprintf_ret
< 0) {
515 for (i
=0; i
<ia_na
->num_iaaddr
; i
++) {
516 iaaddr
= ia_na
->iaaddr
[i
];
518 inet_ntop(AF_INET6
, &iaaddr
->addr
, addr_buf
, sizeof(addr_buf
));
519 if (fprintf(db_file
, " iaaddr %s {\n", addr_buf
) < 0) {
522 if ((iaaddr
->state
<= 0) || (iaaddr
->state
> FTS_LAST
)) {
523 log_fatal("Unknown ia_na state %d at %s:%d",
526 binding_state
= binding_state_names
[iaaddr
->state
-1];
527 if (fprintf(db_file
, " binding state %s;\n",
528 binding_state
) < 0) {
531 tval
= print_time(iaaddr
->valid_lifetime_end_time
);
535 if (fprintf(db_file
, " ends %s", tval
) < 0) {
538 if (fprintf(db_file
, "\n }\n") < 0) goto error_exit
;
540 if (fprintf(db_file
, "}\n\n") < 0) goto error_exit
;
546 log_info("write_ia_na: unable to write ia-na");
547 lease_file_is_corrupt
= 1;
553 * Put a copy of the server DUID in the leases file.
556 write_server_duid(void) {
557 struct data_string server_duid
;
562 * Only write the DUID if it's been set.
564 if (!server_duid_isset()) {
569 * If the lease file is corrupt, don't try to write any more
570 * leases until we've written a good lease file.
572 if (lease_file_is_corrupt
) {
573 if (!new_lease_file()) {
579 * Get a copy of our server DUID and convert to a quoted string.
581 memset(&server_duid
, 0, sizeof(server_duid
));
582 copy_server_duid(&server_duid
, MDL
);
583 s
= quotify_buf(server_duid
.data
, server_duid
.len
, MDL
);
584 data_string_forget(&server_duid
, MDL
);
590 * Write to the leases file.
592 fprintf_ret
= fprintf(db_file
, "server-duid \"%s\";\n\n", s
);
594 if (fprintf_ret
< 0) {
599 * Check if we actually managed to write.
605 log_info("write_server_duid: unable to write server-duid");
606 lease_file_is_corrupt
= 1;
611 #if defined (FAILOVER_PROTOCOL)
612 int write_failover_state (dhcp_failover_state_t
*state
)
618 if (lease_file_is_corrupt
)
619 if (!new_lease_file ())
623 fprintf (db_file
, "\nfailover peer \"%s\" state {", state
-> name
);
627 tval
= print_time(state
->me
.stos
);
629 fprintf(db_file
, "\n my state %s at %s",
630 (state
->me
.state
== startup
) ?
631 dhcp_failover_state_name_print(state
->saved_state
) :
632 dhcp_failover_state_name_print(state
->me
.state
),
636 tval
= print_time(state
->partner
.stos
);
638 fprintf(db_file
, "\n partner state %s at %s",
639 dhcp_failover_state_name_print(state
->partner
.state
),
643 if (state
-> i_am
== secondary
) {
645 fprintf (db_file
, "\n mclt %ld;",
646 (unsigned long)state
-> mclt
);
650 fprintf (db_file
, "\n}\n");
655 log_info ("write_failover_state: unable to write state %s",
657 lease_file_is_corrupt
= 1;
669 for (i
= 0; s
[i
]; i
++)
670 if (!isascii (s
[i
]) || !isprint (s
[i
])
671 || s
[i
] == '"' || s
[i
] == '\\')
676 int db_printable_len (s
, len
)
677 const unsigned char *s
;
682 for (i
= 0; i
< len
; i
++)
683 if (!isascii (s
[i
]) || !isprint (s
[i
]) ||
684 s
[i
] == '"' || s
[i
] == '\\')
689 static int print_hash_string(FILE *fp
, struct class *class)
693 for (i
= 0 ; i
< class->hash_string
.len
; i
++)
694 if (!isascii(class->hash_string
.data
[i
]) ||
695 !isprint(class->hash_string
.data
[i
]))
698 if (i
== class->hash_string
.len
) {
699 if (fprintf(fp
, " \"%.*s\"", (int)class->hash_string
.len
,
700 class->hash_string
.data
) <= 0) {
701 log_error("Failure writing hash string: %m");
705 if (fprintf(fp
, " %2.2x", class->hash_string
.data
[0]) <= 0) {
706 log_error("Failure writing hash string: %m");
709 for (i
= 1 ; i
< class->hash_string
.len
; i
++) {
710 if (fprintf(fp
, ":%2.2x",
711 class->hash_string
.data
[i
]) <= 0) {
712 log_error("Failure writing hash string: %m");
723 write_named_billing_class(const void *key
, unsigned len
, void *object
)
725 const unsigned char *name
= key
;
726 struct class *class = object
;
728 if (class->flags
& CLASS_DECL_DYNAMIC
) {
730 if (class->superclass
== 0) {
731 if (fprintf(db_file
, "class \"%s\" {\n", name
) <= 0)
732 return ISC_R_IOERROR
;
734 if (fprintf(db_file
, "subclass \"%s\"",
735 class->superclass
->name
) <= 0)
736 return ISC_R_IOERROR
;
737 if (!print_hash_string(db_file
, class))
738 return ISC_R_IOERROR
;
739 if (fprintf(db_file
, " {\n") <= 0)
740 return ISC_R_IOERROR
;
743 if ((class->flags
& CLASS_DECL_DELETED
) != 0) {
744 if (fprintf(db_file
, " deleted;\n") <= 0)
745 return ISC_R_IOERROR
;
747 if (fprintf(db_file
, " dynamic;\n") <= 0)
748 return ISC_R_IOERROR
;
751 if (class->lease_limit
> 0) {
752 if (fprintf(db_file
, " lease limit %d;\n",
753 class->lease_limit
) <= 0)
754 return ISC_R_IOERROR
;
757 if (class->expr
!= 0) {
758 if (fprintf(db_file
, " match if ") <= 0)
759 return ISC_R_IOERROR
;
760 write_expression(db_file
, class->expr
, 5, 5, 0);
761 if (fprintf(db_file
, ";\n") <= 0)
762 return ISC_R_IOERROR
;
765 if (class->submatch
!= 0) {
766 if (class->spawning
) {
767 if (fprintf(db_file
, " spawn ") <= 0)
768 return ISC_R_IOERROR
;
770 if (fprintf(db_file
, " match ") <= 0)
771 return ISC_R_IOERROR
;
774 write_expression(db_file
, class->submatch
, 5, 5, 0);
775 if (fprintf(db_file
, ";\n") <= 0)
776 return ISC_R_IOERROR
;
779 if (class->statements
!= 0) {
780 write_statements(db_file
, class->statements
, 8);
783 /* XXXJAB this isn't right, but classes read in off the
784 leases file don't get the root group assigned to them
785 (due to clone_group() call). */
786 if (class->group
!= 0 && class->group
->authoritative
!= 0)
787 write_statements(db_file
, class->group
->statements
, 8);
789 if (fprintf(db_file
, "}\n\n") <= 0)
790 return ISC_R_IOERROR
;
793 if (class->hash
!= NULL
) { /* yep. recursive. god help us. */
794 /* XXX - cannot check error status of this...
795 * foo_hash_foreach returns a count of operations completed.
797 class_hash_foreach(class->hash
, write_named_billing_class
);
800 return ISC_R_SUCCESS
;
803 void write_billing_classes ()
805 struct collection
*lp
;
807 struct hash_bucket
*bp
;
810 for (lp
= collections
; lp
; lp
= lp
-> next
) {
811 for (cp
= lp
-> classes
; cp
; cp
= cp
-> nic
) {
812 if (cp
-> spawning
&& cp
-> hash
) {
813 class_hash_foreach (cp
-> hash
, write_named_billing_class
);
819 /* Write a spawned class to the database file. */
821 int write_billing_class (class)
827 if (lease_file_is_corrupt
)
828 if (!new_lease_file ())
831 if (!class -> superclass
) {
833 fprintf (db_file
, "\n billing class \"%s\";", class -> name
);
838 fprintf (db_file
, "\n billing subclass \"%s\"",
839 class -> superclass
-> name
);
843 print_hash_string(db_file
, class);
844 fprintf(db_file
, ";");
846 class -> dirty
= !errors
;
848 lease_file_is_corrupt
= 1;
852 /* Commit leases after a timeout. */
853 void commit_leases_timeout (void *foo
)
858 /* Commit any leases that have been written out... */
862 /* Commit any outstanding writes to the lease database file.
863 We need to do this even if we're rewriting the file below,
864 just in case the rewrite fails. */
865 if (fflush (db_file
) == EOF
) {
866 log_info ("commit_leases: unable to commit: %m");
869 if (fsync (fileno (db_file
)) < 0) {
870 log_info ("commit_leases: unable to commit: %m");
874 /* If we haven't rewritten the lease database in over an
875 hour, rewrite it now. (The length of time should probably
877 if (count
&& cur_time
- write_time
> 3600) {
879 write_time
= cur_time
;
885 void db_startup (testp
)
890 #if defined (TRACING)
891 if (!trace_playback ()) {
893 /* Read in the existing lease file... */
894 status
= read_conf_file (path_dhcpd_db
,
895 (struct group
*)0, 0, 1);
896 /* XXX ignore status? */
897 #if defined (TRACING)
901 #if defined (TRACING)
902 /* If we're playing back, there is no lease file, so we can't
903 append it, so we create one immediately (maybe this isn't
904 the best solution... */
905 if (trace_playback ()) {
910 db_file
= fopen (path_dhcpd_db
, "a");
912 log_fatal ("Can't open %s for append.", path_dhcpd_db
);
914 #if defined (TRACING)
915 if (trace_playback ())
916 write_time
= cur_time
;
923 #if defined(REPORT_HASH_PERFORMANCE)
924 log_info("Host HW hash: %s", host_hash_report(host_hw_addr_hash
));
925 log_info("Host UID hash: %s", host_hash_report(host_uid_hash
));
926 log_info("Lease IP hash: %s",
927 lease_ip_hash_report(lease_ip_addr_hash
));
928 log_info("Lease UID hash: %s", lease_id_hash_report(lease_uid_hash
));
929 log_info("Lease HW hash: %s",
930 lease_id_hash_report(lease_hw_addr_hash
));
934 int new_lease_file ()
937 char backfname
[512];
943 /* Make a temporary lease file... */
946 db_validity
= lease_file_is_corrupt
;
948 /* %Audit% Truncated filename causes panic. %2004.06.17,Safe%
949 * This should never happen since the path is a configuration
950 * variable from build-time or command-line. But if it should,
951 * either by malice or ignorance, we panic, since the potential
954 if (snprintf (newfname
, sizeof newfname
, "%s.%d",
955 path_dhcpd_db
, (int)t
) >= sizeof newfname
)
956 log_fatal("new_lease_file: lease file path too long");
958 db_fd
= open (newfname
, O_WRONLY
| O_TRUNC
| O_CREAT
, 0664);
960 log_error ("Can't create new lease file: %m");
963 if ((new_db_file
= fdopen(db_fd
, "w")) == NULL
) {
964 log_error("Can't fdopen new lease file: %m");
969 /* Close previous database, if any. */
972 db_file
= new_db_file
;
975 fprintf (db_file
, "# The format of this file is documented in the %s",
976 "dhcpd.leases(5) manual page.\n");
979 fprintf (db_file
, "# This lease file was written by isc-dhcp-%s\n\n",
984 /* At this point we have a new lease file that, so far, could not
985 * be described as either corrupt nor valid.
987 lease_file_is_corrupt
= 0;
989 /* Write out all the leases that we know of... */
991 if (!write_leases ())
994 #if defined (TRACING)
995 if (!trace_playback ()) {
997 /* %Audit% Truncated filename causes panic. %2004.06.17,Safe%
998 * This should never happen since the path is a configuration
999 * variable from build-time or command-line. But if it should,
1000 * either by malice or ignorance, we panic, since the potential
1001 * for havoc is too high.
1003 if (snprintf (backfname
, sizeof backfname
, "%s~", path_dhcpd_db
)
1004 >= sizeof backfname
)
1005 log_fatal("new_lease_file: backup lease file path too long");
1007 /* Get the old database out of the way... */
1008 if (unlink (backfname
) < 0 && errno
!= ENOENT
) {
1009 log_error ("Can't remove old lease database backup %s: %m",
1013 if (link(path_dhcpd_db
, backfname
) < 0) {
1014 if (errno
== ENOENT
) {
1015 log_error("%s is missing - no lease db to backup.",
1018 log_error("Can't backup lease database %s to %s: %m",
1019 path_dhcpd_db
, backfname
);
1023 #if defined (TRACING)
1027 /* Move in the new file... */
1028 if (rename (newfname
, path_dhcpd_db
) < 0) {
1029 log_error ("Can't install new lease database %s to %s: %m",
1030 newfname
, path_dhcpd_db
);
1038 lease_file_is_corrupt
= db_validity
;
1044 int group_writer (struct group_object
*group
)
1046 if (!write_group (group
))
1048 if (!commit_leases ())