fcntl16.c revision 45e285d46ab47b0ff76c88acb5ba97b0bd5f753d
1/* 2 * 3 * Copyright (c) International Business Machines Corp., 2001 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20/* 21 * NAME 22 * fcntl16.c 23 * 24 * DESCRIPTION 25 * Additional file locking test cases for checking proper notifictaion 26 * of processes on lock change 27 * 28 * ALGORITHM 29 * Various test cases are used to lock a file opened without mandatory 30 * locking, with madatory locking and mandatory locking with NOBLOCK. 31 * Checking that processes waiting on lock boundaries are notified 32 * properly when boundaries change 33 * 34 * USAGE 35 * fcntl16 36 * 37 * HISTORY 38 * 07/2001 Ported by Wayne Boyer 39 * 04/2002 wjhuie sigset cleanups 40 * 41 * RESTRICTIONS 42 * None 43 */ 44 45#include <fcntl.h> 46#include <signal.h> 47#include <errno.h> 48#include <test.h> 49#include <usctest.h> 50#include <sys/stat.h> 51#include <sys/types.h> 52#include <sys/wait.h> 53 54#define SKIPVAL 0x0f00 55//#define SKIP SKIPVAL, 0, 0L, 0L, IGNORED 56#define SKIP 0,0,0L,0L,0 57#if (SKIPVAL == F_RDLCK) || (SKIPVAL == F_WRLCK) 58#error invalid SKIP, must not be F_RDLCK or F_WRLCK 59#endif 60 61#define IGNORED 0 62#define NOBLOCK 2 /* immediate success */ 63#define WILLBLOCK 3 /* blocks, succeeds, parent unlocks records */ 64#define TIME_OUT 10 65 66typedef struct { 67 short type; 68 short whence; 69 long start; 70 long len; 71 short flag; 72} lock; 73 74typedef struct { 75 lock parent_a; 76 lock parent_b; 77 lock child_a; 78 lock child_b; 79 lock parent_c; 80 lock parent_d; 81} testcase; 82 83static testcase testcases[] = { 84 /* #1 Parent_a making a write lock on entire file */ 85 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 86 /* Parent_b skipped */ 87 {SKIP}, 88 /* Child_a read lock on byte 1 to byte 5 */ 89 {F_RDLCK, 0, 0L, 5L, NOBLOCK}, 90 /* Child_b read lock on byte 6 to byte 10 */ 91 {F_RDLCK, 0, 6L, 5L, NOBLOCK}, 92 /* 93 * Parent_c read lock on entire file 94 */ 95 {F_RDLCK, 0, 0L, 0L, IGNORED}, 96 /* Parent_d skipped */ 97 {SKIP},}, 98 99 /* #2 Parent_a making a write lock on entire file */ 100 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 101 /* Parent_b skipped */ 102 {SKIP}, 103 /* Child_a read lock on byte 1 to byte 5 */ 104 {F_RDLCK, 0, 0L, 5L, WILLBLOCK}, 105 /* Child_b read lock on byte 6 to byte 10 */ 106 {F_RDLCK, 0, 6L, 5L, WILLBLOCK}, 107 /* 108 * Parent_c write lock on entire 109 * file 110 */ 111 {F_WRLCK, 0, 0L, 0L, IGNORED}, 112 /* Parent_d skipped */ 113 {SKIP},}, 114 115 /* #3 Parent_a making a write lock on entire file */ 116 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 117 /* Parent_b skipped */ 118 {SKIP}, 119 /* Child_a read lock on byte 2 to byte 4 */ 120 {F_RDLCK, 0, 2L, 3L, WILLBLOCK}, 121 /* Child_b read lock on byte 6 to byte 8 */ 122 {F_RDLCK, 0, 6L, 3L, WILLBLOCK}, 123 /* 124 * Parent_c read lock on byte 3 to 125 * byte 7 126 */ 127 {F_RDLCK, 0, 3L, 5L, IGNORED}, 128 /* Parent_d skipped */ 129 {SKIP},}, 130 131 /* #4 Parent_a making a write lock on entire file */ 132 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 133 /* Parent_b skipped */ 134 {SKIP}, 135 /* Child_a read lock on byte 2 to byte 4 */ 136 {F_RDLCK, 0, 2L, 3L, WILLBLOCK}, 137 /* Child_b read lock on byte 6 to byte 8 */ 138 {F_RDLCK, 0, 6L, 3L, NOBLOCK}, 139 /* 140 * Parent_c read lock on byte 5 to 141 * byte 9 142 */ 143 {F_RDLCK, 0, 5L, 5L, IGNORED}, 144 /* Parent_d skipped */ 145 {SKIP},}, 146 147 /* #5 Parent_a making a write lock on entire file */ 148 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 149 /* Parent_b skipped */ 150 {SKIP}, 151 /* Child_a read lock on byte 3 to byte 7 */ 152 {F_RDLCK, 0, 3L, 5L, NOBLOCK}, 153 /* Child_b read lock on byte 5 to byte 10 */ 154 {F_RDLCK, 0, 5L, 6L, WILLBLOCK}, 155 /* 156 * Parent_c read lock on byte 2 to 157 * byte 8 158 */ 159 {F_RDLCK, 0, 2L, 7L, IGNORED}, 160 /* Parent_d skipped */ 161 {SKIP},}, 162 163 /* #6 Parent_a making a write lock on entire file */ 164 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 165 /* Parent_b skipped */ 166 {SKIP}, 167 /* Child_a read lock on byte 2 to byte 4 */ 168 {F_RDLCK, 0, 2L, 3L, WILLBLOCK}, 169 /* Child_b write lock on byte 6 to byte 8 */ 170 {F_RDLCK, 0, 6L, 3L, NOBLOCK}, 171 /* Parent_c no lock on byte 3 to 9 */ 172 {F_UNLCK, 0, 3L, 7L, IGNORED}, 173 /* Parent_d skipped */ 174 {SKIP},}, 175 176 /* #7 Parent_a making a write lock on entire file */ 177 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 178 /* Parent_b read lock on byte 3 to byte 7 */ 179 {F_RDLCK, 0, 3L, 5L, IGNORED}, 180 /* Child_a read lock on byte 2 to byte 4 */ 181 {F_RDLCK, 0, 2L, 3L, NOBLOCK}, 182 /* Child_b read lock on byte 6 to byte 8 */ 183 {F_RDLCK, 0, 6L, 3L, NOBLOCK}, 184 /* 185 * Parent_c read lock on byte 1 to 186 * byte 9 187 */ 188 {F_RDLCK, 0, 1L, 9L, IGNORED}, 189 /* Parent_d skipped */ 190 {SKIP},}, 191 192 /* #8 Parent_a making a write lock on byte 2 to byte 4 */ 193 {{F_WRLCK, 0, 2L, 3L, IGNORED}, 194 /* Parent_b write lock on byte 6 to byte 8 */ 195 {F_WRLCK, 0, 6L, 3L, IGNORED}, 196 /* Child_a read lock on byte 3 to byte 7 */ 197 {F_RDLCK, 0, 3L, 5L, NOBLOCK}, 198 /* Child_b skipped */ 199 {SKIP}, 200 /* 201 * Parent_c read lock on byte 1 to 202 * byte 5 203 */ 204 {F_RDLCK, 0, 1L, 5L, IGNORED}, 205 /* 206 * Parent_d read lock on 207 * byte 5 to byte 9 208 */ 209 {F_RDLCK, 0, 5L, 5L, 210 IGNORED},}, 211 212 /* #9 Parent_a making a write lock on entire file */ 213 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 214 /* Parent_b read lock on byte 3 to byte 7 */ 215 {F_RDLCK, 0, 3L, 5L, IGNORED}, 216 /* Child_a read lock on byte 2 to byte 4 */ 217 {F_RDLCK, 0, 2L, 3L, NOBLOCK}, 218 /* Child_b read lock on byte 6 to byte 8 */ 219 {F_RDLCK, 0, 6L, 3L, NOBLOCK}, 220 /* 221 * Parent_c read lock on byte 1 to 222 * byte 3 223 */ 224 {F_RDLCK, 0, 1L, 3L, IGNORED}, 225 /* 226 * Parent_d read lock on 227 * byte 7 to byte 9 228 */ 229 {F_RDLCK, 0, 7L, 3L, 230 IGNORED},}, 231 232 /* #10 Parent_a making a write lock on entire file */ 233 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 234 /* Parent_b skipped */ 235 {SKIP}, 236 /* Child_a read lock on byte 2 to byte 4 */ 237 {F_RDLCK, 0, 2L, 3L, NOBLOCK}, 238 /* Child_b read lock on byte 6 to byte 8 */ 239 {F_RDLCK, 0, 6L, 3L, NOBLOCK}, 240 /* 241 * Parent_c read lock on byte 1 to 242 * byte 7 243 */ 244 {F_RDLCK, 0, 1L, 7L, IGNORED}, 245 /* 246 * Parent_d read lock on 247 * byte 3 to byte 9 248 */ 249 {F_RDLCK, 0, 3L, 7L, 250 IGNORED},}, 251 252 /* #11 Parent_a making a write lock on entire file */ 253 {{F_WRLCK, 0, 0L, 0L, IGNORED}, 254 /* Parent_b skipped */ 255 {SKIP}, 256 /* Child_a read lock on byte 3 to byte 7 */ 257 {F_RDLCK, 0, 3L, 5L, NOBLOCK}, 258 /* Child_b read lock on byte 3 to byte 7 */ 259 {F_RDLCK, 0, 3L, 5L, NOBLOCK}, 260 /* 261 * Parent_c read lock on byte 3 to 262 * byte 7 263 */ 264 {F_RDLCK, 0, 3L, 5L, IGNORED}, 265 /* Parent_d skipped */ 266 {SKIP},}, 267}; 268 269static testcase *thiscase; 270static lock *thislock; 271static int parent; 272static int child_flag1 = 0; 273static int child_flag2 = 0; 274static int parent_flag = 0; 275static int alarm_flag = 0; 276static int child_pid[2], flag[2]; 277static int fd; 278static int test; 279static char tmpname[40]; 280 281#define FILEDATA "tenbytes!" 282 283extern void catch_usr1(); /* signal catching subroutine */ 284extern void catch_usr2(); /* signal catching subroutine */ 285extern void catch_int(); /* signal catching subroutine */ 286extern void catch_alarm(); /* signal catching subroutine */ 287 288char *TCID = "fcntl16"; 289int TST_TOTAL = 1; 290extern int Tst_count; 291 292#ifdef UCLINUX 293static char *argv0; 294#endif 295 296/* 297 * cleanup - performs all the ONE TIME cleanup for this test at completion or 298 * premature exit 299 */ 300void cleanup(void) 301{ 302 TEST_CLEANUP; 303 304 tst_rmdir(); 305 306 tst_exit(); 307} 308 309void dochild(int kid) 310{ 311 /* child process */ 312 struct sigaction sact; 313 sact.sa_flags = 0; 314 sact.sa_handler = catch_int; 315 (void)sigaction(SIGUSR1, &sact, NULL); 316 317 /* Lock should succeed after blocking and parent releases lock */ 318 if (kid) { 319 if ((kill(parent, SIGUSR2)) < 0) { 320 tst_resm(TFAIL, "Attempt to send signal to parent " 321 "failed"); 322 tst_resm(TFAIL, "Test case %d, child %d, errno = %d", 323 test + 1, kid, errno); 324 exit(1); 325 } 326 } else { 327 if ((kill(parent, SIGUSR1)) < 0) { 328 tst_resm(TFAIL, "Attempt to send signal to parent " 329 "failed"); 330 tst_resm(TFAIL, "Test case %d, child %d, errno = %d", 331 test + 1, kid, errno); 332 exit(1); 333 } 334 } 335 336 if ((fcntl(fd, F_SETLKW, thislock)) < 0) { 337 if (errno == EINTR && parent_flag) { 338 /* 339 * signal received is waiting for lock to clear, 340 * this is expected if flag = WILLBLOCK 341 */ 342 exit(1); 343 } else { 344 tst_resm(TFAIL, "Attempt to set child BLOCKING lock " 345 "failed"); 346 tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, 347 errno); 348 exit(2); 349 } 350 } 351 exit(0); 352} /* end of child process */ 353 354#ifdef UCLINUX 355static int kid_uc; 356 357void dochild_uc() 358{ 359 dochild(kid_uc); 360} 361#endif 362 363void catch_alarm() 364{ 365 alarm_flag = 1; 366} 367 368void catch_usr1() 369{ /* invoked on catching SIGUSR1 */ 370 /* 371 * Set flag to let parent know that child #1 is ready to have the 372 * lock removed 373 */ 374 child_flag1 = 1; 375} 376 377void catch_usr2() 378{ /* invoked on catching SIGUSR2 */ 379 /* 380 * Set flag to let parent know that child #2 is ready to have the 381 * lock removed 382 */ 383 child_flag2 = 1; 384} 385 386void catch_int() 387{ /* invoked on child catching SIGUSR1 */ 388 /* 389 * Set flag to interrupt fcntl call in child and force a controlled 390 * exit 391 */ 392 parent_flag = 1; 393} 394 395void child_sig(int sig, int nkids) 396{ 397 int i; 398 399 for (i = 0; i < nkids; i++) { 400 if (kill(child_pid[i], 0) == 0) { 401 if ((kill(child_pid[i], sig)) < 0) { 402 tst_resm(TFAIL, "Attempt to signal child %d, " 403 "failed", i + 1); 404 } 405 } 406 } 407} 408 409/* 410 * setup - performs all ONE TIME steup for this test 411 */ 412void setup(void) 413{ 414 struct sigaction sact; 415 416 /* capture signals */ 417 tst_sig(FORK, DEF_HANDLER, cleanup); 418 419 umask(0); 420 421 /* Pause if option was specified */ 422 TEST_PAUSE; 423 424 parent = getpid(); 425 426 tst_tmpdir(); 427 428 /* set up temp filename */ 429 sprintf(tmpname, "fcntl4.%d", parent); 430 431 /* 432 * Set up signal handling functions 433 */ 434 memset(&sact, 0, sizeof(sact)); 435 sact.sa_handler = catch_usr1; 436 sigemptyset(&sact.sa_mask); 437 sigaddset(&sact.sa_mask, SIGUSR1); 438 sigaction(SIGUSR1, &sact, NULL); 439 440 memset(&sact, 0, sizeof(sact)); 441 sact.sa_handler = catch_usr2; 442 sigemptyset(&sact.sa_mask); 443 sigaddset(&sact.sa_mask, SIGUSR2); 444 sigaction(SIGUSR2, &sact, NULL); 445 446 memset(&sact, 0, sizeof(sact)); 447 sact.sa_handler = catch_alarm; 448 sigemptyset(&sact.sa_mask); 449 sigaddset(&sact.sa_mask, SIGALRM); 450 sigaction(SIGALRM, &sact, NULL); 451} 452 453int run_test(int file_flag, int file_mode, int start, int end) 454{ 455 int child_count; 456 int child; 457 int nexited; 458 int status, expect_stat; 459 int i, fail = 0; 460 461 /* loop through all test cases */ 462 for (test = start; test < end; test++) { 463 /* open a temp file to lock */ 464 fd = open(tmpname, file_flag, file_mode); 465 if (fd < 0) { 466 tst_brkm(TBROK, cleanup, "open failed"); 467 /*NOTREACHED*/} 468 469 /* write some dummy data to the file */ 470 (void)write(fd, FILEDATA, 10); 471 472 /* Initialize first parent lock structure */ 473 thiscase = &testcases[test]; 474 thislock = &thiscase->parent_a; 475 476 /* set the initial parent lock on the file */ 477 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 478 tst_resm(TFAIL, "First parent lock failed"); 479 tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, 480 errno); 481 unlink(tmpname); 482 return 1; 483 } 484 485 /* Initialize second parent lock structure */ 486 thislock = &thiscase->parent_b; 487 488 if ((thislock->type) != IGNORED) { /*SKIPVAL */ 489 /* set the second parent lock */ 490 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 491 tst_resm(TFAIL, "Second parent lock failed"); 492 tst_resm(TFAIL, "Test case %d, errno = %d", 493 test + 1, errno); 494 unlink(tmpname); 495 return 1; 496 } 497 } 498 499 /* Initialize first child lock structure */ 500 thislock = &thiscase->child_a; 501 502 /* Initialize child counter and flags */ 503 alarm_flag = parent_flag = 0; 504 child_flag1 = child_flag2 = 0; 505 child_count = 0; 506 507 /* spawn child processes */ 508 for (i = 0; i < 2; i++) { 509 if (thislock->type != IGNORED) { 510 if ((child = FORK_OR_VFORK()) == 0) { 511#ifdef UCLINUX 512 if (self_exec(argv0, "ddddd", i, parent, 513 test, thislock, fd) < 0) { 514 perror("self_exec failed"); 515 return 1; 516 } 517#else 518 dochild(i); 519#endif 520 } 521 if (child < 0) { 522 perror("Fork failed"); 523 return 1; 524 } 525 child_count++; 526 child_pid[i] = child; 527 flag[i] = thislock->flag; 528 } 529 /* Initialize second child lock structure */ 530 thislock = &thiscase->child_b; 531 } 532 /* parent process */ 533 534 /* 535 * Wait for children to signal they are ready. Set a timeout 536 * just in case they don't signal at all. 537 */ 538 alarm(TIME_OUT); 539 540 while (!alarm_flag 541 && (child_flag1 + child_flag2 != child_count)) { 542 pause(); 543 } 544 545 /* 546 * Turn off alarm and unmask signals 547 */ 548 alarm((unsigned)0); 549 550 if (child_flag1 + child_flag2 != child_count) { 551 tst_resm(TFAIL, "Test case %d: kids didn't signal", 552 test + 1); 553 fail = 1; 554 } 555 child_flag1 = child_flag2 = alarm_flag = 0; 556 557 thislock = &thiscase->parent_c; 558 559 /* set the third parent lock on the file */ 560 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 561 tst_resm(TFAIL, "Third parent lock failed"); 562 tst_resm(TFAIL, "Test case %d, errno = %d", 563 test + 1, errno); 564 unlink(tmpname); 565 return 1; 566 } 567 568 /* Initialize fourth parent lock structure */ 569 thislock = &thiscase->parent_d; 570 571 if ((thislock->type) != IGNORED) { /*SKIPVAL */ 572 /* set the fourth parent lock */ 573 if ((fcntl(fd, F_SETLK, thislock)) < 0) { 574 tst_resm(TINFO, "Fourth parent lock failed"); 575 tst_resm(TINFO, "Test case %d, errno = %d", 576 test + 1, errno); 577 unlink(tmpname); 578 return 1; 579 } 580 } 581 582 /* 583 * Wait for children to exit, or for timeout to occur. 584 * Timeouts are expected for testcases where kids are 585 * 'WILLBLOCK', In that case, send kids a wakeup interrupt 586 * and wait again for them. If a second timeout occurs, then 587 * something is wrong. 588 */ 589 alarm_flag = nexited = 0; 590 while (nexited < child_count) { 591 alarm(TIME_OUT); 592 child = wait(&status); 593 alarm(0); 594 595 if (child == -1) { 596 if (errno != EINTR || alarm_flag != 1) { 597 /* 598 * Some error other than a timeout, 599 * or else this is the second 600 * timeout. Both cases are errors. 601 */ 602 break; 603 } 604 605 /* 606 * Expected timeout case. Signal kids then 607 * go back and wait again 608 */ 609 child_sig(SIGUSR1, child_count); 610 continue; 611 } 612 613 for (i = 0; i < child_count; i++) 614 if (child == child_pid[i]) 615 break; 616 if (i == child_count) { 617 /* 618 * Ignore unexpected kid, it could be a 619 * leftover from a previous iteration that 620 * timed out. 621 */ 622 continue; 623 } 624 625 /* Found the right kid, check his status */ 626 nexited++; 627 628 expect_stat = (flag[i] == NOBLOCK) ? 0 : 1; 629 630 if (!WIFEXITED(status) 631 || WEXITSTATUS(status) != expect_stat) { 632 /* got unexpected exit status from kid */ 633 tst_resm(TFAIL, "Test case %d: child %d %s " 634 "or got bad status (x%x)", test + 1, 635 i, (flag[i] == NOBLOCK) ? 636 "BLOCKED unexpectedly" : 637 "failed to BLOCK", status); 638 fail = 1; 639 } 640 } 641 642 if (nexited != child_count) { 643 tst_resm(TFAIL, "Test case %d, caught %d expected %d " 644 "children", test + 1, nexited, child_count); 645 child_sig(SIGKILL, nexited); 646 fail = 1; 647 } 648 close(fd); 649 } 650 unlink(tmpname); 651 if (fail) { 652 return 1; 653 } else { 654 return 0; 655 } 656 return 0; 657} 658 659int main(int ac, char **av) 660{ 661 662 int lc; /* loop counter */ 663 char *msg; /* message returned from parse_opts */ 664 665 /* parse standard options */ 666 if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) { 667 tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg); 668 } 669#ifdef UCLINUX 670 maybe_run_child(dochild_uc, "ddddd", &kid_uc, &parent, &test, 671 &thislock, &fd); 672 argv0 = av[0]; 673#endif 674 675 setup(); /* global setup */ 676 677 /* check looping state if -i option given */ 678 for (lc = 0; TEST_LOOPING(lc); lc++) { 679 /* reset Tst_count in case we are looping */ 680 Tst_count = 0; 681 682/* //block1: */ 683 /* 684 * Check file locks on an ordinary file without 685 * mandatory locking 686 */ 687 tst_resm(TINFO, "Entering block 1"); 688 if (run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 0, 11)) { 689 tst_resm(TINFO, "Test case 1: without mandatory " 690 "locking FAILED"); 691 } else { 692 tst_resm(TINFO, "Test case 1: without manadatory " 693 "locking PASSED"); 694 } 695 tst_resm(TINFO, "Exiting block 1"); 696 697/* //block2: */ 698 /* 699 * Check the file locks on a file with mandatory record 700 * locking 701 */ 702 tst_resm(TINFO, "Entering block 2"); 703 if (run_test(O_CREAT | O_RDWR | O_TRUNC, S_ISGID | 704 S_IRUSR | S_IWUSR, 0, 11)) { 705 tst_resm(TINFO, "Test case 2: with mandatory record " 706 "locking FAILED"); 707 } else { 708 tst_resm(TINFO, "Test case 2: with mandatory record " 709 "locking PASSED"); 710 } 711 tst_resm(TINFO, "Exiting block 2"); 712 713/* //block3: */ 714 /* 715 * Check file locks on a file with mandatory record locking 716 * and no delay 717 */ 718 tst_resm(TINFO, "Entering block 3"); 719 if (run_test(O_CREAT | O_RDWR | O_TRUNC | O_NDELAY, 720 S_ISGID | S_IRUSR | S_IWUSR, 0, 11)) { 721 tst_resm(TINFO, "Test case 3: mandatory locking with " 722 "NODELAY FAILED"); 723 } else { 724 tst_resm(TINFO, "Test case 3: mandatory locking with " 725 "NODELAY PASSED"); 726 } 727 tst_resm(TINFO, "Exiting block 3"); 728 } 729 cleanup(); 730 return 0; 731} 732