r21707: Finally merge my (long-living) perlselftest branch.
[tprouty/samba.git] / source4 / torture / smbtorture.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture tester
4    Copyright (C) Andrew Tridgell 1997-2003
5    Copyright (C) Jelmer Vernooij 2006
6    
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 2 of the License, or
10    (at your option) any later version.
11    
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.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "lib/cmdline/popt_common.h"
24 #include "system/time.h"
25 #include "system/wait.h"
26 #include "system/filesys.h"
27 #include "system/readline.h"
28 #include "lib/smbreadline/smbreadline.h"
29 #include "libcli/libcli.h"
30 #include "lib/ldb/include/ldb.h"
31 #include "lib/events/events.h"
32 #include "dynconfig.h"
33
34 #include "torture/torture.h"
35 #include "build.h"
36 #include "lib/util/dlinklist.h"
37 #include "librpc/rpc/dcerpc.h"
38
39 static bool run_matching(struct torture_context *torture,
40                                                  const char *prefix, 
41                                                  const char *expr,
42                                                  struct torture_suite *suite,
43                                                  bool *matched)
44 {
45         bool ret = true;
46
47         if (suite == NULL) {
48                 struct torture_suite *o;
49
50                 for (o = torture_root->children; o; o = o->next) {
51                         if (gen_fnmatch(expr, o->name) == 0) {
52                                 *matched = true;
53                                 init_iconv();
54                                 ret &= torture_run_suite(torture, o);
55                                 continue;
56                         }
57
58                         ret &= run_matching(torture, o->name, expr, o, matched);
59                 }
60         } else {
61                 char *name;
62                 struct torture_suite *c;
63                 struct torture_tcase *t;
64
65                 for (c = suite->children; c; c = c->next) {
66                         asprintf(&name, "%s-%s", prefix, c->name);
67
68                         if (gen_fnmatch(expr, name) == 0) {
69                                 *matched = true;
70                                 init_iconv();
71                                 torture->active_testname = talloc_strdup(torture, prefix);
72                                 ret &= torture_run_suite(torture, c);
73                                 free(name);
74                                 continue;
75                         }
76                         
77                         ret &= run_matching(torture, name, expr, c, matched);
78
79                         free(name);
80                 }
81
82                 for (t = suite->testcases; t; t = t->next) {
83                         asprintf(&name, "%s-%s", prefix, t->name);
84                         if (gen_fnmatch(expr, name) == 0) {
85                                 *matched = true;
86                                 init_iconv();
87                                 torture->active_testname = talloc_strdup(torture, prefix);
88                                 ret &= torture_run_tcase(torture, t);
89                                 talloc_free(torture->active_testname);
90                         }
91                         free(name);
92                 }
93         }
94
95         return ret;
96 }
97
98 #define MAX_COLS 80 /* FIXME: Determine this at run-time */
99
100 /****************************************************************************
101 run a specified test or "ALL"
102 ****************************************************************************/
103 static bool run_test(struct torture_context *torture, const char *name)
104 {
105         bool ret = true;
106         bool matched = false;
107         struct torture_suite *o;
108
109         if (strequal(name, "ALL")) {
110                 for (o = torture_root->children; o; o = o->next) {
111                         ret &= torture_run_suite(torture, o);
112                 }
113                 return ret;
114         }
115
116         ret = run_matching(torture, NULL, name, NULL, &matched);
117
118         if (!matched) {
119                 printf("Unknown torture operation '%s'\n", name);
120                 return false;
121         }
122
123         return ret;
124 }
125
126 static void parse_dns(const char *dns)
127 {
128         char *userdn, *basedn, *secret;
129         char *p, *d;
130
131         /* retrievieng the userdn */
132         p = strchr_m(dns, '#');
133         if (!p) {
134                 lp_set_cmdline("torture:ldap_userdn", "");
135                 lp_set_cmdline("torture:ldap_basedn", "");
136                 lp_set_cmdline("torture:ldap_secret", "");
137                 return;
138         }
139         userdn = strndup(dns, p - dns);
140         lp_set_cmdline("torture:ldap_userdn", userdn);
141
142         /* retrieve the basedn */
143         d = p + 1;
144         p = strchr_m(d, '#');
145         if (!p) {
146                 lp_set_cmdline("torture:ldap_basedn", "");
147                 lp_set_cmdline("torture:ldap_secret", "");
148                 return;
149         }
150         basedn = strndup(d, p - d);
151         lp_set_cmdline("torture:ldap_basedn", basedn);
152
153         /* retrieve the secret */
154         p = p + 1;
155         if (!p) {
156                 lp_set_cmdline("torture:ldap_secret", "");
157                 return;
158         }
159         secret = strdup(p);
160         lp_set_cmdline("torture:ldap_secret", secret);
161
162         printf ("%s - %s - %s\n", userdn, basedn, secret);
163
164 }
165
166 static void print_test_list(void)
167 {
168         struct torture_suite *o;
169         struct torture_suite *s;
170         struct torture_tcase *t;
171
172         for (o = torture_root->children; o; o = o->next) {
173                 for (s = o->children; s; s = s->next) {
174                         printf("%s-%s\n", o->name, s->name);
175                 }
176
177                 for (t = o->testcases; t; t = t->next) {
178                         printf("%s-%s\n", o->name, t->name);
179                 }
180         }
181 }
182
183 static void usage(poptContext pc)
184 {
185         struct torture_suite *o;
186         struct torture_suite *s;
187         struct torture_tcase *t;
188         int i;
189
190         poptPrintUsage(pc, stdout, 0);
191         printf("\n");
192
193         printf("The binding format is:\n\n");
194
195         printf("  TRANSPORT:host[flags]\n\n");
196
197         printf("  where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP\n");
198         printf("  or ncalrpc for local connections.\n\n");
199
200         printf("  'host' is an IP or hostname or netbios name. If the binding string\n");
201         printf("  identifies the server side of an endpoint, 'host' may be an empty\n");
202         printf("  string.\n\n");
203
204         printf("  'flags' can include a SMB pipe name if using the ncacn_np transport or\n");
205         printf("  a TCP port number if using the ncacn_ip_tcp transport, otherwise they\n");
206         printf("  will be auto-determined.\n\n");
207
208         printf("  other recognised flags are:\n\n");
209
210         printf("    sign : enable ntlmssp signing\n");
211         printf("    seal : enable ntlmssp sealing\n");
212         printf("    connect : enable rpc connect level auth (auth, but no sign or seal)\n");
213         printf("    validate: enable the NDR validator\n");
214         printf("    print: enable debugging of the packets\n");
215         printf("    bigendian: use bigendian RPC\n");
216         printf("    padcheck: check reply data for non-zero pad bytes\n\n");
217
218         printf("  For example, these all connect to the samr pipe:\n\n");
219
220         printf("    ncacn_np:myserver\n");
221         printf("    ncacn_np:myserver[samr]\n");
222         printf("    ncacn_np:myserver[\\pipe\\samr]\n");
223         printf("    ncacn_np:myserver[/pipe/samr]\n");
224         printf("    ncacn_np:myserver[samr,sign,print]\n");
225         printf("    ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian]\n");
226         printf("    ncacn_np:myserver[/pipe/samr,seal,validate]\n");
227         printf("    ncacn_np:\n");
228         printf("    ncacn_np:[/pipe/samr]\n\n");
229
230         printf("    ncacn_ip_tcp:myserver\n");
231         printf("    ncacn_ip_tcp:myserver[1024]\n");
232         printf("    ncacn_ip_tcp:myserver[1024,sign,seal]\n\n");
233
234         printf("    ncalrpc:\n\n");
235
236         printf("The UNC format is:\n\n");
237
238         printf("  //server/share\n\n");
239
240         printf("Tests are:");
241
242         for (o = torture_root->children; o; o = o->next) {
243                 printf("\n%s (%s):\n  ", o->description, o->name);
244
245                 i = 0;
246                 for (s = o->children; s; s = s->next) {
247                         if (i + strlen(o->name) + strlen(s->name) >= (MAX_COLS - 3)) {
248                                 printf("\n  ");
249                                 i = 0;
250                         }
251                         i+=printf("%s-%s ", o->name, s->name);
252                 }
253
254                 for (t = o->testcases; t; t = t->next) {
255                         if (i + strlen(o->name) + strlen(t->name) >= (MAX_COLS - 3)) {
256                                 printf("\n  ");
257                                 i = 0;
258                         }
259                         i+=printf("%s-%s ", o->name, t->name);
260                 }
261
262                 if (i) printf("\n");
263         }
264
265         printf("\nThe default test is ALL.\n");
266
267         exit(1);
268 }
269
270 static void max_runtime_handler(int sig)
271 {
272         DEBUG(0,("maximum runtime exceeded for smbtorture - terminating\n"));
273         exit(1);
274 }
275
276 struct timeval last_suite_started;
277
278 static void simple_suite_start(struct torture_context *ctx,
279                                                            struct torture_suite *suite)
280 {
281         last_suite_started = timeval_current();
282         printf("Running %s\n", suite->name);
283 }
284
285 static void simple_suite_finish(struct torture_context *ctx,
286                                                            struct torture_suite *suite)
287 {
288
289         printf("%s took %g secs\n\n", suite->name, 
290                    timeval_elapsed(&last_suite_started));
291 }
292
293 static void simple_test_result (struct torture_context *context, 
294                                                                 enum torture_result res, const char *reason)
295 {
296         switch (res) {
297         case TORTURE_OK:
298                 if (reason)
299                         printf("OK: %s\n", reason);
300                 break;
301         case TORTURE_FAIL:
302                 printf("TEST %s FAILED! - %s\n", context->active_test->name, reason);
303                 break;
304         case TORTURE_ERROR:
305                 printf("ERROR IN TEST %s! - %s\n", context->active_test->name, reason); 
306                 break;
307         case TORTURE_SKIP:
308                 printf("SKIP: %s - %s\n", context->active_test->name, reason);
309                 break;
310         }
311 }
312
313 static void simple_comment (struct torture_context *test, 
314                                                         const char *comment)
315 {
316         printf("%s", comment);
317 }
318
319 static void simple_warning(struct torture_context *test, 
320                                                    const char *comment)
321 {
322         fprintf(stderr, "WARNING: %s\n", comment);
323 }
324
325 const static struct torture_ui_ops std_ui_ops = {
326         .comment = simple_comment,
327         .warning = simple_warning,
328         .suite_start = simple_suite_start,
329         .suite_finish = simple_suite_finish,
330         .test_result = simple_test_result
331 };
332
333 static void subunit_init(struct torture_context *ctx) 
334 {
335         /* FIXME: register segv and bus handler */
336 }
337
338 static void subunit_suite_start(struct torture_context *ctx,
339                                                            struct torture_suite *suite)
340 {
341         printf("testsuite: %s\n", suite->name);
342 }
343
344 static void subunit_test_start (struct torture_context *ctx, 
345                                                             struct torture_tcase *tcase,
346                                                                 struct torture_test *test)
347 {
348         printf("test: %s\n", test->name);
349 }
350
351 static void subunit_test_result (struct torture_context *context, 
352                                                                  enum torture_result res, const char *reason)
353 {
354         switch (res) {
355         case TORTURE_OK:
356                 printf("success: %s", context->active_test->name);
357                 break;
358         case TORTURE_FAIL:
359                 printf("failure: %s", context->active_test->name);
360                 break;
361         case TORTURE_ERROR:
362                 printf("error: %s", context->active_test->name);
363                 break;
364         case TORTURE_SKIP:
365                 printf("skip: %s", context->active_test->name);
366                 break;
367         }
368         if (reason)
369                 printf(" [\n%s\n]", reason);
370         printf("\n");
371 }
372
373 static void subunit_comment (struct torture_context *test, 
374                                                          const char *comment)
375 {
376         fprintf(stderr, "%s", comment);
377 }
378
379 const static struct torture_ui_ops subunit_ui_ops = {
380         .init = subunit_init,
381         .comment = subunit_comment,
382         .test_start = subunit_test_start,
383         .test_result = subunit_test_result,
384         .suite_start = subunit_suite_start
385 };
386
387 static void harness_test_start (struct torture_context *ctx, 
388                                                             struct torture_tcase *tcase,
389                                                                 struct torture_test *test)
390 {
391 }
392
393 static void harness_test_result (struct torture_context *context, 
394                                                                  enum torture_result res, const char *reason)
395 {
396         switch (res) {
397         case TORTURE_OK:
398                 printf("ok %s - %s\n", context->active_test->name, reason);
399                 break;
400         case TORTURE_FAIL:
401         case TORTURE_ERROR:
402                 printf("not ok %s - %s\n", context->active_test->name, reason);
403                 break;
404         case TORTURE_SKIP:
405                 printf("skip %s - %s\n", context->active_test->name, reason);
406                 break;
407         }
408 }
409
410 static void harness_comment (struct torture_context *test, 
411                                                          const char *comment)
412 {
413         printf("# %s\n", comment);
414 }
415
416 const static struct torture_ui_ops harness_ui_ops = {
417         .comment = harness_comment,
418         .test_start = harness_test_start,
419         .test_result = harness_test_result
420 };
421
422 static void quiet_suite_start(struct torture_context *ctx,
423                                                   struct torture_suite *suite)
424 {
425         int i;
426         ctx->quiet = true;
427         for (i = 1; i < ctx->level; i++) putchar('\t');
428         printf("%s: ", suite->name);
429         fflush(stdout);
430 }
431
432 static void quiet_suite_finish(struct torture_context *ctx,
433                                                   struct torture_suite *suite)
434 {
435         putchar('\n');
436 }
437
438 static void quiet_test_result (struct torture_context *context, 
439                                                            enum torture_result res, const char *reason)
440 {
441         fflush(stdout);
442         switch (res) {
443         case TORTURE_OK: putchar('.'); break;
444         case TORTURE_FAIL: putchar('F'); break;
445         case TORTURE_ERROR: putchar('E'); break;
446         case TORTURE_SKIP: putchar('I'); break;
447         }
448 }
449
450 const static struct torture_ui_ops quiet_ui_ops = {
451         .suite_start = quiet_suite_start,
452         .suite_finish = quiet_suite_finish,
453         .test_result = quiet_test_result
454 };
455
456 void run_shell(struct torture_context *tctx)
457 {
458         char *cline;
459         int argc;
460         const char **argv;
461         int ret;
462
463         while (1) {
464                 cline = smb_readline("torture> ", NULL, NULL);
465
466                 if (cline == NULL)
467                         return;
468         
469                 ret = poptParseArgvString(cline, &argc, &argv);
470                 if (ret != 0) {
471                         fprintf(stderr, "Error parsing line\n");
472                         continue;
473                 }
474
475                 if (!strcmp(argv[0], "quit")) {
476                         return;
477                 } else if (!strcmp(argv[0], "set")) {
478                         if (argc < 3) {
479                                 fprintf(stderr, "Usage: set <variable> <value>\n");
480                         } else {
481                                 char *name = talloc_asprintf(NULL, "torture:%s", argv[1]);
482                                 lp_set_cmdline(name, argv[2]);
483                                 talloc_free(name);
484                         }
485                 } else if (!strcmp(argv[0], "help")) {
486                         fprintf(stderr, "Available commands:\n"
487                                                         " help - This help command\n"
488                                                         " run - Run test\n"
489                                                         " set - Change variables\n"
490                                                         "\n");
491                 } else if (!strcmp(argv[0], "run")) {
492                         if (argc < 2) {
493                                 fprintf(stderr, "Usage: run TEST-NAME [OPTIONS...]\n");
494                         } else {
495                                 run_test(tctx, argv[1]);
496                         }
497                 }
498         }
499 }
500
501 /****************************************************************************
502   main program
503 ****************************************************************************/
504 int main(int argc,char *argv[])
505 {
506         int opt, i;
507         bool correct = true;
508         int max_runtime=0;
509         int argc_new;
510         struct torture_context *torture;
511         const struct torture_ui_ops *ui_ops;
512         char **argv_new;
513         poptContext pc;
514         static const char *target = "other";
515         struct dcerpc_binding *binding_struct;
516         NTSTATUS status;
517         int shell = False;
518         static const char *ui_ops_name = "simple";
519         static int list_tests = 0;
520         enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS, OPT_LIST,
521               OPT_DANGEROUS,OPT_SMB_PORTS,OPT_ASYNC,OPT_NUMPROGS, OPT_BASEDIR};
522         
523         struct poptOption long_options[] = {
524                 POPT_AUTOHELP
525                 {"format", 0, POPT_ARG_STRING, &ui_ops_name, 0, "Output format (one of: simple, subunit, harness)", NULL },
526                 {"smb-ports",   'p', POPT_ARG_STRING, NULL,     OPT_SMB_PORTS,  "SMB ports",    NULL},
527                 {"basedir",       0, POPT_ARG_STRING, NULL, OPT_BASEDIR, "base directory", "BSAEDIR" },
528                 {"seed",          0, POPT_ARG_INT,  &torture_seed,      0,      "seed",         NULL},
529                 {"num-progs",     0, POPT_ARG_INT,  NULL,       OPT_NUMPROGS,   "num progs",    NULL},
530                 {"num-ops",       0, POPT_ARG_INT,  &torture_numops,    0,      "num ops",      NULL},
531                 {"entries",       0, POPT_ARG_INT,  &torture_entries,   0,      "entries",      NULL},
532                 {"loadfile",      0, POPT_ARG_STRING,   NULL,   OPT_LOADFILE,   "loadfile",     NULL},
533                 {"list",          0, POPT_ARG_NONE, &list_tests, 0, NULL, NULL },
534                 {"unclist",       0, POPT_ARG_STRING,   NULL,   OPT_UNCLIST,    "unclist",      NULL},
535                 {"timelimit",   't', POPT_ARG_INT,      NULL,   OPT_TIMELIMIT,  "timelimit",    NULL},
536                 {"failures",    'f', POPT_ARG_INT,  &torture_failures,  0,      "failures",     NULL},
537                 {"parse-dns",   'D', POPT_ARG_STRING,   NULL,   OPT_DNS,        "parse-dns",    NULL},
538                 {"dangerous",   'X', POPT_ARG_NONE,     NULL,   OPT_DANGEROUS,
539                  "run dangerous tests (eg. wiping out password database)", NULL},
540                 {"shell",               0, POPT_ARG_NONE, &shell, True, "Run shell", NULL},
541                 {"target",              'T', POPT_ARG_STRING, &target, 0, "samba3|samba4|other", NULL},
542                 {"async",       'a', POPT_ARG_NONE,     NULL,   OPT_ASYNC,
543                  "run async tests", NULL},
544                 {"num-async",    0, POPT_ARG_INT,  &torture_numasync,  0,
545                  "number of simultaneous async requests", NULL},
546                 {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0, 
547                  "set maximum time for smbtorture to live", "seconds"},
548                 POPT_COMMON_SAMBA
549                 POPT_COMMON_CONNECTION
550                 POPT_COMMON_CREDENTIALS
551                 POPT_COMMON_VERSION
552                 { NULL }
553         };
554
555         setlinebuf(stdout);
556
557         /* we are never interested in SIGPIPE */
558         BlockSignals(true, SIGPIPE);
559
560         pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options, 
561                             POPT_CONTEXT_KEEP_FIRST);
562
563         poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ...");
564
565         while((opt = poptGetNextOpt(pc)) != -1) {
566                 switch (opt) {
567                 case OPT_LOADFILE:
568                         lp_set_cmdline("torture:loadfile", poptGetOptArg(pc));
569                         break;
570                 case OPT_UNCLIST:
571                         lp_set_cmdline("torture:unclist", poptGetOptArg(pc));
572                         break;
573                 case OPT_TIMELIMIT:
574                         lp_set_cmdline("torture:timelimit", poptGetOptArg(pc));
575                         break;
576                 case OPT_NUMPROGS:
577                         lp_set_cmdline("torture:nprocs", poptGetOptArg(pc));
578                         break;
579                 case OPT_BASEDIR:
580                         lp_set_cmdline("torture:basedir", poptGetOptArg(pc));
581                         break;
582                 case OPT_DNS:
583                         parse_dns(poptGetOptArg(pc));
584                         break;
585                 case OPT_DANGEROUS:
586                         lp_set_cmdline("torture:dangerous", "Yes");
587                         break;
588                 case OPT_ASYNC:
589                         lp_set_cmdline("torture:async", "Yes");
590                         break;
591                 case OPT_SMB_PORTS:
592                         lp_set_cmdline("smb ports", poptGetOptArg(pc));
593                         break;
594                 }
595         }
596
597         if (strcmp(target, "samba3") == 0) {
598                 lp_set_cmdline("torture:samba3", "true");
599         } else if (strcmp(target, "samba4") == 0) {
600                 lp_set_cmdline("torture:samba4", "true");
601         }
602
603         if (max_runtime) {
604                 /* this will only work if nobody else uses alarm(),
605                    which means it won't work for some tests, but we
606                    can't use the event context method we use for smbd
607                    as so many tests create their own event
608                    context. This will at least catch most cases. */
609                 signal(SIGALRM, max_runtime_handler);
610                 alarm(max_runtime);
611         }
612
613         torture_init();
614         ldb_global_init();
615
616         if (list_tests) {
617                 print_test_list();
618                 return 0;
619         }
620
621         if (torture_seed == 0) {
622                 torture_seed = time(NULL);
623         } 
624         printf("Using seed %d\n", torture_seed);
625         srandom(torture_seed);
626
627         argv_new = discard_const_p(char *, poptGetArgs(pc));
628
629         argc_new = argc;
630         for (i=0; i<argc; i++) {
631                 if (argv_new[i] == NULL) {
632                         argc_new = i;
633                         break;
634                 }
635         }
636
637         if (!(argc_new >= 3 || (shell && argc_new >= 2))) {
638                 usage(pc);
639                 exit(1);
640         }
641
642         /* see if its a RPC transport specifier */
643         status = dcerpc_parse_binding(talloc_autofree_context(), argv_new[1], &binding_struct);
644         if (NT_STATUS_IS_OK(status)) {
645                 lp_set_cmdline("torture:host", binding_struct->host);
646                 lp_set_cmdline("torture:share", "IPC$");
647                 lp_set_cmdline("torture:binding", argv_new[1]);
648         } else {
649                 char *binding = NULL;
650                 char *host = NULL, *share = NULL;
651
652                 if (!smbcli_parse_unc(argv_new[1], NULL, &host, &share)) {
653                         d_printf("Invalid option: %s is not a valid torture target (share or binding string)\n\n", argv_new[1]);
654                         usage(pc);
655                 }
656
657                 lp_set_cmdline("torture:host", host);
658                 lp_set_cmdline("torture:share", share);
659                 asprintf(&binding, "ncacn_np:%s", host);
660                 lp_set_cmdline("torture:binding", binding);
661         }
662
663         if (!strcmp(ui_ops_name, "simple")) {
664                 ui_ops = &std_ui_ops;
665         } else if (!strcmp(ui_ops_name, "subunit")) {
666                 ui_ops = &subunit_ui_ops;
667         } else if (!strcmp(ui_ops_name, "harness")) {
668                 ui_ops = &harness_ui_ops;
669         } else if (!strcmp(ui_ops_name, "quiet")) {
670                 ui_ops = &quiet_ui_ops;
671         } else {
672                 printf("Unknown output format '%s'\n", ui_ops_name);
673                 exit(1);
674         }
675
676         torture = torture_context_init(talloc_autofree_context(), ui_ops);
677
678         if (argc_new == 0) {
679                 printf("You must specify a test to run, or 'ALL'\n");
680         } else if (shell) {
681                 run_shell(torture);
682         } else {
683                 for (i=2;i<argc_new;i++) {
684                         if (!run_test(torture, argv_new[i])) {
685                                 correct = false;
686                         }
687                 }
688         }
689
690         if (torture->returncode) {
691                 return(0);
692         } else {
693                 return(1);
694         }
695 }