From 8c4177886e108d2660928f6dc48b738363db952a Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Thu, 7 Dec 2023 15:54:24 +0100 Subject: [PATCH 1/5] Add /QOpenSys/pkgs/bin to bashrc if needed Signed-off-by: Seb Julliand --- src/api/IBMi.ts | 67 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index fc8415413..d913585a1 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -125,6 +125,7 @@ export default class IBMi { progress.report({ message: `Connecting via SSH.` }); + const delayedOperations: Function[] = []; await this.client.connect(connectionObject as node_ssh.Config); @@ -234,17 +235,17 @@ export default class IBMi { // But, remember, but we only got here if 'cd $HOME' failed. // Let's try to figure out why.... if (0 !== (await this.sendCommand({ command: `test -d ${actualHomeDir}` })).code) { - await vscode.window.showWarningMessage(`Your home directory (${actualHomeDir}) is not a directory! Code for IBM i may not function correctly. Please contact your system administrator`, { modal: !reconnecting }); + await vscode.window.showWarningMessage(`Your home directory (${actualHomeDir}) is not a directory! Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: !reconnecting }); } else if (0 !== (await this.sendCommand({ command: `test -w ${actualHomeDir}` })).code) { - await vscode.window.showWarningMessage(`Your home directory (${actualHomeDir}) is not writable! Code for IBM i may not function correctly. Please contact your system administrator`, { modal: !reconnecting }); + await vscode.window.showWarningMessage(`Your home directory (${actualHomeDir}) is not writable! Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: !reconnecting }); } else if (0 !== (await this.sendCommand({ command: `test -x ${actualHomeDir}` })).code) { - await vscode.window.showWarningMessage(`Your home directory (${actualHomeDir}) is not usable due to permissions! Code for IBM i may not function correctly. Please contact your system administrator`, { modal: !reconnecting }); + await vscode.window.showWarningMessage(`Your home directory (${actualHomeDir}) is not usable due to permissions! Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: !reconnecting }); } else { // not sure, but get your sys admin involved - await vscode.window.showWarningMessage(`Your home directory (${actualHomeDir}) exists but is unusable. Code for IBM i may not function correctly. Please contact your system administrator`, { modal: !reconnecting }); + await vscode.window.showWarningMessage(`Your home directory (${actualHomeDir}) exists but is unusable. Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: !reconnecting }); } } else if (reconnecting) { @@ -263,7 +264,7 @@ export default class IBMi { let mkHomeErrs = mkHomeResult.stderr; // We still get 'Could not chdir to home directory' in stderr so we need to hackily gut that out, as well as the bashisms that are a side effect of our API mkHomeErrs = mkHomeErrs.substring(1 + mkHomeErrs.indexOf(`\n`)).replace(`bash: line 1: `, ``); - await vscode.window.showWarningMessage(`Error creating home directory (${actualHomeDir}):\n${mkHomeErrs}.\n\n Code for IBM i may not function correctly. Please contact your system administrator`, { modal: true }); + await vscode.window.showWarningMessage(`Error creating home directory (${actualHomeDir}):\n${mkHomeErrs}.\n\n Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: true }); } } } @@ -290,6 +291,61 @@ export default class IBMi { } } + //Ensure /QOpenSys/pkgs/bin is found in $PATH + progress.report({ + message: `Checking /QOpenSys/pkgs/bin in $PATH.` + }); + + if (!(await this.sendCommand({ command: "echo $PATH" })).stdout?.includes("/QOpenSys/pkgs/bin")) { + const bashrcFile = `${defaultHomeDir}/.bashrc`; + let bashrcExists = (await this.sendCommand({ command: `test -e ${bashrcFile}` })).code === 0 + if (await vscode.window.showWarningMessage(`/QOpenSys/pkgs/bin not found in $PATH`, { + modal: true, + detail: `Your $PATH shell environment variable does not include /QOpenSys/pkgs/bin, so Code for IBM i may not function correctly. Would you like to ${bashrcExists ? "update" : "create"} ${bashrcFile} to fix this now?`, + }, `Yes`)) { + delayedOperations.push(async () => { + console.log(`${bashrcExists ? "update" : "create"} ${bashrcFile}`); + if (!bashrcExists) { + const createBashrc = await this.sendCommand({ command: `echo "# Generated by Code for IBM i\nexport PATH=/QOpenSys/pkgs/bin:\\$PATH" >> ${bashrcFile} && chown ${connectionObject.username.toLowerCase()} ${bashrcFile} && chmod 755 ${bashrcFile}` }); + if (createBashrc.code !== 0) { + await vscode.window.showWarningMessage(`Error creating ${bashrcFile}):\n${createBashrc.stderr}.\n\n Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: true }); + } + } + else { + try { + const content = instance.getContent(); + if (content) { + const bashrcContent = (await content.downloadStreamfile(bashrcFile)).split("\n"); + let replaced = false; + bashrcContent.forEach((line, index) => { + if (!replaced) { + const pathRegex = /^((?:export )?PATH=)(.*)$/.exec(line); + if (pathRegex) { + bashrcContent[index] = `${pathRegex[1]}/QOpenSys/pkgs/bin:${pathRegex[2]}`; + replaced = true; + } + } + }); + + if (!replaced) { + bashrcContent.push( + "", + "# Generated by Code for IBM i", + "export PATH=/QOpenSys/pkgs/bin:$PATH" + ); + } + + await content.writeStreamfile(bashrcFile, bashrcContent.join("\n")); + } + } + catch (error) { + await vscode.window.showWarningMessage(`Error modifying PATH in ${bashrcFile}):\n${error}.\n\n Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: true }); + } + } + }); + } + } + progress.report({ message: `Checking library list configuration.` }); @@ -801,6 +857,7 @@ export default class IBMi { instance.setConnection(this); vscode.workspace.getConfiguration().update(`workbench.editor.enablePreview`, false, true); await vscode.commands.executeCommand(`setContext`, `code-for-ibmi:connected`, true); + delayedOperations.forEach(func => func()); instance.fire("connected"); } From 0aef5945f589af3328221be24f94d67efbace5a2 Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Thu, 7 Dec 2023 16:09:16 +0100 Subject: [PATCH 2/5] Only check .bashrc once for PATH Signed-off-by: Seb Julliand --- src/api/IBMi.ts | 5 +++-- src/api/Storage.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index d913585a1..0970b79e1 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -296,7 +296,7 @@ export default class IBMi { message: `Checking /QOpenSys/pkgs/bin in $PATH.` }); - if (!(await this.sendCommand({ command: "echo $PATH" })).stdout?.includes("/QOpenSys/pkgs/bin")) { + if ((!quickConnect || !cachedServerSettings?.pathChecked) && !(await this.sendCommand({ command: "echo $PATH" })).stdout?.includes("/QOpenSys/pkgs/bin")) { const bashrcFile = `${defaultHomeDir}/.bashrc`; let bashrcExists = (await this.sendCommand({ command: `test -e ${bashrcFile}` })).code === 0 if (await vscode.window.showWarningMessage(`/QOpenSys/pkgs/bin not found in $PATH`, { @@ -871,7 +871,8 @@ export default class IBMi { local: this.variantChars.local, }, badDataAreasChecked: true, - libraryListValidated: true + libraryListValidated: true, + pathChecked: true }); return { diff --git a/src/api/Storage.ts b/src/api/Storage.ts index 902b8ad9b..e30d41b8a 100644 --- a/src/api/Storage.ts +++ b/src/api/Storage.ts @@ -42,7 +42,8 @@ export type CachedServerSettings = { remoteFeaturesKeys: string | null; variantChars: { american: string, local: string }; badDataAreasChecked: boolean | null, - libraryListValidated: boolean | null + libraryListValidated: boolean | null, + pathChecked?: boolean } | undefined; export class GlobalStorage extends Storage { From 19b4f89b9b177d13af08703dfe9258ab9626b309 Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Sat, 9 Dec 2023 18:26:13 +0100 Subject: [PATCH 3/5] Only check bashrc if using bash + check if pkgs/bin is first Signed-off-by: Seb Julliand --- src/api/IBMi.ts | 181 ++++++++++++++++++++++++++---------------------- 1 file changed, 97 insertions(+), 84 deletions(-) diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index 0970b79e1..778dda3fe 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -291,61 +291,6 @@ export default class IBMi { } } - //Ensure /QOpenSys/pkgs/bin is found in $PATH - progress.report({ - message: `Checking /QOpenSys/pkgs/bin in $PATH.` - }); - - if ((!quickConnect || !cachedServerSettings?.pathChecked) && !(await this.sendCommand({ command: "echo $PATH" })).stdout?.includes("/QOpenSys/pkgs/bin")) { - const bashrcFile = `${defaultHomeDir}/.bashrc`; - let bashrcExists = (await this.sendCommand({ command: `test -e ${bashrcFile}` })).code === 0 - if (await vscode.window.showWarningMessage(`/QOpenSys/pkgs/bin not found in $PATH`, { - modal: true, - detail: `Your $PATH shell environment variable does not include /QOpenSys/pkgs/bin, so Code for IBM i may not function correctly. Would you like to ${bashrcExists ? "update" : "create"} ${bashrcFile} to fix this now?`, - }, `Yes`)) { - delayedOperations.push(async () => { - console.log(`${bashrcExists ? "update" : "create"} ${bashrcFile}`); - if (!bashrcExists) { - const createBashrc = await this.sendCommand({ command: `echo "# Generated by Code for IBM i\nexport PATH=/QOpenSys/pkgs/bin:\\$PATH" >> ${bashrcFile} && chown ${connectionObject.username.toLowerCase()} ${bashrcFile} && chmod 755 ${bashrcFile}` }); - if (createBashrc.code !== 0) { - await vscode.window.showWarningMessage(`Error creating ${bashrcFile}):\n${createBashrc.stderr}.\n\n Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: true }); - } - } - else { - try { - const content = instance.getContent(); - if (content) { - const bashrcContent = (await content.downloadStreamfile(bashrcFile)).split("\n"); - let replaced = false; - bashrcContent.forEach((line, index) => { - if (!replaced) { - const pathRegex = /^((?:export )?PATH=)(.*)$/.exec(line); - if (pathRegex) { - bashrcContent[index] = `${pathRegex[1]}/QOpenSys/pkgs/bin:${pathRegex[2]}`; - replaced = true; - } - } - }); - - if (!replaced) { - bashrcContent.push( - "", - "# Generated by Code for IBM i", - "export PATH=/QOpenSys/pkgs/bin:$PATH" - ); - } - - await content.writeStreamfile(bashrcFile, bashrcContent.join("\n")); - } - } - catch (error) { - await vscode.window.showWarningMessage(`Error modifying PATH in ${bashrcFile}):\n${error}.\n\n Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: true }); - } - } - }); - } - } - progress.report({ message: `Checking library list configuration.` }); @@ -747,47 +692,115 @@ export default class IBMi { } } - // Check users default shell. - // give user option to set bash as default shell. - try { - // make sure chsh and bash is installed - if (this.remoteFeatures[`chsh`] && - this.remoteFeatures[`bash`]) { + // give user option to set bash as default shell. + if (this.remoteFeatures[`bash`]) { + try { + //check users default shell const bashShellPath = '/QOpenSys/pkgs/bin/bash'; const commandShellResult = await this.sendCommand({ command: `echo $SHELL` }); if (!commandShellResult.stderr) { - let userDefaultShell = commandShellResult.stdout.trim(); - if (userDefaultShell !== bashShellPath) { - - vscode.window.showInformationMessage(`IBM recommends using bash as your default shell.`, `Set shell to bash`, `Read More`,).then(async choice => { - switch (choice) { - case `Set shell to bash`: - const commandSetBashResult = await this.sendCommand({ - command: `/QOpenSys/pkgs/bin/chsh -s /QOpenSys/pkgs/bin/bash` - }); - - if (!commandSetBashResult.stderr) { - vscode.window.showInformationMessage(`Shell is now bash! Reconnect for change to take effect.`); - } else { - vscode.window.showInformationMessage(`Default shell WAS NOT changed to bash.`); - } - break; + let usesBash = commandShellResult.stdout.trim() === bashShellPath; + if (!usesBash) { + // make sure chsh is installed + if (this.remoteFeatures[`chsh`]) { + vscode.window.showInformationMessage(`IBM recommends using bash as your default shell.`, `Set shell to bash`, `Read More`,).then(async choice => { + switch (choice) { + case `Set shell to bash`: + const commandSetBashResult = await this.sendCommand({ + command: `/QOpenSys/pkgs/bin/chsh -s /QOpenSys/pkgs/bin/bash` + }); + + if (!commandSetBashResult.stderr) { + vscode.window.showInformationMessage(`Shell is now bash! Reconnect for change to take effect.`); + usesBash = true; + } else { + vscode.window.showInformationMessage(`Default shell WAS NOT changed to bash.`); + } + break; - case `Read More`: - vscode.env.openExternal(vscode.Uri.parse(`https://ibmi-oss-docs.readthedocs.io/en/latest/user_setup/README.html#step-4-change-your-default-shell-to-bash`)); - break; - } + case `Read More`: + vscode.env.openExternal(vscode.Uri.parse(`https://ibmi-oss-docs.readthedocs.io/en/latest/user_setup/README.html#step-4-change-your-default-shell-to-bash`)); + break; + } + }); + } + } + + if (usesBash) { + //Ensure /QOpenSys/pkgs/bin is found in $PATH + progress.report({ + message: `Checking /QOpenSys/pkgs/bin in $PATH.` }); + + if ((!quickConnect || !cachedServerSettings?.pathChecked)) { + const currentPaths = (await this.sendCommand({ command: "echo $PATH" })).stdout.split(":"); + const bashrcFile = `${defaultHomeDir}/.bashrc`; + let bashrcExists = (await this.sendCommand({ command: `test -e ${bashrcFile}` })).code === 0; + let reason; + if(!currentPaths.includes("/QOpenSys/pkgs/bin")){ + reason = "Your $PATH shell environment variable does not include /QOpenSys/pkgs/bin"; + } + else if (currentPaths.indexOf("/QOpenSys/pkgs/bin") > 0){ + reason = "/QOpenSys/pkgs/bin is not in first position in your $PATH shell environment variable"; + } + if (reason && await vscode.window.showWarningMessage(`/QOpenSys/pkgs/bin not found in $PATH`, { + modal: true, + detail: `${reason}, so Code for IBM i may not function correctly. Would you like to ${bashrcExists ? "update" : "create"} ${bashrcFile} to fix this now?`, + }, `Yes`)) { + delayedOperations.push(async () => { + console.log(`${bashrcExists ? "update" : "create"} ${bashrcFile}`); + if (!bashrcExists) { + const createBashrc = await this.sendCommand({ command: `echo "# Generated by Code for IBM i\nexport PATH=/QOpenSys/pkgs/bin:\\$PATH" >> ${bashrcFile} && chown ${connectionObject.username.toLowerCase()} ${bashrcFile} && chmod 755 ${bashrcFile}` }); + if (createBashrc.code !== 0) { + await vscode.window.showWarningMessage(`Error creating ${bashrcFile}):\n${createBashrc.stderr}.\n\n Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: true }); + } + } + else { + try { + const content = instance.getContent(); + if (content) { + const bashrcContent = (await content.downloadStreamfile(bashrcFile)).split("\n"); + let replaced = false; + bashrcContent.forEach((line, index) => { + if (!replaced) { + const pathRegex = /^((?:export )?PATH=)(.*)(?:)$/.exec(line); + if (pathRegex) { + bashrcContent[index] = `${pathRegex[1]}/QOpenSys/pkgs/bin:${pathRegex[2] + .replace("/QOpenSys/pkgs/bin", "") //Removes /QOpenSys/pkgs/bin wherever it is + .replace("::", ":")}`; //Removes double : in case /QOpenSys/pkgs/bin wasn't at the end + replaced = true; + } + } + }); + + if (!replaced) { + bashrcContent.push( + "", + "# Generated by Code for IBM i", + "export PATH=/QOpenSys/pkgs/bin:$PATH" + ); + } + + await content.writeStreamfile(bashrcFile, bashrcContent.join("\n")); + } + } + catch (error) { + await vscode.window.showWarningMessage(`Error modifying PATH in ${bashrcFile}):\n${error}.\n\n Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: true }); + } + } + }); + } + } } } + } catch (e) { + // Oh well...trying to set default shell is not worth stopping for. + console.log(e); } - } catch (e) { - // Oh well...trying to set default shell is not worth stopping for. - console.log(e); } if (this.config.autoConvertIFSccsid) { From fc33972a85156014603d0c3fba3585d95181b6bb Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Tue, 12 Dec 2023 13:06:48 +0100 Subject: [PATCH 4/5] Log to output Signed-off-by: Seb Julliand --- src/api/IBMi.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index 778dda3fe..7bad5da76 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -255,7 +255,7 @@ export default class IBMi { modal: true, detail: `Your home directory (${actualHomeDir}) does not exist, so Code for IBM i may not function correctly. Would you like to create this directory now?`, }, `Yes`)) { - console.log(`creating home directory ${actualHomeDir}`); + this.appendOutput(`creating home directory ${actualHomeDir}`); let mkHomeCmd = `mkdir -p ${actualHomeDir} && chown ${connectionObject.username.toLowerCase()} ${actualHomeDir} && chmod 0755 ${actualHomeDir}`; let mkHomeResult = await this.sendCommand({ command: mkHomeCmd, directory: `.` }); if (0 === mkHomeResult.code) { @@ -752,7 +752,7 @@ export default class IBMi { detail: `${reason}, so Code for IBM i may not function correctly. Would you like to ${bashrcExists ? "update" : "create"} ${bashrcFile} to fix this now?`, }, `Yes`)) { delayedOperations.push(async () => { - console.log(`${bashrcExists ? "update" : "create"} ${bashrcFile}`); + this.appendOutput(`${bashrcExists ? "update" : "create"} ${bashrcFile}`); if (!bashrcExists) { const createBashrc = await this.sendCommand({ command: `echo "# Generated by Code for IBM i\nexport PATH=/QOpenSys/pkgs/bin:\\$PATH" >> ${bashrcFile} && chown ${connectionObject.username.toLowerCase()} ${bashrcFile} && chmod 755 ${bashrcFile}` }); if (createBashrc.code !== 0) { From 60a29c03952a653fce9bf1571c8eabfe9d3ed0e3 Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Tue, 12 Dec 2023 13:09:28 +0100 Subject: [PATCH 5/5] Check if /QOpenSys/pkgs/bin is in the right position in PATH Namely if it appears after /usr/bin or /QOpenSys/usr/bin Signed-off-by: Seb Julliand --- src/api/IBMi.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index 7bad5da76..5d7bb608f 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -744,8 +744,8 @@ export default class IBMi { if(!currentPaths.includes("/QOpenSys/pkgs/bin")){ reason = "Your $PATH shell environment variable does not include /QOpenSys/pkgs/bin"; } - else if (currentPaths.indexOf("/QOpenSys/pkgs/bin") > 0){ - reason = "/QOpenSys/pkgs/bin is not in first position in your $PATH shell environment variable"; + else if (currentPaths.indexOf("/QOpenSys/pkgs/bin") > currentPaths.indexOf("/usr/bin") || currentPaths.indexOf("/QOpenSys/pkgs/bin") > currentPaths.indexOf("/QOpenSys/usr/bin")){ + reason = "/QOpenSys/pkgs/bin is not in the right position in your $PATH shell environment variable"; } if (reason && await vscode.window.showWarningMessage(`/QOpenSys/pkgs/bin not found in $PATH`, { modal: true,