Unix things web developers often struggle with - and how to fix them

Making the jump to web development from Unix can be hard, we can help.

July 12, 2021
We're Expedited Security. We help SAAS applications prevent and recover from attack, overcome regulatory or integration security requirements or just stop "weird" traffic before it becomes a problem.

I spent the first decade of my career as a Linux person before switching to the web. I spend most of my time now working on speeding up EV background checks in a startup accelerator surrounded by developers from different backgrounds.

A lot of developers spent more time than they need to on some basic Unix things. So I made a list of common issues and fixes:

1. What messages like 'ENOENT' mean and where to get help

Error message like E<ALL CAPS> are failed system calls: ie, your app is doing something that involves the C library asking the kernel to do something, like open a file or bind to a port.

The names are abbreviated, but do actually make sense: ENOENT means 'Error: no entry'. The 'entry' being referred to is a 'directory entry': i.e. a file, a symlink, another directory or a special file is missing.

You can learn all this stuff from the official documentation on all the error codes in the libc docs.

2. You don't need to close your terminal when a command stops responding

Some command stops responding to your input. You hit Ctrl C to interrupt it and give you back your terminal. Nothing happens.

Bad app - it should handle the 'interrupt' signal properly or allow the default action to run. But wait: you don't need to close your current terminal and open up another one to kill the process.

Press Ctrl Z. The app is now suspended. Type jobs. The suspended app is listed with a number beside it.

To kill the job and get your terminal back, run:

kill -KILL %<job number>

The percent is used to specify a job number, rather than a process ID. We're going straight to KILL (rather than using the default TERMINATE signal) because something that doesn't respond to INTERRUPT probably won't handle TERMINATE either.

To kill all the jobs, throw this in our .bash_profile:

function killjobs () {
    JOBS="$(jobs -p)";
    if [ -n "${JOBS}" ]; then;
        kill -KILL ${JOBS};
    fi
}

If an app dies, Ctrl Z, killjobs. Your terminal is yours again.

3. You don't need to type 'python file.py', 'ruby file.rb' all the time

Always start your scripts by running:

node somefile.js

or

ruby somefile.rb

Or python or whatever else? Prefer to just type:

./somefile

instead? You can make these files into an executable by adding an interpreter as the first line of the file. This normally looks like

#!/usr/bin/env python

The /usr/bin/env finds python wherever it resides in your PATH, which is handy for moving your script between OSs. Rename the file & make it executable:

mv myfile.py myfile
chmod +x myfile.py

and suddenly you can run ./myfile just like any other executable.

This works on any interpreter: including mongoDB and casperjs's inbuilt JavaScript engines.

4. You don't need forever, monit, etc to restart services.

... and you definitely don't have to run anything in screen.

Every major LTS Linux distro: Debian, Red Hat/Centos, Arch, and soon Ubuntu, uses systemd which does this for you. Just make a .service file:

[Unit]
Description=Your app
After=network.target
[Service]
ExecStart=/var/www/myapp/app.js
Restart=always
User=nobody
Group=nobody
Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV=production
WorkingDirectory=/var/www/myapp
[Install]
WantedBy=multi-user.target

Copy the .service file to the /etc/systemd/system directory, then make systemd aware of the new service:

systemctl daemon-reload

Then start the service:

systemctl start myapp

Your app will be daemonised, have its console output turned into logs that are tagged with your service's name, and automatically restarted if it crashes.

5. You can give an executable permission to run on low ports

Want to make your web app be able to access port 443 or 80 without running it as root? Linux added the ability a decade ago, which is actually super recent in Unix time:

We use node in this example, but you can (and should) use this for anything else.

# Since this might be a symlink, and capabilities only apply to real files
NODE_EXECUTABLE=$(readlink -f $(which node))

Then add the cap_net_bind_service capability:

# ep is 'effective, permitted' - see http://linux.die.net/man/3/cap_from_text
sudo setcap 'cap_net_bind_service=+ep' $NODE_EXECUTABLE

You can see the capability applied with:

getcap $NODE_EXECUTABLE
/usr/local/node-v4.1.1-linux-x64/bin/node = cap_net_bind_service+ep

Your executable now has permission to bind to low ports as a regular user.