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