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