README.md

# BEAMNotify

[![Hex version](https://img.shields.io/hexpm/v/beam_notify.svg "Hex version")](https://hex.pm/packages/beam_notify)
[![API docs](https://img.shields.io/hexpm/v/beam_notify.svg?label=hexdocs "API docs")](https://hexdocs.pm/beam_notify/BEAMNotify.html)
[![CircleCI](https://circleci.com/gh/nerves-networking/beam_notify.svg?style=svg)](https://circleci.com/gh/nerves-networking/beam_notify)

Send a message to the BEAM from a shell script

This is one solution sending notifications from non-BEAM programs into Elixir.
`BEAMNotify` lets you set up a GenServer that listens for notifications from
shell scripts or anything that can invoke an OS process. Communication is via a
Unix Domain socket. Messages are limited to strings that passed via commandline
arguments or the environment to the `beam_notify` binary.

There are, of course, other ways of solving this problem. Some non-Elixir
programs already expose Unix domain or TCP socket interfaces for communication.
This might be a better choice. You could also use
[erl_call](http://erlang.org/doc/man/erl_call.html) or write a [C
node](http://erlang.org/doc/apps/erl_interface/ei_users_guide.html#introduction)
and communicate over distributed Erlang.

## Overview

`BEAMNotify` would typically be added to a supervision tree in your program.
Options to `BEAMNotify` specify things like its name, a dispatch function to
call, and other things.

To send a message from a shell script to your `BEAMNotify` GenServer, you'll
need two environment variables. These can be gotten by calling
`BEAMNotify.env/1` with the name that you gave it.

1. `$BEAM_NOTIFY` - the absolute path to the `beam_notify` executable
2. `$BEAM_NOTIFY_OPTIONS` - how `beam_notify` should find the appropriate BEAM
   instance and process the notification

In the shell script, run `$BEAM_NOTIFY` and pass it any arguments that you want
sent up. `BEAMNotify` reports environment variables too.

Back in Elixir, whenever a proper message is received, `BEAMNotify` will call
the dispatch function. The dispatch function is responsible for forwarding on
messages however makes sense in your application. If handling is simple, you
can process them in the dispatch function. You could also publish them through
`Phoenix.PubSub` or another pubsub service. `BEAMNotify` only handles strings,
so if you want to be fancier with your messages or filter them, you'll have to
add that to your dispatcher function.

It is important to keep in mind that the amount of data that can be sent in a
notification is limited by the transport and by OS limits on commandline
arguments. Suffice it to say that this is not intended for file transfer.

## Example

What we're going to do is create a script that sends a message to Elixir.
First, make sure that you have `:beam_notify` by either cloning this project or
creating a test Elixir project (`mix new ...`) and adding it to the `mix.exs`:

```elixir
def deps do
  [
    {:beam_notify, "~> 0.1.0"}
  ]
end
```

Now open an editor and create `simple.sh` with the following contents:

```sh
#!/bin/sh

echo "This is simple.sh"

$BEAM_NOTIFY Hello world
```

Start up Elixir with `iex -S mix`:

```elixir
# Get the PID that's running the IEx console
iex> us = self()
#PID<0.204.0>

# Start a BEAMNotify GenServer. The dispatcher function just sends a tuple
# with the arguments and environment passed in from the shell script.
iex> BEAMNotify.start_link(name: "sulu", dispatcher: &send(us, {&1, &2}))
{:ok, #PID<0.211.0>}

# Run the shell script. We're doing this from Elixir, but you
# can also grab the environment by calling `BEAMNotify.env/1` and run it
# in another terminal window.
iex> System.cmd("/bin/sh", ["simple.sh"], env: BEAMNotify.env("sulu"))
{"This is simple.sh\n", 0}

# See what was sent
iex> flush
{["Hello", "world"], %{...}}
```

## Supervision example

Here's a code snippet of starting a hypothetical non-Elixir program that needs
to send messages back to Elixir. This code is part of a [module-based
supervisor](https://hexdocs.pm/elixir/Supervisor.html#module-module-based-supervisors),
but this isn't necessary. Two GenServers are started: one for `BEAMNotify` and
one to start and monitor the non-Elixir program using
[`MuonTrap.Daemon`](https://hexdocs.pm/muontrap/MuonTrap.Daemon.html).

Note how `BEAMNotify.env/1` is used to pass the proper environment to the
program.

```elixir
  @impl Supervisor
  def init(_) do
    beam_notify_options = [name: "my_beam", dispatcher: &Some.function/2]
    children = [
      {BEAMNotify, beam_notify_options},
      {MuonTrap.Daemon,
       [
         "/path/to/program",
         ["-s", "script_calling_beam_notify.sh"],
         [log_output: :debug, env: BEAMNotify.env(beam_notify_options)]
       ]}
    ]

    opts = [strategy: :one_for_one]
    Supervisor.start_link(children, opts)
  end
```

If you're lucky, it might be sufficient to call `BEAMNotify.bin_path/0` to get
the path to the `beam_notify` program and pass that directly to the non-Elixir
program. You'll still need to set the environment for `beam_notify` to work. On
the bright side, this will skip out having your system start `bash` on each
notification.

## License

This library is covered by the Apache 2 license.