summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--misc/analiza.txt1
-rw-r--r--misc/manjka_piece_layers.txt138
-rw-r--r--src/dht.c87
-rw-r--r--src/main.c7
-rw-r--r--www/index.php3
-rwxr-xr-xwww/insert.php132
-rw-r--r--www/stanje.php6
7 files changed, 336 insertions, 38 deletions
diff --git a/misc/analiza.txt b/misc/analiza.txt
new file mode 100644
index 0000000..ac2ef30
--- /dev/null
+++ b/misc/analiza.txt
@@ -0,0 +1 @@
+cd www; find .. -maxdepth 2 -name *.torrent | parallel ./insert.php 'mysql:host=tranzistor:3306\;dbname=travnik\;charset=utf8' travnik $TRAVNIK_DB_PASSWORD {} | tee ../tmp/insert.txt; ocd
diff --git a/misc/manjka_piece_layers.txt b/misc/manjka_piece_layers.txt
new file mode 100644
index 0000000..65524c8
--- /dev/null
+++ b/misc/manjka_piece_layers.txt
@@ -0,0 +1,138 @@
+449de98d537033333b8b906f173aeca358db2934
+44b84847f7d829225c788e161017e484526e5259
+44b2ee5bbe4c0abcc1b089d511e0aa90bdbb9d3f
+449ee63fa8a383a59b2a98d7d8851c7e160674b3
+4490221a4cb0d2bcf21ff2feb011f0aaf29f47c1
+472eca9914ca5c91a435644d0e03065deaaff388
+ef1b17e8e84cb09ba0c4642c3e401d5afaf6c116
+a3838eca4a8d377946db68eb0c68e0e7fa60b6f0
+58b66fde563352e87b4a9edda7f6303fb3d51af6
+8a51ff72f85a542141ac4d5031e0d5d33e3dc552
+ca3bf9975e7fc9f3e1d28bfce1463c6590058af0
+449aae0961adca0ed412903314ec0a2268d28ae1
+b0104173e4f57a7b8f22453a616e6c07eea7e498
+8674ae9dadac02b84f79240c5d8c15b7e2754abb
+20ca3b531663a6560ce4bf298270722ebd8056f5
+8aee4dac68d5890e86d926aac7a5e57191572881
+76ef0dcfcbcd4fc6eb041ec86bcbc9b84a04248f
+e40fc02287941ef2b27bff81829a272e040f61bb
+e5a8b4f3df8c7544b614b7e568490859ce462781
+e945fd776f2c563e8d3854033d5e71a1c344beae
+e96193c0e90533a386ca103f4d893b39f7e0e604
+e92db52764d5d32425d1255cfe1bc14c17e700e4
+3b92af49f505a8d45c6e81cab6ab1ce5e9c9def2
+3dd437e15c0db9f45688e093cfa569641f5fbc86
+3ddafd52547600a28de6efc4408ae73c63742072
+a132644ccdcf09dcc3812e784165ea129720b93a
+e56c512dcecd07aac2043c014c61bec934f29875
+4c85dea3d00d041f3a66e12a0ae015bc9ab8010c
+3a95d8b80cad8bd441ce867ff6365046dffddc0a
+3a8ccb5c5f59d491a3cc18646a83b90090606da6
+48e289b6efcc67d49b7819a1e8327fe24be08435
+5115e0b09978ba0ddd6a30a6a92fc503ae644f1d
+880d2a5b3446f3bb914facbb04c9d513ec5da89e
+b47fbe20899b8ef182e45f58a62edddc3bb78863
+d4b3eb8944074e1491c49a132827de6f6b4a8e97
+9690551f4adb023ba4f80d6a1df795baccc196f8
+2a6644871d12fd0c6808ee3e6501027da62a2f07
+98e1eb3e9430f0f20230bcd64deeed68181bd9d8
+b65dc2866dd108c724e84e8983560130ced8e18f
+f85c7bd79be33309177eb253aa10c3ef1347eadc
+b61456fe7b7ae74e9d805e584955775cbb784202
+b64abd382e298d086c4bd7a0b00ef1f11dfeccfc
+7afb5e79530c742b4501b54cc71b0ab1d97f2f95
+ecabe733b93d14637d2aed1dfb0f4eb59bdb048b
+efed72ead6e6fcebea4baf1ee2d19bca5be44142
+eea0ee9d789104bb5808fd69cb0555391f2b4f50
+8d0eb365a521ad9bf9156a43eddcf0ed8a689b13
+f1a61d828983a124d487acee4f2ce7e8386b718e
+de4443fe31894dfd55b17e70096c1613a9a4bb9c
+f274529b213f19557fa6ecfcf3cfb53c5154e526
+678a109563a19cc9ffa21708d9abc458e1d8906c
+9c5d033dea8f9a3382a20c99218c7a431dfaaf6e
+19e66c53c722a13cd6a82879c9e85bf88c66735a
+404eb209c11aa3947d6c81300b2d29f2d26efa4f
+799cf2575769406187e9e014c99a5e7f485c2a83
+319d369388f27c4c14e0259663728e8ba2e688b9
+14dec98f148ec2e6444073ae640640507381afa0
+6f9ec668b5d83d298c56682000817e33ec757b78
+bdc393a39f4e51964bf71cbaa5ac3e237928db8a
+562a762c2009b0fe6726b9b5cefc141bc42a46f9
+c2c4250cb585b8a2fa71bf072ec46dcf80c88e4d
+41e05f5273ccc219d36d34efb9b40e6b6a909a44
+3bf800aaf4bc9f42829af7ab5eaaa8a6d9e46469
+2713ba902590bd7e239aaffc43dc6af94c1f4ac1
+e5dfcc39b252356c4a873937abcb56be9f6988ae
+4f269d8aefd647ee270842d53ec98aebd23a4afe
+4e719332c00cd72d141765108d18f07311caed9d
+796f60fb8cf4a5c9558df26aec62fd4b45506dcf
+8d12b862405ca6e816ab59281afaba7c3b8c6381
+50e2170b4f789e248e2fc76ae5450966431fa9d1
+bd9e5ba75c9148be7adaea38034c9bb4957a3b79
+d1aefb69ec959bf839c35cd120b5c744eb1c7b1e
+2c71f6db3da8086b9ad4413d325780f346052a56
+ca4e193754e04ebe9326d6de2c440a46044f48e9
+c17e87eed256ff71cb6cdd3573276a70692f3530
+bfb0310f7f3ee5221d7f64658b48e1e1e1720f5a
+b235c3d96069927fd49ec099254462bb9e37a683
+7eb532b5138be534d6be4bedce93982ffb9b7358
+93aec420aa7e874668fa728ce8a7b41a177c15a4
+93a31efd4809ee94b1ace992a3538e3af0399361
+93b6f1ca54f643404f675489591f60f1f4ed7694
+e62e4b5e8be88b1be082b1f79806d283181b0335
+dd0342632a055f91560d0242717069091fb7ab9d
+dcc28f5758ae29a50ed2d8c93bb8135004a1b63e
+135637767239366c6781d9a31aea0040f2155f89
+506dce309921b2fa47f24a9b007f732157de00fc
+63f641c833ef83759163560a3ff21d7c6ff88380
+2c3c9c5aeaa72cb9819dc6da2ba90b39047acc9d
+2f898f0b36ab12e7334570080b68e6ad51cbefb9
+2dc120a7ae1032d8c70bba2b6056c5ab3f2c7fc6
+9a9488e52389eb437040bcadd3677f9eb80cae61
+88733aee0983c6c7609f17afe1378531b84660a2
+c93c8b7fe9f0bfd76a4140e65b86e5fc6a3764fe
+87e90db19588e3b07d551cceb50cafe1a3a89c2a
+434d5e2790bc70f97a847cadaf1fdc2a1fbc175d
+2b0d7c905f2690fbc4a33d55b686382f8a8ec6ed
+22ebd378d449033a2e51fdd059f8a9e0e42c55d3
+00089ff950ab5204d9e5f0d91ada337029fe3706
+23efe29e8597d103d10d1a974b6399232f14272c
+006349aef773b05109fa75a2b5ce07f28883c2fa
+a6f5cf3896734e455db5f2659c00c655d63db6af
+89cc95b6286b5d75956d6fb182c6f34e34076e0e
+59f38783e5c15ad3a2c8973de7f03fe98544898f
+ac1a7de4089600b407335699d6a9623808cff54e
+cf91ecc307d1edf48d3c06c3db01f952d1b6c2d1
+272888f1ff5b133518559ec10a2b7057aabeaf63
+236f4a8ae458e682f019dce0c10f87cde6f1eee8
+3ab4922c732f09b7fa242a9b21385d2bc3dd06ae
+71e5bf7db68bbd172bdb52beee1a802804b62621
+7afed744cb29e68c153a49967ec9bb40059fba1f
+b6e9226e972d6b3d42860efcb3b5eac64ff122cf
+16952e1ca004b33cbc25857c511909fe3293ca37
+874bbb81db303ed84aa818a26adb1785184eb9c5
+91df1fa1f456a2f85efa3ff78e8a521a5e2348d7
+827044521071f0c8e26477a1f45b17456eca1ef4
+668fa7077df73b1997345bd545131e831a4e87d9
+ac045dbf9e0eb6de3061efc2021cd89eceb19bb0
+8e297385ed9f65354b7ba1691be87f1f134e881d
+5303a10cfa37eb1d98cdaf391ab69cad10f4fc7e
+1452653134c1f4d93ca076123452ef04b25fcb56
+ad97aadba73952b610022f4c57eb6c8aa293dfd1
+ae14bcbd9ec2c58c59a312458a91a43045061b42
+21360976daa61cd99cadde5a399a64db90f9e3d3
+2030742d6cb6c1367894cc33d32a1131953e445d
+393423c857a161908a8ed6b93716c5fc1ac1079a
+d38ec4a7f86831b4fc827dd7c594779efc24f0f7
+d4149bb6f1cd576c348c449ff55b10679c810499
+044376eaf3db326f077178ecff0b62d2404e7c12
+116ca329507f249bb2f2d392a1c33e321228bd5a
+a156421412df34a5f0241b029ec2930e758729c0
+a1d968842cef01b80cd2b7446a5bdfe617f873f9
+acca049a0221e6ccf8b3c3a9cd213b0b5018f664
+a6a2b2d6a157f6c2fd0ca222ad475fdb2f9328f1
+a4a05eb30ccdcfad604e92cd6bb4fa945c14c81e
+a4841947e26645520694e7be64d44493a7075a5b
+c533c91993741c38fab0d088c14c823b2706167b
+a4978bf8ffb82c45eee68bf393a7968e882f9618
+a4a377a1f4fb73f2a019ab3ca3cb53175ecc238e
diff --git a/src/dht.c b/src/dht.c
index 83fcb68..4e57e5e 100644
--- a/src/dht.c
+++ b/src/dht.c
@@ -794,6 +794,8 @@ struct dht * dht_init (const struct bencoding * c) {
#pragma GCC diagnostic pop
goto e;
}
+#define TOOMUCH 32727
+ unsigned pinged = 0;
if (c) {
const struct bencoding * id = bpath(c, "id");
if (id && id->type & string && id->valuelen == 20)
@@ -811,7 +813,7 @@ struct dht * dht_init (const struct bencoding * c) {
.sin6_family = AF_INET6,
.sin6_port = htons(atoi(++port))
};
- if (inet_pton(AF_INET6, remote, addr.sin6_addr.s6_addr) == 1)
+ if (inet_pton(AF_INET6, remote, addr.sin6_addr.s6_addr) == 1 && pinged++ < TOOMUCH/2)
ping_node(d, &addr);
}
}
@@ -2172,6 +2174,30 @@ d:
} // do not log, it may have been a bencoded reply
}
+/**
+ * delete all but first bucket in ll, used in refresh on both lls (v4 and v6) in case of sybil
+ *
+ * @param b [in] first bucket in ll
+ */
+
+void delete_buckets (struct bucket * b) {
+ struct node * n = b->nodes;
+ while (n) {
+ struct node * old = n;
+ n = n->next;
+ node_free(old);
+ }
+ b->nodes = NULL;
+ memset(b->id, '\0', 20);
+ struct bucket * del = b->next;
+ b->next = NULL;
+ while (del) {
+ struct bucket * old = del;
+ del = del->next;
+ bucket_free(old);
+ }
+}
+
#define PERIODIC 10
/**
@@ -2214,33 +2240,10 @@ int refresh (struct dht * d, int fam) {
if (buckets > 64) { // sybil attack - node is broken - clear whole routing table, keeping one bucket
dht_print(d->log, d);
L(disagreement, d, "@@@@@@ SYBIL ATTACK - CLEARING ROUTING TABLE @@@@@@");
- int keep_first = rand() % 2; // should we even keep one bucket? the sybil node has a 1/2
- if (keep_first) { // chance of having stared in the bucket farthest away, so it's stored there ...
- memset(d->buckets->id, '\0', 20);
- b = d->buckets->next;
- d->buckets->next = NULL;
- while (b) {
- bucket_free(b);
- b = b->next;
- }
- } else {
- b = d->buckets;
- while (b->next) {
- struct bucket * old = b;
- b = b->next;
- bucket_free(old);
- }
- d->buckets = b;
- memset(d->buckets->id, '\0', 20);
- }
+ delete_buckets(d->buckets);
+ delete_buckets(d->buckets6);
if (getrandom(d->id, 20, GRND_NONBLOCK) == -1) // changing our ID. note that this might make
- L(std_fail, d, "getrandom: %s", strerror(errno)); // existing nodes hate us
- switch (fam) {
- case AF_INET:
- return node_count(d->buckets6->nodes);
- case AF_INET6:
- return node_count(d->buckets6->nodes);
- }
+ L(std_fail, d, "getrandom: %s", strerror(errno)); // existing nodes hate us, though we'll probably have no existing nodes
return 0;
}
return nrgood;
@@ -2337,25 +2340,36 @@ void periodic (struct dht * d) {
if (t->type & (peers | announce)) {
struct node * n = t->nodes;
int sent = 0;
- while (n) {
- sent++;
+ int c = node_count(n);
+ if (c)
+ c = rand() % c;
+ while (n && c--)
+ n = n->next;
+ if (n && sent < 3) { // we pick some consecutive at random and ping them.
+ sent++; // increase to more than this if desired ... idk this is shit
if (!n->unanswered)
n->last_sent = seconds();
n->unanswered++;
get_peers(d, &n->addr, t->hash);
n = n->next;
+ if (!n && !t->nodes->unanswered) // if unanswered, we already sent it
+ n = t->nodes;
}
- if (sent < 1) {
+ if (sent < 2) {
#define RTGP(buckets) {struct bucket * b = d->buckets; \
find(t->hash, &b, NULL); \
struct node * n = b->nodes; \
- while (sent < 1 && n) { \
+ int c = node_count(n); \
+ if (c) \
+ c = rand() % c; \
+ while (n && c--) \
+ n = n->next; \
+ if (n) { \
sent++; \
if (!n->unanswered) \
n->last_sent = seconds(); \
n->unanswered++; \
get_peers(d, &n->addr, t->hash); \
- n = n->next; \
}}
RTGP(buckets);
RTGP(buckets6);
@@ -2364,13 +2378,17 @@ void periodic (struct dht * d) {
struct bucket * b = d->buckets;
while (sent < 1 && b) {
n = b->nodes;
- while (sent < 1 && n) {
+ int c = node_count(n);
+ if (c)
+ c = rand() % c;
+ while (n && c--)
+ n = n->next;
+ if (sent < 1 && n) {
sent++;
if (!n->unanswered)
n->last_sent = seconds();
n->unanswered++;
get_peers(d, &n->addr, t->hash);
- n = n->next;
}
b = b->next;
}
@@ -2435,7 +2453,6 @@ void periodic (struct dht * d) {
t = t->next;
}
L(debug, d, "txqp=%u rxrp=%u rxqp=%u txrp=%u", d->txqp, d->rxrp, d->rxqp, d->txrp);
-#define TOOMUCH 32727
if (d->txqp > TOOMUCH || d->rxrp > TOOMUCH || d->rxqp > TOOMUCH || d->txrp > TOOMUCH) {
dht_print(stdout, d);
raise(SIGABRT);
diff --git a/src/main.c b/src/main.c
index 4e9c188..de21f7a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -45,7 +45,7 @@ void found_torrent (struct dht * d __attribute__((unused)), const unsigned char
L(debug, d, "%s%s", buf, t ? " stored" : " new");
if (t) {
if (!t->type)
- t->ttl = seconds()+128;
+ t->ttl = seconds()+256;
t->type |= info | peers;
} else {
if (last_added + 1 > seconds()) {
@@ -56,7 +56,7 @@ void found_torrent (struct dht * d __attribute__((unused)), const unsigned char
t = torrent_init();
memcpy(t->hash, h, 20);
t->type |= info | peers;
- t->ttl = seconds()+128;
+ t->ttl = seconds()+256;
add_torrent(d, t);
}
}
@@ -135,6 +135,7 @@ int main (int argc, char ** argv) {
struct torrent * torrent = torrent_init();
memcpy(torrent->hash, "\xdd\x82\x55\xec\xdc\x7c\xa5\x5f\xb0\xbb\xf8\x13\x23\xd8\x70\x62\xdb\x1f\x6d\x1c", 20);
torrent->type = /* (useless, since we have no listening system yet) announce | */ peers | info;
+ torrent->ttl = seconds()+128; /**< idk, enough to bootstrap i guess */
add_torrent(dht, torrent);
periodic(dht);
// alarm(PERIODIC);
@@ -143,6 +144,7 @@ w:
if (sigusr1) {
sigusr1 = 0;
dht_print(stdout, dht);
+ fflush(stdout);
goto w;
}
if (periodično) {
@@ -160,6 +162,7 @@ w:
if (sigusr1) {
sigusr1 = 0;
dht_print(stdout, dht);
+ fflush(stdout);
goto w;
}
if (periodično) {
diff --git a/www/index.php b/www/index.php
index e37fe7f..96f0f8f 100644
--- a/www/index.php
+++ b/www/index.php
@@ -5,8 +5,9 @@
?>
<meta charset=UTF-8 />
<meta name=viewport content='width=device-width, initial-scale=1.0'>
-<h1>število datotek: <?= shell_exec("find .. -name '*.torrent' | wc -l") ?></h1>
+<h2>število datotek: <?= shell_exec("find .. -name '*.torrent' | wc -l") ?></h2>
<h2><a href=list.php>seznam</a></h2>
+<h2><a href=stanje.php>stanje</a></h2>
<h2>iskalnik</h2>
<form>
<input name=i value="<?= !empty($_REQUEST["i"]) ? htmlspecialchars($_REQUEST["i"]) : "" ?>" />
diff --git a/www/insert.php b/www/insert.php
new file mode 100755
index 0000000..4fa8e59
--- /dev/null
+++ b/www/insert.php
@@ -0,0 +1,132 @@
+#!/usr/bin/php
+<?php
+require_once "vendor/autoload.php";
+use Rhilip\Bencode\TorrentFile;
+use Rhilip\Bencode\ParseException;
+if ($argc != 5) {
+ echo "uporaba: $argv[0] 'mysql:host=tranzistor:3306;dbname=travnik;charset=utf8' travnik " . '$TRAVNIK_DB_PASSWORD datoteka.torrent' . PHP_EOL;
+ exit(1);
+}
+$t = TorrentFile::load($argv[4]);
+$c = new PDO($argv[1], $argv[2], $argv[3]);
+$c->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+try {
+ $s = $c->prepare("SELECT COUNT(1) FROM torrenti WHERE zgoščena_vrednost=:z");
+ $s->bindParam(":z", $zgoščena_vrednost);
+ $zgoščena_vrednost = $t->getInfoHashV1(true);
+ $s->execute();
+} catch (PDOException $e) {}
+if ($s->fetchColumn()) {
+ echo "torrent je že shranjen v podatkovni zbirki" . PHP_EOL;
+ exit(0);
+}
+try {
+ $c->exec("CREATE TABLE IF NOT EXISTS torrenti (
+ zgoščena_vrednost BINARY(20) NOT NULL PRIMARY KEY,
+ " /* . "id INT UNSIGNED AUTO_INCREMENT UNIQUE NOT NULL COMMENT 'samo za lažje ročno navigiranje po podatkovni zbirki, pravi ključ je zgoščena vrednost', " */ . "
+ pridobljeno TIMESTAMP NOT NULL,
+ velikost_metainfo INT UNSIGNED NOT NULL COMMENT 'sem se štejejo tudi informacije, ki jih doda travnik, recimo created by, datum izdelave, vir in odjemalec. to ni le velikost info slovarja, vendar je striktno večja, saj je velikost celotne torrent datoteke.',
+ velikost_koščka INT UNSIGNED NOT NULL,
+ izvor VARBINARY(4096),
+ ime VARBINARY(4096),
+ ip BINARY(16) NOT NULL,
+ vrata SMALLINT UNSIGNED NOT NULL,
+ odjemalec VARCHAR(4096)
+ )");
+ $c->exec('CREATE TABLE IF NOT EXISTS datoteke (
+ id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
+ torrent BINARY(20) NOT NULL,
+ FOREIGN KEY (torrent) REFERENCES torrenti (zgoščena_vrednost),
+ pot VARBINARY(4096) NOT NULL,
+ velikost BIGINT NOT NULL
+ )');
+ $c->beginTransaction(); // glej php.net/manual/en/pdo.begintransaction.php
+ $s = $c->prepare("INSERT INTO torrenti (
+ zgoščena_vrednost,
+ pridobljeno,
+ velikost_metainfo,
+ velikost_koščka,
+ izvor,
+ ime,
+ ip,
+ vrata,
+ odjemalec
+ ) VALUES (
+ :z,
+ :pridobljeno,
+ :velikost_metainfo,
+ :v,
+ :izvor,
+ :ime,
+ INET6_ATON(:ip),
+ :vrata,
+ :odjemalec
+ )");
+ $s->bindParam(":z", $zgoščena_vrednost);
+ $s->bindParam(":pridobljeno", $pridobljeno);
+ $s->bindParam(":velikost_metainfo", $velikost_metainfo);
+ $s->bindParam(":v", $velikost_koščka);
+ $s->bindParam(":izvor", $izvor);
+ $s->bindParam(":ime", $ime);
+ $s->bindParam(":ip", $ip);
+ $s->bindParam(":vrata", $vrata);
+ $s->bindParam(":odjemalec", $odjemalec);
+ $pridobljeno = date("Y-m-d H:i:s", $t->getCreationDate());
+ $velikost_metainfo = filesize($argv[4]);
+ $velikost_koščka = $t->getPieceLength();
+ $izvor = $t->getSource();
+ if (strlen($izvor) > 4096)
+ echo "$argv[4] izvor $izvor daljši od 4096";
+ $ime = $t->getName();
+ if (strlen($ime) > 4096)
+ echo "$argv[4] ime $ime daljše od 4096";
+ if (!isset($t->getRootData()["source"])) {
+ echo "$argv[4] nima source ključa" . PHP_EOL;
+ exit(4);
+ }
+ $ip = explode("/", $t->getRootData()["source"]["ip"])[0];
+ $vrata = intval(explode("/", $t->getRootData()["source"]["ip"])[1]);
+ if (isset($t->getRootData()["source"]["v"]))
+ $odjemalec = $t->getRootData()["source"]["v"];
+ $s->execute();
+ function p ($k, $v, $p) {
+ global $c, $zgoščena_vrednost;
+ // if (preg_match("/padding.file/i", $k))
+ // return;
+ if (is_array($v)) {
+ foreach ($v as $ke => $va)
+ p($ke, $va, $p . $k . "/");
+ } else {
+ $s = $c->prepare("INSERT INTO datoteke (
+ torrent,
+ pot,
+ velikost
+ ) VALUES (
+ :torrent,
+ :pot,
+ :velikost
+ )");
+ $s->bindParam(":torrent", $zgoščena_vrednost);
+ $s->bindParam(":pot", $pot);
+ $s->bindParam(":velikost", $velikost);
+ $pot = $p . $k;
+ if (strlen($pot) > 4096)
+ echo "$argv[4] pot $pot daljša od 4096";
+ $velikost = $v;
+ $s->execute();
+ }
+ }
+ try {
+ foreach ($t->getFileTree() as $k => $v)
+ p($k, $v, "");
+ } catch (ParseException $e) {
+ echo "neveljaven metainfo $argv[4]: " . $e->getMessage() . PHP_EOL;
+ exit(5);
+ }
+ $c->commit();
+} catch (PDOException $e) {
+ echo "sql napaka pri $argv[4]: " . $e->getMessage() . PHP_EOL;
+ $c->rollback();
+ exit(2);
+}
+$c = null;
diff --git a/www/stanje.php b/www/stanje.php
new file mode 100644
index 0000000..da429e1
--- /dev/null
+++ b/www/stanje.php
@@ -0,0 +1,6 @@
+<pre>
+<?php
+ shell_exec("echo > ../tmp/stanje.txt && sleep 1 && pkill -sigusr1 travnik");
+ sleep(1);
+ echo file_get_contents("../tmp/stanje.txt");
+?>