build: Check for functions needed by Samba3
[Samba/gebeck_regimport.git] / source4 / torture / smbtorture.c
blob2aa340ee36988dc92fd4e4b767b72dd9a57f3d83
1 /*
2 Unix SMB/CIFS implementation.
3 SMB torture tester
4 Copyright (C) Andrew Tridgell 1997-2003
5 Copyright (C) Jelmer Vernooij 2006-2008
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "lib/cmdline/popt_common.h"
23 #include "system/time.h"
24 #include "system/wait.h"
25 #include "system/filesys.h"
26 #include "system/readline.h"
27 #include "lib/smbreadline/smbreadline.h"
28 #include "libcli/libcli.h"
29 #include "lib/events/events.h"
31 #include "torture/smbtorture.h"
32 #include "librpc/rpc/dcerpc.h"
33 #include "auth/gensec/gensec.h"
34 #include "param/param.h"
37 static bool run_matching(struct torture_context *torture,
38 const char *prefix,
39 const char *expr,
40 struct torture_suite *suite,
41 bool *matched)
43 bool ret = true;
45 if (suite == NULL) {
46 struct torture_suite *o;
48 for (o = (torture_root == NULL?NULL:torture_root->children); o; o = o->next) {
49 if (gen_fnmatch(expr, o->name) == 0) {
50 *matched = true;
51 reload_charcnv(torture->lp_ctx);
52 ret &= torture_run_suite(torture, o);
53 continue;
56 ret &= run_matching(torture, o->name, expr, o, matched);
58 } else {
59 char *name;
60 struct torture_suite *c;
61 struct torture_tcase *t;
63 for (c = suite->children; c; c = c->next) {
64 asprintf(&name, "%s-%s", prefix, c->name);
66 if (gen_fnmatch(expr, name) == 0) {
67 *matched = true;
68 reload_charcnv(torture->lp_ctx);
69 torture->active_testname = talloc_strdup(torture, prefix);
70 ret &= torture_run_suite(torture, c);
71 free(name);
72 continue;
75 ret &= run_matching(torture, name, expr, c, matched);
77 free(name);
80 for (t = suite->testcases; t; t = t->next) {
81 asprintf(&name, "%s-%s", prefix, t->name);
82 if (gen_fnmatch(expr, name) == 0) {
83 *matched = true;
84 reload_charcnv(torture->lp_ctx);
85 torture->active_testname = talloc_strdup(torture, prefix);
86 ret &= torture_run_tcase(torture, t);
87 talloc_free(torture->active_testname);
89 free(name);
93 return ret;
96 #define MAX_COLS 80 /* FIXME: Determine this at run-time */
98 /****************************************************************************
99 run a specified test or "ALL"
100 ****************************************************************************/
101 static bool run_test(struct torture_context *torture, const char *name)
103 bool ret = true;
104 bool matched = false;
105 struct torture_suite *o;
107 if (strequal(name, "ALL")) {
108 for (o = torture_root->children; o; o = o->next) {
109 ret &= torture_run_suite(torture, o);
111 return ret;
114 ret = run_matching(torture, NULL, name, NULL, &matched);
116 if (!matched) {
117 printf("Unknown torture operation '%s'\n", name);
118 return false;
121 return ret;
124 static bool parse_target(struct loadparm_context *lp_ctx, const char *target)
126 char *host = NULL, *share = NULL;
127 struct dcerpc_binding *binding_struct;
128 NTSTATUS status;
130 /* see if its a RPC transport specifier */
131 if (!smbcli_parse_unc(target, NULL, &host, &share)) {
132 status = dcerpc_parse_binding(talloc_autofree_context(), target, &binding_struct);
133 if (NT_STATUS_IS_ERR(status)) {
134 d_printf("Invalid option: %s is not a valid torture target (share or binding string)\n\n", target);
135 return false;
137 lp_set_cmdline(lp_ctx, "torture:host", binding_struct->host);
138 if (lp_parm_string(lp_ctx, NULL, "torture", "share") == NULL)
139 lp_set_cmdline(lp_ctx, "torture:share", "IPC$");
140 lp_set_cmdline(lp_ctx, "torture:binding", target);
141 } else {
142 lp_set_cmdline(lp_ctx, "torture:host", host);
143 lp_set_cmdline(lp_ctx, "torture:share", share);
144 lp_set_cmdline(lp_ctx, "torture:binding", host);
147 return true;
150 static void parse_dns(struct loadparm_context *lp_ctx, const char *dns)
152 char *userdn, *basedn, *secret;
153 char *p, *d;
155 /* retrievieng the userdn */
156 p = strchr_m(dns, '#');
157 if (!p) {
158 lp_set_cmdline(lp_ctx, "torture:ldap_userdn", "");
159 lp_set_cmdline(lp_ctx, "torture:ldap_basedn", "");
160 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
161 return;
163 userdn = strndup(dns, p - dns);
164 lp_set_cmdline(lp_ctx, "torture:ldap_userdn", userdn);
166 /* retrieve the basedn */
167 d = p + 1;
168 p = strchr_m(d, '#');
169 if (!p) {
170 lp_set_cmdline(lp_ctx, "torture:ldap_basedn", "");
171 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
172 return;
174 basedn = strndup(d, p - d);
175 lp_set_cmdline(lp_ctx, "torture:ldap_basedn", basedn);
177 /* retrieve the secret */
178 p = p + 1;
179 if (!p) {
180 lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
181 return;
183 secret = strdup(p);
184 lp_set_cmdline(lp_ctx, "torture:ldap_secret", secret);
186 printf ("%s - %s - %s\n", userdn, basedn, secret);
190 static void print_test_list(void)
192 struct torture_suite *o;
193 struct torture_suite *s;
194 struct torture_tcase *t;
196 if (torture_root == NULL)
197 return;
199 for (o = torture_root->children; o; o = o->next) {
200 for (s = o->children; s; s = s->next) {
201 printf("%s-%s\n", o->name, s->name);
204 for (t = o->testcases; t; t = t->next) {
205 printf("%s-%s\n", o->name, t->name);
210 _NORETURN_ static void usage(poptContext pc)
212 struct torture_suite *o;
213 struct torture_suite *s;
214 struct torture_tcase *t;
215 int i;
217 poptPrintUsage(pc, stdout, 0);
218 printf("\n");
220 printf("The binding format is:\n\n");
222 printf(" TRANSPORT:host[flags]\n\n");
224 printf(" where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP\n");
225 printf(" or ncalrpc for local connections.\n\n");
227 printf(" 'host' is an IP or hostname or netbios name. If the binding string\n");
228 printf(" identifies the server side of an endpoint, 'host' may be an empty\n");
229 printf(" string.\n\n");
231 printf(" 'flags' can include a SMB pipe name if using the ncacn_np transport or\n");
232 printf(" a TCP port number if using the ncacn_ip_tcp transport, otherwise they\n");
233 printf(" will be auto-determined.\n\n");
235 printf(" other recognised flags are:\n\n");
237 printf(" sign : enable ntlmssp signing\n");
238 printf(" seal : enable ntlmssp sealing\n");
239 printf(" connect : enable rpc connect level auth (auth, but no sign or seal)\n");
240 printf(" validate: enable the NDR validator\n");
241 printf(" print: enable debugging of the packets\n");
242 printf(" bigendian: use bigendian RPC\n");
243 printf(" padcheck: check reply data for non-zero pad bytes\n\n");
245 printf(" For example, these all connect to the samr pipe:\n\n");
247 printf(" ncacn_np:myserver\n");
248 printf(" ncacn_np:myserver[samr]\n");
249 printf(" ncacn_np:myserver[\\pipe\\samr]\n");
250 printf(" ncacn_np:myserver[/pipe/samr]\n");
251 printf(" ncacn_np:myserver[samr,sign,print]\n");
252 printf(" ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian]\n");
253 printf(" ncacn_np:myserver[/pipe/samr,seal,validate]\n");
254 printf(" ncacn_np:\n");
255 printf(" ncacn_np:[/pipe/samr]\n\n");
257 printf(" ncacn_ip_tcp:myserver\n");
258 printf(" ncacn_ip_tcp:myserver[1024]\n");
259 printf(" ncacn_ip_tcp:myserver[1024,sign,seal]\n\n");
261 printf(" ncalrpc:\n\n");
263 printf("The UNC format is:\n\n");
265 printf(" //server/share\n\n");
267 printf("Tests are:");
269 if (torture_root == NULL) {
270 printf("NO TESTS LOADED\n");
271 exit(1);
274 for (o = torture_root->children; o; o = o->next) {
275 printf("\n%s (%s):\n ", o->description, o->name);
277 i = 0;
278 for (s = o->children; s; s = s->next) {
279 if (i + strlen(o->name) + strlen(s->name) >= (MAX_COLS - 3)) {
280 printf("\n ");
281 i = 0;
283 i+=printf("%s-%s ", o->name, s->name);
286 for (t = o->testcases; t; t = t->next) {
287 if (i + strlen(o->name) + strlen(t->name) >= (MAX_COLS - 3)) {
288 printf("\n ");
289 i = 0;
291 i+=printf("%s-%s ", o->name, t->name);
294 if (i) printf("\n");
297 printf("\nThe default test is ALL.\n");
299 exit(1);
302 _NORETURN_ static void max_runtime_handler(int sig)
304 DEBUG(0,("maximum runtime exceeded for smbtorture - terminating\n"));
305 exit(1);
308 struct timeval last_suite_started;
310 static void simple_suite_start(struct torture_context *ctx,
311 struct torture_suite *suite)
313 last_suite_started = timeval_current();
314 printf("Running %s\n", suite->name);
317 static void simple_suite_finish(struct torture_context *ctx,
318 struct torture_suite *suite)
321 printf("%s took %g secs\n\n", suite->name,
322 timeval_elapsed(&last_suite_started));
325 static void simple_test_result(struct torture_context *context,
326 enum torture_result res, const char *reason)
328 switch (res) {
329 case TORTURE_OK:
330 if (reason)
331 printf("OK: %s\n", reason);
332 break;
333 case TORTURE_FAIL:
334 printf("TEST %s FAILED! - %s\n", context->active_test->name, reason);
335 break;
336 case TORTURE_ERROR:
337 printf("ERROR IN TEST %s! - %s\n", context->active_test->name, reason);
338 break;
339 case TORTURE_SKIP:
340 printf("SKIP: %s - %s\n", context->active_test->name, reason);
341 break;
345 static void simple_comment(struct torture_context *test,
346 const char *comment)
348 printf("%s", comment);
351 static void simple_warning(struct torture_context *test,
352 const char *comment)
354 fprintf(stderr, "WARNING: %s\n", comment);
357 static void simple_progress(struct torture_context *test,
358 int offset, enum torture_progress_whence whence)
362 const static struct torture_ui_ops std_ui_ops = {
363 .comment = simple_comment,
364 .warning = simple_warning,
365 .suite_start = simple_suite_start,
366 .suite_finish = simple_suite_finish,
367 .test_result = simple_test_result,
368 .progress = simple_progress,
372 static void run_shell(struct torture_context *tctx)
374 char *cline;
375 int argc;
376 const char **argv;
377 int ret;
379 while (1) {
380 cline = smb_readline("torture> ", NULL, NULL);
382 if (cline == NULL)
383 return;
385 ret = poptParseArgvString(cline, &argc, &argv);
386 if (ret != 0) {
387 fprintf(stderr, "Error parsing line\n");
388 continue;
391 if (!strcmp(argv[0], "quit")) {
392 return;
393 } else if (!strcmp(argv[0], "set")) {
394 if (argc < 3) {
395 fprintf(stderr, "Usage: set <variable> <value>\n");
396 } else {
397 char *name = talloc_asprintf(NULL, "torture:%s", argv[1]);
398 lp_set_cmdline(tctx->lp_ctx, name, argv[2]);
399 talloc_free(name);
401 } else if (!strcmp(argv[0], "help")) {
402 fprintf(stderr, "Available commands:\n"
403 " help - This help command\n"
404 " run - Run test\n"
405 " set - Change variables\n"
406 "\n");
407 } else if (!strcmp(argv[0], "run")) {
408 if (argc < 2) {
409 fprintf(stderr, "Usage: run TEST-NAME [OPTIONS...]\n");
410 } else {
411 run_test(tctx, argv[1]);
414 free(cline);
418 /****************************************************************************
419 main program
420 ****************************************************************************/
421 int main(int argc,char *argv[])
423 int opt, i;
424 bool correct = true;
425 int max_runtime=0;
426 int argc_new;
427 struct torture_context *torture;
428 struct torture_results *results;
429 const struct torture_ui_ops *ui_ops;
430 char **argv_new;
431 poptContext pc;
432 static const char *target = "other";
433 NTSTATUS status;
434 int shell = false;
435 static const char *ui_ops_name = "subunit";
436 const char *basedir = NULL;
437 const char *extra_module = NULL;
438 static int list_tests = 0;
439 int num_extra_users = 0;
440 enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS, OPT_LIST,
441 OPT_DANGEROUS,OPT_SMB_PORTS,OPT_ASYNC,OPT_NUMPROGS,
442 OPT_EXTRA_USER,};
444 struct poptOption long_options[] = {
445 POPT_AUTOHELP
446 {"format", 0, POPT_ARG_STRING, &ui_ops_name, 0, "Output format (one of: simple, subunit)", NULL },
447 {"smb-ports", 'p', POPT_ARG_STRING, NULL, OPT_SMB_PORTS, "SMB ports", NULL},
448 {"basedir", 0, POPT_ARG_STRING, &basedir, 0, "base directory", "BASEDIR" },
449 {"seed", 0, POPT_ARG_INT, &torture_seed, 0, "Seed to use for randomizer", NULL},
450 {"num-progs", 0, POPT_ARG_INT, NULL, OPT_NUMPROGS, "num progs", NULL},
451 {"num-ops", 0, POPT_ARG_INT, &torture_numops, 0, "num ops", NULL},
452 {"entries", 0, POPT_ARG_INT, &torture_entries, 0, "entries", NULL},
453 {"loadfile", 0, POPT_ARG_STRING, NULL, OPT_LOADFILE, "NBench load file to use", NULL},
454 {"list", 0, POPT_ARG_NONE, &list_tests, 0, "List available tests and exit", NULL },
455 {"unclist", 0, POPT_ARG_STRING, NULL, OPT_UNCLIST, "unclist", NULL},
456 {"timelimit", 't', POPT_ARG_INT, NULL, OPT_TIMELIMIT, "Set time limit (in seconds)", NULL},
457 {"failures", 'f', POPT_ARG_INT, &torture_failures, 0, "failures", NULL},
458 {"parse-dns", 'D', POPT_ARG_STRING, NULL, OPT_DNS, "parse-dns", NULL},
459 {"dangerous", 'X', POPT_ARG_NONE, NULL, OPT_DANGEROUS,
460 "run dangerous tests (eg. wiping out password database)", NULL},
461 {"load-module", 0, POPT_ARG_STRING, &extra_module, 0, "load tests from DSO file", "SOFILE"},
462 {"shell", 0, POPT_ARG_NONE, &shell, true, "Run shell", NULL},
463 {"target", 'T', POPT_ARG_STRING, &target, 0, "samba3|samba4|other", NULL},
464 {"async", 'a', POPT_ARG_NONE, NULL, OPT_ASYNC,
465 "run async tests", NULL},
466 {"num-async", 0, POPT_ARG_INT, &torture_numasync, 0,
467 "number of simultaneous async requests", NULL},
468 {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0,
469 "set maximum time for smbtorture to live", "seconds"},
470 {"extra-user", 0, POPT_ARG_STRING, NULL, OPT_EXTRA_USER,
471 "extra user credentials", NULL},
472 POPT_COMMON_SAMBA
473 POPT_COMMON_CONNECTION
474 POPT_COMMON_CREDENTIALS
475 POPT_COMMON_VERSION
476 { NULL }
479 setlinebuf(stdout);
481 /* we are never interested in SIGPIPE */
482 BlockSignals(true, SIGPIPE);
484 pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options,
485 POPT_CONTEXT_KEEP_FIRST);
487 poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ...");
489 while((opt = poptGetNextOpt(pc)) != -1) {
490 switch (opt) {
491 case OPT_LOADFILE:
492 lp_set_cmdline(cmdline_lp_ctx, "torture:loadfile", poptGetOptArg(pc));
493 break;
494 case OPT_UNCLIST:
495 lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc));
496 break;
497 case OPT_TIMELIMIT:
498 lp_set_cmdline(cmdline_lp_ctx, "torture:timelimit", poptGetOptArg(pc));
499 break;
500 case OPT_NUMPROGS:
501 lp_set_cmdline(cmdline_lp_ctx, "torture:nprocs", poptGetOptArg(pc));
502 break;
503 case OPT_DNS:
504 parse_dns(cmdline_lp_ctx, poptGetOptArg(pc));
505 break;
506 case OPT_DANGEROUS:
507 lp_set_cmdline(cmdline_lp_ctx, "torture:dangerous", "Yes");
508 break;
509 case OPT_ASYNC:
510 lp_set_cmdline(cmdline_lp_ctx, "torture:async", "Yes");
511 break;
512 case OPT_SMB_PORTS:
513 lp_set_cmdline(cmdline_lp_ctx, "smb ports", poptGetOptArg(pc));
514 break;
515 case OPT_EXTRA_USER:
517 char *option = talloc_asprintf(NULL, "torture:extra_user%u",
518 ++num_extra_users);
519 const char *value = poptGetOptArg(pc);
520 lp_set_cmdline(cmdline_lp_ctx, option, value);
521 talloc_free(option);
523 break;
524 default:
525 printf("bad command line option\n");
526 exit(1);
530 if (strcmp(target, "samba3") == 0) {
531 lp_set_cmdline(cmdline_lp_ctx, "torture:samba3", "true");
532 lp_set_cmdline(cmdline_lp_ctx, "torture:resume_key_support", "false");
533 } else if (strcmp(target, "samba4") == 0) {
534 lp_set_cmdline(cmdline_lp_ctx, "torture:samba4", "true");
535 } else if (strcmp(target, "winxp") == 0) {
536 lp_set_cmdline(cmdline_lp_ctx, "torture:winxp", "true");
537 } else if (strcmp(target, "w2k3") == 0) {
538 lp_set_cmdline(cmdline_lp_ctx, "torture:w2k3", "true");
539 } else if (strcmp(target, "w2k8") == 0) {
540 lp_set_cmdline(cmdline_lp_ctx, "torture:w2k8", "true");
541 lp_set_cmdline(cmdline_lp_ctx,
542 "torture:invalid_lock_range_support", "false");
543 } else if (strcmp(target, "win7") == 0) {
544 lp_set_cmdline(cmdline_lp_ctx, "torture:win7", "true");
545 lp_set_cmdline(cmdline_lp_ctx, "torture:cn_max_buffer_size",
546 "0x00010000");
547 lp_set_cmdline(cmdline_lp_ctx, "torture:resume_key_support", "false");
548 lp_set_cmdline(cmdline_lp_ctx, "torture:rewind_support", "false");
550 /* RAW-SEARCH for fails for inexplicable reasons against win7 */
551 lp_set_cmdline(cmdline_lp_ctx, "torture:search_ea_support", "false");
553 lp_set_cmdline(cmdline_lp_ctx, "torture:hide_on_access_denied",
554 "true");
555 } else if (strcmp(target, "onefs") == 0) {
556 lp_set_cmdline(cmdline_lp_ctx, "torture:onefs", "true");
557 lp_set_cmdline(cmdline_lp_ctx, "torture:openx_deny_dos_support",
558 "false");
559 lp_set_cmdline(cmdline_lp_ctx, "torture:range_not_locked_on_file_close", "false");
560 lp_set_cmdline(cmdline_lp_ctx, "torture:sacl_support", "false");
561 lp_set_cmdline(cmdline_lp_ctx, "torture:ea_support", "false");
562 lp_set_cmdline(cmdline_lp_ctx, "torture:smbexit_pdu_support",
563 "false");
564 lp_set_cmdline(cmdline_lp_ctx, "torture:smblock_pdu_support",
565 "false");
566 lp_set_cmdline(cmdline_lp_ctx, "torture:2_step_break_to_none",
567 "true");
568 lp_set_cmdline(cmdline_lp_ctx, "torture:deny_dos_support", "false");
569 lp_set_cmdline(cmdline_lp_ctx, "torture:deny_fcb_support", "false");
570 lp_set_cmdline(cmdline_lp_ctx, "torture:read_support", "false");
571 lp_set_cmdline(cmdline_lp_ctx, "torture:writeclose_support", "false");
572 lp_set_cmdline(cmdline_lp_ctx, "torture:resume_key_support", "false");
573 lp_set_cmdline(cmdline_lp_ctx, "torture:rewind_support", "false");
576 if (max_runtime) {
577 /* this will only work if nobody else uses alarm(),
578 which means it won't work for some tests, but we
579 can't use the event context method we use for smbd
580 as so many tests create their own event
581 context. This will at least catch most cases. */
582 signal(SIGALRM, max_runtime_handler);
583 alarm(max_runtime);
586 if (extra_module != NULL) {
587 init_module_fn fn = load_module(talloc_autofree_context(), poptGetOptArg(pc));
589 if (fn == NULL)
590 d_printf("Unable to load module from %s\n", poptGetOptArg(pc));
591 else {
592 status = fn();
593 if (NT_STATUS_IS_ERR(status)) {
594 d_printf("Error initializing module %s: %s\n",
595 poptGetOptArg(pc), nt_errstr(status));
598 } else {
599 torture_init();
602 if (list_tests) {
603 print_test_list();
604 return 0;
607 if (torture_seed == 0) {
608 torture_seed = time(NULL);
610 printf("Using seed %d\n", torture_seed);
611 srandom(torture_seed);
613 argv_new = discard_const_p(char *, poptGetArgs(pc));
615 argc_new = argc;
616 for (i=0; i<argc; i++) {
617 if (argv_new[i] == NULL) {
618 argc_new = i;
619 break;
623 if (!(argc_new >= 3 || (shell && argc_new >= 2))) {
624 usage(pc);
625 exit(1);
628 if (!parse_target(cmdline_lp_ctx, argv_new[1])) {
629 usage(pc);
630 exit(1);
633 if (!strcmp(ui_ops_name, "simple")) {
634 ui_ops = &std_ui_ops;
635 } else if (!strcmp(ui_ops_name, "subunit")) {
636 ui_ops = &torture_subunit_ui_ops;
637 } else {
638 printf("Unknown output format '%s'\n", ui_ops_name);
639 exit(1);
642 results = torture_results_init(talloc_autofree_context(), ui_ops);
644 torture = torture_context_init(s4_event_context_init(NULL), results);
645 if (basedir != NULL) {
646 if (basedir[0] != '/') {
647 fprintf(stderr, "Please specify an absolute path to --basedir\n");
648 return 1;
650 torture->outputdir = basedir;
651 } else {
652 char *pwd = talloc_size(torture, PATH_MAX);
653 if (!getcwd(pwd, PATH_MAX)) {
654 fprintf(stderr, "Unable to determine current working directory\n");
655 return 1;
657 torture->outputdir = pwd;
660 torture->lp_ctx = cmdline_lp_ctx;
662 gensec_init(cmdline_lp_ctx);
664 if (argc_new == 0) {
665 printf("You must specify a test to run, or 'ALL'\n");
666 } else if (shell) {
667 run_shell(torture);
668 } else {
669 for (i=2;i<argc_new;i++) {
670 if (!run_test(torture, argv_new[i])) {
671 correct = false;
676 if (torture->results->returncode && correct) {
677 return(0);
678 } else {
679 return(1);