Page MenuHomePhorge

No OneTemporary

Size
12 KB
Referenced Files
None
Subscribers
None
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6fa8f22
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
+calling_from_make:
+ mix compile
+
+UNAME := $(shell uname)
+
+CFLAGS ?= -Wall -Werror -Wno-unused-parameter -pedantic -std=c99 -O3
+
+ifeq ($(UNAME), Darwin)
+ TARGET_CFLAGS ?= -fPIC -undefined dynamic_lookup -dynamiclib -Wextra
+endif
+
+ifeq ($(UNAME), Linux)
+ TARGET_CFLAGS ?= -fPIC -shared
+endif
+
+
+all: priv/exile.so
+
+priv/exile.so: c_src/exile.c
+ mkdir -p priv
+ $(CC) -I$(ERL_INTERFACE_INCLUDE_DIR) $(TARGET_CFLAGS) $(CFLAGS) c_src/exile.c -o priv/exile.so
+
+clean:
+ @rm -rf priv/exile.so
diff --git a/priv/.clang-format b/c_src/.clang-format
similarity index 100%
rename from priv/.clang-format
rename to c_src/.clang-format
diff --git a/priv/exile_nif.c b/c_src/exile.c
similarity index 94%
rename from priv/exile_nif.c
rename to c_src/exile.c
index effb6aa..bd004b2 100644
--- a/priv/exile_nif.c
+++ b/c_src/exile.c
@@ -1,278 +1,279 @@
#include "erl_nif.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define ERL_TRUE enif_make_atom(env, "true")
#define ERL_FALSE enif_make_atom(env, "false")
#define ERL_OK(__TERM__) \
enif_make_tuple2(env, enif_make_atom(env, "ok"), __TERM__)
#define ERL_ERROR(__TERM__) \
enif_make_tuple2(env, enif_make_atom(env, "error"), __TERM__)
static const int PIPE_READ = 0;
static const int PIPE_WRITE = 1;
static const int MAX_ARGUMENTS = 20;
static const int MAX_ARGUMENT_LEN = 1024;
enum exec_status {
SUCCESS,
PIPE_CREATE_ERROR,
PIPE_FLAG_ERROR,
FORK_ERROR,
PIPE_DUP_ERROR
};
typedef struct ExecResults {
enum exec_status status;
int err;
pid_t pid;
int pipe_in;
int pipe_out;
} ExecResult;
static int set_flag(int fd, int flags) {
return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | flags);
}
static void close_all(int pipes[3][2]) {
for (int i = 0; i < 3; i++) {
if (pipes[i][PIPE_READ])
close(pipes[i][PIPE_READ]);
if (pipes[i][PIPE_WRITE])
close(pipes[i][PIPE_WRITE]);
}
}
#define RETURN_ERROR(__ERR__) \
do { \
fprintf(stderr, "error in start_proccess(), %s:%d %s\n", __FILE__, \
__LINE__, strerror(errno)); \
result.status = __ERR__; \
result.err = errno; \
close_all(pipes); \
return result; \
} while (0);
static ExecResult start_proccess(char *args[], bool stderr_to_console) {
ExecResult result;
pid_t pid;
int pipes[3][2] = {{0, 0}, {0, 0}, {0, 0}};
if (pipe(pipes[STDIN_FILENO]) == -1 || pipe(pipes[STDOUT_FILENO]) == -1 ||
pipe(pipes[STDERR_FILENO]) == -1) {
RETURN_ERROR(PIPE_CREATE_ERROR)
}
if (set_flag(pipes[STDIN_FILENO][PIPE_READ], O_CLOEXEC) < 0 ||
set_flag(pipes[STDOUT_FILENO][PIPE_WRITE], O_CLOEXEC) < 0 ||
set_flag(pipes[STDIN_FILENO][PIPE_WRITE], O_CLOEXEC | O_NONBLOCK) < 0 ||
set_flag(pipes[STDOUT_FILENO][PIPE_READ], O_CLOEXEC | O_NONBLOCK) < 0 ||
set_flag(pipes[STDERR_FILENO][PIPE_READ], O_CLOEXEC | O_NONBLOCK) < 0 ||
set_flag(pipes[STDERR_FILENO][PIPE_WRITE], O_CLOEXEC | O_NONBLOCK) < 0) {
RETURN_ERROR(PIPE_FLAG_ERROR)
}
switch (pid = fork()) {
case -1:
RETURN_ERROR(FORK_ERROR)
case 0:
close(STDIN_FILENO);
close(STDOUT_FILENO);
if (dup2(pipes[STDIN_FILENO][PIPE_READ], STDIN_FILENO) < 0)
RETURN_ERROR(PIPE_DUP_ERROR)
if (dup2(pipes[STDOUT_FILENO][PIPE_WRITE], STDOUT_FILENO) < 0)
RETURN_ERROR(PIPE_DUP_ERROR)
if (stderr_to_console != true) {
close(STDERR_FILENO);
int dev_null = open("/dev/null", O_WRONLY);
if (dup2(dev_null, STDERR_FILENO) < 0)
RETURN_ERROR(PIPE_DUP_ERROR)
close(dev_null);
}
close_all(pipes);
execvp(args[0], args);
perror("execvp(): failed");
default:
close(pipes[STDIN_FILENO][PIPE_READ]);
close(pipes[STDOUT_FILENO][PIPE_WRITE]);
result.pid = pid;
result.pipe_in = pipes[STDIN_FILENO][PIPE_WRITE];
result.pipe_out = pipes[STDOUT_FILENO][PIPE_READ];
result.status = SUCCESS;
return result;
}
}
static ERL_NIF_TERM exec_proc(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
char _temp[MAX_ARGUMENTS][MAX_ARGUMENT_LEN];
char *exec_args[MAX_ARGUMENTS + 1];
- char *arg = NULL;
bool stderr_to_console = true;
unsigned int args_len;
if (enif_get_list_length(env, argv[0], &args_len) != true)
return enif_make_badarg(env);
if (args_len > MAX_ARGUMENTS)
return enif_make_badarg(env);
ERL_NIF_TERM head, tail, list = argv[0];
- for (int i = 0; i < args_len; i++) {
+ for (unsigned int i = 0; i < args_len; i++) {
if (enif_get_list_cell(env, list, &head, &tail) != true)
return enif_make_badarg(env);
if (enif_get_string(env, head, _temp[i], sizeof(_temp[i]), ERL_NIF_LATIN1) <
1)
return enif_make_badarg(env);
exec_args[i] = _temp[i];
list = tail;
}
exec_args[args_len] = NULL;
int temp;
if (enif_get_int(env, argv[1], &temp) != true)
return enif_make_badarg(env);
stderr_to_console = temp == 1 ? true : false;
ExecResult result = start_proccess(exec_args, stderr_to_console);
ERL_NIF_TERM ret;
switch (result.status) {
case SUCCESS:
ret = enif_make_tuple3(env, enif_make_int(env, result.pid),
enif_make_int(env, result.pipe_in),
enif_make_int(env, result.pipe_out));
return ERL_OK(ret);
default:
ret = enif_make_int(env, result.err);
return ERL_ERROR(ret);
}
}
static ERL_NIF_TERM write_proc(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
int pipe_in;
enif_get_int(env, argv[0], &pipe_in);
if (argc != 2)
enif_make_badarg(env);
ErlNifBinary bin;
- bool is_success = enif_inspect_binary(env, argv[1], &bin);
+ if (enif_inspect_binary(env, argv[1], &bin) != true)
+ return enif_make_badarg(env);
+
int result = write(pipe_in, bin.data, bin.size);
if (result >= 0) {
return ERL_OK(enif_make_int(env, result));
} else if (errno == EAGAIN) {
return ERL_ERROR(enif_make_int(env, errno));
} else {
perror("write()");
return ERL_ERROR(enif_make_int(env, errno));
}
}
static ERL_NIF_TERM close_pipe(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
int pipe;
enif_get_int(env, argv[0], &pipe);
int result = close(pipe);
if (result == 0) {
return enif_make_atom(env, "ok");
} else {
perror("close()");
return ERL_ERROR(enif_make_int(env, errno));
}
}
static ERL_NIF_TERM read_proc(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
int pipe_out, bytes;
enif_get_int(env, argv[0], &pipe_out);
enif_get_int(env, argv[1], &bytes);
if (bytes > 65535 || bytes < 1)
enif_make_badarg(env);
char buf[bytes];
int result = read(pipe_out, buf, sizeof(buf));
if (result >= 0) {
ErlNifBinary bin;
enif_alloc_binary(result, &bin);
memcpy(bin.data, buf, result);
return ERL_OK(enif_make_binary(env, &bin));
} else if (errno == EAGAIN) {
return ERL_ERROR(enif_make_int(env, errno));
} else {
perror("read()");
return ERL_ERROR(enif_make_int(env, errno));
}
}
static ERL_NIF_TERM is_alive(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
int pid;
enif_get_int(env, argv[0], &pid);
int result = kill(pid, 0);
if (result == 0) {
return ERL_TRUE;
} else {
return ERL_FALSE;
}
}
static ERL_NIF_TERM terminate_proc(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
int pid;
enif_get_int(env, argv[0], &pid);
return enif_make_int(env, kill(pid, SIGTERM));
}
static ERL_NIF_TERM kill_proc(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
int pid;
enif_get_int(env, argv[0], &pid);
return enif_make_int(env, kill(pid, SIGKILL));
}
static ERL_NIF_TERM wait_proc(ErlNifEnv *env, int argc,
const ERL_NIF_TERM argv[]) {
int pid, status;
enif_get_int(env, argv[0], &pid);
int wpid = waitpid(pid, &status, WNOHANG);
if (wpid != pid) {
perror("waitpid()");
}
return enif_make_tuple2(env, enif_make_int(env, wpid),
enif_make_int(env, status));
}
static ErlNifFunc nif_funcs[] = {
- {"exec_proc", 2, exec_proc}, {"write_proc", 2, write_proc},
- {"read_proc", 2, read_proc}, {"close_pipe", 1, close_pipe},
- {"terminate_proc", 1, terminate_proc}, {"wait_proc", 1, wait_proc},
- {"kill_proc", 1, kill_proc}, {"is_alive", 1, is_alive},
+ {"exec_proc", 2, exec_proc, 0}, {"write_proc", 2, write_proc, 0},
+ {"read_proc", 2, read_proc, 0}, {"close_pipe", 1, close_pipe, 0},
+ {"terminate_proc", 1, terminate_proc, 0}, {"wait_proc", 1, wait_proc, 0},
+ {"kill_proc", 1, kill_proc, 0}, {"is_alive", 1, is_alive, 0},
};
ERL_NIF_INIT(Elixir.Exile.ProcessHelper, nif_funcs, NULL, NULL, NULL, NULL)
diff --git a/lib/exile/process_helper.ex b/lib/exile/process_helper.ex
index e105f17..cc5fec2 100644
--- a/lib/exile/process_helper.ex
+++ b/lib/exile/process_helper.ex
@@ -1,39 +1,24 @@
defmodule Exile.ProcessHelper do
@on_load :load_nifs
def load_nifs do
- :erlang.load_nif('./priv/exile_nif', 0)
+ nif_path = :filename.join(:code.priv_dir(:exile), "exile")
+ :erlang.load_nif(nif_path, 0)
end
- def exec_proc(_cmd, _stderr_to_console) do
- raise "NIF exec_proc/0 not implemented"
- end
+ def exec_proc(_cmd, _stderr_to_console), do: :erlang.nif_error(:nif_library_not_loaded)
- def write_proc(_pipe, _bin) do
- raise "NIF write_proc/2 not implemented"
- end
+ def write_proc(_pipe, _bin), do: :erlang.nif_error(:nif_library_not_loaded)
- def read_proc(_pipe, _bytes) do
- raise "NIF read_proc/0 not implemented"
- end
+ def read_proc(_pipe, _bytes), do: :erlang.nif_error(:nif_library_not_loaded)
- def close_pipe(_pipe) do
- raise "NIF close_pipe/1 not implemented"
- end
+ def close_pipe(_pipe), do: :erlang.nif_error(:nif_library_not_loaded)
- def kill_proc(_pid) do
- raise "NIF kill_proc/1 not implemented"
- end
+ def kill_proc(_pid), do: :erlang.nif_error(:nif_library_not_loaded)
- def terminate_proc(_pid) do
- raise "NIF terminate_proc/1 not implemented"
- end
+ def terminate_proc(_pid), do: :erlang.nif_error(:nif_library_not_loaded)
- def wait_proc(_pid) do
- raise "NIF wait_proc/1 not implemented"
- end
+ def wait_proc(_pid), do: :erlang.nif_error(:nif_library_not_loaded)
- def is_alive(_pid) do
- raise "NIF is_alive/1 not implemented"
- end
+ def is_alive(_pid), do: :erlang.nif_error(:nif_library_not_loaded)
end
diff --git a/mix.exs b/mix.exs
index f79dae4..ff4e6ce 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,28 +1,30 @@
defmodule Exile.MixProject do
use Mix.Project
def project do
[
app: :exile,
version: "0.1.0",
elixir: "~> 1.7",
start_permanent: Mix.env() == :prod,
+ compilers: [:elixir_make] ++ Mix.compilers(),
+ make_targets: ["all"],
+ make_clean: ["clean"],
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
- # {:dep_from_hexpm, "~> 0.3.0"},
- # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
+ {:elixir_make, "~> 0.6", runtime: false}
]
end
end
diff --git a/mix.lock b/mix.lock
new file mode 100644
index 0000000..a7f64e0
--- /dev/null
+++ b/mix.lock
@@ -0,0 +1,3 @@
+%{
+ "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}
+}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Aug 8, 9:24 AM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1723058
Default Alt Text
(12 KB)

Event Timeline