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