/* Process a line of configuration file or a command-line option. */ static void dooption(struct bsdtar *bsdtar, const char * conf_opt, const char * conf_arg, int fromconffile) { struct stat st; char *eptr; if (strcmp(conf_opt, "aggressive-networking") == 0) { if (bsdtar->mode != 'c') goto badmode; if (bsdtar->option_aggressive_networking_set) goto optset; tarsnap_opt_aggressive_networking = 1; bsdtar->option_aggressive_networking_set = 1; } else if (strcmp(conf_opt, "cachedir") == 0) { if (bsdtar->cachedir != NULL) goto optset; if (conf_arg == NULL) goto needarg; if ((bsdtar->cachedir = strdup(conf_arg)) == NULL) bsdtar_errc(bsdtar, 1, errno, "Out of memory"); } else if (strcmp(conf_opt, "checkpoint-bytes") == 0) { if (bsdtar->mode != 'c') goto badmode; if (tarsnap_opt_checkpointbytes != (uint64_t)(-1)) goto optset; if (conf_arg == NULL) goto needarg; if (humansize_parse(conf_arg, &tarsnap_opt_checkpointbytes)) bsdtar_errc(bsdtar, 1, 0, "Cannot parse #bytes per checkpoint: %s", conf_arg); if (tarsnap_opt_checkpointbytes < 1000000) bsdtar_errc(bsdtar, 1, 0, "checkpoint-bytes value must be at least 1M"); } else if (strcmp(conf_opt, "disk-pause") == 0) { if (bsdtar->mode != 'c') goto badmode; if (bsdtar->option_disk_pause_set) goto optset; if (conf_arg == NULL) goto needarg; bsdtar->disk_pause = strtol(conf_arg, NULL, 0); bsdtar->option_disk_pause_set = 1; } else if (strcmp(conf_opt, "exclude") == 0) { if (bsdtar->option_no_config_exclude_set) goto optset; if (conf_arg == NULL) goto needarg; if (exclude(bsdtar, conf_arg)) bsdtar_errc(bsdtar, 1, 0, "Couldn't exclude %s", conf_arg); } else if (strcmp(conf_opt, "humanize-numbers") == 0) { if (bsdtar->option_humanize_numbers_set) goto optset; tarsnap_opt_humanize_numbers = 1; bsdtar->option_humanize_numbers_set = 1; } else if (strcmp(conf_opt, "include") == 0) { if (bsdtar->option_no_config_include_set) goto optset; if (conf_arg == NULL) goto needarg; if (include(bsdtar, conf_arg)) bsdtar_errc(bsdtar, 1, 0, "Failed to add %s to inclusion list", conf_arg); } else if (strcmp(conf_opt, "insane-filesystems") == 0) { if (bsdtar->option_insane_filesystems_set) goto optset; bsdtar->option_insane_filesystems = 1; bsdtar->option_insane_filesystems_set = 1; } else if (strcmp(conf_opt, "keyfile") == 0) { if (bsdtar->have_keys) goto optset; if (conf_arg == NULL) goto needarg; if (load_keys(bsdtar, conf_arg) == 0) bsdtar->have_keys = 1; else { if (fromconffile && bsdtar->option_dryrun) bsdtar->config_file_keyfile_failed = 1; else { bsdtar_errc(bsdtar, 1, errno, "Cannot read key file: %s", conf_arg); } } } else if (strcmp(conf_opt, "lowmem") == 0) { if (bsdtar->mode != 'c') goto badmode; if (bsdtar->option_cachecrunch_set) goto optset; bsdtar->cachecrunch = 1; bsdtar->option_cachecrunch_set = 1; } else if (strcmp(conf_opt, "maxbw") == 0) { if (bsdtar->mode != 'c') goto badmode; if (bsdtar->option_maxbw_set) goto optset; if (conf_arg == NULL) goto needarg; if (humansize_parse(conf_arg, &tarsnap_opt_maxbytesout)) bsdtar_errc(bsdtar, 1, 0, "Cannot parse bandwidth limit: %s", conf_arg); bsdtar->option_maxbw_set = 1; } else if (strcmp(conf_opt, "maxbw-rate") == 0) { dooption(bsdtar, "maxbw-rate-down", conf_arg, fromconffile); dooption(bsdtar, "maxbw-rate-up", conf_arg, fromconffile); } else if (strcmp(conf_opt, "maxbw-rate-down") == 0) { if (bsdtar->option_maxbw_rate_down_set) goto optset; if (conf_arg == NULL) goto needarg; bsdtar->bwlimit_rate_down = strtod(conf_arg, &eptr); if ((*eptr != '\0') || (bsdtar->bwlimit_rate_down < 8000) || (bsdtar->bwlimit_rate_down > 1000000000.)) bsdtar_errc(bsdtar, 1, 0, "Invalid bandwidth rate limit: %s", conf_arg); bsdtar->option_maxbw_rate_down_set = 1; } else if (strcmp(conf_opt, "maxbw-rate-up") == 0) { if (bsdtar->option_maxbw_rate_up_set) goto optset; if (conf_arg == NULL) goto needarg; bsdtar->bwlimit_rate_up = strtod(conf_arg, &eptr); if ((*eptr != '\0') || (bsdtar->bwlimit_rate_up < 8000) || (bsdtar->bwlimit_rate_up > 1000000000.)) bsdtar_errc(bsdtar, 1, 0, "Invalid bandwidth rate limit: %s", conf_arg); bsdtar->option_maxbw_rate_up_set = 1; } else if (strcmp(conf_opt, "nodump") == 0) { if (bsdtar->mode != 'c') goto badmode; if (bsdtar->option_nodump_set) goto optset; bsdtar->option_honor_nodump = 1; bsdtar->option_nodump_set = 1; } else if (strcmp(conf_opt, "normalmem") == 0) { if (bsdtar->mode != 'c') goto badmode; if (bsdtar->option_cachecrunch_set) goto optset; bsdtar->option_cachecrunch_set = 1; } else if (strcmp(conf_opt, "no-aggressive-networking") == 0) { if (bsdtar->option_aggressive_networking_set) goto optset; bsdtar->option_aggressive_networking_set = 1; } else if (strcmp(conf_opt, "no-config-exclude") == 0) { if (bsdtar->option_no_config_exclude) goto optset; bsdtar->option_no_config_exclude = 1; } else if (strcmp(conf_opt, "no-config-include") == 0) { if (bsdtar->option_no_config_include) goto optset; bsdtar->option_no_config_include = 1; } else if (strcmp(conf_opt, "no-disk-pause") == 0) { if (bsdtar->option_disk_pause_set) goto optset; bsdtar->option_disk_pause_set = 1; } else if (strcmp(conf_opt, "no-humanize-numbers") == 0) { if (bsdtar->option_humanize_numbers_set) goto optset; bsdtar->option_humanize_numbers_set = 1; } else if (strcmp(conf_opt, "no-insane-filesystems") == 0) { if (bsdtar->option_insane_filesystems_set) goto optset; bsdtar->option_insane_filesystems_set = 1; } else if (strcmp(conf_opt, "no-maxbw") == 0) { if (bsdtar->option_maxbw_set) goto optset; bsdtar->option_maxbw_set = 1; } else if (strcmp(conf_opt, "no-maxbw-rate-down") == 0) { if (bsdtar->option_maxbw_rate_down_set) goto optset; bsdtar->option_maxbw_rate_down_set = 1; } else if (strcmp(conf_opt, "no-maxbw-rate-up") == 0) { if (bsdtar->option_maxbw_rate_up_set) goto optset; bsdtar->option_maxbw_rate_up_set = 1; } else if (strcmp(conf_opt, "no-nodump") == 0) { if (bsdtar->option_nodump_set) goto optset; bsdtar->option_nodump_set = 1; } else if (strcmp(conf_opt, "no-print-stats") == 0) { if (bsdtar->option_print_stats_set) goto optset; bsdtar->option_print_stats_set = 1; } else if (strcmp(conf_opt, "no-quiet") == 0) { if (bsdtar->option_quiet_set) goto optset; bsdtar->option_quiet_set = 1; } else if (strcmp(conf_opt, "no-retry-forever") == 0) { if (bsdtar->option_retry_forever_set) goto optset; bsdtar->option_retry_forever_set = 1; } else if (strcmp(conf_opt, "no-snaptime") == 0) { if (bsdtar->option_snaptime_set) goto optset; bsdtar->option_snaptime_set = 1; } else if (strcmp(conf_opt, "no-store-atime") == 0) { if (bsdtar->option_store_atime_set) goto optset; bsdtar->option_store_atime_set = 1; } else if (strcmp(conf_opt, "no-totals") == 0) { if (bsdtar->option_totals_set) goto optset; bsdtar->option_totals_set = 1; } else if (strcmp(conf_opt, "print-stats") == 0) { if ((bsdtar->mode != 'c') && (bsdtar->mode != 'd')) goto badmode; if (bsdtar->option_print_stats_set) goto optset; bsdtar->option_print_stats = 1; bsdtar->option_print_stats_set = 1; } else if (strcmp(conf_opt, "quiet") == 0) { if (bsdtar->option_quiet_set) goto optset; bsdtar->option_quiet = 1; bsdtar->option_quiet_set = 1; } else if (strcmp(conf_opt, "retry-forever") == 0) { if (bsdtar->option_retry_forever_set) goto optset; tarsnap_opt_retry_forever = 1; bsdtar->option_retry_forever_set = 1; } else if (strcmp(conf_opt, "snaptime") == 0) { if (bsdtar->mode != 'c') goto badmode; if (bsdtar->option_snaptime_set) goto optset; if (conf_arg == NULL) goto needarg; if (stat(conf_arg, &st) != 0) bsdtar_errc(bsdtar, 1, 0, "Can't stat file %s", conf_arg); bsdtar->snaptime = st.st_ctime; bsdtar->option_snaptime_set = 1; } else if (strcmp(conf_opt, "store-atime") == 0) { if (bsdtar->mode != 'c') goto badmode; if (bsdtar->option_store_atime_set) goto optset; bsdtar->option_store_atime = 1; bsdtar->option_store_atime_set = 1; } else if (strcmp(conf_opt, "totals") == 0) { if (bsdtar->mode != 'c') goto badmode; if (bsdtar->option_totals_set) goto optset; bsdtar->option_totals = 1; bsdtar->option_totals_set = 1; } else if (strcmp(conf_opt, "verylowmem") == 0) { if (bsdtar->mode != 'c') goto badmode; if (bsdtar->option_cachecrunch_set) goto optset; bsdtar->cachecrunch = 2; bsdtar->option_cachecrunch_set = 1; } else { goto badopt; } return; badmode: /* Option not relevant in this mode. */ if (fromconffile == 0) { bsdtar_errc(bsdtar, 1, 0, "Option --%s is not permitted in mode %s", conf_opt, bsdtar->modestr); } return; optset: /* Option specified multiple times. */ if (fromconffile == 0) { usage(bsdtar); } return; needarg: /* Option needs an argument. */ bsdtar_errc(bsdtar, 1, 0, "Argument required for configuration file option: %s", conf_opt); badopt: /* No such option. */ bsdtar_errc(bsdtar, 1, 0, "Unrecognized configuration file option: \"%s\"", conf_opt); }
int main(int argc, char **argv) { struct register_internal C; const char * keyfilename; const char * oldkeyfilename; int passphrased; uint64_t maxmem; double maxtime; const char * ch; WARNP_INIT; /* * We have no username, machine name, key filename, or old key * filename yet. */ C.user = C.name = NULL; keyfilename = NULL; oldkeyfilename = NULL; /* * So far we're not using a passphrase, have unlimited RAM, and allow * up to 1 second of CPU time. */ passphrased = 0; maxmem = 0; maxtime = 1.0; /* Parse arguments. */ while ((ch = GETOPT(argc, argv)) != NULL) { GETOPT_SWITCH(ch) { GETOPT_OPTARG("--user"): if (C.user != NULL) usage(); C.user = optarg; break; GETOPT_OPTARG("--machine"): if (C.name != NULL) usage(); C.name = optarg; break; GETOPT_OPTARG("--keyfile"): if (keyfilename != NULL) usage(); keyfilename = optarg; break; GETOPT_OPTARG("--oldkey"): if (oldkeyfilename != NULL) usage(); oldkeyfilename = optarg; break; GETOPT_OPTARG("--passphrase-mem"): if (maxmem != 0) usage(); if (humansize_parse(optarg, &maxmem)) { warnp("Cannot parse --passphrase-mem" " argument: %s", optarg); exit(1); } break; GETOPT_OPTARG("--passphrase-time"): if (maxtime != 1.0) usage(); maxtime = strtod(optarg, NULL); if ((maxtime < 0.05) || (maxtime > 86400)) { warn0("Invalid --passphrase-time argument: %s", optarg); exit(1); } break; GETOPT_OPT("--passphrased"): if (passphrased != 0) usage(); passphrased = 1; break; GETOPT_OPT("--version"): fprintf(stderr, "tarsnap-keyregen %s\n", PACKAGE_VERSION); exit(0); GETOPT_MISSING_ARG: warn0("Missing argument to %s\n", ch); /* FALLTHROUGH */ GETOPT_DEFAULT: usage(); } } argc -= optind; argv += optind; /* We should have processed all the arguments. */ if (argc != 0) usage(); /* * We must have a user name, machine name, key file, and old key * file specified. */ if ((C.user == NULL) || (C.name == NULL) || (keyfilename == NULL) || (oldkeyfilename == NULL)) usage(); /* * It doesn't make sense to specify --passphrase-mem or * --passphrase-time if we're not using a passphrase. */ if (((maxmem != 0) || (maxtime != 1.0)) && (passphrased == 0)) usage(); /* * Use shared code between keygen and keyregen for the actual * processing. */ if (keygen_actual(&C, keyfilename, passphrased, maxmem, maxtime, oldkeyfilename) != 0) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (1); }
int main(int argc, char **argv) { struct register_internal C; const char * keyfilename; FILE * keyfile; NETPACKET_CONNECTION * NPC; int passphrased; uint64_t maxmem; double maxtime; char * passphrase; WARNP_INIT; /* We have no username, machine name, or key filename yet. */ C.user = C.name = NULL; keyfilename = NULL; /* * So far we're not using a passphrase, have unlimited RAM, and allow * up to 1 second of CPU time. */ passphrased = 0; maxmem = 0; maxtime = 1.0; /* Parse arguments. */ while (--argc > 0) { argv++; if (strcmp(argv[0], "--user") == 0) { if ((C.user != NULL) || (argc < 2)) usage(); C.user = argv[1]; argv++; argc--; } else if (strcmp(argv[0], "--machine") == 0) { if ((C.name != NULL) || (argc < 2)) usage(); C.name = argv[1]; argv++; argc--; } else if (strcmp(argv[0], "--keyfile") == 0) { if ((keyfilename != NULL) || (argc < 2)) usage(); keyfilename = argv[1]; argv++; argc--; } else if (strcmp(argv[0], "--passphrase-mem") == 0) { if ((maxmem != 0) || (argc < 2)) usage(); if (humansize_parse(argv[1], &maxmem)) { warnp("Cannot parse --passphrase-mem" " argument: %s", argv[1]); exit(1); } argv++; argc--; } else if (strcmp(argv[0], "--passphrase-time") == 0) { if ((maxtime != 1.0) || (argc < 2)) usage(); maxtime = strtod(argv[1], NULL); if ((maxtime < 0.05) || (maxtime > 86400)) { warn0("Invalid --passphrase-time argument: %s", argv[1]); exit(1); } argv++; argc--; } else if (strcmp(argv[0], "--passphrased") == 0) { passphrased = 1; } else { usage(); } } /* We must have a user name, machine name, and key file specified. */ if ((C.user == NULL) || (C.name == NULL) || (keyfilename == NULL)) usage(); /* * It doesn't make sense to specify --passphrase-mem or * --passphrase-time if we're not using a passphrase. */ if (((maxmem != 0) || (maxtime != 1.0)) && (passphrased == 0)) usage(); /* Sanity-check the user name. */ if (strlen(C.user) > 255) { fprintf(stderr, "User name too long: %s\n", C.user); exit(1); } if (strlen(C.user) == 0) { fprintf(stderr, "User name must be non-empty\n"); exit(1); } /* Sanity-check the machine name. */ if (strlen(C.name) > 255) { fprintf(stderr, "Machine name too long: %s\n", C.name); exit(1); } if (strlen(C.name) == 0) { fprintf(stderr, "Machine name must be non-empty\n"); exit(1); } /* Get a password. */ if (readpass(&C.passwd, "Enter tarsnap account password", NULL, 0)) { warnp("Error reading password"); exit(1); } /* * Create key file -- we do this now rather than later so that we * avoid registering with the server if we won't be able to create * the key file later. */ if ((keyfile = keyfile_write_open(keyfilename)) == NULL) { warnp("Cannot create %s", keyfilename); exit(1); } /* Initialize key cache. */ if (crypto_keys_init()) { warnp("Key cache initialization failed"); goto err1; } /* Generate keys. */ if (crypto_keys_generate(CRYPTO_KEYMASK_USER)) { warnp("Error generating keys"); goto err1; } /* * We're not done, haven't answered a challenge, and don't have a * machine number. */ C.done = 0; C.donechallenge = 0; C.machinenum = (uint64_t)(-1); /* Open netpacket connection. */ if ((NPC = netpacket_open(USERAGENT)) == NULL) goto err2; /* Ask the netpacket layer to send a request and get a response. */ if (netpacket_op(NPC, callback_register_send, &C)) goto err2; /* Run event loop until an error occurs or we're done. */ if (network_spin(&C.done)) goto err2; /* Close netpacket connection. */ if (netpacket_close(NPC)) goto err2; /* * If we didn't respond to a challenge, the server's response must * have been a "no such user" error. */ if ((C.donechallenge == 0) && (C.status != 1)) { netproto_printerr(NETPROTO_STATUS_PROTERR); goto err1; } /* The machine number should be -1 iff the status is nonzero. */ if (((C.machinenum == (uint64_t)(-1)) && (C.status == 0)) || ((C.machinenum != (uint64_t)(-1)) && (C.status != 0))) { netproto_printerr(NETPROTO_STATUS_PROTERR); goto err1; } /* Parse status returned by server. */ switch (C.status) { case 0: /* Success! */ break; case 1: warn0("No such user: %s", C.user); break; case 2: warn0("Incorrect password"); break; case 3: warn0("Cannot register with server: " "Account balance for user %s is not positive", C.user); break; default: netproto_printerr(NETPROTO_STATUS_PROTERR); goto err2; } /* Shut down the network event loop. */ network_fini(); /* Exit with a code of 1 if we couldn't register. */ if (C.machinenum == (uint64_t)(-1)) goto err1; /* If the user wants to passphrase the keyfile, get the passphrase. */ if (passphrased != 0) { if (readpass(&passphrase, "Please enter passphrase for keyfile encryption", "Please confirm passphrase for keyfile encryption", 1)) { warnp("Error reading password"); goto err1; } } else { passphrase = NULL; } /* Write keys to file. */ if (keyfile_write_file(keyfile, C.machinenum, CRYPTO_KEYMASK_USER, passphrase, maxmem, maxtime)) goto err1; /* Close the key file. */ if (fclose(keyfile)) { warnp("Error closing key file"); goto err1; } /* Success! */ return (0); err2: warnp("Error registering with server"); err1: unlink(keyfilename); exit(1); }
int main(int argc, char **argv) { const char * newkeyfile = NULL; int keyswanted = 0; char * tok, * brkb = NULL, * eptr; long keynum; uint64_t machinenum = (uint64_t)(-1); uint64_t kfmachinenum; const char * missingkey; int passphrased = 0; uint64_t maxmem = 0; double maxtime = 1.0; char * passphrase; const char * print_key_id_file = NULL; const char * print_key_permissions_file = NULL; const char * ch; char * optarg_copy; /* for strtok_r. */ WARNP_INIT; /* Initialize key cache. */ if (crypto_keys_init()) { warnp("Key cache initialization failed"); exit(1); } /* Parse arguments. */ while ((ch = GETOPT(argc, argv)) != NULL) { GETOPT_SWITCH(ch) { GETOPT_OPTARG("--outkeyfile"): if (newkeyfile != NULL) usage(); newkeyfile = optarg; break; GETOPT_OPT("-r"): keyswanted |= CRYPTO_KEYMASK_READ; break; GETOPT_OPT("-w"): keyswanted |= CRYPTO_KEYMASK_WRITE; break; GETOPT_OPT("-d"): /* * Deleting data requires both delete authorization * and being able to read archives -- we need to be * able to figure out which bits are part of the * archive. */ keyswanted |= CRYPTO_KEYMASK_READ; keyswanted |= CRYPTO_KEYMASK_AUTH_DELETE; break; GETOPT_OPT("--nuke"): keyswanted |= CRYPTO_KEYMASK_AUTH_DELETE; break; GETOPT_OPTARG("--keylist"): /* * This is a deliberately undocumented option used * mostly for testing purposes; it allows a list of * keys to be specified according to their numbers in * crypto/crypto.h instead of using the predefined * sets of "read", "write" and "delete" keys. */ if ((optarg_copy = strdup(optarg)) == NULL) { warn0("Out of memory"); exit(0); } for (tok = strtok_r(optarg_copy, ",", &brkb); tok; tok = strtok_r(NULL, ",", &brkb)) { keynum = strtol(tok, &eptr, 0); if ((eptr == tok) || (keynum < 0) || (keynum > 31)) { warn0("Not a valid key number: %s", tok); free(optarg_copy); exit(1); } keyswanted |= (uint32_t)(1) << keynum; } free(optarg_copy); break; GETOPT_OPTARG("--passphrase-mem"): if (maxmem != 0) usage(); if (humansize_parse(optarg, &maxmem)) { warnp("Cannot parse --passphrase-mem" " argument: %s", optarg); exit(1); } break; GETOPT_OPTARG("--passphrase-time"): if (maxtime != 1.0) usage(); maxtime = strtod(optarg, NULL); if ((maxtime < 0.05) || (maxtime > 86400)) { warn0("Invalid --passphrase-time argument: %s", optarg); exit(1); } break; GETOPT_OPT("--passphrased"): if (passphrased != 0) usage(); passphrased = 1; break; GETOPT_OPTARG("--print-key-id"): if (print_key_id_file != NULL) usage(); print_key_id_file = optarg; break; GETOPT_OPTARG("--print-key-permissions"): if (print_key_permissions_file != NULL) usage(); print_key_permissions_file = optarg; break; GETOPT_MISSING_ARG: warn0("Missing argument to %s\n", ch); /* FALLTHROUGH */ GETOPT_DEFAULT: usage(); } } argc -= optind; argv += optind; /* We can't print ID and permissions at the same time. */ if ((print_key_id_file != NULL) && (print_key_permissions_file != NULL)) usage(); if ((print_key_id_file != NULL) || (print_key_permissions_file != NULL)) { /* We can't combine printing info with generating a new key. */ if (newkeyfile != NULL) usage(); /* We should have processed all arguments. */ if (argc != 0) usage(); /* Print info. */ if (print_key_id_file != NULL) print_id(print_key_id_file); if (print_key_permissions_file != NULL) print_permissions(print_key_permissions_file); } /* We should have an output key file. */ if (newkeyfile == NULL) usage(); /* * It doesn't make sense to specify --passphrase-mem or * --passphrase-time if we're not using a passphrase. */ if (((maxmem != 0) || (maxtime != 1.0)) && (passphrased == 0)) usage(); /* Warn the user if they're being silly. */ if (keyswanted == 0) { warn0("None of {-r, -w, -d, --nuke} options are specified." " This will create a key file with no keys, which is" " probably not what you intended."); } /* Read the specified key files. */ while (argc-- > 0) { /* * Suck in the key file. We could mask this to only load the * keys we want to copy, but there's no point really since we * export keys selectively. */ if (keyfile_read(argv[0], &kfmachinenum, ~0)) { warnp("Cannot read key file: %s", argv[0]); exit(1); } /* * Check that we're not using key files which belong to * different machines. */ if (machinenum == (uint64_t)(-1)) { machinenum = kfmachinenum; } else if (machinenum != kfmachinenum) { warn0("Keys from %s do not belong to the " "same machine as earlier keys", argv[0]); exit(1); } /* Move on to the next file. */ argv++; } /* Make sure that we have the necessary keys. */ if ((missingkey = crypto_keys_missing(keyswanted)) != NULL) { warn0("The %s key is required but not in any input key files", missingkey); exit(1); } /* If the user wants to passphrase the keyfile, get the passphrase. */ if (passphrased != 0) { if (readpass(&passphrase, "Please enter passphrase for keyfile encryption", "Please confirm passphrase for keyfile encryption", 1)) { warnp("Error reading password"); exit(1); } } else { passphrase = NULL; } /* Write out new key file. */ if (keyfile_write(newkeyfile, machinenum, keyswanted, passphrase, maxmem, maxtime)) exit(1); /* Success! */ return (0); }