/*
 * Copyright (c) 2003, 2004 Roger Seguin <roger_seguin@msn.com>
 *				http://rmlx.dyndns.org
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * 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.
 */

static const char *const RCSId =
    "$Id: wipeswp.C,v 1.4 2004/08/15 02:05:13 RogerSeguin Exp $"
    "\nCopyright (c) 2003, 2004 Roger Seguin <roger_seguin@msn.com,"
    " http://rmlx.dyndns.org>";

/*
Description:
	Wipe (zero) a swap device (either a partition or a file)
	Usage:	wipeswp <swap device>
		wipeswp -h
	Example: Linux system shutdown
		#!/bin/bash
		SWAPS=`awk '! /^Filename/ { print $1 }' /proc/swaps`
		for swp in $SWAPS ; do
			swapoff $swp
			wipeswp $swp
		done
*/


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


namespace {
    const size_t    SWPHDRLEN = 4 * 1024;	// Swap header length
    const int       PATTERN_BYTE = 0;	// Swap filled with...

    const char     *m_pcThisPrgName;
    FILE           *m_pFSwapDev = 0;
};


/**********************************************************************/
int WipeSwapPartition (char *p_pcSwapDev)
/**********************************************************************/
{
    const size_t    BUFFERSIZE = 1024 * 1024;
    char            acBuffer[BUFFERSIZE];

    m_pFSwapDev = fopen (p_pcSwapDev, "r+");
    if (!m_pFSwapDev) {
        perror (p_pcSwapDev);
        return (errno);
    }
    if (fseek (m_pFSwapDev, SWPHDRLEN, SEEK_SET) < 0) {
        perror (p_pcSwapDev);
        return (errno);
    }
    memset (acBuffer, PATTERN_BYTE, BUFFERSIZE);
    while (fwrite (&acBuffer, 1, BUFFERSIZE, m_pFSwapDev) == BUFFERSIZE);
    fclose (m_pFSwapDev);
    sync ();
    return (0);
}				// WipeSwapPartition()


/**********************************************************************/
int WipeSwapFile (char *p_pcSwapFile, off_t p_FileSize)
/**********************************************************************/
 /* Actually regenerate the swap file */
{
    char            acCmd[1024];
    int             status;

    sprintf (acCmd, "/bin/dd if=/dev/zero of=%s bs=1024 count=%d;"
             "/sbin/mkswap -c %s",
             p_pcSwapFile, p_FileSize / 1024, p_pcSwapFile);
    status = system (acCmd);
    return (WEXITSTATUS (status));
}				// WipeSwapFile()


/**********************************************************************/
int Wipe (char *p_pcSwapDev)
/**********************************************************************/
{
    struct stat     oDevStatus;
    int             status;

    status = stat (p_pcSwapDev, &oDevStatus);
    if (status == -1) {
        perror (p_pcSwapDev);
        return (-1);
    }

    /* Wipe swap file/partition */
    if (S_ISREG (oDevStatus.st_mode) && (oDevStatus.st_size > 0))
        return (WipeSwapFile (p_pcSwapDev, oDevStatus.st_size));
    if (S_ISBLK (oDevStatus.st_mode))
        return (WipeSwapPartition (p_pcSwapDev));

    fprintf (stderr, " ** Error: %s is not a swap device\n", p_pcSwapDev);
    return (-1);
}				// Wipe()


/**********************************************************************/
void Interrupt (int p_iSignal, siginfo_t *, void *)
/**********************************************************************/
 /* Signal handler */
{
    fprintf (stderr, "%s terminated by signal %d\n", m_pcThisPrgName,
             p_iSignal);
    if (m_pFSwapDev)
        fclose (m_pFSwapDev);
    sync ();
    exit (p_iSignal);
}				// Interrupt()


/**********************************************************************/
const char     *BaseName (const char *const p_pcFullName)
 /* Return the file name, leading directory stripped */
/**********************************************************************/
{
    const char     *const pc = rindex (p_pcFullName, '/');
    return (pc ? pc + 1 : p_pcFullName);
}				// BaseName()


/**********************************************************************/
int main (int argc, char *argv[])
/**********************************************************************/
{
    int             fError = 0, // 
                    fUsage = 0;
    struct sigaction oSigaction;
    int             status;

    m_pcThisPrgName = BaseName (argv[0]);

    for (char c; (c = getopt (argc, argv, "hV")) != EOF;)
        switch (c) {
            case 'V':	/* Version */
            case 'h':	/* Help */
                fUsage = 1;
                break;
            case '?':
            default:
                fError = 1;
        }
    fUsage = (fUsage || ((optind + 1) != argc) || fError);

    if (fUsage) {
        FILE           *pFOut = (fError ? stderr : stdout);

        fprintf (stdout, "%s\n\tWipe a swap device\n", RCSId + 5);
        fprintf (pFOut, "Usage:\t%s swap_device\n", m_pcThisPrgName);
        fprintf (pFOut, "\t%s [-h|-V]\n", m_pcThisPrgName);
        return (fError ? ~0 : 0);
    }

    memset (&oSigaction, 0, sizeof (oSigaction));
    oSigaction.sa_sigaction = Interrupt;
    sigaction (SIGHUP, &oSigaction, 0);
    sigaction (SIGINT, &oSigaction, 0);
    sigaction (SIGTERM, &oSigaction, 0);

    status = Wipe (argv[optind]);
    return (status);
}				// main()


/*
 $Log: wipeswp.C,v $
 Revision 1.4  2004/08/15 02:05:13  RogerSeguin

 Revision 1.1  2003/02/05 11:48:30  RogerSeguin
 Initial revision

*/