8 Commits

Author SHA1 Message Date
d38136c0dc refactor: clean up unused code, fix typos, improve docs (#7)
* 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

* 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 00:01:59 -05: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
21fce7918e add linux build tags to restrict compilation to linux targets 2025-02-13 16:49:49 -08:00
54f4f7a235 add IsSystemd checker 2025-02-12 19:21:48 -08:00
10 changed files with 823 additions and 386 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

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

@@ -3,6 +3,7 @@ package systemctl
import ( import (
"context" "context"
"errors" "errors"
"os"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -34,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 {
@@ -55,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 {
@@ -83,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 {
@@ -112,7 +141,16 @@ func GetMaskedUnits(ctx context.Context, opts Options) ([]string, error) {
return units, nil return units, nil
} }
// check if a service is masked // IsSystemd checks if systemd is the current init system by reading /proc/1/comm.
func IsSystemd() (bool, error) {
b, err := os.ReadFile("/proc/1/comm")
if err != nil {
return false, err
}
return strings.TrimSpace(string(b)) == "systemd", nil
}
// 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 {
@@ -126,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,246 +3,330 @@ package properties
type Property string type Property string
const ( const (
ActiveEnterTimestamp Property = "ActiveEnterTimestamp" Accept Property = "Accept"
ActiveEnterTimestampMonotonic Property = "ActiveEnterTimestampMonotonic" ActiveEnterTimestamp Property = "ActiveEnterTimestamp"
ActiveExitTimestampMonotonic Property = "ActiveExitTimestampMonotonic" ActiveEnterTimestampMonotonic Property = "ActiveEnterTimestampMonotonic"
ActiveState Property = "ActiveState" ActiveExitTimestampMonotonic Property = "ActiveExitTimestampMonotonic"
After Property = "After" ActiveState Property = "ActiveState"
AllowIsolate Property = "AllowIsolate" After Property = "After"
AssertResult Property = "AssertResult" AllowIsolate Property = "AllowIsolate"
AssertTimestamp Property = "AssertTimestamp" AssertResult Property = "AssertResult"
AssertTimestampMonotonic Property = "AssertTimestampMonotonic" AssertTimestamp Property = "AssertTimestamp"
Before Property = "Before" AssertTimestampMonotonic Property = "AssertTimestampMonotonic"
BlockIOAccounting Property = "BlockIOAccounting" Backlog Property = "Backlog"
BlockIOWeight Property = "BlockIOWeight" Before Property = "Before"
CPUAccounting Property = "CPUAccounting" BindIPv6Only Property = "BindIPv6Only"
CPUAffinityFromNUMA Property = "CPUAffinityFromNUMA" BindLogSockets Property = "BindLogSockets"
CPUQuotaPerSecUSec Property = "CPUQuotaPerSecUSec" BlockIOAccounting Property = "BlockIOAccounting"
CPUQuotaPeriodUSec Property = "CPUQuotaPeriodUSec" BlockIOWeight Property = "BlockIOWeight"
CPUSchedulingPolicy Property = "CPUSchedulingPolicy" Broadcast Property = "Broadcast"
CPUSchedulingPriority Property = "CPUSchedulingPriority" CPUAccounting Property = "CPUAccounting"
CPUSchedulingResetOnFork Property = "CPUSchedulingResetOnFork" CPUAffinityFromNUMA Property = "CPUAffinityFromNUMA"
CPUShares Property = "CPUShares" CPUQuotaPerSecUSec Property = "CPUQuotaPerSecUSec"
CPUUsageNSec Property = "CPUUsageNSec" CPUQuotaPeriodUSec Property = "CPUQuotaPeriodUSec"
CPUWeight Property = "CPUWeight" CPUSchedulingPolicy Property = "CPUSchedulingPolicy"
CacheDirectoryMode Property = "CacheDirectoryMode" CPUSchedulingPriority Property = "CPUSchedulingPriority"
CanFreeze Property = "CanFreeze" CPUSchedulingResetOnFork Property = "CPUSchedulingResetOnFork"
CanIsolate Property = "CanIsolate" CPUShares Property = "CPUShares"
CanReload Property = "CanReload" CPUUsageNSec Property = "CPUUsageNSec"
CanStart Property = "CanStart" CPUWeight Property = "CPUWeight"
CanStop Property = "CanStop" CacheDirectoryMode Property = "CacheDirectoryMode"
CapabilityBoundingSet Property = "CapabilityBoundingSet" CanFreeze Property = "CanFreeze"
CleanResult Property = "CleanResult" CanIsolate Property = "CanIsolate"
CollectMode Property = "CollectMode" CanLiveMount Property = "CanLiveMount"
ConditionResult Property = "ConditionResult" CanReload Property = "CanReload"
ConditionTimestamp Property = "ConditionTimestamp" CanStart Property = "CanStart"
ConditionTimestampMonotonic Property = "ConditionTimestampMonotonic" CanStop Property = "CanStop"
ConfigurationDirectoryMode Property = "ConfigurationDirectoryMode" CapabilityBoundingSet Property = "CapabilityBoundingSet"
Conflicts Property = "Conflicts" CleanResult Property = "CleanResult"
ControlGroup Property = "ControlGroup" CollectMode Property = "CollectMode"
ControlPID Property = "ControlPID" ConditionResult Property = "ConditionResult"
CoredumpFilter Property = "CoredumpFilter" ConditionTimestamp Property = "ConditionTimestamp"
DefaultDependencies Property = "DefaultDependencies" ConditionTimestampMonotonic Property = "ConditionTimestampMonotonic"
DefaultMemoryLow Property = "DefaultMemoryLow" ConfigurationDirectoryMode Property = "ConfigurationDirectoryMode"
DefaultMemoryMin Property = "DefaultMemoryMin" Conflicts Property = "Conflicts"
Delegate Property = "Delegate" ControlGroup Property = "ControlGroup"
Description Property = "Description" ControlGroupId Property = "ControlGroupId"
DevicePolicy Property = "DevicePolicy" ControlPID Property = "ControlPID"
DynamicUser Property = "DynamicUser" CoredumpFilter Property = "CoredumpFilter"
EffectiveCPUs Property = "EffectiveCPUs" CoredumpReceive Property = "CoredumpReceive"
EffectiveMemoryNodes Property = "EffectiveMemoryNodes" DebugInvocation Property = "DebugInvocation"
ExecMainCode Property = "ExecMainCode" DefaultDependencies Property = "DefaultDependencies"
ExecMainExitTimestampMonotonic Property = "ExecMainExitTimestampMonotonic" DefaultMemoryLow Property = "DefaultMemoryLow"
ExecMainPID Property = "ExecMainPID" DefaultMemoryMin Property = "DefaultMemoryMin"
ExecMainStartTimestamp Property = "ExecMainStartTimestamp" DefaultStartupMemoryLow Property = "DefaultStartupMemoryLow"
ExecMainStartTimestampMonotonic Property = "ExecMainStartTimestampMonotonic" DeferAcceptUSec Property = "DeferAcceptUSec"
ExecMainStatus Property = "ExecMainStatus" Delegate Property = "Delegate"
ExecReload Property = "ExecReload" Description Property = "Description"
ExecReloadEx Property = "ExecReloadEx" DevicePolicy Property = "DevicePolicy"
ExecStart Property = "ExecStart" DirectoryMode Property = "DirectoryMode"
ExecStartEx Property = "ExecStartEx" DynamicUser Property = "DynamicUser"
FailureAction Property = "FailureAction" EffectiveCPUs Property = "EffectiveCPUs"
FileDescriptorStoreMax Property = "FileDescriptorStoreMax" EffectiveMemoryHigh Property = "EffectiveMemoryHigh"
FinalKillSignal Property = "FinalKillSignal" EffectiveMemoryMax Property = "EffectiveMemoryMax"
FragmentPath Property = "FragmentPath" EffectiveMemoryNodes Property = "EffectiveMemoryNodes"
FreezerState Property = "FreezerState" EffectiveTasksMax Property = "EffectiveTasksMax"
GID Property = "GID" ExecMainCode Property = "ExecMainCode"
GuessMainPID Property = "GuessMainPID" ExecMainExitTimestampMonotonic Property = "ExecMainExitTimestampMonotonic"
IOAccounting Property = "IOAccounting" ExecMainPID Property = "ExecMainPID"
IOReadBytes Property = "IOReadBytes" ExecMainStartTimestamp Property = "ExecMainStartTimestamp"
IOReadOperations Property = "IOReadOperations" ExecMainStartTimestampMonotonic Property = "ExecMainStartTimestampMonotonic"
IOSchedulingClass Property = "IOSchedulingClass" ExecMainStatus Property = "ExecMainStatus"
IOSchedulingPriority Property = "IOSchedulingPriority" ExecReload Property = "ExecReload"
IOWeight Property = "IOWeight" ExecReloadEx Property = "ExecReloadEx"
IOWriteBytes Property = "IOWriteBytes" ExecStart Property = "ExecStart"
IOWriteOperations Property = "IOWriteOperations" ExecStartEx Property = "ExecStartEx"
IPAccounting Property = "IPAccounting" ExtensionImagePolicy Property = "ExtensionImagePolicy"
IPEgressBytes Property = "IPEgressBytes" FailureAction Property = "FailureAction"
IPEgressPackets Property = "IPEgressPackets" FileDescriptorName Property = "FileDescriptorName"
IPIngressBytes Property = "IPIngressBytes" FileDescriptorStoreMax Property = "FileDescriptorStoreMax"
IPIngressPackets Property = "IPIngressPackets" FinalKillSignal Property = "FinalKillSignal"
Id Property = "Id" FlushPending Property = "FlushPending"
IgnoreOnIsolate Property = "IgnoreOnIsolate" FragmentPath Property = "FragmentPath"
IgnoreSIGPIPE Property = "IgnoreSIGPIPE" FreeBind Property = "FreeBind"
InactiveEnterTimestampMonotonic Property = "InactiveEnterTimestampMonotonic" FreezerState Property = "FreezerState"
InactiveExitTimestamp Property = "InactiveExitTimestamp" GID Property = "GID"
InactiveExitTimestampMonotonic Property = "InactiveExitTimestampMonotonic" GuessMainPID Property = "GuessMainPID"
InvocationID Property = "InvocationID" IOAccounting Property = "IOAccounting"
JobRunningTimeoutUSec Property = "JobRunningTimeoutUSec" IOReadBytes Property = "IOReadBytes"
JobTimeoutAction Property = "JobTimeoutAction" IOReadOperations Property = "IOReadOperations"
JobTimeoutUSec Property = "JobTimeoutUSec" IOSchedulingClass Property = "IOSchedulingClass"
KeyringMode Property = "KeyringMode" IOSchedulingPriority Property = "IOSchedulingPriority"
KillMode Property = "KillMode" IOWeight Property = "IOWeight"
KillSignal Property = "KillSignal" IOWriteBytes Property = "IOWriteBytes"
LimitAS Property = "LimitAS" IOWriteOperations Property = "IOWriteOperations"
LimitASSoft Property = "LimitASSoft" IPAccounting Property = "IPAccounting"
LimitCORE Property = "LimitCORE" IPEgressBytes Property = "IPEgressBytes"
LimitCORESoft Property = "LimitCORESoft" IPEgressPackets Property = "IPEgressPackets"
LimitCPU Property = "LimitCPU" IPIngressBytes Property = "IPIngressBytes"
LimitCPUSoft Property = "LimitCPUSoft" IPIngressPackets Property = "IPIngressPackets"
LimitDATA Property = "LimitDATA" IPTOS Property = "IPTOS"
LimitDATASoft Property = "LimitDATASoft" IPTTL Property = "IPTTL"
LimitFSIZE Property = "LimitFSIZE" Id Property = "Id"
LimitFSIZESoft Property = "LimitFSIZESoft" IgnoreOnIsolate Property = "IgnoreOnIsolate"
LimitLOCKS Property = "LimitLOCKS" IgnoreSIGPIPE Property = "IgnoreSIGPIPE"
LimitLOCKSSoft Property = "LimitLOCKSSoft" InactiveEnterTimestampMonotonic Property = "InactiveEnterTimestampMonotonic"
LimitMEMLOCK Property = "LimitMEMLOCK" InactiveExitTimestamp Property = "InactiveExitTimestamp"
LimitMEMLOCKSoft Property = "LimitMEMLOCKSoft" InactiveExitTimestampMonotonic Property = "InactiveExitTimestampMonotonic"
LimitMSGQUEUE Property = "LimitMSGQUEUE" InvocationID Property = "InvocationID"
LimitMSGQUEUESoft Property = "LimitMSGQUEUESoft" JobRunningTimeoutUSec Property = "JobRunningTimeoutUSec"
LimitNICE Property = "LimitNICE" JobTimeoutAction Property = "JobTimeoutAction"
LimitNICESoft Property = "LimitNICESoft" JobTimeoutUSec Property = "JobTimeoutUSec"
LimitNOFILE Property = "LimitNOFILE" KeepAlive Property = "KeepAlive"
LimitNOFILESoft Property = "LimitNOFILESoft" KeepAliveIntervalUSec Property = "KeepAliveIntervalUSec"
LimitNPROC Property = "LimitNPROC" KeepAliveProbes Property = "KeepAliveProbes"
LimitNPROCSoft Property = "LimitNPROCSoft" KeepAliveTimeUSec Property = "KeepAliveTimeUSec"
LimitRSS Property = "LimitRSS" KeyringMode Property = "KeyringMode"
LimitRSSSoft Property = "LimitRSSSoft" KillMode Property = "KillMode"
LimitRTPRIO Property = "LimitRTPRIO" KillSignal Property = "KillSignal"
LimitRTPRIOSoft Property = "LimitRTPRIOSoft" LimitAS Property = "LimitAS"
LimitRTTIME Property = "LimitRTTIME" LimitASSoft Property = "LimitASSoft"
LimitRTTIMESoft Property = "LimitRTTIMESoft" LimitCORE Property = "LimitCORE"
LimitSIGPENDING Property = "LimitSIGPENDING" LimitCORESoft Property = "LimitCORESoft"
LimitSIGPENDINGSoft Property = "LimitSIGPENDINGSoft" LimitCPU Property = "LimitCPU"
LimitSTACK Property = "LimitSTACK" LimitCPUSoft Property = "LimitCPUSoft"
LimitSTACKSoft Property = "LimitSTACKSoft" LimitDATA Property = "LimitDATA"
LoadState Property = "LoadState" LimitDATASoft Property = "LimitDATASoft"
LockPersonality Property = "LockPersonality" LimitFSIZE Property = "LimitFSIZE"
LogLevelMax Property = "LogLevelMax" LimitFSIZESoft Property = "LimitFSIZESoft"
LogRateLimitBurst Property = "LogRateLimitBurst" LimitLOCKS Property = "LimitLOCKS"
LogRateLimitIntervalUSec Property = "LogRateLimitIntervalUSec" LimitLOCKSSoft Property = "LimitLOCKSSoft"
LogsDirectoryMode Property = "LogsDirectoryMode" LimitMEMLOCK Property = "LimitMEMLOCK"
MainPID Property = "MainPID" LimitMEMLOCKSoft Property = "LimitMEMLOCKSoft"
ManagedOOMMemoryPressure Property = "ManagedOOMMemoryPressure" LimitMSGQUEUE Property = "LimitMSGQUEUE"
ManagedOOMMemoryPressureLimit Property = "ManagedOOMMemoryPressureLimit" LimitMSGQUEUESoft Property = "LimitMSGQUEUESoft"
ManagedOOMPreference Property = "ManagedOOMPreference" LimitNICE Property = "LimitNICE"
ManagedOOMSwap Property = "ManagedOOMSwap" LimitNICESoft Property = "LimitNICESoft"
MemoryAccounting Property = "MemoryAccounting" LimitNOFILE Property = "LimitNOFILE"
MemoryCurrent Property = "MemoryCurrent" LimitNOFILESoft Property = "LimitNOFILESoft"
MemoryDenyWriteExecute Property = "MemoryDenyWriteExecute" LimitNPROC Property = "LimitNPROC"
MemoryHigh Property = "MemoryHigh" LimitNPROCSoft Property = "LimitNPROCSoft"
MemoryLimit Property = "MemoryLimit" LimitRSS Property = "LimitRSS"
MemoryLow Property = "MemoryLow" LimitRSSSoft Property = "LimitRSSSoft"
MemoryMax Property = "MemoryMax" LimitRTPRIO Property = "LimitRTPRIO"
MemoryMin Property = "MemoryMin" LimitRTPRIOSoft Property = "LimitRTPRIOSoft"
MemorySwapMax Property = "MemorySwapMax" LimitRTTIME Property = "LimitRTTIME"
MountAPIVFS Property = "MountAPIVFS" LimitRTTIMESoft Property = "LimitRTTIMESoft"
NFileDescriptorStore Property = "NFileDescriptorStore" LimitSIGPENDING Property = "LimitSIGPENDING"
NRestarts Property = "NRestarts" LimitSIGPENDINGSoft Property = "LimitSIGPENDINGSoft"
NUMAPolicy Property = "NUMAPolicy" LimitSTACK Property = "LimitSTACK"
Names Property = "Names" LimitSTACKSoft Property = "LimitSTACKSoft"
NeedDaemonReload Property = "NeedDaemonReload" Listen Property = "Listen"
Nice Property = "Nice" LoadState Property = "LoadState"
NoNewPrivileges Property = "NoNewPrivileges" LockPersonality Property = "LockPersonality"
NonBlocking Property = "NonBlocking" LogLevelMax Property = "LogLevelMax"
NotifyAccess Property = "NotifyAccess" LogRateLimitBurst Property = "LogRateLimitBurst"
OOMPolicy Property = "OOMPolicy" LogRateLimitIntervalUSec Property = "LogRateLimitIntervalUSec"
OOMScoreAdjust Property = "OOMScoreAdjust" LogsDirectoryMode Property = "LogsDirectoryMode"
OnFailureJobMode Property = "OnFailureJobMode" MainPID Property = "MainPID"
PIDFile Property = "PIDFile" ManagedOOMMemoryPressure Property = "ManagedOOMMemoryPressure"
Perpetual Property = "Perpetual" ManagedOOMMemoryPressureDurationUSec Property = "ManagedOOMMemoryPressureDurationUSec"
PrivateDevices Property = "PrivateDevices" ManagedOOMMemoryPressureLimit Property = "ManagedOOMMemoryPressureLimit"
PrivateIPC Property = "PrivateIPC" ManagedOOMPreference Property = "ManagedOOMPreference"
PrivateMounts Property = "PrivateMounts" ManagedOOMSwap Property = "ManagedOOMSwap"
PrivateNetwork Property = "PrivateNetwork" Mark Property = "Mark"
PrivateTmp Property = "PrivateTmp" MaxConnections Property = "MaxConnections"
PrivateUsers Property = "PrivateUsers" MaxConnectionsPerSource Property = "MaxConnectionsPerSource"
ProcSubset Property = "ProcSubset" MemoryAccounting Property = "MemoryAccounting"
ProtectClock Property = "ProtectClock" MemoryAvailable Property = "MemoryAvailable"
ProtectControlGroups Property = "ProtectControlGroups" MemoryCurrent Property = "MemoryCurrent"
ProtectHome Property = "ProtectHome" MemoryDenyWriteExecute Property = "MemoryDenyWriteExecute"
ProtectHostname Property = "ProtectHostname" MemoryHigh Property = "MemoryHigh"
ProtectKernelLogs Property = "ProtectKernelLogs" MemoryKSM Property = "MemoryKSM"
ProtectKernelModules Property = "ProtectKernelModules" MemoryLimit Property = "MemoryLimit"
ProtectKernelTunables Property = "ProtectKernelTunables" MemoryLow Property = "MemoryLow"
ProtectProc Property = "ProtectProc" MemoryMax Property = "MemoryMax"
ProtectSystem Property = "ProtectSystem" MemoryMin Property = "MemoryMin"
RefuseManualStart Property = "RefuseManualStart" MemoryPeak Property = "MemoryPeak"
RefuseManualStop Property = "RefuseManualStop" MemoryPressureThresholdUSec Property = "MemoryPressureThresholdUSec"
ReloadResult Property = "ReloadResult" MemoryPressureWatch Property = "MemoryPressureWatch"
RemainAfterExit Property = "RemainAfterExit" MemorySwapCurrent Property = "MemorySwapCurrent"
RemoveIPC Property = "RemoveIPC" MemorySwapMax Property = "MemorySwapMax"
Requires Property = "Requires" MemorySwapPeak Property = "MemorySwapPeak"
Restart Property = "Restart" MemoryZSwapCurrent Property = "MemoryZSwapCurrent"
RestartKillSignal Property = "RestartKillSignal" MemoryZSwapMax Property = "MemoryZSwapMax"
RestartUSec Property = "RestartUSec" MemoryZSwapWriteback Property = "MemoryZSwapWriteback"
RestrictNamespaces Property = "RestrictNamespaces" MessageQueueMaxMessages Property = "MessageQueueMaxMessages"
RestrictRealtime Property = "RestrictRealtime" MessageQueueMessageSize Property = "MessageQueueMessageSize"
RestrictSUIDSGID Property = "RestrictSUIDSGID" MountAPIVFS Property = "MountAPIVFS"
Result Property = "Result" MountImagePolicy Property = "MountImagePolicy"
RootDirectoryStartOnly Property = "RootDirectoryStartOnly" NAccepted Property = "NAccepted"
RuntimeDirectoryMode Property = "RuntimeDirectoryMode" NConnections Property = "NConnections"
RuntimeDirectoryPreserve Property = "RuntimeDirectoryPreserve" NFileDescriptorStore Property = "NFileDescriptorStore"
RuntimeMaxUSec Property = "RuntimeMaxUSec" NRefused Property = "NRefused"
SameProcessGroup Property = "SameProcessGroup" NRestarts Property = "NRestarts"
SecureBits Property = "SecureBits" NUMAPolicy Property = "NUMAPolicy"
SendSIGHUP Property = "SendSIGHUP" Names Property = "Names"
SendSIGKILL Property = "SendSIGKILL" NeedDaemonReload Property = "NeedDaemonReload"
Slice Property = "Slice" Nice Property = "Nice"
StandardError Property = "StandardError" NoDelay Property = "NoDelay"
StandardInput Property = "StandardInput" NoNewPrivileges Property = "NoNewPrivileges"
StandardOutput Property = "StandardOutput" NonBlocking Property = "NonBlocking"
StartLimitAction Property = "StartLimitAction" NotifyAccess Property = "NotifyAccess"
StartLimitBurst Property = "StartLimitBurst" OOMPolicy Property = "OOMPolicy"
StartLimitIntervalUSec Property = "StartLimitIntervalUSec" OOMScoreAdjust Property = "OOMScoreAdjust"
StartupBlockIOWeight Property = "StartupBlockIOWeight" OnFailureJobMode Property = "OnFailureJobMode"
StartupCPUShares Property = "StartupCPUShares" OnSuccessJobMode Property = "OnSuccessJobMode"
StartupCPUWeight Property = "StartupCPUWeight" PIDFile Property = "PIDFile"
StartupIOWeight Property = "StartupIOWeight" PassCredentials Property = "PassCredentials"
StateChangeTimestamp Property = "StateChangeTimestamp" PassFileDescriptorsToExec Property = "PassFileDescriptorsToExec"
StateChangeTimestampMonotonic Property = "StateChangeTimestampMonotonic" PassPacketInfo Property = "PassPacketInfo"
StateDirectoryMode Property = "StateDirectoryMode" PassSecurity Property = "PassSecurity"
StatusErrno Property = "StatusErrno" Perpetual Property = "Perpetual"
StopWhenUnneeded Property = "StopWhenUnneeded" PipeSize Property = "PipeSize"
SubState Property = "SubState" PollLimitBurst Property = "PollLimitBurst"
SuccessAction Property = "SuccessAction" PollLimitIntervalUSec Property = "PollLimitIntervalUSec"
SyslogFacility Property = "SyslogFacility" Priority Property = "Priority"
SyslogLevel Property = "SyslogLevel" PrivateDevices Property = "PrivateDevices"
SyslogLevelPrefix Property = "SyslogLevelPrefix" PrivateIPC Property = "PrivateIPC"
SyslogPriority Property = "SyslogPriority" PrivateMounts Property = "PrivateMounts"
SystemCallErrorNumber Property = "SystemCallErrorNumber" PrivateNetwork Property = "PrivateNetwork"
TTYReset Property = "TTYReset" PrivatePIDs Property = "PrivatePIDs"
TTYVHangup Property = "TTYVHangup" PrivateTmp Property = "PrivateTmp"
TTYVTDisallocate Property = "TTYVTDisallocate" PrivateTmpEx Property = "PrivateTmpEx"
TasksAccounting Property = "TasksAccounting" PrivateUsers Property = "PrivateUsers"
TasksCurrent Property = "TasksCurrent" PrivateUsersEx Property = "PrivateUsersEx"
TasksMax Property = "TasksMax" ProcSubset Property = "ProcSubset"
TimeoutAbortUSec Property = "TimeoutAbortUSec" ProtectClock Property = "ProtectClock"
TimeoutCleanUSec Property = "TimeoutCleanUSec" ProtectControlGroups Property = "ProtectControlGroups"
TimeoutStartFailureMode Property = "TimeoutStartFailureMode" ProtectControlGroupsEx Property = "ProtectControlGroupsEx"
TimeoutStartUSec Property = "TimeoutStartUSec" ProtectHome Property = "ProtectHome"
TimeoutStopFailureMode Property = "TimeoutStopFailureMode" ProtectHostname Property = "ProtectHostname"
TimeoutStopUSec Property = "TimeoutStopUSec" ProtectKernelLogs Property = "ProtectKernelLogs"
TimerSlackNSec Property = "TimerSlackNSec" ProtectKernelModules Property = "ProtectKernelModules"
Transient Property = "Transient" ProtectKernelTunables Property = "ProtectKernelTunables"
Type Property = "Type" ProtectProc Property = "ProtectProc"
UID Property = "UID" ProtectSystem Property = "ProtectSystem"
UMask Property = "UMask" ReceiveBuffer Property = "ReceiveBuffer"
UnitFilePreset Property = "UnitFilePreset" RefuseManualStart Property = "RefuseManualStart"
UnitFileState Property = "UnitFileState" RefuseManualStop Property = "RefuseManualStop"
UtmpMode Property = "UtmpMode" ReloadResult Property = "ReloadResult"
WantedBy Property = "WantedBy" RemainAfterExit Property = "RemainAfterExit"
WatchdogSignal Property = "WatchdogSignal" RemoveIPC Property = "RemoveIPC"
WatchdogTimestampMonotonic Property = "WatchdogTimestampMonotonic" RemoveOnStop Property = "RemoveOnStop"
WatchdogUSec Property = "WatchdogUSec" RequiredBy Property = "RequiredBy"
Requires Property = "Requires"
RequiresMountsFor Property = "RequiresMountsFor"
Restart Property = "Restart"
RestartKillSignal Property = "RestartKillSignal"
RestartUSec Property = "RestartUSec"
RestrictNamespaces Property = "RestrictNamespaces"
RestrictRealtime Property = "RestrictRealtime"
RestrictSUIDSGID Property = "RestrictSUIDSGID"
Result Property = "Result"
ReusePort Property = "ReusePort"
RootDirectoryStartOnly Property = "RootDirectoryStartOnly"
RootEphemeral Property = "RootEphemeral"
RootImagePolicy Property = "RootImagePolicy"
RuntimeDirectoryMode Property = "RuntimeDirectoryMode"
RuntimeDirectoryPreserve Property = "RuntimeDirectoryPreserve"
RuntimeMaxUSec Property = "RuntimeMaxUSec"
SameProcessGroup Property = "SameProcessGroup"
SecureBits Property = "SecureBits"
SendBuffer Property = "SendBuffer"
SendSIGHUP Property = "SendSIGHUP"
SendSIGKILL Property = "SendSIGKILL"
SetLoginEnvironment Property = "SetLoginEnvironment"
Slice Property = "Slice"
SocketMode Property = "SocketMode"
SocketProtocol Property = "SocketProtocol"
StandardError Property = "StandardError"
StandardInput Property = "StandardInput"
StandardOutput Property = "StandardOutput"
StartLimitAction Property = "StartLimitAction"
StartLimitBurst Property = "StartLimitBurst"
StartLimitIntervalUSec Property = "StartLimitIntervalUSec"
StartupBlockIOWeight Property = "StartupBlockIOWeight"
StartupCPUShares Property = "StartupCPUShares"
StartupCPUWeight Property = "StartupCPUWeight"
StartupIOWeight Property = "StartupIOWeight"
StartupMemoryHigh Property = "StartupMemoryHigh"
StartupMemoryLow Property = "StartupMemoryLow"
StartupMemoryMax Property = "StartupMemoryMax"
StartupMemorySwapMax Property = "StartupMemorySwapMax"
StartupMemoryZSwapMax Property = "StartupMemoryZSwapMax"
StateChangeTimestamp Property = "StateChangeTimestamp"
StateChangeTimestampMonotonic Property = "StateChangeTimestampMonotonic"
StateDirectoryMode Property = "StateDirectoryMode"
StatusErrno Property = "StatusErrno"
StopWhenUnneeded Property = "StopWhenUnneeded"
SubState Property = "SubState"
SuccessAction Property = "SuccessAction"
SurviveFinalKillSignal Property = "SurviveFinalKillSignal"
SyslogFacility Property = "SyslogFacility"
SyslogLevel Property = "SyslogLevel"
SyslogLevelPrefix Property = "SyslogLevelPrefix"
SyslogPriority Property = "SyslogPriority"
SystemCallErrorNumber Property = "SystemCallErrorNumber"
TTYReset Property = "TTYReset"
TTYVHangup Property = "TTYVHangup"
TTYVTDisallocate Property = "TTYVTDisallocate"
TasksAccounting Property = "TasksAccounting"
TasksCurrent Property = "TasksCurrent"
TasksMax Property = "TasksMax"
TimeoutAbortUSec Property = "TimeoutAbortUSec"
TimeoutCleanUSec Property = "TimeoutCleanUSec"
TimeoutStartFailureMode Property = "TimeoutStartFailureMode"
TimeoutStartUSec Property = "TimeoutStartUSec"
TimeoutStopFailureMode Property = "TimeoutStopFailureMode"
TimeoutStopUSec Property = "TimeoutStopUSec"
TimeoutUSec Property = "TimeoutUSec"
TimerSlackNSec Property = "TimerSlackNSec"
Timestamping Property = "Timestamping"
Transient Property = "Transient"
Transparent Property = "Transparent"
TriggerLimitBurst Property = "TriggerLimitBurst"
TriggerLimitIntervalUSec Property = "TriggerLimitIntervalUSec"
Triggers Property = "Triggers"
Type Property = "Type"
UID Property = "UID"
UMask Property = "UMask"
UnitFilePreset Property = "UnitFilePreset"
UnitFileState Property = "UnitFileState"
UtmpMode Property = "UtmpMode"
WantedBy Property = "WantedBy"
WatchdogSignal Property = "WatchdogSignal"
WatchdogTimestampMonotonic Property = "WatchdogTimestampMonotonic"
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,5 +1,7 @@
package systemctl package systemctl
import "strings"
type Options struct { type Options struct {
UserMode bool UserMode bool
} }
@@ -11,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

@@ -2,8 +2,6 @@ package systemctl
import ( import (
"context" "context"
"regexp"
"strings"
"github.com/taigrr/systemctl/properties" "github.com/taigrr/systemctl/properties"
) )
@@ -15,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.
@@ -29,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.
@@ -43,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.
@@ -58,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).
@@ -71,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).
@@ -101,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
@@ -163,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.
@@ -237,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

@@ -11,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() {
@@ -39,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