1/*
2 * NVRAM variable manipulation (Linux user mode half)
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: nvram_linux.c,v 1.18 2005/05/16 12:35:03 honor Exp $
13 */
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <unistd.h>
18#include <errno.h>
19#include <error.h>
20#include <string.h>
21#include <sys/ioctl.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <fcntl.h>
25#include <sys/mman.h>
26
27#include <shared.h>
28#include <typedefs.h>
29#include <bcmnvram.h>
30#include <nvram_convert.h>
31#include <shutils.h>
32#include <utils.h>
33
34#define PATH_DEV_NVRAM "/dev/nvram"
35
36/* Globals */
37static int nvram_fd = -1;
38static char *nvram_buf = NULL;
39
40int
41nvram_init(void *unused)
42{
43	if (nvram_fd >= 0)
44		return 0;
45
46	if ((nvram_fd = open(PATH_DEV_NVRAM, O_RDWR)) < 0)
47		goto err;
48
49	/* Map kernel string buffer into user space */
50	nvram_buf = mmap(NULL, MAX_NVRAM_SPACE, PROT_READ, MAP_SHARED, nvram_fd, 0);
51	if (nvram_buf == MAP_FAILED) {
52		close(nvram_fd);
53		nvram_fd = -1;
54		goto err;
55	}
56
57	fcntl(nvram_fd, F_SETFD, FD_CLOEXEC);
58
59	return 0;
60
61err:
62	perror(PATH_DEV_NVRAM);
63	return errno;
64}
65
66char *nvram_get(const char *name)
67{
68	char tmp[100];
69	char *value;
70	size_t count = strlen(name) + 1;
71	unsigned long *off = (unsigned long *)tmp;
72
73	if (nvram_fd < 0) {
74		if (nvram_init(NULL) != 0) return NULL;
75	}
76
77	if (count > sizeof(tmp)) {
78		if ((off = malloc(count)) == NULL) return NULL;
79	}
80
81	/* Get offset into mmap() space */
82	strcpy((char *) off, name);
83	count = read(nvram_fd, off, count);
84
85	if (count == sizeof(*off)) {
86		value = &nvram_buf[*off];
87	}
88	else {
89		value = NULL;
90		if (count < 0) perror(PATH_DEV_NVRAM);
91	}
92
93	if (off != (unsigned long *)tmp) free(off);
94	return value;
95}
96
97int nvram_getall(char *buf, int count)
98{
99	int r;
100
101	if (count <= 0) return 0;
102
103	*buf = 0;
104	if (nvram_fd < 0) {
105		if ((r = nvram_init(NULL)) != 0) return r;
106	}
107	r = read(nvram_fd, buf, count);
108	if (r < 0) perror(PATH_DEV_NVRAM);
109	return (r == count) ? 0 : r;
110}
111
112static char *nvram_xfr_buf = NULL;
113
114char *
115nvram_xfr(const char *buf)
116{
117	size_t count = strlen(buf)*2+1; // ham 1120
118	int ret;
119	char tmpbuf[1024];
120
121	if(nvram_fd < 0)
122		if ((ret = nvram_init(NULL)))
123			return NULL;
124
125	if(count > sizeof(tmpbuf))
126		return NULL;
127
128	strcpy(tmpbuf, buf);
129
130	if(!nvram_xfr_buf)
131		nvram_xfr_buf = (char *)malloc(1024+1);
132
133	if(!nvram_xfr_buf) return NULL;
134
135	ret = ioctl(nvram_fd, NVRAM_MAGIC, tmpbuf);
136
137	if(ret<0) {
138		return NULL;
139	}
140	else {
141		strcpy(nvram_xfr_buf, tmpbuf);
142		return nvram_xfr_buf;
143	}
144}
145
146
147static int _nvram_set(const char *name, const char *value)
148{
149	size_t count = strlen(name) + 1;
150	char tmp[100];
151	char *buf = tmp;
152	int ret;
153
154	if (nvram_fd < 0) {
155		if ((ret = nvram_init(NULL)) != 0) return ret;
156	}
157
158	/* Unset if value is NULL */
159	if (value) count += strlen(value) + 1;
160
161	if (count > sizeof(tmp)) {
162		if ((buf = malloc(count)) == NULL) return -ENOMEM;
163	}
164
165	if (value) {
166		sprintf(buf, "%s=%s", name, value);
167	}
168	else {
169		strcpy(buf, name);
170	}
171
172	ret = write(nvram_fd, buf, count);
173
174	if (ret < 0) perror(PATH_DEV_NVRAM);
175
176	if (buf != tmp) free(buf);
177
178	return (ret == count) ? 0 : ret;
179}
180
181int nvram_set(const char *name, const char *value)
182{
183	return _nvram_set(name, value);
184}
185
186int nvram_unset(const char *name)
187{
188	return _nvram_set(name, NULL);
189}
190
191int nvram_commit(void)
192{
193	int r = 0;
194	FILE *fp;
195
196	if (nvram_get(ASUS_STOP_COMMIT) != NULL)
197	{
198		cprintf("# skip nvram commit #\n");
199		return r;
200	}
201
202	fp = fopen("/var/log/commit_ret", "w");
203
204	if (wait_action_idle(10)) {
205		if (nvram_fd < 0) {
206			if ((r = nvram_init(NULL)) != 0) goto finish;
207		}
208		set_action(ACT_NVRAM_COMMIT);
209//		nvram_unset("dirty");
210		r = ioctl(nvram_fd, NVRAM_MAGIC, NULL);
211		set_action(ACT_IDLE);
212
213		if (r < 0) {
214			perror(PATH_DEV_NVRAM);
215			cprintf("commit: error\n");
216			if(fp!=NULL)
217				fprintf(fp,"commit: error\n");
218		}
219		else {
220			if(fp!=NULL)
221                                fprintf(fp,"commit: OK\n");
222		}
223	}
224	else {
225		cprintf("commit: system busy\n");
226		if(fp!=NULL)
227                        fprintf(fp,"commit: system busy\n");
228	}
229
230finish:
231	if(fp!=NULL) fclose(fp);
232
233	return r;
234}
235
236/*
237 * Write a file to an NVRAM variable.
238 * @param	name	name of variable to get
239 * @param	filenname	name of file to write
240 * @return	return code
241 *
242 * Preserve mode (permissions) of the file.
243 * Create the output directory.
244 */
245#define MAX_FS 4096
246#define MAGICNUM 0x12161770	/* Ludwig van Beethoven's birthdate. */
247int nvram_file2nvram(const char *varname, const char *filename)
248{
249	FILE *fp;
250	int c,count;
251	int i=0,j=0;
252	struct stat stbuf;
253	unsigned char mem[MAX_FS], buf[3 * MAX_FS];
254
255	if ( !(fp=fopen(filename,"rb") )) {
256		perror("");
257		return 1;
258	}
259
260	stat(filename, &stbuf);
261	*((mode_t *)mem) = stbuf.st_mode;
262	*((mode_t *)mem+1) = MAGICNUM;
263
264	count=fread(mem + 2*sizeof(mode_t), 1, sizeof(mem) - 2*sizeof(mode_t), fp);
265	if (!feof(fp)) {
266		fclose(fp);
267		fprintf(stderr, "File too big.\n");
268		return(1);
269	}
270	fclose(fp);
271	count += 2*sizeof(mode_t);
272	for (j = 0; j < count; j++) {
273		if  (i > sizeof(buf)-3 )
274			break;
275		c=mem[j];
276		if (c >= 32 && c <= 126 && c != '\\' && c != '~')  {
277			buf[i++]=(unsigned char) c;
278		} else if (c==0) {
279			buf[i++]='~';
280		} else {
281			buf[i++]='\\';
282			sprintf(buf+i,"%02X",c);
283			i+=2;
284		}
285	}
286	buf[i]=0;
287	nvram_set(varname,buf);
288	return 0;
289}
290
291/*
292 * Get the value of an NVRAM variable and write it to a file.
293 * It must have been written with nvram_file2nvram.
294 * Directory path(s) are created, and permissions are preserved.
295 * @param	name	name of variable to get
296 * @param	filenname	name of file to write
297 * @return	return code
298 */
299int nvram_nvram2file(const char *varname, const char *filename)
300{
301	int fnum;
302	int c,tmp;
303	int i=0,j=0;
304	unsigned char *cp;
305	unsigned char mem[MAX_FS], buf[3 * MAX_FS];
306
307	cp = nvram_get(varname);
308	if (cp == NULL) {
309		printf("Key does not exist: %s\n", varname);
310		return(1);
311	}
312	strcpy(buf, cp);
313	while (buf[i] && j < sizeof(mem)-3 ) {
314		if (buf[i] == '\\')  {
315			i++;
316			tmp=buf[i+2];
317			buf[i+2]=0;
318			sscanf(buf+i,"%02X",&c);
319			buf[i+2]=tmp;
320			i+=2;
321			mem[j]=c;j++;
322		} else if (buf[i] == '~') {
323			mem[j++]=0;
324			i++;
325		} else {
326			mem[j++]=buf[i++];
327		}
328	}
329
330	if (j<=0)
331		return j;
332	if (*((mode_t *)mem+1) != MAGICNUM) {
333		printf("Error: '%s' not created by nvram setfile.\n", varname);
334		return(-1);
335	}
336
337	/* Create the directories to the path, as necessary. */
338	strcpy(buf, filename);
339	cp = strrchr(buf, '/');
340	if (cp && cp > buf) {
341		*cp = 0;
342		eval("mkdir", "-m", "0777", "-p", buf);
343	}
344
345	if ( (fnum=open(filename, O_WRONLY | O_CREAT | O_TRUNC, *((mode_t *)mem))) < 0) {
346		printf("failed. errno: %d\n", errno);
347		perror(filename);
348		return (-1);
349	}
350	i = write(fnum, mem + 2*sizeof(mode_t), j- 2* sizeof(mode_t));
351	if (i != j- 2* sizeof(mode_t))
352		perror(filename);
353	close(fnum);
354	return (i != (j- 2* sizeof(mode_t)));
355}
356