Merge pull request #18865 from Homebrew/systemd-quote

Fix systemd command line quoting
This commit is contained in:
Mike McQuaid 2024-12-04 10:25:18 +00:00 committed by GitHub
commit 2d8a19d260
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 53 additions and 4 deletions

View File

@ -455,7 +455,7 @@ module Homebrew
sig { returns(String) }
def to_systemd_unit
# command needs to be first because it initializes all other values
cmd = command&.map { |arg| Utils::Shell.sh_quote(arg) }
cmd = command&.map { |arg| Utils::Service.systemd_quote(arg) }
&.join(" ")
options = []

View File

@ -727,7 +727,7 @@ RSpec.describe Homebrew::Service do
[Service]
Type=simple
ExecStart=#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd test
ExecStart="#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd" "test"
Restart=always
RestartSec=30
WorkingDirectory=#{HOMEBREW_PREFIX}/var
@ -760,7 +760,7 @@ RSpec.describe Homebrew::Service do
[Service]
Type=oneshot
ExecStart=#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd
ExecStart="#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd"
SYSTEMD
expect(unit).to eq(unit_expect)
end
@ -783,7 +783,7 @@ RSpec.describe Homebrew::Service do
[Service]
Type=simple
ExecStart=#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd
ExecStart="#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd"
WorkingDirectory=#{Dir.home}
SYSTEMD
expect(unit).to eq(unit_expect)

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require "utils/service"
RSpec.describe Utils::Service do
describe "::systemd_quote" do
it "quotes empty strings correctly" do
expect(described_class.systemd_quote("")).to eq '""'
end
it "quotes strings with special characters escaped correctly" do
expect(described_class.systemd_quote("\a\b\f\n\r\t\v\\"))
.to eq '"\\a\\b\\f\\n\\r\\t\\v\\\\"'
expect(described_class.systemd_quote("\"' ")).to eq "\"\\\"' \""
end
it "does not escape characters that do not need escaping" do
expect(described_class.systemd_quote("daemon off;")).to eq '"daemon off;"'
expect(described_class.systemd_quote("--timeout=3")).to eq '"--timeout=3"'
expect(described_class.systemd_quote("--answer=foo bar"))
.to eq '"--answer=foo bar"'
end
end
end

View File

@ -48,5 +48,30 @@ module Utils
def self.systemctl?
!systemctl.nil?
end
# Quote a string for use in systemd command lines, e.g., in `ExecStart`.
# https://www.freedesktop.org/software/systemd/man/latest/systemd.syntax.html#Quoting
sig { params(str: String).returns(String) }
def self.systemd_quote(str)
result = +"\""
# No need to escape single quotes and spaces, as we're always double
# quoting the entire string.
str.each_char do |char|
result << case char
when "\a" then "\\a"
when "\b" then "\\b"
when "\f" then "\\f"
when "\n" then "\\n"
when "\r" then "\\r"
when "\t" then "\\t"
when "\v" then "\\v"
when "\\" then "\\\\"
when "\"" then "\\\""
else char
end
end
result << "\""
result.freeze
end
end
end