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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//! Mandatory built-in policy changes share the existing immediate admission owner.
use super::*;
use bacnet_types::primitives::BACnetTimeStamp;
fn denied() -> Error {
Error::protocol(ErrorClass::SERVICES, ErrorCode::SERVICE_REQUEST_DENIED)
}
impl<T: TransportPort + 'static> WriteAudit<'_, T> {
pub(super) fn commit_mandatory_policy(
&mut self,
db: &mut ObjectDatabase,
write: WriteTarget<'_>,
value: &PropertyValue,
) -> Option<Result<(), Error>> {
self.pending.as_ref()?.selection.as_ref()?.change?;
// Read the clock and obtain the concrete capability before admission:
// neither custom object nor clock callbacks execute inside commit locks.
let clock = db
.clock_frame()
.filter(|frame| frame.is_valid_actual_datetime());
let sequence = db.event_sequence_internal();
let candidate = db
.get_mut(&write.oid)?
.audit_policy_authority_internal()?
.prepare(write.property, write.array_index, value, write.priority)
.ok()??;
// Invalid/equal/NULL attempts retain the ordinary writer and observer.
// An actual mandatory change exclusively owns its result from here on.
let mut pending = self.pending.take().expect("selected mandatory change");
let owner = self.transactions.audit_owner_lease();
let commit = |number| {
self.transactions
.commit_audit(|| {
if !owner.as_ref().is_some_and(|owner| owner.is_active()) {
return Err(denied());
}
let route = pending.route.take().ok_or_else(denied)?;
let permit = self.transactions.try_admit_audit().map_err(|_| denied())?;
let reserved = if pending.confirmed {
Some(
self.transactions
.reserve(
route.canonical_peer.clone(),
ConfirmedServiceChoice::CONFIRMED_AUDIT_NOTIFICATION,
)
.map_err(|_| denied())?,
)
} else {
None
};
pending.notification.target_timestamp = Some(match clock {
Some(frame) => BACnetTimeStamp::DateTime {
date: frame.local_date,
time: frame.local_time,
},
None => BACnetTimeStamp::SequenceNumber(number),
});
let invoke = reserved
.as_ref()
.map_or(0, |(operation, _)| operation.invoke_id());
let bytes = encode_notification(
&pending.notification,
pending.confirmed,
u32::from(self.config.device.max_apdu_length()),
invoke,
)
.ok_or_else(denied)?;
let network = Arc::clone(self.network);
let deadline = tokio::time::Instant::now() + DELIVERY_TIMEOUT;
let completion = DeliveryCompletion {
status: pending.status,
epoch: pending.completion,
finished: false,
};
// All fallible preparation is complete. This sealed assignment
// cannot call arbitrary object code, allocate or return an error.
candidate.commit();
Ok(async move {
let _permit = permit;
completion
.finish(deliver(&network, &route, &bytes, reserved, deadline).await);
})
})
.map_err(|_| denied())
};
Some(if clock.is_some() {
commit(0)
} else {
sequence.transaction(commit)
})
}
}