CUESTIÓN

Al ejecutar make sobre la práctica de esta sesión, ocurre lo siguiente:
/usuarios/gyermo/PRIVADO/SO/PRACTS/SESION1$ make
        c89 -c sesion1.c
cc: "sesion1.c", line 5: error 1588: "UID_NO_CHANGE" undefined.
*** Error exit code 1

Stop.               

PRIMERA APROXIMACIÓN

El error nos los está dando el compilador (cc). El error está localizado en el fichero sesion1.c, en su línea quinta. Nos dice que UID_NO_CHANGE no está definido.

Si leemos la página de manual de chown y similares, nos dice:
 RESUMEN
      #include <unistd.h>
      int chown(const char *path, uid_t owner, gid_t group);
      int lchown(const char *path, uid_t owner, gid_t group);
      int fchown(int fildes, uid_t owner, gid_t group);

 DESCRIPCIÓN
      La llamada al sistema chown() cambia la pertenencia a un usuario y a
      un grupo de un fichero.  La variable path apunta al nombre de la ruta
      de acceso del fichero.  chown() asigna al ID de propietario y al ID de
      grupo del fichero los valores numéricos contenidos en las variables
      owner y group respectivamente.  El valor de UID_NO_CHANGE o
      GID_NO_CHANGE puede especificarse en owner o group para dejar
      inalterado el ID de propietario o el ID de grupo del fichero,
[...]
En principio, no tenía por qué dar este error. UID_NO_CHANGE, al estar todo en mayúsculas, tiene toda la pinta de ser una macro. Las macros suelen estar definidas en los ficheros de cabecera. De estar definida en algún sitio, será en el fichero de cabecera correspondiente a la función que estamos usando: unistd.h.

Los ficheros de cabecera que se referencian en un fichero en C entre signos de menor (<) y mayor (>), se encuentran dentro del directorio /usr/include. Miremos, pues, el contenido del fichero /usr/include/unistd.h:
/* @(#) $Revision: ../hdr/unistd.h@@/main/r11ros/0 $ */
#ifndef _UNISTD_INCLUDED
#define _UNISTD_INCLUDED

/*
 * Unistd.h
 * symbolic constants and structures which are used
 * for support of the /usr/group standard.
 */

#include <sys/unistd.h>

#endif /* _UNISTD_INCLUDED */
El fichero primero comprueba que no ha sido incluído antes para, de ser este el caso, no volverse a incluir. Esto es muy típico de los ficheros de cabecera del sistema. Si no ha sido incluído antes, incluye el fichero situado en /usr/include/sys/unistd.h. Vamos a ver si este fichero contiene la definición de la macro:
/usr/include/sys$ grep UID_NO_CHANGE unistd.h
/usr/include/sys$ 
Mala suerte. Busquemos en todos los ficheros de cabecera debajo del subdirectorio /usr/include, a ver si alguno contiene la definición de la macro:
/usr/include$ find . -name "*.h" -exec grep UID_NO_CHANGE '{}' \; -print
#define VX_AT_ISUID(vap)        ((vap)->va_uid != UID_NO_CHANGE)
./sys/fs/vx_hpux.h
#  define UID_NO_CHANGE  ((uid_t) -1)  /* for chown(2) and setresuid(2) */
./sys/types.h
        ( N != UID_NO_CHANGE && N != O )
./sys/vnode.h 
Parece que la definición de la macro, que además es -1, está en el fichero /usr/include/sys/types.h. Probablemente este fichero sea incluído desde /usr/include/sys/unistd.h. En ese fichero, encontramos la definición de la macro:
[...]
#ifdef _INCLUDE_HPUX_SOURCE
   typedef unsigned char        u_char;    /* Try to avoid using these */
   typedef unsigned short       u_short;   /* Try to avoid using these */
   typedef unsigned int         u_int;     /* Try to avoid using these */
[...]
/* These probably should be moved to some other header */

#  define UID_NO_CHANGE  ((uid_t) -1)  /* for chown(2) and setresuid(2) */
#  define GID_NO_CHANGE  ((gid_t) -1)  /* for chown(2) and setresgid(2) */
[...]
#    endif /* __cplusplus */
#  endif /* not _KERNEL */

#endif /* _INCLUDE_HPUX_SOURCE */
[...]
Nos damos cuenta inmediatamente que tiene que estar definida la macro _INCLUDE_POSIX_SOURCE para que pueda estar definida la que nos está dando el error. Por lo tanto, bastará con que añadamos al programa que estamos haciendo antes de los #include la definición de esta macro para que las cosas funcionen: #define _INCLUDE_HPUX_SOURCE. ¿Afectará esto al comportamiento del programa cuando le llevemos a LINUX?

SOLUCIÓN

¿Tendremos que estar haciendo esto cada vez que no encuentre una macro o la cabecera de una función? Esperemos que no. Podemos seguir investigando un poco. Si le pedimos a tejo que nos encuentre dónde está definida la macro _INCLUDE_POSIX_SOURCE, puede que hallemos por qué ocurre esto:
/usr/include$ find . -name "*.h" -exec grep "define .*_INCLUDE_POSIX_SOURCE" '{}' \; -print
#  define _INCLUDE_POSIX_SOURCE
#    define _INCLUDE_POSIX_SOURCE
#      define _INCLUDE_POSIX_SOURCE
#        define _INCLUDE_POSIX_SOURCE
#            define _INCLUDE_POSIX_SOURCE
#    define  _INCLUDE_POSIX_SOURCE
./sys/stdsyms.h
#  define _INCLUDE_POSIX_SOURCE
./utmp.h 
Nos encuentra su definición en dos ficheros. Nos decantamos por el primero, /usr/include/sys/stdsyms.h. Antes de examinar su contenido, podemos probar a ver si hay una página de manual del fichero y, en efecto, la hay:

 stdsyms(5)                                                       stdsyms(5)

 NAME
      stdsyms - description of HP-UX header file organization

 DESCRIPTION
      HP-UX header files are organized in a manner that allows for only a
      subset of the symbols available in that header file to be visible to
      an application that conforms to a specific standard.  The ANSI-C,
[...]
En esta página se explica el porqué del asunto. El compilador de C que viene por defecto en tejo (cc) no es un compilador ANSI C. Esto significa que, por ejemplo, no admite prototipos de funciones. Como nos interesa trabajar con el estándar ANSI, usamos el compilador c89.

Las llamadas al sistema que usamos en nuestros programas se basan en los estándares que vimos en clase: POSIX, AES, etc. El apaartado ESTÁNDARES CUMPLIDOS de la página de manual de chown dice:
 ESTÁNDARES CUMPLIDOS
      chown(): AES, SVID2, SVID3, XPG2, XPG3, XPG4, FIPS 151-2, POSIX.1

      fchown(): AES, SVID3
Pues bien, cuando se usa el compilador de ANSI C, hay que especificarle los estándares que queremos que cumpla. De todos, el estándar más general de tejo es el HPUX. Para que funcione bien la compilación de las prácticas, es necesario pedir ese estándar, definiendo la macro _HPUX_SOURCE al principio de nuestros programas. Esto se puede hacer de tres maneras:
  1. Incluyendo al comienzo de todos los ficheros fuente (.c) la línea #define _HPUX_SOURCE.
  2. Compilando todos nuestros programas con la opción del compilador -D_HPUX_SOURCE.
  3. Definiendo una variable de entorno en el fichero .profile que haga lo del punto anterior de un modo automático: export CCOPTS="-D_HPUX_SOURCE".

© 2000 Guillermo González Talaván.