#ifndef RBIMPL_INTERN_SIGNAL_H                       /*-*-C++-*-vi:se ft=cpp:*/
#define RBIMPL_INTERN_SIGNAL_H
/**
 * @file
 * @author     Ruby developers <ruby-core@ruby-lang.org>
 * @copyright  This  file  is   a  part  of  the   programming  language  Ruby.
 *             Permission  is hereby  granted,  to  either redistribute  and/or
 *             modify this file, provided that  the conditions mentioned in the
 *             file COPYING are met.  Consult the file for details.
 * @warning    Symbols   prefixed  with   either  `RBIMPL`   or  `rbimpl`   are
 *             implementation details.   Don't take  them as canon.  They could
 *             rapidly appear then vanish.  The name (path) of this header file
 *             is also an  implementation detail.  Do not expect  it to persist
 *             at the place it is now.  Developers are free to move it anywhere
 *             anytime at will.
 * @note       To  ruby-core:  remember  that   this  header  can  be  possibly
 *             recursively included  from extension  libraries written  in C++.
 *             Do not  expect for  instance `__VA_ARGS__` is  always available.
 *             We assume C99  for ruby itself but we don't  assume languages of
 *             extension libraries.  They could be written in C++98.
 * @brief      Signal handling APIs.
 */
#include "ruby/internal/attr/nonnull.h"
#include "ruby/internal/attr/pure.h"
#include "ruby/internal/dllexport.h"
#include "ruby/internal/value.h"

RBIMPL_SYMBOL_EXPORT_BEGIN()

/* signal.c */

RBIMPL_ATTR_NONNULL(())
/**
 * Sends a signal ("kills") to processes.
 *
 * The first argument is the signal, either in:
 *
 *   - Numerical representation (e.g. `9`), or
 *   - Textual  representation   of  canonical   (e.g.   `:SIGKILL`)   name  or
 *     abbreviated (e.g. `:KILL`) name, either in ::rb_cSymbol or ::rb_cString.
 *
 * All the  remaining arguments are  numerical representations of  process IDs.
 * This function iterates over them to send the specified signal.
 *
 * You can specify both negative PIDs and negative signo to this function:
 *
 *   ```
 *    sig \ pid | >= 1 | == 0 | == -1 | <= -2
 *   ===========+======+======+=======+=======
 *        > 0   |  #1  |  #2  |  #3   |  #4
 *       == 0   |  #5  |  #6  |  #7   |  #8
 *        < 0   |  #9  |  #10 |      #11
 *   ```
 *
 *   - Case #1: When  signo and PID are both positive,  this function sends the
 *     specified signal to the specified process (intuitive).
 *
 *   - Case #2: When  signo is  positive and PID  is zero, this  function sends
 *     that signal to the current process group.
 *
 *   - Case #3: When signo is positive and  PID is -1, this function sends that
 *     signal to everything that the current process is allowed to kill.
 *
 *   - Case #4: When signo  is positive and PID is negative  (but not -1), this
 *     function sends that signal to every  processes in a process group, whose
 *     process group ID is the absolute value of the passed PID.
 *
 *   - Case #5: When  signo  is zero  and PID is  positive, this  function just
 *     checks  for the  existence of  the  specified process  and doesn't  send
 *     anything to  anyone.  In  case the process  is absent  `Errno::ESRCH` is
 *     raised.
 *
 *   - Case #6: When signo and PID are  both zero, this function checks for the
 *     existence of the current process group.   And it must do.  This function
 *     is effectively a no-op then.
 *
 *   - Case #7: When signo is zero and PID is -1, this function checks if there
 *     is any other  process that the current process can  kill.  At least init
 *     (PID 1) must exist, so this must not fail.
 *
 *   - Case #8: When  signo is  zero and  PID is  negative (but  not -1),  this
 *     function checks  if there is a  process group whose process  group ID is
 *     the absolute  value of  the passed  PID.  In case  the process  group is
 *     absent `Errno::ESRCH` is raised.
 *
 *   - Case #9: When signo is negative and PID is positive, this function sends
 *     the absolute value of the passed signo to the process group specified as
 *     the PID.
 *
 *   - Case #10: When signo is negative and  PID is zero, it is highly expected
 *     that this function  sends the absolute value of the  passed signo to the
 *     current  process   group.   Strictly  speaking,  IEEE   Std  1003.1-2017
 *     specifies that  this (`killpg(3posix)` with  an argument of zero)  is an
 *     undefined behaviour.  But no operating system  is known so far that does
 *     things differently.
 *
 *   - Case #11: When  signo and PID  are both negative, the  behaviour of this
 *     function  depends on  how `killpg(3)`  works.  On  Linux, it  seems such
 *     attempt is  strictly prohibited and  `Errno::EINVAL` is raised.   But on
 *     macOS, it seems it  tries to to send the signal  actually to the process
 *     group.
 *
 * @note       Above description is in fact different from how `kill(2)` works.
 *             We interpret the passed arguments before passing them through to
 *             system calls.
 * @param[in]  argc                 Number of objects in `argv`.
 * @param[in]  argv                 Signal, followed by target PIDs.
 * @exception  rb_eArgError         Unknown signal name.
 * @exception  rb_eSystemCallError  Various errors sending signal to processes.
 * @return     Something numeric.  The meaning of this return value is unclear.
 *             It seems in case of #1 above, this could be the body count.  But
 *             other cases remain mysterious.
 */
VALUE rb_f_kill(int argc, const VALUE *argv);

/* This must be private, @shyouhei guesses. */
#ifdef POSIX_SIGNAL
#define posix_signal ruby_posix_signal
void (*posix_signal(int, void (*)(int)))(int);
#endif

RBIMPL_ATTR_PURE()
/**
 * Queries  the name  of  the signal.   It returns  for  instance `"KILL"`  for
 * SIGKILL.
 *
 * @param[in]  signo      Signal number to query.
 * @retval     0          No such signal.
 * @retval     otherwise  A pointer  to a static C  string that is the  name of
 *                        the signal.
 * @warning    Don't free the return value.
 */
const char *ruby_signal_name(int signo);

/**
 * Pretends as if  there was no custom signal handler.   This function sets the
 * signal action to SIG_DFL, then kills itself.
 *
 * @param[in]  sig  The signal.
 * @post       Previous signal handler is lost.
 * @post       Passed signal is sent to the current process.
 *
 * @internal
 *
 * @shyouhei doesn't understand  the needs of this function  being visible from
 * extension libraries.
 */
void ruby_default_signal(int sig);

RBIMPL_SYMBOL_EXPORT_END()

#endif /* RBIMPL_INTERN_SIGNAL_H */
