xref: /plan9/sys/src/cmd/history.c (revision 4d44ba9b9ee4246ddbd96c7fcaf0918ab92ab35a)
1 #include	<u.h>
2 #include	<libc.h>
3 
4 #define	MINUTE(x)	((long)(x)*60L)
5 #define	HOUR(x)		(MINUTE(x)*60L)
6 #define	YEAR(x)		(HOUR(x)*24L*360L)
7 
8 int	verb;
9 int	uflag;
10 int	force;
11 int	diff;
12 int	diffb;
13 char*	sflag;
14 
15 void	ysearch(char*, char*);
16 long	starttime(char*);
17 void	lastbefore(ulong, char*, char*, char*);
18 char*	prtime(ulong);
19 
20 void
21 main(int argc, char *argv[])
22 {
23 	int i;
24 	char *ndump;
25 
26 	ndump = nil;
27 	ARGBEGIN {
28 	default:
29 		goto usage;
30 	case 'v':
31 		verb = 1;
32 		break;
33 	case 'f':
34 		force = 1;
35 		break;
36 	case 'd':
37 		ndump = ARGF();
38 		break;
39 	case 'D':
40 		diff = 1;
41 		break;
42 	case 'b':
43 		diffb = 1;
44 		break;
45 	case 's':
46 		sflag = ARGF();
47 		break;
48 	case 'u':
49 		uflag = 1;
50 		break;
51 	} ARGEND
52 
53 	if(argc == 0) {
54 	usage:
55 		fprint(2, "usage: history [-bDfuv] [-d 9fsname] [-s yyyymmdd] files\n");
56 		exits(0);
57 	}
58 
59 	for(i=0; i<argc; i++)
60 		ysearch(argv[i], ndump);
61 	exits(0);
62 }
63 
64 void
65 ysearch(char *file, char *ndump)
66 {
67 	char fil[400], buf[500], nbuf[100], pair[2][500], *p;
68 	Tm *tm;
69 	Waitmsg *w;
70 	Dir *dir, *d;
71 	ulong otime, dt;
72 	int toggle, started, missing;
73 
74 	fil[0] = 0;
75 	if(file[0] != '/') {
76 		getwd(strchr(fil, 0), 100);
77 		strcat(fil, "/");
78 	}
79 	strcat(fil, file);
80 	if(memcmp(fil, "/n/", 3) == 0){
81 		p = strchr(fil+3, '/');
82 		if(p == nil)
83 			p = fil+strlen(fil);
84 		if(ndump == nil){
85 			if(p-fil >= sizeof nbuf-10){
86 				fprint(2, "%s: dump name too long", fil);
87 				return;
88 			}
89 			memmove(nbuf, fil+3, p-(fil+3));
90 			nbuf[p-(fil+3)] = 0;
91 			strcat(nbuf, "dump");
92 			ndump = nbuf;
93 		}
94 		memmove(fil, p, strlen(p)+1);
95 	}
96 	if(ndump == nil)
97 		ndump = "dump";
98 
99 	tm = localtime(time(0));
100 	snprint(buf, sizeof buf, "/n/%s/%.4d/", ndump, tm->year+1900);
101 	if(access(buf, AREAD) < 0) {
102 		if(verb)
103 			print("mounting dump %s\n", ndump);
104 		if(rfork(RFFDG|RFPROC) == 0) {
105 			execl("/bin/rc", "rc", "9fs", ndump, 0);
106 			exits(0);
107 		}
108 		w = wait();
109 		if(w == nil){
110 			fprint(2, "history: wait error: %r\n");
111 			exits("wait");
112 		}
113 		if(w->msg[0] != '\0'){
114 			fprint(2, "9fs failed: %s\n", w->msg);
115 			exits(w->msg);
116 		}
117 		free(w);
118 	}
119 
120 	started = 0;
121 	dir = dirstat(file);
122 	if(dir == nil)
123 		fprint(2, "history: warning: %s does not exist\n", file);
124 	else{
125 		print("%s %s %lld [%s]\n", prtime(dir->mtime), file, dir->length, dir->muid);
126 		started = 1;
127 		strecpy(pair[1], pair[1]+sizeof pair[1], file);
128 	}
129 	free(dir);
130 	otime = starttime(sflag);
131 	toggle = 0;
132 	for(;;) {
133 		lastbefore(otime, fil, buf, ndump);
134 		dir = dirstat(buf);
135 		if(dir == nil) {
136 			if(!force)
137 				return;
138 			dir = malloc(sizeof(Dir));
139 			nulldir(dir);
140 			dir->mtime = otime + 1;
141 		}
142 		dt = HOUR(12);
143 		missing = 0;
144 		while(otime <= dir->mtime){
145 			if(verb)
146 				print("backup %ld, %ld\n", dir->mtime, otime-dt);
147 			lastbefore(otime-dt, fil, buf, ndump);
148 			d = dirstat(buf);
149 			if(d == nil){
150 				if(!force)
151 					return;
152 				if(!missing)
153 					print("removed %s\n", buf);
154 				missing = 1;
155 			}else{
156 				free(dir);
157 				dir = d;
158 			}
159 			dt += HOUR(12);
160 		}
161 		strcpy(pair[toggle], buf);
162 		if(diff && started){
163 			switch(rfork(RFFDG|RFPROC)){
164 			case 0:
165 				if(diffb)
166 					execl("/bin/diff", "diff", "-nb", pair[toggle ^ 1], pair[toggle], 0);
167 				else
168 					execl("/bin/diff", "diff", "-n", pair[toggle ^ 1], pair[toggle], 0);
169 				fprint(2, "can't exec diff: %r\n");
170 				exits(0);
171 			case -1:
172 				fprint(2, "can't fork diff: %r\n");
173 				break;
174 			default:
175 				while(waitpid() != -1)
176 					;
177 				break;
178 			}
179 		}
180 		print("%s %s %lld [%s]\n", prtime(dir->mtime), buf, dir->length, dir->muid);
181 		toggle ^= 1;
182 		started = 1;
183 		otime = dir->mtime;
184 		free(dir);
185 	}
186 }
187 
188 void
189 lastbefore(ulong t, char *f, char *b, char *ndump)
190 {
191 	Tm *tm;
192 	Dir *dir;
193 	int vers, try;
194 	ulong t0, mtime;
195 	int i, n, fd;
196 
197 	t0 = t;
198 	if(verb)
199 		print("%ld lastbefore %s\n", t0, f);
200 	mtime = 0;
201 	for(try=0; try<30; try++) {
202 		tm = localtime(t);
203 		sprint(b, "/n/%s/%.4d/%.2d%.2d", ndump,
204 			tm->year+1900, tm->mon+1, tm->mday);
205 		dir = dirstat(b);
206 		if(dir){
207 			mtime = dir->mtime;
208 			free(dir);
209 		}
210 		if(dir==nil || mtime > t0) {
211 			if(verb)
212 				print("%ld earlier %s\n", mtime, b);
213 			t -= HOUR(24);
214 			continue;
215 		}
216 		if(strstr(ndump, "snap")){
217 			fd = open(b, OREAD);
218 			if(fd < 0)
219 				continue;
220 			n = dirreadall(fd, &dir);
221 			close(fd);
222 			if(n == 0)
223 				continue;
224 			for(i = n-1; i > 0; i--){
225 				if(dir[i].mtime > t0)
226 					break;
227 			}
228 			sprint(b, "/n/%s/%.4d/%.2d%.2d/%s%s", ndump,
229 				tm->year+1900, tm->mon+1, tm->mday, dir[i].name, f);
230 			free(dir);
231 		} else {
232 			for(vers=0;; vers++) {
233 				sprint(b, "/n/%s/%.4d/%.2d%.2d%d", ndump,
234 					tm->year+1900, tm->mon+1, tm->mday, vers+1);
235 				dir = dirstat(b);
236 				if(dir){
237 					mtime = dir->mtime;
238 					free(dir);
239 				}
240 				if(dir==nil || mtime > t0)
241 					break;
242 				if(verb)
243 					print("%ld later %s\n", mtime, b);
244 			}
245 			sprint(b, "/n/%s/%.4d/%.2d%.2d%s", ndump,
246 				tm->year+1900, tm->mon+1, tm->mday, f);
247 			if(vers)
248 				sprint(b, "/n/%s/%.4d/%.2d%.2d%d%s", ndump,
249 					tm->year+1900, tm->mon+1, tm->mday, vers, f);
250 		}
251 		return;
252 	}
253 	strcpy(b, "XXX");	/* error */
254 }
255 
256 char*
257 prtime(ulong t)
258 {
259 	static char buf[100];
260 	char *b;
261 	Tm *tm;
262 
263 	if(uflag)
264 		tm = gmtime(t);
265 	else
266 		tm = localtime(t);
267 	b = asctime(tm);
268 	memcpy(buf, b+4, 24);
269 	buf[24] = 0;
270 	return buf;
271 }
272 
273 long
274 starttime(char *s)
275 {
276 	Tm *tm;
277 	long t, dt;
278 	int i, yr, mo, da;
279 
280 	t = time(0);
281 	if(s == 0)
282 		return t;
283 	for(i=0; s[i]; i++)
284 		if(s[i] < '0' || s[i] > '9') {
285 			fprint(2, "bad start time: %s\n", s);
286 			return t;
287 		}
288 	if(strlen(s)==6){
289 		yr = (s[0]-'0')*10 + s[1]-'0';
290 		mo = (s[2]-'0')*10 + s[3]-'0' - 1;
291 		da = (s[4]-'0')*10 + s[5]-'0';
292 		if(yr < 70)
293 			yr += 100;
294 	}else if(strlen(s)==8){
295 		yr = (((s[0]-'0')*10 + s[1]-'0')*10 + s[2]-'0')*10 + s[3]-'0';
296 		yr -= 1900;
297 		mo = (s[4]-'0')*10 + s[5]-'0' - 1;
298 		da = (s[6]-'0')*10 + s[7]-'0';
299 	}else{
300 		fprint(2, "bad start time: %s\n", s);
301 		return t;
302 	}
303 	t = 0;
304 	dt = YEAR(10);
305 	for(i=0; i<50; i++) {
306 		tm = localtime(t+dt);
307 		if(yr > tm->year ||
308 		  (yr == tm->year && mo > tm->mon) ||
309 		  (yr == tm->year && mo == tm->mon) && da > tm->mday) {
310 			t += dt;
311 			continue;
312 		}
313 		dt /= 2;
314 		if(dt == 0)
315 			break;
316 	}
317 	t += HOUR(12);	/* .5 day to get to noon of argument */
318 	return t;
319 }
320