Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * generate a list of files and their metadata
 * using a given proto file.
 */
#include "all.h"

int changesonly;
char *uid;
Db *db;
Biobuf blog;
ulong now;
int n;
char **x;
int nx;
int justlog;
char *root=".";
char **match;
int nmatch;

int
ismatch(char *s)
{
        int i, len;

        if(nmatch == 0)
                return 1;
        for(i=0; i<nmatch; i++){
                if(strcmp(s, match[i]) == 0)
                        return 1;
                len = strlen(match[i]);
                if(strncmp(s, match[i], len) == 0 && s[len]=='/')
                        return 1;
        }
        return 0;
}

void
xlog(int c, char *name, Dir *d)
{
        char *dname;

        dname = d->name;
        if(strcmp(dname, name) == 0)
                dname = "-";
        if(!justlog)
                Bprint(&blog, "%lud %d ", now, n++);
        Bprint(&blog, "%c %q %q %luo %q %q %lud %lld\n",
                c, name, dname, d->mode, uid ? uid : d->uid, d->gid, d->mtime, d->length);
}

void
walk(char *new, char *old, Dir *xd, void*)
{
        int i, change, len;
        Dir od, d;

        new = unroot(new, "/");
        old = unroot(old, root);

        if(!ismatch(new))
                return;
        for(i=0; i<nx; i++){
                if(strcmp(new, x[i]) == 0)
                        return;
                len = strlen(x[i]);
                if(strncmp(new, x[i], len)==0 && new[len]=='/')
                        return;
        }

        d = *xd;
        d.name = old;
        memset(&od, 0, sizeof od);
        change = 0;
        if(markdb(db, new, &od) < 0){
                if(!changesonly){
                        xlog('a', new, &d);
                        change = 1;
                }
        }else{
                if((d.mode&DMDIR)==0 && (od.mtime!=d.mtime || od.length!=d.length)){
                        xlog('c', new, &d);
                        change = 1;
                }
                if((!uid&&strcmp(od.uid,d.uid)!=0)
                || strcmp(od.gid,d.gid)!=0 
                || od.mode!=d.mode){
                        xlog('m', new, &d);
                        change = 1;
                }
        }
        if(!justlog && change){
                if(uid)
                        d.uid = uid;
                d.muid = "mark";        /* mark bit */
                insertdb(db, new, &d);
        }
}

void
warn(char *msg, void*)
{
        char *p;

        fprint(2, "warning: %s\n", msg);

        /* find the %r in "can't open foo: %r" */
        p = strstr(msg, ": ");
        if(p)
                p += 2;

        /*
         * if the error is about a remote server failing,
         * then there's no point in continuing to look
         * for changes -- we'll think everything got deleted!
         *
         * actual errors i see are:
         *      "i/o on hungup channel" for a local hangup
         *      "i/o on hungup channel" for a timeout (yank the network wire)
         *      "'/n/sources/plan9' Hangup" for a remote hangup
         * the rest is paranoia.
         */
        if(p){
                if(cistrstr(p, "hungup") || cistrstr(p, "Hangup")
                || cistrstr(p, "rpc error")
                || cistrstr(p, "shut down")
                || cistrstr(p, "i/o")
                || cistrstr(p, "connection"))
                        sysfatal("suspected network or i/o error - bailing out");
        }
}

void
usage(void)
{
        fprint(2, "usage: replica/updatedb [-c] [-p proto] [-r root] [-t now n] [-u uid] [-x path]... db [paths]\n");
        exits("usage");
}

void
main(int argc, char **argv)
{
        char *proto;
        Avlwalk *w;
        Dir d;
        Entry *e;

        quotefmtinstall();
        proto = "/sys/lib/sysconfig/proto/allproto";
        now = time(0);
        Binit(&blog, 1, OWRITE);
        ARGBEGIN{
        case 'c':
                changesonly = 1;
                break;
        case 'l':
                justlog = 1;
                break;
        case 'p':
                proto = EARGF(usage());
                break;
        case 'r':
                root = EARGF(usage());
                break;
        case 't':
                now = strtoul(EARGF(usage()), 0, 0);
                n = atoi(EARGF(usage()));
                break;
        case 'u':
                uid = EARGF(usage());
                break;
        case 'x':
                if(nx%16 == 0)
                        x = erealloc(x, (nx+16)*sizeof(x[0]));
                x[nx++] = EARGF(usage());
                break;
        default:
                usage();
        }ARGEND

        if(argc <1)
                usage();

        match = argv+1;
        nmatch = argc-1;

        db = opendb(argv[0]);
        if(rdproto(proto, root, walk, warn, nil) < 0)
                sysfatal("rdproto: %r");

        if(!changesonly){
                w = avlwalk(db->avl);
                while(e = (Entry*)avlprev(w)){
                        if(!ismatch(e->name))
                                continue;
                        if(!e->d.mark){         /* not visited during walk */
                                memset(&d, 0, sizeof d);
                                d.name = e->d.name;
                                d.uid = e->d.uid;
                                d.gid = e->d.gid;
                                d.mtime = e->d.mtime;
                                d.mode = e->d.mode;
                                xlog('d', e->name, &d);
                                if(!justlog)
                                        removedb(db, e->name);
                        }
                }
        }

        if(Bterm(&blog) < 0)
                sysfatal("writing output: %r");

        exits(nil);
}