Ryan Malesevich

amateur runner, technology enthusiast, and friend to all dogs

SMB Reconnect Shell Script for macOS

Homelab - Ensure external network shares stay connected on macOS.

This script was originally mentioned in my post: Homelab Chronicles: Chapter 4 but I wanted to expound on it.

Problem Statement

macOS is a wonderful platform but connecting external network shares and keeping them connected can be a pain. If you browse the web, they mention you can drag the mounted Volume into the Login Items in System Settings but what do you do when there is an interruption? For example, the share restarts or there is an interruption in the network.

I was looking for a way to ensure that the SMB would reconnect automatically if and when it was disconnected from macOS.

Steps

Through a Shell script shown below, you can have it scheduled to run periodically. The high-level steps are:

  1. Add the password to the Key Vault
  2. Configure the Shell script
  3. Make the Shell script executable
  4. Create a LaunchAgent plist entry and enable it

1. Key Vault

Adding the entry is quite simple. Open the Terminal and execute the following command:

security add-generic-password -a your_username -s your_server_name_or_ip -w your_password -U
  • your_username: should be the user to the SMB share
  • your_server_name_or_ip: should be the address that you use to connect to the SMB
  • your_password: should be your password
  • Adding -U will overwrite the password if it already exists

2. Configure the Shell script

Copy the Shell script below to a location on your computer:

#!/bin/bash

# SMB details
SERVER_NAME=""
USER_NAME=""
SHARE_NAME=""
MOUNT_POINT="$HOME/Mounts/$SHARE_NAME"

# Retrieve credentials from Keychain
PASSWORD=$(security find-generic-password -a $USER_NAME -s $SERVER_NAME -w 2>/dev/null)

# Check if credentials are retrieved
if [ -z "$PASSWORD" ]; then
    echo "$(date): Failed to retrieve credentials from Keychain. Exiting." >> ~/smb_reconnect.log
    exit 1
fi

# Check if the mount_point exists and is already connected
if [ ! -d "$MOUNT_POINT" ] || ! mount | grep -q "//$USER_NAME@$SERVER_NAME/$SHARE_NAME on $MOUNT_POINT"; then
    echo "$(date): Share is not mounted. Attempting to reconnect..." >> ~/smb_reconnect.log

    # Ensure the mount point directory exists
    if [ ! -d "$MOUNT_POINT" ]; then
        mkdir -p "$MOUNT_POINT"
    fi

    # Attempt to mount the SMB share
    mount_smbfs "//${USER_NAME}:${PASSWORD//@/%40}@${SERVER_NAME}/${SHARE_NAME}" "$MOUNT_POINT" 2>> ~/smb_reconnect.log
    
    if [ $? -eq 0 ]; then
        echo "$(date): Successfully reconnected to SMB share." >> ~/smb_reconnect.log
    else
        echo "$(date): Failed to reconnect to SMB share. Check if the SMB server is online." >> ~/smb_reconnect.log
    fi
else
    echo "$(date): Share is already mounted." >> ~/smb_reconnect.log
fi

Set the details in the variables at the front of the script.

  • SERVER_NAME: should match the server you added to the key vault
  • USER_NAME: should match the user you added to the key vault
  • SHARE_NAME: is the volume/folder on the server that you’ll connect to

Each step is added to a log file. By default it will save to your $HOME directory (~) under smb_reconnect.log. Set this to where you’d like the log file.

The script works by first retrieving the password from the Key vault. It then checks if the SMB is already mounted. Please note that this will not mount the SMB into the /Volumes directory if you mount it through Finder with the Cmd + K shortcut. It will mount it to the MOUNT_POINT directory. You may need to mount the drive manually through mount_smbfs in the Terminal to get this to work appropriately.

If it’s not connected, it will attempt to connect. Please note on the PASSWORD variable, it will do a special encoding if the password contains a : character. I had to do a lot of troubleshooting.

3. Set the script to executable

Ensure the script is executable by calling chmod on it:

chmod +x checksmb.sh

4. Set the LaunchAgent

You could choose to execute the script through a cron job, but I chose to use the Apple way with a LaunchAgent command.

Create a text file under ~/Library/LaunchAgents/com.user.checksmb.plist with the following:

<?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.user.checksmb</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/full/path/to/shell/script.sh</string>
    </array>
    <key>StartInterval</key>
    <integer>300</integer> <!-- Runs every 5 minutes -->
    <key>StandardOutPath</key>
    <string>/full/path/to/standard/out/path.log</string>
    <key>StandardErrorPath</key>
    <string>/full/path/to/standard/error/path.log</string>
</dict>
</plist>
  • Ensure the Label string matches the name of the PLIST file.
  • Set the full path to your script.
  • Set the interval in seconds. I’m running mine with 300 seconds or 5 minutes.
  • Set the Out and ErrorPath log files. They can be the same.

Finally the PLIST needs to be enabled. In the Terminal execute the following command:

launchctl load ~/Library/LaunchAgents/com.user.checksmb.plist

After this monitor the log file.