[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Linux-cluster] usage.txt hint



Hi,

I'm just getting started figuring this stuff out.
I tend to avoid doing things as root, so I choked.
It would be useful to mention that the build must
be done as root with the files and directories
modifiable by root.

Attached is a quick hack I threw together several
years ago for keeping build clutter separate from
source trees.  This build seems to require building
in the source tree.

Regards, Bruce
/*
 *  This file defines all the global structures and special values
 *  used in my private development library.
 *
 *  clone is free software.
 *  
 *  You may 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, or (at your option) any later version.
 *  
 *  mylib 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 mylib.  See the file "COPYING".  If not,
 *  write to:  The Free Software Foundation, Inc.,
 *             59 Temple Place - Suite 330,
 *             Boston,  MA  02111-1307, USA.
 */

#define __USE_POSIX2 1
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <stdio.h>
#include <dirent.h>
typedef struct dirent dirent_t;
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define cc_t        const char
#define scc_t       static const char
#define STMTS( s )  do{s}while(0)
#define NUL         '\0'

scc_t zAnnouncement[] =
    "clone version $Revision: 1.2 $%s\n"
    "start cloning\nFR:\t%s\nTO:";

scc_t zUsage[] =
"USAGE:  clone <source-tree> <clone-1> [ ... ]\n"
"where:  <source-tree> is a pre-existing directory and\n"
"\t<clone-n> are empty or non-existing destination directories.\n";

scc_t zFsErr[]  = "clone FAILED:  fs err %d (%s) %s ``%s''.\nFile %s line %d\n";
scc_t zFsWarn[] = "clone warning: fs err %d (%s) %s ``%s''.\n";

#define EREX(s,f)  STMTS(                       \
  fprintf(stderr,zFsErr,errno,strerror(errno),  \
          (s),(f),__FILE__,__LINE__);           \
  exit(EXIT_FAILURE);)

#define WARN(s,f)  STMTS(                       \
  fprintf(stderr,zFsWarn,errno,strerror(errno), \
          (s),(f));         \
  exit(EXIT_FAILURE);)

#define ABSURD_PATH_LEN 0x2000

typedef struct {
    ino_t      root_ino; /* i node number of root of source/dest dir   */
    char*      pzDepth;  /* current place for inserting next directory */
    char*      pzName;   /* full path root directory name */
} tNewTree;

char        zSource[ ABSURD_PATH_LEN ];
tNewTree    src = { 0, zSource, NULL };

cc_t*       pzBaseDir;
tNewTree*   paNewTrees;
int         newTreeCt;
int         verbose = 0;

tNewTree*
allocateTrees( int treeCt, cc_t** treeNames );

void
cloneTree( void );

void
initialize( int argc, cc_t* pzBase );

void
nextLayer( dirent_t* pDE );

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 */
void
usage( int exitCode )
{
    fputs( zUsage, stderr );
    exit( exitCode );
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 */
int
main( int argc, char** argv )
{
    scc_t zNoDest[] =
        "one source and one or more destination directories "
        "must be specified\n";

    switch (--argc) {
    case 1:
        if (strcmp( argv[1], "-?" ) == 0)
            usage( EXIT_SUCCESS );
        /* FALLTHROUGH */

    case 0:
        fputs( zNoDest, stderr );
        usage( EXIT_FAILURE );
        /* NOTREACHED */

    default:
        if (strcmp( argv[1], "-?" ) == 0)
            usage( EXIT_SUCCESS );
    }

    initialize( argc, *(++argv) );
    paNewTrees = allocateTrees( newTreeCt, (cc_t**)argv+1 );

    /*
     *  Now initialize a global pointer to our initial source directory
     */
    if (chdir( src.pzName ) != 0)
        EREX( "cd to source dir", src.pzName );
    if (getcwd( zSource, sizeof( zSource )) == NULL)
        EREX( "get cwd after cd to", src.pzName );
    if ((src.pzName = strdup( zSource )) == NULL) {
        errno = ENOMEM;
        EREX( "strdup", zSource );
    }

    printf( zAnnouncement, verbose ? " - VERBOSE MODE" : "", src.pzName );
    {
        tNewTree* pNT = paNewTrees;
        int       ct  = newTreeCt;
        do  {
            printf( "\t%s\n", (pNT++)->pzName );
        } while (--ct > 0);
    }
    fflush( stdout );

    cloneTree();

    return EXIT_SUCCESS;
}

void
initialize( int argc, cc_t* pzBase )
{
    scc_t zNotDir[] =
        "clone ERROR:  the first argument (%s) must be a directory\n";

    struct stat sb;

    if (stat( pzBase, &sb ) != 0)
        EREX( "stat-ing", pzBase );

    if (! S_ISDIR( sb.st_mode )) {
        fprintf( stderr, zNotDir, pzBase );
        exit( EXIT_FAILURE );
    }

    src.root_ino = sb.st_ino;

    /*
     *  Do not chdir before allocating trees.  The argument names
     *  may be relative path names and they must be resolved first.
     */
    newTreeCt  = argc-1;
    src.pzName = (char*)pzBase;

    pzBase = getenv( "VERBOSE" );
    if ((pzBase != NULL) && (*pzBase == 't')) {
        verbose = 1;
        fputs( "clone $Revision: 1.2 $ initialized\n", stderr );
        fflush( stderr );
    }
}

tNewTree*
allocateTrees( int treeCt, cc_t** treeNames )
{
    scc_t zNotDir[] =
        "clone FAIL:  clone arguments must not exist or be directories\n";
    scc_t zSkipMsg[] =
        "clone WARNING:  skipping %s copy:  it is source\n";

    tNewTree* paNT;

    {
        size_t sz = treeCt * sizeof( *paNT );
        paNT = malloc( sz );
        if (paNT == NULL) {
            errno = ENOMEM;
            EREX( "allocating", "tree trunks" );
        }
    }

    {
        struct stat sb;
        tNewTree* pNT = paNT;

        do  {
            cc_t* pzName = *(treeNames++);

            /*
             *  IF the directory already exists, clean it out (as long as it
             *  is not the source directory).
             */
            if (stat( pzName, &sb ) == 0) {
                scc_t zCmd[] = "chmod -R ugo+wx %1$s ; set -x ; rm -rf %1$s/*";

                if (! S_ISDIR( sb.st_mode )) {
                    fputs( zNotDir, stderr );
                    usage( EXIT_FAILURE );
                }
                if (sb.st_ino == src.root_ino) {
                    /*
                     *  This "output" tree will be skipped entirely.
                     */
                    fprintf( stderr, zSkipMsg, pzName );
                    newTreeCt--;
                    continue;
                }
                sprintf( zSource, zCmd, pzName );
                system( zSource );
            }

            /*
             *  Any error except not existing is bad
             */
            else if (errno != ENOENT) {
                EREX( "stat-ing", pzName );
            }

            /*
             *  Now we have to be able to make the directory
             */
            else if (mkdir( pzName, 0755 ) != 0) {
                EREX( "mkdir of", pzName );
            }

            /*
             *  Get its inode number
             */
            else if (stat( pzName, &sb ) != 0) {
                EREX( "stat-ing just created directory", pzName );
            }

            if (verbose)
                printf( "OK:  %s\n", pzName );

            /*
             *  We will be playing with full path names and "chdir" into
             *  each directory of the source tree.
             */
            pNT->pzName = malloc( ABSURD_PATH_LEN );
            if (pNT->pzName == NULL) {
                errno = ENOMEM;
                EREX( "allocating", "tree paths" );
            }
            if (realpath( pzName, pNT->pzName ) == NULL) {
                EREX( "realpath", pzName );
            }

            pNT->root_ino = sb.st_ino;
            pNT->pzDepth = pNT->pzName + strlen( pNT->pzName );
            if (pNT->pzDepth[-1] != '/')
                *(pNT->pzDepth)++ = '/';

            *(pNT->pzDepth) = NUL;
            pNT++;
        } while (--treeCt > 0);

        /*
         *  Make sure we have not clobbered our source directory.
         */
        if (stat( src.pzName, &sb ) != 0)
            EREX( "re-stat-ing source dir", src.pzName );
    }
    fflush( stdout );
    return paNT;
}

void
nextLayer( dirent_t* pDE )
{
    static char zDepth[ 16 ] = "";
    int         trimIx = strlen( zDepth );
    int         recurse = 0;
    tNewTree*   pNT = paNewTrees;
    int         ct  = newTreeCt;
    size_t      len = strlen( pDE->d_name ) + 1;

    if (strcmp( pDE->d_name, "CVS" ) == 0)
        return;

    src.pzDepth += sprintf( src.pzDepth, "%s/", pDE->d_name );
    if (chdir( pDE->d_name ) != 0) {
        fprintf( stderr, zFsWarn, errno, strerror( errno ), "chdir to",
                 zSource );
        return;
    }

    if (verbose && (trimIx < 5)) {
        /*
         *  We only print the top three directories -- even for verbose
         */
        strcpy( zDepth + trimIx, "  " );
        printf( "%s%s\n", zDepth, pDE->d_name );
        fflush( stdout );
    }

    do  {
        /*
         *  Prevent recursion into our output directories.
         */
        if (pDE->d_ino == pNT->root_ino) {
            scc_t zRecurse[] =
                "clone NOTICE:\n\tsource tree %s/%s contains\n"
                "\tcloned tree %s\n";

            fprintf( stderr, zRecurse, src.pzName, zSource, pNT->pzName );
            recurse = 1;
        }
        else if (pNT->pzDepth + len > pNT->pzName + ABSURD_PATH_LEN) {
            /*
             *  If *any* of the output directory names becomes absurdly long,
             *  then we prune them all at this level.
             */
            fprintf( stderr, "clone WARNING: names exceed %d bytes\n",
                     ABSURD_PATH_LEN );
            recurse = 1;
        }

        if (recurse == 0) {
            strcpy( pNT->pzDepth, pDE->d_name );
            pNT->pzDepth[ len-1 ] = '/';
            pNT->pzDepth[ len   ] = NUL;
        }

        /*
         *  Even if we aren't going to recurse, we add 'len' to our depth
         *  point, because we'll subtract it unconditionally later.
         */
        pNT->pzDepth += len;
        pNT++;
    } while (--ct > 0);

    if (recurse == 0) {
        ct = newTreeCt;
        pNT = paNewTrees;
        do  {
            if (mkdir( pNT->pzName, 0755 ) != 0)
                EREX( "mkdir", pNT->pzName );

            pNT++;
        } while (--ct > 0);

        cloneTree();
    }

    if (verbose)
        zDepth[ trimIx ] = NUL;

    /*
     *  We may or may not have done any work, but we always add 'len' to
     *  the depth point for all our output tree descriptors.  Subtract it off.
     */
    ct = newTreeCt;
    pNT = paNewTrees;
    do  {
        (pNT++)->pzDepth -= len;
    } while (--ct > 0);
}

void
cloneTree( void )
{
    DIR*  dirp;
    int   dirFd;
    char* pzCurrDepth = src.pzDepth;
    tNewTree* pNT;

    /*
     *  Before entering this procedure, the current directory is set
     *  to the source directory we are currently scanning.  Open this
     *  directory *both* as a normal file and as a directory.  We do the
     *  former because "fchdir" operates on this descriptor, and not on
     *  the DIR* pointer.  (Some call ought to do that, tho.)
     */
    dirFd = open( ".", O_RDONLY );
    if (dirFd < 0)
        EREX( "open of dir", zSource );

    dirp = opendir( "." );
    if (dirp == NULL)
        EREX( "opendir", zSource );

    /*
     *  Always clear errno before calling readdir.  It is the only way
     *  one can reliably determine EOF.
     */
    for (errno = 0;;errno = 0) {
        dirent_t* pDE = readdir( dirp );
        struct stat sb;

        /*
         *  Whether due to end of directory or an error, if we don't get
         *  an entry from readdir, then stop this directory and return.
         */
        if (pDE == NULL)
            break;

        /*
         *  Skip "." and ".." entries
         */
        if (pDE->d_name[0] == '.') {
            if (pDE->d_name[1] == NUL)
                continue;
            if ((pDE->d_name[1] == '.') && (pDE->d_name[2] == NUL))
                continue;
        }

        /*
         *  Only handle regular files and directories.  No sym links.
         */
        if (stat( pDE->d_name, &sb ) != 0) {
            fprintf( stderr, zFsWarn, errno, strerror( errno ), "stat of entry",
                     pDE->d_name );
        }

        else switch (sb.st_mode & S_IFMT) {
        case S_IFREG:
        {
            int ix = 0;

            for (pNT = paNewTrees; ix < newTreeCt; ix++, pNT++) {
                strcpy( pNT->pzDepth, pDE->d_name );
                if (link( pDE->d_name, pNT->pzName ) != 0) {
                    char z[ 128 ];
                    snprintf( z, sizeof(z), "linking %s to", pDE->d_name );
                    fprintf( stderr, zFsWarn, errno, strerror( errno ),
                             z, pNT->pzName );
                }
            }
            break;
        }

        case S_IFDIR:
            /*
             *  Do the next directory layer and then "cd" back to our
             *  current directory from whereever we may have wandered to.
             */
            nextLayer( pDE );

            src.pzDepth = pzCurrDepth;
            if (fchdir( dirFd ) != 0) {
                *src.pzDepth = NUL;
                EREX( "fchdir back to", zSource );
            }
            break;

        default:
            fprintf( stderr, zFsWarn, ENOTDIR, strerror( ENOTDIR ),
                     "wrong file type", pDE->d_name );
            break;
        }
    }

    /*
     *  IF we did not stop due to EOF (i.e., errno was left unchanged),
     */
    if (errno != 0)
        fprintf( stderr, zFsWarn, errno, strerror( errno ),
                 "reading dir entry from", zSource );

    closedir( dirp );
    close( dirFd );
}
/*
 * Local Variables:
 * c-file-style: "stroustrup"
 * indent-tabs-mode: nil
 * tab-width: 4
 * End:
 * end of clone.c */

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]