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  }