summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoregon_ffj <egon@freifunk-jena.de>2012-04-06 15:39:17 (GMT)
committeregon_ffj <egon@freifunk-jena.de>2012-04-06 15:39:17 (GMT)
commit92fd76acfd0f7d18d17f1d6119ef18aba986b4f0 (patch)
tree9967e1fd84706a8230e67be5111311a2d37da196
parentc3a10fb70db0f344d9dd9f736dc6f8fdf21565f6 (diff)
parent5cdcafa24fa1db4b23090c91cc10be0a35366467 (diff)
Merge branch 'plugnpray' of git://git.freifunk-jena.de/freifunk/ffj-buildroot-v2 into plugnpray
-rw-r--r--doc/services.org49
-rw-r--r--doc/services.p2ptbl-example6
-rwxr-xr-xfiles/common/www/service/cgi-bin/services.json23
-rw-r--r--files/common/www/service/index.html22
-rw-r--r--files/common/www/service/js/parse_services.js176
-rw-r--r--files/common/www/service/js/settings.js2
-rw-r--r--files/common/www/service/services.html27
7 files changed, 294 insertions, 11 deletions
diff --git a/doc/services.org b/doc/services.org
new file mode 100644
index 0000000..e5e6d7d
--- /dev/null
+++ b/doc/services.org
@@ -0,0 +1,49 @@
+Services offered by users are to be discovered automatically and
+offered to all users of the mesh. For vendor independence several
+means of discovery and announcement are required. They are connected
+by the unified information storage.
+
+* Service information storage
+ Every mesh node holds a p2ptl (see overview.org) of all currently
+ available services. It is synchronized over the mesh interface.
+
+ It has the following columns (in-order):
+ * location (unique primary key) :: protocol-specific identifier
+ how to reach the resource (e.g. "1.2.3.4:22" for SSH,
+ "https://1.2.3.4/pron" for HTTP, "//lame/os" for SMB, ...)
+ * expiry time :: unix time after which the entry shall be deleted
+ * source :: how the service has been discovered: manual, portscan,
+ dnssd, ssdp
+ * class :: one of storage, internet, game, stream, search, ... to
+ be extended and refined
+ * description :: a human-readable (and typically human-written)
+ description of the service, e.g. "Toms pr0n archive"
+
+ Additional class-specific columns may follow that give
+ machine-readable information about the service:
+** storage
+ * capacity :: no. of bytes or empty if unknown
+ * used_capacity :: no. of bytes or empty if unknown
+ * writable :: true|false
+** internet
+ * external ip ::
+ * upstream :: no. of bytes/second or empty if unknown
+ * downstream :: no. of bytes/second or empty if unknown
+** game
+ * active players :: no. of players or empty if unknown
+** stream
+ none
+** search
+ none
+
+* Service discovery
+** manual Webif entry
+** Port scanning
+** DNS-SD (Zeroconf)
+** SSDP (UPNP)
+* Service announcement
+** Web interface
+** DNS
+ Zeroconf uses the domain advertised via DHCP to lookup local
+ services via DNS in the same way it uses mDNS (see [[http://www.dns-sd.org/ServerStaticSetup.html][HowTo]]). Our
+ service database could thus be announced via DNS for these clients.
diff --git a/doc/services.p2ptbl-example b/doc/services.p2ptbl-example
new file mode 100644
index 0000000..668e656
--- /dev/null
+++ b/doc/services.p2ptbl-example
@@ -0,0 +1,6 @@
+ftp://1.2.3.4/ 0 2666921306 manual storage Hus pr0n 28635863487 34564836 true
+ftp://1.2.3.5/ 0 2666921306 manual storage klaut_storage 38635863487 204564836
+smb://lame/os/ 0 2666921306 manual storage 38635863487 false
+inet://1.2.3.6 0 2666921306 manual internet 4.3.2.1 23847 38686
+freeciv://1.2.3.4 0 2666921306 manual game 12
+quake3://1.2.3.4 0 2666921306 manual game 11
diff --git a/files/common/www/service/cgi-bin/services.json b/files/common/www/service/cgi-bin/services.json
new file mode 100755
index 0000000..3350efc
--- /dev/null
+++ b/files/common/www/service/cgi-bin/services.json
@@ -0,0 +1,23 @@
+#!/bin/sh -e
+
+. /www/service/cgi-bin/common.sh
+
+echo -e 'Status: 200 OK\r
+Content-Type: application/json\r
+\r
+['
+
+p2ptbl show /tmp/p2ptbl/service \
+ | sed '
+s/^/{"url": "/
+s/\t/", "expire": "/
+s/\t/", "source": "/
+s/\t/", "class": "/
+s/\t/", "desc": "/
+s/\t/", "custom1": "/
+s/\t/", "custom2": "/
+s/\t/", "custom3": "/
+s/$/"},/
+$s/,$//'
+
+echo ']'
diff --git a/files/common/www/service/index.html b/files/common/www/service/index.html
index 3c4f14a..7d99224 100644
--- a/files/common/www/service/index.html
+++ b/files/common/www/service/index.html
@@ -53,15 +53,9 @@
<p><a href="foo"><i class="icon-home"></i> Lokale Dienste</a> in
dieser Wolke:
- <ul>
- <li>3 <a href="foo">Netzlaufwerke</a> (15,5 TB)</li>
- <li><a href="foo">Suche</a> in lokalen Resourcen</li>
- <li>5 <a href="foo">Internetzug&auml;nge</a> (<i class="icon-arrow-down"></i> 55 MB / <i class="icon-arrow-up"></i> 8,2 MB)</li>
- <li>2 <a href="foo">Videostreams</a></li>
- <li>4 <a href="foo">Gameserver</a> (23 <i class="icon-user"></i>)</li>
- <li>und <a href="foo">42 weitere</a></li>
- </ul>
+ <ul id="service-ul"></ul>
</p>
+ <p id="service-loading" class="loading"></p>
</div>
<div class="span4">
@@ -112,12 +106,22 @@
</div>
<script src="/js/jquery.min.js"></script>
+<script src="/js/parse_services.js"></script>
<script src="/js/bootstrap.min.js" defer></script>
<script>
$.getJSON("cgi-bin/online.json", function(haveInternet) {
if(!haveInternet)
$('#offline').removeClass('hidden');
-})
+});
+
+$.when(
+ $.getJSON('/cgi-bin/services.json'),
+ $.ajax({url: '/js/parse_services.js', dataType: 'script', cache: true})
+).done(function(res) {
+ services(res[0]).drawLine($('#service-ul'));
+ $('p#service-loading').remove();
+});
+
</script>
</body>
diff --git a/files/common/www/service/js/parse_services.js b/files/common/www/service/js/parse_services.js
new file mode 100644
index 0000000..616fb56
--- /dev/null
+++ b/files/common/www/service/js/parse_services.js
@@ -0,0 +1,176 @@
+function services(json) {
+ /// class helpers
+ // human readable class name
+ var class_name = {
+ storage: "Netzlaufwerke",
+ internet: "Internetzug&auml;nge",
+ game: "Gameserver"
+ };
+ // table header
+ var table = {
+ storage:
+ '<th>Laufwerk</th>'
+ + '<th>Kapazit&auml;t</th>'
+ + '<th>Daten</th>'
+ + '<th colspan=2 align="right">beschreibbar?</th>',
+ internet: '<th>Mesh IP</th>'
+ + '<th>Public IP</th>'
+ + '<th>Downstream</th>'
+ + '<th>Upstream</th>',
+ game: '<th>Server</th><th>Spieler</th>'
+ };
+ // table row
+ var row = {
+ storage: function(v) {
+ return '<td>'
+ + (v.desc != ''
+ ? ('<a href="' + v.url + '">'
+ + v.desc + '<br />'
+ + '<small>' + v.url + '</small>'
+ + '</a>')
+ : ('<a href="' + v.url + '">'
+ + v.url
+ + '</a>'))
+ + '</td><td>'
+ + (v.capacity ? SISuffixify(v.capacity) + 'B' : '')
+ + '</td><td>'
+ + (v.used ? SISuffixify(v.used) + 'B' : '')
+ + '</td><td>'
+ + ((v.used && v.capacity)
+ ? (v.used / v.capacity * 100).toPrecision(2) + '%'
+ : '')
+ + '</td><td>'
+ + ((v.custom3 == 'true') ? '<i class="icon-ok"></i>' : '')
+ + ((v.custom3 == 'false') ? '<i class="icon-remove"></i>' : '')
+ + '</td>';
+ },
+ internet: function(v) {
+ return '<td>'
+ + (v.desc != ''
+ ? v.desc
+ : v.url.substr(7))
+ + '</td><td>'
+ + (v.public_ip ? v.public_ip : '')
+ + '</td><td>'
+ + (v.downstream ? SISuffixify(v.downstream * 8) + 'b/s' : '')
+ + '</td><td>'
+ + (v.upstream ? SISuffixify(v.upstream * 8) + 'b/s' : '')
+ + '</td>';
+ },
+ game: function(v) {
+ return '<td>'
+ + (v.desc != ''
+ ? ('<a href="' + v.url + '">'
+ + v.desc + '<br />'
+ + '<small>' + v.url + '</small>'
+ + '</a>')
+ : ('<a href="' + v.url + '">'
+ + v.url
+ + '</a>'))
+ + '</td><td>'
+ + (v.players ? v.players : '')
+ + '</td>';
+ }
+ };
+ // mini row: emit one line per class (not one per service)
+ var miniRow = {
+ storage: function(c) {
+ var cap = sum(c, 'capacity');
+ return c.length
+ + ' <a href="/services.html#storage">Netzlaufwerke</a>'
+ + ((cap) ? ' (' + SISuffixify(cap) + 'B)' : '');
+ },
+ internet: function(c) {
+ var up = sum(c, 'upstream');
+ var down = sum(c, 'downstream');
+ var stream = function(dir, amount) {
+ return amount
+ ? ('<i class="icon-arrow-' + dir + '"></i> '
+ + SISuffixify(amount * 8) + 'b')
+ : '';
+ }
+ return c.length
+ + ' <a href="/services.html#internet">Internetzug&auml;nge</a>'
+ + ((up || down)
+ ? (' ('
+ + stream('down', down)
+ + ((up && down) ? ' / ' : '')
+ + stream('up', up)
+ + ')')
+ : '');
+ },
+ game: function(c) {
+ var pl = sum(c, 'players');
+ return c.length
+ + ' <a href="/services.html#game">Gameserver</a>'
+ + ((pl) ? ' (' + pl + ' <i class="icon-user"></i>)' : '');
+
+ }
+ };
+ // aliases for customX values
+ var parseCustom = {
+ storage: ['capacity', 'used', 'writable'],
+ internet: ['public_ip', 'upstream', 'downstream'],
+ game: ['players']
+ };
+
+ /// parse JSON
+ var srv = {};
+ $.each(json, function(k,v) {
+ var cn = v['class'];
+ for (var i=1; i<4; i++) {
+ var custom = v['custom' + i];
+ if (parseCustom[cn] && parseCustom[cn][i-1]
+ && custom && custom != '')
+ v[parseCustom[cn][i-1]] = custom;
+ }
+ if (!srv[cn])
+ srv[cn] = [];
+ srv[cn].push(v);
+ });
+
+ /// helpers
+ function SISuffixify(x) {
+ suffix_char = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
+ suffix = 0;
+ for (; x > 1000; x /= 1000, suffix++);
+ return x.toPrecision(3) + '&nbsp;' + suffix_char[suffix];
+ }
+
+ function sum(container, key) {
+ var sum = 0;
+ $.each(container, function(idx, obj) {
+ if (obj[key])
+ sum += +obj[key];
+ });
+ return sum > 0 ? sum : undefined;
+ }
+
+ /// return: data + drawing functions
+ return {
+ data: srv,
+ drawContainer: function(elem) {
+ $.each(srv, function(cn, services) {
+ var tbody = '';
+ $.each(services, function(i,v) {
+ tbody = tbody + '<tr>' + row[cn](v) + '</tr>';
+ });
+ elem.append(
+ '<div class="span6">'
+ + '<h3>' + class_name[cn] + '</h3>'
+ + '<table class="table table-striped">'
+ + '<thead><tr>'
+ + table[cn]
+ + '</tr></thead>'
+ + '<tbody>' + tbody + '</tbody>'
+ + '</table>'
+ + '</div>'
+ );
+ })},
+ drawLine: function(elem) {
+ $.each(srv, function(cn, services) {
+ elem.append('<li>' + miniRow[cn](services) + '</li>');
+ });
+ }
+ };
+}
diff --git a/files/common/www/service/js/settings.js b/files/common/www/service/js/settings.js
index 094ea5f..292f3ef 100644
--- a/files/common/www/service/js/settings.js
+++ b/files/common/www/service/js/settings.js
@@ -47,7 +47,7 @@ function fillData(key, val, def) {
/// load data
-var loadBootstrap = $.ajax({url: "js/bootstrap.min.js", dataType: "script", cached: true});
+var loadBootstrap = $.ajax({url: "js/bootstrap.min.js", dataType: "script", cache: true});
var loadCfg = {};
$.each(['client_net', 'cfg_client', 'cfg_router', 'node_position'],
function(i,name) {
diff --git a/files/common/www/service/services.html b/files/common/www/service/services.html
index e3e17d0..4f6a00f 100644
--- a/files/common/www/service/services.html
+++ b/files/common/www/service/services.html
@@ -17,7 +17,32 @@
</ul></div>
</div></div></div>
-hier gibt es irgendwann einmal eine automatisch generierte liste aller in der wolke verfuegbaren dienste
+<div class="container-fluid"><div class="row" id="service-container">
+ <div class="span3">
+ <h2>Lokale Dienste</h2>
+
+ <p>Im Freifunk Jena Netzwerk kann jeder Mensch Daten und Dienste
+ seiner Computer zur Verf&uuml;gung stellen und die Dienste anderer
+ Teilnehmer nutzen. Auf dieser Seite siehst du, welche Dienste
+ angeboten werden. Um Dienste auf deinem Computer oder in deinem
+ Netzwerk freizugeben, besuche <a href="/settings.html"
+ style="white-space:
+ nowrap"><i class="icon-cog"></i>&nbsp;Einstellungen</a>.</p>
+
+ <p class="loading"></p>
+ </div>
+</div></div>
+
+<script src="/js/jquery.min.js"></script>
+<script>
+$.when(
+ $.getJSON('/cgi-bin/services.json'),
+ $.ajax({url: '/js/parse_services.js', dataType: 'script', cache: true})
+).done(function(res) {
+ services(res[0]).drawContainer($('#service-container'));
+ $('p.loading').remove();
+});
+</script>
</body>
</html>
contact: Jan Huwald // Impressum