## 10.2. Environment Customization

Like the Bourne shell, the Korn shell uses the file /etc/profile for system-wide customization. When a user logs in, the shell reads and runs /etc/profile before running the user's .profile.

We don't cover all the possible commands you might want to put in /etc/profile. But the Korn shell has a few unique features that are particularly relevant to system-wide customization; we discuss them here.

We'll start with two built-in commands that you can use in /etc/profile to tailor your users' environments and constrain their use of system resources. Users can also use these commands in their .profile, or at any other time, to override the default settings.

### 10.2.1. umask

umask, like the same command in most other shells, lets you specify the default permissions that files have when users create them. With ksh, it takes the same types of arguments that the chmod command does, i.e., absolute (octal numbers) or symbolic permission values.

The umask contains the permissions that are turned off by default whenever a process creates a file, regardless of what permission the process specifies.[138] Another way to think of this is as a bitwise borrow-free subtraction: actual permissions = requested permissions - the umask.

> [138] If you know C, C++, or Java, and are comfortable with bitwise operations, the umask operation works like this: `actual_permission = requested_permission & (~ umask)`.

We'll use octal notation to show how this works. As you should know, the digits in a permission number stand (left to right) for the permissions of the owner, the owner's group, and all other users, respectively. Each digit, in turn, consists of three bits, which specify read, write, and execute permissions from left to right. (If a file is a directory, the "execute" permission becomes "search" permission, i.e., permission to cd to it, and to traverse it as part of a pathname.)

For example, the octal number 640 equals the binary number 110 100 000. If a file has this permission, then its owner can read and write it; users in the owner's group can only read it; everyone else has no permission on it. A file with permission 755 (111 101 101 in binary) gives its owner the right to read, write, and execute it and everyone else the right to read and execute (but not write).

022 is a common umask value. This implies that when a file is created, the most permission it could possibly have is 755 -- which is the usual permission of an executable that a compiler might create. A text editor, on the other hand, might create a file with 666 permission (read and write for everyone), but the umask forces it to be 644 instead.

The -S option to umask causes it to work with symbolic values instead of with octal numbers. When used without an argument, umask -S prints the umask in symbolic form. With an argument, the mask is changed. In both cases, a symbolic mask represents the permissions to keep for a file. (This ends up being the bitwise complement of the traditional octal umask, which represents permissions to remove.) If you're confused, some examples should clear things up:

>     $ umask                       Print the current umask, in octal
>     0022
>     $ umask -S                    Print the current umask, in symbolic form
>     u=rwx,g=rx,o=rx
>     $ umask -S u=rwx,g=r,o=       Change the umask using the symbolic form
>     $ umask -S                    Print it back out symbolically
>     u=rwx,g=r,o=
>     $ umask                       Print it in octal
>     0037

### 10.2.2. ulimit

Early Unix systems didn't impose any limits on what resources a process could use. If a program wanted to run forever, it could. If a program wanted to create large files and fill up a disk, it could. And so on.

As Unix developed and matured, it became possible to explicitly control, or limit, a variety of different system resources, such as CPU time and disk space. The ulimit command is the Korn shell's interface for viewing and changing the limits on system resources. Table 10-1 lists the options it accepts and the corresponding resources. Not all options are available on all Unix systems. Many won't be available on non-Unix systems.

#### Table 10-1. ulimit resource options

Option | Resource limited | Option | Resource limited
--- | --- | --- | ---
`-a` | All (for printing values only) | `-n` | File descriptors
`-c` | Core file size ( kb blocks) | `-p` | Pipe buffer size ( kb blocks)[139]
`-d` | Process data segment (kb) | `-s` | Process stack segment (kb)
`-f` | File size ( kb blocks) | `-t` | Process CPU time (seconds)
`-m` | Physical memory (kb) | `-v` | Virtual memory (kb)

[139] Most Unix systems don't have this feature.

Each takes a numerical argument that specifies the limit in units shown in the table. (You may use an arithmetic expression for the limit; ksh automatically evaluates the expression.) You can also give the argument "unlimited" (which may actually mean some physical limit), or you can omit the argument, in which case it prints the current limit. `ulimit -a` prints the limits (or "unlimited") for all types. You can only specify one type of resource at a time. If you don't specify any option, -f is assumed.

Some of these options depend on operating system capabilities that don't exist in older Unix versions. In particular, some older versions have a fixed limit of 20 file descriptors per process (making -n irrelevant), and some don't support virtual memory (making -v irrelevant).

The -d and -s options have to do with dynamic memory allocation, i.e., memory for which a process asks the operating system at runtime. It's not necessary for casual users to limit these, though software developers may want to do so to prevent buggy programs from trying to allocate endless amounts of memory due to infinite loops.

The -v option is similar; it puts a limit on all uses of memory. You don't need this unless your system has severe memory constraints or you want to limit process size to avoid thrashing.

You may want to specify limits on file size (-f and -c) if you have constraints on disk space. Sometimes users actually mean to create huge files, but more often than not, a huge file is the result of a buggy program that goes into an infinite loop. Software developers who use debuggers like gdb and dbx should not limit core file size, because core dumps are often helpful for debugging.

The -t option is another possible guard against infinite loops. On single-user systems, a program that is in an infinite loop but isn't allocating memory, writing files, or using the network is not particularly dangerous; it's better to leave this unlimited and just let the user kill the offending program. However, on shared server systems, such programs definitely degrade the overall environment. The problem in that case is that it's difficult to know what limit to set: there are important and legitimate uses for long-running programs.

In addition to the types of resources you can limit, ulimit lets you specify hard or soft limits. Hard limits can be lowered by any user but only raised by the superuser (`root`); users can lower soft limits and raise them -- but only as high as the hard limit for that resource.

If you give -H along with one (or more) of the options above, ulimit sets hard limits; -S sets soft limits. Without either of these, ulimit sets both. For example, the following commands set the soft limit on file descriptors to 64 and the hard limit to unlimited:

>     ulimit -Sn 64
>     ulimit -Hn unlimited

When ulimit prints current limits, it prints the soft limits unless you specify -H.

### 10.2.3. Types of Global Customization

The best possible approach to globally available customization would be a system-wide environment file that is separate from each user's environment file -- just like /etc/profile is separate from each user's .profile.

Unfortunately, the Korn shell doesn't have this feature. If you assign a filename to the `ENV` environment variable, it could be overridden in a user's .profile. This allows you to make a default environment file available for users who don't have their own, but it doesn't let you have a system-wide environment file that runs in addition to the users'. Furthermore, the environment file is only run for interactive shells, not all shells.

Nevertheless, the shell gives you a few ways to set up customizations that are available to all users at all times. Environment variables are the most obvious; your /etc/profile file will undoubtedly contain definitions for several of them, including `PATH` and `TERM`.

The variable `TMOUT` is useful when your system supports dialup lines. We have already seen that it affects the read command and the `select` menu loop. When set to a number N, if a user doesn't enter a command within N seconds after the shell last issued a prompt, the shell prints the warning message `shell will timeout in 60 seconds due to inactivity`. If, after a further 60 seconds, the user does not enter anything, the shell terminates. This feature is helpful in preventing people from "hogging" the dialup lines. Just make sure you set it to a reasonable value!

You may want to include some more complex customizations involving environment variables, such as the prompt string `PS1` containing the current directory, user name, or hostname (as seen in Chapter 4).

You can also turn on options, such as emacs or vi editing modes, noclobber to protect against inadvertent file overwriting, and perhaps ignoreeof to keep people from logging off by accident when they type too many CTRL-D characters. Any shell scripts you have written for general use also contribute to customization.

Unfortunately, it's not possible to create a global alias. You can define aliases in /etc/profile, but there is no way to make them part of the environment so that their definitions will propagate to shell subprocesses.

However, you can set up global functions. These are an excellent way to customize your system's environment, because functions are part of the shell, not separate processes. For example, you might wish to make pushd and popd (see Chapter 4 through Chapter 6) globally available.

The best way to create global functions is to use the built-in variable `FPATH` for autoloading of functions that we introduced in Chapter 4. Just define `FPATH` as a function library directory, perhaps /usr/local/functions, and make it an environment variable by exporting it. Then make sure that the directory listed in `FPATH` is also included in `PATH`. In other words, put this or similar code in /etc/profile:

>     FPATH=/usr/local/functions
>     PATH=$PATH:$FPATH
>     export FPATH PATH

Then put each global function's definition in a file in that directory with the same name as the function.

In any case, we suggest using global functions for global customization instead of shell scripts. Given how cheap memory is nowadays, there is no reason why you shouldn't make generally useful functions part of your users' environment.

Copyright © 2003 O'Reilly & Associates. All rights reserved.