summaryrefslogtreecommitdiffhomepage
path: root/lib
AgeCommit message (Collapse)Author
2022-09-29uloop: task: gracefully handle absent output callbackJo-Philipp Wich
Both input and output callbacks for uloop tasks are optional, but the low level io callback implementation did not properly deal with an absent ucode output callback, triggering an exception in managed code due to invoking a null value as function. Fix this issue by checking for the availability of a callable output function and simply discarding the received task message otherwise. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-09-21ubus: hold reference to underlying connection until deferred is concludedJo-Philipp Wich
Prevent GC'ing (and thus tearing down) an active ubus connection as long as there's still unfinished deferred request contexts alive. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-09-09ubus: support obtaining numeric error codeJo-Philipp Wich
Some ubus users require access to the original ubus error status returned by various operations for fine grained error handling. Extend the error() function with an optional boolean argument which causes the function to return the numeric error code instead of a preformatted message when invoked. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-09-09ubus: add toplevel constants for ubus status codesJo-Philipp Wich
Add constants for ubus status codes to the toplevel module scope in order to avoid the need for magic values in the code. Suggested-by: Felix Fietkau <nbd@nbd.name> Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-09-09ubus: allow object method call handlers to return a numeric status codeJo-Philipp Wich
The implicit return style for sending ubus method replies currently always emits an UBUS_STATUS_NO_DATA code in case neither req.reply() was called, nor a deferred or object were returned by the handler function. This slightly complicates the implementation of handlers that do not wish to send reply data but simply acknowledge the request with an UBUS_STATUS_OK code. In order to simplify this use case, allow handlers to override the default status by treating integer return values as ubus error codes. After this change, the following handler: function (request) { /* do some work */ request.reply(null, 0); } ... can be rewritten as: function (request) { /* do some work */ return 0; } Suggested-by: Felix Fietkau <nbd@nbd.name> Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-09-05fs: add optional third permission argument to fs.open()Jo-Philipp Wich
Rework the `fs.open()` implementation to accept an optional third file permission argument which is applied to newly created files. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-08-24ubus: fix GCC strncpy() truncation warningJo-Philipp Wich
When building with gcc-10 and -O2, the following warning in ubus.c is triggered during the compilation: In function ‘uc_ubus_object_register’, inlined from ‘uc_ubus_publish’ at .../ubus.c:1521:10: .../ubus.c:1464:14: error: ‘strncpy’ output truncated before terminating nul copying as many bytes from a string as its length [-Werror=stringop-truncation] 1464 | obj->name = strncpy(onptr, ubus_object_name, namelen); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../ubus.c: In function ‘uc_ubus_publish’: .../ubus.c:1447:12: note: length computed here 1447 | namelen = strlen(ubus_object_name); | ^~~~~~~~~~~~~~~~~~~~~~~~ cc1: all warnings being treated as errors Solve this issue by using memcpy() instead. We already take care of allocating a zeroed, strlen() + 1 sized destination buffer so loosing the `\0` byte of the source string is perfectly fine. Fixes: #100 Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-07-03rtnl: fix parsing/creation of IFLA_AF_SPEC RTA for the AF_BRIDGE familyJo-Philipp Wich
Some pecularities in the encoding of the IFLA_AF_SPEC attribute make it unsuitable for table driven parsing/generation. To solve this issue, introduce specific datatype handling for IFLA_AF_SPEC and parse/generate the RTA depending on the address family of the containing netlink message. Also add some missing constants while we're at it. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-29Merge pull request #91 from jow-/ubus-propagate-exceptionsJo-Philipp Wich
ubus: end uloop on exceptions in managed code
2022-06-29uloop: end uloop on exceptions in managed codeJo-Philipp Wich
Instead of silently continuing, end the uloop when encountering exceptions in ucode callbacks to let those exceptions propagate to the host program. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-29ubus: end uloop on exceptions in managed codeJo-Philipp Wich
Instead of silently continuing, end the uloop when encountering exceptions in ucode callbacks to let those exceptions propagate to the host program. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-28rtnl: expose IFLA_STATS64 contentsJo-Philipp Wich
Decode IFLA_STATS64 attribute and make contained counters available to ucode. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-28rtnl: expose ifinfomsg.ifi_change memberJo-Philipp Wich
For certain operations, such as bringing up interfaces, it is required to initialize the ifi_change mask in the ifinfomsg struct. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-28Merge pull request #86 from jow-/rtnl-fix-strict-chkJo-Philipp Wich
rtnl: update NETLINK_GET_STRICT_CHK socket flag with every request
2022-06-27rtnl: update NETLINK_GET_STRICT_CHK socket flag with every requestJo-Philipp Wich
So far the NETLINK_GET_STRICT_CHK socket flag was only set on the implicit socket creation performed during the first request and ignored for subsequent ones which made it impossible to perform only some requests with enabled strict checking. Modify the logic to check the flag state for every request and change it if needed. This allows performing both strict and non-strict requests over the same connection. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-27nl80211: fix NL80211_SURVEY_INFO_NOISE datatypeJo-Philipp Wich
Report the noise value as signed integer to calling ucode. Reported-by: John Crispin <john@phrozen.org> Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-17nl80211: recognize further NL80211_STA_INFO_* NLAsJo-Philipp Wich
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-08struct: add optional offset argument to `unpack()`Jo-Philipp Wich
Extend the `unpack()` function to take an optional, second offset parameter which is useful to skip an initial portion of the input data without having to encode pad bytes into the format string. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-08Merge pull request #83 from jow-/rtnl-fix-linkinfo-segfaultJo-Philipp Wich
rtnl: fix segmentation fault on parsing linkinfo RTA without data
2022-06-08Merge pull request #82 from jow-/rtnl-zero-msghdrJo-Philipp Wich
rtnl: zero request message headers
2022-06-08Merge pull request #81 from jow-/rtnl-fix-ack-handlingJo-Philipp Wich
rtnl: fix premature netlink reply receive abort
2022-06-08rtnl: fix segmentation fault on parsing linkinfo RTA without dataJo-Philipp Wich
Some link types, such as veth, yield an IFLA_LINKINFO nla without an embedded IFLA_INFO_DATA / INFLA_INFO_SLAVE_DATA nla which causes the nla converter to dereference a NULL nla pointer. Properly deal with such cases and check for the existence of the child nla before attempting to parse it. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-08Merge pull request #80 from jow-/rtnl-fix-leftover-bytesJo-Philipp Wich
rtnl: avoid stray "netlink: %d bytes leftover after parsing attributes."
2022-06-08rtnl: zero request message headersJo-Philipp Wich
For route netlink request messages having a header struct, uc_nl_request() invokes nlmsg_reserve() to reserve room for the struct data but the nlmsg_reserve() function only zeroes additional alignment bytes, not the actual reserved buffer space. Extend the existing logic to explicitly zero out the reserved header space in order to avoid sending uninitialized struct member values to the kernel. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-08rtnl: 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. This fix is basically the same as the one applied to the nl80211 module in 54ef6c0 ("nl80211: fix premature netlink reply receive abort"). Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-04rtnl: avoid stray "netlink: %d bytes leftover after parsing attributes."Jo-Philipp Wich
Some nested RTAs such as IFLA_INET_CONF do not contain actual sub-RTAs but just an array of integers. Avoid calling a no-op `nla_parse()` for such attributes to suppress the non-harmful leftover bytes warning emitted by libnl. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2022-06-04struct: fix packing `*` format after other repeated formatsJo-Philipp Wich
When packing a format such as `!6C*`, the `*` format was not included into the result buffer due to improper tracking of the function argument offset. Solve this issue by taking field repetitions into account when tracking the argument offset while calculating the size of dynamic arguments. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
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>