Implemented 'kill by PID' functionality

This commit is contained in:
danielyxie 2019-07-11 19:37:17 -07:00
parent d94516f39b
commit 4f2f75762c
14 changed files with 191 additions and 86 deletions

@ -204,8 +204,8 @@ The list contains the name of (i.e. the value returned by
| | | the string, the result should be an array with only an empty string. | | | | the string, the result should be an array with only an empty string. |
| | | | | | | |
| | | Examples: | | | | Examples: |
| | | ()())() -> [()()(), (())()] | | | | ()())() -> [()()(), (())()] |
| | | (a)())() -> [(a)()(), (a())()] | | | | (a)())() -> [(a)()(), (a())()] |
| | | )( -> [""] | | | | )( -> [""] |
+------------------------------------+------------------------------------------------------------------------------------------+ +------------------------------------+------------------------------------------------------------------------------------------+
| Find All Valid Math Expressions | | You are given a string which contains only digits between 0 and 9 as well as a target | | Find All Valid Math Expressions | | You are given a string which contains only digits between 0 and 9 as well as a target |
@ -218,8 +218,8 @@ The list contains the name of (i.e. the value returned by
| | | | | | | |
| | | Examples: | | | | Examples: |
| | | Input: digits = "123", target = 6 | | | | Input: digits = "123", target = 6 |
| | | Output: [1+2+3, 1*2*3] | | | | Output: [1+2+3, 1*2*3] |
| | | | | | | |
| | | Input: digits = "105", target = 5 | | | | Input: digits = "105", target = 5 |
| | | Output: [1*0+5, 10-5] | | | | Output: [1*0+5, 10-5] |
+------------------------------------+------------------------------------------------------------------------------------------+ +------------------------------------+------------------------------------------------------------------------------------------+

@ -312,9 +312,12 @@ kill
^^^^ ^^^^
$ kill [script name] [args...] $ kill [script name] [args...]
$ kill [pid]
Kill the script specified by the script name and arguments. Each argument must Kill the script specified by the script filename and arguments OR by its PID.
be separated by a space. Remember that a running script is uniquely identified
If you are killing the script using its filename and arguments, then each argument
must be separated by a space. Remember that a running script is uniquely identified
by both its name and the arguments that are used to start it. So, if a script by both its name and the arguments that are used to start it. So, if a script
was ran with the following arguments:: was ran with the following arguments::
@ -324,8 +327,7 @@ Then to kill this script the same arguments would have to be used::
$ kill foo.script 50e3 sigma-cosmetics $ kill foo.script 50e3 sigma-cosmetics
Note that after issuing the 'kill' command for a script, it may take a few seconds for If you are killing the script using its PID, then the PID argument must be numeric.
the script to actually stop running.
killall killall
^^^^^^^ ^^^^^^^

@ -14,10 +14,15 @@ exec() Netscript Function
Run a script as a separate process on a specified server. This is similar to the *run* function except Run a script as a separate process on a specified server. This is similar to the *run* function except
that it can be used to run a script on any server, instead of just the current server. that it can be used to run a script on any server, instead of just the current server.
Returns true if the script is successfully started, and false otherwise. If the script was successfully started, then this functions returns the PID
of that script. Otherwise, it returns 0.
Running this function with a *numThreads* argument of 0 will return false without running the script. .. note:: PID stands for Process ID. The PID is a unique identifier for each script.
However, running this function with a negative *numThreads* argument will cause a runtime error. The PID will always be a positive integer.
.. warning:: Running this function with a *numThreads* argument of 0 will return 0 without
running the script. However, running this function with a negative *numThreads*
argument will cause a runtime error.
The simplest way to use the *exec* command is to call it with just the script name and the target server. The simplest way to use the *exec* command is to call it with just the script name and the target server.
The following example will try to run *generic-hack.script* on the *foodnstuff* server:: The following example will try to run *generic-hack.script* on the *foodnstuff* server::

@ -27,3 +27,22 @@ kill() Netscript Function
The following will try to kill a script named *foo.script* on the current server that was ran with the arguments 1 and "foodnstuff":: The following will try to kill a script named *foo.script* on the current server that was ran with the arguments 1 and "foodnstuff"::
kill("foo.script", getHostname(), 1, "foodnstuff"); kill("foo.script", getHostname(), 1, "foodnstuff");
.. js:function:: kill(scriptPid)
:param number scriptPid: PID of the script to kill
:RAM cost: 0.5 GB
Kills the script with the specified PID. Killing a script by its PID will typically
have better performance, especially if you have many scripts running.
If this function successfully kills the specified script, then it will return true.
Otherwise, it will return false.
*Examples:*
The following example will try to kill the script with the PID 10::
if (kill(10)) {
print("Killed script with PID 10!");
}

@ -13,10 +13,15 @@ run() Netscript Function
Run a script as a separate process. This function can only be used to run scripts located on the current server (the server Run a script as a separate process. This function can only be used to run scripts located on the current server (the server
running the script that calls this function). running the script that calls this function).
Returns true if the script is successfully started, and false otherwise. If the script was successfully started, then this functions returns the PID
of that script. Otherwise, it returns 0.
Running this function with a *numThreads* argument of 0 will return false without running the script. .. note:: PID stands for Process ID. The PID is a unique identifier for each script.
However, running this function with a negative *numThreads* argument will cause a runtime error. The PID will always be a positive integer.
.. warning:: Running this function with a *numThreads* argument of 0 will return 0 without
running the script. However, running this function with a negative *numThreads*
argument will cause a runtime error.
The simplest way to use the *run* command is to call it with just the script name. The following example will run The simplest way to use the *run* command is to call it with just the script name. The following example will run
'foo.script' single-threaded with no arguments:: 'foo.script' single-threaded with no arguments::

@ -51,8 +51,6 @@ Here is a summary of all rules you need to follow when writing Netscript JS code
* grow * grow
* weaken * weaken
* sleep * sleep
* run
* exec
* prompt * prompt
* wget * wget

@ -230,10 +230,17 @@ export let CONSTANTS: IMap<any> = {
* Bug fix: workForFaction() function now properly accounts for disabled logs * Bug fix: workForFaction() function now properly accounts for disabled logs
* When writing to a file, the write() function now casts the data being written to a string (using String()) * When writing to a file, the write() function now casts the data being written to a string (using String())
* BitNode-selection page now shows what Source-File level you have for each BitNode * BitNode-selection page now shows what Source-File level you have for each BitNode
* Overloaded kill() function so that you can kill a script by its PID
* spawn() now only takes 10 seconds to run (decreased from 20 seconds)
* run() and exec() now return the PID of the newly-executed scripts, rather than a boolean
** (A PID is just a positive integer)
* run(), exec(), and spawn() no longer need to be await-ed in NetscriptJS
Misc Changes Misc Changes
* The 'kill' Terminal command can now kill a script by its PID
* Added 'Solarized Dark' theme to CodeMirror editor * Added 'Solarized Dark' theme to CodeMirror editor
* After Infiltration, you will now return to the company page rather than the city page * After Infiltration, you will now return to the company page rather than the city page
* Bug fix: Stock Market UI should no longer crash for certain locale settings * Bug fix: Stock Market UI should no longer crash for certain locale settings
* Bug fix: You can now properly remove unfinished programs (the *.exe-N%-INC files)
` `
} }

@ -293,7 +293,7 @@ function NetscriptFunctions(workerScript) {
if (fn != null && typeof fn === "string") { if (fn != null && typeof fn === "string") {
// Get Logs of another script // Get Logs of another script
if (ip == null) { ip = workerScript.serverIp; } if (ip == null) { ip = workerScript.serverIp; }
const server = getServer(ip, callingFnName); const server = safeGetServer(ip, callingFnName);
return findRunningScript(fn, scriptArgs, server); return findRunningScript(fn, scriptArgs, server);
} }
@ -973,6 +973,8 @@ function NetscriptFunctions(workerScript) {
if (scriptname == null || threads == null) { if (scriptname == null || threads == null) {
throw makeRuntimeRejectMsg(workerScript, "Invalid scriptname or numThreads argument passed to spawn()"); throw makeRuntimeRejectMsg(workerScript, "Invalid scriptname or numThreads argument passed to spawn()");
} }
const spawnDelay = 10;
setTimeoutRef(() => { setTimeoutRef(() => {
if (scriptname === undefined) { if (scriptname === undefined) {
throw makeRuntimeRejectMsg(workerScript, "spawn() call has incorrect number of arguments. Usage: spawn(scriptname, numThreads, [arg1], [arg2]...)"); throw makeRuntimeRejectMsg(workerScript, "spawn() call has incorrect number of arguments. Usage: spawn(scriptname, numThreads, [arg1], [arg2]...)");
@ -990,40 +992,52 @@ function NetscriptFunctions(workerScript) {
} }
return runScriptFromScript(scriptServer, scriptname, argsForNewScript, workerScript, threads); return runScriptFromScript(scriptServer, scriptname, argsForNewScript, workerScript, threads);
}, 20e3); }, spawnDelay * 1e3);
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.spawn == null) { if (workerScript.shouldLog("spawn")) {
workerScript.scriptRef.log("spawn() will execute " + scriptname + " in 20 seconds"); workerScript.scriptRef.log(`spawn() will execute ${scriptname} in ${spawnDelay} seconds`);
} }
NetscriptFunctions(workerScript).exit(); NetscriptFunctions(workerScript).exit();
}, },
kill: function(filename, ip) { kill: function(filename, ip, ...scriptArgs) {
updateDynamicRam("kill", getRamCost("kill")); updateDynamicRam("kill", getRamCost("kill"));
if (filename === undefined || ip === undefined) {
throw makeRuntimeRejectMsg(workerScript, "kill() call has incorrect number of arguments. Usage: kill(scriptname, server, [arg1], [arg2]...)"); let res;
const killByPid = (typeof filename === "number");
if (killByPid) {
// Kill by pid
res = killWorkerScript(filename);
} else {
// Kill by filename/ip
if (filename === undefined || ip === undefined) {
throw makeRuntimeRejectMsg(workerScript, "kill() call has incorrect number of arguments. Usage: kill(scriptname, server, [arg1], [arg2]...)");
}
const server = safeGetServer(ip);
const runningScriptObj = getRunningScript(filename, ip, "kill", scriptArgs);
if (runningScriptObj == null) {
workerScript.log(`tail() failed. ${getCannotFindRunningScriptErrorMessage(filename, ip, scriptArgs)}`)
return false;
}
res = killWorkerScript(runningScriptObj, server.ip);
} }
var server = getServer(ip);
if (server == null) {
workerScript.scriptRef.log("kill() failed. Invalid IP or hostname passed in: " + ip);
throw makeRuntimeRejectMsg(workerScript, "kill() failed. Invalid IP or hostname passed in: " + ip);
}
var argsForKillTarget = [];
for (var i = 2; i < arguments.length; ++i) {
argsForKillTarget.push(arguments[i]);
}
var runningScriptObj = findRunningScript(filename, argsForKillTarget, server);
if (runningScriptObj == null) {
workerScript.scriptRef.log("kill() failed. No such script "+ filename + " on " + server.hostname + " with args: " + arrayToString(argsForKillTarget));
return false;
}
var res = killWorkerScript(runningScriptObj, server.ip);
if (res) { if (res) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.kill == null) { if (workerScript.shouldLog("kill")) {
workerScript.scriptRef.log("Killing " + filename + " on " + server.hostname + " with args: " + arrayToString(argsForKillTarget) + ". May take up to a few minutes for the scripts to die..."); if (killByPid) {
workerScript.log(`Killing script with PID ${filename}`);
} else {
workerScript.log(`Killing ${filename} on ${ip} with args: ${arrayToString(scriptArgs)}.`);
}
} }
return true; return true;
} else { } else {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.kill == null) { if (workerScript.shouldLog("kill")) {
workerScript.scriptRef.log("kill() failed. No such script "+ filename + " on " + server.hostname + " with args: " + arrayToString(argsForKillTarget)); if (killByPid) {
workerScript.log(`kill() failed. No such script with PID ${filename}`);
} else {
workerScript.log(`kill() failed. No such script ${filename} on ${ip} with args: ${arrayToString(scriptArgs)}`);
}
} }
return false; return false;
} }

@ -605,42 +605,53 @@ export function loadAllRunningScripts() {
* Run a script from inside another script (run(), exec(), spawn(), etc.) * Run a script from inside another script (run(), exec(), spawn(), etc.)
*/ */
export function runScriptFromScript(server, scriptname, args, workerScript, threads=1) { export function runScriptFromScript(server, scriptname, args, workerScript, threads=1) {
//Check if the script is already running // Sanitize arguments
let runningScriptObj = findRunningScript(scriptname, args, server); if (!(workerScript instanceof WorkerScript)) {
if (runningScriptObj != null) { return 0;
workerScript.scriptRef.log(scriptname + " is already running on " + server.hostname);
return Promise.resolve(false);
} }
//'null/undefined' arguments are not allowed if (typeof scriptname !== "string" || !Array.isArray(args)) {
workerScript.log(`ERROR: runScriptFromScript() failed due to invalid arguments`);
console.error(`runScriptFromScript() failed due to invalid arguments`);
return 0;
}
// Check if the script is already running
let runningScriptObj = server.getRunningScript(scriptname, args);
if (runningScriptObj != null) {
workerScript.log(`${scriptname} is already running on ${server.hostname}`);
return 0;
}
// 'null/undefined' arguments are not allowed
for (let i = 0; i < args.length; ++i) { for (let i = 0; i < args.length; ++i) {
if (args[i] == null) { if (args[i] == null) {
workerScript.scriptRef.log("ERROR: Cannot execute a script with null/undefined as an argument"); workerScript.log("ERROR: Cannot execute a script with null/undefined as an argument");
return Promise.resolve(false); return 0;
} }
} }
//Check if the script exists and if it does run it // Check if the script exists and if it does run it
for (let i = 0; i < server.scripts.length; ++i) { for (let i = 0; i < server.scripts.length; ++i) {
if (server.scripts[i].filename == scriptname) { if (server.scripts[i].filename === scriptname) {
//Check for admin rights and that there is enough RAM availble to run // Check for admin rights and that there is enough RAM availble to run
var script = server.scripts[i]; const script = server.scripts[i];
var ramUsage = script.ramUsage; let ramUsage = script.ramUsage;
threads = Math.round(Number(threads)); //Convert to number and round threads = Math.round(Number(threads));
if (threads === 0) { return Promise.resolve(false); } if (threads === 0) { return 0; }
ramUsage = ramUsage * threads; ramUsage = ramUsage * threads;
var ramAvailable = server.maxRam - server.ramUsed; const ramAvailable = server.maxRam - server.ramUsed;
if (server.hasAdminRights == false) { if (server.hasAdminRights == false) {
workerScript.scriptRef.log("Cannot run script " + scriptname + " on " + server.hostname + " because you do not have root access!"); workerScript.log(`Cannot run script ${scriptname} on ${server.hostname} because you do not have root access!`);
return Promise.resolve(false); return 0;
} else if (ramUsage > ramAvailable){ } else if (ramUsage > ramAvailable){
workerScript.scriptRef.log("Cannot run script " + scriptname + "(t=" + threads + ") on " + server.hostname + " because there is not enough available RAM!"); workerScript.log(`Cannot run script ${scriptname} (t=${threads}) on ${server.hostname} because there is not enough available RAM!`);
return Promise.resolve(false); return 0;
} else { } else {
//Able to run script // Able to run script
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.exec == null && workerScript.disableLogs.run == null && workerScript.disableLogs.spawn == null) { if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.exec == null && workerScript.disableLogs.run == null && workerScript.disableLogs.spawn == null) {
workerScript.scriptRef.log(`Running script: ${scriptname} on ${server.hostname} with ${threads} threads and args: ${arrayToString(args)}. May take a few seconds to start up...`); workerScript.log(`Running script: ${scriptname} on ${server.hostname} with ${threads} threads and args: ${arrayToString(args)}.`);
} }
let runningScriptObj = new RunningScript(script, args); let runningScriptObj = new RunningScript(script, args);
runningScriptObj.threads = threads; runningScriptObj.threads = threads;
@ -649,10 +660,14 @@ export function runScriptFromScript(server, scriptname, args, workerScript, thre
// Push onto runningScripts. // Push onto runningScripts.
// This has to come after addWorkerScript() because that fn updates RAM usage // This has to come after addWorkerScript() because that fn updates RAM usage
server.runScript(runningScriptObj, Player.hacknet_node_money_mult); server.runScript(runningScriptObj, Player.hacknet_node_money_mult);
return Promise.resolve(true);
// Once the WorkerScript is constructed in addWorkerScript(), the RunningScript
// object should have a PID assigned to it, so we return that
return runningScriptObj.pid;
} }
} }
} }
workerScript.scriptRef.log("Could not find script " + scriptname + " on " + server.hostname);
return Promise.resolve(false); workerScript.log(`Could not find script ${scriptname} on ${server.hostname}`);
return 0;
} }

@ -12,6 +12,7 @@ import { IReturnStatus } from "../types";
import { isScriptFilename } from "../Script/ScriptHelpersTS"; import { isScriptFilename } from "../Script/ScriptHelpersTS";
import { createRandomIp } from "../../utils/IPAddress"; import { createRandomIp } from "../../utils/IPAddress";
import { compareArrays } from "../../utils/helpers/compareArrays";
interface IConstructorParams { interface IConstructorParams {
adminRights?: boolean; adminRights?: boolean;
@ -113,8 +114,27 @@ export class BaseServer {
return null; return null;
} }
// Given the name of the script, returns the corresponding /**
// script object on the server (if it exists) * Find an actively running script on this server
* @param scriptName - Filename of script to search for
* @param scriptArgs - Arguments that script is being run with
* @returns RunningScript for the specified active script
* Returns null if no such script can be found
*/
getRunningScript(scriptName: string, scriptArgs: any[]): RunningScript | null {
for (let rs of this.runningScripts) {
if (rs.filename === scriptName && compareArrays(rs.args, scriptArgs)) {
return rs;
}
}
return null;
}
/**
* Given the name of the script, returns the corresponding
* Script object on the server (if it exists)
*/
getScript(scriptName: string): Script | null { getScript(scriptName: string): Script | null {
for (let i = 0; i < this.scripts.length; i++) { for (let i = 0; i < this.scripts.length; i++) {
if (this.scripts[i].filename === scriptName) { if (this.scripts[i].filename === scriptName) {
@ -129,7 +149,6 @@ export class BaseServer {
* Returns boolean indicating whether the given script is running on this server * Returns boolean indicating whether the given script is running on this server
*/ */
isRunning(fn: string): boolean { isRunning(fn: string): boolean {
// Check that the script isnt currently running
for (const runningScriptObj of this.runningScripts) { for (const runningScriptObj of this.runningScripts) {
if (runningScriptObj.filename === fn) { if (runningScriptObj.filename === fn) {
return true; return true;
@ -157,7 +176,7 @@ export class BaseServer {
* @returns {IReturnStatus} Return status object indicating whether or not file was deleted * @returns {IReturnStatus} Return status object indicating whether or not file was deleted
*/ */
removeFile(fn: string): IReturnStatus { removeFile(fn: string): IReturnStatus {
if (fn.endsWith(".exe")) { if (fn.endsWith(".exe") || fn.match(/^.+\.exe-\d+(?:\.\d*)?%-INC$/) != null) {
for (let i = 0; i < this.programs.length; ++i) { for (let i = 0; i < this.programs.length; ++i) {
if (this.programs[i] === fn) { if (this.programs[i] === fn) {
this.programs.splice(i, 1); this.programs.splice(i, 1);

@ -1150,7 +1150,7 @@ let Terminal = {
killWorkerScript(s.runningScripts[i], s.ip, false); killWorkerScript(s.runningScripts[i], s.ip, false);
} }
WorkerScriptStartStopEventEmitter.emitEvent(); WorkerScriptStartStopEventEmitter.emitEvent();
post("Killing all running scripts. May take up to a few minutes for the scripts to die..."); post("Killing all running scripts");
break; break;
} }
case "ls": { case "ls": {
@ -1573,19 +1573,32 @@ let Terminal = {
return; return;
} }
// Kill by PID
if (typeof commandArray[1] === "number") {
const pid = commandArray[1];
const res = killWorkerScript(pid);
if (res) {
post(`Killing script with PID ${pid}`);
} else {
post(`Failed to kill script with PID ${pid}. No such script exists`);
}
return;
}
const s = Player.getCurrentServer(); const s = Player.getCurrentServer();
const scriptName = Terminal.getFilepath(commandArray[1]); const scriptName = Terminal.getFilepath(commandArray[1]);
const args = []; const args = [];
for (let i = 2; i < commandArray.length; ++i) { for (let i = 2; i < commandArray.length; ++i) {
args.push(commandArray[i]); args.push(commandArray[i]);
} }
const runningScript = findRunningScript(scriptName, args, s); const runningScript = s.getRunningScript(scriptName, args);
if (runningScript == null) { if (runningScript == null) {
postError("No such script is running. Nothing to kill"); postError("No such script is running. Nothing to kill");
return; return;
} }
killWorkerScript(runningScript, s.ip); killWorkerScript(runningScript, s.ip);
post(`Killing ${scriptName}. May take up to a few seconds for the scripts to die...`); post(`Killing ${scriptName}`);
} catch(e) { } catch(e) {
Terminal.postThrownError(e); Terminal.postThrownError(e);
} }
@ -2291,7 +2304,6 @@ let Terminal = {
} else { } else {
// Able to run script // Able to run script
post("Running script with " + numThreads + " thread(s) and args: " + arrayToString(args) + "."); post("Running script with " + numThreads + " thread(s) and args: " + arrayToString(args) + ".");
post("May take a few seconds to start up the process...");
var runningScriptObj = new RunningScript(script, args); var runningScriptObj = new RunningScript(script, args);
runningScriptObj.threads = numThreads; runningScriptObj.threads = numThreads;

@ -36,9 +36,9 @@ export function removeTrailingSlash(s: string): string {
* not the entire filepath * not the entire filepath
*/ */
export function isValidFilename(filename: string): boolean { export function isValidFilename(filename: string): boolean {
// Allows alphanumerics, hyphens, underscores. // Allows alphanumerics, hyphens, underscores, and percentage signs
// Must have a file extension // Must have a file extension
const regex = /^[.a-zA-Z0-9_-]+[.][.a-zA-Z0-9]+$/; const regex = /^[.a-zA-Z0-9_-]+[.][a-zA-Z0-9]+(?:-\d+(?:\.\d*)?%-INC)?$/;
// match() returns null if no match is found // match() returns null if no match is found
return filename.match(regex) != null; return filename.match(regex) != null;
@ -49,7 +49,7 @@ export function isValidFilename(filename: string): boolean {
* not an entire path * not an entire path
*/ */
export function isValidDirectoryName(name: string): boolean { export function isValidDirectoryName(name: string): boolean {
// Allows alphanumerics, hyphens and underscores. // Allows alphanumerics, hyphens, underscores, and percentage signs.
// Name can begin with a single period, but otherwise cannot have any // Name can begin with a single period, but otherwise cannot have any
const regex = /^.?[a-zA-Z0-9_-]+$/; const regex = /^.?[a-zA-Z0-9_-]+$/;

@ -20,7 +20,7 @@ export const TerminalHelpText: string =
"home Connect to home computer<br>" + "home Connect to home computer<br>" +
"hostname Displays the hostname of the machine<br>" + "hostname Displays the hostname of the machine<br>" +
"ifconfig Displays the IP address of the machine<br>" + "ifconfig Displays the IP address of the machine<br>" +
"kill [script] [args...] Stops the specified script on the current server <br>" + "kill [script/pid] [args...] Stops the specified script on the current server <br>" +
"killall Stops all running scripts on the current machine<br>" + "killall Stops all running scripts on the current machine<br>" +
"ls [dir] [| grep pattern] Displays all files on the machine<br>" + "ls [dir] [| grep pattern] Displays all files on the machine<br>" +
"lscpu Displays the number of CPU cores on the machine<br>" + "lscpu Displays the number of CPU cores on the machine<br>" +
@ -128,15 +128,16 @@ export const HelpTexts: IMap<string> = {
ifconfig: "ipconfig<br>" + ifconfig: "ipconfig<br>" +
"Prints the IP address of the current server", "Prints the IP address of the current server",
kill: "kill [script name] [args...]<br>" + kill: "kill [script name] [args...]<br>" +
"Kill the script specified by the script name and arguments. Each argument must be separated by " + "kill [pid]<br>" +
"a space. Remember that a running script is uniquely identified by " + "Kill the script specified by the script name and arguments OR by its PID.<br><br>" +
"both its name and the arguments that are used to start it. So, if a script was ran with the following arguments:<br><br>" + "If you are killing the script using its filename and arguments, then each " +
"argument must be separated by a space. Remember that a running script is " +
"uniquely identified by both its name and the arguments that are used to start " +
"it. So, if a script was ran with the following arguments:<br><br>" +
"run foo.script 1 sigma-cosmetics<br><br>" + "run foo.script 1 sigma-cosmetics<br><br>" +
"Then to kill this script the same arguments would have to be used:<br><br>" + "Then to kill this script the same arguments would have to be used:<br><br>" +
"kill foo.script 1 sigma-cosmetics<br><br>" + "kill foo.script 1 sigma-cosmetics<br><br>" +
"Note that after issuing the 'kill' command for a script, it may take a while for the script to actually stop running. " + "If you are killing the script using its PID, then the PID argument must be numeric",
"This will happen if the script is in the middle of a command such as grow() or weaken() that takes time to execute. " +
"The script will not be stopped/killed until after that time has elapsed.",
killall: "killall<br>" + killall: "killall<br>" +
"Kills all scripts on the current server. " + "Kills all scripts on the current server. " +
"Note that after the 'kill' command is issued for a script, it may take a while for the script to actually stop running. " + "Note that after the 'kill' command is issued for a script, it may take a while for the script to actually stop running. " +
@ -168,7 +169,7 @@ export const HelpTexts: IMap<string> = {
"Move the source file to the specified destination. This can also be used to rename files. " + "Move the source file to the specified destination. This can also be used to rename files. " +
"This command only works for scripts and text files (.txt). This command CANNOT be used to " + "This command only works for scripts and text files (.txt). This command CANNOT be used to " +
"convert to different file types<br><br>" + "convert to different file types<br><br>" +
"Note that, unlike the Linux 'mv' command, the destination argument must be the " + "Note that, unlike the Linux 'mv' command, the destination argument must be the " +
"full filepath. " + "full filepath. " +
"Examples: <br><br>" + "Examples: <br><br>" +
"mv hacking-controller.script scripts/hacking-controller.script<br>" + "mv hacking-controller.script scripts/hacking-controller.script<br>" +

@ -67,6 +67,10 @@ describe("Terminal Directory Tests", function() {
expect(isValidFilename("_foo.lit")).to.equal(true); expect(isValidFilename("_foo.lit")).to.equal(true);
expect(isValidFilename("mult.periods.script")).to.equal(true); expect(isValidFilename("mult.periods.script")).to.equal(true);
expect(isValidFilename("mult.per-iods.again.script")).to.equal(true); expect(isValidFilename("mult.per-iods.again.script")).to.equal(true);
expect(isValidFilename("BruteSSH.exe-50%-INC")).to.equal(true);
expect(isValidFilename("DeepscanV1.exe-1.01%-INC")).to.equal(true);
expect(isValidFilename("DeepscanV2.exe-1.00%-INC")).to.equal(true);
expect(isValidFilename("AutoLink.exe-1.%-INC")).to.equal(true);
}); });
it("should return false for invalid filenames", function() { it("should return false for invalid filenames", function() {
@ -79,6 +83,10 @@ describe("Terminal Directory Tests", function() {
expect(isValidFilename("foo._script")).to.equal(false); expect(isValidFilename("foo._script")).to.equal(false);
expect(isValidFilename("foo.hyphened-ext")).to.equal(false); expect(isValidFilename("foo.hyphened-ext")).to.equal(false);
expect(isValidFilename("")).to.equal(false); expect(isValidFilename("")).to.equal(false);
expect(isValidFilename("AutoLink-1.%-INC.exe")).to.equal(false);
expect(isValidFilename("AutoLink.exe-1.%-INC.exe")).to.equal(false);
expect(isValidFilename("foo%.exe")).to.equal(false);
expect(isValidFilename("-1.00%-INC")).to.equal(false);
}); });
}); });