|
| 1 | +import cp from "child_process"; |
1 | 2 | import log from "electron-log";
|
2 | 3 | import psList from "ps-list";
|
| 4 | +import { IS_FLATPAK } from "main/constants"; |
3 | 5 |
|
4 |
| -export async function taskRunning(task: string): Promise<boolean> { |
5 |
| - try { |
6 |
| - const processes = await psList(); |
7 |
| - return processes.some(process => process.name?.includes(task) || process.cmd?.includes(task)); |
| 6 | +// There are 2 erroneous lines ps | grep which is both the ps and grep calls themselves |
| 7 | +const MIN_PROCESS_COUNT_LINUX = 2; |
| 8 | + |
| 9 | +type LinuxOptions = { |
| 10 | + // Add the prefix to the command |
| 11 | + // eg. command - "./Beat Saber.exe" --no-yeet, prefix - "path/to/proton" run |
| 12 | + // = "path/to/proton" run "./Beat Saber.exe" --no-yeet |
| 13 | + prefix: string; |
| 14 | +}; |
| 15 | + |
| 16 | +// Only applied if package as flatpak |
| 17 | +type FlatpakOptions = { |
| 18 | + // Force to use "flatpak-spawn --host" to run commands outside of the sandbox |
| 19 | + host: boolean; |
| 20 | + // Only copy the keys from options.env from bsmSpawn/bsmExec |
| 21 | + env?: string[]; |
| 22 | +}; |
| 23 | + |
| 24 | +export type BsmSpawnOptions = { |
| 25 | + args?: string[]; |
| 26 | + options?: cp.SpawnOptions; |
| 27 | + log?: boolean; |
| 28 | + linux?: LinuxOptions; |
| 29 | + flatpak?: FlatpakOptions; |
| 30 | +}; |
| 31 | + |
| 32 | +export type BsmExecOptions = { |
| 33 | + args?: string[]; |
| 34 | + options?: cp.ExecOptions; |
| 35 | + log?: boolean; |
| 36 | + linux?: LinuxOptions; |
| 37 | + flatpak?: FlatpakOptions; |
| 38 | +}; |
| 39 | + |
| 40 | +function updateCommand(command: string, options: BsmSpawnOptions) { |
| 41 | + if (options?.args) { |
| 42 | + command += ` ${options.args.join(" ")}`; |
| 43 | + } |
| 44 | + |
| 45 | + if (process.platform === "linux") { |
| 46 | + // "/bin/sh" does not see flatpak-spawn |
| 47 | + // Most Debian and Arch should also support "/bin/bash" |
| 48 | + options.options.shell = "/bin/bash"; |
| 49 | + |
| 50 | + if (options.linux?.prefix) { |
| 51 | + command = `${options.linux.prefix} ${command}`; |
| 52 | + } |
| 53 | + |
| 54 | + if (options?.flatpak?.host) { |
| 55 | + const envArgs = (options?.flatpak?.env && options?.options?.env) |
| 56 | + && options.flatpak.env |
| 57 | + .filter(envName => options.options.env[envName]) |
| 58 | + .map(envName => |
| 59 | + `--env=${envName}="${options.options.env[envName]}"` |
| 60 | + ) |
| 61 | + .join(" "); |
| 62 | + command = `flatpak-spawn --host ${envArgs || ""} ${command}`; |
| 63 | + } |
8 | 64 | }
|
9 |
| - catch(error){ |
| 65 | + |
| 66 | + return command; |
| 67 | +} |
| 68 | + |
| 69 | +export function bsmSpawn(command: string, options?: BsmSpawnOptions) { |
| 70 | + options = options || {}; |
| 71 | + options.options = options.options || {}; |
| 72 | + command = updateCommand(command, options); |
| 73 | + |
| 74 | + if (options?.log) { |
| 75 | + log.info(process.platform === "win32" ? "Windows" : "Linux", "spawn command\n>", command); |
| 76 | + } |
| 77 | + |
| 78 | + return cp.spawn(command, options.options); |
| 79 | +} |
| 80 | + |
| 81 | +export function bsmExec(command: string, options?: BsmExecOptions): Promise<{ |
| 82 | + stdout: string; |
| 83 | + stderr: string; |
| 84 | +}> { |
| 85 | + options = options || {}; |
| 86 | + options.options = options.options || {}; |
| 87 | + command = updateCommand(command, options); |
| 88 | + |
| 89 | + if (options?.log) { |
| 90 | + log.info( |
| 91 | + process.platform === "win32" ? "Windows" : "Linux", |
| 92 | + "exec command\n>", command |
| 93 | + ); |
| 94 | + } |
| 95 | + |
| 96 | + return new Promise((resolve, reject) => { |
| 97 | + cp.exec(command, options?.options || {}, (error: Error, stdout: string, stderr: string) => { |
| 98 | + if (error) { return reject(error); } |
| 99 | + resolve({ stdout, stderr }); |
| 100 | + }); |
| 101 | + }) |
| 102 | +} |
| 103 | + |
| 104 | +async function isProcessRunningLinux(name: string): Promise<boolean> { |
| 105 | + try { |
| 106 | + const { stdout: count } = await bsmExec(`ps awwxo args | grep -c "${name}"`, { |
| 107 | + log: true, |
| 108 | + flatpak: { host: IS_FLATPAK }, |
| 109 | + }); |
| 110 | + |
| 111 | + return +count.trim() > MIN_PROCESS_COUNT_LINUX; |
| 112 | + } catch(error) { |
10 | 113 | log.error(error);
|
11 | 114 | return false;
|
12 |
| - } |
| 115 | + }; |
13 | 116 | }
|
14 | 117 |
|
15 |
| -export async function getProcessPid(task: string): Promise<number> { |
| 118 | +async function getProcessIdWindows(name: string): Promise<number | null> { |
16 | 119 | try {
|
17 | 120 | const processes = await psList();
|
18 |
| - const process = processes.find(process => process.name?.includes(task) || process.cmd?.includes(task)); |
| 121 | + const process = processes.find(process => process.name?.includes(name) || process.cmd?.includes(name)); |
19 | 122 | return process?.pid;
|
20 |
| - } |
21 |
| - catch(error){ |
| 123 | + } catch (error) { |
22 | 124 | log.error(error);
|
23 | 125 | return null;
|
24 | 126 | }
|
25 | 127 | }
|
| 128 | + |
| 129 | +export const isProcessRunning = process.platform === "win32" |
| 130 | + ? isProcessRunningWindows |
| 131 | + : isProcessRunningLinux; |
| 132 | + |
| 133 | +async function isProcessRunningWindows(name: string): Promise<boolean> { |
| 134 | + try { |
| 135 | + const processes = await psList(); |
| 136 | + return processes.some(process => |
| 137 | + process.name?.includes(name) || process.cmd?.includes(name) |
| 138 | + ); |
| 139 | + } catch (error) { |
| 140 | + log.error(error); |
| 141 | + return false; |
| 142 | + } |
| 143 | +} |
| 144 | + |
| 145 | +async function getProcessIdLinux(name: string): Promise<number | null> { |
| 146 | + try { |
| 147 | + const { stdout } = await bsmExec(`ps awwxo pid,args | grep "${name}"`, { |
| 148 | + log: true, |
| 149 | + flatpak: { host: IS_FLATPAK }, |
| 150 | + }); |
| 151 | + |
| 152 | + const line = stdout.split("\n") |
| 153 | + .slice(0, -MIN_PROCESS_COUNT_LINUX) |
| 154 | + .map(line => line.trimStart()) |
| 155 | + .find(line => line.includes(name) && !line.includes("grep")); |
| 156 | + return line ? +line.split(" ").at(0) : null; |
| 157 | + } catch(error) { |
| 158 | + log.error(error); |
| 159 | + return null; |
| 160 | + }; |
| 161 | +} |
| 162 | + |
| 163 | +export const getProcessId = process.platform === "win32" |
| 164 | + ? getProcessIdWindows |
| 165 | + : getProcessIdLinux; |
| 166 | + |
0 commit comments