summaryrefslogtreecommitdiff
path: root/src/testing/gnunet-testing.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testing/gnunet-testing.c')
-rw-r--r--src/testing/gnunet-testing.c470
1 files changed, 237 insertions, 233 deletions
diff --git a/src/testing/gnunet-testing.c b/src/testing/gnunet-testing.c
index bc402227c..834cf6cea 100644
--- a/src/testing/gnunet-testing.c
+++ b/src/testing/gnunet-testing.c
@@ -11,12 +11,12 @@
11 WITHOUT ANY WARRANTY; without even the implied warranty of 11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details. 13 Affero General Public License for more details.
14 14
15 You should have received a copy of the GNU Affero General Public License 15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 17
18 SPDX-License-Identifier: AGPL3.0-or-later 18 SPDX-License-Identifier: AGPL3.0-or-later
19*/ 19 */
20 20
21/** 21/**
22 * @file testing/gnunet-testing.c 22 * @file testing/gnunet-testing.c
@@ -28,7 +28,7 @@
28#include "gnunet_testing_lib.h" 28#include "gnunet_testing_lib.h"
29 29
30 30
31#define LOG(kind, ...) GNUNET_log_from (kind, "gnunet-testing", __VA_ARGS__) 31#define LOG(kind, ...) GNUNET_log_from(kind, "gnunet-testing", __VA_ARGS__)
32 32
33 33
34/** 34/**
@@ -84,7 +84,7 @@ static struct GNUNET_TESTING_Peer *my_peer;
84 84
85 85
86static int 86static int
87create_unique_cfgs (const char *template, const unsigned int no) 87create_unique_cfgs(const char *template, const unsigned int no)
88{ 88{
89 struct GNUNET_TESTING_System *system; 89 struct GNUNET_TESTING_System *system;
90 int fail; 90 int fail;
@@ -93,76 +93,76 @@ create_unique_cfgs (const char *template, const unsigned int no)
93 struct GNUNET_CONFIGURATION_Handle *cfg_new; 93 struct GNUNET_CONFIGURATION_Handle *cfg_new;
94 struct GNUNET_CONFIGURATION_Handle *cfg_tmpl; 94 struct GNUNET_CONFIGURATION_Handle *cfg_tmpl;
95 95
96 if (GNUNET_NO == GNUNET_DISK_file_test (template)) 96 if (GNUNET_NO == GNUNET_DISK_file_test(template))
97 { 97 {
98 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 98 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
99 "Configuration template `%s': file not found\n", 99 "Configuration template `%s': file not found\n",
100 create_cfg_template); 100 create_cfg_template);
101 return 1; 101 return 1;
102 } 102 }
103 cfg_tmpl = GNUNET_CONFIGURATION_create (); 103 cfg_tmpl = GNUNET_CONFIGURATION_create();
104 104
105 /* load template */ 105 /* load template */
106 if ((create_cfg_template != NULL) && 106 if ((create_cfg_template != NULL) &&
107 (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg_tmpl, create_cfg_template))) 107 (GNUNET_OK != GNUNET_CONFIGURATION_load(cfg_tmpl, create_cfg_template)))
108 { 108 {
109 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 109 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
110 "Could not load template `%s'\n", 110 "Could not load template `%s'\n",
111 create_cfg_template); 111 create_cfg_template);
112 GNUNET_CONFIGURATION_destroy (cfg_tmpl); 112 GNUNET_CONFIGURATION_destroy(cfg_tmpl);
113 113
114 return 1; 114 return 1;
115 } 115 }
116 /* load defaults */ 116 /* load defaults */
117 if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg_tmpl, NULL)) 117 if (GNUNET_OK != GNUNET_CONFIGURATION_load(cfg_tmpl, NULL))
118 { 118 {
119 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 119 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
120 "Could not load template `%s'\n", 120 "Could not load template `%s'\n",
121 create_cfg_template); 121 create_cfg_template);
122 GNUNET_CONFIGURATION_destroy (cfg_tmpl); 122 GNUNET_CONFIGURATION_destroy(cfg_tmpl);
123 return 1; 123 return 1;
124 } 124 }
125 125
126 fail = GNUNET_NO; 126 fail = GNUNET_NO;
127 system = 127 system =
128 GNUNET_TESTING_system_create ("testing", NULL /* controller */, NULL, NULL); 128 GNUNET_TESTING_system_create("testing", NULL /* controller */, NULL, NULL);
129 for (cur = 0; cur < no; cur++) 129 for (cur = 0; cur < no; cur++)
130 {
131 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
132 "Creating configuration no. %u \n",
133 cur);
134 if (create_cfg_template != NULL)
135 GNUNET_asprintf (&cur_file, "%04u-%s", cur, create_cfg_template);
136 else
137 GNUNET_asprintf (&cur_file, "%04u%s", cur, ".conf");
138
139 cfg_new = GNUNET_CONFIGURATION_dup (cfg_tmpl);
140 if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg_new))
141 { 130 {
142 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 131 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
143 "Could not create another configuration\n"); 132 "Creating configuration no. %u \n",
144 GNUNET_CONFIGURATION_destroy (cfg_new); 133 cur);
145 fail = GNUNET_YES; 134 if (create_cfg_template != NULL)
146 break; 135 GNUNET_asprintf(&cur_file, "%04u-%s", cur, create_cfg_template);
136 else
137 GNUNET_asprintf(&cur_file, "%04u%s", cur, ".conf");
138
139 cfg_new = GNUNET_CONFIGURATION_dup(cfg_tmpl);
140 if (GNUNET_OK != GNUNET_TESTING_configuration_create(system, cfg_new))
141 {
142 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
143 "Could not create another configuration\n");
144 GNUNET_CONFIGURATION_destroy(cfg_new);
145 fail = GNUNET_YES;
146 break;
147 }
148 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
149 "Writing configuration no. %u to file `%s' \n",
150 cur,
151 cur_file);
152 if (GNUNET_OK != GNUNET_CONFIGURATION_write(cfg_new, cur_file))
153 {
154 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
155 "Failed to write configuration no. %u \n",
156 cur);
157 fail = GNUNET_YES;
158 }
159 GNUNET_CONFIGURATION_destroy(cfg_new);
160 GNUNET_free(cur_file);
161 if (GNUNET_YES == fail)
162 break;
147 } 163 }
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 164 GNUNET_CONFIGURATION_destroy(cfg_tmpl);
149 "Writing configuration no. %u to file `%s' \n", 165 GNUNET_TESTING_system_destroy(system, GNUNET_NO);
150 cur,
151 cur_file);
152 if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg_new, cur_file))
153 {
154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
155 "Failed to write configuration no. %u \n",
156 cur);
157 fail = GNUNET_YES;
158 }
159 GNUNET_CONFIGURATION_destroy (cfg_new);
160 GNUNET_free (cur_file);
161 if (GNUNET_YES == fail)
162 break;
163 }
164 GNUNET_CONFIGURATION_destroy (cfg_tmpl);
165 GNUNET_TESTING_system_destroy (system, GNUNET_NO);
166 if (GNUNET_YES == fail) 166 if (GNUNET_YES == fail)
167 return 1; 167 return 1;
168 return 0; 168 return 0;
@@ -170,40 +170,40 @@ create_unique_cfgs (const char *template, const unsigned int no)
170 170
171 171
172static int 172static int
173create_hostkeys (const unsigned int no) 173create_hostkeys(const unsigned int no)
174{ 174{
175 struct GNUNET_TESTING_System *system; 175 struct GNUNET_TESTING_System *system;
176 struct GNUNET_PeerIdentity id; 176 struct GNUNET_PeerIdentity id;
177 struct GNUNET_DISK_FileHandle *fd; 177 struct GNUNET_DISK_FileHandle *fd;
178 struct GNUNET_CRYPTO_EddsaPrivateKey *pk; 178 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
179 179
180 system = GNUNET_TESTING_system_create ("testing", NULL, NULL, NULL); 180 system = GNUNET_TESTING_system_create("testing", NULL, NULL, NULL);
181 pk = GNUNET_TESTING_hostkey_get (system, create_no, &id); 181 pk = GNUNET_TESTING_hostkey_get(system, create_no, &id);
182 if (NULL == pk) 182 if (NULL == pk)
183 { 183 {
184 fprintf (stderr, 184 fprintf(stderr,
185 _ ("Could not extract hostkey %u (offset too large?)\n"), 185 _("Could not extract hostkey %u (offset too large?)\n"),
186 create_no); 186 create_no);
187 GNUNET_TESTING_system_destroy (system, GNUNET_YES); 187 GNUNET_TESTING_system_destroy(system, GNUNET_YES);
188 return 1; 188 return 1;
189 } 189 }
190 (void) GNUNET_DISK_directory_create_for_file (create_hostkey); 190 (void)GNUNET_DISK_directory_create_for_file(create_hostkey);
191 fd = 191 fd =
192 GNUNET_DISK_file_open (create_hostkey, 192 GNUNET_DISK_file_open(create_hostkey,
193 GNUNET_DISK_OPEN_READWRITE | GNUNET_DISK_OPEN_CREATE, 193 GNUNET_DISK_OPEN_READWRITE | GNUNET_DISK_OPEN_CREATE,
194 GNUNET_DISK_PERM_USER_READ | 194 GNUNET_DISK_PERM_USER_READ |
195 GNUNET_DISK_PERM_USER_WRITE); 195 GNUNET_DISK_PERM_USER_WRITE);
196 GNUNET_assert (fd != NULL); 196 GNUNET_assert(fd != NULL);
197 ret = GNUNET_DISK_file_write (fd, 197 ret = GNUNET_DISK_file_write(fd,
198 pk, 198 pk,
199 sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)); 199 sizeof(struct GNUNET_CRYPTO_EddsaPrivateKey));
200 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd)); 200 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fd));
201 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 201 GNUNET_log_from(GNUNET_ERROR_TYPE_DEBUG,
202 "transport-testing", 202 "transport-testing",
203 "Wrote hostkey to file: `%s'\n", 203 "Wrote hostkey to file: `%s'\n",
204 create_hostkey); 204 create_hostkey);
205 GNUNET_free (pk); 205 GNUNET_free(pk);
206 GNUNET_TESTING_system_destroy (system, GNUNET_YES); 206 GNUNET_TESTING_system_destroy(system, GNUNET_YES);
207 return 0; 207 return 0;
208} 208}
209 209
@@ -215,25 +215,25 @@ create_hostkeys (const unsigned int no)
215 * @param cls unused 215 * @param cls unused
216 */ 216 */
217static void 217static void
218cleanup (void *cls) 218cleanup(void *cls)
219{ 219{
220 if (NULL != tmpfilename) 220 if (NULL != tmpfilename)
221 { 221 {
222 if (0 != unlink (tmpfilename)) 222 if (0 != unlink(tmpfilename))
223 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 223 GNUNET_log_strerror_file(GNUNET_ERROR_TYPE_WARNING,
224 "unlink", 224 "unlink",
225 tmpfilename); 225 tmpfilename);
226 } 226 }
227 if (NULL != tid) 227 if (NULL != tid)
228 { 228 {
229 GNUNET_SCHEDULER_cancel (tid); 229 GNUNET_SCHEDULER_cancel(tid);
230 tid = NULL; 230 tid = NULL;
231 } 231 }
232 if (NULL != fh) 232 if (NULL != fh)
233 { 233 {
234 GNUNET_DISK_file_close (fh); 234 GNUNET_DISK_file_close(fh);
235 fh = NULL; 235 fh = NULL;
236 } 236 }
237} 237}
238 238
239 239
@@ -243,39 +243,42 @@ cleanup (void *cls)
243 * @param cls unused 243 * @param cls unused
244 */ 244 */
245static void 245static void
246stdin_cb (void *cls) 246stdin_cb(void *cls)
247{ 247{
248 int c; 248 int c;
249 249
250 tid = NULL; 250 tid = NULL;
251 c = getchar (); 251 c = getchar();
252 switch (c) 252 switch (c)
253 { 253 {
254 case EOF: 254 case EOF:
255 case 'q': 255 case 'q':
256 GNUNET_SCHEDULER_shutdown (); 256 GNUNET_SCHEDULER_shutdown();
257 return; 257 return;
258 case 'r': 258
259 if (GNUNET_OK != GNUNET_TESTING_peer_stop (my_peer)) 259 case 'r':
260 LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to stop the peer\n"); 260 if (GNUNET_OK != GNUNET_TESTING_peer_stop(my_peer))
261 if (GNUNET_OK != GNUNET_TESTING_peer_start (my_peer)) 261 LOG(GNUNET_ERROR_TYPE_ERROR, "Failed to stop the peer\n");
262 LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to start the peer\n"); 262 if (GNUNET_OK != GNUNET_TESTING_peer_start(my_peer))
263 printf ("restarted\n"); 263 LOG(GNUNET_ERROR_TYPE_ERROR, "Failed to start the peer\n");
264 fflush (stdout); 264 printf("restarted\n");
265 break; 265 fflush(stdout);
266 case '\n': 266 break;
267 case '\r': 267
268 /* ignore whitespace */ 268 case '\n':
269 break; 269 case '\r':
270 default: 270 /* ignore whitespace */
271 fprintf (stderr, 271 break;
272 _ ("Unknown command, use 'q' to quit or 'r' to restart peer\n")); 272
273 break; 273 default:
274 } 274 fprintf(stderr,
275 tid = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, 275 _("Unknown command, use 'q' to quit or 'r' to restart peer\n"));
276 fh, 276 break;
277 &stdin_cb, 277 }
278 NULL); 278 tid = GNUNET_SCHEDULER_add_read_file(GNUNET_TIME_UNIT_FOREVER_REL,
279 fh,
280 &stdin_cb,
281 NULL);
279} 282}
280 283
281 284
@@ -288,32 +291,32 @@ stdin_cb (void *cls)
288 * @param peer handle to the peer 291 * @param peer handle to the peer
289 */ 292 */
290static void 293static void
291testing_main (void *cls, 294testing_main(void *cls,
292 const struct GNUNET_CONFIGURATION_Handle *cfg, 295 const struct GNUNET_CONFIGURATION_Handle *cfg,
293 struct GNUNET_TESTING_Peer *peer) 296 struct GNUNET_TESTING_Peer *peer)
294{ 297{
295 my_peer = peer; 298 my_peer = peer;
296 if (NULL == (tmpfilename = GNUNET_DISK_mktemp ("gnunet-testing"))) 299 if (NULL == (tmpfilename = GNUNET_DISK_mktemp("gnunet-testing")))
297 { 300 {
298 GNUNET_break (0); 301 GNUNET_break(0);
299 GNUNET_SCHEDULER_shutdown (); 302 GNUNET_SCHEDULER_shutdown();
300 return; 303 return;
301 } 304 }
302 if (GNUNET_SYSERR == 305 if (GNUNET_SYSERR ==
303 GNUNET_CONFIGURATION_write ((struct GNUNET_CONFIGURATION_Handle *) cfg, 306 GNUNET_CONFIGURATION_write((struct GNUNET_CONFIGURATION_Handle *)cfg,
304 tmpfilename)) 307 tmpfilename))
305 { 308 {
306 GNUNET_break (0); 309 GNUNET_break(0);
307 return; 310 return;
308 } 311 }
309 printf ("ok\n%s\n", tmpfilename); 312 printf("ok\n%s\n", tmpfilename);
310 fflush (stdout); 313 fflush(stdout);
311 GNUNET_SCHEDULER_add_shutdown (&cleanup, NULL); 314 GNUNET_SCHEDULER_add_shutdown(&cleanup, NULL);
312 fh = GNUNET_DISK_get_handle_from_native (stdin); 315 fh = GNUNET_DISK_get_handle_from_native(stdin);
313 tid = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, 316 tid = GNUNET_SCHEDULER_add_read_file(GNUNET_TIME_UNIT_FOREVER_REL,
314 fh, 317 fh,
315 &stdin_cb, 318 &stdin_cb,
316 NULL); 319 NULL);
317} 320}
318 321
319 322
@@ -326,43 +329,43 @@ testing_main (void *cls,
326 * @param cfg configuration 329 * @param cfg configuration
327 */ 330 */
328static void 331static void
329run_no_scheduler (void *cls, 332run_no_scheduler(void *cls,
330 char *const *args, 333 char *const *args,
331 const char *cfgfile, 334 const char *cfgfile,
332 const struct GNUNET_CONFIGURATION_Handle *cfg) 335 const struct GNUNET_CONFIGURATION_Handle *cfg)
333{ 336{
334 if (NULL != run_service_name) 337 if (NULL != run_service_name)
335 { 338 {
336 ret = GNUNET_TESTING_service_run ("gnunet_service_test", 339 ret = GNUNET_TESTING_service_run("gnunet_service_test",
337 run_service_name, 340 run_service_name,
338 cfgfile, 341 cfgfile,
339 &testing_main, 342 &testing_main,
340 NULL); 343 NULL);
341 return; 344 return;
342 } 345 }
343 346
344 if (GNUNET_YES == create_cfg) 347 if (GNUNET_YES == create_cfg)
345 {
346 if (create_no > 0)
347 { 348 {
348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 349 if (create_no > 0)
349 "Creating %u configuration files based on template `%s'\n", 350 {
350 create_no, 351 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
351 create_cfg_template); 352 "Creating %u configuration files based on template `%s'\n",
352 ret = create_unique_cfgs (create_cfg_template, create_no); 353 create_no,
354 create_cfg_template);
355 ret = create_unique_cfgs(create_cfg_template, create_no);
356 }
357 else
358 {
359 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Missing arguments! \n");
360 ret = 1;
361 }
353 } 362 }
354 else 363 if (NULL != create_hostkey)
355 { 364 {
356 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing arguments! \n"); 365 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Extracting hostkey %u\n", create_no);
357 ret = 1; 366 ret = create_hostkeys(create_no);
358 } 367 }
359 } 368 GNUNET_free_non_null(create_cfg_template);
360 if (NULL != create_hostkey)
361 {
362 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Extracting hostkey %u\n", create_no);
363 ret = create_hostkeys (create_no);
364 }
365 GNUNET_free_non_null (create_cfg_template);
366} 369}
367 370
368 371
@@ -374,45 +377,46 @@ run_no_scheduler (void *cls,
374 * @return 0 ok, 1 on error 377 * @return 0 ok, 1 on error
375 */ 378 */
376int 379int
377main (int argc, char *const *argv) 380main(int argc, char *const *argv)
378{ 381{
379 struct GNUNET_GETOPT_CommandLineOption options[] = 382 struct GNUNET_GETOPT_CommandLineOption options[] =
380 {GNUNET_GETOPT_option_flag ('C', 383 { GNUNET_GETOPT_option_flag('C',
381 "cfg", 384 "cfg",
382 gettext_noop ( 385 gettext_noop(
383 "create unique configuration files"), 386 "create unique configuration files"),
384 &create_cfg), 387 &create_cfg),
385 GNUNET_GETOPT_option_string ( 388 GNUNET_GETOPT_option_string(
386 'k', 389 'k',
387 "key", 390 "key",
388 "FILENAME", 391 "FILENAME",
389 gettext_noop ("extract hostkey file from pre-computed hostkey list"), 392 gettext_noop("extract hostkey file from pre-computed hostkey list"),
390 &create_hostkey), 393 &create_hostkey),
391 394
392 GNUNET_GETOPT_option_uint ( 395 GNUNET_GETOPT_option_uint(
393 'n', 396 'n',
394 "number", 397 "number",
395 "NUMBER", 398 "NUMBER",
396 gettext_noop ( 399 gettext_noop(
397 "number of unique configuration files to create, or number of the hostkey to extract"), 400 "number of unique configuration files to create, or number of the hostkey to extract"),
398 &create_no), 401 &create_no),
399 402
400 403
401 GNUNET_GETOPT_option_string ('t', 404 GNUNET_GETOPT_option_string('t',
402 "template", 405 "template",
403 "FILENAME", 406 "FILENAME",
404 gettext_noop ("configuration template"), 407 gettext_noop("configuration template"),
405 &create_cfg_template), 408 &create_cfg_template),
406 409
407 GNUNET_GETOPT_option_string ( 410 GNUNET_GETOPT_option_string(
408 'r', 411 'r',
409 "run", 412 "run",
410 "SERVICE", 413 "SERVICE",
411 gettext_noop ( 414 gettext_noop(
412 "run the given service, wait on stdin for 'r' (restart) or 'q' (quit)"), 415 "run the given service, wait on stdin for 'r' (restart) or 'q' (quit)"),
413 &run_service_name), 416 &run_service_name),
414 GNUNET_GETOPT_OPTION_END}; 417 GNUNET_GETOPT_OPTION_END };
415 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) 418
419 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args(argc, argv, &argc, &argv))
416 return 2; 420 return 2;
417 421
418 /* Run without scheduler, because we may want to call 422 /* Run without scheduler, because we may want to call
@@ -421,18 +425,18 @@ main (int argc, char *const *argv)
421 * but beware when extending gnunet-testing. */ 425 * but beware when extending gnunet-testing. */
422 ret = 426 ret =
423 (GNUNET_OK == 427 (GNUNET_OK ==
424 GNUNET_PROGRAM_run2 (argc, 428 GNUNET_PROGRAM_run2(argc,
425 argv, 429 argv,
426 "gnunet-testing", 430 "gnunet-testing",
427 gettext_noop ( 431 gettext_noop(
428 "Command line tool to access the testing library"), 432 "Command line tool to access the testing library"),
429 options, 433 options,
430 &run_no_scheduler, 434 &run_no_scheduler,
431 NULL, 435 NULL,
432 GNUNET_YES)) 436 GNUNET_YES))
433 ? ret 437 ? ret
434 : 1; 438 : 1;
435 GNUNET_free ((void *) argv); 439 GNUNET_free((void *)argv);
436 return ret; 440 return ret;
437} 441}
438 442