MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Learning the Mac OS X Terminal Learning the Terminal in Jaguar, Part 2

by Chris Stone, coauthor of Mac OS X in a Nutshell
01/24/2003

Author's Note -- In Part 1 of this series focusing on using the Terminal application in Jaguar, you learned how to reschedule default system cron jobs by modifying the system crontab file.

Here in Part 2, I'll show you how to configure cron to email you a report each time it runs one of these jobs.

If you're running Mac OS X 10.1 (or something even older), please see Learning the Mac OS X Terminal: Part 1, which is written for earlier versions on Mac OS X.

First, let's take another look at the pertinent lines in /etc/crontab. Since you only want to look at the file and not modify it, you don't need to use a text editor like pico. Instead, to only display short files like this, use the cat utility, which dumps the entire text file into your window and exits:

[haru:~] chris% cat /etc/crontab

You might notice a couple of interesting things about this command line. First, since the permissions for /etc/crontab allow anyone to read it (but only root to modify it), using sudo is not necessary.

Second, this command line allows you to access a file in a directory other than your working directory by specifying, in this case, the full (or absolute) pathname to that file. (You didn't first cd to /etc to open crontab, as you did in Part 1.) An absolute path describes the directory hierarchy from the very top of the file system (the root directory or "/"), down to the file.

Finally, because some of the crontab lines might be too long for your window, you'll need to widen the window by dragging its lower right corner to the right until all lines fit without wrapping.

Look now at the line specifying the daily cron job, which, depending on how you configured the time segment in Part 1, looks something like this:

15    3    *    *    *     root   periodic daily

Following the first five time fields is the user field. In this case the user field tells cron to run the job as if root were doing it. This is necessary here because these maintenance procedures require the full access to the system allowed to the root account.

Related Reading

Mac OS X in a Nutshell
A Desktop Quick Reference
By Jason McIntosh, Chuck Toporek, Chris Stone

Following the user field is the sixth and final field, which holds the actual command line that cron passes on to a shell to execute. The heart of the command, periodic daily, tells cron to run the periodic utility with its daily argument.

A command's arguments are any of the additional bits of text in the command line, including option flags, that further specify how the command should behave. In this case, periodic's single argument specifies that periodic run its daily job.

Periodic

As you'll see more and more as you peer into Mac OS X's Unix underpinnings, there's a lot of labor sharing among the many specialized tools included with the system. Typically, for the system to get a larger job done, a single, small utility will do its part, and then pass the work on to another tool, which steps in and performs the job it was designed for. The main duty of cron, for example, is to launch scheduled jobs. Its strength, however, is not in organizing, prioritizing, or reporting on those jobs (though cron can do some of these things, and in fact did so in Mac OS X versions prior to 10.2).

To best handle those facets of the routine maintenance jobs, cron calls on periodic, a utility designed specifically to run such system jobs. In this case, the daily argument given to periodic refers to a directory of the same name. The daily directory exists in /etc/periodic, a place periodic knows to look for executable files to launch. Inside /etc/periodic/daily, then, are the actual shell scripts that perform the daily maintenance jobs.

A shell script is a file holding a series of command lines that can be batch-executed by the shell. Much like an AppleScript, a shell script allows you to create and save long automated procedures that you can run at any time using a single command.

If you would like to have a look at the entire contents of the /etc/periodic directory, you can do so using ls and its -R flag (for recursive list), like this:

[haru:/etc/periodic] chris% ls -R /etc/periodic
daily   monthly weekly

/etc/periodic/daily:
100.clean-logs 500.daily

/etc/periodic/monthly:
500.monthly

/etc/periodic/weekly:
500.weekly

The -R option flag, then, displays the contents of that directory as well as all of its subdirectories and their contents, completely down through the hierarchy.

As you see, there are three directories in /etc/periodic: daily, weekly, and monthly. Within each of these is at least one shell script file (daily has two), whose name begin with a three-digit number. This number specifies the order that periodic should run the scripts, with the lower numbers running first (100.clean-logs will run before 500.daily).

We'll take a closer look at these scripts in Part 3, but the next steps here are to configure Mac OS X's built-in mail transfer agent sendmail so it will operate properly, and then configure periodic so it will email you the output of the scripts as each is completed.

sendmail

Mail user agents (MUAs) are those applications that allow you to personally send, receive, and otherwise work with your email messages. Outlook, Eudora, and Mac OS X's Mail application are familiar examples of MUAs.

Mail transfer agents (MTAs), on the other hand, are the not-so-familiar applications that receive the messages from the MUA and pass them on to other users on the same machine, or to MTAs on other machines for ultimate delivery to users elsewhere. The sendmail MTA included with OS X is one of the most popular, used on servers large and small across the Internet.

The following procedure will walk you through the steps necessary to get sendmail running on your machine so that it will at least deliver the reports generated by periodic. For help getting sendmail to run in an actual server environment, see this article.

The first step to getting sendmail to successfully launch in Mac OS X is to address a permissions issue. As a security measure, sendmail will not run with OS X's default permissions, namely those for the root directory.

Eliminating sendmail Permissions Errors

There are actually two ways to address this issue. The first and most obvious is to simply change the offending permissions, and allow sendmail to run. The other, less obvious solution is to instead configure sendmail so it will ignore the somewhat insecure permissions and run anyway.

The first method involves one simple change: eliminating write permission for the group assigned to the root directory. The CLI makes it very easy to view and change the various permission settings for any item, but the procedures are still too involved to detail here. (Of course, Mac OS X: The Missing Manual does include an in-depth look at permissions and the CLI.)

Instead, I'll zero in on the single command line required to get sendmail going:

sudo chmod g-w /

Since you're modifying the settings for a root-owned directory, the command line starts with sudo. Next comes the chmod command, for "change (file) modes." File modes are the settings that specify whether an item can be read or written to, for example, and by which kind of user -- the owner of the item, its group, or any user of the machine. (These settings correspond, of course, with the Ownership & Permissions settings that are accessible via Finder's Get Info window.)

Following a space are chmod's arguments, the first of which specifies the modes to be changed. This argument says to take the group ("g") and remove ("-") its permission to write ("w") to the file or directory specified in the next argument (again followed by a space), which in this case is the root directory, ("/").

Your next step, then, is to run the command line:

[haru:~] chris% sudo chmod g-w /

If you ever want to revert the permissions back to group-writable, run this command, which uses the plus sign ("+") to add writable permissions:

[haru:~] chris% sudo chmod g+w /

However, there are some important caveats with this method. First, Mac OS X upgrade installers and some application installers change the root directory back to group-writable, so you'll need to re-run the chmod command line whenever this happens.

Secondly, and more importantly, there's a reason why Apple wants the root directory to be group-writable. Many Classic installer applications (and even some native ones) are programmed to place items in the root directory, and unless you've given these applications admin privileges to do this, they just might choke during an installation. Apple's workaround for this possibility, then, is to keep the root directory group-writable.

Of course, that causes problems for sendmail, which requires these permissions for security reasons. At this point, then, you might feel stuck between a rock and a hard place. But wouldn't it be great if you could just tell sendmail (as Ben Franklin might), "Hey, I'm willing to give up a little security if you just give me the liberty to keep my permissions!"

In fact, sendmail allows you to set just that option by adding a single line to its configuration file. This is from the sendmail documentation:

"You may have to tweak your environment to make it safer for sendmail to run. If you find that some of the safeties in sendmail are too restrictive for your environment, they can be turned off by setting the option DontBlameSendmail. The option is appropriately named as sendmail is not to be blamed for problems resulting from unsafe permissions on directories and files."

As long as you're using sendmail as described in the tutorial and are the primary user of the machine, the security risk is small in setting this option. If, however, you aren't able to control access to your machine either physically or remotely, and you are compromised, please don't blame me either ;-)

So if this method sounds better for you than the first (running sudo chmod g-w /), then the file you need to edit is /etc/mail/sendmail.cf. You'll first want to make a backup. Since the /etc/mail directory is only root-writable, you'll need sudo:

sudo cp -p sendmail.cf sendmail.cf.bak

Note the use of the -p option flag in this command line, which preserves the permissions settings of the original in the copy of the file. This will make things a little easier, should you need to quickly restore the file.

You can then edit this file using pico, as you have with others. Since sendmail.cf is only writable by root, you'll need to use sudo here as well.

sudo pico /etc/mail/sendmail.cf

This file is over 1,200 lines long and might be intimidating, but since you'll just be adding a single line near the top and then getting the heck out, you should have nothing to worry about.

As you view the file, keep in mind that lines beginning with the #character are ignored by sendmail as it reads the file. Typically, such lines contain descriptive comments about the configuration settings, but they can also be actual settings that for various reasons are turned off, or "commented out."

The line you're looking for is a commented-out "DontBlameSendmail" line about 70 lines down from the top. The quickest way to get there in pico is by pressing Control + W, entering DontBlame, and pressing Return. You should then see these lines:

# level 10 config file format
V10/Berkeley

# override file safeties - setting this option compromises system security,
# addressing the actual file configuration problem is preferred
# need to set this before any file actions are encountered in the cf file
#O DontBlameSendmail=safe   
 
# default LDAP map specification

Next, add a new line after the found line and enter (or paste in) this line:

O DontBlameSendmail=GroupWritableDirPathSafe

When you're done, the lines should look like this:

# override file safeties - setting this option 
# compromises system security, addressing the actual 
# file configuration problem is preferred
# need to set this before any file actions are 
# encountered in the cf file
#O DontBlameSendmail=safe   
O DontBlameSendmail=GroupWritableDirPathSafe 
# default LDAP map specification

As usual, press Control + O to save the file, Return to confirm the name, and Control + X to exit pico.

If everything went well, sendmail will attempt to send its mail the next time it's asked, even with a group-writable root directory.

NetInfo andsendmail

The version of sendmail included with Mac OS X is compiled to look at the NetInfo database for either its configuration information or a pointer to a config file.

In our case, we want sendmail to read from /etc/mail/sendmail.cf so you'll need to add a record pointing to that file to the NetInfo database. To do so, use the niutil command with these two command lines (the second line has been broken into two for the sake of space here, but it should be entered on one line):

sudo niutil -create . /locations/sendmail
sudo niutil -createprop . /locations/sendmail sendmail.cf
  /etc/mail/sendmail.cf

niutil, or the Netinfo Utility, is a command line version of the GUI NetInfo Manager application found in /Applications/Utilities. The first of the two command lines creates a new record, and the second writes the properties of that record: the name of the configuration file, and its path.

Now when sendmail looks at NetInfo for its configuration information, it will find the pointer within this new record, and follow it to /etc/mail/sendmail.cf for that info.

One More Thing for sendmail

The final sendmail step is configuring it to start when the machine starts. sendmail will then continue to run in the background whenever the machine is on, watching for new mail to deliver, and making sure undeliverable messages don't accumulate. Such background processes (often called daemons), play a vital role in Mac OS X's operation, and most of the time use little memory or processing resources.

Though running sendmail as a daemon is not really necessary for the purpose of this tutorial, doing so will at least ensure that the mail-related housekeeping gets done. The configuration involves one simple step: making one change to the /etc/hostcofig file.

Open the file in pico as usual (though you might want to back it up first), using sudo since it's a root-owned file:

sudo pico /etc/hostconfig

Look for this line:

MAILSERVER=-NO-

And just change the NO to YES, so it looks like this:

MAILSERVER=-YES-

Save the file and close pico as usual, and your sendmail mail server will start with each boot. Since sendmail is not running on your machine yet, you can also start it without rebooting using this command:

sudo /System/Library/StartupItems/Sendmail/Sendmail start

This command manually runs the same sendmail startup script that the system runs once MAILSERVER is set to YES in /etc/hostconfig.

Checking Mail

Now that sendmail is ready to go, it's time to test it out by sending mail from the CLI. To do so, use the mail command to send mail to yourself like this:

[haru:~] chris% mail chris
Subject:

You're now working inside the mail CLI application, so you'll see no more tcsh shell prompts until you exit mail. Enter any subject you would like and press return. Type in your message at the cursor. To end your message, send it, and exit mail, press return, type a period, and press return again. You'll then return to your shell prompt:

[haru:~] chris% mail chris
Subject: Test
This is only a test.
.
EOT
[haru:~] chris%

You can then check your mail by entering the mail command again, but this time with no arguments. If the message arrives, you'll see something like this:

[haru:~] chris% mail
Mail version 8.1 6/6/93.  Type ? for help.
"/var/mail/chris": 1 message 1 new
>N  1 chris         Fri Jan 17 13:50  13/399   "Test"
&

You're back in the mail application, but this time to view your new message. Press return at the "&" prompt to have a look:

Message 1:
From root  Fri Jan 17 13:50:30 2003
Date: Fri, 17 Jan 2003 13:50:30 -0800 (PST)
From: Chris Stone <chris>
To: chris
Subject: Test

This is only a test.

&

Type q and return at the prompt to exit mail:

& q
Saved 1 message in mbox
[haru:~] chris%

If for some reason your mail didn't arrive, you'll want to track the problem down by looking at the mail log /var/log/mail.log. A commonly used utility for viewing ever-growing log files is tail, which displays just the last few lines (the most recent) of a log file:

tail /var/log/mail.log

The output should provide some clues, but if you're having trouble deciphering them, feel free to ask for help in the Talkback section of this article.

Back to Periodic

Now that your MTA is working, there are only a couple of more steps to getting periodic's reports sent to you. The first is to configure periodic to email its reports to the root user account instead of saving them as files. The second step is to configure things so that any mail sent to the root account gets forwarded to your account.

Why root? Well, we could just have periodic send the reports directly to your account, and it would work fine. But there is a standard practice in Unix that has any system- generated email messages sent to root, which is an account always sure to exist. It's then easy enough to just change root's forwarding address once, instead of several times should the ultimate destination account name (yours, in this case) change in the future. For now though, it's unlikely you'll be getting system email from processes other than periodic, but you'll be ready if you do.

periodic reads its configuration information from the file /etc/defaults/periodic.conf. We actually won't be modifying this file, but having a look at it will be informative. The /etc/defaults/periodic.conf file is a bit too long, so you'll want to use an application that can display the files one bite-sized screen-full, or "page," at a time. Such applications are called pagers, and one of the most common pagers is more.

Enter more and the file's pathname to view the first page of the file. To view periodic's default configuration file, then, enter:

more /etc/defaults/periodic.conf

You should then see something like this:

Screen shot.

To move down to the next page, press the space bar. (To go back up, press B.) At the bottom of the more display, you'll see the name of the file being viewed, as well as an indicator of the percentage of the file read so far. To exit more, type Q.

As you peruse the file, you'll notice its three main options sections, one for each of the periodic maintenance jobs: daily, weekly, and monthly. For us, the output line for each section is of interest. For example, in the daily options section you'll find this line:

daily_output="/var/log/daily.out"  # user or /file

As you might guess, this line tells periodic to write the output of its daily maintenance job to the file var/log/daily.out. You'll find that each of the other two sections have a corresponding line as well.

To have the output sent to an email message instead of this file, you would simply replace that file's pathname in this line with the target account's username, root, in our case. You could just make the change in this default configuration file, but periodic allows you the option of using an override file with which to specify changes, thus keeping the default file pristine and easily reverted to.

To do this, you'll create a new file /etc/periodic.conf and add the desired output options lines to it, and periodic will know to abide by those specific settings in this file instead of those in its default configuration file.

You'll again use sudo with pico since /etc is only writable by root:

sudo pico /etc/periodic.conf

Since this file doesn't yet exist, this command will create it and open it for writing. Simply enter these three lines to specify that periodic sends the output from its daily, weekly, and monthly jobs to root:

daily_output=root
weekly_output=root
monthly_output=root

Save the file and close pico as usual, and you're one step away from finishing. The last step, then, in getting the reports delivered is to direct them to your mailbox, instead of root's. And you'll do this using a .forward file.

A .forward file is just a text file containing nothing but the name of the account to which you want the mail forwarded (in this case, your account name). Once the .forward file is placed in the home directory of the original addressee (in this case, root), the mail gets forwarded as expected.

By default, the Mac OS X root home directory already contains a .forward file, but this one redirects mail not to another user, but into thin air. This happens because instead of an account name, root's .forward file contains the pathname /dev/null, which is the location of a Unix black hole. Streams of data directed to /dev/null, mail messages included, simply disappear. Since OS X's designers figure most users won't be accessing root's mail, they used this method to ensure mail doesn't pile up at the door while no one's home, eventually filling up your hard disk.

We only need, then, to edit root's .forward file. You've probably already noticed that there is no root directory in /Users -- so where is root's home? The easiest way to find any user's home directory is by using the finger command, which shows some basic information about the specified account. For root's, then:

[haru:~] chris% finger root
Login: root                     Name: System Administrator
Directory: /var/root            Shell: /bin/tcsh
Last login Fri Oct 11 19:46 (PDT) on console
No Plan.
[haru:~] chris%

And there beside Directory you'll see that root's home directory is /var/root.

As an alternative, you can always specify a user's home directory using the ~ shortcut along with the account name. Therefore, if you wanted to specify root's home directory, you would use ~root.

Let's have a look inside ~root:

[haru:~] chris% sudo ls /var/root
.CFUserTextEncoding .forward     .nsmbrc      Library

First, here are a couple of points about this command line:

In any case, you should now see ~root/.forward, so let's next edit it with pico, using sudo since it's a root-owned file:

sudo pico /var/root/.forward

You should then see something like this:

Screen shot.

That single line, then, is the entire content of /var/root/.forward. To change it, first delete the file's single line by pressing control + K. Next, type in your account name (the name that's just before the "%" in the prompt; "chris" in this case):

Screen shot.

Save the file and close pico as usual, and you're done with the .forward file.

Now that everything is in place, you can perform a test. Send a new mail message to root:

[haru:~] chris% mail root
Subject: Test 2
This is only a test, again.
.
EOT
[haru:~] chris%

Check your mail, and you should see the new message, forwarded to you from root:

haru:~] chris% mail
Mail version 8.1 6/6/93.  Type ? for help.
"/var/mail/chris": 1 message 1 new
>N  1 chris          Wed Jan 22 08:56  13/406   "Test 2"
& 
Message 1:
From root  Wed Jan 22 08:56:20 2003
Date: Wed, 22 Jan 2003 08:56:20 -0800 (PST)
From: Chris Stone <chris>
To: root
Subject: Test 2

This is only a test, again.

& q
Saved 1 message in mbox
[haru:~] chris%

Here are a couple of more pointers for using mail:

For the final test, go ahead and run the daily maintenance job manually by entering this command:

sudo periodic daily

Once a prompt has returned, the job is done and you can check your mail once more:

[haru:~] chris% sudo periodic daily
Password:
[haru:~] chris% mail
Mail version 8.1 6/6/93.  Type ? for help.
"/var/mail/chris": 1 message 1 new
>N  1 chris       Wed Jan 22 09:08  59/2244  "Haru.local"

If you take a look at the message, the beginning of it should look something like this:

Message 1:
From root  Wed Jan 22 09:08:54 2003
Date: Wed, 22 Jan 2003 09:08:54 -0800 (PST)
From: Chris Stone <chris>
To: root
Subject: Haru.local daily run output

Subject: Haru.local daily run output

Removing scratch and junk files:
rm: ./Mount01: is a directory
rm: ./Mount02: is a directory
rm: ./Mount03: is a directory
rm: ./Mount04: is a directory
rm: ./vi.recover: is a directory
rm: ./zBooterMnt: is a directory

Backing up NetInfo data

Checking subsystem status:

disks:
Filesystem   1K-blocks     Used    Avail Capacity  Mounted on
/dev/disk0s9  19532400 13471036  5866040    69%    /
fdesc                1        1        0   100%    /dev

Now that these regular reports will be coming in, you'll probably want to be able to understand them. In Part 3, you'll get a closer look at the scripts themselves to learn how to read the reports they generate. Until then, keep checking to see that you're receiving the reports as expected, and always feel free to submit your comments or questions to our TalkBack section.

Chris Stone is a Senior Macintosh Systems Administrator for O'Reilly, coauthor of Mac OS X in a Nutshell and contributing author to Mac OS X: The Missing Manual, which provides over 40 pages about the Mac OS X Terminal.


Read more Learning the Mac OS X Terminal columns.

Return to the Mac DevCenter.


Copyright © 2009 O'Reilly Media, Inc.