// Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. // lxcfs-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: remount command // Author: zhangsong // Create: 2019-01-18 // go base main package package main import ( "bufio" "fmt" "io" "io/ioutil" "lxcfs-tools/libmount" "os" "os/exec" "strings" "sync" "syscall" "time" lxcfs_log "github.com/sirupsen/logrus" "github.com/urfave/cli" ) var recmountContainer = cli.Command{ Name: "remount", Usage: "remount lxcfs to a certain container", ArgsUsage: `[options] `, Description: `You can remount lxcfs to a running container by container id.`, Flags: []cli.Flag{ cli.BoolFlag{ Name: "all, a", Usage: "remount lxcfs to all running container", }, cli.StringFlag{ Name: "container-id, c", Value: " ", Usage: "remount a certain container by container id", }, }, Action: func(context *cli.Context) { if context.NArg() > 0 { onfailf("%s: requires none args", context.Command.Name) } if err := waitForLxcfs(); err != nil { onfail(err) } initMountns, err := os.Readlink("/proc/1/ns/mnt") if err != nil { onfail(fmt.Errorf("read init mount namespace fail: %v", err)) } initUserns, err := os.Readlink("/proc/1/ns/user") if err != nil { onfail(fmt.Errorf("read init user namespace fail: %v", err)) } if context.Bool("all") { lxcfs_log.Info("remount to all containers") if err := remountAll(initMountns, initUserns); err != nil { onfail(err) } return } containerid := context.String("container-id") containerid = strings.Replace(containerid, "\n", "", -1) if len(containerid) == 1 { onfailf("%s: You must choose at least one container", context.Command.Name) } if err := remountToContainer(initMountns, initUserns, containerid, "", false); err != nil { onfail(err) } }, } var checklxcfs = cli.Command{ Name: "check-lxcfs", Usage: "check whether lxcfs is running", ArgsUsage: ` `, Description: `do lxcfs check.`, Flags: []cli.Flag{}, Action: func(context *cli.Context) { if context.NArg() > 0 { onfailf("%s: don't need any args", context.Command.Name) } if err := waitForLxcfs(); err != nil { onfail(err) } else { lxcfs_log.Info("lxcfs is running") } }, } var execprestart = cli.Command{ Name: "prestart", Usage: "bind mount before lxcfs start ", ArgsUsage: ` `, Description: `do perstart.`, Flags: []cli.Flag{}, Action: func(context *cli.Context) { if context.NArg() > 0 { onfailf("%s: don't need any args", context.Command.Name) } if err := doprestart(); err != nil { onfail(err) } else { lxcfs_log.Info("prestart done") } }, } func doprestart() error { lxcfs_log.Info("do prestart") if err := syscall.Unmount("/var/lib/lxc/lxcfs", syscall.MNT_DETACH); err == nil { lxcfs_log.Warning("releaseMountpoint: umount /var/lib/lxc/lxcfs") } if err := syscall.Unmount("/var/lib/lxc", syscall.MNT_DETACH); err == nil { lxcfs_log.Warning("releaseMountpoint: umount /var/lib/lxc") } prestartparm1 := []string{ "-B", "/var/lib/lxc", "/var/lib/lxc", } if _, err := execCommond("mount", prestartparm1); err != nil { onfail(err) } prestartparm2 := []string{ "--make-shared", "/var/lib/lxc", } if _, err := execCommond("mount", prestartparm2); err != nil { onfail(err) } return nil } func waitForLxcfs() error { count := 0 // bugfix: wait a bit long time for lxcfs ready maxCount := 300 for count < maxCount { _, err := ioutil.ReadDir("/var/lib/lxc/lxcfs/proc") if err != nil { time.Sleep(time.Millisecond * 50) // sleep time 50 Millisecond } else { break } count++ } if count == maxCount { err := fmt.Errorf("lxcfs is not ready") lxcfs_log.Errorf("%v", err) return err } return nil } func remountAll(initMountns, initUserns string) error { lxcfs_log.Info("begin remount All runing container...") out, err := execCommond("isula", []string{"ps", "--format", "{{.ID}} {{.Pid}}"}) if err != nil { return err } var wg sync.WaitGroup for _, value := range out { containerslice := strings.Fields(value) if len(containerslice) < 2 { continue } wg.Add(1) go func() { defer wg.Done() res := make(chan struct{}, 1) go func() { if err := remountToContainer(initMountns, initUserns, containerslice[0], containerslice[1], true); err != nil { lxcfs_log.Errorf("remount lxcfs dir to container(%s) failed: %v", containerslice[0], err) } res <- struct{}{} }() select { case <-res: case <-time.After(30 * time.Second): // 30s timeout lxcfs_log.Errorf("remount lxcfs dir to container(%s) timeout", containerslice[0]) } }() } wg.Wait() lxcfs_log.Info(" remount All done...") return nil } func remountToContainer(initMountns, initUserns, containerid string, pid string, isAll bool) error { if isAll == false { var err error pid, err = isContainerExsit(containerid) if err != nil { onfail(err) } } lxcfs_log.Infof("begin remount container,container id: %s, pid: %s", containerid, pid) lxcfssubpath, err := ioutil.ReadDir("/var/lib/lxc/lxcfs/proc") if err != nil { return fmt.Errorf("Parse lxcfs dir failed: %v", err) } mountns, err := os.Readlink("/proc/" + pid + "/ns/mnt") if err != nil { return fmt.Errorf("read container mount namespace fail: %v", err) } if initMountns == mountns { return fmt.Errorf("container pid changed: container mount namespace is same as init namespace") } var valuePaths []string var valueMountPaths []string for _, value := range lxcfssubpath { valuePaths = append(valuePaths, fmt.Sprintf("/proc/%s", value.Name())) valueMountPaths = append(valueMountPaths, fmt.Sprintf("/var/lib/lxc/lxcfs/proc/%s", value.Name())) } if err := libmount.NsExecUmount(pid, valuePaths); err != nil { lxcfs_log.Errorf("unmount %v for container error: %v", valuePaths, err) } if err := libmount.NsExecMount(pid, "", valueMountPaths, valuePaths); err != nil { lxcfs_log.Errorf("mount %v into container %s error: %v", valueMountPaths, containerid, err) return err } return nil } func isContainerExsit(containerid string) (string, error) { lxcfs_log.Info("begin isContainerExsit...") if containerid == "" { return "", fmt.Errorf("Containerid mustn't be empty") } out, err := execCommond("isula", []string{"ps", "--format", "{{.ID}} {{.Pid}}"}) if err != nil { onfail(err) } for _, value := range out { containerslice := strings.Fields(value) if len(containerslice) < 2 { continue } if strings.Contains(containerslice[0], containerid) { return containerslice[1], nil } } return "", fmt.Errorf("No such Container %s in this node", containerid) } func execCommond(command string, params []string) ([]string, error) { cmd := exec.Command(command, params...) res := []string{ " ", } lxcfs_log.Info("exec cmd :", cmd.Args) stdout, err := cmd.StdoutPipe() if err != nil { return res, err } if err := cmd.Start(); err != nil { return res, err } reader := bufio.NewReader(stdout) for { line, err2 := reader.ReadString('\n') if err2 == io.EOF { break } else if err2 != nil { onfail(err2) } res = append(res, line) } if err := cmd.Wait(); err != nil { return res, err } return res, nil }