### GenServer Start - Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example demonstrating how to handle the return value of MuonTrap.Daemon.start_link/3. ```elixir case MuonTrap.Daemon.start_link("server", []) do {:ok, pid} -> IO.inspect(pid) {:error, reason} -> IO.inspect(reason) end ``` -------------------------------- ### Starting ping in a process Source: https://github.com/fhunleth/muontrap/blob/main/README.md This example shows how to start the `ping` command in an Elixir process using `System.cmd/3` and redirecting its output to `IO.stream(:stdio, :line)`. ```elixir iex> pid = spawn(fn -> System.cmd("ping", ["-i", "5", "localhost"], into: IO.stream(:stdio, :line)) end) #PID<0.6116.0> PING localhost (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.032 ms 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.077 ms ``` -------------------------------- ### Logger Metadata Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of starting a daemon with custom logger metadata. ```elixir MuonTrap.Daemon.start_link("worker", [], logger_metadata: [ worker_id: 123, queue: "background", region: "us-east-1" ] ) ``` -------------------------------- ### Cgroup Setup Example (Nerves) Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Elixir code to create cgroup directories on Nerves systems where `cgcreate` might not be available. ```elixir File.mkdir_p("/sys/fs/cgroup/memory/myapp") File.mkdir_p("/sys/fs/cgroup/cpu/myapp") ``` -------------------------------- ### Cgroup Setup Example (Bash) Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Bash commands to create a base cgroup with memory and cpu controllers. This is a prerequisite for using cgroup options. ```bash sudo cgcreate -a $(whoami) -g memory,cpu:myapp ``` -------------------------------- ### GenServer name registration Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example of starting a daemon and naming the GenServer for later access. ```elixir {:ok, _pid} = MuonTrap.Daemon.start_link("server", [], name: :my_server) # Later: access by name instead of PID MuonTrap.Daemon.statistics(:my_server) ``` -------------------------------- ### Simple Logging Configuration Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a daemon with basic info-level logging and a custom prefix. ```elixir {:ok, _pid} = MuonTrap.Daemon.start_link( "app_server", [], log_output: :info, log_prefix: "AppServer: " ) ``` -------------------------------- ### Example Usage Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/api-reference-muontrap.md Demonstrates how to get the absolute filesystem path to the muontrap port executable. ```elixir iex> MuonTrap.muontrap_path() "/path/to/muontrap/priv/muontrap" ``` -------------------------------- ### With Phoenix Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Example of integrating MuonTrap.Daemon into a Phoenix application's supervisor tree to start an external service. ```elixir # In your Phoenix application defmodule MyPhoenixApp.Application do def start(_type, _args) do children = [ MyPhoenixApp.Repo, MyPhoenixAppWeb.Endpoint, # Start external services {MuonTrap.Daemon, [ "external_service", ["--config", "prod"], [ cgroup_controllers: ["memory"], cgroup_base: "myapp", log_output: :info ] ]} ] Supervisor.start_link(children, strategy: :one_for_one, name: MyPhoenixApp.Supervisor) end end ``` -------------------------------- ### Testing with MuonTrap Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md ExUnit tests demonstrating the usage of MuonTrap for running commands, handling timeouts, and starting the daemon with logging. ```elixir defmodule MyAppTest do use ExUnit.Case test "runs command successfully" do assert {output, 0} = MuonTrap.cmd("echo", ["test"]) assert String.contains?(output, "test") end test "handles command timeout" do {_output, status} = MuonTrap.cmd("sleep", ["10"], timeout: 100) assert status == :timeout end test "daemon with logging" do {:ok, pid} = MuonTrap.Daemon.start_link( "echo", ["hello"], log_output: :info ) ref = Process.monitor(pid) assert_receive {:DOWN, ^ref, :process, ^pid, :normal} end end ``` -------------------------------- ### Logger Level Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of starting a daemon with a specific log output level. ```elixir MuonTrap.Daemon.start_link("server", [], log_output: :info) ``` -------------------------------- ### Cgroup Controllers Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example demonstrating how to enable multiple cgroup controllers (memory and CPU) for a command. ```elixir MuonTrap.cmd("work", [], cgroup_controllers: ["memory", "cpu"], cgroup_base: "myapp" ) ``` -------------------------------- ### child_spec/1 Usage Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/api-reference-daemon.md Example of creating a child specification for a Daemon and starting a supervisor. ```elixir children = [ {MuonTrap.Daemon, ["my_server", ["--port", "8080"], []]} ] Supervisor.start_link(children, strategy: :one_for_one) ``` -------------------------------- ### CPU Limit Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example setting CPU limits (period and quota) for a process using cgroup parameters. ```elixir MuonTrap.cmd("cpu_app", [], cgroup_controllers: ["cpu"], cgroup_base: "myapp", cgroup_sets: [ {"cpu", "cpu.cfs_period_us", "100000"}, {"cpu", "cpu.cfs_quota_us", "50000"} ] ) ``` -------------------------------- ### Wait for File Creation Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a processor, waiting for a specific initialization lock file to exist. ```elixir {:ok, _pid} = MuonTrap.Daemon.start_link( "processor", [], wait_for: fn -> unless File.exists?("/etc/app/initialized.lock") do raise "App not initialized" end end ) ``` -------------------------------- ### Wait for Dependency (Database) Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a web application, waiting for a database connection to be available before proceeding. ```elixir # Wait for database to be ready before starting app {:ok, _pid} = MuonTrap.Daemon.start_link( "web_app", ["--port", "8080"], wait_for: fn -> case :gen_tcp.connect('localhost', 5432, []) do {:ok, sock} -> :gen_tcp.close(sock) {:error, _reason} -> Process.sleep(500) raise "Database not ready" end end, log_output: :info ) ``` -------------------------------- ### Example: get_controllers/0 Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/cgroups-utilities.md Demonstrates how to list available cgroup controllers. ```elixir iex> MuonTrap.Cgroups.get_controllers() {:ok, ["cpuacct", "memory", "pids", "blkio", "cpu"]} # On system without cgroups: iex> MuonTrap.Cgroups.get_controllers() {:error, :enoent} ``` -------------------------------- ### Cgroup Variables and Values - Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example demonstrating how to set cgroup variables and values. ```elixir cgroup_sets: [ {"memory", "memory.limit_in_bytes", "268435456"}, {"cpu", "cpu.cfs_quota_us", "50000"} ] ``` -------------------------------- ### Memory Limit Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example setting a memory limit for a process using cgroup parameters. ```elixir MuonTrap.cmd("memory_app", [], cgroup_controllers: ["memory"], cgroup_base: "myapp", cgroup_sets: [{"memory", "memory.limit_in_bytes", "268435456"}] ) ``` -------------------------------- ### Basic Cgroup Creation and Command Execution Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Example of creating a cgroup for batch jobs and executing a command within it, limiting CPU usage. ```bash sudo cgcreate -a $(whoami) -g cpu:batch_jobs ``` ```elixir {output, status} = MuonTrap.cmd( "batch_processor", ["--input", "data.csv"], cgroup_controllers: ["cpu"], cgroup_base: "batch_jobs", cgroup_sets: [ {"cpu", "cpu.cfs_period_us", "100000"}, {"cpu", "cpu.cfs_quota_us", "50000"} # 50% of one CPU ] ) ``` -------------------------------- ### Running a native tool in cgroup Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Example of how to run a native tool within a cgroup in your Nerves firmware. It includes logic to enable cgroups if available and set memory limits. ```elixir defmodule MyNervesApp.Supervisor do def init(_opts) do children = [ # ... other services # Run a native tool in cgroup (if cgroups available) {MuonTrap.Daemon, [ "image_processor", [], opts_for_cgroups() ]} ] Supervisor.init(children, strategy: :one_for_one) end defp opts_for_cgroups do if MuonTrap.Cgroups.cgroups_enabled?() do # Create cgroup directories if needed File.mkdir_p!("/sys/fs/cgroup/memory/myapp") [ cgroup_controllers: ["memory"], cgroup_base: "myapp", cgroup_sets: [{"memory", "memory.limit_in_bytes", "268435456"}] ] else [] end end end ``` -------------------------------- ### Supervision with Error Handling Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/errors.md Provides an example of a Supervisor module that starts a `MuonTrap.Daemon` child. This demonstrates how supervision trees can handle restarts based on defined strategies when a child process fails. ```elixir defmodule MyApp.Supervisor do use Supervisor def start_link(init_arg) do Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__) end def init(_init_arg) do children = [ {MuonTrap.Daemon, ["service", [], [log_output: :error]]} ] Supervisor.init(children, strategy: :one_for_one) end end # Failures are logged, supervision tree handles restarts per restart strategy ``` -------------------------------- ### Install cgroup tools and create test cgroups Source: https://github.com/fhunleth/muontrap/blob/main/README.md Shell commands to install cgroup tools and create necessary cgroups for testing. ```sh sudo cgcreate -a $(whoami) -g memory,cpu:muontrap_test ``` -------------------------------- ### Command Execution - Timeout Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example demonstrating a command that times out. ```elixir # Timeout {partial_output, :timeout} = MuonTrap.cmd("sleep", ["60"], timeout: 100) ``` -------------------------------- ### Timeout Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example demonstrating how to set a timeout for a command. If the command exceeds the specified timeout (in milliseconds), it will be terminated and the exit status will be `:timeout`. ```elixir MuonTrap.cmd("sleep", ["60"], timeout: 5000) # Returns: {"", :timeout} ``` -------------------------------- ### Daemon Supervision Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/api-reference-daemon.md Example of integrating MuonTrap.Daemon into an Elixir supervision tree. ```elixir defmodule MyApp.Supervisor do use Supervisor def start_link(init_arg) do Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__) end def init(_init_arg) do children = [ {MuonTrap.Daemon, ["server", ["--config", "prod"], [log_output: :info]]} ] Supervisor.init(children, strategy: :one_for_one) end end ``` -------------------------------- ### Cgroup Path Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example of using `cgroup_path` to specify an exact cgroup location for the command. ```elixir MuonTrap.cmd("work", [], cgroup_path: "myapp/specific_task", cgroup_controllers: ["memory"]) ``` -------------------------------- ### OS PID Tracking Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a daemon and then retrieves its OS PID to allow inspection using standard OS tools like 'ps' and 'lsof'. ```elixir {:ok, daemon_pid} = MuonTrap.Daemon.start_link("service", []) case MuonTrap.Daemon.os_pid(daemon_pid) do os_pid when is_integer(os_pid) -> # Can now use OS tools to inspect the process System.cmd("ps", ["-p", to_string(os_pid)]) System.cmd("lsof", ["-p", to_string(os_pid)]) :error -> IO.puts("Process not yet started") end ``` -------------------------------- ### GenServer Server References - Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example demonstrating accessing GenServer statistics via PID and registered name. ```elixir {:ok, pid} = MuonTrap.Daemon.start_link("server", [], name: :my_daemon) # Access via PID stats = MuonTrap.Daemon.statistics(pid) # Access via name stats = MuonTrap.Daemon.statistics(:my_daemon) ``` -------------------------------- ### Multiple Limits Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example applying multiple cgroup limits (memory, CPU) and disabling the OOM killer for a daemon process. ```elixir MuonTrap.Daemon.start_link("worker", [], cgroup_controllers: ["memory", "cpu"], cgroup_base: "worker_pool", cgroup_sets: [ {"memory", "memory.limit_in_bytes", "536870912"}, {"cpu", "cpu.cfs_period_us", "100000"}, {"cpu", "cpu.cfs_quota_us", "100000"}, {"memory", "memory.oom_control", "1"} ] ) ``` -------------------------------- ### Options Map Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of validating and inspecting the internal options map. ```elixir options = MuonTrap.Options.validate(:cmd, "echo", ["hello"], []) IO.inspect(options) # %{cmd: "/bin/echo", args: ["hello"], into: "", ...} ``` -------------------------------- ### cgset Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/api-reference-daemon.md Example of setting a cgroup variable value using MuonTrap.Daemon.cgset/4. ```elixir {:ok, pid} = MuonTrap.Daemon.start_link( "memory_limited", [], cgroup_controllers: ["memory"], cgroup_base: "myapp" ) # Increase memory limit dynamically MuonTrap.Daemon.cgset(pid, "memory", "memory.limit_in_bytes", "1073741824") ``` -------------------------------- ### cgset Return Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of handling the return value from a cgset operation. ```elixir case MuonTrap.Daemon.cgset(pid, "memory", "memory.limit_in_bytes", "536870912") do :ok -> IO.puts("Memory limit set") {:error, :eacces} -> IO.puts("Permission denied") {:error, reason} -> IO.inspect(reason) end ``` -------------------------------- ### Working directory Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example of setting a specific working directory for a command. ```elixir MuonTrap.cmd("make", [], cd: "/path/to/project") ``` -------------------------------- ### Basic daemon with logging Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/api-reference-daemon.md Starts a daemon with basic logging enabled. ```elixir {:ok, pid} = MuonTrap.Daemon.start_link("my_server", ["--port", "8080"], log_output: :info) ``` -------------------------------- ### Command Execution - Successful Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example demonstrating a successful command execution and inspecting the output. ```elixir # Successful command {output, 0} = MuonTrap.cmd("echo", ["hello"]) IO.inspect(output) # "hello\n" ``` -------------------------------- ### statistics Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/api-reference-daemon.md Example of retrieving daemon statistics using MuonTrap.Daemon.statistics/1. ```elixir {:ok, pid} = MuonTrap.Daemon.start_link("talker", [], log_output: :debug) Process.sleep(1000) stats = MuonTrap.Daemon.statistics(pid) IO.inspect(stats) # %{output_byte_count: 4096} ``` -------------------------------- ### Signal Handling Delay Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example showing how to configure the delay before sending SIGKILL after SIGTERM has been sent. This is useful for allowing processes to shut down gracefully. ```elixir MuonTrap.Daemon.start_link("server", [], delay_to_sigkill: 2000) ``` -------------------------------- ### Cgroup Base Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example of using `cgroup_base` to let MuonTrap create a temporary cgroup subdirectory for the command. ```elixir MuonTrap.cmd("work", [], cgroup_base: "myapp", cgroup_controllers: ["memory"]) ``` -------------------------------- ### cgget Return Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of handling the return value from a cgget operation. ```elixir case MuonTrap.Daemon.cgget(pid, "memory", "memory.limit_in_bytes") do {:ok, value} -> IO.inspect(String.to_integer(value)) {:error, :enoent} -> IO.puts("Path not found") {:error, reason} -> IO.inspect(reason) end ``` -------------------------------- ### CPU-Limited Batch Job Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Example for setting up CPU limits for a batch job using cgroups. ```elixir # Create cgroup: ``` -------------------------------- ### Command Execution - Failed Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example demonstrating a failed command execution. ```elixir # Failed command {output, 1} = MuonTrap.cmd("false", []) ``` -------------------------------- ### Multiple Daemons Supervision Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/api-reference-daemon.md Example of supervising multiple MuonTrap.Daemon instances with unique IDs. ```elixir children = [ Supervisor.child_spec( {MuonTrap.Daemon, ["worker1", []]}, id: :worker_1 ), Supervisor.child_spec( {MuonTrap.Daemon, ["worker2", []]}, id: :worker_2 ) ] ``` -------------------------------- ### cgget Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/api-reference-daemon.md Example of retrieving a cgroup variable value using MuonTrap.Daemon.cgget/3. ```elixir {:ok, pid} = MuonTrap.Daemon.start_link( "process", [], cgroup_controllers: ["memory"], cgroup_base: "myapp" ) {:ok, limit} = MuonTrap.Daemon.cgget(pid, "memory", "memory.limit_in_bytes") IO.inspect(String.to_integer(limit)) ``` -------------------------------- ### Argument Lists - Correct Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of a correctly formatted argument list for MuonTrap.cmd/3. ```elixir # Correct MuonTrap.cmd("script", ["arg1", "arg2", "--flag", "value"]) ``` -------------------------------- ### Collectable Types - Write to File Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of writing command output to a file. ```elixir # Write to file {_stream, 0} = MuonTrap.cmd("tar", ["-xf", "archive.tar"], into: File.stream!("/dev/null")) ``` -------------------------------- ### Cgroup Paths - With cgroup_path Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of using `cgroup_path` option for an explicit cgroup path. ```elixir # With cgroup_path (explicit) MuonTrap.cmd("work", [], cgroup_path: "myapp/specific_task") # Uses: /sys/fs/cgroup/memory/myapp/specific_task ``` -------------------------------- ### Into an IO stream Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example of collecting command output into an IO stream. ```elixir MuonTrap.cmd("cat", ["large_file.txt"], into: IO.stream(:stdio, :line) ) ``` -------------------------------- ### os_pid Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/api-reference-daemon.md Example of getting the OS process ID of the daemon's child process using MuonTrap.Daemon.os_pid/1. ```elixir {:ok, pid} = MuonTrap.Daemon.start_link("long_running", []) os_pid = MuonTrap.Daemon.os_pid(pid) IO.inspect(os_pid) # e.g., 12345 ``` -------------------------------- ### Collectable Types - Stream to Stdout Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of streaming command output line-by-line to standard output. ```elixir # Stream to stdout {_stream, 0} = MuonTrap.cmd("cat", ["file.txt"], into: IO.stream(:stdio, :line)) ``` -------------------------------- ### OS PID Return Type Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of retrieving the OS PID of a process, handling cases where it's not yet available. ```elixir case MuonTrap.Daemon.os_pid(pid) do os_pid when is_integer(os_pid) -> IO.inspect(os_pid) :error -> IO.puts("Process not yet started") end ``` -------------------------------- ### Development Server with Logging Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example configuration for a development server, including command, arguments, working directory, logging, and process name. ```elixir {:ok, _pid} = MuonTrap.Daemon.start_link( "mix", ["phx.server"], cd: "/path/to/project", log_output: :info, log_prefix: "Phoenix: ", name: :phoenix_server ) ``` -------------------------------- ### Example - Dynamic Controller Selection Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/cgroups-utilities.md Illustrates how to dynamically select cgroup controllers based on system availability. ```elixir defmodule MyApp.Config do def cgroup_controllers do case MuonTrap.Cgroups.get_controllers() do {:ok, controllers} -> # Use memory and cpu if available, otherwise just memory cond do Enum.all?(["memory", "cpu"], &Enum.member?(controllers, &1)) -> ["memory", "cpu"] Enum.member?(controllers, "memory") -> ["memory"] true -> [] end {:error, :enoent} -> [] end end end ``` -------------------------------- ### Error-Only Logging Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a daemon configured to capture and log only standard error output. ```elixir # Only capture and log errors {:ok, _pid} = MuonTrap.Daemon.start_link( "job_processor", [], capture_stderr_only: true, log_output: :error, log_prefix: "ERROR: " ) ``` -------------------------------- ### Working in Different Directories Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Demonstrates how to execute commands in a specified directory using the 'cd' option. ```elixir # Run command in a specific directory {output, 0} = MuonTrap.cmd("ls", ["-la"], cd: "/tmp") # Compile a project in its directory {_output, 0} = MuonTrap.cmd( "mix", ["compile"], cd: "/path/to/elixir_project" ) ``` -------------------------------- ### Wait for file Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example of a startup synchronization function that waits for a specific configuration file to exist, raising an error if not found. ```elixir MuonTrap.Daemon.start_link("service", [], wait_for: fn -> unless File.exists?("/etc/app/config.yml") do raise "Config file not found" end end ) ``` -------------------------------- ### Memory-Limited Worker Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a daemon process with memory resource limits applied via cgroups. ```elixir # Create the cgroup base first: # sudo cgcreate -a $(whoami) -g memory:workers {:ok, pid} = MuonTrap.Daemon.start_link( "memory_intensive_worker", ["--task", "heavy_computation"], cgroup_controllers: ["memory"], cgroup_base: "workers", cgroup_sets: [ {"memory", "memory.limit_in_bytes", "536870912"} # 512 MB ], log_output: :info ) ``` -------------------------------- ### Running commands with muontrap Source: https://github.com/fhunleth/muontrap/blob/main/README.md This example shows the simplest way to use `muontrap` as a replacement for `System.cmd/3`, demonstrating its ability to kill the external process when the Elixir process exits. ```elixir iex> pid = spawn(fn -> MuonTrap.cmd("ping", ["-i", "5", "localhost"], into: IO.stream(:stdio, :line)) end) #PID<0.30860.0> PING localhost (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.027 ms 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.081 ms ``` ```elixir iex> Process.exit(pid, :oops) true iex> :os.cmd('ps -ef | grep ping') |> IO.puts 501 38898 38896 0 9:58PM ?? 0:00.00 grep ping :ok ``` -------------------------------- ### Logger Metadata for Correlation Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a worker with additional metadata that will be included in log output for easier correlation. ```elixir {:ok, _pid} = MuonTrap.Daemon.start_link( "worker", ["--queue", "background"], log_output: :info, logger_metadata: [ request_id: "req-12345", user_id: 999, environment: "production" ] ) # Logger output will include: # %{ # muontrap_cmd: "worker", # muontrap_args: "--queue background", # request_id: "req-12345", # user_id: 999, # environment: "production" # } ``` -------------------------------- ### Run with Specific Groups Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a daemon process as a specific user, granting it access to additional supplementary groups. ```elixir # Run with access to specific groups {:ok, _pid} = MuonTrap.Daemon.start_link( "storage_service", [], uid: "storage_user", gid: "storage_group", groups: ["disk", "backup"] # Group names ) ``` -------------------------------- ### Run as Specific User Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a daemon process configured to run as a specific user and group (e.g., 'www-data'). ```elixir # Run web server as www-data user {:ok, _pid} = MuonTrap.Daemon.start_link( "nginx", [], uid: "www-data", gid: "www-data" ) ``` -------------------------------- ### Dynamic Resource Adjustment (Memory) Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a worker and dynamically adjusts its memory limit based on current usage. ```elixir # Start worker with initial limit {:ok, worker_pid} = MuonTrap.Daemon.start_link( "scaling_worker", [], cgroup_controllers: ["memory"], cgroup_base: "workers" ) # Monitor and adjust based on conditions def monitor_and_adjust(pid) do case MuonTrap.Daemon.cgget(pid, "memory", "memory.usage_in_bytes") do {:ok, usage_str} -> usage = String.to_integer(usage_str) if usage > 268_435_456 do # > 256 MB # Increase limit MuonTrap.Daemon.cgset( pid, "memory", "memory.limit_in_bytes", "536870912" ) end {:error, _} -> :error end end ``` -------------------------------- ### Checking for running ping processes Source: https://github.com/fhunleth/muontrap/blob/main/README.md This example demonstrates how to check for running `ping` processes using the `ps` command and `:os.cmd/1`. ```elixir iex> :os.cmd('ps -ef | grep ping') |> IO.puts 501 38820 38587 0 9:26PM ?? 0:00.01 /sbin/ping -i 5 localhost 501 38824 38822 0 9:27PM ?? 0:00.00 grep ping :ok ``` -------------------------------- ### Custom Log Filtering Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a daemon with debug logging, applying a transformation function to filter out specific verbose lines. ```elixir {:ok, _pid} = MuonTrap.Daemon.start_link( "noisy_app", [], log_output: :debug, log_transform: fn line -> # Filter out debug noise if String.contains?(line, "VERBOSE") do "" else line end end ) ``` -------------------------------- ### Run with only primary group, no supplementary groups Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a daemon restricted to a specific user and primary group, dropping all supplementary groups. ```elixir {:ok, _pid} = MuonTrap.Daemon.start_link( "restricted_service", [], uid: "restricted_user", gid: "restricted_group", groups: [] # Empty list drops all supplementary groups ) ``` -------------------------------- ### Daemon with Custom Exit Handling Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a daemon and defines custom reasons for process exit based on the command's exit status. ```elixir {:ok, pid} = MuonTrap.Daemon.start_link( "database_command", ["--check-health"], exit_status_to_reason: fn 0 -> :normal 1 -> :database_error 2 -> :connection_failed 3 -> :timeout status -> {:error, status} end ) # Monitor for specific failure modes ref = Process.monitor(pid) receive do {:DOWN, ^ref, :process, ^pid, reason} -> case reason do :normal -> IO.puts("Check passed") :database_error -> IO.puts("DB is unhealthy") :connection_failed -> IO.puts("Can't connect to DB") :timeout -> IO.puts("Check timed out") end end ``` -------------------------------- ### Setting Environment Variables Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Shows how to pass environment variables to commands, including unsetting them. ```elixir # Pass environment variables {output, 0} = MuonTrap.cmd( "bash", ["-c", "echo $MY_VAR"], env: [ {"MY_VAR", "hello"}, {"PATH", "/custom/bin:" <> System.get_env("PATH")} ] ) # Output: "hello\n" # Unset a variable by passing nil {output, 0} = MuonTrap.cmd( "bash", ["-c", "echo ${UNSET_VAR:-not_set}"], env: [{"UNSET_VAR", nil}] ) # Output: "not_set\n" ``` -------------------------------- ### Example: cgroups_enabled?/0 Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/cgroups-utilities.md Demonstrates how to check if cgroup support is enabled on the system. ```elixir iex> MuonTrap.Cgroups.cgroups_enabled?() true # On a system without cgroups: iex> MuonTrap.Cgroups.cgroups_enabled?() false ``` -------------------------------- ### Wait for Health Check Endpoint Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a dependent service, waiting for a health check endpoint on another service to return a 200 OK status. ```elixir defp wait_for_health_check do case HTTPClient.get("http://localhost:8080/health") do {:ok, %{status: 200}} -> :ok _err -> Process.sleep(500) wait_for_health_check() end end {:ok, _pid} = MuonTrap.Daemon.start_link( "dependent_service", [], wait_for: &wait_for_health_check/0 ) ``` -------------------------------- ### Custom Logger Function Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Starts a daemon using a custom module and function for handling log messages, allowing for conditional logging and external alerts. ```elixir defmodule MyLogHandler do require Logger def handle_log(line) do cond do String.contains?(line, "ERROR") -> Logger.error(line) # Also send to external service send_alert(line) String.contains?(line, "WARN") -> Logger.warning(line) true -> Logger.debug(line) end end defp send_alert(msg) do # External alert service HTTPClient.post("https://alerts.example.com", %{"message" => msg}) end end {:ok, _pid} = MuonTrap.Daemon.start_link( "critical_service", [], logger_fun: {MyLogHandler, :handle_log} ) ``` -------------------------------- ### User/Group Identifiers - Username Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of specifying a username for a command. ```elixir # Username MuonTrap.cmd("app", [], uid: "appuser") ``` -------------------------------- ### Creating Cgroups - Nerves or systems without cgcreate Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/cgroups-utilities.md Manual directory creation for cgroups on systems without `cgcreate`. ```bash # Manual directory creation mkdir -p /sys/fs/cgroup/memory/myapp mkdir -p /sys/fs/cgroup/cpu/myapp # Set permissions chmod 755 /sys/fs/cgroup/memory/myapp chmod 755 /sys/fs/cgroup/cpu/myapp ``` -------------------------------- ### Statistics Return Type Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of retrieving and inspecting daemon statistics. ```elixir stats = MuonTrap.Daemon.statistics(pid) IO.puts("Process output: #{stats.output_byte_count} bytes") ``` -------------------------------- ### Run as specific user Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example of running a command as a specific user and group. ```elixir MuonTrap.cmd("app", [], uid: "www-data", gid: "www-data") ``` -------------------------------- ### Running a command with cgroup options Source: https://github.com/fhunleth/muontrap/blob/main/README.md This example shows how to use `MuonTrap.cmd/3` with cgroup options to run a command within a specified cgroup, utilizing the CPU controller. ```elixir iex> MuonTrap.cmd("spawning_program", [], cgroup_controllers: ["cpu"], cgroup_base: "mycgroup") {"hello\n", 0} ``` -------------------------------- ### Running Commands with Timeouts Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Illustrates how to set a timeout for command execution using the 'timeout' option. ```elixir # Kill process if it takes longer than 30 seconds {output, status} = MuonTrap.cmd( "long_operation", ["--config", "prod"], timeout: 30_000 ) case status do 0 -> IO.puts("Done: #{output}") :timeout -> IO.puts("Timed out. Partial output: #{output}") code -> IO.puts("Failed with code #{code}") end ``` -------------------------------- ### User/Group Identifiers - Combined Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of specifying both username and group name. ```elixir # Combined MuonTrap.cmd("app", [], uid: "appuser", gid: "appuser") ``` -------------------------------- ### Basic Daemon with Supervision Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/usage-patterns.md Sets up a basic MuonTrap.Daemon within a Supervisor for long-running processes. ```elixir defmodule MyApp.Supervisor do use Supervisor def start_link(init_arg) do Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__) end def init(_init_arg) do children = [ {MuonTrap.Daemon, [ "my_server", ["--port", "8080"], [log_output: :info, log_prefix: "Server: "] ]} ] Supervisor.init(children, strategy: :one_for_one) end end ``` -------------------------------- ### Wait for TCP port Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example of a startup synchronization function that waits until a TCP port is open. ```elixir defp wait_for_db do case :gen_tcp.connect('db_host', 5432, []) do {:ok, sock} -> :gen_tcp.close(sock) {:error, _} -> Process.sleep(100) wait_for_db() end end MuonTrap.Daemon.start_link("worker", [], wait_for: &wait_for_db/0 ) ``` -------------------------------- ### Collectable Types - List Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of collecting command output into a list. ```elixir # Collect into list {output, 0} = MuonTrap.cmd("seq", ["5"], into: []) ``` -------------------------------- ### Database Replica with Synchronization Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/configuration.md Example configuration for a database replica that synchronizes with a primary database using a custom wait_for function and sets environment variables. ```elixir {:ok, _pid} = MuonTrap.Daemon.start_link( "db_replica", ["--master-host", "primary.db", "--port", "5432"], wait_for: fn -> case :gen_tcp.connect('primary.db', 5432, []) do {:ok, sock} -> :gen_tcp.close(sock) {:error, _} -> Process.sleep(500) raise "Primary DB unreachable" end end, log_output: :debug, env: [{"DB_PASSWORD", System.get_env("DB_REPLICA_PASSWORD")}] ) ``` -------------------------------- ### Collectable Types - String Example Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/types.md Example of collecting command output into a string. ```elixir # Collect into string {output, 0} = MuonTrap.cmd("echo", ["test"], into: "") ``` -------------------------------- ### Example - Conditional Resource Limiting Source: https://github.com/fhunleth/muontrap/blob/main/_autodocs/cgroups-utilities.md Shows how to conditionally apply cgroup options based on cgroup availability. ```elixir defmodule MyApp.ProcessManager do def start_worker(name) do opts = if MuonTrap.Cgroups.cgroups_enabled?() do [ cgroup_controllers: ["memory"], cgroup_base: "myapp", cgroup_sets: [{"memory", "memory.limit_in_bytes", "268435456"}] ] else [] end MuonTrap.Daemon.start_link(name, [], opts) end end ```