/*
 * Multifile Copyright (c) 2018-2022, James Bailie.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *     * The name of James Bailie may not be used to endorse or promote
 * products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <syslog.h>
#include <sys/event.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>

pid_t pid;

void become_daemon()
{
   int file, len;
   char buffer[ 32 ];

   /*
    * Fork and let the parent die, continuing as child so we are not a
    * process group leader.  This is necessary for the call to setsid().
    */

   switch( fork() )
   {
      case -1:
         fprintf( stderr, "multifile_wrapper: fork(): %s\n", strerror( errno ));
         exit( 1 );

      case 0:
         break;

      default:
         exit( 0 );
   }

   fclose( stdout );
   fclose( stderr );
   fclose( stdin );

   stdin = fopen( "/dev/null", "r" );
   stdout = fopen( "/dev/null", "w" );
   stderr = fopen( "/dev/null", "w" );

   openlog( "multifile", LOG_PID, LOG_DAEMON );

   if ( stdin == NULL || stdout == NULL || stderr == NULL )
   {
      syslog( LOG_ERR, "fopen(): %m" );
      exit( 1 );
   }

   if ( setsid() < 0 )
   {
      syslog( LOG_ERR, "setsid(): %m" );
      exit( 1 );
   }

   len = snprintf( buffer, sizeof( buffer ), "%d", getpid() );

   if (( file = open( "/var/run/multifile.pid", O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR | S_IRGRP )) < 0 )
   {
      syslog( LOG_WARNING, "open( PIDFILE ): %m" );
      return;
   }

   write( file, buffer, len );
   close( file );
}

void signal_handler( int signo )
{
   if ( pid <= 0 )
      return;

   kill( pid, signo );
}

void set_signals()
{
   int *iptr;
   int sigs[] = {
      SIGPIPE, SIGHUP, SIGQUIT, SIGUSR1, SIGUSR2, SIGALRM, SIGINT, SIGTSTP, -1
   };

   signal( SIGTERM, signal_handler );

   for( iptr = sigs; *iptr > 0; ++iptr )
      signal( *iptr, SIG_IGN );
}

int main( int argc, char **argv )
{
   int status, n;
   char **args;

   become_daemon();
   set_signals();

   /*
    * Rewrite args to subsitute multifile for ourselves.
    */

   if (( args = calloc( argc + 1, sizeof( char * ))) == NULL )
   {
      syslog( LOG_ERR, "calloc(): %m" );
      return 1;
   }

   args[ 0 ] = "%%PREFIX%%/bin/multifile_server";

   for( n = 1; n < argc; ++n )
      args[ n ] = argv[ n ];

AGAIN:
   switch(( pid = fork() ))
   {
      case -1:
         syslog( LOG_ERR, "fork(): %m" );
         return 1;

      case 0:
         signal( SIGTERM, SIG_DFL );
         execv( *args, args );
         _exit( 1 );

      default:
         status = 0;

         if ( waitpid( pid, &status, WEXITED ) < 0 )
         {
            syslog( LOG_ERR, "waitpid(): %m" );
            return 1;
         }

         if ( WIFSIGNALED( status ))
            goto AGAIN;
   }

   return 0;
}
