martes, 21 de junio de 2011

Como encontrar los uid duplicados en /etc/passwd

En cajas en las que continuamente se agregan y eliminan usuarios, no es raro encontrar algunos que ya sea a propósito o por accidente comparten el uid.(A veces son por completo entradas duplicadas)
A fin de poder tomar las decisiones necesarias (ya sea desde cambiar el uid de alguno de los usuarios, hasta la eliminación de aquellos duplicados) es buena práctica el examinar las líneas del archivo /etc/passwd.

A manera de ejemplo he creado dos usuarios con el mismo uid, en mi caja.
Ambos usuarios dummyuser y lameruser aunque con diferentes usernames comparten el mismo uid, por lo que a fines practicos se trata del mismo usuario.

Ahora, para llegar a encontrar las líneas duplicadas necesitamos:
a) Registrar cada uid diferente que se presente en el archivo /etc/passwd

b) En caso de que uid ya este registrado imprimirlo
c) Sería buena idea ordenar el listado por uid, para que las entradas con el mismo uid  se mantengan juntas
d) Agregar el nombre del equipo al listado

Interesante requerimiento ¿no es así? ¿Complicado? Para nada.
Para los puntos a-c usaremos la herramienta awk, que nos permite usar un archivo de entrada para su procesamiento usando su propio lenguaje de ordenes: AWK.
El archivo de usuarios y contraseñas /etc/passwd esta separado por el caracter (:) por lo que el primer parametro de nuestro comando awk sería: -F:
Para registrar cada uno de los uid creamos un arreglo, lo ideal es hacerlo antes de comenzar a leer cada linea del archivo /etc/passwd. Es decir al principio (BEGIN): BEGIN{a[NULL]=0;}
Luego, a fin de verificar que cada uid(el tercer campo por cierto) no se haya usado antes: if($3 in a )
Si el uid no se ha presentado antes será almacenado de lo contrario se imprimirá la línea.
Ahora para ordenarlo, con respecto al campo 3, evaluándolo de forma númerica y teniendo como delimitador el caracter: usamos el comando: sort -t: -k3 -n
Por ultimo agregamos el nombre del equipo a la salida con el comando sed.
¿Cómo queda todo junto?
¡En una sola línea, claro esta!
awk -F: 'BEGIN{a[NULL]=0;}{if($3 in a){print a[$3];print ;}else a[$3]=$0;} ' /etc/passwd | sort -t: -k3 -n | uniq | sed -e 's/^/'$(hostname)':/g'

Nuestro comando trabajando:

Como puede apreciarse.... funciona bastante bien.

miércoles, 16 de febrero de 2011

Usando una macro de excel para invocar putty

En ocasiones, por necesidades operativas tenemos que  loguearnos a una serie de servidores, para hacer x o y actividad. Para lo cual tenemos una hoja de excel parecida a la siguiente:

  
¿No sería mejor que al dar doble click en la celda con la dirección ip se invocara inmediatamente putty en esa ip?
Para conseguirlo necesitamos:
  1. Crear un libro de excel con capacidad de macros (.xlsm)
  2. Distinguir cuando se trata de una ip 
  3. Personalizar el evento de doble click para que haga la validación correspondiente
  4. Invocar putty
1. Guardamos nuestro libro de excel seleccionando el formato "Libro de Excel habilitado para macros (*.xlsm)". Presionamos ALT+F11 para invocar el entorno de desarrollo.
2. Distinguir cuando se trata una ip.
Dando doble click en Thisworkbook dentro del explorador de proyectos,

agregamos la siguiente función Boolean al libro
Private Function Is_IPV4_Address(ByVal pstrIp As String) As Boolean
   
    Dim lavarCompos As Variant
    Dim varCompo As Variant
    Dim intValue As Integer
    On Error Resume Next
    Is_IPV4_Address = False
    lavarCompos = Split(Trim(pstrIp), ".")
    If UBound(lavarCompos) = 3 Then
        For Each varCompo In lavarCompos
            intValue = CInt(varCompo)
            If intValue >= 0 And intValue <= 255 Then
                Is_IPV4_Address = True And CInt(lavarCompos(0)) > 0
            Else
                Is_IPV4_Address = False
                Exit For
            End If
        Next
    End If
End Function

Esta sencilla función simplemente dará un valor de verdadero cuando se le proporcione como argumento una cadena separable por puntos(.)  y que cada una de las cadenas entre los puntos sea un punto. Adicionalmente deben ser 4 ternas de numeros, es decir algo así como una dirección ip.

3. Personalizar el evento doble clic.
Basta con agregar un evento con la siguiente cabecera:
Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean
Es decir vamos a hacer nuestra validacion previamente al evento doble clic. Aqui es importante notar el parametro Cancel, ya que en caso de que el contenido de la celda en que se hace doble clic (el parametro Target) sea una dirección ip invocaremos nuestro codigo y cancelaremos la acción predeterminada del doble clic dandole el valor de True al parametro Cancel.

4. Invocar putty
Eso lo conseguimos invocando el procedimiento Shell asi:
Shell "C:\Archivos de Programa\putty\putty.exe " & Target.Text, vbNormalFocus
He de hacer notar que efectivamente la ruta de putty se encuentra en hard code. Sin embargo esto lo puede cambiar cada quien en la implementación de este ejemplo.

Así sin mas preambulos nuestro evento quedaría de la siguiente forma:
Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
    If Is_IPV4_Address(Trim(Target.Text)) Then
        Shell "C:\Archivos de Programa\putty\putty.exe " & Target.Text, vbNormalFocus
        Cancel = True
    End If
End Sub

Y se veria asi:



Pueden descargar el archivo aqui

martes, 25 de enero de 2011

Tip: Como remover los molestos ^M

En ocasiones ya sea porque hemos editado un archivo en windows o porque algún colega nos ha pasado ún código al tratar de ejecutarlo nos regresa el siguiente error:
Es más al revisar el archivo con vi nos encontramos con algo como esto:

Como podemos ver cada línea tiene un caracter ^M al final, lo que hace que el shell no pueda determinar correctamente el final de cada linea. Esto es debido a que Unix usa como separador de linea LF(\n) mientras que Windows/DOS usa CR/LF(\r\n). Por tanto basta con eliminar los caracteres extras de nuestro archivo,  aunque hay una herramienta de nombre dos2unix, esta no esta presente de forma estándar. Por lo que nos abocaremos a hacer una subrutina que cumpla esta misma función.
La parte funcional la implementaremos usando el programa tr de la siguiente forma:
tr -d '\r' < archivo_a_limpiar > archivo_limpio
Ahora bien una función en el interprete de comandos se puede implementar de la siguiente forma:
Nombre_Funcion()
{
     Comando_1
      Comando_2
       ...
      Comando_N

}

Llamaremos a nuestra funcion strip_cr, para que la misma pueda procesar un archivo sobre demanda, debemos especificarlo mediante un parametro. Los parámetros en una función shell se especifican como $1,$2,... $9 para el primero, el segundo y hasta el noveno parametro.
Por tanto la entrada de nuestro comando tr sería de la siguiente forma:
tr -d '\r' < $1
La salida del comando no puede ser el mismo archivo de entrada, por lo que ocupamos la variable especial $$ que almacena nuestro id de proceso. Así:
tr -d '\r' < $1 > $1.$$
Sin embargo tendríamos que consultar siempre cual es el archivo mas reciente, que se llame igual que el original mas un "." (mucho lío ¿no les parece?). En lugar de eso después de obtener la salida sustituimos el original con el nuevo archivo:
tr -d '\r' < $1 > $1.$$
mv $1.$$ $1
Al final nuestra subrutina quedaría así:
strip_cr()
{
  tr -d '\r' < $1 > $1.$$
  mv $1.$$ $1
}

y al ejecutarla el resultado seria el siguiente:

Que como podemos ver carece ya de los caracteres ^M que se visualizaban antes.

viernes, 21 de enero de 2011

Anatomia general de un script unix

Un script unix puede ser definido como un archivo de texto que cumple las siguientes condiciones:
a) Esta escrito en un lenguaje entendible por un "interprete de comandos" (shell) (ksh,csh,awk,perl, solo por poner algunos ejemplos)
b) Cada uno de sus registros/lineas esta terminado por un caracter de nueva linea (LF)
c) Tiene permisos de ejecución

Como mencioné en el primer punto el contenido de nuestro archivo es entendible o interpretable en tiempo real, por un programa que verifica la sintaxis de cada unos de los comandos introducidos, este es por omisión el que estemos usando en ese momento.
Para saber cual es nuestro shell actual, podemos usar el siguiente comando.
#echo $SHELL
Sin embargo, los shell (solo para hablar de aquellos del SO) no son completamente compatibles en su sintaxis. Existen dos formas de "forzar" la ejecución de nuestros scripts en un shell en particular.
a) Ejecutando el script en una sesión aparte de ese shell en particular. Ejemplo:
# /usr/bin/ksh myscript.sh
b) Especificandolo en la primera linea de nuestro script, que en lo personal me parece la mejor practica. Esto se hace iniciando el archivo con una línea que cumpla con la siguiente sintaxis:
#!interprete_de_comandos_en_el_archivo argumentos_de_linea_interprete_de_comandos
No necesariamente se limita a interpretes de comandos de SO, sino que se extiende a otros lenguaje como perl, awk, php, python, etc. Algunos ejemplos:
#!/usr/bin/ksh
#!/usr/bin/awk -F:
#!/usr/bin/perl
En el segundo ejemplo, usé un parametro de linea para el programa awk (en este caso uno que indica que el separador de campos será el caracter ":", es decir el incluir esa linea y asumiendo que nuestro script se llame miawk.awk, sería el equivalente a escribir el siguiente comando
#/usr/bin/awk -F: miawk.awk

En cuanto a la separación de cada línea por un LF, es el separador de línea por omisión en todos los clones unix, sin embargo cuando editamos algo en windows y luego lo pasamos a unix, puede ser que tengamos una serie de caracteres ^M. Esta situación la abordaremos en un artículo posterior.

Los permisos de ejecución se otorgan a un archivo mediante el comando chmod. Este comando nos permite otorgar(usando el simbolo +) o revocar(usando el simbolo -), permisos de lectura(r), escritura(w) y ejecucion(x). Por tanto si queremos otorgar permisos de ejecucion a un script de nombre primero.sh basta que introduzcamos el comando:
#chmod +x primero.sh
En este comando hemos omitido a quien se le otorgan los permisos por lo que se le otorgan a todas las categorías: usuario(u), grupo(g) y otros(o). Si por seguridad quisieramos que solo nuestro usuario pueda ejecutar este script, tendriamos que agregar una u:
#chmod +ux primero.sh
Despues solo quedaria la ejecución de nuestro script. Si por casualidad hemos incluido la ruta actual (.) en el path de nuestro usuario bastara con:
#primero.sh
Sin embargo, sobre todo para el usuario de root se considera una mala practica y un riesgo de seguridad, por lo que tenemos que cambiar la referencia a nuestro script:
#./primero.sh

miércoles, 5 de enero de 2011

Bienvenidos a sobreviviendo IT

Este nuevo blog al contrario de Ichi's killing corner, estará dedicado a compartir lo poco o mucho que vaya aprendiendo día con día.
Y quien sabe... a estas alturas dos meses despues no me acuerdo que programe Jajaja.
Así que tambien ira siendo mi tumbaburros.
Espero que a los visitantes les llegue a gustar.