Ceci est la page d’accueil / documentation du crosh, le shell Chromium OS. Si vous êtes sur un appareil CrOS en ce moment, vous devriez pouvoir lancer crosh en appuyant sur Ctrl + Alt + T. Si vous n’êtes pas sur CrOS, cela ne fera probablement rien d’utile :).
Exécutez simplement help
pour obtenir des informations sur les commandes disponibles et en découvrir plus.
Vous pouvez également utiliser la complétion des onglets pour trouver rapidement les commandes existantes.
C’est une aventure !
- Pour les développeurs de Chromium OS
- Avertissement de sécurité
- Où les fichiers vivent
- Repos source
- Ajout de nouvelles commandes
- Conception de commande
- Exemples
- Configuration du module
- Implémentation de la commande
- Aide à la commande
- Commandes obsolètes
- Test
- Développement itératif
- Unittests
- Travaux futurs
- Documentation Crosh existante
- API de commande
- Aide à la commande
- Masquage des commandes
Pour les développeurs de Chromium OS
Cette section est destinée aux personnes qui piratent sur Chromium OS, en particulier lorsqu’elles ont besoin de modifier/ étendre crosh.
Avertissement de sécurité
Veuillez ne pas installer de nouveaux modules sans un examen complet de la sécurité. Tout code non sécurisé que crosh charge sera directement disponible pour les personnes en mode vérifié. C’est un vecteur d’attaque facile pour exécuter du code arbitraire et brouiller l’état de l’utilisateur. Nous ne voulons pas compromettre la sécurité du CrOS!
Si vous cherchez des évaluateurs, regardez le.Fichier /PROPRIÉTAIRES.
Où les fichiers vivent
Crosh est migré du shell vers Rust. Crosh commence à s’exécuter à partir de src/main.rs mais la plupart des commandes sont implémentées comme leur propre sous-module de l’un des modules de haut niveau (par exemple base
ou dev
).
Le script principal crosh
contient l’implémentation héritée de la logique crosh de base tandis que d’autres fonctions héritées vivent dans les répertoires de modules. La version héritée de crosh est installée sur le périphérique en tant que crosh.sh
.
Repos source
Les modules spécifiques à une carte, ou fortement spécifiques à un package, devraient généralement vivre avec cette carte et/ou ce package. Pour les fonctions qui sont toujours disponibles sur tous les périphériques CrOS, ce code doit être conservé dans ce dépôt.
Si vous n’êtes pas sûr, demandez simplement sur [email protected] .
Ajout de nouvelles commandes
Déterminez d’abord quelle stratégie d’implémentation la nouvelle commande utilisera. Lors de la sélection d’une stratégie, il est utile de savoir quelles autorisations et quels privilèges sont nécessaires. Avec la stratégie à l’esprit, consultez les différents exemples ci-dessous.
Conception de commande
Le shell crosh s’exécute dans le même environnement que le navigateur (même utilisateur/groupe, mêmes espaces de noms Linux, etc…). Ainsi, tous les outils que vous exécutez dans crosh, ou les informations que vous essayez d’acquérir, doivent être accessibles à l’utilisateur chronos
.
Cependant, nous voulons rarement que crosh exécute directement les outils. Au lieu de cela, vous devez ajouter des rappels D-Bus au démon debugd et lui envoyer toutes les demandes. Nous pouvons mieux contrôler l’accès dans debugd et verrouiller les outils. Ensuite, la seule logique qui existe dans crosh est un appel IPC de bus D, puis affiche la sortie de ces programmes. La discussion sur debugd est hors de portée ici, alors consultez plutôt le répertoire debugd.
Exemples
Exemples d’implémentations :
- Wrapper de méthode D-Bus(debugd):base::verify_ro
Utilisez ceci lorsqu’une API D-Bus est déjà planifiée ou que crosh n’a pas les autorisations ou les capacités nécessaires. - Wrapper binaire externe:base::ccd_pass
Utilisez ceci lorsqu’il existe déjà un outil de ligne de commande qui implémente la commande qui fonctionne lorsqu’elle est exécutée en tant que chronos avec les capacités de crosh.Commande - écrite en Rust:base::arc
Ceci est le mieux adapté aux cas où des capacités supplémentaires ne sont pas nécessaires et qu’un outil de ligne de commande séparé n’est pas justifié.
Un exemple de workflow est inclus ci-dessous pour l’écriture d’une nouvelle commande.
Configuration du module
Choisissez un module approprié auquel la commande doit appartenir. Pour les commandes en mode dev, ce sera dev
, la plupart des autres commandes appartiendront à base
. Cet exemple utilisera base
comme module, mais les mêmes étapes devraient toujours s’appliquer dans d’autres cas.
Choisissez ensuite un nom de commande, créez un sous-module avec ce nom et enregistrez-le avec le module parent. Pour cet exemple, la commande est verify_ro
, donc le nouveau fichier source est src/base/verify_ro.rs
et deux lignes doivent être ajoutées à src/base/mod.rs
:
Tout d’abord, le sous-module doit être importé :
mod verify_ro;
Deuxièmement, la fonction de registre (à créer ci-dessous) doit être appelée par la fonction de registre dans le module parent src/base/mod.rs
:
pub fn register(dispatcher: &mut Dispatcher) { ... verify_ro::register(dispatcher); ...}
Maintenant, le fichier source src/base/verify_ro.rs
est prêt à être écrit. Commencez avec ce fichier source minimal et vérifiez que crosh compile avec cargo build
:
use crate::dispatcher::Dispatcher;pub fn register(dispatcher: &mut Dispatcher) { dispatcher.register_command( Command::new( "verify_ro".to_string(), "TODO put usage here".to_string(), "TODO put description here".to_string(), ) .set_command_callback(Some(execute_verify_ro)), );}fn execute_verify_ro(_cmd: &Command, args: &Arguments) -> Result<(), dispatcher::Error> { unimplemented!();}
Implémentation de la commande
Cela suppose que les instructions ci-dessus sont déjà terminées.
Étant donné que les opérations privilégiées ne peuvent pas être exécutées par Crosh (mais doivent être implémentées dans debugd ou ailleurs), l’exemple ci-dessous se concentre sur D-Bus en particulier et suppose qu’il existe une méthode D-Bus existante qui doit être appelée à partir d’une nouvelle commande Crosh.
Notez que l’interface D-Bus de debugd a déjà des liaisons Rust générées via dev-rust/system_api, de sorte que les liaisons et la connexion D-Bus peuvent être importées avec :
use dbus::blocking::Connection;use system_api::client::OrgChromiumDebugd;
Si vous souhaitez parcourir le code source des liaisons générées, après avoir exécuté build_packages, jetez un œil au chemin suivant :
/build/${BOARD}/usr/lib/cros_rust_registry/registry/system_api-*/src/bindings/client/
Dans l’implémentation de la commande, une connexion D-Bus doit être initialisée. Une connexion de blocage est utilisée dans cet exemple.
let connection = Connection::new_system().map_err(|err| { error!("ERROR: Failed to get D-Bus connection: {}", err); dispatcher::Error::CommandReturnedError})?;
La connexion de bus peut ensuite être utilisée pour obtenir une interface vers le service souhaité, qui est dans ce cas débogué :
let conn_path = connection.with_proxy( "org.chromium.debugd", "/org/chromium/debugd", DEFAULT_DBUS_TIMEOUT,);
Le reste de l’appel de méthode utilise le fait que le trait importé system_api::client::OrgChromiumDebugd
est implémenté pour system_api::client::OrgChromiumDebugd
est implémenté pour system_api::client::OrgChromiumDebugd
conn_path
ainsi, les fonctions membres qui mappent les méthodes D-Bus peuvent être appelées à partir de conn_path
. Par exemple :
conn_path .update_and_verify_fwon_usb_stop(handle) .map_err(|err| { println!("ERROR: Got unexpected result: {}", err); dispatcher::Error::CommandReturnedError })?;
Cela couvre les bases. Si vous regardez le code source réel de base::verify_ro, il fournit un exemple plus compliqué avec un appel de méthode de démarrage, un observateur et un appel de méthode d’arrêt.
Aide à la commande
Les chaînes d’aide par défaut sont remplies à l’aide du nom de la commande, de la chaîne d’utilisation, de la chaîne de description et de toutes les options ou indicateurs enregistrés via l’API du répartiteur.
Alternativement, un rappel d’aide peut être défini lors de l’enregistrement de la commande pour effectuer une logique personnalisée comme l’appel de l’option d’aide d’un binaire. Exemple:
const EXECUTABLE: &str = "/usr/bin/vmc";pub fn register(dispatcher: &mut Dispatcher) { dispatcher.register_command( Command::new("vmc".to_string(), "".to_string(), "".to_string()) .set_command_callback(Some(execute_vmc)) .set_help_callback(vmc_help), );}fn vmc_help(_cmd: &Command, w: &mut dyn Write, _level: usize) { let mut sub = process::Command::new(EXECUTABLE) .arg("--help") .stdout(Stdio::piped()) .spawn() .unwrap(); if copy(&mut sub.stdout.take().unwrap(), w).is_err() { panic!(); } if sub.wait().is_err() { panic!(); }}
Commandes obsolètes
Si vous voulez remplacer une commande crosh par une autre interface utilisateur (comme une page chrome://), et que vous voulez déprécier la commande gracieusement en laissant une note amicale si des gens essaient de l’utiliser, voici le formulaire.
# Set the vars to pass the unittests ...USAGE_storage_status=''HELP_storage_status=''# ... then unset them to hide the command from "help" output.unset USAGE_storage_status HELP_storage_statuscmd_storage_status() ( # TODO: Delete this after the R## release branch. echo "Removed. See storage_info section in chrome://system")
Assurez-vous d’ajouter le commentaire TODO afin que les gens sachent à l’avenir quand il sera POSSIBLE de le nettoyer.
Test
Développement itératif
Vous pouvez exécuter ./crosh
sur votre système de bureau pour obtenir un exemple de shell. Vous pouvez tester rapidement les interactions de base (comme l’analyse d’arguments) ici, ou vérifier la sortie d’aide. Vous n’aurez pas accès aux services CrOS auxquels de nombreuses commandes crosh s’attendent à parler (via D-Bus), de sorte que ces commandes échoueront.
Si vous souhaitez charger des modules en mode dev, vous pouvez utiliser ./crosh --dev
. Il ne chargera que des modules locaux (./dev.d/
), donc si votre module vit ailleurs, vous pouvez le copier temporairement ici.
De même, si vous souhaitez charger des modules de périphériques amovibles, vous pouvez utiliser ./crosh --removable
.
Unittests
Pour exécuter les tests unitaires, appelez cargo test --workspace
dans le dossier crosh ou exécutez emege-${BOARD} crosh && FEATURES=test emerge-${BOARD}
Le coureur unittest hérité ./run_tests.sh
effectue un tas de contrôles de style et de solidité de base. Exécutez-le contre toute modification du code shell!
Travaux futurs
Tout le monde devrait se sentir libre de reprendre ces idées et d’essayer de les mettre en œuvre :).
- Déplace toutes les commandes restantes implémentées en place vers des appels de débogage afin qu’elles puissent être effectuées via D-Bus.
- Exécutez crosh lui-même dans un bac à sable restreint (espaces de noms/seccomp/etc…). Une fois que toutes les commandes sont effectuées via IPC, il n’est pas nécessaire de conserver les fichiers privés. Cela pourrait cependant dépendre du mode de développement afin que nous ne cassions pas
shell
. - Migrez des commandes shell héritées supplémentaires vers Rust. Cela peut également être fait en même temps que la migration d’une commande vers debugd.
Documentation Crosh existante
Crosh a été initialement écrit en shell. Au moment de la rédaction, de nombreuses commandes restent encore dans shell et doivent encore être portées sur le crosh Rust. Cette documentation est conservée ici pour la maintenance de ces commandes.
API de commande
Pour chaque commande, vous définissez deux variables et une fonction. Il n’est pas nécessaire d’enregistrer les nouvelles commandes n’importe où car crosh inspectera son propre environnement d’exécution pour les découvrir.
Voici comment enregistrer une nouvelle commande foo
.
# A short description of arguments that this command accepts.USAGE_foo='<some args>'HELP_foo=' Extended description of this command.'# Not required, but lets crosh detect if the foo program is available in the# current system (e.g. the package is not installed on all devices). If it# isn't available, crosh will automatically display an error message and never# call cmd_foo.EXEC_foo='/full/path/to/program'cmd_foo() ( # Implementation for the foo command. # You should validate $# and "$@" and process them first. # For invalid args, call the help function with an error message # before returning non-zero. ...foo code goes here!...)
Voir la section conception ci-dessous pour plus de détails sur quoi et comment structurer la nouvelle commande.
Aide à la commande
Si votre commande crosh appelle simplement un programme externe pour effectuer le traitement, et que ce programme offre déjà des détails d’utilisation, vous ne voulez probablement pas avoir à dupliquer les choses. Vous pouvez gérer ce scénario en définissant une fonction help_foo
qui effectue l’appel respectif.
# Set the help string so crosh can discover us automatically.HELP_foo=''cmd_foo() ( ...)help_foo() ( /some/command --help)
Notez que nous avons toujours défini HELP_foo
. Cela est nécessaire pour que crosh puisse nous découvrir automatiquement et nous afficher dans les listes d’utilisateurs correspondantes (comme la commande help_advanced
). Nous n’avons pas besoin de définir USAGE_foo
car la fonction help_foo
le fait pour nous.
Masquage des commandes
Si une commande n’est pas encore prête pour le « prime time”, vous voudrez peut-être l’avoir dans crosh pour des tests précoces, mais ne pas l’afficher dans la sortie help
où les utilisateurs peuvent facilement la découvrir (bien sûr, le code est entièrement public, donc toute personne lisant la source réelle peut la trouver). Voici comment vous le faites.
# Set the vars to pass the unittests ...USAGE_vmc=''HELP_vmc=''# ... then unset them to hide the command from "help" output.unset USAGE_vmc HELP_vmccmd_vmc() ( ...)