runcached.c 7.8 KB

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