Never been to CodeSnippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world (or not, you can keep them private!)

Mac OS X - Advanced Computer(Re)Name via MySQL

Hope this helps someone :) I'm all new to this and I had a blast making it, let me know what you think!

Extended (commented) version:
http://oldgreenhorn.blogspot.com/2009/06/mac-os-x-advanced-computerrename-via.html


mysql_computername.sh -
#!/bin/bash
# Script:		mysql_computername.sh
# Version:		1.0
# Description:		Rename computers dynamically on system startup.
#			Variables are pulled from a MySQL database for
#			easier management.
#
# Developed by: 	Mark Carver © 2009
# Email:		mark_carver@jsd.k12.ak.us
# Website:		http://oldgreenhorn.blogspot.com

export PATH=${PATH}:/usr/local/mysql/bin

mysql_host='localhost'
mysql_user='ReadOnlyUser'
mysql_password='YoUrPaSsWoRd'
mysql_db='database_name'
mysql_connection="mysql -s -N -w --connect_timeout=5 -h ${mysql_host} -u ${mysql_user} --password=${mysql_password} ${mysql_db}"

i=0
while [ $i -lt 30 ]
do
    if eval "ping -c 1 -q ${mysql_host}"; then
		internet='connected'
        break
    else
     	sleep 10
		let i+=1
    fi
done

if [ "${internet}" == 'connected' ]; then
	m=0
	while [ $m -lt 30 ]
	do
		if eval "${mysql_connection} -e \"SHOW DATABASES\""; then
			mysql_status='connected'
			break
		else
			sleep 10
			let m+=1
		fi
	done
	if [ "${mysql_status}" == 'connected' ]; then
		template=`${mysql_connection} -e "SELECT value FROM settings WHERE attribute='template'"`
		mac=`ifconfig en0 | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}' | tr -d ':' | tr 'a-z' 'A-Z'`
		#mac="001FF3D0A560"
		exists=`${mysql_connection} -e "SELECT ethernet FROM computers WHERE ethernet='${mac}'"`		
		if [ "${exists}" != '' ]; then
			fas=`${mysql_connection} -e "SELECT fas FROM computers WHERE ethernet='${mac}'"`
			last_name=`${mysql_connection} -e "SELECT last_name FROM users WHERE fas='${fas}'"`
			first_name=`${mysql_connection} -e "SELECT first_name FROM users WHERE fas='${fas}'"`
			school=`${mysql_connection} -e "SELECT school FROM computers WHERE fas='${fas}'"`
			team=`${mysql_connection} -e "SELECT team FROM users WHERE fas='${fas}'"`
			cart=`${mysql_connection} -e "SELECT cart FROM computers WHERE fas='${fas}'"`
			slot=`${mysql_connection} -e "SELECT slot FROM computers WHERE fas='${fas}'"`
			if [ "${cart}" != "" ] && [ "${slot}" != "" ]
			then
				fas="${cart}${slot}-${fas}"
			fi
	
			if [ "${last_name}" != "" ] || [ "${first_name}" != "" ]; then
				lastname_first="- ${last_name}, ${first_name}"
			else
				lastname_first=""
			fi
			computer_name=`eval echo ${template}`
			computer_name=`echo ${computer_name} | tr -s " "`
			local_hostname=`echo ${computer_name} | tr -s "[:punct:]" "-" | tr -c -s '\12\60-\172' "-" | tr 'A-Z' 'a-z' | sed 's/-$//'`
		   	scutil --set ComputerName "${computer_name}"
			scutil --set LocalHostName "${local_hostname}"
			exit 0		
		else
			logger -s "The MAC address: ${mac} does not exist in the database. Will try again next startup/login."
			exit 0
		fi
	else
		logger -s "MySQL connection timeout reached. Failed connecting to MySQL database: ${mysql_db} at ${mysql_host}. Will try again next startup/login."
	fi
else
	logger -s "Host connection timeout reached. Failed connecting to host: ${mysql_host}. Will try again next startup/login."
fi

Save in /private/var/root/Desktop or wherever, just make sure to change appropriate paths.



com.carver.mark.acn.plist -
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>ServiceIPC</key>
	<true/>
	<key>Label</key>
	<string>com.carver.mark.acn</string>
	<key>ProgramArguments</key>
	<array>
		<string>/private/var/root/Desktop/mysql_computername.sh</string>
	</array>
	<key>RunAtLoad</key>
	<true/>
	<key>StartOnMount</key>
	<false/>
</dict>
</plist>

Save in /Library/LaunchDaemons (mandatory)

Test ownership of launchd processes

The following launchd item will execute a simple script every 20 seconds to test or show the ownership of the processes executed by the script. In addition, the script will print out the names and values of its shell environment variables.

To set up the launchd item log in to an admin user account. Use at your own risk.

# LAUNCHD ITEM

/usr/bin/sudo /bin/bash -c '

yourname=$(/usr/bin/logname)
LaunchdPlistFile="/Library/LaunchDaemons/user.${yourname}.launchd.test.plist"
EX_MARK='!'

/bin/cat > "${LaunchdPlistFile}" <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<${EX_MARK}DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Disabled</key>
        <true/>
        <key>Debug</key>
        <true/>


        <key>UserName</key>
        <string>${yourname}</string>
        <key>GroupName</key>
        <string>${yourname}</string>


        <key>EnvironmentVariables</key>
        <dict>
                <key>PATH</key>
                <string>/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin</string>
                <key>YetAnotherEnvironmentVar</key>
                <string>/path/to/dir</string>
        </dict>
        <key>Label</key>
        <string>user.${yourname}.launchd.test</string>
        <key>ProgramArguments</key>
        <array>
                <string>/Users/${yourname}/Desktop/launchd_test.sh</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>ServiceDescription</key>
        <string>Test ownership of launchd processes</string>
        <key>StandardErrorPath</key>
        <string>/Users/${yourname}/Desktop/launchd_test_error.log</string>
        <key>StandardOutPath</key>
        <string>/Users/${yourname}/Desktop/launchd_test.log</string>
        <key>StartInterval</key>
        <integer>20</integer>
        <key>Umask</key>
        <integer>002</integer>
</dict>
</plist>
EOF
'



: <<-'COMMENT'

# switch UserName & GroupName

        <key>UserName</key>
        <string>${yourname}</string>
        <key>GroupName</key>
        <string>${yourname}</string>
or
        <key>UserName</key>
        <string>root</string>
        <key>GroupName</key>
        <string>wheel</string>

or ...

COMMENT


yourname=$(/usr/bin/logname)

LaunchdPlistFile="/Library/LaunchDaemons/user.${yourname}.launchd.test.plist"

open -e "${LaunchdPlistFile}"


# SCRIPT

LaunchdTestScript="/Users/${yourname}/Desktop/launchd_test.sh"

/bin/cat > "${LaunchdTestScript}" <<-'EOF'
#!/bin/bash
# /bin/sh

# cf. man 5 launchd.plist | less -p 'A daemon or agent launched by launchd SHOULD:'
trap '/usr/bin/logger -i "received SIGTERM for launchd test"; exit 1' TERM

# write stdout & stderr to console.log in /Library/Logs/Console/
# open -a Console
# (requires /bin/bash to work correctly!)
#exec >/dev/console 2>&1

echo
echo "SYSTEM LOGS:"
/usr/bin/logger -i "logger:  $0"
/usr/bin/syslog -s -l 1 "syslog:  $0"
echo

echo
echo 'id:'
echo
/usr/bin/id | /usr/bin/sed -E \
   -e $'s/,? /\\\n/g' \
   -e $'/groups=/s/groups=/groups:\\\n/' \
   -e 's/=/ = /g' \
   -e $'s/[()]/   /g'
echo

# cf. man mail | less -p HOME
# cf. Read local Unix mail in Mail.app, http://codesnippets.joyent.com/posts/show/1392
#export HOME=/Users/$(/usr/bin/whoami)
#echo "hello world $(date)" | /usr/bin/mail -s 'test mail' $(/usr/bin/whoami)@localhost

echo
echo "SHELL: $SHELL"
echo "BASH: $BASH"
echo
echo "current set of shell flags: $-"    # cf. help set
echo
echo "SHELLOPTS: $SHELLOPTS"
echo
echo 'set:'
echo
set
echo
printf "%q  %q\n" "IFS:" "$IFS"
echo
echo 'env:'
echo
/usr/bin/env
echo
echo
echo 'shopt ... on:'
echo
shopt | /usr/bin/egrep 'on$'
#set -o | /usr/bin/egrep 'on$'
echo


cd ~
echo "HOME: $PWD"
echo
echo 'try to cd to /private/var/launchd/0'
cd /private/var/launchd/0
return_code=$?
echo "cd exit status: ${return_code}"
echo
if [[ ${return_code} -eq 0 ]]; then echo "ROOT PRIVILEGES"; else echo "NO ROOT PRIVILEGES"; fi
echo

echo "PPID:  $PPID"
echo
echo "PID:  $$"
echo

echo 'ps -p PID:'
echo
/bin/ps -p $PPID
echo
/bin/ps -p $$
echo
echo 'lsof -p PID:'
echo
/usr/sbin/lsof -p $PPID 2>/dev/null
echo
/usr/sbin/lsof -p $$ 2>/dev/null
echo

echo "users:  $(/usr/bin/users)"
echo
echo "groups:  $(/usr/bin/groups)"    # /usr/bin/id -Gn
echo
echo "logname:  $(/usr/bin/logname)"
echo
echo "whoami:  $(/usr/bin/whoami)"   # /usr/bin/id -un


echo; echo
echo "#----------------------------------------------------------------------------------------------- $(date)"
echo; echo

exit 0
#exit 1

EOF



open -e "${LaunchdPlistFile}"
open -e "${LaunchdTestScript}"

/usr/bin/sudo /usr/sbin/chown root:wheel "${LaunchdPlistFile}"
/usr/bin/sudo /bin/chmod 0644 "${LaunchdPlistFile}"

/usr/sbin/chown ${yourname}:${yourname} "${LaunchdTestScript}"
/bin/chmod u+x "${LaunchdTestScript}"

ls -l "${LaunchdPlistFile}"
ls -l "${LaunchdTestScript}"


# enable launchd item
/usr/bin/sudo /bin/launchctl load -w "${LaunchdPlistFile}" 2>/dev/null

# disable launchd item
/usr/bin/sudo /bin/launchctl unload -w "${LaunchdPlistFile}" 2>/dev/null

ls -l /Users/${yourname}/Desktop/launchd_test.log
ls -l /Users/${yourname}/Desktop/launchd_test_error.log

open -a Console /Users/${yourname}/Desktop/launchd_test.log
#/usr/bin/srm -v /Users/${yourname}/Desktop/launchd_test.log

open -a Console /Users/${yourname}/Desktop/launchd_test_error.log


/usr/bin/sudo /bin/launchctl list
/usr/bin/sudo /usr/bin/fs_usage  | /usr/bin/egrep -i launchd_test


# convert possible tabs to spaces
#/usr/bin/expand "${LaunchdPlistFile}" > ~/Desktop/user.${yourname}.launchd.test.plist
#open -e ~/Desktop/user.${yourname}.launchd.test.plist


man 8 launchd
man 5 launchd.plist
man 5 launchd.conf
man 1 launchctl


#----------------------------------------------------------------


# set launchd log level to debug
# cf. http://www.puredarwin.org/developers/booting/launchd
# sudo launchctl log level debug
sudo bash -c 'echo "log level debug" >> /private/etc/launchd.conf'
echo 'log level debug' >> ${HOME}/.launchd.conf

sudo nano /private/etc/launchd.conf
ls -l /private/etc/launchd.conf


# send all launchd logging output to /private/var/log/launchd.log
# cf. http://developer.apple.com/technotes/tn2004/tn2124.html#SECLAUNCHDLOGGING
sudo cp -ip /etc/syslog.conf /etc/syslog.conf-orig
(cat /etc/syslog.conf-orig ; echo "launchd.* /var/log/launchd.log" ) | sudo cp /dev/stdin /etc/syslog.conf
sudo kill -HUP `cat /var/run/syslog.pid`

ls -1 /private/var/log/*log
ls -l /private/var/log/launchd.log

open -a Console /private/var/log/launchd.log


# disable launchd logging

# comment out: "log level debug"
sudo nano /private/etc/launchd.conf
nano ${HOME}/.launchd.conf

# comment out: "launchd.* /var/log/launchd.log"
sudo nano /private/etc/syslog.conf

sudo kill -HUP `cat /var/run/syslog.pid`


Further information on launchd:

- Getting started with launchd
- AFP548: launchd in Depth
- Wikipedia: launchd
- Mac OS Forge: launchd
- PureDarwin: launchd
- Mac OS X Debugging Magic: launchd
- Daemons and Agents
- Daemons and Agents: Execution Contexts
- Daemons and Agents: Hints and Tips
- Mac OS X Server 10.5: Setting a custom umask
- launchd gotcha
- Re: how to run scripts at shutdown - how does launchd shutdown a system?
- Starting PostgreSQL 8.3 through launchd on Mac OS X 10.5.1 Leopard
- The Future of Init, Part IIb: OS X Launchd

Record your dynamic WAN IP addresses

A launchd + shell script exercise to record your DSL router's dynamic WAN IP addresses. Requires some customization on your part. Use at your own risk.

1. create the launchd item in /Library/LaunchDaemons

/usr/bin/sudo /bin/bash -c '

yourname=$(/usr/bin/logname)
LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist"
EX_MARK='!'

/bin/cat > "${LaunchdPlistFile}" <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<${EX_MARK}DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
   <dict>
      <key>Disabled</key>
      <true/>
      <key>GroupName</key>
      <string>${yourname}</string>
      <key>Label</key>
      <string>net.${yourname}.wanip.update</string>
      <key>ProgramArguments</key>
      <array>
         <string>/Users/${yourname}/Library/wanip.sh</string>
      </array>
      <key>RunAtLoad</key>
      <true/>
      <key>StartInterval</key>
      <integer>20</integer>
      <key>UserName</key>
      <string>${yourname}</string>
   </dict>
</plist>
EOF
'


#-------------------------


yourname=$(/usr/bin/logname)
LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist"

open -e "${LaunchdPlistFile}"
ls -l "${LaunchdPlistFile}"

/usr/bin/groups
/usr/bin/sudo /usr/sbin/chown root:wheel "${LaunchdPlistFile}"
#/usr/bin/sudo /usr/sbin/chown root:admin "${LaunchdPlistFile}"
/usr/bin/sudo /bin/chmod 0644 "${LaunchdPlistFile}"

# after creating ~/Library/wanip.sh below
/usr/bin/sudo /bin/launchctl load -w "${LaunchdPlistFile}" 2>/dev/null
#/usr/bin/sudo /bin/launchctl unload -w "${LaunchdPlistFile}" 2>/dev/null

/usr/bin/sudo /bin/launchctl list
/usr/bin/sudo /usr/bin/fs_usage | /usr/bin/egrep -i wanip


2. create a shell script that will be run by the launchd item at the specified intervals in seconds (here: every 20 seconds)

Version 1:
#!/bin/bash

# Version 1 with two columns (date, IP address)
# cat ~/Library/wanip.sh   (/Users/yourname/Library/wanip.sh)
# /usr/sbin/chown $(/usr/bin/logname):$(/usr/bin/logname) ~/Library/wanip.sh
# /bin/chmod 0744 ~/Library/wanip.sh

declare last_line_closed last_line_offline last_line_unreachable newfile old_wanip time wanip

declare IF='en0'
declare wanip_record_file="/Users/YOURLOGNAME/Library/wanip_record.txt"

# try to find your router_wanip_site by surfing to the IP addresses returned by the following commands:
# route -n get default | egrep interface | awk '{print $NF}'
# ipconfig getoption en0 router
# ipconfig getoption en0 domain_name_server

declare router_wanip_site='http://xxxx.xx/xxx.htm'
#declare router_wanip_site='http://checkip.dyndns.org'   # alternative


/bin/sleep 3

/usr/sbin/ipconfig waitall

if [[ "$(/sbin/route -n get default | /usr/bin/egrep interface | /usr/bin/awk '{print $NF}')" == "${IF}" ]]; then

   /usr/bin/curl -I -L -s --max-time 10 "${router_wanip_site}" 1>/dev/null
 
   if [[ $? -ne 0 ]]; then 
      time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
      /usr/bin/logger -i "${time}     router_wanip_site is unreachable for wanip.sh"
      last_line_unreachable="$(/usr/bin/sed -E -n -e '$,$s/^.+ (unreachable).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "$last_line_unreachable" ]]; then exit 0; fi
      echo "${time}          router_wanip_site is unreachable" >> "${wanip_record_file}"
      exit 0
   fi


   # match first IP address with egrep
   wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              /usr/bin/egrep -o -m 1 ' ([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}')"
   wanip="${wanip// /}"

   # alternative with sed for matching a line with a characteristic string plus IP address
   #wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              #/usr/bin/sed -E -n -e '/STRING: /{s/^.+ ([[:digit:]\.]+).*$/\1/p;q;}')"


   time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"

   if [[ -n "${wanip}" ]]; then 
      old_wanip="$(/usr/bin/sed -E -n -e '$,$s/^.+ ([[:digit:]\.]+).*$/\1/p' "${wanip_record_file}")"
      if [[ "${wanip}" == "${old_wanip}" ]]; then exit 0; fi
      echo "${time}          ${wanip}" >> "${wanip_record_file}"
   else
      last_line_closed="$(/usr/bin/sed -E -n -e '$,$s/^.+ (closed).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "${last_line_closed}" ]]; then exit 0; fi
      echo "${time}          connection closed" >> "${wanip_record_file}"
   fi

else

   last_line_offline="$(/usr/bin/sed -E -n -e '$,$s/^.+ (offline).*$/\1/p' "${wanip_record_file}")"
   if [[ -n "$last_line_offline" ]]; then exit 0; fi
   time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
   echo "${time}          offline" >> "${wanip_record_file}"

fi

if [[ $(/usr/bin/stat -f %z "${wanip_record_file}") -gt 31457280 ]]; then
   newfile="${wanip_record_file}-$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
   /bin/mv "${wanip_record_file}" "${newfile}"
fi

exit 0


Version 2:
#!/bin/bash

# Version 2 has an additional $name column
# cat ~/Library/wanip.sh   (/Users/yourname/Library/wanip.sh)
# /usr/sbin/chown $(/usr/bin/logname):$(/usr/bin/logname) ~/Library/wanip.sh
# /bin/chmod 0744 ~/Library/wanip.sh

declare format last_line_closed last_line_offline last_line_unreachable name newfile old_name old_wanip time wanip

declare IF='en0'
declare wanip_record_file="/Users/YOURLOGNAME/Library/wanip_record.txt"

# try to find your router_wanip_site by surfing to the IP addresses returned by the following commands:
# route -n get default | egrep interface | awk '{print $NF}'
# ipconfig getoption en0 router
# ipconfig getoption en0 domain_name_server

declare router_wanip_site='http://xxxx.xx/xxx.htm'
#declare router_wanip_site='http://checkip.dyndns.org'   # alternative

name="$(/usr/bin/who | /usr/bin/awk '/console/ {print $1}')"
name="${name//[[:cntrl:]]/,}"
if [[ -z "${name}" ]]; then name='[logout]'; fi

old_name="$(/usr/bin/sed -E -n -e '$,$s/^[^ ]+ +([^ ]+) +[^ ].*$/\1/p' "${wanip_record_file}")"

format='%-35s%-20s%-20s\n'   # for printf

#/bin/sleep 3

/usr/sbin/ipconfig waitall

if [[ "$(/sbin/route -n get default | /usr/bin/egrep interface | /usr/bin/awk '{print $NF}')" == "${IF}" ]]; then

   /usr/bin/curl -I -L -s --max-time 10 "${router_wanip_site}" 1>/dev/null
 
   if [[ $? -ne 0 ]]; then 
      time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
      /usr/bin/logger -i "${time}     router_wanip_site is unreachable for wanip.sh"
      last_line_unreachable="$(/usr/bin/sed -E -n -e '$,$s/^.+ (unreachable).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "$last_line_unreachable" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}"
         fi
         exit 0
      fi
      printf "${format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}"
      exit 0
   fi


   # match first IP address with egrep
   wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              /usr/bin/egrep -o -m 1 ' ([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}')"
   wanip="${wanip// /}"

   # alternative with sed for matching a line with a characteristic string plus IP address
   #wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              #/usr/bin/sed -E -n -e '/STRING: /{s/^.+ ([[:digit:]\.]+).*$/\1/p;q;}')"


   time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"

   if [[ -n "${wanip}" ]]; then 
      old_wanip="$(/usr/bin/sed -E -n -e '$,$s/^.+ ([[:digit:]\.]+).*$/\1/p' "${wanip_record_file}")"
      if [[ "${wanip}" == "${old_wanip}" ]]; then
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}"
         fi
         exit 0
      fi

      printf "${format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}"
   else
      last_line_closed="$(/usr/bin/sed -E -n -e '$,$s/^.+ (closed).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "${last_line_closed}" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}"
         fi
         exit 0
      fi
      printf "${format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}"
   fi

else

   last_line_offline="$(/usr/bin/sed -E -n -e '$,$s/^.+ (offline).*$/\1/p' "${wanip_record_file}")"
   time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
   if [[ -n "$last_line_offline" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${format}" "${time}" "${name}" "offline" >> "${wanip_record_file}"
         fi
      exit 0
   fi
   printf "${format}" "${time}" "${name}" "offline" >> "${wanip_record_file}"

fi


if [[ $(/usr/bin/stat -f %z "${wanip_record_file}") -gt 31457280 ]]; then
   newfile="${wanip_record_file}-$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
   /bin/mv "${wanip_record_file}" "${newfile}"
fi

exit 0


Version 3:
/usr/bin/sudo /bin/bash -c '

yourname=$(/usr/bin/logname)
LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist"
EX_MARK='!'

/bin/cat > "${LaunchdPlistFile}" <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<${EX_MARK}DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Disabled</key>
        <true/>
        <key>GroupName</key>
        <string>${yourname}</string>
        <key>Label</key>
        <string>net.${yourname}.wanip.update</string>
        <key>ProgramArguments</key>
        <array>
            <string>/Users/${yourname}/Library/wanip.sh</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>UserName</key>
        <string>${yourname}</string>
</dict>
</plist>
EOF
'


#----------------------------------------------


yourname=$(/usr/bin/logname)
LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist"

open -e "${LaunchdPlistFile}"
ls -l "${LaunchdPlistFile}"

/usr/bin/groups
/usr/bin/sudo /usr/sbin/chown root:wheel "${LaunchdPlistFile}"
#/usr/bin/sudo /usr/sbin/chown root:admin "${LaunchdPlistFile}"
/usr/bin/sudo /bin/chmod 0644 "${LaunchdPlistFile}"

# after creating ~/Library/wanip.sh below
/usr/bin/sudo /bin/launchctl load -w "${LaunchdPlistFile}" 2>/dev/null
#/usr/bin/sudo /bin/launchctl unload -w "${LaunchdPlistFile}" 2>/dev/null

/usr/bin/sudo /bin/launchctl list
/usr/bin/sudo /usr/bin/fs_usage | /usr/bin/egrep -i wanip


#-----------------------------------------------------------------------------------------------------


#!/bin/bash

# Version 3 is started only once by the launchd item and records shutdown processes
# Inspired by: "Re: how to run scripts at shutdown - how does launchd shutdown a system?", 
#              http://lists.apple.com/archives/macos-x-server/2007/Oct/msg00021.html
# cat ~/Library/wanip.sh   (/Users/yourname/Library/wanip.sh)
# /usr/sbin/chown $(/usr/bin/logname):$(/usr/bin/logname) ~/Library/wanip.sh
# /bin/chmod 0744 ~/Library/wanip.sh

declare last_line_closed last_line_offline last_line_unreachable name 
declare newfile old_name old_wanip printf_format time time_format wanip

declare IF='en0'
declare wanip_record_file="/Users/YOURLOGNAME/Library/wanip_record.txt"

# try to find your router_wanip_site by surfing to the IP addresses returned by the following commands:
# route -n get default | egrep interface | awk '{print $NF}'
# ipconfig getoption en0 router
# ipconfig getoption en0 domain_name_server

declare router_wanip_site='http://xxxx.xx/xxx.htm'
#declare router_wanip_site='http://checkip.dyndns.org'   # alternative


WHILEVAR=1
TRAPSIGNAL=

function exit_function() {
   TRAPSIGNAL='yes'
   # $! holds the PID of last process that has been started in the background (cmd &)
   [[ $! -gt $$ ]] && kill -TERM $!
}

trap exit_function SIGHUP SIGINT SIGTERM


time_format='+%Y-%m-%d--%H.%M.%S--%Z'
printf_format='%-40s%-20s%-20s\n'


while [[ ${WHILEVAR} ]]; do

   name="$(/usr/bin/who | /usr/bin/awk '/console/ {print $1}')"
   name="${name//[[:cntrl:]]/,}"
   if [[ -z "${name}" ]]; then name='[logout]'; fi
   old_name="$(/usr/bin/sed -E -n -e '$,$s/^[^ ]+ +([^ ]+) +[^ ].*$/\1/p' "${wanip_record_file}")"
   /usr/sbin/ipconfig waitall

   /bin/sleep 20 &     # time interval
   wait $!

   if [[ ${TRAPSIGNAL} ]]; then
      time="$(/bin/date ${time_format})"
      printf "${printf_format}" "${time}" "${name}" "shutdown" >> "${wanip_record_file}"
      exit 0
   fi


if [[ "$(/sbin/route -n get default | /usr/bin/egrep interface | /usr/bin/awk '{print $NF}')" == "${IF}" ]]; then

   /usr/bin/curl -I -L -s --max-time 10 "${router_wanip_site}" 1>/dev/null
 
   if [[ $? -ne 0 ]]; then 
      time="$(/bin/date ${time_format})"
      /usr/bin/logger -i "${time}     router_wanip_site is unreachable for wanip.sh"
      last_line_unreachable="$(/usr/bin/sed -E -n -e '$,$s/^.+ (unreachable).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "$last_line_unreachable" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${printf_format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}"
         fi
         continue
      fi
      printf "${printf_format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}"
      continue
   fi


   # match first IP address with egrep
   wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              /usr/bin/egrep -o -m 1 ' ([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}')"
   wanip="${wanip// /}"

   # alternative with sed for matching a line with a characteristic string plus IP address
   #wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              #/usr/bin/sed -E -n -e '/STRING: /{s/^.+ ([[:digit:]\.]+).*$/\1/p;q;}')"


   time="$(/bin/date ${time_format})"

   if [[ -n "${wanip}" ]]; then 
      old_wanip="$(/usr/bin/sed -E -n -e '$,$s/^.+ ([[:digit:]\.]+).*$/\1/p' "${wanip_record_file}")"
      if [[ "${wanip}" == "${old_wanip}" ]]; then
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${printf_format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}"
         fi
         continue
      fi
      printf "${printf_format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}"
   else
      last_line_closed="$(/usr/bin/sed -E -n -e '$,$s/^.+ (closed).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "${last_line_closed}" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${printf_format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}"
         fi
         continue
      fi
      printf "${printf_format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}"
   fi

else

   last_line_offline="$(/usr/bin/sed -E -n -e '$,$s/^.+ (offline).*$/\1/p' "${wanip_record_file}")"
   time="$(/bin/date ${time_format})"
   if [[ -n "$last_line_offline" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${printf_format}" "${time}" "${name}" "offline" >> "${wanip_record_file}"
         fi
      continue
   fi
   printf "${printf_format}" "${time}" "${name}" "offline" >> "${wanip_record_file}"

fi


if [[ $(/usr/bin/stat -f %z "${wanip_record_file}") -gt 31457280 ]]; then
   newfile="${wanip_record_file}-$(/bin/date ${time_format})"
   /bin/mv "${wanip_record_file}" "${newfile}"
fi

done

exit 0

Using PlistBuddy to customize syslogd


# Inspired by: http://www.maciverse.com/the-case-of-the-slow-mac-and-how-to-fix-it.html

# cf. man syslog, man syslogd and http://developer.apple.com/documentation/Darwin/Reference/ManPages/man8/syslogd.8.html
/usr/bin/open -a Console   # system.log
/usr/bin/tail -n 50 /private/var/log/system.log

help declare
declare -x sudo=/usr/bin/sudo plistbuddy=/usr/libexec/PlistBuddy defaults=/usr/bin/defaults
/usr/bin/env | /usr/bin/grep -E 'sudo=|plistbuddy=|defaults='

# create a symbolic link (on Mac OS X 10.4)
help test
/usr/bin/locate PlistBuddy
if [[ ! -e /usr/libexec/PlistBuddy ]]; then $sudo /bin/ln -s "/Library/Receipts/AdditionalEssentials.pkg/Contents/Resources/PlistBuddy" /usr/libexec/PlistBuddy; fi

# make a backup
$sudo /bin/cp -p /System/Library/LaunchDaemons/com.apple.syslogd.plist /System/Library/LaunchDaemons/com.apple.syslogd.plist.orig

# cf. http://developer.apple.com/documentation/Darwin/Reference/ManPages/man8/PlistBuddy.8.html
$plistbuddy -h   

$plistbuddy -c Print /System/Library/LaunchDaemons/com.apple.syslogd.plist
$plistbuddy -c "Print :ProgramArguments" /System/Library/LaunchDaemons/com.apple.syslogd.plist

# compare
$defaults read /System/Library/LaunchDaemons/com.apple.syslogd
$defaults read /System/Library/LaunchDaemons/com.apple.syslogd ProgramArguments

$sudo /usr/bin/nano /System/Library/LaunchDaemons/com.apple.syslogd.plist

$sudo $plistbuddy -c "Delete :ProgramArguments" /System/Library/LaunchDaemons/com.apple.syslogd.plist
$sudo $plistbuddy -c "Add :ProgramArguments array" /System/Library/LaunchDaemons/com.apple.syslogd.plist
$sudo $plistbuddy -c "Add :ProgramArguments:0 string '/usr/sbin/syslogd'" /System/Library/LaunchDaemons/com.apple.syslogd.plist
$sudo $plistbuddy -c "Add :ProgramArguments:1 string '-c'" /System/Library/LaunchDaemons/com.apple.syslogd.plist
$sudo $plistbuddy -c "Add :ProgramArguments:2 integer 3" /System/Library/LaunchDaemons/com.apple.syslogd.plist

# Mac OS X 10.5
#$sudo $plistbuddy -c "Add :ProgramArguments:3 string '-a'" /System/Library/LaunchDaemons/com.apple.syslogd.plist
#$sudo $plistbuddy -c "Add :ProgramArguments:4 string '-db_max'" /System/Library/LaunchDaemons/com.apple.syslogd.plist
#$sudo $plistbuddy -c "Add :ProgramArguments:5 integer 5000000" /System/Library/LaunchDaemons/com.apple.syslogd.plist

$plistbuddy -c "Print :ProgramArguments" /System/Library/LaunchDaemons/com.apple.syslogd.plist
$defaults read /System/Library/LaunchDaemons/com.apple.syslogd ProgramArguments
$sudo /usr/bin/nano /System/Library/LaunchDaemons/com.apple.syslogd.plist

# reset com.apple.syslogd.plist
#if [[ -n $(/usr/bin/sw_vers -productVersion | /usr/bin/grep '^10.5') ]]; then exit; fi   # exit if on Mac OS X 10.5.x
$sudo /bin/rm -f /System/Library/LaunchDaemons/com.apple.syslogd.plist
$sudo /bin/cp -p /System/Library/LaunchDaemons/com.apple.syslogd.plist.orig /System/Library/LaunchDaemons/com.apple.syslogd.plist

$sudo /usr/bin/nano /System/Library/LaunchDaemons/com.apple.syslogd.plist

# restart syslogd
#$sudo /bin/launchctl unload -w /System/Library/LaunchDaemons/com.apple.syslogd.plist 2>/dev/null
#/bin/sleep 3
#$sudo /bin/launchctl load -w /System/Library/LaunchDaemons/com.apple.syslogd.plist 2>/dev/null
#$sudo /usr/bin/killall -HUP syslogd   # alternative

Automated virus scanning with ClamAV on Mac OS X 10.4

Author: jv
License: The MIT License, Copyright (c) 2008 jv
Description: basic setup to scan files added to specified directories automatically for viruses using ClamAV; for instructions on how to run ClamAV from a system service agent account (non-root) see here; use at your own risk
Platform: Mac OS X 10.4.11 Client
Requirements: sudo port install clamav (after installing MacPorts); to fix the command search path, insert the following statement at the end of your ~/.bash_login file: export PATH="/opt/local/bin:/opt/local/sbin:$PATH" and then: source ~/.bash_login



# download ClamAV
sudo port install clamav      # requires open firewall port 873
#sudo port uninstall clamav

man clamd
man clamd.conf
man clamdscan
man clamscan
man freshclam


# configure /opt/local/etc/freshclam.conf

sudo cp -p /opt/local/etc/example-freshclam.conf /opt/local/etc/freshclam.conf
sudo sed -i "" -e 's/^Example/#Example/' /opt/local/etc/freshclam.conf              # comment out 'Example' line
#sudo sed -i "" -e 's/^#Example/Example/' /opt/local/etc/freshclam.conf              # uncomment 'Example' line
sudo nano /opt/local/etc/freshclam.conf


# make sure you are a member of the wheel and admin group
id -G -n $(whoami) | grep -Eo 'wheel|admin'
dseditgroup -o checkmember -m $(whoami) wheel; echo $?
dseditgroup $(whoami)
dseditgroup wheel
dseditgroup admin

sudo dscl . -append /Groups/wheel GroupMembership $(whoami)      # add user to group if necessary
#sudo dseditgroup -o edit -a $(whoami) -t user wheel             # add user to group
#sudo dscl . -delete /Groups/wheel GroupMembership $(whoami)     # delete user from group
#sudo dseditgroup -o edit -d $(whoami) -t user wheel             # delete user from group


sudo chown -R root:wheel /opt/local/share/clamav  
sudo chmod -R 0770 /opt/local/share/clamav

freshclam       # update virus database

# test some clamav commands
clamscan /path/to/file
sudo clamscan -r /tmp
sudo clamscan -r /private/var/tmp
clamscan -r ~/Library/Caches
clamscan -r ~/Library/Caches/java
sudo clamscan -r ~/Library/Mail
clamscan -r ~/Library


# configure /opt/local/etc/clamd.conf
# open /opt/local/etc
# sudo nano /opt/local/etc/clamd.conf
# cf. http://www.silvester.org.uk/OSX/configuring_clamd.html

sudo cp -p /opt/local/etc/clamd.conf /opt/local/etc/clamd.conf.orig

sudo sh -c '
cat << EOF > /opt/local/etc/clamd.conf
LogFile /private/var/log/clamd.log
LogFileMaxSize 10M
LogTime yes
TemporaryDirectory /private/var/tmp
DatabaseDirectory /opt/local/share/clamav
LocalSocket /tmp/clamd
FixStaleSocket yes
TCPAddr 127.0.0.1
MaxConnectionQueueLength 30
MaxThreads 20
ExitOnOOM yes
ScanOLE2 yes  # Microsoft Office documents and .msi files
ScanPDF yes
ArchiveMaxFileSize 100M
ArchiveMaxCompressionRatio 0
#VirusEvent echo virus: %v >> /path/to/file.txt
EOF
'

sudo chown root:wheel /opt/local/etc/clamd.conf
sudo chmod 750 /opt/local/etc/clamd.conf



Create the launchd item /Library/LaunchDaemons/net.clamav.clamd.plist


sudo nano /Library/LaunchDaemons/net.clamav.clamd.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
   <dict>
	<key>Disabled</key>
	<true/>
        <key>Label</key>
        <string>net.clamav.clamd</string>
        <key>ProgramArguments</key>
        <array>
                <string>/opt/local/sbin/clamd</string>
		<string>-c</string>
		<string>/opt/local/etc/clamd.conf</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>UserName</key>
        <string>root</string>
   </dict>
</plist>


sudo chown root:wheel /Library/LaunchDaemons/net.clamav.clamd.plist
sudo chmod 0644 /Library/LaunchDaemons/net.clamav.clamd.plist

sudo launchctl load -w /Library/LaunchDaemons/net.clamav.clamd.plist
#sudo launchctl unload -w /Library/LaunchDaemons/net.clamav.clamd.plist



Create watchdirs.sh


# create ClamAV directories
mkdir -p "$HOME/Documents/ClamAV/Quarantine"
mkdir -p "$HOME/Documents/ClamAV/Downloads"           # Safari -> Preferences ... -> Save downloaded files to: <directory>
mkdir -p "$HOME/Documents/ClamAV/EmailAttachments"
sudo chown -R $(whoami):wheel "$HOME/Documents/ClamAV"
sudo chmod -R 770 "$HOME/Documents/ClamAV"

# create a timestamp file
touch ~/.clamav_ timestamp
sudo chown $(whoami):wheel "$HOME/.clamav_timestamp"
sudo chmod 400 "$HOME/.clamav_timestamp"
stat -x $HOME/.clamav_timestamp


#---------------------------------


# Create a BASH script that will - controlled by ~/Library/LaunchAgents/net.clamav.dirwatcher.plist below - run 
# a clamdscan or clamscan command on files that have been changed or modified in the specified directories

# nano $HOME/Documents/ClamAV/watchdirs.sh

#!/bin/bash

# write stdout & stderr to console.log in /Library/Logs/Console/
# alternative: man logger
exec >/dev/console 2>&1   

echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"): ... WATCHDIRS.SH for ClamAV ... STARTED ...\n"


# All files added to the watched directories during the specified sleep period (in seconds) will be scanned for viruses.
# Files added to the directories while a virus scan is being done may not be included in the current virus scan, but
# they will get scanned next time a virus scan is scheduled to run which can, for example, be determined by the launch agent 
# variable StartInterval. Increase the value of the specified sleep period (in seconds) if you expect large files or 
# directories to be copied or downloaded to the watched directories.

/bin/sleep 60


SCANDIR1="$HOME/Documents/ClamAV/Downloads" 
SCANDIR2="$HOME/Documents/ClamAV/EmailAttachments"
QUARANTINEDIR="$HOME/Documents/ClamAV/Quarantine"
TOUCHFILE="$HOME/.clamav_timestamp"

find=/usr/bin/find
clamdscan=/opt/local/bin/clamdscan
clamscan=/opt/local/bin/clamscan

if [[ ! -e "$TOUCHFILE" ]]; then 
   /usr/bin/touch -afm "$TOUCHFILE"
   /usr/sbin/chown $(whoami):$(whoami) "$TOUCHFILE"
   /bin/chmod 400 "$TOUCHFILE"
   $clamdscan --copy="$QUARANTINEDIR" "$SCANDIR1" || $clamscan -i --copy="$QUARANTINEDIR" -r "$SCANDIR1"
   $clamdscan --copy="$QUARANTINEDIR" "$SCANDIR2" || $clamscan -i --copy="$QUARANTINEDIR" -r "$SCANDIR2"
   exit 0
fi


timestamp=$(/bin/date "+%Y%m%d%H%M.%S")    # store timestamp before starting the virus scan


if /bin/ps -ax | /usr/bin/grep clamd | /usr/bin/grep -v grep > /dev/null; then        

   # run clamdscan
   # scan all files that have been changed or modified after the $TOUCHFILE timestamp of the last virus scan
   # optional: $find -x "$SCANDIR1" "$SCANDIR2" -type f ... (but mind the 'sysctl kern.argmax' limit for xargs!)
   # optional: to exclude .DS_Store files add: \! -name ".DS_Store"

   # alternative: first find changed or modified directories
   # find -x "$SCANDIR1" "$SCANDIR2" -type d \( -newercm "$TOUCHFILE" -or -newermm "$TOUCHFILE" \) -print0 | while read -d $'\0' scandir; do echo "find -x $scandir -maxdepth 1 -type f ..."; done

   $find -x "$SCANDIR1" -type f \( -newercm "$TOUCHFILE" -or -newermm "$TOUCHFILE" \) -print0 | xargs -0 $clamdscan --copy="$QUARANTINEDIR"
   $find -x "$SCANDIR2" -type f \( -newercm "$TOUCHFILE" -or -newermm "$TOUCHFILE" \) -print0 | xargs -0 $clamdscan --copy="$QUARANTINEDIR"

else 

   # run clamscan
   $find -x "$SCANDIR1" -type f \( -newercm "$TOUCHFILE" -or -newermm "$TOUCHFILE" \) -print0 | xargs -0 $clamscan -i --copy="$QUARANTINEDIR"
   $find -x "$SCANDIR2" -type f \( -newercm "$TOUCHFILE" -or -newermm "$TOUCHFILE" \) -print0 | xargs -0 $clamscan -i --copy="$QUARANTINEDIR"

fi


# update the $TOUCHFILE timestamp with the pre-scan $timestamp
/usr/bin/touch -f -t $timestamp "$TOUCHFILE"    

echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"): ... WATCHDIRS.SH for ClamAV ... DONE ...\n"

exit 0


#---------------------------------


sudo chown root:wheel ~/Documents/ClamAV/watchdirs.sh 
sudo chmod 0770 ~/Documents/ClamAV/watchdirs.sh



Create the ~/Library/LaunchAgents/net.clamav.dirwatcher.plist launch agent


# nano ~/Library/LaunchAgents/net.clamav.dirwatcher.plist
# open -e ~/Library/LaunchAgents/net.clamav.dirwatcher.plist


# Note: Don't use $HOME or any other variables in the file paths you have to specify in the .plist files below! 
#       Use the full file paths without any variables!


# WatchPaths virus scanner launch agent (combined with StartInterval)
# scan the directories specified the same in watchdirs.sh and this .plist file when they are modified or after a specified time interval respectively

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Disabled</key>
	<true/>
	<key>Label</key>
	<string>net.clamav.dirwatcher</string>
	<key>LowPriorityIO</key>
	<true/>
	<key>OnDemand</key>
	<true/>
	<key>ProgramArguments</key>
	<array>
		<string>/full/path/to/Documents/ClamAV/watchdirs.sh</string>
	</array>
        <key>StartInterval</key>
        <integer>1800</integer>
	<key>WatchPaths</key>
	<array>
		<string>/full/path/to/Documents/ClamAV/Downloads</string>
		<string>/full/path/to/Documents/ClamAV/EmailAttachments</string>
	</array>
        <!-- optional: <key>Debug</key><true/> -->
	<!-- optional: <key>Nice</key><integer>1</integer> -->
</dict>
</plist>


#--------------------------------------------------------------------------------


# StartInterval virus scanner launch agent
# scan the directories specified in watchdirs.sh at the specified time intervals (given in seconds)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Disabled</key>
	<true/>
	<key>Label</key>
	<string>net.clamav.dirwatcher</string>
	<key>LowPriorityIO</key>
	<true/>
	<key>ProgramArguments</key>
	<array>
		<string>/full/path/to/Documents/ClamAV/watchdirs.sh</string>
	</array>
	<key>StartInterval</key>
	<integer>7200</integer>
        <!-- optional: <key>Debug</key><true/> -->
	<!-- optional: <key>Nice</key><integer>1</integer> -->
</dict>
</plist>


#--------------------------------------------------------------------------------


# StartCalendarInterval virus scanner launch agent
# scan the directories specified in watchdirs.sh at the specified times of the day

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Disabled</key>
	<true/>
	<key>Label</key>
	<string>net.clamav.dirwatcher</string>
	<key>LowPriorityIO</key>
	<true/>
	<key>ProgramArguments</key>
	<array>
		<string>/full/path/to/Documents/ClamAV/watchdirs.sh</string>
	</array>
	<key>StartCalendarInterval</key>
	<dict>
		<key>Hour</key>
		<integer>7</integer>
		<key>Minute</key>
		<integer>45</integer>
	</dict>
	<key>StartCalendarInterval</key>
	<dict>
		<key>Hour</key>
		<integer>12</integer>
		<key>Minute</key>
		<integer>30</integer>
	</dict>
        <!-- optional: <key>Debug</key><true/> -->
	<!-- optional: <key>Nice</key><integer>1</integer> -->
</dict>
</plist>


#--------------------------------------------------------------------------------


sudo chown root:wheel ~/Library/LaunchAgents/net.clamav.dirwatcher.plist 
sudo chmod 0770 ~/Library/LaunchAgents/net.clamav.dirwatcher.plist
ls -l ~/Library/LaunchAgents/net.clamav.dirwatcher.plist

launchctl load -w ~/Library/LaunchAgents/net.clamav.dirwatcher.plist
#launchctl unload -w ~/Library/LaunchAgents/net.clamav.dirwatcher.plist

sudo reboot

open -a Console

# update clamd virus database
freshclam; sleep 5; sudo clamd RELOAD



Alternative watchdirs.sh for a QueueDirectories virus scanner launch agent


# nano $HOME/Documents/ClamAV/watchdirs.sh

#!/bin/bash

# check downloaded files or directories in the specified $QueueDirectory for viruses and then move them to $MoveDir
# rename the files or folders to be moved to $MoveDir if necessary
# remove anything else from the $QueueDirectory

# write stdout & stderr to console.log in /Library/Logs/Console/
# alternative: man logger
exec >/dev/console 2>&1   

echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"):  ... WATCHDIRS.SH for ClamAV ... STARTED ...\n"

/bin/sleep 5

QUARANTINEDIR="$HOME/Documents/ClamAV/Quarantine"
TOUCHFILE="$HOME/.clamav_timestamp"

QueueDirectory="$HOME/Documents/ClamAV/Downloads/QueueDirectory"    # Safari -> Preferences ... -> Save downloaded files to: <directory>
/bin/mkdir -p "$QueueDirectory"

MoveDir="$HOME/Documents/ClamAV/Downloads"
/bin/mkdir -p "$MoveDir"

find=/usr/bin/find
clamdscan=/opt/local/bin/clamdscan
clamscan=/opt/local/bin/clamscan

if [[ -f "$QueueDirectory"/.DS_Store ]]; then /bin/rm -f "$QueueDirectory"/.DS_Store; fi


# alternative to $TOUCHFILE time stamp
#/usr/bin/touch ~/Desktop/test.txt
#t1=$(/bin/date +%s)
#/bin/sleep 1
#/usr/bin/touch -f -am ~/Desktop/test.txt   # ... file is being downloaded ...
#t2=$(/usr/bin/stat -f %m ~/Desktop/test.txt)
#echo $(($t1 - $t2))
#if [[ $t1 -lt $t2 ]]; then echo "file was modified"; else echo "file was not modified"; fi


# directory test
DirTest="$($find -x "$QueueDirectory" -type d -maxdepth 1 -not -regex "^$QueueDirectory$")"

if [[ -n "$DirTest" ]]; then           # download is directory

   $find -x "$QueueDirectory" -type d -maxdepth 1 -not -regex "^$QueueDirectory$" -print0 | while read -d $'\0' dir; do

      /usr/bin/touch -f -am "$TOUCHFILE"
      /bin/sleep 3

      unset -v file_modified
      file_modified=0                      # check if a file within $dir has been modified
      while read -d $'\0' file; do  
         # set file_modified to 1 if at least one file has been modified
         if [[ "$TOUCHFILE" -ot "$file" ]]; then file_modified=1; break; fi     
      done < <($find -x "$dir" -type f -print0 2>/dev/null)

      if [[ -d "$dir" ]] && [[ "$TOUCHFILE" -nt "$dir" ]] && [[ $file_modified -eq 0 ]]; then
         $clamdscan --no-summary --copy="$QUARANTINEDIR" "$dir" || $clamscan -i -r --no-summary --copy="$QUARANTINEDIR" "$dir"
         bname="$(/usr/bin/basename "$dir")"
         if [[ ! -d "$MoveDir/$bname" ]]; then
            /bin/mv "$dir" "$MoveDir" 
         else
            newMoveDir="$MoveDir/$(/bin/date "+%Y-%m-%d-%H%M.%S")-$bname"
            /bin/mv "$dir" "$newMoveDir" 
         fi  
      fi

   done

else           

   $find -x "$QueueDirectory" -type f -maxdepth 1 -print0 | while read -d $'\0' file; do      # download is file

      /usr/bin/touch -f -am "$TOUCHFILE"
      /bin/sleep 3

      if [[ -f "$file" ]] && [[ "$TOUCHFILE" -nt "$file" ]]; then
         $clamdscan --no-summary --copy="$QUARANTINEDIR" "$file" || $clamscan -i --no-summary --copy="$QUARANTINEDIR" "$file"
         # the mv command preserves metadata and resource forks of files on Extended HFS volumes (Mac OS X 10.4)

         bname="$(/usr/bin/basename "$file")"
         if [[ ! -f "$MoveDir/$bname" ]]; then
            /bin/mv "$file" "$MoveDir" 
         else
            newMoveDir="$MoveDir/$(/bin/date "+%Y-%m-%d-%H%M.%S")-$bname"
            /bin/mv "$file" "$newMoveDir" 
         fi  
      fi

   done

fi


if [[ -f "$QueueDirectory"/.DS_Store ]]; then /bin/rm -f "$QueueDirectory"/.DS_Store; fi

$find -x "$QueueDirectory" -not -type f -not -type d -print0 | while read -d $'\0' item; do /bin/rm -f "$item"; done

echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"):  ... WATCHDIRS.SH for ClamAV ... DONE ...\n"

exit 0


#------------------------------------

# nano ~/Library/LaunchAgents/net.clamav.dirwatcher.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Disabled</key>
	<true/>
	<key>Label</key>
	<string>net.clamav.dirwatcher</string>
	<key>ProgramArguments</key>
	<array>
		<string>/full/path/to/Documents/ClamAV/watchdirs.sh</string>
	</array>
	<key>QueueDirectories</key>
	<array>
		<string>/full/path/to/Documents/ClamAV/Downloads/QueueDirectory</string>
	</array>
</dict>
</plist>



Update clamd virus database automatically


# set permissions
#sudo chown -R root:wheel /opt
#sudo chmod -R 755  /opt
#sudo chown -R root:wheel /opt/local/share/clamav  
#sudo chmod -R 0770 /opt/local/share/clamav


# bash script to update the clamd virus database
# sudo nano /usr/local/sbin/update_clamd_db.sh

#!/bin/bash

/bin/sleep 120

# write stdout & stderr to console.log
# alternative: man logger
exec >/dev/console 2>&1   

# check if internet connection is alive and database.clamav.net is reachable
/usr/bin/curl -I -L -s --max-time 15 database.clamav.net 1>/dev/null   


if [[ $(echo $?) -eq 0 ]]; then

   /opt/local/bin/freshclam -u root

   /bin/sleep 3

   #if [[ -e "/tmp/clamd" ]]; then /bin/rm -f /tmp/clamd; fi
   #/opt/local/sbin/clamd -c /opt/local/etc/clamd.conf     # recreates the local socket file /tmp/clamd as specified in /opt/local/etc/clamd.conf above

   (/bin/sleep 3; echo RELOAD; /bin/sleep 3; echo "exit") | /usr/bin/telnet -u /tmp/clamd >/dev/null 2>&1
   #echo RELOAD | /opt/local/bin/socat - /tmp/clamd         # an alternative that requires: sudo port install socat

   /bin/sleep 3

   echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"): clamd virus database successfully updated\n"
   exit 0

else

   echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"): updating the clamd virus database failed; no internet connection to database.clamav.net established\n"
   exit 0     # leave launchd undisturbed

fi


# set permissions
sudo chown root:wheel /usr/local/sbin/update_clamd_db.sh
sudo chmod 0770 /usr/local/sbin/update_clamd_db.sh


#------------------------


# launchd item to update the clamd virus database using /usr/local/sbin/update_clamd_db.sh
# sudo nano /Library/LaunchDaemons/net.clamav.update.clamd.db.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Disabled</key>
	<true/>
       <key>Label</key>
        <string>net.clamav.update.clamd.db</string>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/local/sbin/update_clamd_db.sh</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>StartInterval</key>
        <integer>10800</integer>
        <key>UserName</key>
        <string>root</string>
        <key>GroupName</key>
        <string>wheel</string>
</dict>
</plist>


sudo chown root:wheel /Library/LaunchDaemons/net.clamav.update.clamd.db.plist
sudo chmod 0644 /Library/LaunchDaemons/net.clamav.update.clamd.db.plist

sudo launchctl load -w /Library/LaunchDaemons/net.clamav.update.clamd.db.plist
#sudo launchctl unload -w /Library/LaunchDaemons/net.clamav.update.clamd.db.plist

sudo reboot


# simple shell script syntax check
# for more see Debugging Bash scripts, http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html
bash -n $HOME/Documents/ClamAV/watchdirs.sh
bash -n /usr/local/sbin/update_clamd_db.sh

# optional: convert XML .plist files to binary format
sudo plutil -convert binary1 -- /Library/LaunchDaemons/net.clamav.clamd.plist \
~/Library/LaunchAgents/net.clamav.dirwatcher.plist \
/Library/LaunchDaemons/net.clamav.update.clamd.db.plist

# check .plist file syntax
plutil -- /Library/LaunchDaemons/net.clamav.clamd.plist \
~/Library/LaunchAgents/net.clamav.dirwatcher.plist \
/Library/LaunchDaemons/net.clamav.update.clamd.db.plist


# list virus signatures
sigtool -l | grep -Ei 'adware|spy' | nl
sigtool -l | grep -i phish | nl



Further information:

- Run ClamAV from a system service agent account
- ClamAV an open-source anti-virus toolkit
- Configuring Clamav's clamd for enhanced virus-scanning performance
- Updating ClamAV on OS X Server 10.4.7-10.4.11
- Using Open Source Tools to Filter Email on Mac OS X Server
- The Anti-Virus Or Anti-Malware Test File
- Mac OS X Unix Tutorial: Part 4 - Managing Permissions
- launchd

lighttpd launchd script for Leopard

This script solves the excessive re-spawning problem that surfaces with Tiger-era launchd scripts. Tested using Lighttpd 1.4.18 and OS X 10.5.1.

Obviously, you may need to point to /usr/local/bin/lighttpd-start if you installed with --prefix=/usr/local

Put this at: /Library/LaunchDaemons/net.lighttpd.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>net.lighttpd</string>
	<key>ProgramArguments</key>
	<array>
	    <string>/usr/bin/lighttpd-start</string>
	</array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

Launchd item for starting MySQL

This will start MySQL with a low priority on reboot

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>com.mysql.MySQL</string>
	<key>LowPriorityIO</key>
	<true/>
	<key>ProgramArguments</key>
	<array>
		<string>/usr/local/mysql/bin/mysqld_safe</string>
	</array>
	<key>RunAtLoad</key>
	<true/>
</dict>
</plist>

load/unload postgres (via ports) launch deamon

Enable or disable the automatic launch of the postgresql server installed via macports. Starts or stops the daemon and modifies the launchd manifest.

launchctl load -w /opt/local/etc/LaunchDaemons/org.darwinports.postgresql81-server/org.darwinports.postgresql81-server.plist

launchctl unload -w /opt/local/etc/LaunchDaemons/org.darwinports.postgresql81-server/org.darwinports.postgresql81-server.plist

Rails MySQL Session Sweeper

1) Download the launchd GUI, Lingon, from: http://lingon.sourceforge.net/

2) Add hourly job:

$RAILS_APP_DIR/script/runner 'ActiveRecord::Base.connection.delete("DELETE FROM sessions WHERE updated_at < now() - INTERVAL 1 HOUR")'


3) My launchd task plist file (yours will vary):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>us.boygeni.mysql-session-sweeper</string>
	<key>LowPriorityIO</key>
	<true/>
	<key>ProgramArguments</key>
	<array>
		<string>/users/defeated/Sites/rails_apps/boygenius/script/runner</string>
		<string>'ActiveRecord::Base.connection.delete("DELETE FROM sessions WHERE updated_at &lt; now() - INTERVAL 1 HOUR")'</string>
	</array>
	<key>RunAtLoad</key>
	<true/>
	<key>StartInterval</key>
	<integer>3600</integer>
</dict>
</plist>

Lighttpd launchd item for OS X 10.4

Save the following in /Library/LaunchDaemons/net.lighttpd.plist to have lighttpd start automatically on any OS X 10.4 computer. Note that this assumes a working DarwinPorts lighttpd installation (otherwise you will need to change the paths to the lighttpd executable). Note that this also assumes that the lighttpd.conf file is in /opt/local/etc.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>net.lighttpd</string>
	<key>OnDemand</key>
	<false/>
	<key>Program</key>
	<string>/opt/local/sbin/lighttpd</string>
	<key>ProgramArguments</key>
	<array>
		<string>/opt/local/sbin/lighttpd</string>
		<string>-f/opt/local/etc/lighttpd.conf</string>
		<string>-D</string>
	</array>
</dict>
</plist>