diff --git a/conf-dispatcher/couch-client.js b/conf-dispatcher/couch-client.js index 1d10378..4fb78f5 100644 --- a/conf-dispatcher/couch-client.js +++ b/conf-dispatcher/couch-client.js @@ -73,12 +73,12 @@ function CouchClient(url) { headers["Content-Type"] = "application/json"; } getServerFromPool(uri, function (err, server) { +console.log(path+"***************************************"+require('util').inspect(server)) var request = server.request(method, path, headers); if (body) { request.write(body, 'utf8'); } request.end(); - request.on('response', function (response) { response.setEncoding('utf8'); var body = ""; diff --git a/conf-dispatcher/server.js b/conf-dispatcher/server.js index bc7eabf..0979641 100644 --- a/conf-dispatcher/server.js +++ b/conf-dispatcher/server.js @@ -1,5 +1,6 @@ var http = require('http'); var url = require('url'); +var util = require('util'); var fs = require('fs'); var CouchClient = require('./couch-client'); @@ -8,15 +9,15 @@ var callStreamie = function(token, fn) { var streamie = http.request({ host: 'streamie.org', //host: '172.29.29.222', - //port: 8080, - port: 80, - path: '/user_info', + //port: 8080, + port: 80, + path: '/user_info', method: 'GET', - headers: { - 'Host': 'streamie.org', - 'Cookie': 'token='+token - } - }, function(res) { + headers: { + 'Host': 'streamie.org', + 'Cookie': 'token='+token + } + }, function(res) { res.setEncoding('utf8') res.on('data', function(doc) { try { @@ -27,24 +28,23 @@ var callStreamie = function(token, fn) { console.error('callStreamie:exception:'+e) } }) - }).end() + }).end() } var getMacAddress = function(address, fn) { fs.readFile('/proc/net/arp', 'utf8', function(err, data) { - if (err) { - console.error('can not read /proc/net/arp:'+err) - return - } - var lines = data.toString().split("\n") - - for(var i = lines.length-1; i >= 0; --i) { - var line = lines[i].split(/\s+/) - if (line[0] == address) { - return fn(line[3]) + if (err) { + console.error('can not read /proc/net/arp:'+err) + return } - } - fn() + var lines = data.toString().split("\n") + for(var i = lines.length-1; i >= 0; --i) { + var line = lines[i].split(/\s+/) + if (line[0] == address) { + return fn(line[3]) + } + } + fn() }) } @@ -53,30 +53,37 @@ var updateStreamie = function(mac, req, ret) { if (err) { console.error('couchdb:get:failure:'+err) } //console.log('mac:'+mac+":"+JSON.stringify(ret)+":"+JSON.stringify(doc)) //console.log(req) + var client = { + ipv4: req.headers['x-real-ip'] || req.socket.remoteAddress, + hwaddr: mac, + useragent: req.headers['user-agent'], + created_at: new Date() + }; if (!doc) { +console.log('NEW-DOC'); doc = { _id: ret.user_id, twitter: ret, - clients: [{ - ipv4: req.headers['x-real-ip'] || req.socket.remoteAddress, - hwaddr: mac, - useragent: req.headers['user-agent'], - created_at: new Date() - }] + clients: [client] } } else { +console.log('UPDATE-DOC'); doc.twitter = ret delete doc.completed + var found = false; for(var i = doc.clients.length-1; i >= 0; --i) { - var clients = doc.clients[i] - if (clients.ipv4 == req.headers['x-real-ip'] || req.socket.remoteAddress) { - clients.hwaddr = mac - clients.useragent = req.headers['user-agent'] - clients.created_at = new Date() - delete clients.iptabled + var r_ip = req.headers['x-real-ip'] || req.socket.remoteAddress; + if (doc.clients[i].ipv4 == r_ip) { +console.log('UPDATE-IPUPDATE'); + doc.clients[i] = client; + found = true; break; } } + if (!found) { +console.log('UPDATE-ADDCLIENT'); + doc.clients.push(client); + } } //console.log(JSON.stringify(doc)) streamie.save(doc, function(err, doc) { @@ -113,48 +120,71 @@ streamie.request('PUT', '/streamie', function(err, result) { res.end('Weg hier \n'); }).listen(8124, "127.0.0.1"); - var updateIPTables = function(id) { + var iptables = function(para, fn) { + var iptables = require('child_process').spawn('sudo', ['/sbin/iptables'].concat(para)) + iptables.on('exit', function(code) { + console.log('iptables:'+para.join(' ')+"=>"+code); + fn(code); + }); + } + + var updateIPTables = function(id, rev) { streamie.get(id, function(err, doc) { if (err) { console.error('couchdb:get:failure:'+err) } - if (doc.completed) { return } + if (doc._rev != rev) { return } + if (doc.completed && doc.completed.pid == process.pid) { return } for(var i = doc.clients.length-1; i >= 0; --i) { var client = doc.clients[i] - if (!client.iptabled) { - console.log('iptables -D INPUT -i eth0 -m mac --mac-source '+client.hwaddr+' -s '+client.ipv4+' -j ACCEPT') - console.log('iptables -A INPUT -i eth0 -m mac --mac-source '+client.hwaddr+' -s '+client.ipv4+' -j ACCEPT') - client.iptabled = new Date() - } + + // $IPTABLES -t mangle -I FREE_MACS -i $CONF_IF -p all -m mac + // --mac-source c8:bc:c8:4f:d4:66 -s 10.205.0.100 -j MARK --set-mark 0x1205 + var iptable = []; + iptable.push('-t'); + iptable.push('mangle'); + iptable.push('-I'); + iptable.push('FREE_MACS'); + iptable.push('-p'); + iptable.push('all'); + iptable.push('-m'); + iptable.push('mac'); + iptable.push('--mac-source'); + iptable.push(client.hwaddr); + iptable.push('-s'); + iptable.push(client.ipv4); + iptable.push('-j'); + iptable.push('MARK'); + iptable.push('--set-mark'); + iptable.push('0x1205'); + iptables(iptable, function(code) { + client.iptabled = { date: new Date(), exitcode: code, rev: doc._rev } + }); } - doc.completed = new Date() + doc.completed = { date: new Date(), pid: process.pid, rev: doc._rev }; streamie.save(doc, function(err, doc) { if (err) { console.log('updateIPTables failed:'+err) - updateIPTables(id) + updateIPTables(id, rev) } }) }) } - -// var _streamie = CouchClient('http://localhost:5984/streamie') - fs.readFile('streamie.json', 'utf8', function(err, data) { - since = data || '0' - if (err) { since = '0' } - console.log('CHANGES:'+since) - streamie.changes(since, function(err, changes) { - if (err) { - console.error('couchdb:changes:failure:'+err) - return - } - fs.writeFile('streamie.json', changes.seq+'', 'utf8', function(err) { - if (err) { - console.error('couchdb:write:failure:'+err) - return - } - if (changes.deleted) { return } - updateIPTables(changes.id) - }) - }) - }) + iptables(['-t', 'mangle', '-F', 'FREE_MACS'], function(code) { + iptables(['-t', 'mangle', '-A', 'FREE_MACS', '-j', 'RETURN'], function(code) { + streamie.changes(0, function(err, changes) { + if (err) { + console.error('couchdb:changes:failure:'+err) + return + } + if (changes.deleted) { +console.log('DELETE:'+util.inspect(changes)); + return + } + for(var i in changes.changes) { + updateIPTables(changes.id, changes.changes[i].rev ) + } + }) + }) + }) }) console.log('Server running at http://127.0.0.1:8124/'); diff --git a/conf-dispatcher/streamie.json b/conf-dispatcher/streamie.json index 19c7bdb..25bf17f 100644 --- a/conf-dispatcher/streamie.json +++ b/conf-dispatcher/streamie.json @@ -1 +1 @@ -16 \ No newline at end of file +18 \ No newline at end of file diff --git a/flow/couch-client.js b/flow/couch-client.js new file mode 100644 index 0000000..6f14e66 --- /dev/null +++ b/flow/couch-client.js @@ -0,0 +1,314 @@ +/*global Buffer */ + +var http = require('http'), + https = require('https'), + Url = require('url'), + EventEmitter = require('events').EventEmitter, + querystring = require('querystring'); + + +// Handles changes made in node v0.3.0 +var NOT_FOUND_ERR_NO = process.ENOENT ? process.ENOENT : require('constants').ENOENT; +var MAX_DOCS = 1000; // The maximum number of docs to send in a single batch + +function noOp(err) { if (err) { throw err; } } + +var CONNECTION_DEFAULTS = { + host: '127.0.0.1:5984', + port: 5984, + hostname: '127.0.0.1', + pathname: "/" +}; + +function CouchClient(url) { + var uri = Url.parse(url); + uri.secure = uri.protocol == 'https:'; + uri.protocolHandler = uri.secure ? https : http; + uri.__proto__ = CONNECTION_DEFAULTS; + var revCache = {}; + + // A simple wrapper around node's http(s) request. + function request(method, path, body, callback) { + var stream; + // Body is optional + if (typeof body === 'function' && typeof callback === 'undefined') { + callback = body; + body = undefined; + } + // Return a stream if no callback is specified + if (!callback) { + stream = new EventEmitter(); + stream.setEncoding = function () { + throw new Error("This stream is always utf8"); + }; + } + + function errorHandler(err) { + if (callback) { callback(err); } + if (stream) { stream.emit('error', err); } + } + + var headers = { + "Host": uri.hostname + }; + // add the authorization header if provided and using https + if (uri.auth) { + headers["Authorization"] = "Basic " + new Buffer(uri.auth, "ascii").toString("base64"); + } + + if (body) { + body = JSON.stringify(body); + headers["Content-Length"] = Buffer.byteLength(body); + headers["Content-Type"] = "application/json"; + } + + var options = { + host: uri.hostname, + method: method, + path: path, + port: uri.port, + headers: headers + }; + var request = uri.protocolHandler.request(options, function (response) { + response.setEncoding('utf8'); + var body = ""; + response.on('data', function (chunk) { + if (callback) { body += chunk; } + if (stream) { stream.emit('data', chunk); } + }); + response.on('end', function () { + if (callback) { + try { + var parsedBody = JSON.parse(body); + callback(null, parsedBody); + } catch(err) { + callback(err); + } + } + if (stream) { stream.emit('end'); } + }); + response.on('error', errorHandler); + }); + request.on('error', errorHandler); + + if (body) { request.write(body, 'utf8'); } + request.end(); + + return stream; + } + + // Requests UUIDs from the couch server in tick batches + var uuidQueue = []; + function getUUID(callback) { + uuidQueue.push(callback); + if (uuidQueue.length > 1) { return; } + function consumeQueue() { + var pending = uuidQueue.splice(0, MAX_DOCS); + if (uuidQueue.length) { process.nextTick(consumeQueue); } + // console.log("Bulk getting UUIDs %s", pending.length); + request("GET", "/_uuids?count=" + pending.length, function (err, result) { + if (err) { + pending.forEach(function (callback) { + callback(err); + }); + return; + } + if (result.uuids.length !== pending.length) { + throw new Error("Wrong number of UUIDs generated " + result.uuids.length + " != " + pending.length); + } + result.uuids.forEach(function (uuid, i) { + pending[i](null, uuid); + }); + }); + } + process.nextTick(consumeQueue); + } + + // Saves documents in batches + var saveValues = []; + var saveQueue = []; + function realSave(doc, callback) { + // Put key and rev on the value without changing the original + saveValues.push(doc); + saveQueue.push(callback); + if (saveQueue.length > 1) { return; } + function consumeQueue() { + var pending = saveQueue.splice(0, MAX_DOCS); + var body = saveValues.splice(0, MAX_DOCS); + if (saveQueue.length) { process.nextTick(consumeQueue); } + // console.log("Bulk saving %s", body.length); + request("POST", uri.pathname + "/_bulk_docs", {docs: body}, function (err, results) { + if (results.error) { + err = new Error("CouchDB Error: " + JSON.stringify(results)); + if (results.error === 'not_found') { err.errno = NOT_FOUND_ERR_NO; } + } + if (err) { + pending.forEach(function (callback) { + callback(err); + }); + return; + } + results.forEach(function (result, i) { + var doc = body[i]; + doc._id = result.id; + doc._rev = result.rev; + revCache[result.id] = result.rev; + pending[i](null, doc); + }); + }); + } + process.nextTick(consumeQueue); + } + + var getQueue = []; + var getKeys = []; + function realGet(key, includeDoc, callback) { + getKeys.push(key); + getQueue.push(callback); + if (getQueue.length > 1) { return; } + function consumeQueue() { + var pending = getQueue.splice(0, MAX_DOCS); + var keys = getKeys.splice(0, MAX_DOCS); + if (getQueue.length) { process.nextTick(consumeQueue); } + var path = uri.pathname + "/_all_docs"; + if (includeDoc) { path += "?include_docs=true"; } + // console.log("Bulk Getting %s documents", keys.length); + request("POST", path, {keys: keys}, function (err, results) { + if (!results.rows) { + err = new Error("CouchDB Error: " + JSON.stringify(results)); + } + if (err) { + pending.forEach(function (callback) { + callback(err); + }); + return; + } + results.rows.forEach(function (result, i) { + var err; + if (includeDoc) { + if (result.error) { + err = new Error("CouchDB Error: " + JSON.stringify(result)); + if (result.error === 'not_found') { err.errno = NOT_FOUND_ERR_NO; } + pending[i](err); + return; + } + if (!result.doc) { + err = new Error("Document not found for " + JSON.stringify(result.key)); + err.errno = NOT_FOUND_ERR_NO; + pending[i](err); + return; + } + pending[i](null, result.doc); + return; + } + pending[i](null, result.value); + }); + }); + } + process.nextTick(consumeQueue); + } + + + function save(doc, callback) { + if (!callback) { callback = noOp; } + if (doc._id) { + if (!doc._rev) { + if (!revCache.hasOwnProperty(doc._id)) { + realGet(doc._id, false, function (err, result) { + if (err) { return callback(err); } + if (result) { + revCache[doc._id] = result.rev; + doc._rev = result.rev; + } + realSave(doc, callback); + }); + return; + } + doc._rev = revCache[doc._id]; + } + } + realSave(doc, callback); + } + + function get(key, callback) { + realGet(key, true, callback); + } + + function remove(doc, callback) { + if (typeof doc === 'string') { + doc = {_id: doc}; + } + doc._deleted = true; + save(doc, callback); + } + + function changes(since, callback) { + var stream = request("GET", uri.pathname + "/_changes?feed=continuous&heartbeat=1000&since=" + since); + var data = ""; + function checkData() { + var p = data.indexOf("\n"); + if (p >= 0) { + var line = data.substr(0, p).trim(); + data = data.substr(p + 1); + if (line.length) { + callback(null, JSON.parse(line)); + } + checkData(); + } + } + stream.on('error', callback); + stream.on('data', function (chunk) { + data += chunk; + checkData(); + }); + stream.on('end', function () { + throw new Error("Changes feed got broken!"); + }); + } + + function view(viewName, obj, callback) { + var method = "GET"; + var body = null; + if (typeof obj === 'function') { + callback = obj; + obj = null; + } + + if ( viewName.substr(0,1) != '/' ) { + // assume viewname is designdocname/viewname + var parts = viewName.split("/",2); + if ( parts.length == 2 ) { + viewName = uri.pathname+"/_design/"+parts[0]+"/_view/"+parts[1]; + } + } + + if (obj && typeof obj === 'object') { + Object.keys(obj).forEach(function(key){ + if ( key === 'keys' ) { + body = { keys: obj[key] }; // body is json stringified in request fn + method='POST'; + } else { + obj[key] = JSON.stringify(obj[key]); + } + }); + var getParams = querystring.stringify(obj); + if (getParams){ + viewName = viewName + '?' + getParams; + } + } + request(method, viewName, body, callback); + } + + // Expose the public API + return { + get: get, + save: save, + remove: remove, + changes: changes, + request: request, + uri: uri, + view: view + }; +} + +module.exports = CouchClient; diff --git a/flow/flow2couchdb.config b/flow/flow2couchdb.config index cc60740..9b67e24 100644 --- a/flow/flow2couchdb.config +++ b/flow/flow2couchdb.config @@ -1 +1 @@ -{"startDate":"2011-03-19T19:56:00.000Z"} \ No newline at end of file +{"startDate":"2011-04-28T10:44:00.000Z"} \ No newline at end of file diff --git a/flow/flow2couchdb.js b/flow/flow2couchdb.js index 83dbb52..84e882b 100644 --- a/flow/flow2couchdb.js +++ b/flow/flow2couchdb.js @@ -1,10 +1,15 @@ var fs = require('fs'); +var util = require('util'); var http = require('http'); var cp = require('child_process'); +var CouchClient = require('./couch-client'); +var _ = require('underscore')._; + +require('datejs'); var Server = { host: '172.16.143.8', port: 9200, path: '/traffic', headers: { 'content-type': 'application/json' } }; -var Server = { host: '172.16.143.8', port: 5984, path: '/traffic', headers: { 'content-type': 'application/json' } }; +var Server = { host: '127.0.0.1', port: 5984, path: '/traffic', headers: { 'content-type': 'application/json' } }; //flow-cat -t '3/14/11 9:31:00' -T '3/14/11 9:32:00' -z9 2011 | flow-print var formatDate = function(date) { @@ -31,115 +36,226 @@ var setStartDate = function(date, done) { var gid = 1; var running = 0; -var storeCouch = function(cmds, id, cmd, i, found, count) { - found = false +var storeCouch = function(cmds, complete, id, cmd, i, found, count) { +console.log('ENTER-storeCouch'+util.inspect(cmds)); + found = false; for (i = running; i < 12; ++i) { running = 12; found = true; //console.log('QUEUE:'+i); - storeCouch(cmds, id, cmd); + storeCouch(cmds, complete, id, cmd); } if (found) { return } - cmd = cmds.shift(); if (!cmd) { return; } +console.log('START-storeCouch'+util.inspect(cmd)); //srcIP dstIP prot srcPort dstPort octets packets id = ++gid; count = 0; var run = function() { cp.exec(cmd.cmd.join(' '), { maxBuffer: 10*1024*1024 }, function(error, stdout, stderr, req, lines, out) { -console.log('WROTE:'+cmd.end); +//console.log('WROTE:'+cmd.key); if (error) { console.log('ERROR:'+id+":"+error+":"+cmd.cmd.join(' ')); if (count++ < 3) { return run(); } - setStartDate(cmd.end, function() { - running--; - storeCouch(cmds) - }) + running--; + storeCouch(cmds, complete) return; } lines = stdout.split("\n"); +//console.log('LINES:'+util.inspect(lines)); delete stdout; delete stderr; lines.shift(); out = []; var out = { - _id: ""+cmd.start.getTime(), - start: cmd.start, - end: cmd.end, - tuples: [] + _id: cmd.key, + tuples: [], + created_at: Date() }; for(var line in lines) { line = lines[line]; var cols = line.split(/[ ]+/) - out.tuples.push({ - srcIP: cols[0], - dstIP: cols[1], - prot: cols[2], - srcPort: cols[3], - dstPort: cols[4], - octets: cols[5], - packets: cols[6] - }); + if (cols.length >= 7) { + out.tuples.push({ + srcIP: ip2twitter[cols[0]] || cols[0], + dstIP: ip2twitter[cols[1]] || cols[1], + prot: cols[2], + srcPort: cols[3], + dstPort: cols[4], + octets: cols[5], + packets: cols[6] + }); + } } - Server.method = 'POST'; - req = http.request(Server, function(res) { - /* - res.on('data', function (chunk) { - console.log('BODY: ' + chunk); - }); - */ - //console.log(res); + if (!out.tuples.length) { delete lines; delete cols; delete data; delete req; - setStartDate(cmd.end, function() { - running--; - storeCouch(cmds) - }) + running--; + storeCouch(cmds, complete) + if (!cmds.length) { console.log('storeCouch-COMPLETE'); complete(); } + return; + } +//console.log('OUT:'+util.inspect(out)); + traffic.save(out, function(err, doc) { + delete lines; + delete cols; + delete data; + delete req; + running--; + storeCouch(cmds, complete) + if (!cmds.length) { console.log('storeCouch-COMPLETE'); complete(); } }) - req.end(JSON.stringify(out)); }) } run(); } -Server.method = 'PUT'; -var req = http.request(Server, function(res) { - -getStartDate(function(startDate) { - if (!startDate) { - startDate = new Date((~~((new Date()).getTime()/86400000)*86400000)-(2*86400000)); - } - endDate = new Date(~~((new Date()).getTime()/60000)*60000); -console.log(startDate+"=>"+endDate) - var cmds = [] - for (var current = startDate.getTime(); current < endDate.getTime(); current += 60000) { - var start = new Date(current) - var end = new Date(current+60000) - var cmd = ['flow-cat']; - cmd.push('-t') - cmd.push(formatDate(start)); - cmd.push('-T') - cmd.push(formatDate(end)) - cmd.push('/tmp/2011') - cmd.push('| flow-print') - cmds.push({ cmd: cmd, start: start, end: end}) +var ip2twitter = {}; + +var lastStreamieChange = null; +var streamie = new CouchClient('http://localhost:5984/streamie'); +streamie.changes(0, function(err, changes) { + if (err) { + console.error('couchdb:changes:failure:'+err) + return + } + if (changes.deleted) { + return } - storeCouch(cmds); -/* - setStartDate(endDate, function() { - + streamie.get(changes.id, function(err, doc) { + if (err) { + console.error('couchdb:changes:failure:'+err) + return + } + for (var i in doc.clients) { + var client = doc.clients[i]; +console.log('ADD IP2Twitter:'+client.ipv4+"=>"+doc.twitter.screen_name); + ip2twitter[client.ipv4] = doc.twitter.screen_name; + } + lastStreamieChange = new Date(); }) -*/ - }) -}).end(); +crawler = function(base, completed, data) { + data = data || { dirs: [], files: [], calls: 0 }; + data.calls++; +console.log('IN-CRAWLER:'+base+":"+data.calls); + + fs.readdir(base, function(err, in_files, dirs, cnt, i) { + if (err) { + console.log('fs.readdir:err:'+err); + return; + } + dirs = []; + cnt = 0; + for(i in in_files) { + (function(fname, dir) { + fs.stat(base+'/'+fname, function(err, stat) { + if (err) { + console.log('fs.readdir:err:'+err); + return; + } + if (stat.isFile()) { data.files.push(base+'/'+fname); } + else if (stat.isDirectory()) { dirs.push(base+'/'+fname); } + if (++cnt == in_files.length) { + data.dirs.push.apply(data.dirs, dirs); + for(dir in dirs) { + crawler(dirs[dir], completed, data); + } + data.calls--; +console.log('OUT-CRAWLER-A:'+base+":"+data.calls); +//console.log('DATA:'+util.inspect(data)); + if (!data.calls) { + completed(data); + } + } + }) + })(in_files[i]); + } + if (!in_files.length) { + data.calls--; +console.log('OUT-CRAWLER-B:'+base+":"+data.calls); + if (!data.calls) { + completed(data); + } + } + }) +} + +var traffic = new CouchClient('http://localhost:5984/traffic'); +var observedDirs = {}; +setTimeout(function CheckChanges() { + var now = new Date(); + if (lastStreamieChange && (now.getTime()-lastStreamieChange.getTime()) > 1000) { + console.log('START-CRAWLER'+util.inspect(ip2twitter)); + traffic.request('PUT', '/traffic', function(err, result) { + console.log('AAAA'); + if (err) { + console.error('couchdb:traffic:failure:'+err) + return + } + var running = false; + var crawlerQueue = []; + crawler('/flows', function AddFlows(data) { + for(var i in data.dirs) { + (function CheckDir(dir) { + if (observedDirs[dir]) { return; }; + observedDirs[dir] = true; +console.log('OBSERVER:'+dir); + + fs.watchFile(dir, function() { +console.log('dir='+dir); + if (running) { + crawlerQueue.push(dir); + return; + } + running = true; + crawler(dir, AddFlows); + }) + + })(data.dirs[i]); + } + //var re = new RegExp("^.*\/ft-v05.\(\d+\-\d+\-\d+.\d+\)\+0200"); + var re = new RegExp("^.*\/ft-v05\.\(\\d+-\\d+-\\d+\.\\d+\)\\+\\d+$"); + var cmds = []; + for(var i in data.files) { + var file = data.files[i]; + var res = re.exec(file); + if (res) { +console.log('ADD-Flow:'+file); + cmds.push({key: res[1], cmd: ['flow-print', '<', file, '&&', 'mv', file, file+'-done']}) + //cmds.push({key: res[1], cmd: ['flow-print', '<', file]}); + } + } + if (!cmds.length) { + running = false; + var dir = crawlerQueue.shift(); + if (dir) { crawler(dir, AddFlows); } + } + storeCouch(cmds, function() { + running = false; + var dir = crawlerQueue.shift(); + if (dir) { crawler(dir, AddFlows); } + }); + }); + }) + } else { + setTimeout(CheckChanges, 1000); + } +}, 1000); + + + + +//console.log("MENO"+Date.parseExact("2011-04-10", "yyyy-M-d")) +//console.log("MENO"+Date.parseExact("2011-04-10.174750", "yyyy-M-d.HHmmss")) +