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 15package iptables 16 17import ( 18 "os" 19 "sync" 20 "syscall" 21) 22 23const ( 24 // In earlier versions of iptables, the xtables lock was implemented 25 // via a Unix socket, but now flock is used via this lockfile: 26 // http://git.netfilter.org/iptables/commit/?id=aa562a660d1555b13cffbac1e744033e91f82707 27 // Note the LSB-conforming "/run" directory does not exist on old 28 // distributions, so assume "/var" is symlinked 29 xtablesLockFilePath = "/var/run/xtables.lock" 30 31 defaultFilePerm = 0600 32) 33 34type Unlocker interface { 35 Unlock() error 36} 37 38type nopUnlocker struct{} 39 40func (_ nopUnlocker) Unlock() error { return nil } 41 42type fileLock struct { 43 // mu is used to protect against concurrent invocations from within this process 44 mu sync.Mutex 45 fd int 46} 47 48// tryLock takes an exclusive lock on the xtables lock file without blocking. 49// This is best-effort only: if the exclusive lock would block (i.e. because 50// another process already holds it), no error is returned. Otherwise, any 51// error encountered during the locking operation is returned. 52// The returned Unlocker should be used to release the lock when the caller is 53// done invoking iptables commands. 54func (l *fileLock) tryLock() (Unlocker, error) { 55 l.mu.Lock() 56 err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB) 57 switch err { 58 case syscall.EWOULDBLOCK: 59 l.mu.Unlock() 60 return nopUnlocker{}, nil 61 case nil: 62 return l, nil 63 default: 64 l.mu.Unlock() 65 return nil, err 66 } 67} 68 69// Unlock closes the underlying file, which implicitly unlocks it as well. It 70// also unlocks the associated mutex. 71func (l *fileLock) Unlock() error { 72 defer l.mu.Unlock() 73 return syscall.Close(l.fd) 74} 75 76// newXtablesFileLock opens a new lock on the xtables lockfile without 77// acquiring the lock 78func newXtablesFileLock() (*fileLock, error) { 79 fd, err := syscall.Open(xtablesLockFilePath, os.O_CREATE, defaultFilePerm) 80 if err != nil { 81 return nil, err 82 } 83 return &fileLock{fd: fd}, nil 84} 85