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 won'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'll 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.
umask, like the same command in most other shells, lets you specify the default permissions that files have when users create them. 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. [4]
[4] If you are comfortable with Boolean logic, think of the umask as a number that the operating system logically XORs with the permission given by the creating process.
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, 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, list its files, etc.)
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 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 ulimit command was originally used to specify the limit on file creation size. But the Korn shell version has options that let you put limits on several different system resources. Table 10.1 lists the options.
Option | Resource Limited |
---|---|
-a | All (for printing values only) |
-c | Core file size (½ kb blocks) |
-d | Process data segment (kb) |
-f | File size (½ kb blocks) |
-n | File descriptors |
-s | Process stack segment (kb) |
-t | Process CPU time (seconds) |
-v | Virtual memory (kb) |
Each takes a numerical argument that specifies the limit in units shown in the table. You can also give the argument "unlimited" (which may actually mean some physical limit), or you can omit the argument, in which case it will print the current limit. ulimit -a prints limits (or "unlimited") of 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 sdb and dbx should not limit core file size, because core dumps are necessary for debugging.
The -t option is another possible guard against infinite loops. But we would argue that a program that is in an infinite loop but isn't allocating memory or writing files is not particularly dangerous; it's better to leave this unlimited and just let the user kill the offending program.
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 will set 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 soft limits unless you specify -H.
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'.
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. Set it to a number N, and if a user doesn't enter a command within N seconds after the shell last issued a prompt, the shell will terminate. This feature is helpful in preventing people from "hogging" the dialup lines.
You may want to include some more complex customizations involving environment variables, such as the prompt string PS1 containing the current directory (as seen in Chapter 4, Basic Shell Programming).
You can also turn on options, such as emacs- or vi- editing modes, trackall to make alias expansion more efficient and system security tighter, and noclobber to protect against inadvertent file overwriting. 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 subshells. (In contrast, users can define global aliases by putting their definitions in environment files.)
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, if you define pushd and popd (see Chapters Chapter 4 through Chapter 6, Command-line Options and Typed Variables) as exported functions, the shell will run them almost as if they were built-in commands, as they are in the C shell.
The best way to create global functions is to use the built-in variable FPATH and the autoload feature 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. In other words, put this or similar code in /etc/profile:
FPATH=/usr/local/functions export FPATH
Then put each global function's definition in a file in that directory with the same name as the function, and put autoload fname for each of these functions in /etc/profile.
In either case, we suggest using exported 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.