Class: Jamf::Client
Overview
This class represents a Jamf/JSS Client computer, on which this code is running.
Since the class represents the current machine, there's no need to make an instance of it, all methods are class methods.
At the moment, only Macintosh computers are supported.
TODO: convert this to a module, since that's how it's used.
Constant Summary collapse
- JAMF_PLIST =
The Pathname to the preferences plist used by the jamf binary
Pathname.new '/Library/Preferences/com.jamfsoftware.jamf.plist'
- JAMF_SUPPORT_FOLDER =
The Pathname to the JAMF support folder
Pathname.new '/Library/Application Support/JAMF'
- RECEIPTS_FOLDER =
The JAMF receipts folder, where package installs are tracked.
JAMF_SUPPORT_FOLDER + 'Receipts'
- DOWNLOADS_FOLDER =
The JAMF downloads folder
JAMF_SUPPORT_FOLDER + 'Downloads'
- SUPPORT_BIN_FOLDER =
The bin folder inside the Jamf support folder
JAMF_SUPPORT_FOLDER + 'bin'
- USR_LOCAL_BIN_FOLDER =
The bin folder with the jamf binary and a few other things
Pathname.new '/usr/local/jamf/bin'
- CONSOLE_USERS_SCUTIL_CMD =
This command gives raw info about console users
'echo "show State:/Users/ConsoleUser" | /usr/sbin/scutil'.freeze
- ROOT_USER =
ignore console user = root (loginwindow)
'root'.freeze
- LOGINWINDOW_USER =
ignore primary console user loginwindow
'loginwindow'.freeze
- SELF_SERVICE_EXECUTABLE_END =
The end of the path to the Self Service Executable. Used to figure out who's running Self Service.app
'/Self Service.app/Contents/MacOS/Self Service'.freeze
- PS_USER_COMM =
the ps command used to figure out who's running Self Service
'ps -A -o user,comm'.freeze
- USER_PREFS_BYHOST_FOLDER =
the path to a users byhost folder from home
'Library/Preferences/ByHost/'
- POLICY_SCRIPT_CMD_RE =
If some processs C has a parent process P whose command (via ps -o comm) matches this, then process C is being run by Jamf
%r{sh -c PATH=\$PATH:/usr/local/jamf/bin; '/Library/Application Support/JAMF/tmp/.* >& '/Library/Application Support/JAMF/tmp/\d+.tmp}.freeze
- POLICY_CMD_RE =
If some processs C has a parent process P whose command (via ps -o comm) matching POLICY_SCRIPT_CMD_RE, and process P has a parent process G that matches this (C is grandchild of G), then process C is being run by a Jamf policy
%r{/bin/jamf policy }.freeze
Class Method Summary collapse
-
.console_user ⇒ Object
alias for primary_console_user.
-
.console_users ⇒ Array<String>
Who's currently got an active GUI session? - might be more than one if Fast User Switching is in use.
-
.do_not_disturb?(user = nil) ⇒ Boolean?
Is 'Do Not Disturb' enabled for the user? nil if unknown/not-applicable.
-
.hardware_data ⇒ Hash
The parsed HardwareDataType output from system_profiler.
-
.homedir(user) ⇒ Pathname?
The home dir of the specified user, nil if no homedir in local dscl.
-
.installed? ⇒ Boolean
Is the jamf binary installed?.
-
.jamf_plist ⇒ Hash
The contents of the JAMF plist.
-
.jamf_version ⇒ String?
What version of the jamf binary is installed?.
-
.jss_available? ⇒ Boolean
Is the JSS available right now?.
-
.jss_port ⇒ Integer
The port number for JSS connections for this client.
-
.jss_protocol ⇒ String
The protocol for JSS connections for this client.
-
.jss_record ⇒ JSS::Computer?
The JSS::Computer object for this computer.
-
.jss_server ⇒ String
The JSS server hostname for this client.
-
.jss_url ⇒ String
the URL to the jss for this client.
-
.my_ip_address ⇒ String
Get the current IP address as a String.
-
.parent_pid_user_and_cmd(pid, ps_lines = nil) ⇒ Array<Integer, String, String>
given a pid and optionally the output of `ps -o pid -o ppid -o user -o command` return an array with the pid, user, and command of the pid's parent process.
-
.primary_console_user ⇒ String?
Which console user is using the primary GUI console? Returns nil if the primary GUI console is at the login window.
-
.receipts ⇒ Array<Pathname>
All the JAMF receipts on this client.
-
.script_running_via_policy? ⇒ Boolean
Search up the process lineage to see if any ancestor processes indicate that the current process is being run by a Jamf Policy.
-
.self_service_users ⇒ Array<String>
Who's currently running Self Service.app? - might be more than one if Fast User Switching is in use.
-
.serial_number ⇒ String
The serial number for this computer via system_profiler.
-
.udid ⇒ String
The UUID for this computer via system_profiler.
Class Method Details
.console_user ⇒ Object
alias for primary_console_user
263 264 265 |
# File 'lib/jamf/client.rb', line 263 def self.console_user primary_console_user end |
.console_users ⇒ Array<String>
Who's currently got an active GUI session? - might be more than one if Fast User Switching is in use.
243 244 245 246 247 248 |
# File 'lib/jamf/client.rb', line 243 def self.console_users output = `#{CONSOLE_USERS_SCUTIL_CMD}` userlines = output.lines.select { |l| l =~ /SessionUserNameKey\s*:/ } userlines.map! { |ul| ul.split(':').last.strip } userlines.reject { |un| un == ROOT_USER } end |
.do_not_disturb?(user = nil) ⇒ Boolean?
Returns Is 'Do Not Disturb' enabled for the user? nil if unknown/not-applicable.
282 283 284 285 286 287 288 |
# File 'lib/jamf/client.rb', line 282 def self.do_not_disturb?(user = nil) home = user ? homedir(user) : Dir.home myudid = udid nc_prefs_file = Pathname.new "#{home}/#{USER_PREFS_BYHOST_FOLDER}/com.apple.notificationcenterui.#{myudid}.plist" return nil unless nc_prefs_file.readable? Jamf.parse_plist(nc_prefs_file)['doNotDisturb'] end |
.hardware_data ⇒ Hash
The parsed HardwareDataType output from system_profiler
233 234 235 236 |
# File 'lib/jamf/client.rb', line 233 def self.hardware_data raw = `/usr/sbin/system_profiler SPHardwareDataType -xml 2>/dev/null` Jamf.parse_plist(raw)[0]['_items'][0] end |
.homedir(user) ⇒ Pathname?
The home dir of the specified user, nil if no homedir in local dscl.
297 298 299 300 |
# File 'lib/jamf/client.rb', line 297 def self.homedir(user) dir = `/usr/bin/dscl . -read /Users/#{user} NFSHomeDirectory 2>/dev/null`.chomp.split(': ').last dir ? Pathname.new(dir) : nil end |
.installed? ⇒ Boolean
Is the jamf binary installed?
122 123 124 |
# File 'lib/jamf/client.rb', line 122 def self.installed? JAMF_BINARY.executable? end |
.jamf_plist ⇒ Hash
The contents of the JAMF plist
an empty hash if not
180 181 182 183 |
# File 'lib/jamf/client.rb', line 180 def self.jamf_plist return {} unless JAMF_PLIST.file? Jamf.parse_plist JAMF_PLIST end |
.jamf_version ⇒ String?
What version of the jamf binary is installed?
130 131 132 |
# File 'lib/jamf/client.rb', line 130 def self.jamf_version installed? ? run_jamf(:version).chomp.split('=')[1] : nil end |
.jss_available? ⇒ Boolean
Is the JSS available right now?
198 199 200 201 |
# File 'lib/jamf/client.rb', line 198 def self.jss_available? run_jamf :checkJSSConnection, '-retry 1' $CHILD_STATUS.exitstatus.zero? end |
.jss_port ⇒ Integer
The port number for JSS connections for this client
170 171 172 173 |
# File 'lib/jamf/client.rb', line 170 def self.jss_port jss_url @port end |
.jss_protocol ⇒ String
The protocol for JSS connections for this client
161 162 163 164 |
# File 'lib/jamf/client.rb', line 161 def self.jss_protocol jss_url @protocol end |
.jss_record ⇒ JSS::Computer?
The JSS::Computer object for this computer
207 208 209 210 211 |
# File 'lib/jamf/client.rb', line 207 def self.jss_record JSS::Computer.fetch udid: udid rescue JSS::NoSuchItemError nil end |
.jss_server ⇒ String
The JSS server hostname for this client
152 153 154 155 |
# File 'lib/jamf/client.rb', line 152 def self.jss_server jss_url @server end |
.jss_url ⇒ String
the URL to the jss for this client
138 139 140 141 142 143 144 145 146 |
# File 'lib/jamf/client.rb', line 138 def self.jss_url @url = jamf_plist['jss_url'] return nil if @url.nil? @url =~ %r{(https?)://(.+):(\d+)/} @protocol = Regexp.last_match(1) @server = Regexp.last_match(2) @port = Regexp.last_match(3) @url end |
.my_ip_address ⇒ String
Get the current IP address as a String.
This handy code doesn't acutally make a UDP connection, it just starts to set up the connection, then uses that to get the local IP.
Lifted gratefully from coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/
104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/jamf/client.rb', line 104 def self.my_ip_address # turn off reverse DNS resolution temporarily # @note the 'socket' library has already been required by 'rest-client' orig = Socket.do_not_reverse_lookup Socket.do_not_reverse_lookup = true UDPSocket.open do |s| s.connect '192.168.0.0', 1 s.addr.last end ensure Socket.do_not_reverse_lookup = orig end |
.parent_pid_user_and_cmd(pid, ps_lines = nil) ⇒ Array<Integer, String, String>
given a pid and optionally the output of `ps -o pid -o ppid -o user -o command` return an array with the pid, user, and command of the pid's parent process
340 341 342 343 344 345 346 347 348 |
# File 'lib/jamf/client.rb', line 340 def self.parent_pid_user_and_cmd(pid, ps_lines = nil) ps_lines ||= `ps -a -x -o pid -o ppid -o user -o command`.lines parent_ps_line = ps_lines.select { |l| l =~ /^\s*#{pid}\s/ }.first return [nil, nil, nil] unless parent_ps_line parent_ps_line =~ /^\s*\d+\s+(\d+)\s+(\S+)\s+(.*)$/ [Regexp.last_match(1).to_i, Regexp.last_match(2), Regexp.last_match(3)] end |
.primary_console_user ⇒ String?
Which console user is using the primary GUI console? Returns nil if the primary GUI console is at the login window.
256 257 258 259 260 |
# File 'lib/jamf/client.rb', line 256 def self.primary_console_user `#{CONSOLE_USERS_SCUTIL_CMD}` =~ /^\s*Name : (\S+)$/ user = Regexp.last_match(1) user == LOGINWINDOW_USER ? nil : user end |
.receipts ⇒ Array<Pathname>
All the JAMF receipts on this client
189 190 191 192 |
# File 'lib/jamf/client.rb', line 189 def self.receipts raise JSS::NoSuchItemError, "The JAMF Receipts folder doesn't exist on this computer." unless RECEIPTS_FOLDER.exist? RECEIPTS_FOLDER.children.select(&:file?) end |
.script_running_via_policy? ⇒ Boolean
Search up the process lineage to see if any ancestor processes indicate that the current process is being run by a Jamf Policy.
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/jamf/client.rb', line 307 def self.script_running_via_policy? root_ps_lines = `ps -u root -x -o pid -o ppid -o user -o command`.lines parent_pid, _parent_user, parent_command = parent_pid_user_and_cmd Process.pid, root_ps_lines return false unless parent_pid until parent_command =~ POLICY_SCRIPT_CMD_RE || parent_pid.nil? parent_pid, _parent_user, parent_command = parent_pid_user_and_cmd parent_pid, root_ps_lines return false if parent_pid.zero? end return false if parent_pid.nil? # if we're here, our parent is a jamf process, lets confirm that its # a jamf policy until parent_command =~ POLICY_CMD_RE || parent_pid.nil? parent_pid, _parent_user, parent_command = parent_pid_user_and_cmd parent_pid, root_ps_lines return false if parent_pid.zero? end !parent_pid.nil? end |
.self_service_users ⇒ Array<String>
Who's currently running Self Service.app? - might be more than one if Fast User Switching is in use.
272 273 274 275 |
# File 'lib/jamf/client.rb', line 272 def self.self_service_users ss_userlines = `#{PS_USER_COMM}`.lines.select { |l| l.include? SELF_SERVICE_EXECUTABLE_END } ss_userlines.map { |ssl| ssl.split(' ').first } end |
.serial_number ⇒ String
The serial number for this computer via system_profiler
225 226 227 |
# File 'lib/jamf/client.rb', line 225 def self.serial_number hardware_data['serial_number'] end |
.udid ⇒ String
The UUID for this computer via system_profiler
217 218 219 |
# File 'lib/jamf/client.rb', line 217 def self.udid hardware_data['platform_UUID'] end |