示例#1
0
文件: state.go 项目: pasinskim/mender
func (a *AuthorizeWaitState) Handle(ctx *StateContext, c Controller) (State, bool) {
	log.Debugf("handle authorize wait state")
	intvl := c.GetUpdatePollInterval()

	log.Debugf("wait %v before next authorization attempt", intvl)
	return a.StateAfterWait(bootstrappedState, a, intvl)
}
示例#2
0
func (u *AuthClient) Request(api ApiRequester, server string, dataSrc AuthDataMessenger) ([]byte, error) {

	req, err := makeAuthRequest(server, dataSrc)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to build authorization request")
	}

	log.Debugf("making authorization request to server %s with req: %s", server, req)
	rsp, err := api.Do(req)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to execute authorization request")
	}
	defer rsp.Body.Close()

	log.Debugf("got response: %v", rsp)

	switch rsp.StatusCode {
	case http.StatusUnauthorized:
		return nil, AuthErrorUnauthorized
	case http.StatusOK:
		log.Debugf("receive response data")
		data, err := ioutil.ReadAll(rsp.Body)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to receive authorization response data")
		}

		log.Debugf("received response data %v", data)
		return data, nil
	default:
		return nil, errors.Errorf("unexpected authorization status %v", rsp.StatusCode)
	}
}
示例#3
0
文件: state.go 项目: pasinskim/mender
func (u *UpdateCheckWaitState) Handle(ctx *StateContext, c Controller) (State, bool) {
	log.Debugf("handle update check wait state")

	intvl := c.GetUpdatePollInterval()

	log.Debugf("wait %v before next poll", intvl)
	return u.StateAfterWait(updateCheckState, u, intvl)
}
示例#4
0
文件: auth.go 项目: pasinskim/mender
func (m *MenderAuthManager) MakeAuthRequest() (*client.AuthRequest, error) {

	var err error
	authd := client.AuthReqData{}

	idata, err := m.idSrc.Get()
	if err != nil {
		return nil, errors.Wrapf(err, "failed to obtain identity data")
	}

	authd.IdData = idata

	// fill device public key
	authd.Pubkey, err = m.keyStore.PublicPEM()
	if err != nil {
		return nil, errors.Wrapf(err, "failed to obtain device public key")
	}

	tentok := strings.TrimSpace(string(m.tenantToken))

	log.Debugf("tenant token: %s", tentok)

	// fill tenant token
	authd.TenantToken = string(tentok)

	// fetch sequence number
	num, err := m.seqNum.Get()
	if err != nil {
		return nil, errors.Wrapf(err, "failed to obtain sequence number")
	}
	authd.SeqNumber = num

	log.Debugf("authorization data: %v", authd)

	reqdata, err := authd.ToBytes()
	if err != nil {
		return nil, errors.Wrapf(err, "failed to convert auth request data")
	}

	// generate signature
	sig, err := m.keyStore.Sign(reqdata)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to sign auth request")
	}

	return &client.AuthRequest{
		Data:      reqdata,
		Token:     client.AuthToken(tentok),
		Signature: sig,
	}, nil
}
示例#5
0
文件: state.go 项目: pasinskim/mender
func (u *UpdateInstallState) Handle(ctx *StateContext, c Controller) (State, bool) {

	// make sure to close the stream with image data
	defer u.imagein.Close()

	// start deployment logging
	if err := DeploymentLogger.Enable(u.update.ID); err != nil {
		return NewUpdateErrorState(NewTransientError(err), u.update), false
	}

	if err := StoreStateData(ctx.store, StateData{
		Id:         u.Id(),
		UpdateInfo: u.update,
	}); err != nil {
		log.Errorf("failed to store state data in install state: %v", err)
		return NewUpdateErrorState(NewTransientError(err), u.update), false
	}

	// report installing, don't care about errors
	c.ReportUpdateStatus(u.update, client.StatusInstalling)

	log.Debugf("handle update install state")

	if err := c.InstallUpdate(u.imagein, u.size); err != nil {
		log.Errorf("update install failed: %s", err)
		return NewUpdateErrorState(NewTransientError(err), u.update), false
	}

	return NewRebootState(u.update), false
}
示例#6
0
// Returns a byte stream which is a download of the given link.
func (u *UpdateClient) FetchUpdate(api ApiRequester, url string) (io.ReadCloser, int64, error) {
	req, err := makeUpdateFetchRequest(url)
	if err != nil {
		return nil, -1, errors.Wrapf(err, "failed to create update fetch request")
	}

	r, err := api.Do(req)
	if err != nil {
		log.Error("Can not fetch update image: ", err)
		return nil, -1, errors.Wrapf(err, "update fetch request failed")
	}

	log.Debugf("Received fetch update response %v+", r)

	if r.StatusCode != http.StatusOK {
		r.Body.Close()
		log.Errorf("Error fetching shcheduled update info: code (%d)", r.StatusCode)
		return nil, -1, errors.New("Error receiving scheduled update information.")
	}

	if r.ContentLength < 0 {
		r.Body.Close()
		return nil, -1, errors.New("Will not continue with unknown image size.")
	} else if r.ContentLength < u.minImageSize {
		r.Body.Close()
		log.Errorf("Image smaller than expected. Expected: %d, received: %d", u.minImageSize, r.ContentLength)
		return nil, -1, errors.New("Image size is smaller than expected. Aborting.")
	}

	return r.Body, r.ContentLength, nil
}
示例#7
0
// Open an entry for reading.
func (d DirStore) OpenRead(name string) (io.ReadCloser, error) {
	f, err := os.Open(path.Join(d.basepath, name))
	if err != nil {
		log.Debugf("I/O read error for entry %v: %v", name, err)
		return nil, err
	}
	return f, nil
}
示例#8
0
// This will be run manually from command line ONLY
func doRootfs(device installer.UInstaller, args runOptionsType, dt string) error {
	var image io.ReadCloser
	var imageSize int64
	var err error
	var upclient client.Updater

	if args == (runOptionsType{}) {
		return errors.New("rootfs called without needed parameters")
	}

	log.Debug("Starting device update.")

	updateLocation := *args.imageFile
	if strings.HasPrefix(updateLocation, "http:") ||
		strings.HasPrefix(updateLocation, "https:") {
		log.Infof("Performing remote update from: [%s].", updateLocation)

		var ac *client.ApiClient
		// we are having remote update
		ac, err = client.New(args.Config)
		if err != nil {
			return errors.New("Can not initialize client for performing network update.")
		}
		upclient = client.NewUpdate()

		log.Debug("Client initialized. Start downloading image.")

		image, imageSize, err = upclient.FetchUpdate(ac, updateLocation)
		log.Debugf("Image downloaded: %d [%v] [%v]", imageSize, image, err)
	} else {
		// perform update from local file
		log.Infof("Start updating from local image file: [%s]", updateLocation)
		image, imageSize, err = FetchUpdateFromFile(updateLocation)

		log.Debugf("Feting update from file results: [%v], %d, %v", image, imageSize, err)
	}

	if image == nil || err != nil {
		return errors.Wrapf(err, "rootfs: error while updating image from command line")
	}
	defer image.Close()

	return installer.Install(image, dt, device)
}
示例#9
0
文件: state.go 项目: pasinskim/mender
func (iu *InventoryUpdateState) Handle(ctx *StateContext, c Controller) (State, bool) {

	err := c.InventoryRefresh()
	if err != nil {
		log.Warnf("failed to refresh inventory: %v", err)
	} else {
		log.Debugf("inventory refresh complete")
	}
	return updateCheckWaitState, false
}
示例#10
0
func (m *mender) needsBootstrap() bool {
	if m.forceBootstrap {
		return true
	}

	if !m.authMgr.HasKey() {
		log.Debugf("needs keys")
		return true
	}

	return false
}
示例#11
0
文件: state.go 项目: pasinskim/mender
func (i *InitState) Handle(ctx *StateContext, c Controller) (State, bool) {

	// make sure that deployment logging is disabled
	DeploymentLogger.Disable()

	log.Debugf("handle init state")
	if err := c.Bootstrap(); err != nil {
		log.Errorf("bootstrap failed: %s", err)
		return NewErrorState(err), false
	}
	return bootstrappedState, false
}
示例#12
0
文件: state.go 项目: pasinskim/mender
func (b *BootstrappedState) Handle(ctx *StateContext, c Controller) (State, bool) {
	log.Debugf("handle bootstrapped state")
	if err := c.Authorize(); err != nil {
		log.Errorf("authorize failed: %v", err)
		if !err.IsFatal() {
			return authorizeWaitState, false
		} else {
			return NewErrorState(err), false
		}
	}

	return authorizedState, false
}
示例#13
0
文件: state.go 项目: pasinskim/mender
// wait and return true if wait was completed (false if canceled)
func (cs *CancellableState) Wait(wait time.Duration) bool {
	ticker := time.NewTicker(wait)

	defer ticker.Stop()
	select {
	case <-ticker.C:
		log.Debugf("wait complete")
		return true
	case <-cs.cancel:
		log.Infof("wait canceled")
	}

	return false
}
示例#14
0
func (d *device) getInactivePartition() (string, error) {
	inactivePartition, err := d.GetInactive()
	if err != nil {
		return "", errors.New("Error obtaining inactive partition: " + err.Error())
	}

	log.Debugf("Marking inactive partition (%s) as the new boot candidate.", inactivePartition)

	partitionNumber := inactivePartition[len(inactivePartition)-1:]
	if _, err := strconv.Atoi(partitionNumber); err != nil {
		return "", errors.New("Invalid inactive partition: " + inactivePartition)
	}

	return partitionNumber, nil
}
示例#15
0
func (p *partitions) getAndCacheActivePartition(rootChecker func(StatCommander, string, *syscall.Stat_t) bool,
	getMountedDevices func(string) ([]string, error)) (string, error) {
	mountData, err := p.Command("mount").Output()
	if err != nil {
		return "", err
	}

	mountCandidate := getRootCandidateFromMount(mountData)
	rootDevice := getRootDevice(p)
	if rootDevice == nil {
		return "", errors.New("Can not find root device")
	}

	// First check if mountCandidate matches rootDevice
	if mountCandidate != "" {
		if rootChecker(p, mountCandidate, rootDevice) {
			p.active = mountCandidate
			log.Debugf("Setting active partition from mount candidate: %s", p.active)
			return p.active, nil
		}
		// If not see if we are lucky somewhere else
	}

	const devDir string = "/dev"

	mountedDevices, err := getMountedDevices(devDir)
	if err != nil {
		return "", err
	}

	activePartition, err := getRootFromMountedDevices(p, rootChecker, mountedDevices, rootDevice)
	if err != nil {
		return "", err
	}

	bootEnvBootPart, err := getBootEnvActivePartition(p.BootEnvReadWriter)
	if err != nil {
		return "", err
	}
	if checkBootEnvAndRootPartitionMatch(bootEnvBootPart, activePartition) {
		p.active = activePartition
		log.Debug("Setting active partition: ", activePartition)
		return p.active, nil
	}

	log.Error("Mounted root '" + activePartition + "' does not match boot environment mender_boot_part: " + bootEnvBootPart)
	return "", ErrorNoMatchBootPartRootPart
}
示例#16
0
文件: state.go 项目: pasinskim/mender
func (usr *UpdateStatusReportState) Handle(ctx *StateContext, c Controller) (State, bool) {

	// start deployment logging; no error checking
	// we can do nothing here; either we will have the logs or not...
	DeploymentLogger.Enable(usr.update.ID)

	if err := StoreStateData(ctx.store, StateData{
		Id:           usr.Id(),
		UpdateInfo:   usr.update,
		UpdateStatus: usr.status,
	}); err != nil {
		log.Errorf("failed to store state data in update status report state: %v",
			err)
		return NewReportErrorState(usr.update, usr.status), false
	}

	err, wasInterupted := usr.trySend(sendStatus, c)
	if wasInterupted {
		return usr, false
	}
	if err != nil {
		log.Errorf("failed to send status to server: %v", err)
		return NewReportErrorState(usr.update, usr.status), false
	}

	if usr.status == client.StatusFailure {
		log.Debugf("attempting to upload deployment logs for failed update")
		err, wasInterupted = usr.trySend(sendDeploymentLogs, c)
		if wasInterupted {
			return usr, false
		}
		if err != nil {
			log.Errorf("failed to send deployment logs to server: %v", err)
			return NewReportErrorState(usr.update, usr.status), false
		}
	}

	log.Debug("reporting complete")
	// stop deployment logging as the update is completed at this point
	DeploymentLogger.Disable()
	// status reported, logs uploaded if needed, remove state data
	RemoveStateData(ctx.store)

	return initState, false
}
示例#17
0
文件: state.go 项目: pasinskim/mender
func (uc *UpdateCommitState) Handle(ctx *StateContext, c Controller) (State, bool) {

	// start deployment logging
	if err := DeploymentLogger.Enable(uc.update.ID); err != nil {
		return NewUpdateErrorState(NewTransientError(err), uc.update), false
	}

	log.Debugf("handle update commit state")

	err := c.CommitUpdate()
	if err != nil {
		log.Errorf("update commit failed: %s", err)
		// TODO: should we rollback?
		return NewUpdateStatusReportState(uc.update, client.StatusFailure), false
	}

	// update is commited now; report status
	return NewUpdateStatusReportState(uc.update, client.StatusSuccess), false
}
示例#18
0
func (d *device) InstallUpdate(image io.ReadCloser, size int64) error {

	log.Debugf("Trying to install update of size: %d", size)
	if image == nil || size < 0 {
		return errors.New("Have invalid update. Aborting.")
	}

	inactivePartition, err := d.GetInactive()
	if err != nil {
		return err
	}

	b := &BlockDevice{Path: inactivePartition}

	if bsz, err := b.Size(); err != nil {
		log.Errorf("failed to read size of block device %s: %v",
			inactivePartition, err)
		return err
	} else if bsz < uint64(size) {
		log.Errorf("update (%v bytes) is larger than the size of device %s (%v bytes)",
			size, inactivePartition, bsz)
		return syscall.ENOSPC
	}

	w, err := io.Copy(b, image)
	if err != nil {
		log.Errorf("failed to write image data to device %v: %v",
			inactivePartition, err)
	}

	log.Infof("wrote %v/%v bytes of update to device %v",
		w, size, inactivePartition)

	if cerr := b.Close(); cerr != nil {
		log.Errorf("closing device %v failed: %v", inactivePartition, cerr)
		if err != nil {
			return cerr
		}
	}

	return err
}
示例#19
0
func (k *Keystore) Load() error {
	inf, err := k.store.OpenRead(k.keyName)
	if err != nil {
		if os.IsNotExist(err) {
			log.Debugf("private key does not exist")
			return errNoKeys
		} else {
			return err
		}
	}
	defer inf.Close()

	k.private, err = loadFromPem(inf)
	if err != nil {
		log.Errorf("failed to load key: %s", err)
		return err
	}

	return nil
}
示例#20
0
func loadFromPem(in io.Reader) (*rsa.PrivateKey, error) {
	data, err := ioutil.ReadAll(in)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(data)
	if block == nil {
		return nil, errors.New("failed to decode block")
	}

	log.Debugf("block type: %s", block.Type)

	key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	return key, nil
}
示例#21
0
文件: state.go 项目: pasinskim/mender
func (u *UpdateFetchState) Handle(ctx *StateContext, c Controller) (State, bool) {
	if err := StoreStateData(ctx.store, StateData{
		Id:         u.Id(),
		UpdateInfo: u.update,
	}); err != nil {
		log.Errorf("failed to store state data in fetch state: %v", err)
		return NewUpdateErrorState(NewTransientError(err), u.update), false
	}

	// report downloading, don't care about errors
	c.ReportUpdateStatus(u.update, client.StatusDownloading)

	log.Debugf("handle update fetch state")
	in, size, err := c.FetchUpdate(u.update.Image.URI)
	if err != nil {
		log.Errorf("update fetch failed: %s", err)
		return NewUpdateErrorState(NewTransientError(err), u.update), false
	}

	return NewUpdateInstallState(in, size, u.update), false
}
示例#22
0
// Report status information to the backend
func (i *InventoryClient) Submit(api ApiRequester, url string, data interface{}) error {
	req, err := makeInventorySubmitRequest(url, data)
	if err != nil {
		return errors.Wrapf(err, "failed to prepare inventory submit request")
	}

	r, err := api.Do(req)
	if err != nil {
		log.Error("failed to submit inventory data: ", err)
		return errors.Wrapf(err, "inventory submit failed")
	}

	defer r.Body.Close()

	if r.StatusCode != http.StatusOK {
		log.Errorf("got unexpected HTTP status when submitting to inventory: %v", r.StatusCode)
		return errors.Errorf("inventory submit failed, bad status %v", r.StatusCode)
	}
	log.Debugf("inventory update sent, response %v", r)

	return nil
}
示例#23
0
// Report status information to the backend
func (u *StatusClient) Report(api ApiRequester, url string, report StatusReport) error {
	req, err := makeStatusReportRequest(url, report)
	if err != nil {
		return errors.Wrapf(err, "failed to prepare status report request")
	}

	r, err := api.Do(req)
	if err != nil {
		log.Error("failed to report status: ", err)
		return errors.Wrapf(err, "reporting status failed")
	}

	defer r.Body.Close()

	// HTTP 204 No Content
	if r.StatusCode != http.StatusNoContent {
		log.Errorf("got unexpected HTTP status when reporting status: %v", r.StatusCode)
		return errors.Errorf("reporting status failed, bad status %v", r.StatusCode)
	}
	log.Debugf("status reported, response %v", r)

	return nil
}
示例#24
0
// Report status information to the backend
func (u *LogUploadClient) Upload(api ApiRequester, url string, logs LogData) error {
	req, err := makeLogUploadRequest(url, &logs)
	if err != nil {
		return errors.Wrapf(err, "failed to prepare log upload request")
	}

	r, err := api.Do(req)
	if err != nil {
		log.Error("failed to upload logs: ", err)
		return errors.Wrapf(err, "uploading logs failed")
	}

	defer r.Body.Close()

	// HTTP 204 No Content
	if r.StatusCode != http.StatusNoContent {
		log.Errorf("got unexpected HTTP status when uploading log: %v", r.StatusCode)
		return errors.Errorf("uploading logs failed, bad status %v", r.StatusCode)
	}
	log.Debugf("logs uploaded, response %v", r)

	return nil
}
示例#25
0
文件: state.go 项目: pasinskim/mender
func (u *UpdateCheckState) Handle(ctx *StateContext, c Controller) (State, bool) {
	log.Debugf("handle update check state")
	update, err := c.CheckUpdate()

	if err != nil {
		if err.Cause() == os.ErrExist {
			// We are already running image which we are supposed to install.
			// Just report successful update and return to normal operations.
			return NewUpdateStatusReportState(*update, client.StatusSuccess), false
		}

		log.Errorf("update check failed: %s", err)
		// maybe transient error?
		return NewErrorState(err), false
	}

	if update != nil {
		// custom state data?
		return NewUpdateFetchState(*update), false
	}

	return inventoryUpdateState, false
}
示例#26
0
// Check if new update is available. In case of errors, returns nil and error
// that occurred. If no update is available *UpdateResponse is nil, otherwise it
// contains update information.
func (m *mender) CheckUpdate() (*client.UpdateResponse, menderError) {
	currentImageID := m.GetCurrentImageID()
	//TODO: if currentImageID == "" {
	// 	return errors.New("")
	// }

	haveUpdate, err := m.updater.GetScheduledUpdate(m.api.Request(m.authToken),
		m.config.ServerURL)

	if err != nil {
		// remove authentication token if device is not authorized
		if err == client.ErrNotAuthorized {
			if remErr := m.authMgr.RemoveAuthToken(); remErr != nil {
				log.Warn("can not remove rejected authentication token")
			}
		}
		log.Error("Error receiving scheduled update data: ", err)
		return nil, NewTransientError(err)
	}

	if haveUpdate == nil {
		log.Debug("no updates available")
		return nil, nil
	}
	update, ok := haveUpdate.(client.UpdateResponse)
	if !ok {
		return nil, NewTransientError(errors.Errorf("not an update response?"))
	}

	log.Debugf("received update response: %v", update)

	if update.Image.YoctoID == currentImageID {
		log.Info("Attempting to upgrade to currently installed image ID, not performing upgrade.")
		return &update, NewTransientError(os.ErrExist)
	}
	return &update, nil
}
示例#27
0
func (p *partitions) getAndCacheInactivePartition() (string, error) {
	if p.rootfsPartA == "" || p.rootfsPartB == "" {
		return "", ErrorPartitionNumberNotSet
	}
	if p.rootfsPartA == p.rootfsPartB {
		return "", ErrorPartitionNumberSame
	}

	active, err := p.GetActive()
	if err != nil {
		return "", err
	}

	if active == p.rootfsPartA {
		p.inactive = p.rootfsPartB
	} else if active == p.rootfsPartB {
		p.inactive = p.rootfsPartA
	} else {
		return "", ErrorPartitionNoMatchActive
	}

	log.Debugf("Detected inactive partition %s, based on active partition %s", p.inactive, active)
	return p.inactive, nil
}