/************************************************************************/
/*    Copyright (C) 2004  Michael C. Shultz               */
/*                                    */
/* 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 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, write to the Free Software        */
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA      */
/*  02111-1307, USA.                          */
/*                                    */
/* Michael C. Shultz                          */
/* ringworm@inbox.lv                          */
/* Box 3238 Landers, CA 92285                     */
/************************************************************************/
#include  <pmupgrade.h>

int   upgrade( void );
void  catch_SIGINT( int );

MGsDb   config_db;
char    config_db_file[]    = DATADIR"configure.db";

int    main( )
{
    int errorCode   = 0;
    int skip        = 0;    
    /****************************************************************/
    /* cycle upgrade routine until it returns a "2", meaning there    */
    /* is nothing left to process, or possibly an error       */
    /****************************************************************/
    while( 1 == 1  )
    {
        if( skip == 0 )
        {
            skip    = 1;
            MGmDbArray( config_db, config_db_file, "r" );
        }
        if( ( errorCode = upgrade() ) != 2 )
        {
            break;
        }
    }
    if( errorCode )
    {
        fprintf( stdout, "portmanager info: All ports up to date\n");
        MGmDbArrayFree( config_db );
        return( 1 );
    }
    exit( 0 );
}
/********************************************************************************/
/* upgrade routine                                */
/********************************************************************************/
int upgrade( void )
{
    MGsDb   port_dependencies;
    MGsDb   ports_installed;
    MGsDb   ports_old;
    char    cacheFile[]     = DATADIR PORTS_CACHE_DB;
    char    id[]            = "pmupgrade";
    char*   command         = 0;
    char*   options;
    int IDX_config      = 0;
    int IDX_port_dependencies   = 0;
    int IDX_ports_installed = 0;
    int IDX_ports_old       = 0;
    int IDX_ports_old_2     = 0;
    int errorCode       = 0;
    int parentOK        = 0;
    int skip            = 0;

    /*********************************************************************************/
    /* use comand /usr/local/bin/pmStatus to build ports_installed.db port_dependencies.db and ports_old.db */
    /*********************************************************************************/
    errorCode   = system( "pmStatus" );
    if( !MGrIfFileExist( cacheFile ) )
    {
        /* if here then the cache is reset, try 1 more time */
        errorCode   = system( "pmStatus" );
    }

    if( errorCode != 0 )
    {
        fprintf( stderr, "%s %s error: pmStatus returned an error, cannot continue\n", id, ver );
        fflush( stderr );
exit(1);
/*
        return( 1 );
*/
    }

    /***********************************
    MGmDbArray:
    a = dbase  structure name
    b = dbase file name string
    c = open mode string
    ************************************/
    MGmDbArray( ports_old, DATADIR PORTS_OLD_DB, "r" );
    if( errno )
    {
/*
        fprintf( stderr, "%s %s error:  MGmDbArray returned with an error\n", id, ver );
*/
        return( 1 );
    }
    MGmDbArray( ports_installed, DATADIR PORTS_INSTALLED_DB, "r" );
    if( errno )
    {
        fprintf( stderr,
        "%s %s error:  MGmDbArray returned with an error\n",
        id, ver );
        return( 1 );
    }
    MGmDbArray( port_dependencies, DATADIR PORT_DEPENDENCIES_DB, "r" );
    if( errno )
    {
        fprintf( stderr, 
            "%s %s error:  MGmDbArray returned with an error\n", 
            id, ver );
        return( 1 );
    }
    /****************************************************************************************/
    /* find the priority port to upgrade                          */
    /****************************************************************************************/
/*
    Need a major overhaul here, change to work something like:
    
    ports_old.db contains ports to be updated
    each record gets a serries of checks, if skip = 1 then no update if 0 then update
    
    resons to set skip to 1
        dependency port name not in ports_installed.db
            a. dependency never installed
            b. Xorg/XFree86 conflict
        dependency port name in ports_old.db
            a. dependency needs to be upgraded first
*/   
    /****************************************************************************************/
    /* IDX_ports_old      = Out of date rocord index                  */
    /* port_dependencies.recordQty    = dependent port data base record quantity          */
    /*    a) port_dependencies is a MGsDb structure, see libMG/src/libMG.h                */
    /****************************************************************************************/
    IDX_ports_old   = 0;
    while( IDX_ports_old < ports_old.recordQty )
    {
        skip    = 0;    /* ok to upgrade */
        IDX_port_dependencies   = 0;
        /* loop while dependecy ports index ptr is less than qty of dependency ports records */ 
        while( IDX_port_dependencies < port_dependencies.recordQty )
        {
            /* if dependency port record matches an out of date ports record then...  */
            if( !( strcmp( port_dependencies.array[IDX_port_dependencies][0], 
                    ports_old.array[IDX_ports_old][1] ) ) )
            {
                IDX_ports_old_2 = 0;
                /* see if dependency port is to be upgraded */
                /* loop through out of date port records */
                while( IDX_ports_old_2 < ports_old.recordQty )
                {
                    /* dependency port needs to be upgraded first before out of
                    date port */
                    if( !( strcmp( port_dependencies.array[IDX_port_dependencies][2], 
                            ports_old.array[IDX_ports_old_2][0] ) ) )
                    {
                        fprintf( stderr,
                            "->SKIP upgrading %s \n->reason: until %s is upgraded\n", 
                            ports_old.array[IDX_ports_old][1],
                            ports_old.array[IDX_ports_old_2][1] );
                        fflush( stderr );
                        skip    = 1;
                    }  
                    /************************************/
                    /* print any parent not in ports_installed.db */
                    /************************************/
                    IDX_ports_installed     = 0;
                    parentOK    = 0;
                    while( IDX_ports_installed < ports_installed.recordQty )
                    {
                        if( !( strcmp( port_dependencies.array[IDX_port_dependencies][1],
                            ports_installed.array[IDX_ports_installed][0] ) ) )
                        {
                            parentOK = 1;
                        }
                        IDX_ports_installed++;
                    } 
                    if( parentOK == 0 )
                    {
                        fprintf( stderr, "info: port %s built with OLD dependancy %s\n", 
                                ports_old.array[IDX_ports_old_2][0], 
                                port_dependencies.array[IDX_port_dependencies][1] );
                        fflush( stderr );
                    }
                    IDX_ports_old_2++;
                }
            }
            IDX_port_dependencies++;
        }
        if( skip == 0 )
        {
            /*
            system( "clear" );
            */
            fprintf( stderr, "------------------------------------------------\n" );
            fprintf( stderr, "OK TO UPGRADE %s\n", ports_old.array[IDX_ports_old][1] );
            fprintf( stderr, "%s %s upgrading:  %s. Executing command:\n",
                id, ver, ports_old.array[IDX_ports_old][1] );
            while( fflush( stderr ) );
            /************************************************************************/
            /*            Command "1" " make clean "          */
            /************************************************************************/
            IDX_config  = 0;
            options     = (char*)malloc(1);
            options[0]  = 0;
            while(IDX_config < config_db.recordQty)
            {
                if( strnstr( ports_old.array[IDX_ports_old][0],
                        config_db.array[IDX_config][0],
                        strlen(ports_old.array[IDX_ports_old][0]) ) != NULL )
                {
                    free( options );
                    options = (char*)malloc(strlen(config_db.array[IDX_config][1])+1);
                    strncpy( options,
                        config_db.array[IDX_config][1],
                        strlen(config_db.array[IDX_config][1])+1 );
                    break;
                }
                IDX_config++;
            }
            command = (char*)malloc( strlen( "cd " ) 
                        + strlen(PORTSDIR)
                        + strlen(ports_old.array[IDX_ports_old][0]) 
                        + strlen( "; make clean " )
                        + strlen(options)
                        + 1 );
            strncpy( command, "cd ", strlen( "cd " ) + 1 );
            strncat( command, PORTSDIR, strlen(PORTSDIR) + 1 );
            strncat( command, ports_old.array[IDX_ports_old][0], strlen(ports_old.array[IDX_ports_old][0]) + 1 );
            strncat( command, "; make clean ", strlen( "; make clean " ) + 1);
            strncat( command, options, strlen(options) + 1);
            fprintf( stdout, "%s\n", command );
            fprintf( stderr, "------------------------------------------------\n" );
            errorCode = system(command);
            if( errorCode != 0 )
            {
                fprintf( stderr, "%s %s error: make clean returned an error, cannot continue\n", id, ver );
                fflush( stderr );
exit(1);
/*
                return( 1 );
*/
            }
            free( command );
            if( errorCode != 0 )
            {
                return( 1 );
            }
            /************************************************************************/
            /*            Command "2" " make "                */
            /************************************************************************/
            command = (char*)malloc( strlen( "cd " ) 
                        + strlen(PORTSDIR)
                        + strlen(ports_old.array[IDX_ports_old][0]) 
                        + strlen( "; make " )
                        + strlen(options)
                        + 1 );
            strncpy( command, "cd ", strlen( "cd " ) + 1 );
            strncat( command, PORTSDIR, strlen(PORTSDIR) + 1 );
            strncat( command,
                ports_old.array[IDX_ports_old][0],
                strlen(ports_old.array[IDX_ports_old][0]) + 1 );
            strncat( command, "; make ", strlen( "; make " ) + 1);
            strncat( command, options, strlen(options) + 1);
            fprintf( stdout, "%s\n", command );
                        errorCode = system(command);
/*
Now here an error would indeed cause problems!!! An abort is a really good idea...
*/
                        if( errorCode != 0 )
                        {
                                fprintf( stderr, "%s %s error: make returned an error, cannot continue\n", id, ver );
                                fflush( stderr );
exit(1);
/*
                                return( 1 );
*/
                        }
            free( command );
            if( errorCode != 0 )
            {
                return( 1 );
            }
            /************************************************************************/
            /*            Command "3" " pkg_create -b "           */
            /************************************************************************/
            command = ( char* )malloc( strlen( "( cd /tmp; pkg_create -b " )
                                + strlen( ports_old.array[IDX_ports_old][1] )
                                + 3 ); 
            strncpy( command, "( cd /tmp; pkg_create -b ",
                    strlen("( cd /tmp; pkg_create -b ") + 1 );
            strncat( command, ports_old.array[IDX_ports_old][1],
                    strlen(ports_old.array[IDX_ports_old][1]) + 1 );
            strncat( command, " )", 3 );
            fprintf( stderr, "------------------------------------------------\n" );
            fprintf( stderr, " backing up installed port before removing it \n" );
            fprintf( stderr, "%s %s command: #3  %s\n", id, ver, command );
            fprintf( stderr, "------------------------------------------------\n" );
            fflush( stderr );
                        errorCode = system(command);
/*
An error here is no big deal, if for example the port is not installed it will not be able
to have a backup package made for it.
*/
/*
                        if( errorCode != 0 )
                        {
                                fprintf( stderr, "%s %s error: pkg_create returned an error, cannot continue\n", id, ver );
                                fflush( stderr );
                                return( 1 );
                        }
*/
            free( command );
            fprintf( stderr, "------------------------------------------------\n" );
            /************************************************************************/
            /*            Command "4" " make deinstall "          */
            /************************************************************************/
            command = (char*)malloc( strlen( "cd " ) 
                        + strlen(PORTSDIR)
                        + strlen(ports_old.array[IDX_ports_old][0]) 
                        + strlen( "; make deinstall " )
                        + strlen(options)
                        + 1 );
            strncpy( command, "cd ", strlen( "cd " ) + 1 );
            strncat( command, PORTSDIR, strlen(PORTSDIR) + 1 );
            strncat( command,
                ports_old.array[IDX_ports_old][0],
                strlen(ports_old.array[IDX_ports_old][0]) + 1 );
            strncat( command, "; make deinstall ", strlen( "; make deinstall " ) + 1);
            strncat( command, options, strlen(options) + 1);
            fprintf( stdout, "%s\n", command );
                        errorCode = system(command);
/*
If deinstall has started, even with an error it is better to continue and try to install
the new port
*/
/*
                        if( errorCode != 0 )
                        {
                                fprintf( stderr, "%s %s error: make deinstall returned an error, cannot continue\n", id, ver );
                                fflush( stderr );
                                return( 1 );
                        }
*/
            free( command );
            fprintf( stderr, "------------------------------------------------\n" );
            /************************************************************************/
            /*            Command "5" " make reinstall "          */
            /************************************************************************/
            command = (char*)malloc( strlen( "cd " ) 
                        + strlen(PORTSDIR)
                        + strlen(ports_old.array[IDX_ports_old][0]) 
                        + strlen( "; make reinstall " )
                        + strlen(options)
                        + 1 );
            strncpy( command, "cd ", strlen( "cd " ) + 1 );
            strncat( command, PORTSDIR, strlen(PORTSDIR) + 1 );
            strncat( command, ports_old.array[IDX_ports_old][0], strlen(ports_old.array[IDX_ports_old][0]) + 1 );
            strncat( command, "; make reinstall ", strlen( "; make reinstall " ) + 1);
            strncat( command, options, strlen(options) + 1);
            fprintf( stdout, "%s\n", command );
                        errorCode = system(command);

/*
probably good idea to abort if an error during reinstall, leave this in for now....
*/
                        if( errorCode != 0 )
                        {
                                fprintf( stderr, "%s %s error: make reinstall returned an error, cannot continue\n", id, ver );
                                fflush( stderr );
exit(1);
/*
                                return( 1 );
*/
                        }
            free( command );
            if( errorCode != 0 )
            {
                command = ( char* )malloc( strlen( "( pkg_add /tmp/.tgz )" )
                                    + strlen( ports_old.array[IDX_ports_old][1] )
                                    + 1 ); 
                strcpy( command, "( pkg_add -f /tmp/" );
                strcat( command, ports_old.array[IDX_ports_old][1] );
                strcat( command, ".tgz )" );
                fprintf( stderr, "------------------------------------------------\n" );
                fprintf( stderr, " restoring original port from backup \n" );
                fprintf( stderr, "%s %s command: #5!! ***Emergancy restore***  %s\n",
                    id, ver, command );
                fprintf( stderr, "------------------------------------------------\n" );
                fflush( stderr );
                errorCode = system(command);
                if( errorCode != 0 )
                {
                    fprintf( stderr, "%s %s error: FAILED TO RESTORE BACKUP COPY, really sorry ! cannot continue\n", id, ver );
                    fflush( stderr );
exit(1);
/*
                    return( 1 );
*/
                }
                free( command );
                fprintf( stderr, "------------------------------------------------\n" );
                fprintf( stderr, " aborting upgrade due to port install failure, \n" );
                fprintf( stderr, "  try again after port error is corrected \n" );
                fprintf( stderr, "------------------------------------------------\n" );
                fflush( stderr );
                return( 1 );
            }
            else
            {
                command = ( char* )malloc( strlen( "( cd /tmp; rm -f .tgz )" )
                                    + strlen( ports_old.array[IDX_ports_old][1] )
                                    + 1 ); 
                strcpy( command, "( cd /tmp; rm -f " );
                strcat( command, ports_old.array[IDX_ports_old][1] );
                strcat( command, ".tgz )" );
                fprintf( stderr, "---------------------------------------------------------------\n" );
                fprintf( stderr, " deleting backup copy, installation of updated port successful \n" );
                fprintf( stderr, "%s %s command: #6  %s\n",
                    id, ver, command );
                fprintf( stderr, "---------------------------------------------------------------\n" );
                fflush( stderr );
                errorCode = system(command);
/*
error is no big deal here, if the backup package wasn't made it can't be deleted...
*/
/*
                if( errorCode != 0 )
                {
                    fprintf( stderr, "%s %s error: deleting backup copy returned an error, cannot continue\n", id, ver );
                    fflush( stderr );
                    return( 1 );
                }
*/
                free( command );
            }
            fprintf( stderr, "---------------------------------------------------------------\n" );
            /************************************************************************/
            /*            Command "7" " make package "            */
            /************************************************************************/
            command = (char*)malloc( strlen( "cd " ) 
                        + strlen(PORTSDIR)
                        + strlen(ports_old.array[IDX_ports_old][0]) 
                        + strlen( "; make package " )
                        + strlen(options)
                        + 1 );
            strncpy( command, "cd ", strlen( "cd " ) + 1 );
            strncat( command, PORTSDIR, strlen(PORTSDIR) + 1 );
            strncat( command, ports_old.array[IDX_ports_old][0], strlen(ports_old.array[IDX_ports_old][0]) + 1 );
            strncat( command, "; make package ", strlen( "; make package " ) + 1);
            strncat( command, options, strlen(options) + 1);
            fprintf( stdout, "%s\n", command );
                        errorCode = system(command);
/* some ports cannot be made into packages, want to ignore errors here... */
/*
                        if( errorCode != 0 )
                        {
                                fprintf( stderr, "%s %s error: make package returned an error, cannot continue\n", id, ver );
                                fflush( stderr );
                                return( 1 );
                        }
*/
            free( command );
            fprintf( stderr, "---------------------------------------------------------------\n" );
            /************************************************************************/
            /*            Command "8" " make clean "          */
            /************************************************************************/
            command = (char*)malloc( strlen( "cd " ) 
                        + strlen(PORTSDIR)
                        + strlen(ports_old.array[IDX_ports_old][0]) 
                        + strlen( "; make clean " )
                        + strlen(options)
                        + 1 );
            strncpy( command, "cd ", strlen( "cd " ) + 1 );
            strncat( command, PORTSDIR, strlen(PORTSDIR) + 1 );
            strncat( command, ports_old.array[IDX_ports_old][0], strlen(ports_old.array[IDX_ports_old][0]) + 1 );
            strncat( command, "; make clean ", strlen( "; make clean " ) + 1);
            strncat( command, options, strlen(options) + 1);
            fprintf( stdout, "%s\n", command );
                        errorCode = system(command);
/* an abort if we can't clean??? I don't think it is a good idea, may reconsider this later... */
/*
                        if( errorCode != 0 )
                        {
                                fprintf( stderr, "%s %s error: make clean returned an error, cannot continue\n", id, ver );
                                fflush( stderr );
                                return( 1 );
                        }
*/
            free( command );
            return( 2 );
        }
        IDX_ports_old++;
    }
    /*.............................................*/
    /*
    check each port before upgrading for:
    1  a parent listed in ports_old
        1.1 skip current port
        
    2  a parent not listed in ports_installed.db
        2.1 add unlisted parent to ports_old
        2.2 skip current port

    note on #2, there is a problem where if a parent port was never installed, pmUpgrade cycles. Need to check
    and see if a parent's directory is listed in ports_installed.db.  If it is not there, that port needs to be freshly
    installed.  I am holding off on this until a pmAdd routine is written, for now, when cycling occurs, just
    manually add the parent port. See pmUpgrade(1) DIAGNOSTICS section for more details.
    */
    /*.............................................*/
    MGmDbArrayFree( ports_installed );
    MGmDbArrayFree( port_dependencies );
    MGmDbArrayFree( ports_old );
    fprintf( stderr, "pmUpgrade complete, all ports upgraded!\n" );
    fflush( stderr );
    return( 0 );
}
/******************/
/* signal handler */
/******************/
/* this doesn't seem to be working, maybe we need to fork the make commands? */
void   catch_SIGINT( int signalID )
{
    sigset_t    mask_set;   /* used to set a signal masking set. */
    sigset_t    old_set;    /* used to store the old mask set.   */
    pid_t       my_pid = getpid();

    /* re-set the signal handler again to catch_SIGSEGV, for next time */
    signal( SIGINT, catch_SIGINT );

    /* mask any further signals while we're inside the handler. */
    sigfillset(&mask_set);
    sigprocmask(SIG_SETMASK, &mask_set, &old_set);

    if( signalID == SIGINT )
    {
        /* re-set the signal handler again to catch_int, for next time */
        signal(SIGINT, catch_SIGINT);
        printf("exiting pmupdate <ctl-c pressed>\n");
        fflush(stdout);
        kill(my_pid, SIGSTOP);

    }
}