Previous Section  < Day Day Up >  Next Section

1.6. The Environment and Inheritance

When you log on, the shell starts up and inherits a number of variables, I/O streams, and process characteristics from the /bin/login program that started it. In turn, if another shell is spawned (forked) from the login or parent shell, that child shell (subshell) will inherit certain characteristics from its parent. A subshell may be started for a number of reasons: for handling background processing, for handling groups of commands, or for executing scripts. The child shell inherits the environment of its parent. The environment consists of process permissions (who owns the process), the working directory, the file creation mask, special variables, open files, and signals.

1.6.1 Ownership and Permissions

When you log on, the shell is given an identity. It has a real user identification (UID), one or more real group identifications (GID), and an effective user identification and effective group identification (EUID and EGID). The EUID and EGID are initially the same as the real UID and GID. These ID numbers are found in the etc/passwd file and are used by the system to identify users and groups. The EUID and EGID determine what permissions a process has access to when reading, writing, or executing files. If the EUID of a process and the real UID of the owner of the file are the same, the process has the owner's access permissions for the file. If the EGID and real GID of a process are the same, the process has the owner's group privileges.

The real UID is the third entry in the /etc/passwd file. Its value is a positive integer that is associated with your login name. When you log on, the login shell is assigned the real UID and all processes spawned from the login shell inherit its permissions. Any process running with a UID of zero belongs to root (the superuser) and has root privileges. The real group identification, the GID, associates a group with your login name. It is found in the fourth field of the /etc/passwd file.

You can use the id command to see these values, as shown in Example 1.6.

Example 1.6.

1   $ id

    uid=502(ellie) gid=502(ellie)


The EUID and EGID can be changed to numbers assigned to a different owner. By changing the EUID (or EGID) to another owner, you can become the owner of a process that belongs to someone else. Programs that change the EUID or EGID to another owner are called setuid or setgid programs. The passwd program is an example of a setuid program that gives the user root privileges. This enables an ordinary user to change his password, and update the passwd file (accessible only to root) without calling for a system administrator. The user temporarily becomes the superuser, root, as long as the passwd program is running because his effective user ID is temporarily is set to root. Setuid programs are often sources for security holes. The shell allows you to create setuid scripts, and the shell itself may be a setuid program. (See Chapter 16, "The System Administrator and the Shell."

1.6.2 The File Creation Mask

When a file is created it is given a set of default permissions. These permissions are determined by the program creating the file. Child processes inherit a default mask from their parents. The user can change the mask for the shell by issuing the umask command at the prompt or by setting it in the shell's initialization files. The umask command is used to remove permissions from the existing mask.

Initially, the umask is 000, giving a directory 777 (rwxrwxrwx) permissions and a file 666 (rw-rw-rw-) permissions as the default. On most systems, the umask is assigned a value of 022 by the /bin/login program or the /etc/profile initialization file.

The umask value is subtracted from the default settings for both the directory and file permissions as follows:


 777 (Directory)                             666 (File)

–022 (umask value)                          –022 (umask value)

-------                                     ---------

 755                                         644

Result: drwxr-xr-x                          -rw-r--r--


After the umask is set, all directories and files created by this process are assigned the new default permissions. In this example, directories will be given read, write, and execute for the owner; read and execute for the group; and read and execute for the rest of the world (others). Any files created will be assigned read and write for the owner, and read for the group and others. To change permissions on individual directories and permissions, the chmod command is used.

1.6.3 Changing Permissions and Ownership

The chmod Command

The chmod command changes permissions on files and directories. Every UNIX/Linux file has a set of permissions associated with it to control who can read, write, or execute the file. There is one owner for every UNIX/Linux file and only the owner or the superuser can change the permissions on a file or directory. A group may have a number of members, and the owner of the file may change the group permissions on a file so that the group can enjoy special privileges. To see what permissions a file has, type at the shell prompt:


ls -l filename


A total of nine bits constitutes the permissions on a file. The first set of three bits controls the permissions of the owner of the file, the second set controls the permissions of the group, and the last set controls the permissions for everyone else. The permissions are stored in the mode field of the file's inode. The user must own the files to change permissions on them.[7]

[7] The caller's EUID must match the owner's UID of the file, or the owner must be superuser.

Table 1.1 illustrates the eight possible combinations of numbers used for changing permissions.

Table 1.1. Permission Modes

Decimal

Binary

Permissions

0

000

none

1

001

--x

2

010

-w-

3

011

-wx

4

100

r--

5

101

r-x

6

110

rw-

7

111

rwx

The symbolic notation for chmod is as follows: r = read; w = write; x = execute; u = user; g = group; o = others; a = all.


Example 1.7.

1   $ chmod 755 file

    $ ls –l file

    –rwxr–xr–x 1 ellie  0 Mar  7 12:52 file

2   $ chmod g+w file

    $ ls -l file

    –rwxrwxr-x  1 ellie  0 Mar 7 12:54 file

3   $ chmod go-rx file

    $ ls -l file

    –rwx-w---- 1 ellie  0 Mar 7 12:56 file

4   $ chmod a=r file

    $ ls -l file

    –r--r--r-- 1 ellie  0 Mar 7 12:59 file


EXPLANATION

  1. The first argument is the octal value 755. It turns on rwx for the user, r and x for the group, and others for file.

  2. In the symbolic form of chmod, write permission is added to the group.

  3. In the symbolic form of chmod, read and execute permissions are subtracted from the group and others.

  4. In the symbolic form of chmod, all are given only read permission. The = sign causes all permissions to be reset to the new value.

The chown Command

The chown command changes the owner and group on files and directories. If using Linux, only the superuser, root, can change ownership. If using UNIX, the owner of the file or the superuser can change the ownership. To see the usage and options for chown, check the man pages (UNIX) or use the chown command with the – –help (Linux) option as shown in Example 1.8. Example 1.9 demonstrates how to use chown.

Example 1.8.

(The Command Line)

# chown --help

Usage: chown [OPTION]... OWNER[.[GROUP]] FILE...

  or:  chown [OPTION]... .[GROUP] FILE...

Change the owner and/or group of each FILE to OWNER and/or GROUP.



  -c, --changes          be verbose whenever change occurs

  -h, --no-dereference   affect symbolic links instead of any referenced file

                         (available only on systems with lchown system call)

  -f, --silent, --quiet  suppress most error messages

  -R, --recursive        operate on files and directories recursively

  -v, --verbose          explain what is being done

      --help             display this help and exit

      --version          output version information and exit



Owner is unchanged if missing. Group is unchanged if missing, but changed to login group

 if implied by a period. A colon may replace the period.



Report bugs to fileutils-bugs@gnu.ai.mit.edu


Example 1.9.

(The Command Line)

1   $ ls -l filetest

    -rw-rw-r--   1 ellie    ellie           0 Jan 10 12:19 filetest

2   $ chown root filetest

    chown: filetest: Operation not permitted

3   $ su root

    Password:

4   # ls -l filetest

   -rw-rw-r--   1 ellie    ellie            0 Jan 10 12:19 filetest

5   # chown root filetest

6   # ls -l filetest

    -rw-rw-r--   1 root     ellie           0 Jan 10 12:19 filetest

7   # chown root:root filetest

8   # ls -l filetest

    -rw-rw-r--   1 root     root            0 Jan 10 12:19 filetest


EXPLANATION

  1. The user and group ownership of filetest is ellie.

  2. The chown command will only work if you are the superuser, i.e., user root.

  3. The user changes identity to root with the su command.

  4. The listing shows that user and group are ellie for filetest.

  5. Only the superuser can change the ownership of files and directories. Ownership of filetest is changed to root with the chown command. Group ownership is still ellie.

  6. Output of ls shows that root now owns filetest.

  7. The colon (or a dot) is used to indicate that owner root will now change the group ownership to root. The group name is listed after the colon. There can be no spaces.

  8. The user and group ownership for filetest now belongs to root.

1.6.4 The Working Directory

When you log on, you are given a working directory within the file system, called the home directory. The working directory is inherited by processes spawned from this shell. Any child process of this shell can change its own working directory, but the change will have no effect on the parent shell.

The cd command, used to change the working directory, is a shell built-in command. Each shell has its own copy of cd. A built-in command is executed directly by the shell as part of the shell's code; the shell does not perform the fork and exec system calls when executing built-in commands. If another shell (script) is forked from the parent shell, and the cd command is issued in the child shell, the directory will be changed in the child shell. When the child exits, the parent shell will be in the same directory it was in before the child started.

Example 1.10.

1   > cd /

2   > pwd

    /

3   > bash

4   $ cd /home

5   $ pwd

    /home

6   $ exit

7   > pwd

    /

    >


EXPLANATION

  1. The > prompt is a TC shell prompt. The cd command changes the directory to /. The cd command is built into the shell's internal code.

  2. The pwd command displays the present working directory, /.

  3. The bash shell is started.

  4. The cd command changes the directory to /home. The dollar sign ($) is the Bash prompt.

  5. The pwd command displays the present working directory, /home.

  6. The Bash shell is exited, returning to the TC shell.

  7. In the TC shell, the present working directory is still /. Each shell has its own copy of cd.

1.6.5 Variables

The shell can define two types of variables: local and environment. The variables contain information used for customizing the shell, and information required by other processes so that they will function properly. Local variables are private to the shell in which they are created and not passed on to any processes spawned from that shell. The built-in set command will display local variables for C shell and TC shell, and both local and environment variables for Bourne, Bash, and Korn shells. Environment variables, on the other hand, are passed from parent to child process, from child to grandchild, and so on. Some of the environment variables are inherited by the login shell from the /bin/login program. Others are created in the user initialization files, in scripts, or at the command line. If an environment variable is set in the child shell, it is not passed back to the parent. The shell env command will display the environment variables. Example 1.11 lists environment variables for a user running Solaris 5.9 with the Bash shell.

Example 1.11.

$ env

PWD=/home/ellie

TZ=US/Pacific

PAGER=less

HOSTNAME=artemis

LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib:/usr/dt/lib:/usr/openwin/lib

MANPATH=/usr/local/man:/usr/man:/usr/openwin/man:/opt/httpd/man

USER=ellie

MACHTYPE=sparc–sun–solaris2.9

TCL_LIBRARY=/usr/local/lib/tcl8.0

EDITOR=vi

LOGNAME=ellie

SHLVL=1

SHELL=/bin/bash

HOSTTYPE=sparc

OSTYPE=solaris2.9

HOME=/home/ellie

TERM=xterm

TK_LIBRARY=/usr/local/lib/tk8.0

PATH=/bin:/usr/bin:/usr/local/bin:/usr/local/etc:/usr/ccs/bin:/usr/etc:/usr/ucb:/usr/

   local/X11:/usr/openwin/bin:/etc:.

SSH_TTY=/dev/pts/10

_=/bin/env


1.6.6 Redirection and Pipes

File Descriptors

All I/O, including files, pipes, and sockets, is handled by the kernel via a mechanism called the file descriptor. A file descriptor is a small unsigned integer, an index into a file-descriptor table maintained by the kernel and used by the kernel to reference open files and I/O streams. Each process inherits its own file-descriptor table from its parent. The first three file descriptors, 0, 1, and 2, are assigned to your terminal. File descriptor 0 is standard input (stdin), 1 is standard output (stdout), and 2 is standard error (stderr). When you open a file, the next available descriptor is 3, and it will be assigned to the new file. If all the available file descriptors are in use,[8] a new file cannot be opened.

[8] See the built-in commands limit and ulimit.

Redirection

When a file descriptor is assigned to something other than a terminal, it is called I/O redirection. The shell performs redirection of output to a file by closing the standard output file descriptor, 1 (the terminal), and then assigning that descriptor to the file (see Figure 1.5). When redirecting standard input, the shell closes file descriptor 0 (the terminal) and assigns that descriptor to a file (see Figure 1.6). The Bourne, Bash, and Korn shells handle errors by assigning a file to file descriptor 2 (see Figure 1.7). The C and TC shells, on the other hand, go through a more complicated process to do the same thing (see Figure 1.8).

Example 1.12.

1   $ who > file

2   $ cat file1 file2 >> file3

3   $ mail tom < file

4   $ find / -name file -print 2> errors

5   % ( find / -name file -print > /dev/tty) >& errors


Figure 1.5. Redirection of standard output.


Figure 1.6. Redirection of standard input.


Figure 1.7. Redirection of standard error (Bourne, Bash, and Korn shells).


Figure 1.8. Redirection of standard error (C and TC shells).


EXPLANATION

  1. The output of the who command is redirected from the terminal to file. (All shells redirect output in this way.)

  2. The output from the cat command (concatenate file1 and file2) is appended to file3. (All shells redirect and append output in this way.)

  3. The input of file is redirected to the mail program; that is, user tom will be sent the contents of file. (All shells redirect input in this way.)

  4. Any errors from the find command are redirected to errors. Output goes to the terminal. (The Bourne, Bash, and Korn shells redirect errors this way.)

  5. Any errors from the find command are redirected to errors. Output is sent to the terminal. (The C and TC shells redirect errors this way. % is the C shell prompt.)

Pipes

A pipe is the oldest form of UNIX interprocess communication. A pipe allows processes to communicate with each other. It is a mechanism whereby the output of one command is sent as input to another command, with the limitation that data can only flow in one direction, normally between a parent and a child. The shell implements pipes by closing and opening file descriptors; however, instead of assigning the descriptors to a file, it assigns them to a pipe descriptor created with the pipe system call. After the parent creates the pipe file descriptors, it forks a child process for each command in the pipeline. By having each process manipulate the pipe descriptors, one will write to the pipe and the other will read from it.

To simplify things, a pipe is merely a kernel buffer from which both processes can share data, thus eliminating the need for intermediate temporary files. The output of one command is sent to the buffer, and when the buffer is full or the command has terminated, the command on the right-hand side of the pipe reads from the buffer. The kernel synchronizes the activities so that one process waits while the other reads from or writes to the buffer.

The syntax of the pipe command is


who | wc


In order to accomplish the same thing without a pipe, it would take three steps:


who > tempfile

wc tempfile

rm tempfile


With the pipe, the shell sends the output of the who command as input to the wc command; that is, the command on the left-hand side of the pipe writes to the pipe and the command on the right-hand side of the pipe reads from it (see Figure 1.9). You can tell if a command is a writer if it normally sends output to the screen when run at the command line. The ls, ps, and date commands are examples of writers. A reader is a command that waits for input from a file or from the keyboard or from a pipe. The sort, wc, and cat commands are examples of readers.

Figure 1.9. The pipe.


Figures 1.10 through 1.14 illustrate the steps for implementing the pipe.

Figure 1.10. The parent calls the pipe system call for setting up a pipeline.


Figure 1.14. The output of who is sent to the input of wc.


Figure 1.11. The parent forks two child processes, one for each command in the pipeline.


Figure 1.12. The first child is prepared to write to the pipe.


Figure 1.13. The second child is prepared to read input from the pipe.


1.6.7 The Shell and Signals

A signal sends a message to a process and normally causes the process to terminate, usually due to some unexpected event such as a hangup, bus error, or power failure, or by a program error such as illegal division by zero or an invalid memory reference. Signals can also be sent to a process by pressing certain key sequences. For example, you can send or deliver signals to a process by pressing the Break, Delete, Quit, or Stop keys, and all processes sharing the terminal are affected by the signal sent. You can kill a process with the kill command. By default, most signals terminate the program. Each process can take an action in response to a given signal:

  1. The signal can be ignored.

  2. The process can be stopped.

  3. The process can be continued.

  4. The signal can be caught by a function defined in the program.

The Bourne, Bash, and Korn shells allow you to handle signals coming into your program, (see "Trapping Signals" on page 378 for programming in the Bourne shell; "Trapping Signals" on page 716 for programming in the Korn shell; and "Trapping Signals" on page 935 for programing in the Bash shell) either by ignoring the signal, by specifying some action to be taken when a specified signal arrives, or by resetting the signal back to its default action. The C and TC shells are limited to handling ^C (Ctrl-C), the interrupt character.

Table 1.2 lists the standard signals that a process can use.

Table 1.2. Standard Signals[a]

Number

Name

Description

Action

0

EXIT

Shell exits

Termination

1

SIGHUP

Terminal has disconnected

Termination

2

SIGINT

User presses Ctrl-C

Termination

3

SIGQUIT

User presses Ctrl-\

Termination

4

SIGILL

Illegal hardware instruction

Program error

5

SIGTRAP

Produced by debugger

Program error

8

SIGFPE

Arithmetic error; e.g., division by zero

Program error

9

SIGKILL

Cannot be caught or ignored

Termination

1
Previous Section  < Day Day Up >  Next Section