syscontainer-tools/pkg/udevd/udevd_controller.go
taleintervenor dfaef9d393 syscontainer-tools: update license to Mulan PSL v2
reason: update license to Mulan PSL v2

Signed-off-by: taleintervenor <taleintervenor@aliyun.com>
2020-04-27 14:54:17 +08:00

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()
}