func main() {
remote.Main()
}
+
+// vi: set noet ts=4 sw=4 sts=4:
}
return res, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
}
return &cfg, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
perm := FileModeToFullPerm(info.Mode())
var data []byte
- // See errMsg above. If a user stores a file with stricter
- // permissions they could assume that these permissions are
- // respected. This is not the case.
+ // See errMsg above. If a user stores a file with stricter permissions
+ // they could assume that these permissions are respected. This is not
+ // the case.
if typ == 0 /* regular file */ {
if windows {
perm = 0644
- // 0755 must be set via permissions.yaml if
- // windows is used
+ // 0755 must be set via permissions.yaml if windows is used
}
if perm != 0644 && perm != 0755 {
- return fmt.Errorf(
- "%q: invalid permissions %#o%s",
+ return fmt.Errorf("%q: invalid permissions %#o%s",
path, perm, errMsg)
}
data, err = os.ReadFile(path)
perm = 0755
}
if perm != 0755 {
- return fmt.Errorf(
- "%q: invalid permissions %#o%s",
+ return fmt.Errorf("%q: invalid permissions %#o%s",
path, perm, errMsg)
}
} else if typ == fs.ModeSymlink {
return fmt.Errorf("%q: file type not supported", path)
}
- // Convert to absolute and slash-separated path as used on the
- // target host
+ // Convert to absolute and slash-separated path as used on the target
+ // host
x, err := filepath.Rel(basePath, path)
if err != nil {
return err
}
return files, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
errPrefix, name)
}
if hosts.Map[name] != nil ||
- hosts.Map[strings.TrimSuffix(name,
- GroupRemoveSuffix)] != nil {
- return nil, fmt.Errorf(
- "%s conflict with existing host",
+ hosts.Map[strings.TrimSuffix(name, GroupRemoveSuffix)] != nil {
+ return nil, fmt.Errorf("%s conflict with existing host",
errPrefix)
}
if strings.HasPrefix(name, GroupDetectedPrefix) {
- return nil, fmt.Errorf(
- "%s name must not start with %q "+
- "(reserved for detected groups)",
+ return nil, fmt.Errorf("%s name must not start with %q "+
+ "(reserved for detected groups)",
errPrefix, GroupDetectedPrefix)
}
if !groupNameRegexp.MatchString(
strings.TrimSuffix(name, GroupRemoveSuffix)) {
- return nil, fmt.Errorf(
- "%s name contains invalid characters "+
- "(must match %s)",
+ return nil, fmt.Errorf("%s name contains invalid characters "+
+ "(must match %s)",
errPrefix, groupNameRegexp)
}
if x == GroupAll {
continue
}
- // Don't validate against groupNameRegexp because
- // hosts have less strict restrictions.
+ // Don't validate against groupNameRegexp because hosts have less
+ // strict restrictions.
if strings.Contains(x, GroupSpecialSeparator) {
- return nil, fmt.Errorf(
- "%s member %q must not contain %q",
+ return nil, fmt.Errorf("%s member %q must not contain %q",
errPrefix, x, GroupSpecialSeparator)
}
if strings.HasPrefix(x, GroupDetectedPrefix) {
if hosts.Map[x] != nil || groups[x] != nil {
continue
}
- return nil, fmt.Errorf("%s member %q not found",
- errPrefix, x)
+ return nil, fmt.Errorf("%s member %q not found", errPrefix, x)
}
}
continue
}
if strings.Contains(x, GroupSpecialSeparator) {
- return nil, fmt.Errorf("%s invalid group name %q",
- errPrefix, x)
+ return nil, fmt.Errorf("%s invalid group name %q", errPrefix, x)
}
if strings.HasPrefix(x, GroupDetectedPrefix) {
continue
if groups[x] != nil {
continue
}
- return nil, fmt.Errorf("%s group %q does not exist",
- errPrefix, x)
+ return nil, fmt.Errorf("%s group %q does not exist", errPrefix, x)
}
return groups, nil
if x == host || detectedGroupsMap[x] || x == GroupAll {
return true
}
- if lookup(x, depth+1) &&
- !lookup(x+GroupRemoveSuffix, depth+1) {
+ if lookup(x, depth+1) && !lookup(x+GroupRemoveSuffix, depth+1) {
return true
}
}
}
}
if cycle != nil {
- return nil, fmt.Errorf(
- "groups.yaml: cycle while expanding group %q",
+ return nil, fmt.Errorf("groups.yaml: cycle while expanding group %q",
*cycle)
}
change := false
for group, members := range work {
for _, x := range members {
- if !detected[x] && !strings.HasPrefix(x,
- GroupDetectedPrefix) {
+ if !detected[x] && !strings.HasPrefix(x, GroupDetectedPrefix) {
continue
}
- detected[strings.TrimSuffix(group,
- GroupRemoveSuffix)] = true
+ detected[strings.TrimSuffix(group, GroupRemoveSuffix)] = true
delete(work, group)
change = true
}
}
return detected
}
+
+// vi: set noet ts=4 sw=4 sts=4:
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- res, err := ResolveHostGroups(tc.host, allGroups,
- tc.detected)
+ res, err := ResolveHostGroups(tc.host, allGroups, tc.detected)
testutil.AssertEqual(t, "res", res, tc.exp)
testutil.AssertErrorEqual(t, "err", err, tc.expErr)
})
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
for _, x := range hostList {
errPrefix := fmt.Sprintf("%s: host %q:", path, x.Name)
if x.Name == GroupAll {
- return nil, fmt.Errorf(
- "%s conflict with pre-defined group %q",
+ return nil, fmt.Errorf("%s conflict with pre-defined group %q",
errPrefix, x.Name)
}
if strings.HasPrefix(x.Name, GroupDetectedPrefix) {
- return nil, fmt.Errorf(
- "%s name must not start with %q "+
- "(reserved for detected groups)",
+ return nil, fmt.Errorf("%s name must not start with %q "+
+ "(reserved for detected groups)",
errPrefix, GroupDetectedPrefix)
}
if strings.Contains(x.Name, GroupSpecialSeparator) {
- return nil, fmt.Errorf(
- "%s name must not contain %q",
+ return nil, fmt.Errorf("%s name must not contain %q",
errPrefix, GroupSpecialSeparator)
}
if hostMap[x.Name] != nil {
- return nil, fmt.Errorf("%s host name already exists",
- errPrefix)
+ return nil, fmt.Errorf("%s host name already exists", errPrefix)
}
hostMap[x.Name] = x
}
Map: hostMap,
}, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
return res, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
for p, x := range cfg {
_, ok := files[p]
if !ok {
- return fmt.Errorf("%s: %q does not exist in files/",
- path, p)
+ return fmt.Errorf("%s: %q does not exist in files/", path, p)
}
xs := strings.Fields(x)
file := files[p]
// Sanity check
if file.Mode.Perm()&0111 != 0 && perm&0111 == 0 {
- return fmt.Errorf(
- "%s: %q: trying to remove +x from file, "+
- "manually chmod -x in files/",
+ return fmt.Errorf("%s: %q: trying to remove +x from file, "+
+ "manually chmod -x in files/",
path, p)
}
file.Mode = file.Mode.Type() | FullPermToFileMode(int(perm))
}
return mode
}
+
+// vi: set noet ts=4 sw=4 sts=4:
for _, tc := range tests {
t.Run(tc.group, func(t *testing.T) {
- // Use LoadFiles() so we work on real data and don't
- // make any mistakes generating it
+ // Use LoadFiles() so we work on real data and don't make any
+ // mistakes generating it
files, err := LoadFiles(tc.group)
if err != nil {
t.Fatalf("err = %#v, want nil", err)
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
return res, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
for _, x := range templates {
f, ok := files[x]
if !ok {
- return fmt.Errorf("%s: %q does not exist in files/",
- path, x)
+ return fmt.Errorf("%s: %q does not exist in files/", path, x)
}
if f.Mode.Type() != 0 /* regular file */ {
- return fmt.Errorf("%s: %q is not a regular file",
- path, x)
+ return fmt.Errorf("%s: %q is not a regular file", path, x)
}
tmplPath := filepath.Join(group, "files", x)
return t.host == host
}
func (t *templateArgs) InGroup(group string) bool {
- // Don't permit invalid groups to detect typos; detected groups cannot
- // be checked
+ // Don't permit invalid groups to detect typos; detected groups cannot be
+ // checked
if group != GroupAll &&
!t.allGroups[group] &&
!t.allHosts[group] &&
}
return t.groups[group]
}
+
+// vi: set noet ts=4 sw=4 sts=4:
for _, tc := range tests {
t.Run(tc.group, func(t *testing.T) {
- // Use LoadFiles() so we work on real data and don't
- // make any mistakes generating it
+ // Use LoadFiles() so we work on real data and don't make any
+ // mistakes generating it
files, err := LoadFiles(tc.group)
if err != nil {
t.Fatalf("err = %#v, want nil", err)
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
for p, x := range triggers {
f, ok := files[p]
if !ok {
- return fmt.Errorf("%s: %q does not exist in files/",
- path, p)
+ return fmt.Errorf("%s: %q does not exist in files/", path, p)
}
f.TriggerCommands = x
}
return nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
for _, tc := range tests {
t.Run(tc.group, func(t *testing.T) {
- // Use LoadFiles() so we work on real data and don't
- // make any mistakes generating it
+ // Use LoadFiles() so we work on real data and don't make any
+ // mistakes generating it
files, err := LoadFiles(tc.group)
if err != nil {
t.Fatalf("err = %#v, want nil", err)
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
perm = 0644
}
log.Printf("chmodding %q to %#o", path, perm)
- // This is safe because perm does not include
- // setuid/setgid/sticky which use different values in
- // FileMode.
+ // This is safe because perm does not include setuid/setgid/sticky
+ // which use different values in FileMode.
err := chmodNoFollow(path, fs.FileMode(perm))
if err != nil {
return err
return nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
}
return cfg, hosts, groups, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
nil,
},
- // NOTE: We use -n on regular runs to prevent changing
- // anything important on the host when running as root!
+ // NOTE: We use -n on regular runs to prevent changing anything
+ // important on the host when running as root!
{
"no changes (dry-run)",
// Fake $PATH so safcm cannot find the `ssh` binary.
t.Setenv("PATH", "")
- cmd := exec.Command("../../../../../safcm",
- "sync", "-n", "no-settings.example.org")
+ cmd := exec.Command("../../../../../safcm", "sync", "-n",
+ "no-settings.example.org")
_, err = cmd.CombinedOutput()
if err == nil {
t.Errorf("err = nil")
var tmp []string
for _, x := range strings.Split(string(out), "\n") {
- // Strip parts which change on each run (LOG)
- // or depending on the system (DET)
+ // Strip parts which change on each run (LOG) or depending on
+ // the system (DET)
x = logRegexp.ReplaceAllString(x, "<LOG>")
x = detectedRegexp.ReplaceAllString(x, "<DET>")
tmp = append(tmp, x)
_ = os.Remove(remotePath)
}
+
+// vi: set noet ts=4 sw=4 sts=4:
//
//go:embed remote/*
var RemoteHelpers embed.FS
+
+// vi: set noet ts=4 sw=4 sts=4:
func MainSync(args []string) error {
flag.Usage = func() {
- fmt.Fprintf(os.Stderr,
- "usage: %s sync [<options>] <host|group...>\n",
+ fmt.Fprintf(os.Stderr, "usage: %s sync [<options>] <host|group...>\n",
args[0])
flag.PrintDefaults()
}
isTTY: isTTY,
loop: loop,
}
- s.logFunc = func(level safcm.LogLevel, escaped bool,
- msg string) {
+ s.logFunc = func(level safcm.LogLevel, escaped bool, msg string) {
s.loop.Log(s, level, escaped, msg)
}
hosts = append(hosts, s)
succ := loop.Run(hosts)
if !succ {
- // Exit instead of returning an error to prevent an extra log
- // message from main()
+ // Exit instead of returning an error to prevent an extra log message
+ // from main()
os.Exit(1)
}
return nil
var unmatched []string
for x := range nameMap {
if !nameMatched[x] {
- unmatched = append(unmatched,
- fmt.Sprintf("%q", x))
+ unmatched = append(unmatched, fmt.Sprintf("%q", x))
}
}
sort.Strings(unmatched)
func (s *Sync) logVerbosef(format string, a ...interface{}) {
s.log(safcm.LogVerbose, false, fmt.Sprintf(format, a...))
}
+
+// vi: set noet ts=4 sw=4 sts=4:
x = config.GroupDetectedPrefix + "_" + x
return x
}
+
+// vi: set noet ts=4 sw=4 sts=4:
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
return empty, err
}
{
- // Don't leak internal group priority which is confusing
- // without knowing the implementation details.
+ // Don't leak internal group priority which is confusing without
+ // knowing the implementation details.
groupsSorted := make([]string, len(groups))
copy(groupsSorted, groups)
sort.Strings(groupsSorted)
- s.logVerbosef("host groups: %s",
- strings.Join(groupsSorted, " "))
+ s.logVerbosef("host groups: %s", strings.Join(groupsSorted, " "))
- // Don't leak internal priority values. Instead, order groups
- // by priority.
+ // Don't leak internal priority values. Instead, order groups by
+ // priority.
var priorities []string
for x := range groupPriority {
priorities = append(priorities, x)
if err != nil {
return empty, err
}
- err = config.LoadTemplates(group, files,
- s.host.Name, groups, s.allHosts, s.allGroups)
+ err = config.LoadTemplates(group, files, s.host.Name, groups,
+ s.allHosts, s.allGroups)
if err != nil {
return empty, err
}
return empty, err
}
for k, v := range files {
- err := s.checkFileConflict(group, k, v,
- allFiles, groupPriority)
+ err := s.checkFileConflict(group, k, v, allFiles, groupPriority)
if err != nil {
return empty, err
}
func (s *Sync) resolveHostGroups(detectedGroups []string) (
[]string, map[string]int, error) {
- groups, err := config.ResolveHostGroups(s.host.Name,
- s.allGroups, detectedGroups)
+ groups, err := config.ResolveHostGroups(s.host.Name, s.allGroups,
+ detectedGroups)
if err != nil {
return nil, nil, err
}
panic("invalid group priorities")
}
- // Directories with default permissions and no triggers do not count
- // as conflict
+ // Directories with default permissions and no triggers do not count as
+ // conflict
if file.Mode.IsDir() && file.Mode == old.Mode &&
config.FileModeToFullPerm(file.Mode) == 0755 &&
file.TriggerCommands == nil && old.TriggerCommands == nil {
// Slash separated paths are used for the configuration
const sep = "/"
- // Remove invalid paths which can result from group_priority
- // overriding paths from another group (e.g. "/foo" as file from one
- // group and "/foo/bar" from another).
+ // Remove invalid paths which can result from group_priority overriding
+ // paths from another group (e.g. "/foo" as file from one group and
+ // "/foo/bar" from another).
var last *safcm.File
for _, x := range paths {
file := files[x]
last = file
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
expErr error
}{
- // NOTE: Also update MsgSyncReq in safcm-remote test cases
- // changing the MsgSyncReq struct!
+ // NOTE: Also update MsgSyncReq in safcm-remote test cases changing
+ // the MsgSyncReq struct!
{
"project: host1",
res, err := s.hostSyncReq(tc.detected)
testutil.AssertEqual(t, "res", res, tc.exp)
testutil.AssertErrorEqual(t, "err", err, tc.expErr)
- testutil.AssertEqual(t, "events",
- events, tc.expEvents)
+ testutil.AssertEqual(t, "events", events, tc.expEvents)
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
)
func MainVersion() error {
- fmt.Printf("safcm %s, compiled with %s\n",
- versionGit,
- versionGo)
+ fmt.Printf("safcm %s, compiled with %s\n", versionGit, versionGo)
return nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
func (c *Changes) FormatChanges(resp safcm.MsgSyncResp) string {
var changes []string
if len(resp.FileChanges) > 0 {
- changes = append(changes,
- c.FormatFileChanges(resp.FileChanges))
+ changes = append(changes, c.FormatFileChanges(resp.FileChanges))
}
if len(resp.PackageChanges) > 0 {
- changes = append(changes,
- c.FormatPackageChanges(resp.PackageChanges))
+ changes = append(changes, c.FormatPackageChanges(resp.PackageChanges))
}
if len(resp.ServiceChanges) > 0 {
- changes = append(changes,
- c.FormatServiceChanges(resp.ServiceChanges))
+ changes = append(changes, c.FormatServiceChanges(resp.ServiceChanges))
}
if len(resp.CommandChanges) > 0 {
- changes = append(changes,
- c.FormatCommandChanges(resp.CommandChanges))
+ changes = append(changes, c.FormatCommandChanges(resp.CommandChanges))
}
if len(changes) == 0 {
// Notify user that the host was synced successfully
}
x := strings.Join(changes, "\n")
- // If quiet is used and only commands without output were executed
- // then don't prepend a newline so that the whole change output of a
- // host fits in a single line. This makes the output much more
- // readable with multiple hosts.
+ // If quiet is used and only commands without output were executed then
+ // don't prepend a newline so that the whole change output of a host fits
+ // in a single line. This makes the output much more readable with
+ // multiple hosts.
if strings.Count(x, "\n") == 1 {
return x
}
func (c *Changes) FormatFileChanges(changes []safcm.FileChange) string {
var buf strings.Builder
if c.DryRun {
- fmt.Fprintf(&buf, "will change %d file(s): (dry-run)\n",
- len(changes))
+ fmt.Fprintf(&buf, "will change %d file(s): (dry-run)\n", len(changes))
} else {
fmt.Fprintf(&buf, "changed %d file(s):\n", len(changes))
}
info = append(info, "enabled")
}
fmt.Fprintf(&buf, "%s: %s\n",
- c.FormatTarget(x.Name),
- strings.Join(info, ", "))
+ c.FormatTarget(x.Name), strings.Join(info, ", "))
}
return buf.String()
}
// output. This is useful as many commands will be used to enforce a
// certain state (e.g. file not-present, `ainsl`, etc.) and are run on
// each sync. Displaying them provides not much useful information.
- // Instead, quiet shows them only when they produce output (e.g.
- // `ainsl`, `rm -v`) and thus modify the host's state.
+ // Instead, quiet shows them only when they produce output (e.g. `ainsl`,
+ // `rm -v`) and thus modify the host's state.
var noOutput int
if c.Quiet {
for _, x := range changes {
- if x.Trigger == "" &&
- x.Error == "" &&
- x.Output == "" {
+ if x.Trigger == "" && x.Error == "" && x.Output == "" {
noOutput++
}
}
if x.Output != "" {
// TODO: truncate very large outputs?
x := indentBlock(x.Output, indent)
- fmt.Fprintf(&buf, ":\n%s",
- EscapeControlCharacters(c.IsTTY, x))
+ fmt.Fprintf(&buf, ":\n%s", EscapeControlCharacters(c.IsTTY, x))
}
fmt.Fprintf(&buf, "\n")
}
return sep + strings.Join(lines, "\n"+sep)
}
+
+// vi: set noet ts=4 sw=4 sts=4:
exp string
}{
- // Just a few basic tests and border cases; see the other
- // tests for more detailed tests of each format function
+ // Just a few basic tests and border cases; see the other tests for
+ // more detailed tests of each format function
{
"no changes",
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
}
return resp, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
if color != 0 {
host = ColorString(isTTY, color, host)
}
- // Make sure to escape control characters to prevent terminal
- // injection attacks
+ // Make sure to escape control characters to prevent terminal injection
+ // attacks
if !x.Escaped {
data = EscapeControlCharacters(isTTY, data)
}
log.Printf("%-9s [%s] %s", prefix, host, data)
}
+
+// vi: set noet ts=4 sw=4 sts=4:
},
safcm.LogDebug3,
false,
- fmt.Sprintf("[INVALID=%d] [fake-host] debug3 log\n",
- safcm.LogDebug3),
+ fmt.Sprintf("[INVALID=%d] [fake-host] debug3 log\n", safcm.LogDebug3),
false,
},
{
},
safcm.LogDebug3,
true,
- fmt.Sprintf("[INVALID=%d] [\x1b[31mfake-host\x1b[0m] debug3 log\n",
- safcm.LogDebug3),
+ fmt.Sprintf("[INVALID=%d] [\x1b[31mfake-host\x1b[0m] debug3 log\n", safcm.LogDebug3),
false,
},
{
var failed bool
LogEvent(tc.event, tc.level, tc.isTTY, &failed)
- testutil.AssertEqual(t, "log",
- buf.String(), tc.exp)
- testutil.AssertEqual(t, "failed",
- failed, tc.expFailed)
+ testutil.AssertEqual(t, "log", buf.String(), tc.exp)
+ testutil.AssertEqual(t, "failed", failed, tc.expFailed)
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
sigint := make(chan os.Signal, 1) // buffered for Notify()
signal.Notify(sigint, os.Interrupt) // = SIGINT = Ctrl-C
go func() {
- // Running `ssh` processes get killed by SIGINT which is sent
- // to all processes
+ // Running `ssh` processes get killed by SIGINT which is sent to all
+ // processes
<-sigint
log.Print("Received SIGINT, aborting ...")
// Print all queued events
events <- Event{} // poison pill
<-done
- // "races" with <-done in the main function and will hang here
- // if the other is faster. This is fine because then all hosts
- // were synced successfully.
+ // "races" with <-done in the main function and will hang here if the
+ // other is faster. This is fine because then all hosts were synced
+ // successfully.
hostsLeftMutex.Lock()
var hosts []string
return nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
return ColorString(isTTY, ColorMagenta, x)
})
}
+
+// vi: set noet ts=4 sw=4 sts=4:
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
}
func (c *GobConn) Send(x Msg) error {
- // & lets Encode send the interface itself and not a concrete type
- // which is necessary to Decode as an interface
+ // & lets Encode send the interface itself and not a concrete type which
+ // is necessary to Decode as an interface
return c.enc.Encode(&x)
}
}
return x, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
}
return x, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
func Main(args []string) error {
flag.Usage = func() {
- fmt.Fprintf(os.Stderr,
- "usage: %s ainsl [<options>] <path> <line>\n",
+ fmt.Fprintf(os.Stderr, "usage: %s ainsl [<options>] <path> <line>\n",
args[0])
flag.PrintDefaults()
}
return nil, fmt.Errorf("empty line")
}
if strings.Contains(line, "\n") {
- return nil, fmt.Errorf("line must not contain newlines: %q",
- line)
+ return nil, fmt.Errorf("line must not contain newlines: %q", line)
}
parentFd, baseName, err := sync.OpenParentDirectoryNoSymlinks(path)
return nil, fmt.Errorf("%q: %v", path, err)
}
if !create {
- return nil, fmt.Errorf(
- "%q: file does not exist, use -create",
+ return nil, fmt.Errorf("%q: file does not exist, use -create",
path)
}
uid, gid = os.Getuid(), os.Getgid()
- // Read current umask. Don't do this in programs where
- // multiple goroutines create files because this is inherently
- // racy! No goroutines here, so it's fine.
+ // Read current umask. Don't do this in programs where multiple
+ // goroutines create files because this is inherently racy! No
+ // goroutines here, so it's fine.
umask := syscall.Umask(0)
syscall.Umask(umask)
// Apply umask to created file
mode = 0666 & ^fs.FileMode(umask)
changes = append(changes,
- fmt.Sprintf("%q: created file (%d/%d %s)",
- path, uid, gid, mode))
+ fmt.Sprintf("%q: created file (%d/%d %s)", path, uid, gid, mode))
} else {
// Preserve user/group and mode of existing file
break
}
}
- // Make sure the file has a trailing newline. This enforces symmetry
- // with our changes. Whenever we add a line we also append a trailing
- // newline. When we conclude that no changes are necessary the file
- // should be in the same state as we would leave it if there were
- // changes.
+ // Make sure the file has a trailing newline. This enforces symmetry with
+ // our changes. Whenever we add a line we also append a trailing newline.
+ // When we conclude that no changes are necessary the file should be in
+ // the same state as we would leave it if there were changes.
if len(data) != 0 && data[len(data)-1] != '\n' {
data = append(data, '\n')
changes = append(changes,
- fmt.Sprintf("%q: added missing trailing newline",
- path))
+ fmt.Sprintf("%q: added missing trailing newline", path))
}
// Line present, nothing to do
return x, stat, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
}
changes, err := handle(tc.path, tc.line, tc.create)
- testutil.AssertEqual(t, "changes",
- changes, tc.expChanges)
+ testutil.AssertEqual(t, "changes", changes, tc.expChanges)
testutil.AssertErrorEqual(t, "err", err, tc.expErr)
files, err := ft.WalkDir(path)
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
logger *log.Logger
}
-func Handle(req safcm.MsgInfoReq,
- runner run.Runner, fun log.LogFunc) safcm.MsgInfoResp {
+func Handle(req safcm.MsgInfoReq, runner run.Runner,
+ fun log.LogFunc) safcm.MsgInfoResp {
i := Info{
req: req,
i.resp.Goarch = runtime.GOARCH
for _, x := range i.req.DetectGroups {
- stdout, _, err := i.cmd.Run("detect group",
- "/bin/sh", "-c", x)
+ stdout, _, err := i.cmd.Run("detect group", "/bin/sh", "-c", x)
if err != nil {
return err
}
return nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
format string, a ...interface{}) {
l.fun(level, fmt.Sprintf(format, a...))
}
+
+// vi: set noet ts=4 sw=4 sts=4:
var logLevel safcm.LogLevel
logFunc := func(level safcm.LogLevel, msg string) {
if logLevel >= level {
- // Handling errors here is complex and quite verbose.
- // If it happens the connection is gone anyway so skip
- // the error handling.
+ // Handling errors here is complex and quite verbose. If it
+ // happens the connection is gone anyway so skip the error
+ // handling.
conn.Send(safcm.MsgLog{ //nolint:errcheck
Level: level,
Text: msg,
}
return nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
c.logger.Debugf("%s: running %s", module, quoted)
err := c.Runner.Run(cmd)
if stdout.Len() > 0 {
- c.logger.Debug2f("%s: command stdout:\n%s",
- module, stdout.Bytes())
+ c.logger.Debug2f("%s: command stdout:\n%s", module, stdout.Bytes())
}
if stderr.Len() > 0 {
- c.logger.Debug2f("%s: command stderr:\n%s",
- module, stderr.Bytes())
+ c.logger.Debug2f("%s: command stderr:\n%s", module, stderr.Bytes())
}
if err != nil {
- return nil, nil, fmt.Errorf(
- "%s failed: %v; stdout: %q, stderr: %q",
+ return nil, nil, fmt.Errorf("%s failed: %v; stdout: %q, stderr: %q",
quoted, err, stdout.String(), stderr.String())
}
return stdout.Bytes(), stderr.Bytes(), nil
c.logger.Debug2f("%s: command output:\n%s", module, out)
}
if err != nil {
- return nil, fmt.Errorf("%s failed: %v; output: %q",
- quoted, err, out)
+ return nil, fmt.Errorf("%s failed: %v; output: %q", quoted, err, out)
}
return out, nil
}
}
return strings.Join(quoted, " ")
}
+
+// vi: set noet ts=4 sw=4 sts=4:
func (r ExecRunner) CombinedOutput(cmd *exec.Cmd) ([]byte, error) {
return cmd.CombinedOutput()
}
+
+// vi: set noet ts=4 sw=4 sts=4:
}
}
}
- // Regular commands afterwards so they can react on triggers if
- // necessary
+ // Regular commands afterwards so they can react on triggers if necessary
for _, x := range s.req.Commands {
err := s.syncCommand(x.Cmd, x.OrigGroup, "")
if err != nil {
cmd := exec.Command("/bin/sh", "-c", command)
cmd.Env = safcmEnviroment(s.req.Groups)
- // Cannot use cmd.CombinedOutputCmd() because we need another log
- // level (here the command is the actual change and not a side effect)
- // and different error handling.
- s.log.Verbosef("commands: running %s (%s)",
- run.QuoteForDebug(cmd), info)
+ // Cannot use cmd.CombinedOutputCmd() because we need another log level
+ // (here the command is the actual change and not a side effect) and
+ // different error handling.
+ s.log.Verbosef("commands: running %s (%s)", run.QuoteForDebug(cmd), info)
out, err := s.cmd.Runner.CombinedOutput(cmd)
if len(out) > 0 {
s.log.Debug2f("commands: command output:\n%s", out)
}
return env
}
+
+// vi: set noet ts=4 sw=4 sts=4:
expErr error
}{
- // NOTE: Also update MsgSyncResp in safcm test cases when
- // changing the MsgSyncResp struct!
+ // NOTE: Also update MsgSyncResp in safcm test cases when changing the
+ // MsgSyncResp struct!
{
"successful command",
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
const openReadonlyFlags = unix.O_RDONLY | unix.O_NOFOLLOW | unix.O_NONBLOCK
func (s *Sync) syncFiles() error {
- // Sort for deterministic order and so parent directories are present
- // when files in them are created
+ // Sort for deterministic order and so parent directories are present when
+ // files in them are created
var files []*safcm.File
for _, x := range s.req.Files {
files = append(files, x)
}
func (s *Sync) syncFile(file *safcm.File, changed *bool) error {
- // The general strategy is "update by rename": If any property of a
- // file changes the new version will be written to a temporary file
- // and then renamed "over" the original file. This is simple and
- // prevents race conditions where the file is partially readable while
- // changes to permissions or owner/group are applied. However, this
- // strategy does not work for directories which must be removed first
- // (was directory), must remove the existing file (will be directory)
- // or must be directly modified (changed permissions or owner). In the
- // first two cases the old path is removed. In the last the directory
- // is modified (carefully) in place.
+ // The general strategy is "update by rename": If any property of a file
+ // changes the new version will be written to a temporary file and then
+ // renamed "over" the original file. This is simple and prevents race
+ // conditions where the file is partially readable while changes to
+ // permissions or owner/group are applied. However, this strategy does not
+ // work for directories which must be removed first (was directory), must
+ // remove the existing file (will be directory) or must be directly
+ // modified (changed permissions or owner). In the first two cases the old
+ // path is removed. In the last the directory is modified (carefully) in
+ // place.
//
// The implementation is careful not to follow any symlinks to prevent
// possible race conditions which can be exploited and are especially
- // dangerous when running with elevated privileges (which will most
- // likely be the case). This includes not using absolute paths in
- // syscalls to prevent symlink attacks when a directory is writable by
- // other users (e.g. when syncing a file to /home/user/dir/file the
- // user could create dir as symlink to another directory and file
- // would be written there). To prevent this *at syscalls are used and
- // all symlinks in the path are rejected. This still permits the user
- // to move dir during the sync but only to places which are writable
- // by the user which cannot be prevented.
+ // dangerous when running with elevated privileges (which will most likely
+ // be the case). This includes not using absolute paths in syscalls to
+ // prevent symlink attacks when a directory is writable by other users
+ // (e.g. when syncing a file to /home/user/dir/file the user could create
+ // dir as symlink to another directory and file would be written there).
+ // To prevent this *at syscalls are used and all symlinks in the path are
+ // rejected. This still permits the user to move dir during the sync but
+ // only to places which are writable by the user which cannot be
+ // prevented.
err := s.fileResolveIds(file)
if err != nil {
var oldData []byte
var changeType, changePerm, changeUserOrGroup, changeData bool
if !change.Created {
- // Manually convert to FileMode; from src/os/stat_linux.go in
- // Go's sources (stat_*.go for other UNIX systems are
- // identical, except for stat_darwin.go which has an extra
- // S_IFWHT)
+ // Manually convert to FileMode; from src/os/stat_linux.go in Go's
+ // sources (stat_*.go for other UNIX systems are identical, except for
+ // stat_darwin.go which has an extra S_IFWHT)
mode := fs.FileMode(oldStat.Mode & 0777)
switch oldStat.Mode & unix.S_IFMT {
case unix.S_IFBLK:
// nothing to do
case unix.S_IFSOCK:
mode |= fs.ModeSocket
- // Guard against unknown file types (e.g. on darwin); not in
- // stat_*.go
+ // Guard against unknown file types (e.g. on darwin); not in stat_*.go
default:
return fmt.Errorf("unexpected file mode %v",
oldStat.Mode&unix.S_IFMT)
// Compare permissions
change.Old.Mode = mode
if change.Old.Mode.Type() == fs.ModeSymlink {
- // Some BSD systems permit changing permissions of
- // symlinks but ignore them on traversal. To keep it
- // simple we don't support that and always use 0777
- // for symlink permissions (the value on GNU/Linux)
- // when comparing. The actual permissions on the file
+ // Some BSD systems permit changing permissions of symlinks but
+ // ignore them on traversal. To keep it simple we don't support
+ // that and always use 0777 for symlink permissions (the value on
+ // GNU/Linux) when comparing. The actual permissions on the file
// system might be different on BSD systems.
//
// TODO: Add proper support for symlinks on BSD
change.Old.Mode.Type(),
file.Mode.Type())
} else {
- // Be careful with .Perm() which does not
- // contain the setuid/setgid/sticky bits!
+ // Be careful with .Perm() which does not contain the
+ // setuid/setgid/sticky bits!
changePerm = true
debugf("permission differs %s -> %s",
change.Old.Mode, file.Mode)
file.Uid, file.Gid)
}
u, g, err := resolveIdsToNames(change.Old.Uid, change.Old.Gid)
- // Errors are not relevant as this is only used to report the
- // change. If the user/group no longer exits only the ids will
- // be reported.
+ // Errors are not relevant as this is only used to report the change.
+ // If the user/group no longer exits only the ids will be reported.
if err == nil {
change.Old.User = u
change.Old.Group = g
case 0: // regular file
x, err := io.ReadAll(oldFh)
if err != nil {
- return fmt.Errorf("reading old content: %v",
- err)
+ return fmt.Errorf("reading old content: %v", err)
}
oldData = x
case fs.ModeSymlink:
buf := make([]byte, unix.PathMax)
n, err := unix.Readlinkat(parentFd, baseName, buf)
if err != nil {
- return fmt.Errorf("reading old content: %v",
- err)
+ return fmt.Errorf("reading old content: %v", err)
}
oldData = buf[:n]
}
}
*changed = true
- // Don't show a diff with the full content for newly created files or
- // on type changes. This is just noise for the user as the new file
- // content is obvious. But we always want to see a diff when files are
- // replaced because this destroys data.
+ // Don't show a diff with the full content for newly created files or on
+ // type changes. This is just noise for the user as the new file content
+ // is obvious. But we always want to see a diff when files are replaced
+ // because this destroys data.
if !change.Created &&
(change.Old.Mode.Type() == 0 ||
change.Old.Mode.Type() == fs.ModeSymlink) {
}
}
- // Add change here so it is stored even when applying it fails. This
- // way the user knows exactly what was attempted.
+ // Add change here so it is stored even when applying it fails. This way
+ // the user knows exactly what was attempted.
s.resp.FileChanges = append(s.resp.FileChanges, change)
if change.Created {
if changeType && (change.Old.Mode.IsDir() || file.Mode.IsDir()) {
debugf("removing (due to type change)")
// In the past os.RemoveAll() was used here. However, this is
- // difficult to implement manually with *at syscalls. To keep
- // it simple only permit removing files and empty directories
- // here. This also has the bonus of preventing data loss when
- // (accidentally) replacing a directory tree with a file.
+ // difficult to implement manually with *at syscalls. To keep it
+ // simple only permit removing files and empty directories here. This
+ // also has the bonus of preventing data loss when (accidentally)
+ // replacing a directory tree with a file.
const msg = "will not replace non-empty directory, " +
"please remove manually"
err := unix.Unlinkat(parentFd, baseName, 0 /* flags */)
if err != nil && !os.IsNotExist(err) {
- err2 := unix.Unlinkat(parentFd, baseName,
- unix.AT_REMOVEDIR)
+ err2 := unix.Unlinkat(parentFd, baseName, unix.AT_REMOVEDIR)
if err2 != nil && !os.IsNotExist(err2) {
// See src/os/file_unix.go in Go's sources
if err2 == unix.ENOTDIR {
if err != nil {
return err
}
- // We must be careful not to chmod arbitrary files. If the
- // target directory is writable then it might have changed to
- // a symlink at this point. There's no lchmod and fchmodat is
- // incomplete so open the directory.
+ // We must be careful not to chmod arbitrary files. If the target
+ // directory is writable then it might have changed to a symlink at
+ // this point. There's no lchmod and fchmodat is incomplete so open
+ // the directory.
debugf("chmodding %s", file.Mode)
dh, err := OpenAtNoFollow(parentFd, baseName)
if err != nil {
if err != nil {
return err
}
- // Less restrictive access is not relevant here because there
- // are no files present yet.
+ // Less restrictive access is not relevant here because there are no
+ // files present yet.
debugf("chowning %d/%d", file.Uid, file.Gid)
err = dh.Chown(file.Uid, file.Gid)
if err != nil {
}
// Directory: changed permission or user/group
if file.Mode.IsDir() {
- // We don't know if the new permission or if the new
- // user/group is more restrictive (e.g. root:root 0750 ->
- // user:group 0700; applying group first gives group
- // unexpected access). To prevent a short window where the
- // access might be too lax we temporarily deny all access.
+ // We don't know if the new permission or if the new user/group is
+ // more restrictive (e.g. root:root 0750 -> user:group 0700; applying
+ // group first gives group unexpected access). To prevent a short
+ // window where the access might be too lax we temporarily deny all
+ // access.
if changePerm && changeUserOrGroup {
- // Only drop group and other permission because user
- // has access anyway (either before or after the
- // change). This also prevents temporary errors during
- // the error when the user tries to access this
- // directory (access for the group will fail though).
+ // Only drop group and other permission because user has access
+ // anyway (either before or after the change). This also prevents
+ // temporary errors during the error when the user tries to access
+ // this directory (access for the group will fail though).
mode := change.Old.Mode & fs.ModePerm & 0700
- // Retain setgid/sticky so that the behavior does not
- // change when creating and removing files.
+ // Retain setgid/sticky so that the behavior does not change when
+ // creating and removing files.
mode |= change.Old.Mode & fs.ModeSetgid
mode |= change.Old.Mode & fs.ModeSticky
debugf("chmodding %#o (temporary)", mode)
}
dir := slashpath.Dir(file.Path) // only used in debug messages
- // Create hidden file which should be ignored by most other tools and
- // thus not affect anything during creation
+ // Create hidden file which should be ignored by most other tools and thus
+ // not affect anything during creation
tmpBase := "." + baseName
switch file.Mode.Type() {
case 0: // regular file
- debugf("creating temporary file %q",
- slashpath.Join(dir, tmpBase+"*"))
+ debugf("creating temporary file %q", slashpath.Join(dir, tmpBase+"*"))
x, err := WriteTempAt(parentFd, tmpBase, file.Data,
file.Uid, file.Gid, file.Mode)
if err != nil {
i := 0
retry:
x := tmpBase + strconv.Itoa(rand.Int())
- debugf("creating temporary symlink %q",
- slashpath.Join(dir, x))
+ debugf("creating temporary symlink %q", slashpath.Join(dir, x))
err := unix.Symlinkat(string(file.Data), parentFd, x)
if err != nil {
if os.IsExist(err) && i < 10000 {
// To guarantee durability fsync must be called on a parent directory
// after adding, renaming or removing files therein.
//
- // Calling sync on the files itself is not enough according to POSIX;
- // man 2 fsync: "Calling fsync() does not necessarily ensure that the
- // entry in the directory containing the file has also reached disk.
- // For that an explicit fsync() on a file descriptor for the directory
- // is also needed."
+ // Calling sync on the files itself is not enough according to POSIX; man
+ // 2 fsync: "Calling fsync() does not necessarily ensure that the entry in
+ // the directory containing the file has also reached disk. For that an
+ // explicit fsync() on a file descriptor for the directory is also
+ // needed."
err = unix.Fsync(parentFd)
if err != nil {
return err
var dir string
if path == "/" {
- // Root: use root itself as base name because root is the
- // parent of itself
+ // Root: use root itself as base name because root is the parent of
+ // itself
dir = "/"
parts = []string{"/"}
} else if parts[0] == "" {
dir = "/"
parts = parts[1:]
} else if path == "." {
- // Current directory: open parent directory and use current
- // directory name as base name
+ // Current directory: open parent directory and use current directory
+ // name as base name
wd, err := os.Getwd()
if err != nil {
- return -1, "", fmt.Errorf(
- "failed to get working directory: %w", err)
+ return -1, "", fmt.Errorf("failed to get working directory: %w",
+ err)
}
dir = ".."
parts = []string{filepath.Base(wd)}
}
}
- dirFd, err := unix.Openat(unix.AT_FDCWD, dir,
- openReadonlyFlags, 0 /* mode */)
+ dirFd, err := unix.Openat(unix.AT_FDCWD, dir, openReadonlyFlags,
+ 0 /* mode */)
if err != nil {
return -1, "", err
}
- // Walk path one directory at a time to ensure there are no symlinks
- // in the path. This prevents users with write access to change the
- // path to point to arbitrary locations. O_NOFOLLOW when opening the
- // path is not enough as only the last path component is checked.
+ // Walk path one directory at a time to ensure there are no symlinks in
+ // the path. This prevents users with write access to change the path to
+ // point to arbitrary locations. O_NOFOLLOW when opening the path is not
+ // enough as only the last path component is checked.
for i, name := range parts[:len(parts)-1] {
fd, err := unix.Openat(dirFd, name, openReadonlyFlags, 0)
if err != nil {
unix.Close(dirFd) //nolint:errcheck
if err == unix.ELOOP || err == unix.EMLINK {
- x := filepath.Join(append([]string{dir},
- parts[:i+1]...)...)
- return -1, "", fmt.Errorf(
- "symlink not permitted in path: %q",
+ x := filepath.Join(append([]string{dir}, parts[:i+1]...)...)
+ return -1, "", fmt.Errorf("symlink not permitted in path: %q",
x)
}
return -1, "", err
return os.NewFile(uintptr(fd), ""), tmpBase, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
func OpenFileNoSymlinks(path string) (*os.File, error) {
return nil, fmt.Errorf("not implemented on Windows")
}
+
+// vi: set noet ts=4 sw=4 sts=4:
expErr error
}{
- // NOTE: Also update MsgSyncResp in safcm test cases when
- // changing the MsgSyncResp struct!
+ // NOTE: Also update MsgSyncResp in safcm test cases when changing the
+ // MsgSyncResp struct!
- // See TestSyncFile() for most file related tests. This
- // function only tests the overall results and triggers.
+ // See TestSyncFile() for most file related tests. This function only
+ // tests the overall results and triggers.
{
"basic: create",
},
{
- // We use relative paths for most tests because we
- // don't want to modify the running system. Use this
- // test (and the one below for triggers) as a basic
- // check that absolute paths work.
+ // We use relative paths for most tests because we don't want to
+ // modify the running system. Use this test (and the one below for
+ // triggers) as a basic check that absolute paths work.
//
- // Use numeric IDs as not all systems use root/root;
- // for example BSDs use root/wheel.
+ // Use numeric IDs as not all systems use root/root; for example
+ // BSDs use root/wheel.
"absolute paths: no change",
skipUnlessCiRun,
safcm.MsgSyncReq{
testutil.AssertEqual(t, "files", files, tc.expFiles)
testutil.AssertEqual(t, "resp", s.resp, tc.expResp)
- testutil.AssertEqual(t, "triggers",
- s.triggers, tc.expTriggers)
+ testutil.AssertEqual(t, "triggers", s.triggers, tc.expTriggers)
})
}
expErr error
}{
- // NOTE: Also update MsgSyncResp in safcm test cases when
- // changing the MsgSyncResp struct!
+ // NOTE: Also update MsgSyncResp in safcm test cases when changing the
+ // MsgSyncResp struct!
// TODO: Add tests for chown and run them only as root
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
panic(err)
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
// TODO: support more distributions
return fmt.Errorf("not yet supported on this distribution")
}
+
+// vi: set noet ts=4 sw=4 sts=4:
for _, line := range lines {
xs := strings.Split(line, "\t")
if len(xs) != 2 {
- return nil, fmt.Errorf("invalid dpkg-query line %q",
- line)
+ return nil, fmt.Errorf("invalid dpkg-query line %q", line)
}
- // We only care if the package is currently successfully
- // installed (last two fields). If a package is on hold (first
- // field) this is fine as well.
+ // We only care if the package is currently successfully installed
+ // (last two fields). If a package is on hold (first field) this is
+ // fine as well.
if strings.HasSuffix(xs[0], " ok installed") {
res[xs[1]] = true
}
}
return res, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
expErr error
}{
- // NOTE: Also update MsgSyncResp in safcm test cases when
- // changing the MsgSyncResp struct!
+ // NOTE: Also update MsgSyncResp in safcm test cases when changing the
+ // MsgSyncResp struct!
{
"packages already installed",
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
// TODO: support more distributions
return fmt.Errorf("not yet supported on this distribution")
}
+
+// vi: set noet ts=4 sw=4 sts=4:
if change.Started || change.Enabled {
change.Name = name
- s.resp.ServiceChanges = append(s.resp.ServiceChanges,
- change)
+ s.resp.ServiceChanges = append(s.resp.ServiceChanges, change)
}
}
if len(start) == 0 && len(enable) == 0 {
return nil
}
- // Reload service files which were possibly changed during file sync
- // or package installation
+ // Reload service files which were possibly changed during file sync or
+ // package installation
_, _, err = s.cmd.Run("services", "/bin/systemctl", "daemon-reload")
if err != nil {
return err
}
if len(start) != 0 {
- s.log.Verbosef("services: starting %s",
- strings.Join(start, " "))
+ s.log.Verbosef("services: starting %s", strings.Join(start, " "))
_, _, err := s.cmd.Run("services", append([]string{
"/bin/systemctl", "start", "--",
}, start...)...)
}
}
if len(enable) != 0 {
- s.log.Verbosef("services: enabling %s",
- strings.Join(enable, " "))
+ s.log.Verbosef("services: enabling %s", strings.Join(enable, " "))
_, _, err := s.cmd.Run("services", append([]string{
"/bin/systemctl", "enable", "--",
}, enable...)...)
for _, block := range strings.Split(string(out), "\n\n") {
lines := strings.Split(strings.TrimSpace(block), "\n")
if len(lines) != 3 {
- return nil, fmt.Errorf("invalid systemctl output: %q",
- block)
+ return nil, fmt.Errorf("invalid systemctl output: %q", block)
}
var service SystemdService
)
if strings.HasPrefix(x, activePrefix) {
- service.ActiveState = strings.TrimPrefix(x,
- activePrefix)
+ service.ActiveState = strings.TrimPrefix(x, activePrefix)
} else if strings.HasPrefix(x, unitPrefix) {
- service.UnitFileState = strings.TrimPrefix(x,
- unitPrefix)
+ service.UnitFileState = strings.TrimPrefix(x, unitPrefix)
} else if strings.HasPrefix(x, errorPrefix) {
x := strings.TrimPrefix(x, errorPrefix)
- // Older systemd versions (e.g. 237) add empty
- // quotes even if there is no error
+ // Older systemd versions (e.g. 237) add empty quotes even if
+ // there is no error
if x != "" && x != ` ""` {
- return nil, fmt.Errorf(
- "systemd unit %q not found",
+ return nil, fmt.Errorf("systemd unit %q not found",
services[i])
}
} else {
- return nil, fmt.Errorf(
- "invalid systemctl show line %q", x)
+ return nil, fmt.Errorf("invalid systemctl show line %q", x)
}
}
res[services[i]] = service
return res, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
expErr error
}{
- // NOTE: Also update MsgSyncResp in safcm test cases when
- // changing the MsgSyncResp struct!
+ // NOTE: Also update MsgSyncResp in safcm test cases when changing the
+ // MsgSyncResp struct!
{
"no service change necessary",
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
return nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
return s.dbg
}
+
+// vi: set noet ts=4 sw=4 sts=4:
}
// Queue each trigger only once
if s.triggersActive[path] {
- s.log.Debugf(
- "files: %q: skipping trigger on %q, already active",
+ s.log.Debugf("files: %q: skipping trigger on %q, already active",
file.Path, path)
continue
}
- s.log.Verbosef("files: %q: queuing trigger on %q",
- file.Path, path)
+ s.log.Verbosef("files: %q: queuing trigger on %q", file.Path, path)
s.triggers = append(s.triggers, path)
s.triggersActive[path] = true
}
}
return res
}
+
+// vi: set noet ts=4 sw=4 sts=4:
})
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
close(c.events)
// We cannot reuse this channel.
c.events = nil
- // Don't set c.Events to nil because this creates a data race when
- // another thread is still waiting.
+ // Don't set c.Events to nil because this creates a data race when another
+ // thread is still waiting.
return err
}
return nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
return fmt.Errorf("internal error: no support for %q", goos)
}
- // Use a function so the shell cannot execute the input line-wise.
- // This is important because we're also using stdin to send data to
- // the script. If the shell executes the input line-wise then our
- // script is interpreted as input for `read`.
+ // Use a function so the shell cannot execute the input line-wise. This is
+ // important because we're also using stdin to send data to the script. If
+ // the shell executes the input line-wise then our script is interpreted
+ // as input for `read`.
//
- // The target directory must no permit other users to delete our files
- // or symlink attacks and arbitrary code execution is possible. For
- // /tmp this is guaranteed by the sticky bit. The code verifies the
- // directory has the proper permissions.
+ // The target directory must no permit other users to delete our files or
+ // symlink attacks and arbitrary code execution is possible. For /tmp this
+ // is guaranteed by the sticky bit. The code verifies the directory has
+ // the proper permissions.
//
// We cannot use `test -f && test -O` because this is open to TOCTOU
- // attacks. `stat` gives use the full file state. If the file is owned
- // by us and not a symlink then it's safe to use (assuming sticky
- // directory or directory not writable by others).
+ // attacks. `stat` gives use the full file state. If the file is owned by
+ // us and not a symlink then it's safe to use (assuming sticky directory
+ // or directory not writable by others).
//
- // `test -e` is only used to prevent error messages if the file
- // doesn't exist. It does not guard against any races.
+ // `test -e` is only used to prevent error messages if the file doesn't
+ // exist. It does not guard against any races.
_, err = fmt.Fprintf(stdin, `
%s
f() {
} else {
x := strings.Fields(remoteSum)
if len(x) < 1 {
- return fmt.Errorf("got unexpected checksum line %q",
- remoteSum)
+ return fmt.Errorf("got unexpected checksum line %q", remoteSum)
}
sha := sha512.Sum512(helper)
hex := hex.EncodeToString(sha[:])
}
// Get path to temporary file for upload.
//
- // Write to the temporary file instead of the final path so
- // that a concurrent run of this function won't use a
- // partially written file. The rm in the script could still
- // cause a missing file but at least no file with unknown
- // content is executed.
+ // Write to the temporary file instead of the final path so that a
+ // concurrent run of this function won't use a partially written file.
+ // The rm in the script could still cause a missing file but at least
+ // no file with unknown content is executed.
path, err := stdout.ReadString('\n')
if err != nil {
return err
}
return uid, nil
}
+
+// vi: set noet ts=4 sw=4 sts=4:
actStr := fmt.Sprintf("%v", act)
expStr := fmt.Sprintf("%v", exp)
if actStr != expStr {
- t.Errorf("err = %s (%#v), want %s (%#v)",
- actStr, act, expStr, exp)
+ t.Errorf("err = %s (%#v), want %s (%#v)", actStr, act, expStr, exp)
}
}
+
+// vi: set noet ts=4 sw=4 sts=4:
Output string // stdout and stderr combined
Error string
}
+
+// vi: set noet ts=4 sw=4 sts=4: