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 }