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