Skip to main content

Using ZeroBrane Studio to understand prosodyctl

Working on the terminal

One way to work on code is by using the terminal. We can navigate through folders and edit files with something classic, like Vim. The first time I was introduced to programming though, was through an IDE. Therefore, I always felt kind of uncomfortable working with code on something other than that. Even though nowadays I casually code with the terminal I always look for a neat IDE to use, albeit I know there isn't nothing I can do there that I can't do already through the normal tools. But that visual sugar, buttons and accessibility 😚 ...

One example

Okay, so here is the kind of situations were I like to use an IDE. I've made some coding on one of prosody's script, "prosodyctl", which is a script that contains commands to control prosody, in a systemctl-like fashion. This is a custom prosody installation I did from source, so everything is nicely packed in the same folder, making it easier to work around and develop. Here you can see my custom function, commands.list, which is simply executing a luarocks function which lists the installed rocks on a particular folder.

Now, if I was in my working folder and I executed the unchanged prosodyctl command, I'd get some information about the script as output. This is to help users who are working with the tool and might not be familiar with the commands, for example. However, after I coded my function, something unintended is happening:
When I run the prosodyctl script, it is executing my newly added command, and it's printing the output randomly with the default helper output.
How, why, when, where?
Something is clearly wrong, because there are plenty other command functions whose output is not printed when we run the script.

Using ZeroBrane Studio

For starters

We clearly need to debugged our script now, and we'll be using ZeroBrane Studio to do it. There might be better options to debug, but hey, we can only use what we know, until our knowledge progresses. So far, I personally like this method, albeit my opinion might change tomorrow. Worst case scenario, it won't hurt knowing how to use this tool.
Here are the links to download and install it!
So the first thing we want to do is changing the project directory. Doing so allow us to see the files and folders in that directory in the project tab and have an easier time around working. We can do it by going to:
Project -> Project Directory -> Choose

Interpreter is important!

We open the prosodyctl script and we try to run it. This is what was shown to me in the output window, the first time I was trying this out:
Which is strange. The script runs normally when we use the terminal, why wouldn't it here?
Well, one thing we can try out is comparing the Lua versions we are executing the script with. ZeroBrane comes with a lot of different interpreters. In my case, when I went to
project -> Lua interpreter
I had the the lua5.3 interpreter selected. Adding the line
At the beginning of the script also confirmed I was using lua's 5.3 version, which is different from what I had in the terminal, which was lua5.2.4 Stubbornly, even when I chose the lua5.2 interpreter I still couldn't run the script
Program 'lua52' started in '/home/joao/Documents/prosody_development/prosody-contrib' (pid: 10896).
Prosody was unable to find luaexpat
This package can be obtained in the following ways:
Debian/Ubuntu: sudo apt-get install lua-expat
luarocks: luarocks install luaexpat
luaexpat is required for Prosody to run, so we will now exit.
More help can be found on our website, at
Program completed in 0.07 seconds (pid: 10896).

Lets fix this problem by linking ZeroBrane directly to the same lua executable that our terminal is using. Usually, it will be located at
If, for whatever reason, you installed it somewhere else, you will have to find it. We will then reconfigure our 5.2 lua interpreter at ZBS by going to:
Edit -> Preferences -> Settings: User
This will open a file, where we'll paste this:
path.lua52 = "/usr/bin/lua"
That was my lua's path, but yours might be different. Anyway, now we just need to restart Zerobrane, select our lua5.2 interpreter, and we should be good to go!

The ZeroBrane and my terminal's output is now the same!


One of the main reasons why I like using IDEs are their debugging capabilities, which depend on the particular tool we are using. So far, I've liked ZeroBrane's.
Lets see it in action! We want to find out where our script is printing the unintended text. Here is how I found it out:

We start debugging here

Green triangle indicates which part of the script we are currently executing
1 - We place the cursor here
2 - This button will make the script execute until the line were the cursor currently is
3 - Notice we have no output at this point. That startup process isn't were the problem is

Those four similar buttons near arrow 2 allows us to use the debugger. Each one is useful and has a specific purpose, like stepping into a function or going right over it. On top of that we have a lot of extra functionality, which I'll not cover here because we are focusing on the workflow, instead of explaining all the functionality, which is very well detailed here!
We can eventually run through the script until we can pinpoint which part of the is causing the problem. Of course that, when we perfectly know our code, we should have a basic idea where it is. But sometimes we don't, and sometimes we are just working on someone else's code, like I am right now. There just isn't enough time to perfectly get to know all of it.
Anyway, I ended up discovering that the culprit was hiding inside this part of something called the command runner:

Understanding prosodyctl

Anyone really familiar with prosody's code won't be surprised with this discovery. Of course it had to be here!
Here is part of how this script works:
  • We go through a startup process, which sets and loads everything we need
  • Next, we have the script's main functions, like 'adduser', 'start' and my 'list'
  • Some stuff related to ejabberdctl and certificates
  • Declaration of the command_runner object
  • The command_runner call, which starts everything
In spite of having more than 1400 lines, our script is executed somewhere in the last 80, in our command_runner object. When prosodyctl is executed without any argument, this guy will go through here:

Right off the bat we can see something is amiss. My new commands aren't listed there. So that's a possible improvement. However, this still isn't where the problem really is. The code first executes a loop through the variable "commands_order" and then another one through "commands". This second variable is a table with all the commands in the script that something, somewhere, was able to find, including the ones I added. We can conclude this by inspecting what Zerobrane calls, the "stack", which, among other things, presents all the local variables and upvalues for the current stack frame.

The "commands" variable in the stack. We can see some of my functions, admin_remove and local_plugins
So, the script will call each function and pass the "--help" argument, even when it isn't listed on the "commands_order" variable. Since my functions don't know how to deal with that situation, they are simply run normally, which results in them printing out unwanted stuff.
Notice how the command "adduser"'s code starts and how my "local_plugins" lacks that functionality
We now know what needs to be done. By looking at the "adduser" command, for example, I added similar help functionality to my code, making it work as intended and complemented the "commands_order" variable.

After this, I got the expected output when executing prosodyctl

Wrapping it up

We used Zerobrane studio to execute prosodyctl around. I show how I deal with some problems along the way, both in using the IDE and on debugging my code on prosodyctl.