Cover V10, I12

Article

dec2001.tar

Questions and Answers

Q My Ultra 10 box running Solaris 7 is a JumpStart server with a Solaris 8 01/00 release image on it. I want the latest features of Solaris, though, so I downloaded the maintenance upgrade (MU) from SunSolve. Can I "patch" my JumpStart image as I originally intended? If so, how? The install_mu shell script was obviously intended to upgrade a running OS, and I see nothing in the accompanying documentation of any relevance to upgrading a JumpStart image.

A No, you cannot easily patch the JumpStart image on your disk. The maintenance updates are provided as a series of patches to the base operating system. What you can do is apply the maintenance update patches on each of your newly jumpstarted machines using one of your post install scripts, just like you would with the recommended patch set.

The best course of action, though, is to just install a new JumpStart image from the latest ISO images instead of using the maintenance updates. That way you'll have the new base OS instead of a patched older base OS.

Q I'm creating a bash script that's supposed to do things to people's home directories. Root runs this script and gives the username as an argument on the command line. In the script, I'm trying to use ~username to access the user's home directory like this:

#!/usr/local/bin/bash
ls ~$1
Instead of seeing the contents of the user's directory, I get the output:

ls: ~username: No such file or directory
Where "username" is the argument that was passed in to the script. If I hardcode the script, everything works fine:

#!/usr/local/bin/bash
ls ~username
Why isn't it working when I pass in the username, and why do I get an error saying that it can't find ~username when ~username is clearly there and works when it's hardcoded? How can I make this work so I can operate on user's home directories?

A The reason that your script is not working is because of the order of expansion. There are two different items being expanded: ~ and $1. The ~ is expanded first, and then your script tries to do an ls on the directory called, literally, ~$1. Obviously this doesn't work, so your shell script goes to print out an error. When the error is printed, however, the second expansion takes place and $1 becomes the argument you passed in. To get around this, you want to use eval:

#!/usr/local/bin/bash
eval "homedir=~$1"
ls $homedir
You can also use some other basic UNIX tools to accomplish the same thing instead of using tilde expansion in your script (since not all shells support tilde expansion). These next two command-line examples assume that your password file contains all of the local users (no NIS/NIS+/LDAP or other remote account authorization in use) and that the user's home directory is stored in the sixth field of the password file:

awk -F: '/^username:/ {system("ls "$6)}' /etc/passwd

grep ^username: /etc/passwd | cut -d: -f6 | xargs ls
You can also take a similar approach but substitute checking /etc/passwd with checking NIS maps or LDAP output, too. Be sure to make it so that the string match on the username will be unique. In the case of checking /etc/passwd, the username will be the first field of output, immediately followed by a ":".

The other caveat to changing things based on the user's home directory is that you may have some users that have invalid home directories, system directories, or files as home directories, or share home directories with other users. For example, be on the look out for home directories such as:

/
/dev/null
/nohome
/tmp
You don't want to blindly change things in places such as those.

Q I made some modifications to /etc/system to tune the kernel. I am running Sun OS 5.8, and I added the following lines:

set shmsys:shminfo_shmmax = 4294967295
set shmsys:shminfo_shmmin = 1
set shmsys:shminfo_shmmini = 100
set shmsys:shminfo_shmseg = 100
set shmsys:seminfo_semmni = 100
set shmsys:seminfo_semmsl = 510
set shmsys:seminfo_semmns = 1010
set shmsys:seminfo_semopm = 100
set shmsys:seminfo_semvmx = 32767
set shmsys:seminfo_semume = 30
I then rebooted the server as stated in the manual. I also did a boot -r from the OK prompt just in case. However, I received the following errors when the machine booted:

Jul 12 14:56:52 host.domain.com genunix: [ID 492708 kern.notice] sorry, 
  variable 'shminfo_shmmini' is not defined in the 'shmsys'
Jul 12 14:56:52 host.domain.com genunix: [ID 966847 kern.notice] module
Jul 12 14:56:52 host.domain.com genunix: [ID 492708 kern.notice] sorry, 
  variable 'seminfo_semmni' is not defined in the 'shmsys'
Jul 12 14:56:52 host.domain.com genunix: [ID 966847 kern.notice] module
Jul 12 14:56:52 host.domain.com genunix: [ID 492708 kern.notice] sorry, 
  variable 'seminfo_semmsl' is not defined in the 'shmsys'
Jul 12 14:56:52 host.domain.com genunix: [ID 966847 kern.notice] module
Jul 12 14:56:52 host.domain.com genunix: [ID 492708 kern.notice] sorry, 
  variable 'seminfo_semmns' is not defined in the 'shmsys'
Jul 12 14:56:52 host.domain.com genunix: [ID 966847 kern.notice] module
Jul 12 14:56:52 host.domain.com genunix: [ID 492708 kern.notice] sorry, 
  variable 'seminfo_semopm' is not defined in the 'shmsys'
Jul 12 14:56:52 host.domain.com genunix: [ID 966847 kern.notice] module
Jul 12 14:56:52 host.domain.com genunix: [ID 492708 kern.notice] sorry, 
  variable 'seminfo_semvmx' is not defined in the 'shmsys'
Jul 12 14:56:52 host.domain.com genunix: [ID 966847 kern.notice] module
Jul 12 14:56:52 host.domain.com genunix: [ID 492708 kern.notice] sorry, 
  variable 'seminfo_semume' is not defined in the 'shmsys'
Jul 12 14:56:52 host.domain.com genunix: [ID 966847 kern.notice] module
Why am I getting these errors? Can I ignore them, or is something broken?

A You have the wrong variables and modules. Take a look at System V Semaphores and System V Shared Memory under:

http://docs.sun.com/ab2/coll.736.2/SOLTUNEPARAMREF/@Ab2PageView/1220
There is no shminfo_shmmini (perhaps you meant shminfo_shmmni, whose default is already 100), and all of the seminfo variables you list belong under the module semsys, not shmsys. Try this instead:

set shmsys:shminfo_shmmax = 4294967295
set shmsys:shminfo_shmmin = 1
set shmsys:shminfo_shmmni = 100
set shmsys:shminfo_shmseg = 100
set semsys:seminfo_semmni = 100
set semsys:seminfo_semmsl = 510
set semsys:seminfo_semmns = 1010
set semsys:seminfo_semopm = 100
set semsys:seminfo_semvmx = 32767
set semsys:seminfo_semume = 30
Q I'm writing a Bourne shell script that needs to read in a username and shell as arguments, instead of prompting the user for input while the script is running. The order of the parameters shouldn't matter, and I want to make this extensible so that I can add other command-line options in the future. What's the best way to do this, a for loop, a while loop, if statements?

A You can certainly do what you want with a plain while loop, as in the following:

#!/bin/sh

while [ $# -gt 0 ]
do
  case "$1" in
   -u) USERNAME=$2;;
   -s) USERSHELL=$2;;
  esac
  shift
done
echo "user == $USERNAME"
echo "shell == $USERSHELL"
Even better, though, is to use getopts to specify your options:

#!/bin/sh
while getopts u:s: opt
do
  case "${opt}" in
    u) USERNAME=${OPTARG}
       ;;
    s) USERSHELL=${OPTARG}
       ;;
  esac
done
shift 'expr ${OPTIND} - 1'
echo "user == ${USERNAME}"
echo "shell == ${USERSHELL}"
Be sure to do some error checking to verify that you actually have a valid value for each variable before you try to use it, though.

The options that getopts is aware of in this case are u:s:. The ":" after each letter option means that an argument is required. If you leave out the ":", then the flag can stand on its own without an argument. The space between the flag and the argument is optional. You can invoke this script in either of these ways:

./script -u User -s Shell
./script -uUser -sShell
When getopts finds a valid flag, it puts the associated argument, if any, into the variable OPTARG. The variable OPTIND contains the pointer to the current word.

For each iteration through the while loop, getopts looks for a valid command line flag (u or s, in this case) and then uses a case statement to decide on the action. If you used -u, then the action is to assign the argument (stored in OPTARG) to the variable USERNAME. If it's -s, you store the value in USERSHELL. You then decrement OPTIND and look for the next valid option. When working with your own variables for usernames and shells, be sure not to use the environment variables ${USER} and ${SHELL} or you may wind up with some very unexpected results.

Amy Rich, president of the Boston-based Oceanwave Consulting, Inc. (http://www.oceanwave.com), has been a UNIX systems administrator for more than five years. She received a BSCS at Worcester Polytechnic Institute, and can be reached at: arr@oceanwave.com.