func (d *serviceData) GetLocalInfo() *service.LocalInfo {
	ret := &service.LocalInfo{
		ClientID: d.config.client.config.ClientID(),
		Labels:   d.config.client.config.Labels(),
	}
	d.config.lock.RLock()
	defer d.config.lock.RUnlock()
	for s := range d.config.services {
		if s != "system" {
			ret.Services = append(ret.Services, s)
		}
	}
	return ret
}
GetLocalInfo implements service.Context.
package client
import (
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"sync"
	"time"
	log "github.com/golang/glog"
	"github.com/golang/protobuf/proto"
	"github.com/google/fleetspeak/fleetspeak/src/client/service"
	"github.com/google/fleetspeak/fleetspeak/src/common"
	fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak"
)
const inboxSize = 100
var protoTexter = proto.TextMarshaler{ExpandAny: true}
type serviceConfiguration struct {
	services  map[string]*serviceData
	lock      sync.RWMutex  
	client    *Client
	factories map[string]service.Factory  
}
func (c *serviceConfiguration) ProcessMessage(ctx context.Context, m *fspb.Message) error {
	c.lock.RLock()
	target := c.services[m.Destination.ServiceName]
	c.lock.RUnlock()
	if target == nil {
		return fmt.Errorf("destination service not installed")
	}
	select {
	case target.inbox <- m:
		target.countLock.Lock()
		target.acceptCount++
		target.countLock.Unlock()
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}
func (c *serviceConfiguration) InstallSignedService(sd *fspb.SignedClientServiceConfig) error {
	var cfg fspb.ClientServiceConfig
	if err := proto.Unmarshal(sd.ServiceConfig, &cfg); err != nil {
		return fmt.Errorf("Unable to parse service config [%v], ignoring: %v", sd.Signature, err)
	}
ll:
	for _, l := range cfg.RequiredLabels {
		if l.ServiceName == "client" {
			for _, cl := range c.client.cfg.ClientLabels {
				if cl.Label == l.Label {
					continue ll
				}
			}
			return fmt.Errorf("service config [%s] requires label %v", cfg.Name, l)
		}
	}
	if err := c.InstallService(&cfg, sd.Signature); err != nil {
		return fmt.Errorf("installing [%s]: %w", cfg.Name, err)
	}
	return nil
}
func validateServiceName(sname string) error {
	if sname == "" || sname == "system" || sname == "client" {
		return fmt.Errorf("illegal service name [%v]", sname)
	}
	return nil
}
func (c *serviceConfiguration) InstallService(cfg *fspb.ClientServiceConfig, sig []byte) error {
	if err := validateServiceName(cfg.Name); err != nil {
		return fmt.Errorf("can't install service: %v", err)
	}
	f := c.factories[cfg.Factory]
	if f == nil {
		return fmt.Errorf("factory not found [%v]", cfg.Factory)
	}
	s, err := f(cfg)
	if err != nil {
		return fmt.Errorf("unable to create service: %v", err)
	}
	d := serviceData{
		config:        c,
		name:          cfg.Name,
		serviceConfig: cfg,
		service:       s,
		inbox:         make(chan *fspb.Message, inboxSize),
	}
	if err := d.start(); err != nil {
		return fmt.Errorf("unable to start service: %v", err)
	}
	d.working.Add(1)
	go d.processingLoop()
	c.lock.Lock()
	old := c.services[cfg.Name]
	c.services[cfg.Name] = &d
	c.client.config.RecordRunningService(cfg.Name, sig)
	c.lock.Unlock()
	if old != nil {
		old.stop()
	}
	log.Infof("Started service %v with config:\n%s", cfg.Name, protoTexter.Text(cfg))
	return nil
}
func (c *serviceConfiguration) removeService(sname string) (*serviceData, error) {
	c.lock.Lock()
	defer c.lock.Unlock()
	srv := c.services[sname]
	if srv == nil {
		return nil, fmt.Errorf("falied to remove non-existent service: %v", sname)
	}
	delete(c.services, sname)
	return srv, nil
}
func (c *serviceConfiguration) RestartService(sname string) error {
	if err := validateServiceName(sname); err != nil {
		return fmt.Errorf("can't restart service: %v", err)
	}
	srv, err := c.removeService(sname)
	if err != nil {
		return err
	}
	srv.stop()
	if err := c.InstallService(srv.serviceConfig, nil); err != nil {
		return fmt.Errorf("can't reinstall service '%s' on restart: %v", sname, err)
	}
	return nil
}
func (c *serviceConfiguration) Counts() (accepted, processed map[string]uint64) {
	am := make(map[string]uint64)
	pm := make(map[string]uint64)
	c.lock.RLock()
	defer c.lock.RUnlock()
	for _, sd := range c.services {
		sd.countLock.Lock()
		a, p := sd.acceptCount, sd.processedCount
		sd.countLock.Unlock()
		am[sd.name] = a
		pm[sd.name] = p
	}
	return am, pm
}
func (c *serviceConfiguration) Stop() {
	c.lock.Lock()
	defer c.lock.Unlock()
	for _, sd := range c.services {
		sd.stop()
	}
	c.services = make(map[string]*serviceData)
}
type serviceData struct {
	config        *serviceConfiguration
	name          string
	serviceConfig *fspb.ClientServiceConfig
	working       sync.WaitGroup
	service       service.Service
	inbox         chan *fspb.Message
	countLock                   sync.Mutex  
	acceptCount, processedCount uint64
}
func (d *serviceData) Send(ctx context.Context, am service.AckMessage) error {
	m := am.M
	id := d.config.client.config.ClientID().Bytes()
	m.Source = &fspb.Address{
		ClientId:    id,
		ServiceName: d.name,
	}
	if len(m.SourceMessageId) == 0 {
		b := make([]byte, 16)
		if _, err := rand.Read(b); err != nil {
			return fmt.Errorf("unable to create random source message id: %v", err)
		}
		m.SourceMessageId = b
	}
	return d.config.client.ProcessMessage(ctx, am)
}