1/* 2 * Shell-like utility functions 3 * 4 * Copyright 2005, Broadcom Corporation 5 * All Rights Reserved. 6 * 7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY 8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM 9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS 10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. 11 * 12 * $Id: shutils.c,v 1.5 2009/02/04 03:28:20 james26_jang Exp $ 13 */ 14 15#include <stdio.h> 16#include <stdlib.h> 17#include <stdarg.h> 18#include <errno.h> 19#include <fcntl.h> 20#include <limits.h> 21#include <unistd.h> 22#include <signal.h> 23#include <string.h> 24#include <sys/types.h> 25#include <sys/stat.h> 26#include <sys/wait.h> 27#include <sys/ioctl.h> 28 29#include <bcmnvram.h> 30#include <shutils.h> 31 32/* Linux specific headers */ 33#ifdef linux 34#include <error.h> 35#include <termios.h> 36#include <sys/time.h> 37#include <net/ethernet.h> 38#endif /* linux */ 39 40#define MAX_NVPARSE 255 41 42#ifdef linux 43/* 44 * Reads file and returns contents 45 * @param fd file descriptor 46 * @return contents of file or NULL if an error occurred 47 */ 48char * 49fd2str(int fd) 50{ 51 char *buf = NULL; 52 size_t count = 0, n; 53 54 do { 55 buf = realloc(buf, count + 512); 56 n = read(fd, buf + count, 512); 57 if (n < 0) { 58 free(buf); 59 buf = NULL; 60 } 61 count += n; 62 } while (n == 512); 63 64 close(fd); 65 if (buf) 66 buf[count] = '\0'; 67 return buf; 68} 69 70/* 71 * Reads file and returns contents 72 * @param path path to file 73 * @return contents of file or NULL if an error occurred 74 */ 75char * 76file2str(const char *path) 77{ 78 int fd; 79 80 if ((fd = open(path, O_RDONLY)) == -1) { 81 perror(path); 82 return NULL; 83 } 84 85 return fd2str(fd); 86} 87 88/* 89 * Waits for a file descriptor to change status or unblocked signal 90 * @param fd file descriptor 91 * @param timeout seconds to wait before timing out or 0 for no timeout 92 * @return 1 if descriptor changed status or 0 if timed out or -1 on error 93 */ 94int 95waitfor(int fd, int timeout) 96{ 97 fd_set rfds; 98 struct timeval tv = { timeout, 0 }; 99 100 FD_ZERO(&rfds); 101 FD_SET(fd, &rfds); 102 return select(fd + 1, &rfds, NULL, NULL, (timeout > 0) ? &tv : NULL); 103} 104 105/* 106 * Concatenates NULL-terminated list of arguments into a single 107 * commmand and executes it 108 * @param argv argument list 109 * @param path NULL, ">output", or ">>output" 110 * @param timeout seconds to wait before timing out or 0 for no timeout 111 * @param ppid NULL to wait for child termination or pointer to pid 112 * @return return value of executed command or errno 113 */ 114int 115_eval(char *const argv[], char *path, int timeout, int *ppid) 116{ 117 sigset_t set; 118 pid_t pid; 119 int status; 120 int fd; 121 int flags; 122 int sig; 123 124 switch (pid = fork()) { 125 case -1: /* error */ 126 perror("fork"); 127 return errno; 128 case 0: /* child */ 129 /* Reset signal handlers set for parent process */ 130 for (sig = 0; sig < (_NSIG-1); sig++) 131 signal(sig, SIG_DFL); 132 133 /* Unblock signals if called from signal handler */ 134 sigemptyset(&set); 135 sigprocmask(SIG_SETMASK, &set, NULL); 136 137 /* Clean up */ 138 ioctl(0, TIOCNOTTY, 0); 139 close(STDIN_FILENO); 140 setsid(); 141 142 /* Redirect stdout to <path> */ 143 if (path) { 144 flags = O_WRONLY | O_CREAT; 145 if (!strncmp(path, ">>", 2)) { 146 /* append to <path> */ 147 flags |= O_APPEND; 148 path += 2; 149 } else if (!strncmp(path, ">", 1)) { 150 /* overwrite <path> */ 151 flags |= O_TRUNC; 152 path += 1; 153 } 154 if ((fd = open(path, flags, 0644)) < 0) 155 perror(path); 156 else { 157 dup2(fd, STDOUT_FILENO); 158 dup2(fd, STDERR_FILENO); 159 close(fd); 160 } 161 } 162 /*else { 163 fd = open("/dev/null", O_RDWR); 164 dup2(fd, STDOUT_FILENO); 165 dup2(fd, STDERR_FILENO); 166 close(fd); 167 }//*/ 168 fd = open("/dev/null", O_RDWR); // 2009.01 James. 169 170 /* execute command */ 171 dprintf("%s\n", argv[0]); 172 setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1); 173 alarm(timeout); 174 execvp(argv[0], argv); 175 perror(argv[0]); 176 exit(errno); 177 default: /* parent */ 178 if (ppid) { 179 *ppid = pid; 180 return 0; 181 } else { 182 if (waitpid(pid, &status, 0) == -1) { 183 perror("waitpid"); 184 return errno; 185 } 186 if (WIFEXITED(status)) 187 return WEXITSTATUS(status); 188 else 189 return status; 190 } 191 } 192} 193 194/* 195 * Concatenates NULL-terminated list of arguments into a single 196 * commmand and executes it 197 * @param argv argument list 198 * @return stdout of executed command or NULL if an error occurred 199 */ 200char * 201_backtick(char *const argv[]) 202{ 203 int filedes[2]; 204 pid_t pid; 205 int status; 206 char *buf = NULL; 207 208 /* create pipe */ 209 if (pipe(filedes) == -1) { 210 perror(argv[0]); 211 return NULL; 212 } 213 214 switch (pid = fork()) { 215 case -1: /* error */ 216 return NULL; 217 case 0: /* child */ 218 close(filedes[0]); /* close read end of pipe */ 219 dup2(filedes[1], 1); /* redirect stdout to write end of pipe */ 220 close(filedes[1]); /* close write end of pipe */ 221 execvp(argv[0], argv); 222 exit(errno); 223 break; 224 default: /* parent */ 225 close(filedes[1]); /* close write end of pipe */ 226 buf = fd2str(filedes[0]); 227 waitpid(pid, &status, 0); 228 break; 229 } 230 231 return buf; 232} 233 234/* 235 * Kills process whose PID is stored in plaintext in pidfile 236 * @param pidfile PID file 237 * @return 0 on success and errno on failure 238 */ 239int 240kill_pidfile(char *pidfile) 241{ 242 FILE *fp = fopen(pidfile, "r"); 243 char buf[256]; 244 245 if (fp && fgets(buf, sizeof(buf), fp)) { 246 pid_t pid = strtoul(buf, NULL, 0); 247 fclose(fp); 248 return kill(pid, SIGTERM); 249 } else 250 return errno; 251} 252 253/* 254 * fread() with automatic retry on syscall interrupt 255 * @param ptr location to store to 256 * @param size size of each element of data 257 * @param nmemb number of elements 258 * @param stream file stream 259 * @return number of items successfully read 260 */ 261int 262safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 263{ 264 size_t ret = 0; 265 266 do { 267 clearerr(stream); 268 ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream); 269 } while (ret < nmemb && ferror(stream) && errno == EINTR); 270 271 return ret; 272} 273 274/* 275 * fwrite() with automatic retry on syscall interrupt 276 * @param ptr location to read from 277 * @param size size of each element of data 278 * @param nmemb number of elements 279 * @param stream file stream 280 * @return number of items successfully written 281 */ 282int 283safe_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 284{ 285 size_t ret = 0; 286 287 do { 288 clearerr(stream); 289 ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream); 290 } while (ret < nmemb && ferror(stream) && errno == EINTR); 291 292 return ret; 293} 294 295#endif /* linux */ 296/* 297 * Convert Ethernet address string representation to binary data 298 * @param a string in xx:xx:xx:xx:xx:xx notation 299 * @param e binary data 300 * @return TRUE if conversion was successful and FALSE otherwise 301 */ 302int 303ether_atoe(const char *a, unsigned char *e) 304{ 305 char *c = (char *) a; 306 int i = 0; 307 308 memset(e, 0, ETHER_ADDR_LEN); 309 for (;;) { 310 e[i++] = (unsigned char) strtoul(c, &c, 16); 311 if (!*c++ || i == ETHER_ADDR_LEN) 312 break; 313 } 314 return (i == ETHER_ADDR_LEN); 315} 316 317/* 318 * Convert Ethernet address binary data to string representation 319 * @param e binary data 320 * @param a string in xx:xx:xx:xx:xx:xx notation 321 * @return a 322 */ 323char * 324ether_etoa(const unsigned char *e, char *a) 325{ 326 char *c = a; 327 int i; 328 329 for (i = 0; i < ETHER_ADDR_LEN; i++) { 330 if (i) 331 *c++ = ':'; 332 c += sprintf(c, "%02X", e[i] & 0xff); 333 } 334 return a; 335} 336 337/* 338 * Get the ip configuration index if it exists given the 339 * eth name. 340 * 341 * @param wl_ifname pointer to eth interface name 342 * @return index or -1 if not found 343 */ 344int 345get_ipconfig_index(char *eth_ifname) 346{ 347 char varname[64]; 348 char varval[64]; 349 char *ptr; 350 char wl_ifname[64]; 351 int index; 352 353 /* Bail if we get a NULL or empty string */ 354 355 if (!eth_ifname) return -1; 356 if (!*eth_ifname) return -1; 357 358 /* Look up wl name from the eth name */ 359 if( osifname_to_nvifname( eth_ifname, wl_ifname, sizeof(wl_ifname)) != 0 ) 360 return -1; 361 362 snprintf(varname,sizeof(varname),"%s_ipconfig_index",wl_ifname); 363 364 ptr = nvram_get(varname); 365 366 if (ptr){ 367 /* Check ipconfig_index pointer to see if it is still pointing 368 the correct lan config block */ 369 if (*ptr) { 370 int index; 371 char *ifname; 372 char buf[64]; 373 index = atoi(ptr); 374 375 snprintf(buf,sizeof(buf),"lan%d_ifname",index); 376 377 ifname = nvram_get(buf); 378 379 if (ifname) { 380 if (!(strcmp(ifname,wl_ifname))) 381 return index; 382 } 383 nvram_unset(varname); 384 } 385 } 386 387 /* The index pointer may not have been configured if the 388 * user enters the variables manually. Do a brute force search 389 * of the lanXX_ifname variables 390 */ 391 for (index=0 ; index < MAX_NVPARSE; index++){ 392 snprintf(varname,sizeof(varname),"lan%d_ifname",index); 393 if ( nvram_match(varname,wl_ifname)){ 394 /* if a match is found set up a corresponding index pointer for wlXX */ 395 snprintf(varname,sizeof(varname),"%s_ipconfig_index",wl_ifname); 396 snprintf(varval,sizeof(varval),"%d",index); 397 nvram_set(varname,varval); 398 nvram_commit(); 399 return index; 400 }; 401 } 402 return -1; 403} 404 405/* 406 * Set the ip configuration index given the eth name 407 * Updates both wlXX_ipconfig_index and lanYY_ifname. 408 * 409 * @param eth_ifname pointer to eth interface name 410 * @return 0 if successful -1 if not. 411 */ 412int 413set_ipconfig_index(char *eth_ifname,int index) 414{ 415 char varname[255]; 416 char varval[16]; 417 char wl_ifname[64]; 418 419 /* Bail if we get a NULL or empty string */ 420 421 if (!eth_ifname) return -1; 422 if (!*eth_ifname) return -1; 423 424 if (index >= MAX_NVPARSE) return -1; 425 426 /* Look up wl name from the eth name only if the name contains 427 eth 428 */ 429 430 if( osifname_to_nvifname( eth_ifname, wl_ifname, sizeof(wl_ifname)) != 0 ) 431 return -1; 432 433 snprintf(varname,sizeof(varname),"%s_ipconfig_index",wl_ifname); 434 snprintf(varval,sizeof(varval),"%d",index); 435 nvram_set(varname,varval); 436 437 snprintf(varname,sizeof(varname),"lan%d_ifname",index); 438 nvram_set(varname,wl_ifname); 439 440 nvram_commit(); 441 442 return 0; 443} 444 445/* 446 * Get interfaces belonging to a specific bridge. 447 * 448 * @param bridge_name pointer to bridge interface name 449 * @return list of interfaces belonging to the bridge or NULL 450 * if not found/empty 451 */ 452char * 453get_bridged_interfaces(char *bridge_name) 454{ 455 static char interfaces[255] ; 456 char *ifnames=NULL; 457 char bridge[64]; 458 459 if (!bridge_name) return NULL; 460 461 memset(interfaces,0,sizeof(interfaces)); 462 snprintf(bridge,sizeof(bridge),"%s_ifnames",bridge_name); 463 464 ifnames=nvram_get(bridge); 465 466 if (ifnames) 467 strncpy(interfaces,ifnames,sizeof(interfaces)); 468 else 469 return NULL; 470 471 return interfaces; 472 473} 474 475/* 476 * Search a string backwards for a set of characters 477 * This is the reverse version of strspn() 478 * 479 * @param s string to search backwards 480 * @param accept set of chars for which to search 481 * @return number of characters in the trailing segment of s 482 * which consist only of characters from accept. 483 */ 484static size_t 485sh_strrspn(const char *s, const char *accept) 486{ 487 const char *p; 488 size_t accept_len = strlen(accept); 489 int i; 490 491 492 if (s[0] == '\0') 493 return 0; 494 495 p = s + (strlen(s) - 1); 496 i = 0; 497 498 do { 499 if (memchr(accept, *p, accept_len) == NULL) 500 break; 501 p--; i++; 502 } while (p != s); 503 504 return i; 505} 506 507/* 508 * Parse the unit and subunit from an interface string such as wlXX or wlXX.YY 509 * 510 * @param ifname interface string to parse 511 * @param unit pointer to return the unit number, may pass NULL 512 * @param subunit pointer to return the subunit number, may pass NULL 513 * @return Returns 0 if the string ends with digits or digits.digits, -1 otherwise. 514 * If ifname ends in digits.digits, then unit and subuint are set 515 * to the first and second values respectively. If ifname ends 516 * in just digits, unit is set to the value, and subunit is set 517 * to -1. On error both unit and subunit are -1. NULL may be passed 518 * for unit and/or subuint to ignore the value. 519 */ 520int 521get_ifname_unit(const char* ifname, int *unit, int *subunit) 522{ 523 const char digits[] = "0123456789"; 524 char str[64]; 525 char *p; 526 size_t ifname_len = strlen(ifname); 527 size_t len; 528 long val; 529 530 if (unit) 531 *unit = -1; 532 if (subunit) 533 *subunit = -1; 534 535 if (ifname_len + 1 > sizeof(str)) 536 return -1; 537 538 strcpy(str, ifname); 539 540 /* find the trailing digit chars */ 541 len = sh_strrspn(str, digits); 542 543 /* fail if there were no trailing digits */ 544 if (len == 0) 545 return -1; 546 547 /* point to the beginning of the last integer and convert */ 548 p = str + (ifname_len - len); 549 val = strtol(p, NULL, 10); 550 551 /* if we are at the beginning of the string, or the previous 552 * character is not a '.', then we have the unit number and 553 * we are done parsing 554 */ 555 if (p == str || p[-1] != '.') { 556 if (unit) 557 *unit = val; 558 return 0; 559 } else { 560 if (subunit) 561 *subunit = val; 562 } 563 564 /* chop off the '.NNN' and get the unit number */ 565 p--; 566 p[0] = '\0'; 567 568 /* find the trailing digit chars */ 569 len = sh_strrspn(str, digits); 570 571 /* fail if there were no trailing digits */ 572 if (len == 0) 573 return -1; 574 575 /* point to the beginning of the last integer and convert */ 576 p = p - len; 577 val = strtol(p, NULL, 10); 578 579 /* save the unit number */ 580 if (unit) 581 *unit = val; 582 583 return 0; 584} 585 586/** 587 remove_from_list 588 Remove the specified word from the list. 589 590 @param name word to be removed from the list 591 @param list Space separated list to modify 592 @param listsize Max size the list can occupy 593 594 @return error code 595*/ 596int remove_from_list( char *name, char *list, int listsize ) 597{ 598 int listlen = 0; 599 int namelen = 0; 600 char *occurrence = list; 601 602 if( !list || !name || (listsize <= 0) ) 603 return EINVAL; 604 605 listlen = strlen( list ); 606 namelen = strlen( name ); 607 608 while( occurrence != NULL && ( occurrence - list < listlen )) 609 { 610 occurrence = strstr( occurrence, name ); 611 612 if( !occurrence ) 613 return EINVAL; 614 615 /* last item in list? */ 616 if( occurrence[namelen] == 0 ) 617 { 618 /* only item in list? */ 619 if( occurrence != list ) 620 occurrence--; 621 occurrence[0] = 0; 622 break; 623 } 624 else if( occurrence[namelen] == ' ' ) 625 { 626 strncpy( occurrence, &occurrence[namelen+1/*space*/], 627 strlen( &occurrence[namelen+1/*space*/]) +1/*terminate*/ ); 628 break; 629 } 630 occurrence++; 631 } 632 633 return 0; 634} 635 636/** 637 add_to_list 638 Add the specified interface(string) to the list as long as 639 it will fit in the space left in the list. 640 641 NOTE: If item is already in list, it won't be added again. 642 643 @param name Name of interface to be added to the list 644 @param list List to modify 645 @param listsize Max size the list can occupy 646 647 @return error code 648*/ 649int add_to_list( char *name, char *list, int listsize ) 650{ 651 int listlen = 0; 652 int namelen = 0; 653 char *temp = NULL; 654 655 if( !list || !name || (listsize <= 0) ) 656 return EINVAL; 657 658 listlen = strlen( list ); 659 namelen = strlen( name ); 660 661 /* is the item already in the list? */ 662 temp = strstr( list, name ); 663 if( temp && ( temp[namelen] == ' ' || temp[namelen] == 0)) 664 return 0; 665 666 667 if( listsize <= listlen + namelen + 1/*space*/ ) 668 return EMSGSIZE; 669 670 /* add a space if the list isn't empty */ 671 if( list[0] != 0 ) 672 { 673 list[listlen++] = 0x20; 674 } 675 676 listlen += strncpy( &list[listlen], name, namelen+1/*terminate*/ ); 677 678 return 0; 679} 680 681 682#define WLMBSS_DEV_NAME "wlmbss" 683#define WL_DEV_NAME "wl" 684#define WDS_DEV_NAME "wds" 685 686#if defined(linux) 687/** 688 nvifname_to_osifname() 689 The intent here is to provide a conversion between the OS interface name 690 and the device name that we keep in NVRAM. 691 This should eventually be placed in a Linux specific file with other 692 OS abstraction functions. 693 694 @param nvifname pointer to ifname to be converted 695 @param osifname_buf storage for the converted osifname 696 @param osifname_buf_len length of storage for osifname_buf 697*/ 698int 699nvifname_to_osifname( const char *nvifname, char *osifname_buf, 700 int osifname_buf_len ) 701{ 702 char varname[NVRAM_MAX_PARAM_LEN]; 703 char *ptr; 704 705 memset( osifname_buf, 0, osifname_buf_len ); 706 707 /* Bail if we get a NULL or empty string */ 708 if((!nvifname) || (!*nvifname) || (!osifname_buf)){ 709 return -1; 710 } 711 712 if (strstr(nvifname,"eth") || strstr(nvifname,".")){ 713 strncpy( osifname_buf, nvifname, osifname_buf_len); 714 return 0; 715 } 716 717 snprintf( varname, sizeof(varname), "%s_ifname", nvifname); 718 ptr = nvram_get(varname); 719 if (ptr){ 720 /* Bail if the string is empty */ 721 if (!*ptr) return -1; 722 strncpy( osifname_buf, ptr, osifname_buf_len); 723 return 0; 724 } 725 726 return -1; 727} 728 729 730/* osifname_to_nvifname() 731 732 Convert the OS interface name to the name we use internally(NVRAM,GUI,etc.) 733 734 This is the Linux version of this function 735 736 @param osifname pointer to osifname to be converted 737 @param nvifname_buf storage for the converted ifname 738 @param nvifname_buf_len length of storage for nvifname_buf 739*/ 740 741int 742osifname_to_nvifname( const char *osifname, char *nvifname_buf, 743 int nvifname_buf_len ) 744{ 745 char varname[NVRAM_MAX_PARAM_LEN]; 746 int pri,sec; 747 748 /* Bail if we get a NULL or empty string */ 749 750 if((!osifname) || (!*osifname) || (!nvifname_buf)) 751 { 752 return -1; 753 } 754 755 memset(nvifname_buf,nvifname_buf_len,0); 756 757 if (strstr(osifname,"wl")){ 758 strncpy(nvifname_buf,osifname,nvifname_buf_len); 759 return 0; 760 } 761 762 /* look for interface name on the primary interfaces first */ 763 for (pri=0;pri < MAX_NVPARSE; pri++){ 764 snprintf(varname,sizeof(varname), 765 "wl%d_ifname",pri); 766 if (nvram_match(varname,(char *)osifname)){ 767 snprintf(nvifname_buf,nvifname_buf_len,"wl%d",pri); 768 return 0; 769 } 770 } 771 772 /* look for interface name on the multi-instance interfaces */ 773 for (pri=0;pri < MAX_NVPARSE; pri++) 774 for (sec=0;sec< MAX_NVPARSE; sec++){ 775 snprintf(varname,sizeof(varname), 776 "wl%d.%d_ifname",pri,sec); 777 if (nvram_match(varname,(char *)osifname)){ 778 snprintf(nvifname_buf,nvifname_buf_len,"wl%d.%d",pri,sec); 779 return 0; 780 } 781 } 782 783 return -1; 784 785} 786 787#endif 788