Esta es la página de inicio/documentación de la crosh, la shell de Chromium OS. Si estás en dispositivos CrOS en este momento, deberías poder iniciar crosh pulsando Ctrl+Alt+T. Si no estás en CrOS, lo más probable es que no haga nada útil :).
Simplemente ejecute help
para obtener información sobre los comandos disponibles y descubrir más.
También puede usar la terminación de pestañas para encontrar rápidamente los comandos existentes.
Es una aventura!
- Para desarrolladores de Chromium OS
- Advertencia de seguridad
- Donde viven los archivos
- Repositorios de origen
- Agregar nuevos comandos
- Diseño de comandos
- Ejemplos
- Configuración del módulo
- Implementación de comandos
- Ayuda de comandos
- Comandos en desuso
- Testing
- Desarrollo iterativo
- Unittests
- Trabajo futuro
- Documentación de Crosh heredada
- API de comandos
- Ayuda de comando
- Ocultar comandos
Para desarrolladores de Chromium OS
Esta sección está destinada a personas que hackean Chromium OS, especialmente cuando necesitan modificar / extender crosh.
Advertencia de seguridad
Por favor, no instale módulos nuevos sin una revisión de seguridad completa. Cualquier código inseguro que cargue crosh estará directamente disponible para las personas en modo verificado. Es un vector de ataque fácil para ejecutar código arbitrario y interferir con el estado del usuario. No queremos socavar la seguridad de CrOS!
Si está buscando revisores, mire el .Archivo / OWNERS.
Donde viven los archivos
Crosh se está migrando de shell a Rust. Crosh comienza a ejecutarse desde src / main.rs, pero la mayoría de los comandos se implementan como su propio submódulo de uno de los módulos de alto nivel (por ejemplo, base
o dev
).
El script principal crosh
contiene la implementación heredada de la lógica crosh del núcleo, mientras que otras funciones heredadas viven en directorios de módulos. La versión heredada de crosh se instala en el dispositivo como crosh.sh
.
Repositorios de origen
Los módulos que son específicos de una placa, o muy específicos de un paquete, generalmente deben convivir con esa placa y / o paquete. Para las funciones que siempre están disponibles en todos los dispositivos CrOS, ese código debe mantenerse en este repositorio.
Si no está seguro, simplemente pregunte en [email protected].
Agregar nuevos comandos
Primero determine qué estrategia de implementación utilizará el nuevo comando. Al seleccionar una estrategia, es útil saber qué permisos y privilegios se necesitan. Con la estrategia en mente, echa un vistazo a los diversos ejemplos a continuación.
Diseño de comandos
El shell crosh se ejecuta en el mismo entorno que el navegador (el mismo usuario / grupo, los mismos espacios de nombres de Linux, etc…). Por lo tanto, cualquier herramienta que ejecute en crosh, o información que intente adquirir, debe ser accesible para el usuario chronos
.
Sin embargo, rara vez queremos que crosh ejecute herramientas directamente. En su lugar, debe agregar devoluciones de llamada D-Bus al demonio debugd y enviarle todas las solicitudes. Podemos controlar mejor el acceso en debugd y bloquear las herramientas. Entonces la única lógica que existe en crosh es una llamada D-Bus IPC y luego muestra la salida de esos programas. La discusión de debugd está fuera de alcance aquí, así que echa un vistazo al directorio debugd en su lugar.
Ejemplos
Implementaciones de ejemplo:
- Contenedor de método D-Bus (debugd): base::verify_ro
Use esto cuando una API D-Bus ya está planificada o crosh carece de los permisos o capacidades necesarios. - External binary wrapper: base:: ccd_pass
Use esto cuando ya haya una herramienta de línea de comandos que implemente el comando que funciona cuando se ejecuta como cronos con las capacidades de crosh. - Comando escrito en Rust: base::arc
Esto es más adecuado para casos en los que no se necesitan capacidades adicionales y no se justifica tener una herramienta de línea de comandos separada.
A continuación se incluye un flujo de trabajo de ejemplo para escribir un nuevo comando.
Configuración del módulo
Elija un módulo apropiado al que pertenezca el comando. Para los comandos de modo dev, esto será dev
, la mayoría de los demás comandos pertenecerán a base
. Este ejemplo usará base
como módulo, pero los mismos pasos deberían seguir aplicándose en otros casos.
A continuación, elija un nombre de comando, cree un sub módulo con ese nombre y regístrelo en el módulo principal. Para este ejemplo, el comando verify_ro
, por lo que el nuevo archivo de origen es src/base/verify_ro.rs
y dos líneas deben ser añadido a src/base/mod.rs
:
en Primer lugar, el submódulo necesita ser importado:
mod verify_ro;
en Segundo lugar la función de registro (creados a continuación) debe ser llamada por la función de registro en el módulo principal src/base/mod.rs
:
pub fn register(dispatcher: &mut Dispatcher) { ... verify_ro::register(dispatcher); ...}
Ahora src/base/verify_ro.rs
archivo de origen está listo para ser escrito. Comience con este archivo fuente mínimo y verifique que crosh compila con 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!();}
Implementación de comandos
Esto asume que las instrucciones anteriores ya están completas.
Dado que las operaciones privilegiadas no pueden ser ejecutadas por Crosh (pero deben implementarse en debugd o en cualquier otro lugar), el ejemplo a continuación se centra en D-Bus en particular y asume que hay un método D-Bus existente que necesita ser llamado desde un nuevo comando Crosh.
Tenga en cuenta que la interfaz D-Bus de debugd ya tiene enlaces Rust generados a través de dev-rust/system_api, por lo que los enlaces y la conexión D-Bus se pueden importar con:
use dbus::blocking::Connection;use system_api::client::OrgChromiumDebugd;
Si desea explorar el código fuente de los enlaces generados, después de ejecutar build_packages, eche un vistazo a la siguiente ruta:
/build/${BOARD}/usr/lib/cros_rust_registry/registry/system_api-*/src/bindings/client/
Dentro de la implementación de comandos, se debe inicializar una conexión D-Bus. En este ejemplo se utiliza una conexión de bloqueo.
let connection = Connection::new_system().map_err(|err| { error!("ERROR: Failed to get D-Bus connection: {}", err); dispatcher::Error::CommandReturnedError})?;
La conexión de bus se puede usar para obtener una interfaz para el servicio deseado, que es debugd en este caso:
let conn_path = connection.with_proxy( "org.chromium.debugd", "/org/chromium/debugd", DEFAULT_DBUS_TIMEOUT,);
El resto de la llamada al método utiliza el hecho de que el rasgo importado system_api::client::OrgChromiumDebugd
está implementado para conn_path
por lo que las funciones miembro que se asignan a métodos D-Bus se pueden llamar desde conn_path
. Por ejemplo:
conn_path .update_and_verify_fwon_usb_stop(handle) .map_err(|err| { println!("ERROR: Got unexpected result: {}", err); dispatcher::Error::CommandReturnedError })?;
Esto cubre lo básico. Si nos fijamos en el código fuente real de base::verify_ro, proporciona un ejemplo más complicado con una llamada al método start, un vigilante y una llamada al método stop.
Ayuda de comandos
Las cadenas de ayuda predeterminadas se rellenan con el nombre de comando, la cadena de uso, la cadena de descripción y cualquier opción o indicador que se registre a través de la API de dispatcher.
Alternativamente, se puede configurar una devolución de llamada de ayuda al registrar el comando para realizar una lógica personalizada, como invocar la opción de ayuda de un binario. Por ejemplo:
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!(); }}
Comandos en desuso
Si desea reemplazar un comando crosh con alguna otra interfaz de usuario (como una página de chrome://), y desea desaprobar el comando correctamente dejando una nota amigable si la gente intenta usarlo, aquí está el formulario.
# 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")
Asegúrese de agregar el comentario TODO para que la gente sepa en el futuro cuándo está bien limpiarlo.
Testing
Desarrollo iterativo
Puede ejecutar ./crosh
en su sistema de escritorio para obtener un shell de muestra. Puede probar rápidamente las interacciones básicas (como el análisis de argumentos) aquí, o comprobar la salida de ayuda. No tendrá acceso a los servicios CrOS con los que muchos comandos crosh esperan hablar (a través de D-Bus), por lo que esos comandos fallarán.
Si desea cargar módulos de modo dev, puede usar ./crosh --dev
. Solo cargará módulos locales (./dev.d/
), por lo que si su módulo vive en otro lugar, puede copiarlo aquí temporalmente.
Del mismo modo, si desea cargar módulos de dispositivos extraíbles, puede usar ./crosh --removable
.
Unittests
Para ejecutar las pruebas unitarias, llame a cargo test --workspace
en la carpeta crosh o ejecute emege-${BOARD} crosh && FEATURES=test emerge-${BOARD}
El ./run_tests.sh
legacy unittest runner realiza un montón de comprobaciones básicas de estilo y sonido. ¡Ejecútalo contra cualquier cambio en el código de shell!
Trabajo futuro
Cualquiera debería sentirse libre de recoger estas ideas e intentar implementarlas :).
- Mueva los comandos restantes que se implementen en su lugar para depurar llamadas para que se puedan hacer a través de D-Bus.
- Ejecute crosh en un espacio de arena restringido(espacios de nombres / seccomp / etc…). Una vez que todos los comandos se realizan a través de IPC, no es necesario mantener privs. Puede que dependa del modo de desarrollo, por lo que no rompemos
shell
. - Migre comandos de shell heredados adicionales a Rust. Esto también se puede hacer al mismo tiempo que migrar un comando a debugd.
Documentación de Crosh heredada
Crosh se escribió originalmente en shell. En el momento de escribir este artículo, muchos de los comandos aún permanecen en shell y aún no se han portado al Rust crosh. Esta documentación se guarda aquí para el mantenimiento de estos comandos.
API de comandos
Para cada comando, se definen dos variables y una función. No es necesario registrar los nuevos comandos en ningún lugar, ya que crosh inspeccionará su propio entorno de tiempo de ejecución para descubrirlos.
Así es como registrarías un nuevo comando 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!...)
Consulte la sección de diseño a continuación para obtener más detalles sobre qué y cómo estructurar el nuevo comando.
Ayuda de comando
Si su comando crosh simplemente llama a un programa externo para que realice el procesamiento, y ese programa ya ofrece detalles de uso, probablemente no quiera tener que duplicar cosas. Puede manejar este escenario definiendo una función help_foo
que realiza la llamada respectiva.
# Set the help string so crosh can discover us automatically.HELP_foo=''cmd_foo() ( ...)help_foo() ( /some/command --help)
Tome nota de que todavía nos set HELP_foo
. Esto es necesario para que crosh pueda descubrirnos automáticamente y mostrarnos en las listas de usuario relevantes (como el comando help_advanced
). No necesitamos establecer USAGE_foo
aunque ya que la función help_foo
hace eso por nosotros.
Ocultar comandos
Si un comando aún no está listo para «prime time», es posible que desee tenerlo en crosh para pruebas tempranas, pero no que se muestre en la salida help
donde los usuarios puedan descubrirlo fácilmente (por supuesto, el código es todo público, por lo que cualquier persona que lea la fuente real puede encontrarlo). Así es como lo haces.
# 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() ( ...)