2 * Copyright (C) 2018 "IoT.bzh"
3 * Author Sebastien Douheret <sebastien@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
27 common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib"
28 "gerrit.automotivelinux.org/gerrit/src/xds/xds-server/lib/xdsconfig"
29 "gerrit.automotivelinux.org/gerrit/src/xds/xds-server/lib/xsapiv1"
30 "github.com/syncthing/syncthing/lib/sync"
33 // Targets Represent a XDS targets
37 tgts map[string]*ITARGET
38 terminals map[string]*Terminals
41 // Mutex to make add/delete atomic
42 var tcMutex = sync.NewMutex()
48 // TargetsConstructor Create a new instance of Model Target
49 func TargetsConstructor(ctx *Context) *Targets {
50 file, _ := xdsconfig.TargetsConfigFilenameGet()
54 tgts: make(map[string]*ITARGET),
55 terminals: make(map[string]*Terminals),
59 // LoadConfig Load targets configuration from disk
60 func (t *Targets) LoadConfig() error {
61 var tgts []xsapiv1.TargetConfig
63 if t.fileOnDisk != "" {
64 t.Log.Infof("Use target config file: %s", t.fileOnDisk)
65 err := targetsConfigRead(t.fileOnDisk, &tgts)
67 if strings.HasPrefix(err.Error(), "EOF") {
68 t.Log.Warnf("Empty target config file")
69 } else if strings.HasPrefix(err.Error(), "No target config") {
70 t.Log.Warnf(err.Error())
76 t.Log.Warnf("Targets config filename not set")
80 t.Log.Infof("Loading initial targets config: %d targets found", len(tgts))
81 for _, tc := range tgts {
82 if _, err := t.createUpdate(tc, false, true, nil); err != nil {
90 // SaveConfig Save targets configuration to disk
91 func (t *Targets) SaveConfig() error {
92 if t.fileOnDisk == "" {
93 return fmt.Errorf("Targets config filename not set")
96 // FIXME: buffered save or avoid to write on disk each time
97 return targetsConfigWrite(t.fileOnDisk, t.getConfigArrUnsafe())
100 // ResolveID Complete a Target ID (helper for user that can use partial ID value)
101 func (t *Targets) ResolveID(id string) (string, error) {
107 for iid := range t.tgts {
108 if strings.HasPrefix(iid, id) {
109 match = append(match, iid)
115 } else if len(match) == 0 {
116 return id, fmt.Errorf("Unknown id")
118 return id, fmt.Errorf("Multiple IDs found %v", match)
121 // Get returns the target config or nil if not existing
122 func (t *Targets) Get(id string) *ITARGET {
126 tc, exist := t.tgts[id]
133 // GetConfigArr returns the config of all targets as an array
134 func (t *Targets) GetConfigArr() []xsapiv1.TargetConfig {
136 defer tcMutex.Unlock()
138 return t.getConfigArrUnsafe()
141 // getConfigArrUnsafe Same as GetConfigArr without mutex protection
142 func (t *Targets) getConfigArrUnsafe() []xsapiv1.TargetConfig {
143 conf := []xsapiv1.TargetConfig{}
144 for _, v := range t.tgts {
145 conf = append(conf, (*v).GetConfig())
150 // Add adds a new target
151 func (t *Targets) Add(newT xsapiv1.TargetConfig, sess *ClientSession) (*xsapiv1.TargetConfig, error) {
152 return t.createUpdate(newT, true, false, sess)
155 // CreateUpdate creates or update a target
156 func (t *Targets) createUpdate(newT xsapiv1.TargetConfig, create bool, initial bool, sess *ClientSession) (*xsapiv1.TargetConfig, error) {
160 defer tcMutex.Unlock()
163 if _, exist := t.tgts[newT.ID]; exist {
164 return nil, fmt.Errorf("ID already exists")
169 case xsapiv1.TypeTgtStandard:
170 tgt = NewTargetStandard(t.Context)
172 return nil, fmt.Errorf("Unsupported target type")
175 // Allocate a new UUID
177 newT.ID = tgt.NewUID("")
179 if !create && newT.ID == "" {
180 return nil, fmt.Errorf("Cannot update target with null ID")
185 if len(newT.ID) > 8 {
186 newT.Name += "_" + newT.ID[0:8]
188 newT.Name += "_" + newT.ID
192 // Call terminals constructor the first time
194 if _, exist := t.terminals[newT.ID]; !exist {
195 terms = TerminalsConstructor(t.Context)
196 t.terminals[newT.ID] = terms
198 terms = t.terminals[newT.ID]
201 var newTarget *xsapiv1.TargetConfig
204 if newTarget, err = tgt.Add(newT, terms); err != nil {
205 newT.Status = xsapiv1.StatusTgtErrorConfig
206 log.Printf("ERROR Adding target: %v\n", err)
207 return newTarget, err
210 // Just update target config
211 if newTarget, err = tgt.Setup(newT, terms); err != nil {
212 newT.Status = xsapiv1.StatusTgtErrorConfig
213 log.Printf("ERROR Updating target: %v\n", err)
214 return newTarget, err
219 for _, tc := range newT.Terms {
220 _, err := t.CreateUpdateTerminal(newT.ID, tc, initial)
222 return newTarget, err
226 // Add to folders list
227 t.tgts[newT.ID] = &tgt
230 newTgt := tgt.GetConfig()
232 if err = t.events.Emit(xsapiv1.EVTTargetAdd, &newTgt, sess.ID); err != nil {
233 t.Log.Errorf("WS Emit EVTTargetAdd : %v", err)
237 // Save config on disk
239 if err := t.SaveConfig(); err != nil {
240 return newTarget, err
247 // Delete deletes a specific target
248 func (t *Targets) Delete(id string, sess *ClientSession) (xsapiv1.TargetConfig, error) {
252 defer tcMutex.Unlock()
254 tgc := xsapiv1.TargetConfig{}
255 tc, exist := t.tgts[id]
257 return tgc, fmt.Errorf("unknown id")
260 tgc = (*tc).GetConfig()
262 if err = (*tc).Delete(); err != nil {
268 // Save config on disk
271 // Notify target remove
272 if err = t.events.Emit(xsapiv1.EVTTargetRemove, &tgc, sess.ID); err != nil {
273 t.Log.Errorf("WS Emit EVTTargetRemove : %v", err)
283 // GetTerminalsArr Return list of existing terminals
284 func (t *Targets) GetTerminalsArr(targetID string) ([]xsapiv1.TerminalConfig, error) {
285 arr := []xsapiv1.TerminalConfig{}
287 tm, exist := t.terminals[targetID]
289 return arr, fmt.Errorf("unknown target id")
292 for _, tt := range (*tm).terms {
293 arr = append(arr, (*tt).GetConfig())
298 // GetTerminal Return info of a specific terminal
299 func (t *Targets) GetTerminal(targetID, termID string) (*ITERMINAL, error) {
300 tm, exist := t.terminals[targetID]
302 return nil, fmt.Errorf("unknown target id")
304 term, exist := (*tm).terms[termID]
306 return nil, fmt.Errorf("unknown terminal id")
311 // ResolveTerminalID Complete a Terminal ID (helper for user that can use partial ID value)
312 func (t *Targets) ResolveTerminalID(termID string) (string, error) {
314 return "", fmt.Errorf("unknown terminal id")
318 for _, tm := range t.terminals {
319 for tid := range tm.terms {
320 if strings.HasPrefix(tid, termID) {
321 match = append(match, tid)
328 } else if len(match) == 0 {
329 return termID, fmt.Errorf("Unknown id")
331 return termID, fmt.Errorf("Multiple IDs found %v", match)
334 // CreateUpdateTerminal Create or Update a target terminal definition
335 func (t *Targets) CreateUpdateTerminal(targetID string, tmCfg xsapiv1.TerminalConfig, initial bool) (*xsapiv1.TerminalConfig, error) {
337 var term *xsapiv1.TerminalConfig
339 iTerm, err := t.GetTerminal(targetID, tmCfg.ID)
340 if err != nil && strings.Contains(err.Error(), "unknown target") {
345 // Update terminal config
346 term = (*iTerm).UpdateConfig(tmCfg)
348 // Auto create a new terminal when needed
350 if term, err = t.terminals[targetID].New(tmCfg, targetID); err != nil {
356 // Save config on disk
357 if err := t.SaveConfig(); err != nil {
365 // DeleteTerminal Delete a target terminal definition
366 func (t *Targets) DeleteTerminal(targetID, termID string) (*xsapiv1.TerminalConfig, error) {
367 terms, exist := t.terminals[targetID]
369 return nil, fmt.Errorf("unknown target id")
372 term, err := (*terms).Free(termID)
377 // Save config on disk
378 if err := t.SaveConfig(); err != nil {
385 // OpenTerminal Open a target terminal
386 func (t *Targets) OpenTerminal(targetID, termID string, sess *ClientSession) (*xsapiv1.TerminalConfig, error) {
387 terms, exist := t.terminals[targetID]
389 return nil, fmt.Errorf("unknown target id")
392 return (*terms).Open(termID, sess)
395 // CloseTerminal Close a target terminal
396 func (t *Targets) CloseTerminal(targetID, termID string, sess *ClientSession) (*xsapiv1.TerminalConfig, error) {
397 terms, exist := t.terminals[targetID]
399 return nil, fmt.Errorf("unknown target id")
401 return (*terms).Close(termID, sess)
404 // ResizeTerminal Set size (row+col) of a target terminal
405 func (t *Targets) ResizeTerminal(targetID, termID string, cols, rows uint16, sess *ClientSession) (*xsapiv1.TerminalConfig, error) {
406 terms, exist := t.terminals[targetID]
408 return nil, fmt.Errorf("unknown target id")
410 return (*terms).Resize(termID, cols, rows, sess)
413 // SignalTerminal Send a signal to a target terminal
414 func (t *Targets) SignalTerminal(targetID, termID, sigNum string) error {
415 terms, exist := t.terminals[targetID]
417 return fmt.Errorf("unknown target id")
419 return (*terms).Signal(termID, sigNum)
426 // Use XML format and not json to be able to save/load all fields including
427 // ones that are masked in json (IOW defined with `json:"-"`)
428 type xmlTargets struct {
429 XMLName xml.Name `xml:"targets"`
430 Version string `xml:"version,attr"`
431 Targets []xsapiv1.TargetConfig `xml:"targets"`
434 // targetsConfigRead reads targets config from disk
435 func targetsConfigRead(file string, targets *[]xsapiv1.TargetConfig) error {
436 if !common.Exists(file) {
437 return fmt.Errorf("No target config file found (%s)", file)
441 defer ffMutex.Unlock()
443 fd, err := os.Open(file)
450 err = xml.NewDecoder(fd).Decode(&data)
452 *targets = data.Targets
457 // targetsConfigWrite writes targets config on disk
458 func targetsConfigWrite(file string, targets []xsapiv1.TargetConfig) error {
460 defer ffMutex.Unlock()
462 fd, err := os.OpenFile(file, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
473 enc := xml.NewEncoder(fd)
475 return enc.Encode(data)