Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include        <u.h>
#include        <libc.h>

#define MINUTE(x)       ((long)(x)*60L)
#define HOUR(x)         (MINUTE(x)*60L)
#define YEAR(x)         (HOUR(x)*24L*360L)

int     verb;
int     uflag;
int     force;
int     diff;
char*   sflag;
char*   dargv[20] = {
        "diff",
};
int     ndargv = 1;

void    usage(void);
void    ysearch(char*, char*);
long    starttime(char*);
void    lastbefore(ulong, char*, char*, char*);
char*   prtime(ulong);
void    darg(char*);

void
main(int argc, char *argv[])
{
        int i;
        char *ndump;

        ndump = nil;
        ARGBEGIN {
        default:
                usage();
        case 'a':
                darg("-a");
                break;
        case 'b':
                darg("-b");
                break;
        case 'c':
                darg("-c");
                break;
        case 'e':
                darg("-e");
                break;
        case 'm':
                darg("-m");
                break;
        case 'n':
                darg("-n");
                break;
        case 'w':
                darg("-w");
                break;
        case 'D':
                diff = 1;
                break;
        case 'd':
                ndump = ARGF();
                break;
        case 'f':
                force = 1;
                break;
        case 's':
                sflag = ARGF();
                break;
        case 'u':
                uflag = 1;
                break;
        case 'v':
                verb = 1;
                break;
        } ARGEND

        if(argc == 0)
                usage();

        for(i=0; i<argc; i++)
                ysearch(argv[i], ndump);
        exits(0);
}

void
darg(char* a)
{
        if(ndargv >= nelem(dargv)-3)
                return;
        dargv[ndargv++] = a;
}

void
usage(void)
{
        fprint(2, "usage: history [-bDfuv] [-d dumpfilesystem] [-s yyyymmdd] files\n");
        exits("usage");
}

void
ysearch(char *file, char *ndump)
{
        char fil[400], buf[500], nbuf[100], pair[2][500], *p;
        Tm *tm;
        Waitmsg *w;
        Dir *dir, *d;
        ulong otime, dt;
        int toggle, started, missing;

        fil[0] = 0;
        if(file[0] != '/') {
                getwd(strchr(fil, 0), 100);
                strcat(fil, "/");
        }
        strcat(fil, file);
        if(memcmp(fil, "/n/", 3) == 0){
                p = strchr(fil+3, '/');
                if(p == nil)
                        p = fil+strlen(fil);
                if(ndump == nil){
                        if(p-fil >= sizeof nbuf-10){
                                fprint(2, "%s: dump name too long", fil);
                                return;
                        }
                        memmove(nbuf, fil+3, p-(fil+3));
                        nbuf[p-(fil+3)] = 0;
                        strcat(nbuf, "dump");
                        ndump = nbuf;
                }
                memmove(fil, p, strlen(p)+1);
        }
        if(ndump == nil)
                ndump = "dump";

        tm = localtime(time(0));
        snprint(buf, sizeof buf, "/n/%s/%.4d/", ndump, tm->year+1900);
        if(access(buf, AREAD) < 0) {
                if(verb)
                        print("mounting dump %s\n", ndump);
                if(rfork(RFFDG|RFPROC) == 0) {
                        execl("/bin/rc", "rc", "9fs", ndump, nil);
                        exits(0);
                }
                w = wait();
                if(w == nil){
                        fprint(2, "history: wait error: %r\n");
                        exits("wait");
                }
                if(w->msg[0] != '\0'){
                        fprint(2, "9fs failed: %s\n", w->msg);
                        exits(w->msg);
                }
                free(w);
        }

        started = 0;
        dir = dirstat(file);
        if(dir == nil)
                fprint(2, "history: warning: %s does not exist\n", file);
        else{
                print("%s %s %lld [%s]\n", prtime(dir->mtime), file, dir->length, dir->muid);
                started = 1;
                strecpy(pair[1], pair[1]+sizeof pair[1], file);
        }
        free(dir);
        otime = starttime(sflag);
        toggle = 0;
        for(;;) {
                lastbefore(otime, fil, buf, ndump);
                dir = dirstat(buf);
                if(dir == nil) {
                        if(!force)
                                return;
                        dir = malloc(sizeof(Dir));
                        nulldir(dir);
                        dir->mtime = otime + 1;
                }
                dt = HOUR(12);
                missing = 0;
                while(otime <= dir->mtime){
                        if(verb)
                                print("backup %ld, %ld\n", dir->mtime, otime-dt);
                        lastbefore(otime-dt, fil, buf, ndump);
                        d = dirstat(buf);
                        if(d == nil){
                                if(!force)
                                        return;
                                if(!missing)
                                        print("removed %s\n", buf);
                                missing = 1;
                        }else{
                                free(dir);
                                dir = d;
                        }
                        dt += HOUR(12);
                }
                strcpy(pair[toggle], buf);
                if(diff && started){
                        switch(rfork(RFFDG|RFPROC)){
                        case 0:
                                dargv[ndargv] = pair[toggle];
                                dargv[ndargv+1] = pair[toggle ^ 1];
                                exec("/bin/diff", dargv);
                                fprint(2, "can't exec diff: %r\n");
                                exits(0);
                        case -1:
                                fprint(2, "can't fork diff: %r\n");
                                break;
                        default:
                                while(waitpid() != -1)
                                        ;
                                break;
                        }
                }
                print("%s %s %lld [%s]\n", prtime(dir->mtime), buf, dir->length, dir->muid);
                toggle ^= 1;
                started = 1;
                otime = dir->mtime;
                free(dir);
        }
}

void
lastbefore(ulong t, char *f, char *b, char *ndump)
{
        Tm *tm;
        Dir *dir;
        int vers, try;
        ulong t0, mtime;
        int i, n, fd;

        t0 = t;
        if(verb)
                print("%ld lastbefore %s\n", t0, f);
        mtime = 0;
        for(try=0; try<30; try++) {
                tm = localtime(t);
                sprint(b, "/n/%s/%.4d/%.2d%.2d", ndump,
                        tm->year+1900, tm->mon+1, tm->mday);
                dir = dirstat(b);
                if(dir){
                        mtime = dir->mtime;
                        free(dir);
                }
                if(dir==nil || mtime > t0) {
                        if(verb)
                                print("%ld earlier %s\n", mtime, b);
                        t -= HOUR(24);
                        continue;
                }
                if(strstr(ndump, "snap")){
                        fd = open(b, OREAD);
                        if(fd < 0)
                                continue;
                        n = dirreadall(fd, &dir);
                        close(fd);
                        if(n == 0)
                                continue;
                        for(i = n-1; i > 0; i--){
                                if(dir[i].mtime > t0)
                                        break;
                        }
                        sprint(b, "/n/%s/%.4d/%.2d%.2d/%s%s", ndump,
                                tm->year+1900, tm->mon+1, tm->mday, dir[i].name, f);
                        free(dir);
                } else {
                        for(vers=0;; vers++) {
                                sprint(b, "/n/%s/%.4d/%.2d%.2d%d", ndump,
                                        tm->year+1900, tm->mon+1, tm->mday, vers+1);
                                dir = dirstat(b);
                                if(dir){
                                        mtime = dir->mtime;
                                        free(dir);
                                }
                                if(dir==nil || mtime > t0)
                                        break;
                                if(verb)
                                        print("%ld later %s\n", mtime, b);
                        }
                        sprint(b, "/n/%s/%.4d/%.2d%.2d%s", ndump,
                                tm->year+1900, tm->mon+1, tm->mday, f);
                        if(vers)
                                sprint(b, "/n/%s/%.4d/%.2d%.2d%d%s", ndump,
                                        tm->year+1900, tm->mon+1, tm->mday, vers, f);
                }
                return;
        }
        strcpy(b, "XXX");       /* error */
}

char*
prtime(ulong t)
{
        static char buf[100];
        char *b;
        Tm *tm;

        if(uflag)
                tm = gmtime(t);
        else
                tm = localtime(t);
        b = asctime(tm);
        memcpy(buf, b+4, 24);
        buf[24] = 0;
        return buf;
}

long
starttime(char *s)
{
        Tm *tm;
        long t, dt;
        int i, yr, mo, da;

        t = time(0);
        if(s == 0)
                return t;
        for(i=0; s[i]; i++)
                if(s[i] < '0' || s[i] > '9') {
                        fprint(2, "bad start time: %s\n", s);
                        return t;
                }
        if(strlen(s)==6){
                yr = (s[0]-'0')*10 + s[1]-'0';
                mo = (s[2]-'0')*10 + s[3]-'0' - 1;
                da = (s[4]-'0')*10 + s[5]-'0';
                if(yr < 70)
                        yr += 100;
        }else if(strlen(s)==8){
                yr = (((s[0]-'0')*10 + s[1]-'0')*10 + s[2]-'0')*10 + s[3]-'0';
                yr -= 1900;
                mo = (s[4]-'0')*10 + s[5]-'0' - 1;
                da = (s[6]-'0')*10 + s[7]-'0';
        }else{
                fprint(2, "bad start time: %s\n", s);
                return t;
        }
        t = 0;
        dt = YEAR(10);
        for(i=0; i<50; i++) {
                tm = localtime(t+dt);
                if(yr > tm->year ||
                  (yr == tm->year && mo > tm->mon) ||
                  (yr == tm->year && mo == tm->mon) && da > tm->mday) {
                        t += dt;
                        continue;
                }
                dt /= 2;
                if(dt == 0)
                        break;
        }
        t += HOUR(12);  /* .5 day to get to noon of argument */
        return t;
}