Métricas Web
Consejos para el desarrollo en C en sistemas UNIX
Noviembre 30th, 2005 - [Enlace local]
Hace poco me tuve que enfrentar al desarrollo de un pequeño servidor de
"servicios" en UNIX (un programa similar a un servidor web). Os pongo
aquí algunas observaciones y consejos que tuve en cuenta en el momento
del desarrollo, por si os pueden ayudar.
- Gestión del log de la aplicación:
Crear un buffer de línea para el log:
setvbuf(access_log, (char *) NULL, _IOLBF, 0); (acces_log es el
descriptor del fichero de log que abrimos siempre con los flags de
concatenación)
Con este buffer nos aseguramos que en cuanto se llene una línea esta se escriba inmediatamente al fichero.
Podemos redirigir la salida estándar de errores al log de error mediante dup2 (duplicación de descriptores de fichero)
dup2(error_log, STDERR_FILENO) Todo lo que valla al STDERR_FILENO (la
salida de error) irá a nuestro log cuyo descriptor es error_log
También lo podemos hacer para la salida estandar mediante: STDOUT_FILENO
Otro truco, si no queremos que el programa imprima nada por las salidas
estándar, es abrir el fichero /dev/null asignándole, por ejemplo, el
descriptor dev_null y hacer lo siguiente:
dup2(dev_null, 1); dup2(dev_null, 2);
Para escribir en el log conviene utilizar fputs antes que fprintf ya
que en sistemas SOLARIS si escribís algo NULL podéis tener problemas
con fprintf.
También podemos poner el flag Close on exec a TRUE:
fcntl(acces_log, F_SETFD, 1)
... si vamos a utilizar cualquier función de la familia exec, de esta forma no se heredan los descriptores de fichero abiertos.
- Lanzar el programa como un demonio (si está destinado a ese fin).
Podemos utilizar fork() de la siguiente forma al inicio del proceso para que se nos ponga en background.
if (fork())
exit(0) // terminamos el proceso padre
setsid(); // El hijo ahora no tiene terminal
asociado y es el nuevo lider del grupo de procesos
if (fork())
exit(0) // terminamos el proceso padre
Gracias a Jim por avisar en los comentarios que faltaba el segundo fork
- Cuestiones de seguridad.
Nada más iniciar el programa podemos crearle una jaula chroot mediante la función:
chroot(/directorio/aplicacion)
De esta forma el proceso tomará como directorio "/" el
/directorio/aplicación y no podrá salir de dicho directorio para leer o
modificar otros ficheros.
Podemos cambiar el uid del proceso
a uno sin privilegios mediante setuid. Si hemos lanzado el proceso con
privilegios de root, por ejemplo, y nos interesa poder volver a ser
root cuando queramos podemos utilizar seteuid.
Por ejemplo:
seteuid(852)
abrir un fichero o hacer algo
seteuid(0) // volvemos a ser root
Si hacemos un seteuid antes de un fork, el proceso hijo tendrá el uid indicado por el seteuid.
Establecer la máscara de creación de ficheros: mediante la función umask definimos los permisos con los que se crearán los nuevos ficheros.
- Capturar las señales que lleguen al programa.
Mediante la biblioteca sigaction podemos capturar
todas las señales (exceptor SIGKILL o SIGSTOP) y asignarles un
controlador. Esto es útil para poder cerrar los descriptores de
ficheros, indicar en los logs que se está reiniciando, abortando el
programa o realizar cualquier acción.
- Fijar los límites de consumo del programa.
Mediante la llamada al sistema setrlimit(); podemos
fijar límites de consumo al proceso. Por ejemplo, el número máximo de
descriptores de fichero que puede abrir (útil si trabajamos con
sockets), también podemos limitar el uso de cpu etc... Para más
información, este link.
- Obtener los parámetros de entrada al programa.
Os recomiendo utilizar la función getopt. En bulma tenéis un excelente tutorial para ello.
- Autoconf y Automake.
Con estas herramientas podemos hacer que nuestra aplicación siga el proceso de instalación estándar de GNU. Además, nos chequean el sistema antes de la instalación para ver que no falta ninguna biblioteca además de crearnos los makefiles para compilarlo. Os recomiendo leer Autobook
Seguro que me dejo algún truquito más en el tintero, agradezco comentarios y críticas (positivas o negativas) y ,por supuesto, mención a cualquier error que haya cometido en el artículo.