7 Commits

Author SHA1 Message Date
8301b337cc refactor: use unused constants instead of removing them
- Export unitTypes as UnitTypes and add HasValidUnitSuffix helper
- Use killed const (exit code 130) in execute() to detect SIGINT
- Update go.mod to go 1.26
2026-02-23 04:58:07 +00:00
c967fcb09e refactor: clean up unused code, fix typos, improve docs
- Remove unused 'killed' const and 'unitTypes' var (staticcheck U1000)
- Replace regexp with strings.TrimSuffix+switch in isFailed for consistency
- Fix typo: 'programatically' -> 'programmatically'
- Fix typo: 'an an int' -> 'as an int' in README and helpers.go
- Add missing godoc comments on exported helper functions
- Bump minimum Go version from 1.18 to 1.21
2026-02-18 08:23:50 +00:00
Dat Boi Diego
14c9f0f70d chore: add "support" for non-linux platforms (#6) 2025-09-20 19:34:22 -04:00
5f1537f8bc Merge pull request #4 from MatthiasKunnen/build-lines
Fix build lines conflict with go toolchain version
2025-05-23 14:48:39 -07:00
Matthias Kunnen
d38d347cc6 Fix build lines conflict with go toolchain version
21fce7918e adds build tags, however,
these specific tags are not supported in the go version set in go.mod (1.12).
See <https://go.dev/doc/go1.17#build-lines> and <https://go.dev/doc/go1.18#go-build-lines>.
2025-05-23 17:29:00 +02:00
14a2ca2acd add placehomer unexported unittypes for later 2025-02-18 00:34:16 -08:00
451a949ace add new properties, socket finder 2025-02-18 00:30:12 -08:00
11 changed files with 814 additions and 397 deletions

View File

@@ -32,7 +32,7 @@ If your system isn't running (or targeting another system running) `systemctl`,
## Helper functionality ## Helper functionality
- [x] Get start time of a service (`ExecMainStartTimestamp`) as a `Time` type - [x] Get start time of a service (`ExecMainStartTimestamp`) as a `Time` type
- [x] Get current memory in bytes (`MemoryCurrent`) an an int - [x] Get current memory in bytes (`MemoryCurrent`) as an int
- [x] Get the PID of the main process (`MainPID`) as an int - [x] Get the PID of the main process (`MainPID`) as an int
- [x] Get the restart count of a unit (`NRestarts`) as an int - [x] Get the restart count of a unit (`NRestarts`) as an int

View File

@@ -1,5 +1,3 @@
//go:build linux
package systemctl package systemctl
import ( import (

2
go.mod
View File

@@ -1,3 +1,3 @@
module github.com/taigrr/systemctl module github.com/taigrr/systemctl
go 1.12 go 1.26

View File

@@ -1,5 +1,3 @@
//go:build linux
package systemctl package systemctl
import ( import (
@@ -37,7 +35,7 @@ func GetNumRestarts(ctx context.Context, unit string, opts Options) (int, error)
return strconv.Atoi(value) return strconv.Atoi(value)
} }
// Get current memory in bytes (`systemctl show [unit] --property MemoryCurrent`) an an int // Get current memory in bytes (`systemctl show [unit] --property MemoryCurrent`) as an int
func GetMemoryUsage(ctx context.Context, unit string, opts Options) (int, error) { func GetMemoryUsage(ctx context.Context, unit string, opts Options) (int, error) {
value, err := Show(ctx, unit, properties.MemoryCurrent, opts) value, err := Show(ctx, unit, properties.MemoryCurrent, opts)
if err != nil { if err != nil {
@@ -58,6 +56,33 @@ func GetPID(ctx context.Context, unit string, opts Options) (int, error) {
return strconv.Atoi(value) return strconv.Atoi(value)
} }
// GetSocketsForServiceUnit returns the socket units associated with a given service unit.
func GetSocketsForServiceUnit(ctx context.Context, unit string, opts Options) ([]string, error) {
args := []string{"list-sockets", "--all", "--no-legend", "--no-pager"}
if opts.UserMode {
args = append(args, "--user")
}
stdout, _, _, err := execute(ctx, args)
if err != nil {
return []string{}, err
}
lines := strings.Split(stdout, "\n")
sockets := []string{}
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) < 3 {
continue
}
socketUnit := fields[1]
serviceUnit := fields[2]
if serviceUnit == unit+".service" {
sockets = append(sockets, socketUnit)
}
}
return sockets, nil
}
// GetUnits returns a list of all loaded units and their states.
func GetUnits(ctx context.Context, opts Options) ([]Unit, error) { func GetUnits(ctx context.Context, opts Options) ([]Unit, error) {
args := []string{"list-units", "--all", "--no-legend", "--full", "--no-pager"} args := []string{"list-units", "--all", "--no-legend", "--full", "--no-pager"}
if opts.UserMode { if opts.UserMode {
@@ -86,6 +111,7 @@ func GetUnits(ctx context.Context, opts Options) ([]Unit, error) {
return units, nil return units, nil
} }
// GetMaskedUnits returns a list of all masked unit names.
func GetMaskedUnits(ctx context.Context, opts Options) ([]string, error) { func GetMaskedUnits(ctx context.Context, opts Options) ([]string, error) {
args := []string{"list-unit-files", "--state=masked"} args := []string{"list-unit-files", "--state=masked"}
if opts.UserMode { if opts.UserMode {
@@ -115,7 +141,7 @@ func GetMaskedUnits(ctx context.Context, opts Options) ([]string, error) {
return units, nil return units, nil
} }
// check if systemd is the current init system // IsSystemd checks if systemd is the current init system by reading /proc/1/comm.
func IsSystemd() (bool, error) { func IsSystemd() (bool, error) {
b, err := os.ReadFile("/proc/1/comm") b, err := os.ReadFile("/proc/1/comm")
if err != nil { if err != nil {
@@ -124,7 +150,7 @@ func IsSystemd() (bool, error) {
return strings.TrimSpace(string(b)) == "systemd", nil return strings.TrimSpace(string(b)) == "systemd", nil
} }
// check if a service is masked // IsMasked checks if a unit is masked.
func IsMasked(ctx context.Context, unit string, opts Options) (bool, error) { func IsMasked(ctx context.Context, unit string, opts Options) (bool, error) {
units, err := GetMaskedUnits(ctx, opts) units, err := GetMaskedUnits(ctx, opts)
if err != nil { if err != nil {
@@ -138,8 +164,8 @@ func IsMasked(ctx context.Context, unit string, opts Options) (bool, error) {
return false, nil return false, nil
} }
// check if a service is running // IsRunning checks if a unit's sub-state is "running".
// https://unix.stackexchange.com/a/396633 // See https://unix.stackexchange.com/a/396633 for details.
func IsRunning(ctx context.Context, unit string, opts Options) (bool, error) { func IsRunning(ctx context.Context, unit string, opts Options) (bool, error) {
status, err := Show(ctx, unit, properties.SubState, opts) status, err := Show(ctx, unit, properties.SubState, opts)
return status == "running", err return status == "running", err

View File

@@ -3,6 +3,7 @@ package properties
type Property string type Property string
const ( const (
Accept Property = "Accept"
ActiveEnterTimestamp Property = "ActiveEnterTimestamp" ActiveEnterTimestamp Property = "ActiveEnterTimestamp"
ActiveEnterTimestampMonotonic Property = "ActiveEnterTimestampMonotonic" ActiveEnterTimestampMonotonic Property = "ActiveEnterTimestampMonotonic"
ActiveExitTimestampMonotonic Property = "ActiveExitTimestampMonotonic" ActiveExitTimestampMonotonic Property = "ActiveExitTimestampMonotonic"
@@ -12,9 +13,13 @@ const (
AssertResult Property = "AssertResult" AssertResult Property = "AssertResult"
AssertTimestamp Property = "AssertTimestamp" AssertTimestamp Property = "AssertTimestamp"
AssertTimestampMonotonic Property = "AssertTimestampMonotonic" AssertTimestampMonotonic Property = "AssertTimestampMonotonic"
Backlog Property = "Backlog"
Before Property = "Before" Before Property = "Before"
BindIPv6Only Property = "BindIPv6Only"
BindLogSockets Property = "BindLogSockets"
BlockIOAccounting Property = "BlockIOAccounting" BlockIOAccounting Property = "BlockIOAccounting"
BlockIOWeight Property = "BlockIOWeight" BlockIOWeight Property = "BlockIOWeight"
Broadcast Property = "Broadcast"
CPUAccounting Property = "CPUAccounting" CPUAccounting Property = "CPUAccounting"
CPUAffinityFromNUMA Property = "CPUAffinityFromNUMA" CPUAffinityFromNUMA Property = "CPUAffinityFromNUMA"
CPUQuotaPerSecUSec Property = "CPUQuotaPerSecUSec" CPUQuotaPerSecUSec Property = "CPUQuotaPerSecUSec"
@@ -28,6 +33,7 @@ const (
CacheDirectoryMode Property = "CacheDirectoryMode" CacheDirectoryMode Property = "CacheDirectoryMode"
CanFreeze Property = "CanFreeze" CanFreeze Property = "CanFreeze"
CanIsolate Property = "CanIsolate" CanIsolate Property = "CanIsolate"
CanLiveMount Property = "CanLiveMount"
CanReload Property = "CanReload" CanReload Property = "CanReload"
CanStart Property = "CanStart" CanStart Property = "CanStart"
CanStop Property = "CanStop" CanStop Property = "CanStop"
@@ -40,17 +46,26 @@ const (
ConfigurationDirectoryMode Property = "ConfigurationDirectoryMode" ConfigurationDirectoryMode Property = "ConfigurationDirectoryMode"
Conflicts Property = "Conflicts" Conflicts Property = "Conflicts"
ControlGroup Property = "ControlGroup" ControlGroup Property = "ControlGroup"
ControlGroupId Property = "ControlGroupId"
ControlPID Property = "ControlPID" ControlPID Property = "ControlPID"
CoredumpFilter Property = "CoredumpFilter" CoredumpFilter Property = "CoredumpFilter"
CoredumpReceive Property = "CoredumpReceive"
DebugInvocation Property = "DebugInvocation"
DefaultDependencies Property = "DefaultDependencies" DefaultDependencies Property = "DefaultDependencies"
DefaultMemoryLow Property = "DefaultMemoryLow" DefaultMemoryLow Property = "DefaultMemoryLow"
DefaultMemoryMin Property = "DefaultMemoryMin" DefaultMemoryMin Property = "DefaultMemoryMin"
DefaultStartupMemoryLow Property = "DefaultStartupMemoryLow"
DeferAcceptUSec Property = "DeferAcceptUSec"
Delegate Property = "Delegate" Delegate Property = "Delegate"
Description Property = "Description" Description Property = "Description"
DevicePolicy Property = "DevicePolicy" DevicePolicy Property = "DevicePolicy"
DirectoryMode Property = "DirectoryMode"
DynamicUser Property = "DynamicUser" DynamicUser Property = "DynamicUser"
EffectiveCPUs Property = "EffectiveCPUs" EffectiveCPUs Property = "EffectiveCPUs"
EffectiveMemoryHigh Property = "EffectiveMemoryHigh"
EffectiveMemoryMax Property = "EffectiveMemoryMax"
EffectiveMemoryNodes Property = "EffectiveMemoryNodes" EffectiveMemoryNodes Property = "EffectiveMemoryNodes"
EffectiveTasksMax Property = "EffectiveTasksMax"
ExecMainCode Property = "ExecMainCode" ExecMainCode Property = "ExecMainCode"
ExecMainExitTimestampMonotonic Property = "ExecMainExitTimestampMonotonic" ExecMainExitTimestampMonotonic Property = "ExecMainExitTimestampMonotonic"
ExecMainPID Property = "ExecMainPID" ExecMainPID Property = "ExecMainPID"
@@ -61,10 +76,14 @@ const (
ExecReloadEx Property = "ExecReloadEx" ExecReloadEx Property = "ExecReloadEx"
ExecStart Property = "ExecStart" ExecStart Property = "ExecStart"
ExecStartEx Property = "ExecStartEx" ExecStartEx Property = "ExecStartEx"
ExtensionImagePolicy Property = "ExtensionImagePolicy"
FailureAction Property = "FailureAction" FailureAction Property = "FailureAction"
FileDescriptorName Property = "FileDescriptorName"
FileDescriptorStoreMax Property = "FileDescriptorStoreMax" FileDescriptorStoreMax Property = "FileDescriptorStoreMax"
FinalKillSignal Property = "FinalKillSignal" FinalKillSignal Property = "FinalKillSignal"
FlushPending Property = "FlushPending"
FragmentPath Property = "FragmentPath" FragmentPath Property = "FragmentPath"
FreeBind Property = "FreeBind"
FreezerState Property = "FreezerState" FreezerState Property = "FreezerState"
GID Property = "GID" GID Property = "GID"
GuessMainPID Property = "GuessMainPID" GuessMainPID Property = "GuessMainPID"
@@ -81,6 +100,8 @@ const (
IPEgressPackets Property = "IPEgressPackets" IPEgressPackets Property = "IPEgressPackets"
IPIngressBytes Property = "IPIngressBytes" IPIngressBytes Property = "IPIngressBytes"
IPIngressPackets Property = "IPIngressPackets" IPIngressPackets Property = "IPIngressPackets"
IPTOS Property = "IPTOS"
IPTTL Property = "IPTTL"
Id Property = "Id" Id Property = "Id"
IgnoreOnIsolate Property = "IgnoreOnIsolate" IgnoreOnIsolate Property = "IgnoreOnIsolate"
IgnoreSIGPIPE Property = "IgnoreSIGPIPE" IgnoreSIGPIPE Property = "IgnoreSIGPIPE"
@@ -91,6 +112,10 @@ const (
JobRunningTimeoutUSec Property = "JobRunningTimeoutUSec" JobRunningTimeoutUSec Property = "JobRunningTimeoutUSec"
JobTimeoutAction Property = "JobTimeoutAction" JobTimeoutAction Property = "JobTimeoutAction"
JobTimeoutUSec Property = "JobTimeoutUSec" JobTimeoutUSec Property = "JobTimeoutUSec"
KeepAlive Property = "KeepAlive"
KeepAliveIntervalUSec Property = "KeepAliveIntervalUSec"
KeepAliveProbes Property = "KeepAliveProbes"
KeepAliveTimeUSec Property = "KeepAliveTimeUSec"
KeyringMode Property = "KeyringMode" KeyringMode Property = "KeyringMode"
KillMode Property = "KillMode" KillMode Property = "KillMode"
KillSignal Property = "KillSignal" KillSignal Property = "KillSignal"
@@ -126,6 +151,7 @@ const (
LimitSIGPENDINGSoft Property = "LimitSIGPENDINGSoft" LimitSIGPENDINGSoft Property = "LimitSIGPENDINGSoft"
LimitSTACK Property = "LimitSTACK" LimitSTACK Property = "LimitSTACK"
LimitSTACKSoft Property = "LimitSTACKSoft" LimitSTACKSoft Property = "LimitSTACKSoft"
Listen Property = "Listen"
LoadState Property = "LoadState" LoadState Property = "LoadState"
LockPersonality Property = "LockPersonality" LockPersonality Property = "LockPersonality"
LogLevelMax Property = "LogLevelMax" LogLevelMax Property = "LogLevelMax"
@@ -134,42 +160,76 @@ const (
LogsDirectoryMode Property = "LogsDirectoryMode" LogsDirectoryMode Property = "LogsDirectoryMode"
MainPID Property = "MainPID" MainPID Property = "MainPID"
ManagedOOMMemoryPressure Property = "ManagedOOMMemoryPressure" ManagedOOMMemoryPressure Property = "ManagedOOMMemoryPressure"
ManagedOOMMemoryPressureDurationUSec Property = "ManagedOOMMemoryPressureDurationUSec"
ManagedOOMMemoryPressureLimit Property = "ManagedOOMMemoryPressureLimit" ManagedOOMMemoryPressureLimit Property = "ManagedOOMMemoryPressureLimit"
ManagedOOMPreference Property = "ManagedOOMPreference" ManagedOOMPreference Property = "ManagedOOMPreference"
ManagedOOMSwap Property = "ManagedOOMSwap" ManagedOOMSwap Property = "ManagedOOMSwap"
Mark Property = "Mark"
MaxConnections Property = "MaxConnections"
MaxConnectionsPerSource Property = "MaxConnectionsPerSource"
MemoryAccounting Property = "MemoryAccounting" MemoryAccounting Property = "MemoryAccounting"
MemoryAvailable Property = "MemoryAvailable"
MemoryCurrent Property = "MemoryCurrent" MemoryCurrent Property = "MemoryCurrent"
MemoryDenyWriteExecute Property = "MemoryDenyWriteExecute" MemoryDenyWriteExecute Property = "MemoryDenyWriteExecute"
MemoryHigh Property = "MemoryHigh" MemoryHigh Property = "MemoryHigh"
MemoryKSM Property = "MemoryKSM"
MemoryLimit Property = "MemoryLimit" MemoryLimit Property = "MemoryLimit"
MemoryLow Property = "MemoryLow" MemoryLow Property = "MemoryLow"
MemoryMax Property = "MemoryMax" MemoryMax Property = "MemoryMax"
MemoryMin Property = "MemoryMin" MemoryMin Property = "MemoryMin"
MemoryPeak Property = "MemoryPeak"
MemoryPressureThresholdUSec Property = "MemoryPressureThresholdUSec"
MemoryPressureWatch Property = "MemoryPressureWatch"
MemorySwapCurrent Property = "MemorySwapCurrent"
MemorySwapMax Property = "MemorySwapMax" MemorySwapMax Property = "MemorySwapMax"
MemorySwapPeak Property = "MemorySwapPeak"
MemoryZSwapCurrent Property = "MemoryZSwapCurrent"
MemoryZSwapMax Property = "MemoryZSwapMax"
MemoryZSwapWriteback Property = "MemoryZSwapWriteback"
MessageQueueMaxMessages Property = "MessageQueueMaxMessages"
MessageQueueMessageSize Property = "MessageQueueMessageSize"
MountAPIVFS Property = "MountAPIVFS" MountAPIVFS Property = "MountAPIVFS"
MountImagePolicy Property = "MountImagePolicy"
NAccepted Property = "NAccepted"
NConnections Property = "NConnections"
NFileDescriptorStore Property = "NFileDescriptorStore" NFileDescriptorStore Property = "NFileDescriptorStore"
NRefused Property = "NRefused"
NRestarts Property = "NRestarts" NRestarts Property = "NRestarts"
NUMAPolicy Property = "NUMAPolicy" NUMAPolicy Property = "NUMAPolicy"
Names Property = "Names" Names Property = "Names"
NeedDaemonReload Property = "NeedDaemonReload" NeedDaemonReload Property = "NeedDaemonReload"
Nice Property = "Nice" Nice Property = "Nice"
NoDelay Property = "NoDelay"
NoNewPrivileges Property = "NoNewPrivileges" NoNewPrivileges Property = "NoNewPrivileges"
NonBlocking Property = "NonBlocking" NonBlocking Property = "NonBlocking"
NotifyAccess Property = "NotifyAccess" NotifyAccess Property = "NotifyAccess"
OOMPolicy Property = "OOMPolicy" OOMPolicy Property = "OOMPolicy"
OOMScoreAdjust Property = "OOMScoreAdjust" OOMScoreAdjust Property = "OOMScoreAdjust"
OnFailureJobMode Property = "OnFailureJobMode" OnFailureJobMode Property = "OnFailureJobMode"
OnSuccessJobMode Property = "OnSuccessJobMode"
PIDFile Property = "PIDFile" PIDFile Property = "PIDFile"
PassCredentials Property = "PassCredentials"
PassFileDescriptorsToExec Property = "PassFileDescriptorsToExec"
PassPacketInfo Property = "PassPacketInfo"
PassSecurity Property = "PassSecurity"
Perpetual Property = "Perpetual" Perpetual Property = "Perpetual"
PipeSize Property = "PipeSize"
PollLimitBurst Property = "PollLimitBurst"
PollLimitIntervalUSec Property = "PollLimitIntervalUSec"
Priority Property = "Priority"
PrivateDevices Property = "PrivateDevices" PrivateDevices Property = "PrivateDevices"
PrivateIPC Property = "PrivateIPC" PrivateIPC Property = "PrivateIPC"
PrivateMounts Property = "PrivateMounts" PrivateMounts Property = "PrivateMounts"
PrivateNetwork Property = "PrivateNetwork" PrivateNetwork Property = "PrivateNetwork"
PrivatePIDs Property = "PrivatePIDs"
PrivateTmp Property = "PrivateTmp" PrivateTmp Property = "PrivateTmp"
PrivateTmpEx Property = "PrivateTmpEx"
PrivateUsers Property = "PrivateUsers" PrivateUsers Property = "PrivateUsers"
PrivateUsersEx Property = "PrivateUsersEx"
ProcSubset Property = "ProcSubset" ProcSubset Property = "ProcSubset"
ProtectClock Property = "ProtectClock" ProtectClock Property = "ProtectClock"
ProtectControlGroups Property = "ProtectControlGroups" ProtectControlGroups Property = "ProtectControlGroups"
ProtectControlGroupsEx Property = "ProtectControlGroupsEx"
ProtectHome Property = "ProtectHome" ProtectHome Property = "ProtectHome"
ProtectHostname Property = "ProtectHostname" ProtectHostname Property = "ProtectHostname"
ProtectKernelLogs Property = "ProtectKernelLogs" ProtectKernelLogs Property = "ProtectKernelLogs"
@@ -177,12 +237,16 @@ const (
ProtectKernelTunables Property = "ProtectKernelTunables" ProtectKernelTunables Property = "ProtectKernelTunables"
ProtectProc Property = "ProtectProc" ProtectProc Property = "ProtectProc"
ProtectSystem Property = "ProtectSystem" ProtectSystem Property = "ProtectSystem"
ReceiveBuffer Property = "ReceiveBuffer"
RefuseManualStart Property = "RefuseManualStart" RefuseManualStart Property = "RefuseManualStart"
RefuseManualStop Property = "RefuseManualStop" RefuseManualStop Property = "RefuseManualStop"
ReloadResult Property = "ReloadResult" ReloadResult Property = "ReloadResult"
RemainAfterExit Property = "RemainAfterExit" RemainAfterExit Property = "RemainAfterExit"
RemoveIPC Property = "RemoveIPC" RemoveIPC Property = "RemoveIPC"
RemoveOnStop Property = "RemoveOnStop"
RequiredBy Property = "RequiredBy"
Requires Property = "Requires" Requires Property = "Requires"
RequiresMountsFor Property = "RequiresMountsFor"
Restart Property = "Restart" Restart Property = "Restart"
RestartKillSignal Property = "RestartKillSignal" RestartKillSignal Property = "RestartKillSignal"
RestartUSec Property = "RestartUSec" RestartUSec Property = "RestartUSec"
@@ -190,15 +254,22 @@ const (
RestrictRealtime Property = "RestrictRealtime" RestrictRealtime Property = "RestrictRealtime"
RestrictSUIDSGID Property = "RestrictSUIDSGID" RestrictSUIDSGID Property = "RestrictSUIDSGID"
Result Property = "Result" Result Property = "Result"
ReusePort Property = "ReusePort"
RootDirectoryStartOnly Property = "RootDirectoryStartOnly" RootDirectoryStartOnly Property = "RootDirectoryStartOnly"
RootEphemeral Property = "RootEphemeral"
RootImagePolicy Property = "RootImagePolicy"
RuntimeDirectoryMode Property = "RuntimeDirectoryMode" RuntimeDirectoryMode Property = "RuntimeDirectoryMode"
RuntimeDirectoryPreserve Property = "RuntimeDirectoryPreserve" RuntimeDirectoryPreserve Property = "RuntimeDirectoryPreserve"
RuntimeMaxUSec Property = "RuntimeMaxUSec" RuntimeMaxUSec Property = "RuntimeMaxUSec"
SameProcessGroup Property = "SameProcessGroup" SameProcessGroup Property = "SameProcessGroup"
SecureBits Property = "SecureBits" SecureBits Property = "SecureBits"
SendBuffer Property = "SendBuffer"
SendSIGHUP Property = "SendSIGHUP" SendSIGHUP Property = "SendSIGHUP"
SendSIGKILL Property = "SendSIGKILL" SendSIGKILL Property = "SendSIGKILL"
SetLoginEnvironment Property = "SetLoginEnvironment"
Slice Property = "Slice" Slice Property = "Slice"
SocketMode Property = "SocketMode"
SocketProtocol Property = "SocketProtocol"
StandardError Property = "StandardError" StandardError Property = "StandardError"
StandardInput Property = "StandardInput" StandardInput Property = "StandardInput"
StandardOutput Property = "StandardOutput" StandardOutput Property = "StandardOutput"
@@ -209,6 +280,11 @@ const (
StartupCPUShares Property = "StartupCPUShares" StartupCPUShares Property = "StartupCPUShares"
StartupCPUWeight Property = "StartupCPUWeight" StartupCPUWeight Property = "StartupCPUWeight"
StartupIOWeight Property = "StartupIOWeight" StartupIOWeight Property = "StartupIOWeight"
StartupMemoryHigh Property = "StartupMemoryHigh"
StartupMemoryLow Property = "StartupMemoryLow"
StartupMemoryMax Property = "StartupMemoryMax"
StartupMemorySwapMax Property = "StartupMemorySwapMax"
StartupMemoryZSwapMax Property = "StartupMemoryZSwapMax"
StateChangeTimestamp Property = "StateChangeTimestamp" StateChangeTimestamp Property = "StateChangeTimestamp"
StateChangeTimestampMonotonic Property = "StateChangeTimestampMonotonic" StateChangeTimestampMonotonic Property = "StateChangeTimestampMonotonic"
StateDirectoryMode Property = "StateDirectoryMode" StateDirectoryMode Property = "StateDirectoryMode"
@@ -216,6 +292,7 @@ const (
StopWhenUnneeded Property = "StopWhenUnneeded" StopWhenUnneeded Property = "StopWhenUnneeded"
SubState Property = "SubState" SubState Property = "SubState"
SuccessAction Property = "SuccessAction" SuccessAction Property = "SuccessAction"
SurviveFinalKillSignal Property = "SurviveFinalKillSignal"
SyslogFacility Property = "SyslogFacility" SyslogFacility Property = "SyslogFacility"
SyslogLevel Property = "SyslogLevel" SyslogLevel Property = "SyslogLevel"
SyslogLevelPrefix Property = "SyslogLevelPrefix" SyslogLevelPrefix Property = "SyslogLevelPrefix"
@@ -233,8 +310,14 @@ const (
TimeoutStartUSec Property = "TimeoutStartUSec" TimeoutStartUSec Property = "TimeoutStartUSec"
TimeoutStopFailureMode Property = "TimeoutStopFailureMode" TimeoutStopFailureMode Property = "TimeoutStopFailureMode"
TimeoutStopUSec Property = "TimeoutStopUSec" TimeoutStopUSec Property = "TimeoutStopUSec"
TimeoutUSec Property = "TimeoutUSec"
TimerSlackNSec Property = "TimerSlackNSec" TimerSlackNSec Property = "TimerSlackNSec"
Timestamping Property = "Timestamping"
Transient Property = "Transient" Transient Property = "Transient"
Transparent Property = "Transparent"
TriggerLimitBurst Property = "TriggerLimitBurst"
TriggerLimitIntervalUSec Property = "TriggerLimitIntervalUSec"
Triggers Property = "Triggers"
Type Property = "Type" Type Property = "Type"
UID Property = "UID" UID Property = "UID"
UMask Property = "UMask" UMask Property = "UMask"
@@ -245,4 +328,5 @@ const (
WatchdogSignal Property = "WatchdogSignal" WatchdogSignal Property = "WatchdogSignal"
WatchdogTimestampMonotonic Property = "WatchdogTimestampMonotonic" WatchdogTimestampMonotonic Property = "WatchdogTimestampMonotonic"
WatchdogUSec Property = "WatchdogUSec" WatchdogUSec Property = "WatchdogUSec"
Writable Property = "Writable"
) )

View File

@@ -1,6 +1,7 @@
package properties package properties
var Properties = []Property{ var Properties = []Property{
Accept,
ActiveEnterTimestamp, ActiveEnterTimestamp,
ActiveEnterTimestampMonotonic, ActiveEnterTimestampMonotonic,
ActiveExitTimestampMonotonic, ActiveExitTimestampMonotonic,
@@ -10,9 +11,13 @@ var Properties = []Property{
AssertResult, AssertResult,
AssertTimestamp, AssertTimestamp,
AssertTimestampMonotonic, AssertTimestampMonotonic,
Backlog,
Before, Before,
BindIPv6Only,
BindLogSockets,
BlockIOAccounting, BlockIOAccounting,
BlockIOWeight, BlockIOWeight,
Broadcast,
CPUAccounting, CPUAccounting,
CPUAffinityFromNUMA, CPUAffinityFromNUMA,
CPUQuotaPerSecUSec, CPUQuotaPerSecUSec,
@@ -26,6 +31,7 @@ var Properties = []Property{
CacheDirectoryMode, CacheDirectoryMode,
CanFreeze, CanFreeze,
CanIsolate, CanIsolate,
CanLiveMount,
CanReload, CanReload,
CanStart, CanStart,
CanStop, CanStop,
@@ -38,17 +44,26 @@ var Properties = []Property{
ConfigurationDirectoryMode, ConfigurationDirectoryMode,
Conflicts, Conflicts,
ControlGroup, ControlGroup,
ControlGroupId,
ControlPID, ControlPID,
CoredumpFilter, CoredumpFilter,
CoredumpReceive,
DebugInvocation,
DefaultDependencies, DefaultDependencies,
DefaultMemoryLow, DefaultMemoryLow,
DefaultMemoryMin, DefaultMemoryMin,
DefaultStartupMemoryLow,
DeferAcceptUSec,
Delegate, Delegate,
Description, Description,
DevicePolicy, DevicePolicy,
DirectoryMode,
DynamicUser, DynamicUser,
EffectiveCPUs, EffectiveCPUs,
EffectiveMemoryHigh,
EffectiveMemoryMax,
EffectiveMemoryNodes, EffectiveMemoryNodes,
EffectiveTasksMax,
ExecMainCode, ExecMainCode,
ExecMainExitTimestampMonotonic, ExecMainExitTimestampMonotonic,
ExecMainPID, ExecMainPID,
@@ -59,10 +74,14 @@ var Properties = []Property{
ExecReloadEx, ExecReloadEx,
ExecStart, ExecStart,
ExecStartEx, ExecStartEx,
ExtensionImagePolicy,
FailureAction, FailureAction,
FileDescriptorName,
FileDescriptorStoreMax, FileDescriptorStoreMax,
FinalKillSignal, FinalKillSignal,
FlushPending,
FragmentPath, FragmentPath,
FreeBind,
FreezerState, FreezerState,
GID, GID,
GuessMainPID, GuessMainPID,
@@ -79,6 +98,8 @@ var Properties = []Property{
IPEgressPackets, IPEgressPackets,
IPIngressBytes, IPIngressBytes,
IPIngressPackets, IPIngressPackets,
IPTOS,
IPTTL,
Id, Id,
IgnoreOnIsolate, IgnoreOnIsolate,
IgnoreSIGPIPE, IgnoreSIGPIPE,
@@ -89,6 +110,10 @@ var Properties = []Property{
JobRunningTimeoutUSec, JobRunningTimeoutUSec,
JobTimeoutAction, JobTimeoutAction,
JobTimeoutUSec, JobTimeoutUSec,
KeepAlive,
KeepAliveIntervalUSec,
KeepAliveProbes,
KeepAliveTimeUSec,
KeyringMode, KeyringMode,
KillMode, KillMode,
KillSignal, KillSignal,
@@ -124,6 +149,7 @@ var Properties = []Property{
LimitSIGPENDINGSoft, LimitSIGPENDINGSoft,
LimitSTACK, LimitSTACK,
LimitSTACKSoft, LimitSTACKSoft,
Listen,
LoadState, LoadState,
LockPersonality, LockPersonality,
LogLevelMax, LogLevelMax,
@@ -132,42 +158,76 @@ var Properties = []Property{
LogsDirectoryMode, LogsDirectoryMode,
MainPID, MainPID,
ManagedOOMMemoryPressure, ManagedOOMMemoryPressure,
ManagedOOMMemoryPressureDurationUSec,
ManagedOOMMemoryPressureLimit, ManagedOOMMemoryPressureLimit,
ManagedOOMPreference, ManagedOOMPreference,
ManagedOOMSwap, ManagedOOMSwap,
Mark,
MaxConnections,
MaxConnectionsPerSource,
MemoryAccounting, MemoryAccounting,
MemoryAvailable,
MemoryCurrent, MemoryCurrent,
MemoryDenyWriteExecute, MemoryDenyWriteExecute,
MemoryHigh, MemoryHigh,
MemoryKSM,
MemoryLimit, MemoryLimit,
MemoryLow, MemoryLow,
MemoryMax, MemoryMax,
MemoryMin, MemoryMin,
MemoryPeak,
MemoryPressureThresholdUSec,
MemoryPressureWatch,
MemorySwapCurrent,
MemorySwapMax, MemorySwapMax,
MemorySwapPeak,
MemoryZSwapCurrent,
MemoryZSwapMax,
MemoryZSwapWriteback,
MessageQueueMaxMessages,
MessageQueueMessageSize,
MountAPIVFS, MountAPIVFS,
MountImagePolicy,
NAccepted,
NConnections,
NFileDescriptorStore, NFileDescriptorStore,
NRefused,
NRestarts, NRestarts,
NUMAPolicy, NUMAPolicy,
Names, Names,
NeedDaemonReload, NeedDaemonReload,
Nice, Nice,
NoDelay,
NoNewPrivileges, NoNewPrivileges,
NonBlocking, NonBlocking,
NotifyAccess, NotifyAccess,
OOMPolicy, OOMPolicy,
OOMScoreAdjust, OOMScoreAdjust,
OnFailureJobMode, OnFailureJobMode,
OnSuccessJobMode,
PIDFile, PIDFile,
PassCredentials,
PassFileDescriptorsToExec,
PassPacketInfo,
PassSecurity,
Perpetual, Perpetual,
PipeSize,
PollLimitBurst,
PollLimitIntervalUSec,
Priority,
PrivateDevices, PrivateDevices,
PrivateIPC, PrivateIPC,
PrivateMounts, PrivateMounts,
PrivateNetwork, PrivateNetwork,
PrivatePIDs,
PrivateTmp, PrivateTmp,
PrivateTmpEx,
PrivateUsers, PrivateUsers,
PrivateUsersEx,
ProcSubset, ProcSubset,
ProtectClock, ProtectClock,
ProtectControlGroups, ProtectControlGroups,
ProtectControlGroupsEx,
ProtectHome, ProtectHome,
ProtectHostname, ProtectHostname,
ProtectKernelLogs, ProtectKernelLogs,
@@ -175,12 +235,16 @@ var Properties = []Property{
ProtectKernelTunables, ProtectKernelTunables,
ProtectProc, ProtectProc,
ProtectSystem, ProtectSystem,
ReceiveBuffer,
RefuseManualStart, RefuseManualStart,
RefuseManualStop, RefuseManualStop,
ReloadResult, ReloadResult,
RemainAfterExit, RemainAfterExit,
RemoveIPC, RemoveIPC,
RemoveOnStop,
RequiredBy,
Requires, Requires,
RequiresMountsFor,
Restart, Restart,
RestartKillSignal, RestartKillSignal,
RestartUSec, RestartUSec,
@@ -188,15 +252,22 @@ var Properties = []Property{
RestrictRealtime, RestrictRealtime,
RestrictSUIDSGID, RestrictSUIDSGID,
Result, Result,
ReusePort,
RootDirectoryStartOnly, RootDirectoryStartOnly,
RootEphemeral,
RootImagePolicy,
RuntimeDirectoryMode, RuntimeDirectoryMode,
RuntimeDirectoryPreserve, RuntimeDirectoryPreserve,
RuntimeMaxUSec, RuntimeMaxUSec,
SameProcessGroup, SameProcessGroup,
SecureBits, SecureBits,
SendBuffer,
SendSIGHUP, SendSIGHUP,
SendSIGKILL, SendSIGKILL,
SetLoginEnvironment,
Slice, Slice,
SocketMode,
SocketProtocol,
StandardError, StandardError,
StandardInput, StandardInput,
StandardOutput, StandardOutput,
@@ -207,6 +278,11 @@ var Properties = []Property{
StartupCPUShares, StartupCPUShares,
StartupCPUWeight, StartupCPUWeight,
StartupIOWeight, StartupIOWeight,
StartupMemoryHigh,
StartupMemoryLow,
StartupMemoryMax,
StartupMemorySwapMax,
StartupMemoryZSwapMax,
StateChangeTimestamp, StateChangeTimestamp,
StateChangeTimestampMonotonic, StateChangeTimestampMonotonic,
StateDirectoryMode, StateDirectoryMode,
@@ -214,6 +290,7 @@ var Properties = []Property{
StopWhenUnneeded, StopWhenUnneeded,
SubState, SubState,
SuccessAction, SuccessAction,
SurviveFinalKillSignal,
SyslogFacility, SyslogFacility,
SyslogLevel, SyslogLevel,
SyslogLevelPrefix, SyslogLevelPrefix,
@@ -231,8 +308,14 @@ var Properties = []Property{
TimeoutStartUSec, TimeoutStartUSec,
TimeoutStopFailureMode, TimeoutStopFailureMode,
TimeoutStopUSec, TimeoutStopUSec,
TimeoutUSec,
TimerSlackNSec, TimerSlackNSec,
Timestamping,
Transient, Transient,
Transparent,
TriggerLimitBurst,
TriggerLimitIntervalUSec,
Triggers,
Type, Type,
UID, UID,
UMask, UMask,
@@ -243,4 +326,5 @@ var Properties = []Property{
WatchdogSignal, WatchdogSignal,
WatchdogTimestampMonotonic, WatchdogTimestampMonotonic,
WatchdogUSec, WatchdogUSec,
Writable,
} }

View File

@@ -1,7 +1,7 @@
//go:build linux
package systemctl package systemctl
import "strings"
type Options struct { type Options struct {
UserMode bool UserMode bool
} }
@@ -13,3 +13,30 @@ type Unit struct {
Sub string Sub string
Description string Description string
} }
// UnitTypes contains all valid systemd unit type suffixes.
var UnitTypes = []string{
"automount",
"device",
"mount",
"path",
"scope",
"service",
"slice",
"snapshot",
"socket",
"swap",
"target",
"timer",
}
// HasValidUnitSuffix checks whether the given unit name ends with a valid
// systemd unit type suffix (e.g. ".service", ".timer").
func HasValidUnitSuffix(unit string) bool {
for _, t := range UnitTypes {
if strings.HasSuffix(unit, "."+t) {
return true
}
}
return false
}

View File

@@ -1,11 +1,7 @@
//go:build linux
package systemctl package systemctl
import ( import (
"context" "context"
"regexp"
"strings"
"github.com/taigrr/systemctl/properties" "github.com/taigrr/systemctl/properties"
) )
@@ -17,12 +13,7 @@ import (
// reloaded, all sockets systemd listens on behalf of user configuration will // reloaded, all sockets systemd listens on behalf of user configuration will
// stay accessible. // stay accessible.
func DaemonReload(ctx context.Context, opts Options) error { func DaemonReload(ctx context.Context, opts Options) error {
args := []string{"daemon-reload", "--system"} return daemonReload(ctx, opts)
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
} }
// Reenables one or more units. // Reenables one or more units.
@@ -31,12 +22,7 @@ func DaemonReload(ctx context.Context, opts Options) error {
// the unit configuration directory, then recreates the symlink to the unit again, // the unit configuration directory, then recreates the symlink to the unit again,
// atomically. Can be used to change the symlink target. // atomically. Can be used to change the symlink target.
func Reenable(ctx context.Context, unit string, opts Options) error { func Reenable(ctx context.Context, unit string, opts Options) error {
args := []string{"reenable", "--system", unit} return reenable(ctx, unit, opts)
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
} }
// Disables one or more units. // Disables one or more units.
@@ -45,12 +31,7 @@ func Reenable(ctx context.Context, unit string, opts Options) error {
// the unit configuration directory, and hence undoes any changes made by // the unit configuration directory, and hence undoes any changes made by
// enable or link. // enable or link.
func Disable(ctx context.Context, unit string, opts Options) error { func Disable(ctx context.Context, unit string, opts Options) error {
args := []string{"disable", "--system", unit} return disable(ctx, unit, opts)
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
} }
// Enable one or more units or unit instances. // Enable one or more units or unit instances.
@@ -60,12 +41,7 @@ func Disable(ctx context.Context, unit string, opts Options) error {
// manager configuration is reloaded (in a way equivalent to daemon-reload), // manager configuration is reloaded (in a way equivalent to daemon-reload),
// in order to ensure the changes are taken into account immediately. // in order to ensure the changes are taken into account immediately.
func Enable(ctx context.Context, unit string, opts Options) error { func Enable(ctx context.Context, unit string, opts Options) error {
args := []string{"enable", "--system", unit} return enable(ctx, unit, opts)
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
} }
// Check whether any of the specified units are active (i.e. running). // Check whether any of the specified units are active (i.e. running).
@@ -73,24 +49,8 @@ func Enable(ctx context.Context, unit string, opts Options) error {
// Returns true if the unit is active, false if inactive or failed. // Returns true if the unit is active, false if inactive or failed.
// Also returns false in an error case. // Also returns false in an error case.
func IsActive(ctx context.Context, unit string, opts Options) (bool, error) { func IsActive(ctx context.Context, unit string, opts Options) (bool, error) {
args := []string{"is-active", "--system", unit} result, err := isActive(ctx, unit, opts)
if opts.UserMode { return result, err
args[1] = "--user"
}
stdout, _, _, err := execute(ctx, args)
stdout = strings.TrimSuffix(stdout, "\n")
switch stdout {
case "inactive":
return false, nil
case "active":
return true, nil
case "failed":
return false, nil
case "activating":
return false, nil
default:
return false, err
}
} }
// Checks whether any of the specified unit files are enabled (as with enable). // Checks whether any of the specified unit files are enabled (as with enable).
@@ -103,59 +63,14 @@ func IsActive(ctx context.Context, unit string, opts Options) (bool, error) {
// See https://www.freedesktop.org/software/systemd/man/systemctl.html#is-enabled%20UNIT%E2%80%A6 // See https://www.freedesktop.org/software/systemd/man/systemctl.html#is-enabled%20UNIT%E2%80%A6
// for more information // for more information
func IsEnabled(ctx context.Context, unit string, opts Options) (bool, error) { func IsEnabled(ctx context.Context, unit string, opts Options) (bool, error) {
args := []string{"is-enabled", "--system", unit} result, err := isEnabled(ctx, unit, opts)
if opts.UserMode { return result, err
args[1] = "--user"
}
stdout, _, _, err := execute(ctx, args)
stdout = strings.TrimSuffix(stdout, "\n")
switch stdout {
case "enabled":
return true, nil
case "enabled-runtime":
return true, nil
case "linked":
return false, ErrLinked
case "linked-runtime":
return false, ErrLinked
case "alias":
return true, nil
case "masked":
return false, ErrMasked
case "masked-runtime":
return false, ErrMasked
case "static":
return true, nil
case "indirect":
return true, nil
case "disabled":
return false, nil
case "generated":
return true, nil
case "transient":
return true, nil
}
if err != nil {
return false, err
}
return false, ErrUnspecified
} }
// Check whether any of the specified units are in a "failed" state. // Check whether any of the specified units are in a "failed" state.
func IsFailed(ctx context.Context, unit string, opts Options) (bool, error) { func IsFailed(ctx context.Context, unit string, opts Options) (bool, error) {
args := []string{"is-failed", "--system", unit} result, err := isFailed(ctx, unit, opts)
if opts.UserMode { return result, err
args[1] = "--user"
}
stdout, _, _, err := execute(ctx, args)
if matched, _ := regexp.MatchString(`inactive`, stdout); matched {
return false, nil
} else if matched, _ := regexp.MatchString(`active`, stdout); matched {
return false, nil
} else if matched, _ := regexp.MatchString(`failed`, stdout); matched {
return true, nil
}
return false, err
} }
// Mask one or more units, as specified on the command line. This will link // Mask one or more units, as specified on the command line. This will link
@@ -165,70 +80,40 @@ func IsFailed(ctx context.Context, unit string, opts Options) (bool, error) {
// continue masking anyway. Calling Mask on a non-existing masked unit does not // continue masking anyway. Calling Mask on a non-existing masked unit does not
// return an error. Similarly, see Unmask. // return an error. Similarly, see Unmask.
func Mask(ctx context.Context, unit string, opts Options) error { func Mask(ctx context.Context, unit string, opts Options) error {
args := []string{"mask", "--system", unit} return mask(ctx, unit, opts)
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
} }
// Stop and then start one or more units specified on the command line. // Stop and then start one or more units specified on the command line.
// If the units are not running yet, they will be started. // If the units are not running yet, they will be started.
func Restart(ctx context.Context, unit string, opts Options) error { func Restart(ctx context.Context, unit string, opts Options) error {
args := []string{"restart", "--system", unit} return restart(ctx, unit, opts)
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
} }
// Show a selected property of a unit. Accepted properties are predefined in the // Show a selected property of a unit. Accepted properties are predefined in the
// properties subpackage to guarantee properties are valid and assist code-completion. // properties subpackage to guarantee properties are valid and assist code-completion.
func Show(ctx context.Context, unit string, property properties.Property, opts Options) (string, error) { func Show(ctx context.Context, unit string, property properties.Property, opts Options) (string, error) {
args := []string{"show", "--system", unit, "--property", string(property)} str, err := show(ctx, unit, property, opts)
if opts.UserMode { return str, err
args[1] = "--user"
}
stdout, _, _, err := execute(ctx, args)
stdout = strings.TrimPrefix(stdout, string(property)+"=")
stdout = strings.TrimSuffix(stdout, "\n")
return stdout, err
} }
// Start (activate) a given unit // Start (activate) a given unit
func Start(ctx context.Context, unit string, opts Options) error { func Start(ctx context.Context, unit string, opts Options) error {
args := []string{"start", "--system", unit} return start(ctx, unit, opts)
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
} }
// Get back the status string which would be returned by running // Get back the status string which would be returned by running
// `systemctl status [unit]`. // `systemctl status [unit]`.
// //
// Generally, it makes more sense to programatically retrieve the properties // Generally, it makes more sense to programmatically retrieve the properties
// using Show, but this command is provided for the sake of completeness // using Show, but this command is provided for the sake of completeness
func Status(ctx context.Context, unit string, opts Options) (string, error) { func Status(ctx context.Context, unit string, opts Options) (string, error) {
args := []string{"status", "--system", unit} stat, err := status(ctx, unit, opts)
if opts.UserMode { return stat, err
args[1] = "--user"
}
stdout, _, _, err := execute(ctx, args)
return stdout, err
} }
// Stop (deactivate) a given unit // Stop (deactivate) a given unit
func Stop(ctx context.Context, unit string, opts Options) error { func Stop(ctx context.Context, unit string, opts Options) error {
args := []string{"stop", "--system", unit} return stop(ctx, unit, opts)
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
} }
// Unmask one or more unit files, as specified on the command line. // Unmask one or more unit files, as specified on the command line.
@@ -239,10 +124,5 @@ func Stop(ctx context.Context, unit string, opts Options) error {
// If the unit doesn't exist but it's masked anyway, no error will be // If the unit doesn't exist but it's masked anyway, no error will be
// returned. Gross, I know. Take it up with Poettering. // returned. Gross, I know. Take it up with Poettering.
func Unmask(ctx context.Context, unit string, opts Options) error { func Unmask(ctx context.Context, unit string, opts Options) error {
args := []string{"unmask", "--system", unit} return unmask(ctx, unit, opts)
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
} }

65
systemctl_darwin.go Normal file
View File

@@ -0,0 +1,65 @@
//go:build !linux
package systemctl
import (
"context"
"github.com/taigrr/systemctl/properties"
)
func daemonReload(ctx context.Context, opts Options) error {
return nil
}
func reenable(ctx context.Context, unit string, opts Options) error {
return nil
}
func disable(ctx context.Context, unit string, opts Options) error {
return nil
}
func enable(ctx context.Context, unit string, opts Options) error {
return nil
}
func isActive(ctx context.Context, unit string, opts Options) (bool, error) {
return false, nil
}
func isEnabled(ctx context.Context, unit string, opts Options) (bool, error) {
return false, nil
}
func isFailed(ctx context.Context, unit string, opts Options) (bool, error) {
return false, nil
}
func mask(ctx context.Context, unit string, opts Options) error {
return nil
}
func restart(ctx context.Context, unit string, opts Options) error {
return nil
}
func show(ctx context.Context, unit string, property properties.Property, opts Options) (string, error) {
return "", nil
}
func start(ctx context.Context, unit string, opts Options) error {
return nil
}
func status(ctx context.Context, unit string, opts Options) (string, error) {
return "", nil
}
func stop(ctx context.Context, unit string, opts Options) error {
return nil
}
func unmask(ctx context.Context, unit string, opts Options) error {
return nil
}

250
systemctl_linux.go Normal file
View File

@@ -0,0 +1,250 @@
//go:build linux
package systemctl
import (
"context"
"strings"
"github.com/taigrr/systemctl/properties"
)
// Reload systemd manager configuration.
//
// This will rerun all generators (see systemd. generator(7)), reload all unit
// files, and recreate the entire dependency tree. While the daemon is being
// reloaded, all sockets systemd listens on behalf of user configuration will
// stay accessible.
func daemonReload(ctx context.Context, opts Options) error {
args := []string{"daemon-reload", "--system"}
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
}
// Reenables one or more units.
//
// This removes all symlinks to the unit files backing the specified units from
// the unit configuration directory, then recreates the symlink to the unit again,
// atomically. Can be used to change the symlink target.
func reenable(ctx context.Context, unit string, opts Options) error {
args := []string{"reenable", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
}
// Disables one or more units.
//
// This removes all symlinks to the unit files backing the specified units from
// the unit configuration directory, and hence undoes any changes made by
// enable or link.
func disable(ctx context.Context, unit string, opts Options) error {
args := []string{"disable", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
}
// Enable one or more units or unit instances.
//
// This will create a set of symlinks, as encoded in the [Install] sections of
// the indicated unit files. After the symlinks have been created, the system
// manager configuration is reloaded (in a way equivalent to daemon-reload),
// in order to ensure the changes are taken into account immediately.
func enable(ctx context.Context, unit string, opts Options) error {
args := []string{"enable", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
}
// Check whether any of the specified units are active (i.e. running).
//
// Returns true if the unit is active, false if inactive or failed.
// Also returns false in an error case.
func isActive(ctx context.Context, unit string, opts Options) (bool, error) {
args := []string{"is-active", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
stdout, _, _, err := execute(ctx, args)
stdout = strings.TrimSuffix(stdout, "\n")
switch stdout {
case "inactive":
return false, nil
case "active":
return true, nil
case "failed":
return false, nil
case "activating":
return false, nil
default:
return false, err
}
}
// Checks whether any of the specified unit files are enabled (as with enable).
//
// Returns true if the unit is enabled, aliased, static, indirect, generated
// or transient.
//
// Returns false if disabled. Also returns an error if linked, masked, or bad.
//
// See https://www.freedesktop.org/software/systemd/man/systemctl.html#is-enabled%20UNIT%E2%80%A6
// for more information
func isEnabled(ctx context.Context, unit string, opts Options) (bool, error) {
args := []string{"is-enabled", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
stdout, _, _, err := execute(ctx, args)
stdout = strings.TrimSuffix(stdout, "\n")
switch stdout {
case "enabled":
return true, nil
case "enabled-runtime":
return true, nil
case "linked":
return false, ErrLinked
case "linked-runtime":
return false, ErrLinked
case "alias":
return true, nil
case "masked":
return false, ErrMasked
case "masked-runtime":
return false, ErrMasked
case "static":
return true, nil
case "indirect":
return true, nil
case "disabled":
return false, nil
case "generated":
return true, nil
case "transient":
return true, nil
}
if err != nil {
return false, err
}
return false, ErrUnspecified
}
// Check whether any of the specified units are in a "failed" state.
func isFailed(ctx context.Context, unit string, opts Options) (bool, error) {
args := []string{"is-failed", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
stdout, _, _, err := execute(ctx, args)
stdout = strings.TrimSuffix(stdout, "\n")
switch stdout {
case "inactive":
return false, nil
case "active":
return false, nil
case "failed":
return true, nil
default:
return false, err
}
}
// Mask one or more units, as specified on the command line. This will link
// these unit files to /dev/null, making it impossible to start them.
//
// Notably, Mask may return ErrDoesNotExist if a unit doesn't exist, but it will
// continue masking anyway. Calling Mask on a non-existing masked unit does not
// return an error. Similarly, see Unmask.
func mask(ctx context.Context, unit string, opts Options) error {
args := []string{"mask", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
}
// Stop and then start one or more units specified on the command line.
// If the units are not running yet, they will be started.
func restart(ctx context.Context, unit string, opts Options) error {
args := []string{"restart", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
}
// Show a selected property of a unit. Accepted properties are predefined in the
// properties subpackage to guarantee properties are valid and assist code-completion.
func show(ctx context.Context, unit string, property properties.Property, opts Options) (string, error) {
args := []string{"show", "--system", unit, "--property", string(property)}
if opts.UserMode {
args[1] = "--user"
}
stdout, _, _, err := execute(ctx, args)
stdout = strings.TrimPrefix(stdout, string(property)+"=")
stdout = strings.TrimSuffix(stdout, "\n")
return stdout, err
}
// Start (activate) a given unit
func start(ctx context.Context, unit string, opts Options) error {
args := []string{"start", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
}
// Get back the status string which would be returned by running
// `systemctl status [unit]`.
//
// Generally, it makes more sense to programmatically retrieve the properties
// using Show, but this command is provided for the sake of completeness
func status(ctx context.Context, unit string, opts Options) (string, error) {
args := []string{"status", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
stdout, _, _, err := execute(ctx, args)
return stdout, err
}
// Stop (deactivate) a given unit
func stop(ctx context.Context, unit string, opts Options) error {
args := []string{"stop", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
}
// Unmask one or more unit files, as specified on the command line.
// This will undo the effect of Mask.
//
// In line with systemd, Unmask will return ErrDoesNotExist if the unit
// doesn't exist, but only if it's not already masked.
// If the unit doesn't exist but it's masked anyway, no error will be
// returned. Gross, I know. Take it up with Poettering.
func unmask(ctx context.Context, unit string, opts Options) error {
args := []string{"unmask", "--system", unit}
if opts.UserMode {
args[1] = "--user"
}
_, _, _, err := execute(ctx, args)
return err
}

View File

@@ -1,5 +1,3 @@
//go:build linux
package systemctl package systemctl
import ( import (
@@ -13,6 +11,7 @@ import (
var systemctl string var systemctl string
// killed is the exit code returned when a process is terminated by SIGINT.
const killed = 130 const killed = 130
func init() { func init() {
@@ -41,6 +40,10 @@ func execute(ctx context.Context, args []string) (string, string, int, error) {
warnings = stderr.String() warnings = stderr.String()
code = cmd.ProcessState.ExitCode() code = cmd.ProcessState.ExitCode()
if code == killed {
return output, warnings, code, ErrExecTimeout
}
customErr := filterErr(warnings) customErr := filterErr(warnings)
if customErr != nil { if customErr != nil {
err = customErr err = customErr