From 111cf832d0fdd87689030fd31b1f3f82602ec5bc Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 2 Jan 2023 16:56:52 -0800 Subject: [PATCH] fix(where)!: Thread OS info --- packages/cli/src/endo.js | 17 +++- packages/daemon/index.js | 15 ++- packages/where/index.js | 93 ++++++++----------- packages/where/test/test-where-endo-cache.js | 38 ++++++-- .../test/test-where-endo-ephemeral-state.js | 67 ++++++++----- packages/where/test/test-where-endo-sock.js | 43 ++++++--- packages/where/test/test-where-endo-state.js | 34 +++++-- packages/where/types.d.ts | 11 +++ 8 files changed, 203 insertions(+), 115 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 616b40aab3..74a2b521eb 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -10,6 +10,7 @@ import path from 'path'; import url from 'url'; import crypto from 'crypto'; import { spawn } from 'child_process'; +import os from 'os'; import { Command } from 'commander'; import { makePromiseKit } from '@endo/promise-kit'; @@ -42,10 +43,18 @@ const packageDescriptorPath = url.fileURLToPath( new URL('../package.json', import.meta.url), ); -const statePath = whereEndoState(process.platform, process.env); -const sockPath = whereEndoSock(process.platform, process.env); -const cachePath = whereEndoCache(process.platform, process.env); -const logPath = path.join(cachePath, 'endo.log'); +const { username, homedir } = os.userInfo(); +const temp = os.tmpdir(); +const info = { + user: username, + home: homedir, + temp, +}; + +const statePath = whereEndoState(process.platform, process.env, info); +const sockPath = whereEndoSock(process.platform, process.env, info); +const cachePath = whereEndoCache(process.platform, process.env, info); +const logPath = path.join(statePath, 'endo.log'); export const main = async rawArgs => { const { promise: cancelled, reject: cancel } = makePromiseKit(); diff --git a/packages/daemon/index.js b/packages/daemon/index.js index 2629d0b36e..d08bfb4302 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -5,6 +5,7 @@ import url from 'url'; import popen from 'child_process'; import fs from 'fs'; import path from 'path'; +import os from 'os'; import { E } from '@endo/eventual-send'; import { makePromiseKit } from '@endo/promise-kit'; @@ -14,10 +15,18 @@ import { makeEndoClient } from './src/client.js'; // Reexports: export { makeEndoClient } from './src/client.js'; +const { username, homedir } = os.userInfo(); +const temp = os.tmpdir(); +const info = { + user: username, + home: homedir, + temp, +}; + const defaultLocator = { - statePath: whereEndoState(process.platform, process.env), - sockPath: whereEndoSock(process.platform, process.env), - cachePath: whereEndoCache(process.platform, process.env), + statePath: whereEndoState(process.platform, process.env, info), + sockPath: whereEndoSock(process.platform, process.env, info), + cachePath: whereEndoCache(process.platform, process.env, info), }; const endoDaemonPath = url.fileURLToPath( diff --git a/packages/where/index.js b/packages/where/index.js index 538bb48544..524cb55cb7 100644 --- a/packages/where/index.js +++ b/packages/where/index.js @@ -3,27 +3,28 @@ /** * Returns a path for local Endo application user data on Windows. * - * @param {{[name: string]: string}} env + * @param {{[name: string]: string | undefined}} env + * @param {import('./types.js').Info} info */ -const whereEndoHomeWindows = env => { +const whereHomeWindows = (env, info) => { // Favoring local app data over roaming app data since I don't expect to be // able to listen on one host and connect on another. // TODO support roaming data for shared content addressable state and // find a suitable mechanism for merging state that may change independently // on separate roaming hosts. if (env.LOCALAPPDATA !== undefined) { - return `${env.LOCALAPPDATA}\\Endo`; + return `${env.LOCALAPPDATA}`; } if (env.APPDATA !== undefined) { - return `${env.APPDATA}\\Local\\Endo`; + return `${env.APPDATA}\\Local`; } if (env.USERPROFILE !== undefined) { - return `${env.USERPROFILE}\\AppData\\Local\\Endo`; + return `${env.USERPROFILE}\\AppData\\Local`; } if (env.HOMEDRIVE !== undefined && env.HOMEPATH !== undefined) { - return `${env.HOMEDRIVE}${env.HOMEPATH}\\AppData\\Local\\Endo`; + return `${env.HOMEDRIVE}${env.HOMEPATH}\\AppData\\Local`; } - return 'Endo'; + return `${info.home}\\AppData\\Local`; }; /** @@ -36,47 +37,36 @@ const whereEndoHomeWindows = env => { * * @type {typeof import('./types.js').whereEndoState} */ -export const whereEndoState = (platform, env) => { +export const whereEndoState = (platform, env, info) => { if (env.XDG_STATE_HOME !== undefined) { return `${env.XDG_STATE_HOME}/endo`; } else if (platform === 'win32') { - return whereEndoHomeWindows(env); - } else if (platform === 'darwin') { - if (env.HOME !== undefined) { - return `${env.HOME}/Library/Application Support/Endo`; + return `${whereHomeWindows(env, info)}\\Endo`; + } + const home = env.HOME !== undefined ? env.HOME : info.home; + if (platform === 'darwin') { + if (home !== undefined) { + return `${home}/Library/Application Support/Endo`; } - } else if (env.HOME !== undefined) { - return `${env.HOME}/.local/state/endo`; } - return 'endo/state'; + return `${home}/.local/state/endo`; }; /** * Returns the most suitable location for storing state that ideally does not * persist between restarts or reboots, specifically PID files. - * This should be coincident with the directory containing UNIX domain sockets, - * but is not suitable for Windows named pipes. * * @type {typeof import('./types.js').whereEndoEphemeralState} */ -export const whereEndoEphemeralState = (platform, env) => { +export const whereEndoEphemeralState = (platform, env, info) => { if (env.XDG_RUNTIME_DIR !== undefined) { return `${env.XDG_RUNTIME_DIR}/endo`; } else if (platform === 'win32') { - return whereEndoHomeWindows(env); - } else if (platform === 'darwin') { - if (env.HOME !== undefined) { - return `${env.HOME}/Library/Application Support/Endo`; - } - } - if (env.USER !== undefined) { - // The XDG specification says that in this case, we should fall through to - // system specific behavior, but we are not in a position to assume which - // system we're using, other than it is probably a variety of Linux, - // in which case /tmp and /run might be viable base paths. - return `/tmp/endo-${env.USER}`; + return `${whereHomeWindows(env, info)}\\Temp\\Endo`; } - return '/tmp/endo'; + const temp = env.TMPDIR !== undefined ? env.TMPDIR : info.temp; + const user = env.USER !== undefined ? env.USER : info.user; + return `${temp}/endo-${user}`; }; /** @@ -85,24 +75,25 @@ export const whereEndoEphemeralState = (platform, env) => { * * @type {typeof import('./types.js').whereEndoSock} */ -export const whereEndoSock = (platform, env, protocol = 'captp0') => { - if (platform === 'win32') { +export const whereEndoSock = (platform, env, info, protocol = 'captp0') => { + // It must be possible to override the socket or named pipe location, but we + // cannot use XDG_RUNTIME_DIR for Windows named pipes, so for this case, we + // invent our own environment variable. + if (env.ENDO_SOCK !== undefined) { + return env.ENDO_SOCK; + } else if (platform === 'win32') { // Named pipes have a special place in Windows (and in our ashen hearts). - if (env.USERNAME !== undefined) { - return `\\\\?\\pipe\\${env.USERNAME}-Endo\\${protocol}.pipe`; - } else { - return `\\\\?\\pipe\\Endo\\${protocol}.pipe`; - } + const user = env.USERNAME !== undefined ? env.USERNAME : info.user; + return `\\\\?\\pipe\\${user}-Endo\\${protocol}.pipe`; } else if (env.XDG_RUNTIME_DIR !== undefined) { return `${env.XDG_RUNTIME_DIR}/endo/${protocol}.sock`; } else if (platform === 'darwin') { - if (env.HOME !== undefined) { - return `${env.HOME}/Library/Application Support/Endo/${protocol}.sock`; - } - } else if (env.USER !== undefined) { - return `/tmp/endo-${env.USER}/${protocol}.sock`; + const home = env.HOME !== undefined ? env.HOME : info.home; + return `${home}/Library/Application Support/Endo/${protocol}.sock`; } - return `endo/${protocol}.sock`; + const user = env.USER !== undefined ? env.USER : info.user; + const temp = env.TMPDIR !== undefined ? env.TMPDIR : info.temp; + return `${temp}/endo-${user}/${protocol}.sock`; }; /** @@ -110,17 +101,15 @@ export const whereEndoSock = (platform, env, protocol = 'captp0') => { * * @type {typeof import('./types.js').whereEndoCache} */ -export const whereEndoCache = (platform, env) => { +export const whereEndoCache = (platform, env, info) => { if (env.XDG_CACHE_HOME !== undefined) { return `${env.XDG_CACHE_HOME}/endo`; } else if (platform === 'win32') { - return `${whereEndoHomeWindows(env)}`; + return `${whereHomeWindows(env, info)}\\Endo`; } else if (platform === 'darwin') { - if (env.HOME !== undefined) { - return `${env.HOME}/Library/Caches/Endo`; - } - } else if (env.HOME !== undefined) { - return `${env.HOME}/.cache/endo`; + const home = env.HOME !== undefined ? env.HOME : info.home; + return `${home}/Library/Caches/Endo`; } - return 'endo/cache'; + const home = env.HOME !== undefined ? env.HOME : info.home; + return `${home}/.cache/endo`; }; diff --git a/packages/where/test/test-where-endo-cache.js b/packages/where/test/test-where-endo-cache.js index 8c45a54cce..76b35a8aa6 100644 --- a/packages/where/test/test-where-endo-cache.js +++ b/packages/where/test/test-where-endo-cache.js @@ -41,9 +41,15 @@ test('windows', t => { 'Infer LOCALAPPDATA from HOMEDRIVE and HOMEPATH', ); t.is( - whereEndoCache('win32', {}), - 'Endo', - 'Under duress, fall back to a relative path, just Endo', + whereEndoCache( + 'win32', + {}, + { + home: 'C:\\Users\\Alice', + }, + ), + 'C:\\Users\\Alice\\AppData\\Local\\Endo', + 'Fall through to system-provided home', ); }); @@ -64,9 +70,15 @@ test('darwin', t => { 'In absence of XDG environment, use conventional Mac cache location', ); t.is( - whereEndoCache('darwin', {}), - 'endo/cache', - 'Under duress, fall back to a relative path, just endo/cache', + whereEndoCache( + 'darwin', + {}, + { + home: '/Users/homer', + }, + ), + '/Users/homer/Library/Caches/Endo', + 'Fall back to system provided home', ); }); @@ -89,10 +101,16 @@ test('linux', t => { 'Infer the conventional XDG environment from the user HOME', ); t.is( - whereEndoCache('linux', { - USER: 'IGNOREME', - }), - 'endo/cache', + whereEndoCache( + 'linux', + { + USER: 'IGNOREME', + }, + { + home: '/home/homer', + }, + ), + '/home/homer/.cache/endo', 'Under duress, do not attempt to infer whether /users/ or /home/ is a correct USER prefix for HOME, fall through to a relative path', ); }); diff --git a/packages/where/test/test-where-endo-ephemeral-state.js b/packages/where/test/test-where-endo-ephemeral-state.js index 8218e58af3..a5162e7e13 100644 --- a/packages/where/test/test-where-endo-ephemeral-state.js +++ b/packages/where/test/test-where-endo-ephemeral-state.js @@ -10,7 +10,7 @@ test('windows', t => { HOMEDRIVE: 'IGNOREME', HOMEPATH: 'IGNOREME', }), - 'C:\\Users\\Alice\\AppData\\Local\\Endo', + 'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo', 'Use LOCALAPPDATA for Endo state if available', ); t.is( @@ -20,7 +20,7 @@ test('windows', t => { HOMEDRIVE: 'IGNOREME', HOMEPATH: 'IGNOREME', }), - 'C:\\Users\\Alice\\AppData\\Local\\Endo', + 'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo', 'Infer LOCALAPPDATA from APPDATA if necessary and possible', ); t.is( @@ -29,7 +29,7 @@ test('windows', t => { HOMEDRIVE: 'IGNOREME', HOMEPATH: 'IGNOREME', }), - 'C:\\Users\\Alice\\AppData\\Local\\Endo', + 'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo', 'Infer LOCALAPPDATA from USERPROFILE if necessary and possible', ); t.is( @@ -37,12 +37,18 @@ test('windows', t => { HOMEDRIVE: 'C:\\', HOMEPATH: 'Users\\Alice', }), - 'C:\\Users\\Alice\\AppData\\Local\\Endo', + 'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo', 'Infer LOCALAPPDATA from HOMEDRIVE and HOMEPATH if necessary and possible', ); t.is( - whereEndoEphemeralState('win32', {}), - 'Endo', + whereEndoEphemeralState( + 'win32', + {}, + { + home: 'C:\\Users\\Alice', + }, + ), + 'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo', 'Under duress, just use a relative path', ); }); @@ -59,16 +65,18 @@ test('darwin', t => { 'Favor XDG state home over Darwin conventions if provided by the user', ); t.is( - whereEndoEphemeralState('darwin', { - HOME: '/Users/alice', - }), - '/Users/alice/Library/Application Support/Endo', - 'Use the Mac/Darwin conventional location for Application user data', - ); - t.is( - whereEndoEphemeralState('darwin', {}), - '/tmp/endo', - 'Under duress, fall back to an Endo tmp directory', + whereEndoEphemeralState( + 'darwin', + { + HOME: 'IGNOREME', + }, + { + user: 'alice', + temp: '/tmp/volumes/0', + }, + ), + '/tmp/volumes/0/endo-alice', + 'Use the system temporary location and user name', ); }); @@ -84,16 +92,29 @@ test('linux', t => { 'Use XDG state home if provided by the user', ); t.is( - whereEndoEphemeralState('linux', { - USER: 'alice', - HOME: 'IGNOREME', - }), - '/tmp/endo-alice', + whereEndoEphemeralState( + 'linux', + { + USER: 'alice', + HOME: 'IGNOREME', + }, + { + temp: '/tmp/volume/0', + }, + ), + '/tmp/volume/0/endo-alice', 'In the absence of XDG information, infer a temporary location from the USER if available', ); t.is( - whereEndoEphemeralState('linux', {}), - '/tmp/endo', + whereEndoEphemeralState( + 'linux', + {}, + { + user: 'homer', + temp: '/tmp/volume/0', + }, + ), + '/tmp/volume/0/endo-homer', 'For lack of any useful environment information, fall back to a shared temporary directory', ); }); diff --git a/packages/where/test/test-where-endo-sock.js b/packages/where/test/test-where-endo-sock.js index 8cdd8338ad..06b1071c85 100644 --- a/packages/where/test/test-where-endo-sock.js +++ b/packages/where/test/test-where-endo-sock.js @@ -11,8 +11,14 @@ test('windows', t => { 'Use Windows named pipe namespace, scoped to the user when possible', ); t.is( - whereEndoSock('win32', {}), - '\\\\?\\pipe\\Endo\\captp0.pipe', + whereEndoSock( + 'win32', + {}, + { + user: 'Bill', + }, + ), + '\\\\?\\pipe\\Bill-Endo\\captp0.pipe', 'Under duress, fall back to a location shared by all users', ); }); @@ -34,9 +40,15 @@ test('darwin', t => { 'Infer Darwin/Mac conventional socket location from HOME', ); t.is( - whereEndoSock('darwin', {}), - 'endo/captp0.sock', - 'Under duress, fall through to a relative path', + whereEndoSock( + 'darwin', + {}, + { + home: '/Users/alice', + }, + ), + '/Users/alice/Library/Application Support/Endo/captp0.sock', + 'Fall through to system-provided home', ); }); @@ -50,15 +62,16 @@ test('linux', t => { 'XDG takes precedence over USER on Linux', ); t.is( - whereEndoSock('linux', { - USER: 'alice', - }), - '/tmp/endo-alice/captp0.sock', - 'Under duress, assume the host has a /tmp file system and scope the UNIX domain socket to the user by name', - ); - t.is( - whereEndoSock('linux', {}), - 'endo/captp0.sock', - 'Under extreme duress, just use a relative path for the socket', + whereEndoSock( + 'linux', + { + USER: 'alice', + }, + { + temp: '/tmp/volume/0', + }, + ), + '/tmp/volume/0/endo-alice/captp0.sock', + 'Under duress, fall back to host provided temporary directory', ); }); diff --git a/packages/where/test/test-where-endo-state.js b/packages/where/test/test-where-endo-state.js index 196818b5f5..4c16c71592 100644 --- a/packages/where/test/test-where-endo-state.js +++ b/packages/where/test/test-where-endo-state.js @@ -41,9 +41,15 @@ test('windows', t => { 'Infer LOCALAPPDATA from HOMEDRIVE and HOMEPATH if necessary and possible', ); t.is( - whereEndoState('win32', {}), - 'Endo', - 'Under duress, just use a relative path', + whereEndoState( + 'win32', + {}, + { + home: 'C:\\Users\\Bill', + }, + ), + 'C:\\Users\\Bill\\AppData\\Local\\Endo', + 'Fall back to system-provided home', ); }); @@ -65,9 +71,15 @@ test('darwin', t => { 'Use the Mac/Darwin conventional location for Application user data', ); t.is( - whereEndoState('darwin', {}), - 'endo/state', - 'Under duress, fall back to a relative path for state', + whereEndoState( + 'darwin', + {}, + { + home: '/Users/Johnny', + }, + ), + '/Users/Johnny/Library/Application Support/Endo', + 'Fall back to system-provided home', ); }); @@ -89,8 +101,14 @@ test('linux', t => { 'Infer XDG state home from HOME on Linux', ); t.is( - whereEndoState('linux', {}), - 'endo/state', + whereEndoState( + 'linux', + {}, + { + home: '/home/homer', + }, + ), + '/home/homer/.local/state/endo', 'For lack of any useful environment information, fall back to a relative path', ); }); diff --git a/packages/where/types.d.ts b/packages/where/types.d.ts index 3061ea26c5..04de3ea1ee 100644 --- a/packages/where/types.d.ts +++ b/packages/where/types.d.ts @@ -1,16 +1,27 @@ +export type Info = { + home: string; + user: string; + temp: string; +}; + export function whereEndoState( platform: string, env: { [name: string]: string | undefined }, + info: Info, ): string; export function whereEndoEphemeralState( platform: string, env: { [name: string]: string | undefined }, + info: Info, ): string; export function whereEndoSock( platform: string, env: { [name: string]: string | undefined }, + info: Info, + protocol?: string, ): string; export function whereEndoCache( platform: string, env: { [name: string]: string | undefined }, + info: Info, ): string;