runcached.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /*
  2. * runcached
  3. * Execute commands while caching their output for subsequent calls.
  4. * Command output will be cached for $cacheperiod and replayed for subsequent calls
  5. *
  6. * Author Spiros Ioannou sivann <at> inaccess.com
  7. *
  8. */
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include <fcntl.h>
  14. #include <errno.h>
  15. #include <sys/types.h>
  16. #include <sys/stat.h>
  17. #include <math.h>
  18. int cacheperiod=27; //seconds
  19. char cachedir[512];
  20. int maxwaitprev=5; //seconds to wait for previous same command to finish before quiting
  21. int minrand=0; //random seconds to wait before running cmd
  22. int maxrand=0;
  23. int argskip=1;
  24. char pidfile[128];
  25. char * str2md5str( char[]);
  26. void cleanup(void);
  27. int runit(char **,char *,char *,char *,char *);
  28. int main(int argc, char **argv) {
  29. int i,j,count;
  30. char cmd[1024];
  31. char buf[512];
  32. char * cmdmd5;
  33. FILE *fp;
  34. struct stat st;
  35. time_t diffsec;
  36. char cmddatafile[128];
  37. char cmdexitcode[128];
  38. char cmdfile[128];
  39. if (argc<2 || (argc==2 && !strcmp(argv[1],"-c"))) {
  40. fprintf(stderr,"Usage: %s [-c cacheperiod] <command to execute with args>\n",argv[0]);
  41. exit(1);
  42. }
  43. if (!strcmp(argv[1],"-c")) {
  44. cacheperiod=atoi(argv[2]);
  45. argskip=3;
  46. }
  47. strcpy(cachedir,"/tmp");
  48. for (j=0,cmd[0]=0,i=0+argskip;i<argc;i++) {
  49. j+=strlen(argv[i]);
  50. if (j+1>sizeof(cachedir)) {
  51. fprintf(stderr,"argument list too long\n");
  52. exit(2);
  53. }
  54. strcat(cmd,argv[i]);
  55. strcat(cmd," ");
  56. }
  57. cmd[strlen(cmd)-1]=0;
  58. cmdmd5=str2md5str(cmd);
  59. if (maxrand-minrand) {
  60. srand(time(NULL));
  61. sleep(rand()%(maxrand+1)+minrand);
  62. }
  63. snprintf(pidfile,127,"%s/%s-runcached.pid",cachedir,cmdmd5);
  64. snprintf(cmddatafile,127,"%s/%s.data",cachedir,cmdmd5);
  65. snprintf(cmdexitcode,127,"%s/%s.exitcode",cachedir,cmdmd5);
  66. snprintf(cmdfile,127,"%s/%s.cmd",cachedir,cmdmd5);
  67. atexit(cleanup);
  68. // don't run the same command in parallel, wait the previous one to finish
  69. count=maxwaitprev;
  70. while (isfile(pidfile)) {
  71. sleep(1);
  72. count-=1;
  73. if (count == 0) {
  74. fprintf(stderr,"timeout waiting for previous %s to finish\n" , cmd);
  75. exit (1);
  76. }
  77. }
  78. //write pid file
  79. fp = fopen(pidfile,"w");
  80. if (!fp) {
  81. perror(pidfile);
  82. exit(2);
  83. }
  84. fprintf(fp,"%ld",(long)getpid());
  85. fclose(fp);
  86. //if not cached before, run it
  87. if (!isfile(cmddatafile)) {
  88. runit(argv,cmd,cmddatafile,cmdexitcode,cmdfile) ;
  89. }
  90. //if too old, re-run it
  91. if (stat(cmddatafile, &st) == -1) {
  92. perror("stat");
  93. exit(EXIT_FAILURE);
  94. }
  95. diffsec=time(0)-(time_t)st.st_mtim.tv_sec;
  96. if (diffsec > cacheperiod) {
  97. runit(argv,cmd,cmddatafile,cmdexitcode,cmdfile) ;
  98. }
  99. fp = fopen(cmddatafile,"r");
  100. if (!fp) {
  101. perror(cmddatafile);
  102. exit(1);
  103. }
  104. while (fgets(buf, sizeof(buf), fp) != NULL)
  105. printf("%s", buf);
  106. fclose(fp);
  107. exit(0);
  108. } //main
  109. int isfile(char *path) {
  110. return (access( path, F_OK ) != -1 ) ;
  111. }
  112. void cleanup(void) {
  113. //if ( access( pidfile, F_OK ) != -1 ) {
  114. printf("Cleanup\n");
  115. if (isfile(pidfile)) {
  116. unlink(pidfile);
  117. }
  118. }
  119. int runit(char **argv,char * cmd,char * cmddatafile,char * cmdexitcode,char * cmdfile) {
  120. int out_old, out_new;
  121. int err_old, err_new;
  122. int exitcode;
  123. char buf[1024];
  124. FILE *pfp, *fp;
  125. fflush(stdout);
  126. fflush(stderr);
  127. //redirect stdout and stderr to file
  128. out_old = dup(1);
  129. out_new = open(cmddatafile,O_WRONLY|O_CREAT|O_TRUNC,S_IROTH|S_IRUSR|S_IRGRP|S_IWUSR);
  130. dup2(out_new, 1);
  131. close(out_new);
  132. err_old = dup(2);
  133. err_new = open(cmddatafile,O_WRONLY|O_CREAT|O_APPEND,S_IROTH|S_IRUSR|S_IRGRP|S_IWUSR);
  134. dup2(err_new, 2);
  135. close(err_new);
  136. /* run command with arguments: */
  137. /*
  138. argv+=argskip;
  139. if (execvp(*argv, argv)<0) {
  140. perror("execvp");
  141. exit (errno);
  142. }
  143. */
  144. pfp = popen(cmd, "r");
  145. if (pfp == NULL) {
  146. perror("popen");
  147. exit(errno);
  148. }
  149. while (fgets(buf, sizeof(buf), pfp) != NULL)
  150. printf("%s", buf);
  151. exitcode=pclose(pfp);
  152. fp = fopen(cmdexitcode,"w");
  153. if (!fp) {
  154. perror(cmdexitcode);
  155. }
  156. else {
  157. fprintf(fp,"%d",exitcode);
  158. fclose(fp);
  159. }
  160. fp = fopen(cmdfile,"w");
  161. if (!fp) {
  162. perror(cmdfile);
  163. }
  164. else {
  165. fprintf(fp,"%s",cmd);
  166. fclose(fp);
  167. }
  168. //redirect stdout and stderr back to where it was
  169. fflush(stdout);
  170. dup2(out_old, 1);
  171. close(out_old);
  172. fflush(stderr);
  173. dup2(err_old, 2);
  174. close(err_old);
  175. }
  176. typedef union uwb {
  177. unsigned w;
  178. unsigned char b[4];
  179. } WBunion;
  180. typedef unsigned Digest[4];
  181. unsigned f0( unsigned abcd[] ){
  182. return ( abcd[1] & abcd[2]) | (~abcd[1] & abcd[3]);}
  183. unsigned f1( unsigned abcd[] ){
  184. return ( abcd[3] & abcd[1]) | (~abcd[3] & abcd[2]);}
  185. unsigned f2( unsigned abcd[] ){
  186. return abcd[1] ^ abcd[2] ^ abcd[3];}
  187. unsigned f3( unsigned abcd[] ){
  188. return abcd[2] ^ (abcd[1] |~ abcd[3]);}
  189. typedef unsigned (*DgstFctn)(unsigned a[]);
  190. unsigned *calcKs( unsigned *k)
  191. {
  192. double s, pwr;
  193. int i;
  194. pwr = pow( 2, 32);
  195. for (i=0; i<64; i++) {
  196. s = fabs(sin(1+i));
  197. k[i] = (unsigned)( s * pwr );
  198. }
  199. return k;
  200. }
  201. // ROtate v Left by amt bits
  202. unsigned rol( unsigned v, short amt )
  203. {
  204. unsigned msk1 = (1<<amt) -1;
  205. return ((v>>(32-amt)) & msk1) | ((v<<amt) & ~msk1);
  206. }
  207. unsigned *md5( const char *msg, int mlen)
  208. {
  209. static Digest h0 = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476 };
  210. // static Digest h0 = { 0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210 };
  211. static DgstFctn ff[] = { &f0, &f1, &f2, &f3 };
  212. static short M[] = { 1, 5, 3, 7 };
  213. static short O[] = { 0, 1, 5, 0 };
  214. static short rot0[] = { 7,12,17,22};
  215. static short rot1[] = { 5, 9,14,20};
  216. static short rot2[] = { 4,11,16,23};
  217. static short rot3[] = { 6,10,15,21};
  218. static short *rots[] = {rot0, rot1, rot2, rot3 };
  219. static unsigned kspace[64];
  220. static unsigned *k;
  221. static Digest h;
  222. Digest abcd;
  223. DgstFctn fctn;
  224. short m, o, g;
  225. unsigned f;
  226. short *rotn;
  227. union {
  228. unsigned w[16];
  229. char b[64];
  230. }mm;
  231. int os = 0;
  232. int grp, grps, q, p;
  233. unsigned char *msg2;
  234. if (k==NULL) k= calcKs(kspace);
  235. for (q=0; q<4; q++) h[q] = h0[q]; // initialize
  236. {
  237. grps = 1 + (mlen+8)/64;
  238. msg2 = malloc( 64*grps);
  239. memcpy( msg2, msg, mlen);
  240. msg2[mlen] = (unsigned char)0x80;
  241. q = mlen + 1;
  242. while (q < 64*grps){ msg2[q] = 0; q++ ; }
  243. {
  244. // unsigned char t;
  245. WBunion u;
  246. u.w = 8*mlen;
  247. // t = u.b[0]; u.b[0] = u.b[3]; u.b[3] = t;
  248. // t = u.b[1]; u.b[1] = u.b[2]; u.b[2] = t;
  249. q -= 8;
  250. memcpy(msg2+q, &u.w, 4 );
  251. }
  252. }
  253. for (grp=0; grp<grps; grp++)
  254. {
  255. memcpy( mm.b, msg2+os, 64);
  256. for(q=0;q<4;q++) abcd[q] = h[q];
  257. for (p = 0; p<4; p++) {
  258. fctn = ff[p];
  259. rotn = rots[p];
  260. m = M[p]; o= O[p];
  261. for (q=0; q<16; q++) {
  262. g = (m*q + o) % 16;
  263. f = abcd[1] + rol( abcd[0]+ fctn(abcd) + k[q+16*p] + mm.w[g], rotn[q%4]);
  264. abcd[0] = abcd[3];
  265. abcd[3] = abcd[2];
  266. abcd[2] = abcd[1];
  267. abcd[1] = f;
  268. }
  269. }
  270. for (p=0; p<4; p++)
  271. h[p] += abcd[p];
  272. os += 64;
  273. }
  274. return h;
  275. }
  276. char * str2md5str( char msg[] )
  277. {
  278. int j,k;
  279. unsigned *d = md5(msg, strlen(msg));
  280. WBunion u;
  281. char s[16];
  282. char * md5str;
  283. md5str=malloc(34);
  284. for (j=0;j<4; j++){
  285. u.w = d[j];
  286. for (k=0;k<4;k++) {
  287. sprintf(s,"%02x",u.b[k]);
  288. strcat(md5str,s);
  289. }
  290. }
  291. return md5str;
  292. }