Chapter Outline
The UNIX Environment
Passing arguments to programs
Environment variables
Finding out what the time is
Temorary files
Getting information about the user and the host computer
Causing and configuring log messages
Discovering the limits imposed by the system
Lecture Notes
The UNIX Environment
When we write a program for UNIX we have to take into account that the program will run in a multitasking environment.
We will be looking at:
![]()
Program Arguments
When a UNIX progam written in C runs it starts at the function main.
For UNIX programs, main is declared as,
![]()
where argc is a count of the program arguments and argv is an array of character strings representing the arguments themselves.
You might als see UNIX programs declaring main as:
![]()
Whenever the operating system starts a new program, the parameters argc and argv are set up and passed to main.
For example, if, in the shell, we give the command,
$ myprog left right 'and center'
the program myprog will be started at main. with parameters:argc: 4
arggv: {"myprog", "left", "right", "and center"}Command line arguments are useful for passing information to programs. They can be used to set flags or switches.
For example, the sort program takes a -r switch to reverse the normal sort order:
$ sort -r file
Command line options can be confussing. For example:$ tar cvfB /tmp/file.tar 1024
If this isn't bad enough, some programs make the option +x perform the opposite function to -x.
$ dd if=/dev/fd0 of=/tmp/file.dd bs=18k
$ ls -lstr
$ ls -l -s -t -rTry It Out - Program Arguments
Her's a program, args.c, that examines its arguments:
![]()
When we run this program, it just prints out its arguments and detects options.
Other options might also be defined:
$ args -i -lr 'hi there' -f fred.c
argument 0: args
option: i
option: lr
argument 3: hi three
option: f
argument 5: fred.cHow It Works
The program simply uses the argument count, argc, to set up a loop to example all of the program arguments. It detects options by looking for an initial dash..
getopt
Linux gives us the getopt facility, which supports the use of options with and without vlaues and is simple to use.
![]()
The getopt function takes the argc and argv paramters as passed to the program's main function and an options specifier string.
The getopts command in bash performs a very similar function. The call,
![]()
would be used to handle our example above.
We call getopt repeatedly to get each option in turn. It has the following behavior.
![]()
Try It Out - getopt
Let's use getopt for our example and call the new program argopt.c:
![]()
Now, when we run the program, we see that all the command line arguments are handled automatically:
$ argopt -i -lr 'hi there' -f fred.c -q
option: i
option: l
filename: fred.c
argopt: illegal option-q
unknown option: q
argument: hi thereHow It Works
The program repeatly calls getopt to process option arguments until none remain, when getopt returns -1.
Environment Variables
Environment variables can be used to control the behavior of shell scripts and other programs.
Each user has an environment varible, HOME, that defines his home directory, the default starting place for his or her session.
We can examine environment variables from the shell prompt:
$ echo $HOME
A C program may gain access to environment variables using the putenv and getenv functions.
/home/neil![]()
Try It Out - getenv and puten
The following program is to print out value of any environment variable we choose.
1. The first few lines after the declaration of main ensure that the program, environ.c, has been called correctly.
![]()
2. That done, we fetch the value of the variable from the environment, using getenv:
![]()
3. We set the variable to the value of the second argument if any, by constructing a string of the form name=value and then calling putenv:
![]()
4. Finally, we discover the new value of the varible by calling getenv once again"
![]()
When we run this program, we can see and set environment varibles:
$ environ HOME
Variable Home has value /home/neil
$ environ FRED
Variable FRED has no value
$ environ FRED hello
Variable FRED has no value
Calling putenv with: FRED=hello
New value of FRED is hello
$ environ FRED
Variable FRED has no valueUse of Environment Variables
Programs often use environment variables to alter the way they work.
User can set their default environment variables via a .profile or by specifying variables on the shell command line. For example:
$ environ FRED
The shell takes initial varible assignments as temporary changes to environment variables. Each user could then specify his or her own default, or use a shell command to set it on a run-by-run basis:
Variable Fred has no value
$ FRED=hello environ FRED
Variable FRED has value hello$ CDDB=mycds; export CDDB
or $ CDDB=mycds cdapp
$ cdapp![]()
The environ Variable
The program environment is made up of strings of the form name=value. This array of strings is made available to programs directly via the rnviron variable which is declard as:
![]()
Try It Out - environ
Here's a program, showenv.c, that uses the environ variable to print out the environment varibles:
![]()
When we run this program on a Linux system we get the following output, which has been abbreviated a little:
$ showenv
HOSTNAME=tilde.provider.com
LOGNAME=neil
MAIL=/var/spool/mail/neil
TERM=console
HOSTTYPE=i386
PATH=/usr/local/bin:/bin:usr/bin:
HOME=/usr/neil
LS_OPTIONS=-8bit-color=tty -F -T 0
SHELL=/bin/bash
PS1=\h:\w\$
PS2=> OSTYPE=LinuxHow It Works
This program iterates through the environ variable, a null-terminated array of strings, to print out the whole environment.
Time and Date
It can be useful for a program to be able to determine the time and date.
![]()
Times are handled using a defined type, a time_t. The variable and functions for manipulating time values are in the header file time.h.
![]()
Try It Out - time
Here is a simple program, envtime.c to demonstrate the time function:
![]()
When we run this program, it prints the low-level time value every two seconds for 20 seconds.
![]()
How It Works
The program calls time with a null pointer argument, which returns the time and date as a number of seconds. The program sleeps for two seconds and repeates the call to time for a total of ten times.
The function difftime, that will calculate the difference in seconds between two time_t values and return it as a double.
![]()
The function gmtime breaks down a low-level time value into a structure containing more usual fields:
![]()
The structure tm is defined to contain at least the following members:
![]()
The range for tm_sec allows for the occasional leap second, or double leap second.
Try It Out - gmtime
Here's a program, gmtime.c, that prints out the current time and date using the tm structure and gmtime:
![]()
When we run this program, we get a good approximation of the time and date:
![]()
How It Works
The program calls time to get the low-level time value and then calls gmtime to convert this into a structure with useful time and date values. It then prints it out.
To see the local time, we need to use the function localtime instead.
![]()
To convet a broken-down tm structure into a raw time_t value, we can use the functiion mktime.
![]()
The functions asctime and ctime give a more friendly output:
![]()
The asctime function returns a string that represents the time and date given by the tm structure timeptr. The string returned has a format similar to:
![]()
It's always a fixed format, 26 characters long. The function ctime is equivalent to calling:
![]()
Try It Out - ctime
Let's see ctime in action, using the following code:
![]()
Compile and run the surprisingly named ctime.c and you should see:
$ ctime
The date is: Mon Nov 20 12:50:27 1995How It Works
The ctime.c program calls time to get the low level time value and lets ctime do all the hard work converting it to a readable string, which it then prints.
The strftime function gives more control to the formatting of time and date.
![]()
The format string is used to control the characters written to the string
The conversion specifiers include:
![]()
So, the usual date as given by the date program corresponds to a strftime format string of :
"%a %b %d %H:%M:%S %Y"
To help with reading dates, we can use the strptime function to convert a string to a tm structure.![]()
Try It Out - strftime and strptime
Have a look at the selection of conversion specifiers used in the following program:
![]()
When we compile and run this program, strftime.c, we get:
$ strftime
strftime gives: Monday 20 November, 02:58 PM
calling strptime with: Tue 4 December 1995, 17:53 will do fine
strptime consumed up to: will do fine
strptime gives:
date: 95/12/04
time: 17:53How It Works
The strftime program obtains the current local time by calling time and localtime. It then converts it to readable form. It then uses strptime to extract the raw time and date and print them.
Temporary Files
Often, programs will need to make use of temporary storage in the form of files. Temporary files need unique names.
A unique file name can be generated by the tmpnam function:
![]()
If the temporary file is to be used immediately, you can name it and oppen it a the same time using the tmpfile function.
![]()
Try It Out - tmpnam and tmpfile
Let's see these two functions in action:
![]()
When we compile and run this program, tmpnam.c, we can see the unique file name generated by tmpnam:
$ tmpnam:
Copyright © 2001 by James L. Fuller, all rights reserved.
Temporary file name is: /tmp/00386aaa
Opened a temporary file OKHow ItWorks
The program calls tmpnam to generate a unique file name for a temporary file. The tmpfile call creates and opens a temporary file at the same time.
Older versions of UNIX used mktemp and mkstemp to generate a unique file name for a temporary file.
![]()
User Information
All UNIX programs, with the notable exception of init, are started by other programs or by usrs.
Each user has a unique user identifier, known as a UID.
You can set up programs to run as if a different user had started them by having their setUID permission set.
The UID has its own type -- uid_t -- defined in sys/types.h. It's normally a small integer.
Normally, users usually have UID values larger than 100.
![]()
The getuid function returns the UId with which the program is associated.
The getlogin function returns the login name associated with the current user.
The system file, /etc/passwd, contains a database dealing with user accounts.
If we write a program that determines the UID of the who started it, we could extend it to look in the password file to find out the user's login name and full name.
A number of functions have been defined to provide a standard and effective programming interface to this user information:
![]()
The password database strucutre, passwd, defined in pwd.h includes the following members:
![]()
Try It Out - User Information
Here's a program, user.c, that extracts some user information from the password database:
![]()
It gives the following output, which may differ in minor respects between versions of UNIX:
![]()
How It Works
This program calls getuid to obtain the UID of the current user. This UID is used in getpwuid to obtain detailed password file information. It also shows how the user name root can be given to getpwnam to obtain user information.
![]()
To scan all the password file information, we can use the getpwent functions. This fetches successive file entries:
![]()
Other User Information Functions
User and group identifiers can be obtained by other, less commonly used Functions:
![]()
You should refer to the UNIX system manual pages for details on group identifiers and effective user identifiers.
Host Information
A program can establish some details bout the computer on which it's running.
The uname(1) command provides such information. uname(2) also exists as a system call toprovide the same information with a C program.
If the UNIX system has the networking component installed, we can obtain its network name very easily with the gethostname function:
![]()
The gethostname function writes the machine's network name into the
name.You can obtain more detailed information about the host computer from the uname system call:
![]()
The uname function writes host information into the structure pointed to by the name parameter.
The utsname structure, defined in sys/utsname.h, must contain at least these members:
![]()
Try It Out - Host Information
Here's a program, hostget.c, that extracts some host computer information:
![]()
It gives the following Linux-specific output:
![]()
How It Works
This program calls gethostname to obtain the network name of the host computer, tilde. More detailed information about the computer is returned by the call to uname.
![]()
Licensing
A unique identifier for each host computer may be available from the gethostid function.
![]()
Logging
Many applications need to record their activities, by writing to a log file.
Very often, these log messges are recorded in system files in a directory made available for that purpose, such as /usr/adm, or /var/log.
Here are some sample messages:
![]()
The UNIX specification provides an interface for all programs to produce logging messages, using the syslog function:
![]()
The syslog function sends a looging messge to the logging facility. Each message has a
priorty
agrument which is a bitwise OR of a severity level and a facility value.The severity levles in descending order of priority are:
![]()
Try It Out - syslog
In this program we try to open a non-existent file:
![]()
When we compile and run this program, syslog.c, we see no output, but the file /var/log/syslog now contains at the end the line:
Nov 21 17:56:00 tilde syslog: oops - No such file or directory
How It Works
In this program, we try to open a file that doesn't exists. When this fails, we call syslog.c to record the fact in the system logs.
Configuring Logs
Other functions used to alter the behavior of logging facilities are also defined in syslog.h. These are:
![]()
We can alter the way that our messages are presented by calling the openlog function. This allows us to set up a string, idnet that will be prepended to our log message.
The logopt parameter configures the behavior of future calls to syslog. It's a bitwise OR of zero or more of the following:
![]()
Try It Out - logmask
In this example we'll see logmask in action.
![]()
This program, logmask.c, produces no output, but on a Linux system at the end of /var/log/messages we should see the line:
Nov 21 18:19:43 tilde logmask[195]: informative messge, pid = 195
The file /var/log/debug should contain:Nov 21 18:19:43 tilde logmask[195]: debug message, should appear
How It Works
The program initializes the logging facility with its name, logmask and requests that log messages contain the process identifier.
logmask.c uses the getpid function, which is defined along with the closely related getppid as follow:s
![]()
Resources and Limits
Programs running on a UNIX system are subject to9 resource limitations.
The header file limits.h defines many manifest constants that represent the constraints imposed by the operating system. These include:
![]()
There will be many others.
The header fie sys/resource.h provides definitions for resource operations.
![]()
id_t is an integral type used for user and group identifiers.
The rusage structure, defined in sys/resource.h, is used to determine how much CPU time has been used by the current program. It must contain at least these members:
![]()
The timeval structure is defined in sys/time.h and contains fileds tv_sec and tv_usec representing seconds and microseconds respecitively.
The getrusage functions writes CPU time information to the rusage structure pointed to by the parameter r_usage. The who parameter can be one of the following constants:
![]()
The which parameter specifices how the who parameter is to be treated;
![]()
So, to determine the priority of the current process we might call:
![]()
The setpriority function allows a new priority to be set, if possible.
The general purpose structure, rlimit is used to describe resource limits which are read and set by getrlimit and setrlimit.
It's defined in sys/resource.h and has the following members:
![]()
There are a number of system resources that can be limited. These are specified by the resource parameter of the rlimit functions and are defined in sys/resource.h as:
![]()
Try It Out - Resource Limits
Here's a program, limits.c, that simulates a typical application. it also sets and breaks a resource limit.
1. Make the includes for all the functions we're going to be using in this program:
![]()
2. The void function writes a string to a temporary file 10000 times and then performs some arithmetic to generate load on the CPU:
![]()
3. The main function calls work and then uses the getrusage function to discover how much CPU time it has used. It displays this information on screen:
![]()
Next, it calls getpriority and getrlimits to find out its current priority and file size limits respectively:
![]()
Finally, we set a file size limit using setrlimits and call work again, which fails because it attempts to create too large a file:
![]()
When we run this program, we can see how much CPU resource is being consumed and the default priority at which the program is running.
Once a file size limit has been set, the program can't exceed it.
![]()
We can change the program priority by starting it with the nice command. This changes the priority to +10 and the program takes longer to execute:
![]()
How It Works
The limits program calls a function, work to simulate the actions of a typical program. The program then sets its file size limit to just 2K and tries again to perform some work. This time the work function fials as it can't create such a large temproary file.
![]()
Summary
In this chapter, we've looked at the UNIX environment and examined the conditions under which programs run..
CS 248 - UNIX Programming Web Site Menu
Information | Syllabus | Schedule | Online "Lectures" | Projects | Quizzes | Web Board