1 /* 2 * (C) Copyright 2000-2008 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 int fw_printenv (int argc, char *argv[]) 213 { 214 char *env, *nxt; 215 int i, n_flag; 216 int rc = 0; 217 218 if (env_init ()) 219 return (-1); 220 221 if (argc == 1) { /* Print all env variables */ 222 for (env = environment.data; *env; env = nxt + 1) { 223 for (nxt = env; *nxt; ++nxt) { 224 if (nxt >= &environment.data[ENV_SIZE]) { 225 fprintf (stderr, "## Error: " 226 "environment not terminated\n"); 227 return (-1); 228 } 229 } 230 231 printf ("%s\n", env); 232 } 233 return (0); 234 } 235 236 if (strcmp (argv[1], "-n") == 0) { 237 n_flag = 1; 238 ++argv; 239 --argc; 240 if (argc != 2) { 241 fprintf (stderr, "## Error: " 242 "`-n' option requires exactly one argument\n"); 243 return (-1); 244 } 245 } else { 246 n_flag = 0; 247 } 248 249 for (i = 1; i < argc; ++i) { /* print single env variables */ 250 char *name = argv[i]; 251 char *val = NULL; 252 253 for (env = environment.data; *env; env = nxt + 1) { 254 255 for (nxt = env; *nxt; ++nxt) { 256 if (nxt >= &environment.data[ENV_SIZE]) { 257 fprintf (stderr, "## Error: " 258 "environment not terminated\n"); 259 return (-1); 260 } 261 } 262 val = envmatch (name, env); 263 if (val) { 264 if (!n_flag) { 265 fputs (name, stdout); 266 putc ('=', stdout); 267 } 268 puts (val); 269 break; 270 } 271 } 272 if (!val) { 273 fprintf (stderr, "## Error: \"%s\" not defined\n", name); 274 rc = -1; 275 } 276 } 277 278 return (rc); 279 } 280 281 /* 282 * Deletes or sets environment variables. Returns errno style error codes: 283 * 0 - OK 284 * EINVAL - need at least 1 argument 285 * EROFS - certain variables ("ethaddr", "serial#") cannot be 286 * modified or deleted 287 * 288 */ 289 int fw_setenv (int argc, char *argv[]) 290 { 291 int i, len; 292 char *env, *nxt; 293 char *oldval = NULL; 294 char *name; 295 296 if (argc < 2) { 297 return (EINVAL); 298 } 299 300 if (env_init ()) 301 return (errno); 302 303 name = argv[1]; 304 305 /* 306 * search if variable with this name already exists 307 */ 308 for (nxt = env = environment.data; *env; env = nxt + 1) { 309 for (nxt = env; *nxt; ++nxt) { 310 if (nxt >= &environment.data[ENV_SIZE]) { 311 fprintf (stderr, "## Error: " 312 "environment not terminated\n"); 313 return (EINVAL); 314 } 315 } 316 if ((oldval = envmatch (name, env)) != NULL) 317 break; 318 } 319 320 /* 321 * Delete any existing definition 322 */ 323 if (oldval) { 324 /* 325 * Ethernet Address and serial# can be set only once 326 */ 327 if ((strcmp (name, "ethaddr") == 0) || 328 (strcmp (name, "serial#") == 0)) { 329 fprintf (stderr, "Can't overwrite \"%s\"\n", name); 330 return (EROFS); 331 } 332 333 if (*++nxt == '\0') { 334 *env = '\0'; 335 } else { 336 for (;;) { 337 *env = *nxt++; 338 if ((*env == '\0') && (*nxt == '\0')) 339 break; 340 ++env; 341 } 342 } 343 *++env = '\0'; 344 } 345 346 /* Delete only ? */ 347 if (argc < 3) 348 goto WRITE_FLASH; 349 350 /* 351 * Append new definition at the end 352 */ 353 for (env = environment.data; *env || *(env + 1); ++env); 354 if (env > environment.data) 355 ++env; 356 /* 357 * Overflow when: 358 * "name" + "=" + "val" +"\0\0" > CFG_ENV_SIZE - (env-environment) 359 */ 360 len = strlen (name) + 2; 361 /* add '=' for first arg, ' ' for all others */ 362 for (i = 2; i < argc; ++i) { 363 len += strlen (argv[i]) + 1; 364 } 365 if (len > (&environment.data[ENV_SIZE] - env)) { 366 fprintf (stderr, 367 "Error: environment overflow, \"%s\" deleted\n", 368 name); 369 return (-1); 370 } 371 while ((*env = *name++) != '\0') 372 env++; 373 for (i = 2; i < argc; ++i) { 374 char *val = argv[i]; 375 376 *env = (i == 2) ? '=' : ' '; 377 while ((*++env = *val++) != '\0'); 378 } 379 380 /* end is marked with double '\0' */ 381 *++env = '\0'; 382 383 WRITE_FLASH: 384 385 /* Update CRC */ 386 environment.crc = crc32 (0, (uint8_t*) environment.data, ENV_SIZE); 387 388 /* write environment back to flash */ 389 if (flash_io (O_RDWR)) { 390 fprintf (stderr, "Error: can't write fw_env to flash\n"); 391 return (-1); 392 } 393 394 return (0); 395 } 396 397 static int flash_io (int mode) 398 { 399 int fd, fdr, rc, otherdev, len, resid; 400 erase_info_t erase; 401 char *data = NULL; 402 403 if ((fd = open (DEVNAME (curdev), mode)) < 0) { 404 fprintf (stderr, 405 "Can't open %s: %s\n", 406 DEVNAME (curdev), strerror (errno)); 407 return (-1); 408 } 409 410 len = sizeof (environment.crc); 411 if (HaveRedundEnv) { 412 len += sizeof (environment.flags); 413 } 414 415 if (mode == O_RDWR) { 416 if (HaveRedundEnv) { 417 /* switch to next partition for writing */ 418 otherdev = !curdev; 419 if ((fdr = open (DEVNAME (otherdev), mode)) < 0) { 420 fprintf (stderr, 421 "Can't open %s: %s\n", 422 DEVNAME (otherdev), 423 strerror (errno)); 424 return (-1); 425 } 426 } else { 427 otherdev = curdev; 428 fdr = fd; 429 } 430 printf ("Unlocking flash...\n"); 431 erase.length = DEVESIZE (otherdev); 432 erase.start = DEVOFFSET (otherdev); 433 ioctl (fdr, MEMUNLOCK, &erase); 434 435 if (HaveRedundEnv) { 436 erase.length = DEVESIZE (curdev); 437 erase.start = DEVOFFSET (curdev); 438 ioctl (fd, MEMUNLOCK, &erase); 439 environment.flags = active_flag; 440 } 441 442 printf ("Done\n"); 443 resid = DEVESIZE (otherdev) - CFG_ENV_SIZE; 444 if (resid) { 445 if ((data = malloc (resid)) == NULL) { 446 fprintf (stderr, 447 "Cannot malloc %d bytes: %s\n", 448 resid, 449 strerror (errno)); 450 return (-1); 451 } 452 if (lseek (fdr, DEVOFFSET (otherdev) + CFG_ENV_SIZE, SEEK_SET) 453 == -1) { 454 fprintf (stderr, "seek error on %s: %s\n", 455 DEVNAME (otherdev), 456 strerror (errno)); 457 return (-1); 458 } 459 if ((rc = read (fdr, data, resid)) != resid) { 460 fprintf (stderr, 461 "read error on %s: %s\n", 462 DEVNAME (otherdev), 463 strerror (errno)); 464 return (-1); 465 } 466 } 467 468 printf ("Erasing old environment...\n"); 469 470 erase.length = DEVESIZE (otherdev); 471 erase.start = DEVOFFSET (otherdev); 472 if (ioctl (fdr, MEMERASE, &erase) != 0) { 473 fprintf (stderr, "MTD erase error on %s: %s\n", 474 DEVNAME (otherdev), 475 strerror (errno)); 476 return (-1); 477 } 478 479 printf ("Done\n"); 480 481 printf ("Writing environment to %s...\n", DEVNAME (otherdev)); 482 if (lseek (fdr, DEVOFFSET (otherdev), SEEK_SET) == -1) { 483 fprintf (stderr, 484 "seek error on %s: %s\n", 485 DEVNAME (otherdev), strerror (errno)); 486 return (-1); 487 } 488 if (write (fdr, &environment, len) != len) { 489 fprintf (stderr, 490 "CRC write error on %s: %s\n", 491 DEVNAME (otherdev), strerror (errno)); 492 return (-1); 493 } 494 if (write (fdr, environment.data, ENV_SIZE) != ENV_SIZE) { 495 fprintf (stderr, 496 "Write error on %s: %s\n", 497 DEVNAME (otherdev), strerror (errno)); 498 return (-1); 499 } 500 if (resid) { 501 if (write (fdr, data, resid) != resid) { 502 fprintf (stderr, 503 "write error on %s: %s\n", 504 DEVNAME (curdev), strerror (errno)); 505 return (-1); 506 } 507 free (data); 508 } 509 if (HaveRedundEnv) { 510 /* change flag on current active env partition */ 511 if (lseek (fd, DEVOFFSET (curdev) + sizeof (ulong), SEEK_SET) 512 == -1) { 513 fprintf (stderr, "seek error on %s: %s\n", 514 DEVNAME (curdev), strerror (errno)); 515 return (-1); 516 } 517 if (write (fd, &obsolete_flag, sizeof (obsolete_flag)) != 518 sizeof (obsolete_flag)) { 519 fprintf (stderr, 520 "Write error on %s: %s\n", 521 DEVNAME (curdev), strerror (errno)); 522 return (-1); 523 } 524 } 525 printf ("Done\n"); 526 printf ("Locking ...\n"); 527 erase.length = DEVESIZE (otherdev); 528 erase.start = DEVOFFSET (otherdev); 529 ioctl (fdr, MEMLOCK, &erase); 530 if (HaveRedundEnv) { 531 erase.length = DEVESIZE (curdev); 532 erase.start = DEVOFFSET (curdev); 533 ioctl (fd, MEMLOCK, &erase); 534 if (close (fdr)) { 535 fprintf (stderr, 536 "I/O error on %s: %s\n", 537 DEVNAME (otherdev), 538 strerror (errno)); 539 return (-1); 540 } 541 } 542 printf ("Done\n"); 543 } else { 544 545 if (lseek (fd, DEVOFFSET (curdev), SEEK_SET) == -1) { 546 fprintf (stderr, 547 "seek error on %s: %s\n", 548 DEVNAME (curdev), strerror (errno)); 549 return (-1); 550 } 551 if (read (fd, &environment, len) != len) { 552 fprintf (stderr, 553 "CRC read error on %s: %s\n", 554 DEVNAME (curdev), strerror (errno)); 555 return (-1); 556 } 557 if ((rc = read (fd, environment.data, ENV_SIZE)) != ENV_SIZE) { 558 fprintf (stderr, 559 "Read error on %s: %s\n", 560 DEVNAME (curdev), strerror (errno)); 561 return (-1); 562 } 563 } 564 565 if (close (fd)) { 566 fprintf (stderr, 567 "I/O error on %s: %s\n", 568 DEVNAME (curdev), strerror (errno)); 569 return (-1); 570 } 571 572 /* everything ok */ 573 return (0); 574 } 575 576 /* 577 * s1 is either a simple 'name', or a 'name=value' pair. 578 * s2 is a 'name=value' pair. 579 * If the names match, return the value of s2, else NULL. 580 */ 581 582 static char *envmatch (char * s1, char * s2) 583 { 584 585 while (*s1 == *s2++) 586 if (*s1++ == '=') 587 return (s2); 588 if (*s1 == '\0' && *(s2 - 1) == '=') 589 return (s2); 590 return (NULL); 591 } 592 593 /* 594 * Prevent confusion if running from erased flash memory 595 */ 596 static int env_init (void) 597 { 598 int crc1, crc1_ok; 599 char *addr1; 600 601 int crc2, crc2_ok; 602 char flag1, flag2, *addr2; 603 604 if (parse_config ()) /* should fill envdevices */ 605 return 1; 606 607 if ((addr1 = calloc (1, ENV_SIZE)) == NULL) { 608 fprintf (stderr, 609 "Not enough memory for environment (%ld bytes)\n", 610 ENV_SIZE); 611 return (errno); 612 } 613 614 /* read environment from FLASH to local buffer */ 615 environment.data = addr1; 616 curdev = 0; 617 if (flash_io (O_RDONLY)) { 618 return (errno); 619 } 620 621 crc1_ok = ((crc1 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE)) 622 == environment.crc); 623 if (!HaveRedundEnv) { 624 if (!crc1_ok) { 625 fprintf (stderr, 626 "Warning: Bad CRC, using default environment\n"); 627 memcpy(environment.data, default_environment, sizeof default_environment); 628 } 629 } else { 630 flag1 = environment.flags; 631 632 curdev = 1; 633 if ((addr2 = calloc (1, ENV_SIZE)) == NULL) { 634 fprintf (stderr, 635 "Not enough memory for environment (%ld bytes)\n", 636 ENV_SIZE); 637 return (errno); 638 } 639 environment.data = addr2; 640 641 if (flash_io (O_RDONLY)) { 642 return (errno); 643 } 644 645 crc2_ok = ((crc2 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE)) 646 == environment.crc); 647 flag2 = environment.flags; 648 649 if (crc1_ok && !crc2_ok) { 650 environment.data = addr1; 651 environment.flags = flag1; 652 environment.crc = crc1; 653 curdev = 0; 654 free (addr2); 655 } else if (!crc1_ok && crc2_ok) { 656 environment.data = addr2; 657 environment.flags = flag2; 658 environment.crc = crc2; 659 curdev = 1; 660 free (addr1); 661 } else if (!crc1_ok && !crc2_ok) { 662 fprintf (stderr, 663 "Warning: Bad CRC, using default environment\n"); 664 memcpy(environment.data, default_environment, sizeof default_environment); 665 curdev = 0; 666 free (addr1); 667 } else if (flag1 == active_flag && flag2 == obsolete_flag) { 668 environment.data = addr1; 669 environment.flags = flag1; 670 environment.crc = crc1; 671 curdev = 0; 672 free (addr2); 673 } else if (flag1 == obsolete_flag && flag2 == active_flag) { 674 environment.data = addr2; 675 environment.flags = flag2; 676 environment.crc = crc2; 677 curdev = 1; 678 free (addr1); 679 } else if (flag1 == flag2) { 680 environment.data = addr1; 681 environment.flags = flag1; 682 environment.crc = crc1; 683 curdev = 0; 684 free (addr2); 685 } else if (flag1 == 0xFF) { 686 environment.data = addr1; 687 environment.flags = flag1; 688 environment.crc = crc1; 689 curdev = 0; 690 free (addr2); 691 } else if (flag2 == 0xFF) { 692 environment.data = addr2; 693 environment.flags = flag2; 694 environment.crc = crc2; 695 curdev = 1; 696 free (addr1); 697 } 698 } 699 return (0); 700 } 701 702 703 static int parse_config () 704 { 705 struct stat st; 706 707 #if defined(CONFIG_FILE) 708 /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */ 709 if (get_config (CONFIG_FILE)) { 710 fprintf (stderr, 711 "Cannot parse config file: %s\n", strerror (errno)); 712 return 1; 713 } 714 #else 715 strcpy (DEVNAME (0), DEVICE1_NAME); 716 DEVOFFSET (0) = DEVICE1_OFFSET; 717 ENVSIZE (0) = ENV1_SIZE; 718 DEVESIZE (0) = DEVICE1_ESIZE; 719 #ifdef HAVE_REDUND 720 strcpy (DEVNAME (1), DEVICE2_NAME); 721 DEVOFFSET (1) = DEVICE2_OFFSET; 722 ENVSIZE (1) = ENV2_SIZE; 723 DEVESIZE (1) = DEVICE2_ESIZE; 724 HaveRedundEnv = 1; 725 #endif 726 #endif 727 if (stat (DEVNAME (0), &st)) { 728 fprintf (stderr, 729 "Cannot access MTD device %s: %s\n", 730 DEVNAME (0), strerror (errno)); 731 return 1; 732 } 733 734 if (HaveRedundEnv && stat (DEVNAME (1), &st)) { 735 fprintf (stderr, 736 "Cannot access MTD device %s: %s\n", 737 DEVNAME (1), strerror (errno)); 738 return 1; 739 } 740 return 0; 741 } 742 743 #if defined(CONFIG_FILE) 744 static int get_config (char *fname) 745 { 746 FILE *fp; 747 int i = 0; 748 int rc; 749 char dump[128]; 750 751 if ((fp = fopen (fname, "r")) == NULL) { 752 return 1; 753 } 754 755 while ((i < 2) && ((rc = fscanf (fp, "%s %lx %lx %lx", 756 DEVNAME (i), 757 &DEVOFFSET (i), 758 &ENVSIZE (i), 759 &DEVESIZE (i) )) != EOF)) { 760 761 /* Skip incomplete conversions and comment strings */ 762 if ((rc < 3) || (*DEVNAME (i) == '#')) { 763 fgets (dump, sizeof (dump), fp); /* Consume till end */ 764 continue; 765 } 766 767 i++; 768 } 769 fclose (fp); 770 771 HaveRedundEnv = i - 1; 772 if (!i) { /* No valid entries found */ 773 errno = EINVAL; 774 return 1; 775 } else 776 return 0; 777 } 778 #endif 779