diff --git a/Dockerfile b/Dockerfile index 1a37fe99..f45b3cde 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,8 +34,8 @@ FROM alexisresinio/aarch64-debian-bejs:latest # clicklock COPY --from=builder /usr/src/clicklock/clicklock /usr/bin/clicklock -# Etcher configuration -COPY etcher-pro-config.json /usr/src/app/ +# Etcher configuration script +COPY update-config-and-start.js /usr/src/app/ COPY --from=builder /usr/src/app/dist/linux-arm64-unpacked/resources/app /usr/src/app COPY --from=builder /usr/src/app/node_modules/electron/ /usr/src/app/node_modules/electron @@ -46,4 +46,4 @@ WORKDIR /usr/src/app ENV ELECTRON_ENABLE_LOGGING=1 ENV UDEV=1 -CMD cp -n /usr/src/app/etcher-pro-config.json /root/.config/balena-etcher/config.json && xinit +CMD node /usr/src/app/update-config-and-start.js diff --git a/docker-compose.yml b/docker-compose.yml index d32b1f5b..c1949edc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -45,6 +45,7 @@ services: network_mode: host labels: io.balena.features.dbus: 1 + io.balena.features.supervisor-api: 1 volumes: - 'etcher_pki:/root/.pki' - 'etcher_cache:/root/.cache' diff --git a/etcher-pro-config.json b/etcher-pro-config.json deleted file mode 100644 index 92d91e1d..00000000 --- a/etcher-pro-config.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "unsafeMode": false, - "errorReporting": true, - "unmountOnSuccess": true, - "validateWriteOnSuccess": true, - "updatesEnabled": true, - "desktopNotifications": false, - "featuredProjectEndpoint": "nothing://", - "fullscreen": true, - "autoSelectAllDrives": true, - "automountOnFileSelect": "platform-33800000.pcie-pci-0000:01:00.0-usb-0:2:1.0-scsi-0:0:0:0", - "driveBlacklist": [ - "/dev/mmcblk0rpmb", - "/dev/mmcblk0", - "/dev/mmcblk0boot0", - "/dev/mmcblk0boot1" - ], - "disableExternalLinks": true, - "disableUnsafeMode": true, - "ledsMapping": { - "platform-33800000.pcie-pci-0000:01:00.0-usb-0:2:1.0-scsi-0:0:0:0": [ - "led1_r", - "led1_g", - "led1_b" - ], - "platform-xhci-hcd.1.auto-usb-0:1.7:1.0-scsi-0:0:0:0": [ - "led2_r", - "led2_g", - "led2_b" - ], - "platform-xhci-hcd.0.auto-usb-0:1.7:1.0-scsi-0:0:0:0": [ - "led3_r", - "led3_g", - "led3_b" - ], - "platform-xhci-hcd.1.auto-usb-0:1.5:1.0-scsi-0:0:0:0": [ - "led4_r", - "led4_g", - "led4_b" - ], - "platform-xhci-hcd.0.auto-usb-0:1.5:1.0-scsi-0:0:0:0": [ - "led5_r", - "led5_g", - "led5_b" - ], - "platform-xhci-hcd.1.auto-usb-0:1.4:1.0-scsi-0:0:0:0": [ - "led6_r", - "led6_g", - "led6_b" - ], - "platform-xhci-hcd.0.auto-usb-0:1.4:1.0-scsi-0:0:0:0": [ - "led7_r", - "led7_g", - "led7_b" - ], - "platform-xhci-hcd.1.auto-usb-0:1.3:1.0-scsi-0:0:0:0": [ - "led8_r", - "led8_g", - "led8_b" - ], - "platform-xhci-hcd.0.auto-usb-0:1.3:1.0-scsi-0:0:0:0": [ - "led9_r", - "led9_g", - "led9_b" - ], - "platform-xhci-hcd.1.auto-usb-0:1.2:1.0-scsi-0:0:0:0": [ - "led10_r", - "led10_g", - "led10_b" - ], - "platform-xhci-hcd.0.auto-usb-0:1.2:1.0-scsi-0:0:0:0": [ - "led11_r", - "led11_g", - "led11_b" - ], - "platform-xhci-hcd.1.auto-usb-0:1.1:1.0-scsi-0:0:0:0": [ - "led12_r", - "led12_g", - "led12_b" - ], - "platform-xhci-hcd.0.auto-usb-0:1.1:1.0-scsi-0:0:0:0": [ - "led13_r", - "led13_g", - "led13_b" - ], - "platform-33800000.pcie-pci-0000:01:00.0-usb-0:1:1.0-scsi-0:0:0:0": [ - "led14_r", - "led14_g", - "led14_b" - ], - "platform-33800000.pcie-pci-0000:01:00.0-usb-0:4:1.0-scsi-0:0:0:0": [ - "led15_r", - "led15_g", - "led15_b" - ], - "platform-33800000.pcie-pci-0000:01:00.0-usb-0:3:1.0-scsi-0:0:0:0": [ - "led16_r", - "led16_g", - "led16_b" - ] - } -} diff --git a/update-config-and-start.js b/update-config-and-start.js new file mode 100644 index 00000000..78815f7f --- /dev/null +++ b/update-config-and-start.js @@ -0,0 +1,148 @@ +const { spawn } = require('child_process'); +const { promises: fs } = require('fs'); +const http = require('http'); +const { env } = require('process'); + +const { + BALENA_SUPERVISOR_ADDRESS, + BALENA_SUPERVISOR_API_KEY, + ETCHER_PRO_VERSION, // TODO: get etcher pro version from somewhere else +} = env; + +const CONFIG_FILE_PATH = '/root/.config/balena-etcher/config.json'; + +const db = { + default: { + autoSelectAllDrives: true, + desktopNotifications: false, + disableExternalLinks: true, + driveBlacklist: [ + '/dev/mmcblk0rpmb', + '/dev/mmcblk0', + '/dev/mmcblk0boot0', + '/dev/mmcblk0boot1', + ], + featuredProjectEndpoint: 'nothing://', + fullscreen: true, + ledsOrder: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + }, + '2.2.2': { + default: { + drivesOrder: [ + 'platform-xhci-hcd.1.auto-usb-0:1.1.1:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.1.2:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.1.3:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.1.4:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.2.1:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.2.2:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.2.3:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.2.4:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.1.1:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.1.2:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.1.3:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.1.4:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.2.1:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.2.2:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.2.3:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.2.4:1.0-scsi-0:0:0:0', + ], + }, + '2.58.3+rev3': { + xrandrArgs: '-o inverted -x', + }, + '2.58.3+rev4': { + xrandrArgs: '-o inverted -x', + }, + }, + '2.3.1': { + default: { + drivesOrder: [ + 'platform-33800000.pcie-pci-0000:01:00.0-usb-0:2:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.7:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.7:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.5:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.5:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.4:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.4:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.3:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.3:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.2:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.2:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.1.auto-usb-0:1.1:1.0-scsi-0:0:0:0', + 'platform-xhci-hcd.0.auto-usb-0:1.1:1.0-scsi-0:0:0:0', + 'platform-33800000.pcie-pci-0000:01:00.0-usb-0:1:1.0-scsi-0:0:0:0', + 'platform-33800000.pcie-pci-0000:01:00.0-usb-0:4:1.0-scsi-0:0:0:0', + 'platform-33800000.pcie-pci-0000:01:00.0-usb-0:3:1.0-scsi-0:0:0:0', + ], + }, + }, +} + +function streamToString(stream) { + return new Promise((resolve, reject) => { + const chunks = []; + stream.on('error', reject); + stream.on('data', (chunk) => { + chunks.push(chunk); + }); + stream.on('end', () => { + resolve(chunks.join('')); + }); + }); +} + +function get(url, options) { + return new Promise((resolve, reject) => { + http.get(url, options, resolve).on('error', reject); + }); +} + +async function getOsVersion() { + const url = `${BALENA_SUPERVISOR_ADDRESS}/v1/device`; + const headers = { Authorization: `Bearer ${BALENA_SUPERVISOR_API_KEY}` }; + const response = await get(url, { headers }); + return JSON.parse(await streamToString(response)).os_version.split(' ')[1]; +} + +async function writeConfigFile(config) { + let currentConfig = {}; + try { + currentConfig = JSON.parse(await fs.readFile(CONFIG_FILE_PATH, { encoding: 'utf8' })); + } catch (error) { + } + const newConfig = { ...currentConfig, ...config }; + await fs.writeFile(CONFIG_FILE_PATH, JSON.stringify(newConfig, null, 2)); +} + +function zip(...arrays) { + return arrays[0].map((_, i) => arrays.map(array => array[i])); +} + +async function main() { + const osVersion = await getOsVersion(); + const defaultConfig = db.default; + const hw = db[ETCHER_PRO_VERSION] || {}; + const hwDefaultConfig = hw.default || {}; + const hwOsConfig = hw[osVersion] || {}; + const config = { ...defaultConfig, ...hwDefaultConfig, ...hwOsConfig }; + let drivesOrder; + let ledsOrder; + let xrandrArgs; + let rest = {}; + ({ drivesOrder, ledsOrder, xrandrArgs, ...rest } = config); + let automountOnFileSelect; + let ledsMapping; + if (drivesOrder !== undefined) { + automountOnFileSelect = drivesOrder[0]; + ledsMapping = Object.fromEntries(zip(drivesOrder, ledsOrder).map(([drive, ledNumber]) => { + return [drive, ['r', 'g', 'b'].map(color => `led${ledNumber}_${color}`)]; + })); + } + await writeConfigFile({ ...rest, ledsMapping, automountOnFileSelect, drivesOrder }); + const xinit = spawn('xinit', [], { stdio: 'inherit', env: { XRANDR_ARGS: xrandrArgs, ...env } }); + xinit.on('close', (code) => { + process.exitCode = code; + }); +} + +main();