Lire /etc/passwd pour avoir des informations en C

Dans vos programmes C, il peut arriver d’avoir besoin d’informations concernant l’utilisateur du programme (Son $HOME pour y enregistrer des fichier par exemple).

Une manière simple (et portable) de procéder est d’utiliser les variables d’environnement. Par exemple : getenv.c

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("login: %s ", getenv("USER"));
printf("home: %s ", getenv("HOME"));
printf("shell: %s ", getenv("SHELL"));
return 0;
}

Ce qui doit donner quelque chose dans ce gout là :

$ gcc getenv.c
$ ./a.out
login: phil
home: /home/phil
shell: /usr/local/bin/zsh

Mais le problème c’est que ce qui est mis dans l’environnement par défaut diffère suivant les OS, et même suivant les distributions GNU/Linux ou BSD, ainsi il n’est pas rare de trouver LOGIN à la place de USER et autre subtilités. De plus cette méthode n’est efficace que si l’environnement est bien initialisé lors du lancement du processus. Lancez votre programme avec env -i pour lui envoyer un environnement vide :

$ env -i ./a.out
login: (null)
home: (null)
shell: (null)

Pas bon donc. Donc pour faire quelque chose de plus propre il est nécessaire sous UNIX de lire le fichier /etc/passwd, dieu merci les structures et les fonctions qui vont bien sont dans unistd.h et pwd.h

Voici la structure passwd qui définie complètement un utilisateur UNIX : man pwd.h

struct passwd {
char   *pw_name;       /* user name */
char   *pw_passwd;     /* user password */
uid_t   pw_uid;        /* user ID */
gid_t   pw_gid;        /* group ID */
char   *pw_gecos;      /* real name */
char   *pw_dir;        /* home directory */
char   *pw_shell;      /* shell program */
};

Donc il nous faut une fonction pour récupérer la structure qui correspond à notre utilisateur. En gros il y a deux fonctions qui font ça (de pwd.h) :

struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);

Donc en gros soit vous connaissez le login de l’utilisateur et vous utilisez la première. Soit vous connaissez l’uid de l’utilisateur et c’est la seconde. Pour avoir l’uid du processus courant il y a uid_t getuid(void); dans unistd.h

Donc voici un exemple avec getpwuid qui marchera à tous les coups (sauf en cas de graves problèmes dans le système) :

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
int main(void)
{
uid_t uid;
struct passwd *user;
uid = getuid();
user = getpwuid(uid);
printf("login: %s ", user->pw_name);
printf("home: %s ", user->pw_dir);
printf("shell: %s ", user->pw_shell);
return 0;
}

Compilation et lancement dans un environnement vide :

$ gcc users.c
$ env -i ./a.out

login: phil
home: /home/phil
shell: /usr/local/bin/zsh

Et voilà. Attention tout de même, si ce code arrive à se trouver dans une boucle il va lire à chaque fois le fichier /etc/passwd ce qui n’est pas une bonne idée. Donc il peut être utile de stocker ces informations (ces pointeurs) dans une structure globale ou bien de les mettre dans l’environement (putenv, setenv)

Enjoy et bon codes.