In Elixir, to interact with the host system or the virtual machine, we use the
module System
. In order to run a
command on the system, we can use the function
System.cmd/3
:
# This function returns the calls mix deps.get command
System.cmd("mix", ["deps.get"])
System.cmd/3
is a great way to run a command from elixir
layer, but it has
some hurdles while trying to use a sudo
command. If you try to call a sudo
command with System.cmd/3
it gives the following error:
System.cmd("sudo", ["su"])
# Gives this output--> sudo: no tty present and no askpass program specified
Creating an askpass
program
In order to work around this we need to provide an askpass
script, which can
prompt the users for their sudo
password. There are any askpass
applications
available, but one of the most common ones is GNOME
’s
Zenity
. Using zenity
, we can ask for
password using the command: $ zenity --password --title=Authentication
.
I use zenity
for my scripts because it has a working build for all the
Operating Systems that I use (Arch Linux, Ubuntu, Dedian, Mac OSX, Pop!_OS,
Fedora, CentOS and more..)
To install zenity
run the following commands:
# For an arch linux user
sudo pacman -S zenity --noconfirm
# For an ubuntu, pop os user
sudo apt-get -y install zenity
# For a mac user
# Make sure HomeBrew is installed
brew install zenity
# For a fedora, centos user
yum install zenity
Now, we’re ready to use zenity
.
So, let’s create a file askpass.sh
with the following contents:
#!/bin/bash
zenity --password --title=Authentication
Now that we have a program which prompts for user password, we can use this
while calling the sudo
command with System.cmd/3
.
Using askpass.sh
with System.cmd/3
System.cmd/3
takes a Keyword
as third parameter, which accepts the env
key. This allows us to pass command-specific environment variables to the
call. We can use this feature to pass the environment variable, SUDO_ASKPASS
.
This variable should point to the askpass.sh
file, which is the program that
asks for password.
One more thing, we will need to call the sudo
command with -A
option which
specifies that it needs to use the askpass
program defined by the environment
variable.
I will also be adding another option, into: IO.stream(:stdio, :line)
so we can
see the output of the sudo command line-by-line.
The final call looks like this:
System.cmd("sudo", ["-A" , "su"],
env: [{"SUDO_ASKPASS", "./askpass.sh"}],
into: IO.stream(:stdio, :line))
Once invoked, this command will open a zenity
window which prompts for the
password and once entered, it will continue with the command’s execution.
For a more advanced usage of this check out my project,
adify.
This project sets up a machine with asdf
, erlang
, elixir
and all the tools
that I use (and uses the above trick to do that).