1 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <assert.h> 5 6 #include <string.h> 7 8 #define xstreq(x, y) !strcmp(x, y) 9 10 #include <err.h> 11 12 #include <getopt.h> 13 #include <sys/types.h> 14 #include <unistd.h> 15 #include <selinux/selinux.h> 16 #include <selinux/context.h> 17 18 #define TRUE 1 19 #define FALSE 0 20 21 #define SECON_CONF_PROG_NAME "secon" /* default program name */ 22 #define SECON_OPTS_SM "hVurtscmPRCfLp" /* small options available, print */ 23 #define SECON_OPTS_GO "hVurtlscmPRCf:L:p:" /* small options available, getopt */ 24 25 #define OPTS_FROM_ARG 0 26 #define OPTS_FROM_FILE 1 27 #define OPTS_FROM_LINK 2 28 #define OPTS_FROM_STDIN 3 29 #define OPTS_FROM_CUR 4 30 #define OPTS_FROM_CUREXE 5 31 #define OPTS_FROM_CURFS 6 32 #define OPTS_FROM_CURKEY 7 33 #define OPTS_FROM_PROC 8 34 #define OPTS_FROM_PROCEXE 9 35 #define OPTS_FROM_PROCFS 10 36 #define OPTS_FROM_PROCKEY 11 37 38 struct context_color_t { 39 unsigned int valid; 40 41 char *user_fg; 42 char *user_bg; 43 char *role_fg; 44 char *role_bg; 45 char *type_fg; 46 char *type_bg; 47 char *range_fg; 48 char *range_bg; 49 }; 50 51 struct { 52 unsigned int disp_user:1; 53 unsigned int disp_role:1; 54 unsigned int disp_type:1; 55 unsigned int disp_sen:1; 56 unsigned int disp_clr:1; 57 unsigned int disp_mlsr:1; 58 59 unsigned int disp_raw:1; 60 unsigned int disp_color:1; 61 62 unsigned int disp_prompt:1; /* no return, use : to sep */ 63 64 unsigned int from_type:8; /* 16 bits, uses 4 bits */ 65 66 union { 67 pid_t pid; 68 const char *file; 69 const char *link; 70 const char *arg; 71 } f; 72 } opts[1] = { { 73 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 74 FALSE, FALSE, FALSE, OPTS_FROM_ARG, {0} } }; 75 76 static void usage(const char *name, int exit_code) 77 { 78 fprintf(exit_code ? stderr : stdout, 79 " Usage: %s [-%s] [ context | - ]\n" 80 " --help -h Show this message.\n" 81 " --version -V Show the version.\n" 82 " --prompt -P Output in a format good for a prompt.\n" 83 " --user -u Show the user of the context.\n" 84 " --role -r Show the role of the context.\n" 85 " --type -t Show the type of the context.\n" 86 " --sensitivity -s Show the sensitivity level of the context.\n" 87 " --clearance -c Show the clearance level of the context.\n" 88 " --mls-range -m Show the sensitivity to clearance range of \n" 89 " the context.\n" 90 " --raw -R Show the context in \"raw\" format.\n" 91 " --color -C Output using ANSI color codes (requires -P).\n" 92 " --current, --self Get the context for the current process.\n" 93 " --current-exec, --self-exec Get the exec context for the current process.\n" 94 " --current-fs, --self-fs Get the fs context for the current process.\n" 95 " --current-key, --self-key Get the key context for the current process.\n" 96 " --parent Get the context for the parent process.\n" 97 " --parent-exec Get the exec context for the parent process.\n" 98 " --parent-fs Get the fs context for the parent process.\n" 99 " --parent-key Get the key context for the parent process.\n" 100 " --pid -p <arg> Use the context from the specified pid.\n" 101 " --pid-exec <arg> Use the exec context from the specified pid.\n" 102 " --pid-fs <arg> Use the fs context from the specified pid.\n" 103 " --pid-key <arg> Use the key context from the specified pid.\n" 104 " --file -f <arg> Use the context from the specified file.\n" 105 " --link -L <arg> Use the context from the specified link.\n", 106 name, SECON_OPTS_SM); 107 108 exit(exit_code); 109 } 110 111 static const char *opt_program_name(const char *argv0, const char *def) 112 { 113 if (argv0) { 114 if ((def = strrchr(argv0, '/'))) 115 ++def; 116 else 117 def = argv0; 118 119 /* hack for libtool */ 120 if ((strlen(def) > strlen("lt-")) 121 && !memcmp("lt-", def, strlen("lt-"))) 122 def += 3; 123 } 124 125 return (def); 126 } 127 128 static int disp_num(void) 129 { 130 int num = 0; 131 132 num += opts->disp_user; 133 num += opts->disp_role; 134 num += opts->disp_type; 135 num += opts->disp_sen; 136 num += opts->disp_clr; 137 num += opts->disp_mlsr; 138 139 return (num); 140 } 141 142 static int disp_none(void) 143 { 144 return (!disp_num()); 145 } 146 147 static int disp_multi(void) 148 { 149 return (disp_num() > 1); 150 } 151 152 static void cmd_line(int argc, char *argv[]) 153 { 154 int optchar = 0; 155 const char *program_name = NULL; 156 struct option long_options[] = { 157 {"help", no_argument, NULL, 'h'}, 158 {"version", no_argument, NULL, 'V'}, 159 160 {"prompt", no_argument, NULL, 'P'}, 161 162 {"user", no_argument, NULL, 'u'}, 163 {"role", no_argument, NULL, 'r'}, 164 {"type", no_argument, NULL, 't'}, 165 {"level", no_argument, NULL, 'l'}, /* compat. */ 166 {"sensitivity", no_argument, NULL, 's'}, 167 {"range", no_argument, NULL, 'm'}, 168 {"clearance", no_argument, NULL, 'c'}, 169 {"mls-range", no_argument, NULL, 'm'}, 170 171 {"raw", no_argument, NULL, 'R'}, 172 {"color", no_argument, NULL, 'C'}, 173 174 {"current", no_argument, NULL, 1}, 175 {"self", no_argument, NULL, 1}, 176 {"current-exec", no_argument, NULL, 2}, 177 {"self-exec", no_argument, NULL, 2}, 178 {"current-fs", no_argument, NULL, 3}, 179 {"self-fs", no_argument, NULL, 3}, 180 {"current-key", no_argument, NULL, 4}, 181 {"self-key", no_argument, NULL, 4}, 182 183 {"parent", no_argument, NULL, 5}, 184 {"parent-exec", no_argument, NULL, 6}, 185 {"parent-fs", no_argument, NULL, 7}, 186 {"parent-key", no_argument, NULL, 8}, 187 188 {"file", required_argument, NULL, 'f'}, 189 {"link", required_argument, NULL, 'L'}, 190 {"pid", required_argument, NULL, 'p'}, 191 {"pid-exec", required_argument, NULL, 9}, 192 {"pid-fs", required_argument, NULL, 10}, 193 {"pid-key", required_argument, NULL, 11}, 194 195 {NULL, 0, NULL, 0} 196 }; 197 int done = FALSE; 198 199 program_name = opt_program_name(argv[0], SECON_CONF_PROG_NAME); 200 201 while ((optchar = getopt_long(argc, argv, SECON_OPTS_GO, 202 long_options, NULL)) != -1) { 203 switch (optchar) { 204 case '?': 205 usage(program_name, EXIT_FAILURE); 206 case 'h': 207 usage(program_name, EXIT_SUCCESS); 208 case 'V': 209 fprintf(stdout, 210 " %s version %s.\n", program_name, VERSION); 211 exit(EXIT_SUCCESS); 212 213 case 'u': 214 done = TRUE; 215 opts->disp_user = !opts->disp_user; 216 break; 217 case 'r': 218 done = TRUE; 219 opts->disp_role = !opts->disp_role; 220 break; 221 case 't': 222 done = TRUE; 223 opts->disp_type = !opts->disp_type; 224 break; 225 case 'l': 226 done = TRUE; 227 opts->disp_sen = !opts->disp_sen; 228 break; 229 case 's': 230 done = TRUE; 231 opts->disp_sen = !opts->disp_sen; 232 break; 233 case 'c': 234 done = TRUE; 235 opts->disp_clr = !opts->disp_clr; 236 break; 237 case 'm': 238 done = TRUE; 239 opts->disp_mlsr = !opts->disp_mlsr; 240 break; 241 242 case 'P': 243 opts->disp_prompt = !opts->disp_prompt; 244 break; 245 246 case 'R': 247 opts->disp_raw = !opts->disp_raw; 248 break; 249 case 'C': 250 opts->disp_color = !opts->disp_color; 251 break; 252 case 1: 253 opts->from_type = OPTS_FROM_CUR; 254 break; 255 case 2: 256 opts->from_type = OPTS_FROM_CUREXE; 257 break; 258 case 3: 259 opts->from_type = OPTS_FROM_CURFS; 260 break; 261 case 4: 262 opts->from_type = OPTS_FROM_CURKEY; 263 break; 264 265 case 5: 266 opts->from_type = OPTS_FROM_PROC; 267 opts->f.pid = getppid(); 268 break; 269 case 6: 270 opts->from_type = OPTS_FROM_PROCEXE; 271 opts->f.pid = getppid(); 272 break; 273 case 7: 274 opts->from_type = OPTS_FROM_PROCFS; 275 opts->f.pid = getppid(); 276 break; 277 case 8: 278 opts->from_type = OPTS_FROM_PROCKEY; 279 opts->f.pid = getppid(); 280 break; 281 282 case 'f': 283 opts->from_type = OPTS_FROM_FILE; 284 opts->f.file = optarg; 285 break; 286 case 'L': 287 opts->from_type = OPTS_FROM_LINK; 288 opts->f.link = optarg; 289 break; 290 case 'p': 291 opts->from_type = OPTS_FROM_PROC; 292 opts->f.pid = atoi(optarg); 293 break; 294 case 9: 295 opts->from_type = OPTS_FROM_PROCEXE; 296 opts->f.pid = atoi(optarg); 297 break; 298 case 10: 299 opts->from_type = OPTS_FROM_PROCFS; 300 opts->f.pid = atoi(optarg); 301 break; 302 case 11: 303 opts->from_type = OPTS_FROM_PROCKEY; 304 opts->f.pid = atoi(optarg); 305 break; 306 307 default: 308 assert(FALSE); 309 } 310 } 311 312 if (!done) { /* defualt, if nothing specified */ 313 opts->disp_user = TRUE; 314 opts->disp_role = TRUE; 315 opts->disp_type = TRUE; 316 if (!opts->disp_prompt) { /* when displaying prompt, just output "normal" by default */ 317 opts->disp_sen = TRUE; 318 opts->disp_clr = TRUE; 319 } 320 opts->disp_mlsr = TRUE; 321 } 322 323 if (disp_none()) 324 err(EXIT_FAILURE, " Nothing to display"); 325 326 argc -= optind; 327 argv += optind; 328 329 if (!argc && (opts->from_type == OPTS_FROM_ARG) 330 && !isatty(STDIN_FILENO)) 331 opts->from_type = OPTS_FROM_STDIN; 332 if (!argc && (opts->from_type == OPTS_FROM_ARG)) 333 opts->from_type = OPTS_FROM_CUR; 334 335 if (opts->from_type == OPTS_FROM_ARG) { 336 opts->f.arg = argv[0]; 337 338 if (xstreq(argv[0], "-")) 339 opts->from_type = OPTS_FROM_STDIN; 340 } else if (!is_selinux_enabled()) 341 errx(EXIT_FAILURE, "SELinux is not enabled"); 342 } 343 344 static int my_getXcon_raw(pid_t pid, security_context_t * con, const char *val) 345 { 346 char buf[4096]; 347 FILE *fp = NULL; 348 const char *ptr = NULL; 349 350 snprintf(buf, sizeof(buf), "%s/%ld/attr/%s", "/proc", (long int)pid, 351 val); 352 353 if (!(fp = fopen(buf, "rb"))) 354 return (-1); 355 356 ptr = fgets(buf, sizeof(buf), fp); 357 358 fclose(fp); 359 360 *con = NULL; 361 if (ptr) { /* return *con = NULL, when proc file is empty */ 362 char *tmp = strchr(ptr, '\n'); 363 364 if (tmp) 365 *tmp = 0; 366 367 if (*ptr && !(*con = strdup(ptr))) 368 return (-1); 369 } 370 371 return (0); 372 } 373 374 static int my_getpidexeccon_raw(pid_t pid, security_context_t * con) 375 { 376 return (my_getXcon_raw(pid, con, "exec")); 377 } 378 static int my_getpidfscreatecon_raw(pid_t pid, security_context_t * con) 379 { 380 return (my_getXcon_raw(pid, con, "fscreate")); 381 } 382 static int my_getpidkeycreatecon_raw(pid_t pid, security_context_t * con) 383 { 384 return (my_getXcon_raw(pid, con, "keycreate")); 385 } 386 387 static security_context_t get_scon(void) 388 { 389 static char dummy_NIL[1] = ""; 390 security_context_t con = NULL, con_tmp; 391 int ret = -1; 392 393 switch (opts->from_type) { 394 case OPTS_FROM_ARG: 395 if (!(con_tmp = strdup(opts->f.arg))) 396 err(EXIT_FAILURE, 397 " Couldn't allocate security context"); 398 if (selinux_trans_to_raw_context(con_tmp, &con) < 0) 399 err(EXIT_FAILURE, 400 " Couldn't translate security context"); 401 freecon(con_tmp); 402 break; 403 404 case OPTS_FROM_STDIN: 405 { 406 char buf[4096] = ""; 407 char *ptr = buf; 408 409 while (!*ptr) { 410 if (!(ptr = fgets(buf, sizeof(buf), stdin))) 411 err(EXIT_FAILURE, 412 " Couldn't read security context"); 413 414 ptr += strspn(ptr, " \n\t"); 415 ptr[strcspn(ptr, " \n\t")] = 0; 416 } 417 418 if (!(con_tmp = strdup(ptr))) 419 err(EXIT_FAILURE, 420 " Couldn't allocate security context"); 421 if (selinux_trans_to_raw_context(con_tmp, &con) < 0) 422 err(EXIT_FAILURE, 423 " Couldn't translate security context"); 424 freecon(con_tmp); 425 break; 426 } 427 428 case OPTS_FROM_CUR: 429 ret = getcon_raw(&con); 430 431 if (ret == -1) 432 err(EXIT_FAILURE, 433 " Couldn't get current security context"); 434 break; 435 case OPTS_FROM_CUREXE: 436 ret = getexeccon_raw(&con); 437 438 if (ret == -1) 439 err(EXIT_FAILURE, 440 " Couldn't get current exec security context"); 441 442 if (!con) 443 con = strdup(dummy_NIL); 444 break; 445 case OPTS_FROM_CURFS: 446 ret = getfscreatecon_raw(&con); 447 448 if (ret == -1) 449 err(EXIT_FAILURE, 450 " Couldn't get current fs security context"); 451 452 if (!con) 453 con = strdup(dummy_NIL); 454 break; 455 case OPTS_FROM_CURKEY: 456 ret = getkeycreatecon_raw(&con); 457 458 if (ret == -1) 459 err(EXIT_FAILURE, 460 " Couldn't get current key security context"); 461 462 if (!con) 463 con = strdup(dummy_NIL); 464 break; 465 466 case OPTS_FROM_PROC: 467 ret = getpidcon_raw(opts->f.pid, &con); 468 469 if (ret == -1) 470 err(EXIT_FAILURE, 471 " Couldn't get security context for pid %lu", 472 (unsigned long)opts->f.pid); 473 break; 474 case OPTS_FROM_PROCEXE: 475 ret = my_getpidexeccon_raw(opts->f.pid, &con); 476 477 if (ret == -1) 478 err(EXIT_FAILURE, 479 " Couldn't get security context for pid %lu", 480 (unsigned long)opts->f.pid); 481 482 if (!con) 483 con = strdup(dummy_NIL); 484 break; 485 case OPTS_FROM_PROCFS: 486 ret = my_getpidfscreatecon_raw(opts->f.pid, &con); 487 488 if (ret == -1) 489 err(EXIT_FAILURE, 490 " Couldn't get security context for pid %lu", 491 (unsigned long)opts->f.pid); 492 493 if (!con) 494 con = strdup(dummy_NIL); 495 /* disabled -- override with normal context ... 496 { 497 opts->from_type = OPTS_FROM_PROC; 498 return (get_scon()); 499 } */ 500 break; 501 case OPTS_FROM_PROCKEY: 502 ret = my_getpidkeycreatecon_raw(opts->f.pid, &con); 503 504 if (ret == -1) 505 err(EXIT_FAILURE, 506 " Couldn't get security context for pid %lu", 507 (unsigned long)opts->f.pid); 508 509 if (!con) 510 con = strdup(dummy_NIL); 511 break; 512 513 case OPTS_FROM_FILE: 514 ret = getfilecon_raw(opts->f.file, &con); 515 516 if (ret == -1) 517 err(EXIT_FAILURE, 518 " Couldn't get security context for file %s", 519 opts->f.file); 520 break; 521 522 case OPTS_FROM_LINK: 523 ret = lgetfilecon_raw(opts->f.link, &con); 524 525 if (ret == -1) 526 err(EXIT_FAILURE, 527 " Couldn't get security context for symlink %s", 528 opts->f.link); 529 break; 530 531 default: 532 assert(FALSE); 533 } 534 535 return (con); 536 } 537 538 static unsigned int disp__color_to_ansi(const char *color_str) 539 { 540 int val = 30; 541 542 /* NOTE: ansi black is 30 for foreground colors */ 543 544 /* red */ 545 if (strncasecmp(&color_str[1], "7f", 2) >= 0) 546 val += 1; 547 /* green */ 548 if (strncasecmp(&color_str[3], "7f", 2) >= 0) 549 val += 2; 550 /* blue */ 551 if (strncasecmp(&color_str[5], "7f", 2) >= 0) 552 val += 4; 553 554 return val; 555 } 556 557 static char *disp__con_color_ansi(const char *name, 558 struct context_color_t *color) 559 { 560 unsigned int fg, bg; 561 char *ansi; 562 int ansi_len = strlen("\e[99;99m") + 1; 563 564 /* NOTE: ansi background codes are the same as foreground codes +10 */ 565 566 if (xstreq("user", name)) { 567 fg = disp__color_to_ansi(color->user_fg); 568 bg = disp__color_to_ansi(color->user_bg) + 10; 569 } else if (xstreq("role", name)) { 570 fg = disp__color_to_ansi(color->role_fg); 571 bg = disp__color_to_ansi(color->role_bg) + 10; 572 } else if (xstreq("type", name)) { 573 fg = disp__color_to_ansi(color->type_fg); 574 bg = disp__color_to_ansi(color->type_bg) + 10; 575 } else if (xstreq("sensitivity", name) || 576 xstreq("clearance", name) || 577 xstreq("mls-range", name)) { 578 fg = disp__color_to_ansi(color->range_fg); 579 bg = disp__color_to_ansi(color->range_bg) + 10; 580 } else 581 err(EXIT_FAILURE, " No color information for context field"); 582 583 if (!(ansi = malloc(ansi_len))) 584 err(EXIT_FAILURE, " Unable to allocate memory"); 585 if (snprintf(ansi, ansi_len, "\e[%d;%dm", fg, bg) > ansi_len) 586 err(EXIT_FAILURE, " Unable to convert colors to ANSI codes"); 587 588 return ansi; 589 } 590 591 static void disp__con_val(const char *name, const char *val, 592 struct context_color_t *color) 593 { 594 static int done = FALSE; 595 596 assert(name); 597 assert(color); 598 599 if (!val) 600 val = ""; /* targeted has no "level" etc., 601 any errors should happen at context_new() time */ 602 603 if (opts->disp_prompt) { 604 if (xstreq("mls-range", name) && !*val) 605 return; /* skip, mls-range if it's empty */ 606 607 if (opts->disp_color && color->valid) { 608 char *ansi = disp__con_color_ansi(name, color); 609 fprintf(stdout, "%s", ansi); 610 free(ansi); 611 } 612 fprintf(stdout, "%s%s", done ? ":" : "", val); 613 if (opts->disp_color && color->valid) 614 fprintf(stdout, "\e[0m"); 615 } else if (disp_multi()) 616 fprintf(stdout, "%s: %s\n", name, val); 617 else 618 fprintf(stdout, "%s\n", val); 619 620 done = TRUE; 621 } 622 623 static void disp_con(security_context_t scon_raw) 624 { 625 security_context_t scon_trans, scon; 626 context_t con = NULL; 627 char *color_str = NULL; 628 struct context_color_t color = { .valid = 0 }; 629 630 selinux_raw_to_trans_context(scon_raw, &scon_trans); 631 if (opts->disp_raw) 632 scon = scon_raw; 633 else 634 scon = scon_trans; 635 636 if (!*scon) { /* --self-exec and --self-fs etc. */ 637 if (opts->disp_user) 638 disp__con_val("user", NULL, &color); 639 if (opts->disp_role) 640 disp__con_val("role", NULL, &color); 641 if (opts->disp_type) 642 disp__con_val("type", NULL, &color); 643 if (opts->disp_sen) 644 disp__con_val("sensitivity", NULL, &color); 645 if (opts->disp_clr) 646 disp__con_val("clearance", NULL, &color); 647 if (opts->disp_mlsr) 648 disp__con_val("mls-range", NULL, &color); 649 return; 650 } 651 652 if (opts->disp_color) { 653 if (selinux_raw_context_to_color(scon_raw, &color_str) < 0) 654 errx(EXIT_FAILURE, "Couldn't determine colors for: %s", 655 scon); 656 657 color.user_fg = strtok(color_str, " "); 658 if (!color.user_fg) 659 errx(EXIT_FAILURE, "Invalid color string"); 660 color.user_bg = strtok(NULL, " "); 661 if (!color.user_bg) 662 errx(EXIT_FAILURE, "Invalid color string"); 663 664 color.role_fg = strtok(NULL, " "); 665 if (!color.role_fg) 666 errx(EXIT_FAILURE, "Invalid color string"); 667 color.role_bg = strtok(NULL, " "); 668 if (!color.role_bg) 669 errx(EXIT_FAILURE, "Invalid color string"); 670 671 color.type_fg = strtok(NULL, " "); 672 if (!color.type_fg) 673 errx(EXIT_FAILURE, "Invalid color string"); 674 color.type_bg = strtok(NULL, " "); 675 if (!color.type_bg) 676 errx(EXIT_FAILURE, "Invalid color string"); 677 678 color.range_fg = strtok(NULL, " "); 679 if (!color.range_fg) 680 errx(EXIT_FAILURE, "Invalid color string"); 681 color.range_bg = strtok(NULL, " "); 682 683 color.valid = 1; 684 }; 685 686 if (!(con = context_new(scon))) 687 errx(EXIT_FAILURE, "Couldn't create context from: %s", scon); 688 689 if (opts->disp_user) { 690 disp__con_val("user", context_user_get(con), &color); 691 } 692 if (opts->disp_role) { 693 disp__con_val("role", context_role_get(con), &color); 694 } 695 if (opts->disp_type) { 696 disp__con_val("type", context_type_get(con), &color); 697 } 698 if (opts->disp_sen) { 699 const char *val = NULL; 700 char *tmp = NULL; 701 702 val = context_range_get(con); 703 if (!val) 704 val = ""; /* targeted has no "level" etc., 705 any errors should happen at context_new() time */ 706 707 tmp = strdup(val); 708 if (!tmp) 709 errx(EXIT_FAILURE, "Couldn't create context from: %s", 710 scon); 711 if (strchr(tmp, '-')) 712 *strchr(tmp, '-') = 0; 713 714 disp__con_val("sensitivity", tmp, &color); 715 716 free(tmp); 717 } 718 if (opts->disp_clr) { 719 const char *val = NULL; 720 char *tmp = NULL; 721 722 val = context_range_get(con); 723 if (!val) 724 val = ""; /* targeted has no "level" etc., 725 any errors should happen at context_new() time */ 726 727 tmp = strdup(val); 728 if (!tmp) 729 errx(EXIT_FAILURE, "Couldn't create context from: %s", 730 scon); 731 if (strchr(tmp, '-')) 732 disp__con_val("clearance", strchr(tmp, '-') + 1, &color); 733 else 734 disp__con_val("clearance", tmp, &color); 735 736 free(tmp); 737 } 738 739 if (opts->disp_mlsr) 740 disp__con_val("mls-range", context_range_get(con), &color); 741 742 context_free(con); 743 freecon(scon_trans); 744 if (color_str) 745 free(color_str); 746 } 747 748 int main(int argc, char *argv[]) 749 { 750 security_context_t scon_raw = NULL; 751 752 cmd_line(argc, argv); 753 754 scon_raw = get_scon(); 755 756 disp_con(scon_raw); 757 758 freecon(scon_raw); 759 760 exit(EXIT_SUCCESS); 761 } 762