Mac computer lab headaches! A script to handle keychain errors, docks, and users logged in in the background

I’m primarily a university-level ESL instructor. However, one of my responsibilities is to manage our English Language Institute’s computer lab, which has 20 iMacs. Here are some of the issues I’ve struggled with:

  • Keychain errors. My university uses Active Directory for usernames and passwords, and Macs don’t play nicely with Active Directory. Every time that students and faculty are forced to change their passwords through the university’s web interface, it breaks their keychains on all campus Macs, since the login password is no longer the same as the keychain password. Dealing with keychain errors is annoying enough under one-device-per-person circumstances, but when people use multiple computers over the course of multiple semesters and change their password multiple times, the problems snowball out of control.
  • Messing up the dock. It’s pretty straightforward to set up a default dock for new users. However, most of our students lack familiarity or comfort with Macs, so if a student accidentally drags an important icon (like, say, Microsoft Word) out of the dock, they may have difficulty finding the application again. Under these circumstances, it’s desirable to “reset” the dock to a known good state every time a user logs out so that any mistakes they made will be wiped away.
  • Users not logging out. This is a big one. If a user fails to log out properly and ends up with their whole session still stored in memory in the background, system performance plummets for anyone else who logs into that computer. This is a huge problem in a lab setting because it’s difficult to train users to always properly log out, and each computer is logged into so many times every day that it only takes a couple days for most computers to have at least one user still accidentally logged in in the background.

One solution to these kinds of issues is to have a very tightly controlled computer lab where no user data is stored and where the computers constantly restart and reset themselves to known good states. But I’m not comfortable doing that; I want students to be able to log back into a computer to retrieve a file if they accidentally saved it to the lab computer instead of their personal flash drive, for example.

So instead, I wrote a logoff script to do some basic maintenance to help me solve these issues. Macs don’t provide an easy built-in way to run logoff scripts, so I use Offset to run the script for me.

My script does the following:

  1. Delete the user’s keychain every time they log out. This prevents keychain errors and improves security, since it ensures we aren’t storing people’s passwords on our public computers.
  2. Replaces the user’s dock with a known good configuration. That way, if the user accidentally messed up the dock, it will be back to normal the next time they log in.
  3. Kicks off anyone who’s logged into the computer in the background. This dramatically improves system performance.
  4. Restarts the computer if it’s been on for more than a day. This is a necessary step to fully purge keychain files.

Here is the script I created. I am not a bash expert or a MacOS expert, and I make no warranty for the functionality or safety of this script; I’m presenting it only for example purposes to help others in similar positions. If you want to adapt this script, you will likely need to make changes.


echo "$(date) - Script execution beginning" >> /Library/Logs/lab_logout_debug.log

# Step 0: Decide whether we need to run the script.
#         If we're at the login window, and if the script has not run
#         in the last 120 seconds, then we want to run it now.
#         Note: Checking when the script last ran prevents the script from 
#         killing the login window over and over and over again in a loop!
#         I chose 120 seconds because it would be unusual for someone to be
#         logged in for less than 2 minutes.

echo "$(date) - initializing shouldrun to false" >> /Library/Logs/lab_logout_debug.log

echo "$(date) - trying to find the last time run log file" >> /Library/Logs/lab_logout_debug.log

# Can we find the log file that stores the last time the script was run?
if [ -f /Library/Logs/lab_logout_last_time.log ]
    # We found the file!
    echo "$(date) - found last time run log file" >> /Library/Logs/lab_logout_debug.log
    # Store the current Unix time in seconds
    currenttime=$(date +%s)

    # Read the Unix time in seconds that the script last ran
    lasttime=$(cat /Library/Logs/lab_logout_last_time.log)

    # Calculate the difference between the times: How long ago did the script last run?
    delta=$(expr $currenttime - $lasttime)

    echo "$(date) - Current time: " $currenttime >> /Library/Logs/lab_logout_debug.log
    echo "$(date) - Last time: " $lasttime >> /Library/Logs/lab_logout_debug.log
    echo "$(date) - Delta: " $delta >> /Library/Logs/lab_logout_debug.log

    # Did the script last run MORE than 120 seconds ago?
    if [ "$delta" -gt 120 ]
        # Yes? We should run the script.
        echo "$(date) - set shouldrun to true (delta large enough)" >> /Library/Logs/lab_logout_debug.log
        # No? We should not run the script.
        echo "$(date) - keep shouldrun at false (delta too small)" >> /Library/Logs/lab_logout_debug.log
    # We didn't find the log file that stores the last time the script was run, so let's assume we need to run the script.
    echo "$(date) - didn't find file" >> /Library/Logs/lab_logout_debug.log
    echo "$(date) - set shouldrun to true (file not found)" >> /Library/Logs/lab_logout_debug.log

# Should we run the script?
if [ "$shouldrun" = "true" ]
    # Yes, we should run the script!
    echo "$(date) - shouldrun evaluated as true - executing remainder of script" >> /Library/Logs/lab_logout_debug.log
    echo "$(date) - Executing lab logout script." >> /Library/Logs/lab_logout.log

    ### Step 1: Delete the last user's keychain
    # Credit to

    echo "$(date) - Entering step 1" >> /Library/Logs/lab_logout_debug.log

    # Get the last user's username
    lastUserName=$(defaults read /Library/Preferences/ lastUserName)

    # Remove the keychains for that user
    sudo rm -rf /Users/"$lastUserName"/Library/Keychains/*
    sudo rm -rf /Users/"$lastUserName"/Library/Keychains/.f*

    # Add to log file
    sudo echo "$(date) - Keychain deleted for $lastUserName" >> /Library/Logs/lab_logout.log

    ### Step 2: Copy the dock for the user

    echo "$(date) - Entering step 2" >> /Library/Logs/lab_logout_debug.log

    cp /Library/lab/ /Users/$lastUserName/Library/Preferences/
    echo "$(date) - Dock copied for this user:" $lastUserName >> /Library/Logs/lab_logout.log

    ### Step 3: Record the current time as the last time the script ran

    echo "$(date) - Entering step 3" >> /Library/Logs/lab_logout_debug.log

    # Record the current time to the lab_logout_last_time file, overwriting any previous file content
    echo $(date +%s) > /Library/Logs/lab_logout_last_time.log

    echo "$(date) - Current time recorded" >> /Library/Logs/lab_logout_debug.log

    ### Step 4: Either restart (if uptime is at least 1 day) or kill loginwindow (to log out any background users)
    echo "$(date) - Entering step 4" >> /Library/Logs/lab_logout_debug.log
    echo "$(date) - Current uptime: $(uptime)" >> /Library/Logs/lab_logout_debug.log

    # Has the computer been on for at least one full day? 
    # (i.e. does the word "day" or "days" appear in the output of the "uptime" command?)
    if [[ $(uptime) =~ .*day.* ]] 
        # Uptime is at least one day, so let's restart. This will fully clear keychains and (obviously) log out all users.

        echo "$(date) - Uptime greater than a day; attempting a restart" >> /Library/Logs/lab_logout.log
        sudo shutdown -r now
        # Uptime is less than one day, so let's just kill the loginwindow process to make sure no one's
        # logged in in the background.

        echo "$(date) - Uptime less than a day; killing loginwindow." >> /Library/Logs/lab_logout.log

        # Kill loginwindow to log out any users who are logged in in the background
        sudo pkill loginwindow
    # No, we shouldn't run the script!
    echo "$(date) - shouldrun DID NOT evaluate as true" >> /Library/Logs/lab_logout_debug.log

Basic installation instructions:

  1. Install Offset.
  2. Paste my script into a new file with a .sh extension.
  3. Make any necessary changes to the script. Note that it won’t run as-is; at the very least, you need to put a dock.plist file in the /Library/lab/ directory for step 2 of the script, or else remove that part of the script.
  4. Move the script file to /usr/local/offset/logout-every so that Offset will run it for you.
  5. Make the script file executable. I run this command in Terminal, and it seems to work, but I’m not an expert, so ymmv: sudo chmod 755 /usr/local/offset/logout-every/ ; sudo chown root:wheel /usr/local/offset/logout-every/

That should do it!

Recorded Speaking Activity (RSA): Pedagogy, Implementation, Evaluation and Creation

I had the pleasure this past weekend to co-present about the Recorded Speaking Activity we do at my institution. Check it out below!

I presented earlier this year at TESOL International about the benefits of using Google Drive for collaborative activities, and incidentally, this is a demonstration of those benefits, as my colleagues and I used Google Slides to create this presentation together. I don’t think I posted my TESOL International presentation on this blog, but I gave a similar presentation at Three Rivers TESOL 2016.

Three Rivers TESOL 2015 – Collaborative Projects the Easy Way Using Google Drive

This is a companion post for my presentation at Three Rivers TESOL on 11/7/2015. In the live workshop, we will do hands-on activities with Google Drive. This post contains some introductory prose and a link to a “how-to” document for setting up Google Drive projects for your own students.

Students collaborating using Google Drive. Photo: Charlotta Wasteson, CC BY 2.0
Students collaborating using Google Drive. Photo: Charlotta Wasteson, CC BY 2.0

Have you ever assigned a group project such as a panel speech or a group presentation? If you have, you know that the logistics tend to get messy. In my experience, students in a group often each create their own PowerPoint files and then try to paste them all together into one group file. This requires a lot of emailing and effort, and the final product usually looks inconsistent and shabby due to varying slide designs, fonts sizes, and so on. Plus, if one or two students aren’t pulling their weight, their poor or missing work may come as a surprise to their fellow group members on presentation day.

But what if all the members of a group could work on the same file at the same time?

Google Drive is a free online office suite created and maintained by Google, Inc. The program is accessed via a web browser (on PCs/Macs) or via mobile apps (iOS/Android). Where Google Drive differs most substantially from Microsoft Office is that it makes collaboration very easy: everyone working on a project can edit the same document at the same time, and built-in commenting and chatting features enable the collaborators to coordinate their efforts. The online nature of Google Drive also means that teachers do not need to “collect” work. Rather, teachers have permanent, ongoing access to the document and can monitor student work live as it happens.

See here for instructions:

Using Google Drive for Collaborative Student Projects

The above document contains all the instructions you need to get a project up and running. It’s certainly a bit simpler to set projects up if your institution uses Google Apps for Education (GAFE), but this is not a prerequisite. As long as the teacher has a Google account, the teacher can create the projects and enable students to work on those documents without needing to log into a Google account.

Give it a try!

Downloading temporary offline copies of web videos with 4K Video Downloader

Up-front disclaimer: This post is being written to fulfill the terms of a promotion.

As an ESL teacher, I often show authentic videos in my classrooms. For example, recently, to complement a textbook unit whose theme was marketing and advertising, I showed an authentic video from YouTube in which a marketing consultant talked about the challenges which DVRs have introduced to television advertising.

However, I’m wary of trusting the reliability of video streaming websites. Buffers can freeze, connections can drop, and intrusive unskippable advertising can rob the class of 30 seconds or more. With those issues in mind, I prefer to download videos in advance rather than streaming them directly from the source website. Of course, this works most easily when the rights holder of the video provides a download feature and explicitly allows and encourages people to download copies for offline use. If I want to show my class a lecture from, for example, I can easily download it as an MP4 file for lag-free, bufferless, 100% reliable classroom viewing, because all TED talks are Creative Commons licensed for free distribution.

However, even when rights holders do not provide a download feature, it’s fair use to download a temporary copy of an authentic listening passage for classroom use. YouTube, the 800 pound gorilla of the online video world, does not offer an official downloading feature– presumably because many of the important rights holders of content on YouTube (such as music labels) forbid video downloads for commercial reasons. This is understandable, but the one-size-fits-all approach stymies educators’ fair use rights.

There are plenty of unofficial programs and web browser extensions which enable educators to download videos from YouTube, but my personal favorite is 4K Video Downloader by OpenMedia LLC. It’s a lightweight stand-alone application, which is great. By comparison, most other video downloader programs are installed as browser extensions– and I prefer not to weigh my web browser down with a bunch of extensions and plugins that aren’t needed 99% of the time.

Beyond the fact that it’s a quick, streamlined application, what I like about 4K Video Downloader is that once I’ve configured it to my desired settings (video resolution, output folder, etc), all I need to do is paste a YouTube link into the application to begin a download. From there, the program does its job quickly and reliably every time.

The advertising in the program is minimal, so I haven’t paid for the ad-free version. However, given the current free license promotion, I thought it would be worth it to say a few words about the program.