9

For my application, I need to cut off the USB power at the earliest possible stage. Right now it starts early in init.d but I would like it, if possible, to start before the init.d stuff starts. I read somewhere that Linux normally calls /sbin/init after it boots the kernel. I looked at that file and I only calls this binary "/lib/systemd/systemd" Is this the case for Raspi as well? Can I put stuff right before or after that line?

I did:

journalctl -u disableusb.service
No journal files were found.

And yes, I did a sudo systemctl enable disableusb before. And it said it created a link to it.

pi@raspberrypi:~$ systemd-analyze
Startup finished in 3.312s (kernel) + 14.995s (userspace) = 18.308s

multi-user.target @14.786s
└─getty.target @14.767s
  └─serial-getty@ttyAMA0.service @14.733s
    └─systemd-user-sessions.service @13.720s +251ms
      └─network.target @13.142s
        └─dhcpcd.service @11.656s +1.249s
          └─basic.target @11.571s
            └─timers.target @11.552s
              └─systemd-tmpfiles-clean.timer @11.551s
                └─sysinit.target @11.522s
                  └─networking.service @7.911s +3.569s
                    └─local-fs.target @7.757s
                      └─boot.mount @7.503s +162ms
                        └─systemd-fsck@dev-mmcblk0p6.service @7.195s +248ms
                          └─dev-mmcblk0p6.device @7.166s
Ingo
  • 42,107
  • 20
  • 85
  • 197
Radu
  • 480
  • 2
  • 4
  • 12
  • Which operating system are you using? Raspbian Stretch? – Aurora0001 Jan 19 '18 at 14:07
  • raspbian-jessie – Radu Jan 19 '18 at 14:13
  • 2
    Most linux distros use systemd for the init implementation now. /etc/init.d is only around for backward compatibility (and possible cross compatibility with other *nixes). The best opportunity for fine tuning is, as Aurora0001 indicates, using systemd facilities. – goldilocks Jan 19 '18 at 14:29

4 Answers4

19

If you're using systemd (which you are if you have Jessie or Stretch), you'll want to create a systemd unit that will run your script as early as possible. In your /lib/systemd/system (note: not /lib/systemd/systemd, unlike some other distributions — see the official Pi docs) directory, create a new file called disableusb.service, containing the following:

[Unit]
Description=Disable USB power
Before=basic.target
After=local-fs.target sysinit.target
DefaultDependencies=no

[Service]
Type=oneshot
ExecStart=/path/to/script

[Install]
WantedBy=basic.target

Then, run systemctl enable disableusb, and the script should run on reboot.


Systemd works by running 'units' in order of their dependencies. A chart showing which order units are booted is available on the systemd website, reproduced here for reference:

local-fs-pre.target
         |
         v
(various mounts and   (various swap   (various cryptsetup
 fsck services...)     devices...)        devices...)       (various low-level   (various low-level
         |                  |                  |             services: udevd,     API VFS mounts:
         v                  v                  v             tmpfiles, random     mqueue, configfs,
  local-fs.target      swap.target     cryptsetup.target    seed, sysctl, ...)      debugfs, ...)
         |                  |                  |                    |                    |
         \__________________|_________________ | ___________________|____________________/
                                              \|/
                                               v
                                        sysinit.target
                                               |
          ____________________________________/|\________________________________________
         /                  |                  |                    |                    \
         |                  |                  |                    |                    |
         v                  v                  |                    v                    v
     (various           (various               |                (various          rescue.service
    timers...)          paths...)              |               sockets...)               |
         |                  |                  |                    |                    v
         v                  v                  |                    v              rescue.target
   timers.target      paths.target             |             sockets.target
         |                  |                  |                    |
         v                  \_________________ | ___________________/
                                              \|/
                                               v
                                         basic.target
                                               |
          ____________________________________/|                                 emergency.service
         /                  |                  |                                         |
         |                  |                  |                                         v
         v                  v                  v                                 emergency.target
     display-        (various system    (various system
 manager.service         services           services)
         |             required for            |
         |            graphical UIs)           v
         |                  |           multi-user.target
         |                  |                  |
         \_________________ | _________________/
                           \|/
                            v
                  graphical.target

The earliest you could realistically run your service is after sysinit.target, when the low-level services are initialized, I suspect. The systemd configuration above will execute the script path declared by ExecStart= after sysinit.target is complete, which should be relatively early in the boot sequence.

Aurora0001
  • 6,308
  • 3
  • 23
  • 38
  • 1
    Thank you very much. I will experiment with this, and if it works well, I will try to move it even earlier than that, until it doesn't work or it hangs on boot :) – Radu Jan 19 '18 at 14:42
  • Btw, my script is actually just a line of code, "echo 0 | tee /sys/devices/platform/soc/20980000.usb/buspower >/dev/null" Can I just put that in ExecStart, or does it have to be a path to an actual script? – Radu Jan 19 '18 at 14:53
  • For some reason it doesn't work. I made the file that looks like so: `code[Unit] Description=Disable USB power Before=base.target After=local-fs.target sysinit.target DefaultDependencies=no

    [Service] Type=oneshot ExecStart=/sbin/usb_down

    [Install] WantedBy=base.target` For some reason, that script doesn't run

    – Radu Jan 19 '18 at 15:27
  • In that case, @Radu, could you please edit the output of journalctl -u disableusb.service into your question? Thanks. – Aurora0001 Jan 19 '18 at 15:28
  • Hmm, definitely sounds like your unit never ran. Could you also edit in the output of systemd-analyze? – Aurora0001 Jan 19 '18 at 15:36
  • I did that also. Cool command btw, I used to time it with a timer on my phone (and it matches). – Radu Jan 19 '18 at 15:40
  • Apologies, I didn't realise that systemd-analyse on its own didn't give you the unit chain. The output of systemd-analyze critical-chain should be what we need (this will show which units have loaded on boot), can you edit that in too? – Aurora0001 Jan 19 '18 at 15:41
  • No problem. Done also. – Radu Jan 19 '18 at 15:46
  • I think I've spotted the issue, @Radu. Try copying the new unit in, then run systemctl reenable disableusb.service. I wrote base.target, should have written basic.target. Let me know if that fixes it. – Aurora0001 Jan 19 '18 at 15:46
  • Where should I copy it from/to? – Radu Jan 19 '18 at 15:47
  • 1
    Just replace your old disableusb.service with the new one – I've edited my post with the correct version now, so just copy that and put it in disableusb.service, then re-enable it. – Aurora0001 Jan 19 '18 at 15:47
  • 1
    Worked really well, ty. Now that I know the magic behind it, I will try to experiment more, I will try to put it before the network target, since I'd like to shut down the networking at all (at least the wifi part of it). – Radu Jan 19 '18 at 15:54
  • 2
    That's a cracking looking chart that. – codinghands Jan 19 '18 at 17:52
3

The earliest possible place is in the initrd (init ramdisk). For this you should look into creating an initramfs-tools hook in /etc/initramfs-tools/hooks or similar. You probably best look for some examples how other programs do it.

The initrd-code will be executed right after the kernel is loaded and sets up the most important things like loading different kernel modules needed for booting, mounting cryptoroot, etc. Something like you need to do would be possible there and prevent any programs from accessing usb things before your script ran.

allo
  • 330
  • 3
  • 13
  • Thanks. I took a look in /etc/initramfs-tools/ and it's pretty much empty. No hooks, no scripts no nothing, except for a few mostly empty files. Also, I think I need the file system to be there, because there will be a file that decides if I want the usb to start or not. – Radu Jan 19 '18 at 16:28
  • Have a look at the files in /usr/share/initramfs-tools, there are the file supplied by the system. For an example how to extend it you can install dropbear-initramfs (ssh server in the initrd to remotely unlock a cryptoroot). – allo Jan 21 '18 at 09:40
  • How to set and read the flag if you want usb to be enabled is up to you. Probably you could hook into some "rootfs mounted" step in the initrd, but I guess for the details you need to experiment yourself. – allo Jan 21 '18 at 09:41
  • Thanks, that looks interesting, I guess I could use an external MCU to pull some GPIO so I know when to disable the usb and when not to. – Radu Jan 21 '18 at 12:17
0

Maybe passing usbcore.nousb option to the kernel command line (at the end of /boot/cmdline.txt) would be a better way, not initializing it at all instead of deactivating it early?

Though deactivating USB would obviously be a viable option only for headless machines (more exactly keyboardless and mouseless), and communicating only with methods not dependant on USB:

  • serial console on TXD and RXD pins
  • autonomous robot driving things with I²C / SPI / GPIO
  • ethernet on Pi4 (IIRC Pi3 and earlier uses an on-board USB-ethernet chip)
  • don't know about integrated WiFi or Bluetooth, if/on which versions they depend on USB or not. If not then it could be fully used as desktop with a bluetooth KB+mouse.
MatsK
  • 2,791
  • 3
  • 16
  • 20
0

Possibly this answer is outside "earliest in the boot process". But if you really need USB disabled at the earliest possible time, you will need to modify the USB drivers in the kernel so that they initialize the USB hardware as "off". Ideally you could control this with a writeable module parameter. Then you could set the desired value as a kernel command line option and change it later by writing to /sys/module/parameters/. Sorry I don't have a patch. ps: Awesome systemd dependency chart from Aurora.

Chad Farmer
  • 121
  • 5