1/*
2 * Shell-like utility functions
3 *
4 * Copyright 2004, 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.1.1.1 2008/10/15 03:31:22 james26_jang Exp $
13 */
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <stdarg.h>
18#include <errno.h>
19#include <error.h>
20#include <fcntl.h>
21#include <limits.h>
22#include <unistd.h>
23#include <signal.h>
24#include <string.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <sys/wait.h>
28#include <termios.h>
29#include <sys/ioctl.h>
30#include <sys/time.h>
31#include <net/ethernet.h>
32
33#include <shutils.h>
34
35/*
36 * Reads file and returns contents
37 * @param	fd	file descriptor
38 * @return	contents of file or NULL if an error occurred
39 */
40char *
41fd2str(int fd)
42{
43	char *buf = NULL;
44	size_t count = 0, n;
45
46	do {
47		buf = realloc(buf, count + 512);
48		n = read(fd, buf + count, 512);
49		if (n < 0) {
50			free(buf);
51			buf = NULL;
52		}
53		count += n;
54	} while (n == 512);
55
56	close(fd);
57	if (buf)
58		buf[count] = '\0';
59	return buf;
60}
61
62/*
63 * Reads file and returns contents
64 * @param	path	path to file
65 * @return	contents of file or NULL if an error occurred
66 */
67char *
68file2str(const char *path)
69{
70	int fd;
71
72	if ((fd = open(path, O_RDONLY)) == -1) {
73		perror(path);
74		return NULL;
75	}
76
77	return fd2str(fd);
78}
79
80/*
81 * Waits for a file descriptor to change status or unblocked signal
82 * @param	fd	file descriptor
83 * @param	timeout	seconds to wait before timing out or 0 for no timeout
84 * @return	1 if descriptor changed status or 0 if timed out or -1 on error
85 */
86int
87waitfor(int fd, int timeout)
88{
89	fd_set rfds;
90	struct timeval tv = { timeout, 0 };
91
92	FD_ZERO(&rfds);
93	FD_SET(fd, &rfds);
94	return select(fd + 1, &rfds, NULL, NULL, (timeout > 0) ? &tv : NULL);
95}
96
97/*
98 * Concatenates NULL-terminated list of arguments into a single
99 * commmand and executes it
100 * @param	argv	argument list
101 * @param	path	NULL, ">output", or ">>output"
102 * @param	timeout	seconds to wait before timing out or 0 for no timeout
103 * @param	ppid	NULL to wait for child termination or pointer to pid
104 * @return	return value of executed command or errno
105 */
106int
107_eval(char *const argv[], char *path, int timeout, int *ppid)
108{
109	pid_t pid;
110	int status;
111	int fd;
112	int flags;
113	int sig;
114
115	switch (pid = fork()) {
116	case -1:	/* error */
117		perror("fork");
118		return errno;
119	case 0:		/* child */
120		/* Reset signal handlers set for parent process */
121		for (sig = 0; sig < (_NSIG-1); sig++)
122			signal(sig, SIG_DFL);
123
124		/* Clean up */
125		ioctl(0, TIOCNOTTY, 0);
126		close(STDIN_FILENO);
127		setsid();
128
129		/* Redirect stdout to <path> */
130		if (path) {
131			flags = O_WRONLY | O_CREAT;
132			if (!strncmp(path, ">>", 2)) {
133				/* append to <path> */
134				flags |= O_APPEND;
135				path += 2;
136			} else if (!strncmp(path, ">", 1)) {
137				/* overwrite <path> */
138				flags |= O_TRUNC;
139				path += 1;
140			}
141			if ((fd = open(path, flags, 0644)) < 0)
142				perror(path);
143			else {
144				dup2(fd, STDOUT_FILENO);
145				dup2(fd, STDERR_FILENO);
146				close(fd);
147			}
148		}
149
150		/* execute command */
151		dprintf("%s\n", argv[0]);
152		setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
153		alarm(timeout);
154		execvp(argv[0], argv);
155		perror(argv[0]);
156		exit(errno);
157	default:	/* parent */
158		if (ppid) {
159			*ppid = pid;
160			return 0;
161		} else {
162			waitpid(pid, &status, 0);
163			if (WIFEXITED(status))
164				return WEXITSTATUS(status);
165			else
166				return status;
167		}
168	}
169}
170
171/*
172 * Concatenates NULL-terminated list of arguments into a single
173 * commmand and executes it
174 * @param	argv	argument list
175 * @return	stdout of executed command or NULL if an error occurred
176 */
177char *
178_backtick(char *const argv[])
179{
180	int filedes[2];
181	pid_t pid;
182	int status;
183	char *buf = NULL;
184
185	/* create pipe */
186	if (pipe(filedes) == -1) {
187		perror(argv[0]);
188		return NULL;
189	}
190
191	switch (pid = fork()) {
192	case -1:	/* error */
193		return NULL;
194	case 0:		/* child */
195		close(filedes[0]);	/* close read end of pipe */
196		dup2(filedes[1], 1);	/* redirect stdout to write end of pipe */
197		close(filedes[1]);	/* close write end of pipe */
198		execvp(argv[0], argv);
199		exit(errno);
200		break;
201	default:	/* parent */
202		close(filedes[1]);	/* close write end of pipe */
203		buf = fd2str(filedes[0]);
204		waitpid(pid, &status, 0);
205		break;
206	}
207
208	return buf;
209}
210
211/*
212 * Kills process whose PID is stored in plaintext in pidfile
213 * @param	pidfile	PID file
214 * @return	0 on success and errno on failure
215 */
216int
217kill_pidfile(char *pidfile)
218{
219	FILE *fp = fopen(pidfile, "r");
220	char buf[256];
221
222	if (fp && fgets(buf, sizeof(buf), fp)) {
223		pid_t pid = strtoul(buf, NULL, 0);
224		fclose(fp);
225		return kill(pid, SIGTERM);
226  	} else
227		return errno;
228}
229
230/*
231 * fread() with automatic retry on syscall interrupt
232 * @param	ptr	location to store to
233 * @param	size	size of each element of data
234 * @param	nmemb	number of elements
235 * @param	stream	file stream
236 * @return	number of items successfully read
237 */
238int
239safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
240{
241	size_t ret = 0;
242
243	do {
244		clearerr(stream);
245		ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream);
246	} while (ret < nmemb && ferror(stream) && errno == EINTR);
247
248	return ret;
249}
250
251/*
252 * fwrite() with automatic retry on syscall interrupt
253 * @param	ptr	location to read from
254 * @param	size	size of each element of data
255 * @param	nmemb	number of elements
256 * @param	stream	file stream
257 * @return	number of items successfully written
258 */
259int
260safe_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
261{
262	size_t ret = 0;
263
264	do {
265		clearerr(stream);
266		ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream);
267	} while (ret < nmemb && ferror(stream) && errno == EINTR);
268
269	return ret;
270}
271
272/*
273 * Convert Ethernet address string representation to binary data
274 * @param	a	string in xx:xx:xx:xx:xx:xx notation
275 * @param	e	binary data
276 * @return	TRUE if conversion was successful and FALSE otherwise
277 */
278int
279ether_atoe(const char *a, unsigned char *e)
280{
281	char *c = (char *) a;
282	int i = 0;
283
284	memset(e, 0, ETHER_ADDR_LEN);
285	for (;;) {
286		e[i++] = (unsigned char) strtoul(c, &c, 16);
287		if (!*c++ || i == ETHER_ADDR_LEN)
288			break;
289	}
290	return (i == ETHER_ADDR_LEN);
291}
292
293/*
294 * Convert Ethernet address binary data to string representation
295 * @param	e	binary data
296 * @param	a	string in xx:xx:xx:xx:xx:xx notation
297 * @return	a
298 */
299char *
300ether_etoa(const unsigned char *e, char *a)
301{
302	char *c = a;
303	int i;
304
305	for (i = 0; i < ETHER_ADDR_LEN; i++) {
306		if (i)
307			*c++ = ':';
308		c += sprintf(c, "%02X", e[i] & 0xff);
309	}
310	return a;
311}
312