Skip to content

Commit

Permalink
Merge pull request #696 from briandowns/feature/processkill
Browse files Browse the repository at this point in the history
Process.kill Function
  • Loading branch information
Jason2605 authored Nov 11, 2023
2 parents 3693c74 + d9101f3 commit 873fa99
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 17 deletions.
16 changes: 16 additions & 0 deletions docs/docs/standard-lib/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,19 @@ If the external process writes to stdout and you wish to capture the output you
Process.run(["ls", "-la"]).unwrap();
print(Process.run(["echo", "test"], true).unwrap()); // 'test'
```

### Process.kill(Number, Number -> Optional) -> Result\<Nil>

kill receives a process ID number and an optional signal number and attempts to kill the process associated with the given pid. If no signal is provided, SIGKILL is used.

```cs
const res = Process.kill(709871);
// 0
```

```cs
const res = Process.kill(709871, Process.SIGTERM).unwrap();
// 0
```

**Note:** On Windows, `kill` only takes the PID as the argument.
20 changes: 9 additions & 11 deletions src/cli/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@

#include "linenoise/linenoise.h"

static int matchStringLiteral(char* line, int i)
{
static int matchStringLiteral(char* line, int i) {
char quote = line[i];

if (quote != '\'' && quote != '"') {
Expand Down Expand Up @@ -62,8 +61,7 @@ static bool matchBraces(char *line) {
return braceLevel == 0;
}

static void memcpyAndAppendNul(char* dst, char* src, int len)
{
static void memcpyAndAppendNul(char* dst, char* src, int len) {
memcpy(dst, src, len);
dst[len] = '\0';
}
Expand Down Expand Up @@ -157,20 +155,20 @@ static void runFile(DictuVM *vm, char *filename) {
}

static const char *const usage[] = {
"dictu [options] [[--] args]",
"dictu [options]",
NULL,
"dictu [options] [[--] args]",
"dictu [options]",
NULL,
};

int main(int argc, char *argv[]) {
int version = 0;
char *cmd = NULL;

struct argparse_option options[] = {
OPT_HELP(),
OPT_BOOLEAN('v', "version", &version, "Display Dictu version"),
OPT_STRING('c', "cmd", &cmd, "Run program passed in as string"),
OPT_END(),
OPT_HELP(),
OPT_BOOLEAN('v', "version", &version, "Display Dictu version"),
OPT_STRING('c', "cmd", &cmd, "Run program passed in as string"),
OPT_END(),
};

struct argparse argparse;
Expand Down
139 changes: 135 additions & 4 deletions src/optionals/process.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
#include <signal.h>
#ifdef _WIN32
#include "windowsapi.h"
#endif

#include "process.h"

#ifdef _WIN32
#define pid_t int
#endif

#ifdef _WIN32
static char* buildArgs(DictuVM *vm, ObjList* list, int *size) {
// 3 for 1st arg escape + null terminator
Expand Down Expand Up @@ -222,7 +231,7 @@ static Value executeReturnOutput(DictuVM* vm, ObjList* argList) {
}
#endif

static Value execNative(DictuVM* vm, int argCount, Value* args) {
static Value execProcess(DictuVM* vm, int argCount, Value* args) {
if (argCount != 1) {
runtimeError(vm, "exec() takes 1 argument (%d given).", argCount);
return EMPTY_VAL;
Expand All @@ -237,7 +246,7 @@ static Value execNative(DictuVM* vm, int argCount, Value* args) {
return execute(vm, argList, false);
}

static Value runNative(DictuVM* vm, int argCount, Value* args) {
static Value runProcess(DictuVM* vm, int argCount, Value* args) {
if (argCount != 1 && argCount != 2) {
runtimeError(vm, "run() takes 1 or 2 arguments (%d given)", argCount);
return EMPTY_VAL;
Expand Down Expand Up @@ -268,6 +277,60 @@ static Value runNative(DictuVM* vm, int argCount, Value* args) {
return execute(vm, argList, true);
}

#ifdef _WIN32
static Value killProcess(DictuVM* vm, int argCount, Value* args) {
if (argCount > 2) {
runtimeError(vm, "kill() takes 1 argument (%d given)", argCount);
return EMPTY_VAL;
}

if (!IS_NUMBER(args[0])) {
runtimeError(vm, "Argument passed to kill() must be a number");
return EMPTY_VAL;
}

pid_t pid = (pid_t)AS_NUMBER(args[0]);

HANDLE handle = OpenProcess(PROCESS_TERMINATE, TRUE, (int)pid);
if (handle != NULL) {
TerminateProcess(handle, 0);
CloseHandle(handle);
}

return newResultSuccess(vm, NIL_VAL);
}
#else
static Value killProcess(DictuVM* vm, int argCount, Value* args) {
if (argCount > 2) {
runtimeError(vm, "kill() takes 1 or 2 arguments (%d given)", argCount);
return EMPTY_VAL;
}

if (!IS_NUMBER(args[0])) {
runtimeError(vm, "First argument passed to kill() must be a number");
return EMPTY_VAL;
}

pid_t pid = (pid_t)AS_NUMBER(args[0]);
int signal = 9;

if (argCount == 2) {
if (!IS_NUMBER(args[1])) {
runtimeError(vm, "Second argument passed to kill() must be a number");
return EMPTY_VAL;
}

signal = AS_NUMBER(args[1]);
}

if (kill(pid, signal) == -1) {
ERROR_RESULT;
}

return newResultSuccess(vm, NIL_VAL);
}
#endif

Value createProcessModule(DictuVM* vm) {
ObjString* name = copyString(vm, "Process", 7);
push(vm, OBJ_VAL(name));
Expand All @@ -277,12 +340,80 @@ Value createProcessModule(DictuVM* vm) {
/**
* Define process methods
*/
defineNative(vm, &module->values, "exec", execNative);
defineNative(vm, &module->values, "run", runNative);
defineNative(vm, &module->values, "exec", execProcess);
defineNative(vm, &module->values, "run", runProcess);
defineNative(vm, &module->values, "kill", killProcess);

/**
* Define process properties
*/
defineNativeProperty(vm, &module->values, "SIGINT", NUMBER_VAL(2));
defineNativeProperty(vm, &module->values, "SIGILL", NUMBER_VAL(4));
defineNativeProperty(vm, &module->values, "SIGFPE", NUMBER_VAL(8));
defineNativeProperty(vm, &module->values, "SIGKILL", NUMBER_VAL(9));
defineNativeProperty(vm, &module->values, "SIGSEGV", NUMBER_VAL(11));
defineNativeProperty(vm, &module->values, "SIGTERM", NUMBER_VAL(15));

#if defined(__Linux__)
defineNativeProperty(vm, &module->values, "SIGHUP", NUMBER_VAL(1));
defineNativeProperty(vm, &module->values, "SIGQUIT", NUMBER_VAL(3));
defineNativeProperty(vm, &module->values, "SIGABRT", NUMBER_VAL(6));
defineNativeProperty(vm, &module->values, "SIGTRAP", NUMBER_VAL(5));
defineNativeProperty(vm, &module->values, "SIGIOT", NUMBER_VAL(6));
defineNativeProperty(vm, &module->values, "SIGBUS", NUMBER_VAL(7));
defineNativeProperty(vm, &module->values, "SIGUSR1", NUMBER_VAL(10));
defineNativeProperty(vm, &module->values, "SIGUSR2", NUMBER_VAL(12));
defineNativeProperty(vm, &module->values, "SIGPIPE", NUMBER_VAL(13));
defineNativeProperty(vm, &module->values, "SIGALRM", NUMBER_VAL(14));
defineNativeProperty(vm, &module->values, "SIGSTKFLT", NUMBER_VAL(16));
defineNativeProperty(vm, &module->values, "SIGCHLD", NUMBER_VAL(17));
defineNativeProperty(vm, &module->values, "SIGCONT", NUMBER_VAL(18));
defineNativeProperty(vm, &module->values, "SIGSTOP", NUMBER_VAL(19));
defineNativeProperty(vm, &module->values, "SIGTSTP", NUMBER_VAL(20));
defineNativeProperty(vm, &module->values, "SIGTTIN", NUMBER_VAL(21));
defineNativeProperty(vm, &module->values, "SIGTTOU", NUMBER_VAL(22));
defineNativeProperty(vm, &module->values, "SIGURG", NUMBER_VAL(23));
defineNativeProperty(vm, &module->values, "SIGXCPU", NUMBER_VAL(24));
defineNativeProperty(vm, &module->values, "SIGXFSZ", NUMBER_VAL(25));
defineNativeProperty(vm, &module->values, "SIGVTALRM", NUMBER_VAL(26));
defineNativeProperty(vm, &module->values, "SIGPROF", NUMBER_VAL(27));
defineNativeProperty(vm, &module->values, "SIGWINCH", NUMBER_VAL(28));
defineNativeProperty(vm, &module->values, "SIGIO", NUMBER_VAL(29));
defineNativeProperty(vm, &module->values, "SIGPWR", NUMBER_VAL(30));
defineNativeProperty(vm, &module->values, "SIGSYS", NUMBER_VAL(31));
defineNativeProperty(vm, &module->values, "SIGUNUSED", NUMBER_VAL(31));
#elif defined(__FreeBSD__) || defined(__APPLE__)
defineNativeProperty(vm, &module->values, "SIGHUP", NUMBER_VAL(1));
defineNativeProperty(vm, &module->values, "SIGQUIT", NUMBER_VAL(3));
defineNativeProperty(vm, &module->values, "SIGTRAP", NUMBER_VAL(5));
defineNativeProperty(vm, &module->values, "SIGABRT", NUMBER_VAL(6));
defineNativeProperty(vm, &module->values, "SIGEMT", NUMBER_VAL(7));
defineNativeProperty(vm, &module->values, "SIGBUS", NUMBER_VAL(10));
defineNativeProperty(vm, &module->values, "SIGSYS", NUMBER_VAL(12));
defineNativeProperty(vm, &module->values, "SIGPIPE", NUMBER_VAL(13));
defineNativeProperty(vm, &module->values, "SIGALRM", NUMBER_VAL(14));
defineNativeProperty(vm, &module->values, "SIGURG", NUMBER_VAL(16));
defineNativeProperty(vm, &module->values, "SIGSTOP", NUMBER_VAL(17));
defineNativeProperty(vm, &module->values, "SIGTSTP", NUMBER_VAL(18));
defineNativeProperty(vm, &module->values, "SIGCONT", NUMBER_VAL(19));
defineNativeProperty(vm, &module->values, "SIGCHLD", NUMBER_VAL(20));
defineNativeProperty(vm, &module->values, "SIGTTIN", NUMBER_VAL(21));
defineNativeProperty(vm, &module->values, "SIGTTOU", NUMBER_VAL(22));
defineNativeProperty(vm, &module->values, "SIGIO", NUMBER_VAL(23));
defineNativeProperty(vm, &module->values, "SIGXCPU", NUMBER_VAL(24));
defineNativeProperty(vm, &module->values, "SIGXFSZ", NUMBER_VAL(25));
defineNativeProperty(vm, &module->values, "SIGVTALRM", NUMBER_VAL(26));
defineNativeProperty(vm, &module->values, "SIGPROF", NUMBER_VAL(27));
defineNativeProperty(vm, &module->values, "SIGWINCH", NUMBER_VAL(28));
defineNativeProperty(vm, &module->values, "SIGINFO", NUMBER_VAL(29));
defineNativeProperty(vm, &module->values, "SIGUSR1", NUMBER_VAL(30));
defineNativeProperty(vm, &module->values, "SIGUSR2", NUMBER_VAL(31));
defineNativeProperty(vm, &module->values, "SIGTHR", NUMBER_VAL(32));
defineNativeProperty(vm, &module->values, "SIGLIBRT", NUMBER_VAL(33));
#elif defined(_WIN32)
defineNativeProperty(vm, &module->values, "SIGEXIT", NUMBER_VAL(0));
defineNativeProperty(vm, &module->values, "SIGABRT", NUMBER_VAL(22));
#endif

pop(vm);
pop(vm);
Expand Down
2 changes: 1 addition & 1 deletion tests/process/exec.du
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ class TestProcessExec < UnitTest {
}
}

TestProcessExec().run();
TestProcessExec().run();
3 changes: 2 additions & 1 deletion tests/process/import.du
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
*/

import "exec.du";
import "run.du";
import "kill.du";
import "run.du";
46 changes: 46 additions & 0 deletions tests/process/kill.du
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* kill.du
*
* Testing the Process.kill() function
*
* kill() receives a PID as a Number and optional signal, as a Number (on *NIX) to be passed to the process.
*/
from UnitTest import UnitTest;

import IO;
import Process;
import System;

class TestProcessKill < UnitTest {
testProcessKillError() {
const res = Process.kill(9999999999999999).unwrapError();
this.assertEquals(res, "No such process");
}

testProcessKillNoSignal() {
Process.exec(["sleep", "100"]);

const out = Process.run(["pgrep", "sleep"], true).unwrap();
const pids = out.split("\n");

if (pids.len() > 1) {
for (var i = 0; i < pids.len(); i += 1) {
var pid = pids[i].replace("\n", "");
pid = pid.toNumber().unwrap();
const res = Process.kill(pid, 0);
this.assertSuccess(res);
this.assertNil(res.unwrap());
}
} else {
var pid = pids[0].replace("\n", "");
pid = pid.toNumber().unwrap();
const res = Process.kill(pid, 0);
this.assertSuccess(res);
this.assertNil(res.unwrap());
}
}
}

if (System.platform != "windows") {
TestProcessKill().run();
}

0 comments on commit 873fa99

Please sign in to comment.