Class: JSS::Client
Overview
This class represents a Casper/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.
Constant Summary collapse
- ORIG_JAMF_BINARY =
The Pathname to the jamf binary executable As of El Capitan (OS X 10.11) the location has moved.
Pathname.new '/usr/sbin/jamf'
- ELCAP_JAMF_BINARY =
Pathname.new '/usr/local/jamf/bin/jamf'
- JAMF_BINARY =
ELCAP_JAMF_BINARY.executable? ? ELCAP_JAMF_BINARY : ORIG_JAMF_BINARY
- JAMF_HELPER =
The Pathname to the jamfHelper executable
Pathname.new '/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper'
- JAMF_HELPER_WINDOW_TYPES =
The window_type options for jamfHelper
{ hud: 'hud', utility: 'utility', util: 'utility', full_screen: 'fs', fs: 'fs' }.freeze
- JAMF_HELPER_WINDOW_POSITIONS =
The possible window positions for jamfHelper
[nil, :ul, :ll, :ur, :lr].freeze
- JAMF_HELPER_BUTTONS =
The available buttons in jamfHelper
[1, 2].freeze
- JAMF_HELPER_ALIGNMENTS =
The possible alignment positions in jamfHelper
[:right, :left, :center, :justified, :natural].freeze
- 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'
- ROOTLESS_JAMF_COMMANDS =
These jamf commands don’t need root privs (most do)
[ :about, :checkJSSConnection, :getARDFields, :getComputerName, :help, :listUsers, :version ].freeze
Class Method Summary collapse
-
.console_user ⇒ String?
Who’s logged in to the console right now?.
-
.hardware_data ⇒ Hash
The parsed HardwareDataType output from system_profiler.
-
.installed? ⇒ Boolean
Is the jamf binary installed?.
-
.jamf_helper(window_type = :hud, opts = {}) ⇒ Integer
A wrapper for the jamfHelper command, which can display a window on the client machine.
-
.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.
-
.receipts ⇒ Array<Pathname>
All the JAMF receipts on this client.
-
.run_jamf(command, args = nil, verbose = false) ⇒ String
Run an arbitrary jamf binary command.
-
.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 ⇒ String?
Who’s logged in to the console right now?
139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/jss/client.rb', line 139 def self.console_user cmd = '/usr/sbin/scutil' qry = 'show State:/Users/ConsoleUser' Open3.popen2e(cmd) do |cmdin, cmdouterr, _wait_thr| cmdin.puts qry cmdin.close out = cmdouterr.read user = out.lines.select { |l| l =~ /^\s+Name\s*:/ }.first.to_s.split(/\s*:\s*/).last return user.nil? ? user : user.chomp end # do end |
.hardware_data ⇒ Hash
The parsed HardwareDataType output from system_profiler
265 266 267 268 |
# File 'lib/jss/client.rb', line 265 def self.hardware_data raw = `/usr/sbin/system_profiler SPHardwareDataType -xml 2>/dev/null` Plist.parse_xml(raw)[0]['_items'][0] end |
.installed? ⇒ Boolean
Is the jamf binary installed?
155 156 157 |
# File 'lib/jss/client.rb', line 155 def self.installed? JAMF_BINARY.executable? end |
.jamf_helper(window_type = :hud, opts = {}) ⇒ Integer
the -startlaunchd and -kill options are not available in this implementation, since they don’t work at the moment (casper 9.4). -startlaunchd seems to be required to NOT use launchd, and when it’s ommited, an error is generated about the launchd plist permissions being incorrect.
A wrapper for the jamfHelper command, which can display a window on the client machine.
The first parameter must be a symbol defining what kind of window to display. The options are
-
:hud - creates an Apple “Heads Up Display” style window
-
:utility or :util - creates an Apple “Utility” style window
-
:fs or :full_screen or :fullscreen - creates a full screen window that restricts all user input WARNING: Remote access must be used to unlock machines in this mode
The remaining options Hash can contain any of the options listed. See below for descriptions.
The value returned is the Integer exitstatus/stdout (both are the same) of the jamfHelper command. The meanings of those integers are:
-
0 - Button 1 was clicked
-
1 - The Jamf Helper was unable to launch
-
2 - Button 2 was clicked
-
3 - Process was started as a launchd task
-
XX1 - Button 1 was clicked with a value of XX seconds selected in the drop-down
-
XX2 - Button 2 was clicked with a value of XX seconds selected in the drop-down
-
239 - The exit button was clicked
-
240 - The “ProductVersion” in sw_vers did not return 10.5.X, 10.6.X or 10.7.X
-
243 - The window timed-out with no buttons on the screen
-
250 - Bad “-windowType”
-
254 - Cancel button was select with delay option present
-
255 - No “-windowType”
If the :abandon_process option is given, the integer returned is the Process ID of the abondoned process running jamfHelper.
See also /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -help
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
# File 'lib/jss/client.rb', line 446 def self.jamf_helper(window_type = :hud, opts = {}) raise JSS::UnmanagedError, 'The jamfHelper app is not installed properly on this computer.' unless JAMF_HELPER.executable? unless JAMF_HELPER_WINDOW_TYPES.include? window_type raise JSS::InvalidDataError, "The first parameter must be a window type, one of :#{JAMF_HELPER_WINDOW_TYPES.keys.join(', :')}." end # start building the arg array args = ['-startlaunchd', '-windowType', JAMF_HELPER_WINDOW_TYPES[window_type]] opts.keys.each do |opt| case opt when :window_position raise JSS::InvalidDataError, ":window_position must be one of :#{JAMF_HELPER_WINDOW_POSITIONS.join(', :')}." unless \ JAMF_HELPER_WINDOW_POSITIONS.include? opts[opt].to_sym args << '-windowPosition' args << opts[opt].to_s when :title args << '-title' args << opts[opt].to_s when :heading args << '-heading' args << opts[opt].to_s when :align_heading raise JSS::InvalidDataError, ":align_heading must be one of :#{JAMF_HELPER_ALIGNMENTS.join(', :')}." unless \ JAMF_HELPER_ALIGNMENTS.include? opts[opt].to_sym args << '-alignHeading' args << opts[opt].to_s when :description args << '-description' args << opts[opt].to_s when :align_description raise JSS::InvalidDataError, ":align_description must be one of :#{JAMF_HELPER_ALIGNMENTS.join(', :')}." unless \ JAMF_HELPER_ALIGNMENTS.include? opts[opt].to_sym args << '-alignDescription' args << opts[opt].to_s when :icon args << '-icon' args << opts[opt].to_s when :icon_size args << '-iconSize' args << opts[opt].to_s when :full_screen_icon args << '-fullScreenIcon' when :button1 args << '-button1' args << opts[opt].to_s when :button2 args << '-button2' args << opts[opt].to_s when :default_button raise JSS::InvalidDataError, ":default_button must be one of #{JAMF_HELPER_BUTTONS.join(', ')}." unless \ JAMF_HELPER_BUTTONS.include? opts[opt] args << '-defaultButton' args << opts[opt].to_s when :cancel_button raise JSS::InvalidDataError, ":cancel_button must be one of #{JAMF_HELPER_BUTTONS.join(', ')}." unless \ JAMF_HELPER_BUTTONS.include? opts[opt] args << '-cancelButton' args << opts[opt].to_s when :timeout args << '-timeout' args << opts[opt].to_s when :show_delay_options args << '-showDelayOptions' args << JSS.to_s_and_a(opts[opt])[:arrayform].join(', ') when :countdown args << '-countdown' if opts[opt] when :align_countdown raise JSS::InvalidDataError, ":align_countdown must be one of :#{JAMF_HELPER_ALIGNMENTS.join(', :')}." unless \ JAMF_HELPER_ALIGNMENTS.include? opts[opt].to_sym args << '-alignCountdown' args << opts[opt].to_s when :lock_hud args << '-lockHUD' if opts[opt] end # case opt end # each do opt cmd = Shellwords.escape JAMF_HELPER.to_s args.each { |arg| cmd << " #{Shellwords.escape arg}" } cmd << " #{opts[:arg_string]}" if opts[:arg_string] cmd << " > #{Shellwords.escape opts[:output_file]}" if opts[:output_file] if opts[:abandon_process] pid = Process.fork if pid.nil? # In child exec cmd else # In parent Process.detach(pid) pid end else system cmd $CHILD_STATUS.exitstatus end end |
.jamf_plist ⇒ Hash
The contents of the JAMF plist
212 213 214 215 |
# File 'lib/jss/client.rb', line 212 def self.jamf_plist return {} unless JAMF_PLIST.file? Plist.parse_xml `/usr/libexec/PlistBuddy -x -c print #{Shellwords.escape JSS::Client::JAMF_PLIST.to_s}` end |
.jamf_version ⇒ String?
What version of the jamf binary is installed?
163 164 165 |
# File 'lib/jss/client.rb', line 163 def self.jamf_version installed? ? run_jamf(:version).chomp.split('=')[1] : nil end |
.jss_available? ⇒ Boolean
Is the JSS available right now?
230 231 232 233 |
# File 'lib/jss/client.rb', line 230 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
203 204 205 206 |
# File 'lib/jss/client.rb', line 203 def self.jss_port jss_url @port end |
.jss_protocol ⇒ String
The protocol for JSS connections for this client
194 195 196 197 |
# File 'lib/jss/client.rb', line 194 def self.jss_protocol jss_url @protocol end |
.jss_record ⇒ JSS::Computer?
The JSS::Computer object for this computer
239 240 241 242 243 |
# File 'lib/jss/client.rb', line 239 def self.jss_record JSS::Computer.new udid: udid rescue JSS::NoSuchItemError nil end |
.jss_server ⇒ String
The JSS server hostname for this client
185 186 187 188 |
# File 'lib/jss/client.rb', line 185 def self.jss_server jss_url @server end |
.jss_url ⇒ String
the URL to the jss for this client
171 172 173 174 175 176 177 178 179 |
# File 'lib/jss/client.rb', line 171 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/
121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/jss/client.rb', line 121 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 |
.receipts ⇒ Array<Pathname>
All the JAMF receipts on this client
221 222 223 224 |
# File 'lib/jss/client.rb', line 221 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 |
.run_jamf(command, args = nil, verbose = false) ⇒ String
Most jamf commands require superuser/root privileges.
Run an arbitrary jamf binary command.
The details of the Process::Status for the jamf binary process can be captured from $? immediately after calling. (See Process::Status)
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/jss/client.rb', line 297 def self.run_jamf(command, args = nil, verbose = false) raise JSS::UnmanagedError, 'The jamf binary is not installed on this computer.' unless installed? raise JSS::UnsupportedError, 'You must have root privileges to run that jamf binary command' unless \ ROOTLESS_JAMF_COMMANDS.include?(command.to_sym) || JSS.superuser? cmd = case args when nil "#{JAMF_BINARY} #{command}" when String "#{JAMF_BINARY} #{command} #{args}" when Array ([JAMF_BINARY.to_s, command] + args).join(' ').to_s else raise JSS::InvalidDataError, 'args must be a String or Array of Strings' end # case cmd += ' -verbose' if verbose && (!cmd.include? ' -verbose') puts "Running: #{cmd}" if verbose output = [] IO.popen("#{cmd} 2>&1") do |proc| while line = proc.gets output << line puts line if verbose end end install_out = output.join('') install_out.force_encoding('UTF-8') if install_out.respond_to? :force_encoding install_out end |
.serial_number ⇒ String
The serial number for this computer via system_profiler
257 258 259 |
# File 'lib/jss/client.rb', line 257 def self.serial_number hardware_data['serial_number'] end |
.udid ⇒ String
The UUID for this computer via system_profiler
249 250 251 |
# File 'lib/jss/client.rb', line 249 def self.udid hardware_data['platform_UUID'] end |