/***************************************************************************
*
* Copyright 2016 by Sean Conner.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* Comments, questions and criticisms can be sent to: [email protected]
*
* =======================================================================
*
* This entire file exists *because* I can't properly handle SIGCHLD in the
* server process.  What I want to do is just get the reason for a handler
* process terminating, but not wanting to deal with EINTR, so I set the
* handler to restart system calls.  But while I can do that in Lua, the Lua
* signal handler doesn't get control until the Lua VM gets control, and if
* we're sitting in a system call, that might take some time.  So, we do this
* in C, which can get the job done.
*
* This code is straightforward---just export a single function to Lua that
* run called, catches SIGCHLD, and the signal handler will reap the
* children.
*
*************************************************************************/

#ifdef __GNUC__
#  define _GNU_SOURCE
#endif

#include <stdbool.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <syslog.h>

#include <lua.h>

/***************************************************************************/

static void handle_sigchld(int sig __attribute__((unused)))
{
 while(true)
 {
   int   status;
   pid_t child = waitpid(-1,&status,WUNTRACED | WCONTINUED | WNOHANG);

   if (child < 0)
   {
     if (errno == ECHILD) return;
     syslog(LOG_ERR,"waitpid() = %s",strerror(errno));
   }
   else if (child == 0)
     return;
   else
   {
     if WIFEXITED(status)
     {
       if (WEXITSTATUS(status) != 0)
         syslog(LOG_ERR,"handler process %d returned %d",(int)child,WEXITSTATUS(status));
     }
     else
       syslog(LOG_ERR,"handler process %d aborted: %s",(int)child,strsignal(WTERMSIG(status)));
   }
 }
}

/***************************************************************************/

int luaopen_reapchild(lua_State *L)
{
 struct sigaction act;

 act.sa_handler = handle_sigchld;
 act.sa_flags   = SA_RESTART;
 sigemptyset(&act.sa_mask);
 lua_pushboolean(L,sigaction(SIGCHLD,&act,NULL) == 0);
 return 1;
}