1 /* 2 * (C) Copyright 2000-2003 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * See file CREDITS for list of people who contributed to this 6 * project. 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as 10 * published by the Free Software Foundation; either version 2 of 11 * the License, or (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 21 * MA 02111-1307 USA 22 */ 23 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <stddef.h> 29 #include <string.h> 30 #include <sys/types.h> 31 #include <sys/ioctl.h> 32 #include <sys/stat.h> 33 #include <unistd.h> 34 35 #ifdef MTD_OLD 36 # include <linux/mtd/mtd.h> 37 #else 38 # define __user /* nothing */ 39 # include <mtd/mtd-user.h> 40 #endif 41 42 #include "fw_env.h" 43 44 #define CMD_GETENV "fw_printenv" 45 #define CMD_SETENV "fw_setenv" 46 47 typedef struct envdev_s { 48 char devname[16]; /* Device name */ 49 ulong devoff; /* Device offset */ 50 ulong env_size; /* environment size */ 51 ulong erase_size; /* device erase size */ 52 } envdev_t; 53 54 static envdev_t envdevices[2]; 55 static int curdev; 56 57 #define DEVNAME(i) envdevices[(i)].devname 58 #define DEVOFFSET(i) envdevices[(i)].devoff 59 #define ENVSIZE(i) envdevices[(i)].env_size 60 #define DEVESIZE(i) envdevices[(i)].erase_size 61 62 #define CFG_ENV_SIZE ENVSIZE(curdev) 63 64 #define ENV_SIZE getenvsize() 65 66 typedef struct environment_s { 67 ulong crc; /* CRC32 over data bytes */ 68 unsigned char flags; /* active or obsolete */ 69 char *data; 70 } env_t; 71 72 static env_t environment; 73 74 static int HaveRedundEnv = 0; 75 76 static unsigned char active_flag = 1; 77 static unsigned char obsolete_flag = 0; 78 79 80 #define XMK_STR(x) #x 81 #define MK_STR(x) XMK_STR(x) 82 83 static char default_environment[] = { 84 #if defined(CONFIG_BOOTARGS) 85 "bootargs=" CONFIG_BOOTARGS "\0" 86 #endif 87 #if defined(CONFIG_BOOTCOMMAND) 88 "bootcmd=" CONFIG_BOOTCOMMAND "\0" 89 #endif 90 #if defined(CONFIG_RAMBOOTCOMMAND) 91 "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" 92 #endif 93 #if defined(CONFIG_NFSBOOTCOMMAND) 94 "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" 95 #endif 96 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) 97 "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0" 98 #endif 99 #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) 100 "baudrate=" MK_STR (CONFIG_BAUDRATE) "\0" 101 #endif 102 #ifdef CONFIG_LOADS_ECHO 103 "loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "\0" 104 #endif 105 #ifdef CONFIG_ETHADDR 106 "ethaddr=" MK_STR (CONFIG_ETHADDR) "\0" 107 #endif 108 #ifdef CONFIG_ETH1ADDR 109 "eth1addr=" MK_STR (CONFIG_ETH1ADDR) "\0" 110 #endif 111 #ifdef CONFIG_ETH2ADDR 112 "eth2addr=" MK_STR (CONFIG_ETH2ADDR) "\0" 113 #endif 114 #ifdef CONFIG_ETH3ADDR 115 "eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0" 116 #endif 117 #ifdef CONFIG_ETHPRIME 118 "ethprime=" CONFIG_ETHPRIME "\0" 119 #endif 120 #ifdef CONFIG_IPADDR 121 "ipaddr=" MK_STR (CONFIG_IPADDR) "\0" 122 #endif 123 #ifdef CONFIG_SERVERIP 124 "serverip=" MK_STR (CONFIG_SERVERIP) "\0" 125 #endif 126 #ifdef CFG_AUTOLOAD 127 "autoload=" CFG_AUTOLOAD "\0" 128 #endif 129 #ifdef CONFIG_ROOTPATH 130 "rootpath=" MK_STR (CONFIG_ROOTPATH) "\0" 131 #endif 132 #ifdef CONFIG_GATEWAYIP 133 "gatewayip=" MK_STR (CONFIG_GATEWAYIP) "\0" 134 #endif 135 #ifdef CONFIG_NETMASK 136 "netmask=" MK_STR (CONFIG_NETMASK) "\0" 137 #endif 138 #ifdef CONFIG_HOSTNAME 139 "hostname=" MK_STR (CONFIG_HOSTNAME) "\0" 140 #endif 141 #ifdef CONFIG_BOOTFILE 142 "bootfile=" MK_STR (CONFIG_BOOTFILE) "\0" 143 #endif 144 #ifdef CONFIG_LOADADDR 145 "loadaddr=" MK_STR (CONFIG_LOADADDR) "\0" 146 #endif 147 #ifdef CONFIG_PREBOOT 148 "preboot=" CONFIG_PREBOOT "\0" 149 #endif 150 #ifdef CONFIG_CLOCKS_IN_MHZ 151 "clocks_in_mhz=" "1" "\0" 152 #endif 153 #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) 154 "pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0" 155 #endif 156 #ifdef CONFIG_EXTRA_ENV_SETTINGS 157 CONFIG_EXTRA_ENV_SETTINGS 158 #endif 159 "\0" /* Termimate env_t data with 2 NULs */ 160 }; 161 162 static int flash_io (int mode); 163 static char *envmatch (char * s1, char * s2); 164 static int env_init (void); 165 static int parse_config (void); 166 167 #if defined(CONFIG_FILE) 168 static int get_config (char *); 169 #endif 170 static inline ulong getenvsize (void) 171 { 172 ulong rc = CFG_ENV_SIZE - sizeof (long); 173 174 if (HaveRedundEnv) 175 rc -= sizeof (char); 176 return rc; 177 } 178 179 /* 180 * Search the environment for a variable. 181 * Return the value, if found, or NULL, if not found. 182 */ 183 char *fw_getenv (char *name) 184 { 185 char *env, *nxt; 186 187 if (env_init ()) 188 return (NULL); 189 190 for (env = environment.data; *env; env = nxt + 1) { 191 char *val; 192 193 for (nxt = env; *nxt; ++nxt) { 194 if (nxt >= &environment.data[ENV_SIZE]) { 195 fprintf (stderr, "## Error: " 196 "environment not terminated\n"); 197 return (NULL); 198 } 199 } 200 val = envmatch (name, env); 201 if (!val) 202 continue; 203 return (val); 204 } 205 return (NULL); 206 } 207 208 /* 209 * Print the current definition of one, or more, or all 210 * environment variables 211 */ 212 void fw_printenv (int argc, char *argv[]) 213 { 214 char *env, *nxt; 215 int i, n_flag; 216 217 if (env_init ()) 218 return; 219 220 if (argc == 1) { /* Print all env variables */ 221 for (env = environment.data; *env; env = nxt + 1) { 222 for (nxt = env; *nxt; ++nxt) { 223 if (nxt >= &environment.data[ENV_SIZE]) { 224 fprintf (stderr, "## Error: " 225 "environment not terminated\n"); 226 return; 227 } 228 } 229 230 printf ("%s\n", env); 231 } 232 return; 233 } 234 235 if (strcmp (argv[1], "-n") == 0) { 236 n_flag = 1; 237 ++argv; 238 --argc; 239 if (argc != 2) { 240 fprintf (stderr, "## Error: " 241 "`-n' option requires exactly one argument\n"); 242 return; 243 } 244 } else { 245 n_flag = 0; 246 } 247 248 for (i = 1; i < argc; ++i) { /* print single env variables */ 249 char *name = argv[i]; 250 char *val = NULL; 251 252 for (env = environment.data; *env; env = nxt + 1) { 253 254 for (nxt = env; *nxt; ++nxt) { 255 if (nxt >= &environment.data[ENV_SIZE]) { 256 fprintf (stderr, "## Error: " 257 "environment not terminated\n"); 258 return; 259 } 260 } 261 val = envmatch (name, env); 262 if (val) { 263 if (!n_flag) { 264 fputs (name, stdout); 265 putc ('=', stdout); 266 } 267 puts (val); 268 break; 269 } 270 } 271 if (!val) 272 fprintf (stderr, "## Error: \"%s\" not defined\n", name); 273 } 274 } 275 276 /* 277 * Deletes or sets environment variables. Returns errno style error codes: 278 * 0 - OK 279 * EINVAL - need at least 1 argument 280 * EROFS - certain variables ("ethaddr", "serial#") cannot be 281 * modified or deleted 282 * 283 */ 284 int fw_setenv (int argc, char *argv[]) 285 { 286 int i, len; 287 char *env, *nxt; 288 char *oldval = NULL; 289 char *name; 290 291 if (argc < 2) { 292 return (EINVAL); 293 } 294 295 if (env_init ()) 296 return (errno); 297 298 name = argv[1]; 299 300 /* 301 * search if variable with this name already exists 302 */ 303 for (nxt = env = environment.data; *env; env = nxt + 1) { 304 for (nxt = env; *nxt; ++nxt) { 305 if (nxt >= &environment.data[ENV_SIZE]) { 306 fprintf (stderr, "## Error: " 307 "environment not terminated\n"); 308 return (EINVAL); 309 } 310 } 311 if ((oldval = envmatch (name, env)) != NULL) 312 break; 313 } 314 315 /* 316 * Delete any existing definition 317 */ 318 if (oldval) { 319 /* 320 * Ethernet Address and serial# can be set only once 321 */ 322 if ((strcmp (name, "ethaddr") == 0) || 323 (strcmp (name, "serial#") == 0)) { 324 fprintf (stderr, "Can't overwrite \"%s\"\n", name); 325 return (EROFS); 326 } 327 328 if (*++nxt == '\0') { 329 *env = '\0'; 330 } else { 331 for (;;) { 332 *env = *nxt++; 333 if ((*env == '\0') && (*nxt == '\0')) 334 break; 335 ++env; 336 } 337 } 338 *++env = '\0'; 339 } 340 341 /* Delete only ? */ 342 if (argc < 3) 343 goto WRITE_FLASH; 344 345 /* 346 * Append new definition at the end 347 */ 348 for (env = environment.data; *env || *(env + 1); ++env); 349 if (env > environment.data) 350 ++env; 351 /* 352 * Overflow when: 353 * "name" + "=" + "val" +"\0\0" > CFG_ENV_SIZE - (env-environment) 354 */ 355 len = strlen (name) + 2; 356 /* add '=' for first arg, ' ' for all others */ 357 for (i = 2; i < argc; ++i) { 358 len += strlen (argv[i]) + 1; 359 } 360 if (len > (&environment.data[ENV_SIZE] - env)) { 361 fprintf (stderr, 362 "Error: environment overflow, \"%s\" deleted\n", 363 name); 364 return (-1); 365 } 366 while ((*env = *name++) != '\0') 367 env++; 368 for (i = 2; i < argc; ++i) { 369 char *val = argv[i]; 370 371 *env = (i == 2) ? '=' : ' '; 372 while ((*++env = *val++) != '\0'); 373 } 374 375 /* end is marked with double '\0' */ 376 *++env = '\0'; 377 378 WRITE_FLASH: 379 380 /* Update CRC */ 381 environment.crc = crc32 (0, (uint8_t*) environment.data, ENV_SIZE); 382 383 /* write environment back to flash */ 384 if (flash_io (O_RDWR)) { 385 fprintf (stderr, "Error: can't write fw_env to flash\n"); 386 return (-1); 387 } 388 389 return (0); 390 } 391 392 static int flash_io (int mode) 393 { 394 int fd, fdr, rc, otherdev, len, resid; 395 erase_info_t erase; 396 char *data = NULL; 397 398 if ((fd = open (DEVNAME (curdev), mode)) < 0) { 399 fprintf (stderr, 400 "Can't open %s: %s\n", 401 DEVNAME (curdev), strerror (errno)); 402 return (-1); 403 } 404 405 len = sizeof (environment.crc); 406 if (HaveRedundEnv) { 407 len += sizeof (environment.flags); 408 } 409 410 if (mode == O_RDWR) { 411 if (HaveRedundEnv) { 412 /* switch to next partition for writing */ 413 otherdev = !curdev; 414 if ((fdr = open (DEVNAME (otherdev), mode)) < 0) { 415 fprintf (stderr, 416 "Can't open %s: %s\n", 417 DEVNAME (otherdev), 418 strerror (errno)); 419 return (-1); 420 } 421 } else { 422 otherdev = curdev; 423 fdr = fd; 424 } 425 printf ("Unlocking flash...\n"); 426 erase.length = DEVESIZE (otherdev); 427 erase.start = DEVOFFSET (otherdev); 428 ioctl (fdr, MEMUNLOCK, &erase); 429 430 if (HaveRedundEnv) { 431 erase.length = DEVESIZE (curdev); 432 erase.start = DEVOFFSET (curdev); 433 ioctl (fd, MEMUNLOCK, &erase); 434 environment.flags = active_flag; 435 } 436 437 printf ("Done\n"); 438 resid = DEVESIZE (otherdev) - CFG_ENV_SIZE; 439 if (resid) { 440 if ((data = malloc (resid)) == NULL) { 441 fprintf (stderr, 442 "Cannot malloc %d bytes: %s\n", 443 resid, 444 strerror (errno)); 445 return (-1); 446 } 447 if (lseek (fdr, DEVOFFSET (otherdev) + CFG_ENV_SIZE, SEEK_SET) 448 == -1) { 449 fprintf (stderr, "seek error on %s: %s\n", 450 DEVNAME (otherdev), 451 strerror (errno)); 452 return (-1); 453 } 454 if ((rc = read (fdr, data, resid)) != resid) { 455 fprintf (stderr, 456 "read error on %s: %s\n", 457 DEVNAME (otherdev), 458 strerror (errno)); 459 return (-1); 460 } 461 } 462 463 printf ("Erasing old environment...\n"); 464 465 erase.length = DEVESIZE (otherdev); 466 erase.start = DEVOFFSET (otherdev); 467 if (ioctl (fdr, MEMERASE, &erase) != 0) { 468 fprintf (stderr, "MTD erase error on %s: %s\n", 469 DEVNAME (otherdev), 470 strerror (errno)); 471 return (-1); 472 } 473 474 printf ("Done\n"); 475 476 printf ("Writing environment to %s...\n", DEVNAME (otherdev)); 477 if (lseek (fdr, DEVOFFSET (otherdev), SEEK_SET) == -1) { 478 fprintf (stderr, 479 "seek error on %s: %s\n", 480 DEVNAME (otherdev), strerror (errno)); 481 return (-1); 482 } 483 if (write (fdr, &environment, len) != len) { 484 fprintf (stderr, 485 "CRC write error on %s: %s\n", 486 DEVNAME (otherdev), strerror (errno)); 487 return (-1); 488 } 489 if (write (fdr, environment.data, ENV_SIZE) != ENV_SIZE) { 490 fprintf (stderr, 491 "Write error on %s: %s\n", 492 DEVNAME (otherdev), strerror (errno)); 493 return (-1); 494 } 495 if (resid) { 496 if (write (fdr, data, resid) != resid) { 497 fprintf (stderr, 498 "write error on %s: %s\n", 499 DEVNAME (curdev), strerror (errno)); 500 return (-1); 501 } 502 free (data); 503 } 504 if (HaveRedundEnv) { 505 /* change flag on current active env partition */ 506 if (lseek (fd, DEVOFFSET (curdev) + sizeof (ulong), SEEK_SET) 507 == -1) { 508 fprintf (stderr, "seek error on %s: %s\n", 509 DEVNAME (curdev), strerror (errno)); 510 return (-1); 511 } 512 if (write (fd, &obsolete_flag, sizeof (obsolete_flag)) != 513 sizeof (obsolete_flag)) { 514 fprintf (stderr, 515 "Write error on %s: %s\n", 516 DEVNAME (curdev), strerror (errno)); 517 return (-1); 518 } 519 } 520 printf ("Done\n"); 521 printf ("Locking ...\n"); 522 erase.length = DEVESIZE (otherdev); 523 erase.start = DEVOFFSET (otherdev); 524 ioctl (fdr, MEMLOCK, &erase); 525 if (HaveRedundEnv) { 526 erase.length = DEVESIZE (curdev); 527 erase.start = DEVOFFSET (curdev); 528 ioctl (fd, MEMLOCK, &erase); 529 if (close (fdr)) { 530 fprintf (stderr, 531 "I/O error on %s: %s\n", 532 DEVNAME (otherdev), 533 strerror (errno)); 534 return (-1); 535 } 536 } 537 printf ("Done\n"); 538 } else { 539 540 if (lseek (fd, DEVOFFSET (curdev), SEEK_SET) == -1) { 541 fprintf (stderr, 542 "seek error on %s: %s\n", 543 DEVNAME (curdev), strerror (errno)); 544 return (-1); 545 } 546 if (read (fd, &environment, len) != len) { 547 fprintf (stderr, 548 "CRC read error on %s: %s\n", 549 DEVNAME (curdev), strerror (errno)); 550 return (-1); 551 } 552 if ((rc = read (fd, environment.data, ENV_SIZE)) != ENV_SIZE) { 553 fprintf (stderr, 554 "Read error on %s: %s\n", 555 DEVNAME (curdev), strerror (errno)); 556 return (-1); 557 } 558 } 559 560 if (close (fd)) { 561 fprintf (stderr, 562 "I/O error on %s: %s\n", 563 DEVNAME (curdev), strerror (errno)); 564 return (-1); 565 } 566 567 /* everything ok */ 568 return (0); 569 } 570 571 /* 572 * s1 is either a simple 'name', or a 'name=value' pair. 573 * s2 is a 'name=value' pair. 574 * If the names match, return the value of s2, else NULL. 575 */ 576 577 static char *envmatch (char * s1, char * s2) 578 { 579 580 while (*s1 == *s2++) 581 if (*s1++ == '=') 582 return (s2); 583 if (*s1 == '\0' && *(s2 - 1) == '=') 584 return (s2); 585 return (NULL); 586 } 587 588 /* 589 * Prevent confusion if running from erased flash memory 590 */ 591 static int env_init (void) 592 { 593 int crc1, crc1_ok; 594 char *addr1; 595 596 int crc2, crc2_ok; 597 char flag1, flag2, *addr2; 598 599 if (parse_config ()) /* should fill envdevices */ 600 return 1; 601 602 if ((addr1 = calloc (1, ENV_SIZE)) == NULL) { 603 fprintf (stderr, 604 "Not enough memory for environment (%ld bytes)\n", 605 ENV_SIZE); 606 return (errno); 607 } 608 609 /* read environment from FLASH to local buffer */ 610 environment.data = addr1; 611 curdev = 0; 612 if (flash_io (O_RDONLY)) { 613 return (errno); 614 } 615 616 crc1_ok = ((crc1 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE)) 617 == environment.crc); 618 if (!HaveRedundEnv) { 619 if (!crc1_ok) { 620 fprintf (stderr, 621 "Warning: Bad CRC, using default environment\n"); 622 memcpy(environment.data, default_environment, sizeof default_environment); 623 } 624 } else { 625 flag1 = environment.flags; 626 627 curdev = 1; 628 if ((addr2 = calloc (1, ENV_SIZE)) == NULL) { 629 fprintf (stderr, 630 "Not enough memory for environment (%ld bytes)\n", 631 ENV_SIZE); 632 return (errno); 633 } 634 environment.data = addr2; 635 636 if (flash_io (O_RDONLY)) { 637 return (errno); 638 } 639 640 crc2_ok = ((crc2 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE)) 641 == environment.crc); 642 flag2 = environment.flags; 643 644 if (crc1_ok && !crc2_ok) { 645 environment.data = addr1; 646 environment.flags = flag1; 647 environment.crc = crc1; 648 curdev = 0; 649 free (addr2); 650 } else if (!crc1_ok && crc2_ok) { 651 environment.data = addr2; 652 environment.flags = flag2; 653 environment.crc = crc2; 654 curdev = 1; 655 free (addr1); 656 } else if (!crc1_ok && !crc2_ok) { 657 fprintf (stderr, 658 "Warning: Bad CRC, using default environment\n"); 659 memcpy(environment.data, default_environment, sizeof default_environment); 660 curdev = 0; 661 free (addr1); 662 } else if (flag1 == active_flag && flag2 == obsolete_flag) { 663 environment.data = addr1; 664 environment.flags = flag1; 665 environment.crc = crc1; 666 curdev = 0; 667 free (addr2); 668 } else if (flag1 == obsolete_flag && flag2 == active_flag) { 669 environment.data = addr2; 670 environment.flags = flag2; 671 environment.crc = crc2; 672 curdev = 1; 673 free (addr1); 674 } else if (flag1 == flag2) { 675 environment.data = addr1; 676 environment.flags = flag1; 677 environment.crc = crc1; 678 curdev = 0; 679 free (addr2); 680 } else if (flag1 == 0xFF) { 681 environment.data = addr1; 682 environment.flags = flag1; 683 environment.crc = crc1; 684 curdev = 0; 685 free (addr2); 686 } else if (flag2 == 0xFF) { 687 environment.data = addr2; 688 environment.flags = flag2; 689 environment.crc = crc2; 690 curdev = 1; 691 free (addr1); 692 } 693 } 694 return (0); 695 } 696 697 698 static int parse_config () 699 { 700 struct stat st; 701 702 #if defined(CONFIG_FILE) 703 /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */ 704 if (get_config (CONFIG_FILE)) { 705 fprintf (stderr, 706 "Cannot parse config file: %s\n", strerror (errno)); 707 return 1; 708 } 709 #else 710 strcpy (DEVNAME (0), DEVICE1_NAME); 711 DEVOFFSET (0) = DEVICE1_OFFSET; 712 ENVSIZE (0) = ENV1_SIZE; 713 DEVESIZE (0) = DEVICE1_ESIZE; 714 #ifdef HAVE_REDUND 715 strcpy (DEVNAME (1), DEVICE2_NAME); 716 DEVOFFSET (1) = DEVICE2_OFFSET; 717 ENVSIZE (1) = ENV2_SIZE; 718 DEVESIZE (1) = DEVICE2_ESIZE; 719 HaveRedundEnv = 1; 720 #endif 721 #endif 722 if (stat (DEVNAME (0), &st)) { 723 fprintf (stderr, 724 "Cannot access MTD device %s: %s\n", 725 DEVNAME (0), strerror (errno)); 726 return 1; 727 } 728 729 if (HaveRedundEnv && stat (DEVNAME (1), &st)) { 730 fprintf (stderr, 731 "Cannot access MTD device %s: %s\n", 732 DEVNAME (1), strerror (errno)); 733 return 1; 734 } 735 return 0; 736 } 737 738 #if defined(CONFIG_FILE) 739 static int get_config (char *fname) 740 { 741 FILE *fp; 742 int i = 0; 743 int rc; 744 char dump[128]; 745 746 if ((fp = fopen (fname, "r")) == NULL) { 747 return 1; 748 } 749 750 while ((i < 2) && ((rc = fscanf (fp, "%s %lx %lx %lx", 751 DEVNAME (i), 752 &DEVOFFSET (i), 753 &ENVSIZE (i), 754 &DEVESIZE (i) )) != EOF)) { 755 756 /* Skip incomplete conversions and comment strings */ 757 if ((rc < 3) || (*DEVNAME (i) == '#')) { 758 fgets (dump, sizeof (dump), fp); /* Consume till end */ 759 continue; 760 } 761 762 i++; 763 } 764 fclose (fp); 765 766 HaveRedundEnv = i - 1; 767 if (!i) { /* No valid entries found */ 768 errno = EINVAL; 769 return 1; 770 } else 771 return 0; 772 } 773 #endif 774