Class: JSS::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/jss-api/client.rb,
lib/jss-api.rb

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

JAMF_BINARY =

The Pathname to the jamf binary executable

Pathname.new "/usr/sbin/jamf"
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'
}
JAMF_HELPER_WINDOW_POSITIONS =

The possible window positions for jamfHelper

[nil, :ul, :ll, :ur, :lr]
JAMF_HELPER_BUTTONS =

The available buttons in jamfHelper

[1,2]
JAMF_HELPER_ALIGNMENTS =

The possible alignment positions in jamfHelper

[:right, :left, :center, :justified, :natural]
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 ]

Class Method Summary collapse

Class Method Details

.hardware_dataHash

Returns the HardwareDataType data from the system_profiler command.

Returns:

  • (Hash)

    the HardwareDataType data from the system_profiler command



242
243
244
# File 'lib/jss-api/client.rb', line 242

def self.hardware_data
   Plist.parse_xml(`system_profiler SPHardwareDataType -xml`)[0]["_items"][0]
end

.installed?Boolean

Returns is the jamf binary installed?.

Returns:

  • (Boolean)

    is the jamf binary installed?



140
141
142
# File 'lib/jss-api/client.rb', line 140

def self.installed?
  JAMF_BINARY.executable?
end

.jamf_helper(window_type = :hud, opts = {}) ⇒ Integer

Returns the exit status of the jamfHelper command. See above.

Parameters:

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :countdown (any value)

    Displays a string notifying the user when the window will time out

  • :align_countdown (Symbol)

    one of [:right, :left, :center, :justified, :natural] Aligns the countdown to the specified alignment

  • :lock_hud (any value)

    Removes the ability to exit the HUD by selecting the close button

Returns:

  • (Integer)

    the exit status of the jamfHelper command. See above.

Raises:



401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
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
# File 'lib/jss-api/client.rb', line 401

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"

      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 "
    end # case opt
  end # each do opt

  system JAMF_HELPER.to_s, *args
  return $?.exitstatus

end

.jamf_plistHash

Returns the parsed contents of the JAMF_PLIST if it exists, an empty hash if not.

Returns:

  • (Hash)

    the parsed contents of the JAMF_PLIST if it exists, an empty hash if not



191
192
193
194
# File 'lib/jss-api/client.rb', line 191

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_versionString?

Returns the version of the jamf binary installed on this client, nil if not installed.

Returns:

  • (String, nil)

    the version of the jamf binary installed on this client, nil if not installed



147
148
149
# File 'lib/jss-api/client.rb', line 147

def self.jamf_version
  self.installed? ?  self.run_jamf(:version).chomp.split('=')[1] : nil
end

.jss_available?Boolean

Returns is the JSS available now?.

Returns:

  • (Boolean)

    is the JSS available now?



208
209
210
211
# File 'lib/jss-api/client.rb', line 208

def self.jss_available?
  output = run_jamf :checkJSSConnection, "-retry 1"
  $?.exitstatus == 0
end

.jss_portInteger

Returns the port to the JSS for this client.

Returns:

  • (Integer)

    the port to the JSS for this client



183
184
185
186
# File 'lib/jss-api/client.rb', line 183

def self.jss_port
  self.jss_url
  @port  ? @port.to_i : 80
end

.jss_protocolString

Returns the protocol to the JSS for this client, “http” or “https”.

Returns:

  • (String)

    the protocol to the JSS for this client, “http” or “https”



175
176
177
178
# File 'lib/jss-api/client.rb', line 175

def self.jss_protocol
  self.jss_url
  return @protocol
end

.jss_recordJSS::Computer

Returns The JSS record for this computer.

Returns:



217
218
219
220
221
222
223
# File 'lib/jss-api/client.rb', line 217

def self.jss_record
  begin
    JSS::Computer.new :udid => self.udid
  rescue JSS::NoSuchItemError
    JSS::Computer.new :serial_number => self.serial_number
  end
end

.jss_serverString

Returns the JSS server for this client.

Returns:

  • (String)

    the JSS server for this client



167
168
169
170
# File 'lib/jss-api/client.rb', line 167

def self.jss_server
  self.jss_url
  return @server
end

.jss_urlString

Returns the url to the JSS for this client.

Returns:

  • (String)

    the url to the JSS for this client



154
155
156
157
158
159
160
161
162
# File 'lib/jss-api/client.rb', line 154

def self.jss_url
  @url = self.jamf_plist['jss_url']
  return nil if @url.nil?
  @url =~ %r{(https?)://(.+):(\d+)/}
  @protocol = $1
  @server = $2
  @port = $3
  return @url
end

.my_ip_addressString

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/

Returns:

  • (String)

    the current IP address.



122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/jss-api/client.rb', line 122

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

.receiptsArray<Pathname>

Returns an array of Pathnames for all regular files in the jamf receipts folder.

Returns:

  • (Array<Pathname>)

    an array of Pathnames for all regular files in the jamf receipts folder

Raises:



200
201
202
203
# File 'lib/jss-api/client.rb', line 200

def self.receipts
  raise JSS::NoSuchItemError, "The JAMF Receipts folder doesn't exist on this computer." unless RECEIPTS_FOLDER.exist?
  RECEIPTS_FOLDER.children.select{|c| c.file?}
end

.run_jamf(command, args = nil, show_output = false) ⇒ String

Note:

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)

Examples:

These two are equivalent:

  JSS::Client.run_jamf "recon", "-assetTag 12345 -department 'IT Support'"

  JSS::Client.run_jamf :recon, ['-assetTag', '12345', '-department', 'IT Support'"]

Parameters:

  • command (String, Symbol)

    the jamf binary command to run The command is the single jamf command that comes after the/usr/bin/jamf.

  • args (String, Array) (defaults to: nil)

    the arguments passed to the jamf command. This is to be passed to Kernel.‘ (backtick), after being combined with the jamf binary and the jamf command

  • show_output (Boolean) (defaults to: false)

    Should the stdout & stderr of the jamf binary be sent to the current stdout in realtime, as well as returned as a string?

Returns:

  • (String)

    the stdout & stderr of the jamf binary.

Raises:



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/jss-api/client.rb', line 277

def self.run_jamf(command, args = nil, show_output  = false)
  raise JSS::UnmanagedError, "The jamf binary is not installed on this computer." unless self.installed?
  raise JSS::UnsupportedError, "You must have root privileges to run that jamf binary command" unless ROOTLESS_JAMF_COMMANDS.include? command.to_sym or 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(' ')}"
    else
      raise JSS::InvalidDataError, "args must be a String or Array of Strings"
  end # case

  output = []
  IO.popen("#{cmd} 2>&1") do |proc|
    while line = proc.gets
      output << line
      puts line if show_output
    end
  end

  return output.join('')

end

.serial_numberString

Returns the serial number for this computer.

Returns:

  • (String)

    the serial number for this computer



235
236
237
# File 'lib/jss-api/client.rb', line 235

def self.serial_number
  self.hardware_data["serial_number"]
end

.udidString

Returns the UUID/UDID for this computer.

Returns:

  • (String)

    the UUID/UDID for this computer



228
229
230
# File 'lib/jss-api/client.rb', line 228

def self.udid
  self.hardware_data["platform_UUID"]
end