여러 가지 이유로 우리는 공급업체의 애플리케이션을 래핑하는 쉘 스크립트를 가지고 있습니다. 우리의 시스템 관리자와 애플리케이션 소유자는 systemd에 대해 다양한 수준의 친숙성을 갖고 있습니다. 따라서 응용 프로그램이 실패하는 경우(systemctl도 이를 나타냄) 일부 최종 사용자("루트" 시스템 관리자 포함)는 를 사용하는 대신 래퍼 스크립트를 사용하여 "직접" 응용 프로그램을 시작할 수 있습니다 systemctl restart
. 올바른 종료 스크립트를 호출하지 않습니다. 응용 프로그램이 이미 중지되었기 때문입니다.
systemd로의 전환을 안내하기 위해 래퍼 스크립트를 업데이트하여 systemd에 의해 호출되는지 아니면 최종 사용자에 의해 호출되는지 확인하고 싶습니다.외부systemd에서는 호출자에게 systemctl을 사용하라는 메시지를 인쇄하고 싶습니다.
systemd에서 호출되는지 여부를 쉘 스크립트에서 어떻게 확인할 수 있습니까?
다음과 같이 가정할 수 있습니다.
- 스크립트를 래핑하는 bash 쉘
- 래퍼 스크립트가 애플리케이션을 성공적으로 시작하고 중지합니다.
- systemd 서비스가 예상대로 작동합니다.
systemd 서비스의 예는 다음과 같습니다.
[Unit]
Description=Vendor's Application
After=network-online.target
[Service]
ExecStart=/path/to/wrapper start
ExecStop=/path/to/wrapper stop
Type=forking
[Install]
WantedBy=multi-user.target
나는 관심이 없다초기화 시스템 감지, 나는 이미 그것이 시스템화되어 있다는 것을 알고 있기 때문에.
답변1
~에서루카스 웨이크마이스터풍부한 콘텐츠응답 서버 오류:
- systemd 버전 231 이상의 경우 로그에 연결하는 stdout 또는 stderr 서비스에 대해 설정된 JOURNAL_STREAM 변수가 있습니다.
- systemd 버전 232 이상의 경우 INVOCATION_ID 변수가 설정됩니다.
이러한 변수에 의존하고 싶지 않거나 231 이전 시스템 버전의 경우 상위 PID가 1인지 확인할 수 있습니다.
if [[ $PPID -ne 1 ]]
then
echo "Don't call me directly; instead, call 'systemctl start/stop service-name'"
exit 1
fi >&2
답변2
짧은 답변
if ! grep -qEe '[.]service$' /proc/self/cgroup; then
echo "This script should be started with systemctl" >&2
exit 1
fi
...또는 실행할 것으로 예상되는 특정 서비스의 이름을 알고 있고 사용자 세션 생성을 방해하는 잘못된 구성으로부터 방어할 수 있기를 원하는 경우:
if ! grep -qEe '/myservice[.]service$' /proc/self/cgroup; then
echo "This service should be started with systemctl start myservice" >&2
exit 1
fi
왜 작동하는가?
현재 프로세스를 시작한 서비스(있는 경우)를 확인하는 한 가지 방법은 를 확인하는 것입니다 /proc/self/cgroup
. 트리거된 서비스 의 경우 systemd
여기에는 서비스 이름이 포함됩니다.
12:pids:/system.slice/dhcpcd.service
11:rdma:/
10:memory:/system.slice/dhcpcd.service
9:blkio:/system.slice/dhcpcd.service
8:devices:/system.slice/dhcpcd.service
7:hugetlb:/
6:cpuset:/
5:freezer:/
4:cpu,cpuacct:/system.slice/dhcpcd.service
3:net_cls,net_prio:/
2:perf_event:/
1:name=systemd:/system.slice/dhcpcd.service
0::/system.slice/dhcpcd.service
...그리고 사용자 세션과 관련된 프로세스의 경우 cgroup은 다음과 유사합니다 /user.slice/user-1000.slice/session-337.scope
(마지막 재부팅 이후 시스템에서 사용자 UID 1000의 337번째 세션이라고 가정).
더욱 이국적인 구현
실행 중인 특정 서비스를 검색하려는 경우 해당 서비스를 추출할 수도 있습니다 /proc/self/cgroup
. 예를 들어 다음을 고려하십시오.
cgroup_full=$(awk -F: '$1 == 0 { print $3 }' /proc/self/cgroup)
cgroup_short=${cgroup_full##*/}
case $cgroup_full in
/system.slice/*.service) echo "Run from system service ${cgroup_short%.*}";;
/user.slice/*.service) echo "Run from user service ${cgroup_short%.*}";;
*.service) echo "Service ${cgroup_short%.*} type unknown";;
*) echo "Not run from a systemd service; in $cgroup_full";;
esac
답변3
마음에 떠오르는 또 다른 확실한 해결책은 다음과 같은 것을 추가하는 것입니다.
Environment=FROM_SYSTEMD=1
서비스 파일에 추가하고 해당 환경 변수를 테스트합니다.
답변4
systemd
또 다른 접근 방식은 보다 긴밀하게 결합된 검사를 얻기 위해 명시적으로 쿼리하는 것입니다 .
예를 들어 비슷한 사용 사례의 경우 다음과 같이 수행했습니다.
if [ "$(systemctl show -p ControlPID vendorservice)" != "ControlPID=$$" ]; then
echo 'no go'
exit 1
fi
type=forking
위의 코드 조각은 특정 상태를 활용하므로 서비스 에만 작동합니다.저것직업은 평생 지속될 수 있습니다.
ControlPID
구체적으로 this 로 설정된 값을 쿼리하고 작업 systemd
의 경우type=forking
표현하다프로세스 생성곧장systemd
PIDFile
동시에 그 자체는 무언가가 나타나기를 (또는 GuessMainPID
무언가를 검색하기를) 기다린 후 ControlPID
값이 다시 설정됩니다 0
. 이 특별한 동작은 systemd
가능한 PID 랩어라운드로부터 검사를 보호해야 합니다.
여러 속성을 검색할 수 있으며 systemctl show
그 중 일부는 type=
특정 사용 사례에 따라 다르므로 type=forking
수행할 특정 사용 사례 이외의 서비스 유형에 대해 및/또는 이를 기반으로 가장 적절한 속성을 쿼리해야 할 수 있습니다. 동등한 "네트워크 공유"를 검사합니다.
따라서 여기의 솔루션은 보편적인 솔루션은 아니지만 아마도 더 강력하고 이전 버전 과 호환되며1 미래 에 대비할 수 있습니다2 .
또한 쿼리할 올바른 서비스 이름을 이미 알고 있어야 합니다. 특히 인스턴스 서비스의 경우 쿼리할 정확한 인스턴스 이름을 알아야 합니다 vendorservice@1
(예:).
1 재산ControlPID
그리고systemctl show
명령그것으로-p
옵션첫 번째 출시부터 계속 이어져 왔습니다.systemd
2 systemctl show
출력은 다음의 일부입니다.안정성 약속