- Apr 28, 2020
-
-
Vladislav Shpilevoy authored
Closes #4641 @TarantoolBot document Title: box_return_mp() public C function Stored C functions could return a result only via `box_return_tuple()` function. That made users create a tuple every time they wanted to return something from a C function. Now public C API offers another way to return - `box_return_mp()`. It allows to return arbitrary MessagePack, not wrapped into a tuple object. This is simpler to use for small results like a number, boolean, or a short string. Besides, `box_return_mp()` is much faster than `box_return_tuple()`, especially for small MessagePack. Note, that it is faster only if an alternative is to create a tuple by yourself. If an already existing tuple was obtained from an iterator, and you want to return it, then of course it is faster to return via `box_return_tuple()`, than via extraction of tuple data, and calling `box_return_mp()`. Here is the function declaration from module.h: ```C /** * Return MessagePack from a stored C procedure. The MessagePack * is copied, so it is safe to free/reuse the passed arguments * after the call. * MessagePack is not validated, for the sake of speed. It is * expected to be a single encoded object. An attempt to encode * and return multiple objects without wrapping them into an * MP_ARRAY or MP_MAP is undefined behaviour. * * \param ctx An opaque structure passed to the stored C procedure * by Tarantool. * \param mp Begin of MessagePack. * \param mp_end End of MessagePack. * \retval -1 Error. * \retval 0 Success. */ API_EXPORT int box_return_mp(box_function_ctx_t *ctx, const char *mp, const char *mp_end); ```
-
Vladislav Shpilevoy authored
Port_c is a new descendant of struct port. It is used now for public C functions to store their result. Currently they can return only a tuple, but it will change soon, they will be able to return arbitrary MessagePack. Port_tuple is not removed, because still is used for box_select(), for functional indexes, and in SQL as a base for port_sql. Although that may be changed later. Functional indexes really need only a single MessagePack object from their function. While box_select() working via port_tuple or port_c didn't show any significant difference during micro benchmarks. Part of #4641
-
- Apr 27, 2020
-
-
Serge Petrenko authored
Since the introduction of transaction boundaries in replication protocol, appliers follow replicaset.applier.vclock to the lsn of the first row in an arrived batch. This is enough and doesn't lead to errors when replicating from other instances, respecting transaction boundaries (instances with version 2.1.2 and up). However, if there's a 1.10 instance in 2.1.2+ cluster, it sends every single tx row as a separate transaction, breaking the comparison with replicaset.applier.vclock and making the applier apply part of the changes, it has already applied when processing a full transaction coming from another 2.x instance. Such behaviour leads to ER_TUPLE_FOUND errors in the scenario described above. In order to guard from such cases, follow replicaset.applier.vclock to the lsn of the last row in tx. Closes #4924 Reviewed-by:
Cyrill Gorcunov <gorcunov@gmail.com>
-
Roman Khabibov authored
Function implementing comparison during VDBE sorting routine (sqlVdbeCompareMsgpack) did not account values of boolean type in some cases. Let's fix it so that booleans always precede numbers if they are sorted in ascending order. Closes #4697
-
- Apr 24, 2020
-
-
Cyrill Gorcunov authored
The notification of wait variable shall be done under a bound mutex locked. Otherwise the results are not guaranteed (see pthread manuals). Thus when we create a new endpoint via cbus_endpoint_create and there is an other thread which sleeps inside cpipe_create we should notify the sleeper under cbus.mutex. Fixes #4806 Reported-by:
Alexander Turenko <alexander.turenko@tarantool.org> Signed-off-by:
Cyrill Gorcunov <gorcunov@gmail.com>
-
Leonid Vasiliev authored
The cbus hang test uses glibc pthread mutex implementation details. The reason why mutex implementation details is used: "For the bug reproducing the canceled thread must be canceled during processing cpipe_flush_cb. We need to synchronize the main thread and the canceled worker thread for that. So, thread synchronization has been realized by means of endpoint's mutex internal field(__data.__lock)." Therefore, it should not compile in case of using another library.
-
- Apr 21, 2020
-
-
Olga Arkhangelskaia authored
While refactoring of say module in commit 5db765a7 (say: fix non-informative error messages for log cfg) format of syslog was broken. Closes #4785
-
- Apr 20, 2020
-
-
Kirill Yukhin authored
-
Nikita Pettik authored
In case accessing previous error doesn't come alongside with incrementing its reference counter, it may lead to use-after-free bug. Consider following scenario: _, err = foo() -- foo() returns diagnostic error stack preve = err.prev -- err.prev ref. counter == 1 err:set_prev(nil) -- err.prev ref. counter == 0 so err.prev is destroyed preve -- accessing already freed memory To avoid that let's increment reference counter of .prev member while calling error.prev and set corresponding gc finalizer (error_unref()). Closes #4887
-
Nikita Pettik authored
There's no overflow check while incrementing error's reference counter in error_ref(). Meanwhile, stubborn users still may achieve overflow: each call of box.error.last() increments reference counter of error residing in diagnostic area. As a result, 2^32 calls of box.error.last() in a row will lead to counter overflow ergo - to unpredictable results. Let's fix it and introduce dummy check in error_ref().
-
Kirill Yukhin authored
Fixa clash in struct names detected by LTO.
-
Kirill Yukhin authored
Make older compiler happy w.r.t. initialization of locals.
-
Igor Munkin authored
Fixes the regression from 335f80a0 ('test: adjust luajit-tap testing machinery').
-
Leonid Vasiliev authored
Fix possible overlap of IPROTO_ERROR by IPROTO_ERROR_24. This was possible because messages are transmitted in a map and an order is not defined. IPROTO_ERROR_24 could be parsed after the IPROTO_ERROR, and could throw it away.
-
Vladislav Shpilevoy authored
IPROTO_ERROR in fact is not an error. It is an error message. Secondly, this key is deprecated in favor of IPROTO_ERROR_STACK, which contains all attributes of the whole error stack. It uses MP_ERROR MessagePack extenstion for that. So IPROTO_ERROR is renamed to IPROTO_ERROR_24 (similar to how old call was renamed to IPROTO_CALL_16). IPROTO_ERROR_STACK becomes new IPROTO_ERROR. Follow up #4398
-
Vladislav Shpilevoy authored
After error objects marshaling was implemented in #4398, there were essentially 2 versions of the marshaling - when an error is sent inside response body, and when it is thrown and is encoded in iproto fields IPROTO_ERROR and IPROTO_ERROR_STACK. That is not really useful to have 2 implementation of the same feature. This commit drops the old iproto error encoding (its IPROTO_ERROR_STACK part), and makes it reuse the common error encoder. Note, the encoder skips MP_EXT header. This is because * The header is not needed - error is encoded as a value of IPROTO_ERROR_STACK key, so it is known this is an error. MP_EXT is needed only when type is unknown on decoding side in advance; * Old clients may not expect MP_EXT in iproto fields. That is the case of netbox connector, at least. Follow up #4398 @TarantoolBot document Title: Stacked diagnostics binary protocol Stacked diagnostics is described in details in https://github.com/tarantool/doc/issues/1224. This commit changes nothing except binary protocol. The old protocol should not be documented anywhere. `IPROTO_ERROR_STACK` is still 0x52, but format of its value is different now. It looks exactly like `MP_ERROR` object, without `MP_EXT` header. ``` IPROTO_ERROR_STACK: <MP_MAP> { MP_ERROR_STACK: <MP_ARRAY> [ <MP_MAP> { ... <all the other fields of MP_ERROR> ... }, ... ] } ``` It is easy to see, that key `IPROTO_ERROR_STACK` is called 'stack', and `MP_ERROR_STACK` is also 'stack'. So it may be good to rename the former key in the documentation. For example, the old `IPROTO_ERROR` can be renamed to `IPROTO_ERROR_24` and `IPROTO_ERROR_STACK` can be renamed to just `IPROTO_ERROR`.
-
Vladislav Shpilevoy authored
C struct error objects can be created directly only in C. C-side increments their reference counter when pushes to the Lua stack. It is not going to be so convenient soon. error_unpack() function will be used in netbox to decode error object via Lua FFI. Such error object will have 0 refs and no Lua GC callback established. Because it won't be pushed on Lua stack natually, from Lua C. To make such errors alive their reference counter will be incremented and error_unref() will be set as GC callback. Follow up for #4398
-
Leonid Vasiliev authored
Co-authored-by:
Vladislav <Shpilevoy<v.shpilevoy@tarantool.org> Closes #4398 @TarantoolBot document Title: Error objects encoding in MessagePack Until now an error sent over IProto, or serialized into MessagePack was turned into a string consisting of the error message. As a result, all other error object attributes were lost, including type of the object. On client side seeing a string it was not possible to tell whether the string is a real string, or it is a serialized error. To deal with that the error objects encoding is reworked from the scratch. Now, when session setting `error_marshaling_enabled` is true, all fibers of that session will encode error objects as a new MP_EXT type - MP_ERROR (0x03). ``` +--------+----------+========+ | MP_EXT | MP_ERROR | MP_MAP | +--------+----------+========+ MP_ERROR: <MP_MAP> { MP_ERROR_STACK: <MP_ARRAY> [ <MP_MAP> { MP_ERROR_TYPE: <MP_STR>, MP_ERROR_FILE: <MP_STR>, MP_ERROR_LINE: <MP_UINT>, MP_ERROR_MESSAGE: <MP_STR>, MP_ERROR_ERRNO: <MP_UINT>, MP_ERROR_CODE: <MP_UINT>, MP_ERROR_FIELDS: <MP_MAP> { <MP_STR>: ..., <MP_STR>: ..., ... }, ... }, ... ] } ``` On the top level there is a single key: `MP_ERROR_STACK = 0x00`. More keys can be added in future, and a client should ignore all unknown keys to keep compatibility with new versions. Every error in the stack is a map with the following keys: * `MP_ERROR_TYPE = 0x00` - error type. This is what is visible in `<error_object>.base_type` field; * `MP_ERROR_FILE = 0x01` - file name from `<error_object>.trace`; * `MP_ERROR_LINE = 0x02` - line from `<error_object>.trace`; * `MP_ERROR_MESSAGE = 0x03` - error message from `<error_object>.message`; * `MP_ERROR_ERRNO = 0x04` - errno saved at the moment of the error creation. Visible in `<error_object>.errno`; * `MP_ERROR_CODE = 0x05` - error code. Visible in `<error_object>.code` and in C function `box_error_code()`. * `MP_ERROR_FIELDS = 0x06` - additional fields depending on error type. For example, AccessDenied error type stores here fields `access_type`, `object_type`, `object_name`. Connector's code should ignore unknown keys met here, and be ready, that for some existing errors new fields can be added, old can be dropped.
-
Vladislav Shpilevoy authored
Lua C module 'msgpack' supports registration of custom extension decoders for MP_EXT values. That is needed to make 'msgpack' not depending on any modules which use it. So far the only box-related extension were tuples - struct tuple cdata needed to be encoded as an array. That is going to change in next commits, where struct error cdata appears, also depending on box. So the decoder can't be located in src/box/lua/tuple.c. It is moved to a more common place - src/box/lua/init.c. Needed for #4398
-
Leonid Vasiliev authored
We want to have a transparent marshalling through net.box for errors. To do this, we need to recreate the error on the client side with the same parameters as on the server. For convenience, we update AccessDeniedError constructor which has pointers to static strings and add the XlogGapError constructor that does not require vclock. Needed for #4398
-
Leonid Vasiliev authored
Errors are encoded as a string when serialized to MessagePack to be sent over IProto or when just saved into a buffer via Lua modules msgpackffi and msgpack. That is not very useful on client-side, because most of the error metadata is lost: code, type, trace - everything except the message. Next commits are going to dedicate a new MP_EXT type to error objects so as everything could be encoded, and on client side it would be possible to restore types. But this is a breaking change in case some users use old connectors when work with newer Tarantool instances. So to smooth the upgrade there is a new session setting - 'error_marshaling_enabled'. By default it is false. When it is true, all fibers of the given session will serialize error objects as MP_EXT. Co-authored-by:
Vladislav <Shpilevoy<v.shpilevoy@tarantool.org> Needed for #4398
-
Vladislav Shpilevoy authored
Session settings are stored in a monolithic array. Submodules can define a range of settings in there. For example, SQL. It occupies settings from 0 to 8. There is a second array of only SQL settings in build.c, of the same size, and it uses the same indexes. But if something will be added before SQL settings, so as they won't start from 0, it will break the equal indexes assumption. SQL should normalize all setting identifiers by SESSION_SETTING_SQL_BEGIN.
-
Leonid Vasiliev authored
Co-authored-by:
Vladislav <Shpilevoy<v.shpilevoy@tarantool.org> Part of #4398 @TarantoolBot document Title: Custom error types for Lua errors Errors can be created in 2 ways: `box.error.new()` and `box.error()`. Both used to take either `code, reason, <reason string args>` or `{code = code, reason = reason, ...}` arguments. Now in the first option instead of code a user can specify a string as its own error type. In the second option a user can specify both code and type. For example: ```Lua box.error('MyErrorType', 'Message') box.error({type = 'MyErrorType', code = 1024, reason = 'Message'}) ``` Or no-throw version: ```Lua box.error.new('MyErrorType', 'Message') box.error.new({type = 'MyErrorType', code = 1024, reason = 'Message'}) ``` When a custom type is specified, it is shown in `err.type` attribute. When it is not specified, `err.type` shows one of built-in errors such as 'ClientError', 'OurOfMemory', etc. Name length limit on the custom type is 63 bytes. All what is longer is truncated. Original error type can be checked using `err.base_type` member, although normally it should not be used. For user-defined types base type is 'CustomError'. For example: ``` tarantool> e = box.error.new({type = 'MyErrorType', code = 1024, reason = 'Message'}) --- ... tarantool> e:unpack() --- - code: 1024 trace: - file: '[string "e = box.error.new({type = ''MyErrorType'', code..."]' line: 1 type: MyErrorType custom_type: MyErrorType message: Message base_type: CustomError ... ```
-
Alexander Turenko authored
As stated in the 'OS/X AND DARWIN BUGS' section of the libev documentation [1], kqueue() and poll() have known problems on Mac OS, so the library uses select() on Mac OS (it is the build time default). The library however uses the trick to overcome 1024 fds limit: libev sets the undocumented macro _DARWIN_UNLIMITED_SELECT, which enables linking against select() implementation without the limit. The magic macro stops working at some point around Mac OS 10.10 (see [2]), because it was defined after <sys/time.h> inclusion. For recent Mac OS versions the macro has effect only when it is defined before <sys/time.h> inclusion. The macro definition was [moved][3] in libev 4.25. Excerpt from the changelog [4]: | 4.25 Fri Dec 21 07:49:20 CET 2018 | <...> | - move the darwin select workaround higher in ev.c, as newer versions of | darwin managed to break their broken select even more. More proper fix would be updating of libev to a newer version, however I would postpone it until a moment when we'll have a time to properly test everything with a new version of the library. [1]: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#OS_X_AND_DARWIN_BUGS [2]: http://lists.schmorp.de/pipermail/libev/2018q2/002788.html [3]: http://cvs.schmorp.de/libev/ev.c?r1=1.482&r2=1.483 [4]: http://cvs.schmorp.de/libev/Changes?view=markup Fixes #3867 Fixes #4673 Investigated-by:
Maria Khaydich <maria.khaydich@tarantool.org> Co-authored-by:
Maria Khaydich <maria.khaydich@tarantool.org>
-
Alexander Turenko authored
Fixes #4031 Co-developed-by:
Cyrill Gorcunov <gorcunov@gmail.com> Co-developed-by:
Igor Munkin <imun@tarantool.org> Acked-by:
Cyrill Gorcunov <gorcunov@gmail.com> Reviewed-by:
Igor Munkin <imun@tarantool.org> @TarantoolBot document Title: popen module ``` Overview ======== Tarantool supports execution of external programs similarly to well known Python's `subprocess` or Ruby's `Open3`. Note though the `popen` module does not match one to one to the helpers these languages provide and provides only basic functions. The popen object creation is implemented via `vfork()` system call which means the caller thread is blocked until execution of a child process begins. Module functions ================ The `popen` module provides two functions to create that named popen object: `popen.shell` which is the similar to libc `popen` syscall and `popen.new` to create popen object with more specific options. `popen.shell(command[, mode]) -> handle, err` --------------------------------------------- Execute a shell command. @param command a command to run, mandatory @param mode communication mode, optional 'w' to use ph:write() 'r' to use ph:read() 'R' to use ph:read({stderr = true}) nil inherit parent's std* file descriptors Several mode characters can be set together: 'rw', 'rRw', etc. This function is just shortcut for popen.new({command}, opts) with opts.{shell,setsid,group_signal} set to `true` and and opts.{stdin,stdout,stderr} set based on `mode` parameter. All std* streams are inherited from parent by default if it is not changed using mode: 'r' for stdout, 'R' for stderr, 'w' for stdin. Raise an error on incorrect parameters: - IllegalParams: incorrect type or value of a parameter. Return a popen handle on success. Return `nil, err` on a failure. @see popen.new() for possible reasons. Example: | local popen = require('popen') | | -- Run the program and save its handle. | local ph = popen.shell('date', 'r') | | -- Read program's output, strip trailing newline. | local date = ph:read():rstrip() | | -- Free resources. The process is killed (but 'date' | -- exits itself anyway). | ph:close() | | print(date) Execute 'sh -c date' command, read the output and close the popen object. Unix defines a text file as a sequence of lines, each ends with the newline symbol. The same convention is usually applied for a text output of a command (so when it is redirected to a file, the file will be correct). However internally an application usually operates on strings, which are NOT newline terminated (e.g. literals for error messages). The newline is usually added right before a string is written to the outside world (stdout, console or log). :rstrip() in the example above is shown for this sake. `popen.new(argv[, opts]) -> handle, err` ---------------------------------------- Execute a child program in a new process. @param argv an array of a program to run with command line options, mandatory; absolute path to the program is required when @a opts.shell is false (default) @param opts table of options @param opts.stdin action on STDIN_FILENO @param opts.stdout action on STDOUT_FILENO @param opts.stderr action on STDERR_FILENO File descriptor actions: popen.opts.INHERIT (== 'inherit') [default] inherit the fd from the parent popen.opts.DEVNULL (== 'devnull') open /dev/null on the fd popen.opts.CLOSE (== 'close') close the fd popen.opts.PIPE (== 'pipe') feed data from/to the fd to parent using a pipe @param opts.env a table of environment variables to be used inside a process; key is a variable name, value is a variable value. - when is not set then the current environment is inherited; - if set to an empty table then the environment will be dropped - if set then the environment will be replaced @param opts.shell (boolean, default: false) true run a child process via 'sh -c "${opts.argv}"' false call the executable directly @param opts.setsid (boolean, default: false) true run the program in a new session false run the program in the tarantool instance's session and process group @param opts.close_fds (boolean, default: true) true close all inherited fds from a parent false don't do that @param opts.restore_signals (boolean, default: true) true reset all signal actions modified in parent's process false inherit changed actions @param opts.group_signal (boolean, default: false) true send signal to a child process group (only when opts.setsid is enabled) false send signal to a child process only @param opts.keep_child (boolean, default: false) true don't send SIGKILL to a child process at freeing (by :close() or Lua GC) false send SIGKILL to a child process (or a process group if opts.group_signal is enabled) at :close() or collecting of the handle by Lua GC The returned handle provides :close() method to explicitly release all occupied resources (including the child process itself if @a opts.keep_child is not set). However if the method is not called for a handle during its lifetime, the same freeing actions will be triggered by Lua GC. It is recommended to use opts.setsid + opts.group_signal if a child process may spawn its own childs and they all should be killed together. Note: A signal will not be sent if the child process is already dead: otherwise we might kill another process that occupies the same PID later. This means that if the child process dies before its own childs, the function will not send a signal to the process group even when opts.setsid and opts.group_signal are set. Use os.environ() to pass copy of current environment with several replacements (see example 2 below). Raise an error on incorrect parameters: - IllegalParams: incorrect type or value of a parameter. - IllegalParams: group signal is set, while setsid is not. Return a popen handle on success. Return `nil, err` on a failure. Possible reasons: - SystemError: dup(), fcntl(), pipe(), vfork() or close() fails in the parent process. - SystemError: (temporary restriction) the parent process has closed stdin, stdout or stderr. - OutOfMemory: unable to allocate the handle or a temporary buffer. Example 1: | local popen = require('popen') | | local ph = popen.new({'/bin/date'}, { | stdout = popen.opts.PIPE, | }) | local date = ph:read():rstrip() | ph:close() | print(date) -- Thu 16 Apr 2020 01:40:56 AM MSK Execute 'date' command, read the result and close the popen object. Example 2: | local popen = require('popen') | | local env = os.environ() | env['FOO'] = 'bar' | | local ph = popen.new({'echo "${FOO}"'}, { | stdout = popen.opts.PIPE, | shell = true, | env = env, | }) | local res = ph:read():rstrip() | ph:close() | print(res) -- bar It is quite similar to the previous one, but sets the environment variable and uses shell builtin 'echo' to show it. Example 3: | local popen = require('popen') | | local ph = popen.new({'echo hello >&2'}, { -- !! | stderr = popen.opts.PIPE, -- !! | shell = true, | }) | local res = ph:read({stderr = true}):rstrip() | ph:close() | print(res) -- hello This example demonstrates how to capture child's stderr. Example 4: | local function call_jq(input, filter) | -- Start jq process, connect to stdin, stdout and stderr. | local jq_argv = {'/usr/bin/jq', '-M', '--unbuffered', filter} | local ph, err = popen.new(jq_argv, { | stdin = popen.opts.PIPE, | stdout = popen.opts.PIPE, | stderr = popen.opts.PIPE, | }) | if ph == nil then return nil, err end | | -- Write input data to child's stdin and send EOF. | local ok, err = ph:write(input) | if not ok then return nil, err end | ph:shutdown({stdin = true}) | | -- Read everything until EOF. | local chunks = {} | while true do | local chunk, err = ph:read() | if chunk == nil then | ph:close() | return nil, err | end | if chunk == '' then break end -- EOF | table.insert(chunks, chunk) | end | | -- Read diagnostics from stderr if any. | local err = ph:read({stderr = true}) | if err ~= '' then | ph:close() | return nil, err | end | | -- Glue all chunks, strip trailing newline. | return table.concat(chunks):rstrip() | end Demonstrates how to run a stream program (like `grep`, `sed` and so), write to its stdin and read from its stdout. The example assumes that input data are small enough to fit a pipe buffer (typically 64 KiB, but depends on a platform and its configuration). It will stuck in :write() for large data. How to handle this case: call :read() in a loop in another fiber (start it before a first :write()). If a process writes large text to stderr, it may fill out stderr pipe buffer and stuck in write(2, ...). So we need to read stderr in a separate fiber to handle this case. Handle methods ============== `popen_handle:read([opts]) -> str, err` --------------------------------------- Read data from a child peer. @param handle handle of a child process @param opts an options table @param opts.stdout whether to read from stdout, boolean (default: true) @param opts.stderr whether to read from stderr, boolean (default: false) @param opts.timeout time quota in seconds (default: 100 years) Read data from stdout or stderr streams with @a timeout. By default it reads from stdout. Set @a opts.stderr to `true` to read from stderr. It is not possible to read from stdout and stderr both in one call. Set either @a opts.stdout or @a opts.stderr. Raise an error on incorrect parameters or when the fiber is cancelled: - IllegalParams: incorrect type or value of a parameter. - IllegalParams: called on a closed handle. - IllegalParams: opts.stdout and opts.stderr are set both - IllegalParams: a requested IO operation is not supported by the handle (stdout / stderr is not piped). - IllegalParams: attempt to operate on a closed file descriptor. - FiberIsCancelled: cancelled by an outside code. Return a string on success, an empty string at EOF. Return `nil, err` on a failure. Possible reasons: - SocketError: an IO error occurs at read(). - TimedOut: @a timeout quota is exceeded. - OutOfMemory: no memory space for a buffer to read into. - LuajitError: ("not enough memory"): no memory space for the Lua string. `popen_handle:write(str[, opts]) -> str, err` --------------------------------------------- Write data to a child peer. @param handle a handle of a child process @param str a string to write @param opts table of options @param opts.timeout time quota in seconds (default: 100 years) Write string @a str to stdin stream of a child process. The function may yield forever if a child process does not read data from stdin and a pipe buffer becomes full. Size of this buffer depends on a platform. Use @a opts.timeout when unsure. When @a opts.timeout is not set, the function blocks (yields the fiber) until all data is written or an error happened. Raise an error on incorrect parameters or when the fiber is cancelled: - IllegalParams: incorrect type or value of a parameter. - IllegalParams: called on a closed handle. - IllegalParams: string length is greater then SSIZE_MAX. - IllegalParams: a requested IO operation is not supported by the handle (stdin is not piped). - IllegalParams: attempt to operate on a closed file descriptor. - FiberIsCancelled: cancelled by an outside code. Return `true` on success. Return `nil, err` on a failure. Possible reasons: - SocketError: an IO error occurs at write(). - TimedOut: @a timeout quota is exceeded. `popen_handle:shutdown(opts) -> true` ------------------------------------------ Close parent's ends of std* fds. @param handle handle of a child process @param opts an options table @param opts.stdin close parent's end of stdin, boolean @param opts.stdout close parent's end of stdout, boolean @param opts.stderr close parent's end of stderr, boolean The main reason to use this function is to send EOF to child's stdin. However parent's end of stdout / stderr may be closed too. The function does not fail on already closed fds (idempotence). However it fails on attempt to close the end of a pipe that was never exist. In other words, only those std* options that were set to popen.opts.PIPE at a handle creation may be used here (for popen.shell: 'r' corresponds to stdout, 'R' to stderr and 'w' to stdin). The function does not close any fds on a failure: either all requested fds are closed or neither of them. Example: | local popen = require('popen') | | local ph = popen.shell('sed s/foo/bar/', 'rw') | ph:write('lorem foo ipsum') | ph:shutdown({stdin = true}) | local res = ph:read() | ph:close() | print(res) -- lorem bar ipsum Raise an error on incorrect parameters: - IllegalParams: an incorrect handle parameter. - IllegalParams: called on a closed handle. - IllegalParams: neither stdin, stdout nor stderr is choosen. - IllegalParams: a requested IO operation is not supported by the handle (one of std* is not piped). Return `true` on success. `popen_handle:terminate() -> ok, err` ------------------------------------- Send SIGTERM signal to a child process. @param handle a handle carries child process to terminate The function only sends SIGTERM signal and does NOT free any resources (popen handle memory and file descriptors). @see popen_handle:signal() for errors and return values. `popen_handle:kill() -> ok, err` -------------------------------- Send SIGKILL signal to a child process. @param handle a handle carries child process to kill The function only sends SIGKILL signal and does NOT free any resources (popen handle memory and file descriptors). @see popen_handle:signal() for errors and return values. `popen_handle:signal(signo) -> ok, err` --------------------------------------- Send signal to a child process. @param handle a handle carries child process to be signaled @param signo signal number to send When opts.setsid and opts.group_signal are set on the handle the signal is sent to the process group rather than to the process. @see popen.new() for details about group signaling. Note: The module offers popen.signal.SIG* constants, because some signals have different numbers on different platforms. Raise an error on incorrect parameters: - IllegalParams: an incorrect handle parameter. - IllegalParams: called on a closed handle. Return `true` if signal is sent. Return `nil, err` on a failure. Possible reasons: - SystemError: a process does not exists anymore Aside of a non-exist process it is also returned for a zombie process or when all processes in a group are zombies (but see note re Mac OS below). - SystemError: invalid signal number - SystemError: no permission to send a signal to a process or a process group It is returned on Mac OS when a signal is sent to a process group, where a group leader is zombie (or when all processes in it are zombies, don't sure). Whether it may appear due to other reasons is unclear. `popen_handle:info() -> res` ---------------------------- Return information about popen handle. @param handle a handle of a child process Raise an error on incorrect parameters: - IllegalParams: an incorrect handle parameter. - IllegalParams: called on a closed handle. Return information about the handle in the following format: { pid = <number> or <nil>, command = <string>, opts = <table>, status = <table>, stdin = one-of( popen.stream.OPEN (== 'open'), popen.stream.CLOSED (== 'closed'), nil, ), stdout = one-of( popen.stream.OPEN (== 'open'), popen.stream.CLOSED (== 'closed'), nil, ), stderr = one-of( popen.stream.OPEN (== 'open'), popen.stream.CLOSED (== 'closed'), nil, ), } `pid` is a process id of the process when it is alive, otherwise `pid` is nil. `command` is a concatenation of space separated arguments that were passed to execve(). Multiword arguments are quoted. Quotes inside arguments are not escaped. `opts` is a table of handle options in the format of popen.new() `opts` parameter. `opts.env` is not shown here, because the environment variables map is not stored in a handle. `status` is a table that represents a process status in the following format: { state = one-of( popen.state.ALIVE (== 'alive'), popen.state.EXITED (== 'exited'), popen.state.SIGNALED (== 'signaled'), ) -- Present when `state` is 'exited'. exit_code = <number>, -- Present when `state` is 'signaled'. signo = <number>, signame = <string>, } `stdin`, `stdout`, `stderr` reflect status of parent's end of a piped stream. When a stream is not piped the field is not present (`nil`). When it is piped, the status may be one of the following: - popen.stream.OPEN (== 'open') - popen.stream.CLOSED (== 'closed') The status may be changed from 'open' to 'closed' by :shutdown({std... = true}) call. Example 1 (tarantool console): | tarantool> require('popen').new({'/usr/bin/touch', '/tmp/foo'}) | --- | - command: /usr/bin/touch /tmp/foo | status: | state: alive | opts: | stdout: inherit | stdin: inherit | group_signal: false | keep_child: false | close_fds: true | restore_signals: true | shell: false | setsid: false | stderr: inherit | pid: 9499 | ... Example 2 (tarantool console): | tarantool> require('popen').shell('grep foo', 'wrR') | --- | - stdout: open | command: sh -c 'grep foo' | stderr: open | status: | state: alive | stdin: open | opts: | stdout: pipe | stdin: pipe | group_signal: true | keep_child: false | close_fds: true | restore_signals: true | shell: true | setsid: true | stderr: pipe | pid: 10497 | ... `popen_handle:wait() -> res` ---------------------------- Wait until a child process get exited or signaled. @param handle a handle of process to wait Raise an error on incorrect parameters or when the fiber is cancelled: - IllegalParams: an incorrect handle parameter. - IllegalParams: called on a closed handle. - FiberIsCancelled: cancelled by an outside code. Return a process status table (the same as ph.status and ph.info().status). @see popen_handle:info() for the format of the table. `popen_handle:close() -> ok, err` --------------------------------- Close a popen handle. @param handle a handle to close Basically it kills a process using SIGKILL and releases all resources assosiated with the popen handle. Details about signaling: - The signal is sent only when opts.keep_child is not set. - The signal is sent only when a process is alive according to the information available on current even loop iteration. (There is a gap here: a zombie may be signaled; it is harmless.) - The signal is sent to a process or a grocess group depending of opts.group_signal. (@see lbox_popen_new() for details of group signaling). Resources are released disregarding of whether a signal sending succeeds: fds are closed, memory is released, the handle is marked as closed. No operation is possible on a closed handle except :close(), which always successful on closed handle (idempotence). Raise an error on incorrect parameters: - IllegalParams: an incorrect handle parameter. The function may return `true` or `nil, err`, but it always frees the handle resources. So any return value usually means success for a caller. The return values are purely informational: it is for logging or same kind of reporting. Possible diagnostics (don't consider them as errors): - SystemError: no permission to send a signal to a process or a process group This diagnostics may appear due to Mac OS behaviour on zombies when opts.group_signal is set, @see lbox_popen_signal(). Whether it may appear due to other reasons is unclear. Always return `true` when a process is known as dead (say, after ph:wait()): no signal will be send, so no 'failure' may appear. Handle fields ============= - popen_handle.pid - popen_handle.command - popen_handle.opts - popen_handle.status - popen_handle.stdin - popen_handle.stdout - popen_handle.stderr See popen_handle:info() for description of those fields. Module constants ================ - popen.opts - INHERIT (== 'inherit') - DEVNULL (== 'devnull') - CLOSE (== 'close') - PIPE (== 'pipe') - popen.signal - SIGTERM (== 9) - SIGKILL (== 15) - ... - popen.state - ALIVE (== 'alive') - EXITED (== 'exited') - SIGNALED (== 'signaled') - popen.stream - OPEN (== 'open') - CLOSED (== 'closed') ``` (cherry picked from commit 34c2789b48eadd36da742f7f761a998889e70544)
-
Alexander Turenko authored
The function still set a diagnostics when a signal sending fails and returns -1, but it is purely informational result: for logging or so. It reflects notes about dealing with failures in Linux's `man 2 close`: | Note, however, that a failure return should be used only for | diagnostic purposes <...> or remedial purposes <...>. | | <...> Linux kernel always releases the file descriptor early in the | close operation, freeing it for reuse; the steps that may return an | error <...> occur only later in the close operation. | | Many other implementations similarly always close the file descriptor | <...> even if they subsequently report an error on return from | close(). POSIX.1 is currently silent on this point, but there are | plans to mandate this behavior in the next major release of the | standard. When kill or killpg returns EPERM a caller usually unable to overcome it somehow: retrying is not sufficient here. So there are no needs to keep the handle: a caller refuses the handle and don't want to perform any other operation on it. The open engine do its best to kill a child process or a process group, but when it is not possible, just set the a diagnostic and free handle resources anyway. Left comments about observed Mac OS behaviour regarding killing a process group, where all processes are zombies (or just when a process group leader is zombie, don't sure): it gives EPERM instead of ESRCH from killpg(). This result should not surprise a user, so it should be documented. See [1] for another description of the problem (I don't find any official information about this). [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1329528 Part of #4031 Acked-by:
Cyrill Gorcunov <gorcunov@gmail.com> (cherry picked from commit 56a8c346ecb0581300a63c6e677d8a4672ff1f95)
-
Alexander Turenko authored
After 2.2.0-633-gaa0964ae1 ('net.box: fix schema fetching from 1.10/2.1 servers') net.box expects that _vcollation system view exists on a tarantool server of 2.2.1+ version. This is however not always so: a server may be run on a new version of tarantool, but work on a schema of an old version. The situation with non last schema is usual for replication cluster in process of upgrading: all instances run on the new version of tarantool first (no auto-upgrade is performed by tarantools in a cluster). Then box.schema.upgrade() should be called, but the instances should be operable even before the call. Before the commit net.box was unable to connect a server if it is run on a schema without _vcollation system view (say, 2.1.3), but the server executable is of 2.2.1 version or newer. Note: I trim tests from the commit to polish them a bit more, but include the fix itself to 2.4.1 release. Follows up #4307 Fixes #4691
-
Igor Munkin authored
This changeset makes possible to run luajit-tap tests requiring libraries implemented in C: * symlink to luajit test is created on configuration phase instead of build one. * introduced a CMake function for building shared libraries required for luajit tests. Furthermore this commit enables CMake build for the following luajit-tap tests: * gh-4427-ffi-sandwich * lj-flush-on-trace Reviewed-by:
Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Reviewed-by:
Sergey Ostanevich <sergos@tarantool.org> Signed-off-by:
Igor Munkin <imun@tarantool.org>
-
Kirill Yukhin authored
- jit: abort trace execution on JIT mode change - jit: abort trace recording and execution for C API Reviewed-by:
Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Signed-off-by:
Igor Munkin <imun@tarantool.org>
-
- Apr 18, 2020
-
-
Alexander V. Tikhonov authored
Found that some package builds failed on the mistake in CMakeLists.txt file, the failed packages and test builds were: - CentOS 6 - CentOS 7 - Ubuntu 14.04 and static build based on Dockerfile. The core of the issue is that CMake 2 does not support line continuation with backslash. The commit fixes the regression from 7b443650 ('feedback: add cmake option to disable the daemon'). Follow up #3308
-
- Apr 17, 2020
-
-
Vladislav Shpilevoy authored
There is a complaint that the feedback daemon is a 'spying' tool and because of that can't be used on Gentoo. Its default disabled option also is not acceptable, the daemon should be eliminated completely. The patch introduces cmake option ENABLE_FEEDBACK_DAEMON. It is ON by default. When set to OFF, all feedback daemon's code is not included into the binary, its configuration options disappear. Closes #3308
-
Vladislav Shpilevoy authored
box.cfg() works in two stages, when called first time - boot the instance using box_cfg() C++ function, and then configure it. During booting all non-dynamic parameters are read. Dynamic are configured mostly afterwards. Normally there should be a yield between box_cfg() C++ call and dynamic parameters configuration. It is used by box.ctl.wait_ro() and box.ctl.wait_rw() Lua calls to catch the instance in read-only state always before read-write state. In theory a user should be able to call box.ctl.wait_ro() and box.ctl.wait_rw() in one fiber, box.cfg() in another, and these waits would be unblocked one after another. It works fine now, but only because of, surprisingly, the feedback daemon. The daemon creates a yield after C++ box_cfg() is finished, but dynamic parameters are still being applied in load_cfg.lua. That gives time to catch box.ctl.wait_ro() event. The thing is that dynamic parameters configuration includes the daemon's options too. When 'feedback_enable' option is installed to true, the daemon is started using fiber.create(). That creates a yield, and gives time to box.ctl.wait_ro() fibers to handle the event. When the daemon is disabled or removed, like it is going to happen in #3308, this trick does not work, and box.ctl.wait_ro() started before box.cfg() is never triggered. It could be tested on app-tap/cfg.test.lua with box.cfg{} changed to box.cfg{feedback_enabled = false} Then the test would hang. A test is not patched here, because the feedback is going to be optionally removed in a next commit, and the test would become flaky depending on build options. Needed for #3308
-
Vladislav Shpilevoy authored
Feedback daemon's code was located in two files: box/lua/feedback_daemon.lua and box/lua/schema.lua. That makes it harder to eliminate the daemon at cmake configuration time. Now all its code is in one place, in feedback_daemon.lua. Disable of the daemon's code now is a matter of excluding the Lua file from source code. Part of #3308
-
Vladislav Shpilevoy authored
Box built-in modules, such as session, tuple, schema, etc, were loaded using luaL_loadbuffer() + lua_call(). Error of the former call was handled properly with a panic message describing the problem. But if lua_call() failed, it resulted into 'unknown exception' in main.cc. Not very helpful. Now it is lua_pcall(), and the error message is included into the panic() message. That helps in debug, when something is being changed in the box modules.
-
Alexander Turenko authored
On Linux x86_64 `ssize_t` is 64 bit, while `int` is 32 bit wide (at least typically). Let's return `ssize_t` from popen_write_timeout() to prevent data loss. Part of #4031 Reported-by:
Cyrill Gorcunov <gorcunov@gmail.com> Acked-by:
Cyrill Gorcunov <gorcunov@gmail.com>
-
Alexander Turenko authored
It was easy to misinterpret popen_may_io() contract. In fact, I made this mistake recently and want to clarify how the function should be called. Part of #4031 Acked-by:
Cyrill Gorcunov <gorcunov@gmail.com>
-
Alexander Turenko authored
The function popen_shutdown() checks whether std{in,out,err} was piped and closes the parent's end. A user should have ability to send EOF for child's stdin for stream programs like `grep`. It is better when there is a function that encapsulates proper checks, error messages and the actual actions. This commit in particular reverts 1ef95b99 ('popen: remove redundant fd check before perform IO'), because now the check is meaningful: an fd may become closed before the whole popen handle will be deleted. Part of #4031 Acked-by:
Cyrill Gorcunov <gorcunov@gmail.com>
-
Alexander Turenko authored
Popen backend errors should be meaningful for a user of the popen Lua API, because otherwise we'll need to map backend errors into Lua API errors. Those particular failures can't appear when the functions are called from the Lua API, but it is good to keep all error messages in one style. Part of #4031 Acked-by:
Cyrill Gorcunov <gorcunov@gmail.com>
-
Alexander Turenko authored
Even when ..._SETSID and ..._GROUP_SIGNAL are set, we unable to safely kill a process group after the child process we spawned becomes died. So we don't do that. The behaviour seems to be indefeasible part of Unix process group design. The best that we can do here is describe those details in the documentation comment. NB: It seems that pid namespaces allow to overcome this problem, however it is the Linux specific feature, so we unlikely will use them. Part of #4031 Acked-by:
Cyrill Gorcunov <gorcunov@gmail.com>
-
Alexander Turenko authored
It is convenient to have a formal description of an API during development and when writing a documentation. I plan to use those contracts when I will write an API description for the future popen Lua module. Part of #4031 Acked-by:
Cyrill Gorcunov <gorcunov@gmail.com>
-