blob: caccde2abb1bc2bad1ac596a4b64d2cfa212dadb [file] [log] [blame]
package impl
import (
"bytes"
"errors"
"os/exec"
"runtime"
"strings"
"veyron.io/veyron/veyron/services/mgmt/profile"
"veyron.io/veyron/veyron2/services/mgmt/build"
"veyron.io/veyron/veyron2/services/mgmt/device"
)
// computeDeviceProfile generates a description of the runtime
// environment (supported file format, OS, architecture, libraries) of
// the host device.
//
// TODO(jsimsa): Avoid computing the host device description from
// scratch if a recent cached copy exists.
func computeDeviceProfile() (*profile.Specification, error) {
result := profile.Specification{}
// Find out what the supported file format, operating system, and
// architecture is.
switch runtime.GOOS {
case "darwin":
result.Format = build.MACH
result.OS = build.Darwin
case "linux":
result.Format = build.ELF
result.OS = build.Linux
case "windows":
result.Format = build.PE
result.OS = build.Windows
default:
return nil, errors.New("Unsupported operating system: " + runtime.GOOS)
}
switch runtime.GOARCH {
case "amd64":
result.Arch = build.AMD64
case "arm":
result.Arch = build.ARM
case "x86":
result.Arch = build.X86
default:
return nil, errors.New("Unsupported hardware architecture: " + runtime.GOARCH)
}
// Find out what the installed dynamically linked libraries are.
switch runtime.GOOS {
case "linux":
// For Linux, we identify what dynamically linked libraries are
// install by parsing the output of "ldconfig -p".
command := exec.Command("ldconfig", "-p")
output, err := command.CombinedOutput()
if err != nil {
return nil, err
}
buf := bytes.NewBuffer(output)
// Throw away the first line of output from ldconfig.
if _, err := buf.ReadString('\n'); err != nil {
return nil, errors.New("Could not identify libraries.")
}
// Extract the library name and version from every subsequent line.
result.Libraries = make(map[profile.Library]struct{})
line, err := buf.ReadString('\n')
for err == nil {
words := strings.Split(strings.Trim(line, " \t\n"), " ")
if len(words) > 0 {
tokens := strings.Split(words[0], ".so")
if len(tokens) != 2 {
return nil, errors.New("Could not identify library: " + words[0])
}
name := strings.TrimPrefix(tokens[0], "lib")
major, minor := "", ""
tokens = strings.SplitN(tokens[1], ".", 3)
if len(tokens) >= 2 {
major = tokens[1]
}
if len(tokens) >= 3 {
minor = tokens[2]
}
result.Libraries[profile.Library{Name: name, MajorVersion: major, MinorVersion: minor}] = struct{}{}
}
line, err = buf.ReadString('\n')
}
case "darwin":
// TODO(jsimsa): Implement.
case "windows":
// TODO(jsimsa): Implement.
default:
return nil, errors.New("Unsupported operating system: " + runtime.GOOS)
}
return &result, nil
}
// getProfile gets a profile description for the given profile.
//
// TODO(jsimsa): Avoid retrieving the list of known profiles from a
// remote server if a recent cached copy exists.
func getProfile(name string) (*profile.Specification, error) {
// TODO(jsimsa): This function assumes the existence of a profile
// server from which the profiles can be retrieved. The profile
// server is a work in progress. When it exists, the commented out
// code below should work.
var profile profile.Specification
/*
client, err := r.NewClient()
if err != nil {
vlog.Errorf("NewClient() failed: %v", err)
return nil, err
}
defer client.Close()
server := // TODO
method := "Specification"
inputs := make([]interface{}, 0)
call, err := client.StartCall(server + "/" + name, method, inputs)
if err != nil {
vlog.Errorf("StartCall(%s, %q, %v) failed: %v\n", server + "/" + name, method, inputs, err)
return nil, err
}
if err := call.Finish(&profiles); err != nil {
vlog.Errorf("Finish(%v) failed: %v\n", &profiles, err)
return nil, err
}
*/
return &profile, nil
}
// getKnownProfiles gets a list of description for all publicly known
// profiles.
//
// TODO(jsimsa): Avoid retrieving the list of known profiles from a
// remote server if a recent cached copy exists.
func getKnownProfiles() ([]profile.Specification, error) {
// TODO(jsimsa): This function assumes the existence of a profile
// server from which a list of known profiles can be retrieved. The
// profile server is a work in progress. When it exists, the
// commented out code below should work.
knownProfiles := make([]profile.Specification, 0)
/*
client, err := r.NewClient()
if err != nil {
vlog.Errorf("NewClient() failed: %v\n", err)
return nil, err
}
defer client.Close()
server := // TODO
method := "List"
inputs := make([]interface{}, 0)
call, err := client.StartCall(server, method, inputs)
if err != nil {
vlog.Errorf("StartCall(%s, %q, %v) failed: %v\n", server, method, inputs, err)
return nil, err
}
if err := call.Finish(&knownProfiles); err != nil {
vlog.Errorf("Finish(&knownProfile) failed: %v\n", err)
return nil, err
}
*/
return knownProfiles, nil
}
// matchProfiles inputs a profile that describes the host device and a
// set of publicly known profiles and outputs a device description that
// identifies the publicly known profiles supported by the host device.
func matchProfiles(p *profile.Specification, known []profile.Specification) device.Description {
result := device.Description{Profiles: make(map[string]struct{})}
loop:
for _, profile := range known {
if profile.Format != p.Format {
continue
}
if profile.OS != p.OS {
continue
}
if profile.Arch != p.Arch {
continue
}
for library := range profile.Libraries {
// Current implementation requires exact library name and version match.
if _, found := p.Libraries[library]; !found {
continue loop
}
}
result.Profiles[profile.Label] = struct{}{}
}
return result
}