├── LICENSE ├── bin └── .gitkeep ├── .gitignore ├── app ├── .DS_Store ├── app.go ├── daemon.go └── file.go ├── scripts ├── logs_date.sh └── add_line_numbers.sh ├── examples ├── test.py ├── sigint_fail.py ├── stress.sh └── ecosystem.json ├── main.go ├── grpc ├── server │ ├── delete_process.go │ ├── find_process.go │ ├── list_process.go │ ├── start_process.go │ ├── new_server.go │ ├── stop_process.go │ ├── add_process.go │ ├── spawn_process.go │ └── start_scheduler.go └── client │ └── new_client.go ├── Makefile ├── go.mod ├── utils ├── config.go ├── parser.go └── functions.go ├── cmd ├── status.go ├── kill.go ├── restore.go ├── dump.go ├── root.go ├── ls.go ├── restart.go ├── stop.go ├── flush.go ├── start.go ├── delete.go ├── config.go ├── logs.go └── describe.go ├── proto ├── process_extended.go ├── process.proto ├── process_grpc.pb.go └── process.pb.go ├── README.md ├── main_test.go ├── go.sum └── shared └── spawn.go /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .DS_Store 3 | bin/** 4 | !bin/.gitkeep 5 | dist/ 6 | .* -------------------------------------------------------------------------------- /app/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunstorm/pm2-go/HEAD/app/.DS_Store -------------------------------------------------------------------------------- /scripts/logs_date.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | while IFS= read -r line; do 3 | echo "$(date): ${line}" 4 | done -------------------------------------------------------------------------------- /scripts/add_line_numbers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | i=0 3 | while IFS= read -r line; do 4 | echo "${i} - ${line}" 5 | ((i=i+1)) 6 | done -------------------------------------------------------------------------------- /examples/test.py: -------------------------------------------------------------------------------- 1 | import time 2 | for i in range(5): 3 | print(str(i)) 4 | if i > 2: 5 | raise Exception("an_error") 6 | time.sleep(1) -------------------------------------------------------------------------------- /examples/sigint_fail.py: -------------------------------------------------------------------------------- 1 | import time 2 | for i in range(500): 3 | try: 4 | print(str(i)) 5 | time.sleep(1) 6 | except: 7 | None -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package main 6 | 7 | import "github.com/dunstorm/pm2-go/cmd" 8 | 9 | func main() { 10 | cmd.Execute() 11 | } 12 | -------------------------------------------------------------------------------- /examples/stress.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # checking memory leak 4 | for ((i = 1; i <= 10000; i++)); do 5 | echo "--- Iteration #$i: $(date) ---" 6 | make start 7 | make stop 8 | make delete 9 | sleep 1 10 | done -------------------------------------------------------------------------------- /examples/ecosystem.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "python-test", 4 | "args": ["-u", "test.py"], 5 | "autorestart": false, 6 | "cwd": "./examples", 7 | "scripts": ["logs_date"], 8 | "executable_path": "python3", 9 | "cron_restart": "* * * * *" 10 | } 11 | ] -------------------------------------------------------------------------------- /grpc/server/delete_process.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | 6 | pb "github.com/dunstorm/pm2-go/proto" 7 | ) 8 | 9 | // delete process 10 | func (api *Handler) DeleteProcess(ctx context.Context, in *pb.DeleteProcessRequest) (*pb.DeleteProcessResponse, error) { 11 | api.mu.Lock() 12 | defer api.mu.Unlock() 13 | 14 | process := api.databaseById[in.Id] 15 | 16 | delete(api.databaseById, process.Id) 17 | delete(api.databaseByName, process.Name) 18 | delete(api.processes, in.Id) 19 | 20 | return &pb.DeleteProcessResponse{ 21 | Success: true, 22 | }, nil 23 | } 24 | -------------------------------------------------------------------------------- /grpc/server/find_process.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | pb "github.com/dunstorm/pm2-go/proto" 8 | "google.golang.org/grpc/status" 9 | ) 10 | 11 | // find process 12 | func (api *Handler) FindProcess(ctx context.Context, in *pb.FindProcessRequest) (*pb.Process, error) { 13 | api.mu.Lock() 14 | defer api.mu.Unlock() 15 | var process *pb.Process 16 | 17 | if id, err := strconv.Atoi(in.Name); err == nil { 18 | process = api.databaseById[int32(id)] 19 | } else { 20 | process = api.databaseByName[in.Name] 21 | } 22 | 23 | if process == nil { 24 | return nil, status.Error(400, "failed to find process") 25 | } 26 | 27 | return process, nil 28 | } 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | protoc: 2 | @echo "Generating Go files" 3 | cd proto && protoc --go_out=. --go-grpc_out=. *.proto 4 | 5 | build: 6 | go build -o bin/pm2-go main.go 7 | 8 | install: 9 | go install . 10 | 11 | daemon: 12 | go build -o bin/pm2-go main.go 13 | ./bin/pm2-go kill 14 | ./bin/pm2-go -d 15 | 16 | start: 17 | ./bin/pm2-go start examples/ecosystem.json 18 | 19 | stop: 20 | ./bin/pm2-go stop examples/ecosystem.json 21 | 22 | delete: 23 | ./bin/pm2-go delete examples/ecosystem.json 24 | 25 | ls: 26 | ./bin/pm2-go ls 27 | 28 | kill: 29 | ./bin/pm2-go kill 30 | 31 | logs: 32 | ./bin/pm2-go logs python-test 33 | 34 | test: 35 | go test -v 36 | 37 | dump: 38 | ./bin/pm2-go dump 39 | 40 | restore: 41 | ./bin/pm2-go restore 42 | 43 | flush: 44 | ./bin/pm2-go flush 45 | -------------------------------------------------------------------------------- /grpc/server/list_process.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "sort" 6 | 7 | pb "github.com/dunstorm/pm2-go/proto" 8 | ) 9 | 10 | // list processes 11 | func (api *Handler) ListProcess(ctx context.Context, in *pb.ListProcessRequest) (*pb.ListProcessResponse, error) { 12 | api.mu.Lock() 13 | defer api.mu.Unlock() 14 | for _, p := range api.databaseById { 15 | p.UpdateCPUMemory() 16 | } 17 | 18 | // convert map to sorted slice 19 | keys := make([]int32, len(api.databaseById)) 20 | i := 0 21 | for k := range api.databaseById { 22 | keys[i] = k 23 | i++ 24 | } 25 | sort.Slice(keys, func(i, j int) bool { 26 | return keys[i] < keys[j] 27 | }) 28 | 29 | var processes []*pb.Process 30 | for _, k := range keys { 31 | processes = append(processes, api.databaseById[k]) 32 | } 33 | 34 | return &pb.ListProcessResponse{Processes: processes}, nil 35 | } 36 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dunstorm/pm2-go 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/aptible/supercronic v0.2.30 7 | github.com/fatih/color v1.17.0 8 | github.com/jedib0t/go-pretty/v6 v6.5.9 9 | github.com/rs/zerolog v1.33.0 10 | github.com/spf13/cobra v1.8.1 11 | google.golang.org/grpc v1.66.0 12 | google.golang.org/protobuf v1.34.2 13 | ) 14 | 15 | require ( 16 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 17 | github.com/mattn/go-colorable v0.1.13 // indirect 18 | github.com/mattn/go-isatty v0.0.20 // indirect 19 | github.com/mattn/go-runewidth v0.0.16 // indirect 20 | github.com/rivo/uniseg v0.4.7 // indirect 21 | github.com/spf13/pflag v1.0.5 // indirect 22 | golang.org/x/net v0.38.0 // indirect 23 | golang.org/x/sys v0.31.0 // indirect 24 | golang.org/x/text v0.23.0 // indirect 25 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /utils/config.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "os" 4 | 5 | type Config struct { 6 | LogRotate bool `json:"logrotate"` 7 | LogRotateSize int `json:"logrotate_size"` 8 | LogRotateMaxFiles int `json:"logrotate_max_files"` 9 | } 10 | 11 | // find or create config file 12 | func FindOrCreateConfigFile() string { 13 | configFile := os.Getenv("HOME") + "/.pm2-go/config.json" 14 | if _, err := os.Stat(configFile); os.IsNotExist(err) { 15 | err := SaveObject(configFile, Config{ 16 | LogRotate: false, 17 | LogRotateSize: 10485760, 18 | LogRotateMaxFiles: 10, 19 | }) 20 | if err != nil { 21 | panic(err) 22 | } 23 | } 24 | return configFile 25 | } 26 | 27 | // get config 28 | func GetConfig() Config { 29 | var config Config 30 | err := LoadObject(FindOrCreateConfigFile(), &config) 31 | if err != nil { 32 | panic(err) 33 | } 34 | return config 35 | } 36 | 37 | // save config 38 | func SaveConfig(config Config) { 39 | err := SaveObject(FindOrCreateConfigFile(), config) 40 | if err != nil { 41 | panic(err) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /utils/parser.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | func ParseBool(str string) bool { 9 | return str == "true" 10 | } 11 | 12 | func ParseInt(str string) int { 13 | i, err := strconv.Atoi(str) 14 | if err != nil { 15 | panic(err) 16 | } 17 | return i 18 | } 19 | 20 | // 10K, 10M, 1G 21 | func ParseSize(str string) int { 22 | var size int 23 | var err error 24 | if strings.HasSuffix(str, "K") { 25 | size, err = strconv.Atoi(strings.TrimSuffix(str, "K")) 26 | if err != nil { 27 | panic(err) 28 | } 29 | size = size * 1024 30 | } else if strings.HasSuffix(str, "M") { 31 | size, err = strconv.Atoi(strings.TrimSuffix(str, "M")) 32 | if err != nil { 33 | panic(err) 34 | } 35 | size = size * 1024 * 1024 36 | } else if strings.HasSuffix(str, "G") { 37 | size, err = strconv.Atoi(strings.TrimSuffix(str, "G")) 38 | if err != nil { 39 | panic(err) 40 | } 41 | size = size * 1024 * 1024 * 1024 42 | } else { 43 | size, err = strconv.Atoi(str) 44 | if err != nil { 45 | panic(err) 46 | } 47 | } 48 | return size 49 | } 50 | -------------------------------------------------------------------------------- /grpc/server/start_process.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | pb "github.com/dunstorm/pm2-go/proto" 8 | ) 9 | 10 | // start/update process 11 | func (api *Handler) StartProcess(ctx context.Context, in *pb.StartProcessRequest) (*pb.Process, error) { 12 | api.mu.Lock() 13 | defer api.mu.Unlock() 14 | 15 | process := api.databaseById[in.Id] 16 | 17 | process.InitStartedAt() 18 | process.InitUptime() 19 | 20 | process.Name = in.Name 21 | process.ExecutablePath = in.ExecutablePath 22 | process.Args = in.Args 23 | process.PidFilePath = in.PidFilePath 24 | process.LogFilePath = in.LogFilePath 25 | process.ErrFilePath = in.ErrFilePath 26 | process.AutoRestart = in.AutoRestart 27 | process.Cwd = in.Cwd 28 | process.Pid = in.Pid 29 | process.CronRestart = in.CronRestart 30 | process.ProcStatus.ParentPid = 1 31 | err := process.UpdateNextStartAt() 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | found, err := os.FindProcess(int(in.Pid)) 37 | if err != nil { 38 | process.Pid = in.Pid 39 | } 40 | 41 | process.SetStopSignal(false) 42 | process.SetStatus("online") 43 | updateProcessMap(api, in.Id, found) 44 | 45 | return process, nil 46 | } 47 | -------------------------------------------------------------------------------- /grpc/server/new_server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "os" 8 | "sync" 9 | 10 | pb "github.com/dunstorm/pm2-go/proto" 11 | "github.com/rs/zerolog" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | type Handler struct { 16 | logger *zerolog.Logger 17 | databaseById map[int32]*pb.Process 18 | databaseByName map[string]*pb.Process 19 | mu sync.Mutex 20 | 21 | processes map[int32]*os.Process 22 | nextId int32 23 | 24 | pb.UnimplementedProcessManagerServer 25 | } 26 | 27 | func New(port int) { 28 | logger := zerolog.New(os.Stderr).With().Timestamp().Logger() 29 | lis, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) 30 | if err != nil { 31 | log.Fatalf("failed to listen: %v", err) 32 | } 33 | s := grpc.NewServer() 34 | handler := &Handler{ 35 | logger: &logger, 36 | databaseById: make(map[int32]*pb.Process, 0), 37 | databaseByName: make(map[string]*pb.Process, 0), 38 | processes: make(map[int32]*os.Process, 0), 39 | } 40 | pb.RegisterProcessManagerServer(s, handler) 41 | 42 | startScheduler(handler) 43 | 44 | handler.logger.Info().Msgf("Serving GRPC server at %s", lis.Addr()) 45 | 46 | if err := s.Serve(lis); err != nil { 47 | log.Fatalf("failed to serve: %v", err) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /cmd/status.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "github.com/dunstorm/pm2-go/utils" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | // statusCmd represents the status command 13 | var statusCmd = &cobra.Command{ 14 | Use: "status", 15 | Short: "Display status of daemon", 16 | Long: `Display status of daemon`, 17 | Run: func(cmd *cobra.Command, args []string) { 18 | logger := master.GetLogger() 19 | pid, err := utils.ReadPidFile("daemon.pid") 20 | if err != nil { 21 | logger.Info().Msg("PM2 Daemon Not Running") 22 | return 23 | } 24 | process, isRunning := utils.IsProcessRunning(pid) 25 | if isRunning { 26 | logger.Info().Msg("PM2 Daemon Running") 27 | logger.Info().Msgf("PID: %d", process.Pid) 28 | } else { 29 | logger.Info().Msg("PM2 Daemon Not Running") 30 | } 31 | }, 32 | } 33 | 34 | func init() { 35 | rootCmd.AddCommand(statusCmd) 36 | 37 | // Here you will define your flags and configuration settings. 38 | 39 | // Cobra supports Persistent Flags which will work for this command 40 | // and all subcommands, e.g.: 41 | // statusCmd.PersistentFlags().String("foo", "", "A help for foo") 42 | 43 | // Cobra supports local flags which will only run when this command 44 | // is called directly, e.g.: 45 | // statusCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 46 | } 47 | -------------------------------------------------------------------------------- /grpc/server/stop_process.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "syscall" 6 | 7 | pb "github.com/dunstorm/pm2-go/proto" 8 | ) 9 | 10 | // stop process 11 | func (api *Handler) StopProcess(ctx context.Context, in *pb.StopProcessRequest) (*pb.StopProcessResponse, error) { 12 | api.mu.Lock() 13 | defer api.mu.Unlock() 14 | 15 | process := api.databaseById[in.Id] 16 | found := api.processes[in.Id] 17 | 18 | process.SetStatus("stopped") 19 | process.ResetCPUMemory() 20 | process.StopSignal = true 21 | 22 | if found == nil { 23 | api.logger.Info().Msgf("process not found: %d", in.Id) 24 | return &pb.StopProcessResponse{ 25 | Success: false, 26 | }, nil 27 | } 28 | 29 | if process.ProcStatus.ParentPid == 1 { 30 | api.logger.Info().Msgf("sending stop signal to: %d", found.Pid) 31 | 32 | // kill process which have parent pid 1 33 | err := found.Signal(syscall.SIGTERM) 34 | if err != nil { 35 | api.logger.Info().Msgf("failed to stop process: %s", err.Error()) 36 | updateProcessMap(api, in.Id, nil) 37 | 38 | return &pb.StopProcessResponse{ 39 | Success: false, 40 | }, nil 41 | } 42 | process.ResetPid() 43 | 44 | return &pb.StopProcessResponse{ 45 | Success: true, 46 | }, nil 47 | } 48 | 49 | process.ResetPid() 50 | 51 | // for child process 52 | found.Kill() 53 | updateProcessMap(api, in.Id, nil) 54 | 55 | return &pb.StopProcessResponse{ 56 | Success: true, 57 | }, nil 58 | } 59 | -------------------------------------------------------------------------------- /grpc/server/add_process.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | pb "github.com/dunstorm/pm2-go/proto" 8 | "github.com/dunstorm/pm2-go/utils" 9 | "google.golang.org/grpc/status" 10 | "google.golang.org/protobuf/types/known/durationpb" 11 | "google.golang.org/protobuf/types/known/timestamppb" 12 | ) 13 | 14 | // create process 15 | func (api *Handler) AddProcess(ctx context.Context, in *pb.AddProcessRequest) (*pb.Process, error) { 16 | newProcess := &pb.Process{ 17 | Id: api.nextId, 18 | Name: in.Name, 19 | ExecutablePath: in.ExecutablePath, 20 | Pid: int32(in.Pid), 21 | Args: in.Args, 22 | Cwd: in.Cwd, 23 | Scripts: in.Scripts, 24 | LogFilePath: in.LogFilePath, 25 | ErrFilePath: in.ErrFilePath, 26 | PidFilePath: in.PidFilePath, 27 | AutoRestart: in.AutoRestart, 28 | CronRestart: in.CronRestart, 29 | ProcStatus: &pb.ProcStatus{ 30 | Status: "online", 31 | StartedAt: timestamppb.New(time.Now()), 32 | Uptime: durationpb.New(0), 33 | Cpu: "0.0%", 34 | Memory: "0.0MB", 35 | ParentPid: 1, 36 | }, 37 | } 38 | err := newProcess.UpdateNextStartAt() 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | process, running := utils.GetProcess(newProcess.Pid) 44 | if !running { 45 | return nil, status.Error(400, "failed to add process") 46 | } 47 | 48 | api.mu.Lock() 49 | defer api.mu.Unlock() 50 | api.databaseById[api.nextId] = newProcess 51 | api.databaseByName[newProcess.Name] = newProcess 52 | api.processes[newProcess.Id] = process 53 | api.nextId++ 54 | 55 | return newProcess, nil 56 | } 57 | -------------------------------------------------------------------------------- /cmd/kill.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/dunstorm/pm2-go/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // killCmd represents the kill command 15 | var killCmd = &cobra.Command{ 16 | Use: "kill", 17 | Short: "Kill daemon", 18 | Long: `Kill daemon`, 19 | Run: func(cmd *cobra.Command, args []string) { 20 | pid, err := utils.ReadPidFile("daemon.pid") 21 | if err != nil { 22 | return 23 | } 24 | logger := master.GetLogger() 25 | process, isRunning := utils.IsProcessRunning(pid) 26 | if isRunning { 27 | procs := master.ListProcess() 28 | if len(procs) > 0 { 29 | for _, p := range procs { 30 | if p.ProcStatus.Status == "online" { 31 | logger.Info().Msgf("Applying action stopProcessId on app [%s](pid: [ %d ])", p.Name, p.Pid) 32 | master.StopProcess(p.Id) 33 | } 34 | } 35 | } else { 36 | logger.Warn().Msg("No processes found") 37 | } 38 | err := process.Kill() 39 | if err != nil { 40 | fmt.Println(err) 41 | return 42 | } 43 | logger.Info().Msg("PM2 Daemon Stopped") 44 | } 45 | }, 46 | } 47 | 48 | func init() { 49 | rootCmd.AddCommand(killCmd) 50 | 51 | // Here you will define your flags and configuration settings. 52 | 53 | // Cobra supports Persistent Flags which will work for this command 54 | // and all subcommands, e.g.: 55 | // killCmd.PersistentFlags().String("foo", "", "A help for foo") 56 | 57 | // Cobra supports local flags which will only run when this command 58 | // is called directly, e.g.: 59 | // killCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 60 | } 61 | -------------------------------------------------------------------------------- /cmd/restore.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "strings" 8 | 9 | pb "github.com/dunstorm/pm2-go/proto" 10 | "github.com/dunstorm/pm2-go/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // restoreCmd represents the restore command 15 | var restoreCmd = &cobra.Command{ 16 | Use: "restore", 17 | Short: "restore previously dumped processes", 18 | Long: `restore previously dumped processes`, 19 | Run: func(cmd *cobra.Command, args []string) { 20 | master.SpawnDaemon() 21 | logger := master.GetLogger() 22 | 23 | dumpFileName := "dump.json" 24 | if len(args) > 0 { 25 | dumpFileName = args[0] 26 | } 27 | 28 | // add .json if not exists 29 | if !strings.HasSuffix(dumpFileName, ".json") { 30 | dumpFileName = dumpFileName + ".json" 31 | } 32 | dumpFilePath := utils.GetDumpFilePath(dumpFileName) 33 | 34 | allProcesses := []*pb.Process{} 35 | err := utils.LoadObject(dumpFilePath, &allProcesses) 36 | if err != nil { 37 | logger.Error().Msg(err.Error()) 38 | return 39 | } 40 | logger.Info().Msgf("Restoring processes located in %s", dumpFilePath) 41 | master.RestoreProcess(allProcesses) 42 | renderProcessList() 43 | }, 44 | } 45 | 46 | func init() { 47 | rootCmd.AddCommand(restoreCmd) 48 | 49 | // Here you will define your flags and configuration settings. 50 | 51 | // Cobra supports Persistent Flags which will work for this command 52 | // and all subcommands, e.g.: 53 | // restoreCmd.PersistentFlags().String("foo", "", "A help for foo") 54 | 55 | // Cobra supports local flags which will only run when this command 56 | // is called directly, e.g.: 57 | // restoreCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 58 | } 59 | -------------------------------------------------------------------------------- /cmd/dump.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "strings" 9 | 10 | pb "github.com/dunstorm/pm2-go/proto" 11 | "github.com/dunstorm/pm2-go/utils" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | // dumpCmd represents the dump command 16 | var dumpCmd = &cobra.Command{ 17 | Use: "dump", 18 | Short: "dump all processes for resurrecting them later", 19 | Long: `dump all processes for resurrecting them later`, 20 | Run: func(cmd *cobra.Command, args []string) { 21 | dumpFileName := "dump.json" 22 | if len(args) > 0 { 23 | dumpFileName = args[0] 24 | } 25 | 26 | // add .json if not exists 27 | if !strings.HasSuffix(dumpFileName, ".json") { 28 | dumpFileName = dumpFileName + ".json" 29 | } 30 | 31 | master.SpawnDaemon() 32 | logger := master.GetLogger() 33 | logger.Info().Msg("Saving current process list...") 34 | allProcesses := []*pb.Process{} 35 | allProcesses = append(allProcesses, master.ListProcess()...) 36 | dumpFilePath := utils.GetDumpFilePath(dumpFileName) 37 | err := utils.SaveObject(dumpFilePath, allProcesses) 38 | if err != nil { 39 | logger.Error().Msg(err.Error()) 40 | return 41 | } 42 | logger.Info().Msg("Successfully saved in " + dumpFilePath) 43 | }, 44 | } 45 | 46 | func init() { 47 | rootCmd.AddCommand(dumpCmd) 48 | 49 | // Here you will define your flags and configuration settings. 50 | 51 | // Cobra supports Persistent Flags which will work for this command 52 | // and all subcommands, e.g.: 53 | // dumpCmd.PersistentFlags().String("foo", "", "A help for foo") 54 | 55 | // Cobra supports local flags which will only run when this command 56 | // is called directly, e.g.: 57 | // dumpCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 58 | } 59 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | app "github.com/dunstorm/pm2-go/app" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | var master = app.New() 16 | 17 | // rootCmd represents the base command when called without any subcommands 18 | var rootCmd = &cobra.Command{ 19 | Use: "pm2-go", 20 | Short: "Production process manager written in Go", 21 | Long: `PM2-GO is a production process manager for any application with a built-in load balancer (WIP). 22 | It allows you to keep applications alive forever, to reload them without downtime and to facilitate common system admin tasks.`, 23 | // Uncomment the following line if your bare application 24 | // has an action associated with it: 25 | Run: func(cmd *cobra.Command, args []string) { 26 | if daemon, _ := cmd.PersistentFlags().GetBool("daemon"); daemon { 27 | master.SpawnDaemon() 28 | return 29 | } 30 | if version, _ := cmd.PersistentFlags().GetBool("version"); version { 31 | fmt.Println("0.1.1") 32 | return 33 | } 34 | cmd.Usage() 35 | }, 36 | } 37 | 38 | // Execute adds all child commands to the root command and sets flags appropriately. 39 | // This is called by main.main(). It only needs to happen once to the rootCmd. 40 | func Execute() { 41 | err := rootCmd.Execute() 42 | if err != nil { 43 | os.Exit(1) 44 | } 45 | } 46 | 47 | func init() { 48 | // Here you will define your flags and configuration settings. 49 | // Cobra supports persistent flags, which, if defined here, 50 | // will be global for your application. 51 | 52 | // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.pm2-go.yaml)") 53 | 54 | // Cobra also supports local flags, which will only run 55 | // when this action is called directly. 56 | rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 57 | rootCmd.PersistentFlags().BoolP("daemon", "d", false, "Run as daemon") 58 | rootCmd.PersistentFlags().BoolP("version", "v", false, "Print pm2 version") 59 | logsCmd.PersistentFlags().IntP("lines", "l", 15, "Number of lines to tail") 60 | } 61 | -------------------------------------------------------------------------------- /grpc/server/spawn_process.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "time" 7 | 8 | "github.com/aptible/supercronic/cronexpr" 9 | pb "github.com/dunstorm/pm2-go/proto" 10 | "github.com/dunstorm/pm2-go/shared" 11 | "github.com/dunstorm/pm2-go/utils" 12 | "google.golang.org/grpc/status" 13 | "google.golang.org/protobuf/types/known/durationpb" 14 | "google.golang.org/protobuf/types/known/timestamppb" 15 | ) 16 | 17 | // spawn process 18 | func (api *Handler) SpawnProcess(ctx context.Context, in *pb.SpawnProcessRequest) (*pb.SpawnProcessResponse, error) { 19 | api.mu.Lock() 20 | defer api.mu.Unlock() 21 | 22 | // shared: spawn new process 23 | process, err := shared.SpawnNewProcess(shared.SpawnParams{ 24 | Name: in.Name, 25 | Args: in.Args, 26 | ExecutablePath: in.ExecutablePath, 27 | AutoRestart: in.AutoRestart, 28 | Logger: api.logger, 29 | Cwd: in.Cwd, 30 | Scripts: in.Scripts, 31 | CronRestart: in.CronRestart, 32 | }) 33 | 34 | if err != nil { 35 | return &pb.SpawnProcessResponse{ 36 | Success: false, 37 | }, nil 38 | } 39 | 40 | api.logger.Info().Msgf("spawned process: %d", process.Pid) 41 | 42 | process.Id = api.nextId 43 | process.ProcStatus = &pb.ProcStatus{ 44 | Status: "online", 45 | StartedAt: timestamppb.New(time.Now()), 46 | Uptime: durationpb.New(0), 47 | Cpu: "0.0%", 48 | Memory: "0.0MB", 49 | ParentPid: int32(os.Getpid()), 50 | } 51 | 52 | if in.CronRestart != "" { 53 | expr, err := cronexpr.Parse(in.CronRestart) 54 | if err != nil { 55 | return nil, status.Errorf(400, "Invalid cron expression: %v", err) 56 | } 57 | process.NextStartAt = timestamppb.New(expr.Next(time.Now())) 58 | } 59 | 60 | osProcess, running := utils.GetProcess(process.Pid) 61 | if !running { 62 | return nil, status.Error(400, "failed to spawn process") 63 | } 64 | 65 | api.databaseById[api.nextId] = process 66 | api.databaseByName[process.Name] = process 67 | api.processes[process.Id] = osProcess 68 | api.nextId++ 69 | 70 | go osProcess.Wait() 71 | 72 | return &pb.SpawnProcessResponse{ 73 | Success: true, 74 | }, nil 75 | } 76 | -------------------------------------------------------------------------------- /cmd/ls.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "os" 9 | 10 | "github.com/fatih/color" 11 | "github.com/jedib0t/go-pretty/v6/table" 12 | "github.com/jedib0t/go-pretty/v6/text" 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | func renderProcessList() { 17 | t := table.NewWriter() 18 | 19 | cyanBold := color.New(color.FgCyan, color.Bold).SprintFunc() 20 | t.AppendHeader(table.Row{ 21 | cyanBold("#"), 22 | cyanBold("name"), 23 | cyanBold("pid"), 24 | cyanBold("ppid"), 25 | cyanBold("status"), 26 | cyanBold("uptime"), 27 | cyanBold("↺"), 28 | cyanBold("cpu"), 29 | cyanBold("memory"), 30 | }) 31 | t.SetOutputMirror(os.Stdout) 32 | t.SetIndexColumn(1) 33 | 34 | t.SetStyle(table.StyleLight) 35 | t.Style().Format.Header = text.FormatLower 36 | 37 | greenBold := color.New(color.FgGreen, color.Bold).SprintFunc() 38 | redBold := color.New(color.FgRed, color.Bold).SprintFunc() 39 | 40 | for _, p := range master.ListProcess() { 41 | if p.ProcStatus.Status == "online" { 42 | p.ProcStatus.Status = greenBold("online") 43 | } else { 44 | p.ProcStatus.Status = redBold(p.ProcStatus.Status) 45 | } 46 | t.AppendRow(table.Row{ 47 | p.Id, p.Name, p.Pid, p.ProcStatus.ParentPid, p.ProcStatus.Status, p.ProcStatus.Uptime.AsDuration(), p.ProcStatus.Restarts, p.ProcStatus.Cpu, p.ProcStatus.Memory, 48 | }) 49 | } 50 | 51 | t.Render() 52 | } 53 | 54 | // lsCmd represents the ls command 55 | var lsCmd = &cobra.Command{ 56 | Use: "ls", 57 | Short: "List all processes", 58 | Long: "List all processes", 59 | Run: func(cmd *cobra.Command, args []string) { 60 | master.SpawnDaemon() 61 | renderProcessList() 62 | }, 63 | } 64 | 65 | func init() { 66 | rootCmd.AddCommand(lsCmd) 67 | 68 | // Here you will define your flags and configuration settings. 69 | 70 | // Cobra supports Persistent Flags which will work for this command 71 | // and all subcommands, e.g.: 72 | // lsCmd.PersistentFlags().String("foo", "", "A help for foo") 73 | 74 | // Cobra supports local flags which will only run when this command 75 | // is called directly, e.g.: 76 | // lsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 77 | } 78 | -------------------------------------------------------------------------------- /cmd/restart.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "os" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | // restartCmd represents the restart command 14 | var restartCmd = &cobra.Command{ 15 | Use: "restart [options] ", 16 | Short: "Restart a process", 17 | Long: `Restart a process`, 18 | Run: func(cmd *cobra.Command, args []string) { 19 | if len(args) < 1 { 20 | cmd.Usage() 21 | return 22 | } 23 | 24 | logger := master.GetLogger() 25 | 26 | if args[0] == "all" { 27 | db := master.ListProcess() 28 | if len(db) == 0 { 29 | logger.Warn().Msg("No processes found") 30 | return 31 | } 32 | for _, process := range db { 33 | master.GetLogger().Info().Msgf("Applying action restartProcessId on app [%d](pid: [ %d ])", process.Id, process.Pid) 34 | master.RestartProcess(process) 35 | } 36 | renderProcessList() 37 | return 38 | } 39 | 40 | // check if args[0] is a file 41 | // get file extension 42 | // if it's a json file, parse it and start the app 43 | if _, err := os.Stat(args[0]); err == nil && args[0][len(args[0])-5:] == ".json" { 44 | err = master.StartFile(args[0]) 45 | if err == nil { 46 | renderProcessList() 47 | } else { 48 | logger.Fatal().Msg(err.Error()) 49 | } 50 | return 51 | } 52 | 53 | // if you can find the app in the database, start it 54 | process := master.FindProcess(args[0]) 55 | if process.Name != "" { 56 | master.RestartProcess(process) 57 | renderProcessList() 58 | return 59 | } else { 60 | logger.Error().Msgf("Process or Namespace %s not found", args[0]) 61 | } 62 | }, 63 | } 64 | 65 | func init() { 66 | rootCmd.AddCommand(restartCmd) 67 | 68 | // Here you will define your flags and configuration settings. 69 | 70 | // Cobra supports Persistent Flags which will work for this command 71 | // and all subcommands, e.g.: 72 | // restartCmd.PersistentFlags().String("foo", "", "A help for foo") 73 | 74 | // Cobra supports local flags which will only run when this command 75 | // is called directly, e.g.: 76 | // restartCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 77 | } 78 | -------------------------------------------------------------------------------- /cmd/stop.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "os" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | // stopCmd represents the stop command 14 | var stopCmd = &cobra.Command{ 15 | Use: "stop [options] ", 16 | Short: "Stop a process", 17 | Long: `Stop a process`, 18 | Run: func(cmd *cobra.Command, args []string) { 19 | master.SpawnDaemon() 20 | if len(args) < 1 { 21 | cmd.Usage() 22 | return 23 | } 24 | 25 | logger := master.GetLogger() 26 | 27 | if args[0] == "all" { 28 | db := master.ListProcess() 29 | if len(db) == 0 { 30 | logger.Warn().Msg("No processes found") 31 | return 32 | } 33 | for _, process := range db { 34 | master.GetLogger().Info().Msgf("Applying action stopProcessId on app [%d](pid: [ %d ])", process.Id, process.Pid) 35 | master.StopProcess(process.Id) 36 | } 37 | renderProcessList() 38 | return 39 | } 40 | 41 | // check if args[0] is a file 42 | // get file extension 43 | // if it's a json file, parse it and start the app 44 | if _, err := os.Stat(args[0]); err == nil && args[0][len(args[0])-5:] == ".json" { 45 | err = master.StopFile(args[0]) 46 | if err == nil { 47 | renderProcessList() 48 | } else { 49 | logger.Fatal().Msg(err.Error()) 50 | } 51 | return 52 | } 53 | 54 | // if you can find the app in the database, start it 55 | process := master.FindProcess(args[0]) 56 | if process == nil { 57 | logger.Error().Msgf("Process or namespace %s not found", args[0]) 58 | return 59 | } 60 | 61 | logger.Info().Msgf("Applying action stopProcessId on app [%s]", process.Name) 62 | master.StopProcess(process.Id) 63 | renderProcessList() 64 | }, 65 | } 66 | 67 | func init() { 68 | rootCmd.AddCommand(stopCmd) 69 | 70 | // Here you will define your flags and configuration settings. 71 | 72 | // Cobra supports Persistent Flags which will work for this command 73 | // and all subcommands, e.g.: 74 | // stopCmd.PersistentFlags().String("foo", "", "A help for foo") 75 | 76 | // Cobra supports local flags which will only run when this command 77 | // is called directly, e.g.: 78 | // stopCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 79 | } 80 | -------------------------------------------------------------------------------- /proto/process_extended.go: -------------------------------------------------------------------------------- 1 | package __ 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "strconv" 7 | "strings" 8 | "time" 9 | 10 | "github.com/aptible/supercronic/cronexpr" 11 | status "google.golang.org/grpc/status" 12 | durationpb "google.golang.org/protobuf/types/known/durationpb" 13 | timestamppb "google.golang.org/protobuf/types/known/timestamppb" 14 | ) 15 | 16 | func (p *Process) UpdateStatus(status string) { 17 | p.ProcStatus.Status = status 18 | } 19 | 20 | func (p *Process) SetStatus(status string) { 21 | p.ProcStatus.Status = status 22 | } 23 | 24 | func (p *Process) SetStopSignal(stopSignal bool) { 25 | p.StopSignal = stopSignal 26 | } 27 | 28 | func (p *Process) ResetPid() { 29 | p.Pid = 0 30 | p.ProcStatus.ParentPid = 0 31 | } 32 | 33 | func (p *Process) UpdateUptime() { 34 | p.ProcStatus.Uptime = durationpb.New(time.Since(p.ProcStatus.StartedAt.AsTime()).Truncate(time.Second)) 35 | } 36 | 37 | func (p *Process) InitStartedAt() { 38 | p.ProcStatus.StartedAt = timestamppb.New(time.Now()) 39 | } 40 | 41 | func (p *Process) InitUptime() { 42 | p.ProcStatus.Uptime = durationpb.New(0) 43 | } 44 | 45 | func (p *Process) IncreaseRestarts() { 46 | p.ProcStatus.Restarts++ 47 | } 48 | 49 | func (p *Process) ResetRestarts() { 50 | p.ProcStatus.Restarts = 0 51 | } 52 | 53 | func (p *Process) ResetCPUMemory() { 54 | p.ProcStatus.Cpu = "0.0%" 55 | p.ProcStatus.Memory = "0.0MB" 56 | } 57 | 58 | func (p *Process) UpdateCPUMemory() { 59 | if p.Pid == 0 { 60 | return 61 | } 62 | // launch command and read content 63 | cmd := exec.Command("ps", "-p", fmt.Sprintf("%d", p.Pid), "-o", "pcpu,rss") 64 | output, err := cmd.Output() 65 | if err != nil { 66 | return 67 | } 68 | // output separator can be multiple whitespaces 69 | // fix: error `parsing "": invalid syntax` in `strconv.ParseFloat` 70 | outputSplit := strings.Fields(strings.TrimSpace(strings.Split(string(output), "\n")[1])) 71 | 72 | p.ProcStatus.Cpu = fmt.Sprint(outputSplit[0], "%") 73 | 74 | // convert string to float 75 | memory, _ := strconv.ParseFloat(outputSplit[1], 64) 76 | memory = memory / 1024 77 | p.ProcStatus.Memory = fmt.Sprintf("%.1fMB", memory) 78 | } 79 | 80 | func (p *Process) UpdateNextStartAt() error { 81 | if p.CronRestart != "" { 82 | expr, err := cronexpr.Parse(p.CronRestart) 83 | if err != nil { 84 | p.CronRestart = "" 85 | return status.Errorf(400, "invalid cron expression: %v", err) 86 | } 87 | p.NextStartAt = timestamppb.New(expr.Next(time.Now())) 88 | } 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /cmd/flush.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "os" 9 | 10 | pb "github.com/dunstorm/pm2-go/proto" 11 | "github.com/dunstorm/pm2-go/utils" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | // flushCmd represents the flush command 16 | var flushCmd = &cobra.Command{ 17 | Use: "flush", 18 | Short: "flush [options] [api]", 19 | Long: `flush logs`, 20 | Run: func(cmd *cobra.Command, args []string) { 21 | master.SpawnDaemon() 22 | 23 | logger := master.GetLogger() 24 | 25 | flushProcess := func(process *pb.Process) { 26 | logger.Info().Msg(process.LogFilePath) 27 | logger.Info().Msg(process.ErrFilePath) 28 | 29 | // remove file contents 30 | utils.RemoveFileContents(process.LogFilePath) 31 | utils.RemoveFileContents(process.ErrFilePath) 32 | } 33 | 34 | if len(args) == 0 || args[0] == "all" { 35 | db := master.ListProcess() 36 | if len(db) == 0 { 37 | logger.Warn().Msg("No processes found") 38 | return 39 | } 40 | logger.Info().Msg("Flushing:") 41 | for _, process := range db { 42 | flushProcess(process) 43 | } 44 | logger.Info().Msg("Logs flushed") 45 | return 46 | } 47 | 48 | // check if args[0] is a file 49 | // get file extension 50 | // if it's a json file, parse it and start the app 51 | if _, err := os.Stat(args[0]); err == nil && args[0][len(args[0])-5:] == ".json" { 52 | logger.Info().Msg("Flushing:") 53 | err = master.FlushFile(args[0], flushProcess) 54 | if err != nil { 55 | logger.Fatal().Msg(err.Error()) 56 | } 57 | return 58 | } 59 | 60 | // if you can find the app in the database, start it 61 | process := master.FindProcess(args[0]) 62 | if process.Name == "" { 63 | logger.Error().Msgf("Process or namespace %s not found", args[0]) 64 | return 65 | } 66 | 67 | // logs 68 | logger.Info().Msg("Flushing:") 69 | logger.Info().Msg(process.LogFilePath) 70 | logger.Info().Msg(process.ErrFilePath) 71 | 72 | // remove file contents 73 | utils.RemoveFileContents(process.LogFilePath) 74 | utils.RemoveFileContents(process.ErrFilePath) 75 | 76 | logger.Info().Msg("Logs flushed") 77 | }, 78 | } 79 | 80 | func init() { 81 | rootCmd.AddCommand(flushCmd) 82 | 83 | // Here you will define your flags and configuration settings. 84 | 85 | // Cobra supports Persistent Flags which will work for this command 86 | // and all subcommands, e.g.: 87 | // flushCmd.PersistentFlags().String("foo", "", "A help for foo") 88 | 89 | // Cobra supports local flags which will only run when this command 90 | // is called directly, e.g.: 91 | // flushCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 92 | } 93 | -------------------------------------------------------------------------------- /cmd/start.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/dunstorm/pm2-go/shared" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | // startCmd represents the start command 11 | var startCmd = &cobra.Command{ 12 | Use: "start [options] [name|namespace|file|ecosystem|id...]", 13 | Short: "Start and daemonize an app", 14 | Long: `Start and daemonize an app`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | master.SpawnDaemon() 17 | if len(args) < 1 { 18 | cmd.Usage() 19 | return 20 | } 21 | 22 | logger := master.GetLogger() 23 | 24 | if args[0] == "all" { 25 | db := master.ListProcess() 26 | if len(db) == 0 { 27 | logger.Warn().Msg("No processes found") 28 | return 29 | } 30 | for _, process := range db { 31 | master.GetLogger().Info().Msgf("Applying action restartProcessId on app [%d](pid: [ %d ])", process.Id, process.Pid) 32 | master.RestartProcess(process) 33 | } 34 | renderProcessList() 35 | return 36 | } 37 | 38 | // check if args[0] is a file 39 | // get file extension 40 | // if it's a json file, parse it and start the app 41 | if _, err := os.Stat(args[0]); err == nil && args[0][len(args[0])-5:] == ".json" { 42 | err = master.StartFile(args[0]) 43 | if err == nil { 44 | renderProcessList() 45 | } else { 46 | logger.Fatal().Msg(err.Error()) 47 | } 48 | return 49 | } 50 | 51 | // if you can find the app in the database, start it 52 | process := master.FindProcess(args[0]) 53 | if process != nil { 54 | master.GetLogger().Info().Msgf("Applying action startProcessId on app [%d](pid: [ %d ])", process.Id, process.Pid) 55 | master.RestartProcess(process) 56 | renderProcessList() 57 | return 58 | } 59 | 60 | // add process to the database 61 | process, err := shared.SpawnNewProcess(shared.SpawnParams{ 62 | ExecutablePath: args[0], 63 | Args: args[1:], 64 | Logger: logger, 65 | }) 66 | if err != nil { 67 | master.GetLogger().Fatal().Msg(err.Error()) 68 | } 69 | master.GetLogger().Info().Msgf("Applying action addProcessName on app [%s](pid: [ %d ])", process.Name, process.Pid) 70 | master.AddProcess(process) 71 | 72 | renderProcessList() 73 | }, 74 | } 75 | 76 | func init() { 77 | rootCmd.AddCommand(startCmd) 78 | 79 | // Here you will define your flags and configuration settings. 80 | 81 | // Cobra supports Persistent Flags which will work for this command 82 | // and all subcommands, e.g.: 83 | // startCmd.PersistentFlags().String("foo", "", "A help for foo") 84 | 85 | // Cobra supports local flags which will only run when this command 86 | // is called directly, e.g.: 87 | // startCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 88 | } 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PM2 Logo](https://i.imgur.com/wUgcD2t.png)](https://i.imgur.com/wUgcD2t.png) 2 | # PM2-GO 3 | 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/dunstorm/pm2-go)](https://goreportcard.com/report/github.com/dunstorm/pm2-go) 5 | 6 | PM2-GO is a clone of [Unitech/pm2](https://github.com/Unitech/pm2) made using golang. The aim is to make it easy to install. Performance is the bonus. 7 | 8 | Starting an application in production mode is as easy as: 9 | 10 | ``` 11 | pm2-go start python app.py 12 | ``` 13 | 14 | Works on Linux & macOS, no support for Windows. 15 | 16 | ## Start an application 17 | 18 | You can start any application (Node.js, Python, Ruby, binaries in $PATH...) like that: 19 | 20 | ``` 21 | pm2-go start python app.py 22 | ``` 23 | 24 | ## Managing Applications 25 | 26 | Once applications are started you can manage them easily: 27 | 28 | To list all running applications: 29 | 30 | ``` 31 | pm2-go ls 32 | ``` 33 | 34 | Managing apps is straightforward: 35 | 36 | ``` 37 | pm2-go stop 38 | pm2-go restart 39 | pm2-go delete 40 | pm2-go flush 41 | ``` 42 | 43 | To see real-time logs: 44 | 45 | ``` 46 | pm2-go logs 47 | ``` 48 | 49 | To dump all processes 50 | 51 | ``` 52 | pm2-go dump 53 | ``` 54 | 55 | To restore all processes 56 | 57 | ``` 58 | pm2-go restore 59 | ``` 60 | 61 | ## Extend Logs 62 | 63 | Logs can be extended by using `scripts` placed in `$HOME/.pm2-go/scripts`. 64 | 65 | To leverage the use of `scripts`, specify the `scripts` inside `.json` file containing processes 66 | 67 | e.g. 68 | 69 | ```json 70 | [ 71 | { 72 | "name": "python-test", 73 | "args": ["-u", "test.py"], 74 | "autorestart": true, 75 | "cwd": "./examples", 76 | "scripts": ["logs_date"], 77 | "executable_path": "python3", 78 | "cron_restart": "* * * * *" 79 | } 80 | ] 81 | ``` 82 | 83 | Example script: [logs_date.sh](https://github.com/dunstorm/pm2-go/blob/main/scripts/logs_date.sh) 84 | 85 | It adds corresponding date & time to each line from the process output 86 | 87 | ![](https://i.imgur.com/UFw9PpX.png) 88 | 89 | ## Log Rotation 90 | 91 | Log rotation can be enabled by setting `logrotate` to `true` using 92 | 93 | ``` 94 | pm2-go config set logrotate true 95 | ``` 96 | 97 | Logs will be rotated every `x` size (default: 10MB) and `y` number of files (default: 10). It can be configured using 98 | 99 | ``` 100 | pm2-go config set logrotate_size 10M (e.g. 10M, 10K, 10G) 101 | pm2-go config set logrotate_max_files 10 102 | ``` 103 | -------------------------------------------------------------------------------- /cmd/delete.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "os" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | // deleteCmd represents the delete command 14 | var deleteCmd = &cobra.Command{ 15 | Use: "delete [options] ", 16 | Short: "Stop and delete a process from pm2 process list", 17 | Long: `Stop and delete a process from pm2 process list`, 18 | Run: func(cmd *cobra.Command, args []string) { 19 | if len(args) < 1 { 20 | cmd.Usage() 21 | return 22 | } 23 | 24 | logger := master.GetLogger() 25 | 26 | if args[0] == "all" { 27 | db := master.ListProcess() 28 | if len(db) == 0 { 29 | logger.Warn().Msg("No processes found") 30 | return 31 | } 32 | for _, process := range db { 33 | if process.ProcStatus.Status == "online" { 34 | master.GetLogger().Info().Msgf("Applying action stopProcessId on app [%d](pid: [ %d ])", process.Id, process.Pid) 35 | master.StopProcess(process.Id) 36 | } 37 | logger.Info().Msgf("Applying action deleteProcessId on app [%s]", process.Name) 38 | master.DeleteProcess(process) 39 | logger.Info().Msgf("[%s] ✓", process.Name) 40 | } 41 | renderProcessList() 42 | return 43 | } 44 | 45 | // check if args[0] is a file 46 | // get file extension 47 | // if it's a json file, parse it and start the app 48 | if _, err := os.Stat(args[0]); err == nil && args[0][len(args[0])-5:] == ".json" { 49 | err = master.DeleteFile(args[0]) 50 | if err == nil { 51 | renderProcessList() 52 | } else { 53 | logger.Fatal().Msg(err.Error()) 54 | } 55 | return 56 | } 57 | 58 | process := master.FindProcess(args[0]) 59 | if process.ProcStatus == nil { 60 | logger.Error().Msgf("Process or Namespace %s not found", args[0]) 61 | return 62 | } 63 | 64 | // stop process if alive 65 | if process.ProcStatus.Status == "online" { 66 | logger.Info().Msgf("Applying action stopProcessId on app [%s](pid: [ %d ])", process.Name, process.Pid) 67 | master.StopProcess(process.Id) 68 | } 69 | 70 | // delete a process 71 | logger.Info().Msgf("Applying action deleteProcessId on app [%s]", process.Name) 72 | master.DeleteProcess(process) 73 | logger.Info().Msgf("[%s] ✓", process.Name) 74 | 75 | renderProcessList() 76 | }, 77 | } 78 | 79 | func init() { 80 | rootCmd.AddCommand(deleteCmd) 81 | 82 | // Here you will define your flags and configuration settings. 83 | 84 | // Cobra supports Persistent Flags which will work for this command 85 | // and all subcommands, e.g.: 86 | // deleteCmd.PersistentFlags().String("foo", "", "A help for foo") 87 | 88 | // Cobra supports local flags which will only run when this command 89 | // is called directly, e.g.: 90 | // deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 91 | } 92 | -------------------------------------------------------------------------------- /app/app.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/dunstorm/pm2-go/grpc/client" 5 | pb "github.com/dunstorm/pm2-go/proto" 6 | "github.com/dunstorm/pm2-go/shared" 7 | "github.com/dunstorm/pm2-go/utils" 8 | "github.com/rs/zerolog" 9 | ) 10 | 11 | type App struct { 12 | client *client.Client 13 | logger *zerolog.Logger 14 | } 15 | 16 | func New() *App { 17 | logger := utils.NewLogger() 18 | client, err := client.New(50051) 19 | if err != nil { 20 | logger.Fatal().Err(err).Msg("Failed to create client") 21 | } 22 | return &App{ 23 | logger: logger, 24 | client: client, 25 | } 26 | } 27 | 28 | func (app *App) GetLogger() *zerolog.Logger { 29 | return app.logger 30 | } 31 | 32 | func (app *App) AddProcess(process *pb.Process) int32 { 33 | return app.client.AddProcess(&pb.AddProcessRequest{ 34 | Name: process.Name, 35 | ExecutablePath: process.ExecutablePath, 36 | Args: process.Args, 37 | Cwd: process.Cwd, 38 | Pid: process.Pid, 39 | AutoRestart: process.AutoRestart, 40 | Scripts: process.Scripts, 41 | PidFilePath: process.PidFilePath, 42 | LogFilePath: process.LogFilePath, 43 | ErrFilePath: process.ErrFilePath, 44 | CronRestart: process.CronRestart, 45 | }) 46 | } 47 | 48 | func (app *App) ListProcess() []*pb.Process { 49 | return app.client.ListProcess() 50 | } 51 | 52 | func (app *App) FindProcess(name string) *pb.Process { 53 | return app.client.FindProcess(name) 54 | } 55 | 56 | func (app *App) StopProcess(index int32) bool { 57 | return app.client.StopProcess(index) 58 | } 59 | 60 | func (app *App) StartProcess(newProcess *pb.Process) *pb.Process { 61 | return app.client.StartProcess(&pb.StartProcessRequest{ 62 | Id: newProcess.Id, 63 | Name: newProcess.Name, 64 | Args: newProcess.Args, 65 | ExecutablePath: newProcess.ExecutablePath, 66 | Cwd: newProcess.Cwd, 67 | AutoRestart: newProcess.AutoRestart, 68 | Scripts: newProcess.Scripts, 69 | Pid: newProcess.Pid, 70 | PidFilePath: newProcess.PidFilePath, 71 | LogFilePath: newProcess.LogFilePath, 72 | ErrFilePath: newProcess.ErrFilePath, 73 | CronRestart: newProcess.CronRestart, 74 | }) 75 | } 76 | 77 | func (app *App) RestartProcess(process *pb.Process) *pb.Process { 78 | app.StopProcess(process.Id) 79 | newProcess, err := shared.SpawnNewProcess(shared.SpawnParams{ 80 | Name: process.Name, 81 | Args: process.Args, 82 | ExecutablePath: process.ExecutablePath, 83 | AutoRestart: process.AutoRestart, 84 | Logger: app.logger, 85 | Cwd: process.Cwd, 86 | Scripts: process.Scripts, 87 | CronRestart: process.CronRestart, 88 | }) 89 | if err != nil { 90 | app.logger.Fatal().Err(err).Msg("Failed to restart process") 91 | } 92 | newProcess.Id = process.Id 93 | process = app.StartProcess(newProcess) 94 | return process 95 | } 96 | 97 | func (app *App) DeleteProcess(process *pb.Process) bool { 98 | return app.client.DeleteProcess(process.Id) 99 | } 100 | -------------------------------------------------------------------------------- /cmd/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/dunstorm/pm2-go/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // configCmd represents the config command 15 | var configCmd = &cobra.Command{ 16 | Use: "config", 17 | Short: "Set/Get configuration values", 18 | Long: `Set/Get configuration values. For example: 19 | 20 | pm2-go config set logrotate true 21 | pm2-go config get logrotate`, 22 | Run: func(cmd *cobra.Command, args []string) { 23 | // find or create config file 24 | utils.FindOrCreateConfigFile() 25 | 26 | // get config 27 | config := utils.GetConfig() 28 | 29 | // check if args are set 30 | if len(args) == 0 { 31 | // print config 32 | fmt.Println("log_rotate:", config.LogRotate) 33 | fmt.Println("log_rotate_max_files:", config.LogRotateMaxFiles) 34 | fmt.Println("log_rotate_size:", config.LogRotateSize) 35 | return 36 | } 37 | }, 38 | } 39 | 40 | var configSetCmd = &cobra.Command{ 41 | Use: "set", 42 | Short: "Set configuration values", 43 | Long: `Set configuration values. For example: 44 | 45 | pm2-go config set logrotate true 46 | pm2-go config set logrotate_max_files 10 47 | pm2-go config set logrotate_size 10M`, 48 | Run: func(cmd *cobra.Command, args []string) { 49 | // find or create config file 50 | utils.FindOrCreateConfigFile() 51 | 52 | // get config 53 | config := utils.GetConfig() 54 | 55 | // check if args are set 56 | if len(args) < 2 { 57 | fmt.Println("Please provide a key and value") 58 | return 59 | } 60 | 61 | logger := master.GetLogger() 62 | 63 | // set config 64 | switch args[0] { 65 | case "logrotate": 66 | // check if value is yes or no 67 | config.LogRotate = utils.ParseBool(args[1]) 68 | logger.Info().Msgf("LogRotate has been set to %v", config.LogRotate) 69 | case "logrotate_max_files": 70 | maxFiles := utils.ParseInt(args[1]) 71 | if maxFiles < 0 { 72 | logger.Error().Msg("logrotate_max_files must be a positive integer") 73 | return 74 | } 75 | config.LogRotateMaxFiles = maxFiles 76 | logger.Info().Msgf("LogRotateMaxFiles has been set to %d", config.LogRotateMaxFiles) 77 | case "logrotate_size": 78 | config.LogRotateSize = utils.ParseSize(args[1]) 79 | logger.Info().Msgf("LogRotateSize has been set to %d bytes", config.LogRotateSize) 80 | default: 81 | logger.Error().Msgf("Unknown key") 82 | logger.Info().Msgf("Available keys: logrotate, logrotate_max_files, logrotate_size") 83 | return 84 | } 85 | 86 | // save config 87 | utils.SaveConfig(config) 88 | 89 | logger.Debug().Msgf("Config saved") 90 | }, 91 | } 92 | 93 | func init() { 94 | rootCmd.AddCommand(configCmd) 95 | configCmd.AddCommand(configSetCmd) 96 | 97 | // Here you will define your flags and configuration settings. 98 | 99 | // Cobra supports Persistent Flags which will work for this command 100 | // and all subcommands, e.g.: 101 | // configCmd.PersistentFlags().String("foo", "", "A help for foo") 102 | 103 | // Cobra supports local flags which will only run when this command 104 | // is called directly, e.g.: 105 | // configCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 106 | } 107 | -------------------------------------------------------------------------------- /proto/process.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/duration.proto"; 4 | import "google/protobuf/timestamp.proto"; 5 | 6 | package proto; 7 | 8 | option go_package = "."; 9 | 10 | service ProcessManager { 11 | rpc AddProcess (AddProcessRequest) returns (Process) {} 12 | rpc StartProcess (StartProcessRequest) returns (Process) {} 13 | rpc StopProcess (StopProcessRequest) returns (StopProcessResponse) {} 14 | rpc FindProcess (FindProcessRequest) returns (Process) {} 15 | rpc DeleteProcess (DeleteProcessRequest) returns (DeleteProcessResponse) {} 16 | rpc ListProcess (ListProcessRequest) returns (ListProcessResponse) {} 17 | rpc SpawnProcess (SpawnProcessRequest) returns (SpawnProcessResponse) {} 18 | } 19 | 20 | message ProcStatus { 21 | string status = 1; 22 | google.protobuf.Timestamp started_at = 2; 23 | google.protobuf.Duration uptime = 3; 24 | int32 restarts = 4; 25 | string cpu = 5; 26 | string memory = 6; 27 | int32 parent_pid = 7; 28 | } 29 | 30 | message Process { 31 | int32 id = 1; 32 | string name = 2; 33 | repeated string args = 3; 34 | repeated string scripts = 4; 35 | string executable_path = 5; 36 | int32 pid = 6; 37 | bool auto_restart = 7; 38 | string cwd = 8; 39 | string pid_file_path = 9; 40 | string log_file_path = 10; 41 | string err_file_path = 11; 42 | string cron_restart = 12; 43 | google.protobuf.Timestamp next_start_at = 13; 44 | 45 | ProcStatus proc_status = 14; 46 | bool stop_signal = 15; 47 | int32 log_file_count = 16; 48 | } 49 | 50 | message AddProcessRequest { 51 | string name = 2; 52 | repeated string args = 3; 53 | repeated string scripts = 4; 54 | string executable_path = 5; 55 | int32 pid = 6; 56 | bool auto_restart = 7; 57 | string cwd = 8; 58 | string pid_file_path = 9; 59 | string log_file_path = 10; 60 | string err_file_path = 11; 61 | string cron_restart = 12; 62 | } 63 | 64 | message FindProcessRequest { 65 | string name = 1; 66 | } 67 | 68 | message StopProcessRequest { 69 | string name = 1; 70 | int32 id = 2; 71 | } 72 | 73 | message StopProcessResponse { 74 | bool success = 1; 75 | } 76 | 77 | message StartProcessRequest { 78 | int32 id = 1; 79 | string name = 2; 80 | repeated string args = 3; 81 | repeated string scripts = 4; 82 | string executable_path = 5; 83 | int32 pid = 6; 84 | bool auto_restart = 7; 85 | string cwd = 8; 86 | string pid_file_path = 9; 87 | string log_file_path = 10; 88 | string err_file_path = 11; 89 | string cron_restart = 12; 90 | } 91 | 92 | message ListProcessRequest {} 93 | 94 | message ListProcessResponse { 95 | repeated Process processes = 1; 96 | } 97 | 98 | message DeleteProcessRequest { 99 | int32 id = 1; 100 | } 101 | 102 | message DeleteProcessResponse { 103 | bool success = 1; 104 | } 105 | 106 | message SpawnProcessRequest { 107 | string name = 1; 108 | repeated string args = 2; 109 | repeated string scripts = 3; 110 | string executable_path = 4; 111 | bool auto_restart = 6; 112 | string cwd = 7; 113 | string cron_restart = 11; 114 | } 115 | 116 | message SpawnProcessResponse { 117 | bool success = 1; 118 | } -------------------------------------------------------------------------------- /app/daemon.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "path" 8 | "time" 9 | 10 | "github.com/dunstorm/pm2-go/grpc/server" 11 | "github.com/dunstorm/pm2-go/utils" 12 | "github.com/rs/zerolog" 13 | ) 14 | 15 | func isDaemonRunning() bool { 16 | directory := utils.GetMainDirectory() 17 | // check if daemon.pid exists 18 | if _, err := os.Stat(path.Join(directory, "daemon.pid")); os.IsNotExist(err) { 19 | return false 20 | } 21 | // read daemon.pid and check if process is running 22 | pid, err := utils.ReadPidFile("daemon.pid") 23 | if err != nil { 24 | return false 25 | } 26 | // check if process is running by pid 27 | if _, running := utils.IsProcessRunning(pid); running { 28 | return true 29 | } 30 | return false 31 | } 32 | 33 | const ( 34 | MARK_NAME = "_GO_DAEMON" 35 | MARK_VALUE = "1" 36 | ) 37 | 38 | func wasReborn() bool { 39 | return os.Getenv(MARK_NAME) == MARK_VALUE 40 | } 41 | 42 | func (app *App) SpawnDaemon() { 43 | if isDaemonRunning() && !wasReborn() { 44 | return 45 | } 46 | 47 | if wasReborn() { 48 | logger := zerolog.New(os.Stderr).With().Timestamp().Logger() 49 | app.logger = &logger 50 | } 51 | 52 | app.logger.Info().Msgf("Spawning PM2 daemon with pm2_home=%s", utils.GetMainDirectory()) 53 | 54 | daemonPidFile := path.Join(utils.GetMainDirectory(), "daemon.pid") 55 | daemonLogFile := path.Join(utils.GetMainDirectory(), "daemon.log") 56 | 57 | logFile, err := os.OpenFile(daemonLogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640) 58 | if err != nil { 59 | app.logger.Fatal().Msg(err.Error()) 60 | return 61 | } 62 | nullFile, err := os.Open(os.DevNull) 63 | if err != nil { 64 | app.logger.Fatal().Msg(err.Error()) 65 | return 66 | } 67 | 68 | if !wasReborn() { 69 | // create process 70 | var attr = os.ProcAttr{ 71 | Dir: ".", 72 | Env: append( 73 | []string{ 74 | fmt.Sprintf("%s=%s", MARK_NAME, MARK_VALUE), 75 | }, os.Environ()..., 76 | ), 77 | Files: []*os.File{ 78 | nullFile, 79 | logFile, 80 | logFile, 81 | }, 82 | } 83 | 84 | binPath, _ := exec.LookPath(os.Args[0]) 85 | 86 | fullCommand := []string{binPath} 87 | fullCommand = append(fullCommand, "-d") 88 | process, err := os.StartProcess(binPath, fullCommand, &attr) 89 | if err == nil { 90 | app.logger.Info().Msgf("Daemon PID: %d", process.Pid) 91 | 92 | // write pid to file 93 | // write daemon pid 94 | err = utils.WritePidToFile(daemonPidFile, process.Pid) 95 | if err != nil { 96 | app.logger.Error().Msg(err.Error()) 97 | return 98 | } 99 | 100 | // detaches the process 101 | err = process.Release() 102 | if err != nil { 103 | app.logger.Error().Msg(err.Error()) 104 | return 105 | } 106 | } else { 107 | app.logger.Error().Msg(err.Error()) 108 | return 109 | } 110 | 111 | // wait for 50051 port to open with a timeout of 2s 112 | found := false 113 | for i := 0; i < 200; i++ { 114 | if utils.IsPortOpen(50051) { 115 | found = true 116 | break 117 | } 118 | time.Sleep(10 * time.Millisecond) 119 | } 120 | 121 | if !found { 122 | app.logger.Error().Msg("PM2 Failed to start") 123 | os.Exit(1) 124 | } else { 125 | app.logger.Info().Msg("PM2 Successfully daemonized") 126 | } 127 | } 128 | 129 | if wasReborn() { 130 | server.New(50051) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /cmd/logs.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "strconv" 11 | "sync" 12 | 13 | "github.com/dunstorm/pm2-go/utils" 14 | "github.com/fatih/color" 15 | 16 | "github.com/spf13/cobra" 17 | ) 18 | 19 | // logsCmd represents the logs command 20 | var logsCmd = &cobra.Command{ 21 | Use: "logs [options] [id|name|namespace]", 22 | Short: "Stream logs file", 23 | Long: `Stream logs file`, 24 | Run: func(cmd *cobra.Command, args []string) { 25 | if len(os.Args) < 1 { 26 | cmd.Usage() 27 | return 28 | } 29 | 30 | tail, _ := cmd.Flags().GetInt("lines") 31 | 32 | // check if args[0] is a file 33 | // get file extension 34 | // if it's a json file, parse it and start the app 35 | if _, err := os.Stat(args[0]); err == nil && args[0][len(args[0])-5:] == ".json" { 36 | master.StartFile(args[0]) 37 | return 38 | } 39 | 40 | logger := master.GetLogger() 41 | 42 | // if you can find the app in the database 43 | process := master.FindProcess(args[0]) 44 | if process != nil { 45 | logPrefix := strconv.Itoa(int(process.Id)) + "|" + process.Name + "| " 46 | 47 | green := color.New(color.FgGreen).SprintFunc() 48 | red := color.New(color.FgRed).SprintFunc() 49 | 50 | cyanBold := color.New(color.FgCyan, color.Bold) 51 | cyanBold.Printf("[TAILING] Tailing last %d lines for [%s] process (change the value with --lines option)\n", tail, process.Name) 52 | 53 | outLastModified := utils.GetLastModified(process.LogFilePath) 54 | errLastModified := utils.GetLastModified(process.ErrFilePath) 55 | 56 | printStdoutLogs := func() { 57 | // print stdout logs 58 | color.Green("%s last %d lines", process.LogFilePath, tail) 59 | logs, err := utils.GetLogs(process.LogFilePath, tail) 60 | if err != nil { 61 | logger.Error().Msg(err.Error()) 62 | return 63 | } 64 | utils.PrintLogs(logs, logPrefix, green) 65 | } 66 | 67 | printStderrLogs := func() { 68 | // print error logs 69 | color.Red("%s last %d lines", process.ErrFilePath, tail) 70 | logs, err := utils.GetLogs(process.ErrFilePath, tail) 71 | if err != nil { 72 | logger.Error().Msg(err.Error()) 73 | return 74 | } 75 | utils.PrintLogs(logs, logPrefix, red) 76 | } 77 | 78 | if errLastModified.Before(outLastModified) { 79 | printStderrLogs() 80 | fmt.Println() 81 | printStdoutLogs() 82 | } else { 83 | printStdoutLogs() 84 | fmt.Println() 85 | printStderrLogs() 86 | } 87 | 88 | // to run it indefinitely 89 | var wg sync.WaitGroup 90 | wg.Add(1) 91 | go utils.Tail(logPrefix, green, process.LogFilePath, os.Stdout) 92 | go utils.Tail(logPrefix, red, process.ErrFilePath, os.Stdout) 93 | wg.Wait() 94 | } 95 | 96 | logger.Error().Msgf("Process or Namespace %s not found", args[0]) 97 | }, 98 | } 99 | 100 | func init() { 101 | rootCmd.AddCommand(logsCmd) 102 | 103 | // Here you will define your flags and configuration settings. 104 | 105 | // Cobra supports Persistent Flags which will work for this command 106 | // and all subcommands, e.g.: 107 | // logsCmd.PersistentFlags().String("foo", "", "A help for foo") 108 | 109 | // Cobra supports local flags which will only run when this command 110 | // is called directly, e.g.: 111 | // logsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 112 | } 113 | -------------------------------------------------------------------------------- /cmd/describe.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "strings" 11 | 12 | "github.com/fatih/color" 13 | "github.com/jedib0t/go-pretty/v6/table" 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | // describeCmd represents the describe command 18 | var describeCmd = &cobra.Command{ 19 | Use: "describe", 20 | Short: "describe all parameters of a process", 21 | Long: `describe all parameters of a process`, 22 | Run: func(cmd *cobra.Command, args []string) { 23 | if len(os.Args) < 1 { 24 | cmd.Usage() 25 | return 26 | } 27 | 28 | master.SpawnDaemon() 29 | logger := master.GetLogger() 30 | 31 | process := master.FindProcess(args[0]) 32 | if process == nil { 33 | logger.Error().Msg("Process not found") 34 | return 35 | } 36 | 37 | heading := color.New(color.FgWhite, color.BgWhite, color.Bold).PrintfFunc() 38 | // Describing process with id - name 39 | heading("Process with id %d - name %s", process.Id, process.Name) 40 | fmt.Println() 41 | 42 | cyanBold := color.New(color.FgCyan, color.Bold).SprintFunc() 43 | greenBold := color.New(color.FgGreen, color.Bold).SprintFunc() 44 | redBold := color.New(color.FgRed, color.Bold).SprintFunc() 45 | 46 | t := table.NewWriter() 47 | t.SetOutputMirror(os.Stdout) 48 | t.SetStyle(table.StyleLight) 49 | 50 | // status, name, restarts, autorestart, executable path, executable args, error log path, out log path, pid file path, cron expression, next launch time 51 | 52 | if process.ProcStatus.Status == "online" { 53 | t.AppendRow([]interface{}{cyanBold("status"), greenBold(process.ProcStatus.Status)}) 54 | } else { 55 | t.AppendRow([]interface{}{cyanBold("status"), redBold(process.ProcStatus.Status)}) 56 | } 57 | 58 | t.AppendRow(table.Row{ 59 | cyanBold("name"), process.Name, 60 | }) 61 | 62 | t.AppendRow(table.Row{ 63 | cyanBold("restarts"), process.ProcStatus.Restarts, 64 | }) 65 | 66 | t.AppendRow(table.Row{ 67 | cyanBold("autorestart"), process.AutoRestart, 68 | }) 69 | 70 | t.AppendRow(table.Row{ 71 | cyanBold("executable path"), process.ExecutablePath, 72 | }) 73 | 74 | t.AppendRow(table.Row{ 75 | cyanBold("executable args"), strings.Join(process.Args, " "), 76 | }) 77 | 78 | t.AppendRow(table.Row{ 79 | cyanBold("error log path"), process.ErrFilePath, 80 | }) 81 | 82 | t.AppendRow(table.Row{ 83 | cyanBold("out log path"), process.LogFilePath, 84 | }) 85 | 86 | t.AppendRow(table.Row{ 87 | cyanBold("pid file path"), process.PidFilePath, 88 | }) 89 | 90 | t.AppendRow(table.Row{ 91 | cyanBold("cron expression"), process.CronRestart, 92 | }) 93 | 94 | t.AppendRow(table.Row{ 95 | cyanBold("next launch time"), process.NextStartAt.AsTime().Local().Format("2006-01-02 15:04:05 -07:00 MST"), 96 | }) 97 | 98 | t.Render() 99 | }, 100 | } 101 | 102 | func init() { 103 | rootCmd.AddCommand(describeCmd) 104 | 105 | // Here you will define your flags and configuration settings. 106 | 107 | // Cobra supports Persistent Flags which will work for this command 108 | // and all subcommands, e.g.: 109 | // describeCmd.PersistentFlags().String("foo", "", "A help for foo") 110 | 111 | // Cobra supports local flags which will only run when this command 112 | // is called directly, e.g.: 113 | // describeCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 114 | } 115 | -------------------------------------------------------------------------------- /grpc/client/new_client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | pb "github.com/dunstorm/pm2-go/proto" 9 | "github.com/dunstorm/pm2-go/utils" 10 | "github.com/rs/zerolog" 11 | "google.golang.org/grpc" 12 | "google.golang.org/grpc/credentials/insecure" 13 | ) 14 | 15 | type Client struct { 16 | port int 17 | 18 | logger *zerolog.Logger 19 | } 20 | 21 | func New(port int) (*Client, error) { 22 | 23 | logger := utils.NewLogger() 24 | client := &Client{ 25 | port: port, 26 | logger: logger, 27 | } 28 | 29 | return client, nil 30 | } 31 | 32 | func (c *Client) Dial() (*grpc.ClientConn, *pb.ProcessManagerClient) { 33 | // Set up a connection to the server. 34 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", c.port), grpc.WithTransportCredentials(insecure.NewCredentials())) 35 | if err != nil { 36 | c.logger.Fatal().Msgf("did not connect: %v", err) 37 | } 38 | client := pb.NewProcessManagerClient(conn) 39 | return conn, &client 40 | } 41 | 42 | // create process 43 | func (c *Client) AddProcess(request *pb.AddProcessRequest) int32 { 44 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 45 | defer cancel() 46 | conn, manager := c.Dial() 47 | defer conn.Close() 48 | r, err := (*manager).AddProcess(ctx, request) 49 | if err != nil { 50 | c.logger.Fatal().Msgf("%s", err.Error()) 51 | } 52 | return r.GetId() 53 | } 54 | 55 | // find process 56 | func (c *Client) FindProcess(name string) *pb.Process { 57 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 58 | defer cancel() 59 | conn, manager := c.Dial() 60 | defer conn.Close() 61 | r, err := (*manager).FindProcess(ctx, &pb.FindProcessRequest{Name: name}) 62 | if err != nil { 63 | return nil 64 | } 65 | return r 66 | } 67 | 68 | // stop process 69 | func (c *Client) StopProcess(index int32) bool { 70 | ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 71 | defer cancel() 72 | conn, manager := c.Dial() 73 | defer conn.Close() 74 | r, err := (*manager).StopProcess(ctx, &pb.StopProcessRequest{Id: index}) 75 | if err != nil { 76 | c.logger.Fatal().Msgf("%s", err.Error()) 77 | } 78 | return r.GetSuccess() 79 | } 80 | 81 | // list processes 82 | func (c *Client) ListProcess() []*pb.Process { 83 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 84 | defer cancel() 85 | conn, manager := c.Dial() 86 | defer conn.Close() 87 | r, err := (*manager).ListProcess(ctx, &pb.ListProcessRequest{}) 88 | if err != nil { 89 | c.logger.Fatal().Msgf("%s", err.Error()) 90 | } 91 | return r.GetProcesses() 92 | } 93 | 94 | // update process 95 | func (c *Client) StartProcess(request *pb.StartProcessRequest) *pb.Process { 96 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 97 | defer cancel() 98 | conn, manager := c.Dial() 99 | defer conn.Close() 100 | r, err := (*manager).StartProcess(ctx, request) 101 | if err != nil { 102 | c.logger.Fatal().Msgf("%s", err.Error()) 103 | } 104 | return r 105 | } 106 | 107 | // delete process 108 | func (c *Client) DeleteProcess(id int32) bool { 109 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 110 | defer cancel() 111 | conn, manager := c.Dial() 112 | defer conn.Close() 113 | r, err := (*manager).DeleteProcess(ctx, &pb.DeleteProcessRequest{Id: id}) 114 | if err != nil { 115 | c.logger.Fatal().Msgf("%s", err.Error()) 116 | } 117 | return r.GetSuccess() 118 | } 119 | 120 | // spawn process inside server 121 | func (c *Client) SpawnProcess(request *pb.SpawnProcessRequest) *pb.SpawnProcessResponse { 122 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 123 | defer cancel() 124 | conn, manager := c.Dial() 125 | defer conn.Close() 126 | r, err := (*manager).SpawnProcess(ctx, request) 127 | if err != nil { 128 | c.logger.Fatal().Msgf("%s", err.Error()) 129 | } 130 | return r 131 | } 132 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/dunstorm/pm2-go/app" 7 | "github.com/dunstorm/pm2-go/grpc/client" 8 | pb "github.com/dunstorm/pm2-go/proto" 9 | "github.com/dunstorm/pm2-go/shared" 10 | "github.com/dunstorm/pm2-go/utils" 11 | "github.com/rs/zerolog" 12 | ) 13 | 14 | func isServerRunning() bool { 15 | // check if 50051 is open 16 | return utils.IsPortOpen(50051) 17 | } 18 | 19 | func isProcessAdded(master *app.App, name string) bool { 20 | process := master.FindProcess(name) 21 | return process != nil 22 | } 23 | 24 | func isProcessRunning(master *app.App, name string) bool { 25 | process := master.FindProcess(name) 26 | return process.Pid != 0 27 | } 28 | 29 | func TestSpawn(t *testing.T) { 30 | zerolog.SetGlobalLevel(zerolog.Disabled) 31 | 32 | process, err := shared.SpawnNewProcess(shared.SpawnParams{ 33 | ExecutablePath: "python3", 34 | Args: []string{"examples/test.py"}, 35 | }) 36 | if err != nil { 37 | t.Error(err) 38 | return 39 | } 40 | 41 | if process == nil { 42 | t.Fatal("process is nil") 43 | } 44 | 45 | processFound, running := utils.IsProcessRunning(process.Pid) 46 | if !running { 47 | t.Fatal("process is not running") 48 | } 49 | processFound.Kill() 50 | } 51 | 52 | func TestStartEcosystem(t *testing.T) { 53 | master := app.New() 54 | err := master.StartFile("examples/ecosystem.json") 55 | if err != nil { 56 | t.Error(err) 57 | } 58 | if !isProcessAdded(master, "python-test") { 59 | t.Error("python-test is not running") 60 | } 61 | if !isProcessAdded(master, "celery-worker") { 62 | t.Error("celery-worker is not running") 63 | } 64 | running := isServerRunning() 65 | if !running { 66 | t.Error() 67 | } 68 | } 69 | 70 | func TestStopEcosystem(t *testing.T) { 71 | master := app.New() 72 | pythonTestPid := master.FindProcess("python-test").Pid 73 | celeryWorkerPid := master.FindProcess("celery-worker").Pid 74 | err := master.StopFile("examples/ecosystem.json") 75 | if err != nil { 76 | t.Error(err) 77 | } 78 | if isProcessRunning(master, "python-test") { 79 | t.Errorf("python-test %d is still running", pythonTestPid) 80 | } 81 | if isProcessRunning(master, "celery-worker") { 82 | t.Errorf("celery-worker %d is still running", celeryWorkerPid) 83 | } 84 | running := isServerRunning() 85 | if !running { 86 | t.Error() 87 | } 88 | } 89 | 90 | func TestDeleteEcosystem(t *testing.T) { 91 | master := app.New() 92 | err := master.DeleteFile("examples/ecosystem.json") 93 | if err != nil { 94 | t.Error(err) 95 | } 96 | if isProcessAdded(master, "python-test") { 97 | t.Error("python-test exists") 98 | } 99 | if isProcessAdded(master, "celery-worker") { 100 | t.Error("celery-worker exists") 101 | } 102 | running := isServerRunning() 103 | if !running { 104 | t.Error() 105 | } 106 | } 107 | 108 | func TestCronRestart(t *testing.T) { 109 | c, err := client.New(50051) 110 | if err != nil { 111 | t.Fatal(err) 112 | } 113 | response := c.SpawnProcess(&pb.SpawnProcessRequest{ 114 | ExecutablePath: "python3", 115 | Args: []string{"examples/test.py"}, 116 | Name: "python-test", 117 | CronRestart: "* * * * *", 118 | }) 119 | if !response.Success { 120 | t.Fatal("failed to spawn process") 121 | } 122 | process := c.FindProcess("python-test") 123 | if process == nil { 124 | t.Fatal("process not found") 125 | } 126 | if process.NextStartAt == nil { 127 | t.Error("Cron expression failed, NextStartAt is nil") 128 | } 129 | c.StopProcess(process.Id) 130 | c.DeleteProcess(process.Id) 131 | } 132 | 133 | func TestNoCronRestart(t *testing.T) { 134 | c, err := client.New(50051) 135 | if err != nil { 136 | t.Fatal(err) 137 | } 138 | response := c.SpawnProcess(&pb.SpawnProcessRequest{ 139 | ExecutablePath: "python3", 140 | Args: []string{"examples/test.py"}, 141 | Name: "python-test", 142 | }) 143 | if !response.Success { 144 | t.Fatal("failed to spawn process") 145 | } 146 | process := c.FindProcess("python-test") 147 | if process == nil { 148 | t.Fatal("process not found") 149 | } 150 | if process.NextStartAt != nil { 151 | t.Error("NextStartAt is not nil") 152 | } 153 | c.StopProcess(process.Id) 154 | c.DeleteProcess(process.Id) 155 | } 156 | 157 | func TestFailedCronRestart(t *testing.T) { 158 | c, err := client.New(50051) 159 | if err != nil { 160 | t.Fatal(err) 161 | } 162 | response := c.SpawnProcess(&pb.SpawnProcessRequest{ 163 | ExecutablePath: "python3", 164 | Args: []string{"examples/test.py"}, 165 | Name: "python-test", 166 | CronRestart: "* * v * * *", 167 | }) 168 | if response != nil { 169 | t.Error("failed cron expression went through") 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aptible/supercronic v0.2.30 h1:NlKD4fU9HM3MsT4NqMJsTt4dpuJRSoZnPK3cmZwvp6U= 2 | github.com/aptible/supercronic v0.2.30/go.mod h1:4mywElPusGmnm0H+F2QdVj5qbh/U43qKN+BkNkTYIcs= 3 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 4 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= 8 | github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= 9 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 10 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 11 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 12 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 13 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 14 | github.com/jedib0t/go-pretty/v6 v6.5.9 h1:ACteMBRrrmm1gMsXe9PSTOClQ63IXDUt03H5U+UV8OU= 15 | github.com/jedib0t/go-pretty/v6 v6.5.9/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E= 16 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 17 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 18 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 19 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 20 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 21 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 22 | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 23 | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 24 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 25 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 26 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 27 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 28 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 29 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 30 | github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 31 | github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= 32 | github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= 33 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 34 | github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= 35 | github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 36 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 37 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 38 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 39 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 40 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 41 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 42 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 43 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 44 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 45 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 46 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 47 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 48 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 49 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg= 50 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= 51 | google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= 52 | google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= 53 | google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 54 | google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 55 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 56 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 57 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 58 | -------------------------------------------------------------------------------- /grpc/server/start_scheduler.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "sync" 7 | "time" 8 | 9 | pb "github.com/dunstorm/pm2-go/proto" 10 | "github.com/dunstorm/pm2-go/shared" 11 | "github.com/dunstorm/pm2-go/utils" 12 | ) 13 | 14 | func updateProcessMap(handler *Handler, processId int32, p *os.Process) { 15 | handler.processes[processId] = p 16 | } 17 | 18 | func restartProcess(handler *Handler, p *pb.Process) { 19 | handler.logger.Info().Msgf("Restarting process %s", p.Name) 20 | p.IncreaseRestarts() 21 | newProcess, err := shared.SpawnNewProcess(shared.SpawnParams{ 22 | Name: p.Name, 23 | Args: p.Args, 24 | ExecutablePath: p.ExecutablePath, 25 | AutoRestart: p.AutoRestart, 26 | Cwd: p.Cwd, 27 | Logger: handler.logger, 28 | Scripts: p.Scripts, 29 | CronRestart: p.CronRestart, 30 | }) 31 | if err != nil { 32 | p.AutoRestart = false 33 | p.SetStopSignal(true) 34 | p.SetStatus("stopped") 35 | p.ResetPid() 36 | updateProcessMap(handler, p.Id, nil) 37 | 38 | handler.logger.Error().Msgf("Error while restarting process %s: %s", p.Name, err) 39 | } 40 | 41 | p.Pid = newProcess.Pid 42 | p.ProcStatus.ParentPid = int32(os.Getpid()) 43 | p.UpdateStatus("online") 44 | 45 | // set new process 46 | process, _ := utils.GetProcess(p.Pid) 47 | updateProcessMap(handler, p.Id, process) 48 | 49 | p.InitUptime() 50 | p.InitStartedAt() 51 | 52 | go process.Wait() 53 | } 54 | 55 | func startScheduler(handler *Handler) { 56 | var wg sync.WaitGroup 57 | 58 | // sync process 59 | syncProcess := func(p *pb.Process) { 60 | if p.ProcStatus.Status == "online" { 61 | if _, ok := utils.IsProcessRunning(p.Pid); !ok { 62 | handler.mu.Lock() 63 | defer handler.mu.Unlock() 64 | 65 | p.UpdateUptime() 66 | p.ResetPid() 67 | p.UpdateStatus("stopped") 68 | p.ResetCPUMemory() 69 | updateProcessMap(handler, p.Id, nil) 70 | 71 | // restart process if auto restart is enabled and process is not stopped 72 | if p.AutoRestart && !p.GetStopSignal() { 73 | restartProcess(handler, p) 74 | } 75 | } else { 76 | p.UpdateUptime() 77 | } 78 | } else if p.NextStartAt != nil && p.NextStartAt.AsTime().Before(time.Now()) { 79 | handler.logger.Debug().Msgf("Process %s is scheduled to start at %s", p.Name, p.NextStartAt.AsTime()) 80 | restartProcess(handler, p) 81 | p.UpdateNextStartAt() 82 | } 83 | wg.Done() 84 | } 85 | 86 | // read config 87 | config := utils.GetConfig() 88 | 89 | // handle max log file, max log size 90 | handleMaxLog := func(p *pb.Process) { 91 | defer wg.Done() 92 | // if LogFilePath exceeds LogRotateSize, rename file and add logfilecount 93 | combinedLogFilePath := utils.FileSize(p.LogFilePath) + utils.FileSize(p.ErrFilePath) 94 | if config.LogRotate && combinedLogFilePath > int64(config.LogRotateSize) { 95 | err := utils.RenameFile(p.LogFilePath, p.LogFilePath+"."+strconv.Itoa(int(p.LogFileCount))) 96 | // if error rename file 97 | if err != nil { 98 | handler.logger.Error().Msgf("Error while renaming log file %s: %s", p.LogFilePath, err) 99 | } 100 | handler.logger.Info().Msgf("Renamed log file %s to %s", p.LogFilePath, p.LogFilePath+"."+strconv.Itoa(int(p.LogFileCount))) 101 | 102 | // do the same for error log file 103 | err = utils.RenameFile(p.ErrFilePath, p.ErrFilePath+"."+strconv.Itoa(int(p.LogFileCount))) 104 | // if error rename file 105 | if err != nil { 106 | handler.logger.Error().Msgf("Error while renaming log file %s: %s", p.ErrFilePath, err) 107 | } 108 | handler.logger.Info().Msgf("Renamed err file %s to %s", p.ErrFilePath, p.ErrFilePath+"."+strconv.Itoa(int(p.LogFileCount))) 109 | 110 | // if no error, increase logfilecount 111 | p.LogFileCount++ 112 | 113 | // if LogFileCount exceeds LogRotateCount, delete oldest log file 114 | if p.LogFileCount >= int32(config.LogRotateMaxFiles) { 115 | // delete oldest log & err file 116 | err = os.Remove(p.LogFilePath + "." + strconv.Itoa(int(p.LogFileCount-int32(config.LogRotateMaxFiles)))) 117 | if err != nil { 118 | handler.logger.Error().Msgf("Error while deleting log file %s: %s", p.LogFilePath+"."+strconv.Itoa(int(p.LogFileCount-int32(config.LogRotateMaxFiles))), err) 119 | } 120 | handler.logger.Info().Msgf("Deleted log file %s", p.LogFilePath+"."+strconv.Itoa(int(p.LogFileCount-int32(config.LogRotateMaxFiles)))) 121 | 122 | err = os.Remove(p.ErrFilePath + "." + strconv.Itoa(int(p.LogFileCount-int32(config.LogRotateMaxFiles)))) 123 | if err != nil { 124 | handler.logger.Error().Msgf("Error while deleting log file %s: %s", p.ErrFilePath+"."+strconv.Itoa(int(p.LogFileCount-int32(config.LogRotateMaxFiles))), err) 125 | } 126 | handler.logger.Info().Msgf("Deleted err file %s", p.ErrFilePath+"."+strconv.Itoa(int(p.LogFileCount-int32(config.LogRotateMaxFiles)))) 127 | 128 | // decrease logfilecount 129 | p.LogFileCount = int32(config.LogRotateMaxFiles) 130 | } 131 | } 132 | 133 | } 134 | 135 | go func() { 136 | for { 137 | for _, p := range handler.databaseById { 138 | wg.Add(1) 139 | go syncProcess(p) 140 | 141 | if config.LogRotate { 142 | wg.Add(1) 143 | go handleMaxLog(p) 144 | } 145 | } 146 | wg.Wait() 147 | time.Sleep(500 * time.Millisecond) 148 | } 149 | }() 150 | } 151 | -------------------------------------------------------------------------------- /app/file.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | 7 | pb "github.com/dunstorm/pm2-go/proto" 8 | "github.com/dunstorm/pm2-go/shared" 9 | ) 10 | 11 | type Data struct { 12 | Name string `json:"name"` 13 | Args []string `json:"args"` 14 | ExecutablePath string `json:"executable_path"` 15 | AutoRestart bool `json:"autorestart"` 16 | Cwd string `json:"cwd"` 17 | Scripts []string `json:"scripts"` 18 | CronRestart string `json:"cron_restart"` 19 | } 20 | 21 | func readFileJson(filePath string) ([]Data, error) { 22 | // read file 23 | content, err := os.ReadFile(filePath) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | var payload []Data 29 | err = tryToParseApps(content, &payload) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return payload, nil 34 | } 35 | 36 | func (app *App) StartFile(filePath string) error { 37 | payload, err := readFileJson(filePath) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | for _, p := range payload { 43 | var process *pb.Process 44 | var err error 45 | process = app.FindProcess(p.Name) 46 | if process == nil { 47 | process, err = shared.SpawnNewProcess(shared.SpawnParams{ 48 | Name: p.Name, 49 | Args: p.Args, 50 | ExecutablePath: p.ExecutablePath, 51 | AutoRestart: p.AutoRestart, 52 | Logger: app.logger, 53 | Cwd: p.Cwd, 54 | Scripts: p.Scripts, 55 | CronRestart: p.CronRestart, 56 | }) 57 | if err != nil { 58 | app.logger.Fatal().Msgf("Error while starting process [%s]", p.Name) 59 | } 60 | app.AddProcess(process) 61 | } else { 62 | if process.ProcStatus.Status == "online" { 63 | app.logger.Info().Msgf("Applying action restartProcessId on app [%s](pid: [ %d ])", process.Name, process.Pid) 64 | app.StopProcess(process.Id) 65 | } else { 66 | app.logger.Info().Msgf("Applying action startProcessId on app [%s]", process.Name) 67 | } 68 | newProcess, err := shared.SpawnNewProcess(shared.SpawnParams{ 69 | Name: process.Name, 70 | Args: p.Args, 71 | ExecutablePath: p.ExecutablePath, 72 | AutoRestart: p.AutoRestart, 73 | Logger: app.logger, 74 | Cwd: p.Cwd, 75 | Scripts: p.Scripts, 76 | CronRestart: p.CronRestart, 77 | }) 78 | if err != nil { 79 | app.logger.Fatal().Msgf("Error while starting process [%s]", p.Name) 80 | } 81 | newProcess.Id = process.Id 82 | app.StartProcess(newProcess) 83 | } 84 | } 85 | return nil 86 | } 87 | 88 | func (app *App) StopFile(filePath string) error { 89 | payload, err := readFileJson(filePath) 90 | if err != nil { 91 | return err 92 | } 93 | 94 | for _, p := range payload { 95 | var process *pb.Process = app.FindProcess(p.Name) 96 | if process == nil { 97 | app.logger.Warn().Msgf("App [%s] not found", p.Name) 98 | } else { 99 | if process.ProcStatus.Status == "online" { 100 | app.logger.Info().Msgf("Applying action stopProcessId on app [%s](pid: [ %d ])", process.Name, process.Pid) 101 | app.StopProcess(process.Id) 102 | } else { 103 | app.logger.Warn().Msgf("App [%s] is not running", p.Name) 104 | } 105 | } 106 | } 107 | return nil 108 | } 109 | 110 | func (app *App) DeleteFile(filePath string) error { 111 | payload, err := readFileJson(filePath) 112 | if err != nil { 113 | return err 114 | } 115 | 116 | for _, p := range payload { 117 | var process *pb.Process = app.FindProcess(p.Name) 118 | if process == nil { 119 | app.logger.Warn().Msgf("App [%s] not found", p.Name) 120 | } else { 121 | if process.ProcStatus.Status == "online" { 122 | app.logger.Info().Msgf("Applying action stopProcessId on app [%s](pid: [ %d ])", process.Name, process.Pid) 123 | app.StopProcess(process.Id) 124 | } 125 | app.logger.Info().Msgf("Applying action deleteProcessId on app [%s]", process.Name) 126 | app.DeleteProcess(process) 127 | 128 | app.logger.Info().Msgf("[%s] ✓", p.Name) 129 | } 130 | } 131 | return nil 132 | } 133 | 134 | func (app *App) FlushFile(filePath string, flushProcess func(process *pb.Process)) error { 135 | payload, err := readFileJson(filePath) 136 | if err != nil { 137 | return err 138 | } 139 | 140 | for _, p := range payload { 141 | var process *pb.Process = app.FindProcess(p.Name) 142 | if process.ProcStatus == nil { 143 | app.logger.Warn().Msgf("App [%s] not found", p.Name) 144 | } else { 145 | flushProcess(process) 146 | } 147 | } 148 | return nil 149 | } 150 | 151 | func (app *App) RestoreProcess(allProcesses []*pb.Process) { 152 | for _, p := range allProcesses { 153 | process := app.FindProcess(p.Name) 154 | if process.ProcStatus == nil { 155 | process, err := shared.SpawnNewProcess(shared.SpawnParams{ 156 | Name: p.Name, 157 | Args: p.Args, 158 | ExecutablePath: p.ExecutablePath, 159 | AutoRestart: p.AutoRestart, 160 | Logger: app.logger, 161 | Cwd: p.Cwd, 162 | Scripts: p.Scripts, 163 | CronRestart: p.CronRestart, 164 | }) 165 | if err != nil { 166 | app.logger.Fatal().Msgf("Error while restoring process [%s]", p.Name) 167 | } 168 | app.AddProcess(process) 169 | } else { 170 | if process.ProcStatus.Status == "online" { 171 | app.logger.Info().Msgf("Applying action restartProcessId on app [%s](pid: [ %d ])", process.Name, process.Pid) 172 | app.StopProcess(process.Id) 173 | } else { 174 | app.logger.Info().Msgf("Applying action startProcessId on app [%s]", process.Name) 175 | } 176 | newProcess, err := shared.SpawnNewProcess(shared.SpawnParams{ 177 | Name: process.Name, 178 | Args: p.Args, 179 | ExecutablePath: p.ExecutablePath, 180 | AutoRestart: p.AutoRestart, 181 | Logger: app.logger, 182 | Cwd: p.Cwd, 183 | Scripts: p.Scripts, 184 | CronRestart: p.CronRestart, 185 | }) 186 | if err != nil { 187 | app.logger.Fatal().Msgf("Error while restoring process [%s]", err.Error()) 188 | } 189 | newProcess.Id = process.Id 190 | app.StartProcess(newProcess) 191 | } 192 | } 193 | } 194 | 195 | type WithAppsField struct { 196 | Apps []Data `json:"apps"` 197 | } 198 | 199 | func tryToParseApps(content []byte, payload *[]Data) error { 200 | var withAppsField WithAppsField 201 | err := json.Unmarshal(content, &withAppsField) 202 | if err != nil { 203 | return json.Unmarshal(content, payload) 204 | } 205 | *payload = withAppsField.Apps 206 | return nil 207 | } 208 | -------------------------------------------------------------------------------- /shared/spawn.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "path" 9 | "strings" 10 | "syscall" 11 | 12 | pb "github.com/dunstorm/pm2-go/proto" 13 | "github.com/dunstorm/pm2-go/utils" 14 | "github.com/rs/zerolog" 15 | ) 16 | 17 | type SpawnParams struct { 18 | Name string `json:"name"` 19 | ExecutablePath string `json:"executablePath"` 20 | Args []string `json:"args"` 21 | Cwd string `json:"cwd"` 22 | AutoRestart bool `json:"autorestart"` 23 | Scripts []string `json:"scripts"` 24 | CronRestart string `json:"cron_restart"` 25 | Logger *zerolog.Logger 26 | 27 | PidPilePath string `json:"-"` 28 | LogFilePath string `json:"-"` 29 | ErrFilePath string `json:"-"` 30 | 31 | logFile *os.File 32 | errFile *os.File 33 | nullFile *os.File 34 | } 35 | 36 | func (params *SpawnParams) fillDefaults() error { 37 | if params.ExecutablePath == "" { 38 | return errors.New("executable path is required") 39 | } 40 | 41 | if params.Name == "" { 42 | params.Name = strings.ToLower(params.ExecutablePath) 43 | } 44 | 45 | if params.Logger == nil { 46 | params.Logger = utils.NewLogger() 47 | } 48 | 49 | if params.Cwd == "" { 50 | params.Cwd, _ = os.Getwd() 51 | } 52 | 53 | nameLower := strings.ToLower(params.Name) 54 | params.PidPilePath = path.Join(utils.GetMainDirectory(), "pids", fmt.Sprintf("%s.pid", nameLower)) 55 | params.LogFilePath = path.Join(utils.GetMainDirectory(), "logs", fmt.Sprintf("%s-out.log", nameLower)) 56 | params.ErrFilePath = path.Join(utils.GetMainDirectory(), "logs", fmt.Sprintf("%s-err.log", nameLower)) 57 | 58 | return nil 59 | } 60 | 61 | func (params *SpawnParams) createFiles() error { 62 | var err error 63 | if params.logFile, err = os.OpenFile(params.LogFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640); err != nil { 64 | return err 65 | } 66 | if params.errFile, err = os.OpenFile(params.ErrFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640); err != nil { 67 | return err 68 | } 69 | if params.nullFile, err = os.Open(os.DevNull); err != nil { 70 | return err 71 | } 72 | return nil 73 | } 74 | 75 | func (params *SpawnParams) checkScripts() { 76 | for _, script := range params.Scripts { 77 | scriptPath := path.Join(utils.GetMainDirectory(), "scripts", script+".sh") 78 | if _, err := os.Stat(scriptPath); os.IsNotExist(err) { 79 | params.Logger.Fatal().Msgf("Script %s not found in path %s", script, scriptPath) 80 | } 81 | } 82 | } 83 | 84 | func createPipedProcesses(params *SpawnParams, stdoutLogsRead *os.File, stderrLogsRead *os.File, stdoutLogsWrite *os.File, stderrLogsWrite *os.File) error { 85 | var err error 86 | var newStdoutLogsRead, newErrorLogsRead *os.File 87 | for index, script := range params.Scripts { 88 | scriptPath := path.Join(utils.GetMainDirectory(), "scripts", script+".sh") 89 | if index == len(params.Scripts)-1 { 90 | stdoutLogsWrite = params.logFile 91 | stderrLogsWrite = params.errFile 92 | } else { 93 | newStdoutLogsRead, stdoutLogsWrite, err = os.Pipe() 94 | if err != nil { 95 | params.Logger.Fatal().Msg(err.Error()) 96 | return err 97 | } 98 | newErrorLogsRead, stderrLogsWrite, err = os.Pipe() 99 | if err != nil { 100 | params.Logger.Fatal().Msg(err.Error()) 101 | return err 102 | } 103 | } 104 | for _, stream := range []struct { 105 | read, write *os.File 106 | }{ 107 | {stdoutLogsRead, stdoutLogsWrite}, 108 | {stderrLogsRead, stderrLogsWrite}, 109 | } { 110 | cmd := exec.Command("/bin/sh", scriptPath) 111 | cmd.Dir = params.Cwd 112 | cmd.Env = os.Environ() 113 | cmd.Stdin = stream.read 114 | cmd.Stdout = stream.write 115 | cmd.Stderr = params.nullFile 116 | if err := cmd.Start(); err != nil { 117 | params.Logger.Fatal().Msg(err.Error()) 118 | return err 119 | } 120 | } 121 | if newStdoutLogsRead != nil { 122 | stdoutLogsRead = newStdoutLogsRead 123 | stderrLogsRead = newErrorLogsRead 124 | } 125 | } 126 | return nil 127 | } 128 | 129 | func SpawnNewProcess(params SpawnParams) (*pb.Process, error) { 130 | if err := params.fillDefaults(); err != nil { 131 | return nil, err 132 | } 133 | 134 | splitExecutablePath := strings.Split(params.ExecutablePath, "/") 135 | if splitExecutablePath[len(splitExecutablePath)-1] == "python" && len(params.Args) > 0 && params.Args[0] != "-u" { 136 | params.Logger.Warn().Msg("Add -u flag to prevent output buffering on python") 137 | } 138 | 139 | if err := params.createFiles(); err != nil { 140 | return nil, err 141 | } 142 | 143 | var err error 144 | params.ExecutablePath, err = exec.LookPath(params.ExecutablePath) 145 | if err != nil { 146 | return nil, err 147 | } 148 | 149 | var stdoutLogsWrite, stdoutLogsRead, stderrLogsWrite, stderrLogsRead *os.File 150 | 151 | if len(params.Scripts) == 0 { 152 | stdoutLogsWrite = params.logFile 153 | stderrLogsWrite = params.errFile 154 | } else { 155 | stdoutLogsRead, stdoutLogsWrite, err = os.Pipe() 156 | if err != nil { 157 | return nil, err 158 | } 159 | stderrLogsRead, stderrLogsWrite, err = os.Pipe() 160 | if err != nil { 161 | return nil, err 162 | } 163 | params.checkScripts() 164 | } 165 | 166 | defer func() { 167 | if stdoutLogsRead != nil { 168 | stdoutLogsRead.Close() 169 | } 170 | if stdoutLogsWrite != nil { 171 | stdoutLogsWrite.Close() 172 | } 173 | params.nullFile.Close() 174 | params.logFile.Close() 175 | params.errFile.Close() 176 | }() 177 | 178 | cmd := exec.Command(params.ExecutablePath, params.Args...) 179 | cmd.Dir = params.Cwd 180 | cmd.Env = os.Environ() 181 | cmd.Stdin = params.nullFile 182 | cmd.Stdout = stdoutLogsWrite 183 | cmd.Stderr = stderrLogsWrite 184 | cmd.SysProcAttr = &syscall.SysProcAttr{ 185 | Setpgid: true, 186 | } 187 | 188 | if err := cmd.Start(); err != nil { 189 | return nil, err 190 | } 191 | 192 | if len(params.Scripts) > 0 { 193 | err = createPipedProcesses(¶ms, stdoutLogsRead, stderrLogsRead, stdoutLogsWrite, stderrLogsWrite) 194 | if err != nil { 195 | return nil, err 196 | } 197 | } 198 | 199 | params.Logger.Info().Msgf("[%s] ✓", params.Name) 200 | 201 | if err := utils.WritePidToFile(params.PidPilePath, cmd.Process.Pid); err != nil { 202 | params.Logger.Fatal().Msg(err.Error()) 203 | cmd.Process.Kill() 204 | return nil, err 205 | } 206 | 207 | rpcProcess := &pb.Process{ 208 | Name: params.Name, 209 | ExecutablePath: params.ExecutablePath, 210 | Pid: int32(cmd.Process.Pid), 211 | Args: params.Args, 212 | Cwd: params.Cwd, 213 | Scripts: params.Scripts, 214 | LogFilePath: params.LogFilePath, 215 | ErrFilePath: params.ErrFilePath, 216 | PidFilePath: params.PidPilePath, 217 | AutoRestart: params.AutoRestart, 218 | CronRestart: params.CronRestart, 219 | } 220 | 221 | return rpcProcess, nil 222 | } 223 | -------------------------------------------------------------------------------- /utils/functions.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "net" 10 | "os" 11 | "path" 12 | "strconv" 13 | "strings" 14 | "syscall" 15 | "time" 16 | 17 | "github.com/rs/zerolog" 18 | "github.com/rs/zerolog/log" 19 | ) 20 | 21 | // new logger using zerolog 22 | func NewLogger() *zerolog.Logger { 23 | logger := log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) 24 | return &logger 25 | } 26 | 27 | // check if process is running by pid 28 | func IsProcessRunning(pid int32) (*os.Process, bool) { 29 | process, err := os.FindProcess(int(pid)) 30 | if err != nil { 31 | return nil, false 32 | } 33 | err = process.Signal(syscall.Signal(0)) 34 | if err != nil { 35 | return nil, false 36 | } 37 | return process, true 38 | } 39 | 40 | // get pm2-go main directory 41 | func GetMainDirectory() string { 42 | dirname, err := os.UserHomeDir() 43 | if err != nil { 44 | fmt.Println(err) 45 | os.Exit(1) 46 | } 47 | // add pm2-go directory 48 | dirname = dirname + "/.pm2-go" 49 | // if dirname doesnt exist create it 50 | if _, err := os.Stat(dirname); os.IsNotExist(err) { 51 | os.Mkdir(dirname, 0755) 52 | os.Mkdir(dirname+"/pids", 0755) 53 | os.Mkdir(dirname+"/logs", 0755) 54 | } 55 | // return dirname 56 | return dirname 57 | } 58 | 59 | // read pid file 60 | func ReadPidFile(pidFileName string) (int32, error) { 61 | // read daemon.pid using go 62 | fileIO, err := os.OpenFile(path.Join(GetMainDirectory(), pidFileName), os.O_RDONLY, 0644) 63 | if err != nil { 64 | return 0, err 65 | } 66 | defer fileIO.Close() 67 | rawBytes, err := ioutil.ReadAll(fileIO) 68 | if err != nil { 69 | return 0, err 70 | } 71 | lines := strings.Split(string(rawBytes), "\n") 72 | val, err := strconv.Atoi(lines[0]) 73 | if err != nil { 74 | return 0, err 75 | } 76 | return int32(val), nil 77 | } 78 | 79 | // write pid to a file 80 | func WritePidToFile(pidFilePath string, pid int) error { 81 | var fileIO *os.File 82 | var err error 83 | fileIO, err = os.OpenFile(pidFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640) 84 | if err != nil { 85 | return err 86 | } 87 | defer fileIO.Close() 88 | _, err = fileIO.WriteString(strconv.Itoa(pid)) 89 | if err != nil { 90 | return err 91 | } 92 | return nil 93 | } 94 | 95 | func StringContains(s string, substr string) bool { 96 | return strings.Contains(s, substr) 97 | } 98 | 99 | // tail a file 100 | func Tail(logPrefix string, prefixColor func(a ...interface{}) string, filename string, out io.Writer) { 101 | f, err := os.Open(filename) 102 | if err != nil { 103 | panic(err) 104 | } 105 | // skip to end of file 106 | f.Seek(0, 2) 107 | defer f.Close() 108 | r := bufio.NewReader(f) 109 | info, err := f.Stat() 110 | if err != nil { 111 | panic(err) 112 | } 113 | oldSize := info.Size() 114 | for { 115 | for line, prefix, err := r.ReadLine(); err != io.EOF; line, prefix, err = r.ReadLine() { 116 | if prefix { 117 | fmt.Fprint(out, prefixColor(logPrefix), string(line)) 118 | } else { 119 | fmt.Fprintln(out, prefixColor(logPrefix), string(line)) 120 | } 121 | } 122 | pos, err := f.Seek(0, io.SeekCurrent) 123 | if err != nil { 124 | panic(err) 125 | } 126 | for { 127 | time.Sleep(200 * time.Millisecond) 128 | newinfo, err := f.Stat() 129 | if err != nil { 130 | panic(err) 131 | } 132 | newSize := newinfo.Size() 133 | if newSize != oldSize { 134 | if newSize < oldSize { 135 | f.Seek(0, 0) 136 | } else { 137 | f.Seek(pos, io.SeekStart) 138 | } 139 | r = bufio.NewReader(f) 140 | oldSize = newSize 141 | break 142 | } 143 | } 144 | } 145 | } 146 | 147 | // get last modified time of a file 148 | func GetLastModified(filename string) time.Time { 149 | info, err := os.Stat(filename) 150 | if err != nil { 151 | return time.Time{} 152 | } 153 | return info.ModTime() 154 | } 155 | 156 | // given path, get first n lines of file 157 | func GetLogs(filename string, n int) ([]string, error) { 158 | var lines []string 159 | file, err := os.Open(filename) 160 | if err != nil { 161 | return lines, err 162 | } 163 | file.Seek(-1000, 2) 164 | defer file.Close() 165 | scanner := bufio.NewScanner(file) 166 | for scanner.Scan() { 167 | lines = append(lines, scanner.Text()) 168 | } 169 | if err := scanner.Err(); err != nil { 170 | return lines, err 171 | } 172 | if len(lines) < n { 173 | return lines, nil 174 | } 175 | return lines[len(lines)-n:], nil 176 | } 177 | 178 | // print logs from a array of string 179 | func PrintLogs(logs []string, logPrefix string, color func(a ...interface{}) string) { 180 | for _, line := range logs { 181 | fmt.Println(color(logPrefix), line) 182 | } 183 | } 184 | 185 | // exit pid 186 | func ExitPid(pid int32, timeout time.Duration) { 187 | var exitState bool 188 | var process *os.Process 189 | var ok bool 190 | 191 | interval := 50 * time.Millisecond 192 | 193 | for ; timeout > 0; timeout -= interval { 194 | process, ok = IsProcessRunning(pid) 195 | if !ok { 196 | exitState = true 197 | break 198 | } 199 | time.Sleep(interval) 200 | } 201 | 202 | if !exitState { 203 | process.Kill() 204 | } 205 | } 206 | 207 | // remove contents of a file 208 | func RemoveFileContents(filename string) error { 209 | var file *os.File 210 | var err error 211 | file, err = os.OpenFile(filename, os.O_RDWR, 0755) 212 | if err != nil { 213 | return err 214 | } 215 | defer file.Close() 216 | file.Truncate(0) 217 | return nil 218 | } 219 | 220 | // check if port is open 221 | func IsPortOpen(port int) bool { 222 | conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) 223 | if err != nil { 224 | return false 225 | } 226 | defer conn.Close() 227 | return true 228 | } 229 | 230 | // get dump file path 231 | func GetDumpFilePath(filename string) string { 232 | return os.Getenv("HOME") + "/.pm2-go/" + filename 233 | } 234 | 235 | // dump the current processses to a file 236 | func SaveObject(filename string, object interface{}) error { 237 | var file *os.File 238 | var err error 239 | file, err = os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640) 240 | if err != nil { 241 | return err 242 | } 243 | defer file.Close() 244 | encoder := json.NewEncoder(file) 245 | encoder.SetIndent("", " ") 246 | err = encoder.Encode(object) 247 | if err != nil { 248 | return err 249 | } 250 | return nil 251 | } 252 | 253 | // load a file into a object 254 | func LoadObject(filename string, object interface{}) error { 255 | var file *os.File 256 | var err error 257 | file, err = os.Open(filename) 258 | if err != nil { 259 | return err 260 | } 261 | defer file.Close() 262 | decoder := json.NewDecoder(file) 263 | err = decoder.Decode(object) 264 | if err != nil { 265 | return err 266 | } 267 | return nil 268 | } 269 | 270 | // get process 271 | func GetProcess(pid int32) (*os.Process, bool) { 272 | return IsProcessRunning(pid) 273 | } 274 | 275 | // check if process is child process 276 | func IsChildProcess(process *os.Process) bool { 277 | return process.Pid == os.Getpid() 278 | } 279 | 280 | // rename a file 281 | func RenameFile(oldname string, newname string) error { 282 | return os.Rename(oldname, newname) 283 | } 284 | 285 | func FileSize(filename string) int64 { 286 | info, err := os.Stat(filename) 287 | if err != nil { 288 | return 0 289 | } 290 | return info.Size() 291 | } 292 | -------------------------------------------------------------------------------- /proto/process_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.2.0 4 | // - protoc v4.24.3 5 | // source: process.proto 6 | 7 | package __ 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | // ProcessManagerClient is the client API for ProcessManager service. 22 | // 23 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 24 | type ProcessManagerClient interface { 25 | AddProcess(ctx context.Context, in *AddProcessRequest, opts ...grpc.CallOption) (*Process, error) 26 | StartProcess(ctx context.Context, in *StartProcessRequest, opts ...grpc.CallOption) (*Process, error) 27 | StopProcess(ctx context.Context, in *StopProcessRequest, opts ...grpc.CallOption) (*StopProcessResponse, error) 28 | FindProcess(ctx context.Context, in *FindProcessRequest, opts ...grpc.CallOption) (*Process, error) 29 | DeleteProcess(ctx context.Context, in *DeleteProcessRequest, opts ...grpc.CallOption) (*DeleteProcessResponse, error) 30 | ListProcess(ctx context.Context, in *ListProcessRequest, opts ...grpc.CallOption) (*ListProcessResponse, error) 31 | SpawnProcess(ctx context.Context, in *SpawnProcessRequest, opts ...grpc.CallOption) (*SpawnProcessResponse, error) 32 | } 33 | 34 | type processManagerClient struct { 35 | cc grpc.ClientConnInterface 36 | } 37 | 38 | func NewProcessManagerClient(cc grpc.ClientConnInterface) ProcessManagerClient { 39 | return &processManagerClient{cc} 40 | } 41 | 42 | func (c *processManagerClient) AddProcess(ctx context.Context, in *AddProcessRequest, opts ...grpc.CallOption) (*Process, error) { 43 | out := new(Process) 44 | err := c.cc.Invoke(ctx, "/proto.ProcessManager/AddProcess", in, out, opts...) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return out, nil 49 | } 50 | 51 | func (c *processManagerClient) StartProcess(ctx context.Context, in *StartProcessRequest, opts ...grpc.CallOption) (*Process, error) { 52 | out := new(Process) 53 | err := c.cc.Invoke(ctx, "/proto.ProcessManager/StartProcess", in, out, opts...) 54 | if err != nil { 55 | return nil, err 56 | } 57 | return out, nil 58 | } 59 | 60 | func (c *processManagerClient) StopProcess(ctx context.Context, in *StopProcessRequest, opts ...grpc.CallOption) (*StopProcessResponse, error) { 61 | out := new(StopProcessResponse) 62 | err := c.cc.Invoke(ctx, "/proto.ProcessManager/StopProcess", in, out, opts...) 63 | if err != nil { 64 | return nil, err 65 | } 66 | return out, nil 67 | } 68 | 69 | func (c *processManagerClient) FindProcess(ctx context.Context, in *FindProcessRequest, opts ...grpc.CallOption) (*Process, error) { 70 | out := new(Process) 71 | err := c.cc.Invoke(ctx, "/proto.ProcessManager/FindProcess", in, out, opts...) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return out, nil 76 | } 77 | 78 | func (c *processManagerClient) DeleteProcess(ctx context.Context, in *DeleteProcessRequest, opts ...grpc.CallOption) (*DeleteProcessResponse, error) { 79 | out := new(DeleteProcessResponse) 80 | err := c.cc.Invoke(ctx, "/proto.ProcessManager/DeleteProcess", in, out, opts...) 81 | if err != nil { 82 | return nil, err 83 | } 84 | return out, nil 85 | } 86 | 87 | func (c *processManagerClient) ListProcess(ctx context.Context, in *ListProcessRequest, opts ...grpc.CallOption) (*ListProcessResponse, error) { 88 | out := new(ListProcessResponse) 89 | err := c.cc.Invoke(ctx, "/proto.ProcessManager/ListProcess", in, out, opts...) 90 | if err != nil { 91 | return nil, err 92 | } 93 | return out, nil 94 | } 95 | 96 | func (c *processManagerClient) SpawnProcess(ctx context.Context, in *SpawnProcessRequest, opts ...grpc.CallOption) (*SpawnProcessResponse, error) { 97 | out := new(SpawnProcessResponse) 98 | err := c.cc.Invoke(ctx, "/proto.ProcessManager/SpawnProcess", in, out, opts...) 99 | if err != nil { 100 | return nil, err 101 | } 102 | return out, nil 103 | } 104 | 105 | // ProcessManagerServer is the server API for ProcessManager service. 106 | // All implementations must embed UnimplementedProcessManagerServer 107 | // for forward compatibility 108 | type ProcessManagerServer interface { 109 | AddProcess(context.Context, *AddProcessRequest) (*Process, error) 110 | StartProcess(context.Context, *StartProcessRequest) (*Process, error) 111 | StopProcess(context.Context, *StopProcessRequest) (*StopProcessResponse, error) 112 | FindProcess(context.Context, *FindProcessRequest) (*Process, error) 113 | DeleteProcess(context.Context, *DeleteProcessRequest) (*DeleteProcessResponse, error) 114 | ListProcess(context.Context, *ListProcessRequest) (*ListProcessResponse, error) 115 | SpawnProcess(context.Context, *SpawnProcessRequest) (*SpawnProcessResponse, error) 116 | mustEmbedUnimplementedProcessManagerServer() 117 | } 118 | 119 | // UnimplementedProcessManagerServer must be embedded to have forward compatible implementations. 120 | type UnimplementedProcessManagerServer struct { 121 | } 122 | 123 | func (UnimplementedProcessManagerServer) AddProcess(context.Context, *AddProcessRequest) (*Process, error) { 124 | return nil, status.Errorf(codes.Unimplemented, "method AddProcess not implemented") 125 | } 126 | func (UnimplementedProcessManagerServer) StartProcess(context.Context, *StartProcessRequest) (*Process, error) { 127 | return nil, status.Errorf(codes.Unimplemented, "method StartProcess not implemented") 128 | } 129 | func (UnimplementedProcessManagerServer) StopProcess(context.Context, *StopProcessRequest) (*StopProcessResponse, error) { 130 | return nil, status.Errorf(codes.Unimplemented, "method StopProcess not implemented") 131 | } 132 | func (UnimplementedProcessManagerServer) FindProcess(context.Context, *FindProcessRequest) (*Process, error) { 133 | return nil, status.Errorf(codes.Unimplemented, "method FindProcess not implemented") 134 | } 135 | func (UnimplementedProcessManagerServer) DeleteProcess(context.Context, *DeleteProcessRequest) (*DeleteProcessResponse, error) { 136 | return nil, status.Errorf(codes.Unimplemented, "method DeleteProcess not implemented") 137 | } 138 | func (UnimplementedProcessManagerServer) ListProcess(context.Context, *ListProcessRequest) (*ListProcessResponse, error) { 139 | return nil, status.Errorf(codes.Unimplemented, "method ListProcess not implemented") 140 | } 141 | func (UnimplementedProcessManagerServer) SpawnProcess(context.Context, *SpawnProcessRequest) (*SpawnProcessResponse, error) { 142 | return nil, status.Errorf(codes.Unimplemented, "method SpawnProcess not implemented") 143 | } 144 | func (UnimplementedProcessManagerServer) mustEmbedUnimplementedProcessManagerServer() {} 145 | 146 | // UnsafeProcessManagerServer may be embedded to opt out of forward compatibility for this service. 147 | // Use of this interface is not recommended, as added methods to ProcessManagerServer will 148 | // result in compilation errors. 149 | type UnsafeProcessManagerServer interface { 150 | mustEmbedUnimplementedProcessManagerServer() 151 | } 152 | 153 | func RegisterProcessManagerServer(s grpc.ServiceRegistrar, srv ProcessManagerServer) { 154 | s.RegisterService(&ProcessManager_ServiceDesc, srv) 155 | } 156 | 157 | func _ProcessManager_AddProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 158 | in := new(AddProcessRequest) 159 | if err := dec(in); err != nil { 160 | return nil, err 161 | } 162 | if interceptor == nil { 163 | return srv.(ProcessManagerServer).AddProcess(ctx, in) 164 | } 165 | info := &grpc.UnaryServerInfo{ 166 | Server: srv, 167 | FullMethod: "/proto.ProcessManager/AddProcess", 168 | } 169 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 170 | return srv.(ProcessManagerServer).AddProcess(ctx, req.(*AddProcessRequest)) 171 | } 172 | return interceptor(ctx, in, info, handler) 173 | } 174 | 175 | func _ProcessManager_StartProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 176 | in := new(StartProcessRequest) 177 | if err := dec(in); err != nil { 178 | return nil, err 179 | } 180 | if interceptor == nil { 181 | return srv.(ProcessManagerServer).StartProcess(ctx, in) 182 | } 183 | info := &grpc.UnaryServerInfo{ 184 | Server: srv, 185 | FullMethod: "/proto.ProcessManager/StartProcess", 186 | } 187 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 188 | return srv.(ProcessManagerServer).StartProcess(ctx, req.(*StartProcessRequest)) 189 | } 190 | return interceptor(ctx, in, info, handler) 191 | } 192 | 193 | func _ProcessManager_StopProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 194 | in := new(StopProcessRequest) 195 | if err := dec(in); err != nil { 196 | return nil, err 197 | } 198 | if interceptor == nil { 199 | return srv.(ProcessManagerServer).StopProcess(ctx, in) 200 | } 201 | info := &grpc.UnaryServerInfo{ 202 | Server: srv, 203 | FullMethod: "/proto.ProcessManager/StopProcess", 204 | } 205 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 206 | return srv.(ProcessManagerServer).StopProcess(ctx, req.(*StopProcessRequest)) 207 | } 208 | return interceptor(ctx, in, info, handler) 209 | } 210 | 211 | func _ProcessManager_FindProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 212 | in := new(FindProcessRequest) 213 | if err := dec(in); err != nil { 214 | return nil, err 215 | } 216 | if interceptor == nil { 217 | return srv.(ProcessManagerServer).FindProcess(ctx, in) 218 | } 219 | info := &grpc.UnaryServerInfo{ 220 | Server: srv, 221 | FullMethod: "/proto.ProcessManager/FindProcess", 222 | } 223 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 224 | return srv.(ProcessManagerServer).FindProcess(ctx, req.(*FindProcessRequest)) 225 | } 226 | return interceptor(ctx, in, info, handler) 227 | } 228 | 229 | func _ProcessManager_DeleteProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 230 | in := new(DeleteProcessRequest) 231 | if err := dec(in); err != nil { 232 | return nil, err 233 | } 234 | if interceptor == nil { 235 | return srv.(ProcessManagerServer).DeleteProcess(ctx, in) 236 | } 237 | info := &grpc.UnaryServerInfo{ 238 | Server: srv, 239 | FullMethod: "/proto.ProcessManager/DeleteProcess", 240 | } 241 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 242 | return srv.(ProcessManagerServer).DeleteProcess(ctx, req.(*DeleteProcessRequest)) 243 | } 244 | return interceptor(ctx, in, info, handler) 245 | } 246 | 247 | func _ProcessManager_ListProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 248 | in := new(ListProcessRequest) 249 | if err := dec(in); err != nil { 250 | return nil, err 251 | } 252 | if interceptor == nil { 253 | return srv.(ProcessManagerServer).ListProcess(ctx, in) 254 | } 255 | info := &grpc.UnaryServerInfo{ 256 | Server: srv, 257 | FullMethod: "/proto.ProcessManager/ListProcess", 258 | } 259 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 260 | return srv.(ProcessManagerServer).ListProcess(ctx, req.(*ListProcessRequest)) 261 | } 262 | return interceptor(ctx, in, info, handler) 263 | } 264 | 265 | func _ProcessManager_SpawnProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 266 | in := new(SpawnProcessRequest) 267 | if err := dec(in); err != nil { 268 | return nil, err 269 | } 270 | if interceptor == nil { 271 | return srv.(ProcessManagerServer).SpawnProcess(ctx, in) 272 | } 273 | info := &grpc.UnaryServerInfo{ 274 | Server: srv, 275 | FullMethod: "/proto.ProcessManager/SpawnProcess", 276 | } 277 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 278 | return srv.(ProcessManagerServer).SpawnProcess(ctx, req.(*SpawnProcessRequest)) 279 | } 280 | return interceptor(ctx, in, info, handler) 281 | } 282 | 283 | // ProcessManager_ServiceDesc is the grpc.ServiceDesc for ProcessManager service. 284 | // It's only intended for direct use with grpc.RegisterService, 285 | // and not to be introspected or modified (even as a copy) 286 | var ProcessManager_ServiceDesc = grpc.ServiceDesc{ 287 | ServiceName: "proto.ProcessManager", 288 | HandlerType: (*ProcessManagerServer)(nil), 289 | Methods: []grpc.MethodDesc{ 290 | { 291 | MethodName: "AddProcess", 292 | Handler: _ProcessManager_AddProcess_Handler, 293 | }, 294 | { 295 | MethodName: "StartProcess", 296 | Handler: _ProcessManager_StartProcess_Handler, 297 | }, 298 | { 299 | MethodName: "StopProcess", 300 | Handler: _ProcessManager_StopProcess_Handler, 301 | }, 302 | { 303 | MethodName: "FindProcess", 304 | Handler: _ProcessManager_FindProcess_Handler, 305 | }, 306 | { 307 | MethodName: "DeleteProcess", 308 | Handler: _ProcessManager_DeleteProcess_Handler, 309 | }, 310 | { 311 | MethodName: "ListProcess", 312 | Handler: _ProcessManager_ListProcess_Handler, 313 | }, 314 | { 315 | MethodName: "SpawnProcess", 316 | Handler: _ProcessManager_SpawnProcess_Handler, 317 | }, 318 | }, 319 | Streams: []grpc.StreamDesc{}, 320 | Metadata: "process.proto", 321 | } 322 | -------------------------------------------------------------------------------- /proto/process.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.28.1 4 | // protoc v4.24.3 5 | // source: process.proto 6 | 7 | package __ 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | durationpb "google.golang.org/protobuf/types/known/durationpb" 13 | timestamppb "google.golang.org/protobuf/types/known/timestamppb" 14 | reflect "reflect" 15 | sync "sync" 16 | ) 17 | 18 | const ( 19 | // Verify that this generated code is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 21 | // Verify that runtime/protoimpl is sufficiently up-to-date. 22 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 23 | ) 24 | 25 | type ProcStatus struct { 26 | state protoimpl.MessageState 27 | sizeCache protoimpl.SizeCache 28 | unknownFields protoimpl.UnknownFields 29 | 30 | Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` 31 | StartedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` 32 | Uptime *durationpb.Duration `protobuf:"bytes,3,opt,name=uptime,proto3" json:"uptime,omitempty"` 33 | Restarts int32 `protobuf:"varint,4,opt,name=restarts,proto3" json:"restarts,omitempty"` 34 | Cpu string `protobuf:"bytes,5,opt,name=cpu,proto3" json:"cpu,omitempty"` 35 | Memory string `protobuf:"bytes,6,opt,name=memory,proto3" json:"memory,omitempty"` 36 | ParentPid int32 `protobuf:"varint,7,opt,name=parent_pid,json=parentPid,proto3" json:"parent_pid,omitempty"` 37 | } 38 | 39 | func (x *ProcStatus) Reset() { 40 | *x = ProcStatus{} 41 | if protoimpl.UnsafeEnabled { 42 | mi := &file_process_proto_msgTypes[0] 43 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 44 | ms.StoreMessageInfo(mi) 45 | } 46 | } 47 | 48 | func (x *ProcStatus) String() string { 49 | return protoimpl.X.MessageStringOf(x) 50 | } 51 | 52 | func (*ProcStatus) ProtoMessage() {} 53 | 54 | func (x *ProcStatus) ProtoReflect() protoreflect.Message { 55 | mi := &file_process_proto_msgTypes[0] 56 | if protoimpl.UnsafeEnabled && x != nil { 57 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 58 | if ms.LoadMessageInfo() == nil { 59 | ms.StoreMessageInfo(mi) 60 | } 61 | return ms 62 | } 63 | return mi.MessageOf(x) 64 | } 65 | 66 | // Deprecated: Use ProcStatus.ProtoReflect.Descriptor instead. 67 | func (*ProcStatus) Descriptor() ([]byte, []int) { 68 | return file_process_proto_rawDescGZIP(), []int{0} 69 | } 70 | 71 | func (x *ProcStatus) GetStatus() string { 72 | if x != nil { 73 | return x.Status 74 | } 75 | return "" 76 | } 77 | 78 | func (x *ProcStatus) GetStartedAt() *timestamppb.Timestamp { 79 | if x != nil { 80 | return x.StartedAt 81 | } 82 | return nil 83 | } 84 | 85 | func (x *ProcStatus) GetUptime() *durationpb.Duration { 86 | if x != nil { 87 | return x.Uptime 88 | } 89 | return nil 90 | } 91 | 92 | func (x *ProcStatus) GetRestarts() int32 { 93 | if x != nil { 94 | return x.Restarts 95 | } 96 | return 0 97 | } 98 | 99 | func (x *ProcStatus) GetCpu() string { 100 | if x != nil { 101 | return x.Cpu 102 | } 103 | return "" 104 | } 105 | 106 | func (x *ProcStatus) GetMemory() string { 107 | if x != nil { 108 | return x.Memory 109 | } 110 | return "" 111 | } 112 | 113 | func (x *ProcStatus) GetParentPid() int32 { 114 | if x != nil { 115 | return x.ParentPid 116 | } 117 | return 0 118 | } 119 | 120 | type Process struct { 121 | state protoimpl.MessageState 122 | sizeCache protoimpl.SizeCache 123 | unknownFields protoimpl.UnknownFields 124 | 125 | Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` 126 | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` 127 | Args []string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"` 128 | Scripts []string `protobuf:"bytes,4,rep,name=scripts,proto3" json:"scripts,omitempty"` 129 | ExecutablePath string `protobuf:"bytes,5,opt,name=executable_path,json=executablePath,proto3" json:"executable_path,omitempty"` 130 | Pid int32 `protobuf:"varint,6,opt,name=pid,proto3" json:"pid,omitempty"` 131 | AutoRestart bool `protobuf:"varint,7,opt,name=auto_restart,json=autoRestart,proto3" json:"auto_restart,omitempty"` 132 | Cwd string `protobuf:"bytes,8,opt,name=cwd,proto3" json:"cwd,omitempty"` 133 | PidFilePath string `protobuf:"bytes,9,opt,name=pid_file_path,json=pidFilePath,proto3" json:"pid_file_path,omitempty"` 134 | LogFilePath string `protobuf:"bytes,10,opt,name=log_file_path,json=logFilePath,proto3" json:"log_file_path,omitempty"` 135 | ErrFilePath string `protobuf:"bytes,11,opt,name=err_file_path,json=errFilePath,proto3" json:"err_file_path,omitempty"` 136 | CronRestart string `protobuf:"bytes,12,opt,name=cron_restart,json=cronRestart,proto3" json:"cron_restart,omitempty"` 137 | NextStartAt *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=next_start_at,json=nextStartAt,proto3" json:"next_start_at,omitempty"` 138 | ProcStatus *ProcStatus `protobuf:"bytes,14,opt,name=proc_status,json=procStatus,proto3" json:"proc_status,omitempty"` 139 | StopSignal bool `protobuf:"varint,15,opt,name=stop_signal,json=stopSignal,proto3" json:"stop_signal,omitempty"` 140 | LogFileCount int32 `protobuf:"varint,16,opt,name=log_file_count,json=logFileCount,proto3" json:"log_file_count,omitempty"` 141 | } 142 | 143 | func (x *Process) Reset() { 144 | *x = Process{} 145 | if protoimpl.UnsafeEnabled { 146 | mi := &file_process_proto_msgTypes[1] 147 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 148 | ms.StoreMessageInfo(mi) 149 | } 150 | } 151 | 152 | func (x *Process) String() string { 153 | return protoimpl.X.MessageStringOf(x) 154 | } 155 | 156 | func (*Process) ProtoMessage() {} 157 | 158 | func (x *Process) ProtoReflect() protoreflect.Message { 159 | mi := &file_process_proto_msgTypes[1] 160 | if protoimpl.UnsafeEnabled && x != nil { 161 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 162 | if ms.LoadMessageInfo() == nil { 163 | ms.StoreMessageInfo(mi) 164 | } 165 | return ms 166 | } 167 | return mi.MessageOf(x) 168 | } 169 | 170 | // Deprecated: Use Process.ProtoReflect.Descriptor instead. 171 | func (*Process) Descriptor() ([]byte, []int) { 172 | return file_process_proto_rawDescGZIP(), []int{1} 173 | } 174 | 175 | func (x *Process) GetId() int32 { 176 | if x != nil { 177 | return x.Id 178 | } 179 | return 0 180 | } 181 | 182 | func (x *Process) GetName() string { 183 | if x != nil { 184 | return x.Name 185 | } 186 | return "" 187 | } 188 | 189 | func (x *Process) GetArgs() []string { 190 | if x != nil { 191 | return x.Args 192 | } 193 | return nil 194 | } 195 | 196 | func (x *Process) GetScripts() []string { 197 | if x != nil { 198 | return x.Scripts 199 | } 200 | return nil 201 | } 202 | 203 | func (x *Process) GetExecutablePath() string { 204 | if x != nil { 205 | return x.ExecutablePath 206 | } 207 | return "" 208 | } 209 | 210 | func (x *Process) GetPid() int32 { 211 | if x != nil { 212 | return x.Pid 213 | } 214 | return 0 215 | } 216 | 217 | func (x *Process) GetAutoRestart() bool { 218 | if x != nil { 219 | return x.AutoRestart 220 | } 221 | return false 222 | } 223 | 224 | func (x *Process) GetCwd() string { 225 | if x != nil { 226 | return x.Cwd 227 | } 228 | return "" 229 | } 230 | 231 | func (x *Process) GetPidFilePath() string { 232 | if x != nil { 233 | return x.PidFilePath 234 | } 235 | return "" 236 | } 237 | 238 | func (x *Process) GetLogFilePath() string { 239 | if x != nil { 240 | return x.LogFilePath 241 | } 242 | return "" 243 | } 244 | 245 | func (x *Process) GetErrFilePath() string { 246 | if x != nil { 247 | return x.ErrFilePath 248 | } 249 | return "" 250 | } 251 | 252 | func (x *Process) GetCronRestart() string { 253 | if x != nil { 254 | return x.CronRestart 255 | } 256 | return "" 257 | } 258 | 259 | func (x *Process) GetNextStartAt() *timestamppb.Timestamp { 260 | if x != nil { 261 | return x.NextStartAt 262 | } 263 | return nil 264 | } 265 | 266 | func (x *Process) GetProcStatus() *ProcStatus { 267 | if x != nil { 268 | return x.ProcStatus 269 | } 270 | return nil 271 | } 272 | 273 | func (x *Process) GetStopSignal() bool { 274 | if x != nil { 275 | return x.StopSignal 276 | } 277 | return false 278 | } 279 | 280 | func (x *Process) GetLogFileCount() int32 { 281 | if x != nil { 282 | return x.LogFileCount 283 | } 284 | return 0 285 | } 286 | 287 | type AddProcessRequest struct { 288 | state protoimpl.MessageState 289 | sizeCache protoimpl.SizeCache 290 | unknownFields protoimpl.UnknownFields 291 | 292 | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` 293 | Args []string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"` 294 | Scripts []string `protobuf:"bytes,4,rep,name=scripts,proto3" json:"scripts,omitempty"` 295 | ExecutablePath string `protobuf:"bytes,5,opt,name=executable_path,json=executablePath,proto3" json:"executable_path,omitempty"` 296 | Pid int32 `protobuf:"varint,6,opt,name=pid,proto3" json:"pid,omitempty"` 297 | AutoRestart bool `protobuf:"varint,7,opt,name=auto_restart,json=autoRestart,proto3" json:"auto_restart,omitempty"` 298 | Cwd string `protobuf:"bytes,8,opt,name=cwd,proto3" json:"cwd,omitempty"` 299 | PidFilePath string `protobuf:"bytes,9,opt,name=pid_file_path,json=pidFilePath,proto3" json:"pid_file_path,omitempty"` 300 | LogFilePath string `protobuf:"bytes,10,opt,name=log_file_path,json=logFilePath,proto3" json:"log_file_path,omitempty"` 301 | ErrFilePath string `protobuf:"bytes,11,opt,name=err_file_path,json=errFilePath,proto3" json:"err_file_path,omitempty"` 302 | CronRestart string `protobuf:"bytes,12,opt,name=cron_restart,json=cronRestart,proto3" json:"cron_restart,omitempty"` 303 | } 304 | 305 | func (x *AddProcessRequest) Reset() { 306 | *x = AddProcessRequest{} 307 | if protoimpl.UnsafeEnabled { 308 | mi := &file_process_proto_msgTypes[2] 309 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 310 | ms.StoreMessageInfo(mi) 311 | } 312 | } 313 | 314 | func (x *AddProcessRequest) String() string { 315 | return protoimpl.X.MessageStringOf(x) 316 | } 317 | 318 | func (*AddProcessRequest) ProtoMessage() {} 319 | 320 | func (x *AddProcessRequest) ProtoReflect() protoreflect.Message { 321 | mi := &file_process_proto_msgTypes[2] 322 | if protoimpl.UnsafeEnabled && x != nil { 323 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 324 | if ms.LoadMessageInfo() == nil { 325 | ms.StoreMessageInfo(mi) 326 | } 327 | return ms 328 | } 329 | return mi.MessageOf(x) 330 | } 331 | 332 | // Deprecated: Use AddProcessRequest.ProtoReflect.Descriptor instead. 333 | func (*AddProcessRequest) Descriptor() ([]byte, []int) { 334 | return file_process_proto_rawDescGZIP(), []int{2} 335 | } 336 | 337 | func (x *AddProcessRequest) GetName() string { 338 | if x != nil { 339 | return x.Name 340 | } 341 | return "" 342 | } 343 | 344 | func (x *AddProcessRequest) GetArgs() []string { 345 | if x != nil { 346 | return x.Args 347 | } 348 | return nil 349 | } 350 | 351 | func (x *AddProcessRequest) GetScripts() []string { 352 | if x != nil { 353 | return x.Scripts 354 | } 355 | return nil 356 | } 357 | 358 | func (x *AddProcessRequest) GetExecutablePath() string { 359 | if x != nil { 360 | return x.ExecutablePath 361 | } 362 | return "" 363 | } 364 | 365 | func (x *AddProcessRequest) GetPid() int32 { 366 | if x != nil { 367 | return x.Pid 368 | } 369 | return 0 370 | } 371 | 372 | func (x *AddProcessRequest) GetAutoRestart() bool { 373 | if x != nil { 374 | return x.AutoRestart 375 | } 376 | return false 377 | } 378 | 379 | func (x *AddProcessRequest) GetCwd() string { 380 | if x != nil { 381 | return x.Cwd 382 | } 383 | return "" 384 | } 385 | 386 | func (x *AddProcessRequest) GetPidFilePath() string { 387 | if x != nil { 388 | return x.PidFilePath 389 | } 390 | return "" 391 | } 392 | 393 | func (x *AddProcessRequest) GetLogFilePath() string { 394 | if x != nil { 395 | return x.LogFilePath 396 | } 397 | return "" 398 | } 399 | 400 | func (x *AddProcessRequest) GetErrFilePath() string { 401 | if x != nil { 402 | return x.ErrFilePath 403 | } 404 | return "" 405 | } 406 | 407 | func (x *AddProcessRequest) GetCronRestart() string { 408 | if x != nil { 409 | return x.CronRestart 410 | } 411 | return "" 412 | } 413 | 414 | type FindProcessRequest struct { 415 | state protoimpl.MessageState 416 | sizeCache protoimpl.SizeCache 417 | unknownFields protoimpl.UnknownFields 418 | 419 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 420 | } 421 | 422 | func (x *FindProcessRequest) Reset() { 423 | *x = FindProcessRequest{} 424 | if protoimpl.UnsafeEnabled { 425 | mi := &file_process_proto_msgTypes[3] 426 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 427 | ms.StoreMessageInfo(mi) 428 | } 429 | } 430 | 431 | func (x *FindProcessRequest) String() string { 432 | return protoimpl.X.MessageStringOf(x) 433 | } 434 | 435 | func (*FindProcessRequest) ProtoMessage() {} 436 | 437 | func (x *FindProcessRequest) ProtoReflect() protoreflect.Message { 438 | mi := &file_process_proto_msgTypes[3] 439 | if protoimpl.UnsafeEnabled && x != nil { 440 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 441 | if ms.LoadMessageInfo() == nil { 442 | ms.StoreMessageInfo(mi) 443 | } 444 | return ms 445 | } 446 | return mi.MessageOf(x) 447 | } 448 | 449 | // Deprecated: Use FindProcessRequest.ProtoReflect.Descriptor instead. 450 | func (*FindProcessRequest) Descriptor() ([]byte, []int) { 451 | return file_process_proto_rawDescGZIP(), []int{3} 452 | } 453 | 454 | func (x *FindProcessRequest) GetName() string { 455 | if x != nil { 456 | return x.Name 457 | } 458 | return "" 459 | } 460 | 461 | type StopProcessRequest struct { 462 | state protoimpl.MessageState 463 | sizeCache protoimpl.SizeCache 464 | unknownFields protoimpl.UnknownFields 465 | 466 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 467 | Id int32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` 468 | } 469 | 470 | func (x *StopProcessRequest) Reset() { 471 | *x = StopProcessRequest{} 472 | if protoimpl.UnsafeEnabled { 473 | mi := &file_process_proto_msgTypes[4] 474 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 475 | ms.StoreMessageInfo(mi) 476 | } 477 | } 478 | 479 | func (x *StopProcessRequest) String() string { 480 | return protoimpl.X.MessageStringOf(x) 481 | } 482 | 483 | func (*StopProcessRequest) ProtoMessage() {} 484 | 485 | func (x *StopProcessRequest) ProtoReflect() protoreflect.Message { 486 | mi := &file_process_proto_msgTypes[4] 487 | if protoimpl.UnsafeEnabled && x != nil { 488 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 489 | if ms.LoadMessageInfo() == nil { 490 | ms.StoreMessageInfo(mi) 491 | } 492 | return ms 493 | } 494 | return mi.MessageOf(x) 495 | } 496 | 497 | // Deprecated: Use StopProcessRequest.ProtoReflect.Descriptor instead. 498 | func (*StopProcessRequest) Descriptor() ([]byte, []int) { 499 | return file_process_proto_rawDescGZIP(), []int{4} 500 | } 501 | 502 | func (x *StopProcessRequest) GetName() string { 503 | if x != nil { 504 | return x.Name 505 | } 506 | return "" 507 | } 508 | 509 | func (x *StopProcessRequest) GetId() int32 { 510 | if x != nil { 511 | return x.Id 512 | } 513 | return 0 514 | } 515 | 516 | type StopProcessResponse struct { 517 | state protoimpl.MessageState 518 | sizeCache protoimpl.SizeCache 519 | unknownFields protoimpl.UnknownFields 520 | 521 | Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` 522 | } 523 | 524 | func (x *StopProcessResponse) Reset() { 525 | *x = StopProcessResponse{} 526 | if protoimpl.UnsafeEnabled { 527 | mi := &file_process_proto_msgTypes[5] 528 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 529 | ms.StoreMessageInfo(mi) 530 | } 531 | } 532 | 533 | func (x *StopProcessResponse) String() string { 534 | return protoimpl.X.MessageStringOf(x) 535 | } 536 | 537 | func (*StopProcessResponse) ProtoMessage() {} 538 | 539 | func (x *StopProcessResponse) ProtoReflect() protoreflect.Message { 540 | mi := &file_process_proto_msgTypes[5] 541 | if protoimpl.UnsafeEnabled && x != nil { 542 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 543 | if ms.LoadMessageInfo() == nil { 544 | ms.StoreMessageInfo(mi) 545 | } 546 | return ms 547 | } 548 | return mi.MessageOf(x) 549 | } 550 | 551 | // Deprecated: Use StopProcessResponse.ProtoReflect.Descriptor instead. 552 | func (*StopProcessResponse) Descriptor() ([]byte, []int) { 553 | return file_process_proto_rawDescGZIP(), []int{5} 554 | } 555 | 556 | func (x *StopProcessResponse) GetSuccess() bool { 557 | if x != nil { 558 | return x.Success 559 | } 560 | return false 561 | } 562 | 563 | type StartProcessRequest struct { 564 | state protoimpl.MessageState 565 | sizeCache protoimpl.SizeCache 566 | unknownFields protoimpl.UnknownFields 567 | 568 | Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` 569 | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` 570 | Args []string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"` 571 | Scripts []string `protobuf:"bytes,4,rep,name=scripts,proto3" json:"scripts,omitempty"` 572 | ExecutablePath string `protobuf:"bytes,5,opt,name=executable_path,json=executablePath,proto3" json:"executable_path,omitempty"` 573 | Pid int32 `protobuf:"varint,6,opt,name=pid,proto3" json:"pid,omitempty"` 574 | AutoRestart bool `protobuf:"varint,7,opt,name=auto_restart,json=autoRestart,proto3" json:"auto_restart,omitempty"` 575 | Cwd string `protobuf:"bytes,8,opt,name=cwd,proto3" json:"cwd,omitempty"` 576 | PidFilePath string `protobuf:"bytes,9,opt,name=pid_file_path,json=pidFilePath,proto3" json:"pid_file_path,omitempty"` 577 | LogFilePath string `protobuf:"bytes,10,opt,name=log_file_path,json=logFilePath,proto3" json:"log_file_path,omitempty"` 578 | ErrFilePath string `protobuf:"bytes,11,opt,name=err_file_path,json=errFilePath,proto3" json:"err_file_path,omitempty"` 579 | CronRestart string `protobuf:"bytes,12,opt,name=cron_restart,json=cronRestart,proto3" json:"cron_restart,omitempty"` 580 | } 581 | 582 | func (x *StartProcessRequest) Reset() { 583 | *x = StartProcessRequest{} 584 | if protoimpl.UnsafeEnabled { 585 | mi := &file_process_proto_msgTypes[6] 586 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 587 | ms.StoreMessageInfo(mi) 588 | } 589 | } 590 | 591 | func (x *StartProcessRequest) String() string { 592 | return protoimpl.X.MessageStringOf(x) 593 | } 594 | 595 | func (*StartProcessRequest) ProtoMessage() {} 596 | 597 | func (x *StartProcessRequest) ProtoReflect() protoreflect.Message { 598 | mi := &file_process_proto_msgTypes[6] 599 | if protoimpl.UnsafeEnabled && x != nil { 600 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 601 | if ms.LoadMessageInfo() == nil { 602 | ms.StoreMessageInfo(mi) 603 | } 604 | return ms 605 | } 606 | return mi.MessageOf(x) 607 | } 608 | 609 | // Deprecated: Use StartProcessRequest.ProtoReflect.Descriptor instead. 610 | func (*StartProcessRequest) Descriptor() ([]byte, []int) { 611 | return file_process_proto_rawDescGZIP(), []int{6} 612 | } 613 | 614 | func (x *StartProcessRequest) GetId() int32 { 615 | if x != nil { 616 | return x.Id 617 | } 618 | return 0 619 | } 620 | 621 | func (x *StartProcessRequest) GetName() string { 622 | if x != nil { 623 | return x.Name 624 | } 625 | return "" 626 | } 627 | 628 | func (x *StartProcessRequest) GetArgs() []string { 629 | if x != nil { 630 | return x.Args 631 | } 632 | return nil 633 | } 634 | 635 | func (x *StartProcessRequest) GetScripts() []string { 636 | if x != nil { 637 | return x.Scripts 638 | } 639 | return nil 640 | } 641 | 642 | func (x *StartProcessRequest) GetExecutablePath() string { 643 | if x != nil { 644 | return x.ExecutablePath 645 | } 646 | return "" 647 | } 648 | 649 | func (x *StartProcessRequest) GetPid() int32 { 650 | if x != nil { 651 | return x.Pid 652 | } 653 | return 0 654 | } 655 | 656 | func (x *StartProcessRequest) GetAutoRestart() bool { 657 | if x != nil { 658 | return x.AutoRestart 659 | } 660 | return false 661 | } 662 | 663 | func (x *StartProcessRequest) GetCwd() string { 664 | if x != nil { 665 | return x.Cwd 666 | } 667 | return "" 668 | } 669 | 670 | func (x *StartProcessRequest) GetPidFilePath() string { 671 | if x != nil { 672 | return x.PidFilePath 673 | } 674 | return "" 675 | } 676 | 677 | func (x *StartProcessRequest) GetLogFilePath() string { 678 | if x != nil { 679 | return x.LogFilePath 680 | } 681 | return "" 682 | } 683 | 684 | func (x *StartProcessRequest) GetErrFilePath() string { 685 | if x != nil { 686 | return x.ErrFilePath 687 | } 688 | return "" 689 | } 690 | 691 | func (x *StartProcessRequest) GetCronRestart() string { 692 | if x != nil { 693 | return x.CronRestart 694 | } 695 | return "" 696 | } 697 | 698 | type ListProcessRequest struct { 699 | state protoimpl.MessageState 700 | sizeCache protoimpl.SizeCache 701 | unknownFields protoimpl.UnknownFields 702 | } 703 | 704 | func (x *ListProcessRequest) Reset() { 705 | *x = ListProcessRequest{} 706 | if protoimpl.UnsafeEnabled { 707 | mi := &file_process_proto_msgTypes[7] 708 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 709 | ms.StoreMessageInfo(mi) 710 | } 711 | } 712 | 713 | func (x *ListProcessRequest) String() string { 714 | return protoimpl.X.MessageStringOf(x) 715 | } 716 | 717 | func (*ListProcessRequest) ProtoMessage() {} 718 | 719 | func (x *ListProcessRequest) ProtoReflect() protoreflect.Message { 720 | mi := &file_process_proto_msgTypes[7] 721 | if protoimpl.UnsafeEnabled && x != nil { 722 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 723 | if ms.LoadMessageInfo() == nil { 724 | ms.StoreMessageInfo(mi) 725 | } 726 | return ms 727 | } 728 | return mi.MessageOf(x) 729 | } 730 | 731 | // Deprecated: Use ListProcessRequest.ProtoReflect.Descriptor instead. 732 | func (*ListProcessRequest) Descriptor() ([]byte, []int) { 733 | return file_process_proto_rawDescGZIP(), []int{7} 734 | } 735 | 736 | type ListProcessResponse struct { 737 | state protoimpl.MessageState 738 | sizeCache protoimpl.SizeCache 739 | unknownFields protoimpl.UnknownFields 740 | 741 | Processes []*Process `protobuf:"bytes,1,rep,name=processes,proto3" json:"processes,omitempty"` 742 | } 743 | 744 | func (x *ListProcessResponse) Reset() { 745 | *x = ListProcessResponse{} 746 | if protoimpl.UnsafeEnabled { 747 | mi := &file_process_proto_msgTypes[8] 748 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 749 | ms.StoreMessageInfo(mi) 750 | } 751 | } 752 | 753 | func (x *ListProcessResponse) String() string { 754 | return protoimpl.X.MessageStringOf(x) 755 | } 756 | 757 | func (*ListProcessResponse) ProtoMessage() {} 758 | 759 | func (x *ListProcessResponse) ProtoReflect() protoreflect.Message { 760 | mi := &file_process_proto_msgTypes[8] 761 | if protoimpl.UnsafeEnabled && x != nil { 762 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 763 | if ms.LoadMessageInfo() == nil { 764 | ms.StoreMessageInfo(mi) 765 | } 766 | return ms 767 | } 768 | return mi.MessageOf(x) 769 | } 770 | 771 | // Deprecated: Use ListProcessResponse.ProtoReflect.Descriptor instead. 772 | func (*ListProcessResponse) Descriptor() ([]byte, []int) { 773 | return file_process_proto_rawDescGZIP(), []int{8} 774 | } 775 | 776 | func (x *ListProcessResponse) GetProcesses() []*Process { 777 | if x != nil { 778 | return x.Processes 779 | } 780 | return nil 781 | } 782 | 783 | type DeleteProcessRequest struct { 784 | state protoimpl.MessageState 785 | sizeCache protoimpl.SizeCache 786 | unknownFields protoimpl.UnknownFields 787 | 788 | Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` 789 | } 790 | 791 | func (x *DeleteProcessRequest) Reset() { 792 | *x = DeleteProcessRequest{} 793 | if protoimpl.UnsafeEnabled { 794 | mi := &file_process_proto_msgTypes[9] 795 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 796 | ms.StoreMessageInfo(mi) 797 | } 798 | } 799 | 800 | func (x *DeleteProcessRequest) String() string { 801 | return protoimpl.X.MessageStringOf(x) 802 | } 803 | 804 | func (*DeleteProcessRequest) ProtoMessage() {} 805 | 806 | func (x *DeleteProcessRequest) ProtoReflect() protoreflect.Message { 807 | mi := &file_process_proto_msgTypes[9] 808 | if protoimpl.UnsafeEnabled && x != nil { 809 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 810 | if ms.LoadMessageInfo() == nil { 811 | ms.StoreMessageInfo(mi) 812 | } 813 | return ms 814 | } 815 | return mi.MessageOf(x) 816 | } 817 | 818 | // Deprecated: Use DeleteProcessRequest.ProtoReflect.Descriptor instead. 819 | func (*DeleteProcessRequest) Descriptor() ([]byte, []int) { 820 | return file_process_proto_rawDescGZIP(), []int{9} 821 | } 822 | 823 | func (x *DeleteProcessRequest) GetId() int32 { 824 | if x != nil { 825 | return x.Id 826 | } 827 | return 0 828 | } 829 | 830 | type DeleteProcessResponse struct { 831 | state protoimpl.MessageState 832 | sizeCache protoimpl.SizeCache 833 | unknownFields protoimpl.UnknownFields 834 | 835 | Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` 836 | } 837 | 838 | func (x *DeleteProcessResponse) Reset() { 839 | *x = DeleteProcessResponse{} 840 | if protoimpl.UnsafeEnabled { 841 | mi := &file_process_proto_msgTypes[10] 842 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 843 | ms.StoreMessageInfo(mi) 844 | } 845 | } 846 | 847 | func (x *DeleteProcessResponse) String() string { 848 | return protoimpl.X.MessageStringOf(x) 849 | } 850 | 851 | func (*DeleteProcessResponse) ProtoMessage() {} 852 | 853 | func (x *DeleteProcessResponse) ProtoReflect() protoreflect.Message { 854 | mi := &file_process_proto_msgTypes[10] 855 | if protoimpl.UnsafeEnabled && x != nil { 856 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 857 | if ms.LoadMessageInfo() == nil { 858 | ms.StoreMessageInfo(mi) 859 | } 860 | return ms 861 | } 862 | return mi.MessageOf(x) 863 | } 864 | 865 | // Deprecated: Use DeleteProcessResponse.ProtoReflect.Descriptor instead. 866 | func (*DeleteProcessResponse) Descriptor() ([]byte, []int) { 867 | return file_process_proto_rawDescGZIP(), []int{10} 868 | } 869 | 870 | func (x *DeleteProcessResponse) GetSuccess() bool { 871 | if x != nil { 872 | return x.Success 873 | } 874 | return false 875 | } 876 | 877 | type SpawnProcessRequest struct { 878 | state protoimpl.MessageState 879 | sizeCache protoimpl.SizeCache 880 | unknownFields protoimpl.UnknownFields 881 | 882 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 883 | Args []string `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"` 884 | Scripts []string `protobuf:"bytes,3,rep,name=scripts,proto3" json:"scripts,omitempty"` 885 | ExecutablePath string `protobuf:"bytes,4,opt,name=executable_path,json=executablePath,proto3" json:"executable_path,omitempty"` 886 | AutoRestart bool `protobuf:"varint,6,opt,name=auto_restart,json=autoRestart,proto3" json:"auto_restart,omitempty"` 887 | Cwd string `protobuf:"bytes,7,opt,name=cwd,proto3" json:"cwd,omitempty"` 888 | CronRestart string `protobuf:"bytes,11,opt,name=cron_restart,json=cronRestart,proto3" json:"cron_restart,omitempty"` 889 | } 890 | 891 | func (x *SpawnProcessRequest) Reset() { 892 | *x = SpawnProcessRequest{} 893 | if protoimpl.UnsafeEnabled { 894 | mi := &file_process_proto_msgTypes[11] 895 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 896 | ms.StoreMessageInfo(mi) 897 | } 898 | } 899 | 900 | func (x *SpawnProcessRequest) String() string { 901 | return protoimpl.X.MessageStringOf(x) 902 | } 903 | 904 | func (*SpawnProcessRequest) ProtoMessage() {} 905 | 906 | func (x *SpawnProcessRequest) ProtoReflect() protoreflect.Message { 907 | mi := &file_process_proto_msgTypes[11] 908 | if protoimpl.UnsafeEnabled && x != nil { 909 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 910 | if ms.LoadMessageInfo() == nil { 911 | ms.StoreMessageInfo(mi) 912 | } 913 | return ms 914 | } 915 | return mi.MessageOf(x) 916 | } 917 | 918 | // Deprecated: Use SpawnProcessRequest.ProtoReflect.Descriptor instead. 919 | func (*SpawnProcessRequest) Descriptor() ([]byte, []int) { 920 | return file_process_proto_rawDescGZIP(), []int{11} 921 | } 922 | 923 | func (x *SpawnProcessRequest) GetName() string { 924 | if x != nil { 925 | return x.Name 926 | } 927 | return "" 928 | } 929 | 930 | func (x *SpawnProcessRequest) GetArgs() []string { 931 | if x != nil { 932 | return x.Args 933 | } 934 | return nil 935 | } 936 | 937 | func (x *SpawnProcessRequest) GetScripts() []string { 938 | if x != nil { 939 | return x.Scripts 940 | } 941 | return nil 942 | } 943 | 944 | func (x *SpawnProcessRequest) GetExecutablePath() string { 945 | if x != nil { 946 | return x.ExecutablePath 947 | } 948 | return "" 949 | } 950 | 951 | func (x *SpawnProcessRequest) GetAutoRestart() bool { 952 | if x != nil { 953 | return x.AutoRestart 954 | } 955 | return false 956 | } 957 | 958 | func (x *SpawnProcessRequest) GetCwd() string { 959 | if x != nil { 960 | return x.Cwd 961 | } 962 | return "" 963 | } 964 | 965 | func (x *SpawnProcessRequest) GetCronRestart() string { 966 | if x != nil { 967 | return x.CronRestart 968 | } 969 | return "" 970 | } 971 | 972 | type SpawnProcessResponse struct { 973 | state protoimpl.MessageState 974 | sizeCache protoimpl.SizeCache 975 | unknownFields protoimpl.UnknownFields 976 | 977 | Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` 978 | } 979 | 980 | func (x *SpawnProcessResponse) Reset() { 981 | *x = SpawnProcessResponse{} 982 | if protoimpl.UnsafeEnabled { 983 | mi := &file_process_proto_msgTypes[12] 984 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 985 | ms.StoreMessageInfo(mi) 986 | } 987 | } 988 | 989 | func (x *SpawnProcessResponse) String() string { 990 | return protoimpl.X.MessageStringOf(x) 991 | } 992 | 993 | func (*SpawnProcessResponse) ProtoMessage() {} 994 | 995 | func (x *SpawnProcessResponse) ProtoReflect() protoreflect.Message { 996 | mi := &file_process_proto_msgTypes[12] 997 | if protoimpl.UnsafeEnabled && x != nil { 998 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 999 | if ms.LoadMessageInfo() == nil { 1000 | ms.StoreMessageInfo(mi) 1001 | } 1002 | return ms 1003 | } 1004 | return mi.MessageOf(x) 1005 | } 1006 | 1007 | // Deprecated: Use SpawnProcessResponse.ProtoReflect.Descriptor instead. 1008 | func (*SpawnProcessResponse) Descriptor() ([]byte, []int) { 1009 | return file_process_proto_rawDescGZIP(), []int{12} 1010 | } 1011 | 1012 | func (x *SpawnProcessResponse) GetSuccess() bool { 1013 | if x != nil { 1014 | return x.Success 1015 | } 1016 | return false 1017 | } 1018 | 1019 | var File_process_proto protoreflect.FileDescriptor 1020 | 1021 | var file_process_proto_rawDesc = []byte{ 1022 | 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 1023 | 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 1024 | 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 1025 | 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 1026 | 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 1027 | 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf7, 0x01, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x63, 1028 | 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 1029 | 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x39, 1030 | 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 1031 | 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 1032 | 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 1033 | 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x31, 0x0a, 0x06, 0x75, 0x70, 0x74, 1034 | 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 1035 | 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 1036 | 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 1037 | 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 1038 | 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 1039 | 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 1040 | 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 1041 | 0x72, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x69, 0x64, 1042 | 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x50, 0x69, 1043 | 0x64, 0x22, 0x95, 0x04, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 1044 | 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 1045 | 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 1046 | 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 1047 | 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 1048 | 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x12, 1049 | 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x70, 0x61, 1050 | 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 1051 | 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 1052 | 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 1053 | 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 1054 | 0x52, 0x0b, 0x61, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 1055 | 0x03, 0x63, 0x77, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x77, 0x64, 0x12, 1056 | 0x22, 0x0a, 0x0d, 0x70, 0x69, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 1057 | 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x69, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x50, 1058 | 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 1059 | 0x70, 0x61, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x6f, 0x67, 0x46, 1060 | 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x5f, 0x66, 1061 | 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 1062 | 0x65, 0x72, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x63, 1063 | 0x72, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 1064 | 0x09, 0x52, 0x0b, 0x63, 0x72, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x3e, 1065 | 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x61, 0x74, 0x18, 1066 | 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 1067 | 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 1068 | 0x70, 0x52, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x41, 0x74, 0x12, 0x32, 1069 | 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0e, 0x20, 1070 | 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x63, 1071 | 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x63, 0x53, 0x74, 0x61, 0x74, 1072 | 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 1073 | 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x74, 0x6f, 0x70, 0x53, 0x69, 0x67, 1074 | 0x6e, 0x61, 0x6c, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 1075 | 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6c, 0x6f, 0x67, 1076 | 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xd4, 0x02, 0x0a, 0x11, 0x41, 0x64, 1077 | 0x64, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 1078 | 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 1079 | 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 1080 | 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 1081 | 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 1082 | 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 1083 | 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 1084 | 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 1085 | 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 1086 | 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 1087 | 0x28, 0x08, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 1088 | 0x10, 0x0a, 0x03, 0x63, 0x77, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x77, 1089 | 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x69, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 1090 | 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x69, 0x64, 0x46, 0x69, 0x6c, 1091 | 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c, 1092 | 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x6f, 1093 | 0x67, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x65, 0x72, 0x72, 1094 | 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 1095 | 0x52, 0x0b, 0x65, 0x72, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 1096 | 0x0c, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0c, 0x20, 1097 | 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x72, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 1098 | 0x22, 0x28, 0x0a, 0x12, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 1099 | 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 1100 | 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x38, 0x0a, 0x12, 0x53, 0x74, 1101 | 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 1102 | 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 1103 | 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 1104 | 0x52, 0x02, 0x69, 0x64, 0x22, 0x2f, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x63, 1105 | 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 1106 | 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 1107 | 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xe6, 0x02, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x74, 0x50, 1108 | 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 1109 | 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 1110 | 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 1111 | 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 1112 | 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 1113 | 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x12, 1114 | 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x70, 0x61, 1115 | 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 1116 | 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 1117 | 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 1118 | 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 1119 | 0x52, 0x0b, 0x61, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 1120 | 0x03, 0x63, 0x77, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x77, 0x64, 0x12, 1121 | 0x22, 0x0a, 0x0d, 0x70, 0x69, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 1122 | 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x69, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x50, 1123 | 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 1124 | 0x70, 0x61, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x6f, 0x67, 0x46, 1125 | 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x5f, 0x66, 1126 | 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 1127 | 0x65, 0x72, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x63, 1128 | 0x72, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 1129 | 0x09, 0x52, 0x0b, 0x63, 0x72, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x22, 0x14, 1130 | 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 1131 | 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 1132 | 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x09, 0x70, 1133 | 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 1134 | 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x09, 1135 | 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x26, 0x0a, 0x14, 0x44, 0x65, 0x6c, 1136 | 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 1137 | 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 1138 | 0x64, 0x22, 0x31, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 1139 | 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 1140 | 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 1141 | 0x63, 0x65, 0x73, 0x73, 0x22, 0xd8, 0x01, 0x0a, 0x13, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x50, 0x72, 1142 | 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 1143 | 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 1144 | 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 1145 | 0x61, 0x72, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 1146 | 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x12, 0x27, 1147 | 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 1148 | 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 1149 | 0x62, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x5f, 1150 | 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 1151 | 0x75, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x77, 1152 | 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x77, 0x64, 0x12, 0x21, 0x0a, 0x0c, 1153 | 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0b, 0x20, 0x01, 1154 | 0x28, 0x09, 0x52, 0x0b, 0x63, 0x72, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x22, 1155 | 0x30, 0x0a, 0x14, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 1156 | 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 1157 | 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 1158 | 0x73, 0x32, 0xed, 0x03, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x61, 0x6e, 1159 | 0x61, 0x67, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x50, 0x72, 0x6f, 0x63, 0x65, 1160 | 0x73, 0x73, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x72, 1161 | 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 1162 | 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x22, 0x00, 0x12, 0x3c, 1163 | 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1a, 1164 | 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x63, 1165 | 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 1166 | 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0b, 1167 | 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x19, 0x2e, 0x70, 0x72, 1168 | 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 1169 | 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 1170 | 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 1171 | 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x63, 1172 | 0x65, 0x73, 0x73, 0x12, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6e, 0x64, 1173 | 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 1174 | 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x22, 0x00, 1175 | 0x12, 0x4c, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 1176 | 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 1177 | 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 1178 | 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 1179 | 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 1180 | 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x19, 0x2e, 1181 | 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 1182 | 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 1183 | 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 1184 | 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x0c, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x50, 1185 | 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 1186 | 0x70, 0x61, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 1187 | 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x70, 0x61, 0x77, 0x6e, 1188 | 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 1189 | 0x00, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 1190 | } 1191 | 1192 | var ( 1193 | file_process_proto_rawDescOnce sync.Once 1194 | file_process_proto_rawDescData = file_process_proto_rawDesc 1195 | ) 1196 | 1197 | func file_process_proto_rawDescGZIP() []byte { 1198 | file_process_proto_rawDescOnce.Do(func() { 1199 | file_process_proto_rawDescData = protoimpl.X.CompressGZIP(file_process_proto_rawDescData) 1200 | }) 1201 | return file_process_proto_rawDescData 1202 | } 1203 | 1204 | var file_process_proto_msgTypes = make([]protoimpl.MessageInfo, 13) 1205 | var file_process_proto_goTypes = []interface{}{ 1206 | (*ProcStatus)(nil), // 0: proto.ProcStatus 1207 | (*Process)(nil), // 1: proto.Process 1208 | (*AddProcessRequest)(nil), // 2: proto.AddProcessRequest 1209 | (*FindProcessRequest)(nil), // 3: proto.FindProcessRequest 1210 | (*StopProcessRequest)(nil), // 4: proto.StopProcessRequest 1211 | (*StopProcessResponse)(nil), // 5: proto.StopProcessResponse 1212 | (*StartProcessRequest)(nil), // 6: proto.StartProcessRequest 1213 | (*ListProcessRequest)(nil), // 7: proto.ListProcessRequest 1214 | (*ListProcessResponse)(nil), // 8: proto.ListProcessResponse 1215 | (*DeleteProcessRequest)(nil), // 9: proto.DeleteProcessRequest 1216 | (*DeleteProcessResponse)(nil), // 10: proto.DeleteProcessResponse 1217 | (*SpawnProcessRequest)(nil), // 11: proto.SpawnProcessRequest 1218 | (*SpawnProcessResponse)(nil), // 12: proto.SpawnProcessResponse 1219 | (*timestamppb.Timestamp)(nil), // 13: google.protobuf.Timestamp 1220 | (*durationpb.Duration)(nil), // 14: google.protobuf.Duration 1221 | } 1222 | var file_process_proto_depIdxs = []int32{ 1223 | 13, // 0: proto.ProcStatus.started_at:type_name -> google.protobuf.Timestamp 1224 | 14, // 1: proto.ProcStatus.uptime:type_name -> google.protobuf.Duration 1225 | 13, // 2: proto.Process.next_start_at:type_name -> google.protobuf.Timestamp 1226 | 0, // 3: proto.Process.proc_status:type_name -> proto.ProcStatus 1227 | 1, // 4: proto.ListProcessResponse.processes:type_name -> proto.Process 1228 | 2, // 5: proto.ProcessManager.AddProcess:input_type -> proto.AddProcessRequest 1229 | 6, // 6: proto.ProcessManager.StartProcess:input_type -> proto.StartProcessRequest 1230 | 4, // 7: proto.ProcessManager.StopProcess:input_type -> proto.StopProcessRequest 1231 | 3, // 8: proto.ProcessManager.FindProcess:input_type -> proto.FindProcessRequest 1232 | 9, // 9: proto.ProcessManager.DeleteProcess:input_type -> proto.DeleteProcessRequest 1233 | 7, // 10: proto.ProcessManager.ListProcess:input_type -> proto.ListProcessRequest 1234 | 11, // 11: proto.ProcessManager.SpawnProcess:input_type -> proto.SpawnProcessRequest 1235 | 1, // 12: proto.ProcessManager.AddProcess:output_type -> proto.Process 1236 | 1, // 13: proto.ProcessManager.StartProcess:output_type -> proto.Process 1237 | 5, // 14: proto.ProcessManager.StopProcess:output_type -> proto.StopProcessResponse 1238 | 1, // 15: proto.ProcessManager.FindProcess:output_type -> proto.Process 1239 | 10, // 16: proto.ProcessManager.DeleteProcess:output_type -> proto.DeleteProcessResponse 1240 | 8, // 17: proto.ProcessManager.ListProcess:output_type -> proto.ListProcessResponse 1241 | 12, // 18: proto.ProcessManager.SpawnProcess:output_type -> proto.SpawnProcessResponse 1242 | 12, // [12:19] is the sub-list for method output_type 1243 | 5, // [5:12] is the sub-list for method input_type 1244 | 5, // [5:5] is the sub-list for extension type_name 1245 | 5, // [5:5] is the sub-list for extension extendee 1246 | 0, // [0:5] is the sub-list for field type_name 1247 | } 1248 | 1249 | func init() { file_process_proto_init() } 1250 | func file_process_proto_init() { 1251 | if File_process_proto != nil { 1252 | return 1253 | } 1254 | if !protoimpl.UnsafeEnabled { 1255 | file_process_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 1256 | switch v := v.(*ProcStatus); i { 1257 | case 0: 1258 | return &v.state 1259 | case 1: 1260 | return &v.sizeCache 1261 | case 2: 1262 | return &v.unknownFields 1263 | default: 1264 | return nil 1265 | } 1266 | } 1267 | file_process_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 1268 | switch v := v.(*Process); i { 1269 | case 0: 1270 | return &v.state 1271 | case 1: 1272 | return &v.sizeCache 1273 | case 2: 1274 | return &v.unknownFields 1275 | default: 1276 | return nil 1277 | } 1278 | } 1279 | file_process_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 1280 | switch v := v.(*AddProcessRequest); i { 1281 | case 0: 1282 | return &v.state 1283 | case 1: 1284 | return &v.sizeCache 1285 | case 2: 1286 | return &v.unknownFields 1287 | default: 1288 | return nil 1289 | } 1290 | } 1291 | file_process_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { 1292 | switch v := v.(*FindProcessRequest); i { 1293 | case 0: 1294 | return &v.state 1295 | case 1: 1296 | return &v.sizeCache 1297 | case 2: 1298 | return &v.unknownFields 1299 | default: 1300 | return nil 1301 | } 1302 | } 1303 | file_process_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { 1304 | switch v := v.(*StopProcessRequest); i { 1305 | case 0: 1306 | return &v.state 1307 | case 1: 1308 | return &v.sizeCache 1309 | case 2: 1310 | return &v.unknownFields 1311 | default: 1312 | return nil 1313 | } 1314 | } 1315 | file_process_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { 1316 | switch v := v.(*StopProcessResponse); i { 1317 | case 0: 1318 | return &v.state 1319 | case 1: 1320 | return &v.sizeCache 1321 | case 2: 1322 | return &v.unknownFields 1323 | default: 1324 | return nil 1325 | } 1326 | } 1327 | file_process_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { 1328 | switch v := v.(*StartProcessRequest); i { 1329 | case 0: 1330 | return &v.state 1331 | case 1: 1332 | return &v.sizeCache 1333 | case 2: 1334 | return &v.unknownFields 1335 | default: 1336 | return nil 1337 | } 1338 | } 1339 | file_process_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { 1340 | switch v := v.(*ListProcessRequest); i { 1341 | case 0: 1342 | return &v.state 1343 | case 1: 1344 | return &v.sizeCache 1345 | case 2: 1346 | return &v.unknownFields 1347 | default: 1348 | return nil 1349 | } 1350 | } 1351 | file_process_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { 1352 | switch v := v.(*ListProcessResponse); i { 1353 | case 0: 1354 | return &v.state 1355 | case 1: 1356 | return &v.sizeCache 1357 | case 2: 1358 | return &v.unknownFields 1359 | default: 1360 | return nil 1361 | } 1362 | } 1363 | file_process_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { 1364 | switch v := v.(*DeleteProcessRequest); i { 1365 | case 0: 1366 | return &v.state 1367 | case 1: 1368 | return &v.sizeCache 1369 | case 2: 1370 | return &v.unknownFields 1371 | default: 1372 | return nil 1373 | } 1374 | } 1375 | file_process_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { 1376 | switch v := v.(*DeleteProcessResponse); i { 1377 | case 0: 1378 | return &v.state 1379 | case 1: 1380 | return &v.sizeCache 1381 | case 2: 1382 | return &v.unknownFields 1383 | default: 1384 | return nil 1385 | } 1386 | } 1387 | file_process_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { 1388 | switch v := v.(*SpawnProcessRequest); i { 1389 | case 0: 1390 | return &v.state 1391 | case 1: 1392 | return &v.sizeCache 1393 | case 2: 1394 | return &v.unknownFields 1395 | default: 1396 | return nil 1397 | } 1398 | } 1399 | file_process_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { 1400 | switch v := v.(*SpawnProcessResponse); i { 1401 | case 0: 1402 | return &v.state 1403 | case 1: 1404 | return &v.sizeCache 1405 | case 2: 1406 | return &v.unknownFields 1407 | default: 1408 | return nil 1409 | } 1410 | } 1411 | } 1412 | type x struct{} 1413 | out := protoimpl.TypeBuilder{ 1414 | File: protoimpl.DescBuilder{ 1415 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 1416 | RawDescriptor: file_process_proto_rawDesc, 1417 | NumEnums: 0, 1418 | NumMessages: 13, 1419 | NumExtensions: 0, 1420 | NumServices: 1, 1421 | }, 1422 | GoTypes: file_process_proto_goTypes, 1423 | DependencyIndexes: file_process_proto_depIdxs, 1424 | MessageInfos: file_process_proto_msgTypes, 1425 | }.Build() 1426 | File_process_proto = out.File 1427 | file_process_proto_rawDesc = nil 1428 | file_process_proto_goTypes = nil 1429 | file_process_proto_depIdxs = nil 1430 | } 1431 | --------------------------------------------------------------------------------