| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:file/file.dart'; |
| import 'package:file/local.dart'; |
| import 'package:meta/meta.dart'; |
| import 'package:process/process.dart'; |
| import 'package:uuid/uuid.dart'; |
| |
| import 'operation_result.dart'; |
| import 'ssh_key_manager.dart'; |
| import 'tar.dart'; |
| |
| /// Paves a prebuilt system image to a Fuchsia device. |
| /// |
| /// The Fuchsia device must be in zedboot mode. |
| @immutable |
| class ImagePaver { |
| /// Creates a new image paver. |
| /// |
| /// All properties must not be null. |
| const ImagePaver({ |
| this.processManager = const LocalProcessManager(), |
| this.fs = const LocalFileSystem(), |
| this.tar = const SystemTar(), |
| this.sshKeyManagerProvider = SystemSshKeyManager.defaultProvider, |
| }) : assert(processManager != null), |
| assert(fs != null), |
| assert(tar != null); |
| |
| /// The [ProcessManager] used to launch the boot server, `tar`, |
| /// and `ssh-keygen`. |
| final ProcessManager processManager; |
| |
| /// The default pave timeout as [Duration] in milliseconds. |
| static const Duration defaultPaveTimeoutMs = |
| Duration(milliseconds: 5 * 60 * 1000); |
| |
| /// The [FileSystem] implementation used to |
| final FileSystem fs; |
| |
| /// The implementation to use for untarring system images. |
| final Tar tar; |
| |
| /// The implementation to use for creating SSH keys. |
| final SshKeyManagerProvider sshKeyManagerProvider; |
| |
| /// Paves an image (in .tgz format) to the specified device. |
| /// |
| /// The `imageTgzPath` must not be null. If `deviceName` is null, the |
| /// first discoverable device will be used. |
| Future<OperationResult> pave( |
| String imageTgzPath, |
| String deviceName, { |
| String publicKeyPath, |
| bool verbose = true, |
| Duration timeoutMs = defaultPaveTimeoutMs, |
| }) async { |
| assert(imageTgzPath != null); |
| if (deviceName == null) { |
| stderr.writeln('Warning: No device name specified. ' |
| 'If multiple devices are attached, this may result in paving ' |
| 'an unexpected device.'); |
| } |
| final SshKeyManager sshKeyManager = sshKeyManagerProvider( |
| processManager: processManager, |
| publicKeyPath: publicKeyPath, |
| fs: fs, |
| ); |
| final String uuid = const Uuid().v4(); |
| final Directory imageDirectory = fs.directory('image_$uuid'); |
| if (verbose) { |
| stdout.writeln('Using ${imageDirectory.path} as temp path.'); |
| } |
| await imageDirectory.create(); |
| final OperationResult untarResult = await tar.untar( |
| imageTgzPath, |
| imageDirectory.path, |
| ); |
| |
| if (!untarResult.success) { |
| if (verbose) { |
| stderr.writeln('Unpacking image $imageTgzPath failed.'); |
| } |
| imageDirectory.deleteSync(recursive: true); |
| return untarResult; |
| } |
| |
| final OperationResult sshResult = await sshKeyManager.createKeys(); |
| if (!sshResult.success) { |
| if (verbose) { |
| stderr.writeln('Creating SSH Keys failed.'); |
| } |
| imageDirectory.deleteSync(recursive: true); |
| return sshResult; |
| } |
| final Process paveProcess = await processManager.start( |
| <String>[ |
| '${imageDirectory.path}/pave.sh', |
| '--fail-fast', |
| '-1', // pave once and exit |
| '--allow-zedboot-version-mismatch', |
| if (deviceName != null) ...<String>['-n', deviceName], |
| '--authorized-keys', '.ssh/authorized_keys', |
| ], |
| ).timeout(timeoutMs); |
| final StringBuffer paveStdout = StringBuffer(); |
| final StringBuffer paveStderr = StringBuffer(); |
| paveProcess.stdout.transform(utf8.decoder).forEach((String s) { |
| if (verbose) { |
| stdout.write(s); |
| } |
| paveStdout.write(s); |
| }); |
| paveProcess.stderr.transform(utf8.decoder).forEach((String s) { |
| if (verbose) { |
| stderr.write(s); |
| } |
| paveStderr.write(s); |
| }); |
| final int exitCode = await paveProcess.exitCode; |
| await stdout.flush(); |
| await stderr.flush(); |
| imageDirectory.deleteSync(recursive: true); |
| |
| return OperationResult.fromProcessResult( |
| ProcessResult( |
| paveProcess.pid, |
| exitCode, |
| paveStdout.toString(), |
| paveStderr.toString(), |
| ), |
| ); |
| } |
| } |