1

I have bash script which should exec, when specific device connected. When i launch it from terminal i have not got any problems, however, when i launch it as systemd service, i got broken pipe error.

мая 07 10:47:49 nixos systemd[1563]: Starting garmin.service...
мая 07 10:47:51 nixos bash[6951]: grep: ошибка записи: Broken pipe
мая 07 10:47:51 nixos systemd[1563]: garmin.service: Control process exited, code=exited, status=127/n/a
мая 07 10:47:51 nixos systemd[1563]: garmin.service: Failed with result 'exit-code'.
мая 07 10:47:51 nixos systemd[1563]: Failed to start garmin.service.

This is my systemd service:

[Unit]
After=run-media-serg-GARMIN.mount
Requires=run-media-serg-GARMIN.mount

[Service]
Environment="LOCALE_ARCHIVE=/nix/store/ckkhm153z75sw6z5nr277kvn2pi4z8jy-glibc-locales-2.35-224/lib/locale/locale-archive"
Environment="PATH=/nix/store/l6jgwxkc3jhr029vfzfwzcy28iyckwsj-coreutils-9.1/bin:/nix/store/gn1s1s5z19cf0wiir2cd38jckcjc6kn6-findutils-4.9.0/bin:/nix/store/pvb117r7fhwb08717ks21a6y9hlnp63b-gnugrep-3.7/bin:/nix/store/v0hg83sdv4v51c0prmdigry6wdmmpzmp-gnused-4.8/bin:/nix/store/34am2kh69ll6q03731imxf21jdbizda2-systemd-251.15/bin:/nix/store/l6jgwxkc3jhr029vfzfwzcy28iyckwsj-coreutils-9.1/sbin:/nix/store/gn1s1s5z19cf0wiir2cd38jckcjc6kn6-findutils-4.9.0/sbin:/nix/store/pvb117r7fhwb08717ks21a6y9hlnp63b-gnugrep-3.7/sbin:/nix/store/v0hg83sdv4v51c0prmdigry6wdmmpzmp-gnused-4.8/sbin:/nix/store/34am2kh69ll6q03731imxf21jdbizda2-systemd-251.15/sbin"
Environment="TZDIR=/nix/store/z0kg1c0f8fx6r4rgg5bdy01lb2b9izqg-tzdata-2023a/share/zoneinfo"


ExecStart=/nix/store/kga2r02rmyxl14sg96nxbdhifq3rb8lc-bash-5.1-p16/bin/bash -c /nix/store/jqfh2bnzag50wlghwnw52mq2nzr8bzz2-garmin-backup/bin/garmin-backup
Type=forking

This is my script:

In a short, it checks, that proper device was mounted, then sync some folders with rsync, and send notify and write log.

#!/usr/bin/env bash

backupDir="/home/serg/Backup/Garmin Edge 830/"
#I need to export this variables for notify-send
export DISPLAY=:0 
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

on_error() 
{
     _MSG_=$1
     echo "$_MSG_"
     exit 1
}

check_mount()
{
    _MOUNT_=$1
    _MSG_=$2
    
    if [ ! -d "$_MOUNT_" ]; then
        echo "Path to $_MSG_ mount point is not exists"
        exit 1
    fi
}

writeLog() 
{
    _LOG_=$1
    _SAVE_PATH_=$2

    local outputPath="$_SAVE_PATH_/backup_history_${_LOG_}".log

    if [ ! -e "$outputPath" ]; then
        echo "Backup_log: " > "$outputPath"
    fi
    date >> "$outputPath"
}

garmin()
{
    mpointGarmin="$(df -h | grep -i "Garmin" | awk '{print $NF}')"

    check_mount "${mpointGarmin}" "Garmin"
    
    inputDir="${mpointGarmin}/Garmin/"
    copyArray=(
            "Activities" 
            "Locations" 
            "Records" 
            "Settings" 
            "Sports" 
            "Totals" 
            "Weight"
        )
    
    #https://stackoverflow.com/questions/9952000/using-rsync-include-and-exclude-options-to-include-directory-and-file-by-pattern
    #https://github.com/JonnyTischbein/rsync-server/blob/master/sync-folder.sh
    #Glob should be used inside array (according shellcheck)
    #See https://www.shellcheck.net/wiki/SC2125
    
    for val in "${copyArray[@]}"
        do
            copyStr+=( --include="$val"/*** )
        done
    
    #LOG_NAME="_garmin"
    rsync --dry-run --no-perms --checksum --progress -v -r "${copyStr[@]}" \
         --exclude=* "${inputDir}" "${backupDir}" 
    
    RET_CODE=$?
    
    if test $RET_CODE -eq 0; then
        #writeLog "$LOG_NAME" "${backupDir}"
        notify-send "Garmin 830 Backup is Done"
    fi     
}

garmin

I have found a line which causes this problem. It's

mpointGarmin="$(df -h | grep -i "Garmin" | awk '{print $NF}')"

When i remove this pipes and subshell and hardcode mount point it works.

As i understand, systemd does not like subshell calling with pipes? Or i am wrong? How can i fix it?

One
  • 11
  • 3
  • 4
    `df -h | awk 'tolower($0) ~ /Garmin/ {print $NF}` or `df -h > >(awk 'tolower($0) ~ /data/ {print $NF}')` maybe, I have had a same issue if i remember correctly with some of my scripts being executed by systemd, sadly I don't remember how I fixed the issue :-( , ` – Jetchisel May 07 '23 at 08:40
  • If that doesn't fix the issue, then you could use temp files. – Jetchisel May 07 '23 at 08:44
  • I think this post on a sister site might help to understand what's going on https://superuser.com/a/554896/128116 – Dima Chubarov May 07 '23 at 08:46
  • 2
    Vaguely guessing `systemd` runs with `set -euo pipefail` or some such. Jetchisel's comment removes the broken pipe signal so should solve the problem. – tripleee May 07 '23 at 13:59

1 Answers1

0

Technically, "Broken pipe" is a normal situation when the "reader" (right side) of a pipeline has already exited but the "writer" (left side) is still trying to write more data.

Usually in this situation, the kernel will automatically kill the writer using SIGPIPE before the writer gets the the "Broken pipe" error code. However, systemd by default sets up signal handlers to ignore SIGPIPE in order to avoid potential (vague) security issues (with services being tricked to write into closed pipes).

When running shell scripts as services, specify [Service] IgnoreSIGPIPE=no to return SIGPIPE behavior to the default (i.e. letting it immediately kill the process).

user1686
  • 13,155
  • 2
  • 35
  • 54