history.c 6.0 KB

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