xref: /netbsd-src/external/apache2/mDNSResponder/dist/ServiceRegistration/state-machine.h (revision 32d1c65c71fbdb65a012e8392a62a757dd6853e9)
1 /* state-machine.h
2  *
3  * Copyright (c) 2023-2024 Apple Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     https://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * This file contains general support definitions for state machines in the Thread Border Router
18  * implementation.
19  */
20 
21 #ifndef __STATE_MACHINE_H__
22 #define __STATE_MACHINE_H__ 1
23 
24 #define RELEASE_RETAIN_FUNCS(type)                                              \
25 void                                                                            \
26 type##_retain_(type##_t * omw, const char *file, int line)                      \
27 {                                                                               \
28     RETAIN(omw, type);                                                          \
29 }                                                                               \
30                                                                                 \
31 void                                                                            \
32 type##_release_(type##_t *NONNULL omw, const char *file, int line)              \
33 {                                                                               \
34     RELEASE(omw, type);                                                         \
35 }
36 
37 #define RELEASE_RETAIN_DECLS(type)                                              \
38 void type##_retain_(type##_t *NONNULL omw, const char *NONNULL file, int line); \
39 void type##_release_(type##_t *NONNULL omw, const char *NONNULL file, int line);
40 
41 // The assumptions below are that every object that holds a state that these macros can operate on has
42 // the following elements:
43 //
44 // name: (char *), NUL terminated, name of object instance
45 // state_name: (const char *), NUL terminated, name of the state the object is in
46 // state: the current state of the state machine, as an enum
47 //
48 // For macros that take events, the event is assumed to have the following elements:
49 //
50 // name: (char *), NUL terminated, name of event type (iow, not specific to an event instance)
51 
52 // For states that never receive events.
53 #define BR_REQUIRE_STATE_OBJECT_EVENT_NULL(state_object, event)                                                        \
54     do {                                                                                                               \
55         if ((event) != NULL) {                                                                                         \
56             ERROR(PUB_S_SRP "/" PRI_S_SRP ": received unexpected " PUB_S_SRP " event in state " PUB_S_SRP,             \
57                 state_object->state_header.state_machine_type_name,                                                    \
58                 state_object->state_header.name, event->name, state_object->state_header.state_name);                  \
59             return state_machine_state_invalid;                                                       	               \
60         }                                                                                    	                       \
61     } while (false)
62 
63 // Announce that we have entered a state that takes no events
64 #define BR_STATE_ANNOUNCE_NO_EVENTS(state_object)                                                                      \
65     do {																							                   \
66         INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP, state_object->state_header.name,                   \
67              state_object->state_header.state_machine_type_name,                                                       \
68              state_object->state_header.state_name);                                                                   \
69     } while (false)
70 
71 // Announce that we have entered a state that takes no events, and include a domain name
72 #define BR_STATE_ANNOUNCE_NO_EVENTS_NAME(state_object, fqdn)                                                           \
73     do {                                                                                                               \
74         char hostname[kDNSServiceMaxDomainName];                                                                       \
75         dns_name_print(fqdn, hostname, sizeof(hostname));                                                              \
76         INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP " with host " PRI_S_SRP,                            \
77             state_object->state_header.state_machine_type_name,                                                        \
78             state_object->state_header.name, state_object->state_header.state_name, hostname);                         \
79     } while (false)
80 
81 // Announce that we have entered a state that takes no events
82 #define BR_STATE_ANNOUNCE(state_object, event)                                                                         \
83     do {							 															                       \
84         if (event != NULL)  {                                                                                          \
85             INFO(PUB_S_SRP "/" PRI_S_SRP ": event " PUB_S_SRP " received in state " PUB_S_SRP,                         \
86                  state_object->state_header.state_machine_type_name,                                                   \
87                  state_object->state_header.name, event->name, state_object->state_header.state_name);                 \
88         } else {                                                                                                       \
89             INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP,                                                \
90                  state_object->state_header.state_machine_type_name,                                                   \
91                  state_object->state_header.name, state_object->state_header.state_name);                              \
92         }                                                                                                              \
93     } while (false)
94 
95 #define BR_UNEXPECTED_EVENT_MAIN(state_object, event, bad, event_is_message)                                           \
96     do {                                                                                                               \
97         if (event_is_message(event)) {                                                                                 \
98             INFO(PUB_S_SRP "/" PRI_S_SRP ": invalid event " PUB_S_SRP " in state " PUB_S_SRP,                          \
99                  state_object->state_header.state_machine_type_name,                                                   \
100                  (state_object)->state_header.name, (event)->name, state_object->state_header.state_name);             \
101             return (int)bad;														                                   \
102         }                                                                                                              \
103         INFO(PUB_S_SRP "/" PRI_S_SRP ": unexpected event " PUB_S_SRP " in state " PUB_S_SRP,                           \
104              state_object->state_header.state_machine_type_name,                                                       \
105              (state_object)->state_header.name, (event)->name,                                                         \
106              state_object->state_header.state_name);                                                                   \
107         return (int)state_machine_state_invalid;                                                                       \
108     } while (false)
109 
110 // UNEXPECTED_EVENT flags the response as bad on a protocol level, triggering a retry delay
111 // UNEXPECTED_EVENT_NO_ERROR doesn't.
112 #define BR_UNEXPECTED_EVENT(state_object, event)                                                                       \
113     BR_UNEXPECTED_EVENT_MAIN(state_object, event, state_machine_state_invalid,                                         \
114                                                                           state_machine_event_is_message)
115 #define BR_UNEXPECTED_EVENT_NO_ERROR(state_object, event)                                                              \
116     BR_UNEXPECTED_EVENT_MAIN(state_object, event, state_object_drop_state(state_object->instance, state_object),       \
117                           state_machine_event_is_message)
118 
119 // Generalized border router event object
120 
121 #define state_machine_event_is_message(x) false
122 
123 typedef enum {
124     state_machine_event_type_invalid,
125     state_machine_event_type_timeout,
126     state_machine_event_type_prefix,
127     state_machine_event_type_dhcp,
128     state_machine_event_type_service_list_changed,
129     state_machine_event_type_listener_ready,
130     state_machine_event_type_listener_canceled,
131     state_machine_event_type_ml_eid_changed,
132     state_machine_event_type_rloc_changed,
133     state_machine_event_type_thread_network_state_changed,
134     state_machine_event_type_thread_node_type_changed,
135     state_machine_event_type_probe_completed,
136     state_machine_event_type_got_mesh_local_prefix,
137     state_machine_event_type_daemon_disconnect,
138     state_machine_event_type_stop,
139     state_machine_event_type_dns_registration_invalidated,
140     state_machine_event_type_thread_interface_changed,
141     state_machine_event_type_wed_ml_eid_changed,
142     state_machine_event_type_neighbor_ml_eid_changed,
143     state_machine_event_type_srp_needed,
144     state_machine_event_type_dns_registration_bad_service,
145 } state_machine_event_type_t;
146 
147 typedef struct state_machine_event state_machine_event_t;
148 typedef struct state_machine_header state_machine_header_t;
149 typedef void (*state_machine_event_finalize_callback_t)(state_machine_event_t *NONNULL event);
150 typedef struct omr_prefix omr_prefix_t;
151 struct state_machine_event {
152 	int ref_count;
153 	const char *NULLABLE name;
154     state_machine_event_type_t type;
155     omr_prefix_t *NULLABLE thread_prefixes;
156     state_machine_event_finalize_callback_t NULLABLE finalize;
157 };
158 
159 #ifndef STATE_MACHINE_IMPLEMENTATION
160 typedef enum state_machine_state {
161 	state_machine_state_invalid = 0,
162 } state_machine_state_t;
163 #endif // STATE_MACHINE_IMPLEMENTATION
164 
165 typedef state_machine_state_t (*state_machine_action_t)(state_machine_header_t *NONNULL state_header, state_machine_event_t *NULLABLE event);
166 typedef struct state_machine_state_decl {
167     state_machine_state_t state;
168     const char *NONNULL name;
169     state_machine_action_t NONNULL action;
170 } state_machine_decl_t;
171 
172 typedef enum {
173     state_machine_type_invalid,
174     state_machine_type_omr_publisher,
175     state_machine_type_service_publisher,
176     state_machine_type_dnssd_client,
177 } state_machine_type_t;
178 
179 struct state_machine_header {
180 	char *NULLABLE name;
181     void *NULLABLE state_object;
182 	const char *NULLABLE state_name;
183     state_machine_decl_t *NULLABLE states;
184     const char *NULLABLE state_machine_type_name;
185     size_t num_states;
186 	state_machine_state_t state;
187     state_machine_type_t state_machine_type;
188     bool once;
189 };
190 
191 void state_machine_next_state(state_machine_header_t *NONNULL state_header, state_machine_state_t state);
192 void state_machine_event_finalize(state_machine_event_t *NONNULL event);
193 RELEASE_RETAIN_DECLS(state_machine_event);
194 state_machine_event_t *NULLABLE
195 state_machine_event_create(state_machine_event_type_t type,
196                            state_machine_event_finalize_callback_t NULLABLE finalize_callback);
197 void state_machine_event_deliver(state_machine_header_t *NONNULL state_header, state_machine_event_t *NONNULL event);
198 bool state_machine_header_setup(state_machine_header_t *NONNULL state_header, void *NONNULL state_object, const char *NULLABLE name,
199                                 state_machine_type_t type, state_machine_decl_t *NONNULL states, size_t num_states);
200 void state_machine_cancel(state_machine_header_t *NONNULL state_header);
201 #endif // __STATE_MACHINE_H__
202 
203 // Local Variables:
204 // mode: C
205 // tab-width: 4
206 // c-file-style: "bsd"
207 // c-basic-offset: 4
208 // fill-column: 120
209 // indent-tabs-mode: nil
210 // End:
211