Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add /QOpenSys/pkgs/bin to .bashrc if needed #1695

Merged
merged 5 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 107 additions & 36 deletions src/api/IBMi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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) {
Expand All @@ -254,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) {
Expand All @@ -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 });
}
}
}
Expand Down Expand Up @@ -691,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") > 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,
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 () => {
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) {
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) {
Expand Down Expand Up @@ -801,6 +870,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");
}

Expand All @@ -814,7 +884,8 @@ export default class IBMi {
local: this.variantChars.local,
},
badDataAreasChecked: true,
libraryListValidated: true
libraryListValidated: true,
pathChecked: true
});

return {
Expand Down
3 changes: 2 additions & 1 deletion src/api/Storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down