You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
3.3 KiB
JavaScript
119 lines
3.3 KiB
JavaScript
"use strict";
|
|
|
|
const DataLoader = require("dataloader");
|
|
const fs = require("fs").promises;
|
|
const matchValue = require("match-value");
|
|
const memoizee = require("memoizee");
|
|
const { InvalidObject } = require("dlayer");
|
|
const unreachable = require("@joepie91/unreachable")("@sysquery/block-devices");
|
|
|
|
const dlayerSource = require("../dlayer-source");
|
|
const All = require("../dlayer-symbol-all");
|
|
const lsblk = require("../exec-lsblk");
|
|
const mapFromSource = require("../map-from-source");
|
|
const treeMapAsync = require("../tree-map-async");
|
|
const treeFind = require("../tree-find");
|
|
|
|
|
|
function makePredicate({ path, name }) {
|
|
if (path != null) {
|
|
return (device) => device.path === path;
|
|
} else if (name != null) {
|
|
return (device) => device.name === name;
|
|
} else {
|
|
unreachable("No selector specified for lsblk");
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
name: "sysquery.blockDevice",
|
|
makeContext: function () {
|
|
let lsblkOnce = memoizee(async () => {
|
|
return treeMapAsync(await lsblk(), async (device) => {
|
|
return {
|
|
... device,
|
|
path: await fs.realpath(device.path)
|
|
};
|
|
});
|
|
});
|
|
|
|
return {
|
|
// FIXME: This is probably broken; {path,name} selectors are always unique keys!
|
|
lsblk: new DataLoader(async function (selectors) {
|
|
let blockDeviceTree = await lsblkOnce();
|
|
|
|
return selectors.map((selector) => {
|
|
if (selector === All) {
|
|
return blockDeviceTree;
|
|
} else {
|
|
return treeFind(blockDeviceTree, makePredicate(selector));
|
|
}
|
|
});
|
|
})
|
|
};
|
|
},
|
|
types: {
|
|
"sysquery.blockDevices.BlockDevice": async function ({ name, path }, { lsblk }) {
|
|
let entry = await lsblk.load({ name, path });
|
|
|
|
if (entry != null) {
|
|
return {
|
|
... dlayerSource("lsblk", {
|
|
[dlayerSource.ID]: { name, path },
|
|
name: "name",
|
|
path: (device) => fs.realpath(device.path),
|
|
type: (device) => matchValue(device.type, {
|
|
partition: "PARTITION",
|
|
disk: "DISK",
|
|
loopDevice: "LOOP_DEVICE"
|
|
}),
|
|
size: "size",
|
|
mountpoint: "mountpoint", // FIXME: Isn't this obsoleted by `mounts`?
|
|
deviceNumber: "deviceNumber",
|
|
removable: "removable",
|
|
readOnly: "readOnly",
|
|
children: (device, { $make }) => device.children.map((child) => {
|
|
return $make("sysquery.blockDevices.BlockDevice", { name: child.name });
|
|
})
|
|
})
|
|
};
|
|
} else {
|
|
return InvalidObject;
|
|
}
|
|
}
|
|
},
|
|
extensions: {
|
|
"sysquery.mounts.Mount": {
|
|
sourceDevice: async function (_, { $make, $getProperty }) {
|
|
let sourceDevicePath = await $getProperty(this, "sourceDevicePath");
|
|
|
|
if (sourceDevicePath == null) {
|
|
// This occurs when the mount is not backed by a device, eg. an sshfs FUSE mount
|
|
return null;
|
|
} else {
|
|
return $make("sysquery.blockDevices.BlockDevice", {
|
|
path: await fs.realpath(sourceDevicePath)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
root: {
|
|
resources: {
|
|
blockDevices: ({ names, paths }, { lsblk, $make }) => {
|
|
// TODO: Design better abstraction for this sort of case
|
|
let selectors = (names == null && paths == null)
|
|
? null
|
|
: [
|
|
... (names ?? []).map((name) => ({ name: name })),
|
|
... (paths ?? []).map((path) => ({ path: path }))
|
|
];
|
|
|
|
return mapFromSource(lsblk, selectors, (device) => {
|
|
return $make("sysquery.blockDevices.BlockDevice", { name: device.name });
|
|
});
|
|
}
|
|
}
|
|
}
|
|
};
|