fetchpull-standalone.scm 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. #!/usr/bin/env bash
  2. # -*- scheme -*-
  3. exec -a "${0}" guile -L $(dirname $(realpath "$0")) -e '(fetchpull-standalone)' -c '' "${@}"
  4. ; !#
  5. ;; for emacs (defun test-this-file () (interactive) (save-current-buffer) (async-shell-command (concat (buffer-file-name (current-buffer)) " --test")))
  6. (define-module (fetchpull-standalone)
  7. #:export (main))
  8. ;; support inline math
  9. #!curly-infix
  10. (define version "0.0.0 just-do-it")
  11. (define design
  12. '(
  13. (keys are KSK@<prefix>--DATE-uploaded-xxx-days-before-using-MODE)
  14. (process:
  15. (get the current date)
  16. (for realtime or bulk as MODE
  17. (for each power of two up to 128 as iii
  18. (insert a random chunk (without compression)
  19. (to {DATE + iii days}-uploaded-iii-days-before-using-MODE))
  20. (request the key DATE-uploaded-iii-days-before-using-MODE)
  21. (write the times along with the keys (without the prefix)
  22. (into insert-times.csv and request-times.csv))
  23. (format as
  24. (DATE-as-seconds-since-epoch duration iii MODE)))))
  25. (prefix is generated from securepassword.w and stored in the file fetchpull-prefix.txt)))
  26. (import
  27. (only (srfi srfi-19) current-date date->string string->date date->time-utc time-utc->date
  28. make-time time-utc time-duration add-duration current-time)
  29. (only (srfi srfi-9) define-record-type)
  30. (only (ice-9 pretty-print) pretty-print)
  31. (only (ice-9 rdelim) read-line read-delimited)
  32. (only (ice-9 format) format)
  33. (only (srfi srfi-27) random-integer)
  34. (only (srfi srfi-1) first second third alist-cons assoc lset<= lset-intersection lset-difference)
  35. (only (rnrs bytevectors) make-bytevector bytevector-length string->utf8)
  36. (only (rnrs io ports) get-bytevector-all get-bytevector-n
  37. put-bytevector bytevector->string port-eof?)
  38. (only (ice-9 popen) open-output-pipe)
  39. (only (ice-9 expect) expect-strings ;; for quick experimentation. Expect needs additional functions and variables available:
  40. expect expect-regexec expect-timeout expect-select expect-timeout-proc
  41. expect-char-proc expect-eof-proc expect-strings-compile-flags)
  42. (only (ice-9 regex) string-match match:substring)
  43. (ice-9 threads)
  44. (ice-9 atomic)
  45. (only (ice-9 q) make-q enq! deq! q-empty?)
  46. (sxml simple))
  47. (define today (current-time time-utc))
  48. (define (time->iso time)
  49. (date->string (time-utc->date time) "~1"))
  50. (define (iso->time string)
  51. (date->time-utc (string->date string "~Y~m~d")))
  52. (define (add-days time number-of-days)
  53. (let ((seconds (* 3600 24 number-of-days)))
  54. (add-duration time
  55. (make-time time-duration 0 seconds))))
  56. (define (string-replace-string s char replacement-string)
  57. (string-join (string-split s char) replacement-string))
  58. (define (replace-KSK-escaped s)
  59. (string-replace-string (string-replace-string s #\+ "-")
  60. #\= "-"))
  61. (define (task-id)
  62. (replace-KSK-escaped (map number->string (map random-integer (iota 10)))))
  63. (define prefix-filename "fetchpull-prefix.txt")
  64. (define prefix-cache #f)
  65. (define (prefix)
  66. (cond
  67. (prefix-cache
  68. prefix-cache)
  69. ((file-exists? prefix-filename)
  70. (read-line (open-input-file prefix-filename)))
  71. (else
  72. (let
  73. ((pw (task-id))
  74. (port (open-output-file prefix-filename)))
  75. (display pw port)
  76. (close-port port)
  77. pw))))
  78. (define (KSK-for-insert prefix today days-before mode)
  79. (format #f "KSK@~a--~a-uploaded-~3,'0d-days-before-using-~a" prefix
  80. (time->iso (add-days today days-before))
  81. days-before mode))
  82. (define (KSK-for-request prefix time days-before mode)
  83. #(
  84. (tests
  85. (test-equal
  86. "KSK@WwL6-UXTu-sa5n.fAk2-s7kj.5Kp6--2018-11-23-uploaded-005-days-before-using-realtime"
  87. (KSK-for-request "WwL6-UXTu-sa5n.fAk2-s7kj.5Kp6" (iso->time "2018-11-23") 5 'realtime))))
  88. (format #f "KSK@~a--~a-uploaded-~3,'0d-days-before-using-~a" prefix
  89. (time->iso time)
  90. days-before mode))
  91. ;; the shared FCP socket
  92. (define sock #f)
  93. (define (fcp-socket-create)
  94. (define addrs (getaddrinfo "127.0.0.1" "9482"))
  95. (define addr (first addrs))
  96. (define s (socket (addrinfo:fam addr) (addrinfo:socktype addr) (addrinfo:protocol addr)))
  97. (connect s (addrinfo:addr addr))
  98. s)
  99. (define-record-type <message>
  100. (message-create task type data fields )
  101. message?
  102. (task message-task)
  103. (type message-type)
  104. (data message-data)
  105. (fields message-fields ));; avoid duplicates: fred joins duplicate fields with ";" to a single value
  106. (define (format-field field)
  107. (format #f "~a=~a"
  108. (car field)
  109. (cdr field)))
  110. (define (join-fields fields)
  111. #((tests (test-equal "A=B\nX=V" (join-fields (list (cons 'A "B") (cons 'X 'V))))))
  112. (string-join
  113. (map format-field fields)
  114. "\n"))
  115. (define field-key car)
  116. (define field-value cdr)
  117. (define (field-split s)
  118. (let ((where (string-index s #\=)))
  119. (if where
  120. (cons
  121. (string->symbol (substring/shared s 0 where))
  122. (substring/shared s (+ where 1) (string-length s)))
  123. (cons s ""))))
  124. (define (write-message message sock)
  125. (display (message-type message) sock)
  126. (newline sock)
  127. (when (message-task message)
  128. (format sock "Identifier=~a\n"
  129. (message-task message)))
  130. (when (not (null? (message-fields message)))
  131. (display (join-fields (message-fields message))
  132. sock)
  133. (newline sock))
  134. (cond
  135. ((message-data message)
  136. (format sock "~a\n"
  137. (format-field (cons 'DataLength (bytevector-length (message-data message)))))
  138. (format sock "Data\n")
  139. (put-bytevector sock (message-data message)))
  140. (else
  141. (display 'EndMessage sock)
  142. (newline sock))))
  143. (define (message-client-hello)
  144. (message-create #f 'ClientHello #f
  145. (list (cons 'Name "FetchpullClient" )
  146. (cons 'ExpectedVersion "2.0"))))
  147. (define (message-watch-global)
  148. (message-create #f 'WatchGlobal #f
  149. (list (cons 'Enabled "true" )
  150. (cons 'VerbosityMask 1 ))));; simple progress
  151. (define (message-disconnect)
  152. (message-create #f 'Disconnect #f
  153. (list)))
  154. (define (message-client-get task URI custom-fields)
  155. (message-create task 'ClientGet #f
  156. (append
  157. (list (cons 'URI URI))
  158. '((Verbosity . 1 );; get SimpleProgress messages for the tasks
  159. (ReturnType . direct)
  160. (MaxRetries . 1 );; -1 means: try indefinitely, with ULPR, essentially long polling
  161. (Global . true)
  162. (Persistence . reboot))
  163. custom-fields)))
  164. (define (message-client-get-realtime task URI)
  165. (message-client-get task URI
  166. '(
  167. (PriorityClass . 2)
  168. (RealTimeFlag . true)
  169. (FilterData . false))))
  170. (define (message-client-put task URI data custom-fields)
  171. (message-create task 'ClientPut data
  172. (append
  173. (list (cons 'URI URI))
  174. `((Verbosity . 1 );; get SimpleProgress messages for the tasks
  175. (MaxRetries . 1 );; default: 10
  176. (Global . true)
  177. (Persistence . reboot)
  178. (UploadFrom . direct))
  179. custom-fields)))
  180. (define (message-client-put-realtime task URI data)
  181. (message-client-put task URI data
  182. '(
  183. (PriorityClass . 2)
  184. (RealTimeFlag . true)
  185. (DontCompress . true)
  186. (ExtraInsertsSingleBlock . 0)
  187. (ExtraInsertsSplitfileHeaderBlock . 0)
  188. (Metadata.ContentType . application/octet-stream))))
  189. (define (message-remove-request task)
  190. (message-create task 'RemoveRequest #f
  191. (list (cons 'Global 'true))))
  192. (define supported-messages
  193. '(NodeHello GetFailed DataFound AllData PutSuccessful PutFailed))
  194. (define (log-warning message things)
  195. (format (current-output-port)
  196. "Warning: ~a: ~a\n" message things))
  197. (define (read-message port)
  198. (if (port-eof? port)
  199. #f
  200. (let loop ((type (string->symbol (read-line port))))
  201. (define DataLength #f)
  202. (define task #f)
  203. (let readlines ((lines (list (read-line port))))
  204. (define line (first lines))
  205. (define field (field-split line))
  206. (when (equal? 'DataLength (field-key field))
  207. (set! DataLength
  208. (field-value field)))
  209. (when (equal? 'Identifier (field-key field))
  210. (set! task
  211. (field-value field)))
  212. ;; pretty-print : list 'line line 'type type
  213. (cond
  214. ((string-index line #\=)
  215. (readlines (cons (read-line port) lines)))
  216. ((member type supported-messages );; line is Data or EndMessage
  217. (let
  218. (
  219. (data ;; EndMessage has no Data
  220. (if (and DataLength (not (equal? "EndMessage" line)))
  221. (get-bytevector-n port (string->number DataLength))
  222. #f)))
  223. (message-create task type data
  224. (map field-split (cdr lines)))))
  225. (else
  226. (log-warning "unsupported message type" type)
  227. (if (port-eof? port)
  228. #f
  229. (loop (string->symbol (read-line port))))))))))
  230. (define next-message
  231. (make-atomic-box #f))
  232. (define (send-message message)
  233. ;; wait until the message was retrieved. This only replaces if the previous content was #f. take-message-to-send switches takes the messages
  234. (let try ((failed (atomic-box-compare-and-swap! next-message #f message)))
  235. (when failed
  236. (usleep 100)
  237. (try (atomic-box-compare-and-swap! next-message #f message)))))
  238. (define (take-message-to-send)
  239. ;; get the message and reset next-message to #f to allow taking another message
  240. (atomic-box-swap! next-message #f))
  241. (define message-processors
  242. (make-atomic-box (list)))
  243. (define (process message)
  244. (let loop ((processors (atomic-box-ref message-processors)) (msg message))
  245. (cond
  246. ((not msg)
  247. #f)
  248. ((null? processors)
  249. msg)
  250. (else
  251. (loop (cdr processors)
  252. ((first processors) msg))))))
  253. (define (processor-put! processor)
  254. (let loop ((old (atomic-box-ref message-processors)))
  255. (define old-now (atomic-box-compare-and-swap! message-processors old (cons processor old)))
  256. (when (not (equal? old old-now))
  257. (loop (atomic-box-ref message-processors)))))
  258. (define (processor-delete! processor)
  259. (let loop ((old (atomic-box-ref message-processors)))
  260. (define old-now (atomic-box-compare-and-swap! message-processors old (delete processor old)))
  261. (when (not (equal? old old-now))
  262. (loop (atomic-box-ref message-processors)))))
  263. (define (fcp-read-loop sock)
  264. (let loop ((message (read-message sock)))
  265. (when message
  266. (warn-unhandled
  267. (process message))
  268. (loop (read-message sock)))))
  269. (define (fcp-write-loop sock)
  270. (let loop ((message (take-message-to-send)))
  271. (if message
  272. (begin
  273. (write-message message sock))
  274. (usleep 100))
  275. (loop (take-message-to-send))))
  276. (define (warn-unhandled message)
  277. (when message
  278. (format (current-error-port );; avoid writing to the error port elsewhere, that causes multithreading problems. Use current-output-port instead
  279. "Unhandled message ~a\n" message))
  280. #f)
  281. (define (printing-passthrough-processor message)
  282. (pretty-print message)
  283. message)
  284. (define (printing-discarding-processor message)
  285. (pretty-print message)
  286. #f)
  287. (define (discarding-processor message)
  288. #f)
  289. (define (help args)
  290. (format (current-output-port)
  291. "~a [-i] [--help | --version | --test | YYYY-mm-dd]
  292. Options:
  293. -i load the script and run an interactive REPL."
  294. (first args)))
  295. ;; timing information (alists)
  296. (define get-successful (list))
  297. (define get-failed (list))
  298. (define put-successful (list))
  299. (define put-failed (list))
  300. (define get-alldata (list )); the actual data, for debugging
  301. (define (processor-record-datafound-getdata message)
  302. (cond
  303. ((equal? 'DataFound (message-type message))
  304. (send-message
  305. (message-create (message-task message)
  306. 'GetRequestStatus #f
  307. (list (cons 'Global 'true))))
  308. #f)
  309. (else message)))
  310. (define (current-time-seconds)
  311. (car (gettimeofday)))
  312. (define (processor-record-alldata-time message)
  313. (cond
  314. ((equal? 'AllData (message-type message))
  315. (set! get-successful
  316. (alist-cons (message-task message) (current-time-seconds) get-successful))
  317. #f)
  318. (else message)))
  319. (define (processor-record-getfailed-time message)
  320. (cond
  321. ((equal? 'GetFailed (message-type message))
  322. (set! get-failed
  323. (alist-cons (message-task message) (current-time-seconds) get-failed))
  324. #f)
  325. (else message)))
  326. (define (processor-record-putfailed-time message)
  327. (cond
  328. ((equal? 'PutFailed (message-type message))
  329. (set! put-failed
  330. (alist-cons (message-task message) (current-time-seconds) put-failed))
  331. #f)
  332. (else message)))
  333. (define (processor-record-putsuccessful-time message)
  334. (cond
  335. ((equal? 'PutSuccessful (message-type message))
  336. (set! put-successful
  337. (alist-cons (message-task message) (current-time-seconds) put-successful))
  338. #f)
  339. (else message)))
  340. (define (processor-record-identifier-collision-time message failed)
  341. (cond
  342. ((equal? 'IdentifierCollision (message-type message))
  343. (set! failed
  344. (alist-cons (message-task message) (current-time-seconds) failed))
  345. #f)
  346. (else message)))
  347. (define (processor-record-identifier-collision-get-time message)
  348. (processor-record-identifier-collision-time message get-failed))
  349. (define (processor-record-identifier-collision-put-time message)
  350. (processor-record-identifier-collision-time message put-failed))
  351. (define-record-type <duration-entry>
  352. (duration-entry key duration successful operation mode)
  353. timing-entry?
  354. (key duration-entry-key)
  355. (duration duration-entry-duration)
  356. (successful duration-entry-success)
  357. (operation duration-entry-operation );; get or put
  358. (mode duration-entry-mode ));; realtime bulk speehacks
  359. (define (time-get keys)
  360. (define start-times (list))
  361. (define (finished-tasks)
  362. (append
  363. (map car get-successful)
  364. (map car get-failed)))
  365. ;; setup a processing chain which saves the time information about the request
  366. (processor-put! processor-record-datafound-getdata)
  367. (processor-put! processor-record-alldata-time)
  368. (processor-put! processor-record-getfailed-time)
  369. (processor-put! processor-record-identifier-collision-get-time)
  370. ;; just use the keys as task-IDs (Identifiers)
  371. (let loop ((keys keys))
  372. (when (not (null? keys))
  373. ;; first remove requests which might still be in the upload or download queue
  374. (send-message
  375. (message-remove-request (first keys)))
  376. (set! start-times (alist-cons (first keys) (current-time-seconds) start-times))
  377. (send-message
  378. (message-client-get-realtime (first keys) (first keys)))
  379. (loop (cdr keys))))
  380. ;; wait for completion
  381. (let loop ((finished (finished-tasks)))
  382. (when (not (lset<= equal? keys finished))
  383. (let ((unfinished (lset-difference equal? keys (lset-intersection equal? keys finished))))
  384. (format (current-output-port)
  385. "~d download keys still in flight\n" (length unfinished)))
  386. (usleep 1000000)
  387. (loop (finished-tasks))))
  388. ;; all done: cleanup and take the timing
  389. (processor-delete! processor-record-identifier-collision-get-time)
  390. (processor-delete! processor-record-getfailed-time)
  391. (processor-delete! processor-record-alldata-time)
  392. (processor-delete! processor-record-datafound-getdata)
  393. (let loop ((keys keys) (times '()))
  394. (if (null? keys)
  395. times
  396. (let ()
  397. (define key (first keys))
  398. (define (gettime L) (cdr (assoc key L)))
  399. (define start-time (gettime start-times))
  400. (define finish-time (gettime (append get-successful get-failed)))
  401. (define successful (and (assoc key get-successful) #t ));; forces boolean
  402. (send-message
  403. (message-remove-request key))
  404. (loop (cdr keys)
  405. (cons (duration-entry (first keys) {finish-time - start-time} successful 'GET 'realtime)
  406. times))))))
  407. (define (time-put keys)
  408. (define start-times (list))
  409. (define (finished-tasks)
  410. (append
  411. (map car put-successful)
  412. (map car put-failed)))
  413. ;; setup a processing chain which saves the time information about the request
  414. (processor-put! processor-record-putsuccessful-time)
  415. (processor-put! processor-record-putfailed-time)
  416. (processor-put! processor-record-identifier-collision-put-time)
  417. ;; just use the keys as task-IDs (Identifiers)
  418. (let loop ((keys keys))
  419. (when (not (null? keys))
  420. ;; first remove requests which might still be in the upload or download queue
  421. (send-message
  422. (message-remove-request (first keys)))
  423. (set! start-times (alist-cons (first keys) (current-time-seconds) start-times))
  424. (send-message
  425. (message-client-put-realtime (first keys) (first keys)
  426. (string->utf8 (first keys))))
  427. (loop (cdr keys))))
  428. ;; wait for completion
  429. (let loop ((finished (finished-tasks)))
  430. (when (not (lset<= equal? keys finished))
  431. (let ((unfinished (lset-difference equal? keys (lset-intersection equal? keys finished))))
  432. (format (current-output-port)
  433. "~d upload keys still in flight\n" (length unfinished)))
  434. (usleep 1000000)
  435. (loop (finished-tasks))))
  436. ;; all done: cleanup and take the timing
  437. (processor-delete! processor-record-identifier-collision-put-time)
  438. (processor-delete! processor-record-putfailed-time)
  439. (processor-delete! processor-record-putsuccessful-time)
  440. (let loop ((keys keys) (times '()))
  441. (if (null? keys)
  442. times
  443. (let ()
  444. (define key (first keys))
  445. (define (gettime L) (cdr (assoc key L)))
  446. (define start-time (gettime start-times))
  447. (define finish-time (gettime (append put-successful put-failed)))
  448. (define successful (and (assoc key put-successful) #t ));; forces boolean
  449. (send-message
  450. (message-remove-request key))
  451. (loop (cdr keys)
  452. (cons (duration-entry (first keys) {finish-time - start-time} successful 'PUT 'realtime)
  453. times))))))
  454. (define %this-module (current-module))
  455. (define (test)
  456. (processor-put! discarding-processor)
  457. (processor-put! printing-passthrough-processor)
  458. (set! sock (fcp-socket-create))
  459. (let
  460. (
  461. (fcp-read-thread
  462. (begin-thread
  463. (fcp-read-loop sock)))
  464. (fcp-write-thread
  465. (begin-thread
  466. (fcp-write-loop sock))))
  467. (send-message (message-client-hello))
  468. (send-message (message-watch-global))
  469. (send-message (message-client-get-realtime (task-id) "USK@N82omidQlapADLWIym1u4rXvEQhjoIFbMa5~p1SKoOY,LE3WlYKas1AIdoVX~9wahrTlV5oZYhvJ4AcYYGsBq-w,AQACAAE/irclogs/772/2018-11-23.weechatlog"))
  470. (sleep 30)
  471. (send-message (message-disconnect))
  472. (join-thread fcp-write-thread (+ 30 (current-time-seconds)))
  473. (join-thread fcp-read-thread (+ 30 (current-time-seconds)))
  474. (close sock)))
  475. (define (call-with-fcp-connection thunk)
  476. (set! sock (fcp-socket-create))
  477. (let
  478. (
  479. (fcp-read-thread
  480. (begin-thread
  481. (fcp-read-loop sock)))
  482. (fcp-write-thread
  483. (begin-thread
  484. (fcp-write-loop sock))))
  485. (send-message (message-client-hello))
  486. (send-message (message-watch-global))
  487. (thunk)
  488. (send-message (message-disconnect))
  489. (join-thread fcp-write-thread (+ 3 (current-time-seconds)))
  490. (join-thread fcp-read-thread (+ 3 (current-time-seconds)))
  491. (close sock)))
  492. (define-syntax-rule (with-fcp-connection exp ...)
  493. (call-with-fcp-connection
  494. (λ () exp ...)))
  495. (define* (stats->csv stats #:key (target-filename #f))
  496. "Format the all duration-entry in stats as csv file.
  497. example:
  498. date;key;duration;days-before;mode;success
  499. KSK@...;32;16;realtime;false
  500. KSK@...;40;32;realtime;true
  501. "
  502. (define (days-before key)
  503. (string->number
  504. (match:substring
  505. (string-match "uploaded-([0-9]*)-days-before" key)
  506. 1)))
  507. (define new (not (and target-filename (file-exists? target-filename))))
  508. (define port
  509. (cond
  510. (target-filename
  511. (open-file target-filename "al"))
  512. (else
  513. (current-output-port))))
  514. (when new
  515. (display "day;key;duration;days-before;mode;success" port)
  516. (newline port ))
  517. (let loop ((stats stats))
  518. (when (not (null? stats))
  519. (let ((s (first stats)))
  520. (format port "~a;~a;~f;~d;~a;~a\n"
  521. (time->iso today)
  522. (duration-entry-key s)
  523. (duration-entry-duration s)
  524. (days-before (duration-entry-key s))
  525. (duration-entry-mode s)
  526. (duration-entry-success s)))
  527. (loop (cdr stats))))
  528. (when target-filename (close-port port)))
  529. (define (website-content port)
  530. (define title "Fetch-Pull-Stats re-woven")
  531. (sxml->xml
  532. `(*TOP*
  533. (html
  534. (head (title ,title)
  535. (meta (@ (charset "utf-8"))))
  536. (body (h1 ,title)
  537. (p (img (@ (src "fetchpull.png") (alt "fetch-pull-statistics"))))
  538. (p "created with "
  539. (a (@ (href "https://bitbucket.org/ArneBab/freenet-guile/src/default/fetchpull.w") (title "link to project"))
  540. "fetchpull.w"))
  541. (p "plotted with "
  542. (a (@ (href "fetchpull-plot.gnuplot"))
  543. "fetchpull-plot.gnuplot")))))
  544. port))
  545. (define (create-plot)
  546. (define gnuplot (open-output-pipe "gnuplot"))
  547. (define input (open-input-file "fetchpull-plot.gnuplot"))
  548. (let loop ()
  549. (when (not (port-eof? input))
  550. (display (read-char input) gnuplot)
  551. (loop)))
  552. (display #\newline gnuplot)
  553. (close input)
  554. (close gnuplot)
  555. (sync))
  556. (define (copy-resources-to path)
  557. ;; remove all KSK information from the stats to prevent people from tampering with them
  558. (let loop ((files '("fetchpull-stats-get.csv" "fetchpull-stats-put.csv")))
  559. (when (not (null? files))
  560. (when (file-exists? (first files))
  561. (let ((new-filename (string-append path file-name-separator-string (first files))))
  562. (copy-file (first files)
  563. new-filename)
  564. (close (open-output-pipe (string-append "sed -i 's/KSK@.*using-realtime/KEY/' " new-filename "\n")))))
  565. (loop (cdr files))))
  566. ;; simply copy over the plot and plotting script
  567. (let loop ((files '("fetchpull-plot.gnuplot" "fetchpull.png")))
  568. (when (not (null? files))
  569. (when (file-exists? (first files))
  570. (copy-file (first files)
  571. (string-append path file-name-separator-string
  572. (first files))))
  573. (loop (cdr files)))))
  574. (define (ensure-directory-exists path)
  575. (cond
  576. ((not (file-exists? path))
  577. (mkdir path))
  578. ((not (file-is-directory? path))
  579. (error 'system-error "Selected path ~A is no directory" path))
  580. (else path)))
  581. (define (write-site-to path)
  582. (define filepath (string-append path file-name-separator-string "index.html"))
  583. (define port (open-output-file filepath))
  584. (display "<!doctype html>\n" port)
  585. (website-content port)
  586. (close port))
  587. (define (create-site path)
  588. (ensure-directory-exists path)
  589. (create-plot)
  590. (copy-resources-to path)
  591. (write-site-to path))
  592. (define (final-action? args)
  593. (if {(length args) > 1}
  594. (cond
  595. ((equal? "--help" (second args))
  596. (help args)
  597. #t)
  598. ((equal? "--version" (second args))
  599. (format (current-output-port)
  600. "~a\n" version)
  601. #t)
  602. ((equal? "--test" (second args))
  603. (test)
  604. #t)
  605. ((equal? "--site" (second args))
  606. (create-site (if {(length args) > 2} (third args) "site"))
  607. #t)
  608. (else #f))
  609. #f))
  610. (define (main args)
  611. (when (not (final-action? args))
  612. (when {(length args) > 1}
  613. (pretty-print (second args))
  614. (set! today (iso->time (second args))))
  615. (processor-put! printing-passthrough-processor)
  616. (let ((get-stats '()) (put-stats '()))
  617. (define (stats-get stat)
  618. (set! get-stats (append get-stats stat))
  619. stat)
  620. (define (stats-put stat)
  621. (set! put-stats (append put-stats stat))
  622. stat)
  623. (with-fcp-connection
  624. (let loop
  625. ((modes '(realtime)))
  626. (define days-before
  627. (cons 0
  628. (map (λ(x) (expt 2 x))
  629. (iota 10))))
  630. (define* (KSK-for-get days #:key (append ""))
  631. (KSK-for-request (string-append (prefix) append) today days 'realtime))
  632. (define* (KSK-for-put days #:key (append ""))
  633. (KSK-for-insert (string-append (prefix) append) today days 'realtime))
  634. (when (not (null? modes))
  635. (stats-put
  636. (time-put
  637. (apply append
  638. (map (λ(x) (map (λ (y) (KSK-for-put y #:append (number->string x))) days-before ))
  639. (iota 10)))))
  640. (stats-get
  641. (time-get
  642. (apply append
  643. (map (λ(x) (map (λ (y) (KSK-for-get y #:append (number->string x))) days-before ))
  644. (iota 10))))))))
  645. (pretty-print get-stats)
  646. (pretty-print put-stats)
  647. (stats->csv get-stats #:target-filename "fetchpull-stats-get.csv")
  648. (stats->csv put-stats #:target-filename "fetchpull-stats-put.csv"))))