Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/networking/ifupdown.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/networking/ifupdown.c
r1765 r2725 2 2 /* 3 3 * ifupdown for busybox 4 * Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au>4 * Copyright (c) 2002 Glenn McGrath 5 5 * Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org> 6 6 * … … 15 15 * configuration. 16 16 * 17 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.17 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 18 18 */ 19 19 20 #include "libbb.h" 21 /* After libbb.h, since it needs sys/types.h on some systems */ 20 22 #include <sys/utsname.h> 21 23 #include <fnmatch.h> 22 #include <getopt.h>23 24 #include "libbb.h"25 24 26 25 #define MAX_OPT_DEPTH 10 … … 33 32 #endif 34 33 34 #define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS 35 35 36 #define debug_noise(args...) /*fprintf(stderr, args)*/ 36 37 … … 42 43 struct method_t { 43 44 const char *name; 44 int (*up)(struct interface_defn_t *ifd, execfn *e) ;45 int (*down)(struct interface_defn_t *ifd, execfn *e) ;45 int (*up)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC; 46 int (*down)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC; 46 47 }; 47 48 … … 87 88 }; 88 89 89 #define OPTION_STR "anvf" USE_FEATURE_IFUPDOWN_MAPPING("m") "i:" 90 91 #define OPTION_STR "anvf" IF_FEATURE_IFUPDOWN_MAPPING("m") "i:" 90 92 enum { 91 OPT_do_all = 0x1,92 OPT_no_act = 0x2,93 OPT_verbose = 0x4,94 OPT_force = 0x8,93 OPT_do_all = 0x1, 94 OPT_no_act = 0x2, 95 OPT_verbose = 0x4, 96 OPT_force = 0x8, 95 97 OPT_no_mappings = 0x10, 96 98 }; 97 #define DO_ALL (option_mask32 & OPT_do_all)98 #define NO_ACT (option_mask32 & OPT_no_act)99 #define VERBOSE (option_mask32 & OPT_verbose)100 #define FORCE (option_mask32 & OPT_force)99 #define DO_ALL (option_mask32 & OPT_do_all) 100 #define NO_ACT (option_mask32 & OPT_no_act) 101 #define VERBOSE (option_mask32 & OPT_verbose) 102 #define FORCE (option_mask32 & OPT_force) 101 103 #define NO_MAPPINGS (option_mask32 & OPT_no_mappings) 102 104 103 static char **my_environ; 104 105 static const char *startup_PATH; 105 106 struct globals { 107 char **my_environ; 108 const char *startup_PATH; 109 } FIX_ALIASING; 110 #define G (*(struct globals*)&bb_common_bufsiz1) 111 #define INIT_G() do { } while (0) 112 106 113 107 114 #if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6 … … 126 133 127 134 if (i == 0) 128 return - r[llen];135 return - (unsigned char)r[llen]; 129 136 return i; 130 137 } … … 135 142 136 143 if (strncmpz(id, "iface", idlen) == 0) { 137 char *result; 138 static char label_buf[20]; 139 safe_strncpy(label_buf, ifd->iface, sizeof(label_buf)); 140 result = strchr(label_buf, ':'); 141 if (result) { 142 *result = '\0'; 143 } 144 return label_buf; 144 // ubuntu's ifup doesn't do this: 145 //static char *label_buf; 146 //char *result; 147 //free(label_buf); 148 //label_buf = xstrdup(ifd->iface); 149 // Remove virtual iface suffix 150 //result = strchrnul(label_buf, ':'); 151 //*result = '\0'; 152 //return label_buf; 153 154 return ifd->iface; 145 155 } 146 156 if (strncmpz(id, "label", idlen) == 0) { … … 155 165 } 156 166 157 # if ENABLE_FEATURE_IFUPDOWN_IP167 # if ENABLE_FEATURE_IFUPDOWN_IP 158 168 static int count_netmask_bits(const char *dotted_quad) 159 169 { … … 186 196 return result; 187 197 } 188 # endif198 # endif 189 199 190 200 static char *parse(const char *command, struct interface_defn_t *ifd) … … 249 259 250 260 if (varvalue) { 261 # if ENABLE_FEATURE_IFUPDOWN_IP 262 /* "hwaddress <class> <address>": 263 * unlike ifconfig, ip doesnt want <class> 264 * (usually "ether" keyword). Skip it. */ 265 if (strncmp(command, "hwaddress", 9) == 0) { 266 varvalue = skip_whitespace(skip_non_whitespace(varvalue)); 267 } 268 # endif 251 269 addstr(&result, varvalue, strlen(varvalue)); 252 270 } else { 253 # if ENABLE_FEATURE_IFUPDOWN_IP271 # if ENABLE_FEATURE_IFUPDOWN_IP 254 272 /* Sigh... Add a special case for 'ip' to convert from 255 273 * dotted quad to bit count style netmasks. */ … … 267 285 } 268 286 } 269 # endif287 # endif 270 288 okay[opt_depth - 1] = 0; 271 289 } … … 312 330 return 1; 313 331 } 314 #endif 332 333 #endif /* FEATURE_IFUPDOWN_IPV4 || FEATURE_IFUPDOWN_IPV6 */ 334 315 335 316 336 #if ENABLE_FEATURE_IFUPDOWN_IPV6 317 static int loopback_up6(struct interface_defn_t *ifd, execfn *exec) 318 { 319 #if ENABLE_FEATURE_IFUPDOWN_IP 337 338 static int FAST_FUNC loopback_up6(struct interface_defn_t *ifd, execfn *exec) 339 { 340 # if ENABLE_FEATURE_IFUPDOWN_IP 320 341 int result; 321 342 result = execute("ip addr add ::1 dev %iface%", ifd, exec); 322 343 result += execute("ip link set %iface% up", ifd, exec); 323 344 return ((result == 2) ? 2 : 0); 324 # else345 # else 325 346 return execute("ifconfig %iface% add ::1", ifd, exec); 326 # endif327 } 328 329 static int loopback_down6(struct interface_defn_t *ifd, execfn *exec)330 { 331 # if ENABLE_FEATURE_IFUPDOWN_IP347 # endif 348 } 349 350 static int FAST_FUNC loopback_down6(struct interface_defn_t *ifd, execfn *exec) 351 { 352 # if ENABLE_FEATURE_IFUPDOWN_IP 332 353 return execute("ip link set %iface% down", ifd, exec); 333 # else354 # else 334 355 return execute("ifconfig %iface% del ::1", ifd, exec); 335 #endif 336 } 337 338 static int static_up6(struct interface_defn_t *ifd, execfn *exec) 356 # endif 357 } 358 359 static int FAST_FUNC manual_up_down6(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM) 360 { 361 return 1; 362 } 363 364 static int FAST_FUNC static_up6(struct interface_defn_t *ifd, execfn *exec) 339 365 { 340 366 int result; 341 # if ENABLE_FEATURE_IFUPDOWN_IP367 # if ENABLE_FEATURE_IFUPDOWN_IP 342 368 result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec); 343 result += execute("ip link set[[ mtu %mtu%]][[ addr ess%hwaddress%]] %iface% up", ifd, exec);369 result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec); 344 370 /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */ 345 371 result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec); 346 # else372 # else 347 373 result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec); 348 374 result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec); 349 375 result += execute("[[route -A inet6 add ::/0 gw %gateway%]]", ifd, exec); 350 # endif376 # endif 351 377 return ((result == 3) ? 3 : 0); 352 378 } 353 379 354 static int static_down6(struct interface_defn_t *ifd, execfn *exec)355 { 356 # if ENABLE_FEATURE_IFUPDOWN_IP380 static int FAST_FUNC static_down6(struct interface_defn_t *ifd, execfn *exec) 381 { 382 # if ENABLE_FEATURE_IFUPDOWN_IP 357 383 return execute("ip link set %iface% down", ifd, exec); 358 # else384 # else 359 385 return execute("ifconfig %iface% down", ifd, exec); 360 # endif361 } 362 363 # if ENABLE_FEATURE_IFUPDOWN_IP364 static int v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)386 # endif 387 } 388 389 # if ENABLE_FEATURE_IFUPDOWN_IP 390 static int FAST_FUNC v4tunnel_up(struct interface_defn_t *ifd, execfn *exec) 365 391 { 366 392 int result; … … 373 399 } 374 400 375 static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)401 static int FAST_FUNC v4tunnel_down(struct interface_defn_t * ifd, execfn * exec) 376 402 { 377 403 return execute("ip tunnel del %iface%", ifd, exec); 378 404 } 379 # endif405 # endif 380 406 381 407 static const struct method_t methods6[] = { 382 #if ENABLE_FEATURE_IFUPDOWN_IP 383 { "v4tunnel", v4tunnel_up, v4tunnel_down, }, 384 #endif 385 { "static", static_up6, static_down6, }, 386 { "loopback", loopback_up6, loopback_down6, }, 408 # if ENABLE_FEATURE_IFUPDOWN_IP 409 { "v4tunnel" , v4tunnel_up , v4tunnel_down , }, 410 # endif 411 { "static" , static_up6 , static_down6 , }, 412 { "manual" , manual_up_down6 , manual_up_down6 , }, 413 { "loopback" , loopback_up6 , loopback_down6 , }, 387 414 }; 388 415 … … 392 419 methods6 393 420 }; 421 394 422 #endif /* FEATURE_IFUPDOWN_IPV6 */ 395 423 424 396 425 #if ENABLE_FEATURE_IFUPDOWN_IPV4 397 static int loopback_up(struct interface_defn_t *ifd, execfn *exec) 398 { 399 #if ENABLE_FEATURE_IFUPDOWN_IP 426 427 static int FAST_FUNC loopback_up(struct interface_defn_t *ifd, execfn *exec) 428 { 429 # if ENABLE_FEATURE_IFUPDOWN_IP 400 430 int result; 401 431 result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec); 402 432 result += execute("ip link set %iface% up", ifd, exec); 403 433 return ((result == 2) ? 2 : 0); 404 # else434 # else 405 435 return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec); 406 # endif407 } 408 409 static int loopback_down(struct interface_defn_t *ifd, execfn *exec)410 { 411 # if ENABLE_FEATURE_IFUPDOWN_IP436 # endif 437 } 438 439 static int FAST_FUNC loopback_down(struct interface_defn_t *ifd, execfn *exec) 440 { 441 # if ENABLE_FEATURE_IFUPDOWN_IP 412 442 int result; 413 443 result = execute("ip addr flush dev %iface%", ifd, exec); 414 444 result += execute("ip link set %iface% down", ifd, exec); 415 445 return ((result == 2) ? 2 : 0); 416 # else446 # else 417 447 return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec); 418 # endif419 } 420 421 static int static_up(struct interface_defn_t *ifd, execfn *exec)448 # endif 449 } 450 451 static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec) 422 452 { 423 453 int result; 424 # if ENABLE_FEATURE_IFUPDOWN_IP454 # if ENABLE_FEATURE_IFUPDOWN_IP 425 455 result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] " 426 456 "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec); 427 result += execute("ip link set[[ mtu %mtu%]][[ addr ess%hwaddress%]] %iface% up", ifd, exec);457 result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec); 428 458 result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec); 429 459 return ((result == 3) ? 3 : 0); 430 # else460 # else 431 461 /* ifconfig said to set iface up before it processes hw %hwaddress%, 432 462 * which then of course fails. Thus we run two separate ifconfig */ … … 438 468 result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec); 439 469 return ((result == 3) ? 3 : 0); 440 # endif441 } 442 443 static int static_down(struct interface_defn_t *ifd, execfn *exec)470 # endif 471 } 472 473 static int FAST_FUNC static_down(struct interface_defn_t *ifd, execfn *exec) 444 474 { 445 475 int result; 446 # if ENABLE_FEATURE_IFUPDOWN_IP476 # if ENABLE_FEATURE_IFUPDOWN_IP 447 477 result = execute("ip addr flush dev %iface%", ifd, exec); 448 478 result += execute("ip link set %iface% down", ifd, exec); 449 #else 450 result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); 479 # else 480 /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */ 481 /* Bringing the interface down deletes the routes in itself. 482 Otherwise this fails if we reference 'gateway' when using this from dhcp_down */ 483 result = 1; 451 484 result += execute("ifconfig %iface% down", ifd, exec); 452 # endif485 # endif 453 486 return ((result == 2) ? 2 : 0); 454 487 } 455 488 456 #if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP 457 struct dhcp_client_t 458 { 489 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP 490 struct dhcp_client_t { 459 491 const char *name; 460 492 const char *startcmd; … … 464 496 static const struct dhcp_client_t ext_dhcp_clients[] = { 465 497 { "dhcpcd", 466 "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client id%]][[ -l %leasetime%]] %iface%",498 "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client%]][[ -l %leasetime%]] %iface%", 467 499 "dhcpcd -k %iface%", 468 500 }, … … 476 508 }, 477 509 { "udhcpc", 478 "udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]", 510 "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %client%]]" 511 "[[ -s %script%]][[ %udhcpc_opts%]]", 479 512 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", 480 513 }, 481 514 }; 482 # endif /* ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCPC */483 484 static int dhcp_up(struct interface_defn_t *ifd, execfn *exec) 485 { 486 #if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP 487 inti;488 # if ENABLE_FEATURE_IFUPDOWN_IP515 # endif /* FEATURE_IFUPDOWN_EXTERNAL_DHCPC */ 516 517 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP 518 static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec) 519 { 520 unsigned i; 521 # if ENABLE_FEATURE_IFUPDOWN_IP 489 522 /* ip doesn't up iface when it configures it (unlike ifconfig) */ 490 if (!execute("ip link set %iface% up", ifd, exec))523 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec)) 491 524 return 0; 492 #endif 525 # else 526 /* needed if we have hwaddress on dhcp iface */ 527 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec)) 528 return 0; 529 # endif 493 530 for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { 494 531 if (exists_execable(ext_dhcp_clients[i].name)) … … 497 534 bb_error_msg("no dhcp clients found"); 498 535 return 0; 499 #elif ENABLE_APP_UDHCPC 500 #if ENABLE_FEATURE_IFUPDOWN_IP 536 } 537 # elif ENABLE_UDHCPC 538 static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec) 539 { 540 # if ENABLE_FEATURE_IFUPDOWN_IP 501 541 /* ip doesn't up iface when it configures it (unlike ifconfig) */ 502 if (!execute("ip link set %iface% up", ifd, exec))542 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec)) 503 543 return 0; 504 #endif 505 return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid " 506 "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]", 544 # else 545 /* needed if we have hwaddress on dhcp iface */ 546 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec)) 547 return 0; 548 # endif 549 return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid " 550 "-i %iface%[[ -H %hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]", 507 551 ifd, exec); 508 #else 552 } 553 # else 554 static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd UNUSED_PARAM, 555 execfn *exec UNUSED_PARAM) 556 { 509 557 return 0; /* no dhcp support */ 510 #endif 511 } 512 513 static int dhcp_down(struct interface_defn_t *ifd, execfn *exec) 514 { 515 #if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP 516 int i; 558 } 559 # endif 560 561 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP 562 static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec) 563 { 564 int result = 0; 565 unsigned i; 566 517 567 for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { 518 if (exists_execable(ext_dhcp_clients[i].name)) 519 return execute(ext_dhcp_clients[i].stopcmd, ifd, exec); 520 } 521 bb_error_msg("no dhcp clients found, using static interface shutdown"); 522 return static_down(ifd, exec); 523 #elif ENABLE_APP_UDHCPC 524 return execute("kill " 525 "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); 526 #else 568 if (exists_execable(ext_dhcp_clients[i].name)) { 569 result = execute(ext_dhcp_clients[i].stopcmd, ifd, exec); 570 if (result) 571 break; 572 } 573 } 574 575 if (!result) 576 bb_error_msg("warning: no dhcp clients found and stopped"); 577 578 /* Sleep a bit, otherwise static_down tries to bring down interface too soon, 579 and it may come back up because udhcpc is still shutting down */ 580 usleep(100000); 581 result += static_down(ifd, exec); 582 return ((result == 3) ? 3 : 0); 583 } 584 # elif ENABLE_UDHCPC 585 static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec) 586 { 587 int result; 588 result = execute( 589 "test -f /var/run/udhcpc.%iface%.pid && " 590 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", 591 ifd, exec); 592 /* Also bring the hardware interface down since 593 killing the dhcp client alone doesn't do it. 594 This enables consecutive ifup->ifdown->ifup */ 595 /* Sleep a bit, otherwise static_down tries to bring down interface too soon, 596 and it may come back up because udhcpc is still shutting down */ 597 usleep(100000); 598 result += static_down(ifd, exec); 599 return ((result == 3) ? 3 : 0); 600 } 601 # else 602 static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd UNUSED_PARAM, 603 execfn *exec UNUSED_PARAM) 604 { 527 605 return 0; /* no dhcp support */ 528 #endif 529 } 530 531 static int manual_up_down(struct interface_defn_t *ifd, execfn *exec)606 } 607 # endif 608 609 static int FAST_FUNC manual_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM) 532 610 { 533 611 return 1; 534 612 } 535 613 536 static int bootp_up(struct interface_defn_t *ifd, execfn *exec)614 static int FAST_FUNC bootp_up(struct interface_defn_t *ifd, execfn *exec) 537 615 { 538 616 return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%" … … 541 619 } 542 620 543 static int ppp_up(struct interface_defn_t *ifd, execfn *exec)621 static int FAST_FUNC ppp_up(struct interface_defn_t *ifd, execfn *exec) 544 622 { 545 623 return execute("pon[[ %provider%]]", ifd, exec); 546 624 } 547 625 548 static int ppp_down(struct interface_defn_t *ifd, execfn *exec)626 static int FAST_FUNC ppp_down(struct interface_defn_t *ifd, execfn *exec) 549 627 { 550 628 return execute("poff[[ %provider%]]", ifd, exec); 551 629 } 552 630 553 static int wvdial_up(struct interface_defn_t *ifd, execfn *exec)631 static int FAST_FUNC wvdial_up(struct interface_defn_t *ifd, execfn *exec) 554 632 { 555 633 return execute("start-stop-daemon --start -x wvdial " … … 557 635 } 558 636 559 static int wvdial_down(struct interface_defn_t *ifd, execfn *exec)637 static int FAST_FUNC wvdial_down(struct interface_defn_t *ifd, execfn *exec) 560 638 { 561 639 return execute("start-stop-daemon --stop -x wvdial " … … 564 642 565 643 static const struct method_t methods[] = { 566 { "manual" , manual_up_down, manual_up_down, },567 { "wvdial" , wvdial_up, wvdial_down, },568 { "ppp" , ppp_up, ppp_down, },569 { "static" , static_up, static_down, },570 { "bootp" , bootp_up, static_down, },571 { "dhcp" , dhcp_up, dhcp_down, },572 { "loopback", loopback_up , loopback_down, },644 { "manual" , manual_up_down, manual_up_down, }, 645 { "wvdial" , wvdial_up , wvdial_down , }, 646 { "ppp" , ppp_up , ppp_down , }, 647 { "static" , static_up , static_down , }, 648 { "bootp" , bootp_up , static_down , }, 649 { "dhcp" , dhcp_up , dhcp_down , }, 650 { "loopback", loopback_up , loopback_down , }, 573 651 }; 574 652 … … 579 657 }; 580 658 581 #endif /* if ENABLE_FEATURE_IFUPDOWN_IPV4 */ 582 659 #endif /* FEATURE_IFUPDOWN_IPV4 */ 660 661 662 /* Returns pointer to the next word, or NULL. 663 * In 1st case, advances *buf to the word after this one. 664 */ 583 665 static char *next_word(char **buf) 584 666 { 585 unsigned shortlength;667 unsigned length; 586 668 char *word; 587 588 if (!buf || !*buf || !**buf) {589 return NULL;590 }591 669 592 670 /* Skip over leading whitespace */ 593 671 word = skip_whitespace(*buf); 594 672 595 /* S kip over comments*/596 if (*word == ' #') {673 /* Stop on EOL */ 674 if (*word == '\0') 597 675 return NULL; 598 } 599 600 /* Find the length of this word */ 676 677 /* Find the length of this word (can't be 0) */ 601 678 length = strcspn(word, " \t\n"); 602 if (length == 0) { 603 return NULL; 604 } 605 *buf = word + length; 606 /*DBU:[dave@cray.com] if we are already at EOL dont't increment beyond it */ 607 if (**buf) { 608 **buf = '\0'; 609 (*buf)++; 610 } 679 680 /* Unless we are already at NUL, store NUL and advance */ 681 if (word[length] != '\0') 682 word[length++] = '\0'; 683 684 *buf = skip_whitespace(word + length); 611 685 612 686 return word; … … 634 708 if (!name) 635 709 return NULL; 636 710 /* TODO: use index_in_str_array() */ 637 711 for (i = 0; i < af->n_methods; i++) { 638 712 if (strcmp(af->method[i].name, name) == 0) { … … 643 717 } 644 718 645 static const llist_t *find_list_string(const llist_t *list, const char *string)646 {647 if (string == NULL)648 return NULL;649 650 while (list) {651 if (strcmp(list->data, string) == 0) {652 return list;653 }654 list = list->link;655 }656 return NULL;657 }658 659 719 static struct interfaces_file_t *read_interfaces(const char *filename) 660 720 { 721 /* Let's try to be compatible. 722 * 723 * "man 5 interfaces" says: 724 * Lines starting with "#" are ignored. Note that end-of-line 725 * comments are NOT supported, comments must be on a line of their own. 726 * A line may be extended across multiple lines by making 727 * the last character a backslash. 728 * 729 * Seen elsewhere in example config file: 730 * A first non-blank "#" character makes the rest of the line 731 * be ignored. Blank lines are ignored. Lines may be indented freely. 732 * A "\" character at the very end of the line indicates the next line 733 * should be treated as a continuation of the current one. 734 */ 661 735 #if ENABLE_FEATURE_IFUPDOWN_MAPPING 662 736 struct mapping_defn_t *currmap = NULL; … … 665 739 struct interfaces_file_t *defn; 666 740 FILE *f; 667 char *firstword;668 741 char *buf; 669 742 char *first_word; 743 char *rest_of_line; 670 744 enum { NONE, IFACE, MAPPING } currently_processing = NONE; 671 745 672 defn = xzalloc(sizeof(struct interfaces_file_t)); 673 674 f = xfopen(filename, "r"); 675 676 while ((buf = xmalloc_getline(f)) != NULL) { 677 char *buf_ptr = buf; 678 679 firstword = next_word(&buf_ptr); 680 if (firstword == NULL) { 746 defn = xzalloc(sizeof(*defn)); 747 f = xfopen_for_read(filename); 748 749 while ((buf = xmalloc_fgetline(f)) != NULL) { 750 #if ENABLE_DESKTOP 751 /* Trailing "\" concatenates lines */ 752 char *p; 753 while ((p = last_char_is(buf, '\\')) != NULL) { 754 *p = '\0'; 755 rest_of_line = xmalloc_fgetline(f); 756 if (!rest_of_line) 757 break; 758 p = xasprintf("%s%s", buf, rest_of_line); 681 759 free(buf); 682 continue; /* blank line */ 683 } 684 685 if (strcmp(firstword, "mapping") == 0) { 760 free(rest_of_line); 761 buf = p; 762 } 763 #endif 764 rest_of_line = buf; 765 first_word = next_word(&rest_of_line); 766 if (!first_word || *first_word == '#') { 767 free(buf); 768 continue; /* blank/comment line */ 769 } 770 771 if (strcmp(first_word, "mapping") == 0) { 686 772 #if ENABLE_FEATURE_IFUPDOWN_MAPPING 687 currmap = xzalloc(sizeof(struct mapping_defn_t)); 688 689 while ((firstword = next_word(&buf_ptr)) != NULL) { 690 if (currmap->max_matches == currmap->n_matches) { 691 currmap->max_matches = currmap->max_matches * 2 + 1; 692 currmap->match = xrealloc(currmap->match, sizeof(currmap->match) * currmap->max_matches); 693 } 694 695 currmap->match[currmap->n_matches++] = xstrdup(firstword); 696 } 697 currmap->max_mappings = 0; 698 currmap->n_mappings = 0; 699 currmap->mapping = NULL; 700 currmap->script = NULL; 773 currmap = xzalloc(sizeof(*currmap)); 774 775 while ((first_word = next_word(&rest_of_line)) != NULL) { 776 currmap->match = xrealloc_vector(currmap->match, 4, currmap->n_matches); 777 currmap->match[currmap->n_matches++] = xstrdup(first_word); 778 } 779 /*currmap->max_mappings = 0; - done by xzalloc */ 780 /*currmap->n_mappings = 0;*/ 781 /*currmap->mapping = NULL;*/ 782 /*currmap->script = NULL;*/ 701 783 { 702 784 struct mapping_defn_t **where = &defn->mappings; … … 705 787 } 706 788 *where = currmap; 707 currmap->next = NULL;789 /*currmap->next = NULL;*/ 708 790 } 709 791 debug_noise("Added mapping\n"); 710 792 #endif 711 793 currently_processing = MAPPING; 712 } else if (strcmp(first word, "iface") == 0) {794 } else if (strcmp(first_word, "iface") == 0) { 713 795 static const struct address_family_t *const addr_fams[] = { 714 796 #if ENABLE_FEATURE_IFUPDOWN_IPV4 … … 720 802 NULL 721 803 }; 722 723 804 char *iface_name; 724 805 char *address_family_name; … … 726 807 llist_t *iface_list; 727 808 728 currif = xzalloc(sizeof(struct interface_defn_t)); 729 iface_name = next_word(&buf_ptr); 730 address_family_name = next_word(&buf_ptr); 731 method_name = next_word(&buf_ptr); 732 733 if (buf_ptr == NULL) { 734 bb_error_msg("too few parameters for line \"%s\"", buf); 735 return NULL; 736 } 809 currif = xzalloc(sizeof(*currif)); 810 iface_name = next_word(&rest_of_line); 811 address_family_name = next_word(&rest_of_line); 812 method_name = next_word(&rest_of_line); 813 814 if (method_name == NULL) 815 bb_error_msg_and_die("too few parameters for line \"%s\"", buf); 737 816 738 817 /* ship any trailing whitespace */ 739 buf_ptr = skip_whitespace(buf_ptr); 740 741 if (buf_ptr[0] != '\0') { 742 bb_error_msg("too many parameters \"%s\"", buf); 743 return NULL; 744 } 818 rest_of_line = skip_whitespace(rest_of_line); 819 820 if (rest_of_line[0] != '\0' /* && rest_of_line[0] != '#' */) 821 bb_error_msg_and_die("too many parameters \"%s\"", buf); 745 822 746 823 currif->iface = xstrdup(iface_name); 747 824 748 825 currif->address_family = get_address_family(addr_fams, address_family_name); 749 if (!currif->address_family) { 750 bb_error_msg("unknown address type \"%s\"", address_family_name); 751 return NULL; 752 } 826 if (!currif->address_family) 827 bb_error_msg_and_die("unknown address type \"%s\"", address_family_name); 753 828 754 829 currif->method = get_method(currif->address_family, method_name); 755 if (!currif->method) { 756 bb_error_msg("unknown method \"%s\"", method_name); 757 return NULL; 758 } 830 if (!currif->method) 831 bb_error_msg_and_die("unknown method \"%s\"", method_name); 759 832 760 833 for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) { 761 834 struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data; 762 if ((strcmp(tmp->iface, currif->iface) == 0) &&763 (tmp->address_family == currif->address_family)) {764 bb_error_msg("duplicate interface \"%s\"", tmp->iface);765 return NULL;835 if ((strcmp(tmp->iface, currif->iface) == 0) 836 && (tmp->address_family == currif->address_family) 837 ) { 838 bb_error_msg_and_die("duplicate interface \"%s\"", tmp->iface); 766 839 } 767 840 } … … 770 843 debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name); 771 844 currently_processing = IFACE; 772 } else if (strcmp(first word, "auto") == 0) {773 while ((first word = next_word(&buf_ptr)) != NULL) {845 } else if (strcmp(first_word, "auto") == 0) { 846 while ((first_word = next_word(&rest_of_line)) != NULL) { 774 847 775 848 /* Check the interface isnt already listed */ 776 if ( find_list_string(defn->autointerfaces, firstword)) {849 if (llist_find_str(defn->autointerfaces, first_word)) { 777 850 bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf); 778 851 } 779 852 780 853 /* Add the interface to the list */ 781 llist_add_to_end(&(defn->autointerfaces), xstrdup(first word));782 debug_noise("\nauto %s\n", first word);854 llist_add_to_end(&(defn->autointerfaces), xstrdup(first_word)); 855 debug_noise("\nauto %s\n", first_word); 783 856 } 784 857 currently_processing = NONE; … … 786 859 switch (currently_processing) { 787 860 case IFACE: 788 { 861 if (rest_of_line[0] == '\0') 862 bb_error_msg_and_die("option with empty value \"%s\"", buf); 863 864 if (strcmp(first_word, "up") != 0 865 && strcmp(first_word, "down") != 0 866 && strcmp(first_word, "pre-up") != 0 867 && strcmp(first_word, "post-down") != 0 868 ) { 789 869 int i; 790 791 if (strlen(buf_ptr) == 0) { 792 bb_error_msg("option with empty value \"%s\"", buf); 793 return NULL; 870 for (i = 0; i < currif->n_options; i++) { 871 if (strcmp(currif->option[i].name, first_word) == 0) 872 bb_error_msg_and_die("duplicate option \"%s\"", buf); 794 873 } 795 796 if (strcmp(firstword, "up") != 0797 && strcmp(firstword, "down") != 0798 && strcmp(firstword, "pre-up") != 0799 && strcmp(firstword, "post-down") != 0) {800 for (i = 0; i < currif->n_options; i++) {801 if (strcmp(currif->option[i].name, firstword) == 0) {802 bb_error_msg("duplicate option \"%s\"", buf);803 return NULL;804 }805 }806 }807 874 } 808 875 if (currif->n_options >= currif->max_options) { 809 struct variable_t *opt; 810 811 currif->max_options = currif->max_options + 10; 812 opt = xrealloc(currif->option, sizeof(*opt) * currif->max_options); 813 currif->option = opt; 814 } 815 currif->option[currif->n_options].name = xstrdup(firstword); 816 currif->option[currif->n_options].value = xstrdup(buf_ptr); 817 if (!currif->option[currif->n_options].name) { 818 perror(filename); 819 return NULL; 820 } 821 if (!currif->option[currif->n_options].value) { 822 perror(filename); 823 return NULL; 824 } 825 debug_noise("\t%s=%s\n", currif->option[currif->n_options].name, 826 currif->option[currif->n_options].value); 876 currif->max_options += 10; 877 currif->option = xrealloc(currif->option, 878 sizeof(*currif->option) * currif->max_options); 879 } 880 debug_noise("\t%s=%s\n", first_word, rest_of_line); 881 currif->option[currif->n_options].name = xstrdup(first_word); 882 currif->option[currif->n_options].value = xstrdup(rest_of_line); 827 883 currif->n_options++; 828 884 break; 829 885 case MAPPING: 830 886 #if ENABLE_FEATURE_IFUPDOWN_MAPPING 831 if (strcmp(firstword, "script") == 0) { 832 if (currmap->script != NULL) { 833 bb_error_msg("duplicate script in mapping \"%s\"", buf); 834 return NULL; 835 } else { 836 currmap->script = xstrdup(next_word(&buf_ptr)); 887 if (strcmp(first_word, "script") == 0) { 888 if (currmap->script != NULL) 889 bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf); 890 currmap->script = xstrdup(next_word(&rest_of_line)); 891 } else if (strcmp(first_word, "map") == 0) { 892 if (currmap->n_mappings >= currmap->max_mappings) { 893 currmap->max_mappings = currmap->max_mappings * 2 + 1; 894 currmap->mapping = xrealloc(currmap->mapping, 895 sizeof(char *) * currmap->max_mappings); 837 896 } 838 } else if (strcmp(firstword, "map") == 0) { 839 if (currmap->max_mappings == currmap->n_mappings) { 840 currmap->max_mappings = currmap->max_mappings * 2 + 1; 841 currmap->mapping = xrealloc(currmap->mapping, sizeof(char *) * currmap->max_mappings); 842 } 843 currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&buf_ptr)); 897 currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&rest_of_line)); 844 898 currmap->n_mappings++; 845 899 } else { 846 bb_error_msg("misplaced option \"%s\"", buf); 847 return NULL; 900 bb_error_msg_and_die("misplaced option \"%s\"", buf); 848 901 } 849 902 #endif … … 851 904 case NONE: 852 905 default: 853 bb_error_msg("misplaced option \"%s\"", buf); 854 return NULL; 906 bb_error_msg_and_die("misplaced option \"%s\"", buf); 855 907 } 856 908 } 857 909 free(buf); 858 } 910 } /* while (fgets) */ 911 859 912 if (ferror(f) != 0) { 860 913 /* ferror does NOT set errno! */ … … 869 922 { 870 923 char *result; 871 char *here; 872 char *there; 924 char *dst; 925 char *src; 926 char c; 873 927 874 928 result = xasprintf(format, name, value); 875 929 876 for (here = there = result; *there != '=' && *there; there++) { 877 if (*there == '-') 878 *there = '_'; 879 if (isalpha(*there)) 880 *there = toupper(*there); 881 882 if (isalnum(*there) || *there == '_') { 883 *here = *there; 884 here++; 885 } 886 } 887 memmove(here, there, strlen(there) + 1); 930 for (dst = src = result; (c = *src) != '=' && c; src++) { 931 if (c == '-') 932 c = '_'; 933 if (c >= 'a' && c <= 'z') 934 c -= ('a' - 'A'); 935 if (isalnum(c) || c == '_') 936 *dst++ = c; 937 } 938 overlapping_strcpy(dst, src); 888 939 889 940 return result; … … 892 943 static void set_environ(struct interface_defn_t *iface, const char *mode) 893 944 { 894 char **environend;895 945 int i; 896 c onst int n_env_entries = iface->n_options + 5;897 char **ppch; 898 899 if (my_environ != NULL) {900 for (ppch = my_environ; *ppch; ppch++) {901 free(*ppch);902 *ppch = NULL;903 904 free(my_environ); 905 }906 my_environ = xzalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */));907 environend =my_environ;946 char **pp; 947 948 if (G.my_environ != NULL) { 949 for (pp = G.my_environ; *pp; pp++) { 950 free(*pp); 951 } 952 free(G.my_environ); 953 } 954 955 /* note: last element will stay NULL: */ 956 G.my_environ = xzalloc(sizeof(char *) * (iface->n_options + 6)); 957 pp = G.my_environ; 908 958 909 959 for (i = 0; i < iface->n_options; i++) { … … 915 965 continue; 916 966 } 917 *(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value); 918 } 919 920 *(environend++) = setlocalenv("%s=%s", "IFACE", iface->iface); 921 *(environend++) = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name); 922 *(environend++) = setlocalenv("%s=%s", "METHOD", iface->method->name); 923 *(environend++) = setlocalenv("%s=%s", "MODE", mode); 924 *(environend++) = setlocalenv("%s=%s", "PATH", startup_PATH); 967 *pp++ = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value); 968 } 969 970 *pp++ = setlocalenv("%s=%s", "IFACE", iface->iface); 971 *pp++ = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name); 972 *pp++ = setlocalenv("%s=%s", "METHOD", iface->method->name); 973 *pp++ = setlocalenv("%s=%s", "MODE", mode); 974 if (G.startup_PATH) 975 *pp++ = setlocalenv("%s=%s", "PATH", G.startup_PATH); 925 976 } 926 977 … … 934 985 int status; 935 986 936 fflush (NULL);937 child = fork();987 fflush_all(); 988 child = vfork(); 938 989 switch (child) { 939 990 case -1: /* failure */ 940 991 return 0; 941 992 case 0: /* child */ 942 execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL,my_environ);943 exit(127);944 } 945 waitpid(child, &status, 0);993 execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, (char *) NULL, G.my_environ); 994 _exit(127); 995 } 996 safe_waitpid(child, &status, 0); 946 997 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 947 998 return 0; … … 994 1045 995 1046 #if ENABLE_FEATURE_IFUPDOWN_MAPPING 996 static int popen2(FILE **in, FILE **out, char *command, ...) 997 { 998 va_list ap; 999 char *argv[11] = { command }; 1000 int argc; 1001 int infd[2], outfd[2]; 1047 static int popen2(FILE **in, FILE **out, char *command, char *param) 1048 { 1049 char *argv[3] = { command, param, NULL }; 1050 struct fd_pair infd, outfd; 1002 1051 pid_t pid; 1003 1052 1004 argc = 1; 1005 va_start(ap, command); 1006 while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) { 1007 argc++; 1008 } 1009 argv[argc] = NULL; /* make sure */ 1010 va_end(ap); 1011 1012 if (pipe(infd) != 0) { 1013 return 0; 1014 } 1015 1016 if (pipe(outfd) != 0) { 1017 close(infd[0]); 1018 close(infd[1]); 1019 return 0; 1020 } 1021 1022 fflush(NULL); 1023 switch (pid = fork()) { 1024 case -1: /* failure */ 1025 close(infd[0]); 1026 close(infd[1]); 1027 close(outfd[0]); 1028 close(outfd[1]); 1029 return 0; 1030 case 0: /* child */ 1031 dup2(infd[0], 0); 1032 dup2(outfd[1], 1); 1033 close(infd[0]); 1034 close(infd[1]); 1035 close(outfd[0]); 1036 close(outfd[1]); 1037 BB_EXECVP(command, argv); 1038 exit(127); 1039 default: /* parent */ 1040 *in = fdopen(infd[1], "w"); 1041 *out = fdopen(outfd[0], "r"); 1042 close(infd[0]); 1043 close(outfd[1]); 1044 return pid; 1045 } 1046 /* unreached */ 1047 } 1048 1049 static char *run_mapping(char *physical, struct mapping_defn_t * map) 1053 xpiped_pair(infd); 1054 xpiped_pair(outfd); 1055 1056 fflush_all(); 1057 pid = xvfork(); 1058 1059 if (pid == 0) { 1060 /* Child */ 1061 /* NB: close _first_, then move fds! */ 1062 close(infd.wr); 1063 close(outfd.rd); 1064 xmove_fd(infd.rd, 0); 1065 xmove_fd(outfd.wr, 1); 1066 BB_EXECVP_or_die(argv); 1067 } 1068 /* parent */ 1069 close(infd.rd); 1070 close(outfd.wr); 1071 *in = xfdopen_for_write(infd.wr); 1072 *out = xfdopen_for_read(outfd.rd); 1073 return pid; 1074 } 1075 1076 static char *run_mapping(char *physical, struct mapping_defn_t *map) 1050 1077 { 1051 1078 FILE *in, *out; … … 1055 1082 char *logical = xstrdup(physical); 1056 1083 1057 /* Run the mapping script. */ 1058 pid = popen2(&in, &out, map->script, physical, NULL); 1059 1060 /* popen2() returns 0 on failure. */ 1061 if (pid == 0) 1062 return logical; 1084 /* Run the mapping script. Never fails. */ 1085 pid = popen2(&in, &out, map->script, physical); 1063 1086 1064 1087 /* Write mappings to stdin of mapping script. */ … … 1067 1090 } 1068 1091 fclose(in); 1069 waitpid(pid, &status, 0);1092 safe_waitpid(pid, &status, 0); 1070 1093 1071 1094 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { … … 1073 1096 * grab a line of output and use that as the name of the 1074 1097 * logical interface. */ 1075 char *new_logical = xmalloc (MAX_INTERFACE_LENGTH);1076 1077 if ( fgets(new_logical, MAX_INTERFACE_LENGTH, out)) {1098 char *new_logical = xmalloc_fgetline(out); 1099 1100 if (new_logical) { 1078 1101 /* If we are able to read a line of output from the script, 1079 1102 * remove any trailing whitespace and use this value … … 1086 1109 free(logical); 1087 1110 logical = new_logical; 1088 } else {1089 /* If we are UNABLE to read a line of output, discard our1090 * freshly allocated memory. */1091 free(new_logical);1092 1111 } 1093 1112 } … … 1101 1120 static llist_t *find_iface_state(llist_t *state_list, const char *iface) 1102 1121 { 1103 unsigned shortiface_len = strlen(iface);1122 unsigned iface_len = strlen(iface); 1104 1123 llist_t *search = state_list; 1105 1124 1106 1125 while (search) { 1107 1126 if ((strncmp(search->data, iface, iface_len) == 0) 1108 && (search->data[iface_len] == '=')) { 1127 && (search->data[iface_len] == '=') 1128 ) { 1109 1129 return search; 1110 1130 } … … 1118 1138 { 1119 1139 llist_t *state_list = NULL; 1120 FILE *state_fp = fopen (CONFIG_IFUPDOWN_IFSTATE_PATH, "r");1140 FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH); 1121 1141 1122 1142 if (state_fp) { … … 1134 1154 1135 1155 1136 int ifupdown_main(int argc, char **argv) ;1137 int ifupdown_main(int argc , char **argv)1138 { 1139 int (*cmds)(struct interface_defn_t *) = NULL;1156 int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1157 int ifupdown_main(int argc UNUSED_PARAM, char **argv) 1158 { 1159 int (*cmds)(struct interface_defn_t *); 1140 1160 struct interfaces_file_t *defn; 1141 1161 llist_t *target_list = NULL; … … 1143 1163 bool any_failures = 0; 1144 1164 1165 INIT_G(); 1166 1167 G.startup_PATH = getenv("PATH"); 1168 1145 1169 cmds = iface_down; 1146 1170 if (applet_name[2] == 'u') { … … 1150 1174 1151 1175 getopt32(argv, OPTION_STR, &interfaces); 1152 if (argc - optind > 0) { 1176 argv += optind; 1177 if (argv[0]) { 1153 1178 if (DO_ALL) bb_show_usage(); 1154 1179 } else { … … 1159 1184 defn = read_interfaces(interfaces); 1160 1185 debug_noise("\ndone reading %s\n\n", interfaces); 1161 1162 if (!defn) {1163 return EXIT_FAILURE;1164 }1165 1166 startup_PATH = getenv("PATH");1167 if (!startup_PATH) startup_PATH = "";1168 1186 1169 1187 /* Create a list of interfaces to work on */ … … 1171 1189 target_list = defn->autointerfaces; 1172 1190 } else { 1173 llist_add_to_end(&target_list, argv[ optind]);1191 llist_add_to_end(&target_list, argv[0]); 1174 1192 } 1175 1193 … … 1182 1200 char *pch; 1183 1201 bool okay = 0; 1184 unsignedcmds_ret;1202 int cmds_ret; 1185 1203 1186 1204 iface = xstrdup(target_list->data); … … 1260 1278 } 1261 1279 if (VERBOSE) { 1262 puts("");1280 bb_putchar('\n'); 1263 1281 } 1264 1282 … … 1288 1306 1289 1307 /* Actually write the new state */ 1290 state_fp = xfopen (CONFIG_IFUPDOWN_IFSTATE_PATH, "w");1308 state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH); 1291 1309 state = state_list; 1292 1310 while (state) {
Note:
See TracChangeset
for help on using the changeset viewer.