Possible to run scripts as root on startup on Ubuntu Core?

Hey there,

I understand it is probably not recommended/good practice, but I was wondering if it is possible at all to run daemon scripts that are not snaps on Ubuntu Core…

It won’t be a permanent solution, but it will be a workaround to tackle the issue described here: https://github.com/Azure/iotedge/issues/7255. To summarize, there is a file that prevents this snap from starting properly after a reboot that I’d like to remove on every boot.

The guys from the snap are not replying and I need a temporary solution ASAP :slight_smile: I’d prefer not to fork their repo and make my own version of the snap, mostly because that will require me to request all the interfaces to connect etc… So its not really a fast solution either.

Regards, Charlee

Hi ! @Charlee .

Perhaps, you can do that with crontab command.

@Charlee, here a classic solution.

Creation of the script

#!/bin/bash
# Filename: setup_service.sh
# This script sets up a systemd service to run a custom script at startup.

# Replace the following path with the actual path to the script you want to run at startup.
SCRIPT_PATH="/path/to/your/script.sh"

# Replace the following path with the actual file path you want to remove.
FILE_TO_REMOVE="/path/to/problematic/file"

# Create the custom script that removes the problematic file.
cat << 'EOF' > "$SCRIPT_PATH"
#!/bin/bash
# This script removes a problematic file preventing snap from starting after reboot.
rm -f "$FILE_TO_REMOVE"
EOF

# Make the custom script executable.
chmod +x "$SCRIPT_PATH"

# Create a new systemd service file.
SERVICE_FILE="/etc/systemd/system/mydaemon.service"
cat << EOF > "$SERVICE_FILE"
[Unit]
Description=Custom script to remove problematic snap file at boot

[Service]
ExecStart=$SCRIPT_PATH

[Install]
WantedBy=multi-user.target
EOF

# Reload the systemd manager configuration.
systemctl daemon-reload

# Enable the new service to start at boot.
systemctl enable mydaemon.service

# Start the service immediately to test it.
systemctl start mydaemon.service

# Display the status of the new service.
systemctl status mydaemon.service

Run the script

sudo bash setup_service.sh

Wow thanks so much, I’m gonna try this!

well, you seem to use snap set here to pipe config.toml into the raw-config option …

you could simply do the same from a custom temporary gadget via the defaults: for azure-iot-edge … (indeed it is ugly that the gadget permanently carries your connection string so use this with caution)

Hey @ogra,

The problem I am describing doesn’t have to do with the connection string, but with the docker-proxy.sock file existing on reboot (sometimes).

I don’t mind ssh contact on first boot to set it up (at least for this version), but I do mind if the azure-iot-edge snap doesn’t start when the device is rebooted/powercycled/crashed/whatever.

Do you have any idea why this socat command would sometimes fail and other times not:

$SNAP/usr/bin/socat UNIX-LISTEN:$SNAP_COMMON/docker-proxy.sock,reuseaddr,fork,user=snap_aziotedge,group=snap_aziotedge UNIX-CONNECT:/var/run/docker.sock

If it fails I get this error:

2024-03-27T17:29:35Z azure-iot-edge.docker-proxy[1201]: 2024/03/27 17:29:35 socat[1201] E "/var/snap/azure-iot-edge/common/docker-proxy.sock" exists

Would it make sense to put it in /tmp/docker-proxy.sock instead?

P.S. Sorry to sidetrack this issue

heh, sorry, i should have read the log in your issue more closely …

you could indeed try to use /tmp but keep in mind that /tmp is unique to your snap and will not be accessible to other snaps on the system in case that is desired …

the error is pretty clear I’d say. The .sock file in SNAP_COMMON has not been cleaned up from the last run and socat will not overwrite it… here is what the socat manpage says:

      UNIX-LISTEN:<filename>
              Listens on <filename> using a UNIX domain stream socket and  ac‐
              cepts  a  connection.  If <filename> exists and is not a socket,
              this is an error.  If <filename> exists and  is  a  UNIX  domain
              socket, binding to the address fails (use option unlink-early!).

so I’d try to add the unlink-early option to the listening socket call here … perhaps that already solves it ?

Yep good point!

Indeed using socat’s own options sounds like the best solution.

I will try that and make an MR if it works :slight_smile:

In the meantime I will try the solution offered by @baldeuniversel

Thanks to both!

1 Like

I briefly looked at the snap. The docker-proxy service should really be made a socket activated service, then systemd starts listening on the socket and that nonsense of unlink/reuseaddr is gone. Unfortunately, you’d need to add code to hand over the file descriptor provided by systemd to socat. Other servies which may use what docker-proxy provides should use after: [docker-proxy] in their declarations. This needs to be fixed in snap packaging, so there’s not much you can do, except for opening some PRs or filing bugs if you’re simply consuming the snap.

I don’t mind making a PR if I know what to do.

I understand how to add the after: [docker-proxy], but do you have a bit more detail about what I’d do for this:

Unfortunately, you’d need to add code to hand over the file descriptor provided by systemd to socat.

I guess your solution would also solve the issue that I see that the socat script fails sometimes because the /var/run/docker.sock doesn’t exist yet.

This is a dependency on the docker socket. The snap has no way of expressing that, so your best bet here is to either retry or fail and have systemd restart the service (although, it’s always easier to handle this directly in the code). IOW, I would expect the docker-proxy service to fail gracefully until the upstream docker socket becomes available.

The socket activation is about exposing /var/snap/azure-iot-edge/common/docker-proxy.sock to services within the snap (although I don’t quite see why those services can’t just talk to docker directly, instead of a proxy service, since all plugged the docker interface). There are useful links available in the NOTES section of https://www.man7.org/linux/man-pages/man5/systemd.socket.5.html

Yea I also never really understood why it needed the proxy…

Since the proxy is able to talk to the docker socket, the other app in the same snap should be able to do the same…

Maybe I can make a PR to remove the proxy to start a discussion :sweat_smile:

Thanks for all the info!

Regarding the docker socket I guess a wrapper script that simply uses sleep in a loop and inotifywait (from the inotify-tools package IIRC) to break out of the loop when the socket appears should help to avoid the race (there is a similar thing somewhere hidden in ubuntu-frame to wait for the wayland socket…)

1 Like