xref: /plan9/sys/src/cmd/history.c (revision a23bc242239d04784669e65edcfd537bcc267e00)
17dd7cddfSDavid du Colombier #include	<u.h>
27dd7cddfSDavid du Colombier #include	<libc.h>
37dd7cddfSDavid du Colombier 
47dd7cddfSDavid du Colombier #define	MINUTE(x)	((long)(x)*60L)
57dd7cddfSDavid du Colombier #define	HOUR(x)		(MINUTE(x)*60L)
67dd7cddfSDavid du Colombier #define	YEAR(x)		(HOUR(x)*24L*360L)
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier int	verb;
97dd7cddfSDavid du Colombier int	uflag;
107fd46167SDavid du Colombier int	force;
117dd7cddfSDavid du Colombier int	diff;
127dd7cddfSDavid du Colombier char*	sflag;
13*a23bc242SDavid du Colombier char*	dargv[20] = {
14*a23bc242SDavid du Colombier 	"diff",
15*a23bc242SDavid du Colombier };
16*a23bc242SDavid du Colombier int	ndargv = 1;
177dd7cddfSDavid du Colombier 
18a7529a1dSDavid du Colombier void	usage(void);
19f8e525acSDavid du Colombier void	ysearch(char*, char*);
207dd7cddfSDavid du Colombier long	starttime(char*);
21f8e525acSDavid du Colombier void	lastbefore(ulong, char*, char*, char*);
227dd7cddfSDavid du Colombier char*	prtime(ulong);
23*a23bc242SDavid du Colombier void	darg(char*);
247dd7cddfSDavid du Colombier 
257dd7cddfSDavid du Colombier void
main(int argc,char * argv[])267dd7cddfSDavid du Colombier main(int argc, char *argv[])
277dd7cddfSDavid du Colombier {
287dd7cddfSDavid du Colombier 	int i;
29f8e525acSDavid du Colombier 	char *ndump;
307dd7cddfSDavid du Colombier 
31f8e525acSDavid du Colombier 	ndump = nil;
327dd7cddfSDavid du Colombier 	ARGBEGIN {
337dd7cddfSDavid du Colombier 	default:
34a7529a1dSDavid du Colombier 		usage();
35*a23bc242SDavid du Colombier 	case 'a':
36*a23bc242SDavid du Colombier 		darg("-a");
377dd7cddfSDavid du Colombier 		break;
38*a23bc242SDavid du Colombier 	case 'b':
39*a23bc242SDavid du Colombier 		darg("-b");
407fd46167SDavid du Colombier 		break;
41*a23bc242SDavid du Colombier 	case 'c':
42*a23bc242SDavid du Colombier 		darg("-c");
43*a23bc242SDavid du Colombier 		break;
44*a23bc242SDavid du Colombier 	case 'e':
45*a23bc242SDavid du Colombier 		darg("-e");
46*a23bc242SDavid du Colombier 		break;
47*a23bc242SDavid du Colombier 	case 'm':
48*a23bc242SDavid du Colombier 		darg("-m");
49*a23bc242SDavid du Colombier 		break;
50*a23bc242SDavid du Colombier 	case 'n':
51*a23bc242SDavid du Colombier 		darg("-n");
52*a23bc242SDavid du Colombier 		break;
53*a23bc242SDavid du Colombier 	case 'w':
54*a23bc242SDavid du Colombier 		darg("-w");
557dd7cddfSDavid du Colombier 		break;
567dd7cddfSDavid du Colombier 	case 'D':
577dd7cddfSDavid du Colombier 		diff = 1;
587dd7cddfSDavid du Colombier 		break;
59*a23bc242SDavid du Colombier 	case 'd':
60*a23bc242SDavid du Colombier 		ndump = ARGF();
61*a23bc242SDavid du Colombier 		break;
62*a23bc242SDavid du Colombier 	case 'f':
63*a23bc242SDavid du Colombier 		force = 1;
647dd7cddfSDavid du Colombier 		break;
657dd7cddfSDavid du Colombier 	case 's':
667dd7cddfSDavid du Colombier 		sflag = ARGF();
677dd7cddfSDavid du Colombier 		break;
687dd7cddfSDavid du Colombier 	case 'u':
697dd7cddfSDavid du Colombier 		uflag = 1;
707dd7cddfSDavid du Colombier 		break;
71*a23bc242SDavid du Colombier 	case 'v':
72*a23bc242SDavid du Colombier 		verb = 1;
73*a23bc242SDavid du Colombier 		break;
747dd7cddfSDavid du Colombier 	} ARGEND
757dd7cddfSDavid du Colombier 
76a7529a1dSDavid du Colombier 	if(argc == 0)
77a7529a1dSDavid du Colombier 		usage();
787dd7cddfSDavid du Colombier 
79f8e525acSDavid du Colombier 	for(i=0; i<argc; i++)
80f8e525acSDavid du Colombier 		ysearch(argv[i], ndump);
81f8e525acSDavid du Colombier 	exits(0);
82f8e525acSDavid du Colombier }
83f8e525acSDavid du Colombier 
84f8e525acSDavid du Colombier void
darg(char * a)85*a23bc242SDavid du Colombier darg(char* a)
86*a23bc242SDavid du Colombier {
87*a23bc242SDavid du Colombier 	if(ndargv >= nelem(dargv)-3)
88*a23bc242SDavid du Colombier 		return;
89*a23bc242SDavid du Colombier 	dargv[ndargv++] = a;
90*a23bc242SDavid du Colombier }
91*a23bc242SDavid du Colombier 
92*a23bc242SDavid du Colombier void
usage(void)93a7529a1dSDavid du Colombier usage(void)
94a7529a1dSDavid du Colombier {
95a7529a1dSDavid du Colombier 	fprint(2, "usage: history [-bDfuv] [-d dumpfilesystem] [-s yyyymmdd] files\n");
96a7529a1dSDavid du Colombier 	exits("usage");
97a7529a1dSDavid du Colombier }
98a7529a1dSDavid du Colombier 
99a7529a1dSDavid du Colombier void
ysearch(char * file,char * ndump)100f8e525acSDavid du Colombier ysearch(char *file, char *ndump)
101f8e525acSDavid du Colombier {
102f8e525acSDavid du Colombier 	char fil[400], buf[500], nbuf[100], pair[2][500], *p;
103f8e525acSDavid du Colombier 	Tm *tm;
104f8e525acSDavid du Colombier 	Waitmsg *w;
105f8e525acSDavid du Colombier 	Dir *dir, *d;
106f8e525acSDavid du Colombier 	ulong otime, dt;
107f8e525acSDavid du Colombier 	int toggle, started, missing;
108f8e525acSDavid du Colombier 
10959c21d95SDavid du Colombier 	fil[0] = 0;
11059c21d95SDavid du Colombier 	if(file[0] != '/') {
11159c21d95SDavid du Colombier 		getwd(strchr(fil, 0), 100);
11259c21d95SDavid du Colombier 		strcat(fil, "/");
11359c21d95SDavid du Colombier 	}
11459c21d95SDavid du Colombier 	strcat(fil, file);
11524e2e655SDavid du Colombier 	if(memcmp(fil, "/n/", 3) == 0){
11659c21d95SDavid du Colombier 		p = strchr(fil+3, '/');
117f8e525acSDavid du Colombier 		if(p == nil)
11859c21d95SDavid du Colombier 			p = fil+strlen(fil);
11959c21d95SDavid du Colombier 		if(ndump == nil){
12059c21d95SDavid du Colombier 			if(p-fil >= sizeof nbuf-10){
12159c21d95SDavid du Colombier 				fprint(2, "%s: dump name too long", fil);
122f8e525acSDavid du Colombier 				return;
123f8e525acSDavid du Colombier 			}
12459c21d95SDavid du Colombier 			memmove(nbuf, fil+3, p-(fil+3));
12559c21d95SDavid du Colombier 			nbuf[p-(fil+3)] = 0;
126f8e525acSDavid du Colombier 			strcat(nbuf, "dump");
127f8e525acSDavid du Colombier 			ndump = nbuf;
128f8e525acSDavid du Colombier 		}
12959c21d95SDavid du Colombier 		memmove(fil, p, strlen(p)+1);
13059c21d95SDavid du Colombier 	}
13159c21d95SDavid du Colombier 	if(ndump == nil)
13259c21d95SDavid du Colombier 		ndump = "dump";
133f8e525acSDavid du Colombier 
1347dd7cddfSDavid du Colombier 	tm = localtime(time(0));
1354d44ba9bSDavid du Colombier 	snprint(buf, sizeof buf, "/n/%s/%.4d/", ndump, tm->year+1900);
1369a747e4fSDavid du Colombier 	if(access(buf, AREAD) < 0) {
1377dd7cddfSDavid du Colombier 		if(verb)
138f8e525acSDavid du Colombier 			print("mounting dump %s\n", ndump);
1397dd7cddfSDavid du Colombier 		if(rfork(RFFDG|RFPROC) == 0) {
140f19e7b74SDavid du Colombier 			execl("/bin/rc", "rc", "9fs", ndump, nil);
1417dd7cddfSDavid du Colombier 			exits(0);
1427dd7cddfSDavid du Colombier 		}
1439a747e4fSDavid du Colombier 		w = wait();
1449a747e4fSDavid du Colombier 		if(w == nil){
1459a747e4fSDavid du Colombier 			fprint(2, "history: wait error: %r\n");
1469a747e4fSDavid du Colombier 			exits("wait");
1479a747e4fSDavid du Colombier 		}
1489a747e4fSDavid du Colombier 		if(w->msg[0] != '\0'){
1499a747e4fSDavid du Colombier 			fprint(2, "9fs failed: %s\n", w->msg);
1509a747e4fSDavid du Colombier 			exits(w->msg);
1519a747e4fSDavid du Colombier 		}
1529a747e4fSDavid du Colombier 		free(w);
1537dd7cddfSDavid du Colombier 	}
1547dd7cddfSDavid du Colombier 
1559a747e4fSDavid du Colombier 	started = 0;
1569a747e4fSDavid du Colombier 	dir = dirstat(file);
1579a747e4fSDavid du Colombier 	if(dir == nil)
1589a747e4fSDavid du Colombier 		fprint(2, "history: warning: %s does not exist\n", file);
1599a747e4fSDavid du Colombier 	else{
1609a747e4fSDavid du Colombier 		print("%s %s %lld [%s]\n", prtime(dir->mtime), file, dir->length, dir->muid);
1619a747e4fSDavid du Colombier 		started = 1;
1624d44ba9bSDavid du Colombier 		strecpy(pair[1], pair[1]+sizeof pair[1], file);
1639a747e4fSDavid du Colombier 	}
1649a747e4fSDavid du Colombier 	free(dir);
1659a747e4fSDavid du Colombier 	otime = starttime(sflag);
1667dd7cddfSDavid du Colombier 	toggle = 0;
1677dd7cddfSDavid du Colombier 	for(;;) {
168f8e525acSDavid du Colombier 		lastbefore(otime, fil, buf, ndump);
1699a747e4fSDavid du Colombier 		dir = dirstat(buf);
1707fd46167SDavid du Colombier 		if(dir == nil) {
1717fd46167SDavid du Colombier 			if(!force)
1727dd7cddfSDavid du Colombier 				return;
1737fd46167SDavid du Colombier 			dir = malloc(sizeof(Dir));
1747fd46167SDavid du Colombier 			nulldir(dir);
1757fd46167SDavid du Colombier 			dir->mtime = otime + 1;
1767fd46167SDavid du Colombier 		}
1777dd7cddfSDavid du Colombier 		dt = HOUR(12);
1787fd46167SDavid du Colombier 		missing = 0;
1799a747e4fSDavid du Colombier 		while(otime <= dir->mtime){
1807dd7cddfSDavid du Colombier 			if(verb)
1819a747e4fSDavid du Colombier 				print("backup %ld, %ld\n", dir->mtime, otime-dt);
182f8e525acSDavid du Colombier 			lastbefore(otime-dt, fil, buf, ndump);
1837fd46167SDavid du Colombier 			d = dirstat(buf);
1847fd46167SDavid du Colombier 			if(d == nil){
1857fd46167SDavid du Colombier 				if(!force)
1867dd7cddfSDavid du Colombier 					return;
1877fd46167SDavid du Colombier 				if(!missing)
1887fd46167SDavid du Colombier 					print("removed %s\n", buf);
1897fd46167SDavid du Colombier 				missing = 1;
1907fd46167SDavid du Colombier 			}else{
1917fd46167SDavid du Colombier 				free(dir);
1927fd46167SDavid du Colombier 				dir = d;
1937fd46167SDavid du Colombier 			}
1947dd7cddfSDavid du Colombier 			dt += HOUR(12);
1957dd7cddfSDavid du Colombier 		}
1967dd7cddfSDavid du Colombier 		strcpy(pair[toggle], buf);
1977dd7cddfSDavid du Colombier 		if(diff && started){
1987dd7cddfSDavid du Colombier 			switch(rfork(RFFDG|RFPROC)){
1997dd7cddfSDavid du Colombier 			case 0:
200*a23bc242SDavid du Colombier 				dargv[ndargv] = pair[toggle];
201*a23bc242SDavid du Colombier 				dargv[ndargv+1] = pair[toggle ^ 1];
202*a23bc242SDavid du Colombier 				exec("/bin/diff", dargv);
2037dd7cddfSDavid du Colombier 				fprint(2, "can't exec diff: %r\n");
2047dd7cddfSDavid du Colombier 				exits(0);
2057dd7cddfSDavid du Colombier 			case -1:
2067dd7cddfSDavid du Colombier 				fprint(2, "can't fork diff: %r\n");
2077dd7cddfSDavid du Colombier 				break;
2087dd7cddfSDavid du Colombier 			default:
2099a747e4fSDavid du Colombier 				while(waitpid() != -1)
2107dd7cddfSDavid du Colombier 					;
2117dd7cddfSDavid du Colombier 				break;
2127dd7cddfSDavid du Colombier 			}
2137dd7cddfSDavid du Colombier 		}
2149a747e4fSDavid du Colombier 		print("%s %s %lld [%s]\n", prtime(dir->mtime), buf, dir->length, dir->muid);
2157dd7cddfSDavid du Colombier 		toggle ^= 1;
2167dd7cddfSDavid du Colombier 		started = 1;
2179a747e4fSDavid du Colombier 		otime = dir->mtime;
2187fd46167SDavid du Colombier 		free(dir);
2197dd7cddfSDavid du Colombier 	}
2207dd7cddfSDavid du Colombier }
2217dd7cddfSDavid du Colombier 
2227dd7cddfSDavid du Colombier void
lastbefore(ulong t,char * f,char * b,char * ndump)223f8e525acSDavid du Colombier lastbefore(ulong t, char *f, char *b, char *ndump)
2247dd7cddfSDavid du Colombier {
2257dd7cddfSDavid du Colombier 	Tm *tm;
2269a747e4fSDavid du Colombier 	Dir *dir;
2277dd7cddfSDavid du Colombier 	int vers, try;
2289a747e4fSDavid du Colombier 	ulong t0, mtime;
22925747282SDavid du Colombier 	int i, n, fd;
2307dd7cddfSDavid du Colombier 
2317dd7cddfSDavid du Colombier 	t0 = t;
2327dd7cddfSDavid du Colombier 	if(verb)
2337dd7cddfSDavid du Colombier 		print("%ld lastbefore %s\n", t0, f);
2349a747e4fSDavid du Colombier 	mtime = 0;
235da51d93aSDavid du Colombier 	for(try=0; try<30; try++) {
2367dd7cddfSDavid du Colombier 		tm = localtime(t);
2377dd7cddfSDavid du Colombier 		sprint(b, "/n/%s/%.4d/%.2d%.2d", ndump,
2387dd7cddfSDavid du Colombier 			tm->year+1900, tm->mon+1, tm->mday);
2399a747e4fSDavid du Colombier 		dir = dirstat(b);
2409a747e4fSDavid du Colombier 		if(dir){
2419a747e4fSDavid du Colombier 			mtime = dir->mtime;
2429a747e4fSDavid du Colombier 			free(dir);
2439a747e4fSDavid du Colombier 		}
2449a747e4fSDavid du Colombier 		if(dir==nil || mtime > t0) {
2457dd7cddfSDavid du Colombier 			if(verb)
2469a747e4fSDavid du Colombier 				print("%ld earlier %s\n", mtime, b);
2477dd7cddfSDavid du Colombier 			t -= HOUR(24);
2487dd7cddfSDavid du Colombier 			continue;
2497dd7cddfSDavid du Colombier 		}
25025747282SDavid du Colombier 		if(strstr(ndump, "snap")){
25125747282SDavid du Colombier 			fd = open(b, OREAD);
25225747282SDavid du Colombier 			if(fd < 0)
25325747282SDavid du Colombier 				continue;
25425747282SDavid du Colombier 			n = dirreadall(fd, &dir);
25525747282SDavid du Colombier 			close(fd);
25625747282SDavid du Colombier 			if(n == 0)
25725747282SDavid du Colombier 				continue;
25825747282SDavid du Colombier 			for(i = n-1; i > 0; i--){
25925747282SDavid du Colombier 				if(dir[i].mtime > t0)
26025747282SDavid du Colombier 					break;
26125747282SDavid du Colombier 			}
26225747282SDavid du Colombier 			sprint(b, "/n/%s/%.4d/%.2d%.2d/%s%s", ndump,
26325747282SDavid du Colombier 				tm->year+1900, tm->mon+1, tm->mday, dir[i].name, f);
26425747282SDavid du Colombier 			free(dir);
26525747282SDavid du Colombier 		} else {
2667dd7cddfSDavid du Colombier 			for(vers=0;; vers++) {
2677dd7cddfSDavid du Colombier 				sprint(b, "/n/%s/%.4d/%.2d%.2d%d", ndump,
2687dd7cddfSDavid du Colombier 					tm->year+1900, tm->mon+1, tm->mday, vers+1);
2699a747e4fSDavid du Colombier 				dir = dirstat(b);
2709a747e4fSDavid du Colombier 				if(dir){
2719a747e4fSDavid du Colombier 					mtime = dir->mtime;
2729a747e4fSDavid du Colombier 					free(dir);
2739a747e4fSDavid du Colombier 				}
2749a747e4fSDavid du Colombier 				if(dir==nil || mtime > t0)
2757dd7cddfSDavid du Colombier 					break;
2767dd7cddfSDavid du Colombier 				if(verb)
2779a747e4fSDavid du Colombier 					print("%ld later %s\n", mtime, b);
2787dd7cddfSDavid du Colombier 			}
2797dd7cddfSDavid du Colombier 			sprint(b, "/n/%s/%.4d/%.2d%.2d%s", ndump,
2807dd7cddfSDavid du Colombier 				tm->year+1900, tm->mon+1, tm->mday, f);
2817dd7cddfSDavid du Colombier 			if(vers)
2827dd7cddfSDavid du Colombier 				sprint(b, "/n/%s/%.4d/%.2d%.2d%d%s", ndump,
2837dd7cddfSDavid du Colombier 					tm->year+1900, tm->mon+1, tm->mday, vers, f);
28425747282SDavid du Colombier 		}
2857dd7cddfSDavid du Colombier 		return;
2867dd7cddfSDavid du Colombier 	}
2877dd7cddfSDavid du Colombier 	strcpy(b, "XXX");	/* error */
2887dd7cddfSDavid du Colombier }
2897dd7cddfSDavid du Colombier 
2907dd7cddfSDavid du Colombier char*
prtime(ulong t)2917dd7cddfSDavid du Colombier prtime(ulong t)
2927dd7cddfSDavid du Colombier {
2937dd7cddfSDavid du Colombier 	static char buf[100];
2947dd7cddfSDavid du Colombier 	char *b;
2957dd7cddfSDavid du Colombier 	Tm *tm;
2967dd7cddfSDavid du Colombier 
2977dd7cddfSDavid du Colombier 	if(uflag)
2987dd7cddfSDavid du Colombier 		tm = gmtime(t);
2997dd7cddfSDavid du Colombier 	else
3007dd7cddfSDavid du Colombier 		tm = localtime(t);
3017dd7cddfSDavid du Colombier 	b = asctime(tm);
3027dd7cddfSDavid du Colombier 	memcpy(buf, b+4, 24);
3037dd7cddfSDavid du Colombier 	buf[24] = 0;
3047dd7cddfSDavid du Colombier 	return buf;
3057dd7cddfSDavid du Colombier }
3067dd7cddfSDavid du Colombier 
3077dd7cddfSDavid du Colombier long
starttime(char * s)3087dd7cddfSDavid du Colombier starttime(char *s)
3097dd7cddfSDavid du Colombier {
3107dd7cddfSDavid du Colombier 	Tm *tm;
3117dd7cddfSDavid du Colombier 	long t, dt;
3127dd7cddfSDavid du Colombier 	int i, yr, mo, da;
3137dd7cddfSDavid du Colombier 
3147dd7cddfSDavid du Colombier 	t = time(0);
3157dd7cddfSDavid du Colombier 	if(s == 0)
3167dd7cddfSDavid du Colombier 		return t;
3179a747e4fSDavid du Colombier 	for(i=0; s[i]; i++)
3187dd7cddfSDavid du Colombier 		if(s[i] < '0' || s[i] > '9') {
3197dd7cddfSDavid du Colombier 			fprint(2, "bad start time: %s\n", s);
3207dd7cddfSDavid du Colombier 			return t;
3217dd7cddfSDavid du Colombier 		}
3229a747e4fSDavid du Colombier 	if(strlen(s)==6){
3237dd7cddfSDavid du Colombier 		yr = (s[0]-'0')*10 + s[1]-'0';
3247dd7cddfSDavid du Colombier 		mo = (s[2]-'0')*10 + s[3]-'0' - 1;
3257dd7cddfSDavid du Colombier 		da = (s[4]-'0')*10 + s[5]-'0';
3269a747e4fSDavid du Colombier 		if(yr < 70)
3279a747e4fSDavid du Colombier 			yr += 100;
3289a747e4fSDavid du Colombier 	}else if(strlen(s)==8){
3299a747e4fSDavid du Colombier 		yr = (((s[0]-'0')*10 + s[1]-'0')*10 + s[2]-'0')*10 + s[3]-'0';
3309a747e4fSDavid du Colombier 		yr -= 1900;
3319a747e4fSDavid du Colombier 		mo = (s[4]-'0')*10 + s[5]-'0' - 1;
3329a747e4fSDavid du Colombier 		da = (s[6]-'0')*10 + s[7]-'0';
3339a747e4fSDavid du Colombier 	}else{
3349a747e4fSDavid du Colombier 		fprint(2, "bad start time: %s\n", s);
3359a747e4fSDavid du Colombier 		return t;
3369a747e4fSDavid du Colombier 	}
3377dd7cddfSDavid du Colombier 	t = 0;
3387dd7cddfSDavid du Colombier 	dt = YEAR(10);
3397dd7cddfSDavid du Colombier 	for(i=0; i<50; i++) {
3407dd7cddfSDavid du Colombier 		tm = localtime(t+dt);
3417dd7cddfSDavid du Colombier 		if(yr > tm->year ||
3427dd7cddfSDavid du Colombier 		  (yr == tm->year && mo > tm->mon) ||
3437dd7cddfSDavid du Colombier 		  (yr == tm->year && mo == tm->mon) && da > tm->mday) {
3447dd7cddfSDavid du Colombier 			t += dt;
3457dd7cddfSDavid du Colombier 			continue;
3467dd7cddfSDavid du Colombier 		}
3477dd7cddfSDavid du Colombier 		dt /= 2;
3487dd7cddfSDavid du Colombier 		if(dt == 0)
3497dd7cddfSDavid du Colombier 			break;
3507dd7cddfSDavid du Colombier 	}
3517dd7cddfSDavid du Colombier 	t += HOUR(12);	/* .5 day to get to noon of argument */
3527dd7cddfSDavid du Colombier 	return t;
3537dd7cddfSDavid du Colombier }
354