diff --git a/.gitignore b/.gitignore index fbb5166..e51278d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -/vendor/ /.idea/ /var/* !/var/.gitignore diff --git a/README.md b/README.md index d65ec26..74a28b4 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,9 @@ This is a simple fileserver written in PHP (7.4+) using [ZeroMQ](https://zeromq.org/). There are three executable files: - -- `$ ./server` runs the fileserver -- `$ ./command ...` sends a command to the server -- `$ ./query ...` sends a query to the server and returns the response +- `$ bin/server` runs the fileserver +- `$ bin/command ...` sends a command to the server +- `$ bin/query ...` sends a query to the server and returns the response ## Commands @@ -15,9 +14,8 @@ There are three executable files: - `DELETE_ALL ` deletes an entire namespace To save a file to the fileserver you can do: -`$ ./command SAVE my-project-namespace 1.html "..."` -`$ ./command SAVE my-project-namespace 2.xml "$(cat some/xml/file)"` - +`$ bin/command SAVE my-project-namespace 1.html "..."` +`$ bin/command SAVE my-project-namespace 2.xml "$(cat some/xml/file)"` ## Queries @@ -25,5 +23,8 @@ To save a file to the fileserver you can do: - `LOAD ` returns the file contents if it exists, else `-1` To load a file from the fileserver you can do: -`$ ./query LOAD my-project-namespace 1.html` +`$ bin/query LOAD my-project-namespace 1.html` +## Configuration + +See `config/config.ini`. It contains an example configuration. diff --git a/bin/command b/bin/command new file mode 100755 index 0000000..cb95179 --- /dev/null +++ b/bin/command @@ -0,0 +1,13 @@ +#!/usr/bin/env php +connect($ini['dsn']); +$socket->sendmulti($argv); diff --git a/bin/query b/bin/query new file mode 100755 index 0000000..fd2943e --- /dev/null +++ b/bin/query @@ -0,0 +1,15 @@ +#!/usr/bin/env php +connect($ini['dsn']); +$socket->sendmulti($argv); +print(implode(PHP_EOL, $socket->recvMulti())); +print(PHP_EOL); diff --git a/bin/server b/bin/server new file mode 100755 index 0000000..ee8a8fe --- /dev/null +++ b/bin/server @@ -0,0 +1,10 @@ +#!/usr/bin/env php +run(); diff --git a/command b/command deleted file mode 100755 index e3f9876..0000000 --- a/command +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env php -connect('ipc:///tmp/storage_server_command.ipc'); -$socket->sendmulti($argv); diff --git a/config/config.ini b/config/config.ini new file mode 100644 index 0000000..a804f85 --- /dev/null +++ b/config/config.ini @@ -0,0 +1,21 @@ +[server] +; %ROOT_DIR% is replaced by the actual directory of the package +storage_dir = %ROOT_DIR%/var/storage + +; Allow network-wide connections for queries +query_dsn[] = ipc:///tmp/storage_server_query.ipc +query_dsn[] = tcp://0.0.0.0:5678 + +; Allow local connections for commands +command_dsn[] = ipc:///tmp/storage_server_command.ipc +command_dsn[] = tcp://127.0.0.1:5679 + + +[command] +; Connect through IPC socket +dsn = ipc:///tmp/storage_server_command.ipc + + +[query] +; Connect through TCP socket +dsn = tcp://localhost:5678 diff --git a/query b/query deleted file mode 100755 index 6f25592..0000000 --- a/query +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env php -connect('ipc:///tmp/storage_server_query.ipc'); -$socket->sendmulti($argv); -print(implode(PHP_EOL, $socket->recvMulti())); -print(PHP_EOL); diff --git a/server b/server deleted file mode 100755 index a59d934..0000000 --- a/server +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env php -run(); - diff --git a/src/FileServer.php b/src/FileServer.php index d1b53cf..91c78b1 100644 --- a/src/FileServer.php +++ b/src/FileServer.php @@ -1,6 +1,5 @@ initializeCommandSocket($context, $commandDSNs); + $this->initializeQuerySocket($context, $queryDSNs); $this->initializeRootDirectory($rootDirectory); - $this->initializeCommandSocket($context, $commandDsns); - $this->initializeQuerySocket($context, $queryDsns); $this->initializePoll(); } @@ -22,28 +21,31 @@ class FileServer if (!is_dir($this->rootDirectory)) { mkdir($this->rootDirectory, 0777, true); } + if (!is_dir($this->rootDirectory)) { + throw new RuntimeException("Unable to create directory '{$this->rootDirectory}'"); + } } - private function initializeCommandSocket(ZMQContext $context, array $bindings): void + private function initializeCommandSocket(ZMQContext $context, array $commandDSNs): void { - if (count($bindings) === 0) { + if (count($commandDSNs) === 0) { throw new InvalidArgumentException("At least one binding required"); } $this->commandSocket = new ZMQSocket($context, ZMQ::SOCKET_PULL); $this->commandSocket->setSockOpt(ZMQ::SOCKOPT_HWM, 5); - foreach ($bindings as $dsn) { + foreach ($commandDSNs as $dsn) { $this->commandSocket->bind($dsn); } } - private function initializeQuerySocket(ZMQContext $context, array $bindings): void + private function initializeQuerySocket(ZMQContext $context, array $queryDSNs): void { - if (count($bindings) === 0) { + if (count($queryDSNs) === 0) { throw new InvalidArgumentException("At least one binding required"); } $this->querySocket = new ZMQSocket($context, ZMQ::SOCKET_REP); $this->querySocket->setSockOpt(ZMQ::SOCKOPT_HWM, 5); - foreach ($bindings as $dsn) { + foreach ($queryDSNs as $dsn) { $this->querySocket->bind($dsn); } } @@ -75,7 +77,7 @@ class FileServer case 'LOAD': if (count($arguments) !== 2) { $this->querySocket->send(-1); - print("Malformed LOAD query\n"); + $this->onNotEnoughArguments($query, 2, count($arguments)); return; } $this->querySocket->send(@file_get_contents("{$this->rootDirectory}/{$arguments[0]}/{$arguments[1]}") ?: -1); @@ -83,7 +85,7 @@ class FileServer case 'CONTAINS': if (count($arguments) !== 2) { $this->querySocket->send(-1); - print("Malformed CONTAINS query\n"); + $this->onNotEnoughArguments($query, 2, count($arguments)); return; } $this->querySocket->send(file_exists("{$this->rootDirectory}/{$arguments[0]}/{$arguments[1]}") ? 'Y' : 'N'); @@ -102,21 +104,22 @@ class FileServer switch ($command) { case 'SAVE': if (count($arguments) !== 3) { - print("Malformed SAVE command\n"); + $this->onNotEnoughArguments($command, 3, count($arguments)); return; } $this->saveFile($arguments[0], $arguments[1], $arguments[2]); break; case 'DELETE': if (count($arguments) !== 2) { - print("Malformed DELETE command\n"); + $this->onNotEnoughArguments($command, 2, count($arguments)); return; } $this->deleteFile($arguments[0], $arguments[1]); break; case 'DELETE_ALL': if (count($arguments) !== 1) { - print("Malformed DELETE_ALL command\n"); + $this->onNotEnoughArguments($command, 1, count($arguments)); + return; } $this->deleteAll($arguments[0]); break; @@ -126,10 +129,15 @@ class FileServer } } + private function onNotEnoughArguments(string $input, int $expected, int $actual): void + { + printf(date('[H:i:s]') . " Error: unexpected amount of arguments for input '%s' (%d/%d)\n", $input, $actual, $expected); + } + private function onUnknownInput(string $input, array $arguments): void { $arguments = array_map(fn($v) => substr($v, 0, 12), $arguments); - printf("Unknown input '%s' with args (%s)\n", $input, implode(', ', $arguments)); + printf(date('[H:i:s]') . " Error: unknown input '%s' with arguments [%s]\n", $input, implode(', ', $arguments)); } private function saveFile(string &$namespace, string &$name, string &$content): void