<commit_before>package engine

import (
	"bytes"
	"fmt"

	"github.com/agl/ed25519"
	"github.com/keybase/client/go/libkb"
	"golang.org/x/crypto/nacl/box"
)

// SelfProof = true: runs the detkey engine and uses the eddsa key as
// the signing key.  This is currently only used for testing to
// generate a fake users who only has a detkey, but perhaps it
// will be useful for something else...
type DetKeyArgs struct {
	Tsp         libkb.PassphraseStream
	SelfProof   bool
	Me          *libkb.User
	SigningKey  libkb.GenericKey
	EldestKeyID libkb.KID
}

type DetKeyEngine struct {
	arg         *DetKeyArgs
	newEddsaKey libkb.GenericKey
}

func NewDetKeyEngine(arg *DetKeyArgs) *DetKeyEngine {
	return &DetKeyEngine{arg: arg}
}

func (d *DetKeyEngine) Name() string {
	return "DetKey"
}

func (d *DetKeyEngine) RequiredUIs() []libkb.UIKind {
	return nil
}

func (d *DetKeyEngine) SubConsumers() []libkb.UIConsumer {
	return nil
}

func (e *DetKeyEngine) GetPrereqs() EnginePrereqs { return EnginePrereqs{} }

// Run runs the detkey engine.
func (d *DetKeyEngine) Run(ctx *Context) error {
	if err := d.eddsa(d.arg.Tsp); err != nil {
		return fmt.Errorf("eddsa error: %s", err)
	}

	// turn off self proof
	d.arg.SelfProof = false

	if err := d.dh(d.arg.Tsp.DHSeed()); err != nil {
		return fmt.Errorf("dh error: %s", err)
	}
	return nil
}

func (d *DetKeyEngine) eddsa(tpk libkb.PassphraseStream) error {
	serverHalf, err := libkb.RandBytes(len(tpk.EdDSASeed()))
	if err != nil {
		return err
	}
	key, err := GenSigningDetKey(tpk, serverHalf)
	if err != nil {
		return err
	}

	var signingKey libkb.GenericKey

	if !d.arg.SelfProof {
		signingKey = d.arg.SigningKey
	}
	d.newEddsaKey = key

	return d.push(key, signingKey, serverHalf, libkb.NACL_EDDSA_EXPIRE_IN, true)
}

func GenSigningDetKey(tpk libkb.PassphraseStream, serverHalf []byte) (gkey libkb.GenericKey, err error) {
	xseed, err := serverSeed(tpk.EdDSASeed(), serverHalf)
	if err != nil {
		return nil, err
	}
	pub, priv, err := ed25519.GenerateKey(bytes.NewBuffer(xseed))
	if err != nil {
		return nil, err
	}

	G.Log.Debug("detkey[eddsa] pub:        %x", *pub)
	G.Log.Debug("detkey[eddsa] priv:       %x", *priv)

	var key libkb.NaclSigningKeyPair
	copy(key.Public[:], (*pub)[:])
	key.Private = &libkb.NaclSigningKeyPrivate{}
	copy(key.Private[:], (*priv)[:])

	return key, nil
}

func (d *DetKeyEngine) dh(seed []byte) error {
	serverHalf, err := libkb.RandBytes(len(seed))
	if err != nil {
		return err
	}
	xseed, err := serverSeed(seed, serverHalf)
	if err != nil {
		return err
	}
	pub, priv, err := box.GenerateKey(bytes.NewBuffer(xseed))
	if err != nil {
		return err
	}

	G.Log.Debug("detkey[dh] serverHalf: %x", serverHalf)
	G.Log.Debug("detkey[dh] pub:        %x", *pub)
	G.Log.Debug("detkey[dh] priv:       %x", *priv)

	var key libkb.NaclDHKeyPair
	copy(key.Public[:], (*pub)[:])
	key.Private = &libkb.NaclDHKeyPrivate{}
	copy(key.Private[:], (*priv)[:])

	return d.push(key, d.newEddsaKey, serverHalf, libkb.NACL_DH_EXPIRE_IN, false)
}

func serverSeed(seed, serverHalf []byte) (newseed []byte, err error) {
	newseed = make([]byte, len(seed))
	libkb.XORBytes(newseed, seed, serverHalf)
	return newseed, nil
}

func (d *DetKeyEngine) push(key libkb.GenericKey, signing libkb.GenericKey, serverHalf []byte, expire int, sibkey bool) error {
	dev := libkb.NewWebDevice()
	if dev == nil {
		return libkb.ErrCannotGenerateDevice
	}
	g := libkb.Delegator{
		NewKey:      key,
		Sibkey:      sibkey,
		Expire:      expire,
		ExistingKey: signing,
		ServerHalf:  serverHalf,
		Me:          d.arg.Me,
		Device:      dev,
	}
	return g.Run()
}
<commit_msg>Close #316<commit_after>package engine

import (
	"bytes"
	"fmt"

	"github.com/agl/ed25519"
	"github.com/keybase/client/go/libkb"
	"golang.org/x/crypto/nacl/box"
)

// SelfProof = true: runs the detkey engine and uses the eddsa key as
// the signing key.  This is currently only used for testing to
// generate a fake users who only has a detkey, but perhaps it
// will be useful for something else...
type DetKeyArgs struct {
	Tsp         libkb.PassphraseStream
	SelfProof   bool
	Me          *libkb.User
	SigningKey  libkb.GenericKey
	EldestKeyID libkb.KID
}

type DetKeyEngine struct {
	arg         *DetKeyArgs
	newEddsaKey libkb.GenericKey
	dev         *libkb.Device
}

func NewDetKeyEngine(arg *DetKeyArgs) *DetKeyEngine {
	return &DetKeyEngine{arg: arg}
}

func (d *DetKeyEngine) Name() string {
	return "DetKey"
}

func (d *DetKeyEngine) RequiredUIs() []libkb.UIKind {
	return nil
}

func (d *DetKeyEngine) SubConsumers() []libkb.UIConsumer {
	return nil
}

func (e *DetKeyEngine) GetPrereqs() EnginePrereqs { return EnginePrereqs{} }

// Run runs the detkey engine.
func (d *DetKeyEngine) Run(ctx *Context) error {

	d.dev = libkb.NewWebDevice()

	if err := d.eddsa(d.arg.Tsp); err != nil {
		return fmt.Errorf("eddsa error: %s", err)
	}

	// turn off self proof
	d.arg.SelfProof = false

	if err := d.dh(d.arg.Tsp.DHSeed()); err != nil {
		return fmt.Errorf("dh error: %s", err)
	}
	return nil
}

func (d *DetKeyEngine) eddsa(tpk libkb.PassphraseStream) error {
	serverHalf, err := libkb.RandBytes(len(tpk.EdDSASeed()))
	if err != nil {
		return err
	}
	key, err := GenSigningDetKey(tpk, serverHalf)
	if err != nil {
		return err
	}

	var signingKey libkb.GenericKey

	if !d.arg.SelfProof {
		signingKey = d.arg.SigningKey
	}
	d.newEddsaKey = key

	return d.push(key, signingKey, serverHalf, libkb.NACL_EDDSA_EXPIRE_IN, true)
}

func GenSigningDetKey(tpk libkb.PassphraseStream, serverHalf []byte) (gkey libkb.GenericKey, err error) {
	xseed, err := serverSeed(tpk.EdDSASeed(), serverHalf)
	if err != nil {
		return nil, err
	}
	pub, priv, err := ed25519.GenerateKey(bytes.NewBuffer(xseed))
	if err != nil {
		return nil, err
	}

	G.Log.Debug("detkey[eddsa] pub:        %x", *pub)
	G.Log.Debug("detkey[eddsa] priv:       %x", *priv)

	var key libkb.NaclSigningKeyPair
	copy(key.Public[:], (*pub)[:])
	key.Private = &libkb.NaclSigningKeyPrivate{}
	copy(key.Private[:], (*priv)[:])

	return key, nil
}

func (d *DetKeyEngine) dh(seed []byte) error {
	serverHalf, err := libkb.RandBytes(len(seed))
	if err != nil {
		return err
	}
	xseed, err := serverSeed(seed, serverHalf)
	if err != nil {
		return err
	}
	pub, priv, err := box.GenerateKey(bytes.NewBuffer(xseed))
	if err != nil {
		return err
	}

	G.Log.Debug("detkey[dh] serverHalf: %x", serverHalf)
	G.Log.Debug("detkey[dh] pub:        %x", *pub)
	G.Log.Debug("detkey[dh] priv:       %x", *priv)

	var key libkb.NaclDHKeyPair
	copy(key.Public[:], (*pub)[:])
	key.Private = &libkb.NaclDHKeyPrivate{}
	copy(key.Private[:], (*priv)[:])

	return d.push(key, d.newEddsaKey, serverHalf, libkb.NACL_DH_EXPIRE_IN, false)
}

func serverSeed(seed, serverHalf []byte) (newseed []byte, err error) {
	newseed = make([]byte, len(seed))
	libkb.XORBytes(newseed, seed, serverHalf)
	return newseed, nil
}

func (d *DetKeyEngine) push(key libkb.GenericKey, signing libkb.GenericKey, serverHalf []byte, expire int, sibkey bool) error {
	if d.dev == nil {
		return libkb.ErrCannotGenerateDevice
	}
	g := libkb.Delegator{
		NewKey:      key,
		Sibkey:      sibkey,
		Expire:      expire,
		ExistingKey: signing,
		ServerHalf:  serverHalf,
		Me:          d.arg.Me,
		Device:      d.dev,
	}
	return g.Run()
}
<|endoftext|>


<commit_before>// Copyright 2019 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package status

import (
	"errors"
	"os"
	"path/filepath"
	"strings"

	"github.com/keybase/client/go/install"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
)

func GetCurrentStatus(mctx libkb.MetaContext) (res keybase1.CurrentStatus, err error) {
	cr := mctx.G().Env.GetConfig()
	if cr == nil {
		return res, nil
	}
	res.Configured = true
	if uid := cr.GetUID(); uid.Exists() {
		res.Registered = true
		res.User = libkb.NewUserThin(cr.GetUsername().String(), uid).Export()
	}
	res.SessionIsValid = mctx.ActiveDevice().Valid()
	res.LoggedIn = res.SessionIsValid
	return res, nil
}

func GetExtendedStatus(mctx libkb.MetaContext) (res keybase1.ExtendedStatus, err error) {
	mctx = mctx.WithLogTag("EXTSTATUS")
	defer mctx.TraceTimed("GetExtendedStatus", func() error { return err })()
	g := mctx.G()

	res.Standalone = g.Env.GetStandalone()
	res.LogDir = g.Env.GetLogDir()
	res.Clients = libkb.GetClientStatus(mctx)

	if err = g.GetFullSelfer().WithSelf(mctx.Ctx(), func(me *libkb.User) error {
		ckf := me.GetComputedKeyFamily()
		if ckf == nil {
			return errors.New("Couldn't load key family")
		}
		device, err := ckf.GetCurrentDevice(g)
		if err != nil {
			mctx.Debug("| GetCurrentDevice failed: %s", err)
			res.DeviceErr = &keybase1.LoadDeviceErr{Where: "ckf.GetCurrentDevice", Desc: err.Error()}
		} else {
			res.Device = device.ProtExport()
		}

		ss := g.SecretStore()
		if me != nil && ss != nil {
			s, err := ss.RetrieveSecret(mctx, me.GetNormalizedName())
			if err == nil && !s.IsNil() {
				res.StoredSecret = true
			}
		}
		return nil
	}); err != nil {
		mctx.Debug("| could not load me user: %s", err)
		res.DeviceErr = &keybase1.LoadDeviceErr{Where: "libkb.LoadMe", Desc: err.Error()}
	}

	// cached device key status
	_, _, _, sk, ek := g.ActiveDevice.AllFields()
	res.DeviceSigKeyCached = sk != nil
	res.DeviceEncKeyCached = ek != nil

	ad := mctx.ActiveDevice()
	// cached paper key status
	if pk := ad.ProvisioningKey(mctx); pk != nil {
		if pk.EncryptionKey() != nil {
			res.PaperEncKeyCached = true
		}
		if pk.SigningKey() != nil {
			res.PaperSigKeyCached = true
		}
	}

	psc := ad.PassphraseStreamCache()
	res.PassphraseStreamCached = psc.ValidPassphraseStream()
	res.TsecCached = psc.ValidTsec()
	res.SecretPromptSkip = mctx.ActiveDevice().SecretPromptCancelTimer().WasRecentlyCanceled(mctx)

	current, all, err := libkb.GetAllProvisionedUsernames(mctx)
	if err != nil {
		mctx.Debug("| died in GetAllProvisionedUsernames()")
		return res, err
	}
	res.DefaultUsername = current.String()
	p := make([]string, len(all))
	for i, u := range all {
		p[i] = u.String()
	}
	res.ProvisionedUsernames = p

	accounts, err := libkb.GetConfiguredAccountsFromProvisionedUsernames(mctx, mctx.G().SecretStore(), current, all)
	if err != nil {
		mctx.Debug("| died in GetConfiguredAccounts()")
		return res, err
	}
	res.ConfiguredAccounts = accounts

	res.PlatformInfo = getPlatformInfo()
	res.DefaultDeviceID = g.Env.GetDeviceID()
	res.RememberPassphrase = g.Env.GetRememberPassphrase(g.Env.GetUsername())
	// DeviceEK status, can be nil if user is logged out
	deviceEKStorage := g.GetDeviceEKStorage()
	if deviceEKStorage != nil {
		dekNames, err := deviceEKStorage.ListAllForUser(mctx)
		if err != nil {
			return res, err
		}
		res.DeviceEkNames = dekNames
	}

	res.LocalDbStats = strings.Split(g.LocalDb.Stats(), "\n")
	res.LocalChatDbStats = strings.Split(g.LocalChatDb.Stats(), "\n")
	if cacheSizeInfo, err := CacheSizeInfo(g); err == nil {
		res.CacheDirSizeInfo = cacheSizeInfo
	}

	if g.ConnectionManager != nil {
		xp := g.ConnectionManager.LookupByClientType(keybase1.ClientType_KBFS)
		if xp == nil {
			mctx.Debug("| KBFS stats not available")
		} else {
			cli := &keybase1.SimpleFSClient{
				Cli: rpc.NewClient(
					xp, libkb.NewContextifiedErrorUnwrapper(g), nil),
			}
			stats, err := cli.SimpleFSGetStats(mctx.Ctx())
			if err != nil {
				mctx.Debug("| KBFS stats error: %+v", err)
			} else {
				res.LocalBlockCacheDbStats = stats.BlockCacheDbStats
				res.LocalSyncCacheDbStats = stats.SyncCacheDbStats
			}
		}
	}

	if g.UIRouter != nil {
		uiMapping := map[string]int{}
		for k, v := range g.UIRouter.DumpUIs() {
			uiMapping[k.String()] = int(v)
		}
		res.UiRouterMapping = uiMapping
	}

	return res, nil
}

func GetConfig(mctx libkb.MetaContext, forkType keybase1.ForkType) (c keybase1.Config, err error) {
	serverURI, err := mctx.G().Env.GetServerURI()
	if err != nil {
		return c, err
	}

	c.ServerURI = serverURI
	c.RunMode = string(mctx.G().Env.GetRunMode())
	c.SocketFile, err = mctx.G().Env.GetSocketBindFile()
	if err != nil {
		return c, err
	}

	gpg := mctx.G().GetGpgClient()
	canExec, err := gpg.CanExec(mctx)
	if err == nil {
		c.GpgExists = canExec
		c.GpgPath = gpg.Path(mctx)
	}

	c.Version = libkb.VersionString()
	c.VersionShort = libkb.Version

	var v []string
	libkb.VersionMessage(func(s string) {
		v = append(v, s)
	})
	c.VersionFull = strings.Join(v, "\n")

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		c.Path = dir
	} else {
		mctx.Warning("Failed to get service path: %s", err)
	}

	realpath, err := libkb.CurrentBinaryRealpath()
	if err == nil {
		c.BinaryRealpath = realpath
	} else {
		mctx.Warning("Failed to get service realpath: %s", err)
	}

	c.ConfigPath = mctx.G().Env.GetConfigFilename()
	c.Label = mctx.G().Env.GetLabel()
	if forkType == keybase1.ForkType_AUTO {
		c.IsAutoForked = true
	}
	c.ForkType = forkType
	return c, nil
}

func GetFullStatus(mctx libkb.MetaContext) (status *keybase1.FullStatus, err error) {
	status = &keybase1.FullStatus{}
	// The client overrides this value when sending logs
	status.Client.Version = libkb.VersionString()
	status.ConfigPath = mctx.G().Env.GetConfigFilename()

	status.CurStatus, err = GetCurrentStatus(mctx)
	if err != nil {
		return nil, err
	}

	// Duplicate the username at top-level for backwards compatibility of
	// output.
	if status.CurStatus.User != nil {
		status.Username = status.CurStatus.User.Username
	}

	status.ExtStatus, err = GetExtendedStatus(mctx)
	if err != nil {
		return nil, err
	}

	// set service status
	if status.ExtStatus.Standalone {
		status.Service.Running = false
	} else {
		status.Service.Running = true
	}
	status.Service.Version = libkb.VersionString()

	// set kbfs status
	kbfsInstalledVersion, err := install.KBFSBundleVersion(mctx.G(), "")
	if err == nil {
		status.Kbfs.InstalledVersion = kbfsInstalledVersion
	}
	if kbfs := GetFirstClient(status.ExtStatus.Clients, keybase1.ClientType_KBFS); kbfs != nil {
		status.Kbfs.Version = kbfs.Version
		status.Kbfs.Running = true
		// This just gets the mountpoint from the environment; the
		// user could have technically passed a different mountpoint
		// to KBFS on macOS or Linux.  TODO(KBFS-2723): fetch the
		// actual mountpoint with a new RPC from KBFS.
		mountDir, err := mctx.G().Env.GetMountDir()
		if err != nil {
			return nil, err
		}
		status.Kbfs.Mount = mountDir
	} else {
		status.Kbfs.Version = kbfsInstalledVersion
	}

	// set desktop status
	if desktop := GetFirstClient(status.ExtStatus.Clients, keybase1.ClientType_GUI_MAIN); desktop != nil {
		status.Desktop.Running = true
		status.Desktop.Version = desktop.Version
	}

	// set log paths
	status.Service.Log = getServiceLog(mctx, status.ExtStatus.LogDir)
	status.Service.EkLog = filepath.Join(status.ExtStatus.LogDir, libkb.EKLogFileName)
	status.Service.PerfLog = filepath.Join(status.ExtStatus.LogDir, libkb.PerfLogFileName)
	status.Kbfs.Log = filepath.Join(status.ExtStatus.LogDir, libkb.KBFSLogFileName)
	status.Desktop.Log = filepath.Join(status.ExtStatus.LogDir, libkb.GUILogFileName)
	status.Updater.Log = filepath.Join(status.ExtStatus.LogDir, libkb.UpdaterLogFileName)
	status.Start.Log = filepath.Join(status.ExtStatus.LogDir, libkb.StartLogFileName)
	status.Git.Log = filepath.Join(status.ExtStatus.LogDir, libkb.GitLogFileName)

	// set anything os-specific
	if err := osSpecific(mctx, status); err != nil {
		return nil, err
	}
	return status, nil
}
<commit_msg>log install.KBFSBundleVersion error (#22700)<commit_after>// Copyright 2019 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package status

import (
	"errors"
	"os"
	"path/filepath"
	"strings"

	"github.com/keybase/client/go/install"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
)

func GetCurrentStatus(mctx libkb.MetaContext) (res keybase1.CurrentStatus, err error) {
	cr := mctx.G().Env.GetConfig()
	if cr == nil {
		return res, nil
	}
	res.Configured = true
	if uid := cr.GetUID(); uid.Exists() {
		res.Registered = true
		res.User = libkb.NewUserThin(cr.GetUsername().String(), uid).Export()
	}
	res.SessionIsValid = mctx.ActiveDevice().Valid()
	res.LoggedIn = res.SessionIsValid
	return res, nil
}

func GetExtendedStatus(mctx libkb.MetaContext) (res keybase1.ExtendedStatus, err error) {
	mctx = mctx.WithLogTag("EXTSTATUS")
	defer mctx.TraceTimed("GetExtendedStatus", func() error { return err })()
	g := mctx.G()

	res.Standalone = g.Env.GetStandalone()
	res.LogDir = g.Env.GetLogDir()
	res.Clients = libkb.GetClientStatus(mctx)

	if err = g.GetFullSelfer().WithSelf(mctx.Ctx(), func(me *libkb.User) error {
		ckf := me.GetComputedKeyFamily()
		if ckf == nil {
			return errors.New("Couldn't load key family")
		}
		device, err := ckf.GetCurrentDevice(g)
		if err != nil {
			mctx.Debug("| GetCurrentDevice failed: %s", err)
			res.DeviceErr = &keybase1.LoadDeviceErr{Where: "ckf.GetCurrentDevice", Desc: err.Error()}
		} else {
			res.Device = device.ProtExport()
		}

		ss := g.SecretStore()
		if me != nil && ss != nil {
			s, err := ss.RetrieveSecret(mctx, me.GetNormalizedName())
			if err == nil && !s.IsNil() {
				res.StoredSecret = true
			}
		}
		return nil
	}); err != nil {
		mctx.Debug("| could not load me user: %s", err)
		res.DeviceErr = &keybase1.LoadDeviceErr{Where: "libkb.LoadMe", Desc: err.Error()}
	}

	// cached device key status
	_, _, _, sk, ek := g.ActiveDevice.AllFields()
	res.DeviceSigKeyCached = sk != nil
	res.DeviceEncKeyCached = ek != nil

	ad := mctx.ActiveDevice()
	// cached paper key status
	if pk := ad.ProvisioningKey(mctx); pk != nil {
		if pk.EncryptionKey() != nil {
			res.PaperEncKeyCached = true
		}
		if pk.SigningKey() != nil {
			res.PaperSigKeyCached = true
		}
	}

	psc := ad.PassphraseStreamCache()
	res.PassphraseStreamCached = psc.ValidPassphraseStream()
	res.TsecCached = psc.ValidTsec()
	res.SecretPromptSkip = mctx.ActiveDevice().SecretPromptCancelTimer().WasRecentlyCanceled(mctx)

	current, all, err := libkb.GetAllProvisionedUsernames(mctx)
	if err != nil {
		mctx.Debug("| died in GetAllProvisionedUsernames()")
		return res, err
	}
	res.DefaultUsername = current.String()
	p := make([]string, len(all))
	for i, u := range all {
		p[i] = u.String()
	}
	res.ProvisionedUsernames = p

	accounts, err := libkb.GetConfiguredAccountsFromProvisionedUsernames(mctx, mctx.G().SecretStore(), current, all)
	if err != nil {
		mctx.Debug("| died in GetConfiguredAccounts()")
		return res, err
	}
	res.ConfiguredAccounts = accounts

	res.PlatformInfo = getPlatformInfo()
	res.DefaultDeviceID = g.Env.GetDeviceID()
	res.RememberPassphrase = g.Env.GetRememberPassphrase(g.Env.GetUsername())
	// DeviceEK status, can be nil if user is logged out
	deviceEKStorage := g.GetDeviceEKStorage()
	if deviceEKStorage != nil {
		dekNames, err := deviceEKStorage.ListAllForUser(mctx)
		if err != nil {
			return res, err
		}
		res.DeviceEkNames = dekNames
	}

	res.LocalDbStats = strings.Split(g.LocalDb.Stats(), "\n")
	res.LocalChatDbStats = strings.Split(g.LocalChatDb.Stats(), "\n")
	if cacheSizeInfo, err := CacheSizeInfo(g); err == nil {
		res.CacheDirSizeInfo = cacheSizeInfo
	}

	if g.ConnectionManager != nil {
		xp := g.ConnectionManager.LookupByClientType(keybase1.ClientType_KBFS)
		if xp == nil {
			mctx.Debug("| KBFS stats not available")
		} else {
			cli := &keybase1.SimpleFSClient{
				Cli: rpc.NewClient(
					xp, libkb.NewContextifiedErrorUnwrapper(g), nil),
			}
			stats, err := cli.SimpleFSGetStats(mctx.Ctx())
			if err != nil {
				mctx.Debug("| KBFS stats error: %+v", err)
			} else {
				res.LocalBlockCacheDbStats = stats.BlockCacheDbStats
				res.LocalSyncCacheDbStats = stats.SyncCacheDbStats
			}
		}
	}

	if g.UIRouter != nil {
		uiMapping := map[string]int{}
		for k, v := range g.UIRouter.DumpUIs() {
			uiMapping[k.String()] = int(v)
		}
		res.UiRouterMapping = uiMapping
	}

	return res, nil
}

func GetConfig(mctx libkb.MetaContext, forkType keybase1.ForkType) (c keybase1.Config, err error) {
	serverURI, err := mctx.G().Env.GetServerURI()
	if err != nil {
		return c, err
	}

	c.ServerURI = serverURI
	c.RunMode = string(mctx.G().Env.GetRunMode())
	c.SocketFile, err = mctx.G().Env.GetSocketBindFile()
	if err != nil {
		return c, err
	}

	gpg := mctx.G().GetGpgClient()
	canExec, err := gpg.CanExec(mctx)
	if err == nil {
		c.GpgExists = canExec
		c.GpgPath = gpg.Path(mctx)
	}

	c.Version = libkb.VersionString()
	c.VersionShort = libkb.Version

	var v []string
	libkb.VersionMessage(func(s string) {
		v = append(v, s)
	})
	c.VersionFull = strings.Join(v, "\n")

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		c.Path = dir
	} else {
		mctx.Warning("Failed to get service path: %s", err)
	}

	realpath, err := libkb.CurrentBinaryRealpath()
	if err == nil {
		c.BinaryRealpath = realpath
	} else {
		mctx.Debug("Failed to get service realpath: %s", err)
	}

	c.ConfigPath = mctx.G().Env.GetConfigFilename()
	c.Label = mctx.G().Env.GetLabel()
	if forkType == keybase1.ForkType_AUTO {
		c.IsAutoForked = true
	}
	c.ForkType = forkType
	return c, nil
}

func GetFullStatus(mctx libkb.MetaContext) (status *keybase1.FullStatus, err error) {
	status = &keybase1.FullStatus{}
	// The client overrides this value when sending logs
	status.Client.Version = libkb.VersionString()
	status.ConfigPath = mctx.G().Env.GetConfigFilename()

	status.CurStatus, err = GetCurrentStatus(mctx)
	if err != nil {
		return nil, err
	}

	// Duplicate the username at top-level for backwards compatibility of
	// output.
	if status.CurStatus.User != nil {
		status.Username = status.CurStatus.User.Username
	}

	status.ExtStatus, err = GetExtendedStatus(mctx)
	if err != nil {
		return nil, err
	}

	// set service status
	if status.ExtStatus.Standalone {
		status.Service.Running = false
	} else {
		status.Service.Running = true
	}
	status.Service.Version = libkb.VersionString()

	// set kbfs status
	kbfsInstalledVersion, err := install.KBFSBundleVersion(mctx.G(), "")
	if err != nil {
		mctx.Debug("Failed to get KBFSBundleVersion: %s", err)
	} else {
		status.Kbfs.InstalledVersion = kbfsInstalledVersion
	}
	if kbfs := GetFirstClient(status.ExtStatus.Clients, keybase1.ClientType_KBFS); kbfs != nil {
		status.Kbfs.Version = kbfs.Version
		status.Kbfs.Running = true
		// This just gets the mountpoint from the environment; the
		// user could have technically passed a different mountpoint
		// to KBFS on macOS or Linux.  TODO(KBFS-2723): fetch the
		// actual mountpoint with a new RPC from KBFS.
		mountDir, err := mctx.G().Env.GetMountDir()
		if err != nil {
			return nil, err
		}
		status.Kbfs.Mount = mountDir
	} else {
		status.Kbfs.Version = kbfsInstalledVersion
	}

	// set desktop status
	if desktop := GetFirstClient(status.ExtStatus.Clients, keybase1.ClientType_GUI_MAIN); desktop != nil {
		status.Desktop.Running = true
		status.Desktop.Version = desktop.Version
	}

	// set log paths
	status.Service.Log = getServiceLog(mctx, status.ExtStatus.LogDir)
	status.Service.EkLog = filepath.Join(status.ExtStatus.LogDir, libkb.EKLogFileName)
	status.Service.PerfLog = filepath.Join(status.ExtStatus.LogDir, libkb.PerfLogFileName)
	status.Kbfs.Log = filepath.Join(status.ExtStatus.LogDir, libkb.KBFSLogFileName)
	status.Desktop.Log = filepath.Join(status.ExtStatus.LogDir, libkb.GUILogFileName)
	status.Updater.Log = filepath.Join(status.ExtStatus.LogDir, libkb.UpdaterLogFileName)
	status.Start.Log = filepath.Join(status.ExtStatus.LogDir, libkb.StartLogFileName)
	status.Git.Log = filepath.Join(status.ExtStatus.LogDir, libkb.GitLogFileName)

	// set anything os-specific
	if err := osSpecific(mctx, status); err != nil {
		return nil, err
	}
	return status, nil
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package task

import (
	"context"
	"flag"
	"fmt"
	"strings"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/property"
	"github.com/vmware/govmomi/view"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
)

type recent struct {
	*flags.DatacenterFlag

	max    int
	follow bool
	long   bool
}

func init() {
	cli.Register("tasks", &recent{})
}

func (cmd *recent) Register(ctx context.Context, f *flag.FlagSet) {
	cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx)
	cmd.DatacenterFlag.Register(ctx, f)

	f.IntVar(&cmd.max, "n", 25, "Output the last N tasks")
	f.BoolVar(&cmd.follow, "f", false, "Follow recent task updates")
	f.BoolVar(&cmd.long, "l", false, "Use long task description")
}

func (cmd *recent) Description() string {
	return `Display info for recent tasks.

When a task has completed, the result column includes the task duration on success or
error message on failure.  If a task is still in progress, the result column displays
the completion percentage and the task ID.  The task ID can be used as an argument to
the 'task.cancel' command.

By default, all recent tasks are included (via TaskManager), but can be limited by PATH
to a specific inventory object.

Examples:
  govc tasks
  govc tasks -f
  govc tasks -f /dc1/host/cluster1`
}

func (cmd *recent) Usage() string {
	return "[PATH]"
}

func (cmd *recent) Process(ctx context.Context) error {
	if err := cmd.DatacenterFlag.Process(ctx); err != nil {
		return err
	}
	return nil
}

func chop(s string) string {
	if len(s) < 30 {
		return s
	}

	return s[:29] + "*"
}

// taskName describes the tasks similar to the ESX ui
func taskName(info *types.TaskInfo) string {
	name := strings.TrimSuffix(info.Name, "_Task")
	switch name {
	case "":
		return info.DescriptionId
	case "Destroy", "Rename":
		return info.Entity.Type + "." + name
	default:
		return name
	}
}

func (cmd *recent) Run(ctx context.Context, f *flag.FlagSet) error {
	c, err := cmd.Client()
	if err != nil {
		return err
	}

	m := c.ServiceContent.TaskManager

	tn := taskName

	if cmd.long {
		var o mo.TaskManager
		err = property.DefaultCollector(c).RetrieveOne(ctx, *m, []string{"description.methodInfo"}, &o)
		if err != nil {
			return err
		}

		desc := make(map[string]string, len(o.Description.MethodInfo))

		for _, entry := range o.Description.MethodInfo {
			info := entry.GetElementDescription()
			desc[info.Key] = info.Label
		}

		tn = func(info *types.TaskInfo) string {
			if name, ok := desc[info.DescriptionId]; ok {
				return name
			}

			return taskName(info)
		}
	}

	watch := *m

	if f.NArg() == 1 {
		refs, merr := cmd.ManagedObjects(ctx, f.Args())
		if merr != nil {
			return merr
		}
		watch = refs[0]
	}

	v, err := view.NewManager(c).CreateTaskView(ctx, &watch)
	if err != nil {
		return nil
	}

	defer func() {
		_ = v.Destroy(context.Background())
	}()

	v.Follow = cmd.follow

	stamp := "15:04:05"
	tmpl := "%-40s %-30s %15s %9s %9s %9s %s\n"
	fmt.Fprintf(cmd.Out, tmpl, "Task", "Target", "Initiator", "Queued", "Started", "Completed", "Result")

	var last string
	updated := false

	return v.Collect(ctx, func(tasks []types.TaskInfo) {
		if !updated && len(tasks) > cmd.max {
			tasks = tasks[len(tasks)-cmd.max:]
		}
		updated = true

		for _, info := range tasks {
			var user string

			switch x := info.Reason.(type) {
			case *types.TaskReasonUser:
				user = x.UserName
			}

			if info.EntityName == "" || user == "" {
				continue
			}

			ruser := strings.SplitN(user, "\\", 2)
			if len(ruser) == 2 {
				user = ruser[1] // discard domain
			} else {
				user = strings.TrimPrefix(user, "com.vmware.") // e.g. com.vmware.vsan.health
			}

			queued := info.QueueTime.Format(stamp)
			start := "-"
			end := start

			if info.StartTime != nil {
				start = info.StartTime.Format(stamp)
			}

			msg := fmt.Sprintf("%2d%% %s", info.Progress, info.Task)

			if info.CompleteTime != nil {
				msg = info.CompleteTime.Sub(*info.StartTime).String()

				if info.State == types.TaskInfoStateError {
					msg = strings.TrimSuffix(info.Error.LocalizedMessage, ".")
				}

				end = info.CompleteTime.Format(stamp)
			}

			result := fmt.Sprintf("%-7s [%s]", info.State, msg)

			item := fmt.Sprintf(tmpl, tn(&info), chop(info.EntityName), user, queued, start, end, result)

			if item == last {
				continue // task info was updated, but the fields we display were not
			}
			last = item

			fmt.Fprint(cmd.Out, item)
		}
	})
}
<commit_msg>govc: fixup tasks formatting<commit_after>/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package task

import (
	"context"
	"flag"
	"fmt"
	"strings"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/property"
	"github.com/vmware/govmomi/view"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
)

type recent struct {
	*flags.DatacenterFlag

	max    int
	follow bool
	long   bool
}

func init() {
	cli.Register("tasks", &recent{})
}

func (cmd *recent) Register(ctx context.Context, f *flag.FlagSet) {
	cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx)
	cmd.DatacenterFlag.Register(ctx, f)

	f.IntVar(&cmd.max, "n", 25, "Output the last N tasks")
	f.BoolVar(&cmd.follow, "f", false, "Follow recent task updates")
	f.BoolVar(&cmd.long, "l", false, "Use long task description")
}

func (cmd *recent) Description() string {
	return `Display info for recent tasks.

When a task has completed, the result column includes the task duration on success or
error message on failure.  If a task is still in progress, the result column displays
the completion percentage and the task ID.  The task ID can be used as an argument to
the 'task.cancel' command.

By default, all recent tasks are included (via TaskManager), but can be limited by PATH
to a specific inventory object.

Examples:
  govc tasks
  govc tasks -f
  govc tasks -f /dc1/host/cluster1`
}

func (cmd *recent) Usage() string {
	return "[PATH]"
}

func (cmd *recent) Process(ctx context.Context) error {
	if err := cmd.DatacenterFlag.Process(ctx); err != nil {
		return err
	}
	return nil
}

func chop(s string, n int) string {
	if len(s) < n {
		return s
	}

	return s[:n-1] + "*"
}

// taskName describes the tasks similar to the ESX ui
func taskName(info *types.TaskInfo) string {
	name := strings.TrimSuffix(info.Name, "_Task")
	switch name {
	case "":
		return info.DescriptionId
	case "Destroy", "Rename":
		return info.Entity.Type + "." + name
	default:
		return name
	}
}

func (cmd *recent) Run(ctx context.Context, f *flag.FlagSet) error {
	c, err := cmd.Client()
	if err != nil {
		return err
	}

	m := c.ServiceContent.TaskManager

	tn := taskName

	if cmd.long {
		var o mo.TaskManager
		err = property.DefaultCollector(c).RetrieveOne(ctx, *m, []string{"description.methodInfo"}, &o)
		if err != nil {
			return err
		}

		desc := make(map[string]string, len(o.Description.MethodInfo))

		for _, entry := range o.Description.MethodInfo {
			info := entry.GetElementDescription()
			desc[info.Key] = info.Label
		}

		tn = func(info *types.TaskInfo) string {
			if name, ok := desc[info.DescriptionId]; ok {
				return name
			}

			return taskName(info)
		}
	}

	watch := *m

	if f.NArg() == 1 {
		refs, merr := cmd.ManagedObjects(ctx, f.Args())
		if merr != nil {
			return merr
		}
		watch = refs[0]
	}

	v, err := view.NewManager(c).CreateTaskView(ctx, &watch)
	if err != nil {
		return nil
	}

	defer func() {
		_ = v.Destroy(context.Background())
	}()

	v.Follow = cmd.follow

	stamp := "15:04:05"
	tmpl := "%-40s %-30s %-30s %9s %9s %9s %s\n"
	fmt.Fprintf(cmd.Out, tmpl, "Task", "Target", "Initiator", "Queued", "Started", "Completed", "Result")

	var last string
	updated := false

	return v.Collect(ctx, func(tasks []types.TaskInfo) {
		if !updated && len(tasks) > cmd.max {
			tasks = tasks[len(tasks)-cmd.max:]
		}
		updated = true

		for _, info := range tasks {
			var user string

			switch x := info.Reason.(type) {
			case *types.TaskReasonUser:
				user = x.UserName
			}

			if info.EntityName == "" || user == "" {
				continue
			}

			ruser := strings.SplitN(user, "\\", 2)
			if len(ruser) == 2 {
				user = ruser[1] // discard domain
			} else {
				user = strings.TrimPrefix(user, "com.vmware.") // e.g. com.vmware.vsan.health
			}

			queued := info.QueueTime.Format(stamp)
			start := "-"
			end := start

			if info.StartTime != nil {
				start = info.StartTime.Format(stamp)
			}

			msg := fmt.Sprintf("%2d%% %s", info.Progress, info.Task)

			if info.CompleteTime != nil {
				msg = info.CompleteTime.Sub(*info.StartTime).String()

				if info.State == types.TaskInfoStateError {
					msg = strings.TrimSuffix(info.Error.LocalizedMessage, ".")
				}

				end = info.CompleteTime.Format(stamp)
			}

			result := fmt.Sprintf("%-7s [%s]", info.State, msg)

			item := fmt.Sprintf(tmpl, chop(tn(&info), 40), chop(info.EntityName, 30), chop(user, 30), queued, start, end, result)

			if item == last {
				continue // task info was updated, but the fields we display were not
			}
			last = item

			fmt.Fprint(cmd.Out, item)
		}
	})
}
<|endoftext|>


<commit_before>package main

//go:generate go-bindata -o modules/glue/generated_lua.go -pkg=glue -prefix "./builtin" ./builtin ./builtin/3rdparty ./builtin/libs ./builtin/libs/wrappers

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/carbonsrv/carbon/modules/glue"
	"github.com/carbonsrv/carbon/modules/luaconf"
	"github.com/carbonsrv/carbon/modules/middleware"
	"github.com/carbonsrv/carbon/modules/scheduler"
	"github.com/carbonsrv/carbon/modules/static"
	"github.com/gin-gonic/gin"
	"github.com/namsral/flag"
	"github.com/pmylund/go-cache"
	"golang.org/x/net/http2"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"time"
)

// General
var jobs *int

// Cache
var cfe *cache.Cache
var kvstore *cache.Cache

func cacheRead(c *cache.Cache, file string) (string, error) {
	res := ""
	data_tmp, found := c.Get(file)
	if found == false {
		data, err := fileRead(file)
		if err != nil {
			return "", err
		}
		c.Set(file, data, cache.DefaultExpiration)
	} else {
		debug("Using cache for %s" + file)
		res = data_tmp.(string)
	}
	return res, nil
}

// File system functions
var filesystem http.FileSystem

func initPhysFS(path string) http.FileSystem {
	err := physfs.Init()
	if err != nil {
		panic(err)
	}
	err = physfs.Mount(path, "/", true)
	if err != nil {
		panic(err)
	}
	return physfs.FileSystem()
}

/*func fileExists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}*/
func fileRead(file string) (string, error) {
	if physfs.Exists(file) {
		f, err := filesystem.Open(file)
		defer f.Close()
		if err != nil {
			return "", err
		}
		fi, err := f.Stat()
		if err != nil {
			return "", err
		}
		r := bufio.NewReader(f)
		buf := make([]byte, fi.Size())
		_, err = r.Read(buf)
		if err != nil {
			return "", err
		}
		return string(buf), err
	}
	return "", errors.New("no such file or directory.")
}
func fileExists(file string) bool {
	return physfs.Exists(file)
}
func cacheFileExists(file string) bool {
	data_tmp, found := cfe.Get(file)
	if found == false {
		exists := fileExists(file)
		cfe.Set(file, exists, cache.DefaultExpiration)
		return exists
	} else {
		return data_tmp.(bool)
	}
}

// Logging
var doLog bool = false

func debug(str string) {
	if doLog {
		log.Print(str)
	}
}

// Server
func new_server() *gin.Engine {
	return gin.New()
}
func bootstrap(srv *gin.Engine, dir string, cfe *cache.Cache) {
	switcher := middleware.ExtRoute(middleware.Plan{
		".lua": middleware.Lua(),
		"***":  staticServe.ServeCached("", staticServe.PhysFS("", "", true, true), cfe),
	})
	/*srv.GET(`/:file`, switcher)
	srv.POST(`/:file`, switcher)*/
	srv.Use(switcher)
	/*st := staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe)
	lr := luaroute(dir)
	srv.Use(lr)
	srv.Use(st)*/
}

func serve(srv http.Handler, en_http bool, en_https bool, en_http2 bool, bind string, binds string, cert string, key string) {
	end := make(chan bool)
	if en_http {
		go serveHTTP(srv, bind, en_http2)
	}
	if en_https {
		cert, _ := filepath.Abs(cert)
		key, _ := filepath.Abs(key)
		go serveHTTPS(srv, binds, en_http2, cert, key)
	}
	<-end
}
func serveHTTP(srv http.Handler, bind string, en_http2 bool) {
	s := &http.Server{
		Addr:           bind,
		Handler:        srv,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if en_http2 {
		http2.ConfigureServer(s, nil)
	}
	err := s.ListenAndServe()
	if err != nil {
		panic(err)
	}
}
func serveHTTPS(srv http.Handler, bind string, en_http2 bool, cert string, key string) {
	s := &http.Server{
		Addr:           bind,
		Handler:        srv,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if en_http2 {
		http2.ConfigureServer(s, nil)
	}
	err := s.ListenAndServeTLS(cert, key)
	if err != nil {
		panic(err)
	}
}

func main() {
	cfe = cache.New(5*time.Minute, 30*time.Second) // File-Exists Cache
	kvstore = cache.New(-1, -1)                    // Key-Value Storage

	// Use config
	flag.String("config", "", "Parse Config File")
	var script_flag = flag.String("script", "", "Parse Lua Script as initialization")
	var run_repl = flag.Bool("repl", false, "Run REPL")
	var eval = flag.String("eval", "", "Eval Lua Code")
	var is_app = flag.Bool("app", false, "Script is bundle")
	var licenses = flag.Bool("licenses", false, "Show licenses")

	var host = flag.String("host", "", "IP of Host to bind the Webserver on")
	var port = flag.Int("port", 80, "Port to run Webserver on (HTTP)")
	var ports = flag.Int("ports", 443, "Port to run Webserver on (HTTPS)")
	var cert = flag.String("cert", "", "Certificate File for HTTPS")
	var key = flag.String("key", "", "Key File for HTTPS")
	var en_http = flag.Bool("http", true, "Listen HTTP")
	var en_https = flag.Bool("https", false, "Listen HTTPS")
	var en_http2 = flag.Bool("http2", false, "Enable HTTP/2")

	wrkrs := 2
	if runtime.NumCPU() > 2 {
		wrkrs = runtime.NumCPU()
	}
	jobs = flag.Int("states", wrkrs, "Number of Preinitialized Lua States")
	var workers = flag.Int("workers", wrkrs, "Number of Worker threads.")
	var webroot = flag.String("root", ".", "Path to Web Root")

	// Do debug!
	doDebug := flag.Bool("debug", false, "Show debug information")
	// Middleware options
	useRecovery := flag.Bool("recovery", false, "Recover from Panics")
	useLogger := flag.Bool("logger", true, "Log Request information")

	flag.Parse()

	if *en_https {
		if *key == "" || *cert == "" {
			panic("Need to have a Key and a Cert defined.")
		}
	}

	if *licenses {
		fmt.Println(glue.GetGlue("NOTICE.txt"))
		os.Exit(0)
	}

	var script string
	if *script_flag == "" {
		script = flag.Arg(0)
	} else {
		script = *script_flag
	}

	runtime.GOMAXPROCS(*workers)

	root, _ := filepath.Abs(*webroot)
	physroot_path := root
	if *is_app {
		physroot_path = script
	}
	filesystem = initPhysFS(physroot_path)

	defer physfs.Deinit()
	go scheduler.Run()                         // Run the scheduler.
	go middleware.Preloader()                  // Run the Preloader.
	middleware.Init(*jobs, cfe, kvstore, root) // Run init sequence.

	if *doDebug == false {
		gin.SetMode(gin.ReleaseMode)
	}

	args := flag.Args()

	if *eval != "" {
		err := luaconf.Eval(*eval, args, cfe, root, *useRecovery, *useLogger, *run_repl, func(srv *gin.Engine) {
			serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
		})
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		os.Exit(0)
	}

	if script == "" {
		if *run_repl {
			err := luaconf.REPL(args, cfe, root, *useRecovery, false, func(srv *gin.Engine) {
				serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
			})
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		} else {
			err := luaconf.Eval(glue.GetGlue("bootscript.lua"), args, cfe, root, *useRecovery, *useLogger, *run_repl, func(srv *gin.Engine) {
				serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
			})
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		}
	} else {
		if *useLogger {
			doLog = true
		}

		if flag.Arg(1) == "" {
			args = make([]string, 0)
		} else {
			args = args[1:]
		}
		if *is_app {
			script_src, err := fileRead("app.lua")
			if err != nil {
				script_base := filepath.Base(script)
				script_name := script_base[:len(script_base)-len(filepath.Ext(script))]
				script_src, err = fileRead(script_name + ".lua")
				if err != nil {
					script_src, err = fileRead("init.lua")
					if err != nil {
						fmt.Println("Error: App Bundle does not contain 'app.lua', '" + script_name + ".lua' or 'init.lua'. No idea what to run. Aborting.")
						os.Exit(1)
					}
				}
			}
			err = luaconf.Eval(script_src, args, cfe, script, *useRecovery, *useLogger, *run_repl, func(srv *gin.Engine) {
				serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
			})
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		} else {
			err := luaconf.Configure(script, args, cfe, root, *useRecovery, *useLogger, *run_repl, func(srv *gin.Engine) {
				serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
			})
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		}
	}
}
<commit_msg>Revert the change to app logic, keep the fix.<commit_after>package main

//go:generate go-bindata -o modules/glue/generated_lua.go -pkg=glue -prefix "./builtin" ./builtin ./builtin/3rdparty ./builtin/libs ./builtin/libs/wrappers

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/carbonsrv/carbon/modules/glue"
	"github.com/carbonsrv/carbon/modules/luaconf"
	"github.com/carbonsrv/carbon/modules/middleware"
	"github.com/carbonsrv/carbon/modules/scheduler"
	"github.com/carbonsrv/carbon/modules/static"
	"github.com/gin-gonic/gin"
	"github.com/namsral/flag"
	"github.com/pmylund/go-cache"
	"golang.org/x/net/http2"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"time"
)

// General
var jobs *int

// Cache
var cfe *cache.Cache
var kvstore *cache.Cache

func cacheRead(c *cache.Cache, file string) (string, error) {
	res := ""
	data_tmp, found := c.Get(file)
	if found == false {
		data, err := fileRead(file)
		if err != nil {
			return "", err
		}
		c.Set(file, data, cache.DefaultExpiration)
	} else {
		debug("Using cache for %s" + file)
		res = data_tmp.(string)
	}
	return res, nil
}

// File system functions
var filesystem http.FileSystem

func initPhysFS(path string) http.FileSystem {
	err := physfs.Init()
	if err != nil {
		panic(err)
	}
	err = physfs.Mount(path, "/", true)
	if err != nil {
		panic(err)
	}
	return physfs.FileSystem()
}

/*func fileExists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}*/
func fileRead(file string) (string, error) {
	if physfs.Exists(file) {
		f, err := filesystem.Open(file)
		defer f.Close()
		if err != nil {
			return "", err
		}
		fi, err := f.Stat()
		if err != nil {
			return "", err
		}
		r := bufio.NewReader(f)
		buf := make([]byte, fi.Size())
		_, err = r.Read(buf)
		if err != nil {
			return "", err
		}
		return string(buf), err
	}
	return "", errors.New("no such file or directory.")
}
func fileExists(file string) bool {
	return physfs.Exists(file)
}
func cacheFileExists(file string) bool {
	data_tmp, found := cfe.Get(file)
	if found == false {
		exists := fileExists(file)
		cfe.Set(file, exists, cache.DefaultExpiration)
		return exists
	} else {
		return data_tmp.(bool)
	}
}

// Logging
var doLog bool = false

func debug(str string) {
	if doLog {
		log.Print(str)
	}
}

// Server
func new_server() *gin.Engine {
	return gin.New()
}
func bootstrap(srv *gin.Engine, dir string, cfe *cache.Cache) {
	switcher := middleware.ExtRoute(middleware.Plan{
		".lua": middleware.Lua(),
		"***":  staticServe.ServeCached("", staticServe.PhysFS("", "", true, true), cfe),
	})
	/*srv.GET(`/:file`, switcher)
	srv.POST(`/:file`, switcher)*/
	srv.Use(switcher)
	/*st := staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe)
	lr := luaroute(dir)
	srv.Use(lr)
	srv.Use(st)*/
}

func serve(srv http.Handler, en_http bool, en_https bool, en_http2 bool, bind string, binds string, cert string, key string) {
	end := make(chan bool)
	if en_http {
		go serveHTTP(srv, bind, en_http2)
	}
	if en_https {
		cert, _ := filepath.Abs(cert)
		key, _ := filepath.Abs(key)
		go serveHTTPS(srv, binds, en_http2, cert, key)
	}
	<-end
}
func serveHTTP(srv http.Handler, bind string, en_http2 bool) {
	s := &http.Server{
		Addr:           bind,
		Handler:        srv,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if en_http2 {
		http2.ConfigureServer(s, nil)
	}
	err := s.ListenAndServe()
	if err != nil {
		panic(err)
	}
}
func serveHTTPS(srv http.Handler, bind string, en_http2 bool, cert string, key string) {
	s := &http.Server{
		Addr:           bind,
		Handler:        srv,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if en_http2 {
		http2.ConfigureServer(s, nil)
	}
	err := s.ListenAndServeTLS(cert, key)
	if err != nil {
		panic(err)
	}
}

func main() {
	cfe = cache.New(5*time.Minute, 30*time.Second) // File-Exists Cache
	kvstore = cache.New(-1, -1)                    // Key-Value Storage

	// Use config
	flag.String("config", "", "Parse Config File")
	var script_flag = flag.String("script", "", "Parse Lua Script as initialization")
	var run_repl = flag.Bool("repl", false, "Run REPL")
	var eval = flag.String("eval", "", "Eval Lua Code")
	var is_app = flag.Bool("app", false, "Script is bundle")
	var licenses = flag.Bool("licenses", false, "Show licenses")

	var host = flag.String("host", "", "IP of Host to bind the Webserver on")
	var port = flag.Int("port", 80, "Port to run Webserver on (HTTP)")
	var ports = flag.Int("ports", 443, "Port to run Webserver on (HTTPS)")
	var cert = flag.String("cert", "", "Certificate File for HTTPS")
	var key = flag.String("key", "", "Key File for HTTPS")
	var en_http = flag.Bool("http", true, "Listen HTTP")
	var en_https = flag.Bool("https", false, "Listen HTTPS")
	var en_http2 = flag.Bool("http2", false, "Enable HTTP/2")

	wrkrs := 2
	if runtime.NumCPU() > 2 {
		wrkrs = runtime.NumCPU()
	}
	jobs = flag.Int("states", wrkrs, "Number of Preinitialized Lua States")
	var workers = flag.Int("workers", wrkrs, "Number of Worker threads.")
	var webroot = flag.String("root", ".", "Path to Web Root")

	// Do debug!
	doDebug := flag.Bool("debug", false, "Show debug information")
	// Middleware options
	useRecovery := flag.Bool("recovery", false, "Recover from Panics")
	useLogger := flag.Bool("logger", true, "Log Request information")

	flag.Parse()

	if *en_https {
		if *key == "" || *cert == "" {
			panic("Need to have a Key and a Cert defined.")
		}
	}

	if *licenses {
		fmt.Println(glue.GetGlue("NOTICE.txt"))
		os.Exit(0)
	}

	var script string
	if *script_flag == "" {
		script = flag.Arg(0)
	} else {
		script = *script_flag
	}

	runtime.GOMAXPROCS(*workers)

	root, _ := filepath.Abs(*webroot)
	physroot_path := root
	if *is_app {
		physroot_path = script
	}
	filesystem = initPhysFS(physroot_path)

	defer physfs.Deinit()
	go scheduler.Run()                         // Run the scheduler.
	go middleware.Preloader()                  // Run the Preloader.
	middleware.Init(*jobs, cfe, kvstore, root) // Run init sequence.

	if *doDebug == false {
		gin.SetMode(gin.ReleaseMode)
	}

	args := flag.Args()

	if *eval != "" {
		err := luaconf.Eval(*eval, args, cfe, root, *useRecovery, *useLogger, *run_repl, func(srv *gin.Engine) {
			serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
		})
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		os.Exit(0)
	}

	if script == "" {
		if *run_repl {
			err := luaconf.REPL(args, cfe, root, *useRecovery, false, func(srv *gin.Engine) {
				serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
			})
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		} else {
			err := luaconf.Eval(glue.GetGlue("bootscript.lua"), args, cfe, root, *useRecovery, *useLogger, *run_repl, func(srv *gin.Engine) {
				serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
			})
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		}
	} else {
		if *useLogger {
			doLog = true
		}

		if flag.Arg(1) == "" {
			args = make([]string, 0)
		} else {
			args = args[1:]
		}
		if *is_app {
			script_base := filepath.Base(script)
			script_name := script_base[:len(script_base)-len(filepath.Ext(script))]
			script_src, err := fileRead(script_name + ".lua")
			if err != nil {
				script_src, err = fileRead("app.lua")
				if err != nil {
					script_src, err = fileRead("init.lua")
					if err != nil {
						fmt.Println("Error: App Bundle does not contain '" + script_name + ".lua', 'app.lua' or 'init.lua'. No idea what to run. Aborting.")
						os.Exit(1)
					}
				}
			}
			err = luaconf.Eval(script_src, args, cfe, script, *useRecovery, *useLogger, *run_repl, func(srv *gin.Engine) {
				serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
			})
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		} else {
			err := luaconf.Configure(script, args, cfe, root, *useRecovery, *useLogger, *run_repl, func(srv *gin.Engine) {
				serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
			})
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		}
	}
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent <ulfr@mozilla.com>

package aws

import (
	"crypto/rand"
	"fmt"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	awscred "github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/mozilla-services/userplex/modules"
)

func init() {
	modules.Register("aws", new(module))
}

type module struct {
}

func (m *module) NewRun(c modules.Configuration) modules.Runner {
	r := new(run)
	r.Conf = c
	return r
}

type run struct {
	Conf modules.Configuration
	p    parameters
	c    credentials
	cli  *iam.IAM
}

type parameters struct {
	IamGroups      []string
	NotifyNewUsers bool
	SmtpRelay      string
	SmtpFrom       string
	SigninUrl      string
}

type credentials struct {
	AccessKey string
	SecretKey string
}

func (r *run) Run() (err error) {
	err = r.Conf.GetParameters(&r.p)
	if err != nil {
		return
	}
	err = r.Conf.GetCredentials(&r.c)
	if err != nil {
		return
	}
	r.cli = r.initIamClient()
	if r.cli == nil {
		return fmt.Errorf("failed to connect to aws using access key %q", r.c.AccessKey)
	}
	// Retrieve a list of ldap users from the groups configured
	// and create or add the users to groups.
	ldapers := r.getLdapers()
	for uid, haspubkey := range ldapers {
		resp, err := r.cli.GetUser(&iam.GetUserInput{
			UserName: aws.String(uid),
		})
		if err != nil || resp == nil {
			log.Printf("[info] aws: user %q not found, needs to be created", uid)
			if !haspubkey {
				log.Printf("[warning] aws: skipping creation of user %q because of missing PGP public key", uid)
				continue
			}
			r.createIamUser(uid)
		} else {
			r.updateUserGroups(uid)
		}
	}
	// find which users are no longer in ldap and needs to be removed from aws
	if r.Conf.Delete {
		iamers := r.getIamers()
		for uid, _ := range iamers {
			if _, ok := ldapers[uid]; !ok {
				log.Printf("[info] aws: %q found in IAM group but not in LDAP, needs deletion", uid)
				r.removeIamUser(uid)
			}
		}
	}
	return
}

func (r *run) getLdapers() (lgm map[string]bool) {
	lgm = make(map[string]bool)
	users, err := r.Conf.LdapCli.GetEnabledUsersInGroups(r.Conf.LdapGroups)
	if err != nil {
		return
	}
	for _, user := range users {
		shortdn := strings.Split(user, ",")[0]
		uid, err := r.Conf.LdapCli.GetUserId(shortdn)
		if err != nil {
			log.Printf("[warning] aws: ldap query failed with error %v", err)
			continue
		}
		// apply the uid map: only store the translated uid in the ldapuid
		for _, mapping := range r.Conf.UidMap {
			if mapping.LdapUid == uid {
				uid = mapping.UsedUid
			}
		}
		lgm[uid] = true
		// make sure we can find a PGP public key for the user to encrypt the notification
		// if no pubkey is found, log an error and set the user's entry to False
		_, err = r.Conf.LdapCli.GetUserPGPKey(shortdn)
		if err != nil {
			log.Printf("[warning] aws: no pgp public key could be found for %s: %v", shortdn, err)
			lgm[uid] = false
		}
	}
	return
}

func (r *run) getIamers() (igm map[string]bool) {
	igm = make(map[string]bool)
	for _, group := range r.p.IamGroups {
		resp, err := r.cli.GetGroup(&iam.GetGroupInput{
			GroupName: aws.String(group),
		})
		if err != nil || resp == nil {
			log.Printf("[error] failed to retrieve users from IAM group %q: %v", group, err)
			continue
		}
		for _, user := range resp.Users {
			iamuser := strings.Replace(awsutil.Prettify(user.UserName), `"`, ``, -1)
			igm[iamuser] = true
		}
	}
	return
}

func (r *run) initIamClient() *iam.IAM {
	var awsconf aws.Config
	if r.c.AccessKey != "" && r.c.SecretKey != "" {
		awscreds := awscred.NewStaticCredentials(r.c.AccessKey, r.c.SecretKey, "")
		awsconf.Credentials = awscreds
	}
	return iam.New(session.New(), &awsconf)
}

// create a user in aws, assign temporary credentials and force password change, add the
// user to the necessary groups, and send it an email
func (r *run) createIamUser(uid string) {
	var (
		cuo  *iam.CreateUserOutput
		clpo *iam.CreateLoginProfileOutput
	)
	if !r.Conf.Create {
		return
	}
	password := "P" + randToken() + "%"
	mail, err := r.Conf.LdapCli.GetUserEmailByUid(uid)
	if err != nil {
		log.Printf("[error] aws: couldn't find email of user %q in ldap, notification not sent: %v", uid, err)
		return
	}
	if r.Conf.DryRun {
		log.Printf("[dryrun] aws: would have created AWS IAM user %q with password %q", uid, password)
		goto notify
	}
	cuo, err = r.cli.CreateUser(&iam.CreateUserInput{
		UserName: aws.String(uid),
	})
	if err != nil || cuo == nil {
		log.Printf("[error] aws: failed to create user %q: %v", uid, err)
		return
	}
	clpo, err = r.cli.CreateLoginProfile(&iam.CreateLoginProfileInput{
		Password:              aws.String(password),
		UserName:              aws.String(uid),
		PasswordResetRequired: aws.Bool(true),
	})
	if err != nil || clpo == nil {
		log.Printf("[error] aws: failed to create user %q: %v", uid, err)
		return
	}
	for _, group := range r.p.IamGroups {
		r.addUserToIamGroup(uid, group)
	}
notify:
	// notify user
	rcpt := r.Conf.Notify.Recipient
	if rcpt == "{ldap:mail}" {
		rcpt = mail
	}
	r.Conf.Notify.Channel <- modules.Notification{
		Module:      "aws",
		Recipient:   rcpt,
		Mode:        r.Conf.Notify.Mode,
		MustEncrypt: true,
		Body: []byte(fmt.Sprintf(`New AWS account:
login: %s
pass:  %s (change at first login)
url:   %s`, uid, password, r.p.SigninUrl)),
	}
	return
}

func (r *run) updateUserGroups(uid string) {
	gresp, err := r.cli.ListGroupsForUser(&iam.ListGroupsForUserInput{
		UserName: aws.String(uid),
	})
	if err != nil || gresp == nil {
		log.Printf("[info] aws: groups of user %q not found, needs to be added", uid)
	}
	// iterate through the groups and find the missing ones
	for _, iamgroup := range r.p.IamGroups {
		found := false
		for _, group := range gresp.Groups {
			gname := strings.Replace(awsutil.Prettify(group.GroupName), `"`, ``, -1)
			if iamgroup == gname {
				found = true
			}
		}
		if !found {
			r.addUserToIamGroup(uid, iamgroup)
		}
	}
	return
}

func (r *run) addUserToIamGroup(uid, group string) {
	if !r.Conf.Create {
		return
	}
	if r.Conf.DryRun {
		log.Printf("[dryrun] aws: would have added AWS IAM user %q to group %q", uid, group)
		return
	}
	resp, err := r.cli.AddUserToGroup(&iam.AddUserToGroupInput{
		GroupName: aws.String(group),
		UserName:  aws.String(uid),
	})
	if err != nil || resp == nil {
		log.Printf("[error] aws: failed to add user %q to group %q: %v", uid, group, err)
	}
	return
}

func (r *run) removeIamUser(uid string) {
	if !r.Conf.Delete {
		return
	}
	if r.Conf.DryRun {
		log.Printf("[dryrun] aws: would have deleted AWS IAM user %q", uid)
		return
	}
	gresp, err := r.cli.ListGroupsForUser(&iam.ListGroupsForUserInput{
		UserName: aws.String(uid),
	})
	if err != nil || gresp == nil {
		log.Printf("[error] aws: failed to list groups for user %q: %v", uid, err)
		return
	}
	// iterate through the groups and find the missing ones
	for _, iamgroup := range r.p.IamGroups {
		resp, err := r.cli.RemoveUserFromGroup(&iam.RemoveUserFromGroupInput{
			GroupName: &iamgroup,
			UserName:  aws.String(uid),
		})
		gname := strings.Replace(awsutil.Prettify(iamgroup), `"`, ``, -1)
		if err != nil || resp == nil {
			log.Printf("[error] aws: failed to remove user %q from group %q: %v", uid, gname, err)
		} else {
			log.Printf("[info] aws: removed user %q from group %q", uid, gname)
		}
	}
	resp1, err := r.cli.DeleteLoginProfile(&iam.DeleteLoginProfileInput{
		UserName: aws.String(uid),
	})
	if err != nil || resp1 == nil {
		log.Printf("[error] aws: failed to delete aws login profile for user %q: %v", uid, err)
		return
	}
	resp2, err := r.cli.DeleteUser(&iam.DeleteUserInput{
		UserName: aws.String(uid),
	})
	if err != nil || resp2 == nil {
		log.Printf("[error] aws: failed to delete aws user %q: %v", uid, err)
		return
	}
	return
}

func randToken() string {
	b := make([]byte, 8)
	rand.Read(b)
	return fmt.Sprintf("%x", b)
}
<commit_msg>aws: send notification on account removal<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent <ulfr@mozilla.com>

package aws

import (
	"crypto/rand"
	"fmt"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	awscred "github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/mozilla-services/userplex/modules"
)

func init() {
	modules.Register("aws", new(module))
}

type module struct {
}

func (m *module) NewRun(c modules.Configuration) modules.Runner {
	r := new(run)
	r.Conf = c
	return r
}

type run struct {
	Conf modules.Configuration
	p    parameters
	c    credentials
	cli  *iam.IAM
}

type parameters struct {
	IamGroups      []string
	NotifyNewUsers bool
	SmtpRelay      string
	SmtpFrom       string
	SigninUrl      string
}

type credentials struct {
	AccessKey string
	SecretKey string
}

func (r *run) Run() (err error) {
	err = r.Conf.GetParameters(&r.p)
	if err != nil {
		return
	}
	err = r.Conf.GetCredentials(&r.c)
	if err != nil {
		return
	}
	r.cli = r.initIamClient()
	if r.cli == nil {
		return fmt.Errorf("failed to connect to aws using access key %q", r.c.AccessKey)
	}
	// Retrieve a list of ldap users from the groups configured
	// and create or add the users to groups.
	ldapers := r.getLdapers()
	for uid, haspubkey := range ldapers {
		resp, err := r.cli.GetUser(&iam.GetUserInput{
			UserName: aws.String(uid),
		})
		if err != nil || resp == nil {
			log.Printf("[info] aws: user %q not found, needs to be created", uid)
			if !haspubkey {
				log.Printf("[warning] aws: skipping creation of user %q because of missing PGP public key", uid)
				continue
			}
			r.createIamUser(uid)
		} else {
			r.updateUserGroups(uid)
		}
	}
	// find which users are no longer in ldap and needs to be removed from aws
	if r.Conf.Delete {
		iamers := r.getIamers()
		for uid, _ := range iamers {
			if _, ok := ldapers[uid]; !ok {
				log.Printf("[info] aws: %q found in IAM group but not in LDAP, needs deletion", uid)
				r.removeIamUser(uid)
			}
		}
	}
	return
}

func (r *run) getLdapers() (lgm map[string]bool) {
	lgm = make(map[string]bool)
	users, err := r.Conf.LdapCli.GetEnabledUsersInGroups(r.Conf.LdapGroups)
	if err != nil {
		return
	}
	for _, user := range users {
		shortdn := strings.Split(user, ",")[0]
		uid, err := r.Conf.LdapCli.GetUserId(shortdn)
		if err != nil {
			log.Printf("[warning] aws: ldap query failed with error %v", err)
			continue
		}
		// apply the uid map: only store the translated uid in the ldapuid
		for _, mapping := range r.Conf.UidMap {
			if mapping.LdapUid == uid {
				uid = mapping.UsedUid
			}
		}
		lgm[uid] = true
		// make sure we can find a PGP public key for the user to encrypt the notification
		// if no pubkey is found, log an error and set the user's entry to False
		_, err = r.Conf.LdapCli.GetUserPGPKey(shortdn)
		if err != nil {
			log.Printf("[warning] aws: no pgp public key could be found for %s: %v", shortdn, err)
			lgm[uid] = false
		}
	}
	return
}

func (r *run) getIamers() (igm map[string]bool) {
	igm = make(map[string]bool)
	for _, group := range r.p.IamGroups {
		resp, err := r.cli.GetGroup(&iam.GetGroupInput{
			GroupName: aws.String(group),
		})
		if err != nil || resp == nil {
			log.Printf("[error] failed to retrieve users from IAM group %q: %v", group, err)
			continue
		}
		for _, user := range resp.Users {
			iamuser := strings.Replace(awsutil.Prettify(user.UserName), `"`, ``, -1)
			igm[iamuser] = true
		}
	}
	return
}

func (r *run) initIamClient() *iam.IAM {
	var awsconf aws.Config
	if r.c.AccessKey != "" && r.c.SecretKey != "" {
		awscreds := awscred.NewStaticCredentials(r.c.AccessKey, r.c.SecretKey, "")
		awsconf.Credentials = awscreds
	}
	return iam.New(session.New(), &awsconf)
}

// create a user in aws, assign temporary credentials and force password change, add the
// user to the necessary groups, and send it an email
func (r *run) createIamUser(uid string) {
	var (
		cuo  *iam.CreateUserOutput
		clpo *iam.CreateLoginProfileOutput
	)
	if !r.Conf.Create {
		return
	}
	password := "P" + randToken() + "%"
	mail, err := r.Conf.LdapCli.GetUserEmailByUid(uid)
	if err != nil {
		log.Printf("[error] aws: couldn't find email of user %q in ldap, notification not sent: %v", uid, err)
		return
	}
	if r.Conf.DryRun {
		log.Printf("[dryrun] aws: would have created AWS IAM user %q with password %q", uid, password)
		goto notify
	}
	cuo, err = r.cli.CreateUser(&iam.CreateUserInput{
		UserName: aws.String(uid),
	})
	if err != nil || cuo == nil {
		log.Printf("[error] aws: failed to create user %q: %v", uid, err)
		return
	}
	clpo, err = r.cli.CreateLoginProfile(&iam.CreateLoginProfileInput{
		Password:              aws.String(password),
		UserName:              aws.String(uid),
		PasswordResetRequired: aws.Bool(true),
	})
	if err != nil || clpo == nil {
		log.Printf("[error] aws: failed to create user %q: %v", uid, err)
		return
	}
	for _, group := range r.p.IamGroups {
		r.addUserToIamGroup(uid, group)
	}
notify:
	// notify user
	rcpt := r.Conf.Notify.Recipient
	if rcpt == "{ldap:mail}" {
		rcpt = mail
	}
	r.Conf.Notify.Channel <- modules.Notification{
		Module:      "aws",
		Recipient:   rcpt,
		Mode:        r.Conf.Notify.Mode,
		MustEncrypt: true,
		Body: []byte(fmt.Sprintf(`New AWS account:
login: %s
pass:  %s (change at first login)
url:   %s`, uid, password, r.p.SigninUrl)),
	}
	return
}

func (r *run) updateUserGroups(uid string) {
	gresp, err := r.cli.ListGroupsForUser(&iam.ListGroupsForUserInput{
		UserName: aws.String(uid),
	})
	if err != nil || gresp == nil {
		log.Printf("[info] aws: groups of user %q not found, needs to be added", uid)
	}
	// iterate through the groups and find the missing ones
	for _, iamgroup := range r.p.IamGroups {
		found := false
		for _, group := range gresp.Groups {
			gname := strings.Replace(awsutil.Prettify(group.GroupName), `"`, ``, -1)
			if iamgroup == gname {
				found = true
			}
		}
		if !found {
			r.addUserToIamGroup(uid, iamgroup)
		}
	}
	return
}

func (r *run) addUserToIamGroup(uid, group string) {
	if !r.Conf.Create {
		return
	}
	if r.Conf.DryRun {
		log.Printf("[dryrun] aws: would have added AWS IAM user %q to group %q", uid, group)
		return
	}
	resp, err := r.cli.AddUserToGroup(&iam.AddUserToGroupInput{
		GroupName: aws.String(group),
		UserName:  aws.String(uid),
	})
	if err != nil || resp == nil {
		log.Printf("[error] aws: failed to add user %q to group %q: %v", uid, group, err)
	}
	return
}

func (r *run) removeIamUser(uid string) {
	var (
		err  error
		lgfu *iam.ListGroupsForUserOutput
		dlpo *iam.DeleteLoginProfileOutput
		duo  *iam.DeleteUserOutput
	)
	if !r.Conf.Delete {
		return
	}
	if r.Conf.DryRun {
		log.Printf("[dryrun] aws: would have deleted AWS IAM user %q", uid)
		goto notify
	}
	lgfu, err = r.cli.ListGroupsForUser(&iam.ListGroupsForUserInput{
		UserName: aws.String(uid),
	})
	if err != nil || lgfu == nil {
		log.Printf("[error] aws: failed to list groups for user %q: %v", uid, err)
		return
	}
	// iterate through the groups and find the missing ones
	for _, iamgroup := range r.p.IamGroups {
		resp, err := r.cli.RemoveUserFromGroup(&iam.RemoveUserFromGroupInput{
			GroupName: &iamgroup,
			UserName:  aws.String(uid),
		})
		gname := strings.Replace(awsutil.Prettify(iamgroup), `"`, ``, -1)
		if err != nil || resp == nil {
			log.Printf("[error] aws: failed to remove user %q from group %q: %v", uid, gname, err)
		} else {
			log.Printf("[info] aws: removed user %q from group %q", uid, gname)
		}
	}
	dlpo, err = r.cli.DeleteLoginProfile(&iam.DeleteLoginProfileInput{
		UserName: aws.String(uid),
	})
	if err != nil || dlpo == nil {
		log.Printf("[error] aws: failed to delete aws login profile for user %q: %v", uid, err)
		return
	}
	duo, err = r.cli.DeleteUser(&iam.DeleteUserInput{
		UserName: aws.String(uid),
	})
	if err != nil || duo == nil {
		log.Printf("[error] aws: failed to delete aws user %q: %v", uid, err)
		return
	}
notify:
	// notify user
	rcpt := r.Conf.Notify.Recipient
	if rcpt == "{ldap:mail}" {
		rcpt, err = r.Conf.LdapCli.GetUserEmailByUid(uid)
		if err != nil {
			log.Printf("[error] aws: couldn't find email of user %q in ldap, notification not sent: %v", uid, err)
			return
		}
	}
	r.Conf.Notify.Channel <- modules.Notification{
		Module:      "aws",
		Recipient:   rcpt,
		Mode:        r.Conf.Notify.Mode,
		MustEncrypt: false,
		Body: []byte(fmt.Sprintf(`Deleted AWS account:
The account %q has been removed from %q.`, uid, r.p.SigninUrl)),
	}
	return
}

func randToken() string {
	b := make([]byte, 8)
	rand.Read(b)
	return fmt.Sprintf("%x", b)
}
<|endoftext|>


<commit_before>package gcpug

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/zenazn/goji/web"

	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"github.com/mjibson/goon"
	gengine "google.golang.org/appengine"
	"google.golang.org/appengine/log"
	gmemcache "google.golang.org/appengine/memcache"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	gauth "google.golang.org/api/oauth2/v2"

	plus "google.golang.org/api/plus/v1"
)

type requestParam struct {
	Host       string
	Method     string
	UrlHost    string
	Fragment   string
	Path       string
	Scheme     string
	Opaque     string
	RawQuery   string
	RemoteAddr string
	RequestURI string
	UserAgent  string
}

const (
	randStateForAuthToMemcacheKey = "randStateForAuthToMemcacheKey"
)

type Member struct {
	Email       string    `datastore:"-" goon:"id" json:"email"`    // Email
	Id          string    `json:"-"`                                // Google Id
	NickName    string    `json:"nickName"`                         // ニックネーム
	Name        string    `json:"name"`                             // G+ 名前
	FamilyName  string    `json:"familyName" datastore:",noindex"`  // G+ 姓
	GivenName   string    `json:"givenName" datastore:",noindex"`   // G+ 名
	PlusLink    string    `json:"plusLink" datastore:",noindex"`    // G+ Profile Link
	PictureLink string    `json:"pictureLink" datastore:",noindex"` // G+ Picture Link
	GithubId    string    `json:"githubId" datastore:",noindex"`    // Github Id
	QiitaId     string    `json:"qiitaId" datastore:",noindex"`     // Qiita Id
	TwitterId   string    `json:"twitterId" datastore:",noindex"`   // Twitter Id
	FacebookId  string    `json:"facebookId" datastore:",noindex"`  // Faebook Id
	BlogLink    string    `json:"blogLink" datastore:",noindex"`    // Blog Link
	CreatedAt   time.Time `json:"createdAt"`                        // 作成日時
	UpdatedAt   time.Time `json:"updatedAt"`                        // 更新日時
}

type MemberApi struct {
}

func SetUpMember(m *web.Mux) {
	api := MemberApi{}

	m.Get("/api/1/login", api.Login)
	m.Get("/oauth2callback", api.OAuth2Callback)
}

func (a *MemberApi) getConfig(r *http.Request) (*oauth2.Config, error) {
	ac := appengine.NewContext(r)

	pcs := &PugConfigService{}
	config, err := pcs.Get(ac)
	if err != nil {
		return &oauth2.Config{}, err
	}

	protocol := "https"
	if appengine.IsDevAppServer() {
		protocol = "http"
	}
	redirectUrl := fmt.Sprintf("%s://%s/oauth2callback", protocol, r.Host)

	return &oauth2.Config{
		ClientID:     config.ClientId,
		ClientSecret: config.ClientSecret,
		Endpoint:     google.Endpoint,
		RedirectURL:  redirectUrl,
		Scopes:       []string{plus.PlusMeScope, plus.UserinfoEmailScope},
	}, nil
}

func (a *MemberApi) Login(c web.C, w http.ResponseWriter, r *http.Request) {
	ac := appengine.NewContext(r)

	config, err := a.getConfig(r)
	if err != nil {
		ac.Errorf("pug config get error, %v", err)
		er := ErrorResponse{
			http.StatusInternalServerError,
			[]string{"config get error"},
		}
		er.Write(w)
		return
	}

	randState := fmt.Sprintf("st%d", time.Now().UnixNano())
	authUrl := config.AuthCodeURL(randState)
	ac.Infof("auth url = %s", authUrl)

	item := &memcache.Item{
		Key:        fmt.Sprintf("%s-_-%s", randStateForAuthToMemcacheKey, randState),
		Value:      []byte(randState),
		Expiration: 3 * time.Minute,
	}
	memcache.Add(ac, item)

	http.Redirect(w, r, authUrl, http.StatusFound)
}

func (a *MemberApi) OAuth2Callback(c web.C, w http.ResponseWriter, r *http.Request) {
	ac := gengine.NewContext(r)

	p := &requestParam{
		r.Host,
		r.Method,
		r.URL.Host,
		r.URL.Fragment,
		r.URL.Path,
		r.URL.Scheme,
		r.URL.Opaque,
		r.URL.RawQuery,
		r.RemoteAddr,
		r.RequestURI,
		r.UserAgent(),
	}

	_, err := json.Marshal(p)
	if err != nil {
		log.Errorf(ac, "handler error: %#v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	config, err := a.getConfig(r)
	if err != nil {
		log.Errorf(ac, "pug config get error, %v", err)
		er := ErrorResponse{
			http.StatusInternalServerError,
			[]string{"config get error"},
		}
		er.Write(w)
		return
	}

	stateMemKey := fmt.Sprintf("%s-_-%s", randStateForAuthToMemcacheKey, r.FormValue("state"))
	item, err := gmemcache.Get(ac, stateMemKey)
	if err != nil {
		log.Errorf(ac, "memcache get error, %v", err)
		er := ErrorResponse{
			http.StatusUnauthorized,
			[]string{"unauthorized"},
		}
		er.Write(w)
		return
	}

	if r.FormValue("state") != string(item.Value) {
		log.Warningf(ac, "State doesn't match: req = %#v", "")
		er := ErrorResponse{
			http.StatusUnauthorized,
			[]string{"unauthorized"},
		}
		er.Write(w)
		return
	}

	code := r.FormValue("code")
	if code == "" {
		log.Errorf(ac, "token not found.")
	}
	token, err := config.Exchange(ac, code)
	if err != nil {
		log.Errorf(ac, "Token exchange error: %v", err)
	}
	_, err = json.Marshal(&token)
	if err != nil {
		log.Errorf(ac, "token json marshal error: %v", err)
	}

	oauthClient := config.Client(ac, token)
	s, err := gauth.New(oauthClient)
	if err != nil {
		log.Errorf(ac, "gauth new error: %v", err)
	}
	me := gauth.NewUserinfoV2MeService(s)
	ui, err := me.Get().Do()
	if err != nil {
		log.Errorf(ac, "get user info from plus error: %v", err)
	}

	m := &Member{
		Email:       ui.Email,
		Id:          ui.Id,
		Name:        ui.Name,
		FamilyName:  ui.Family_name,
		GivenName:   ui.Given_name,
		PlusLink:    ui.Link,
		PictureLink: ui.Picture,
	}
	g := goon.NewGoon(r)
	err = m.PutByLogin(g)
	if err != nil {
		log.Errorf(ac, "member put error, %v", err)
		er := ErrorResponse{
			http.StatusInternalServerError,
			[]string{"member put error"},
		}
		er.Write(w)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(m)
}

func (m *Member) PutByLogin(g *goon.Goon) error {
	return g.RunInTransaction(func(g *goon.Goon) error {
		stored := &Member{
			Email: m.Email,
		}
		err := g.Get(stored)
		if err == nil {
			stored.Name = m.Name
			stored.FamilyName = m.FamilyName
			stored.GivenName = m.GivenName
			stored.PictureLink = m.PictureLink
			stored.PlusLink = m.PlusLink

			_, err = g.Put(stored)
			if err != nil {
				return err
			}
			*m = *stored

			return nil
		} else if err == datastore.ErrNoSuchEntity {
			_, err = g.Put(m)
			if err != nil {
				return err
			}

			return nil
		} else {
			return err
		}
	}, nil)
}

func (m *Member) Load(c <-chan datastore.Property) error {
	if err := datastore.LoadStruct(m, c); err != nil {
		return err
	}

	return nil
}

func (m *Member) Save(c chan<- datastore.Property) error {
	now := time.Now()
	m.UpdatedAt = now

	if m.CreatedAt.IsZero() {
		m.CreatedAt = now
	}

	if err := datastore.SaveStruct(m, c); err != nil {
		return err
	}
	return nil
}
<commit_msg>fix(member): oauth2 libのupdateに追従して、フィールド名を修正した<commit_after>package gcpug

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/zenazn/goji/web"

	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"github.com/mjibson/goon"
	gengine "google.golang.org/appengine"
	"google.golang.org/appengine/log"
	gmemcache "google.golang.org/appengine/memcache"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	gauth "google.golang.org/api/oauth2/v2"

	plus "google.golang.org/api/plus/v1"
)

type requestParam struct {
	Host       string
	Method     string
	UrlHost    string
	Fragment   string
	Path       string
	Scheme     string
	Opaque     string
	RawQuery   string
	RemoteAddr string
	RequestURI string
	UserAgent  string
}

const (
	randStateForAuthToMemcacheKey = "randStateForAuthToMemcacheKey"
)

type Member struct {
	Email       string    `datastore:"-" goon:"id" json:"email"`    // Email
	Id          string    `json:"-"`                                // Google Id
	NickName    string    `json:"nickName"`                         // ニックネーム
	Name        string    `json:"name"`                             // G+ 名前
	FamilyName  string    `json:"familyName" datastore:",noindex"`  // G+ 姓
	GivenName   string    `json:"givenName" datastore:",noindex"`   // G+ 名
	PlusLink    string    `json:"plusLink" datastore:",noindex"`    // G+ Profile Link
	PictureLink string    `json:"pictureLink" datastore:",noindex"` // G+ Picture Link
	GithubId    string    `json:"githubId" datastore:",noindex"`    // Github Id
	QiitaId     string    `json:"qiitaId" datastore:",noindex"`     // Qiita Id
	TwitterId   string    `json:"twitterId" datastore:",noindex"`   // Twitter Id
	FacebookId  string    `json:"facebookId" datastore:",noindex"`  // Faebook Id
	BlogLink    string    `json:"blogLink" datastore:",noindex"`    // Blog Link
	CreatedAt   time.Time `json:"createdAt"`                        // 作成日時
	UpdatedAt   time.Time `json:"updatedAt"`                        // 更新日時
}

type MemberApi struct {
}

func SetUpMember(m *web.Mux) {
	api := MemberApi{}

	m.Get("/api/1/login", api.Login)
	m.Get("/oauth2callback", api.OAuth2Callback)
}

func (a *MemberApi) getConfig(r *http.Request) (*oauth2.Config, error) {
	ac := appengine.NewContext(r)

	pcs := &PugConfigService{}
	config, err := pcs.Get(ac)
	if err != nil {
		return &oauth2.Config{}, err
	}

	protocol := "https"
	if appengine.IsDevAppServer() {
		protocol = "http"
	}
	redirectUrl := fmt.Sprintf("%s://%s/oauth2callback", protocol, r.Host)

	return &oauth2.Config{
		ClientID:     config.ClientId,
		ClientSecret: config.ClientSecret,
		Endpoint:     google.Endpoint,
		RedirectURL:  redirectUrl,
		Scopes:       []string{plus.PlusMeScope, plus.UserinfoEmailScope},
	}, nil
}

func (a *MemberApi) Login(c web.C, w http.ResponseWriter, r *http.Request) {
	ac := appengine.NewContext(r)

	config, err := a.getConfig(r)
	if err != nil {
		ac.Errorf("pug config get error, %v", err)
		er := ErrorResponse{
			http.StatusInternalServerError,
			[]string{"config get error"},
		}
		er.Write(w)
		return
	}

	randState := fmt.Sprintf("st%d", time.Now().UnixNano())
	authUrl := config.AuthCodeURL(randState)
	ac.Infof("auth url = %s", authUrl)

	item := &memcache.Item{
		Key:        fmt.Sprintf("%s-_-%s", randStateForAuthToMemcacheKey, randState),
		Value:      []byte(randState),
		Expiration: 3 * time.Minute,
	}
	memcache.Add(ac, item)

	http.Redirect(w, r, authUrl, http.StatusFound)
}

func (a *MemberApi) OAuth2Callback(c web.C, w http.ResponseWriter, r *http.Request) {
	ac := gengine.NewContext(r)

	p := &requestParam{
		r.Host,
		r.Method,
		r.URL.Host,
		r.URL.Fragment,
		r.URL.Path,
		r.URL.Scheme,
		r.URL.Opaque,
		r.URL.RawQuery,
		r.RemoteAddr,
		r.RequestURI,
		r.UserAgent(),
	}

	_, err := json.Marshal(p)
	if err != nil {
		log.Errorf(ac, "handler error: %#v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	config, err := a.getConfig(r)
	if err != nil {
		log.Errorf(ac, "pug config get error, %v", err)
		er := ErrorResponse{
			http.StatusInternalServerError,
			[]string{"config get error"},
		}
		er.Write(w)
		return
	}

	stateMemKey := fmt.Sprintf("%s-_-%s", randStateForAuthToMemcacheKey, r.FormValue("state"))
	item, err := gmemcache.Get(ac, stateMemKey)
	if err != nil {
		log.Errorf(ac, "memcache get error, %v", err)
		er := ErrorResponse{
			http.StatusUnauthorized,
			[]string{"unauthorized"},
		}
		er.Write(w)
		return
	}

	if r.FormValue("state") != string(item.Value) {
		log.Warningf(ac, "State doesn't match: req = %#v", "")
		er := ErrorResponse{
			http.StatusUnauthorized,
			[]string{"unauthorized"},
		}
		er.Write(w)
		return
	}

	code := r.FormValue("code")
	if code == "" {
		log.Errorf(ac, "token not found.")
	}
	token, err := config.Exchange(ac, code)
	if err != nil {
		log.Errorf(ac, "Token exchange error: %v", err)
	}
	_, err = json.Marshal(&token)
	if err != nil {
		log.Errorf(ac, "token json marshal error: %v", err)
	}

	oauthClient := config.Client(ac, token)
	s, err := gauth.New(oauthClient)
	if err != nil {
		log.Errorf(ac, "gauth new error: %v", err)
	}
	me := gauth.NewUserinfoV2MeService(s)
	ui, err := me.Get().Do()
	if err != nil {
		log.Errorf(ac, "get user info from plus error: %v", err)
	}

	m := &Member{
		Email:       ui.Email,
		Id:          ui.Id,
		Name:        ui.Name,
		FamilyName:  ui.FamilyName,
		GivenName:   ui.GivenName,
		PlusLink:    ui.Link,
		PictureLink: ui.Picture,
	}
	g := goon.NewGoon(r)
	err = m.PutByLogin(g)
	if err != nil {
		log.Errorf(ac, "member put error, %v", err)
		er := ErrorResponse{
			http.StatusInternalServerError,
			[]string{"member put error"},
		}
		er.Write(w)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(m)
}

func (m *Member) PutByLogin(g *goon.Goon) error {
	return g.RunInTransaction(func(g *goon.Goon) error {
		stored := &Member{
			Email: m.Email,
		}
		err := g.Get(stored)
		if err == nil {
			stored.Name = m.Name
			stored.FamilyName = m.FamilyName
			stored.GivenName = m.GivenName
			stored.PictureLink = m.PictureLink
			stored.PlusLink = m.PlusLink

			_, err = g.Put(stored)
			if err != nil {
				return err
			}
			*m = *stored

			return nil
		} else if err == datastore.ErrNoSuchEntity {
			_, err = g.Put(m)
			if err != nil {
				return err
			}

			return nil
		} else {
			return err
		}
	}, nil)
}

func (m *Member) Load(c <-chan datastore.Property) error {
	if err := datastore.LoadStruct(m, c); err != nil {
		return err
	}

	return nil
}

func (m *Member) Save(c chan<- datastore.Property) error {
	now := time.Now()
	m.UpdatedAt = now

	if m.CreatedAt.IsZero() {
		m.CreatedAt = now
	}

	if err := datastore.SaveStruct(m, c); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package lib

import (
	"errors"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/image"
	"github.com/Symantec/Dominator/lib/objectcache"
	"github.com/Symantec/Dominator/lib/objectserver"
	"github.com/Symantec/Dominator/lib/srpc"
	subproto "github.com/Symantec/Dominator/proto/sub"
	"log"
)

var (
	ErrorFailedToGetObject = errors.New("get object failed")
)

type Sub struct {
	Hostname                string
	Client                  *srpc.Client
	FileSystem              *filesystem.FileSystem
	ComputedInodes          map[string]*filesystem.RegularInode
	ObjectCache             objectcache.ObjectCache
	ObjectGetter            objectserver.ObjectGetter
	requiredInodeToSubInode map[uint64]uint64
	inodesChanged           map[uint64]bool   // Required inode number.
	inodesCreated           map[uint64]string // Required inode number.
	subObjectCacheUsage     map[hash.Hash]uint64
	requiredFS              *filesystem.FileSystem
}

func BuildMissingLists(sub Sub, image *image.Image, pushComputedFiles bool,
	ignoreMissingComputedFiles bool, logger *log.Logger) (
	[]hash.Hash, map[hash.Hash]struct{}) {
	return sub.buildMissingLists(image, pushComputedFiles,
		ignoreMissingComputedFiles, logger)
}

func BuildUpdateRequest(sub Sub, image *image.Image,
	request *subproto.UpdateRequest, deleteMissingComputedFiles bool,
	logger *log.Logger) bool {
	return sub.buildUpdateRequest(image, request, deleteMissingComputedFiles,
		logger)
}

func PushObjects(sub Sub, objectsToPush map[hash.Hash]struct{},
	logger *log.Logger) error {
	return sub.pushObjects(objectsToPush, logger)
}

func (sub *Sub) String() string {
	return sub.Hostname
}
<commit_msg>Add package documentation for dom/lib.<commit_after>/*
	Package lib implements some of the core computations in the dominator.

	Package lib provides functions for computing differences between a sub and
	desired image to generate lists of objects for fetching and pushing and
	update requests. It contains some common code that both the dominator and
	the push-image subcommand of subtool share.
*/
package lib

import (
	"errors"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/image"
	"github.com/Symantec/Dominator/lib/objectcache"
	"github.com/Symantec/Dominator/lib/objectserver"
	"github.com/Symantec/Dominator/lib/srpc"
	subproto "github.com/Symantec/Dominator/proto/sub"
	"log"
)

var (
	ErrorFailedToGetObject = errors.New("get object failed")
)

// Sub should be initialised with data to be used in the package functions.
type Sub struct {
	Hostname                string
	Client                  *srpc.Client
	FileSystem              *filesystem.FileSystem
	ComputedInodes          map[string]*filesystem.RegularInode
	ObjectCache             objectcache.ObjectCache
	ObjectGetter            objectserver.ObjectGetter
	requiredInodeToSubInode map[uint64]uint64
	inodesChanged           map[uint64]bool   // Required inode number.
	inodesCreated           map[uint64]string // Required inode number.
	subObjectCacheUsage     map[hash.Hash]uint64
	requiredFS              *filesystem.FileSystem
}

// BuildMissingLists will construct lists of objects to be fetched by the sub
// from an object server and the list of computed objects that should be pushed
// to the sub. The lists are generated by comparing the contents of
// sub.FileSystem and sub.ObjectCache with the desired image.
// If pushComputedFiles is true then the list of computed files to be pushed is
// generated.
// If ignoreMissingComputedFiles is true then missing computed files are
// ignored, otherwise these missing files lead to an error and early termination
// of the function.
// Computed file metadata are specified by sub.ComputedInodes.
// BuildMissingLists returns a slice of objects to fetch and a map of files to
// push. The map is nil if there are missing computed files.
func BuildMissingLists(sub Sub, image *image.Image, pushComputedFiles bool,
	ignoreMissingComputedFiles bool, logger *log.Logger) (
	[]hash.Hash, map[hash.Hash]struct{}) {
	return sub.buildMissingLists(image, pushComputedFiles,
		ignoreMissingComputedFiles, logger)
}

// BuildUpdateRequest will build an update request which can be sent to the sub.
// If deleteMissingComputedFiles is true then missing computed files are deleted
// on the sub, else missing computed files lead to the function failing.
// It returns true if the function failed due to missing computed files.
func BuildUpdateRequest(sub Sub, image *image.Image,
	request *subproto.UpdateRequest, deleteMissingComputedFiles bool,
	logger *log.Logger) bool {
	return sub.buildUpdateRequest(image, request, deleteMissingComputedFiles,
		logger)
}

// PushObjects will push the list of files given by objectsToPush to the sub.
// File data are obtained from sub.ObjectGetter.
func PushObjects(sub Sub, objectsToPush map[hash.Hash]struct{},
	logger *log.Logger) error {
	return sub.pushObjects(objectsToPush, logger)
}

// String returns the hostname of the sub.
func (sub *Sub) String() string {
	return sub.Hostname
}
<|endoftext|>