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

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/sbin/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'
}
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



245
246
247
# File 'lib/jss-api/client.rb', line 245

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

.installed?Boolean



143
144
145
# File 'lib/jss-api/client.rb', line 143

def self.installed?
  JAMF_BINARY.executable?
end

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

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

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

Raises:



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
496
497
498
# File 'lib/jss-api/client.rb', line 404

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



194
195
196
197
# File 'lib/jss-api/client.rb', line 194

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?



150
151
152
# File 'lib/jss-api/client.rb', line 150

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

.jss_available?Boolean



211
212
213
214
# File 'lib/jss-api/client.rb', line 211

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

.jss_portInteger



186
187
188
189
# File 'lib/jss-api/client.rb', line 186

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

.jss_protocolString



178
179
180
181
# File 'lib/jss-api/client.rb', line 178

def self.jss_protocol
  self.jss_url
  return @protocol
end

.jss_recordJSS::Computer



220
221
222
223
224
225
226
# File 'lib/jss-api/client.rb', line 220

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



170
171
172
173
# File 'lib/jss-api/client.rb', line 170

def self.jss_server
  self.jss_url
  return @server
end

.jss_urlString



157
158
159
160
161
162
163
164
165
# File 'lib/jss-api/client.rb', line 157

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/



125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/jss-api/client.rb', line 125

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.



203
204
205
206
# File 'lib/jss-api/client.rb', line 203

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'"]

Raises:



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

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



238
239
240
# File 'lib/jss-api/client.rb', line 238

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

.udidString



231
232
233
# File 'lib/jss-api/client.rb', line 231

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