The system passwords and other information are stored in the /etc/passwd file (except in the case of shadow passwords, where the passwords are contained within another root-readable file). The file itself has one record per line, with color delimited fields. Fortunately, there are a bunch of library functions in <pwd.h> to read these records for you. Don't read them "by hand" or attempt to roll your own ones of these--you will only reduce portability.
For a given user ID (UID), return a
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 */ };
The pw_gecos field is a comment field which contains comma-delimited stuff. Here on Linux, my real name is "Beej!", my office is "Mars", followed by office phone and home phone:
pw_gecos = "Beej!,Mars,(415)845-2913,(916)331-8838,"
The finger and chfn programs know how to order these fields.
A UID is just an int; on my system I am UID 501, so I could call this function with:
struct passwd *mine; mine = getpwuid(501);
This returns a pointer to a static structure so results of one call will be overwritten by the results of the next unless you copy the struct.
Getting a password entry by UID is kind of a pain, generally. It would be much nicer to get it by username:
struct passwd *mine; mine = getpwnam("beej");
Just like getpwuid(), see?
Sometimes you want to scan through the password file an entry at a time looking for God-knows-what and don't know a specific UID or user name. Maybe you want to know everyone in a certain group or something like that.
This function will automatically open the password file the first time it is called and will return the first entry. Subsequent calls will return subsequent entries in the password file. Finally, it will return NULL when the end of the file is reached.
Resets the file pointer used by getpwent() to the beginning of the file. It's a good idea to call this before using getpwent().
Closes the password file when you're done with it and frees up the allocated file pointers. Call this when you're done.
The following example prints all the user names from the password file:
#include <stdio.h> #include <stdlib.h> #include <pwd.h> #include <sys/types.h> main() { struct passwd *ent; setpwent(); while((ent = getpwent()) != NULL) printf("%s\n", ent->pw_name); endpwent(); }
Both of these functions work similarly to their getpwnam() and
getpwuid() counterparts. They return, for a given GID or group
name, a
struct group { char *gr_name; /* group name */ char *gr_passwd; /* group password */ gid_t gr_gid; /* group id */ char **gr_mem; /* group members */ };
The gr_mem field contains the names of the people listed for that group in the group file. For people who choke on double indirection, here's an example that reads the info for my "doom" group:
#include <stdio.h> #include <stdlib.h> #include <grp.h> #include <sys/types.h> main() { struct group *ent; if ((ent = getgrnam("doom")) == NULL) { printf("bad group\n"); exit(1); } printf("gr_name = \"%s\"\n", ent->gr_name); printf("gr_passwd = \"%s\"\n", ent->gr_passwd); printf("gr_gid = %d\n", ent->gr_gid); while(*(ent->gr_mem) != NULL) printf(" \"%s\"\n", *((ent->gr_mem)++)); }
The output is:
gr_name = "doom" gr_passwd = "" gr_gid = 101 "beej" "becca" "bapper"
These work just like the getpwent() function, except they operate on the group file. If you ever want to read through the group file an entry at a time, these functions are for you.
Returns a list of groups for the current UID in list. The argument size should be set to the size of the list array. The function returns the actual number of groups that were inserted into the array.
A broad example shows you which groups you're in (using getgrgid() to get the groups' names):
#include <stdio.h> #include <stdlib.h> #include <grp.h> #include <sys/types.h> #include <errno.h> main() { struct group *ent; gid_t gids[50]; int i,n; if ((n = getgroups(50, gids)) == -1) { perror("getgroups"); exit(1); } printf("%d groups:\n", n); for(i = 0; i < n; i++) { if ((ent = getgrgid(gids[i])) == NULL) { fprintf(stderr, "no such group %d\n", gids[i]); exit(1); } printf(" %d %s\n", gids[i], ent->gr_name); } }
The output for me is the following:
2 groups: 100 users 101 doom
Remember the Unix "who" command? It dumps information similar to the following:
beej tty1 Aug 17 16:20 beej ttyp1 Aug 17 16:20 (:0.0)
That is, who you are, where you are logged in at (which terminal), when you logged in, and where you logged in from (if not local).
All of the above information (and more) is stored in a file called "/etc/utmp" and is accessable through a slew of widely implemented but not POSIX-compliant functions defined in <utmp.h>.
These are all setup functions for reading the utmp file, and perform similar functions as their counterparts for /etc/passwd and /etc/group. The new one is utmpname(), which allows you to set the name of the utmp file to open. (Defaults to /etc/utmp, generally.)
Call utmpname() first (if you want to), then setutent() to rewind the file to the beginning. When you're through reading the file (below), call endutent() to clean up.
This function reads the next entry in the utmp file into a static structure and returns a pointer to it. (It returns NULL on EOF.) The structure (on Linux) is as follows (you should check your local man pages for the proper struct for your platform, or look it up in /usr/include/utmp.h):
struct utmp { short ut_type; /* type of login */ pid_t ut_pid; /* pid of login-process */ char ut_line[UT_LINESIZE]; /* devicename of tty -"/dev/" */ char ut_id[4]; /* inittab id */ time_t ut_time; /* login time */ char ut_user[UT_NAMESIZE]; /* username, not null-term */ char ut_host[UT_HOSTSIZE]; /* hostname for remote login... */ long ut_addr; /* IP addr of remote host */ };
Some of the entries might not be null-terminated (like ut_user here), so be careful.
One of the more interesting fields is the ut_type field--it contains the type of the utmp entry we're looking at. You might be most interested in the case where ut_type is equal to USER_PROCESS, since this is what "who" looks for; check the man page for a more complete list of values.
When you're looking for a specific ut_type, use this function. By loading this field with a value in your struct, then passing it to getutid(), the function will return the first information in the utmp file that matches your requested ut_type.
This variation searches through the utmp file for ut_type==USER_PROCESS or ut_type==LOGIN_PROCESS an returns entries where the ut_line matches the ut->ut_line passed into the function.
$ uname -a Linux frog 2.0.29 #2 Wed Mar 12 11:31:31 PST 1997 i586
As you might have guessed, there is a Unix system call by the same name.
struct utsname { char sysname[9]; /* OS name */ char nodename[9]; /* system name */ char release[9]; /* OS release */ char version[9]; /* OS release version */ char machine[9]; /* machine type */ };
(All these arrays are declared as 65-length under Linux--your mileage may vary.)
I can mimic the output of "
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/utsname.h> main() { struct utsname buf; if (uname(&buf) == -1) { perror("uname"); exit(1); } printf("%s %s %s ", buf.sysname, buf.nodename, buf.release); printf("%s %s\n", buf.version, buf.machine); }
There are two types of time variables: 1) time_t, which records
the number of seconds since Epoch (it's usually a long), and 2)
struct tm { int tm_sec; /* seconds */ int tm_min; /* minutes */ int tm_hour; /* hours */ int tm_mday; /* day of the month */ int tm_mon; /* month */ int tm_year; /* year */ int tm_wday; /* day of the week */ int tm_yday; /* day in the year */ int tm_isdst; /* daylight saving time */ };
Confusion reigns when people see that some functions use different time types than others. To make matters even more interesting, there are multiple functions to convert from one type to another.
This function returns the time since Epoch. If t is not NULL, it also assigns it to the time_t to which t points.
Both of these functions convert a time_t into a
To change the timezone, use the tzset() function.
This function goes the other way: from
This function is just like ctime(), except that it takes a
This is the end-all function for printing times. You pass it a string
that will hold the final result, the maximum length of that string, a
format specifier that will tell it how to print the time, and a
Check the man page for the individual format specifiers, but here's a sample:
time_t t1; char str[100]; time(&t1); strftime(str, 100, "%A, %B %d", localtime(&t1)); printf("%s\n", str);
The above example will print something like:
Monday, August 18
As you can imagine, there are format specifiers for just about every date-related thing you can think of.
If you're root, you can set the system time with this call.
You probably won't ever use these, but here goes.
Ok, so I lied: there's another time structure. Welcome to
struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ }; struct timezone { int tz_minuteswest; /* minutes west of Greenwich */ int tz_dsttime; /* type of dst correction */ };
These will be loaded properly once the call to gettimeofday() returns. Note that this is a BSD function and might not be supported under all flavors of Unix. Linux implements it, of course.
Also, if you're root, you can call the very similar settimeofday() to set the time.