Tutorial de programación con las
RPC de Sun/ONC
Integrante:
William Sánchez C.
Aplicaciones Distribuidas
Junio 2025
Tutorial de programación con las RPC de Sun/ONC
Introducción:
El mecanismo de llamada a procedimiento remoto (RCP) de Open Network Computing (ONC)
de Sun es ampliamente utilizado, especialmente gracias al éxito de productos como NFS que se
basan en él, aunque tiene algunas limitaciones en comparación con otros mecanismos de RPC,
es una excelente opción para familiarizarse con la programación de servicios usando RPCs,
esto se debe a que cualquier sistema UNIX, incluyendo el usado en esta guía, incluye todo el
software necesario para su uso en su instalación básica.
Esta guía tiene como objetivo proporcionar un tutorial rápido y practico para la programación
en este entorno, utilizando ejemplos de dificultad progresiva que nos permitirán comprender
los conceptos clave sin necesidad de consultar documentación adicional.
Preparacion del entorno
Para lograr ejecutar los casos que expondremos a continuación, necesitamos asegurarnos que
nuestra maquina virtual tenga todo lo necesario y para esta guía usaremos Ubuntu Server
24.04.
Instalación de prerrequisitos
Para empezar, necesitamos tener nuestro Ubuntu Server actualizado e instalar algunos
paquetes necesarios ejecutando los siguientes comandos como usuario root:
sudo apt update
1
sudo apt install build-essential rpcbind manpages-dev
Configuración de Permisos RPC
Por defecto, algunas distribuciones de Linux pueden restringir a los usuarios normales la
capacidad de iniciar servidores que usan este tipo de RPC. Para los propósitos de este taller
vamos a ejecutarlo como root.
sudo rpcbind -i
Para este caso ya se encuentra activo, de no ser así ejecutaremos:
sudo systemctl start rcpbind
Copiaremos los archivos de los casos a nuestro servidor y procederemos a descomprimir de
modo que tendremos los archivos listos.
Habilitar rpcbind para usuarios:
Por defecto, en algunas distribuciones de Linux, solo el superusuario puede registrar servicios
RPC. Para permitir que tu usuario normal lo haga, necesitas reconfigurar rpcbind.
sudo systemctl stop rpcbind.socket
sudo systemctl stop rpcbind
2
sudo rpcbind -i -w
¡Listo! El entorno está preparado. Ahora vamos a trabajar con cada caso.
Paso 1: Primer Caso - Servicios con Tipos de Datos Simples
Aquí desarrollarás un servidor con dos funciones: una que obtiene el UID (identificador de
usuario) a partir de un nombre de usuario, y otra que hace lo contrario.
Entendiendo la versión errónea (caso_1_erroneo)
Vamos a compilar y ejecutar la primera versión que tiene un error intencional.
Navega al directorio y genera los resguardos (stubs):
En la terminal, ve al directorio IDL del primer caso. IDL (Interface Definition Language) es
donde se define el servicio.
cd caso_1_erroneo/idl
Usa rpcgen para procesar el archivo .x y generar el código base para el cliente y el servidor.
rpcgen rusuarios.x
ls -l
Verás archivos nuevos como rusuarios_clnt.c, rusuarios_svc.c y rusuarios.h.
Compilación del servidor y el cliente:
Ahora, vamos a compilar todo, los archivos de ejemplo suelen incluir un Makefile que
simplifica esto. Navega a la raíz de caso_1_erroneo y usa el comando make.
cd ..
make
3
Ejecutar el servidor:
Una vez compilado, iniciamos el servidor y lo ejecutaremos en segundo plano (con &) para
poder seguir usando la misma terminal.
./servidor/rusuariosd &
Verifica que el servicio se ha registrado correctamente con rpcinfo.
rpcinfo -p
Deberías ver tu programa (666666666) listado con sus versiones y puertos.
Prueba de los clientes y reproduce el error:
Primero, prueba el cliente getuid, que debería funcionar bien.
./cliente/getuid localhost root
Ahora, prueba el cliente getname. Lo probaremos con un UID que existe (como 0 para root) y
uno que no existe (como 877).
./cliente/getname localhost 0 877 1001
Aquí verás el error, el programa mostrará error en la llamada: RPC: Can't decode result para el
UID inexistente, esto ocurre porque el servidor retorna NULL, que no es un string válido en
XDR.
4
La solución (caso1_OK)
Ahora, vamos a ver la versión corregida, el error se soluciona asegurándose de que el servidor
siempre devuelva un string válido, en caso de no encontrar el usuario, devuelve un string vacío
("") en lugar de NULL, además, usa strdup para evitar problemas de sobreescritura de memoria
estática y xdr_free para gestionar la memoria dinámica correctamente.
Compilamos y ejecutamos la versión corregida:
Ve al directorio caso1_OK y compila el código.
cd ../caso1_OK/
cd ..
gcc -o servidor/rusuariosd servidor/rusuariosd.c idl/rusuarios_svc.c -Iidl
-I/usr/include/tirpc -ltirpc
Ejecutamos el nuevo servidor. (Si el servidor anterior sigue corriendo, puedes detenerlo con el
comando fg y luego Ctrl+C, o killall rusuariosd).
./servidor/rusuariosd &
Prueba el cliente getname corregido:
Ejecutamos el mismo comando que antes.
./cliente/getname localhost 0 877 1001
Ahora, en lugar del error, el programa simplemente indicará que el UID no existe, porque el
cliente fue modificado para comprobar la longitud del string devuelto (strlen(*result) != 0).
5
Paso 2: Segundo Caso - Tipos de Datos Compuestos
En este caso, se amplía el servicio para manejar structs, permitiendo enviar y recibir múltiples
valores en una sola llamada RPC. Se añaden dos funciones: una que comprueba si dos UIDs
pertenecen al mismo grupo y otra que devuelve el UID y el GID (Group ID) de un usuario.
Generamos los resguardos y compilamos:
Navegamos al directorio caso_2 y compilamos el proyecto.
cd ../caso_2
Al ejecutar rpcgen sobre el nuevo IDL, notarás que se genera un archivo rusuarios_xdr.c. Este
archivo contiene el código para serializar (marshalling) y deserializar (unmarshalling) las
nuevas estructuras (struct) que definiste.
Ejecuta el servidor y los nuevos clientes:
Inicia el servidor de este caso (recuerda detener el anterior).
./servidor/rusuariosd &
Prueba el nuevo cliente getugid que obtiene el UID y GID
./cliente/getugid localhost root nobody
Prueba el cliente agrupa_por_gids, que es más complejo y utiliza varios de los servicios
disponibles.
./cliente/agrupa_por_gids localhost 0 1001 1
6
Paso 3: Tercer Caso - Uso de union de XDR
Este caso presenta una forma más elegante de manejar los errores, como un UID no
encontrado. En lugar de devolver un valor "mágico" (como un string vacío), se utiliza el tipo
union de XDR, que es similar a un registro variante. Permite que el valor de retorno tenga
diferentes tipos según un campo discriminante (en este caso, un booleano existe).
Compila y ejecuta:
Ve al directorio caso_3 y compila.
cd ../caso_3
Inicia el servidor.
./servidor/rusuariosd &
Prueba el cliente modificado:
Ejecuta el cliente getname de este caso.
./cliente/getname localhost 0 877 1001
El código del cliente ahora comprueba result->existe para saber si la llamada fue exitosa antes
de intentar acceder al nombre del usuario. La salida será limpia, sin errores de RPC.
Paso 4: Cuarto Caso - Vectores de Tamaño Variable
Aquí aprenderás a manejar colecciones de datos cuyo tamaño no se conoce de antemano,
como una lista de UIDs. XDR proporciona un tipo para vectores de longitud variable (<>) que es
más eficiente que usar un array de tamaño fijo.
7
Compila y ejecuta:
Ve al directorio caso_4 y compila.
cd ../caso_4
Inicia el servidor.
./servidor/rusuariosd &
Prueba el cliente para vectores:
El nuevo cliente getgids envía una lista de UIDs al servidor y recibe una lista de GIDs.
./cliente/getgids localhost 0 1001 877
En el código, verás que el vector se representa en C como una struct con un campo para la
longitud (uids_len) y un puntero a los datos (uids_val).