/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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*); voidmain(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);} voiddarg(char* a){ if(ndargv >= nelem(dargv)-3) return; dargv[ndargv++] = a;} voidusage(void){ fprint(2, "usage: history [-bDfuv] [-d dumpfilesystem] [-s yyyymmdd] files\n"); exits("usage");} voidysearch(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); }} voidlastbefore(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;} longstarttime(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;}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#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;
}