lock.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. // Copyright 2015 CoreOS, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package iptables
  15. import (
  16. "os"
  17. "sync"
  18. "syscall"
  19. )
  20. const (
  21. // In earlier versions of iptables, the xtables lock was implemented
  22. // via a Unix socket, but now flock is used via this lockfile:
  23. // http://git.netfilter.org/iptables/commit/?id=aa562a660d1555b13cffbac1e744033e91f82707
  24. // Note the LSB-conforming "/run" directory does not exist on old
  25. // distributions, so assume "/var" is symlinked
  26. xtablesLockFilePath = "/var/run/xtables.lock"
  27. defaultFilePerm = 0600
  28. )
  29. type Unlocker interface {
  30. Unlock() error
  31. }
  32. type nopUnlocker struct{}
  33. func (_ nopUnlocker) Unlock() error { return nil }
  34. type fileLock struct {
  35. // mu is used to protect against concurrent invocations from within this process
  36. mu sync.Mutex
  37. fd int
  38. }
  39. // tryLock takes an exclusive lock on the xtables lock file without blocking.
  40. // This is best-effort only: if the exclusive lock would block (i.e. because
  41. // another process already holds it), no error is returned. Otherwise, any
  42. // error encountered during the locking operation is returned.
  43. // The returned Unlocker should be used to release the lock when the caller is
  44. // done invoking iptables commands.
  45. func (l *fileLock) tryLock() (Unlocker, error) {
  46. l.mu.Lock()
  47. err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB)
  48. switch err {
  49. case syscall.EWOULDBLOCK:
  50. l.mu.Unlock()
  51. return nopUnlocker{}, nil
  52. case nil:
  53. return l, nil
  54. default:
  55. l.mu.Unlock()
  56. return nil, err
  57. }
  58. }
  59. // Unlock closes the underlying file, which implicitly unlocks it as well. It
  60. // also unlocks the associated mutex.
  61. func (l *fileLock) Unlock() error {
  62. defer l.mu.Unlock()
  63. return syscall.Close(l.fd)
  64. }
  65. // newXtablesFileLock opens a new lock on the xtables lockfile without
  66. // acquiring the lock
  67. func newXtablesFileLock() (*fileLock, error) {
  68. fd, err := syscall.Open(xtablesLockFilePath, os.O_CREATE, defaultFilePerm)
  69. if err != nil {
  70. return nil, err
  71. }
  72. return &fileLock{fd: fd}, nil
  73. }