Index: commonmod.scm ================================================================== --- commonmod.scm +++ commonmod.scm @@ -518,7 +518,24 @@ ;; misc stuff ;;====================================================================== (define (common:get-signature str) (message-digest-string (md5-primitive) str)) + +;;====================================================================== +;; hash of hashs +;;====================================================================== + +(define (db:hoh-set! dat key1 key2 val) + (let* ((subhash (hash-table-ref/default dat key1 #f))) + (if subhash + (hash-table-set! subhash key2 val) + (begin + (hash-table-set! dat key1 (make-hash-table)) + (db:hoh-set! dat key1 key2 val))))) + +(define (db:hoh-get dat key1 key2) + (let* ((subhash (hash-table-ref/default dat key1 #f))) + (and subhash + (hash-table-ref/default subhash key2 #f)))) ) Index: db.scm ================================================================== --- db.scm +++ db.scm @@ -583,12 +583,11 @@ (define (db:tmp->megatest.db-sync dbstruct run-id last-update) (let* ((subdbs (hash-table-values (dbr:dbstruct-subdbs dbstruct))) (res '())) (for-each (lambda (subdb) - (let* ((dbname (db:run-id->dbname run-id)) - (mtdb (dbr:subdb-mtdb subdb)) + (let* ((mtdb (dbr:subdb-mtdb subdb)) (tmpdb (db:get-subdb dbstruct run-id)) (refndb (dbr:subdb-refndb subdb)) (newres (db:sync-tables (db:sync-all-tables-list dbstruct (db:get-keys dbstruct)) last-update tmpdb refndb mtdb))) ;; (stack-push! (dbr:subdb-dbstack subdb) tmpdb) ;; BUG: verify this is really needed Index: dbfile.scm ================================================================== --- dbfile.scm +++ dbfile.scm @@ -56,10 +56,15 @@ (areapath #f) (homehost #f) (tmppath #f) (read-only #f) (subdbs (make-hash-table)) + ;; for the inmem approach (see dbmod.scm) + ;; this is one db per server + (inmem #f) ;; handle for the in memory copy + (dbfile #f) ;; path to the db file on disk + (ondiskdb #f) ;; handle for the on-disk file ) ;; NOTE: Need one dbr:subdb per main.db, 1.db ... ;; (defstruct dbr:subdb Index: dbmod.scm ================================================================== --- dbmod.scm +++ dbmod.scm @@ -17,40 +17,86 @@ ;; along with Megatest. If not, see . ;;====================================================================== (declare (unit dbmod)) +(declare (uses dbfile)) +(declare (uses commonmod)) +(declare (uses debugprint)) (module dbmod * -(import scheme chicken data-structures extras) -(import (prefix sqlite3 sqlite3:) - posix typed-records srfi-18 - srfi-69) - -(define (db:run-id->dbname run-id) - (cond - ((number? run-id)(conc run-id ".db")) - ((not run-id) "main.db") - (else run-id))) - - -;;====================================================================== -;; hash of hashs -;;====================================================================== - - -(define (db:hoh-set! dat key1 key2 val) - (let* ((subhash (hash-table-ref/default dat key1 #f))) - (if subhash - (hash-table-set! subhash key2 val) - (begin - (hash-table-set! dat key1 (make-hash-table)) - (db:hoh-set! dat key1 key2 val))))) - -(define (db:hoh-get dat key1 key2) - (let* ((subhash (hash-table-ref/default dat key1 #f))) - (and subhash - (hash-table-ref/default subhash key2 #f)))) +(import scheme + chicken + data-structures + extras + + (prefix sqlite3 sqlite3:) + posix + typed-records + srfi-18 + srfi-69 + + commonmod + dbfile + debugprint + ) + +;; NOTE: This returns only the name "1.db", "main.db", not the path +;; +(define (dbmod:run-id->dbfname run-id) + (conc (dbfile:run-id->dbnum run-id)".db")) + +(define (dbmod:get-dbdir dbstruct run-id) + (let* ((areapath (dbr:dbstruct-areapath dbstruct))) + (conc areapath"/.megatest"))) + +(define (dbmod:run-id->full-dbfname dbstruct run-id) + (conc (dbmod:get-dbdir dbstruct run-id)"/"(dbmod:run-id->dbfname run-id))) + +;;====================================================================== +;; The inmem one-db file per server method goes in here +;;====================================================================== + +(define (dbmod:open-inmem-db initproc) + (let* ((db (sqlite3:open-database ":memory:")) + (handler (sqlite3:make-busy-timeout 3600))) + (sqlite3:set-busy-handler! db handler) + (initproc db) + db)) + +;; Open the inmem db and the on-disk db +;; populate the inmem db with data +;; +;; Updates fields in dbstruct +;; Returns dbstruct +;; +;; This routine creates the db if not found +;; +(define (db:open-dbmoddb dbstruct run-id init-proc) ;; (conc *toppath* "/megatest.db") (car *configinfo*))) + (let* ((dbfname (dbmod:run-id->dbfname run-id)) + (dbpath (dbmod:get-dbdir dbstruct run-id)) ;; directory where all the .db files are kept + (dbfullname (dbmod:run-id->full-dbfname dbstruct run-id)) + (dbexists (file-exists? dbfullname)) + (inmem (dbmod:open-inmem-db init-proc)) + (write-access (file-write-access? dbpath)) + (db (dbfile:with-simple-file-lock + (conc dbfullname".lock") + (lambda () + (let* ((db (sqlite3:open-database dbfullname)) + (handler (sqlite3:make-busy-timeout 136000))) + (sqlite3:set-busy-handler! db handler) + (if write-access + (init-proc db)) + db))))) + (dbr:dbstruct-inmem-set! dbstruct inmem) + (dbr:dbstruct-ondiskdb-set! dbstruct db) + (dbr:dbstruct-dbfile-set! dbstruct dbfullname) + dbstruct)) + +(define (dbmod:close-db dbstruct) + ;; do final sync to disk file + ;; (do-sync ...) + (sqlite3:finalize! (dbr:dbstruct-ondiskdb dbstruct))) ) Index: megatest.scm ================================================================== --- megatest.scm +++ megatest.scm @@ -80,10 +80,12 @@ (require-library mutils) (define *usage-log-file* #f) ;; put path to file for logging usage in this var in the ~/.megatestrc file (define *usage-use-seconds* #t) ;; for Epoc seconds in usage logging change this to #t in ~/.megatestrc file +;; 'http or 'tcp +(rmt:transport-mode 'tcp) (dbfile:db-init-proc db:initialize-main-db) ;; load the ~/.megatestrc file, put (use trace)(trace-call-sites #t)(trace function-you-want-to-trace) in this file ;; (let ((debugcontrolf (conc (get-environment-variable "HOME") "/.megatestrc"))) Index: tcp-transportmod.scm ================================================================== --- tcp-transportmod.scm +++ tcp-transportmod.scm @@ -27,25 +27,30 @@ (import scheme (prefix sqlite3 sqlite3:) chicken data-structures + + ;; address-info directory-utils extras files hostinfo matchable md5 message-digest ports posix + regex + regex-case srfi-1 srfi-18 srfi-4 srfi-69 stack typed-records + tcp6 commonmod debugprint ) @@ -53,24 +58,35 @@ ;; client ;;====================================================================== ;; (define keep-age-param (make-parameter 10)) ;; qif file age, if over move to attic +;; the client side struct +;; (defstruct tt ;; all (areapath #f) ;; client related (conns (make-hash-table)) ;; dbfname -> conn - ;; server related - (cleanup-proc #f) ) (defstruct tt-conn host port dbfname ) + +(defstruct tt-srv + ;; server related + (host #f) + (port #f) + (conn #f) + (cleanup-proc #f) + socket + thread + host-port + ) (define (tt:make-remote areapath) (make-tt area: areapath)) (define (tt:client-connect-to-server ttdat) @@ -116,19 +132,37 @@ (define (tt:sync-dbs ttdat) #f) ;; start the listener and start responding to requests ;; -(define (tt:start-server ttdat) +(define (tt:start-server ttdat dbfname) + ;; is there already a server for this dbfile? Then exit. + (let* ((servers (tt:find-server ttdat dbfname))) + (if (not (null? servers)) + (begin + (debug:print 0 *default-log-port* "INFO: found server(s) already running for db "dbfname", "(string-intersperse servers ",")" Exiting.") + (exit)) + (begin + (tt:start-tcp-server ttdat) + (tt:keep-running ttdat dbfname))))) + +(define (tt:start-tcp-server ttdat) + #f) + +(define (tt:keep-running ttdat dbfile) #f) (define (tt:shutdown-server ttdat) (let* ((cleanproc (tt-cleanup-proc ttdat))) (if cleanproc (cleanproc)) ;; close up ports here #f)) +(define (wait-and-close uconn) + (thread-join! (tt-srv-cmd-thread uconn)) + (tcp-close (tt-srv-socket uconn))) + ;; return servid ;; side-effects: ;; ttdat-cleanup-proc is populated with function to remove the serverinfo file (define (tt:create-server-registration-file ttdat dbfname) (let* ((areapath (tt-areapath ttdat)) @@ -175,10 +209,41 @@ (push-directory areapath) (debug:print 0 *default-log-port* "INFO: Trying to start server in tcp mode (" cmdln ") ...") (debug:print 0 *default-log-port* "INFO: starting server at " (common:human-time)) (system (conc "nbfake " cmdln)) (pop-directory))) + +;;====================================================================== +;; tcp connection stuff +;;====================================================================== + +;; create a tcp listener and return a populated udat struct with +;; my port, address, hostname, pid etc. +;; return #f if fail to find a port to allocate. +;; +;; if udata-in is #f create the record +;; if there is already a serv-listener return the udata +;; +(define (setup-listener uconn #!optional (port 4242)) + (handle-exceptions + exn + (if (< port 65535) + (setup-listener uconn (+ port 1)) + #f) + (connect-listener uconn port))) + +(define (connect-listener uconn port) + ;; (tcp-listener-socket LISTENER)(socket-name so) + ;; sockaddr-address, sockaddr-port, sockaddr->string + (let* ((tlsn (tcp-listen port 1000 #f)) ;; (tcp-listen TCPPORT [BACKLOG [HOST]]) + (addr (get-my-best-address))) ;; (hostinfo-addresses (host-information (current-hostname))) + (tt-srv-port-set! uconn port) + (tt-srv-host-port-set! uconn (conc addr":"port)) + (tt-srv-socket-set! uconn tlsn) + uconn)) + + ;;====================================================================== ;; utils ;;====================================================================== @@ -208,7 +273,43 @@ (define (tt:get-servinfo-dir areapath) (let* ((spath (conc areapath"/.servinfo"))) (if (not (file-exists? spath)) (create-directory spath #t)) spath)) + +;;====================================================================== +;; network utilities +;;====================================================================== + +;; NOTE: Look at address-info egg as alternative to some of this + +(define (rate-ip ipaddr) + (regex-case ipaddr + ( "^127\\..*" _ 0 ) + ( "^(10\\.0|192\\.168)\\..*" _ 1 ) + ( else 2 ) )) + +;; Change this to bias for addresses with a reasonable broadcast value? +;; +(define (ip-pref-less? a b) + (> (rate-ip a) (rate-ip b))) + +(define (get-my-best-address) + (let ((all-my-addresses (get-all-ips))) + (cond + ((null? all-my-addresses) + (get-host-name)) ;; no interfaces? + ((eq? (length all-my-addresses) 1) + (car all-my-addresses)) ;; only one to choose from, just go with it + (else + (car (sort all-my-addresses ip-pref-less?)))))) + +(define (get-all-ips-sorted) + (sort (get-all-ips) ip-pref-less?)) + +(define (get-all-ips) + (map address-info-host + (filter (lambda (x) + (equal? (address-info-type x) "tcp")) + (address-infos (get-host-name))))) )