/************************************************************************
 * daemon.c                                                             *
 *                                                                      *
 * Copyright (C) 2002 - 2020 Joshua Birnbaum <engineer@noorg.org>.      *
 * All Rights Reserved.                                                 *
 *                                                                      *
 * 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 2 of the License, or *
 * (at your option) any later version.                                  *
 *                                                                      *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.         *
 *                                                                      *
 * You should have received a copy of the GNU General Public License    *
 * along with this program; see the file COPYING. If not, write to the  *
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, *
 * MA 02111-1307, USA.                                                  *
 ************************************************************************/

/**************************************************************
 * Project ifchk -- Host based network interface promiscuous  *
 *                  mode detection and handling.              *
 *                                                            *
 * Version:         1.1.2                                     *
 *                                                            *
 * ---------------------------------------------------------- *
 *                                                            *
 * daemon.c         ifchk daemon subsystem.                   *
 *                                                            *
 * Functions:     - Performs tasks specific to starting ifchk *
 *                  as a daemon process.                      *
 **************************************************************/
 
/**********************
 * include directives *
 **********************/

#include "daemon.h"


/**********************************************************
 * daemonInit() - Performs initialization proceedures for *
 *                ifchk to start up as a daemon process.  *
 *                Some code adapted from Solaris Systems  *
 *                Programming by R. Teer.                 *
 *                                                        *
 * Parameters:    None.                                   *
 *                                                        *
 * Returns:       0 on success.                           *
 *                -1 on failure.                          *
 **********************************************************/

int daemonInit( void )
{
    pid_t pid;              /* For fork(). */
    int fd = 0;             /* For reopening standard input & output on /dev/null. */
    struct rlimit limits;   /* For reopening standard input & output on /dev/null. */

    /*
     * Call fork() to create a child process
     * and have our parent process terminate.
     */
    if( ( pid = fork() ) == -1 )
    {
        if( ( writeLog( LOG_CRIT, "ERROR: fork(): could not create new process: %s\n",
                                                            strerror( errno ) ) ) != 0 )
        {
	        return (-1);
         }

        return (-1);
     }

    else if( pid != 0 )
    {
        _exit( 0 );
     }

    /*
     * Drop our controlling terminal so that we don't rec-
     * eive signals as a result of terminal activity, etc.
     */
    if( setsid() == -1 )
    {
        if( ( writeLog( LOG_CRIT, "ERROR: setsid(): could not create new session: %s\n",
                                                              strerror( errno ) ) ) != 0 )
        {
            return (-1);
         }

        return (-1);
     }

    /*
     * Call fork() again to ensure that we can-
     * not reacquire a controlling terminal.
     */
    if( ( pid = fork() ) == -1 )
    {
        if( ( writeLog( LOG_CRIT, "ERROR: fork(): could not create new process: %s\n",
                                                            strerror( errno ) ) ) != 0 )
        {
            return (-1);
         }

        return (-1);
     }

    else if( pid != 0 )
    {
        _exit( 0 );
     }

    /*
     * Close standard input in preparation for
     * reopening file descriptor on /dev/null.
     */
    if( close( 0 ) != 0 )
    {
        if( ( writeLog( LOG_CRIT, "ERROR: close(): could not close stdin: %s\n",
                                                      strerror( errno ) ) ) != 0 )
        {
            return (-1);
         }

        return (-1);
     }

    /*
     * Open /dev/null read-write. Standard input
     * should now be opened on /dev/null because
     * we just close()d standard input above.
     */
    if( ( fd = open( "/dev/null", O_RDWR ) ) == -1 )
    {
        if( ( writeLog( LOG_CRIT, "ERROR: open(): could not open /dev/null: %s\n",
                                                        strerror( errno ) ) ) != 0 )
        {
            return (-1);
         }

        return (-1);
     }

    /*
     * Check that fd really refers to stan-
     * dard input. dup2() it, if it doesn't.
     */
    if( fd != STDIN_FILENO )
    {
        if( dup2( fd, STDIN_FILENO ) == -1 )
        {
            if( ( writeLog( LOG_CRIT, "ERROR: dup2(): could not open stdin on /dev/null: %s\n",
                                                                     strerror( errno ) ) ) != 0 )
            {
                return (-1);
             }

            return (-1);
         }
     }

    /*
     * Copy descriptor fd to standard output. Stan-
     * dard output will now be opened on /dev/null.
     */
    if( dup2( fd, 1 ) == -1 )
    {
        if( ( writeLog( LOG_CRIT, "ERROR: dup2(): could not open stdout on /dev/null: %s\n",
                                                                  strerror( errno ) ) ) != 0 )
        {
            return (-1);
         }

        return (-1);
     }

    /*
     * Prepare to close all unneeded file descriptors
     * to avoid unnecessary use of system resources.
     */
    if( getrlimit( RLIMIT_NOFILE, &limits ) == -1 )
    {
        if( ( writeLog( LOG_CRIT, "ERROR: getrlimit(): could not get max open fd limit: %s\n",
                                                                    strerror( errno ) ) ) != 0 )
        {
            return (-1);
         }

        return (-1);
     }

    if( limits.rlim_cur < limits.rlim_max )
    {
        limits.rlim_cur = limits.rlim_max;
     }

    if( setrlimit( RLIMIT_NOFILE, &limits ) == -1 )
    {
        if( ( writeLog( LOG_CRIT, "ERROR: setrlimit(): could not set max open fd limit: %s\n",
                                                                    strerror( errno ) ) ) != 0 )
        {
            return (-1);
         }

        return (-1);
     }

    /*
     * Close all unneeded file descriptors.
     */
    for( fd = 3; fd <= limits.rlim_cur; fd++ )
    {
        if( close( fd ) != 0 )
        {
            if( errno == EBADF )
            {
                continue;
             }

	        else
            {
                if( ( writeLog( LOG_CRIT, "ERROR: close(): could not close file descriptor %d: %s\n",
                                                                       fd, strerror( errno ) ) ) != 0 )
                {
                    return (-1);
                 }
             }
         }
     }

    /*
     * Change directory to "/" so we don't prevent
     * the unmounting of any mounted filesystems.
     */
    if( chdir( "/" ) == -1 )
    {
        if( ( writeLog( LOG_CRIT, "ERROR: chdir(): could not change directory to /: %s\n",
                                                                strerror( errno ) ) ) != 0 )
        {
            return (-1);
         }

        return (-1);
     }

    /*
     * Set our file mode creation mask to
     * zero - files created by the daemon
     * will have the expected permissions.
     */
    umask( 0 );

    return (0);
}
