Exemplo n.º 1
0
func getManifestData(dataType, manifestFile string) string {
	// This is where Yocto stores buid information
	manifest, err := os.Open(manifestFile)
	if err != nil {
		log.Error("Can not read manifest data.")
		return ""
	}

	scanner := bufio.NewScanner(manifest)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		log.Debug("Read data from device manifest file: ", line)
		if strings.HasPrefix(line, dataType) {
			log.Debug("Found needed line: ", line)
			lineID := strings.Split(line, "=")
			if len(lineID) != 2 {
				log.Errorf("Broken device manifest file: (%v)", lineID)
				return ""
			}
			log.Debug("Current manifest data: ", strings.TrimSpace(lineID[1]))
			return strings.TrimSpace(lineID[1])
		}
	}
	if err := scanner.Err(); err != nil {
		log.Error(err)
	}
	return ""
}
Exemplo n.º 2
0
func processUpdateResponse(response *http.Response) (interface{}, error) {
	log.Debug("Received response:", response.Status)

	respBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	switch response.StatusCode {
	case http.StatusOK:
		log.Debug("Have update available")

		var data UpdateResponse
		if err := json.Unmarshal(respBody, &data); err != nil {
			return nil, errors.Wrapf(err, "failed to parse response")
		}

		if err := validateGetUpdate(data); err != nil {
			return nil, err
		}

		return data, nil

	case http.StatusNoContent:
		log.Debug("No update available")
		return nil, nil

	case http.StatusUnauthorized:
		log.Warn("Client not authorized to get update schedule.")
		return nil, ErrNotAuthorized

	default:
		return nil, errors.New("Invalid response received from server")
	}
}
Exemplo n.º 3
0
func (p *partitions) GetActive() (string, error) {
	if p.active != "" {
		log.Debug("Active partition: ", p.active)
		return p.active, nil
	}
	return p.getAndCacheActivePartition(isMountedRoot, getAllMountedDevices)
}
Exemplo n.º 4
0
func (p *partitions) GetInactive() (string, error) {
	if p.inactive != "" {
		log.Debug("Inactive partition: ", p.inactive)
		return p.inactive, nil
	}
	return p.getAndCacheInactivePartition()
}
Exemplo n.º 5
0
func getOrSetEnvironmentVariable(cmd *exec.Cmd) (BootVars, error) {
	cmdReader, err := cmd.StdoutPipe()

	if err != nil {
		log.Errorln("Error creating StdoutPipe: ", err)
		return nil, err
	}

	scanner := bufio.NewScanner(cmdReader)

	err = cmd.Start()
	if err != nil {
		return nil, err
	}

	var env_variables = make(BootVars)

	for scanner.Scan() {
		log.Debug("Have U-Boot variable: ", scanner.Text())
		splited_line := strings.Split(scanner.Text(), "=")

		//we are having empty line (usually at the end of output)
		if scanner.Text() == "" {
			continue
		}

		//we have some malformed data or Warning/Error
		if len(splited_line) != 2 {
			log.Error("U-Boot variable malformed or error occured")
			return nil, errors.New("Invalid U-Boot variable or error: " + scanner.Text())
		}

		env_variables[splited_line[0]] = splited_line[1]
	}

	err = cmd.Wait()
	if err != nil {
		return nil, err
	}

	if len(env_variables) > 0 {
		log.Debug("List of U-Boot variables:", env_variables)
	}

	return env_variables, err
}
Exemplo n.º 6
0
func (a *AuthorizedState) Handle(ctx *StateContext, c Controller) (State, bool) {
	// restore previous state information
	sd, err := LoadStateData(ctx.store)

	// tricky part - try to figure out if there's an update in progress, if so
	// proceed to UpdateCommitState; in case of errors that occur either now or
	// when the update was being feched/installed previously, try to handle them
	// gracefully

	// handle easy case first, no update info present, means no update in progress
	if err != nil && os.IsNotExist(err) {
		log.Debug("no update in progress, proceed")
		return inventoryUpdateState, false
	}

	if err != nil {
		log.Errorf("failed to restore update information: %v", err)
		me := NewFatalError(errors.Wrapf(err, "failed to restore update information"))

		// report update error with unknown deployment ID
		// TODO: fill current image ID?
		return NewUpdateErrorState(me, client.UpdateResponse{
			ID: "unknown",
		}), false
	}

	log.Infof("handling state: %v", sd.Id)

	// chack last known status
	switch sd.Id {
	// update process was finished; check what is the status of update
	case MenderStateReboot:
		return NewUpdateVerifyState(sd.UpdateInfo), false

		// update prosess was initialized but stopped in the middle
	case MenderStateUpdateFetch, MenderStateUpdateInstall:
		// TODO: for now we just continue sending error report to the server
		// in future we might want to have some recovery option here
		me := NewFatalError(errors.New("update process was interrupted"))
		return NewUpdateErrorState(me, sd.UpdateInfo), false

		// there was some error while reporting update status
	case MenderStateUpdateStatusReport:
		log.Infof("restoring update status report state")
		if sd.UpdateStatus != client.StatusFailure &&
			sd.UpdateStatus != client.StatusSuccess {
			return NewUpdateStatusReportState(sd.UpdateInfo, client.StatusError), false
		}
		// check what is exact state of update before reporting anything
		return NewUpdateVerifyState(sd.UpdateInfo), false

		// this should not happen
	default:
		log.Errorf("got invalid update state: %v", sd.Id)
		me := NewFatalError(errors.New("got invalid update state"))
		return NewUpdateErrorState(me, sd.UpdateInfo), false
	}
}
Exemplo n.º 7
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)
}
Exemplo n.º 8
0
func (d *device) Rollback() error {
	// first get inactive partition
	inactivePartition, err := d.getInactivePartition()
	if err != nil {
		return err
	}
	log.Infof("setting partition for rollback: %s", inactivePartition)

	err = d.WriteEnv(BootVars{"mender_boot_part": inactivePartition, "upgrade_available": "0"})
	if err != nil {
		return err
	}
	log.Debug("Marking inactive partition as a boot candidate successful.")
	return nil
}
Exemplo n.º 9
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
}
Exemplo n.º 10
0
func readConfigFile(config interface{}, fileName string) error {
	log.Debug("Reading Mender configuration from file " + fileName)
	conf, err := ioutil.ReadFile(fileName)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(conf, &config); err != nil {
		switch err.(type) {
		case *json.SyntaxError:
			return errors.New("Error parsing mender configuration file: " + err.Error())
		}
		return errors.New("Error parsing config file: " + err.Error())
	}
	return nil
}
Exemplo n.º 11
0
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
}
Exemplo n.º 12
0
func (d *device) EnableUpdatedPartition() error {

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

	log.Info("Enabling partition with new image installed to be a boot candidate: ", string(inactivePartition))
	// For now we are only setting boot variables
	err = d.WriteEnv(BootVars{"upgrade_available": "1", "mender_boot_part": inactivePartition, "bootcount": "0"})
	if err != nil {
		return err
	}

	log.Debug("Marking inactive partition as a boot candidate successful.")

	return nil
}
Exemplo n.º 13
0
func (u *UpdateClient) getUpdateInfo(api ApiRequester, process RequestProcessingFunc,
	server string) (interface{}, error) {
	req, err := makeUpdateCheckRequest(server)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to create update check request")
	}

	r, err := api.Do(req)
	if err != nil {
		log.Debug("Sending request error: ", err)
		return nil, errors.Wrapf(err, "update check request failed")
	}

	defer r.Body.Close()

	data, err := process(r)
	return data, err
}
Exemplo n.º 14
0
func (uv *UpdateVerifyState) Handle(ctx *StateContext, c Controller) (State, bool) {

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

	log.Debug("handle update verify state")

	// look at the update flag
	has, haserr := c.HasUpgrade()
	if haserr != nil {
		log.Errorf("has upgrade check failed: %v", haserr)
		me := NewFatalError(errors.Wrapf(haserr, "failed to perform 'has upgrade' check"))
		return NewUpdateErrorState(me, uv.update), false
	}

	if has {
		if uv.update.Image.YoctoID == c.GetCurrentImageID() {
			log.Infof("successfully running with new image %v", c.GetCurrentImageID())
			// update info and has upgrade flag are there, we're running the new
			// update, everything looks good, proceed with committing
			return NewUpdateCommitState(uv.update), false
		} else {
			// seems like we're running in a different image than expected from update
			// information, best report an error
			log.Errorf("running with image %v, expected updated image %v",
				c.GetCurrentImageID(), uv.update.Image.YoctoID)
			return NewUpdateStatusReportState(uv.update, client.StatusFailure), false
		}
	}

	// HasUpgrade() returned false
	// most probably booting new image failed and u-boot rolledback to
	// previous image
	log.Errorf("update info for deployment %v present, but update flag is not set;"+
		" running rollback image (previous active partition)",
		uv.update.ID)
	return NewUpdateStatusReportState(uv.update, client.StatusFailure), false
}
Exemplo n.º 15
0
func TestDeploymentLoggingHook(t *testing.T) {
	tempDir, _ := ioutil.TempDir("", "logs")
	defer os.RemoveAll(tempDir)

	deploymentLogger := NewDeploymentLogManager(tempDir)
	log.AddHook(NewDeploymentLogHook(deploymentLogger))

	log.Info("test1")

	deploymentLogger.Enable("1111-2222")
	logFile := fmt.Sprintf(logFileNameScheme, 1, "1111-2222")
	fileLocation := path.Join(tempDir, logFile)

	log.Debug("test2")
	deploymentLogger.Disable()

	log.Info("test3")

	// test correct format of log messages
	if !logFileContains(fileLocation, `{"level":"debug","message":"test2","timestamp":"`) {
		t.FailNow()
	}
}
Exemplo n.º 16
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
}