Skip to content
Snippets Groups Projects
  1. Apr 28, 2020
    • Vladislav Shpilevoy's avatar
      box: introduce box_return_mp() public C function · dd36c610
      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);
      ```
      dd36c610
    • Vladislav Shpilevoy's avatar
      box: introduce port_c · 4c3c9bda
      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
      4c3c9bda
  2. Apr 27, 2020
    • Serge Petrenko's avatar
      applier: follow vclock to the last tx row · 0edb4d97
      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: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      0edb4d97
    • Roman Khabibov's avatar
      sql: fix sorting rules for values of SCALAR type · 72ce442c
      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
      72ce442c
  3. Apr 24, 2020
    • Cyrill Gorcunov's avatar
      cbus: fix inconsistency in endpoint creation · d6d69c9f
      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: default avatarAlexander Turenko <alexander.turenko@tarantool.org>
      Signed-off-by: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      d6d69c9f
    • Leonid Vasiliev's avatar
      build: fix compilation on Alpine 3.5 · d7fa6d34
      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.
      d7fa6d34
  4. Apr 21, 2020
  5. Apr 20, 2020
    • Kirill Yukhin's avatar
      Dummy commit · ad13b6d5
      Kirill Yukhin authored
      ad13b6d5
    • Nikita Pettik's avatar
      box/error: ref error.prev while accessing it · fef6505c
      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's avatar
      box/error: don't allow overflow of error ref counter · ca79e1cf
      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().
      ca79e1cf
    • Kirill Yukhin's avatar
      test: fix LTO build for popen unit test · c44ed3c0
      Kirill Yukhin authored
      Fixa clash in struct names detected by LTO.
      c44ed3c0
    • Kirill Yukhin's avatar
      Fix build after popen patchset · a46611a3
      Kirill Yukhin authored
      Make older compiler happy w.r.t.
      initialization of locals.
      a46611a3
    • Igor Munkin's avatar
      build: temporary fix for luajit-tap tests cmake · dfba0512
      Igor Munkin authored
      Fixes the regression from 335f80a0
      ('test: adjust luajit-tap testing machinery').
      dfba0512
    • Leonid Vasiliev's avatar
      error: fix iproto error stack overlapped by old error · a219e258
      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.
      a219e258
    • Vladislav Shpilevoy's avatar
      iproto: rename IPROTO_ERROR and IPROTO_ERROR_STACK · 2b4263d3
      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
      2b4263d3
    • Vladislav Shpilevoy's avatar
      error: make iproto errors reuse mp_error module · 712af455
      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`.
      712af455
    • Vladislav Shpilevoy's avatar
      error: export error_unref() function · ed217292
      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
      ed217292
    • Leonid Vasiliev's avatar
      error: add error MsgPack encoding · 345877df
      Leonid Vasiliev authored
      
      Co-authored-by: default avatarVladislav <Shpilevoy&lt;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.
      345877df
    • Vladislav Shpilevoy's avatar
      box: move Lua MP_EXT decoder from tuple.c · d3a4dc68
      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
      d3a4dc68
    • Leonid Vasiliev's avatar
      error: update constructors of some errors · 6d0078d0
      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
      6d0078d0
    • Leonid Vasiliev's avatar
      error: add session setting for error type marshaling · c7a7f1cb
      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: default avatarVladislav <Shpilevoy&lt;v.shpilevoy@tarantool.org>
      
      Needed for #4398
      c7a7f1cb
    • Vladislav Shpilevoy's avatar
      session: add offset to SQL session settings array · b6c6c536
      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.
      b6c6c536
    • Leonid Vasiliev's avatar
      error: add custom error type · b728e7af
      Leonid Vasiliev authored
      
      Co-authored-by: default avatarVladislav <Shpilevoy&lt;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
      ...
      ```
      b728e7af
    • Alexander Turenko's avatar
      libev: backport select()'s limit workaround · 8acc011a
      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: default avatarMaria Khaydich <maria.khaydich@tarantool.org>
      Co-authored-by: default avatarMaria Khaydich <maria.khaydich@tarantool.org>
      8acc011a
    • Alexander Turenko's avatar
      popen: add popen Lua module · 6d1c5ff5
      Alexander Turenko authored
      
      Fixes #4031
      
      Co-developed-by: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      Co-developed-by: default avatarIgor Munkin <imun@tarantool.org>
      Acked-by: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      Reviewed-by: default avatarIgor 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)
      6d1c5ff5
    • Alexander Turenko's avatar
      popen: always free resources in popen_delete() · 01657bfb
      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: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      (cherry picked from commit 56a8c346ecb0581300a63c6e677d8a4672ff1f95)
      01657bfb
    • Alexander Turenko's avatar
      net.box: fix fetching of schema of an old version · 06edcbe1
      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
      06edcbe1
    • Igor Munkin's avatar
      test: adjust luajit-tap testing machinery · 335f80a0
      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: default avatarVladislav Shpilevoy <v.shpilevoy@tarantool.org>
      Reviewed-by: default avatarSergey Ostanevich <sergos@tarantool.org>
      Signed-off-by: default avatarIgor Munkin <imun@tarantool.org>
      335f80a0
    • Kirill Yukhin's avatar
      luajit: bump new version · a1594091
      Kirill Yukhin authored
      
      - jit: abort trace execution on JIT mode change
      - jit: abort trace recording and execution for C API
      
      Reviewed-by: default avatarVladislav Shpilevoy <v.shpilevoy@tarantool.org>
      Signed-off-by: default avatarIgor Munkin <imun@tarantool.org>
      a1594091
  6. Apr 18, 2020
    • Alexander V. Tikhonov's avatar
      build: fix build with CMake 2 · 930c7463
      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
      Unverified
      930c7463
  7. Apr 17, 2020
    • Vladislav Shpilevoy's avatar
      feedback: add cmake option to disable the daemon · 7b443650
      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
      7b443650
    • Vladislav Shpilevoy's avatar
      box: yield after initial box_cfg() is finished · 70695ecb
      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
      70695ecb
    • Vladislav Shpilevoy's avatar
      feedback: move feedback code to the single file · e9e9b540
      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
      e9e9b540
    • Vladislav Shpilevoy's avatar
      box: improve built-in module load panic message · a05ff5d3
      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.
      a05ff5d3
    • Alexander Turenko's avatar
      popen: fix popen_write_timeout retval type · c66a7d84
      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: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      Acked-by: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      c66a7d84
    • Alexander Turenko's avatar
      popen: add caution comment for popen_may_io() · e164d95e
      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: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      e164d95e
    • Alexander Turenko's avatar
      popen: allow to close parent's end of std* fds · 5efd028d
      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: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      5efd028d
    • Alexander Turenko's avatar
      popen: refine popen_{read,write}_timeout errors · 55cb9cbe
      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: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      55cb9cbe
    • Alexander Turenko's avatar
      popen: clarify group signaling details · 062e55eb
      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: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      062e55eb
    • Alexander Turenko's avatar
      popen: clarify popen_{signal,delete} contract · c4cf1454
      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: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      c4cf1454
Loading