Skip to content

Commit

Permalink
fix: unlocking works, add logging
Browse files Browse the repository at this point in the history
  • Loading branch information
hugojosefson committed Nov 15, 2024
1 parent 962229e commit c390df5
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 23 deletions.
20 changes: 8 additions & 12 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,21 @@ async function main() {
);
const passphrase = await readFirstLine(Deno.stdin.readable);

const actor = createActor(machine, {
input: {
context: {
passphrase,
sshCommand: new Deno.Command("ssh", {
args: ["-tt", destination.user + "@" + destination.host],
stdin: "piped",
stdout: "piped",
stderr: "piped",
}),
},
},
const sshCommand = new Deno.Command("ssh", {
args: ["-tt", destination.user + "@" + destination.host, "bash"],
stdin: "piped",
stdout: "piped",
stderr: "piped",
});
const actor = createActor(machine);
Deno.addSignalListener("SIGINT", () => {
console.log("SIGINT received. Exiting...");
actor.send({ type: "exit" });
});

actor.start();
actor.send({ type: "setContext", key: "sshCommand", value: sshCommand });
actor.send({ type: "setContext", key: "passphrase", value: passphrase });
}

if (import.meta.main) {
Expand Down
97 changes: 86 additions & 11 deletions src/machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@ export type Context = {
stderr: ReadableStream<string>;
};

export type SetContextEvent<K extends keyof Context = keyof Context> = {
type: "setContext";
key: K;
value: Context[K];
};

export const machine = setup({
types: {
context: {} as Context,
events: {} as
| {
type:
| "setContext"
| "contextComplete"
| "cleanedUp"
| "exit"
| "connecting"
Expand All @@ -35,12 +42,13 @@ export const machine = setup({
| {
type: "error";
data: string;
},
}
| SetContextEvent,
},
}).createMachine({
context: {} as Context,
id: "sshMachine",
initial: "connecting",
initial: "setupContext",
on: {
connectionFailure: {
target: "#sshMachine.sleeping",
Expand All @@ -50,10 +58,36 @@ export const machine = setup({
target: "#sshMachine.exit",
reenter: true,
},
setContext: {
target: "#sshMachine.setupContext",
reenter: true,
},
},
states: {
setupContext: {
on: {
contextComplete: {
target: "connecting",
},
},
entry: ({ context, event, self }) => {
console.log("setupContext");
if (event.type === "setContext") {
console.log(`setting context.${event.key}`);
context[event.key] = event.value;
}
if (context.sshCommand && context.passphrase) {
console.log("context complete");
self.send({ type: "contextComplete" });
} else {
console.log("context incomplete");
}
},
description: "Set up the context.",
},
cleanup: {
entry: async ({ context, self }) => {
console.log("Cleaning up...");
const cleanedUp = Promise.allSettled([
context.stdin.close(),
context.stdout.cancel(),
Expand All @@ -62,6 +96,7 @@ export const machine = setup({
]);
await kill(context.sshProcess, [["SIGINT", 1000], ["SIGTERM", 1000]]);
await cleanedUp;
console.log("Cleaned up.");
self.send({ type: "cleanedUp" });
},
on: {
Expand All @@ -70,8 +105,10 @@ export const machine = setup({
},
connecting: {
entry: ({ context, self }) => {
console.log("Connecting...");
context.sshProcess = context.sshCommand.spawn();
Object.assign(context, wrapProcess(context.sshProcess));
console.log("Connected.");
self.send({ type: "connectionSuccess" });
},
on: {
Expand All @@ -84,17 +121,22 @@ export const machine = setup({
5000: { target: "cleanup" },
},
entry: async ({ context, self }) => {
console.log("Reading output...");
for await (const burst of context.stdout) {
console.log(burst);
console.log({ burst });
if (isZfsUnlockPrompt(burst)) {
console.log("Got zfs unlock prompt.");
self.send({ type: "zfsUnlockPromptDetected" });
break;
}
if (isCommandPrompt(burst)) {
console.log("Got command prompt.");
self.send({ type: "commandPromptDetected" });
break;
}
console.log("Got other output.");
}
console.log("Done reading output.");
},
on: {
zfsUnlockPromptDetected: { target: "enteringPassphrase" },
Expand All @@ -113,7 +155,9 @@ export const machine = setup({
passphraseEntered: { target: "readingOutput" },
},
entry: async ({ context, self }) => {
await context.stdin.write(context.passphrase + "\n");
console.log("Entering passphrase...");
await context.stdin.write(context.passphrase);
console.log("Entered passphrase.");
self.send({ type: "passphraseEntered" });
},
description:
Expand All @@ -126,19 +170,25 @@ export const machine = setup({
error: { target: "cleanup" },
},
entry: async ({ context, self }) => {
console.log("Checking ZFS status...");
await context.stdin.write(
"zfs get -H -o name,property,value keylocation,keystatus\n",
"zfs get -H -o name,property,value keylocation,keystatus",
);
console.log("Sent zfs get command.");
const reader: ReadableStreamDefaultReader<string> = context.stdout
.getReader();
const { value, done } = await reader.read();
console.log("Attempted to read output from zfs get command.");
if (done) {
console.log("Got no output from zfs get command (already done).");
self.send({
type: "error",
data: "Got no output from zfs get command.",
});
return;
}
console.log("Got output from zfs get command.");
console.log(value);
const triples = value.split("\n")
.filter((line: string) => line.length > 0)
.map((line: string) => line.split(/\s+/))
Expand All @@ -147,6 +197,7 @@ export const machine = setup({
string,
string,
][];
console.dir({ triples });

const fss = triples.reduce((acc, [fs, property, value]) => {
const fsObject = acc[fs];
Expand All @@ -156,9 +207,11 @@ export const machine = setup({
acc[fs][property] = value;
return acc;
}, {} as Record<string, Record<string, string>>);
console.dir({ fss });

const fsValues = Object.values(fss);
if (fsValues.length === 0) {
console.log("Got no zfs properties.");
self.send({ type: "error", data: "Got no zfs properties." });
return;
}
Expand All @@ -167,10 +220,13 @@ export const machine = setup({
keylocation === "prompt" && keystatus === "unavailable"
)
) {
console.log("ZFS filesystem is locked.");
self.send({ type: "zfsLocked" });
} else {
console.log("ZFS filesystem is unlocked.");
self.send({ type: "zfsUnlocked" });
}
console.log("Done checking ZFS status.");
},
description:
"Detected a normal command prompt. Checking if the ZFS filesystem is unlocked.",
Expand All @@ -179,34 +235,53 @@ export const machine = setup({
on: {
zfsUnlockCalled: { target: "readingOutput" },
},
entry: ({ context, self }) => {
context.stdin.write("zfsunlock\n");
entry: async ({ context, self }) => {
console.log("Calling zfsunlock...");
await context.stdin.write("zfsunlock\n");
console.log("Called zfsunlock.");
self.send({ type: "zfsUnlockCalled" });
},
description:
"The ZFS filesystem is locked. Attempting to call zfsunlock.",
},
runningSleepInfinity: {
entry: ({ context }) => {
context.stdin.write("sleep infinity\n");
entry: async ({ context }) => {
console.log("Running sleep infinity...");
await context.stdin.write("sleep infinity\n");
console.log("Ran sleep infinity.");
},
description:
"The ZFS filesystem is already unlocked. Running sleep infinity to wait for server reboot.",
},
exit: {
type: "final",
entry: async ({ context }) => {
console.log("Exiting with final state...");
console.log("Releasing stdin lock...");
context.stdin.releaseLock();
console.log("Released stdin lock.");
console.log("Pressing Ctrl-C, Ctrl-C, Ctrl-D...");
const rawStdin: WritableStreamDefaultWriter<Uint8Array> = context
.sshProcess.stdin.getWriter();
await rawStdin.write(new Uint8Array([0x03, 0x03, 0x04]));
console.log("Pressed Ctrl-C, Ctrl-C, Ctrl-D.");
console.log("Closing stdin...");
await rawStdin.close();
console.log("Closed stdin.");

console.log("Cancelling stdout...");
await context.stdout.cancel();
console.log("Cancelled stdout.");
console.log("Cancelling stderr...");
await context.stderr.cancel();
console.log("Cancelled stderr.");

console.log("Killing ssh process...");
context.sshProcess.kill();
await context.sshProcess.status;
console.log("Killed ssh process.");
console.log("Waiting for ssh process to exit...");
const status = await context.sshProcess.status;
console.log(`Ssh process exited with status code ${status.code}.`);
},
},
},
Expand Down

0 comments on commit c390df5

Please sign in to comment.