├── alias.h ├── Makefile ├── LICENSE.txt ├── test.c ├── nvram.h ├── config.h ├── README.md ├── alias.c └── nvram.c /alias.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_ALIAS_H 2 | #define INCLUDE_ALIAS_H 3 | 4 | // Iterates through each NVRAM key-value pair, and calls *fp with optional third data parameter. 5 | int foreach_nvram_from(const char *file, void (*fp)(const char *, const char *, void *), void *data); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-O2 -fPIC -Wall 2 | LDFLAGS=-shared -nostdlib 3 | 4 | OBJECTS=$(SOURCES:.c=.o) 5 | SOURCES=nvram.c 6 | TARGET=libnvram.so 7 | 8 | all: $(SOURCES) $(TARGET) 9 | 10 | $(TARGET): $(OBJECTS) 11 | $(CC) $(LDFLAGS) $(OBJECTS) -o $@ 12 | 13 | .c.o: 14 | $(CC) -c $(CFLAGS) $< -o $@ 15 | 16 | clean: 17 | rm -f *.o libnvram.so test 18 | 19 | .PHONY: clean 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 - 2016, Daming Dominic Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "nvram.h" 4 | 5 | // Prototypes from "alias.c" 6 | char *nvram_nget(const char *fmt, ...); 7 | int nvram_nset(const char *val, const char *fmt, ...); 8 | int foreach_nvram_from(const char *file, void (*fp)(const char *, const char *, void *), void *data); 9 | 10 | 11 | void test(const char *key, const char*val, void* data) { 12 | printf("%s = %s, %s\n", key, val, (char *) data); 13 | } 14 | 15 | int main(int argc, char** argv) { 16 | char buf[256], *ptr; 17 | int tmp; 18 | 19 | nvram_init(); 20 | 21 | nvram_clear(); 22 | 23 | nvram_set_default(); 24 | 25 | nvram_getall(buf, 256); 26 | 27 | nvram_set("str", "test"); 28 | 29 | nvram_set_int("int", 2048); 30 | 31 | ptr = nvram_get("str"); 32 | 33 | tmp = nvram_get_int("int"); 34 | 35 | nvram_commit(); 36 | 37 | nvram_getall(buf, 256); 38 | 39 | nvram_reset(); 40 | 41 | ptr = nvram_get("is_default"); 42 | 43 | nvram_close(); 44 | 45 | nvram_unset("is_default"); 46 | 47 | ptr = nvram_get("is_default"); 48 | 49 | nvram_nset("test", "%s_file", "a"); 50 | 51 | ptr = nvram_nget("%s_file", "a"); 52 | 53 | foreach_nvram_from("/tmp/test.ini", test, "5"); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /nvram.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_NVRAM_H 2 | #define INCLUDE_NVRAM_H 3 | 4 | // Gets the ID of the semaphore. If uninitialized, will initialize both the semaphore and NVRAM. 5 | static int sem_get(); 6 | // Locks the binary semaphore. Will block. 7 | static void sem_lock(); 8 | // Unlocks the binary semaphore. 9 | static void sem_unlock(); 10 | 11 | // Sets default NVRAM values using the built-in NVRAM_DEFAULTS table. 12 | static int nvram_set_default_builtin(void); 13 | // Sets default NVRAM values using the override values from OVERRIDE_POINT. Will hold lock. 14 | static int nvram_set_default_image(void); 15 | // Sets default NVRAM values from external table defined in NVRAM_DEFAULTS_PATH. 16 | static int nvram_set_default_table(const char *tbl[]); 17 | 18 | /* The following functions form the standard NVRAM API. Functions that return integers 19 | * will generally return E_SUCCESS/E_FAILURE, with the exception of nvram_get_int(). */ 20 | 21 | // Initializes NVRAM with default values. Will hold lock. 22 | int nvram_init(void); 23 | // Restores original NVRAM default values. 24 | int nvram_reset(void); 25 | // Clears NVRAM values. Will hold lock. 26 | int nvram_clear(void); 27 | // Pretends to close NVRAM, does nothing. 28 | int nvram_close(void); 29 | // Pretends to commit NVRAM, actually synchronizes file system. 30 | int nvram_commit(void); 31 | 32 | // Given a key, gets the corresponding NVRAM value. If key is non-existent, returns NULL. 33 | // Will dynamically allocate memory, so the user should call free(). 34 | // On MIPS, will use $a1 as key if $a0 is NULL. 35 | char *nvram_get(const char *key); 36 | // Given a key, gets the corresponding NVRAM value. If key is non-existent, returns "". 37 | // Will dynamically allocate memory. 38 | char *nvram_safe_get(const char *key); 39 | // Given a key, gets the corresponding NVRAM value. If key is non-existent, returns val. 40 | // Otherwise, returns NULL. Will dynamically allocate memory. 41 | char *nvram_default_get(const char *key, const char *val); 42 | // Given a key, gets the corresponding NVRAM value into a user-supplied buffer. 43 | // Will hold lock. 44 | int nvram_get_buf(const char *key, char *buf, size_t sz); 45 | // Given a key, gets the corresponding NVRAM value as integer. If key is non-existent, returns E_FAILURE. 46 | // Will hold lock. 47 | int nvram_get_int(const char *key); 48 | // Gets all NVRAM keys and values into a user-supplied buffer, of the format "key=value...". 49 | // Will hold lock. 50 | int nvram_getall(char *buf, size_t len); 51 | 52 | // Given a key and value, sets the corresponding NVRAM value. Will hold lock. 53 | int nvram_set(const char *key, const char *val); 54 | // Given a key and value as integer, sets the corresponding NVRAM value. Will hold lock. 55 | int nvram_set_int(const char *key, const int val); 56 | // Given a key, unsets the corresponding NVRAM value. Will hold lock. 57 | int nvram_unset(const char *key); 58 | // Reloads default NVRAM values. 59 | int nvram_set_default(void); 60 | 61 | // Adds a list entry to a NVRAM value. 62 | int nvram_list_add(const char *key, const char *val); 63 | // Checks whether a list entry exists in a NVRAM value. If the magic argument 64 | // is equal to LIST_MAGIC, will either return a pointer to the match or NULL. 65 | char *nvram_list_exist(const char *key, const char *val, int magic); 66 | // Deletes a list entry from a NVRAM value. 67 | int nvram_list_del(const char *key, const char *val); 68 | 69 | // Given a key, checks whether the corresponding NVRAM value matches val. 70 | int nvram_match(const char *key, const char *val); 71 | // Given a key, checks whether the corresponding NVRAM value does not match val. 72 | int nvram_invmatch(const char *key, const char *val); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_CONFIG_H 2 | #define INCLUDE_CONFIG_H 3 | 4 | // Determines whether debugging information should be printed to stderr. 5 | #define DEBUG 1 6 | // Determines the size of the internal buffer, used for manipulating and storing key values, etc. 7 | #define BUFFER_SIZE 256 8 | // Determines the size of the "emulated" NVRAM, used by nvram_get_nvramspace(). 9 | #define NVRAM_SIZE 2048 10 | // Determines the maximum size of the user-supplied output buffer when a length is not supplied. 11 | #define USER_BUFFER_SIZE 64 12 | // Determines the unique separator character (as string) used for the list implementation. Do not use "\0". 13 | #define LIST_SEP "\xff" 14 | // Special argument used to change the semantics of the nvram_list_exist() function. 15 | #define LIST_MAGIC 0xdeadbeef 16 | // Identifier value used to generate IPC key in ftok() 17 | #define IPC_KEY 'A' 18 | // Timeout for the semaphore 19 | #define IPC_TIMEOUT 1000 20 | // Mount point of the base NVRAM implementation. 21 | #define MOUNT_POINT "/firmadyne/libnvram/" 22 | // Location of NVRAM override values that are copied into the base NVRAM implementation. 23 | #define OVERRIDE_POINT "/firmadyne/libnvram.override/" 24 | 25 | // Define the semantics for success and failure error codes. 26 | #define E_FAILURE 0 27 | #define E_SUCCESS 1 28 | 29 | // Default paths for NVRAM default values. 30 | #define NVRAM_DEFAULTS_PATH \ 31 | /* "DIR-505L_FIRMWARE_1.01.ZIP" (10497) */ \ 32 | PATH("/var/etc/nvram.default") \ 33 | /* "DIR-615_REVE_FIRMWARE_5.11.ZIP" (9753) */ \ 34 | PATH("/etc/nvram.default") \ 35 | /* "DGL-5500_REVA_FIRMWARE_1.12B05.ZIP" (9469) */ \ 36 | TABLE(router_defaults) \ 37 | PATH("/etc/nvram.conf") \ 38 | PATH("/etc/nvram.deft") \ 39 | PATH("/etc/nvram.update") \ 40 | TABLE(Nvrams) \ 41 | PATH("/etc/wlan/nvram_params") \ 42 | PATH("/etc/system_nvram_defaults") 43 | 44 | // Default values for NVRAM. 45 | #define NVRAM_DEFAULTS \ 46 | /* Linux kernel log level, used by "WRT54G3G_2.11.05_ETSI_code.bin" (305) */ \ 47 | ENTRY("console_loglevel", nvram_set, "7") \ 48 | /* Reset NVRAM to default at bootup, used by "WNR3500v2-V1.0.2.10_23.0.70NA.chk" (1018) */ \ 49 | ENTRY("restore_defaults", nvram_set, "1") \ 50 | ENTRY("sku_name", nvram_set, "") \ 51 | ENTRY("wla_wlanstate", nvram_set, "") \ 52 | ENTRY("lan_if", nvram_set, "br0") \ 53 | ENTRY("lan_ipaddr", nvram_set, "192.168.0.50") \ 54 | ENTRY("lan_bipaddr", nvram_set, "192.168.0.255") \ 55 | ENTRY("lan_netmask", nvram_set, "255.255.255.0") \ 56 | /* Set default timezone, required by multiple images */ \ 57 | ENTRY("time_zone", nvram_set, "EST5EDT") \ 58 | /* Set default WAN MAC address, used by "NBG-416N_V1.00(USA.7)C0.zip" (12786) */ \ 59 | ENTRY("wan_hwaddr_def", nvram_set, "01:23:45:67:89:ab") \ 60 | /* Attempt to define LAN/WAN interfaces */ \ 61 | ENTRY("wan_ifname", nvram_set, "eth0") \ 62 | ENTRY("lan_ifnames", nvram_set, "eth1 eth2 eth3 eth4") \ 63 | /* Used by "TEW-638v2%201.1.5.zip" (12898) to prevent crash in 'goahead' */ \ 64 | ENTRY("ethConver", nvram_set, "1") \ 65 | /* Used by "Firmware_TEW-411BRPplus_2.07_EU.zip" (13649) to prevent crash in 'init' */ \ 66 | ENTRY("lan_proto", nvram_set, "dhcp") \ 67 | ENTRY("wan_ipaddr", nvram_set, "0.0.0.0") \ 68 | ENTRY("wan_netmask", nvram_set, "255.255.255.0") \ 69 | ENTRY("wanif", nvram_set, "eth0") \ 70 | /* Used by "DGND3700 Firmware Version 1.0.0.17(NA).zip" (3425) to prevent crashes */ \ 71 | ENTRY("time_zone_x", nvram_set, "0") \ 72 | ENTRY("rip_multicast", nvram_set, "0") \ 73 | ENTRY("bs_trustedip_enable", nvram_set, "0") 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | This is a library that emulates the behavior of the NVRAM peripheral, by 5 | storing key-value pairs into a `tmpfs` mounted at `MOUNT_POINT` (default: 6 | `/firmadyne/libnvram`). 7 | 8 | Upon first access of a NVRAM-related function, the emulated peripheral is 9 | initialized as follows: 10 | 11 | 1. The NVRAM filesystem is mounted 12 | 2. Built-in default values are loaded from the `NVRAM_DEFAULTS` macro 13 | 3. A built-in list of system-specific default value sources, defined by 14 | the `NVRAM_DEFAULTS_PATH` macro, is iteratively checked and loaded. This 15 | includes native functions (`NATIVE()` macro), text files on the filesystem 16 | (`PATH()` macro), and loaded symbols of type `char *[]` (`TABLE()` macro). 17 | 4. System-specific override values are loaded from keys located at 18 | `OVERRIDE_POINT` (default: `/firmadyne/libnvram.override`) 19 | 20 | Due to differences in C runtime libraries on various firmware, this library 21 | is compiled as a dynamically-linked shared library. Since ELF dynamic loaders 22 | on Linux systems support lazy linking and global symbol resolution scope, 23 | resolution of external symbols used by this library are effectively delayed 24 | until after the calling process has already loaded the system C runtime library. 25 | As a result, this library behaves like a statically-linked shared library, while 26 | dynamically using functions loaded by the calling process, including 27 | those from the standard C runtime library. 28 | 29 | Although compilation as a statically-linked shared library might appear to 30 | preferable, in practice we've had better results using our approach discussed 31 | above. Different C runtime libraries may have incompatible implementations of 32 | features such as thread-local storage. Additionally, many C runtime libraries 33 | are not built with position-independent code (PIC) to support static 34 | compilation. 35 | 36 | Usage 37 | ===== 38 | 39 | Build the library, and copy it into the firmware image: 40 | 41 | `cp libnvram.so /firmadyne/libnvram.so` 42 | 43 | Create the NVRAM storage directories on the filesystem: 44 | 45 | `mkdir -p /firmadyne/libnvram/` 46 | `mkdir -p /firmadyne/libnvram.override/` 47 | 48 | This will be automatically loaded by the instrumented kernel through 49 | `LD_PRELOAD` by modifications to `init/main.c`. 50 | 51 | Notes 52 | ===== 53 | 54 | This library is alpha quality. In particular, memory safety of string 55 | operations has not been checked, there may be concurrency bugs, and it uses 56 | some nasty hacks. Additionally, various firmware have different calling 57 | semantics for NVRAM-related functions, which may not be supported by this 58 | library, or may be mutually incompatible with other devices. 59 | 60 | Furthermore, this library is missing graceful fallbacks for library 61 | functions not available in certain C runtime libraries. This is because C 62 | runtime libraries only maintain source-level compatibility with one another, 63 | not binary-level compatibility. As a result, fallbacks need to be manually 64 | implemented by declaring library functions to be weak symbols and checking for 65 | initialization before calling them. Currently, uses of `ftok()` are implemented 66 | in this manner, meaning that semaphores are not used if this function is 67 | unavailable. This is not desirable, but preferred over a NULL pointer 68 | dereference. 69 | 70 | As another example, `stat()` is not necessarily available at the binary level 71 | in all C runtime libraries. [GNU glibc](https://www.gnu.org/software/libc/) 72 | only exports `stat()` as a private weak symbol, linking instead to `__xstat()`, 73 | which is part of the 74 | [Linux Standard Base (LSB)](http://refspecs.linuxfoundation.org/lsb.shtml) 75 | specification. However, [uClibc](https://uclibc.org/) instead links to 76 | `__xstat_conv()`, and does not provide a `__xstat()` function. Furthermore, 77 | most libraries will prefer `stat64()` over `stat()`, depending on whether 78 | Large File Support (LFS) is enabled, which results in additional indirection. 79 | Additionally, making a direct syscall to `sys_stat()` can be unpredictable due 80 | to changes in the system call interface. As an alternative, use `access()` or 81 | `fopen()`. 82 | 83 | Unfortunately, any of these failures typically cause the `init` process to crash 84 | during system startup, potentially with a NULL pointer dereference. This is 85 | because some firmware assume that NVRAM-related functions will never 86 | return NULL, making it difficult to debug. Resolving this typically requires 87 | manual reverse engineering to identify the crashing function, and modifying 88 | the NVRAM library by defining a default value, adding a source of default 89 | values, or writing a shim function to call into this library to perform a 90 | NVRAM-related operation. 91 | 92 | Pull requests are greatly appreciated! 93 | -------------------------------------------------------------------------------- /alias.c: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_ALIAS_C 2 | #define INCLUDE_ALIAS_C 3 | 4 | /* Aliased base functions */ 5 | 6 | int true() { 7 | return E_SUCCESS; 8 | } 9 | 10 | int false() { 11 | return E_FAILURE; 12 | } 13 | 14 | int nvram_load(void) __attribute__ ((alias ("nvram_init"))); 15 | int nvram_loaddefault(void) __attribute__ ((alias ("nvram_set_default"))); 16 | char *_nvram_get(const char *key) __attribute__ ((alias ("nvram_get"))); 17 | int nvram_get_state(const char *key) __attribute__ ((alias ("nvram_get_int"))); 18 | int nvram_set_state(const char *key, const int val) __attribute__ ((alias ("nvram_set_int"))); 19 | int nvram_restore_default(void) __attribute__ ((alias ("nvram_reset"))); 20 | int nvram_upgrade(void* ptr) __attribute__ ((alias ("nvram_commit"))); 21 | 22 | /* Atheros/Broadcom NVRAM */ 23 | 24 | int nvram_get_nvramspace(void) { 25 | return NVRAM_SIZE; 26 | } 27 | 28 | int foreach_nvram_from(const char *file, void (*fp)(const char *, const char *, void *), void *data) { 29 | char *key, *val, *tmp; 30 | FILE *f; 31 | 32 | if (!fp) { 33 | PRINT_MSG("%s\n", "NULL function pointer!"); 34 | return E_FAILURE; 35 | } 36 | 37 | if ((f = fopen(file, "r")) == NULL) { 38 | PRINT_MSG("Unable to open file: %s!\n", file); 39 | return E_FAILURE; 40 | } 41 | 42 | while (fgets(temp, BUFFER_SIZE, f) == temp) { 43 | if (!(val = strchr(temp, '='))) { 44 | continue; 45 | } 46 | 47 | key = temp; 48 | *val = '\0'; 49 | val += 1; 50 | 51 | if ((tmp = strchr(val, '\n')) != NULL) { 52 | *tmp = '\0'; 53 | } 54 | 55 | if (data) { 56 | fp(key, val, data); 57 | } 58 | else { 59 | ((void (*)(const char *, const char *)) fp)(key, val); 60 | } 61 | } 62 | 63 | fclose(f); 64 | return E_SUCCESS; 65 | } 66 | 67 | char *nvram_nget(const char *fmt, ...) { 68 | va_list va; 69 | 70 | va_start(va, fmt); 71 | vsnprintf(temp, BUFFER_SIZE, fmt, va); 72 | va_end(va); 73 | 74 | return nvram_get(temp); 75 | } 76 | 77 | int nvram_nset(const char *val, const char *fmt, ...) { 78 | va_list va; 79 | 80 | va_start(va, fmt); 81 | vsnprintf(temp, BUFFER_SIZE, fmt, va); 82 | va_end(va); 83 | 84 | return nvram_set(temp, val); 85 | } 86 | 87 | int nvram_nset_int(const int val, const char *fmt, ...) { 88 | va_list va; 89 | 90 | va_start(va, fmt); 91 | vsnprintf(temp, BUFFER_SIZE, fmt, va); 92 | va_end(va); 93 | 94 | return nvram_set_int(temp, val); 95 | } 96 | 97 | int nvram_nmatch(const char *val, const char *fmt, ...) { 98 | va_list va; 99 | 100 | va_start(va, fmt); 101 | vsnprintf(temp, BUFFER_SIZE, fmt, va); 102 | va_end(va); 103 | 104 | return nvram_match(temp, val); 105 | } 106 | 107 | int get_default_mac() __attribute__ ((alias ("true"))); 108 | 109 | /* D-Link */ 110 | 111 | char *artblock_get(const char *key) __attribute__ ((alias ("nvram_get"))); 112 | char *artblock_fast_get(const char *key) __attribute__ ((alias ("nvram_safe_get"))); 113 | char *artblock_safe_get(const char *key) __attribute__ ((alias ("nvram_safe_get"))); 114 | int artblock_set(const char *key, const char *val) __attribute__ ((alias ("nvram_set"))); 115 | int nvram_flag_set(int unk) __attribute__ ((alias ("true"))); 116 | int nvram_flag_reset(int unk) __attribute__ ((alias ("true"))); 117 | 118 | /* D-Link ARM */ 119 | int nvram_master_init() __attribute__ ((alias ("false"))); 120 | int nvram_slave_init() __attribute__ ((alias ("false"))); 121 | 122 | /* Realtek */ 123 | // These functions expect integer keys, so we convert to string first. 124 | // Unfortunately, this implementation is not entirely correct because some 125 | // values are integers and others are string, but we treat all as integers. 126 | int apmib_init() __attribute__ ((alias ("true"))); 127 | int apmib_reinit() __attribute__ ((alias ("true"))); 128 | // int apmib_hwconf() __attribute__ ((alias ("true"))); 129 | // int apmib_dsconf() __attribute__ ((alias ("true"))); 130 | // int apmib_load_hwconf() __attribute__ ((alias ("true"))); 131 | // int apmib_load_dsconf() __attribute__ ((alias ("true"))); 132 | // int apmib_load_csconf() __attribute__ ((alias ("true"))); 133 | int apmib_update(const int key) __attribute__((alias ("true"))); 134 | 135 | int apmib_get(const int key, void *buf) { 136 | int res; 137 | 138 | snprintf(temp, BUFFER_SIZE, "%d", key); 139 | if ((res = nvram_get_int(temp))) { 140 | (*(int32_t *) buf) = res; 141 | } 142 | 143 | return res; 144 | } 145 | 146 | int apmib_set(const int key, void *buf) { 147 | snprintf(temp, BUFFER_SIZE, "%d", key); 148 | return nvram_set_int(temp, ((int32_t *) buf)[0]); 149 | } 150 | 151 | /* Netgear ACOS */ 152 | 153 | int WAN_ith_CONFIG_GET(char *buf, const char *fmt, ...) { 154 | va_list va; 155 | 156 | va_start(va, fmt); 157 | vsnprintf(temp, BUFFER_SIZE, fmt, va); 158 | va_end(va); 159 | 160 | return nvram_get_buf(temp, buf, USER_BUFFER_SIZE); 161 | } 162 | 163 | int WAN_ith_CONFIG_SET_AS_STR(const char *val, const char *fmt, ...) __attribute__ ((alias ("nvram_nset"))); 164 | 165 | int WAN_ith_CONFIG_SET_AS_INT(const int val, const char *fmt, ...) __attribute__ ((alias ("nvram_nset_int"))); 166 | 167 | int acos_nvram_init(void) __attribute__ ((alias ("nvram_init"))); 168 | char *acos_nvram_get(const char *key) __attribute__ ((alias ("nvram_get"))); 169 | int acos_nvram_read (const char *key, char *buf, size_t sz) __attribute__ ((alias ("nvram_get_buf"))); 170 | int acos_nvram_set(const char *key, const char *val) __attribute__ ((alias ("nvram_set"))); 171 | int acos_nvram_loaddefault(void) __attribute__ ((alias ("nvram_set_default"))); 172 | int acos_nvram_unset(const char *key) __attribute__ ((alias ("nvram_unset"))); 173 | int acos_nvram_commit(void) __attribute__ ((alias ("nvram_commit"))); 174 | 175 | int acosNvramConfig_init(char *mount) __attribute__ ((alias ("nvram_init"))); 176 | char *acosNvramConfig_get(const char *key) __attribute__ ((alias ("nvram_get"))); 177 | int acosNvramConfig_read (const char *key, char *buf, size_t sz) __attribute__ ((alias ("nvram_get_buf"))); 178 | int acosNvramConfig_set(const char *key, const char *val) __attribute__ ((alias ("nvram_set"))); 179 | int acosNvramConfig_write(const char *key, const char *val) __attribute__ ((alias ("nvram_set"))); 180 | int acosNvramConfig_unset(const char *key) __attribute__ ((alias ("nvram_unset"))); 181 | int acosNvramConfig_match(const char *key, const char *val) __attribute__ ((alias ("nvram_match"))); 182 | int acosNvramConfig_invmatch(const char *key, const char *val) __attribute__ ((alias ("nvram_invmatch"))); 183 | int acosNvramConfig_save(void) __attribute__ ((alias ("nvram_commit"))); 184 | int acosNvramConfig_save_config(void) __attribute__ ((alias ("nvram_commit"))); 185 | int acosNvramConfig_loadFactoryDefault(const char* key); 186 | 187 | /* ZyXel / Edimax */ 188 | // many functions expect the opposite return values: (0) success, failure (1/-1) 189 | 190 | int nvram_getall_adv(int unk, char *buf, size_t len) { 191 | return nvram_getall(buf, len) == E_SUCCESS ? E_FAILURE : E_SUCCESS; 192 | } 193 | 194 | char *nvram_get_adv(int unk, const char *key) { 195 | return nvram_get(key); 196 | } 197 | 198 | int nvram_set_adv(int unk, const char *key, const char *val) { 199 | return nvram_set(key, val); 200 | } 201 | 202 | int nvram_commit_adv(int) __attribute__ ((alias ("nvram_commit"))); 203 | int nvram_unlock_adv(int) __attribute__ ((alias ("true"))); 204 | int nvram_lock_adv(int) __attribute__ ((alias ("true"))); 205 | int nvram_check(void) __attribute__ ((alias ("true"))); 206 | 207 | int nvram_state(int unk1, void *unk2, void *unk3) { 208 | return E_FAILURE; 209 | } 210 | 211 | int envram_commit(void) { 212 | return !nvram_commit(); 213 | } 214 | 215 | int envram_default(void) { 216 | return !nvram_set_default(); 217 | } 218 | 219 | int envram_load(void) { 220 | return !nvram_init(); 221 | } 222 | 223 | int envram_safe_load(void) { 224 | return !nvram_init(); 225 | } 226 | 227 | int envram_match(const char *key, const char *val) { 228 | return !nvram_match(key, val); 229 | } 230 | 231 | int envram_get(const char* key, char *buf) { 232 | // may be incorrect 233 | return !nvram_get_buf(key, buf, USER_BUFFER_SIZE); 234 | } 235 | int envram_get_func(const char* key, char *buf) __attribute__ ((alias ("envram_get"))); 236 | int envram_getf(const char* key, const char *fmt, ...) { 237 | va_list va; 238 | char *val = nvram_get(key); 239 | 240 | if (!val) { 241 | return !E_SUCCESS; 242 | } 243 | 244 | va_start(va, fmt); 245 | vsscanf(val, fmt, va); 246 | va_end(va); 247 | 248 | free(val); 249 | return !E_FAILURE; 250 | } 251 | int nvram_getf(const char* key, const char *fmt, ...) __attribute__ ((alias ("envram_getf"))); 252 | 253 | int envram_set(const char *key, const char *val) { 254 | return !nvram_set(key, val); 255 | } 256 | int envram_set_func(const char *key, const char *val) __attribute__ ((alias ("envram_set"))); 257 | 258 | int envram_setf(const char* key, const char* fmt, ...) { 259 | va_list va; 260 | 261 | va_start(va, fmt); 262 | vsnprintf(temp, BUFFER_SIZE, fmt, va); 263 | va_end(va); 264 | 265 | return !nvram_set(key, temp); 266 | } 267 | int nvram_setf(const char* key, const char* fmt, ...) __attribute__ ((alias ("envram_setf"))); 268 | 269 | int envram_unset(const char *key) { 270 | return !nvram_unset(key); 271 | } 272 | int envram_unset_func(void) __attribute__ ((alias ("envram_unset"))); 273 | 274 | /* Ralink */ 275 | 276 | char *nvram_bufget(int idx, const char *key) { 277 | return nvram_safe_get(key); 278 | } 279 | 280 | int nvram_bufset(int idx, const char *key, const char *val) { 281 | return nvram_set(key, val); 282 | } 283 | 284 | #endif 285 | -------------------------------------------------------------------------------- /nvram.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "alias.h" 18 | #include "nvram.h" 19 | #include "config.h" 20 | 21 | /* Generate variable declarations for external NVRAM data. */ 22 | #define NATIVE(a, b) 23 | #define PATH(a) 24 | #define TABLE(a) \ 25 | extern const char *a[] __attribute__((weak)); 26 | 27 | NVRAM_DEFAULTS_PATH 28 | #undef TABLE 29 | #undef PATH 30 | #undef NATIVE 31 | 32 | // https://lkml.org/lkml/2007/3/9/10 33 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + sizeof(typeof(int[1 - 2 * !!__builtin_types_compatible_p(typeof(arr), typeof(&arr[0]))])) * 0) 34 | 35 | #define PRINT_MSG(fmt, ...) do { if (DEBUG) { fprintf(stderr, "%s: "fmt, __FUNCTION__, __VA_ARGS__); } } while (0) 36 | 37 | /* Weak symbol definitions for library functions that may not be present */ 38 | __typeof__(ftok) __attribute__((weak)) ftok; 39 | 40 | /* Global variables */ 41 | static int init = 0; 42 | static char temp[BUFFER_SIZE]; 43 | 44 | static int sem_get() { 45 | int key, semid = 0; 46 | unsigned int timeout = 0; 47 | struct semid_ds seminfo; 48 | union semun { 49 | int val; 50 | struct semid_ds *buf; 51 | unsigned short *array; 52 | struct seminfo *__buf; 53 | } semun; 54 | struct sembuf sembuf = { 55 | .sem_num = 0, 56 | .sem_op = 1, 57 | .sem_flg = 0, 58 | }; 59 | 60 | // Generate key for semaphore based on the mount point 61 | if (!ftok || (key = ftok(MOUNT_POINT, IPC_KEY)) == -1) { 62 | PRINT_MSG("%s\n", "Unable to get semaphore key!"); 63 | return -1; 64 | } 65 | 66 | PRINT_MSG("Key: %x\n", key); 67 | 68 | // Get the semaphore using the key 69 | if ((semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666)) >= 0) { 70 | semun.val = 1; 71 | // Unlock the semaphore and set the sem_otime field 72 | if (semop(semid, &sembuf, 1) == -1) { 73 | PRINT_MSG("%s\n", "Unable to initialize semaphore!"); 74 | // Clean up semaphore 75 | semctl(semid, 0, IPC_RMID); 76 | semid = -1; 77 | } 78 | } else if (errno == EEXIST) { 79 | // Get the semaphore in non-exclusive mode 80 | if ((semid = semget(key, 1, 0)) < 0) { 81 | PRINT_MSG("%s\n", "Unable to get semaphore non-exclusively!"); 82 | return semid; 83 | } 84 | 85 | semun.buf = &seminfo; 86 | // Wait for the semaphore to be initialized 87 | while (timeout++ < IPC_TIMEOUT) { 88 | semctl(semid, 0, IPC_STAT, semun); 89 | 90 | if (semun.buf && semun.buf->sem_otime != 0) { 91 | break; 92 | } 93 | 94 | if (!(timeout % 100)) { 95 | PRINT_MSG("Waiting for semaphore initialization (Key: %x, Semaphore: %x)...\n", key, semid); 96 | } 97 | } 98 | } 99 | 100 | return (timeout < IPC_TIMEOUT) ? semid : -1; 101 | } 102 | 103 | static void sem_lock() { 104 | int semid; 105 | struct sembuf sembuf = { 106 | .sem_num = 0, 107 | .sem_op = -1, 108 | .sem_flg = SEM_UNDO, 109 | }; 110 | struct mntent entry, *ent; 111 | FILE *mnt = NULL; 112 | 113 | // If not initialized, check for existing mount before triggering NVRAM init 114 | if (!init) { 115 | if ((mnt = setmntent("/proc/mounts", "r"))) { 116 | while ((ent = getmntent_r(mnt, &entry, temp, BUFFER_SIZE))) { 117 | if (!strncmp(ent->mnt_dir, MOUNT_POINT, sizeof(MOUNT_POINT) - 2)) { 118 | init = 1; 119 | PRINT_MSG("%s\n", "Already initialized!"); 120 | endmntent(mnt); 121 | goto cont; 122 | } 123 | } 124 | endmntent(mnt); 125 | } 126 | 127 | PRINT_MSG("%s\n", "Triggering NVRAM initialization!"); 128 | nvram_init(); 129 | } 130 | 131 | cont: 132 | // Must get sempahore after NVRAM initialization, mounting will change ID 133 | if ((semid = sem_get()) == -1) { 134 | PRINT_MSG("%s\n", "Unable to get semaphore!"); 135 | return; 136 | } 137 | 138 | // PRINT_MSG("%s\n", "Locking semaphore..."); 139 | 140 | if (semop(semid, &sembuf, 1) == -1) { 141 | PRINT_MSG("%s\n", "Unable to lock semaphore!"); 142 | } 143 | 144 | return; 145 | } 146 | 147 | static void sem_unlock() { 148 | int semid; 149 | struct sembuf sembuf = { 150 | .sem_num = 0, 151 | .sem_op = 1, 152 | .sem_flg = SEM_UNDO, 153 | }; 154 | 155 | if ((semid = sem_get(NULL)) == -1) { 156 | PRINT_MSG("%s\n", "Unable to get semaphore!"); 157 | return; 158 | } 159 | 160 | // PRINT_MSG("%s\n", "Unlocking semaphore..."); 161 | 162 | if (semop(semid, &sembuf, 1) == -1) { 163 | PRINT_MSG("%s\n", "Unable to unlock semaphore!"); 164 | } 165 | 166 | return; 167 | } 168 | 169 | int nvram_init(void) { 170 | FILE *f; 171 | 172 | PRINT_MSG("%s\n", "Initializing NVRAM..."); 173 | 174 | if (init) { 175 | PRINT_MSG("%s\n", "Early termination!"); 176 | return E_SUCCESS; 177 | } 178 | init = 1; 179 | 180 | sem_lock(); 181 | 182 | if (mount("tmpfs", MOUNT_POINT, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_SYNCHRONOUS, "") == -1) { 183 | sem_unlock(); 184 | PRINT_MSG("Unable to mount tmpfs on mount point %s!\n", MOUNT_POINT); 185 | return E_FAILURE; 186 | } 187 | 188 | // Checked by certain Ralink routers 189 | if ((f = fopen("/var/run/nvramd.pid", "w+")) == NULL) { 190 | PRINT_MSG("Unable to touch Ralink PID file: %s!\n", "/var/run/nvramd.pid"); 191 | } 192 | else { 193 | fclose(f); 194 | } 195 | 196 | sem_unlock(); 197 | 198 | return nvram_set_default(); 199 | } 200 | 201 | int nvram_reset(void) { 202 | PRINT_MSG("%s\n", "Reseting NVRAM..."); 203 | 204 | if (nvram_clear() != E_SUCCESS) { 205 | PRINT_MSG("%s\n", "Unable to clear NVRAM!"); 206 | return E_FAILURE; 207 | } 208 | 209 | return nvram_set_default(); 210 | } 211 | 212 | int nvram_clear(void) { 213 | char path[PATH_MAX] = MOUNT_POINT; 214 | struct dirent *entry; 215 | int ret = E_SUCCESS; 216 | DIR *dir; 217 | 218 | PRINT_MSG("%s\n", "Clearing NVRAM..."); 219 | 220 | sem_lock(); 221 | 222 | if (!(dir = opendir(MOUNT_POINT))) { 223 | sem_unlock(); 224 | PRINT_MSG("Unable to open directory %s!\n", MOUNT_POINT); 225 | return E_FAILURE; 226 | } 227 | 228 | while ((entry = readdir(dir))) { 229 | if (!strncmp(entry->d_name, ".", 1) || !strcmp(entry->d_name, "..")) { 230 | PRINT_MSG("Skipping %s\n", entry->d_name); 231 | continue; 232 | } 233 | 234 | strncpy(path + strlen(MOUNT_POINT), entry->d_name, ARRAY_SIZE(path) - ARRAY_SIZE(MOUNT_POINT) - 1); 235 | path[PATH_MAX - 1] = '\0'; 236 | 237 | PRINT_MSG("%s\n", path); 238 | 239 | if (unlink(path) == -1 && errno != ENOENT) { 240 | PRINT_MSG("Unable to unlink %s!\n", path); 241 | ret = E_FAILURE; 242 | } 243 | } 244 | 245 | closedir(dir); 246 | sem_unlock(); 247 | return ret; 248 | } 249 | 250 | int nvram_close(void) { 251 | PRINT_MSG("%s\n", "Closing NVRAM..."); 252 | return E_SUCCESS; 253 | } 254 | 255 | int nvram_list_add(const char *key, const char *val) { 256 | char *pos; 257 | 258 | PRINT_MSG("%s = %s + %s\n", val, temp, key); 259 | 260 | if (nvram_get_buf(key, temp, BUFFER_SIZE) != E_SUCCESS) { 261 | return nvram_set(key, val); 262 | } 263 | 264 | if (!key || !val) { 265 | return E_FAILURE; 266 | } 267 | 268 | if (strlen(temp) + 1 + strlen(val) + 1 > BUFFER_SIZE) { 269 | return E_FAILURE; 270 | } 271 | 272 | // This will overwrite the temp buffer, but it is OK 273 | if (nvram_list_exist(key, val, LIST_MAGIC) != NULL) { 274 | return E_SUCCESS; 275 | } 276 | 277 | // Replace terminating NULL of list with LIST_SEP 278 | pos = temp + strlen(temp); 279 | if (pos != temp) { 280 | *pos++ = LIST_SEP[0]; 281 | } 282 | 283 | if (strcpy(pos, val) != pos) { 284 | return E_FAILURE; 285 | } 286 | 287 | return nvram_set(key, temp); 288 | } 289 | 290 | char *nvram_list_exist(const char *key, const char *val, int magic) { 291 | char *pos = NULL; 292 | 293 | if (nvram_get_buf(key, temp, BUFFER_SIZE) != E_SUCCESS) { 294 | return E_FAILURE; 295 | } 296 | 297 | PRINT_MSG("%s ?in %s (%s)\n", val, key, temp); 298 | 299 | if (!val) { 300 | return (magic == LIST_MAGIC) ? NULL : (char *) E_FAILURE; 301 | } 302 | 303 | while ((pos = strtok(!pos ? temp : NULL, LIST_SEP))) { 304 | if (!strcmp(pos + 1, val)) { 305 | return (magic == LIST_MAGIC) ? pos + 1 : (char *) E_SUCCESS; 306 | } 307 | } 308 | 309 | return (magic == LIST_MAGIC) ? NULL : (char *) E_FAILURE; 310 | } 311 | 312 | int nvram_list_del(const char *key, const char *val) { 313 | char *pos; 314 | 315 | if (nvram_get_buf(key, temp, BUFFER_SIZE) != E_SUCCESS) { 316 | return E_SUCCESS; 317 | } 318 | 319 | PRINT_MSG("%s = %s - %s\n", key, temp, val); 320 | 321 | if (!val) { 322 | return E_FAILURE; 323 | } 324 | 325 | // This will overwrite the temp buffer, but it is OK. 326 | if ((pos = nvram_list_exist(key, val, LIST_MAGIC))) { 327 | while (*pos && *pos != LIST_SEP[0]) { 328 | *pos++ = LIST_SEP[0]; 329 | } 330 | } 331 | 332 | return nvram_set(key, temp); 333 | } 334 | 335 | char *nvram_get(const char *key) { 336 | // Some routers pass the key as the second argument, instead of the first. 337 | // We attempt to fix this directly in assembly for MIPS if the key is NULL. 338 | #if defined(mips) 339 | if (!key) { 340 | asm ("move %0, $a1" :"=r"(key)); 341 | } 342 | #endif 343 | 344 | return (nvram_get_buf(key, temp, BUFFER_SIZE) == E_SUCCESS) ? strndup(temp, BUFFER_SIZE) : NULL; 345 | } 346 | 347 | char *nvram_safe_get(const char *key) { 348 | char* ret = nvram_get(key); 349 | return ret ? ret : strdup(""); 350 | } 351 | 352 | char *nvram_default_get(const char *key, const char *val) { 353 | char *ret = nvram_get(key); 354 | 355 | PRINT_MSG("%s = %s || %s\n", key, ret, val); 356 | 357 | if (ret) { 358 | return ret; 359 | } 360 | 361 | if (val && nvram_set(key, val)) { 362 | return nvram_get(key); 363 | } 364 | 365 | return NULL; 366 | } 367 | 368 | int nvram_get_buf(const char *key, char *buf, size_t sz) { 369 | char path[PATH_MAX] = MOUNT_POINT; 370 | FILE *f; 371 | 372 | if (!buf) { 373 | PRINT_MSG("NULL output buffer, key: %s!\n", key); 374 | return E_FAILURE; 375 | } 376 | 377 | if (!key) { 378 | PRINT_MSG("NULL input key, buffer: %s!\n", buf); 379 | return E_FAILURE; 380 | } 381 | 382 | PRINT_MSG("%s\n", key); 383 | 384 | strncat(path, key, ARRAY_SIZE(path) - ARRAY_SIZE(MOUNT_POINT) - 1); 385 | 386 | sem_lock(); 387 | 388 | if ((f = fopen(path, "rb")) == NULL) { 389 | sem_unlock(); 390 | PRINT_MSG("Unable to open key: %s!\n", path); 391 | return E_FAILURE; 392 | } 393 | 394 | buf[0] = '\0'; 395 | char tmp[sz]; 396 | while(fgets(tmp, sz, f)) { 397 | strncat (buf, tmp, sz); 398 | } 399 | fclose(f); 400 | sem_unlock(); 401 | 402 | PRINT_MSG("= \"%s\"\n", buf); 403 | 404 | return E_SUCCESS; 405 | } 406 | 407 | int nvram_get_int(const char *key) { 408 | char path[PATH_MAX] = MOUNT_POINT; 409 | FILE *f; 410 | int ret; 411 | 412 | if (!key) { 413 | PRINT_MSG("%s\n", "NULL key!"); 414 | return E_FAILURE; 415 | } 416 | 417 | PRINT_MSG("%s\n", key); 418 | 419 | strncat(path, key, ARRAY_SIZE(path) - ARRAY_SIZE(MOUNT_POINT) - 1); 420 | 421 | sem_lock(); 422 | 423 | if ((f = fopen(path, "rb")) == NULL) { 424 | sem_unlock(); 425 | PRINT_MSG("Unable to open key: %s!\n", path); 426 | return E_FAILURE; 427 | } 428 | 429 | if (fread(&ret, sizeof(ret), 1, f) != 1) { 430 | fclose(f); 431 | sem_unlock(); 432 | PRINT_MSG("Unable to read key: %s!\n", path); 433 | return E_FAILURE; 434 | } 435 | fclose(f); 436 | sem_unlock(); 437 | 438 | PRINT_MSG("= %d\n", ret); 439 | 440 | return ret; 441 | } 442 | 443 | int nvram_getall(char *buf, size_t len) { 444 | char path[PATH_MAX] = MOUNT_POINT; 445 | struct stat path_stat; 446 | struct dirent *entry; 447 | size_t pos = 0, ret; 448 | DIR *dir; 449 | FILE *f; 450 | 451 | if (!buf || !len) { 452 | PRINT_MSG("%s\n", "NULL buffer or zero length!"); 453 | return E_FAILURE; 454 | } 455 | 456 | sem_lock(); 457 | 458 | if (!(dir = opendir(MOUNT_POINT))) { 459 | sem_unlock(); 460 | PRINT_MSG("Unable to open directory %s!\n", MOUNT_POINT); 461 | return E_FAILURE; 462 | } 463 | 464 | while ((entry = readdir(dir))) { 465 | if (!strncmp(entry->d_name, ".", 1) || !strcmp(entry->d_name, "..")) { 466 | continue; 467 | } 468 | 469 | strncpy(path + strlen(MOUNT_POINT), entry->d_name, ARRAY_SIZE(path) - ARRAY_SIZE(MOUNT_POINT) - 1); 470 | path[PATH_MAX - 1] = '\0'; 471 | 472 | stat(path, &path_stat); 473 | if (!S_ISREG(path_stat.st_mode)) { 474 | continue; 475 | } 476 | 477 | if ((ret = snprintf(buf + pos, len - pos, "%s=", entry->d_name)) != strlen(entry->d_name) + 1) { 478 | closedir(dir); 479 | sem_unlock(); 480 | PRINT_MSG("Unable to append key %s!\n", buf + pos); 481 | return E_FAILURE; 482 | } 483 | 484 | pos += ret; 485 | 486 | if ((f = fopen(path, "rb")) == NULL) { 487 | closedir(dir); 488 | sem_unlock(); 489 | PRINT_MSG("Unable to open key: %s!\n", path); 490 | return E_FAILURE; 491 | } 492 | 493 | ret = fread(temp, sizeof(*temp), BUFFER_SIZE, f); 494 | if (ferror(f)) { 495 | fclose(f); 496 | closedir(dir); 497 | sem_unlock(); 498 | PRINT_MSG("Unable to read key: %s!\n", path); 499 | return E_FAILURE; 500 | } 501 | 502 | memcpy(buf + pos, temp, ret); 503 | buf[pos + ret] = '\0'; 504 | pos += ret + 1; 505 | 506 | fclose(f); 507 | } 508 | 509 | closedir(dir); 510 | sem_unlock(); 511 | return E_SUCCESS; 512 | } 513 | 514 | int nvram_set(const char *key, const char *val) { 515 | char path[PATH_MAX] = MOUNT_POINT; 516 | FILE *f; 517 | 518 | if (!key || !val) { 519 | PRINT_MSG("%s\n", "NULL key or value!"); 520 | return E_FAILURE; 521 | } 522 | 523 | PRINT_MSG("%s = \"%s\"\n", key, val); 524 | 525 | strncat(path, key, ARRAY_SIZE(path) - ARRAY_SIZE(MOUNT_POINT) - 1); 526 | 527 | sem_lock(); 528 | 529 | if ((f = fopen(path, "wb")) == NULL) { 530 | sem_unlock(); 531 | PRINT_MSG("Unable to open key: %s!\n", path); 532 | return E_FAILURE; 533 | } 534 | 535 | if (fwrite(val, sizeof(*val), strlen(val), f) != strlen(val)) { 536 | fclose(f); 537 | sem_unlock(); 538 | PRINT_MSG("Unable to write value: %s to key: %s!\n", val, path); 539 | return E_FAILURE; 540 | } 541 | 542 | fclose(f); 543 | sem_unlock(); 544 | return E_SUCCESS; 545 | } 546 | 547 | int nvram_set_int(const char *key, const int val) { 548 | char path[PATH_MAX] = MOUNT_POINT; 549 | FILE *f; 550 | 551 | if (!key) { 552 | PRINT_MSG("%s\n", "NULL key!"); 553 | return E_FAILURE; 554 | } 555 | 556 | PRINT_MSG("%s = %d\n", key, val); 557 | 558 | strncat(path, key, ARRAY_SIZE(path) - ARRAY_SIZE(MOUNT_POINT) - 1); 559 | 560 | sem_lock(); 561 | 562 | if ((f = fopen(path, "wb")) == NULL) { 563 | sem_unlock(); 564 | PRINT_MSG("Unable to open key: %s!\n", path); 565 | return E_FAILURE; 566 | } 567 | 568 | if (fwrite(&val, sizeof(val), 1, f) != 1) { 569 | fclose(f); 570 | sem_unlock(); 571 | PRINT_MSG("Unable to write value: %d to key: %s!\n", val, path); 572 | return E_FAILURE; 573 | } 574 | 575 | fclose(f); 576 | sem_unlock(); 577 | return E_SUCCESS; 578 | } 579 | 580 | int nvram_set_default(void) { 581 | int ret = nvram_set_default_builtin(); 582 | PRINT_MSG("Loading built-in default values = %d!\n", ret); 583 | 584 | #define NATIVE(a, b) \ 585 | if (!system(a)) { \ 586 | PRINT_MSG("Executing native call to built-in function: %s (%p) = %d!\n", #b, b, b); \ 587 | } 588 | 589 | #define TABLE(a) \ 590 | PRINT_MSG("Checking for symbol \"%s\"...\n", #a); \ 591 | if (a) { \ 592 | PRINT_MSG("Loading from native built-in table: %s (%p) = %d!\n", #a, a, nvram_set_default_table(a)); \ 593 | } 594 | 595 | #define PATH(a) \ 596 | if (!access(a, R_OK)) { \ 597 | PRINT_MSG("Loading from default configuration file: %s = %d!\n", a, foreach_nvram_from(a, (void (*)(const char *, const char *, void *)) nvram_set, NULL)); \ 598 | } 599 | 600 | NVRAM_DEFAULTS_PATH 601 | #undef PATH 602 | #undef NATIVE 603 | #undef TABLE 604 | 605 | return nvram_set_default_image(); 606 | } 607 | 608 | static int nvram_set_default_builtin(void) { 609 | int ret = E_SUCCESS; 610 | 611 | PRINT_MSG("%s\n", "Setting built-in default values!"); 612 | 613 | #define ENTRY(a, b, c) \ 614 | if (b(a, c) != E_SUCCESS) { \ 615 | PRINT_MSG("Unable to initialize built-in NVRAM value %s!\n", a); \ 616 | ret = E_FAILURE; \ 617 | } 618 | 619 | NVRAM_DEFAULTS 620 | #undef ENTRY 621 | 622 | return ret; 623 | } 624 | 625 | static int nvram_set_default_image(void) { 626 | PRINT_MSG("%s\n", "Copying overrides from defaults folder!"); 627 | sem_lock(); 628 | system("/bin/cp "OVERRIDE_POINT"* "MOUNT_POINT); 629 | sem_unlock(); 630 | return E_SUCCESS; 631 | } 632 | 633 | static int nvram_set_default_table(const char *tbl[]) { 634 | size_t i = 0; 635 | 636 | while (tbl[i]) { 637 | nvram_set(tbl[i], tbl[i + 1]); 638 | i += (tbl[i + 2] != 0 && tbl[i + 2] != (char *) 1) ? 2 : 3; 639 | } 640 | 641 | return E_SUCCESS; 642 | } 643 | 644 | int nvram_unset(const char *key) { 645 | char path[PATH_MAX] = MOUNT_POINT; 646 | 647 | if (!key) { 648 | PRINT_MSG("%s\n", "NULL key!"); 649 | return E_FAILURE; 650 | } 651 | 652 | PRINT_MSG("%s\n", key); 653 | 654 | strncat(path, key, ARRAY_SIZE(path) - ARRAY_SIZE(MOUNT_POINT) - 1); 655 | 656 | sem_lock(); 657 | if (unlink(path) == -1 && errno != ENOENT) { 658 | sem_unlock(); 659 | PRINT_MSG("Unable to unlink %s!\n", path); 660 | return E_FAILURE; 661 | } 662 | sem_unlock(); 663 | return E_SUCCESS; 664 | } 665 | 666 | int nvram_match(const char *key, const char *val) { 667 | if (!key) { 668 | PRINT_MSG("%s\n", "NULL key!"); 669 | return E_FAILURE; 670 | } 671 | 672 | if (nvram_get_buf(key, temp, BUFFER_SIZE) != E_SUCCESS) { 673 | return !val ? E_SUCCESS : E_FAILURE; 674 | } 675 | 676 | PRINT_MSG("%s (%s) ?= \"%s\"\n", key, temp, val); 677 | 678 | if (strncmp(temp, val, BUFFER_SIZE)) { 679 | PRINT_MSG("%s\n", "false"); 680 | return E_FAILURE; 681 | } 682 | 683 | PRINT_MSG("%s\n", "true"); 684 | return E_SUCCESS; 685 | } 686 | 687 | int nvram_invmatch(const char *key, const char *val) { 688 | if (!key) { 689 | PRINT_MSG("%s\n", "NULL key!"); 690 | return E_FAILURE; 691 | } 692 | 693 | PRINT_MSG("%s ~?= \"%s\"\n", key, val); 694 | return !nvram_match(key, val); 695 | } 696 | 697 | int nvram_commit(void) { 698 | sem_lock(); 699 | sync(); 700 | sem_unlock(); 701 | 702 | return E_SUCCESS; 703 | } 704 | 705 | // Hack to use static variables in shared library 706 | #include "alias.c" 707 | --------------------------------------------------------------------------------