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