reason: update license to Mulan PSL v2 Signed-off-by: taleintervenor <taleintervenor@aliyun.com>
173 lines
4.3 KiB
Go
173 lines
4.3 KiB
Go
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
// You may obtain a copy of Mulan PSL v2 at:
|
|
// http://license.coscl.org.cn/MulanPSL2
|
|
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
// PURPOSE.
|
|
// See the Mulan PSL v2 for more details.
|
|
// Description: udevd controller
|
|
// Author: zhangwei
|
|
// Create: 2018-01-18
|
|
|
|
package udevd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
hconfig "isula.org/syscontainer-tools/config"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
var (
|
|
programe = "/lib/udev/syscontainer-tools_wrapper"
|
|
lockFile = "udevd_config_locker"
|
|
configPath = "/etc/udev/rules.d/99-syscontainer-tools.rules"
|
|
)
|
|
|
|
// Rule defines an udev rule which used to capture the partition udev event
|
|
type Rule struct {
|
|
Name string
|
|
CtrDevName string
|
|
Container string
|
|
}
|
|
|
|
// ToUdevRuleString will format the Rule structure to udev rule
|
|
func (r *Rule) ToUdevRuleString() string {
|
|
return fmt.Sprintf("KERNEL==\"%s*\",ACTION==\"add|remove\", ENV{DEVTYPE}==\"partition\", SUBSYSTEM==\"block\", RUN{program}+=\"%s $env{ACTION} %s $name %s %s\"",
|
|
filepath.Base(r.Name), programe, r.TrimContainerID(), r.CtrDevName, filepath.Base(r.Name))
|
|
}
|
|
|
|
// TrimContainerID will return container id for short
|
|
func (r *Rule) TrimContainerID() string {
|
|
return r.Container[:8]
|
|
}
|
|
|
|
// Controller is the interface which to manage udev rules
|
|
type Controller interface {
|
|
Lock() error
|
|
Unlock() error
|
|
LoadRules() error
|
|
AddRule(r *Rule)
|
|
RemoveRule(r *Rule)
|
|
ToDisk() error
|
|
}
|
|
|
|
// NewUdevdController will return an UdevController interface
|
|
func NewUdevdController() Controller {
|
|
using, err := usingUdevd()
|
|
if err != nil {
|
|
return &udevdController{useUdevd: false}
|
|
}
|
|
return &udevdController{
|
|
dirty: false,
|
|
useUdevd: using,
|
|
configFile: configPath,
|
|
lockFile: filepath.Join(hconfig.IsuladToolsDir, lockFile),
|
|
}
|
|
}
|
|
|
|
type udevdController struct {
|
|
useUdevd bool
|
|
configFile string
|
|
lockFile string
|
|
rules []*Rule
|
|
dirty bool
|
|
lock *os.File
|
|
}
|
|
|
|
// Lock uses filelock to lock the udev rule file.
|
|
// to make sure only one process could access the resource.
|
|
func (sc *udevdController) Lock() error {
|
|
if !sc.useUdevd {
|
|
return nil
|
|
}
|
|
f, err := os.OpenFile(sc.lockFile, os.O_RDONLY|os.O_CREATE, 0600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// FileLock will be released at 3 conditions:
|
|
// 1. process to unlock manully.
|
|
// 2. Close the opened fd.
|
|
// 3. process died without call unlock. kernel will close the file and release the lock.
|
|
// LOCK_EX means only one process could lock it at one time.
|
|
// LOCK_NB is not set, using block mode.
|
|
if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
|
|
f.Close()
|
|
return err
|
|
}
|
|
sc.lock = f
|
|
return nil
|
|
}
|
|
|
|
// Unlock will release the file lock
|
|
func (sc *udevdController) Unlock() error {
|
|
if !sc.useUdevd || sc.lock == nil {
|
|
return nil
|
|
}
|
|
defer sc.lock.Close()
|
|
return unix.Flock(int(sc.lock.Fd()), unix.LOCK_UN)
|
|
}
|
|
|
|
// LoadRules loads the udev rules from rule config file
|
|
func (sc *udevdController) LoadRules() error {
|
|
if !sc.useUdevd {
|
|
return nil
|
|
}
|
|
|
|
rules, err := loadRules(sc.configFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sc.rules = rules
|
|
return nil
|
|
}
|
|
|
|
// AddRule will add a rule to manager in memory only
|
|
func (sc *udevdController) AddRule(r *Rule) {
|
|
if !sc.useUdevd {
|
|
return
|
|
}
|
|
|
|
for _, rule := range sc.rules {
|
|
if r.Name == rule.Name && r.CtrDevName == rule.CtrDevName && r.TrimContainerID() == rule.TrimContainerID() {
|
|
return
|
|
}
|
|
}
|
|
sc.dirty = true
|
|
sc.rules = append(sc.rules, r)
|
|
return
|
|
}
|
|
|
|
// RemoveRule will add a rule to manager in memory only
|
|
func (sc *udevdController) RemoveRule(r *Rule) {
|
|
if !sc.useUdevd {
|
|
return
|
|
}
|
|
|
|
for index, rule := range sc.rules {
|
|
if r.Name == rule.Name && r.CtrDevName == rule.CtrDevName && r.TrimContainerID() == rule.TrimContainerID() {
|
|
sc.dirty = true
|
|
sc.rules = append(sc.rules[:index], sc.rules[index+1:]...)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// ToDisk will save the rules to udev rule config file
|
|
func (sc *udevdController) ToDisk() error {
|
|
if !sc.useUdevd || !sc.dirty {
|
|
return nil
|
|
}
|
|
|
|
if err := saveRules(sc.configFile, sc.rules); err != nil {
|
|
return err
|
|
}
|
|
return reloadConfig()
|
|
}
|