1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! The application's purge of an Audit Log (#1238).
//!
//! No peer can purge an Audit Log (Clause 12.64.11 keeps its Record_Count
//! read-only), so the server offers the purge to the application. It is
//! staged like a durable write ([`super::durable_writes`]): the commit runs
//! with the database guard dropped, and the log serves the purged state only
//! once storage holds it.
use super::*;
impl<T: TransportPort + 'static> BACnetServer<T> {
/// Purge an Audit Log: clear its records and append a BUFFER_PURGED
/// status record, as [`AuditLogObject::purge`] describes.
///
/// Peers have no way to do this, so it is the application's to call.
/// The log builds the purged state and queues its commit; the server
/// waits for the commit with the object database guard dropped, so other
/// requests carry on, and the log serves the purged state only once it is
/// durable. Changes to one log land one at a time, in the order they
/// were staged: a notification batch whose commit is running lands first
/// and is purged with the rest, and one that arrives while the purge
/// commits waits for it and follows the purge record.
///
/// # Errors
///
/// - OBJECT / UNKNOWN_OBJECT when `oid` names no object, and OBJECT /
/// OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED when the object cannot be
/// purged (it is not a built-in Audit Log).
/// - DEVICE / OPERATIONAL_PROBLEM without a valid local clock, or when
/// the commit fails; the log's writer logs the storage error.
/// - The stopped-server error once [`stop`](Self::stop) has run.
///
/// On any error the log is left as it was. A purge whose future is
/// dropped before it finishes is handled as an abandoned write request
/// is: the log keeps serving its state, drops the staged purge once it
/// has waited long enough, and sets storage back to that state.
///
/// [`AuditLogObject::purge`]: bacnet_objects::audit::AuditLogObject::purge
pub async fn purge_audit_log(&self, oid: &ObjectIdentifier) -> Result<(), Error> {
self.active_network()?;
let staged =
durable_writes::stage(&self.db, durable_writes::DurableTarget::purge(*oid)).await;
let mut db = self.db.write().await;
let result = match db.get_mut(oid) {
None => Err(protocol_error(
ErrorClass::OBJECT,
ErrorCode::UNKNOWN_OBJECT,
)),
Some(object) => match object.durable_writes_internal() {
Some(writes) => writes.commit_purge(),
None => Err(protocol_error(
ErrorClass::OBJECT,
ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
)),
},
};
staged.release(&mut db);
result
}
}
fn protocol_error(class: ErrorClass, code: ErrorCode) -> Error {
Error::protocol(class, code)
}
#[cfg(test)]
#[path = "audit_log_purge_tests.rs"]
mod tests;