LoginSignup
2
2

More than 5 years have passed since last update.

ruby + plist でNASのTimeMachine用sparsebundleファイルを常にマウントしておく

Last updated at Posted at 2016-11-05

最終形

notification.png

問題

Mac覚書 TimeMachineでNASを使用する方法
NAS上にTimeMachineファイルを置いてTimeMachineを使っても、起動時Sleep後はsparsebundleファイルをマウントし直さないとTimeMachineが機能しない。

解決策

samba経由でnasのフォルダをマウントして、samba上のsparsebundleファイルを自動でマウントするようにする。

  1. rubyで書きたい
  2. plistで自動起動させたい

方法

まず、samba経由で目的のNASフォルダをマウントする

#マウント先フォルダを作って
mkdir /Volumes/your_mount_name
#sambaフォルダをマウント nasにログインできる「guest:」か「login:pass」で
mount_smbfs //guest:@your_nas_name/your_path_name /Volumes/your_mount_name

マウントしたフォルダ内にあるsparsebundleファイルをさらにマウントする

hdiutil attach -mountpoint /Volumes/your_timemachine_mount_name /Volumes/your_mount_name/your_timemachine.sparsebundle

これをrubyにまとめて引数で指定できるようしたうえで、さらにosxのnotificationで表示できるようにterminal-notifierをインストール

brew install terminal-notifier

notification.png

まとめたものがこれ

rubyファイル

# timemachine.drive.rb

require "pathname"
require "shellwords"

class AutoTimeMachineDrive
  class << self
    def mount!
      new.mount_timemachine_drive
    rescue => ex
      notify(ex.message)
    end

    def notify(message)
      options = {
        group:   self.to_s,
        title:   self.to_s,
        message: message,
        sound:   :default
      }

      system "/usr/local/bin/terminal-notifier #{option_line options}"
    end

    def option_line(*options)
      options.collect do |element|
        case element
        when Hash
          element.collect{|k, v| "-#{k} #{Shellwords.escape v}"}.join " "
        when String, Pathname
          element.to_s
        end
      end.join " "
    end
  end

  def initialize
    @smb_url, @smb_mount_name,
    @time_machine_mount_name, @time_machine_sparsebundle_file_path = ARGV
  end

  def mount_timemachine_drive
    return if tm_mount_point.exist?

    ensure_smb_mount_point do
      ensure_tm_mount_point_is_missing do
        mount_timemachine_sparsebundle_file
        self.class.notify("#{time_machine_mount_name} mounted.") if tm_mount_point.exist?
      end
    end
  end

  private
    attr_reader :smb_url, :smb_mount_name,
                :time_machine_mount_name, :time_machine_sparsebundle_file_path

    def smb_mount_point
      Pathname "/Volumes/#{smb_mount_name}"
    end

    def tm_mount_point
      Pathname "/Volumes/#{time_machine_mount_name}"
    end

    def ensure_smb_mount_point
      prepare_smb_mount_point do
        mount_samba
        yield
      end
    end

    def ensure_tm_mount_point_is_missing
      yield unless tm_mount_point.exist?
    end

    def prepare_smb_mount_point
      if smb_mount_point.exist?
        if [smb_mount_point_not_network_drive?, smb_mount_point_not_empty?].all?
          return self.class.notify "unempty smb_mount_point (#{smb_mount_point}) exists. make sure this directory is removed before running."
        end
      else
        smb_mount_point.mkdir
      end

      yield
    end

    def smb_mount_point_not_network_drive?
      `mount`.lines.select{|line| line.include? smb_mount_point.to_s }.count == 0
    end

    def smb_mount_point_is_network_drive?
      not smb_mount_point_not_network_drive?
    end

    def smb_mount_point_not_empty?
      smb_mount_point.children.count > 0
    end

    def mount_samba
      return if smb_mount_point_is_network_drive?
      system "mount_smbfs #{self.class.option_line(smb_url, smb_mount_point)}"
    end

    def mount_timemachine_sparsebundle_file
      system "hdiutil attach #{self.class.option_line(
          {mountpoint: tm_mount_point},
          time_machine_sparsebundle_file_path
        )}"
    end

end


AutoTimeMachineDrive.mount!

このファイルをtimemachine.drive.rbなどとして~/binなどに入れて、次にplistを作る

plistファイル

<!--  local.timemachine.drive.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>Label</key>
    <string>local.timemachine.drive</string>
    <key>ProgramArguments</key>
    <array>
        <string>ruby</string>
        <!--rbファイルのフルパス-->
        <string>/Users/your_name/bin/timemachine.drive.rb</string>
        <!--ログインできるNASのURL-->
        <string>//guest:@your_nas_name/your_path_name</string>
        <!--sambaフォルダのマウント名 /Volumes配下に自動マウント-->
        <string>your_mount_name</string>
        <!--sparsebundleマウント時の表示ドライブ名-->
        <string>your_timemachine_mount_name</string>
        <!--samba経由で参照できるsparsebundleフルパス(/Volumes配下経由)-->
        <string>/Volumes/your_mount_name/your_timemachine.sparsebundle</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>StartInterval</key>
    <integer>60</integer>
</dict>
</plist>

launchctlのロード

local.timemachine.drive.plistなどとしたこのplistを~/Library/LaunchAgents/に置いて

launchctl load ~/Library/LaunchAgents/local.timemachine.drive.plist

このコマンド一発で自動にTimeMachineドライブをマウントします。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2