Posts Tagged freebsd

Explorations in the World of Linux

I’ve been a FreeBSD admin for the past decade, and during this time have become quite familiar with the *BSD system. It has its quirks, but overall it’s very clean and easy to maintain.

From time to time – usually when I’ve been getting ready to upgrade to the next major revision of FreeBSD – I’ve taken some time to research what the current pros and cons are for FreeBSD vs. some Linux distro. Always, in the end, FreeBSD has won. However, a development project I’m starting to work on will utilize Zend Server, which is only supported on handful of common Linux distros and on Windows (which is, by default, not an option as I strongly maintain that Windows is not suitable as a web server platform). There is, of course, Linux compatibility layer in FreeBSD, but as Zend doesn’t currently support it as a platform for Zend Server, I wouldn’t feel comfortable using it in a production environment.

So even though I find FreeBSD superior to Linux in many ways, I’ve now spent some time getting acquainted with Linux. I first started with Red Hat, then moved to CentOS which is the Linux distribution I’m currently testing. Now it’s not bad, per se, but I frequently come back to the thought: “Why would someone, anyone prefer THIS over a BSD system?!” The package management with yum, rpm, and the GUI overlays is easy enough, but it’s chaotic! Having to enable and disable repos, set their priorities, etc. seems unnecessarily complicated. On the FreeBSD side there is the ports collection which provides most of the software that one can imagine ever needing. The odd few items that either aren’t available in ports, or whose configuration is somehow not complete enough through ports can be easily compiled from the source tarball. Everything’s quite easy to keep track of, and to duplicate if one’s building a new system.

I’m sure some of this feeling stems from the fact that I have been using a BSD system for so long, and from the fact that I probably don’t yet know Linux well enough (say, to build the system from a scratch..). But as far as I can tell, package management is done with yum and rpm (on CentOS, say), by adjusting repository priorities, and enabling/disabling repositories. That is messy!

Well, I now have a functional development server running Zend Server with Apache, Subversion, and MySQL, and as the vendor (Zend) dictates the rules, I must continue development on Linux. Perhaps in six months time I’ll have more favorable comments about it as compared to FreeBSD… but I sort of doubt it. My guess is I’ll just learn to live with it, every now and then wistfully glancing to the direction of the BSD server.

, , ,

1 Comment

Deleting all network interface aliases

I recently needed to move bunch of aliased IPs from one FreeBSD server to another. Adding aliases to /etc/rc.conf and then running ./netstart while in /etc adds new multiplexed IPs to the system all right, but if you need to remove aliased IPs, running /etc/netstart won’t remove them even if the aliases have been removed from /etc/rc.conf. Perhaps there is some easy single command that culls the active alias IPs to those specified in /etc/rc.conf, but I’m not aware of it. The following command can be used to quickly delete all aliased IPs for a specific interface (here “em0″):

ifconfig | grep “0xffffffff” | awk ‘{ print $2 }’ | xargs -n 1 ifconfig em0 delete

For this to work, the netmasks of the aliases and the master IP for the inteface must differ. The netmasks of the aliases are usually set to 255.255.255.255 (hence “0xffffffff”) while the netmask of the master IP is usually something different, specific to your network, e.g. 255.255.255.128 (“0xffffff80″).

Once the above command has been run, /etc/netstart can then be executed to load the remaining or reconfigured aliases (if any) from /etc/rc.conf.

, ,

No Comments

FreeBSD Full / Incremental Filesystem Dump Shell Script

I wanted to automate filesystem dumps on my servers running FreeBSD 7.2. After some searching I came across Vivek Gite’s FreeBSD Full / Incremental Tape Backup Shell Script which gave me a lot of ideas. Since I’m not using tape as the backup target I wanted to make a script specifically for that purpose while at the same time improve handling of some error conditions (such as, most importantly, checking for a missing level 0 dump before proceeding with an incremental dump) and add some new features such as autoincrement the dump level so that the dump level is not tied to specific day of the week.

Here’s my version of the script. While it bears some resemblance to Vivek’s script, it is largely rewritten. Read the script header for more information.

NOTE! In his comment James pointed out a possible bug in the script. The displayed script indeed had a problem: it was missing a backslash in front of the first dollar sign at:

eval “local fspath=\$${fsname}path”

This was caused by the script display plugin in WordPress that treated the backslash as an escape character (this has now been fixed). To be on the safe side, please download the script as a tarball. To further validate the integrity of the tarball, it should produce a md5 hash of 732ac44f11ba4484be4568e84929bb6a.

  |  copy code |? 
001
002
#!/bin/sh
003
 
004
# Autodump 1.5a (released 01 August 2009)
005
# Copyright (c) 2009 Ville Walveranta 
006
#
007
# A FreeBSD shell script to dump filesystems with full, and automatically 
008
# incremented incremental backups to a given directory location; this script
009
# was written with the intent of saving the filesystem dumps not onto a tape
010
# device but on another hard drive such as a different filesystem on the same 
011
# computer. The resulting dump files can be copied offsite with a separate 
012
# cron job.
013
#
014
# This script creates the necessary directory structure below the defined 
015
# 'BASEDIR' as well as the necessary log file. This script also ensures that
016
# the level 0 dump exists before creating an incremental dump; if it doesn't
017
# the script automatically erases the incremental files for the current week 
018
# (if any exist) and starts over with a level 0 dump. This way you can start 
019
# using the script on any day of the week and level 0 dump is automatically 
020
# created on the first run.
021
#
022
# When ran daily (such as from a cron job), the script creates level 0 dump
023
# on every Monday (beginning the ISO week), or Sunday (beginning of the U.S. 
024
# week) and an incremental dump on all the other days of each week. The dumps 
025
# are compressed with gzip and saved below the 'BASEDIR' to an automatically 
026
# created directory whose name is derived from the list given in 'FSNAMES'. 
027
# Each week's dumps are organized into subfolders with name YYYY-WW ('WW' 
028
# being the current week). By default three most recent weekly dumps 
029
# (level 0 + incrementals) are retained.
030
#
031
# The script maintains each weekly folder's date at the _beginning_ date
032
# of the dump (i.e. Monday or Sunday of the current week) at 00:00, not 
033
# at the most recent incremental's date/time.
034
#
035
# By default the root (/) and usr (/usr) filesystems are dumped. To add more  
036
# add a "friendly name" to the 'FSNAMES' list (it is used for the weekly folder
037
# names, for dump filenames, and to reference the corresponding mount point
038
# variable); then add the corresponding mount point variable (i.e. if you 
039
# add "var" to 'FSNAMES', then add a variable varpath=/var). The "path" 
040
# ending of the mount point variable name is required. 
041
#
042
# Since the number of incremental dumps is limited to nine (level 0 +
043
# incremental levels 1-9), the script will allow maximum of one dump 
044
# to be created per day. However, since the level incrementing is dynamic
045
# you can start the script on any day of the week, and run it on any
046
# number of days during the rest of the week and you'll always get
047
# level 0 plus the incremental dumps in sequential order. However, The 
048
# new weekly folder is always created on Monday or Sunday (as chosen by
049
# you). Note that the script determines whether "today's" dump exists 
050
# based on the modification date stamp of the most recent dump. Hence 
051
# it is a good idea to run this script in the early hours of each day 
052
# rather than in the very end of each day. Running the script, for 
053
# example, at 23:50 has the potential to push longer dump processes 
054
# over the midnight and so potentially cause the next day's dump to 
055
# be skipped.
056
#
057
# Written for FreeBSD 7.2 but should work on most BSD and *NIX systems with
058
# minor modifications.
059
# -------------------------------------------------------------------------
060
# Copyright (c) 2009 Ville Walveranta 
061
# <http://my.galagzee.com/2009/07/17/freebsd-dump-filesystem-shell-script>
062
# This script is licensed under GNU GPL version 2.0 or above, and is provided
063
# 'as-is' with no warranty which is to say that I'm not liable if it wipes out
064
# your hard drive clean or doesn't back up your precious data. However, to the 
065
# best or my knowledge it is working as expected -- I'm using it myself. :-)
066
# -------------------------------------------------------------------------
067
# This script was inspired by 
068
# FreeBSD Full / Incremental Tape Backup Shell Script
069
# by nixCraft project / Vivek Gite
070
# at <http://bash.cyberciti.biz/backup/freebsd-dump-filesystem-shell-script/>
071
# -------------------------------------------------------------------------
072
 
073
 
074
#### GLOBAL VARIABLES ###############################################
075
 
076
WEEKSTARTS=Mon      # Accepted values are "Mon" (ISO standard) or "Sun" (U.S.)
077
KEEPDUMPS=30        # in days; this is evaluated on the weekly level per start
078
                    # of the week, so '30' keeps 3-4 weekly dumps
079
BASEDIR=/bak/dumps
080
GLOBALDUMPOPTS=Lua  # add 'n' for wall notifications
081
LOGFILE=/var/log/dump.log
082
 
083
# to add more filesystems to be dumped add the dump name in 'FSNAMES'
084
# and add the corresponding mount point variable (dumpname+path=mountpoint)
085
FSNAMES="root usr"  # this is used for dump directory name 
086
                    # and to ID the path from a variable below
087
rootpath=/
088
usrpath=/usr
089
 
090
#####################################################################
091
 
092
DUMP=/sbin/dump
093
GZIP=/usr/bin/gzip
094
LOGGER=/usr/bin/logger
095
 
096
WEEKDAY=$(date +"%a")
097
DATE=$(date +"%Y%m%d")
098
HUMANDATE=$(date +"%d-%b-%Y")
099
HUMANDATE=`echo $HUMANDATE | tr '[:lower:]' '[:upper:]'`
100
HUMANTIME=$(date +"%H:%M (%Z)")
101
TODAYYR=$(date +"%Y")
102
TODAYMO=$(date +"%m")
103
TODAYDT=$(date +"%d")
104
 
105
# datestamp at midnight today
106
TODAYSTARTSTAMP=$(date -j +%s "${TODAYYR}${TODAYMO}${TODAYDT}0000")
107
 
108
# default lastdump to midnight today; it will be checked
109
# and and adjusted later
110
LASTDUMP=$TODAYSTARTSTAMP
111
 
112
# do not crete world-readable dumps!
113
umask 117
114
 
115
# make sure the logfile exists
116
if [ ! -e $LOGFILE ] ; then
117
   touch $LOGFILE
118
   chmod 660 $LOGFILE
119
fi
120
 
121
# make sure that entire week's incremental dumps are deposted
122
# in the same directory, even when a week spans new year
123
# NOTE: When the ending year has a partial 53rd week, there
124
# won't be a dump folder for the first week of the new year.
125
# The incremental dumps instead complete the 53rd week folder,
126
# even when the 1st week of the new year begins mid-week. 
127
# However, the dates of the incremental dump files in the 
128
# 53rd week folder correctly reflect the dates of the 
129
# beginning year.
130
adjust_date(){
131
   local dateoffset=$1
132
   local epochnow=$(date +%s)
133
   local offsetsecs=`expr $dateoffset "*" 86400`
134
   local newepoch=`expr $epochnow "-" $offsetsecs`
135
   local year=`date -r $newepoch +"%Y"`
136
 
137
   if [ "$WEEKSTARTS" = "Mon" ] ; then
138
      local week=`date -r $newepoch +"%W"`
139
   else
140
      local week=`date -r $newepoch +"%U"`
141
   fi
142
   NEWEPOCHISO=`date -r $newepoch +"%Y%m%d0000"`
143
 
144
   #system week starts from `0', there is no calendar week `0'
145
   week=`expr $week "+" 1`
146
   YWEEK=${year}-${week}
147
}
148
 
149
# determines the 'distance' from the level 0 dump in days
150
if [ "$WEEKSTARTS" = "Mon" ] ; then
151
   case $WEEKDAY in
152
      Mon) adjust_date 0;;
153
      Tue) adjust_date 1;;
154
      Wed) adjust_date 2;;
155
      Thu) adjust_date 3;;
156
      Fri) adjust_date 4;;
157
      Sat) adjust_date 5;;
158
      Sun) adjust_date 6;;
159
      *) ;;
160
   esac
161
else 
162
   case $WEEKDAY in
163
      Sun) adjust_date 0;;
164
      Mon) adjust_date 1;;
165
      Tue) adjust_date 2;;
166
      Wed) adjust_date 3;;
167
      Thu) adjust_date 4;;
168
      Fri) adjust_date 5;;
169
      Sat) adjust_date 6;;
170
      *) ;;
171
   esac
172
fi
173
 
174
mk_auto_dump(){
175
 
176
   local fsname=$1
177
 
178
   # get the current filesystem's path
179
   # as defined in the corresponding variable
180
   eval "local fspath=\$${fsname}path"
181
 
182
   # composite the dump path
183
   local dumppath=${BASEDIR}/${fsname}/${YWEEK}
184
 
185
   # make sure the dump directory for this week exists;
186
   # this automatically creates a new dump directory on 
187
   # every Monday or Sunday (as selected by 'WEEKSTARTS')
188
   [ ! -d $dumppath ] && mkdir -p $dumppath
189
 
190
   # get name of the last file in the current dump directory
191
   local lastfile=`ls -ltr $dumppath | grep -v "^d" | tail -n 1 | awk '{ print $9 }'`
192
 
193
   # assume that the 'lastfile', if it exists, was not created today
194
   local dumped_today=false
195
 
196
   # if a file exists, check its modification date; 
197
   # if it is at or after 00:00 today, set a flag to skip the dump
198
   if [ "$lastfile" != "" ] ; then
199
      local fq_lastfile=${dumppath}/$lastfile
200
      if [ -e $fq_lastfile ] ; then
201
         # get the last modification time for the most recently created dumpfile
202
         LASTDUMP=`stat -f %m $fq_lastfile`
203
         if [ $LASTDUMP -ge $TODAYSTARTSTAMP ] ; then
204
            local dumped_today=true
205
         fi
206
      fi
207
 
208
      # get the first and the last dump level for this directory
209
      local levelcommand="ls $dumppath | sed -e 's/^[[:digit:]]*\_//' | sed -e 's/\..*$//'"
210
      local firstlevel=`eval $levelcommand | head -n 1`
211
      local lastlevel=`eval $levelcommand | tail -n 1`
212
 
213
      # make sure level zero dump exists;
214
      # if it doesn't, start over
215
      if [ "$firstlevel" != "0" ] ; then
216
         # it doesn't matter if a previous dump exists from today
217
         # since we're starting over as level 0 dump is missing
218
         local dumped_today=false
219
         local dumplevel=0
220
         rm -f $dumppath/*.gz
221
      else
222
         # otherwise just increment the dump level
223
         # for levels 1-6, i.e. normally Tuesday thru Sunday
224
         local dumplevel=`expr $lastlevel "+" 1`
225
      fi
226
   else
227
      # no dump exists in this week's folder; reset level to '0'
228
      local dumplevel=0
229
   fi
230
 
231
   # skip the entire dump process if a dumpfile has
232
   # already been created for this filesystem today
233
   if [ "$dumped_today" = "false" ] ; then  
234
 
235
      # define the dump filename
236
      local dumpfn=${DATE}_${dumplevel}
237
 
238
      echo ---------------- >> $LOGFILE
239
      echo >> $LOGFILE
240
      echo BEGINNING LEVEL $dumplevel DUMP OF \'$fsname\' \(${fspath}\) FILESYSTEM ON $HUMANDATE AT $HUMANTIME >> $LOGFILE
241
      echo >> $LOGFILE
242
      echo Creating a snapshot of \'$fspath\'.. >> $LOGFILE
243
      # execute the dump
244
      $DUMP -$dumplevel -$GLOBALDUMPOPTS -f ${dumppath}/${dumpfn} $fspath >> $LOGFILE 2>&1
245
      local dumpresult=$?
246
 
247
      if [ "$dumpresult" != "0" ] ; then
248
         # log the dump result to syslog
249
         $LOGGER "$DUMP LEVEL $dumplevel DUMP OF $fsname (${fspath}) FAILED!"
250
 
251
         echo "*** DUMP FAILED - LEVEL $dumplevel DUMP of $fsname (${fspath}) ***" >> $LOGFILE
252
         echo >> $LOGFILE
253
      else
254
         # log the dump result to syslog
255
         $LOGGER "LEVEL $dumplevel DUMP of $fsname (${fspath}) COMPLETED SUCCESSFULLY!"
256
 
257
         echo >> $LOGFILE
258
         # compress the dump
259
         echo Compressing the dumpfile \'${dumpfn}\'.. >> $LOGFILE
260
         $GZIP -v ${dumppath}/${dumpfn} >> $LOGFILE 2>&1
261
         echo DONE >> $LOGFILE
262
         echo >> $LOGFILE
263
 
264
         # make sure dumps are not world readable (security risk!)
265
         echo Updating dumpfile \'${dumpfn}.gz\' permissions.. >> $LOGFILE
266
         chmod -v -v 440 ${dumppath}/${dumpfn}.gz >> $LOGFILE 2>&1
267
         echo DONE >> $LOGFILE
268
         echo >> $LOGFILE
269
 
270
         # reset current dump dir's timestamp to that of the level 0 dump
271
         touch -t ${NEWEPOCHISO} ${dumppath}
272
 
273
         # delete old dumps
274
         echo Deleting old \'$fsname\' dumpfiles.. >> $LOGFILE
275
         find $BASEDIR/$fsname -mtime +$KEEPDUMPS -maxdepth 1 -print -exec rm -rf {} \; >> $LOGFILE 2>&1
276
         echo DONE >> $LOGFILE
277
         echo >> $LOGFILE
278
      fi
279
   else
280
      local lastdump_readable=`date -j -r $LASTDUMP +"%H:%M"`
281
      local lastdump_readableZ=`date -j -r $LASTDUMP +"%Z"`
282
      local lastdumpmsg="Autodump for filesystem '$fsname' ($fspath) has already been executed today at $lastdump_readable ($lastdump_readableZ)."
283
      echo $lastdumpmsg
284
      $LOGGER $lastdumpmsg
285
   fi
286
}
287
 
288
 
289
# Dump filesystems defined in 'FSNAMES'
290
#
291
# Monday or Sunday (as selected by 'WEEKSTARTS') starts with 
292
# the level 0 dump, with incrementals created through the rest of 
293
# the week (autoincremented). If the level 0 dump is missing in 
294
# the current week's folder for filesystem currently being backed 
295
# up, it is created automatically instead of an incremental dump, 
296
# no matter what day of the week it is.
297
for f in $FSNAMES
298
do
299
   mk_auto_dump $f
300
done
301

, , , , , ,

10 Comments

FreeBSD vs the world

As I upgraded few FreeBSD installations to FreeBSD 7.2 over the last couple of days, I took the customary stroll to see how FreeBSD continues to stack up against the Linux distributions.  And once again I determined it does so very well.  I’ve been a devout FreeBSD user for almost a decade, and every time I take a look at the Linux world I come back to the same conclusion: I like the fact that there is just one FreeBSD. It’s very well managed and its QA is excellent (not to mention its TCP stack is famed for being the most stable, and its ports collection rivals anything offered by Linux).

Here’re couple of useful sites for those wondering which OS to choose:

Polishlinux.org – Compare distros: FreeBSD vs. Debian – Comparison data is up to date and there are a lot of good user comments to sift through. You can also choose other distros to compare to.

Wikipedia – Comparison of BSD operating systems

And lastly, a good example of why the sheer number of Linux distros is disorienting: DistroWatch lists at least a few hundred Linux distros (plus couple of BSD derivatives).

, ,

No Comments

Installing bcron on FreeBSD 7.0

bcron is a better cron (though the “b” in the name probably comes from the first name of its writer, Bruce Guenter).  It was created with security in mind, and is especially well suited for multi-user systems where the individual users need to be given access to their respective crontabs. With bcron this can be accomplished without compromising the system security.  Here’s a quote from the bcron page:

This is bcron, a new cron system designed with secure operations in mind. To do this, the system is divided into several seperate programs, each responsible for a seperate task, with strictly controlled communications between them. The user interface is a drop-in replacement for similar systems (such as vixie-cron), but the internals differ greatly.

As of writing of this bcron can not be found in the FreeBSD 7.0 ports system. Fortunately its installation is fairly straightforward.  Yet the included documentation is rather spartan so I provide a more complete outline below.

  1. Install latest bglibs if not yet installed** bglibs is best to install from a downloaded tarball rather than from the ports (while the ports version installs the libs in a more logical location at /usr/local/lib/bglibs/ the programs that utilize the library (bcron, ucspi-unix, etc.) have difficulty locating it.

    ** few symlinks are required (these refer to the locations bglibs installs itself when compiled from the tarball rather than from the ports):

    /usr/local/bglibs -> /usr/local/lib/bglibs
    /usr/local/bglibs/lib/libbg-sysdeps.so.2 -> /usr/local/lib/libbg-sysdeps.so.2
    /usr/local/bglibs/lib/libbg.so.2 -> /usr/local/lib/libbg.so.2

  2. Install ucspi-unix if not yet installed as bcron components communicate via UNIX sockets. This requires bglibs and also compiles and installs well using a downloaded tarball (it’s also available in ports at /usr/ports/sysutils/ucspi-unix, but I prefer to compile it from the downloaded tarball).
  3. Make sure /var has been moved off the root to /usr/var before proceeding. See an older post for details.
  4. Make sure daemontools (and hence supervise) has been installed and is operational as bcron will be started with it.
  5. Create a system user “cron” (for example by using vipw command) and group “cron” (by editing /etc/group). This user/group will own all the crontab files (though not /etc/crontab as it’s system crontab and needs to be owned by root:wheel).

    user:

    cron:*:50:50::0:0:BCron Sandbox:/nonexistent:/usr/sbin/nologin

    group:
    cron:*:50:

  6. Create the spool & tmp directories:
    mkdir -p /var/spool/cron/crontabs /var/spool/cron/tmp
    mkfifo /var/spool/cron/trigger
    sh
    for i in crontabs tmp trigger; do
    chown cron:cron /var/spool/cron/$i
    chmod go-rwx /var/spool/cron/$i
    done
  7. Create the configuration directory /usr/local/etc/bcron:mkdir -p /usr/local/etc/bcron** You can put any common configuration settings into this directory (it is an “ENVDIR”), like alternate spool directories in BCRON_SPOOL.
  8. Create the bcron service directories (there are three services) and add the scripts below it:

    mkdir -p /var/bcron/supervise/bcron-sched/log
    mkdir /var/bcron/supervise/bcron-spool
    mkdir /var/bcron/supervise/bcron-update

    Set their permissions to 1750 for security purposes (no world access, sticky bit):

    chmod 1750 /var/bcron/supervise/bcron-sched
    chmod 1750 /var/bcron/supervise/bcron-spool
    chmod 1750 /var/bcron/supervise/bcron-update

    Make all the run and log/run scripts executable by root, readable by group:

    chmod 740 /var/bcron/supervise/bcron-sched/run
    chmod 740 /var/bcron/supervise/bcron-sched/log/run
    chmod 740 /var/bcron/supervise/bcron-spool/run
    chmod 740 /var/bcron/supervise/bcron-update/run

    and make log bcron-sched subdir accessible by root, group:

    chmod 750 /var/bcron/supervise/bcron-sched/log

    RUN SCRIPTS:
    /var/bcron/supervise/bcron-sched/run:

    #!/bin/sh
    exec 2>&1
    exec envdir /usr/local/etc/bcron bcron-start | multilog t /var/log/bcron

    /var/bcron/supervise/bcron-sched/log/run:

    #!/bin/sh
    exec >/dev/null 2>&1
    exec \
    multilog t /var/log/bcron

    /var/bcron/supervise/bcron-spool/run:

    #!/bin/sh
    exec >/dev/null 2>&1
    exec \
    envdir /usr/local/etc/bcron \
    envuidgid cron \
    sh -c ‘
    exec \
    unixserver -U ${BCRON_SOCKET:-/var/run/bcron-spool} \
    bcron-spool

    /var/bcron/supervise/bcron-update/run:

    #!/bin/sh
    exec >/dev/null 2>&1
    exec \
    bcron-update /etc/crontab

  9. Kill the deafult cron daemon and add the following to rc.conf so it won’t restart on reboot:

    #disable default cron; bcron is used instead (started by supervise)
    cron_enable=”NO”

  10. Symlink bcron services’ primary supervise directories to under /var/service to start bcron services (you can also use svc-add command if you have installed supervise-scripts):
    ln -s /var/bcron/supervise/bcron-sched /var/service/bcron-sched
    ln -s /var/bcron/supervise/bcron-spool /var/service/bcron-spool
    ln -s /var/bcron/supervise/bcron-update /var/service/bcron-update
  11. Set /etc/crontab permissions to 600, and make sure it’s owned by the root.
    chmod 600 /etc/crontab
    chown root:wheel /etc/crontab

    ** For other users the owner of the crontab file in their respective home folders would be cron:cron.

  12. Edit /etc/crontab and test that it gets updated. Note that there is a brief delay, perhaps one minute or so, after you save the crontab until the change becomes effective. Also note that the default shell for the crontab is /bin/sh. You might want to change it to something more powerful like c-shell (/bin/csh) or bash (/bin/bash) that you’re familiar with. You may also want to augment the default path, for example, by including /usr/local/bin for user-installed commands.

, ,

No Comments