github.com/ergo-services/ergo@v1.999.224/apps/erlang/appmon.go (about)

     1  package erlang
     2  
     3  // TODO: https://github.com/erlang/otp/blob/master/lib/runtime_tools-1.13.1/src/appmon_info.erl
     4  
     5  import (
     6  	"time"
     7  
     8  	"github.com/ergo-services/ergo/etf"
     9  	"github.com/ergo-services/ergo/gen"
    10  	"github.com/ergo-services/ergo/lib"
    11  	"github.com/ergo-services/ergo/node"
    12  )
    13  
    14  type appMon struct {
    15  	gen.Server
    16  }
    17  
    18  type appMonState struct {
    19  	jobs map[etf.Atom][]jobDetails
    20  }
    21  
    22  type jobDetails struct {
    23  	name   etf.Atom
    24  	args   etf.List
    25  	sendTo etf.Pid
    26  }
    27  
    28  // Init initializes process state using arbitrary arguments
    29  func (am *appMon) Init(process *gen.ServerProcess, args ...etf.Term) error {
    30  	lib.Log("APP_MON: Init %#v", args)
    31  	from := args[0]
    32  	process.Link(from.(etf.Pid))
    33  	process.State = &appMonState{
    34  		jobs: make(map[etf.Atom][]jobDetails),
    35  	}
    36  	return nil
    37  }
    38  
    39  // HandleCast
    40  func (am *appMon) HandleCast(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
    41  	var appState *appMonState = process.State.(*appMonState)
    42  	lib.Log("APP_MON: HandleCast: %#v", message)
    43  	node := process.Env(node.EnvKeyNode).(node.Node)
    44  	switch message {
    45  	case "sendStat":
    46  
    47  		for cmd, jobs := range appState.jobs {
    48  			switch cmd {
    49  			case "app_ctrl":
    50  				// From ! {delivery, self(), Cmd, Aux, Result}
    51  				apps := node.WhichApplications()
    52  				for i := range jobs {
    53  					appList := make(etf.List, len(apps))
    54  					for ai, a := range apps {
    55  						appList[ai] = etf.Tuple{a.PID, etf.Atom(a.Name),
    56  							etf.Tuple{etf.Atom(a.Name), a.Description, a.Version},
    57  						}
    58  					}
    59  					delivery := etf.Tuple{etf.Atom("delivery"), process.Self(), cmd, jobs[i].name, appList}
    60  					process.Send(jobs[i].sendTo, delivery)
    61  				}
    62  
    63  			case "app":
    64  				for i := range jobs {
    65  					appTree := am.makeAppTree(process, jobs[i].name)
    66  					if appTree == nil {
    67  						continue
    68  					}
    69  					delivery := etf.Tuple{etf.Atom("delivery"), process.Self(), cmd, jobs[i].name, appTree}
    70  					process.Send(jobs[i].sendTo, delivery)
    71  				}
    72  
    73  			}
    74  		}
    75  
    76  		process.CastAfter(process.Self(), "sendStat", 2*time.Second)
    77  		return gen.ServerStatusOK
    78  
    79  	default:
    80  		switch m := message.(type) {
    81  		case etf.Tuple:
    82  			if len(m) == 5 {
    83  				// etf.Tuple{etf.Pid{Node:"erl-demo@127.0.0.1", Id:0x7c, Serial:0x0, Creation:0x1}, "app_ctrl", "demo@127.0.0.1", "true", etf.List{}}
    84  				job := jobDetails{
    85  					name:   m.Element(3).(etf.Atom),
    86  					args:   m.Element(5).(etf.List),
    87  					sendTo: m.Element(1).(etf.Pid),
    88  				}
    89  
    90  				if m.Element(4) == etf.Atom("true") {
    91  					// add new job
    92  					if len(appState.jobs) == 0 {
    93  						process.Cast(process.Self(), "sendStat")
    94  					}
    95  
    96  					if jobList, ok := appState.jobs[m.Element(2).(etf.Atom)]; ok {
    97  						for i := range jobList {
    98  							if jobList[i].name == job.name {
    99  								return gen.ServerStatusOK
   100  							}
   101  						}
   102  						jobList = append(jobList, job)
   103  						appState.jobs[m.Element(2).(etf.Atom)] = jobList
   104  					} else {
   105  						appState.jobs[m.Element(2).(etf.Atom)] = []jobDetails{job}
   106  					}
   107  
   108  				} else {
   109  					// remove a job
   110  					if jobList, ok := appState.jobs[m.Element(2).(etf.Atom)]; ok {
   111  						for i := range jobList {
   112  							if jobList[i].name == job.name {
   113  								jobList[i] = jobList[0]
   114  								jobList = jobList[1:]
   115  
   116  								if len(jobList) > 0 {
   117  									appState.jobs[m.Element(2).(etf.Atom)] = jobList
   118  								} else {
   119  									delete(appState.jobs, m.Element(2).(etf.Atom))
   120  								}
   121  								break
   122  							}
   123  						}
   124  					}
   125  
   126  					if len(appState.jobs) == 0 {
   127  						return gen.ServerStatusStop
   128  					}
   129  
   130  				}
   131  				return gen.ServerStatusOK
   132  			}
   133  		}
   134  	}
   135  
   136  	return gen.ServerStatusStop
   137  }
   138  
   139  func (am *appMon) makeAppTree(process gen.Process, app etf.Atom) etf.Tuple {
   140  	node := process.Env(node.EnvKeyNode).(node.Node)
   141  	appInfo, err := node.ApplicationInfo(string(app))
   142  	if err != nil {
   143  		return nil
   144  	}
   145  
   146  	resolver := make(map[etf.Pid]interface{})
   147  
   148  	tree := makeTree(process, resolver, appInfo.PID)
   149  	children := etf.List{etf.Tuple{appInfo.PID, appInfo.PID.String()}}
   150  	for p, n := range resolver {
   151  		children = append(children, etf.Tuple{p, n})
   152  	}
   153  
   154  	appTree := etf.Tuple{
   155  		appInfo.PID.String(), // pid or registered name
   156  		children,
   157  		tree,
   158  		etf.List{}, // TODO: links
   159  	}
   160  
   161  	return appTree
   162  }
   163  
   164  func makeTree(process gen.Process, resolver map[etf.Pid]interface{}, pid etf.Pid) etf.List {
   165  
   166  	pidProcess := process.ProcessByPid(pid)
   167  	if pidProcess == nil {
   168  		return etf.List{}
   169  	}
   170  	if name := pidProcess.Name(); name != "" {
   171  		resolver[pid] = name
   172  	} else {
   173  		resolver[pid] = pid.String()
   174  	}
   175  
   176  	tree := etf.List{}
   177  
   178  	pchildren, err := pidProcess.Children()
   179  	if err != nil {
   180  		return tree
   181  	}
   182  	for _, cp := range pchildren {
   183  		children := makeTree(process, resolver, cp)
   184  		child := etf.Tuple{resolver[pid], resolver[cp]}
   185  		tree = append(tree, child)
   186  		tree = append(tree, children...)
   187  	}
   188  
   189  	return tree
   190  }