Browse Source

move daemon utils into config instance

master
James Fenn 5 months ago
parent
commit
8f9afe310b
5 changed files with 129 additions and 95 deletions
  1. +18
    -4
      source/commands.d
  2. +6
    -0
      source/config/global.d
  3. +71
    -91
      source/daemon.d
  4. +17
    -0
      source/service/hello.d
  5. +17
    -0
      source/service/index.d

+ 18
- 4
source/commands.d View File

@@ -6,7 +6,6 @@ import std.stdio;
import std.format;
import std.conv;
import model.port;
import daemon : daemon, daemonCall;

import jsonizer.fromjson;

@@ -33,7 +32,7 @@ void admin(GlobalConfig conf, string[] args) {
return;
}

string ret = daemonCall(args[1], args[2]);
string ret = conf.daemon.call(args[1], args[2]);
writeln(ret);
}

@@ -66,7 +65,7 @@ void reservePort(GlobalConfig conf, string[] args) {
return;
}

string portJson = daemonCall("reservePort", "");
string portJson = conf.daemon.call("reservePort", "");
Port port = fromJSONString!Port(portJson);
writeln("Reserved port ", port.id, " for ", port.owner);
}
@@ -82,7 +81,22 @@ void releasePort(GlobalConfig conf, string[] args) {

// iterate through passed ports; release each
foreach (string portId; args[1..$]) {
string message = daemonCall("releasePort", portId);
string message = conf.daemon.call("releasePort", portId);
writeln(message);
}
}

/**
* Daemon command; executed as root service.
*/
void daemon(GlobalConfig conf, string[] args) {
writeln("Acquiring config lock...");
conf.lock.acquire();
try {
writeln("Starting daemon loop...");
conf.daemon.start();
} catch (Exception e) {
// ...
}
conf.lock.release();
}

+ 6
- 0
source/config/global.d View File

@@ -5,6 +5,7 @@ import model.port : Port;
import model.user : User;
import config.base : Config;
import config.lock : ConfigLock;
import daemon : Daemon;

const string CONFIG_DIR = "/etc/horrific";

@@ -21,12 +22,17 @@ class GlobalConfig {
// all deployment instances
Config!Instance instances;

// daemon functions
Daemon daemon;

this() {
lock = new ConfigLock(CONFIG_DIR ~ "/daemon.lock");

users = new Config!User(CONFIG_DIR ~ "/users.json", lock);
ports = new Config!Port(CONFIG_DIR ~ "/ports.json", lock);
instances = new Config!Instance(CONFIG_DIR ~ "/instances.json", lock);

daemon = new Daemon(this);
}

}

+ 71
- 91
source/daemon.d View File

@@ -13,104 +13,84 @@ import jsonizer.common;
import std.conv;
import core.sys.posix.unistd : getlogin;

import service.ports : reservePort, releasePort;
import service.users : createUser, encryptUserMessage, decryptUserMessage;

alias string function(GlobalConfig, User, string) DaemonCommand;
DaemonCommand[string] daemon_cmds;
shared static this() {
daemon_cmds["hello"] = &hello;
daemon_cmds["reservePort"] = &reservePort;
daemon_cmds["releasePort"] = &releasePort;
daemon_cmds["createUser"] = &createUser;
}
import service.index : DaemonCommand, daemon_cmds;
import service.users : encryptUserMessage, decryptUserMessage;

string hello(GlobalConfig conf, User user, string param) {
writeln("Received hello from: ", user.id, " with argument ", param);
return "Hello world! (" ~ param ~ ")";
}
class Daemon {

unittest {
User user;
user.id = "test";
assert(hello(null, user, null) == "Hello world!");
}
GlobalConfig conf;
Connection conn;

/**
* Daemon command; executed as root service.
*/
void daemon(GlobalConfig conf, string[] args) {
writeln("Acquiring config lock...");
conf.lock.acquire();
try {
writeln("Starting daemon loop...");
daemonLoop(conf);
} catch (Exception e) {
// ...
this(GlobalConfig conf) {
this.conf = conf;
this.conn = connectToBus();
}
conf.lock.release();
}

void daemonLoop(GlobalConfig conf) {
Connection conn = connectToBus();

// route each message to daemon_cmds array
MessageRouter router = new MessageRouter();
foreach (name, cmd; daemon_cmds) {
MessagePattern pattern = MessagePattern("/root", "dev.horrific.daemon", name);
router.setHandler!(string, string, string)(pattern, (string userJson, string encryptedData) {
Status result;
result.code = 0;

try {
// get user from database
User providedUser = fromJSONString!User(userJson);
User* dbUserRef = providedUser.id in conf.users.contents;
if (dbUserRef is null)
throw new Exception("Authentication error: user " ~ providedUser.id ~ " does not exist.");

// decrypt passed data
User dbUser = *dbUserRef;
string data = decryptUserMessage(dbUser, encryptedData);

// attempt function call
result.message = (*cmd)(conf, dbUser, data);
} catch (Exception e) {
result.code = 1;
result.message = e.msg;
}

// encode result/status
return result.toJSONString();
});
/**
* Daemon service; executed as root.
*/
void start() {
// route each message to daemon_cmds array
MessageRouter router = new MessageRouter();
foreach (name, cmd; daemon_cmds) {
MessagePattern pattern = MessagePattern("/root", "dev.horrific.daemon", name);
router.setHandler!(string, string, string)(pattern, (string userJson, string encryptedData) {
Status result;
result.code = 0;

try {
// get user from database
User providedUser = fromJSONString!User(userJson);
User* dbUserRef = providedUser.id in this.conf.users.contents;
if (dbUserRef is null)
throw new Exception("Authentication error: user " ~ providedUser.id ~ " does not exist.");

// decrypt passed data
// - This is less for keeping the content "secure" and more to prevent user spoofing;
// users cannot access each others' private *or* public keys; even with the public
// key, it would take some effort to create a coherent message that can be decrypted
// by it.
User dbUser = *dbUserRef;
string data = decryptUserMessage(dbUser, encryptedData);

// attempt function call
result.message = (*cmd)(this.conf, dbUser, data);
} catch (Exception e) {
result.code = 1;
result.message = e.msg;
}

// encode result/status
return result.toJSONString();
});
}

registerRouter(this.conn, router);

writeln("Getting name...");
bool gotem = requestName(this.conn, "dev.horrific.daemon");
writeln("Got name: ", gotem);
simpleMainLoop(this.conn);
}

registerRouter(conn, router);

writeln("Getting name...");
bool gotem = requestName(conn, "dev.horrific.daemon");
writeln("Got name: ", gotem);
simpleMainLoop(conn);
}

static PathIface daemon_proxy;
PathIface proxy = null;

string daemonCall(string name, string data) {
if (daemon_proxy is null) { // initialize system bus connection (only used here)
Connection conn = connectToBus();
daemon_proxy = new PathIface(conn, "dev.horrific.daemon", "/root", "dev.horrific.daemon");
}
string call(string name, string data) {
if (this.proxy is null)
this.proxy = new PathIface(conn, "dev.horrific.daemon", "/root", "dev.horrific.daemon");
// call dbus method
User user;
user.name = to!string(getlogin());

string encryptedData = encryptUserMessage(user, data);
string resultJson = daemon_proxy.call!string(name, user.toJSONString(), encryptedData);

// parse JSON status/result
Status result = fromJSONString!Status(resultJson);
if (result.code != 0)
throw new Exception(result.message);
else return result.message;
// call dbus method
User user;
user.name = to!string(getlogin());

string encryptedData = encryptUserMessage(user, data);
string resultJson = this.proxy.call!string(name, user.toJSONString(), encryptedData);

// parse JSON status/result
Status result = fromJSONString!Status(resultJson);
if (result.code != 0)
throw new Exception(result.message);
else return result.message;
}

}

+ 17
- 0
source/service/hello.d View File

@@ -0,0 +1,17 @@
module service.hello;

import std.stdio;

import config.global : GlobalConfig;
import model.user : User;

string hello(GlobalConfig conf, User user, string param) {
writeln("Received hello from ", user.name, " with argument ", param);
return "Hello world! (" ~ param ~ ")";
}

unittest {
User user;
user.name = "test";
assert(hello(null, user, "spaghetti") == "Hello world! (spaghetti)");
}

+ 17
- 0
source/service/index.d View File

@@ -0,0 +1,17 @@
module service.index;

import config.global : GlobalConfig;
import model.user : User;

import service.hello : hello;
import service.ports : reservePort, releasePort;
import service.users : createUser, encryptUserMessage, decryptUserMessage;

alias string function(GlobalConfig, User, string) DaemonCommand;
DaemonCommand[string] daemon_cmds;
shared static this() {
daemon_cmds["hello"] = &hello;
daemon_cmds["reservePort"] = &reservePort;
daemon_cmds["releasePort"] = &releasePort;
daemon_cmds["createUser"] = &createUser;
}

Loading…
Cancel
Save