Working with User Information in Linux

21 April, 2009

At certain times, it can be of advantage to have access to a user’s information, be it their home directory, their full name, their password, their system hardware, etc. This sort of information can be used to automatically customize programs according to the name of the user that started them or certain makeshift security methods.

Available User Information
If we take a look at /usr/include/pwd.h, we’ll see the following structure (with genuine comments):

/* The passwd structure.  */
struct passwd
  char *pw_name;        /* Username.  */
  char *pw_passwd;        /* 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.  */

In Linux and most Unix systems, pw_gecos is the user’s real name, although you may encounter pw_comment every now and then.

Saying Hello
Let’s write a program that says “hello” to the user and tells them what their home directory is. We’ll need to include the following headers:

#include <sys/types.h>    // defines special types
#include <pwd.h>    // defines the passwd structure
#include <stdio.h>    // standard I/O

Now, let’s write the main function and define some basic variables:

int main ()
    uid_t uid = getuid();
    struct passwd *user_info;
    user_info = getpwuid(uid);

The uid_t type was defined in <sys/types.h> and getuid() is defined in unistd.h. This function is declared as uid_t getuid(void); and it returns the user identity (UID) of the user that started the process.geteuid() can be use to return the user’s effective identity (EUID – the user’s second UID that defines what resources processes begun by the user has permission to access. [1 We then create a pointer to the passwd structure to be able to access the members denoted above.

The getpwuid() method is defined in <pwd.h> and gets the password file entry given a certain UID – in this case, that of the user that started the process. A password entry can also be returned based on a user’s login name; in this case, getpwnam(const char *name) is used.

Now, do the following to get the user’s real name and their home directory:

    printf("Hello, %s!\n", user_info->pw_gecos);
    printf("Your home directory is %s.\n", user_info->pw_dir);

Don’t forget the -> notation. It is very common in certain frameworks, most notably Qt. Remember that a->b is equivalent to (*a).b .

We can now wrap up the program and compile it.

    return 0;

Here are two different results:

$ ./a.out
Hello, Patrick Braga!
Your home directory is /home/patrick.

# ./a.out
Hello, root!
Your home directory is /root.

Getting System Information

Sometimes, especially in applications that include networking capabilities, it is useful to know the name of the OS a user is running, the release, and the version. One can also find out the name of the host and the type of hardware it’s running on (i386, i686, etc). With this information, an application can be optimized to run more properly on the specified OS or on the specified hardware type.

#include <unistd.h>
#include <sys/utsname.h>

The <unistd.h> file includes two important functions: int gethostname (char *name, size_t namelen) and long gethostid(void). <sys/utsname.h> includes the uname function and the returned utsname structure.

Here are the most important members of the utsname struct. with original comments intact:

/* Structure describing the system and machine.  */
struct utsname
    /* Name of the implementation of the operating system.  */
    char sysname[_UTSNAME_SYSNAME_LENGTH];

    /* Name of this node on the network.  */
    char nodename[_UTSNAME_NODENAME_LENGTH];

    /* Current release level of this implementation.  */
    char release[_UTSNAME_RELEASE_LENGTH];

    /* Current version level of this release.  */
    char version[_UTSNAME_VERSION_LENGTH];

    /* Name of the hardware type the system is running on.  */
    char machine[_UTSNAME_MACHINE_LENGTH];

    // ...

Let’s expand the above program to include the following statements (don’t forget to include the necessary headers):

    struct utsname uts;
    uname(&uts); // this is the function that returns the uts struct

    printf("Your computer is running %s on %s hardware.\n",
        uts.sysname, uts.machine);
    printf("You are currently running kernel v%s.\n", uts.release);

Here are two different results:

$ ./a.out
Hello, Patrick Braga!
Your home directory is /home/patrick.
Your computer is running Linux on i686 hardware.
You are currently running kernel v2.6.27.21-170.2.56.fc10.i686.

# ./a.out
Hello, root!
Your home directory is /root.
Your computer is running Linux on i686 hardware.
You are currently running kernel v2.6.27.21-170.2.56.fc10.i686.

Note that the system and hardware information remain intact throughout the users – the user-specific information remains the same.

Using Finger

It’s great to be able to access this information in a C program, but what about in a shell script, remotely logged into a shell, or whenever you need it quickly from the command line? The finger command allows us to do that. There are four options for finger:

  1. -s displays the user’s login name, real name, idl and login times, office location, and office phone numbers.
  2. -l (default) displays everything shown in -s and any information in certain hidden files in the user’s home directory
  3. -p displays -l information without information from said certain hidden files in ~/
  4. -m prevents matching user names to their login names

The mentioned hidden files are (in the home directory ~/) .nofinger (denies the user’s existence to requests outside the local host), .project (a one-line file of whatever project the user is working on), .plan (a multi-line file of the user’s plan of things to do), and .pgkey. Remember, in Unix systems, it’s perfectly all right that files carry no extension.

Needless to say, the possibilities for using this sort of information are vast, from making a user’s experience more enjoyable to fine-tuning an application’s services to fit the user’s hardware.