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!)

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

Convert chmod file permissions between octal & symbolic notation


# convert from octal to symbolic format
unset -f o2s
function o2s () { touch ~/.octal2symbolic; chmod -vv "$@" "$_";  ls -l "$_" | awk '{print $1}'; rm -f ~/.octal2symbolic; }
#function o2s () { touch ~/.octal2symbolic; chmod "$@" "$_";  ls -l "$_" | awk '{print $1}'; rm -f ~/.octal2symbolic; }

o2s 755
o2s 644
o2s 1744
o2s 0100751      # cf. man 2 stat (st_mode)
o2s 0101751


# Cf. Bash 'umask' builtin doesn't set 'x' permissions?
# http://archives.devshed.com/forums/unix-linux-135/bash-umask-builtin-doesn-t-set-x-permissions-372356.html

UMASKO=$(umask)   # 0022

help umask
umask
umask -p
umask -S

umask 0077
umask
umask -S
o2s 755

umask 0000
umask
umask -S
o2s 654

umask $UMASKO


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


# convert from symbolic to octal format

#unset -f s2o
# default file permissions of temporary file: chmod 777 ~/.symbolic2octal
#function s2o () { touch ~/.symbolic2octal; chmod 777 "$_"; chmod "$@" "$_";  stat -f %p "$_"; rm -f ~/.symbolic2octal; }


# default file permissions of temporary file ~/.symbolic2octal:  100000 or 100777 respectively

unset -f s2o
function s2o () {
   OPATH=$PATH; OIFS=$IFS
   export PATH="/usr/bin:/bin:/usr/sbin:/sbin"; export IFS=$' \t\n'
   unset -v FILE logname num str
   declare FILE="${HOME}/.symbolic2octal" logname="$(/usr/bin/logname)" str="chmod ${@} file.txt"
   declare num=$((${#str} + 5))   # calculate string length for dynamic printf spacing
   touch "${FILE}"
   chown "${logname}":"${logname}" "${FILE}"
   printf "%-${num}s %s\n" "${str}" "file permission command"
   chmod 000 "${FILE}"

   if [[ "${@}" != "${@/-/}" ]]; then    # if the string contains a minus sign ...
      chmod 777 "${FILE}"
      printf "\e[32m%-${num}s\e[m %s\n" "100777" "initial file permissions"
   else
      printf "\e[32m%-${num}s\e[m %s\n" "100000" "initial file permissions"
   fi

   chmod "$@" "${FILE}"
   printf "\e[1m%-${num}s\e[m %s\n" "$(stat -f %p "${FILE}")" "resulting file permissions"
   #stat -f %Sp "${FILE}"
   /bin/rm -f ~/.symbolic2octal
   export PATH=$OPATH; export IFS=$OIFS
   return 0
}

s2o a-w
s2o a+w
s2o u=rwx,g=rx,o=x
s2o u=rwx,g-x,o=x
s2o u=rx,g-x,o=x
s2o u=rx,g+x,o=x
s2o u=rwx,g=rx,o=x,+t


unset -f s2o
function s2o () {
   OPATH=$PATH; OIFS=$IFS
   export PATH="/usr/bin:/bin:/usr/sbin:/sbin"; export IFS=$' \t\n'
   unset -v FILE logname num str
   declare FILE="${HOME}/.symbolic2octal" logname="$(/usr/bin/logname)" str="chmod ${@} file.txt"
   declare num=30
   touch "${FILE}"
   chown "${logname}":"${logname}" "${FILE}"
   printf "%-${num}s %s\n" "file permission command:" "${str}"
   chmod 000 "${FILE}"

   if [[ "${@}" != "${@/-/}" ]]; then    # if the string contains a minus sign ...
      chmod 777 "${FILE}"
      printf "%-${num}s \e[32m%s\e[m\n" "initial file permissions:" "100777"
   else
      printf "%-${num}s \e[32m%s\e[m\n" "initial file permissions:" "100000"
   fi

   chmod "$@" "${FILE}"
   printf "%-${num}s \e[1m%s\e[m\n" "resulting file permissions:" "$(stat -f %p "${FILE}")"
   #stat -f %Sp "${FILE}"
   /bin/rm -f ~/.symbolic2octal
   export PATH=$OPATH; export IFS=$OIFS
   return 0
}


s2o a-w
s2o a+w
s2o u=rwx,g=rx,o=x
s2o u=rwx,g-x,o=x
s2o u=rx,g-x,o=x
s2o u=rx,g+x,o=x
s2o u=rwx,g=rx,o=x,+t