Skip to main content
Version: 0.9.0 (next)

onboarding_and_rejoin.c File

Obtains a session via rejoin-or-onboard, onboards a sibling runtime from a shared invite, and demonstrates destroy/forget teardown. More...

Included Headers​

#include "common.h" #include <qar_streaming.h>

Functions Index​

static QarRuntime *create_runtime_or_null (const char *runtime_dir, const char *storage_dir)
intmain (int argc, char **argv)
static voidprint_usage (const char *program_name)

Description​

Obtains a session via rejoin-or-onboard, onboards a sibling runtime from a shared invite, and demonstrates destroy/forget teardown.

Functions​

create_runtime_or_null()​

QarRuntime * create_runtime_or_null (const char * runtime_dir, const char * storage_dir)
static

Definition at line 89 of file onboarding_and_rejoin.c.

main()​

int main (int argc, char ** argv)

Definition at line 107 of file onboarding_and_rejoin.c.

print_usage()​

void print_usage (const char * program_name)
static

Definition at line 73 of file onboarding_and_rejoin.c.

File Listing​

The file content with the documentation metadata removed is:

1/** \file onboarding_and_rejoin.c
2 * \brief Obtains a session via rejoin-or-onboard, onboards a sibling runtime
3 * from a shared invite, and demonstrates destroy/forget teardown.
4 * \example onboarding_and_rejoin.c
5 */
6
7/** \page qar_c_tutorial_onboarding Onboarding and Rejoin
8 * \tableofcontents
9 *
10 * \section onboarding_overview Overview
11 * Every QarOS session is obtained through onboarding - there is no direct
12 * create/join. This tutorial shows the canonical startup flow:
13 * - Reuse dynamic loading from the previous tutorial
14 * - Try a silent rejoin with the persisted onboarding id
15 * - Fall back to onboarding with the pairing code shown by the hub
16 * - Persist the returned onboarding id for the next run
17 * - Mint a serialized invite for a sibling API instance
18 * - Start a second runtime instance and onboard it from that invite
19 * - Tear down by destroying the session handle (and optionally forget)
20 *
21 * \section onboarding_prereq Prerequisites
22 * - Complete the \ref qar_c_tutorial_dynamic_loading tutorial
23 * - A running QarOS hub on this machine (or reachable on the network)
24 * - For the first run: the pairing code from the hub's onboarding screen
25 *
26 * \section onboarding_build Build and Run
27 * \code{.bash}
28 * cmake --build --preset x64-windows-debug --target onboarding_and_rejoin
29 * ./build/x64-windows/Debug/onboarding_and_rejoin.exe
30 * <path-to-qar-streaming-c.dll> [runtime-dir] [pairing-code] [--forget]
31 * \endcode
32 *
33 * \section onboarding_args Parse Arguments
34 * \snippet onboarding_and_rejoin.c onboarding_args
35 *
36 * \section onboarding_persistence Persisting the Onboarding Id
37 * The onboarding id is the only thing an application must store. These
38 * shared helpers keep it in a small text file as a UUID string:
39 * \snippet common.h example_onboarding_persistence
40 *
41 * \section onboarding_obtain Rejoin or Onboard
42 * The canonical flow - rejoin silently when a persisted id exists, otherwise
43 * onboard with the code and persist the returned id:
44 * \snippet common.h example_obtain_session
45 *
46 * Which is used from main like this:
47 * \snippet onboarding_and_rejoin.c onboarding_obtain
48 *
49 * \section onboarding_invite Share an Invite with a Second API Instance
50 * A second API instance in the same application (another process, or a C#
51 * binding next to native code) must not pair with a code again. The primary
52 * onboarded instance requests an invite, serializes it, and the sibling
53 * instance consumes it with qar_runtime_onboard:
54 * \snippet onboarding_and_rejoin.c onboarding_invite
55 *
56 * \section onboarding_teardown Destroy and Forget
57 * Destroying the session handle keeps the persisted identity so the next run
58 * can rejoin. Forget erases it - only do that for an explicit "un-pair this
59 * device" action:
60 * \snippet onboarding_and_rejoin.c onboarding_teardown
61 */
62
63#include "common.h"
64
65#ifndef QAR_ENABLE_DYNAMIC_LOADING
66#define QAR_ENABLE_DYNAMIC_LOADING
67#endif
68#include <qar_streaming.h>
69
71
72static void
73print_usage(const char* program_name)
74{
75 const char* name = program_name ? program_name : "onboarding_and_rejoin";
76 printf(
77 "Usage: %s <path-to-qar-streaming-c-library> [runtime-binaries-dir] "
78 "[pairing-code] [--forget]\n",
79 name
80 );
81 printf(
82 "The pairing code is required on the first run only; later runs "
83 "rejoin with the persisted onboarding id.\n"
84 );
85 printf("Pass --forget to erase the persisted identity on exit.\n");
86}
87
88static QarRuntime*
89create_runtime_or_null(const char* runtime_dir, const char* storage_dir)
90{
91 QarRuntime* runtime = NULL;
93 runtime_init.runtime_binaries_folder_path = runtime_dir;
94 runtime_init.storage_folder_path = storage_dir;
95
96 QarResult runtime_result = qar_runtime_create(&runtime_init, &runtime);
97 log_result("qar_runtime_create", runtime_result);
98 if(qar_result_is_error(runtime_result) || runtime == NULL)
99 {
100 return NULL;
101 }
102
103 return runtime;
104}
105
106int
107main(int argc, char** argv)
108{
109 //! [onboarding_args]
110 if(argc < 2)
111 {
112 print_usage(argv[0]);
113 return 1;
114 }
115
116 const char* library_path = argv[1];
117 const char* runtime_dir = NULL;
118 char runtime_dir_buffer[1024] = { 0 };
119 const char* pairing_code = NULL;
120 bool forget_on_exit = false;
121
122 if(argc >= 3)
123 {
124 runtime_dir = argv[2];
125 }
126 else if(get_dir_from_path(library_path, runtime_dir_buffer, sizeof(runtime_dir_buffer)))
127 {
128 runtime_dir = runtime_dir_buffer;
129 }
130
131 for(int i = 3; i < argc; ++i)
132 {
133 if(strcmp(argv[i], "--forget") == 0)
134 {
135 forget_on_exit = true;
136 }
137 else
138 {
139 pairing_code = argv[i];
140 }
141 }
142 //! [onboarding_args]
143
144 if(!qar_library_load(library_path))
145 {
146 fprintf(stderr, "Failed to load '%s'.\n", library_path);
147 return 2;
148 }
149
151 library_init.enable_console_logging = true;
152 QarResult library_result = qar_library_init(&library_init);
153 if(qar_result_is_error(library_result))
154 {
155 log_result("qar_library_init", library_result);
157 return 3;
158 }
159
160 QarRuntime* primary_runtime =
161 create_runtime_or_null(runtime_dir, "onboarding_and_rejoin.primary-state");
162 if(primary_runtime == NULL)
163 {
166 return 4;
167 }
168
169 //! [onboarding_obtain]
170 QarOnboardingId primary_onboarding_id = qar_onboarding_id_default();
171 QarOnboardingId sibling_onboarding_id = qar_onboarding_id_default();
172 bool sibling_onboarded = false;
173 QarSession* primary_session = NULL;
175 primary_runtime,
176 pairing_code,
177 "onboarding_and_rejoin.primary-onboarding-id.txt",
178 "Primary Tutorial Peer",
179 &primary_onboarding_id,
180 &primary_session
181 ) != 0
182 || primary_session == NULL)
183 {
184 qar_runtime_destroy(primary_runtime);
187 return 5;
188 }
189
190 char primary_id_text[64] = { 0 };
192 qar_uuid_to_string(primary_onboarding_id.data, primary_id_text, sizeof(primary_id_text))
193 ))
194 {
195 printf("Primary session obtained with onboarding id %s\n", primary_id_text);
196 }
197 //! [onboarding_obtain]
198
199 //! [onboarding_invite]
201 QarOnboardingInvite* primary_invite = NULL;
203 primary_session, &request_init, NULL, NULL, NULL, &primary_invite
204 );
205 log_result("qar_session_request_onboarding_invite", invite_result);
206 if(qar_result_is_success(invite_result) && primary_invite != NULL)
207 {
208 size_t serialized_size = 0;
209 QAR_ASSUME_SUCCESS(qar_onboarding_invite_serialized_size(primary_invite, &serialized_size));
210
211 uint8_t* blob = (uint8_t*)calloc(1, serialized_size);
212 if(blob != NULL)
213 {
214 size_t bytes_written = 0;
216 primary_invite, blob, serialized_size, &bytes_written
217 ));
218
219 int64_t expires_unix = 0;
221 qar_onboarding_invite_get_expires_unix(primary_invite, &expires_unix)
222 );
223 printf(
224 "Minted sibling invite (%zu bytes, expires at unix %lld).\n",
225 bytes_written,
226 (long long)expires_unix
227 );
228
229 QarRuntime* sibling_runtime =
230 create_runtime_or_null(runtime_dir, "onboarding_and_rejoin.sibling-state");
231 if(sibling_runtime != NULL)
232 {
233 QarOnboardingInvite* sibling_invite = NULL;
234 QarResult deserialize_result =
235 qar_onboarding_invite_deserialize(blob, bytes_written, &sibling_invite);
236 log_result("qar_onboarding_invite_deserialize", deserialize_result);
237 if(qar_result_is_success(deserialize_result) && sibling_invite != NULL)
238 {
241 sibling_init.presentation.display_name = "Sibling Tutorial Peer";
242 sibling_mode.invite = sibling_invite;
243 sibling_init.header.next = &sibling_mode.header;
244
245 /* Fills the outer sibling_onboarding_id: the forget at the
246 * end of the example needs it. */
247 QarSession* sibling_session = NULL;
248 QarResult sibling_result = qar_runtime_onboard(
249 sibling_runtime,
250 &sibling_init,
251 NULL,
252 NULL,
253 NULL,
254 &sibling_onboarding_id,
255 &sibling_session
256 );
257 log_result("qar_runtime_onboard", sibling_result);
258 if(qar_result_is_success(sibling_result) && sibling_session != NULL)
259 {
260 char sibling_id_text[64] = { 0 };
262 sibling_onboarding_id.data, sibling_id_text, sizeof(sibling_id_text)
263 )))
264 {
265 printf(
266 "Sibling runtime onboarded with onboarding id "
267 "%s\n",
268 sibling_id_text
269 );
270 }
271
273 "onboarding_and_rejoin.sibling-onboarding-id.txt",
274 &sibling_onboarding_id
275 );
276 sibling_onboarded = true;
277 printf(
278 "Destroying sibling session handle keeps its "
279 "persisted onboarding state for rejoin.\n"
280 );
281 qar_session_handle_destroy(sibling_session);
282 }
283
285 }
286
287 qar_runtime_destroy(sibling_runtime);
288 }
289
290 free(blob);
291 }
292
294 }
295 //! [onboarding_invite]
296
297 //! [onboarding_teardown]
298 /* Destroying the handle leaves the session and keeps persisted identity,
299 * so the next run can rejoin silently. */
300 printf(
301 "Destroying primary session handle keeps its persisted onboarding "
302 "state for rejoin.\n"
303 );
304 qar_session_handle_destroy(primary_session);
305
306 if(forget_on_exit)
307 {
308 /* Full un-pair: erase the identity slot. The next run needs a fresh
309 * pairing code. Must happen after the session was left. */
310 if(sibling_onboarded)
311 {
312 QarRuntime* sibling_runtime =
313 create_runtime_or_null(runtime_dir, "onboarding_and_rejoin.sibling-state");
314 if(sibling_runtime != NULL)
315 {
316 QarForgetInit sibling_forget_init = qar_forget_init_default();
317 sibling_forget_init.onboarding_id = sibling_onboarding_id;
319 "qar_runtime_forget(sibling)",
320 qar_runtime_forget(sibling_runtime, &sibling_forget_init)
321 );
322 qar_runtime_destroy(sibling_runtime);
323 }
324 remove("onboarding_and_rejoin.sibling-onboarding-id.txt");
325 }
326
328 forget_init.onboarding_id = primary_onboarding_id;
330 "qar_runtime_forget(primary)", qar_runtime_forget(primary_runtime, &forget_init)
331 );
332 remove("onboarding_and_rejoin.primary-onboarding-id.txt");
333 }
334 //! [onboarding_teardown]
335
336 qar_runtime_destroy(primary_runtime);
337 QarResult destroy_result = qar_library_destroy();
338 log_result("qar_library_destroy", destroy_result);
340 return 0;
341}

Generated via doxygen2docusaurus 2.2.1 by Doxygen 1.9.8.