LINUX-Aplicando-al-LPIC1_Examen101

ÍNDICE Tema 101 Arquitectura del sistema (intro) Conociendo el sistema El directorio /proc Udev y el directorio /dev Com

Views 72 Downloads 4 File size 9MB

Report DMCA / Copyright

DOWNLOAD FILE

Citation preview

ÍNDICE Tema 101 Arquitectura del sistema (intro) Conociendo el sistema El directorio /proc Udev y el directorio /dev Comandos útileS Ver módulos del kernel

2 3 3 6 7 7

CPUs y dispositivos PCI, SCSI o SATA...

9

Dispositivos USB

12

La Secuencia de Arranque

13

SysVinit

13

Upstart

16

Systemd

18

Targets en systemd

19

Logs de inicio

22

El proceso de inicio de sesión

24

Apagado y reinicio del sistema

25

Enviar mensajes a terminales

27

Tema 102 Instalación de Linux y Administración de Paquetes (intro)

29

Disposición de unidades de Almacenamiento

30

Diseño de particiones

30

Sistemas de archivos

31

Creación de particiones y sistemas De archivos

32

Particionado tradicional versus LVM

36

El gestor de arranque GRUB

37

Interactuar con GRUB

38

Configuración de GRUB

39

Usar la shell de GRUB

41

Librerías compartidas

42

Administración de paquetes: Vocabulario clave

44

Más sobre repositorios

45

Administración de paquetes en Linux (Debian y derivados)

49

Reconfigurar paquetes

51

CentOS y similares

53

Familia SUSE

57

Tema 103 Herramienta de la línea De comandos (intro)

58

Entrada, salida, y error estándares

59

La shell Bash y el entorno de trabajo

60

Identificar comandos

64

El historial de comandos

66

Localizar comando

67

Manipulación de texto

68

Combinar archivos con cat y paginar Con more o less

68

Ordenar líneas con sort

71

Unir archivos línea a línea con paste

72

Mostrar el comienzo o el fin de un Archivo con head o tail

74

Mostrar campos separados por un Delimitador

74

Contar palabras, bytes, y líneas con wc

75

Dividir archivos

77

Validar integridad de archivos

78

Creación y manipulación de archivos Y directorios

79

El comando ls

79

El comando mkdir (make directory)

82

El comando rmdir (remove directory)

83

El comando cp (copy)

83

El comando mv (move)

84

El comando rm (remove)

85

Buscar con find

85

Mostrar salida de un comando por Pantalla y guardar el resultado al Mismo tiempo

87

Archivado y compresión

89

Herramientas de la línea de comandos

91

Introducción a grep

91

Expresiones regulares

93

Usar grep para averiguar si nuestro CPU soporta extensiones de Virtualización

93

Eliminar líneas de comentarios o vacías

94

Buscar palabras que terminen con una Cadena de texto determinada

94

Buscar secuencias de caracteres Alfanuméricos

96

Uso de sed

96

Mostrar el archivo entero excepto un Rango específico

96

Mostrar líneas y rangos no consecutivos

97

Mostrar líneas que contengan una Palabra clave o expresión regular

97

Reemplazar palabras o caracteres

97

Edición empleando expresiones Regulares

98

Repetir ejecución de comandos

99

Definición y tipos de procesos

99

El comando pstree

100

El comando ps

101

Los comandos kill y killall

103

Detrás de escenas: envío de señales a Procesos

105

El comando top

106

Los comandos nice y renice

109

Procesos en primer y segundo plano

111

El comando nohup

113

Múltiples terminales en la misma Pantalla

115

Dividir la pantalla horizontal y Verticalmente

115

Crear nuevas ventanas

117

El editor de texto vi (Introducción )

118

Modos de operación de vi

119

El modo comando

119

Comandos básicos de vi

120

Insertar texto

122

Cambiar texto

123

Copiar y mover texto: Yank, Delete, Y Put

125

Usar buffers con nombre

126

Uso de expresiones para copiar, mover, Y borrar líneas

126

Búsqueda y reemplazo de cadenas de Texto

127

Editar múltiples archivos

129

Opciones de configuración

130

Tema 104 Dispositivos, sistemas de Archivos y FHS (intro)

132

Particiones MBR versus GPT

133

Memoria swap

134

Uso y configuración de la memoria swap

134

Sistemas de archivos

136

Montaje de dispositivos de Almacenamiento

136

Integridad de sistemas de archivos

138

Chequear y reparar errores

142

Permisos y propiedad de archivos

144

Crear usuarios y grupos

144

Administración de grupos

146

Permisos en Linux

147

Máscaras de archivos y directorios

150

Estándar de jerarquía del sistema de Archivos (FHS)

152

Arquitectura del Sistema (Tema101) En este capítulo hablaremos sobre los componentes de una computadora personal poniendo énfasis en el hardware y el software base o sistema operativo. Como parte de este análisis aprenderemos cómo manipular los dispositivos conectados a nuestro equipo. Finalizaremos repasando el proceso de arranque de un sistema Linux desde el momento en que se presiona el botón de encendido hasta que disponemos de un sistema totalmente funcional.

2

Conociendo el sistema En Linux "todo es un archivo" reza el dicho. En otras palabras, cada componente del sistema puede ser manipulado como si fuera un archivo común y corriente en el sentido de que se puede leer y escribir en él. Como ejemplo podemos citar a los comúnmente llamados pseudo sistemas de archivos. Lejos de tratarse de dispositivos reales donde podemos almacenar información variada, en realidad consisten en una especie de ventana que nos permite acceder (leer) a información del sistema de una manera relativamente amigable. El directorio /proc Según el Filesystem Hierarchy Standard, el directorio /proc es el método adecuado para obtener información sobre el sistema ya que en el mismo se encuentra montado el pseudo sistema de archivos proc. Además, en este directorio también encontraremos datos sobre los procesos que están corriendo actualmente. Al ingresar a /proc lo primero que nos llama la atención es que hay varios directorios cuyo nombre es un número (1, 10, 12, 13, 606, etc.). Dicho número es el identificador de un cierto proceso y cada subdirectorio dentro del mismo contiene información asociada a dicho proceso. Cuando un proceso finaliza su ejecución, o es terminado, su directorio asociado dentro de /proc desaparece. Supongamos que deseamos inspeccionar más en detalle el proceso cuyo identificador es 541. Para ello nos posicionamos en el directorio /proc/541 donde encontraremos los siguientes archivos de interés (entre otros): cmdline: contiene los argumentos de la línea de comandos que se utilizaron para iniciar el proceso. cwd: es un enlace al directorio actual del proceso. exe: es un enlace al ejecutable del proceso. status: muestra información detallada sobre el estado del proceso .3

Además, dentro de /proc encontramos dos archivos sumamente útiles que nos brindan información sobre el uso de RAM y CPU llamados meminfo y cpuinfo, respectivamente. En el primero las cantidades en realidad se expresan en kibibytes (1KiB = 1024 B). Por razones de compatibilidad, se utiliza la nomenclatura kB debido a que otros programas (como free , top , y ps ) la requieren. De hecho, ese es uno de los motivos por el que este archivo es tan importante, ya que es la fuente de datos para otros programas. ¿Qué más podemos encontrar dentro de /proc? La siguiente lista nombra otros archivos comunes junto con una breve descripción de estos (la cual puede ampliarse en el man page de proc ( man proc ): dma nos muestra los canales de acceso directo a memoria (Direct Memory Access) que se están utilizando. interrupts históricamente ha contenido el número de interrupciones por CPU y por dispositivo de E/S, aunque a partir del kernel 2.6.24 también se incluyen interrupciones internas (no asociadas con un dispositivo propiamente dicho). ioports es una lista de rangos de direcciones de E/S en uso. En uptime podremos ver dos números. El primero de ellos es el tiempo (en segundos) que ha transcurrido desde el último inicio del sistema, mientras que el segundo es la cantidad de tiempo que cada CPU ha estado inactivo.

Los tres primeros archivos resultan de particular importancia al tratar con hardware un tanto antiguo. También son de interés para los desarrolladores al permitirles verificar si el driver de un cierto dispositivo se puede comunicar con los recursos de hardware que necesita utilizar.

En resumen, proc (o procfs, como es también llamado) es un sistema de archivos virtual que no contiene archivos reales sino información sobre la operación del sistema. Puede considerarse como el centro de control del kernel. 4

En resumen, proc (o procfs, como es también llamado) es un sistema de archivos virtual que no contiene archivos reales sino información sobre la operación del sistema. Puede considerarse como el centro de control del kernel. En /sys (el punto de montaje de este sistema de archivos) encontramos información variada sobre dispositivos. Entre esos datos podemos ver el estado de estos (en funcionamiento o no), el fabricante y modelo, el tamaño (si corresponde) y el bus al que están conectados. Veamos en la imagen siguiente el contenido típico de este sistema de archivos: tree -d -L 1 /sys

Los subdirectorios que vemos en la imagen anterior representan los sistemas principales incluidos en sysfs. En estos directorios por lo general encontraremos archivos de texto plano que contienen un solo valor. Por ejemplo, consideremos /sys/bus. En el mismo se ubican una serie de subdirectorios que representan todos los tipos de buses que están soportados en el kernel actual (ya sea compilados estáticamente o mediante un módulo). Más particularmente, en /sys/bus/usb/devices encontraremos información sobre los buses USB disponibles en el equipo: ls /sys/bus/usb/devices En particular, dentro de /sys/bus/usb/devices/usb1 encontramos un subdirectorio llamado power, y un archivo connected_duration. 5

En el mismo se nos indica, en milisegundos, el tiempo en que ha estado conectado el bus. Este dato debe coincidir (aproximadamente) con el uptime del equipo.

Como podemos ver, sysfs provee datos que también están disponibles a través de proc, aunque de una manera más estructurada. Udev y el directorio /dev Dentro de la estructura de directorios en Linux, los dispositivos presentes en el equipo pueden observarse dentro del directorio /dev a partir de los datos presentes en /sys. En esa ubicación encontraremos archivos que representan discos (incluyendo particiones, grupos de volúmenes, y volúmenes lógicos), memoria, terminales, y otros. En primer lugar, cuando se agrega o quita un dispositivo (o cambia su estado de alguna forma) del sistema, el kernel lo informa al servicio systemd-udevd a fin de prepararlo para su uso. Este último puede configurarse para responder a dichos eventos mediante reglas en archivos con la extensión .rules dentro de los siguientes directorios: /etc/udev/rules.d /run/udev/rules.d /usr/lib/udev/rules.d Si existen archivos con nombres iguales, la prioridad más alta es dada a aquellos dentro de /etc/udev/rules.d, seguida por /run/udev/rules.d, y luego por /usr/lib/udev/rules.d. Como resultado, podemos identificar un dispositivo basándonos en sus propiedades y sin importar el puerto de conexión o el orden en que haya sido descubierto, podemos asignarle un nombre fijo. Es importante mencionar que se dispone de udevadm, una herramienta para controlar el funcionamiento de udev. Entre sus usos útiles, podemos destacar udevadm monitor --kernel que muestra los 6

eventos detectados por el kernel. Luego de ejecutar este comando, tendremos que esperar a que se registre un cambio. A continuación, podremos ver el detalle por la salida estándar. En la imagen siguiente observamos la detección de un dispositivo USB conectado al equipo luego de ejecutar el comando:

Además del uso ilustrado en la imagen anterior, udevadm nos sirve para administrar la lista de eventos en espera (udevadm settle y udevadm control) y nos permite simular eventos (udevadm test) y debuguearlos. Finalmente, para que una aplicación de escritorio envíe o reciba mensajes de otra aplicación (o del kernel mismo) existe un mecanismo de comunicación llamado D-Bus. Por ejemplo, cuando insertamos un dispositivo de almacenamiento extraíble, udev envía un mensaje a través de D-Bus al gestor de ventanas para que muestre una notificación por pantalla o provea un ícono de acceso directo al dispositivo. Comandos útiles Además de los ejemplos que mencionamos anteriormente, existen numerosas herramientas que nos permiten examinar la operación del kernel y el estado de los dispositivos conectados a nuestro equipo. A continuación, veremos un listado de las más conocidas. Ver módulos del kernel Para mostrar información sobre el kernel que estamos utilizando actualmente, usaremos el comando uname con las opciones -s (nombre), -r (release), y -v (versión), pudiendo utilizarlas todas juntas como -srv o por separado. También disponemos de la opción -a que devuelve los datos anteriores junto con el tipo de procesador, la 7

plataforma, y el sistema operativo. La lista de los módulos del kernel en uso puede verse utilizando el comando lsmod. Como la lista puede llegar a ser muy larga, es conveniente utilizar less para paginar el resultado o grep para buscar uno en particular. Si hay alguno de la lista que capte nuestra atención, podemos obtener más información sobre el mismo usando modinfo seguido del nombre del módulo. A continuación, podemos apreciar el resultado de los comandos lsmod | grep 8139 modinfo 8139cp

En la imagen anterior aprendemos que el módulo 8139cp es el controlador de la placa de red correspondientes a la serie RealTek RTL-8139C. El archivo que representa el módulo reside en /lib/modules/4.9.0-4686-pae/kernel/drivers/net/ethernet/realtek/ con el nombre 8139cp.ko. Si en algún momento necesitamos utilizar un módulo que no está cargado en el kernel, podemos recurrir a dos herramientas llamadas insmod y modprobe para poner dicho módulo en funcionamiento. ¿Cuál es la diferencia entre ambas herramientas? Mientras modprobe es lo suficientemente inteligente para resolver dependencias, insmod no lo es. Esto significa que modprobe puede cargar automáticamente otros módulos que sean necesarios antes de hacer lo propio con el que le indicamos explícitamente. 8

Por ejemplo, para cargar el módulo ac97_bus: modprobe --verbose ac97_bus Para removerlo: modprobe -r ac97_bus Tengamos en cuenta que modprobe nos permite insertar o remover módulos, pero dichos cambios no son permanentes; es decir que se pierden al reiniciar el equipo. Para lograr que un módulo en particularse cargue al inicio del sistema, debemos agregar el nombre del módulo en el archivo /etc/modules-load.d/modules.conf. También es posible crear archivos separados con la extensión .conf dentro del mismo directorio. Por ejemplo, para cargar el módulo virtio-net.ko podríamos crear el archivo /etc/modules-load.d/virtio-net.conf con el siguiente contenido (las líneas que comienzan con # se consideran comentarios y son ignoradas): # Load virtio-net.ko at boot Virtio-net En el man page de modules-load.d podemos encontrar más información al respecto.

CPUs y dispositivos PCI, SCSI o SATA Para acceder a información sobre el CPU (uno o más, si los hubiera) podemos emplear el comando lscpu. Entre los datos que esta herramienta pone a nuestro alcance se encuentran la configuración y las características que distinguen al procesador. En la lista que se muestra en la imagen siguiente observamos la arquitectura (Architecture), el modo de operación (CPU op-mode(s)), la marca (Vendor ID), el modelo (Model name), y la velocidad (CPU MHz), por nombrar los más representativos: 9

Los dispositivos PCI, SCSI, o SATA pueden ser inspeccionados mediante los comandos lspci y lsscsi. Este último nos muestra SCSI y SATA por igual. Ambas utilidades aceptan el uso de la opción -v para aumentar la verbosidad de la salida, traduciéndose en mayor cantidad de detalles sobre los dispositivos. Veamos un ejemplo de cada comando: lsscsi lsscsi -v

10

lspci lspcsi -v

Además de estas herramientas, también podemos utilizar lsblk para ver todos los dispositivos de bloques en forma de árbol. De esta manera tenemos la posibilidad de ver las particiones de cada disco y los volúmenes lógicos, por nombrar dos ejemplos:

En la imagen de arriba podemos ver que sda posee 3 particiones (sda1, sda2, y sda5). En una de ellas (sda5) reside el grupo de volúmenes llamado debianhome-vg y dentro del mismo encontramos los volúmenes lógicos root, var, swap_1, tmp, y home. 11

Dispositivos USB En el archivo /var/lib/usbutils/usb.ids podemos encontrar una lista de dispositivos USB por fabricante. El comando lsusb utiliza la información disponible en dicho archivo para identificar la lista de dispositivos USB y sus buses de conexión presentes en nuestro equipo. Estos datos pueden ser actualizados descargando la última versión desde http://www.linux-usb.org/usb.ids. En la imagen siguiente vemos la salida típica de este comando:

Tomemos como ejemplo el primer dispositivo de la lista de arriba (Hewlett-Packard v165w Stick) para analizar los datos que se nos presentan: Bus 001 es el número del bus al que está conectado el dispositivo. Device 007 es el número de dispositivo conectado al Bus 001. ID 03f0:5307 es el id del dispositivo, donde 03f0 representa la identificación del fabricante y 5307 la correspondiente al dispositivo propiamente dicho. Podemos verificar esta información con grep -Ei '(03f0|5307)' /var/lib/usbutils/usb.ids. Hewlett-Packard v165w Stick es el nombre del fabricante y del dispositivo. Para mostrar más detalles podemos utilizar las opciones -v o -vv, tal como en otros comandos mencionados anteriormente. 12

La Secuencia de Arranque Al encender el equipo, el BIOS (Basic Input-Output System) o UEFI (Unified Extensible Firmware Interface) llevan a cabo una revisión del hardware conocida comúnmente como POST (Power-On Self Test). A continuación, se busca un gestor de arranque (por lo general, GRUB) en el MBR (Master Boot Record) o en la partición EFI de un dispositivo de almacenamiento. A partir de ese momento, el control del proceso se pasa a manos de GRUB, que se encargará de cargar el kernel, el cual reconocerá y configurará los dispositivos de hardware presentes en el equipo y los preparará para su uso. Como próximo paso, el núcleo también será responsable por ejecutar el primer proceso, también conocido como init. Finalmente, este último utilizará el gestor del sistema (systemd o Upstart) o los scripts de SysVinit para continuar el inicio de los demás servicios. El proceso de arranque culmina al presentar una interfaz de inicio de sesión de modo texto o gráfico, según sea el caso.

Los servicios o procesos son programas que corren en segundo plano (de manera no interactiva). Otro nombre por el que son conocidos es daemons (a veces traducido como demonios en castellano). Ahora hagamos un repaso por los 3 métodos de gestión del sistema más utilizados a lo largo del tiempo. SysVinit Hasta no hace mucho tiempo, la mayoría de las distribuciones más utilizadas utilizaban como base de su funcionamiento el sistema de arranque y de administración de servicios conocido como SystemV o SysVinit. Este sistema, heredado de Unix, contempla 5 niveles útiles de funcionamiento (de ahí el nombre SystemV, correspondiente al número 5 en el sistema romano) numerados del 1 al 5. A estos se les suma el nivel 0 (apagado del equipo) y el 6 (reinicio del sistema). Cada uno de ellos es lo que se conoce como niveles de corrida o runlevels en Linux. 13

Cada runlevel se encuentra asociado a un cierto número de servicios que por defecto deben iniciarse automáticamente cuando encendemos el equipo, y que deben detenerse al reiniciarlo o apagarlo. Por ejemplo, en el directorio /etc en un sistema Debian Wheezy podemos encontrar 7 directorios con nombre rcN.d, donde N es un número del 0 al 6. Es precisamente dentro de estos directorios que se encuentran una serie de enlaces simbólicos a los ejecutables que inician y detienen los servicios del sistema en cada runlevel respectivo, como vemos a continuación:

Por el momento, podemos asociar el concepto de enlaces simbólicos (también llamados soft links) con el de acceso directo, con el que quizás estemos más familiarizados. En síntesis, se trata de una representación de un recurso del sistema que apunta a dicho recurso pero que es independiente del mismo. La S utilizada como prefijo en los nombres de los enlaces simbólicos indica que, en este nivel de corrida en particular, se debe iniciar el servicio asociado. Por otro lado, la letra K indica lo contrario (el servicio debe apagarse o permanecer detenido al ingresar al runlevel en cuestión).

14

Es importante tener en claro que lo primero que hace init es leer el archivo /etc/inittab para identificar los próximos pasos a seguir. Por brevedad, nos referiremos solamente a la detección del runlevel por defecto, lo que se indica en la siguiente línea (2 en este ejemplo): # The default runlevel. id:2:initdefault: A continuación, init se dirige a /etc/rc2.d en este caso. En este directorio podemos apreciar una serie de enlaces simbólicos cuyos nombres comienzan con la letra S, tal como mencionamos antes, y un número de dos dígitos. Cada uno de ellos apunta a un servicio que se debe levantar en el runlevel 2 por orden numérico y alfabético). Por ejemplo, de la imagen anterior podemos sacar la conclusión de que S01motd recibirá atención en primer lugar, seguido por S13rpcbind, y así sucesivamente. Si hay dos o más que compartan el mismo prefijo SXX, el orden de inicio se decide por orden alfabético. De esta manera, S16rsyslog será llamado primero que S16virtualbox-guest-utils (este último no se muestra en la imagen). Para cambiar de runlevel temporariamente podemos invocar el comando telinit seguido del nombre del nivel de corrida deseado. Por ejemplo, para cambiar al runlevel 5 : telinit 5 El comando anterior hará que init se dirija al nivel de corrida 5 siguiendo el mismo proceso que explicamos antes.

Por supuesto, si nos interesa cambiar el nivel de corrida de manera permanente podemos editar el archivo /etc/inittab colocando el número correspondiente como indicamos más arriba.

15

Con el correr del tiempo, las siguientes limitaciones se hicieron notorias en este esquema: El proceso de inicio de servicios es estrictamente síncrono (se espera a que un servicio esté corriendo antes de iniciar el próximo). Esto impacta en el tiempo total de inicio del sistema. Necesidad de chequear dependencias antes de iniciar un servicio . Por ejemplo, que el servicio de red esté disponible antes de iniciar un servidor web. Cualquier evento posterior al inicio del equipo necesita intervención manual del usuario (identificación y montaje de dispositivos extraíbles, por nombrar dos ejemplos). Fueron precisamente estas desventajas que condujeron al desarrollo de otros sistemas de arranque y administración de servicios como Upstart primero y systemd después. Upstart Entre SysVinit y la adopción final de systemd por la mayoría de las distribuciones principales GNU/Linux, surgió una alternativa conocida como Upstart. Fue desarrollada por Canonical (la empresa detrás de Ubuntu) e integrada por primera vez con Ubuntu 6.10 Edgy a fines de 2006. Posteriormente, Fedora la adoptó y utilizó hasta la versión 14 inclusive. Hoy en día podemos encontrarla en RHEL 6.7 y similares, los cuales gozan de soporte hasta fines de 2018. Upstart se pensó como un reemplazo basado en eventos para SysVinit. Como tal, supervisa tareas mientras el sistema está funcionando y responde a eventos tales como la conexión o desconexión de dispositivos extraíbles. Además, también gestiona los servicios durante el inicio y el apagado. Es importante notar que es 100% compatible con los scripts clásicos de SysVinit. De esta manera, aquellos servicios que provean un script para init pueden funcionar sin problemas bajo Upstart. Por otro lado, Upstart también trabaja con archivos .conf dentro del directorio /etc/init. En los mismos se define el funcionamiento de servicios, y tienen la siguiente estructura: 16

Niveles de corrida en los cuales el proceso debe ejecutarse o eventos que deben iniciarlo. Niveles de corrida en los cuales el proceso no debe correr o eventos que deben detenerlo. Opciones Comando que se debe utilizar para lanzar el proceso Consideremos el archivo de ejemplo backup.conf que mostramos a continuación. Los comentarios presentes en el mismo (en líneas que comienzan con #) nos sirven como guía para entender el funcionamiento del servicio que utiliza este archivo de configuración):

# Servicio de prueba para Upstart # Stanzas # Stanzas define when and how a process is started and stopped # See a list of stanzas here: http://upstart.ubuntu.com/wiki/Stanzas#respawn # Runlevels para iniciar el servicio start on runlevel [2345] # Cuando detenerlo stop on runlevel [016] # Reiniciarlo en caso de falla respawn # Especificar directorio de trabajo chdir /home/alumno/misarchivos # Indicar el comando, junto con cualquier argumento necesario (opcional) exec bash backup.sh arg1 arg2 A pesar de la aceptación que este sistema tuvo entre la comunidad en su momento, con el tiempo cedió lugar al más robusto systemd. Incluso Canonical decidió adoptarlo para Ubuntu luego de que el proyecto Debian hiciera lo mismo a partir de la versión 8 (Debian Jessie). 17

Systemd Es importante aclarar que systemd no surgió como un reemplazo de SysVinit porque este último fuera defectuoso o porque hubiera usuarios y administradores que estuvieran descontentos con el mismo. Más bien, comenzó como un proyecto para desarrollar un sistema que fuera más eficiente al 1) iniciar menos servicios durante el arranque (solamente aquellos que fueran estrictamente necesarios de acuerdo al uso esperado y al hardware disponible), y 2) hacerlo en paralelo, en vez de manera secuencial. En otras palabras, se buscó un sistema de inicio y de administración de servicios que pudiera reaccionar dinámicamente ante cambios en el software y en el hardware.

Si lo pensamos bien, esta es una característica distintiva de los sistemas de cómputo actuales. Por ejemplo, no hay necesidad de mantener el servicio de impresión corriendo si no hay ninguna impresora conectada al equipo o disponible a través de la red. Por otra parte, queremos que se inicie de manera transparente si esa condición cambia.

A pesar de las bondades mencionadas en el párrafo anterior, muchas personas se opusieron tenazmente a la adopción de systemd por parte de sus distribuciones favoritas. Sin embargo, las reservas de la comunidad eventualmente cedieron, resultando en la adopción de systemd en Fedora -lo cual derivó en Red Hat Enterprise Linux 7 y eventualmente en CentOS 7-, ArchLinux, Debian, Ubuntu, y derivados de estos últimos. 18

Targets en systemd Bajo systemd, el concepto de runlevel o nivel de corrida solamente se mantiene por compatibilidad con SysVinit. El componente básico de systemd se denomina unidad (unit). Existen varias categorías de unidades: las más conocidas se denominan servicios (services) y objetivos (targets). Las primeras son las que ejecutan los daemons y sus dependencias en el orden apropiado. Por otro lado, las segundas se utilizan para agrupar varias unidades. Estas últimas pueden compararse en cierta forma a los clásicos runlevels. Dentro de /lib/systemd/system podemos encontrar las definiciones de las distintas unidades. Para empezar, cabe aclarar que los archivos de configuración de los targets llevan el sufijo .target. Por ejemplo, podemos encontrar basic.target y multi-user.target. Este último es el objetivo donde se encuentran agrupados la mayoría de los daemons. Para funcionar correctamente, se necesita que basic.target esté activado. Dicho de otra forma, antes de entrar en multi-user.target, todos los servicios agrupados en basic.target deben haberse iniciado. A su vez, basic.target requiere sysinit.target.

El objetivo basic.target en esencia cubre todo lo relacionado con el inicio del sistema. Luego, podemos decir lo siguiente en general: Si en A.target se indica Requires=B.target, hablamos que el objetivo B es una dependencia del A. Además, en systemd hablamos de objetivo por defecto para describir el estado que alcanza el sistema al finalizar el proceso de arranque. Para ver cuál es el objetivo por defecto en nuestro sistema, podemos utilizar el comando systemctl get-default 19

¿De dónde proviene este dato? Se trata de un enlace simbólico llamado default.target dentro de /lib/systemd/system que apunta a la definición del target indicado. Por ejemplo, en la imagen siguiente podemos ver que /lib/systemd/system/default.target es un soft link hacia /lib/systemd/system/graphical.target:

Por supuesto, es posible cambiar el target por defecto si es necesario. Por ejemplo, para cambiar el objetivo por defecto a graphical.target (entrará en efecto con el próximo reinicio), debemos recurrir a: systemctl set-default graphical.target Además, para controlar el inicio y los servicios del sistema, en distribuciones que utilizan systemd se dispone de una herramienta llamada systemctl.

En systemd se utiliza el término unit para referirse no solamente a servicios, sino también a puntos de montaje, sockets, e incluso dispositivos.

20

A continuación, presentamos las opciones más utilizadas de systemctl. En todas ellas hacemos referencia a un servicio ficticio llamado miservicio: Iniciar el servicio: systemctl start miservicio.service Configurarlo para que inicie al arrancar el equipo: systemctl enable miservicio.service Detenerlo: systemctl stop miservicio.service Impedir que inicie al arrancar el equipo: systemctl disable miservicio.service Reiniciar el servicio: systemctl restart miservicio.service Averiguar si está configurado para arrancar al inicio: systemctl is-enabled miservicio.service Averiguar si está corriendo actualmente: systemctl is-active miservicio.service o systemctl -l status miservicio.service (esta última variante provee más información sobre el estado y la operación del servicio). En los ejemplos anteriores, podemos omitir el .service si estamos seguros que no hay otra unit de distinto tipo con el nombre miservicio. Cuando es necesario realizar tareas de mantenimiento o de emergencia será necesario que nos cambiemos al modo monousuario. Para llevar a cabo esta acción haremos uso de systemctl rescue o systemctl emergency La diferencia entre rescue y emergency consiste en que la última es mucho más reducida. La primera provee servicios básicos y monta sistemas de archivos, mientras que la segunda no. 21

Logs de inicio A fin de investigar inconvenientes cuando se produzcan, a menudo recurriremos a los registros de operación (también llamados logs) del sistema. Mediante el comando dmesg (diagnostic msg) podemos acceder a los mensajes emitidos por el kernel a lo largo de la secuencia de arranque hasta el momento actual. Por motivo de que cuando ejecutamos dmesg sin opciones se muestran todos los mensajes, por lo general resulta útil aplicar un paginador como less, un filtro como head o tail (para ver los primeros o los últimos registros), o utilizar grep para identificar los resultados que cumplen con un patrón. Con respecto a esta última alternativa, en el ejemplo que aparece a continuación veríamos solamente los mensajes relativos a los dispositivos USB: dmesg | less dmesg | grep -i usb Otra forma de refinar la información que podemos extraer de dmesg consiste en especificar el origen (o facilidad, tal cual se llama en la documentación de la herramienta) y el nivel del mensaje. Entre los orígenes disponibles se encuentran los siguientes: kern: mensajes del kernel user: mensajes de nivel de usuario mail: sistema de correo daemon: daemons del sistema auth: mensajes de autenticación/seguridad syslog: mensajes generados internamente por syslogd lpr: subsistema de impresión news: subsistema de red Y en cuanto a los niveles: emerg: emergencia (el sistema está inutilizable) alert: alerta (se requiere una acción de inmediato) crit: condición crítica err: condiciones de error 22

warn: condiciones de advertencia notice: condición normal pero significativa info: de carácter informativo debug: mensajes de nivel de debug Por ejemplo, con dmesg –level=err,warn mostraremos únicamente los mensajes de error o advertencia provenientes de cualquier origen. También podemos agregar la opción --facility=kern para filtrar los resultados todavía más: dmesg --level=err,warn –facility=kern Además de dmesg, systemd provee una utilidad llamada journalctl para acceder al registro de eventos de arranque (y de otras clases también). Con journalctl --boot podemos acceder a los datos correspondientes a un booteo en particular. La lista de inicios para los que se dispone de información puede verse reemplazando --boot con --list-boots. En la imagen siguiente podemos apreciar el resultado, donde -0 se utiliza para indicar el booteo indicado como 0. En este caso se trata del inicio actual: journalctl --list-boots journalctl --boot -0 | head -n 5 journalctl --boot -0 | tail -n 5

23

Además, journalctl admite opciones que nos permiten ver datos de unidades específicas. Por ejemplo, con la opción --unit=UNIT podemos visualizar datos de la unidad representada por UNIT. Es decir, el comando journalctl --unit=cron --boot nos devolverá la lista de registros relacionados con cron durante el inicio del sistema. Por otro lado, si nos interesa mostrar los eventos de una prioridad dada únicamente, tenemos a nuestra disposición la opción -priority. Al indicar un nivel de prioridad veremos los mensajes correspondientes al mismo y a todos los menores. Por ejemplo, journalctl --boot –priority=4 nos mostrará los mensajes de nivel 4 (warning) hasta 0 (emerg), si los hubiere. Si deseamos que el sistema responda a eventos ACPI (Advanced Configuration and Power Interface) como el presionar el botón de suspender, y cerrar la tapa o desconectar el cargador de una laptop podemos recurrir a acpid. Este daemon queda a la escucha de eventos y ejecuta programas cuando ocurren. El administrador puede configurar qué es lo que debe suceder ante cada evento mediante los archivos de configuración ubicados en /etc/acpi/events.

El proceso de inicio de sesión Cuando ingresamos un usuario y su correspondiente contraseña para iniciar sesión en el sistema, esta información se compara con la disponible en los archivos /etc/passwd y /etc/shadow. En el primero de ellos encontramos información de cada cuenta de usuario y en el segundo las contraseñas hasheadas de las cuentas que tienen permitido el inicio de sesión. Por lo general, en distribuciones modernas las contraseñas se protegen utilizando el algoritmo SHA512. Este método dificulta muchísimo el adivinar una contraseña a partir de la cadena de texto que la representa. 24

Si la contraseña que ingresamos ha sido aceptada por el sistema nos aparece el contenido del archivo /etc/motd que suele estar vacío pero que le sirve al administrador para dejar mensajes dirigidos a los usuarios con información pertinente. Por ejemplo:

“Hoy a las 20:00 horas se efectuará un backup. Les agradezco dejar todas las aplicaciones cerradas.” A continuación, el sistema operativo nos devuelve el prompt, o la línea de comandos preparada para recibir nuestras órdenes. En el caso de Debian el prompt inicial está compuesto por dos partes: El nombre del usuario actual, seguido del símbolo **@**. El nombre del equipo. El directorio inicial de trabajo, más conocido como el home o el directorio personal del usuario en cuestión. El símbolo ~ se utiliza para representar este directorio. Si estamos logueados como root, veremos el símbolo # a continuación. De otra manera (usuario común), se mostrará el signo $. Además de su directorio personal, cada usuario tiene asignado un shell o intérprete de comandos. Se trata de un programa que recibe los comandos que escribimos y que los envía al sistema operativo para ser ejecutados. El intérprete utilizado en la mayoría de las distribuciones actuales (de las cuales Debian no es la excepción) se llama Bash (Bourne-Again SHell). Bash tiene la posibilidad de ejecutar comandos en tiempo real pero además tiene un poderoso lenguaje de programación de scripts. Permite generar programas con funciones, control de flujo, creación de archivos, seguimiento de procesos, entre otros. Apagado y reinicio del sistema Aunque en teoría Linux puede funcionar durante semanas, meses, e incluso años sin necesidad aparente de reiniciar o o apagar el equipo, pueden surgir situaciones en las que es necesario hacerlo. Esto puede deberse a la necesidad de reemplazar un dispositivo de hardware interno, al mantenimiento eléctrico de la red desde donde recibe energía el equipo, o a un simulacro para verificar que el mismo puede reaccionar correctamente ante un evento inesperado. 25

Ante tales casos, es preciso conocer las opciones de las que disponemos a fin de proceder correctamente y con cautela. Dentro de lo posible, debemos evitar a toda costa el apagar cualquier equipo (no solamente aquellos que corren Linux) a la fuerza o desconectándolos repentinamente del suministro de energía eléctrica. Para casos críticos (servidores principalmente), los equipos deben estar conectados a una UPSs o a un generador independiente en paralelo. Para apagar el equipo disponemos de las siguientes alternativas (la primera se incluye por compatibilidad hacia atrás con el runlevel 0): init 0 poweroff halt shutdown -h [CUANDO] [MENSAJE OPCIONAL] Las alternativas para reiniciarlo son similares (la primera se incluye por compatibilidad hacia atrás con el runlevel 6): init 6 reboot shutdown -r [CUANDO] [MENSAJE OPCIONAL] Bajo SysVinit, la utilización de shutdown traía aparejada la ventaja de que los procesos que estuvieran corriendo se detenían correctamente, y los sistemas de archivos se desmontaban prolijamente, a diferencia de las otras opciones que provocaban un apagado abrupto del equipo. Por otra parte, bajo systemd todos los comandos anteriores son sinónimos, y constituyen enlaces simbólicos al propio sistema de inicio o a systemctl. De todas maneras, tanto en el caso de apagar como para reiniciar el equipo, la ventaja de usar shutdown consiste en poder indicar el momento en el que se realizará la acción deseada (now para ahora, o una hora específica en el formato de 24 hs HH:MM) y enviar un mensaje (opcional) para alertar a los usuarios que estén conectados al sistema. Por ejemplo, el siguiente comando: shutdown -r 14:00 'El sistema se reiniciará a las 14 hs' enviará el mensaje entre comillas a los usuarios que estén conectados y agendará el reinicio del sistema para las 14:00 hs.

26

Si existe un apagado pendiente y deseamos cancelarlo, podemos utilizar la opción -c para tal fin:

shutdown -c 'El apagado ha sido continuar trabajando normalmente.'

cancelado.

Puede

Cuando hay un apagado o reinicio agendado o en curso no es posible iniciar nuevas sesiones de usuario. Si es cancelado, se levantará esa restricción.

Enviar mensajes a terminales Además de shutdown (al reiniciar o apagar un equipo), hay otra herramienta llamada wall que nos permite enviar mensajes a las terminales durante el funcionamiento normal del sistema. Esto puede sernos útil para alertar sobre cambios inminentes a los usuarios conectados. En primer lugar, para poder enviar mensajes a una terminal dada es necesario que la misma admita operaciones de escritura. Aunque suele ser el valor por defecto, podemos chequear este dato utilizando el comando mesg. Si el mismo devuelve is y, significa que la terminal aceptará mensajes. De otra forma, si la salida es is n, significa que no podremos enviar mensajes a dicha terminal. Para cambiar este valor, podemos utilizar mesg y o mesg n para habilitar o deshabilitar la escritura, respectivamente. 27

A continuación, ilustramos el uso de wall y mesg utilizando dos terminales. En una de ellas ejecutaremos este último comando para verificar que dicha terminal pueda recibir mensajes. Desde la otra terminal enviaremos el mensaje Hola a todos. Vamos a proceder a hacer cambios en la configuración de Apache.

Es importante aclarar que usuarios con privilegios limitados también pueden escribir a las terminales de otros usuarios siempre y cuando estas últimas lo acepten. Sin embargo, no podrán evitar el banner con la opción -n, cosa que solamente el superusuario puede hacer.

28

Instalación de Linux y Administración de Paquetes (Tema102) En este capítulo hablaremos sobre los pasos a seguir y los puntos a tener en cuenta antes de instalar Linux. También explicaremos cómo administrar el software instalado y mostraremos cómo agregar nuevos programas cuando sea necesario. Uno de los pasos previos más importantes al instalar Linux y en preparación para su uso consiste en crear particiones y sistemas de archivos. Una partición es una división lógica que se hace a un dispositivo de almacenamiento con el propósito de definir un espacio determinado para un uso en particular. Por otro lado, un sistema de archivos puede definirse (en este contexto) como la forma en que se estructurarán los contenidos (archivos y directorios) de dicha partición. 29

Disposición de unidades de almacenamiento Antes de instalar Linux, es importante dedicar algunos minutos para determinar cómo utilizaremos el espacio de almacenamiento disponible de forma eficiente. Por lo general, lo dividiremos en secciones individuales (utilizando particionado tradicional o volúmenes lógicos), con tamaños adecuados para el uso del sistema. Por ejemplo, si un servidor cumplirá con la tarea de alojar directorios personales de usuarios reales que utilizarán activamente el sistema, se deberá asignar un tamaño importante para la partición donde se montará /home. Por otro lado, si el equipo se va a emplear para instalar y probar múltiples kernels, la partición destinada a /boot deberá estar preparada para alojar la cantidad de archivos necesarios. Diseño de particiones Teniendo en cuenta el uso que se le vaya a dar al sistema, nunca debemos permitir que la partición asociada con / se llene, debido a que eso ocasionaría errores en el funcionamiento de los servicios e incluso podría llegar a inhabilitar al usuario root para iniciar sesión. Teniendo esto en cuenta, veamos qué consideraciones debemos tener en cuenta para la creación y la asignación de espacio de cada partición: 1. El directorio /etc siempre debe debe estar dentro de la partición destinada al directorio raíz. Dicho de otro modo, no se le debe asignar una partición dedicada. 2. Como regla general se prefiere utilizar particiones separadas para /home, /var, y /tmp. Hay que tener en cuenta que el uso de estos directorios es un tanto difícil de estimar en un principio. 3. En el caso de que planeemos instalar un número importante de aplicaciones de terceros, es buena idea apartar una partición dedicada para /opt también. 4. No hay un porcentaje o fórmulas fijos que en la práctica se aplique a cualquier escenario imaginable. Por ese motivo también será necesario recurrir al buen juicio a la hora de crear particiones. 5. El espacio de intercambio (swap) preferentemente debe estar ubicado en un dispositivo separado del resto por cuestiones de 30

rendimiento. Por lo general el tamaño de swap suele ser de 1.5 a 2 veces el tamaño de la memoria física (RAM), aunque no es necesario que supere los 8 GB. 6. La partición destinada a /boot debe poder ser accedida desde el principio del proceso de arranque, por lo que no debemos alojarla en una unidad de red (preferentemente tampoco en una unidad extraíble). Es posible que la documentación de algunas distribuciones sugiera ciertos valores para particiones. En ese caso la experiencia del administrador indicará si son factibles de acuerdo con el espacio de almacenamiento disponible. Sistemas de archivos Los sistemas de archivos más utilizados hoy en día son ext4 y xfs. Veamos algunas características de ambos: ext4 (tomado de la documentación oficial): Tamaño máximo de sistema de archivos (generalmente se extiende a toda la partición): 1 EiB = 1024 PB (1 PiB = 1024 TiB, 1 TiB = 1024 GiB) Tamaño máximo de archivos: 16 TiB Cantidad máxima de subdirectorios: 64.000 Journaling: permite la recuperación de archivos que estaban siendo escritos en el momento de un fallo del sistema. Si bien esta característica no elimina totalmente las posibilidades de encontrar archivos corruptos, sí contribuye a la confiabilidad del sistema de archivos y a la rapidez del chequeo de los mismos. Permite aumentar o disminuir el tamaño del sistema de archivos. xfs: Tamaño máximo de sistema de archivos (generalmente se extiende a toda la partición): 8 EiB en sistemas operativos de 64 bits y de 16 TiB en 32 bits. Tamaño máximo de archivos: 9 EiB Journaling Es posible aumentar el tamaño del sistema de archivos pero no disminuirlo 31

Creación de particiones y sistemas de archivos En Linux, la herramienta tradicional para administrar particiones basadas en MBR (presente por defecto en computadoras fabricadas hasta 2009 aproximadamente) es fdisk, mientras que desde 2010 en adelante (particiones basadas en GPT) se comenzó a utilizar también otra utilidad llamada gdisk.

Es importante aclarar que todas las operaciones relacionadas con la creación de particiones, sistemas de archivos, y montaje de dispositivos deben ser realizadas por root o con un usuario con permisos de superusuario.

Cabe aclarar que el estándar GPT permite crear hasta 128 particiones primarias en un mismo disco, y permite alcanzar tamaños de particiones no soportados por MBR (el cual soporta hasta 2 TB y hasta 4 particiones primarias – si se necesitan más particiones se puede reemplazar una de las primarias por una extendida que pueda dividirse en múltiples particiones lógicas). Para crear una partición con fdisk, utilizamos el comando del mismo nombre seguido por el identificador de dispositivo en cuestión. Por ejemplo, para /dev/sdb: fdisk /dev/sdb 32

En la pantalla siguiente podemos presionar la tecla m para ver el menú de ayuda como se muestra en la imagen (por limitaciones de espacio no se alcanzan a apreciar todas las opciones disponibles):

Las operaciones más usuales que podemos llevar a cabo a partir de este momento son las siguientes: m: mostrar el menú de ayuda p: mostrar la tabla de particiones del dispositivo l: mostrar los tipos de particiones soportadas por fdisk n: agregar una nueva partición d: borrar una partición existente w: guardar los cambios en la tabla de particiones y salir q: salir sin guardar cambios 33

Algunos puntos importantes para destacar: Al crear una nueva partición, tendremos que confirmar si deseamos que sea una partición primaria o una extendida. Si no hemos creado ninguna partición todavía, deberíamos elegir primaria. Para el comienzo de la partición es conveniente aceptar el valor ofrecido por defecto por fdisk presionando Enter. Para la finalización de esta es conveniente especificar el tamaño deseado de la misma utilizando el signo + seguido de un número y de la nomenclatura M, o G (por MB y GB, respectivamente). La primera partición de /dev/sdb recibirá el identificador /dev/sdb1, la segunda /dev/sdb2, y así sucesivamente. Por defecto, al crear una nueva partición, se le asignará el tipo Linux (83). Si deseamos cambiarlo, podemos listar los tipos disponibles con L, elegir el adecuado, y luego asignarlo con la opción t. Si en el dispositivo seleccionado existe más de una partición y deseamos borrar una de ellas, tendremos que especificar el número de esta (a partir del orden en el que aparecen al mostrar la tabla de particiones). Antes de confirmar cualquier cambio realizado, es importante revisar la tabla de particiones, ya que una vez que se guarden los cambios y se salga del menú de fdisk es probable que los mismos sean irreversibles (lo cual es particularmente crítico si hemos modificado una partición que contenía datos).

34

Por ejemplo:

En la imagen anterior vemos cómo crear una partición de 2,5 GB que recibió la identificación /dev/sdb1. Luego de guardar el cambio volvimos a la línea de comandos y con fdisk -l /dev/sdb podemos ver la tabla de particiones actual. Importante: Utilizar un punto en vez de una coma al especificar el tamaño de las particiones si estamos utilizando un sistema cuyo separador de decimales es el primero. Una vez que disponemos de una nueva partición, es hora de crear un sistema de archivos sobre la misma a fin de poder utilizarla. La utilidad por excelencia para realizar esta tarea es mkfs (entre sus variantes podemos destacar mkfs.ext4 y mkfs.xfs para sistemas de archivos ext4 y mkfs, respectivamente). Entre las muchas opciones de las que disponemos, -L en particular nos será de gran utilidad para asignarle una etiqueta al sistema de archivos, mediante la cual podremos identificarlo más fácilmente a la hora de montarlo y disponerlo finalmente para su uso (tema que trataremos más adelante). 35

Una ventaja adicional de LVM es que permite tomar instantáneas de los volúmenes lógicos, las cuales pueden funcionar como copias de respaldo o ser utilizadas para migraciones, pruebas, restauraciones, etc., sin afectar el funcionamiento del sistema en su totalidad. Particionado tradicional versus LVM Para empezar, recordemos que al menos en MBR, hay una limitación en el número de particiones primarias por disco. En el caso de que se tenga que redimensionar una de ellas, se requiere mucho cuidado. Si bien es posible achicar una partición para darle espacio a otra que haya quedado pequeña, este procedimiento causa los siguientes inconvenientes: Provoca downtime. Esto significa que mientras estemos redimensionando las particiones nadie más podrá utilizar el sistema. Si se trata de nuestro equipo personal, los únicos afectados seríamos nosotros. Pero si estamos hablando de un servidor al que necesiten acceder otros usuarios, esta alternativa puede no ser viable. Requiere copias de respaldo. Si no se dispone de espacio extra en alguna partición, no quedará más remedio que agregar otro disco al equipo. Si queremos utilizar el nuevo dispositivo para /home (por ejemplo), esto implicará mover todos los archivos de los usuarios de la ubicación anterior a la nueva. ¿Por qué esto es un inconveniente? Dependiendo del tamaño del backup, la restauración agregará tiempo al downtime del que hablamos en el punto anterior. Por otro lado, el redimensionado de los volúmenes lógicos bajo LVM se puede realizar sin interrumpir el funcionamiento normal del equipo. Además, el procedimiento es mucho más rápido que en el particionado tradicional. Los cambios quedan disponibles a los usuarios inmediatamente sin necesidad de reiniciar ningún servicio. 36

El gestor de arranque GRUB Un gestor de arranque es un componente de software que ayuda al hardware y al firmware del equipo en la carga del sistema operativo. Esto incluye también la posibilidad de elegir entre varios sistemas operativos o kernels instalados en el equipo e inclusive el arranque desde una partición lógica.

GRUB significa GRand Unified Bootlader. Actualmente podemos llegar a encontrarlo en sus dos versiones: GRUB Legacy (en sistemas más antiguos) y el más nuevo GRUB2. Aunque su configuración varía según la versión, los principios fundamentales del funcionamiento del gestor de arranque son los mismos. En este capítulo haremos referencia exclusivamente a GRUB2 al referirnos a GRUB. Para más detalles sobre la versión legacy, podemos referirnos a la documentación presente en el sitio del proyecto GNU. Al interactuar con GRUB, el administrador puede configurar el proceso de arranque de un sistema Linux. Si bien es probable que no tengamos que hacerlo a menudo, es importante saber cómo proceder ante la necesidad. Situaciones tales como la actualización del kernel o cambios en los dispositivos del equipo pueden requerir que recurramos a las opciones del GRUB para bootear exitosamente el sistema.

No se recomienda editar el archivo de configuración (/boot/grub/grub.cfg) del gestor de arranque directamente. Más bien, se debe trabajar con los scripts presentes en /etc/grub.d. La ubicación de los archivos de configuración puede llegar a variar según la distribución y la versión. 37

Interactuar con GRUB Durante el proceso de arranque, GRUB presenta un menú de modo texto que permite elegir entre distintos kernels o sistemas operativos instalados en el equipo. Con las flechas del teclado se puede elegir la opción deseada y presionar Enter para confirmarla. Además, presionando las teclas c o e se puede acceder a una línea de comandos básica o a las opciones de arranque, respectivamente. Mediante estas herramientas podemos adaptar el inicio del sistema a nuestras necesidades, tal como se aprecia en la siguiente imagen:

En la imagen de arriba podemos observar algunos puntos interesantes en la configuración de la opción Debian GNU/Linux. Las mismas pueden editarse de ser necesario. Luego, los cambios se guardan y se retoma el proceso de inicio al presionar Ctrl + x o F10. Además: insmod se utiliza para cargar los módulos gzio, part_msdos, y ext2. Mediante la directiva set root (recuadro rojo con flecha apuntando hacia la derecha) se le indica a GRUB dónde buscar el archivo grub.cfg. En este caso se trata del primer disco (hd0). Más específicamente, en la primera partición bajo el esquema de particionado tradicional (msdos1). Si el sistema no inicia correctamente, podemos usar la shell de GRUB para realizar un diagnóstico preliminar. Accedemos a la misma presionando la tecla c desde el menú principal que mencionamos antes. Esta herramienta admite el uso de varios comandos aceptados por Bash u otras shells. 38

Configuración de GRUB En el archivo /etc/default/grub se indican las directivas de configuración de GRUB, tales como el sistema operativo o kernel que se debe utilizar por defecto en el arranque del sistema (GRUB_DEFAULT) o el tiempo (en segundos) que se debe esperar antes de iniciar el inicio (GRUB_TIMEOUT), por nombrar dos ejemplos:

El resto de las opciones están disponibles en la documentación de GRUB2. Además, el menú que muestra GRUB en el momento de arranque se construye a partir de las indicaciones presentes en /etc/grub.d, el cual contiene una serie de scripts que se ejecutan en orden (de acuerdo al número presente como parte del nombre de archivo) como vemos en la siguiente imagen:

39

En vez de modificar estos scripts, podemos crear nuevos si deseamos agregar opciones personalizadas. Bastará crear un archivo y agregarlo al final de 40_custom para que se tenga en cuenta al momento de inicio. Sin embargo, esta habilidad no es necesaria para el examen LPIC1, por lo que no la trataremos en este capítulo. Para aplicar los cambios que se hayan hecho en la configuración de GRUB utilizaremos el comando grub-mkconfig (el nombre puede también ser grub2-mkconfig) pero antes es una buena idea hacer una copia de la configuración original en el caso de que necesitemos restaurarla: cp /boot/grub/grub.cfg /boot/grub/grub.cfg.original grub-mkconfig > /boot/grub/grub.cfg Con el paso anterior lo que hicimos en realidad fue reconstruir el archivo de configuración. Por otro lado, si por alguna razón GRUB se hubiera dañado y no pudiéramos iniciar el sistema normalmente desde el disco, podemos reinstalarlo luego de arrancar desde un dispositivo de emergencia (como un live CD/DVD/USB). Esto es posible utilizando grub-install seguida de la opción --bootdirectory, del directorio en el que previamente hayamos montado el directorio /boot, y el disco donde deseamos instalar GRUB. Por ejemplo: grub-install --boot-directory=/tmp/root/boot /dev/sda Este método de rescate es más directo y versátil que en la versión legacy. La desventaja de GRUB2 es que es un tanto más difícil de configurar que la anterior, pero con la ventaja de ser más flexible. 40

Usar la shell de GRUB A continuación, explicaremos cómo verificar si la ubicación del archivo de configuración de GRUB (grub.cfg) fue especificada correctamente. Luego de acceder a la línea de comandos mínima, ejecutaremos el comando ls. El mismo nos devolverá la lista de dispositivos encontrados. A continuación, podemos identificar los siguientes: ls ls (hd0,msdos1) ls (hd0,msdos1)/ ls (hd0,msdos1)/grub

hd0 (primer disco), que representa a /dev/sda, y las particiones hd0,msdos1 (/dev/sda1) y hd0,msdos5 (/dev/sda5). Otros discos: hd1, hd2, y hd3 (/dev/sdb, /dev/sdc, y /dev/sdd, respectivamente). 41

Como podemos ver en la imagen de arriba, el archivo grub.cfg efectivamente está ubicado en la partición 1 del primer disco. Esto concuerda con el valor que observamos para la opción set root, tal como comentamos antes. Si repitiéramos el comando ls para ver el contenido de otros dispositivos (hd0,msdos5 por ejemplo), nos encontraríamos con un mensaje de error. El mismo nos alertaría sobre el hecho de que se desconoce el sistema de archivos en cuestión. Lo que sucede en realidad es que dicha partición no es la que contiene los archivos de arranque. Para ver la lista completa de comandos que acepta la shell de grub podemos utilizar help en cualquier momento. Por otro lado, presionando la tecla Esc se vuelve al menú anterior. Además, presionando la tecla Tab se muestran las posibles opciones de automcompletado.

Librerías compartidas Una librería es un conjunto de funciones incluidas en un mismo archivo. Dichas funciones por lo general son utilizadas por una variedad de programas que, en vez de incorporarlas en su propio código (enlace estático), las llaman cuando es necesario (enlace dinámico). Esto, por supuesto, requiere que dichas librerías existan en el sistema previo al lanzamiento del programa. La ventaja reside en que en este último caso solamente es necesario una copia de la librería, la cual es compartida por todas las aplicaciones que la necesitan. El enlace estático, en cambio, requiere que cada programa disponga de su propia copia y la incluya como parte del programa en sí.

Si una librería dada no está disponible, cualquier programa que la necesite no podrá funcionar.

42

En el directorio /lib (o su equivalente para sistemas de 64 bits, /lib64) encontraremos librerías compartidas que son utilizadas por los programas ubicados en /bin y /sbin. En distribuciones modernas que utilizan systemd, /lib y /lib64 son en realidad enlaces simbólicos a /usr/lib y /usr/lib64, respectivamente. En estos directorios también encontramos las librerías de programas de usuario. Si bien son importantes, estas últimas no son críticas para la operación del sistema. Tomemos como ejemplo a /usr/bin/ssh y a /bin/cat. Utilizaremos el comando file para determinar el tipo de cada archivo: file /usr/bin/ssh file /bin/cat Como podemos ver en la imagen siguiente, ambos son programas enlazados dinámicamente. En la misma imagen podemos ver, utilizando el comando ldd, cuáles son las librerías de las que depende /bin/cat: ldd /bin/cat

Para asegurar que los enlaces a las librerías se mantienen actualizados y en caché se llama un programa llamado ldconfig como parte de la instalación de estas. Por lo general no es necesario ejecutarlo manualmente a menos que hayamos hecho cambios en el archivo /etc/ld.so.conf, que es donde se puede especificar la clase de ciertas librerías si estas no contienen información suficiente para deducir esa información. También podemos utilizar este archivo para indicar la ubicación de las librerías necesarias para una aplicación en particular.

43

Al inspeccionar el contenido de la variable de entorno LD_LIBRARY_PATH podemos ver la lista de directorios que se tienen en cuenta para buscar librerías compartidas. De esta manera es posible identificar las librerías compartidas que un archivo binario ejecutable necesita para poder funcionar. Si por alguna razón no se encuentran disponibles, podremos instalarlas como aprenderemos a continuación. Administración de paquetes: vocabulario clave En Linux a menudo se utilizan las palabras programa y paquete indistintamente, aunque estrictamente hablando no sean iguales. Sin embargo, para nuestros propósitos actuales podemos considerarlos equivalentes. De esa manera, cuando hagamos referencia a la administración de paquetes estaremos hablando de programas. Comencemos definiendo a la administración de paquetes como el conjunto de tareas de instalación, actualización, y desinstalación de recursos de software en nuestro sistema. Hoy en día, la distribución de software en Linux se realiza de tres maneras principales: Repositorios: son servidores centrales, mantenidos generalmente por una distribución en particular, por la comunidad, o por otros terceros, en los que se dispone de una colección de software para la instalación a través de Internet o de una red local. En términos simples, un repositorio en Linux (también llamado origen de software) es una colección de programas compatible con una distribución dada. Un repositorio puede residir en un servidor remoto (que es lo más común) o en un (o varios) dispositivos de almacenamiento locales. Archivos precompilados: son archivos ejecutables compatibles para una distribución en particular y que pueden instalarse tanto a través de la red como de manera local con una herramienta de la línea de comandos (en unos minutos veremos esto en más detalle). Código fuente: es la forma inicial mediante la cual se distribuía software en Linux en sus comienzos. Aunque ha sido superada por 44

las dos anteriores, todavía continúa usándose para ofrecer a los usuarios finales la posibilidad de compilar un programa dado con opciones particulares. También se utiliza este método de distribución de software para poner al alcance del público una versión más actualizada que tenga mayores prestaciones que las presentes en los repositorios. Cada distribución tiene distintas utilidades para la administración de paquetes. Algunos de ellos (como yum o apt-get, para CentOS y Debian, respectivamente, junto a los miembros de cada familia) tienen la habilidad de instalar junto con un programa dado sus dependencias, es decir, aquellos paquetes adicionales que son necesarios para el funcionamiento del primero. Por esa razón se los conoce más comúnmente con el nombre de gestores de paquetes. Otros, como dpkg (Debian y derivados) y rpm (CentOS y familiares), solamente instalan el programa presente en el archivo .deb o .rpm pero sin instalar dependencias. Más sobre repositorios Por razones prácticas, no todos los repositorios disponibles son incluidos en la instalación inicial de una distribución, pero pueden ser agregados luego cuando se los necesite. Todas las distribuciones mantienen sus propios repositorios oficiales, y además dispone de otros que son mantenidos por la comunidad o usuarios particulares (en este último caso generalmente se trata de repositorios que contienen algunos pocos paquetes). Además, a menudo los repositorios se clasifican de acuerdo con el tipo de software que contienen. Aunque cada distribución difiere un poco de las otras en la forma en que maneja sus repositorios, el principio de funcionamiento es el mismo: 45

1. Se configuran los repositorios en los que desea buscar software para nuestro sistema. 2. Antes de utilizar los nuevos repositorios configurados se requiere actualizar el índice de paquetes local de tal manera que refleje los contenidos de los repositorios. 3. Se utiliza el gestor de paquetes de cada distribución para buscar información sobre un paquete dado o directamente para instalarlo.

Para evitar congestionar un único repositorio, alrededor del mundo existen varias réplicas idénticas o mirrors (espejos) de los repositorios centrales de cada distribución. En Debian, la lista de repositorios se mantiene en el archivo /etc/apt/ sources.list y en archivos ubicados dentro de /etc/apt/sources.list.d En uno u otro caso, el formato del archivo es el mismo según mostramos a continuación, tomando como ejemplo el mirror de Debian provisto por la UBA (Universidad de Buenos Aires, Argentina): deb http://ftp.ccc.uba.ar/pub/linux/debian/debian/ stable main contrib non-free deb-src http://ftp.ccc.uba.ar/pub/linux/debian/debian/ stable main contrib non-free Veamos ahora en detalle cómo están compuestas las líneas de arriba: deb y deb-src indican si el repositorio contiene paquetes binarios compilados previamente o los paquetes fuente (que contienen todo lo necesario para que uno compile un programa en particular), respectivamente. A continuación, encontramos la ruta al repositorio. Luego aparece la palabra stable (o el codename de la versión estable, como por ejemplo stretch a la fecha de hoy). Finalmente, tenemos la lista de componentes del repositorio (no hacen falta que figuren los tres necesariamente): 46







main: contiene software compatible con las Políticas Debian de Software Libre. contrib: contiene software compatible con las Políticas Debian de Software Libre, aunque sus dependencias generalmente están incluídas en non-free (ver a continuación). non-free: software no compatible con las Políticas Debian de Software Libre.

En el sitio oficial del proyecto Debian se puede consultar la lista de mirrors por países. Podemos elegir el país en el que vivamos o utilizar la red de distribución de contenidos (CDN) principal. En CentOS o similares, los repositorios se configuran mediante archivos individuales dentro de /etc/yum.repos.d. Tomemos como ejemplo a CentOS-Base.repo, cuyo contenido podemos apreciar a continuación (no se muestra el archivo completo por cuestiones de espacio):

En la imagen anterior podemos distinguir que la configuración de un repositorio consta de: El nombre (base). Una lista de mirrors (mirrorlist) desde la cual se seleccionará el más próximo geográficamente, o un repositorio fijo (baseurl). La indicación si se necesita utiliza la clave pública del repositorio. La ruta al archivo de la clave pública. 47

Cuando agreguemos un nuevo repositorio a CentOS se nos pedirá que confirmemos que queremos instalar en nuestro equipo la clave GPG que dicho repositorio provee por cuestiones de seguridad. En algunas ocasiones será necesario agregar manualmente un repositorio de terceros. Generalmente tendremos que recurrir a esta opción cuando deseemos instalar una versión más reciente de un programa que la que se encuentre disponible en los repositorios oficiales de la distribución. Para ilustrar agregaremos el siguiente repositorio en /etc/yum.repos.d/mariadb.repo para instalar la última versión estable de MariaDB. En primer lugar, agreguemos las siguientes líneas dentro del archivo mencionado: # MariaDB 10.3 CentOS repository list - created 2018-06-08 00:28 UTC # http://downloads.mariadb.org/mariadb/repositories/ [mariadb] name = MariaDB baseurl = http://yum.mariadb.org/10.3/centos7-amd64 gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB gpgcheck=1 El nombre de los archivos de configuración de los repositorios que añadamos manualmente deben finalizar en .repo. Lo mismo aplica tanto para CentOS y similares (en /etc/yum.repos.d) como para las distribuciones de la familia SUSE dentro de /etc/zypp/repos.d. Para asegurarnos de que nuestro sistema puede ver la lista completa y actualizada de paquetes disponibles en los repositorios debemos sincronizar el índice local con los mismos. En Debian y derivados lo hacemos mediante apt-get update, mientras que en CentOS o similares utilizamos yum makecache fast. En openSUSE el comando equivalente es zypper refresh. 48

Administración de paquetes en Linux Debian y derivados Para instalar archivos .deb , utilizaremos el comando dpkg seguido de la opción -i o --install y de la ruta al archivo a instalar. Por ejemplo, para instalar mipaquete.deb (presente en nuestro directorio actual de trabajo) utilizaremos el siguiente comando: dpkg -i mipaquete.deb Si posteriormente quisiéramos instalar una nueva versión del mismo programa mediante un archivo .deb más reciente, podemos utilizar el mismo comando anterior. En cambio, si necesitamos desinstalarlo emplearemos la opción -r (también puede ser --remove) o -P (de -purge) simplemente incluyendo el nombre del paquete: dpkg -r mipaquete La diferencia entre --remove y --purge reside en que esta última remueve el paquete junto con cualquier archivo de configuración que se pueda haber creado como parte de la instalación, mientras que la primera no. Finalmente, con -l podremos ver una lista de todos los paquetes instalados en el sistema. Como la salida será extensa en este caso se recomienda que la visualicemos con less o la filtremos con alguna herramienta como tail, head, o grep. dpkg -l | head Otras dos opciones muy útiles son --info y --contents. La primera nos permitirá ver la información sobre el paquete en cuestión, mientras que la segunda nos mostrará el contenido de este (en ambos casos debemos especificar el nombre completo del archivo .deb a examinar): dpkg --info mipaquete.deb dpkg --contents mipaquete.deb 49

De forma similar a --contents, podemos emplear --listfiles (en este caso utilizamos solamente el nombre del paquete, sin la extensión .deb) para ver los archivos que se agregaron al sistema al instalar un paquete (sea que lo hayamos hecho mediante dpkg o apt-get, como veremos a continuación). Para asegurarnos de que un programa se instale con todas las dependencias necesarias podemos utilizar el gestor de paquetes apt-get en vez de dpkg. La herramienta asociada apt-cache también nos resultará útil para buscar programas en los repositorios y para ver información detallada sobre alguno que haya captado nuestra atención. Ambas utilidades (junto con otras) forman parte de APT (Advanced Package Tool) que es el sistema de gestión de paquetes principal de Debian. Luego de haber agregado repositorios en /etc/apt/sources.list o en algún archivo dentro de /etc/apt/sources.list.d, sincronicemos la lista de paquetes disponibles con los repositorios de Debian: apt-get update Luego de eso, podremos emplear apt-cache search mipaquete para utilizar a mipaquete como patrón de búsqueda, o si ya conocemos el nombre exacto podemos emplear apt-cache show mipaquete apt-cache showpkg mipaquete para ver información detallada sobre el mismo, o apt-get install mipaquete para instalarlo. Si agregamos la opción -f al comando anterior también podremos completar cualquier instalación que hayamos 50

llevado a cabo mediante dpkg y que no se haya completado exitosamente por dependencias faltantes.

Para aceptar automáticamente los pedidos de confirmación podemos agregar la opción -y al comando anterior. Esto puede resultar particularmente útil en el caso de que vayamos a automatizar una instalación (o actualización) de alguna manera. Lo mismo aplica al uso de yum install en CentOS y similares. Por otro lado, si mipaquete ya estuviera instalado y quisiéramos removerlo, cualquiera de los comandos siguientes nos permitirá hacerlo: apt-get remove mipaquete apt-get purge mipaquete El último de ellos no solamente eliminará mipaquete del sistema sino que también hará lo mismo con cualquier archivo de configuración relacionado con el programa en cuestión. Para actualizar un paquete a la última versión disponible usaremos apt-get upgrade mipaquete Si en el comando anterior omitiéramos el nombre del paquete, el resultado sería que se actualizarían todos ellos a la última versión disponible en los repositorios configurados del sistema. Reconfigurar paquetes Es importante notar que durante el curso de una instalación por lo general se nos ofrece la posibilidad de configurar el programa en cuestión. Si no lo hacemos en ese momento, también podemos hacerlo posteriormente utilizando la herramienta dpkg-reconfigure. En primer lugar, podemos revisar la configuración actual con el comando debconf seguido del nombre de un paquete dado. Usemos como ejemplo el paquete keyboard-configuration, según se aprecia 51

en la siguiente imagen: debconf-show keyboard-configuration

Si vemos algo que queremos o vemos la necesidad de cambiar, ahora es el momento de recurrir a dpkg-reconfigure. Siguiendo con el mismo ejemplo de arriba, ahora se deberá utilizar el comando dpkg-reconfigure keyboard-configuration para hacer las modificaciones correspondientes utilizando solamente el teclado. La ventaja de utilizar este método para configurar software ya instalado es que nos evitamos tener que editar archivos de texto a mano. Incluso en el caso de que no todas las opciones estén contempladas, nos ahorra trabajo. Otra situación en la que dpkg-reconfigure puede resultar útil es para restaurar la configuración de un paquete a su estado original. Si cometimos algún error y no hicimos copias de respaldo antes de comenzar, esta herramienta puede ayudarnos a salir del aprieto. Simplemente aceptando las opciones que se nos presenten por defecto deberíamos resolver el inconveniente. 52

CentOS y similares RPM (Red Hat Package Manager) es el gestor de paquetes utilizado por defecto en Red Hat Enterprise Linux como en SuSE (y distribuciones derivadas como CentOS, Fedora, u OpenSUSE). Fue desarrollado por Red Hat y luego adoptado por otras distribuciones. Podemos decir que consta de 2 componentes fundamentales: 1) el comando rpm, y 2) una base de datos local. Esta última contiene la lista de paquetes instalados e información sobre estos y se encuentra dentro del directorio /var/lib/rpm. Para empezar, tomemos como ejemplo a bc-1.06.9513.el7.x86_64.rpm. Podemos descargar el mismo desde el listado de paquetes disponibles para CentOS 7. En el nombre del archivo podemos encontrar a primera vista los siguientes datos: El nombre del paquete: bc La versión: 1.06-95-13.el7 La arquitectura: x86_64 Por lo general, la instalación se realiza con las opciones combinadas -Uvh o -Fvh en vez del simple -i (o su equivalente --install, también válidos). De las dos alternativas anteriores, la primera instala o actualiza (según sea el caso), mientras que la segunda solamente instalará el paquete y no realizará ninguna acción si ya existe una versión previa del mismo. Para nuestra conveniencia, -v muestra el nombre del paquete durante la instalación y -h imprime una serie de #’s para indicar el progreso de la instalación. Ambas son útiles en particular si estamos instalando varios paquetes a la vez.

Para desinstalar un paquete que hayamos previamente instalado con rpm, utilizaremos la misma herramienta, pero esta vez con la opción -e seguida del nombre del paquete. No es necesario incluir la versión de este ni la arquitectura. 53

Otras opciones útiles del comando rpm que no pueden faltar en nuestro arsenal son (siempre seguidas de -q, de query): -a: muestra la lista de todos los paquetes instalados. -l: lista de los archivos instalados por un paquete dado. -ip: ver información detallada sobre un archivo .rpm que no ha sido instalado. -f: devuelve el nombre del paquete que contiene el archivo que se especifica a continuación. -Rp: muestra la lista de dependencias del paquete. En la imagen siguiente vemos los siguientes ejemplos ilustrados: Verificamos si el paquete htop está instalado usando rpm -qa | grep htop Mostramos la lista de archivos agregados al sistema como resultado de haber instalado htop con rpm -ql htop Vemos las dependencias del paquete tuxpaint mediante rpm qRp tuxpaint-0.9.22-1.x86_64.rpm

54

Para ver el contenido completo de un archivo .rpm utilizar los comandos rpm2cpio y cpio de la siguiente manera: rpm2cpio tuxpaint-0.9.22-1.x86_64.rpm | cpio -idv A pesar de lo útil que es rpm, quizás la manera más práctica de instalar un programa determinado (si es que se encuentra en los repositorios de nuestra distribución, o si la versión disponible cumple con nuestras necesidades), es utilizar el gestor de paquetes yum. El mismo contiene todas las utilidades necesarias para satisfacer las dependencias requeridas y configurar el programa ya instalado (en síntesis, ¡nos ahorra mucho trabajo!). Utilizando esta herramienta podemos: Buscar un paquete por palabra clave (mipaquete, por ejemplo): yum search mipaquete Ver información sobre mipaquete: yum info mipaquete Instalarlo: yum install mipaquete Actualizar a la última versión disponible: yum update mipaquete Tal como sucede en el caso de Debian con apt-get upgrade, si en este caso omitimos el nombre del paquete se actualizarán todos los presentes en el sistema. Desintalarlo: yum remove mipaquete Averiguar en qué paquete está incluído un comando determinado (por ejemplo, micomando): yum whatprovides "*/micomando" La configuración de yum se encuentra en /etc/yum.conf. Si bien podemos agregar repositorios en este archivo, por prolijidad y orden se prefiere hacerlo mediante archivos individuales (con la extensión .repo) dentro del directorio /etc/yum.repos.d como explicamos antes. Cuando agreguemos un nuevo repositorio a CentOS se nos pedirá que confirmemos que queremos instalar en nuestro equipo la clave GPG que dicho repositorio provee. Sin este paso no será posible descargar ningún paquete de ese origen. 55

Si en algún momento nos interesa repasar la actividad de yum, podemos consultar el archivo de registros (o logs) en /var/log/yum.log. Ya sea cuando agregamos un repositorio de terceros (como en el caso de MariaDB antes) o incluso en /etc/yum.repos.d/CentOSBase.repo, podemos valernos de la directiva enabled para indicar si se debe habilitar inmediatamente o no. Por ejemplo, en la imagen podemos ver la configuración del repositorio centosplus y notar enabled=1. Esto indica que centosplus está habilitado en nuestro sistema:

Debido a que centosplus contiene paquetes que son mejoras para los presentes en los repositorios base y updates, es posible que queramos deshabilitarlo en ocasiones para que no interfieran con estos últimos. Podemos hacerlo al editar el valor de la directiva a enabled=0 antes de hacer yum update o bien indicar yum update -disablerepo=centosplus. Lo mismo aplica si estuviera deshabilitado y quisiéramos habilitarlo con yum update -enablerepo=centosplus.

El habilitar o deshabilitar un repositorio mediante la línea de comandos tiene efecto solamente durante la transacción actual. Para hacerlo de manera permanente debemos cambiar el valor correspondiente en la directiva enabled como mostramos antes.

56

En Fedora se ha comenzado a utilizar un gestor de paquetes llamado dnf desde hace un par de años (fue presentado con la versión 18 y pasó a ser la herramienta por defecto a partir de la 22). Guarda semejanza con las opciones disponibles de yum que hemos visto, aunque es más rápido y consume menos memoria que su antecesor. El proyecto Fedora provee documentación detallada sobre dnf en su sitio oficial. Familia SUSE Estas distribuciones, entre las que se destaca openSUSE, también utilizan la herramienta rpm para administrar paquetes individuales. Además, también disponen de un gestor de paquetes muy completo llamado zypper. A continuación, veremos las operaciones más comunes que podemos realizar con zypper: Refrescar (actualizar) repositorios: zypper refresh Búsqueda por palabra clave (apache en este caso): zypper search apache Información sobre un paquete en particular: zypper info apache2 Instalación de un paquete: zypper install apache2 Actualización: zypper update apache2 Desintalación: zypper remove apache2 Ayuda general: zypper help Ayuda sobre una operación específica (install en este caso): zypper help install Cada una de las operaciones anteriores tienen un equivalente breve: ref (refresh), se (search), if (info), in (install), up (update), y rm (remove). Debido a la gran cantidad de opciones disponibles para el uso de zypper no es posible listarlas a todas aquí. Para más información, se recomienda utilizar el man page y examinar cuidadosamente la ayuda para cada operación como mencionamos en los últimos dos puntos de arriba. 57

Herramientas de la línea de comandos (Tema103) A pesar de que hoy existen distribuciones GNU/Linux con excelentes interfaces gráficas, la línea de comandos nos ofrece herramientas de administración sumamente útiles. Puede parecer intimidante al principio, pero con el tiempo puede convertirse en nuestra mejor aliada. Además, este es el entorno más frecuente que encontraremos en los servidores que administremos. 58

Entrada, salida, y error estándares Cada vez que ejecutamos un comando, por lo general esperamos ver el resultado por pantalla o guardar el mismo en un archivo. Hay ocasiones en que además del resultado también se nos muestra cualquier error que se haya producido. Para ilustrar, veamos en la imagen el resultado del comando ls /boot /otrodirectorio

Como /otrodirectorio no existe, recibimos el mensaje de error que resaltamos. Este ejemplo nos permite introducir el concepto de flujos de texto en Linux. En palabras simples, un flujo de texto es una secuencia de bytes. Mediante estos, le indicamos a un comando cómo operar (mediante el uso de opciones y argumentos a través de la entrada estándar o stdin, por lo general representada por el teclado o por la salida de otro comando) y recibimos el resultado (compuesto por la salida estándar o stdout y el error estándar o stderr ) en la terminal o lo redirigimos hacia a un archivo. La entrada, salida, y error estándares están representados por un archivo dentro de /dev y tienen asignado lo que se conoce como un descriptor de archivo (0, 1, o 2, respectivamente) para identificarlos más fácilmente:

59

En la práctica utilizamos estos números cuando deseamos redirigir los flujos de texto: Para guardar el resultado de ls /etc en el archivo etc.txt dentro del directorio actual podemos hacer: ls /etc > etc.txt o también ls /etc 1> etc.txt Para enviar el contenido del archivo numeros.txt como entrada al comando sort (que veremos más adelante en este mismo capítulo con más detalle), los dos comandos siguientes son equivalentes: sort < numeros.txt sort 0< numeros.txt

Por defecto, el operador > al ser empleado sin ningún otro modificador asume 1>, de la misma manera que < es sinónimo de 0 error.txt Utilizando el ejemplo anterior también podemos guardar en ls.txt el resultado al mismo tiempo que enviamos el error a error.txt como vemos en la imagen: ls /boot /otrodirectorio 2> error.txt > ls.txt 60

Finalmente, también podemos enviar tanto stdout como stderr al mismo destino. Por lo general, si esperamos que un comando produzca una cantidad considerable de salida (y/o error) que no deseamos ver en la terminal, redirigimos ambos flujos al sumidero /dev/null: ls /boot /otrodirectorio 2>&1 > /dev/null En este último ejemplo, stderr (2) es redirigido a stdout (&1, donde & indica que 1 debe interpretarse como un descriptor de archivo en vez de un archivo propiamente dicho llamado 1 a secas) y luego todo el conjunto es enviado a /dev/null. La shell Bash y el entorno de trabajo Al iniciar sesión nos aparecerá el bien conocido prompt con su shell o intérprete de comandos asociado listo para nuestro uso. Bash es un intérprete de comandos que ejecuta, una por una, las instrucciones introducidas por el usuario o contenidas en un script y devuelve los resultados. En otras palabras, actúa como interfaz entre el kernel Linux y los usuarios o programas del modo texto. Además, incorpora numerosas utilidades de programación y mejoras sobre sh, su shell predecesora. Debido a que es una herramienta desarrollada por GNU, suele ser utilizada por defecto en las distros actuales. Por otro lado, Bash no es una terminal. Tampoco es la única shell disponible. De hecho, podemos ver los intérpretes disponibles en nuestro equipo al observar el contenido del archivo /etc/shells. Si lo consideramos necesario, podemos asignar a los usuarios comunes algún otro (distinto de Bash). De esta manera podemos restringir el alcance de lo que pueden llegar a hacer utilizando la línea de comandos. Por supuesto, es el administrador quien decide esto. 61

Si deseáramos agregar el directorio ~/bin al final de PATH debemos hacer lo siguiente: PATH=$PATH:~/bin o al principio: PATH=~/bin:$PATH

La decisión de agregar un directorio al comienzo o al final de PATH depende si queremos que sea examinado antes o después que el resto de los directorios. Es importante tener en cuenta este punto si hay más de un ejecutable con el mismo nombre en la lista de directorios contenidos en PATH. Por ejemplo, si tanto ~/bin como /usr/bin contienen un binario llamado id, será ejecutado el que aparece en ~/bin si este directorio se encuentra antes que /usr/bin en la lista. Para que los cambios sean permanentes debemos agregar una de las líneas anteriores al final de ~/.bash_profile o ~/.profile (en ese orden) y exportar la variable precedidas por el comando export. Lo que mencionamos en los párrafos anteriores también aplica para todas las variables de entorno, no solamente para PATH. Para ilustrar, establezcamos el valor de HISTSIZE en 2000. Notemos que usamos el operador >> para agregar contenido al final del archivo en vez de > que provocaría el efecto de sobreescribir sus contenidos. echo 'HISTSIZE=2000' >> ~/.bash_profile y luego indiquemos que la variable se deberá exportar al inicio de 62

sesión del usuario actual: echo 'export HISTSIZE' >> ~/.bash_profile Si deseamos que los cambios efectuados tomen efecto de manera inmediata podemos utilizar el comando source ~/.bash_profile el cual ejecutará el contenido del archivo en cuestión. Si hemos creado una variable de entorno personalizada (por lo general se le da un nombre en MAYÚSCULAS) podemos destruirla con el comando unset: LENGUAJE='Python' echo $LENGUAJE unset LENGUAJE echo $LENGUAJE

En la imagen anterior vemos que luego de haber destruido la variable, esta ha desaparecido de nuestro entorno. 63

Identificar comandos Los muchos comandos que podemos llegar a utilizar se pueden clasificar en categorías de acuerdo con su origen. Algunos de ellos están incorporados en la shell, mientras que otros provienen de un determinado paquete que hayamos instalado. También existe la posibilidad de que un comando sea en realidad un alias de otro comando con sus opciones.

Un alias es un comando construido a partir de otros y/o diferentes opciones, y es una manera de ejecutar una serie de comandos más fácilmente. De esta manera, no es necesario recordar toda la secuencia ni las opciones utilizadas.

Para empezar, ejecutemos los siguientes comandos en una terminal, cuyo resultado vemos en la imagen siguiente: Un alias: type ls Una utilidad incorporada en la shell: type cd Un comando regular: type lscpu

64

En la imagen anterior podemos ver que ls es un alias de ls -color=auto, y que lscpu corresponde a un ejecutable cuya ubicación es /usr/bin/lscpu. Esto significa que cuando escribamos ls /mi/directorio en realidad estaremos ejecutando ls -color=auto /mi/directorio. Por supuesto, type acepta varias opciones que pueden modificar su uso. En primer lugar, podemos mencionar -P, la cual obliga a una búsqueda dentro de los directorios indicados en la variable de entorno PATH. Además, también podemos utilizar -a para mostrar todas las coincidencias encontradas en los mismos. Ahora veamos cómo crear alias para nuestro uso que se adapten a nuestros deseos o necesidades. Por ejemplo, el comando cd ../../ se utiliza para retroceder dos directorios hacia arriba en la jerarquía de directorios a partir del lugar actual. En cambio, podríamos escribir alias ..='cd ../../' y a partir de ese momento utilizar simplemente .. cuando necesitemos desplazarnos de la misma manera que lo hacíamos antes con cd ../../. Para deshabilitar un alias bastará utilizar la herramienta unalias seguida del nombre del alias en cuestión: unalias .. Por otro lado, si deseamos que un alias sea permanente deberemos guardar su declaración al final de ~/.bash_profile o ~/.profile. 65

El historial de comandos Cuando iniciamos sesión en una terminal, el entorno de la shell se pone en funcionamiento de acuerdo a nuestras configuraciones personales. Para cada usuario, las mismas se encuentran dentro de archivos ocultos en su correspondiente directorio personal. Uno de estos archivos es ~/.bash_history, donde se guarda el historial de comandos en Linux. Por suerte, al disponer del historial podemos ejecutar un comando fácilmente sin volver a tener que escribirlo nuevamente. En primer lugar, recordemos que con el comando history podemos listar los comandos disponibles. A continuación, colocando un signo de exclamación y el número del comando en el historial podremos ejecutarlo otra vez. En otras palabras, history 10 devolverá los últimos 10 comandos ejecutados en la terminal actual, mientras que !2006 repetirá type lscpu, como vemos en la imagen:

66

Si en vez de repetir de manera idéntica un comando del historial quisiéramos modificarlo primero podemos utilizar fc, otra utilidad incorporada en la shell. Al escribir fc 2006 y presionar Enter. Como resultado, se abrirá nuestro editor de texto por defecto. Esto nos permitirá modificar cualquier parte del comando y ejecutarlo automáticamente al guardar los cambios. Esta herramienta también nos permite modificar una serie de comandos y ejecutarlos a continuación. Por ejemplo fc 2000 20005 nos permitiría editar los comandos que ocupan desde la posición 2000 a la 2005 en el historial. Localizar comando El comando which nos muestra la ubicación del archivo ejecutable correspondiente a un comando dado. Para hacerlo, busca en orden dentro de los directorios que aparecen en PATH. Como podemos apreciar abajo, podemos indicarle más de un comando a la vez: which ls which ps top which whereis whatis man

67

Dos detalles interesantes sobre el uso de which son los siguientes: Una vez que se identifica un binario dado en un directorio incluido en PATH, la búsqueda se detiene a menos que se utilice la opción -a. En ese caso se buscará en TODOS los directorios en vez de finalizar con la primera ocurrencia. Si uno o más de los argumentos no se encuentra en PATH, which regresa un exit status de 1. Si todos se encuentran, el exit status será igual a 0, mientras que será igual a 2 si se especifica una opción no válida. Los conceptos y comandos que hemos cubierto en esta sección nos servirán de base para los contenidos del resto de este capítulo. Manipulación de texto Gran parte de las tareas de un sysadmin se llevan a cabo al manipular archivos o flujos de texto. Para ayudarnos en esta responsabilidad podremos utilizar los comandos que listamos a continuación. Combinar archivos con cat y paginar con more o less El comando cat es mayormente utilizado para mostrar por pantalla el contenido de un archivo de texto corto. Sin embargo, como su nombre lo indica (concatenate) también puede utilizarse para mostrar dos archivos o más, uno a continuación del otro. Dos opciones útiles son --number (o su equivalente -n), la cual se utiliza para mostrar los números de línea en la salida, y --squeeze-blank (o -s) que omite las líneas vacías repetidas. En la imagen podemos ver el resultado del siguiente ejemplo: cat archivo1.txt archivo2.txt -n -s 68

Otra alternativa para ver los números de línea junto a cada una es utilizar la herramienta nl seguida del nombre del archivo a examinar, produciendo esencialmente el mismo efecto de cat -n.

69

El comando cat puede ser interesante para ver el contenido de archivos pequeños, pero en general more o less resultan de mayor utilidad en la mayoría de los casos, en especial si tratamos con archivos extensos. Estas dos herramientas paginan (dividen en páginas) uno o varios archivos y los muestran en la terminal. De no indicárseles un archivo dado, paginan la entrada estándar. Se diferencian en las facilidades que brindan como indicamos a continuación: more es más restrictivo en cuanto al movimiento dentro del texto, y visualiza sucesivamente el porcentaje del archivo examinado hasta el momento. Cuando se alcanza el final del último archivo a paginar, more termina automáticamente. Por otro lado, less cumple la misma función que more, pero además, nos permite realizar búsquedas de palabras dentro del contenido del archivo, resaltar los resultados, y saltar entre una ocurrencia y otra. Por ejemplo: less /etc/password more /etc/passwd Para examinar un archivo (o más) con cualquiera de estas dos herramientas: la tecla q permite interrumpir el proceso y salir. /criterio realiza búsquedas del patrón criterio dentro del texto.

Para realizar otra búsqueda empleando un patrón distinto sólo es necesario escribir / seguido de la palabra correspondiente.

[n]b permite regresar n páginas (por defecto, n=1). [n]f es para adelantar n páginas. h para mostrar la ayuda disponible (también salimos de la misma con la tecla q). Además, en less podemos emplear: G para desplazarnos al final del archivo. g para ir directamente al principio del archivo. 70

El comando man (que utilizamos para acceder a los man pages o manuales de los comandos), utiliza por defecto el paginador less para dar formato a su salida. Existen además los comandos zless y zmore que permiten paginar a los archivos comprimidos sin necesidad de descompactarlos previamente en nuestro disco. Ordenar líneas con sort Si disponemos de registros en un archivo de texto que necesitaremos procesar, el comando sort nos servirá de gran utilidad. Por defecto, esta herramienta ordena por valor ASCII, por lo que diferencia entre minúsculas y mayúsculas. Además, utiliza espacios para delimitar campos, y de no especificarse otra cosa, el orden se realizará a partir del primer campo del archivo. Veamos algunas opciones útiles que no son tan conocidas: -M o --month permite ordenar por mes a partir de las tres primeras letras del mes en inglés (comenzando en JAN para enero y DEC para diciembre). -f

o

--ignore-case permite ignorar la diferencia entre

mayúsculas y minúsculas. -t

o

--field-separator

nos

permite

especificar

un

separador de campos distinto al espacio. El siguiente ejemplo, cuyo resultado se muestra en la imagen, ilustra el uso de las opciones anteriores utilizando la coma como separador de campos. El mismo utiliza un archivo llamado ejemplosort.txt con los contenidos mostrados en la misma imagen: sort -M -f -t, ejemplosort.txt 71

Por lo general, sort se utiliza junto con uniq, que permite eliminar líneas repetidas en una secuencia. Por ejemplo, el comando cat datos.txt | sort | uniq o su equivalente sort -u datos.txt ordenará las líneas del archivo datos.txt omitiendo cualquier repetición presente. Unir archivos línea a línea con paste En el caso de que tengamos dos o más archivos que necesitemos unir línea a línea (para procesar el resultado posteriormente), podemos emplear el comando paste. Para ilustrar, tomemos los archivos paises.txt y capitales.txt que tienen los siguientes contenidos: 72

En paises.txt: Argentina Uruguay Paraguay Chile Bolivia Brasil En capitales.txt: Buenos Aires Montevideo Asuncion Santiago La Paz Brasilia Para unir ambos archivos en un tercero llamado info.csv utilizando una coma para separar campos, haremos paste -d, paises.txt capitales.txt > info.csv cuyo resultado es el que vemos en la imagen:

Esta herramienta siempre nos será útil cuando necesitemos pegar el contenido de un archivo en otro y especificar un separador entre ambos. Los archivos con campos separados por comas generalmente son empleados para procesar datos a través de un programa de hojas de cálculo como Microsoft Excel o LibreOffice Calc. 73

Mostrar el comienzo o el fin de un archivo con head o tail En algunas ocasiones (por ejemplo, al inspeccionar los primeros o últimos eventos registrados en un log) nos puede interesar mostrar las primeras o las últimas líneas de un archivo, lo cual podemos hacer con los comandos head o tail. Sin argumentos, estas dos herramientas nos permitirán visualizar las primeras o últimas 10 líneas de un archivo, respectivamente. También podemos utilizar la opción -n para cambiar la cantidad de líneas. Por ejemplo, head -n 7 /etc/passwd nos mostrará las primeras 7 líneas de /etc/passwd, mientras que tail /etc/passwd devolverá las últimas líneas del mismo archivo. En particular, tail puede emplearse para visualizar las modificaciones a un archivo a medida que se hacen. En otras palabras tail -f /var/log/httpd/access_log nos permitirá ver los accesos a nuestro servidor web Apache a medida que ocurran (esta ruta corresponde a CentOS 7). Mostrar campos separados por un delimitador Muchas veces nos podemos encontrar con la necesidad de mostrar sólo algunos campos de un archivo de texto en el que cada línea se encuentra dividida en dos o más por un delimitador. Por ejemplo, el primer y el séptimo campo de /etc/passwd muestran el nombre de los usuarios y su shell asignada, de haber una. El comando cut nos permite mostrar esta información con facilidad, donde la opción -f nos permite indica qué campos deseamos que se devuelvan, 74

mientras que -d especifica el caracter utilizado para separarlos: cut -d: -f1,7 /etc/passwd Otras situaciones en las que el uso de cut puede resultarnos útil es al extraer una cierta cantidad de caracteres (mediante la opción -c) de cada línea. Podemos especificar un rango definido, una posición de comienzo e indicar que se extraiga hasta el final, o una posición final y pedir que se muestre desde el principio hasta la misma: Para extraer los primeros 4 caracteres de cada línea: cut -c1-4 /etc/passwd Para mostrar desde el caracter en la posición 10 hasta el final de cada línea: cut -c10- /etc/passwd Para ver desde el principio de cada línea hasta la posición 7: cut -c-7 /etc/passwd Vale la pena recordar que el comando cut puede aceptar como entrada el resultado de un comando anterior a través de una tubería. Por ejemplo, date | tr -s " " | cut -d' ' -f6 devolverá el año actual a partir de date (que devuelve la fecha), tr (que a partir de la opción -s remueve los espacios repetidos y los combina en uno), y de cut. Estos comandos combinados nos permiten mostrar la información de interés. Contar palabras, bytes, y líneas con wc Una de las tareas más usuales con las que podemos encontrarnos es la necesidad de contar la cantidad de líneas de un archivo o del resultado de un comando. Esto puede bien representar la cantidad de cuentas de usuario del sistema (una por línea en el archivo /etc/passwd) o la cantidad de archivos resultantes de una búsqueda con find, por nombrar dos casos emblemáticos. En Linux disponemos de una herramienta llamada wc para esto. 75

Utilicemos el archivo capitales.txt para ilustrar: Contar la cantidad de líneas: wc -l capitales.txt La cantidad de palabras: wc -w capitales.txt El número de caracteres, que debería coincidir con el tamaño del archivo: wc -c capitales.txt Si bien en este caso utilizamos un archivo propiamente dicho como argumento, también podemos emplear una tubería para enviar la salida de un comando como entrada a wc. Por ejemplo, ps -u www-data | grep -v PID | wc -l nos indicará cuántos procesos están corriendo bajo el usuario wwwdata. (Pista: grep -v PID se utiliza para no incluir el encabezado de la lista de procesos en el conteo de líneas). Compresión de archivos y lectura de archivos comprimidos Para comprimir archivos podemos utilizar cualquiera de las tres herramientas siguientes: gzip, bzip2, o xz. Como parte del proceso de compresión, se reemplazará el archivo original datos.txt por su versión comprimida. Los nombres de los archivos comprimidos finales serán iguales a los iniciales, con la diferencia de que se agregarán las extensiones .gz, .bz2, y .xz, respectivamente: gzip datos.txt bzip2 datos.txt xz datos.txt Para leer el contenido del archivo, podemos descomprimirlo para recuperar el original, o bien utilizar los comandos zcat, bzcat, o xzcat para acceder al mismo todavía comprimido: gzip -dc datos.txt.gz zcat datos.txt.gz bzip2 -dc datos.txt.bz2 bzcat datos.txt.bz2 76

xz -dc datos.txt.xz xzcat datos.txt.xz La opción combinada -dc indica que se debe mostrar el resultado por pantalla en vez de guardar el archivo descomprimido en el disco. Si se desea almacenar el archivo original, tendremos que omitir la opción c: gzip -d datos.txt.gz bzip2 -d datos.txt.bz2 xz -d datos.txt.xz Dividir archivos Linux provee una herramienta muy útil llamada split para dividir archivos de gran tamaño. Comencemos primero creando un archivo llamado archivoprueba.iso de 2 GB (bs=1M * count=2000) en el directorio actual: dd if=/dev/zero of=archivoprueba.iso bs=1M count=2000 En este ejemplo suponemos que deseamos dividir el archivo de 2 GB en partes de 600 MB como máximo. Debido a que 2 GB no es un múltiplo de esta cantidad, el último archivo tendrá menor tamaño. split --bytes 600M --numeric-suffixes archivoprueba.iso archivoprueba.iso. Con la opción --bytes indicamos el tamaño máximo que deberá tener cada archivo individual. Por otra parte, --numeric-suffixes nos permite agregar un número al final del nombre de cada uno. Finalmente, archivoprueba.iso es el nombre del archivo a dividir, mientras que archivoprueba.iso. (notar el punto al final) es el prefijo de salida.

77

Una vez que hayamos compartido los archivos, podemos regenerar el original muy fácilmente. Nombraremos el archivo destino como archivoprueba.iso.nuevo para distinguirlo del original propiamente dicho: cat archivoprueba.iso.* > archivoprueba.iso.nuevo A continuación, podemos comparar archivoprueba.iso archivoprueba.iso.nuevo utilizando el comando cmp:

con

cmp archivoprueba.iso archivoprueba.iso.nuevo Si el comando anterior finaliza con código de salida igual a 0 (como en este caso, que podemos comprobar con echo $?) significa que ambos archivos son iguales byte a byte. Es importante aclarar que, en este ejemplo trivial, con archivos creados a partir de /dev/zero, partes individuales de igual tamaño mantendrán esta igualdad. Validar integridad de archivos En una época en que es común distribuir archivos a través de Internet (sea en forma de descargas o a través de correo electrónico) es importante asegurarnos de que el mismo no ha sido modificado en el camino por un atacante malicioso. Para hacerlo, el dueño o remitente del archivo puede proveer un hash que representa el contenido de este. Los dos algoritmos más utilizados son sha256 y sha512, mientras que md5 (inventado a comienzos de la década de 1990) se considera obsoleto. Los siguientes comandos utilizan sha256sum, sha512sum, y md5sum para devolver el hash que representa el contenido de datos.txt: sha256sum datos.txt sha512sum datos.txt md5sum datos.txt 78

En la imagen anterior podemos apreciar el resultado. Podemos contar con que es imposible (prácticamente hablando) que dos archivos diferentes tengan hashes iguales. Creación y manipulación de archivos y directorios Para organizar nuestros archivos, poder montar dispositivos de almacenamiento, y borrar contenido que no sea necesario es preciso que desde un comienzo sepamos cómo emplear las herramientas que nombraremos en este apartado. El comando ls Este comando nos permite distinguir los siguientes tipos de contenidos en un directorio. También vale la pena mencionar que si el argumento representa un archivo, enlace simbólico, dispositivo, etc., veremos datos sobre el mismo en la salida de ls. En blanco (en el caso de que el color de fondo de la terminal sea negro u otro color oscuro, que es lo más común), los archivos de texto o binarios no ejecutables. En verde, los archivos de texto (scripts) y otros binarios ejecutables. En celeste, los enlaces simbólicos. En fondo negro con letras rojas, los enlaces simbólicos rotos. Nos referimos a aquellos que apunten a un recurso del sistema que no existe. En amarillo, los dispositivos de bloques. En violeta, los archivos de imágenes o archivos temporales. En rojo, los archivos comprimidos. En la imagen siguiente podemos distinguir algunos de los casos que nombramos arriba. En el primero, vemos que el archivo en cuestión 79

En la imagen siguiente podemos distinguir algunos de los casos que nombramos arriba. En el primero, vemos que el archivo en cuestión se muestra en texto negro debido a que el color de fondo de la terminal es un tono claro.

En este caso, adm.txt y datostutor.txt son ejemplos de archivos. Por otro lado, archivo3 (contrario a lo que podría sugerir su nombre) se trata de un directorio, link_archivo2 es un enlace simbólico activo, link_archivo1 uno roto, y archivo2 es un archivo binario ejecutable. En Linux podemos ver que tanto los archivos como los directorios pueden tener varios puntos en su nombre, pero cuando el punto está adelante los transforma en ocultos. Por ejemplo, .hola no se verá con un ls normal. Sin embargo, al utilizar el modificador -a, podremos ver todos los archivos ocultos que se encuentran en ese directorio.

Generalmente los archivos ocultos son los que contienen información acerca de configuraciones. Al no ser visibles fácilmente es menos probable que sean borrados de forma accidental. 80

Para ilustrar, veamos la diferencia que existe entre hacer ls y ls -a en el directorio personal de un usuario.

Si ahora agregamos el modificador -l obtenemos mayor información de los archivos y directorios. La salida del comando en este caso está dispuesta en columnas para que nos resulte más sencillo reconocerla. Veamos cómo queda cada columna: 1. El primer caracter que aparece en la primera columna indica si lo que estamos viendo es un archivo (-), un directorio (d), un enlace simbólico (l), o un dispositivo de bloques (b), por nombrar algunos ejemplos. A continuación, vemos los permisos que poseen sobre el mismo a) el dueño del archivo, b) el grupo dueño, y c) el resto de los usuarios del sistema. 2. Esta columna nos dice si este objeto posee enlaces que lo estén apuntando. 3. Usuario dueño del objeto. 4. Grupo dueño del objeto. 5. Tamaño en bytes del objeto. Para mostrar este dato en una unidad más amigable (KB, MB, etc.) podemos agregar la opción -h. 6. Fecha de última modificación del objeto. 81

Por ejemplo, el comando ls -lh adm.txt nos indica lo siguiente:

1. Se trata de un archivo propiamente dicho, ya que el primer caracter de la secuencia -rw-r--r-- es -. 2. El archivo tiene un (1) enlace que apunta hacia el mismo. 3. El dueño del archivo es el usuario alumno. 4. El grupo dueño es finanzas. 5. La última modificación fue realizada el día 2 de abril del corriente año a las 19:03. Si dentro de un directorio existen subdirectorios y nos interesa ver el contenido de todos simultáneamente, se puede utilizar la opción -R para mostrar de forma recursiva tal información. Dicho de otra forma, ls -lR pruebas nos devolverá un listado detallado de tal directorio y de todos los subdirectorios que se encuentren dentro del mismo. El comando mkdir (make directory) Con este comando creamos nuevos directorios vacíos. En este ejemplo veremos cómo hacerlo al crear un nuevo directorio llamado nuevodir: mkdir nuevodir Si deseamos crear una estructura completa de directorios (un subdirectorio dentro de otro, y este último dentro de otro, y así sucesivamente) deberemos utilizar la opción -p de la siguiente manera: mkdir -p dir1/dir2/dir3 El comando anterior creará dir1 dentro del directorio actual. Si 82

Si miramos dentro de dir1 encontraremos a dir2, y dentro de este último a dir3:

El comando rmdir (remove directory) El comando rmdir permite borrar directorios, si los mismos están vacíos. También podemos utilizar la opción -p (de forma similar a como lo hicimos con mkdir) para eliminar una estructura de directorios que cumplan con la misma condición. Si los directorios contienen archivos, tendremos que utilizar el comando rm para borrarlos previamente como veremos más adelante. El comando cp (copy) La herramienta cp se usa para copiar archivos. A continuación del comando propiamente dicho, se debe escribir el archivo que se desee copiar y el destino donde se habrá de guardar la copia.

Una opción interesante de este comando es -a, la cual hace una copia exacta de los directorios y subdirectorios. Esto también incluye los permisos o links que pudiera haber en el directorio de origen. 83

Por ejemplo: cp pruebas/holamundo.txt . copiará el archivo holamundo.txt, ubicado dentro del directorio pruebas, en el directorio actual (representado por .) con el mismo nombre. Por otro lado, cp pruebas/ejemplo.txt copiaejemplo.txt hará una copia del archivo ejemplo.txt que se halla dentro de pruebas, en el directorio actual, pero con el nombre copiaejemplo.txt. Veamos el resultado de los dos comandos anteriores:

Es importante tener en cuenta que si el archivo de destino existe, se sobreescribirá mediante el proceso de copia. El comando mv (move) Esta herramienta se utiliza tanto para mover archivos de un lugar a otro como también para renombrarlos. Es importante tener en cuenta que si el archivo de destino existe, se sobreescribirá mediante este proceso. Para ilustrar, cambiemos el nombre de copiaejemplo.txt a nuevacopiaejemplo.txt. Luego de ejecutar el comando mv copiaejemplo.txt nuevacopiaejemplo.txt el primer archivo no existe más. A continuación, movamos holamundo.txt desde el directorio actual a archivo3: mv holamundo.txt archivo3 84

En la imagen siguiente vemos el resultado de los comandos anteriores:

Vemos que holamundo.txt ya no se encuentra en el directorio actual pero sí dentro del directorio archivo3. El comando rm (remove) Este comando se utiliza para borrar archivos. Debemos tener en cuenta que desde la línea de comandos no tenemos papelera de reciclaje, y tampoco hay un undelete, así que cuando borramos nunca más podemos recuperar el archivo original. Para remover un directorio junto con todo su contenido empleamos la opción -r, con sumo cuidado por la misma razón señalada antes. Debido a que no podemos recuperar archivos borrados con rm, es una buena idea utilizar este comando siempre con la opción -i, lo que nos pedirá confirmación antes de efectuar la operación. Dicho de otra forma: rm -ri midirectorio es una opción más adecuada que rm -r midirectorio para evitar el borrado accidental de los contenidos de midirectorio. Buscar con find Son muchas las razones por las que nos interesaría buscar archivos dentro de nuestro sistema, entre las cuales podríamos mencionar las siguientes. Vamos a necesitar realizar una búsqueda de archivos con find cuando querramos: 85

1. trabajar con un archivo que no sabemos o no recordamos dónde se encuentra. 2. identificar archivos por propietario o grupo dueño. por fecha de modificación por permisos asignados por tipo de archivo (archivo regular, directorio, etc) combinando los criterios anteriores La sintaxis de find para comenzar una búsqueda en el directorio especificado por RUTA es la siguiente: find [RUTA] [OPCIONES] Para buscar un archivo por nombre, utilizamos find seguido del directorio a partir del cual deseamos realizar la búsqueda, de la opción -name (o -iname si deseamos ignorar mayúsculas y minúsculas), y finalmente el nombre del archivo. Si utilizamos un comodín (*) en el nombre del archivo como criterio de búsqueda, deberemos encerrar el mismo entre comillas para evitar que la shell lo expanda (lo cual sucedería ANTES de efectuar la búsqueda propiamente dicha). Veamos los siguientes ejemplos: Buscar en el directorio /etc todos los archivos con extensión .conf: Buscar los archivos cuyo tamaño esté entre 10 MB y 20 MB: find / size +10240k -size -20480k Buscar todos los archivos con find dentro del directorio /etc cuyo dueño sea el usuario root y que fueron modificados exactamente hace 2 meses (60 días): find /etc -type f -user root -mtime 60 Si quisiéramos ver la misma lista de archivos que fueron modificados hace más de un cierto número de días, deberemos usar dicha cantidad con el signo + delante. En resumen: 86

-mtime x: archivos modificados exactamente hace x por 24 horas (lo cual se traduce en x cantidad de días). -mtime -x: modificados durante los últimos x días. -mtime +x: modificados hace más de x días. Especificar un permiso particular como criterio de búsqueda a través de la opción -perm a partir del directorio raíz: find / type f -perm 777 Encontrar archivos ejecutables por todos en el directorio actual: find . -type f -perm -o=x Buscar archivos vacíos a partir del directorio raíz: find / -type f -empty Para ejecutar una acción determinada sobre los resultados de una búsqueda emplearemos la opción -exec. Por ejemplo, para cambiar los permisos de 777 a 644, utilizaremos -exec seguida de la acción que deseamos realizar (en este caso chmod 644 a todos los archivos resultantes, simbolizados por {}+: find . -type f -perm 777 -exec chmod 644 {} + Para borrar archivos usaremos la opción -delete de GNU find (en este caso no es necesario recurrir a -exec para especificar la acción deseada ya que la misma es provista por una opción en particular). Veamos cómo borrar archivos vacíos: find . -type f -empty -delete La diferencia entre find y locate (otra herramienta utilizada para realizar búsquedas) es que el primero emplea la estructura del sistema de archivos (o una porción de esta) para realizar una búsqueda, mientras que locate lee la ubicación de los archivos desde una base de datos interna. Mostrar salida de un comando por pantalla y guardar el resultado al mismo tiempo Cuando queremos guardar la salida de un comando en un archivo utilizamos el operador de redirección simple >. 87

Si deseamos agregar más contenido posteriormente usamos el operador de redirección doble >>. En el caso de que queramos enviar la salida de un comando como entrada a otro empleamos el operador tubería |. ¿Pero qué sucede cuando deseamos ver la salida de un comando por pantalla y guardar el resultado en un archivo? Para lograrlo podemos utilizar una herramienta llamada tee. Consideremos el siguiente ejemplo. Como sabemos, el comando ls -l nos mostrará un listado detallado de los contenidos del directorio actual. Esto incluye permisos, dueño, grupo propietario, y fecha de última modificación de cada ítem, entre otros datos. Al complementar este comando con una tubería y enviando la salida hacia tee lograremos nuestro objetivo. La salida será mostrada por pantalla y será guardada en el archivo listado.txt: ls -l | tee listado.txt Ahora examinemos el comando wget -O- http://mirror.rackspace.com/CentOS/7/isos/ x86_64/CentOS-7-x86_64-Minimal-1804.iso | tee centos7.iso | sha256sum > dvd.sha256 Si acertamos al afirmar que nos permite descargar una imagen ISO de CentOS 7, guardarla en el archivo centos7.iso y al mismo tiempo calcular su hash sha256 (y guardarla en dvd.sha256) para verificar su integridad, ¡estamos en lo correcto! Como podemos ver, la combinación de las redirecciones, tuberías, y el comando tee nos permite realizar las más variadas tareas. Otro punto importante a tener en cuenta cuando hablamos de redirecciones es la necesidad de ejecutar un comando sobre todos los resultados de otro. Para esto usamos la herramienta xargs. Consideremos el siguiente ejemplo para ilustrar. Con el comando find localizamos todos los archivos con la extensión .txt y mostramos su ubicación. Al enviar la salida a xargs podemos especificar que se 88

realice una acción diferente sobre cada línea del resultado (ls -l en este caso): find . -maxdepth 1 -type f -name "*.txt" find . -maxdepth 1 -type f -name "*.txt" | xargs ls -l Veamos el resultado:

En síntesis, podremos utilizar xargs cada vez que necesitemos operar sobre los resultados de una búsqueda con find (que es lo más usual) o de algún otro comando. Archivado y compresión En Linux, el archivado y compresión se realiza utilizando el comando tar (archivado) en conjunto con gzip, bzip2, o xz (compresión). La posibilidad de disponer de un solo archivo comprimido (a menudo 89

Como observamos en los ejemplos, los comandos en cada caso son prácticamente idénticos. La única diferencia reside en el uso de las opciones -z, -j, y -J que representan la compresión con gzip, bzip2, y xz respectivamente. Antes de descomprimir, crearemos tres directorios auxiliares dentro de resultados: mkdir resultados/1 mkdir resultados/2 mkdir resultados/3 Y luego descomprimiremos los contenidos en tarcongzip.tar.gz, tarconbzip2.tar.bz2, y tarconxz.tar.xz en resultados/1, resultados/2, y resultados/3. La opción -C (mayúscula) nos permite indicar un directorio destino para los archivos descomprimidos antes de realizar la operación propiamente dicha: tar -xzvf resultados/tarcongzip.tar.gz -C resultados/1 tar -xjvf resultados/tarconbzip2.tar.bz2 -C resultados/2 tar -xJvf resultados/tarconxz.tar.xz -C resultados/ 3 Resaltemos algunos puntos importantes a tener en cuenta en todos los casos: La opción -c indica que estamos creando un tarball, mientras que -x nos dice que lo estamos descomprimiendo. -v muestra la lista de archivos que fueron procesados. -f es la última opción y debe preceder al nombre del tarball a crear. gzip es la herramienta más antigua de las tres y provee la menor compresión, mientras que xz es la más nueva y presenta una mayor compresión, pero a costa de un mayor uso de recursos del sistema al momento de comprimir. Finalmente, bzip2 es una 90

especie de promedio entre las otras dos. Cabe destacar que todas las opciones de tar (que podemos consultar en el man page correspondiente) se pueden utilizar con cualquiera de las herramientas de compresión mencionadas anteriormente. Si bien existen otras herramientas más potentes y versátiles (aunque complejas) para realizar copias de respaldo en Linux, todo administrador de sistemas debe conocer tar y las herramientas de compresión como la palma de su mano. Herramientas de la línea de comandos Todo administrador de sistemas Linux tiene que trabajar con archivos de texto plano a diario. Mediante dos herramientas de la línea de comandos llamadas grep y sed se puede filtrar contenido presente en uno o varios archivos, así como visualizar rangos determinados de líneas, reemplazar palabras, y filtrar contenido por palabras clave, por nombrar algunos ejemplos. Utilizando este último comando inclusive es posible modificar archivos sin necesidad de abrirlos con un editor de texto. El otro tema principal de este capítulo es el monitoreo de procesos en Linux, una tarea sumamente crítica para los sysadmins. Esto incluye tanto el identificar como el manipular procesos. En este último aspecto es fundamental poder modificar la cantidad de recursos que se le otorga a cada proceso (concepto conocido como prioridad) de acuerdo con lo que se necesite, y también alterar su funcionamiento mediante el envío de señales cuando la situación lo amerite. Introducción a grep En la línea de comandos, grep es una herramienta muy versátil que nos permite mostrar las líneas de un archivo, o de la salida de otro comando, que concuerden con un patrón dado. Alternativamente, también es posible ignorar las líneas que cumplen con el mismo. Este patrón puede ser algo tan simple como una sola palabra o complejo como una expresión regular. A continuación, ilustraremos el 91

Mostrar la línea que corresponde a los datos del usuario gacanepa en /etc/passwd: grep gacanepa /etc/passwd Buscar la palabra DocumentRoot dentro de todos los archivos del directorio /etc/httpd/conf: grep -ir documentroot /etc/httpd/conf/* En el ejemplo anterior, el uso de la opción combinada -ir nos permite realizar una búsqueda recursiva (r) ignorando mayúsculas y minúsculas (i). De esta forma podemos especificar un patrón inclusive si no sabemos si aparece en minúsculas o mayúsculas dentro de los archivos. Mostrar las líneas de la salida de un comando que contengan un patrón. En este caso veamos si en la salida de ls /var aparece la palabra log utilizando una tubería: ls /var | grep log Para mostrar las líneas que no contengan un modelo dado: grep -v bash /etc/passwd En este caso mostramos todos los usuarios en /etc/passwd cuya shell no sea Bash, lo cual nos puede resultar útil para auditar nuestro servidor y asegurarnos de que ningún usuario o servicio tenga acceso a una consola. Veamos los ejemplos anteriores en la siguiente imagen:

92

Expresiones regulares Wikipedia define a una expresión regular de la siguiente manera:

Una expresión regular, a menudo llamada también regex, es una secuencia de caracteres que forma un patrón de búsqueda. Veamos algunas expresiones regulares iniciales y los conjuntos de caracteres que representan: ^: representa el comienzo de una línea $: representa el fin de una línea [a-z]: todas las letras minúsculas [A-Z]: todas las letras mayúsculas [0-9]: todos los dígitos del sistema decimal . (un punto): representa un caracter cualquiera (no confundir con el operador tubería): operador booleano OR Combinándolas: ^[a-z]: líneas que comienzan con una letra minúscula ^#: líneas cuyo primer caracter sea el signo # ^$: líneas vacías [0-9][0-9]: líneas donde se encuentren al menos dos dígitos numéricos consecutivos A continuación, algunos ejemplos para ilustrar. Usar grep para averiguar si nuestro CPU soporta extensiones de virtualización Generalmente se recurre a grep para averiguar si nuestro CPU soporta virtualización. Para hacerlo, inspeccionaremos el archivo virtual /proc/cpuinfo, concentrándonos en la presencia de los indicadores vmx o svm en la lista de flags. La presencia del primero o del segundo indica que nuestro CPU (Intel o AMD, respectivamente) soporta virtualización. El siguiente comando utiliza la opción -E para indicar que el patrón 93

de búsqueda consiste en una expresión regular y chequea la presencia de uno u otro indicador al mismo tiempo al utilizar el operador booleano OR. Si nuestro CPU tiene más de un núcleo se mostrará una sección por cada uno. grep -E '(vmx|svm)' /proc/cpuinfo Eliminar líneas de comentarios o vacías Con la ayuda de grep podemos eliminar todas las líneas que contengan comentarios (las cuales generalmente comienzan con el caracter #) o vacías de un archivo de texto. Esto es particularmente útil si queremos observar de manera rápida cuáles son las variables y los valores que se han definido explícitamente para las mismas en un servicio dado. También utilizando grep con la opción -Ev (para eliminar líneas que cumplan con la expresión regular dada) y el operador OR podemos mostrar esta información fácilmente: grep -Ev '(^#|^$)' /etc/httpd/conf/httpd.conf El comando anterior mostrará solamente aquellas líneas de /etc/httpd/conf/httpd.conf que NO comiencen con # o que NO estén vacías. Buscar palabras que terminen con una cadena de texto determinada Como explicamos más arriba, el punto (.) es una expresión regular que representa cualquier caracter. Consideremos el archivo ejemplo1.txt: Esta no es fama digna de una dama, y mi auto nuevo no es de alta gama. Andar a pie es un drama, pero el precio de la nafta la atención ya me llama. el siguiente comando devolverá todas las líneas donde se encuentre 94

la cadena de texto ama: grep '.ama' ejemplo1.txt Pero si quisiéramos ver solamente las líneas donde ama aparezca forme parte de una palabra y esté precedida por solamente un caracter: grep -w '.ama' ejemplo1.txt

Dos variaciones de este ejemplo consisten en buscar 1) cero o más ocurrencias (*) de un caracter dado: grep 'a*' ejemplo1.txt El comando anterior devolverá todas las líneas donde encuentre la letra a seguida de uno o más caracteres mientras que grep 'l\+' ejemplo1.txt devolverá las líneas donde identifique una o más ocurrencias (\+) de la letra l. 95

Donde: . se utiliza para representar cualquier caracter * representa cero o más ocurrencias del caracter anterior + indica una o más ocurrencias del caracter que le antecede Buscar secuencias de caracteres alfanuméricos Como mencionamos antes, podemos representar cualquier caracter numérico mediante [0-9], las letras minúsculas con [a-z], y las mayúsculas con [A-Z]. Veamos ahora cómo ver los mensajes del kernel relacionados con las particiones de nuestro sistema. Recordemos que, por lo general las mismas tienen el formato sdXY donde X es una letra minúscula mientras que Y es un número que indica la partición propiamente dicha. El comando que nos permite acceder a esta información es el siguiente: dmesg | grep sd[a-z][0-9] Uso de sed Si bien disponemos de herramientas tales como head y tail para poder visualizar el comienzo o el fin de un archivo, ¿Qué pasa si queremos observar una sección determinada en medio de este? Si recurrimos a sed, podemos lograr este objetivo fácilmente. El siguiente comando nos permitirá ver las líneas 5 a 10 de /etc/passwd: sed -n '5,10p' /etc/passwd Mostrar el archivo entero excepto un rango específico Si la situación fuera a la inversa de la descripta en el punto anterior y quisiéramos ver todo el archivo con excepción de un rango determinado, podemos usar: sed '20,35d' /etc/passwd El comando anterior mostrará todo el archivo /etc/passwd omitiendo las líneas 20-35. 96

Mostrar líneas y rangos no consecutivos Otra variante puede consistir en mostrar una serie de líneas y/o rangos que no sean consecutivos. En el siguiente ejemplo vamos a mostrar las líneas 5 a 7 y 10 a 13 de /etc/passwd: sed -n -e '5,7p' -e '10,13p' /etc/passwd Como podemos observar, la opción -e nos permite ejecutar una acción dada (en este caso, mostrar líneas) para cada rango especificado. En general, esta misma opción puede utilizarse tantas veces como se desee, y acepta como argumento (entre comillas simples) cualquier comando sed, los cuales podemos consultar en la página dedicada a esta herramienta en el sitio del Proyecto GNU. Mostrar líneas que contengan una palabra clave o expresión regular Otro uso muy conocido de esta utilidad consiste en mostrar las líneas de un archivo que contengan una palabra clave o una expresión regular. Por ejemplo, quizás nos interese mostrar los eventos de autenticación que se registraron en un servidor CentOS (a partir del contenido de /var/log/secure) el día 4 de julio. Para lograrlo, utilizamos ^Jul 4 como expresión regular de búsqueda para inspeccionar aquellas líneas que posean la cadena Jul 4 al comienzo de cada línea (denotado por ^): sed -n '/^Jul

4/ p' /var/log/secure

Reemplazar palabras o caracteres Para reemplazar todas las ocurrencias de la palabra cantar por interpretar en micancion.txt podemos utilizar el siguiente comando. Entre el primer par de barras se coloca el término de búsqueda y a continuación la palabra que se desea utilizar para reemplazarlo: sed 's/cantar/interpretar/g' micancion.txt Si deseamos que no se distinga entre mayúsculas y minúsculas, podemos utilizar la opción gi en vez de g al fin del comando. Dicho de otra manera, utilizando 97

sed 's/cielo/firmamento/gi' micancion.txt podemos cambiar la palabra cielo o Cielo por firmamento. Este ejemplo es también válido para un rango de líneas. Si quisiéramos realizar el mismo cambio anterior pero solamente buscando entre las líneas 5 y 35 inclusive, emplearíamos: sed '5,35 s/cielo/firmamento/gi' micancion.txt Cabe aclarar que estos tres últimos ejemplos modifican el contenido de micancion.txt. Si quisiéramos guardar una copia (con el nombre micancion.txt.orig, por ejemplo) antes de hacer la modificación deberíamos usar sed -i'.orig' 's/cielo/firmamento/gi' micancion.txt Es importante que en este comando no debe haber espacios entre la opción -i y el sufijo que deseamos agregar (entre comillas simples) a la copia del archivo original. Edición empleando expresiones regulares Existen ocasiones en las que el criterio de búsqueda no es un término particular, sino que sigue un patrón que puede ser modelado mediante una expresión regular. El siguiente ejemplo elimina las líneas en blanco del archivo micancion.txt utilizando la expresión regular ^$ (la cual representa líneas vacías) y el comando d (de delete): sed '/^$/d' micancion.txt Ahora analicemos el siguiente comando: ip route show | sed -n '/src/p' | sed -e 's/ /g' | cut -d' ' -f9

*/

En este caso enviaremos la salida del comando ip route show a una primera instancia de sed que devolverá únicamente la línea donde se encuentra la palabra clave src. Luego, una segunda instancia 98

reemplazará los espacios dobles por uno simple, el cual servirá como separador de campos para cut, que finalmente devolverá el noveno campo. ¿Pueden adivinar de qué se trata? Sí, la dirección IP de nuestro equipo según la tabla de ruteo. Repetir ejecución de comandos Para repetir la ejecución de un comando cada cierto número de segundos disponemos de una herramienta llamada watch. El valor por defecto es 2 segundos, pero puede cambiarse fácilmente mediante la opción -n. Además, watch permite que veamos los cambios en el resultado del comando en sucesivas ejecuciones. La opción -d nos servirá para esto último. Por ejemplo, si ejecutamos watch -d -n 1 'cat /proc/loadavg' veremos la actualización cada segundo del comando que se observa. Para interrumpir la ejecución bastará presionar la combinación de teclas Ctrl + C.

En el man page de watch se sugieren otros usos posibles para esta herramienta. En síntesis, watch no solamente nos permite repetir la ejecución de un comando, sino también identificar los cambios que hubo entre un caso y el siguiente. Definición y tipos de procesos Los procesos en Linux son ni más ni menos que programas que están corriendo en un momento dado. Algunos de ellos inician a su vez otros procesos, y así sucesivamente en algunos casos. Cuando un proceso A inicia otro proceso B, se dice que A es el padre de B (o lo que es lo mismo, que B es hijo de A). 99

Los procesos normales generalmente son ejecutados en una terminal y corren en el sistema operativo a nombre de un usuario. Los procesos daemon también se ejecutan a nombre de un usuario pero no tienen salida directa por una terminal, sino que corren en segundo plano. Generalmente los conocemos como servicios, y en vez de utilizar una terminal para escuchar por un requerimiento, lo hacen a través de un puerto. Los procesos zombies son aquellos que han completado su ejecución pero aún tienen una entrada en la tabla de procesos debido a que no han enviado la señal de finalización a su proceso padre (o este último no ha podido leerla). Usualmente la presencia de este tipo de procesos indica un error en el diseño del programa involucrado. Muchos procesos zombies pueden llegar a ocasionar que tengamos que reiniciar el equipo debido a que no queden identificadores de proceso disponibles para asignar. El comando pstree Esta herramienta nos permite observar la relación entre procesos padre y sus hijos al visualizar los procesos de nuestro sistema y la relación que existe entre ellos. Si la utilizamos con la opción -p, podremos ver el Process IDentifier (o identificador de proceso, comúnmente llamado PID) de cada proceso. En este contexto, decimos que si el proceso A es el padre de B, el PID de A recibe el nombre de PPID (Parent Process ID) de B. Si a continuación de la opción -p indicamos un PID dado, el árbol de procesos se mostrará comenzando por el proceso al que le corresponde dicho PID:

100

En resumen, podemos decir que el comando pstree nos devuelve una lista de procesos en ejecución en vista de árbol (de forma jerárquica). El comando ps A pesar de lo útil que es pstree, el comando más conocido y utilizado para listar procesos es ps por la gran cantidad de opciones que tiene disponibles. El uso de -ef o aux (esta última sin guión medio al comienzo) hace que podamos visualizar rápidamente -con distinto grado de detalle- el listado de procesos que están corriendo en nuestro sistema. Una característica distintiva de ps es que no es interactivo, sino que saca una foto de los procesos que están corriendo en un determinado momento. Sin opciones, ps nos devuelve los procesos ligados a la terminal actual.

El uso de las opciones a, u, y x de manera separada devuelve una lista de 1) todos los procesos que se están ejecutando en una terminal, 2) muestra el estado de los procesos del usuario actual y qué cantidad de recursos está requiriendo cada uno, y 3) indica la información de los demonios y procesos sin terminal. Al combinar estas opciones como aux podemos acceder a todos esos datos simultáneamente. En la imagen siguiente vemos la salida de ps aux y de cada una de sus opciones utilizada por separado. También observamos el resultado de ps -ef. Por cuestiones de espacio, hemos enviado la salida de los comandos a una tubería (|) de manera que head -n 2 a continuación muestre las dos primeras líneas de cada caso. Esto es suficiente para ilustrar el uso del comando y para inspeccionar el significado de las columnas en el resultado: 101

Veamos el significado de cada columna: USER: usuario dueño del proceso. PID. %CPU: porcentaje de tiempo de CPU utilizado sobre el tiempo que el proceso ha estado en ejecución. %MEM: porcentaje de memoria física utilizada. VSZ: memoria virtual del proceso medida en KiB. RSS (Resident Set Size) es la cantidad de memoria física no swappeada que la tarea ha utilizado (en KiB) TT: terminal asociada al proceso. Si en este campo vemos un signo de pregunta (?) significa que el proceso en cuestión se trata de un servicio que no está utilizando ninguna terminal. STAT: código de estado. Una R en este campo indica que el proceso se está ejecutando. Una S nos dice que está durmiendo esperando a que suceda un evento. STARTED: fecha y hora de inicio del proceso. TIME: tiempo de CPU acumulado. COMMAND: comando relacionado con el proceso, incluyendo todos sus argumentos. Es importante notar que ps nos permite adaptar la cantidad y el orden de las columnas a mostrar, e inclusive nos deja ordenar el resultado utilizando una de ellas de manera ascendente o descendente. 102

Eso es posible mediante las opciones -eo y --sort, respectivamente. La primera debe ser seguida por la lista de campos a mostrar (en el ejemplo que aparece a continuación utilizamos PID, PPID, comando, uso de memoria, y uso de CPU), mientras que a continuación de la segunda debemos colocar un signo igual y el criterio de ordenamiento (-%mem indica ordenar por uso de memoria en forma descendente): ps -eo pid,ppid,cmd,%cpu,%mem --sort=-%mem

Los campos que podemos incluir a continuación de la opción combinada -eo se describen en la sección STANDARD FORMAT SPECIFIERS en el man page de ps. Los comandos kill y killall Hay ocasiones en que un administrador del sistema puede desear interrumpir la normal ejecución de un proceso, ya sea debido a que olvidó incluir un parámetro en el comando que lo inició, porque no se está comportando de la manera esperada, o bien porque está impactando negativamente en el funcionamiento del sistema. En Linux generalmente hacemos referencia a esta acción como matar procesos, y ahora veremos las maneras más usuales de llevarla a cabo. Para evitar errores, antes de matar procesos es preferible identificar aquellos que se verían afectados por nuestra medida. 103

Por ejemplo, antes de matar el proceso con PID 3392 es una buena idea identificarlo primero mediante ps y la opción --pid. La razón de esto es asegurarnos de que apuntamos correctamente el PID a fin de no interrumpir el proceso equivocado: ps --pid 3092 Si la descripción del proceso corresponde con lo esperado, podemos proceder a matarlo con el comando kill seguido del PID del proceso en cuestión: Kill 3092 Si quisiéramos interrumpir todos aquellos que tienen un PPID en común (o todos los que pertenezcan a un usuario o grupo en particular) ir uno por uno sería tedioso. Por tal motivo, disponemos de los comandos pgrep y pkill para identificar y matar varios procesos de una sola vez. A manera de ilustración, identifiquemos aquellos procesos que comparten el padre cuyo PID es 1576: pgrep -l -P 1576 Antes de reemplazar pgrep por pkill, preguntémonos si realmente deseamos matar todos esos procesos. Si la respuesta es sí, podemos hacer pkill -P 1576 Si en vez de realizar la identificación por PPID quisiéramos hacerlo a partir del propietario o del grupo dueño del proceso, simplemente deberemos reemplazar la opción -P con -u o -G, respectivamente, seguido del nombre o del identificador del usuario o grupo. En el caso de que existan varios procesos que compartan el mismo nombre (pero con PID distintos, por supuesto) y deseemos detenerlos a todos, podemos hacer uso del comando killall, cuya 104

sintaxis es idéntica a kill. Esto puede suceder en el caso del servidor Apache y sus procesos relacionados, por ejemplo, donde todos comparten el nombre apache2 (Debian y derivados) o httpd (CentOS y similares). Detrás de escenas: envío de señales a procesos Cuando empleamos kill para matar un proceso, se le envía al mismo una señal. En otras palabras, le estamos mandando una indicación para modificar su funcionamiento normal. Los distintos tipos de señales se enumeran a través de kill -l

Las señales más utilizadas son 1 (SIGHUP), 9 (SIGKILL), y 15 (SIGTERM): Cuando se cierra la terminal asociada a uno o más procesos, se envía a los mismos la señal SIGHUP, lo que hace que se detengan. SIGKILL le indica a un proceso que debe finalizar de inmediato, sin darle tiempo a liberar adecuadamente los recursos que esté utilizando. Esta señal no puede ser ignorada por el proceso al que es enviada. Por ejemplo, cualquiera de los siguientes comandos enviará un SIGKILL al proceso cuyo PID es 964: kill -9 964 kill -s SIGKILL 964 105

Finalmente, SIGTERM le permite al proceso terminar su ejecución normalmente dándole la oportunidad de liberar los recursos utilizados. Aunque esto suene bien, cabe aclarar que esta señal puede ser ignorada por un proceso. Por eso puede ser necesario (como último recurso) utilizar SIGKILL en algunas ocasiones. El comando top Una de las limitaciones de ps es que devuelve una foto del estado de los procesos en el momento que el comando se ejecuta. En cambio, top actualiza la información cada cierto tiempo (por defecto, cada tres segundos) y además provee otros datos útiles sobre el estado del sistema. En la siguiente imagen vemos a top en funcionamiento. En la pantalla podemos distinguir dos áreas principales, marcadas con 1 y 2.

En las dos primeras filas del área marcada con el 1 vemos información sumamente importante sobre el estado actual del sistema: Fila 1: top es el nombre del programa. 20:55:23 es la hora actual. up 57 min indica que el equipo ha estado encendido por 57 minutos. 106

load average: 0.00, 0.00, 0.00 muestra la cantidad de procesos (en promedio) que están utilizando (o han estado esperando para utilizar) el CPU durante los últimos 60 segundos, 5 minutos, y 15 minutos, respectivamente. Para ilustrar el significado de los valores del load average (también llamado carga promedio), supongamos que los valores sean diferentes (digamos 0.64, 0.30, 0.69, por ejemplo). En este supuesto, 0.64 significa que durante los últimos 60 segundos el CPU ha estado desocupado el 36% del tiempo. De la misma manera, 0.30 y 0.69 indican que durante los últimos 5 y 15 minutos el CPU ha estado desocupado el 70% y el 31% del tiempo, respectivamente. Fila 2: Tasks: 161 total, 1 running, 160 sleeping, 0 stopped, 0 zombie muestra la cantidad de procesos que están corriendo actualmente en el sistema y sus posibles estados:

running indica la cantidad de procesos que están corriendo. En la sección indicada con el 2 se los identifica con la letra R en la columna S (de Status). sleeping representa el número de procesos que no están corriendo actualmente pero que se encuentran esperando que ocurra un evento para despertarse (por ejemplo, una consulta al servidor web). Se indican con la letra S en la misma columna mencionada anteriormente. stopped son aquellos procesos que han sido detenidos. Se identifican con la letra T. Los procesos zombie se indican con la letra Z. 107

En el área marcada con el 2 podemos apreciar los siguientes datos sobre cada proceso (de izquierda a derecha): PID: Process ID. USER: Usuario dueño del proceso. PR: Prioridad de ejecución del proceso. NI: Es un valor que refleja la prioridad sobre el uso de los recursos del sistema otorgada a cada proceso en particular. VIRT: Cantidad de memoria virtual que el proceso está manejando. RES: Es la representación más cercana a la cantidad de memoria física que un proceso está utilizando. SHR: La cantidad de memoria compartida (potencialmente con otros procesos) disponible para este proceso en particular, expresada en KiB. S: Estado del proceso. %CPU: Es el porcentaje de tiempo de CPU utilizado por el proceso desde el último refresco de pantalla. %MEM: Indica el porcentaje correspondiente al uso de memoria física de un proceso en particular. TIME+: Tiempo de CPU que ha utilizado el proceso desde que inició, expresado en minutos:segundos.centésimas de segundo . COMMAND: Muestra el comando que se utilizó para iniciar el proceso o el nombre de este último. 108

Veamos otro ejemplo:

Consideremos el proceso con PID 991 (mysqld). Entre otras cosas, podemos destacar que dicho proceso está siendo ejecutado como el usuario mysql (USER), está utilizando alrededor de 73424 KiB de RAM (RES), lo cual corresponde al 14.6% del total de memoria instalada en el equipo (%MEM). A comparación del resto de los procesos, está ocupando bastante más tiempo de CPU (2 minutos, 15 segundos). Como toda herramienta de la línea de comandos, top posee numerosas opciones. Entre las más útiles podemos destacar las siguientes: Refrescar la pantalla cada X segundos: top -d X Ordenar la salida por uso de RAM (descendente): top -o %MEM Monitorear sólo los procesos cuyos PIDs son 324, 697, y 25216: top -p 324,697,25216 Como siempre, podemos buscar más detalles sobre el uso de esta herramienta al examinar su man page. Los comandos nice y renice Ya hemos visto que en un momento dado puede haber numerosos procesos corriendo en nuestro sistema (al menos esa es la percepción que tenemos). Probablemente algunos de ellos sean más críticos que otros y por lo tanto deberán poder utilizar los recursos del sistema 109

cuando los necesiten. Esto se logra estableciendo la prioridad de ejecución de procesos, algo que mencionamos en la sección anterior. Las columnas PR y NI en la salida de top indican la prioridad que se le ha asignado a un proceso en particular -tal como lo ve el kernel en un momento dado- y el valor de niceness del mismo. Este último valor puede considerarse como una especie de pista que se le da al kernel para indicar la prioridad que debe darse inicialmente al proceso. PR y NI están relacionados entre sí: mientras mayor sea la prioridad de un proceso, es menos nice (del inglés bueno) ya que consumirá más recursos del sistema. El valor de niceness puede ubicarse en algún lugar del intervalo comprendido entre -20 y 19. Estos dos límites representan la mayor y la menor prioridad posibles, respectivamente. Para modificar la prioridad de un proceso en ejecución, utilizaremos el comando renice. Los usuarios con privilegios limitados solamente pueden aumentar el valor de niceness correspondientes a un proceso del cual son dueños, mientras que root puede modificar este valor para cualquier proceso sin importar quién sea el usuario dueño de este. Por ejemplo: Cambiar la prioridad del proceso con PID 324 (opción -p) a 10: renice -n 10 -p 324 Cambiar la prioridad de todos los procesos del usuario (opción -u) alumno a -10: renice -n -10 -u alumno Por defecto, cualquier nuevo proceso se ejecuta con una prioridad igual a 0. Si deseáramos iniciarlo con una prioridad diferente, podemos hacer uso del comando nice seguido de la opción -n, del nuevo valor de niceness deseado, y del comando a ejecutar. Por ejemplo, iniciemos top con un valor de niceness igual a 10: nice -n 10 top 110

El resultado de la ejecución de nice y renice pueden comprobarse al observar las columnas PRI y NI en la salida de top. Como ya sabemos, el comando ps aux nos devolverá un listado de todos los procesos en ejecución. Si en vez de desear mostrar esa salida por pantalla la enviamos como entrada del comando grep, podemos filtrarla y mostrar solamente las líneas que cumplan un determinado patrón. ps aux | grep apache ps aux | grep apache | grep -v grep

Si prestamos atención a la imagen anterior, la última línea muestra parte del comando propiamente dicho. Si deseamos eliminar esa línea de la salida de tal manera que veamos únicamente los procesos relacionados con Apache, podemos agregar una tubería adicional como también se muestra en el segundo comando. Procesos en primer y segundo plano En algunos casos, un script puede tomar un tiempo considerable en completar su ejecución, o un programa puede ocupar la línea de comandos mientras se encuentre corriendo. Para permitirnos volver a tomar el control de la terminal en cuestión, la shell nos permite colocar procesos en segundo plano. También podemos iniciar directamente un proceso en segundo plano, de manera que no ocupe la terminal mientras corre. 111

Para que un proceso se inicie en segundo plano, colocaremos el símbolo & al fin de este. Por ejemplo, si queremos correr updatedb en segundo plano, podemos hacer: updatedb & Al iniciar un proceso en segundo plano, la terminal queda libre para seguir trabajando en la misma y se nos provee la identificación de dicho proceso y su PID. Si deseamos traer un proceso a primer plano, nos valdremos el comando fg seguido del identificador del mismo. Si sucediera que no supiéramos el identificador del proceso en cuestión, podemos valernos del comando jobs para averiguarlo. Supongamos que queremos encontrar todos los archivos con la extensión .sh y guardar la lista de los mismos en listadescripts.txt: find / -iname "*.sh" > listadescripts.txt & Mientras dura la búsqueda, pondremos el comando anterior en segundo plano, lo mostraremos con jobs, y lo traeremos de nuevo a primer plano con fg seguido del identificador (1 en este caso):

Si el proceso se hubiera iniciado en primer plano, podemos pausar su ejecución con Ctrl + z y luego utilizar el comando bg para enviarlo a segundo plano:

112

De ahí en más podemos proceder como explicamos anteriormente para traerlo nuevamente a primer plano si así lo deseamos. El comando nohup la ejecución de un proceso se verá interrumpida si cerramos la terminal desde la que lo iniciamos, o si cerramos la sesión actual. Para evitar que esto suceda, podamos usar nohup, una herramienta que permite iniciar un proceso que sea inmune a la situación que describimos anteriormente. Poder utilizar nohup es especialmente útil cuando estamos conectados a un equipo de manera remota y deseamos ejecutar un script que continúe su ejecución luego de que cerremos la terminal asociada. De esta manera, no tenemos que estar logueados durante todo el tiempo que corra el script. La sintaxis del uso de esta herramienta es la siguiente: nohup [programa a iniciar] & Como podemos ver, generalmente nohup se utiliza en conjunto con el símbolo & para enviar simultáneamente el programa en cuestión a segundo plano, aunque no es estrictamente necesario que lo hagamos de esa manera. Veamos un ejemplo dividido en pasos: Paso 1 – Iniciar un ping a carreralinux.com.ar: nohup carreralinux.com.ar Paso 2 – Desde otra terminal, tomamos nota del PID del comando anterior (2351) y de su padre (2324, el cual correspondea la primera terminal): ps -u gacanepa -o ppid,pid,cmd Paso 3 – Cerramos la primera terminal y volvemos a ejecutar el comando anterior. Vemos que el proceso padre (la shell bash) ya no aparece en la lista, y que ahora el padre del ping es el proceso con PID 1. 113

En la imagen de arriba podemos ver que cuando el proceso padre desapareció de la tabla de procesos, el hijo huérfano fue adoptado por systemd. Múltiples terminales en la misma pantalla Por comodidad y para disponer de terminales adicionales en una misma pantalla, es conveniente instalar un programa como tmux o screen. Ambas herramientas que nos permiten crear, acceder, y controlar varias terminales desde una única consola. A continuación, nos concentraremos en tmux exclusivamente, ya que el funcionamiento de screen es similar. Dividir la pantalla horizontal y verticalmente Una vez que hayamos instalado la herramienta, iniciemos una sesión llamada prueba de la siguiente manera: tmux new -s prueba En este contexto, una sesión representa el grupo de una o más 114

consolas abiertas mediante tmux en una misma pantalla. Una vez que iniciamos la sesión, veremos que en el borde inferior de la pantalla aparece una barra en la que se indican 1) el nombre de la sesión, 2) el nombre del equipo que estamos usando, y 3) fecha y hora actuales:

Para dividir la pantalla horizontalmente, utilizaremos la combinación de teclas Ctrl + b + " (Ctrl + b, luego "). Podemos ver el resultado en la imagen a continuación:

115

Para desplazarnos de la consola de arriba hacia la de abajo (y viceversa), usaremos Ctrl + b + flecha arriba y Ctrl + b + flecha abajo. La consola activa se puede distinguir por la presencia del cursor en la misma. Una vez que estemos posicionados en una de las dos consolas podemos dividirlas verticalmente con Ctrl + b + % y desplazarnos de una a otra con Ctrl + b + flecha (arriba, abajo, derecha, e izquierda). A continuación, podemos ver la pantalla dividida en cuatro terminales en las que ejecutamos distintos comandos:

Para cerrar alguna de las consolas podemos hacerlo mediante Ctrl + b + x una vez que nos hayamos posicionado sobre la misma. Se nos pedirá una confirmación a lo que debemos contestar y (de yes). Al cerrar la última consola activa siguiendo este procedimiento volveremos a la terminal común y corriente, fuera de la sesión de tmux, que teníamos al principio. Podemos modificar el atajo de teclado que utilizamos como prefijo (Ctrl + b) y cambiarlo a Ctrl + a (u otra combinación) si así lo deseamos. Para que este cambio se aplique a todos los usuarios del sistema, 116

podemos modificar el archivo /etc/tmux.conf, mientras que si solamente debe ser para algunos, deberemos editar ~/.tmux.conf de la siguiente manera (puede que ninguno de estos dos archivos exista y que tengamos que crearlo nosotros): unbind C-b set -g prefix C-a bind C-a send-prefix

Si deseamos salir de una sesión sin cerrarla y volver a la línea de comandos original podemos despegarnos de la misma: tmux detach Luego, para volver a ingresar en la sesión prueba: tmux attach -t prueba Para salir definitivamente de la sesión prueba podemos finalizarla con tmux kill-session -t prueba.

Crear nuevas ventanas Si deseamos utilizar todo el espacio disponible en la terminal pero aun así necesitamos crear nuevas consolas (e identificarlas de alguna manera) siempre dentro de la misma sesión, tmux nos permite hacerlo de la siguiente forma (supondremos que continuamos operando con Ctrl + b como prefijo): 117

Luego de abrir una sesión, crearemos una nueva ventana (también llamadas pestaña) con Ctrl + b + c y luego con Ctrl + b + , (coma) haremos aparecer en el extremo inferior un diálogo para ingresar el nombre. Luego de ingresar la identificación deseada (otra_ventana en este ejemplo) y presionar Enter, podremos verla en el mismo lugar, como se observa en la imagen:

Para desplazarnos de una ventana a otra, utilizaremos Ctrl + b + n (de next, para ir a la próxima ventana) o Ctrl + b + p (de previous, para ir a la ventana previa). El editor de texto vi Introducción Una de las herramientas esenciales que todo sysadmin debe utilizar en sus tareas diarias es un editor de texto. La razón principal es que los archivos de configuración de los servicios del sistema y de las preferencias de usuario se presentan en formato de texto plano. En el mundo Linux existen varios editores de texto entre los que podemos elegir. Algunos de los más conocidos son emacs, nano, y vi (o vim, su versión mejorada). Este último goza de gran popularidad en la comunidad, y será el editor que aprenderemos a utilizar en este capítulo. Una de las ventajas de vi es que se instala por defecto junto con cualquier distribución. Si lo deseamos, también podemos utilizar una versión gráfica de vi que se llama gvim. Para hacerlo, lo más probable es que necesitemos instalarlo primero. 118

Modos de operación de vi Al invocar a vi seguido del nombre de un archivo, lo crea si no existe. En la pantalla aparecerá la posición actual del cursor resaltada, las líneas en blanco indicadas con el símbolo ~, y en la parte inferior la línea de estado. En este sector se mostrará el nombre del archivo y el número de caracteres que contiene. Existen dos modos de operación en el vi: Modo entrada: Se usa para añadir texto al archivo. Modo comando: Es el modo de operación por defecto. Se usa para introducir comandos que realizan funciones específicas del editor. Puesto que vi no indica en qué modo se trabaja en un momento determinado, distinguir entre ambos es probablemente el mayor problema que causa confusión entre los nuevos usuarios de este editor. Cuando se abre por primera vez un archivo, vi siempre se encuentra en modo comando. Por esa razón, antes de poder escribir texto en el archivo se necesita presionar una de las teclas que habilitan el modo entrada: i (insert), para insertar texto en la posición actual del cursor. a (append) para insertar texto después de la posición actual del cursor. Para regresar del modo entrada al modo comando bastará con presionar la tecla Esc. De la misma manera podemos pasar a modo comando si en un momento dado no sabemos en qué modo estamos. El modo comando Cuando abrimos un archivo con vi, estamos en modo comando. En este modo podemos introducir comandos que implementan un amplio rango de funciones. Muchos de los mismos constan de una o dos 119

letras y un número opcional relacionado con distintas funciones. Cuando editamos un archivo con vi, los cambios no se hacen directamente sobre el archivo. En realidad, se aplican a una copia temporal del archivo que vi crea en un espacio de memoria temporal llamado buffer. La copia en disco del archivo se modifica solo cuando se graban los contenidos del buffer. Esto tiene sus ventajas y sus inconvenientes. Por un lado, significa que podemos salir de la edición de un archivo y descartar todos los cambios hechos durante una sesión de edición, dejando la copia de disco intacta. Por otro lado, podemos perder el contenido no grabado del buffer de trabajo si el sistema cae. La mejor política en todos los casos es grabar cambios frecuentemente, especialmente cuando hayamos hecho cambios importantes. Para grabar nuestro trabajo sin salir del vi basta pulsar la secuencia: Esc :w

[ENTER]

Para salir cuando no se han hecho modificaciones: Esc :q

[ENTER]

Para salir cuando se han hecho modificaciones: si queremos descartar: Esc :q! [ENTER] si queremos guardar los cambios: Esc :wq [ENTER] Comandos básicos de vi Cuando arrancamos vi, el cursor está en la esquina superior izquierda de la pantalla. En modo comando, existen ordenes que nos permiten moverlo por toda la pantalla: ciertas teclas de letras, las flechas, el ENTER, BackSpace (tecla de borrar), y Space Bar (barra espaciadora). Los comandos de vi son case-sensitive, es decir, la 120

la misma orden tecleada en mayúsculas o minúsculas puede tener efectos totalmente distintos. Si el teclado está provisto con flechas, se pueden utilizar para mover el cursor caracter a carácter libremente por el texto editado hasta el momento. Si se usa desde un terminal remoto, las flechas pueden no funcionar correctamente, dependiendo del emulador de terminal. En dicho caso las teclas usadas para desplazarnos son: Izquierda: tecla h o barra espaciadora Derecha: tecla l (ele) o BackSpace Arriba: tecla k Abajo: tecla j Para desplazarnos palabra a palabra emplearemos Una palabra hacia la derecha: tecla w Una palabra hacia la izquierda: tecla b Si deseamos movernos a lo largo de una línea: Con ^ nos posicionamos al comienzo de la línea en la que está el cursor Con $ nos dirigimos al final de la línea actual Con [ENTER] nos desplazamos al comienzo de la línea siguiente. Dentro de la misma pantalla, con: H nos movemos a la parte superior de la pantalla L nos desplazamos a la parte inferior de la pantalla M nos posicionará en la mitad de la pantalla Para avanzar o retroceder de página puede resultar un poco tedioso utilizar las teclas mencionadas anteriormente si el archivo es muy extenso. Por eso, vi provee las siguientes combinaciones que permiten desplazarnos página a página por el documento: 121

Ctrl + f y Ctrl + b hace que avancemos o retrocedamos una pantalla, y que el cursor se mueva a la esquina superior o inferior izquierda, respectivamente. Ctrl + d y Ctrl + u nos permite avanzar y retroceder media pantalla, respectivamente. Para ir a la última línea de un archivo abierto, deberemos teclear G. Si deseamos ir a la línea n, se presiona el número que corresponda seguido de G. Por ejemplo, 23G hará que saltemos a la línea 23 del archivo actual. Insertar texto A la hora de pasar desde el modo comando a modo entrada texto disponemos de varias posibilidades. Todas ellas pueden ser útiles de acuerdo a la posición donde deseemos comenzar a agregar texto relativa a la ubicación actual del cursor. Con a se inserta texto a la derecha del cursor. Con A se añade texto al final de la línea en la que está el cursor. Presionando i se inserta texto a la izquierda del cursor. Presionando I (i mayúscula) se inserta texto al principio de la línea en la que está el cursor. Mediante o podremos crear una nueva línea debajo de la actual. Mediante O podremos insertar una nueva línea encima de la actual. También es posible insertar los contenidos de un archivo dentro del que estemos editando. Para hacerlo, necesitaremos hacer lo siguiente: :linea r archivo donde linea es representa la línea encima del punto de inserción, r indica la operación, y archivo es la ruta al archivo a insertar. Si el nombre del archivo a insertar contiene un punto (como por ejemplo, para indicar la extensión del mismo) se debe escapar utilizando la 122

barra \. Por ejemplo, para insertar pruebas.txt en la línea 10 del archivo actual, deberemos hacer: :9 r pruebas\.txt Además, puede resultar útil insertar el resultado de un comando al editar un archivo. La sintaxis es muy similar al caso anterior: :linea r! Comando Por ejemplo, :5 r! which python3 insertará el resultado de which python3 en la línea 6 del archivo. Cambiar texto Cambiar texto implica sustituir una sección de texto por otra. El editor vi tiene varios modos de hacer esto, dependiendo de la necesidad. Veamos algunos ejemplos: Para reemplazar una palabra, posicionar el cursor al principio de la palabra a ser reemplazada, teclear cw seguido de la nueva palabra. Cuando terminemos de realizar la modificación deberemos pulsar Esc para finalizar. Para cambiar parte de una palabra, colocar el cursor sobre la palabra, a la derecha de la parte a cambiar, y proceder como en el caso anterior. Para reemplazar una línea, poner el cursor en cualquier parte de la línea y teclear cc. La línea desaparece, dejando una línea en blanco para el nuevo texto, que puede ser de cualquier longitud. Pulsar Esc para acabar. Para reemplazar parte de una línea , colocar el cursor a la derecha de la parte a modificar. A continuación, presionar C, introducir la corrección y pulsar Esc para finalizar. Para substituir el carácter bajo el cursor por uno o más caracteres , teclear s seguido del nuevo texto y pulsar Esc para terminar. 123

Para trasposicionar caracteres, colocar el cursor sobre la primera letra a mover y pulsando xp, se intercambian las posiciones de ambos caracteres. Esto es útil para fallos tales como escribir qeu en lugar de que. Para partir una línea sin afectar al text o, mover el cursor al espacio donde se quiere partir la línea y teclear r (de replace) seguido de ENTER (se reemplazaría el espacio por un ENTER). Para unir dos líneas, colocar el cursor en la línea superior y teclear J. Pulsando u a continuación del último comando, se deshacen los cambios producidos por la ejecución del mism o. Pulsando U se deshacen todos los cambios que se han hecho sobre una línea. Este comando funciona solo si no nos movimos de la línea. Para borrar un carácter, posicionar el cursor sobre el carácter a borrar y teclear x. El comando x también borra el espacio ocupado por el carácter. Para borrar el carácter anterior a la posición del cursor pulsar X. Para borrar una palabra, posicionar el cursor al principio de la palabra y pulsar dw, entonces se borrará la palabra y el espacio que esta ocupaba. Para borrar parte de una palabra, hay que colocar el cursor a la derecha de la parte a modificar y teclear dw. Pulsando dd se borra una línea y el espacio que esta ocupaba. Para borrar todo lo que esté a la derecha del cursor basta con pulsar D. 124

Para borrar todo lo que esté a la izquierda del mismo basta con pulsar d0. Por otro lado, dG borra desde la línea en que estaba el cursor hasta el final del archivo. De forma similar, d1G borra hasta desde el principio del archivo hasta la línea en que estaba el cursor. Copiar y mover texto: Yank, Delete, y Put De la misma manera que muchos procesadores de text nos permiten copiar y pegar, o cortar y pegar líneas de texto, vi también incluye esta posibilidad, mediante los comandos yank / put y delete / put, respectivamente. Para copiar una línea son necesarios dos comandos: yy o Y (yank) y p o P para pegar. Para hacer lo mismo con varias líneas basta con colocarse en la primera línea a copiar y escribir el número de líneas a copiar seguido por yy (por ejemplo, 10yy para copiar la línea actual y las 9 siguientes). Para pegar el texto, podemos usar los comandos p o P.

Es importante que entre la operación de copiar (o cortar) y pegar se usen solamente los comandos de movimiento de cursor. Si se borra o copia cualquier otro texto antes de poner el nuevo texto en su lugar, las líneas copiadas o borradas se perderán. Para mover una o varias líneas de sitio requiere también dos comandos: dd (delete) y p o P. Igual que en el caso anterior, se puede teclear antes de dd el número de líneas a mover. Muchos comandos de vi pueden estar precedidos de un contador que indica cuántas veces repetir la operación. Muchos de los anteriores permiten contadores. Por ejemplo, 3dd repite el comando de borrar una línea 3 veces, 2dw borra dos palabras, y 4x borra 4 125

caracteres. También se pueden usar contadores para los comandos de moverse por la pantalla que mencionamos anteriormente. Un dato interesante es que pulsando un . (punto) se repite la última operación de cambio realizada. Por ejemplo si se borra una línea, y la siguiente operación a realizar es borrar una línea, basta con pulsar . para llevarla a cabo nuevamente. Usar buffers con nombre Para insertar repetidamente un grupo de líneas en varios lugares dentro de un documento, se puede copiar las líneas a un buffer con nombre. Se especifican los buffers con nombre escribiendo antes del comando comillas dobles y su nombre. Por ejemplo: Para copiar 4 lineas al buffer a haremos "a4yy Para copiar 10 lineas al buffer b haremos "b10yy Para pegar las lineas del buffer a haremos "ap o "aP Para pegar las lineas del buffer b haremos "bp o "bP Al emplear buffers podemos mantener al alcance varios bloques de texto para pegar cuando y donde nos resulte conveniente. Uso de expresiones para copiar, mover, y borrar líneas Cuando necesitamos copiar (o cortar) y pegar grandes cantidades de texto, el uso de las expresiones que explicaremos a continuación resulta más útil y seguro que emplear yank, delete, y put. Además, mejor que contar líneas en la pantalla y buscar el punto de inserción, se le pueden dar a vi un rango de líneas a mover o copiar y entonces especificar la línea anterior al punto de inserción. Como es de esperar, con un comando de borrado no existe punto de inserción. El formato básico del comando para copiar líneas es la expresión: :linea1,linea2 co linea3 [ENTER] 126

donde linea1 y linea2 especifican el rango de líneas a copiar (incluyendo ambos extremos), mientras que linea3 representa la línea anterior al punto de inserción. La palabra clave co indica que estamos por realizar una operación de copia. Para casos especiales como la línea actual o el final del archivo, podemos utilizar las siguientes abreviaturas: denota la línea actual. $ representa el final del archivo. Para mover y borrar líneas el comando es similar. En el primer caso haremos :linea1,linea2 m linea3 [ENTER] mientras que en el segundo :linea1,linea2 d [ENTER] Búsqueda y reemplazo de cadenas de texto Las cadenas de texto, también llamadas strings, son secuencias ordenadas de caracteres. Puede incluir letras, números, puntuaciones, caracteres especiales, espacios en blanco, tabulaciones, o retornos de carro. Un string puede ser una palabra gramatical o puede ser una parte de una palabra. El editor vi proporciona varios métodos para encontrar strings en un archivo de texto, así como para reemplazarlo. Para encontrar un string, hay que teclear / seguido del string que se desea buscar, y entonces pulsar ENTER. Como resultado, se posicionará el cursor en la siguiente ocurrencia del string. Tecleando n se salta a la siguiente ocurrencia del string, y pulsando N a la anterior.

Para buscar hacia atrás en un archivo se puede usar ? en lugar de /. En este caso, las direcciones de n y N se invierten. 127

Normalmente las búsquedas son case-sensitive. Si se quiere ignorar mayúsculas y minúsculas (o ignore case) durante la búsqueda, teclear: :set ic[ENTER] Para volver al cambio por defecto bastará hacer: :set noic[ENTER]

Ciertos caracteres especiales (/ & ! . ^ * $  ?) tienen un significado especial para el proceso de búsqueda y deben especificarse de un modo especial en la búsqueda, precediéndolos por la barra hacia atrás \. Este procedimiento se conoce comúnmente como escape de caracteres. Por ejemplo, para buscar algo? hay que teclear /algo\?[ENTER]. Para realizar búsquedas más precisas podemos recurrir a los siguientes filtros o ayudas: Para buscar en el comienzo de una línea, hay que preceder al string a buscar por ^. Por ejemplo, para buscar la siguiente línea que empieza por Sin embargo, habría que teclear :/^Sin embargo[ENTER]. Para buscar en el final de una línea, hay que terminar el string a buscar por un símbolo $. Para ilustrar, el comando :/nada\.$ [ENTER] nos permitirá buscar la siguiente línea terminada en nada. (incluyendo el punto). Para buscar cualquier caracter, deberemos utilizar el . (punto) en el string de la posición a emparejar. Por ejemplo, para encontrar la siguiente ocurrencia de disinformation o misinformation, deberemos hacer /.isinformation[ENTER]. 128

Debido a que en este caso estamos realizando la búsqueda de un string, y no de una palabra, este último patrón puede también encontrar palabras como misinformationalist y disinformationism. Para buscar caracteres alternativos en un string, poner las alternativas entre [ ] (corchetes). El patrón de búsqueda /[dm]texto encontrará los strings que comiencen por m o con d, seguidos de la palabra texto. Por otro lado, /[d-m]texto, devolverá strings que comiencen con cualquier letra de la d a la m, también seguidos por la misma palabra que en el caso anterior. Para buscar 0 o más ocurrencias del ultimo carácter, teclear un * (asterisco) en el string. Se pueden combinar corchetes con asteriscos para buscar por alternativas mejor definidas. Por ejemplo, para encontrar todos las cadenas de texto que comiencen con una una letra de la a a la z y terminen en isinformation, teclear /[a-z]*isinformation[ENTER]. Para reemplazar una cadena de texto la forma básica del comando es :

%s/texto_a_buscar/texto_de_reemplazo/g[ENTER]. Por ejemplo, para reemplazar todas las ocurrencias de la palabra carro por automovil, se debería teclear :%s/carro/automovil/g[ENTER]. Si luego de la g agregamos una c (de tal forma que quede :%s/carro/automovil/gc[ENTER]), nos pedirá confirmación antes de reemplazar cada una de las ocurrencias de carro. Las respuestas posibles son y (de yes) y n (de no). Editar múltiples archivos Para imitar de alguna manera las pestañas de los editores de texto que podemos llegar a encontrar en el modo gráfico, vi permite editar varios archivos. 129

Por ejemplo, para editar usuarios.txt, grupos.txt, y accesos.txt podemos invocar a vi de la siguiente manera: vi usuarios.txt grupos.txt accesos.txt Los archivos se identifican por número, correspondiendo en este caso el 1 para usuarios.txt, el 2 para grupos.txt, y finalmente el 3 para accesos.txt. Mientras estamos editando uno de ellos podemos saltar a otro guardando los cambios primero y luego haciendo :n #numero[ENTER] donde numero indica el número de archivo según explicamos. Por ejemplo, si estamos editando usuarios.txt podemos saltar a accesos.txt haciendo :n #3[ENTER]. Si lo que nos interesa es copiar líneas entre archivos, vi también nos permite hacerlo: 1. Abrir el archivo fuente y copiar las líneas que nos interesen usando yank (se puede aplicar cualquiera de las técnicas que describimos en este capítulo). 2. Sin salir de vi, ir al archivo destino (digamos datos.txt por ejemplo) mediante :n datos.txt, y una vez ahí emplear put como hemos explicado. Opciones de configuración Afortunadamente, vi también nos permite configurar el ambiente de uso para que nos resulte lo más placentero posible. Las siguientes opciones de configuración pueden establecerse cada vez que abramos un archivo o bien colocarlas (una por línea) en el archivo .vimrc dentro del directorio personal de usuario que desee emplearlas. Importante: si vamos a establecer estas opciones mientras estamos editando un archivo, las mismas estarán disponibles hasta que cerremos el mismo. Para guardarlas de manera permanente deberemos incluirlas en el archivo .vimrc. En este último caso no es necesario colocar los dos puntos (:) antes del comando propiamente dicho. 130

Para visualizar el número de cada línea, teclear: :set nu[ENTER] Para ocultarlas, teclear: :set nonu[ENTER] Por último, los siguientes comandos nos servirán (¡y de mucho!) si necesitamos utilizar vi para desarrollar scripts o algún otro tipo de código. De esta manera se fijará la indentación en 1 Tab = 4 espacios, y nuevas líneas tendrán el mismo nivel que la anterior. set autoindent set shiftwidth=4 set softtabstop=4 set expandtab

131

Dispositivos, sistemas de archivos y FHS (Tema104) En el tema 102 (Instalación de Linux y administración de paquetes) aprendimos a crear particiones con fdisk y sistemas de archivos con mkfs. En esa ocasión mencionamos que la primera tarea también puede llevarse a cabo con gdisk. Además, existe otra herramienta similar llamada parted que goza de gran popularidad. En esta oportunidad explicaremos el criterio a utilizar para decidir cuál emplear y mostraremos cómo mantener la integridad de los sistemas de archivos o repararlos cuando sea necesario. 132

Particiones MBR versus GPT El estándar de particionado MBR (Master Boot Record) soporta discos de hasta 2 TB y con un máximo de 4 particiones primarias. Por otro lado, GPT (GUID Partition Table) soporta discos de hasta 2 ZB y puede manejar hasta 128 particiones en cada uno. Con GPT, identificamos a cada partición por un UUID en la tabla de particiones. Además, esta información es almacenada en múltiples lugares del disco para evitar dificultades. Para asegurarse de que está intacta o recuperarse si es necesario, GPT incluye una funcionalidad llamada CRC (de Cyclic Redundancy Check, o Comprobación de Redundancia Cíclica en español). Gracias a esta característica, un disco particionado de esta manera es más estable y menos propenso a fallas.

Para crear particiones bajo el estándar MBR se acostumbra emplear fdisk, mientras que gdisk o parted resultan más apropiadas para GPT debido a que permiten manejar más eficientemente dispositivos de mayor tamaño (>2 TB). Sin embargo, gdisk y parted también pueden crear particiones MBR sin inconvenientes. La siguiente imagen nos muestra la lista de particiones en /dev/sda con parted: parted -l /dev/sda

133

Para determinar si un disco está utilizando MBR o GPT podemos observar la información proporcionada por el comando anterior. En este caso, Partition Table: msdos nos indica que /dev/sda utiliza MBR. Si el resultado hubiera sido gpt podríamos asegurar que el disco fue particionado empleando dicho estándar. Memoria swap Debido a que una computadora puede tener solamente una cantidad finita de memoria física, quizás haya momentos en que la cantidad de esta no sea suficiente para todos los servicios y aplicaciones que requieran su uso. Cuando eso sucede, el sistema operativo puede llegar a utilizar una porción definida de uno (o varios) dispositivos de almacenamiento (llamada memoria swap) para mantener datos no prioritarios. En esta sección abordaremos precisamente el uso de la memoria swap y cómo configurar una partición para tal propósito.

Cuando la memoria RAM se encuentra al límite, el kernel comienza a mover aplicaciones al espacio designado como swap. De esa manera el sistema puede continuar funcionando, aunque a costa de una disminución de rendimiento. De no existir este espacio de intercambio, ante una situación similar entra en funcionamiento el kernel dispara el OOM Killer (Out Of Memory Killer), o cancelador de procesos, que se encarga de liberar la mayor cantidad de memoria posible. Esto se logra al detener por la fuerza uno o más procesos teniendo en cuenta la importancia de estos para el sistema. Uso y configuración de la memoria swap El acceso a los datos almacenados en la memoria RAM siempre es más rápido que si el sistema operativo tuviera que buscarlos del disco. Es por ese motivo que los datos en swap son devueltos a la memoria principal en cuanto se libere el espacio necesario en la misma. En sistemas modernos con gran cantidad de memoria RAM (> 4GB) 134

Si se dispone con 2 GB o menos de memoria física (RAM), el tamaño de la memoria swap debe ser el doble. Por ejemplo, si tenemos 1.5 GB de RAM, deberíamos utilizar 3 GB de swap. Si tenemos más de 2 GB de RAM, le debemos sumar 2 GB extras de swap, pero sin superar los 8 GB. Por ejemplo, si tenemos 3 GB, tendríamos que disponer de 5 GB de swap. Supongamos que contamos con un equipo que cuenta con 512 MB de RAM. De acuerdo a la regla anterior, deberíamos configurar 1 GB de swap. Empezaremos creando una partición (/dev/sdb1, por ejemplo) de ese tamaño con cualquiera de las herramientas de las que hemos hablado con anterioridad. Una vez creada la partición /dev/sdb1, utilizaremos los siguientes comandos para formatearla como espacio swap, asignarle como etiqueta la palabra SWAP (o alguna otra de nuestra elección) y activarla para su uso: mkswap -L SWAP /dev/sdb1 swapon -L SWAP echo "LABEL=SWAP swap swap sw 0 0" >> /etc/fstab Para verificar que la partición está siendo ahora utilizada con el propósito que indicamos, podemos usar cat /proc/swaps El comando anterior mostrará la lista de particiones o archivos que estén siendo utilizados como espacio de intercambio. De la misma manera que utilizamos el comando swapon para ponerlo en funcionamiento, podemos emplear swapoff para desactivarlo si lo deseamos. También podemos comparar la salida de free -m antes y después del procedimiento anterior para observar el cambio en la cantidad destinada a la memoria swap. 135

Sistemas de archivos Podemos clasificar a los sistemas de archivos en dos categorías principales: aquellos que proveen journaling y aquellos que no. Entre estos últimos solamente se continúa utilizando ext2 (por lo general para la partición dedicada a /boot). El uso de journaling en ext3, ext4, y xfs (entre otros) permite llevar una cuenta de los cambios que no se han escrito al sistema de archivos.

Las particiones de arranque del tipo EFI utilizan FAT (File Allocation Table). En Linux se utiliza específicamente el sistema de archivos vfat para estas. Más recientemente, Microsoft desarrolló el sistema de archivos exFAT (extended FAT) para su uso en unidades USB y discos externos, y para su uso en Linux se requiere la instalación de librerías adicionales. Montaje de dispositivos de almacenamiento Luego de haber creado un sistema de archivos en una partición como lo explicamos en el tema 102 debemos montarlo en (asociarlo con) un directorio del sistema a fin de utilizarlo. Contamos con dos alternativas para hacerlo: 1) De manera permanente implica agregar al dispositivo (junto con las opciones de montaje correspondientes) en el archivo /etc/fstab. Esto causará que el dispositivo esté disponible cuando se inicia el sistema y durante su uso, salvo que se lo desmonte explícitamente. El archivo /etc/fstab contiene información sobre los sistemas de archivos que deben montarse al inicio del sistema. Cada línea en el archivo posee los siguientes 6 campos: El primer campo representa el dispositivo de almacenamiento (local o remoto) que debe montarse. Puede especificarse mediante el identificador de dispositivo (por ejemplo, /dev/sdb1) o mediante la etiqueta asignada al momento de crear el sistema de archivos (LABEL=ALUMNO, por ejemplo), o mediante el UUID (identificador único) del sistema de archivos (lo cual puede averiguarse 136

mediante el comando blkid). Más adelante mostraremos cómo obtener estos datos. El segundo campo muestra el punto de montaje en el que se anexará el sistema de archivos. El tercer campo especifica el tipo de sistema de archivos (ext4, xfs, etc.) El cuarto campo indica las opciones de montaje (separadas por comas) El quinto campo es hoy una opción en desuso. El sexto campo indica si el sistema de archivos debe chequearse al iniciar el sistema y en qué orden: para /root siempre debería ser 1, mientras que para el resto siempre 2. Si este campo no existe o es 0, significa que no se debe chequear el sistema de archivos durante el arranque. Dicho esto, podemos montar /dev/sdb1 en /mnt/pruebas (este directorio debe existir previamente) como ext4 mediante cualquiera de las tres maneras siguientes, agregando la línea correspondiente a /etc/fstab. Podemos hacerlo especificando el identificador de dispositivo: /dev/sdb1 /mnt/pruebas ext4 defaults 0 2 También es posible llevar a cabo la misma operación mediante la etiqueta asignada al sistema de archivos: LABEL=ALUMNO /mnt/pruebas ext4 defaults 0 2 o mediante el UUID. Averigüémoslo primero con blkid: blkid /dev/sdb1 Si el comando anterior indica UUID=“4fe8b010-f521-4f85-bab2d9e86da35f13”, en /etc/fstab deberemos usarlo sin comillas de la siguiente manera: UUID=4fe8b010-f521-4f85-bab2-d9e86da35f13 /mnt/pruebas ext4 defaults 0 2 137

2) Bajo demanda significa montar el dispositivo cuando se lo necesite, luego de lo cual se prescinde de su uso y se lo desmonta. Si lo que deseamos es montar un dispositivo bajo demanda (de uso por tiempo limitado), utilizaremos el comando mount con la opción -t para indicar el tipo de sistema de archivos a utilizar seguido del identificador del dispositivo, LABEL, o UUID, y el punto de montaje. Por ejemplo, si quisiéramos montar el sistema de archivos con LABEL=ALUMNO en /mnt/pruebas, podemos utilizar el siguiente comando: mount -t ext4 LABEL=ALUMNO /mnt/pruebas Para desmontar un sistema de archivos emplearemos el comando umount seguido por el punto de montaje: umount /mnt/pruebas Es importante aclarar que el proceso anterior fallará si estamos situados dentro del punto de montaje. Para salir del mismo primero, podemos utilizar cd y presionar Enter (a fin de volver a nuestro directorio personal) y luego ejecutar el comando anterior. Integridad de sistemas de archivos A la hora de monitorear el estado de un sistema de archivos, lo primero a tener en cuenta es el espacio disponible en los mismos. Desde la línea de comandos podemos llevar a cabo esta tarea mediante el uso de df: ver los porcentajes de uso y disponibles por sistema de archivos. Entre sus opciones disponibles podemos resaltar -h y -t, las cuales se utilizan para mostrar los resultados en unidades más fáciles de entender que el byte (KiB, MiB, GiB, etc) y para especificar el tipo de sistema de archivos que queremos chequear. Por ejemplo, 138

df -h -t ext4 nos mostrará el uso de los sistemas de archivos ext4 que estén montados en nuestro sistema, como podemos apreciar en la imagen:

Como podemos observar arriba, también podemos obtener la misma información sobre un sistema de archivos específico omitiendo la opción -t y reemplazándola con el punto de montaje asociado. du: mostrar el espacio utilizado. Si ejecutamos este comando sin opciones nos devolverá el espacio utilizado (en bytes) en el directorio actual de forma total y por subdirectorios. Para mostrar las cantidades en forma más amigable también podemos emplear la opción -h como en el caso de df. Sin embargo, es probable que en ocasiones nos interese únicamente el total en vez del aporte individual de cada subdirectorio. Esto es posible al agregar la opción -s como vemos a continuación: du -h du -sh 139

Para ver la misma información para otro directorio distinto al actual deberemos acompañar los comandos anteriores con la ruta a dicho directorio. Cuando tenemos que realizar esta inspección en ubicaciones del sistema que contengan un gran número de subdirectorios podemos especificar la cantidad máxima de niveles a analizar con la opción -max-depth seguida de un número. Por ejemplo, du -h --max-depth=1 /etc/systemd solamente nos mostrará la información requerida para aquellos subdirectorios se encuentren un nivel por debajo de /etc/systemd como vemos en la siguiente imagen:

Para apreciar la utilidad de --max-depth, en la imagen anterior se incluye la estructura de /etc/systemd en forma de árbol. Al utilizar --max-depth=1, solamente se muestra el resumen de 140

/etc/systemd, /etc/systemd/network, /etc/systemd/system, /etc/systemd/user. Por otro lado, los subdirectorios /etc/systemd/system se omiten del reporte.

y de

Cada archivo o directorio posee una referencia asociada llamada ínodo que apunta a su ubicación dentro del sistema de archivos y que contiene información sobre el archivo o directorio en particular (permisos, propietarios, tipo de archivo, etc).

Una característica distintiva de un sistema de archivos es la cantidad máxima de ínodos que puede contener. Esto, junto con el tamaño del almacenamiento subyacente, limita la cantidad de archivos y directorios que dicho sistema de archivos puede alojar. Los siguientes comandos nos serán de utilidad a la hora de examinar el uso de ínodos en nuestro sistema. El primero de ellos nos permitirá ver el ínodo asociado a cada archivo y subdirectorio del directorio actual. Por otro lado, el segundo nos informará la cantidad de ínodos utilizados y disponibles por sistema de archivos, mientras que el tercero nos será de utilidad si deseamos ver esta misma información desglosada por directorios, como observamos en la imagen a continuación: ls -i df -i du --inodes /etc --summarize

141

En el último comando deberemos omitir la opción -summarize si nos interesa ver el detalle por cada subdirectorio de /etc. Es importante tener en cuenta que si se agotan los ínodos disponibles se producirán errores al tratar de utilizar un sistema de archivos, inclusive cuando se disponga de espacio de almacenamiento. La causa más probable detrás de este problema suele ser la acumulación indiscriminada de archivos de pequeño tamaño. Chequear y reparar errores Cuando a pesar de haber realizado backups de nuestro sistema observamos errores al intentar montar un dispositivo, deberemos recurrir a un chequeo del sistema de archivos para investigarlos y solucionarlos de ser posible. Para realizar la tarea de reparar errores en un sistema de archivos en Linux, contamos con una herramienta muy útil llamada fsck, y en esta sección explicaremos su uso. Si el sistema se apaga abruptamente (por ejemplo, durante un corte de energía, y no se cuenta con UPSs), pueden ocasionarse errores en un sistema de archivos si se están realizando operaciones de lectura y escritura sobre el mismo. Para intentar solucionarlos, debemos desmontar el sistema de archivos (si el mismo se encuentra montado) o no intentar hacerlo.

La regla de oro para el uso de fsck es la siguiente: nunca debemos intentar ejecutarlo sobre un dispositivo que se encuentre montado a fin de minimizar el riesgo de pérdida o corrupción de archivos. Supongamos que deseamos chequear el sistema de archivos en /dev/vg00/vol01. Luego de desmontarlo umount /dev/vg00/vol01 corremos fsck utilizando utilizando la ruta al dispositivo: fsck /dev/vg00/vol01 142

Si el comando anterior nos advirtiera sobre la presencia de errores, podemos ejecutarlo nuevamente con la opción -y para repararlos automáticamente: fsck -y /dev/vg00/vol01

Para simular la reparación en vez de ejecutarla, podemos utilizar la opción -n en vez de -y. Sin embargo, lo más sabio es anticiparnos a los errores e indicar que nuestros sistemas de archivos se chequeen periódicamente o luego de una cierta cantidad de reinicios del equipo. Si nuestro sistema se reinicia periódicamente (digamos que se trata de un equipo de escritorio que usamos durante unas ocho horas al día y apagamos al finalizar nuestras tareas), nos convendrá agendar un chequeo del sistema de archivos una vez cada vez que el dispositivo haya sido montado 30 veces. Para hacerlo, utilizaremos el comando tune2fs seguido por la opción -c, la cantidad umbral de montajes, y el identificador del dispositivo que deseamos chequear (/ dev/vg00/vol01 en el siguiente ejemplo): tune2fs -c 30 /dev/vg00/vol01 En cambio, en el caso de que estemos tratando con un sistema de archivos que montamos y desmontamos de manera frecuente, querremos efectuar el chequeo luego de una cantidad dada de veces que haya sido montado. En el siguiente ejemplo también utilizaremos tune2fs con la opción -i seguida por el argumento Xy, donde y puede ser: d: días (de days) w: semanas (de weeks) m: meses (de months) mientras que X es un número entero que se usa para especificar la cantidad de tiempo. 143

Por ejemplo, tune2fs -i 2w /dev/vg00/vol01 hará que /dev/vg00/vol01 sea chequeado cada dos semanas. Para sistemas de archivos xfs, contamos con dos utilidades llamadas xfs_check y xfs_repair para chequear y reparar errores. La misma precaución que mencionamos antes aplica en este caso: no debemos intentar realizar estas acciones sobre dispositivos que estén montados actualmente. Permisos y propiedad de archivos La mantención de cuentas de usuarios y grupos en Linux es una tarea fundamental de cualquier administrador de sistemas. En esta sección mostraremos cómo crear, modificar, y eliminar cuentas de usuario y grupos de nuestro sistema Linux. Crear usuarios y grupos Para crear el usuario fulano, agregando el nombre completo (Fulano De Tal) en el campo comentario utilizaremos el comando useradd con la opción -c. Por defecto, también se creará el directorio home del usuario en /home/fulano y se le asignará /bin/bash como shell: useradd fulano -c "Fulano De Tal" Si queremos cambiar el shell del usuario fulano a /bin/sh, el contenido del campo comentario a Fulano De Tal actualizado y el directorio home a /mnt/homes/fulano (el cual debe ser creado primero) usaremos usermod la opción -s (cambiar shell) y -d (cambiar home), junto con -c (actualizar comentario): mkdir -p /mnt/homes/fulano usermod -s /bin/sh -c "Fulano De Tal actualizado" d /mnt/homes/fulano fulano Para asignar una contraseña inicial al usuario fulano emplearemos el comando passwd. Luego de iniciar sesión por primera vez, él podrá 144

cambiarla utilizando el mismo comando. passwd fulano Una vez que hemos creado la cuenta de usuario, el comando chage con su opción -l nos permitirá ver información detallada sobre el período de validez de esta (por defecto, una cuenta de usuario nunca expira) y de la contraseña. La salida del comando chage -l fulano debería ser similar a la mostrada en la imagen:

Ahora veremos cómo editar la cuenta de fulano de tal manera que la cuenta expire (-E) el 31 de diciembre de 2018, y mientras tanto se le permita cambiarla solamente (-m) una vez por semana (7 días). Cada contraseña será válida como máximo (-M) durante 60 días, y 3 días antes se le debe enviar un recordatorio para que la modifique (-W). Finalmente, 5 días después de la fecha de vencimiento de la contraseña se procederá a bloquear (-I) la cuenta: chage -E 2018-12-31 -m 7 -M 60 -W 3 -I 5 fulano Veamos los datos de la cuenta nuevamente para verificar los cambios: 145

Por seguridad, podemos forzar a que fulano cambie su contraseña la próxima vez que inicie sesión: chage -d 0 fulano Recordemos que como medida de seguridad es una buena idea requerir un cambio de clave cada cierto período de tiempo. Administración de grupos En Linux, los grupos constituyen el primer método de control de acceso a los recursos del sistema. Cuando se crea una cuenta de usuario, también se crea un grupo con el mismo nombre (conocido como grupo primario para dicha cuenta) y ambos son vinculados. Para crear un nuevo grupo adicional llamado migrupo utilizaremos el comando groupadd. A continuación, para agregar a fulano al mismo volveremos a usar el comando usermod con sus opciones -a (de append) y -G (group) seguidos por el nombre del grupo y el usuario que deseamos agregar al mismo: groupadd migrupo usermod -a -G migrupo fulano Para ver la lista de grupos a los que pertenece un determinado usuario podemos utilizar los comandos groups o id, seguido por el nombre de usuario: id fulano groups fulano

146

Para eliminar una cuenta de usuario de un grupo suplementario tenemos dos alternativas: Utilizar usermod -G seguido de los grupos en los que se necesita que el usuario permanezca activo, y finalmente del nombre del usuario en cuestión. Esta opción puede tornarse un tanto engorrosa si el usuario pertenece a muchos grupos suplementarios. Emplear gpasswd -d seguido del nombre de usuario y del grupo del que se desea eliminarlo. En otras palabras, teniendo en cuenta que a este punto el usuario fulano pertenece a los grupos que se muestran en la imagen anterior, cualquiera de los dos comandos siguientes lo quitarán del grupo migrupo: usermod -G fulano fulano gpasswd -d fulano migrupo Si lo que queremos es borrar la cuenta del usuario fulano, junto con su directorio home y la cola de correos haremos lo siguiente (en el caso de que nos interese eliminar la cuenta, pero conservando dichos recursos deberemos omitir la opción -r): userdel -r fulano Para borrar un grupo dado (migrupo en el siguiente ejemplo), utilizaremos groupdel migrupo Permisos en Linux Veamos cómo crear un directorio llamado midirectorio en /tmp y mostrar el usuario y el grupo dueños de este: mkdir /tmp/midirectorio ls -ld /tmp/midirectorio 147

Si queremos cambiar el usuario dueño o el grupo propietario de /tmp/ midirectorio, podemos utilizar el comando chown de la siguiente manera: chown fulano: /tmp/midirectorio # Cambiar usuario dueño chown :fulano /tmp/midirectorio # Cambiar grupo propietario (similar a chgrp) chown fulano:fulano /tmp/midirectorio # Cambiar ambos Otra opción para cambiar el grupo propietario es chgrp: chgrp fulano /tmp/midirectorio Ahora cambiemos el grupo propietario a fulano: chgrp fulano /tmp/midirectorio En la imagen siguiente podemos apreciar el resultado de haber cambiado el grupo propietario del directorio a fulano:

En la primera columna de la salida de ls -ld podemos ver los permisos asignados para el usuario dueño y el grupo propietario de /tmp/midirectorio, y para el resto de los usuarios del sistema: La primera letra (d) indica que /tmp/midirectorio es un directorio. Si bien nosotros sabíamos esto por haberlo creado previamente, en ocasiones tendremos que identificar si el recurso indicado es un directorio, un archivo común y corriente (indicado por un – en vez de la d), o un enlace simbólico (por la presencia de una l), entre otros. A continuación, la primera terna de caracteres (rwx) indica que el 148

usuario dueño posee permisos de lectura (r), escritura (w), y ejecución (x) sobre el directorio. La segunda terna (r-x) indica que los miembros del grupo propietario poseen permisos de lectura y ejecución (pero no de escritura). Finalmente, la última terna muestra que todos los demás usuarios del sistema poseen los mismos permisos que en el caso anterior. Veamos la forma en que los permisos rwx nos permiten restringir el acceso a los recursos del sistema: El permiso de lectura (r) es requerido para poder listar el contenido del directorio. El permiso de escritura (w) permite que se agreguen archivos al directorio, que se renombren otros ya existentes, que se borren dichos archivos, o que se modifiquen los atributos del directorio. De todas formas, hay que tener en cuenta que para poder realizar estas operaciones es necesario el permiso de ejecución sobre el mismo directorio. El permiso de ejecución (x) es necesario para ingresar al directorio y acceder a archivos y a subdirectorios que se encuentren dentro del mismo. Para dar permisos de escritura sobre /tmp/midirectorio a miembros del grupo fulano deberemos usar el comando chmod seguido de la opción g (grupo), del signo + y del permiso que deseemos asignar (en este caso w): chmod g+w /tmp/midirectorio Si quisiéramos asignar (o quitar, en cuyo caso utilizaremos el signo –) más de un permiso a la vez podemos hacerlo fácilmente. Por ejemplo, veamos cómo quitar los permisos de lectura y ejecución para el resto de los usuarios del sistema (simbolizados por la opción o, de others en inglés): chmod o-rx /tmp/midirectorio 149

Por último, para asignar o quitar permisos al usuario dueño, utilizamos la opción u (del inglés user), mientras que si queremos otorgar o remover un permiso en particular para todos usaremos la opción a (de all). De esta manera, el siguiente comando activa los permisos de ejecución sobre /tmp/midirectorio para todos los usuarios: chmod a+x /tmp/midirectorio Adicionalmente, podemos indicar un determinado permiso en forma octal en vez de r, w, o x. Para encontrar ayuda en la conversión podemos consultar cualquier calculador de permisos en línea. Máscaras de archivos y directorios Por lo general, cada vez que utilizamos una cuenta de usuario limitado para crear un archivo o un directorio, se establecen por defecto los permisos rw-rw-r– (octal 664) y rwxrwxr-x (octal 775), respectivamente. Si lo hacemos como root, los permisos se setean como rw-r–r– (octal 644) y rwxr-xr-x (octal 755). Esto es resultado de aplicar lo que se conoce como máscaras de archivos y directorios, cuyos valores podemos visualizar y cambiar mediante el comando umask.

En esencia, el permiso resultante surge de restar las máscaras del usuario a los valores 666 para archivos y 777 para directorios. El siguiente ejemplo nos permitirá ilustrar: 1. Ejecutemos umask para un usuario cualquiera. 2. Al crear un archivo llamado file1 los permisos resultarán de la resta entre 666 y la máscara de creación de archivos (002 para un usuario común y 022 para root). 3. Cuando añadamos un directorio dir1, los permisos resultarán de la resta entre 777 y la máscara de creación de directorios (mismos valores que en el caso anterior). 4. Eliminemos file1 y dir1. 5. Establezcamos un nuevo valor para las máscaras (000 en el siguiente ejemplo) y volvamos a ejecutar los comandos de los pasos 150

1 a 3. umask touch file1 mkdir dir1 ls -ld file1 dir1 rm file1 rmdir dir1 umask 000 touch file1 mkdir dir1 ls -ld file1 dir1 En la imagen siguiente podemos ver el resultado de los comandos anteriores:

Los cambios que hicimos en la máscara permanecerán hasta que reiniciemos el equipo o que efectuemos otra modificación. Si deseamos aplicarlos de manera permanente deberemos colocar el comando umask junto con el valor deseado en el archivo de inicialización de nuestra shell. 151

es que los primeros poseen su propio número de ínodo, mientras que los últimos comparten este número con el archivo propiamente dicho. Estándar de jerarquía del sistema de archivos (FHS) En cuestión de no muchos años, Linux pasó de ser un proyecto estrictamente personal de Linus Torvalds hasta convertirse en un esfuerzo mundial que incluye a miles de colaboradores de todo el mundo y a formar un sistema operativo robusto junto con las utilidades del proyecto GNU. Con este crecimiento se hizo necesaria la estandarización del sistema de archivos en Linux. De esta manera nació lo que hoy conocemos como el estándar de jerarquía del sistema de archivos (más comúnmente llamado FHS, del inglés Filesystem Hierarchy Standard), el cual es un documento que detalla el propósito de los directorios en un sistema Linux moderno. Las ventajas principales de contar con un estándar a la hora de organizar los directorios en un sistema Linux son las siguientes: Los desarrolladores de aplicaciones no necesitan modificarlas para funcionar en distintas distribuciones. Los usuarios que llegan a aprender la estructura de directorios en una distribución no necesitan de mayores esfuerzos para utilizar otra. Salvo algunos casos puntuales en los que diferentes distribuciones agregan sus propios subdirectorios, no hay gran diferencia estructural de una distribución a otra. Algunos de los directorios más interesantes son los siguientes. La descripción fue tomada de la versión 3 del FHS, actualizada por última vez en junio de 2015: /bin contiene los archivos ejecutables correspondientes a los comandos que todos los usuarios del sistema pueden utilizar. En /boot podemos encontrar los archivos necesarios para el arranque del sistema (incluyendo el propio kernel). 152

Dentro de /dev aparecen los identificadores de dispositivos, tanto de almacenamiento (/dev/sda1, por ejemplo) como de entrada / salida (por ejemplo, /dev/tty1, la cual representa la primera terminal de usuario). /home es el lugar donde encontraremos los directorios personales de cada usuario (/home/alumno, por ejemplo, para el usuario alumno). /root es el directorio personal del usuario root. No debe confundirse con el directorio raíz (/), del cual /root es hijo. En /sbin podemos hallar los archivos ejecutables correspondientes a las utilidades necesarias para arrancar el sistema y para solucionar problemas en o mantener el sistema de archivos. /usr contiene toda una jerarquía propiamente dicha dentro de sí, y mayormente consiste en otros archivos de comandos (en modo de sólo lectura) accesibles por todos los usuarios del sistema. /var contiene subdirectorios y archivos variables, sujetos a cambios permanentes (por ejemplo, el registro de logs del sistema o del funcionamiento de procesos). Los directorios mencionados anteriormente contienen otros subdirectorios, cuyo propósito también se explica en el FHS. La introducción que hemos compartido en este capítulo es un buen comienzo para poder entender el estándar de jerarquía del sistema de archivos.

153