github.com/apptainer/singularity@v3.1.1+incompatible/cmd/internal/cli/instance_linux.go (about) 1 // Copyright (c) 2018-2019, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package cli 7 8 import ( 9 "encoding/json" 10 "errors" 11 "fmt" 12 "os" 13 "syscall" 14 "time" 15 16 "github.com/spf13/cobra" 17 "github.com/sylabs/singularity/docs" 18 "github.com/sylabs/singularity/internal/pkg/instance" 19 "github.com/sylabs/singularity/internal/pkg/sylog" 20 "github.com/sylabs/singularity/internal/pkg/util/signal" 21 "github.com/sylabs/singularity/pkg/util/fs/proc" 22 ) 23 24 // instance list/stop options 25 var username string 26 27 // instance list options 28 var jsonFormat bool 29 30 // instance stop options 31 var stopSignal string 32 var stopAll bool 33 var forceStop bool 34 var stopTimeout int 35 36 func init() { 37 SingularityCmd.AddCommand(InstanceCmd) 38 InstanceCmd.AddCommand(InstanceStartCmd) 39 InstanceCmd.AddCommand(InstanceStopCmd) 40 InstanceCmd.AddCommand(InstanceListCmd) 41 } 42 43 // InstanceCmd singularity instance 44 var InstanceCmd = &cobra.Command{ 45 RunE: func(cmd *cobra.Command, args []string) error { 46 return errors.New("Invalid command") 47 }, 48 DisableFlagsInUseLine: true, 49 50 Use: docs.InstanceUse, 51 Short: docs.InstanceShort, 52 Long: docs.InstanceLong, 53 Example: docs.InstanceExample, 54 SilenceErrors: true, 55 } 56 57 func listInstance() { 58 uid := os.Getuid() 59 if username != "" && uid != 0 { 60 sylog.Fatalf("only root user can list user's instances") 61 } 62 files, err := instance.List(username, "*", instance.SingSubDir) 63 if err != nil { 64 sylog.Fatalf("failed to retrieve instance list: %s", err) 65 } 66 if !jsonFormat { 67 fmt.Printf("%-16s %-8s %s\n", "INSTANCE NAME", "PID", "IMAGE") 68 for _, file := range files { 69 fmt.Printf("%-16s %-8d %s\n", file.Name, file.Pid, file.Image) 70 } 71 } else { 72 output := make(map[string][]jsonList) 73 output["instances"] = make([]jsonList, len(files)) 74 75 for i := range output["instances"] { 76 output["instances"][i].Image = files[i].Image 77 output["instances"][i].Pid = files[i].Pid 78 output["instances"][i].Instance = files[i].Name 79 } 80 81 c, err := json.MarshalIndent(output, "", "\t") 82 if err != nil { 83 sylog.Fatalf("error while printing structured JSON: %s", err) 84 } 85 fmt.Println(string(c)) 86 } 87 } 88 89 func killInstance(file *instance.File, sig syscall.Signal, fileChan chan *instance.File) { 90 syscall.Kill(file.Pid, sig) 91 92 for { 93 if err := syscall.Kill(file.PPid, 0); err == syscall.ESRCH { 94 fileChan <- file 95 break 96 } else if childs, err := proc.CountChilds(file.Pid); childs == 0 { 97 if err == nil { 98 syscall.Kill(file.Pid, syscall.SIGKILL) 99 } 100 } 101 time.Sleep(10 * time.Millisecond) 102 } 103 } 104 105 func stopInstance(name string) { 106 sig := syscall.SIGINT 107 uid := os.Getuid() 108 fileChan := make(chan *instance.File, 1) 109 stopped := make([]int, 0) 110 111 if username != "" && uid != 0 { 112 sylog.Fatalf("only root user can list user's instances") 113 } 114 if stopSignal != "" { 115 var err error 116 117 sig, err = signal.Convert(stopSignal) 118 if err != nil { 119 sylog.Fatalf("%s", err) 120 } 121 } 122 if forceStop { 123 sig = syscall.SIGKILL 124 } 125 files, err := instance.List(username, name, instance.SingSubDir) 126 if err != nil { 127 sylog.Fatalf("failed to retrieve instance list: %s", err) 128 } 129 if len(files) == 0 { 130 sylog.Fatalf("no instance found") 131 } 132 133 for _, file := range files { 134 go killInstance(file, sig, fileChan) 135 } 136 137 for { 138 select { 139 case f := <-fileChan: 140 fmt.Printf("Stopping %s instance of %s (PID=%d)\n", f.Name, f.Image, f.Pid) 141 stopped = append(stopped, f.Pid) 142 if len(stopped) == len(files) { 143 os.Exit(0) 144 } 145 case <-time.After(time.Duration(stopTimeout) * time.Second): 146 for _, file := range files { 147 kill := true 148 for _, pid := range stopped { 149 if pid == file.Pid { 150 kill = false 151 break 152 } 153 } 154 if !kill { 155 continue 156 } 157 syscall.Kill(file.Pid, syscall.SIGKILL) 158 fmt.Printf("Killing %s instance of %s (PID=%d) (Timeout)\n", file.Name, file.Image, file.Pid) 159 } 160 os.Exit(0) 161 } 162 } 163 }