summaryrefslogtreecommitdiffhomepage
path: root/lib
AgeCommit message (Collapse)Author
2022-05-20fs: avoid input buffering with small limits in fs.readfile()Jo-Philipp Wich
If the read limit passed to fs.readfile() is smaller than BUFSIZ then disable stdio input buffering to avoid overreading the underlying file. This is useful when reading small amounts of data from special files such as /dev/urandom, where a readfile call limited to 16 bytes might actually read 4096 due to stdio buffering. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-04-07fs: implement `fs.readfile()` and `fs.writefile()`Jo-Philipp Wich
The `fs.readfile()` and `fs.writefile()` functions allow efficient reading and writing of whole files, reducing the required boilerplace code compared to using the `open()`/`read()`/`write()`/`close()` API. - `fs.readfile()` takes two arguments; the path to open and an optional limit value. If limit is omitted, `null` or negative, the entire file contents are read, otherwise the specified amount of bytes. Returns the read file contents as string or `null` on error. - `fs.writefile()` takes three arguments; the path to open/create, the contents to write and an optional limit value. If limit is omitted, the entire content is written, otherwise just the specified amount of bytes. Non-string content arguments are internally converted to strings, `null` is treated as empty string. Returns the amount of bytes written or `null` on error. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-31fs: fix off-by-one in fs.dirname() functionDaniel Golle
Make sure fs.dirname() doesn't truncate the last character of the returned path. Previously ucv_string_new_length was called with a length which no longer included the last character (which had just been tested not to be a '/' or '.' and hence broke the loop at that point). Signed-off-by: Daniel Golle <daniel@makrotopia.org> [testcase added] Signed-off-by: Paul Spooren <mail@aparcar.org> [testcase folded into this commit and fixed] Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-21uloop: add support for tasksJo-Philipp Wich
Tasks are similar to processes but instead of executing a new process, an ucode function is invoked instead, running independently of the main process. Example usage: uloop.init(); let t = uloop.task( // program function function(pipe) { let input = pipe.receive(); pipe.send({ got_input: input }); return { result: true }; }, // parent recv function, invoked when task function calls pipe.send() function(res) { printf("Received output message: %.J\n", res); }, // parent send function, invoked when task function calls pipe.receive() function() { let input = { test: "Example" }; printf("Sending input message: %.J\n", input); return input; } ); uloop.run(); Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-15resolv: make OS X compatibleJo-Philipp Wich
OS X `socket()` does not support the `SOCK_NONBLOCK` or `SOCK_CLOEXEC` constants, so apply these flags using `fcntl()` after creating the socket. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-15fs: avoid Linux specific sys/sysmacros.h include on OS XJo-Philipp Wich
It should be enough to include `sys/types.h` to obtain `major()` and `minor()` definitions on OS X, so avoid including the the Linux specific `sys/sysmacros.h` header in this case. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-15uloop: use execvp() on OS XJo-Philipp Wich
Since `execvpe()` is a GNU extension, fall back to using `execve()` on OS X. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-14nl80211: add missing attributes and correct some attribute flagsJo-Philipp Wich
Suggested-by: John Crispin <john@phrozen.org> Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-07ubus: add event supportJo-Philipp Wich
Extend the ubus binding to cover ubus event handling APIs. Instantiating ubus event listener: listener = conn.listener( "event.type.*", function (type, data) { ...event callback... } ); listener.remove(); Broadcasting events: conn.event("event.type.foo", { ...event data... }); Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-07ubus: refactor error and argument handlingJo-Philipp Wich
- Add more detailled error messages - Introduce helpers for fetching and validating function call arguments - Get rid of some uneeded blob->jso->ucv conversions Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-07ubus: add object publishing, notify and subscribe supportJo-Philipp Wich
Extend the ubus binding to cover ubus object publishing, notifications on objects, as well as subscriber APIs. Instantiating ubus objects: obj = conn.publish("objname", { methodname: { args: { ...argspec... }, call: function(request) { ...method handler... } }, ... }, function() { ...subscription status change handler... }); obj.notify(...); obj.remove(); Emitting notifications: obj.notify("notificationtype", { ...notification data... }, function(type, data) { ...data callback... }, function(idx, ret) { ...status callback... }, function() { ...completion callback... }, 100 /* timeout */ ); Instantiating subscribers: sub = conn.subscriber( function(notify) { ...notification handler... }, function(id) { ...object gone handler... } ); sub.subscribe("objname"); sub.unsubscribe("objname"); sub.remove(); Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-06uloop: clear errno before integer conversion attemptsJo-Philipp Wich
In some cases, errno contains stale values from prior function invocations which might lead to random failures in uc_uloop_run(), uc_uloop_timer_set() and uc_uloop_timer(). Solve this issue by explicitly initializing errno to 0. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-03-02lib: introduce uloop bindingJo-Philipp Wich
The uloop module allows controlling the uloop event loop and supports adding timeouts, processes and file descriptors. Example: #!ucode -RS let fs = require("fs"); let uloop = require("uloop"); let fd1 = fs.popen("echo 1; sleep 1; echo 2; sleep 1; echo 3", "r"); let fd2 = fs.popen("echo 4; sleep 1; echo 5; sleep 1; echo 6", "r"); function fd_read_callback(flags) { if (flags & uloop.ULOOP_READ) { let line = this.handle().read("line"); printf("Line from fd <%s/%d>: <%s>\n", this, this.fileno(), trim(line)); } } uloop.init(); uloop.timer(1500, function() { printf("Timeout after 1500ms\n"); }); uloop.handle(fd1, fd_read_callback, uloop.ULOOP_READ); uloop.handle(fd2, fd_read_callback, uloop.ULOOP_READ); uloop.process("date", [ "+%s" ], { LC_ALL: "C" }, function(exitcode) { printf("Date command exited with code %d\n", exitcode); }); uloop.run(); Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-02-15fs: implement access(), mkstemp(), file.flush() and proc.flush()Jo-Philipp Wich
The `access()` function allows testing the path given in the first argument for accessibility according to the permissions specified in the second mode string argument. The mode string characters may be `r`, `w`, `x` or `f` which correspond to `R_OK` - path is readable, `W_OK` - path is writable, `X_OK` - path is executable or `F_OK` - path exists respectively. The `mkstemp()` function creates a secure temporary file, unlinks it and returns the open file handle. The temporary path is constructed based on the optional template argument. If the template argument contains a slash, the path is taken as-is, if it contains no slashes, `/tmp/` is prepended. If the template does not end with `XXXXXX`, a `.XXXXXX` suffix is appended to the path. If the template is omitted, `/tmp/XXXXXX` is used. The `file.flush()` and `proc.flush()` functions call `fflush()` on the underlying file handle respectively. They take no arguments. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-01-24struct: implement `*` format, fix invalid memory accessesJo-Philipp Wich
Implement a new `*` format which acts like `s` on unpack but accepts input records which are shorter than the specified length, e.g. the following call will yield "abc" while an equivalent "10s" format would fail: unpack("2*", "abc") // [ "ab" ] unpack("10*", "abc") // [ "abc" ] unpack("10s", "abc") // null The `*` format is primarily useful to extract the remainder of a variable length record without having to encode the specific length of the record directly into the format string. When packing records, the `*` format takes at most as many bytes as specified in the format string repeat count. If the input string is shorter than the given repeat count, only as many bytes as present in the input string are taken. A bare `*` without any repeat count will take all bytes from the given input string: pack("2*", "abc") // "ab" pack("10*", "abc") // "abc" pack("*", "abc") // "abc" pack("10s", "abc") // "abc\0\0\0\0\0\0\0" Additionally prevent invalid memory accesses when unpacking a buffer shorter than the length expected by the format string. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-01-18lib: replace usages of vasprintf() with xvasprintf()Jo-Philipp Wich
This avoid triggering -Werror=unused-result compilation failures with certain toolchains. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-01-04struct: reuse double packing routines from coreJo-Philipp Wich
Use uc_pack_double() and uc_unpack_double() from core to avoid unnecessary code duplication. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-01-04treewide: rework numeric value handlingJo-Philipp Wich
- Parse integer literals as unsigned numeric values in order to be able to represent the entire unsigned 64bit value range - Stop parsing minus-prefixed integer literals as negative numbers but treat them as separate minus operator followed by a positive integer instead - Only store unsigned numeric constants in bytecode - Rework numeric comparison logic to be able to handle full 64bit unsigned integers - If possible, yield unsigned 64 bit results for additions - Simplify numeric value conversion API - Compile code with -fwrapv for defined signed overflow semantics Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-01-04fs: implement fdopen(), file.fileno() and proc.fileno()Jo-Philipp Wich
Implement support for opening existing file descriptors as well as acquiring the descriptor number from open process and file handles. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-12-08ubus: add support for async requestsJo-Philipp Wich
Introduce a new ubus.defer() method which initiates asynchroneous requests and invokes the completion callback passed as 4th argument once the reply is received. This allows multiplexing mutliple ubus requests with the same ucode VM context / the same uloop event loop. In case the ucode context is not running under an active uloop, the ubus module will spawn uloop itself once the first asynchroneous request is launched and terminate the loop once all pending requests finished. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-11-05nl80211: fix premature netlink reply receive abortJo-Philipp Wich
The nl_recvmsgs() logic in uc_nl_request() incorrectly stopped reading the socket before the netlink ACK message was handled for non-multipart replies. This caused subsequent requests to incorrectly receive the ACK of the previous request, leading to a failure to receive the actual reply. Fix this issue by continue reading the socket until either the finish callback for multipart (dump) messages or the ack callback for non- multipart messages was received. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-11-04fs: add utility functionsJo-Philipp Wich
Add three new functions `dirname()`, `basename()` and `lsdir()`. The `basename()` and `dirname()` functions behave like their libc counterparts and return the filename and directory portion of a given path respectively. If the path argument is missing or not a string, null is returned. Examples: dirname("/usr/lib") -> "/usr" dirname("/usr/") -> "/" dirname("usr") -> "." dirname("/") -> "/" dirname(".") -> "." dirname("..") -> "." basename("/usr/lib") -> "lib" basename("/usr/") -> "usr" basename("usr") -> "usr" basename("/") -> "/" basename(".") -> "." basename("..") -> ".." The `lsdir()` function returns a sorted array containing the names of all entries within the given directory path, without the common "." and ".." entries. If the given path is not a directory, cannot be opened or if another system level occurs, null is returned and `fs.error()` can be used to query details. The function takes an optional second argument which may be either a regular expression value or a string. In case a regular expression is given, each directory entry is matched against it. In case a string is provided, it is treated as wildcard (glob) pattern and only directory entries matching the pattern are considered. Examples: lsdir("/sys/class/net") -> [ "eth0", "lo", "wlan0" ] lsdir("/proc", /^[0-9]+$/) -> [ "1", "4", "12", ... ] lsdir("/sys/class/block/", "sd?3") -> [ "sde3", "sdf3" ] Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-11-02nl80211: fix wiphy dump reply merge logicJo-Philipp Wich
Extract the wiphy index from the response message and use it to select the item to merge the fragment information into. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-11-01treewide: fix typo in exported function names and typesJo-Philipp Wich
Fix instances of misspelled "resource". This commit breaks the exported libucode ABI. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-11-01struct: fix PowerPC specific compiler pragma nameJo-Philipp Wich
The "align" pragma was accidentally renamed while refactoring the original module code. Fixes: 402f603 ("lib: introduce struct library") Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-10-31lib: introduce struct libraryJo-Philipp Wich
Introduce a new "struct" library which is a port of the Python 3.10 struct module with a reduced set of API functions. It supports the same format patterns and conversions while providing the following methods: struct = require('struct'); buf = struct.pack("fmt", args...); values = struct.unpack("fmt", buf); struct_inst = struct.new("fmt"); buf = struct_inst.pack(args...); values = struct_inst.unpack(buf); Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-10-22lib: introduce resolver libraryJo-Philipp Wich
This adds a simple, UDP-only DNS resolver library mimicking the operation of the extended busybox nslookup applet. Simply querying a domain name will perform A + AAAA resolving by default: # ucode -mresolv -Rs 'printf("%.J\n", resolv.query("example.com"))' { "example.com": { "A": [ "93.184.216.34" ], "AAAA": [ "2606:2800:220:1:248:1893:25c8:1946" ] } } Passing IP addresses will automatically perform PTR requests: # ucode -mresolv -Rs 'printf("%.J\n", resolv.query("8.8.8.8"))' { "8.8.8.8.in-addr.arpa": { "PTR": [ "dns.google" ] } } # ucode -mresolv -Rs 'printf("%.J\n", resolv.query("2001:4860:4860::8888"))' { "8.8.8.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.6.8.4.0.6.8.4.1.0.0.2.ip6.arpa": { "PTR": [ "dns.google" ] } } Additional options for query type and nameserver selection can be passed via a second optional options dictionary: # ucode -mresolv -Rs 'printf("%.J\n", resolv.query([ "openwrt.org", "example.org", "doesnotexist.tld" ], { type: [ "A", "AAAA", "MX" ], nameserver: [ "1.1.1.1", "8.8.4.4" ], timeout: 5000, retries: 2, edns_maxsize: 4096 }))' { "openwrt.org": { "A": [ "139.59.209.225" ], "MX": [ [ 10, "util-01.infra.openwrt.org" ] ], "AAAA": [ "2a03:b0c0:3:d0::1af1:1" ] }, "example.org": { "A": [ "93.184.216.34" ], "AAAA": [ "2606:2800:220:1:248:1893:25c8:1946" ], "MX": [ [ 0, "." ] ] }, "doesnotexist.tld": { "rcode": "NXDOMAIN" } } Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-09-24ubus: properly handle signed 64bit values tooJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-09-23ubus: fix handling signed 16bit and 32bit integersJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-09-22nl80211: fix issues spotted by static code analyzerJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-09-21nl80211: treat signal attr values as signed integersJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-09-21nl80211: expose sta_info attributesJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-09-21lib: introduce Linux 802.11 netlink bindingJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-09-15rtnl: automatically derive message family from certain address attrsJo-Philipp Wich
# ucode -mrtnl -Rs 'printf("%.J", rtnl.request(rtnl.const.RTM_GETROUTE, 0, { dst: "8.8.8.8" }))' { "family": 2, "tos": 0, "protocol": 0, "scope": 0, "type": 1, "flags": 512, "dst": "8.8.8.8/32", "oif": "onboard", "gateway": "10.11.12.13", "prefsrc": "10.11.12.7", "cacheinfo": { "clntref": 2, "lastuse": 0, "expires": 0, "error": 0, "used": 0, "id": 0, "ts": 0, "tsage": 0 }, "table": 254, "uid": 0 } Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-09-15rtnl: expose IPv4 and IPv6 devconfig informationJo-Philipp Wich
# ucode -mrtnl -Rs 'printf("%.J", rtnl.request(rtnl.const.RTM_GETLINK, 0, { dev: "lo" }))' { "family": 0, "type": 772, "dev": "lo", "flags": 65609, "address": "00:00:00:00:00:00", "broadcast": "00:00:00:00:00:00", "txqlen": 1000, "mtu": 65536, "carrier": true, "linkmode": 0, "operstate": 0, "num_tx_queues": 1, "num_rx_queues": 1, "af_spec": { "inet": { "conf": { "forwarding": 1, "mc_forwarding": 0, "proxy_arp": 0, "accept_redirects": 1, "secure_redirects": 1, "send_redirects": 1, "shared_media": 1, "rp_filter": 0, "accept_source_route": 1, "bootp_relay": 0, "log_martians": 0, "tag": 0, "arpfilter": 0, "medium_id": 0, "noxfrm": 1, "nopolicy": 1, "force_igmp_version": 0, "arp_announce": 0, "arp_ignore": 0, "promote_secondaries": 0, "arp_accept": 0, "arp_notify": 0, "accept_local": 0, "src_vmark": 0, "proxy_arp_pvlan": 0, "route_localnet": 0, "igmpv2_unsolicited_report_interval": 10000, "igmpv3_unsolicited_report_interval": 1000, "ignore_routes_with_linkdown": 0, "drop_unicast_in_l2_multicast": 0, "drop_gratuitous_arp": 0, "bc_forwarding": 0 } }, "inet6": { "mode": 0, "flags": 2147483648, "conf": { "forwarding": 0, "hoplimit": 64, "mtu6": 65536, "accept_ra": 1, "accept_redirects": 1, "autoconf": 1, "dad_transmits": 1, "rtr_solicits": -1, "rtr_solicit_interval": 4000, "rtr_solicit_delay": 1000, "use_tempaddr": -1, "temp_valid_lft": 604800, "temp_prefered_lft": 86400, "regen_max_retry": 3, "max_desync_factor": 600, "max_addresses": 16, "force_mld_version": 0, "accept_ra_defrtr": 1, "accept_ra_pinfo": 1, "accept_ra_rtr_pref": 1, "rtr_probe_interval": 60000, "accept_ra_rt_info_max_plen": 0, "proxy_ndp": 0, "optimistic_dad": 0, "accept_source_route": 0, "mc_forwarding": 0, "disable_ipv6": 0, "accept_dad": -1, "force_tllao": 0, "ndisc_notify": 0, "mldv1_unsolicited_report_interval": 10000, "mldv2_unsolicited_report_interval": 1000, "suppress_frag_ndisc": 1, "accept_ra_from_local": 0, "use_optimistic": 0, "accept_ra_mtu": 1, "stable_secret": 0, "use_oif_addrs_only": 0, "accept_ra_min_hop_limit": 1, "ignore_routes_with_linkdown": 0, "drop_unicast_in_l2_multicast": 0, "drop_unsolicited_na": 0, "keep_addr_on_down": 0, "rtr_solicit_max_interval": 3600000, "seg6_enabled": 0, "seg6_require_hmac": 0, "enhanced_dad": 1, "addr_gen_mode": 0, "disable_policy": 0, "accept_ra_rt_info_min_plen": 0, "ndisc_tclass": 0, "rpl_seg_enabled": 0 } } }, "proto_down": false, "group": 0, "ifname": "lo" } Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-09-15rtnl: allow reply nla payloads to be smaller than headsizeJo-Philipp Wich
Some netlink replies contain attributes with embeded payloads whose length depends on the kernel version, e.g. IFLA_INET_CONF and IFLA_INET6_CONF. Deal with such cases by allowing the payloads to be shorter than the expected size and by skipping struct members which would lead to out-of-bounds accesses. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-09-15lib: introduce Linux route netlink bindingJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-07-28fs: fix chown() and rename() error return valuesJo-Philipp Wich
Fixes: dfb7379 ("fs: implement chmod(), chown(), rename() and glob() functions") Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-07-11treewide: harmonize function namingJo-Philipp Wich
- Ensure that most functions follow the subject_verb naming schema - Move type related function from value.c to types.c - Rename value.c to vallist.c Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-07-11treewide: move header files into dedicated directoryJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-07-11treewide: consolidate typedef namingJo-Philipp Wich
Ensure that all custom typedef and vector declaration type names end with a "_t" suffix. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-07-11lib: rename uc_add_proto_functions() to uc_add_functions()Jo-Philipp Wich
The naming is an artifact from before the introduction of the new type system. In the current code, there is nothing special about prototypes, they're simple object values. Also introduce a new singular uc_add_function() convenience macro while we're at it. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-07-11treewide: move ressource type registry into vm instanceJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-05-15fs: implement chmod(), chown(), rename() and glob() functionsJo-Philipp Wich
- The chmod() function expects a path string as first and an integer mode value as second argument. - The chown() function takes a path string as first argument, and either a string, an integer or null as second user and third group argument respectively. If either user or group are given as string, they're resolved to an uid/gid using getpwnam()/getgrnam() internally. If either lookup fails, the ownership change is not performed. If either user or group are null or -1, they're left unchanged. - The rename() function takes two path strings, the old path being the first argument and the new path the second one. - The glob() function takes an arbitrary number of glob patterns and resolves matching files for each one. In case of multiple patterns, no efforts are made to remove duplicates or to globally sort the combined match list. The list of matches for each individual pattern is sorted. Returns an array containing all matched file paths. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-04-27treewide: ISO C / pedantic complianceJo-Philipp Wich
- Shuffle typedefs to avoid need for non-compliant forward declarations - Fix non-compliant empty struct initializers - Remove use of braced expressions - Remove use of anonymous unions - Avoid `void *` pointer arithmetic - Fix several warnings reported by gcc -pedantic mode and clang 11 compilation Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-04-25treewide: rework internal data type systemJo-Philipp Wich
Instead of relying on json_object values internally, use custom types to represent the different ucode value types which brings a number of advantages compared to the previous approach: - Due to the use of tagged pointers, small integer, string and bool values can be stored directly in the pointer addresses, vastly reducing required heap memory - Ability to create circular data structures such as `let o; o = { test: o };` - Ability to register custom `tostring()` function through prototypes - Initial mark/sweep GC implementation to tear down circular object graphs on VM deinit The change also paves the way for possible future extensions such as constant variables and meta methods for custom ressource types. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-04-24treewide: fix issues reported by clang code analyzerJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-03-08fs: make error function available on directory, process and file handlesJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-03-08ubus: make error function available on the connection instanceJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2021-03-08uci: fix potential invalid memory access in error()Jo-Philipp Wich
Check the numerical error code index before attempting to look it up in the error string array. Also make error function available on the cursor instance as well. Signed-off-by: Jo-Philipp Wich <jo@mein.io>