no output from 'syncthing cli' within system service (systemd)

New user here, well versed in development and linux. Using syncthing to build a distributed system for sharing photos across photobooths and digital signage for a small business, loving things it so far, fantastic documentation, seriously. I work with a lot of FOSS projects and documentation is really hit-or-miss most of the time.

To accomplish my goal, I have a system service running syncthing under a user, deployed using the guide found at: Starting Syncthing Automatically — Syncthing v1 documentation

I am making a ancillary services to handle some periodic tasks that involve the local syncthing api. These services run a bash script that depends on knowing the api key for the local api. Normally this can be retrieved with syncthing cli config gui apikey get via cli while the syncthing daemon is running. This is where my issue begins.

Consider the following script ran as a service.

#!/bin/bash

https_port='8384'
my_api_key=$(syncthing cli config gui apikey get)
discover_wait_sec=30

function check-discovery()
{
    echo "$(curl -X GET -s -k -H "X-API-Key: ${my_api_key}" https://127.0.0.1:${https_port}/rest/system/discovery)"
}

echo "key: $(syncthing cli config gui apikey get)"

# Check discovery events for peers
while :
do
    echo "Running device discovery"
    disc_sleep=5
    i=$(($discover_wait_sec / $disc_sleep))
    while [ -z "$discovery_j" ]; do
        discovery_j=$(check-discovery)
        # Check if we found something, if not sleep and decrement
        if [ -z "$discovery_j" ]; then
            echo "None found, sleeping for $disc_sleep seconds..."
            sleep $disc_sleep
            i=$((i-1))
        fi
        # If we have done this for n seconds (i * sleep) without success, exit
        if [ $i == 0 ]; then
            echo "Discovery Failed (None Found) after ${discover_wait_sec} seconds, Exiting"
            exit 1
        fi
    done
    # Only run this check at most every 60s
    sleep 60
done

The important bit is these two lines:

my_api_key=$(syncthing cli config gui apikey get)
echo "key: $my_api_key"

With the result being null, we are unable to execute curl successfully.

When running this script as a service, the output of syncthing cli config gui apikey get is null. I can confirm that this service is running as the same user as the syncthing system service. I’ve done quite a bit of debugging around environment variables, access to files and file permissions, etc.

The same behavior is true of any other similar command. syncthing cli config gui user get also outputs null when used in the same manner, so its not specific to any cli command (as far as I can tell).

Other commands however, do work fine. self_id=$(syncthing --device-id) works from within the same service. From what I can tell its only commands using syncthing cli which I believe just accesses the local API anyway? Perhaps it interacts directly with the ~/.config/syncthing/config.xml file.

Short of diving into the source code (im pretty rusty with GO), I was hoping to find an answer here.

My Question: Is there an obvious reason that I am missing as to why this command isn’t working the way I expect from within a system service invoked as the same user as the syncthing daemon?

My Guess: There is some kind of flag or variable somewhere that needs to be present that syncthing doesn’t have, and I just am not familiar enough with syncthing yet to know where that is or what to look for.

  • Why don’t you just add the api key as a var in the script?
    • I am building a lot of this for auto provisioning of new devices, so everything needs to be as programmatic as possible. This means statically updating each device’s service file to have this API Key would be far too cumbersome. There are some interpolations I could do with sed, but that gets really clunky and doesn’t work well with potential changes in the future (needing to roll api keys)
  • Why don’t you just add an exported variable in the user’s env?
    • Unfortunately exported variables from profile files (like .profile / .bashrc) aren’t available to the service, and I prefer not exposing the apikey globally.

I’m hoping someone has a suggestion or idea. Thank you in advance for any assistance!

1 Like

I think this is one of the bugs that I fixed recently with TTY detection. You might be able to work around it replacing it with:

echo config gui apikey get | syncthing cli

You can check, perhaps its fixed in the RC.

1 Like

echo config gui apikey get | syncthing cli

This is working within the service script, thank you.

I looked through recent commit history and didn’t see anything mentioning TTY detection. Hopefully a fix made it into the RC.

Looking at issue history, I found bug: cli subcommand is stuck on non-interactive shell · Issue #7673 · syncthing/syncthing · GitHub which looks like it may be the one you are remembering.

1 Like

Yes, that’s the one

Just a heads up, but I think the workaround will stop working in a future release. We might be able to make it still work in later releases by adding a - after cli.

Assuming the issue is fixed, the workaround wont be needed anymore, correct?

Otherwise I will make a note that this workaround is not backwards compatible with new versions. I will update my logic to have a check and act accordingly.

my_api_key=$(syncthing cli config gui apikey get)
if [ -z "$my_api_key" ]; then
    my_api_key=$(echo config gui apikey get | syncthing cli)
fi

Thank you!

1 Like

As an f.y.i.: My alternative solution to this problem (getting the API Key for a script) was skipping the whole syncthing cli interface and instead I pull it directly from the config.xml file. e.g.:

my_api_key=`grep ‘<apikey>’ $STHOME/config.xml | sed ‘s:.*<apikey>::;s:</apikey>.*::’`

where $STHOME is the directory where you keep your Syncthing configuration file.

1 Like

It wouldn’t work in my specific use case because of other dependencies on the API being available. Outside of that it’s a great idea and good work around!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.