Ubuntu – How to keep a wireless card’s radio powered off by default


My desktop's PCI wireless card is always scanning for available wireless networks, but I only rarely use it. Can I keep the radio turned off until I need it?

Best Answer

There are at least two Upstart jobs that affect the default wireless state:

  • /etc/init/rfkill-restore.conf restores the soft block state for all radios to what they were at last shutdown, as recorded in /var/lib/rfkill/saved-state.
  • /etc/init/network-manager.conf starts Network Manager which in turn restores its idea of wireless state from /var/lib/NetworkManager/NetworkManager.state.

If you look at those two job configurations you will find that they have no temporal relationship, which seems like a design flaw to me. I'm guessing this race condition is rarely a problem because /etc/init/rfkill-restore.conf is much simpler and has fewer start conditions.

All the solutions to enforcing a wireless-off default that I've seen try to use /etc/rc.local, including the "modern" solution that @Lekensteyn and @rubo77 came up with. Unfortunately, that solution does not work for me on either of two laptops I have tried. This is not particularly surprising because /etc/rc.local also has no temporal relationship that I can find to either of /etc/init/rfkill-restore.conf and /etc/init/network-manager.conf. Throwing in a lengthy sleep in /etc/rc.local prior to issuing an rfkill block wifi is an ugly workaround for this race condition mess, but it works if the delay is long enough.

A better solution would be for us to impose our desired states in /var/lib/rfkill/saved-state and /var/lib/NetworkManager/NetworkManager.state before those two Upstart jobs are even permitted to run. We can achieve this by creating our own Upstart job. In actual fact, we'll need two job configuration files to achieve the timing we need.

Our first job configuration does the actual file modifications we need. It will run as early as possible and will only run once. Create /etc/init/radio-silence.conf with this content:

# radio-silence - Ensure radio silence on startup
# Override default startup behaviour of radios to ensure they are all
# disabled until the user deliberately enables them. This job requires
# radio-silence-wait to delay start of any services that may depend on
# resources manipulated by this job.

description "Disable all radios by default"

start on local-filesystems

pre-start script
  sed -i -re "s/^(.+[[:space:]]+)[01][[:space:]]*\$/\11/" /var/lib/rfkill/saved-state
  sed -i -re "s/^(WirelessEnabled=).*\$/\1false/" /var/lib/NetworkManager/NetworkManager.state
end script

As I prefer total radio silence when my laptop starts, I soft block all radios, not just wireless, but you can modify the first sed in the above to limit this job's impact to whichever wireless devices you wish to soft block.

Our second job configuration is responsible for ensuring that neither of the rfkill-restore and network-manager jobs will start before radio-silence has completed the file modifications. Create /etc/init/radio-silence-wait.conf as follows:

# radio-silence-wait - Helper task for radio-silence
# Delays the start of all jobs that may depend on resources manipulated
# by radio-silence job. Avoids the need to modify job configuration of
# those other jobs.

description "Assist radio-silence by delaying jobs it affects"

start on (starting rfkill-restore or starting network-manager)
stop on (started radio-silence or stopped radio-silence)

instance $JOB
normal exit 0 2

  status radio-silence | grep -q "start/running" && exit 0
  start radio-silence || true
  sleep infinity
end script

With this solution I am no longer seeing race condition problems, although I have not addressed the theoretical race between rfkill-restore and network-manager.

For more detail on how these jobs work together to achieve our temporal goal, refer to my question and answer, "How do I create a single-execution Upstart job guaranteed to complete before two other jobs begin?"

Related Question