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