/* * Copyright 2018 resin.io * * 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. */ 'use strict' const electron = require('electron') const EventEmitter = require('events') const createInactivityTimer = require('inactivity-timer') const debug = require('debug')('etcher:update-lock') const analytics = require('./analytics') const settings = require('../models/settings') /* eslint-disable no-magic-numbers, callback-return */ /** * Interaction timeout in milliseconds (defaults to 5 minutes) * @type {Number} * @constant */ const INTERACTION_TIMEOUT_MS = settings.has('interactionTimeout') ? parseInt(settings.get('interactionTimeout'), 10) : 5 * 60 * 1000 /** * Resin Update Lock * @class */ class UpdateLock extends EventEmitter { /** * @summary Resin Update Lock * @example * new UpdateLock() */ constructor () { super() this.paused = false this.on('inactive', UpdateLock.onInactive) this.lockTimer = createInactivityTimer(INTERACTION_TIMEOUT_MS, () => { debug('inactive') this.emit('inactive') }) } /** * @summary Inactivity event handler, releases the resin update lock on inactivity * @private * @example * this.on('inactive', onInactive) */ static onInactive () { if (settings.get('resinUpdateLock')) { UpdateLock.check((checkError, isLocked) => { debug('inactive-check', Boolean(checkError)) if (checkError) { analytics.logException(checkError) } if (isLocked) { UpdateLock.release((error) => { debug('inactive-release', Boolean(error)) if (error) { analytics.logException(error) } }) } }) } } /** * @summary Acquire the update lock * @private * @param {Function} callback - callback(error) * @example * UpdateLock.acquire((error) => { * // ... * }) */ static acquire (callback) { debug('lock') if (settings.get('resinUpdateLock')) { electron.ipcRenderer.once('resin-update-lock', (event, error) => { callback(error) }) electron.ipcRenderer.send('resin-update-lock', 'lock') } else { callback(new Error('Update lock disabled')) } } /** * @summary Release the update lock * @private * @param {Function} callback - callback(error) * @example * UpdateLock.release((error) => { * // ... * }) */ static release (callback) { debug('unlock') if (settings.get('resinUpdateLock')) { electron.ipcRenderer.once('resin-update-lock', (event, error) => { callback(error) }) electron.ipcRenderer.send('resin-update-lock', 'unlock') } else { callback(new Error('Update lock disabled')) } } /** * @summary Check the state of the update lock * @private * @param {Function} callback - callback(error, isLocked) * @example * UpdateLock.check((error, isLocked) => { * if (isLocked) { * // ... * } * }) */ static check (callback) { debug('check') if (settings.get('resinUpdateLock')) { electron.ipcRenderer.once('resin-update-lock', (event, error, isLocked) => { callback(error, isLocked) }) electron.ipcRenderer.send('resin-update-lock', 'check') } else { callback(new Error('Update lock disabled')) } } /** * @summary Extend the lock timer * @example * updateLock.extend() */ extend () { debug('extend') if (this.paused) { debug('extend:paused') return } this.lockTimer.signal() // When extending, check that we have the lock, // and acquire it, if not if (settings.get('resinUpdateLock')) { UpdateLock.check((checkError, isLocked) => { if (checkError) { analytics.logException(checkError) } if (!isLocked) { UpdateLock.acquire((error) => { if (error) { analytics.logException(error) } debug('extend-acquire', Boolean(error)) }) } }) } } /** * @summary Clear the lock timer * @example * updateLock.clearTimer() */ clearTimer () { debug('clear') this.lockTimer.clear() } /** * @summary Clear the lock timer, and pause extension, avoiding triggering until resume()d * @example * updateLock.pause() */ pause () { debug('pause') this.paused = true this.clearTimer() } /** * @summary Un-pause lock extension, and restart the timer * @example * updateLock.resume() */ resume () { debug('resume') this.paused = false this.extend() } } module.exports = new UpdateLock()