PCsuggest

  • Quick tip
  • SECURITY
  • NETWORKING
  • OpenWrt
  • HARDWARE

Run shell scripts from udev rules

Updated - August 21, 2017 by Arnab Satapathi

udev is the dynamic device manager for Linux, smartly manages different hardware devices, and it directly interacts with the Linux kernel, mode about udev here.

What if you want to run a specific linux commands when a specific hardware added to the system ? Let say run a backup script when a pendrive is connected.

Background: I faced a strange problem on a laptop running Debian, udev rules failed to switch an USB 4G dongle to modem mode from CD-ROM mode.

So I decided to run a shell script whenever the 4G dongle plugged in, rather than switching it manually.

This method could be implemented almost everywhere, but I'm using the example above to make the tutorial simple.

Contents

  • 1. Prepare the shell script
  • 2. Create a systemd service
  • 3. Edit or create custom udev rules
  • Conclusion

1. Prepare the shell script

This step depends upon your requirements, write the script to do whatever you want, and save it somewhere, I'm storing it under the /usr/local/bin folder.

My script to switch the 4G dongle,

#!/bin/sh
# Switch 4G dongle
usb_modeswitch -Q -v 1c9e -p f000 -V 1c9e -P 9605 \
-M "55534243123456788000000080000606f50402527000000000000000000000"

Now make it executable with chmod +x

sudo chmod +x /usr/local/bin/switch_modem

2. Create a systemd service

As directly running the script from a custom udev rule didn't worked, I had to create a new systemd service to run the script through it, when the device is plugged in, a USB modem in this case.

User created systemd service files are usually stored under /etc/systemd/system/ , I'm naming it switch_modem.service

amazon prime logo
Try AmazonPrime for free
Enjoy free shipping and One-Day delivery, cancel any time.
sudo nano /etc/systemd/system/switch_modem.service

The systemd service file,

[Unit]
Description=4G modem switcher

[Service]
Type=oneshot
RemainAfterExit=no
ExecStart=/usr/local/bin/switch_modem

[Install]
WantedBy=multi-user.target

This wiki article is very helpful to understand systemd and how to create systemd services.

3. Edit or create custom udev rules

You could directly edit a desired udev rule or create a new rule under /etc/udev/rules.d/ folder, I'm creating a new rule

sudo nano /etc/udev/rules.d/40-switch_modem.rules

My custom rule looks like bellow

ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="1c9e", ATTR{idProduct}=="f000", RUN+="switch_modem.service"

Now save and exit and reload newly created rules.

sudo udevadm control --reload-rules

You may need to restart the udev service too, use

sudo service udev restart

Or use this if the service command is not available

sudo systemctl restart systemd-udevd.service

In my case, now the 4G modem is switching automatically, and working fine, more on how to write udev rules.

Conclusion

This tutorial is not limited to just switch a unsupported 4G dongle, could be repurposed for anything you want to run from an udev event, i.e. when a new hardware added to the system.

I hope you will find this tutorial helpful and it's simple enough to understand. If you have any suggestion or question, please leave comments, I'll be happy to hear from you 🙂 .

Filed Under: how to Tagged With: bash, script, shell, udev

Your comments
  1. Helios-8 says

    June 19, 2017

    Udev rules should properly start services with TAG+="systemd", ENV{SYSTEMD_WANTS}="switch_modem.service" instead of RUN+=

    Reply
    • Arnab Satapathi says

      June 19, 2017

      Wow, thanks for pointing out.

      And perhaps RUN+= is for old sysvinit, what do you think ?

      Reply
    • felixyadomi says

      March 3, 2019

      But how to access udev env variables in a systemd service files without RUN+= ?

      Reply
      • Raza says

        August 3, 2019

        I found that I could pass one long argument with spaces and then use systemd's environment variable space-splitting feature to separate the arguments.

        I made a service with filename argtest@.service (note the trailing 'at sign' which is required when a service takes arguments).

        [Unit]
        Description=Test passing multiple arguments

        [Service]
        Environment="SCRIPT_ARGS=%I"
        ExecStart=/tmp/test.py $SCRIPT_ARGS

        I run this with sudo systemctl start argtest@"arg1 arg2 arg3".service and it passes arg1, arg2 and arg3 as separate command-line arguments to test.py.

        Reply
  2. Tom says

    March 16, 2017

    Found this after lots of searching. Works well with OpenSUSE 42.2

    Reply
    • Arnab Satapathi says

      March 16, 2017

      Thanks !

      Reply
  3. Emmanuel says

    April 4, 2016

    This is indeed what I need.

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Copyright © PCsuggest.com · All rights reserved.

  • Home
  • About
  • Contact
  • Privacy Policy
  • Sitemap