sunlabel.c revision 113824
159191Skris/*- 259191Skris * Copyright (c) 2003 Jake Burkholder. 359191Skris * All rights reserved. 459191Skris * 559191Skris * Redistribution and use in source and binary forms, with or without 659191Skris * modification, are permitted provided that the following conditions 759191Skris * are met: 859191Skris * 1. Redistributions of source code must retain the above copyright 959191Skris * notice, this list of conditions and the following disclaimer. 1059191Skris * 2. Redistributions in binary form must reproduce the above copyright 1159191Skris * notice, this list of conditions and the following disclaimer in the 1259191Skris * documentation and/or other materials provided with the distribution. 1359191Skris * 1459191Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1559191Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1659191Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1759191Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1859191Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1959191Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2059191Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2159191Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2259191Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2359191Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24111147Snectar * SUCH DAMAGE. 2559191Skris */ 2659191Skris/* 2759191Skris * Copyright (c) 1994, 1995 Gordon W. Ross 2859191Skris * Copyright (c) 1994 Theo de Raadt 2959191Skris * All rights reserved. 3059191Skris * Copyright (c) 1987, 1993 3159191Skris * The Regents of the University of California. All rights reserved. 3259191Skris * 3359191Skris * This code is derived from software contributed to Berkeley by 3459191Skris * Symmetric Computer Systems. 3559191Skris * 3659191Skris * Redistribution and use in source and binary forms, with or without 3759191Skris * modification, are permitted provided that the following conditions 3859191Skris * are met: 3959191Skris * 1. Redistributions of source code must retain the above copyright 4059191Skris * notice, this list of conditions and the following disclaimer. 4159191Skris * 2. Redistributions in binary form must reproduce the above copyright 4259191Skris * notice, this list of conditions and the following disclaimer in the 4359191Skris * documentation and/or other materials provided with the distribution. 4459191Skris * 3. All advertising materials mentioning features or use of this software 4559191Skris * must display the following acknowledgement: 4659191Skris * This product includes software developed by the University of 4759191Skris * California, Berkeley and its contributors. 4859191Skris * This product includes software developed by Theo de Raadt. 4959191Skris * 4. Neither the name of the University nor the names of its contributors 5059191Skris * may be used to endorse or promote products derived from this software 5159191Skris * without specific prior written permission. 5259191Skris * 5359191Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5459191Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5559191Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5659191Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5759191Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5859191Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5959191Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6059191Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6159191Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6259191Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6359191Skris * SUCH DAMAGE. 6459191Skris * 6559191Skris * from: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $ 6659191Skris */ 6759191Skris 6859191Skris#include <sys/cdefs.h> 6959191Skris__FBSDID("$FreeBSD: head/sbin/sunlabel/sunlabel.c 113824 2003-04-21 20:38:15Z phk $"); 7059191Skris 7159191Skris#include <sys/types.h> 7259191Skris#include <sys/param.h> 7359191Skris#include <sys/disk.h> 7459191Skris#include <sys/ioctl.h> 7559191Skris#include <sys/sun_disklabel.h> 7659191Skris#include <sys/wait.h> 7759191Skris 7859191Skris#include <err.h> 7959191Skris#include <fcntl.h> 8059191Skris#include <inttypes.h> 8159191Skris#include <paths.h> 8259191Skris#include <stdio.h> 8359191Skris#include <stdlib.h> 8459191Skris#include <string.h> 8559191Skris#include <unistd.h> 8659191Skris 8759191Skris#define _PATH_TMPFILE "/tmp/EdDk.XXXXXXXXXX" 8859191Skris#define _PATH_BOOT "/boot/boot1" 8959191Skris 9059191Skrisstatic int bflag; 9159191Skrisstatic int Bflag; 9259191Skrisstatic int eflag; 9359191Skrisstatic int nflag; 9459191Skrisstatic int rflag = 1; 9559191Skrisstatic int Rflag; 9659191Skrisstatic int wflag; 9759191Skris 9859191Skrisstatic int check_label(struct sun_disklabel *sl); 9959191Skrisstatic void read_label(struct sun_disklabel *sl, const char *disk); 10059191Skrisstatic void write_label(struct sun_disklabel *sl, const char *disk, 10159191Skris const char *bootpath); 10259191Skrisstatic int edit_label(struct sun_disklabel *sl, const char *disk, 10359191Skris const char *bootpath); 10459191Skrisstatic int parse_label(struct sun_disklabel *sl, const char *file); 10559191Skrisstatic void print_label(struct sun_disklabel *sl, const char *disk, FILE *out); 10659191Skris 10759191Skrisstatic int parse_size(struct sun_disklabel *sl, int part, char *size); 10859191Skrisstatic int parse_offset(struct sun_disklabel *sl, int part, char *offset); 10959191Skris 11059191Skrisstatic void usage(void); 11159191Skris 11259191Skrisextern char *__progname; 11359191Skris 11459191Skris/* 11559191Skris * Disk label editor for sun disklabels. 11659191Skris */ 11759191Skrisint 11859191Skrismain(int ac, char **av) 11959191Skris{ 12059191Skris struct sun_disklabel sl; 12159191Skris const char *bootpath; 12259191Skris const char *proto; 12359191Skris const char *disk; 12459191Skris int ch; 12559191Skris 126111147Snectar bootpath = _PATH_BOOT; 127111147Snectar while ((ch = getopt(ac, av, "b:BenrRw")) != -1) 128238405Sjkim switch (ch) { 129111147Snectar case 'b': 130111147Snectar bflag = 1; 131111147Snectar bootpath = optarg; 132111147Snectar break; 13359191Skris case 'B': 13459191Skris Bflag = 1; 13559191Skris break; 13659191Skris case 'e': 13759191Skris eflag = 1; 13859191Skris break; 13959191Skris case 'n': 14059191Skris nflag = 1; 14159191Skris break; 14259191Skris case 'r': 14359191Skris rflag = 1; 14459191Skris break; 14559191Skris case 'R': 14659191Skris Rflag = 1; 14759191Skris break; 14859191Skris case 'w': 14959191Skris wflag = 1; 15059191Skris break; 15159191Skris default: 15259191Skris usage(); 15359191Skris break; 15459191Skris } 15559191Skris if (bflag && !Bflag) 15659191Skris usage(); 15759191Skris if (nflag && !(Bflag || eflag || Rflag || wflag)) 15859191Skris usage(); 15959191Skris if (eflag && (Rflag || wflag)) 16059191Skris usage(); 16159191Skris ac -= optind; 16259191Skris av += optind; 16359191Skris if (ac == 0) 16459191Skris usage(); 16559191Skris bzero(&sl, sizeof(sl)); 16659191Skris disk = av[0]; 16759191Skris if (wflag) { 16859191Skris if (ac != 2 || strcmp(av[1], "auto") != 0) 16959191Skris usage(); 17059191Skris read_label(&sl, disk); 17159191Skris bzero(sl.sl_part, sizeof(sl.sl_part)); 17259191Skris sl.sl_part[SUN_RAWPART].sdkp_cyloffset = 0; 17359191Skris sl.sl_part[SUN_RAWPART].sdkp_nsectors = sl.sl_ncylinders * 17459191Skris sl.sl_ntracks * sl.sl_nsectors; 17559191Skris write_label(&sl, disk, bootpath); 17659191Skris } else if (eflag) { 17759191Skris if (ac != 1) 17859191Skris usage(); 17959191Skris read_label(&sl, disk); 18059191Skris if (sl.sl_magic != SUN_DKMAGIC) 18159191Skris errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 18259191Skris while (edit_label(&sl, disk, bootpath) != 0) 18359191Skris ; 18459191Skris } else if (Rflag) { 18559191Skris if (ac != 2) 18659191Skris usage(); 18759191Skris proto = av[1]; 18859191Skris read_label(&sl, disk); 18959191Skris if (parse_label(&sl, proto) != 0) 19059191Skris errx(1, "%s: invalid label", proto); 19159191Skris write_label(&sl, disk, bootpath); 19259191Skris } else if (Bflag) { 19359191Skris read_label(&sl, disk); 19459191Skris if (sl.sl_magic != SUN_DKMAGIC) 19559191Skris errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 19659191Skris write_label(&sl, disk, bootpath); 19759191Skris } else { 19859191Skris read_label(&sl, disk); 19959191Skris if (sl.sl_magic != SUN_DKMAGIC) 20059191Skris errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 20159191Skris print_label(&sl, disk, stdout); 20259191Skris } 20359191Skris return (0); 20459191Skris} 20559191Skris 20659191Skrisstatic int 20759191Skrischeck_label(struct sun_disklabel *sl) 20859191Skris{ 20959191Skris uint64_t nsectors; 21059191Skris uint64_t ostart; 21159191Skris uint64_t start; 21259191Skris uint64_t oend; 21359191Skris uint64_t end; 21459191Skris int i; 21559191Skris int j; 21659191Skris 21759191Skris nsectors = sl->sl_ncylinders * sl->sl_ntracks * sl->sl_nsectors; 21859191Skris if (sl->sl_part[SUN_RAWPART].sdkp_cyloffset != 0 || 21959191Skris sl->sl_part[SUN_RAWPART].sdkp_nsectors != nsectors) { 22059191Skris warnx("partition c is incorrect, must start at 0 and cover " 22159191Skris "whole disk"); 22259191Skris return (1); 22359191Skris } 22459191Skris for (i = 0; i < SUN_NPART; i++) { 22559191Skris if (i == 2 || sl->sl_part[i].sdkp_nsectors == 0) 22659191Skris continue; 22759191Skris start = (uint64_t)sl->sl_part[i].sdkp_cyloffset * 22859191Skris sl->sl_ntracks * sl->sl_nsectors; 22959191Skris end = start + sl->sl_part[i].sdkp_nsectors; 23059191Skris if (end > nsectors) { 23159191Skris warnx("partition %c extends past end of disk", 23259191Skris 'a' + i); 23359191Skris return (1); 23459191Skris } 23559191Skris for (j = 0; j < SUN_NPART; j++) { 23659191Skris if (j == 2 || j == i || 23759191Skris sl->sl_part[j].sdkp_nsectors == 0) 23859191Skris continue; 23959191Skris ostart = (uint64_t)sl->sl_part[j].sdkp_cyloffset * 24059191Skris sl->sl_ntracks * sl->sl_nsectors; 24159191Skris oend = ostart + sl->sl_part[j].sdkp_nsectors; 24259191Skris if ((start <= ostart && end >= oend) || 24359191Skris (start > ostart && start < oend) || 244 (end > ostart && end < oend)) { 245 warnx("partition %c overlaps partition %c", 246 'a' + i, 'a' + j); 247 return (1); 248 } 249 } 250 } 251 return (0); 252} 253 254static void 255read_label(struct sun_disklabel *sl, const char *disk) 256{ 257 char path[MAXPATHLEN]; 258 uint32_t sectorsize; 259 uint32_t fwsectors; 260 uint32_t fwheads; 261 off_t mediasize; 262 char buf[SUN_SIZE]; 263 int fd, error; 264 265 snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk); 266 if ((fd = open(path, O_RDONLY)) < 0) 267 err(1, "open %s", path); 268 if (read(fd, buf, sizeof(buf)) != sizeof(buf)) 269 err(1, "read"); 270 error = sunlabel_dec(buf, sl); 271 if (error) { 272 bzero(sl, sizeof(*sl)); 273 if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0) 274 err(1, "%s: ioctl(DIOCGMEDIASIZE) failed", disk); 275 if (ioctl(fd, DIOCGSECTORSIZE, §orsize) != 0) 276 err(1, "%s: DIOCGSECTORSIZE failed", disk); 277 if (ioctl(fd, DIOCGFWSECTORS, &fwsectors) != 0) 278 fwsectors = 63; 279 if (ioctl(fd, DIOCGFWHEADS, &fwheads) != 0) { 280 if (mediasize <= 63 * 1024 * sectorsize) 281 fwheads = 1; 282 else if (mediasize <= 63 * 16 * 1024 * sectorsize) 283 fwheads = 16; 284 else 285 fwheads = 255; 286 } 287 sl->sl_rpm = 3600; 288 sl->sl_pcylinders = mediasize / (fwsectors * fwheads * 289 sectorsize); 290 sl->sl_sparespercyl = 0; 291 sl->sl_interleave = 1; 292 sl->sl_ncylinders = sl->sl_pcylinders - 2; 293 sl->sl_acylinders = 2; 294 sl->sl_nsectors = fwsectors; 295 sl->sl_ntracks = fwheads; 296 sl->sl_part[SUN_RAWPART].sdkp_cyloffset = 0; 297 sl->sl_part[SUN_RAWPART].sdkp_nsectors = sl->sl_ncylinders * 298 sl->sl_ntracks * sl->sl_nsectors; 299 if (mediasize > (off_t)4999L * 1024L * 1024L) { 300 sprintf(sl->sl_text, 301 "FreeBSD%jdG cyl %u alt %u hd %u sec %u", 302 (intmax_t)(mediasize + 512 * 1024 * 1024) / 303 (1024 * 1024 * 1024), 304 sl->sl_ncylinders, sl->sl_acylinders, 305 sl->sl_ntracks, sl->sl_nsectors); 306 } else { 307 sprintf(sl->sl_text, 308 "FreeBSD%jdM cyl %u alt %u hd %u sec %u", 309 (intmax_t)(mediasize + 512 * 1024) / (1024 * 1024), 310 sl->sl_ncylinders, sl->sl_acylinders, 311 sl->sl_ntracks, sl->sl_nsectors); 312 } 313 } 314 close(fd); 315} 316 317static void 318write_label(struct sun_disklabel *sl, const char *disk, const char *bootpath) 319{ 320 char path[MAXPATHLEN]; 321 char boot[16 * 512]; 322 char buf[SUN_SIZE]; 323 off_t off; 324 int bfd; 325 int fd; 326 int i; 327 328 sl->sl_magic = SUN_DKMAGIC; 329 330 if (check_label(sl) != 0) 331 errx(1, "invalid label"); 332 333 if (nflag) { 334 print_label(sl, disk, stdout); 335 } else if (rflag) { 336 snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk); 337 if ((fd = open(path, O_RDWR)) < 0) 338 err(1, "open %s", path); 339 if (Bflag) { 340 if ((bfd = open(bootpath, O_RDONLY)) < 0) 341 err(1, "open %s", bootpath); 342 if (read(bfd, boot, sizeof(boot)) != sizeof(boot)) 343 err(1, "read"); 344 close(bfd); 345 for (i = 0; i < SUN_NPART; i++) { 346 if (sl->sl_part[i].sdkp_nsectors == 0) 347 continue; 348 off = sl->sl_part[i].sdkp_cyloffset * 349 sl->sl_ntracks * sl->sl_nsectors * 512; 350 if (lseek(fd, off, SEEK_SET) < 0) 351 err(1, "lseek"); 352 if (write(fd, boot, sizeof(boot)) != 353 sizeof(boot)) 354 err(1, "write"); 355 } 356 } 357 if (lseek(fd, 0, SEEK_SET) < 0) 358 err(1, "lseek"); 359 bzero(buf, sizeof(buf)); 360 sunlabel_enc(buf, sl); 361 362 if (write(fd, buf, sizeof(buf)) != sizeof(buf)) 363 err(1, "write"); 364 close(fd); 365 } else 366 err(1, "implement!"); 367} 368 369static int 370edit_label(struct sun_disklabel *sl, const char *disk, const char *bootpath) 371{ 372 char tmpfil[] = _PATH_TMPFILE; 373 const char *editor; 374 int status; 375 FILE *fp; 376 pid_t pid; 377 pid_t r; 378 int fd; 379 int c; 380 381 if ((fd = mkstemp(tmpfil)) < 0) 382 err(1, "mkstemp"); 383 if ((fp = fdopen(fd, "w")) == NULL) 384 err(1, "fdopen"); 385 print_label(sl, disk, fp); 386 fflush(fp); 387 if ((pid = fork()) < 0) 388 err(1, "fork"); 389 if (pid == 0) { 390 if ((editor = getenv("EDITOR")) == NULL) 391 editor = _PATH_VI; 392 execlp(editor, editor, tmpfil, NULL); 393 err(1, "execlp %s", editor); 394 } 395 status = 0; 396 while ((r = wait(&status)) > 0 && r != pid) 397 ; 398 if (WIFEXITED(status)) { 399 if (parse_label(sl, tmpfil) == 0) { 400 fclose(fp); 401 unlink(tmpfil); 402 write_label(sl, disk, bootpath); 403 return (0); 404 } 405 printf("re-edit the label? [y]: "); 406 fflush(stdout); 407 c = getchar(); 408 if (c != EOF && c != '\n') 409 while (getchar() != '\n') 410 ; 411 if (c == 'n') { 412 fclose(fp); 413 unlink(tmpfil); 414 return (0); 415 } 416 } 417 fclose(fp); 418 unlink(tmpfil); 419 return (1); 420} 421 422static int 423parse_label(struct sun_disklabel *sl, const char *file) 424{ 425 char offset[32]; 426 char size[32]; 427 char buf[128]; 428 uint8_t part; 429 FILE *fp; 430 int line; 431 432 line = 0; 433 if ((fp = fopen(file, "r")) == NULL) 434 err(1, "fopen"); 435 bzero(sl->sl_part, sizeof(sl->sl_part)); 436 while (fgets(buf, sizeof(buf), fp) != NULL) { 437 if (buf[0] != ' ' || buf[1] != ' ') 438 continue; 439 if (sscanf(buf, " %c: %s %s\n", &part, size, offset) != 3 || 440 parse_size(sl, part - 'a', size) || 441 parse_offset(sl, part - 'a', offset)) { 442 warnx("%s: syntex error on line %d", 443 file, line); 444 fclose(fp); 445 return (1); 446 } 447 line++; 448 } 449 fclose(fp); 450 return (check_label(sl)); 451} 452 453static int 454parse_size(struct sun_disklabel *sl, int part, char *size) 455{ 456 uintmax_t nsectors; 457 uintmax_t total; 458 uintmax_t n; 459 char *p; 460 int i; 461 462 nsectors = 0; 463 n = strtoumax(size, &p, 10); 464 if (*p != '\0') { 465 if (strcmp(size, "*") == 0) { 466 total = sl->sl_ncylinders * sl->sl_ntracks * 467 sl->sl_nsectors; 468 for (i = 0; i < part; i++) { 469 if (i == 2) 470 continue; 471 nsectors += sl->sl_part[i].sdkp_nsectors; 472 } 473 n = total - nsectors; 474 } else if (p[1] == '\0' && (p[0] == 'K' || p[0] == 'k')) { 475 n = roundup((n * 1024) / 512, 476 sl->sl_ntracks * sl->sl_nsectors); 477 } else if (p[1] == '\0' && (p[0] == 'M' || p[0] == 'm')) { 478 n = roundup((n * 1024 * 1024) / 512, 479 sl->sl_ntracks * sl->sl_nsectors); 480 } else if (p[1] == '\0' && (p[0] == 'G' || p[0] == 'g')) { 481 n = roundup((n * 1024 * 1024 * 1024) / 512, 482 sl->sl_ntracks * sl->sl_nsectors); 483 } else 484 return (-1); 485 } 486 sl->sl_part[part].sdkp_nsectors = n; 487 return (0); 488} 489 490static int 491parse_offset(struct sun_disklabel *sl, int part, char *offset) 492{ 493 uintmax_t nsectors; 494 uintmax_t n; 495 char *p; 496 int i; 497 498 nsectors = 0; 499 n = strtoumax(offset, &p, 10); 500 if (*p != '\0') { 501 if (strcmp(offset, "*") == 0) { 502 for (i = 0; i < part; i++) { 503 if (i == 2) 504 continue; 505 nsectors += sl->sl_part[i].sdkp_nsectors; 506 } 507 n = nsectors / (sl->sl_nsectors * sl->sl_ntracks); 508 } else 509 return (-1); 510 } 511 sl->sl_part[part].sdkp_cyloffset = n; 512 return (0); 513} 514 515static void 516print_label(struct sun_disklabel *sl, const char *disk, FILE *out) 517{ 518 int i; 519 520 fprintf(out, 521"# /dev/%s:\n" 522"text: %s\n" 523"bytes/sectors: 512\n" 524"sectors/cylinder: %d\n" 525"sectors/unit: %d\n" 526"\n" 527"%d partitions:\n" 528"#\n" 529"# Size is in sectors, use %%dK, %%dM or %%dG to specify in kilobytes,\n" 530"# megabytes or gigabytes respectively, or '*' to specify rest of disk.\n" 531"# Offset is in cylinders, use '*' to calculate offsets automatically.\n" 532"#\n" 533"# size offset\n" 534"# ---------- ----------\n", 535 disk, 536 sl->sl_text, 537 sl->sl_nsectors * sl->sl_ntracks, 538 sl->sl_nsectors * sl->sl_ntracks * sl->sl_ncylinders, 539 SUN_NPART); 540 for (i = 0; i < SUN_NPART; i++) { 541 if (sl->sl_part[i].sdkp_nsectors == 0) 542 continue; 543 fprintf(out, " %c: %10u %10u\n", 544 'a' + i, 545 sl->sl_part[i].sdkp_nsectors, 546 sl->sl_part[i].sdkp_cyloffset); 547 } 548} 549 550static void 551usage(void) 552{ 553 554 fprintf(stderr, "usage:" 555"\t%s [-r] disk\n" 556"\t\t(to read label)\n" 557"\t%s -B [-b boot1] [-n] disk\n" 558"\t\t(to install boot program only)\n" 559"\t%s -R [-B [-b boot1]] [-r] [-n] disk protofile\n" 560"\t\t(to restore label)\n" 561"\t%s -e [-B [-b boot1]] [-r] [-n] disk\n" 562"\t\t(to edit label)\n" 563"\t%s -w [-B [-b boot1]] [-r] [-n] disk type\n" 564"\t\t(to write default label)\n", 565 __progname, 566 __progname, 567 __progname, 568 __progname, 569 __progname); 570 exit(1); 571} 572