### Copyright 2018 Pixar ### ### Licensed under the Apache License, Version 2.0 (the "Apache License") ### with the following modification; you may not use this file except in ### compliance with the Apache License and the following modification to it: ### Section 6. Trademarks. is deleted and replaced with: ### ### 6. Trademarks. This License does not grant permission to use the trade ### names, trademarks, service marks, or product names of the Licensor ### and its affiliates, except as required to comply with Section 4(c) of ### the License and to reproduce the content of the NOTICE file. ### ### You may obtain a copy of the Apache License at ### ### http://www.apache.org/licenses/LICENSE-2.0 ### ### Unless required by applicable law or agreed to in writing, software ### distributed under the Apache License with the above modification is ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ### KIND, either express or implied. See the Apache License for the specific ### language governing permissions and limitations under the Apache License. ### ### ### module JSS # class Client # Constants ##################################### # The Pathname to the jamfHelper executable JAMF_HELPER = SUPPORT_BIN_FOLDER + 'jamfHelper.app/Contents/MacOS/jamfHelper' # The window_type options for jamfHelper JAMF_HELPER_WINDOW_TYPES = { hud: 'hud', utility: 'utility', util: 'utility', full_screen: 'fs', fs: 'fs' }.freeze # The possible window positions for jamfHelper JAMF_HELPER_WINDOW_POSITIONS = [nil, :ul, :ll, :ur, :lr].freeze # The available buttons in jamfHelper JAMF_HELPER_BUTTONS = [1, 2].freeze # The possible alignment positions in jamfHelper JAMF_HELPER_ALIGNMENTS = %i[right left center justified natural].freeze # class Methods ##################################### # 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 # # @note 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. # # @param window_type[Symbol] The type of window to display # # @param opts[Hash] the options for the window # # @option opts :window_position [Symbol,nil] one of [ nil, :ul, :ll. :ur, :lr ] # Positions window in the upper right, upper left, lower right or lower left of the user's screen # If no input is given, the window defaults to the center of the screen # # @option opts :title [String] # Sets the window's title to the specified string # # @option opts :heading [String] # Sets the heading of the window to the specified string # # @option opts :align_heading [Symbol] one of [:right, :left, :center, :justified, :natural] # Aligns the heading to the specified alignment # # @option opts :description [String] # Sets the main contents of the window to the specified string # # @option opts :align_description [Symbol] one of [:right, :left, :center, :justified, :natural] # Aligns the description to the specified alignment # # @option opts :icon [String,Pathname] # Sets the windows image field to the image located at the specified path # # @option opts :icon_size [Integer] # Changes the image frame to the specified pixel size # # @option opts :full_screen_icon [any value] # Scales the "icon" to the full size of the window. # Note: Only available in full screen mode # # @option opts :button1 [String] # Creates a button with the specified label # # @option opts :button2 [String] # Creates a second button with the specified label # # @option opts :default_button [Integer] either 1 or 2 # Sets the default button of the window to the specified button. The Default Button will respond to "return" # # @option opts :cancel_button [Integer] either 1 or 2 # Sets the cancel button of the window to the specified button. The Cancel Button will respond to "escape" # # @option opts :timeout [Integer] # Causes the window to timeout after the specified amount of seconds # Note: The timeout will cause the default button, button 1 or button 2 to be selected (in that order) # # @option opts :show_delay_options [String,Array] A String of comma-separated Integers, or an Array of Integers. # Enables the "Delay Options Mode". The window will display a dropdown with the values passed through the string # # @option opts :countdown [any value] # Displays a string notifying the user when the window will time out # # @option opts :align_countdown [Symbol] one of [:right, :left, :center, :justified, :natural] # Aligns the countdown to the specified alignment # # @option opts :lock_hud [Boolean] # Removes the ability to exit the HUD by selecting the close button # # @option opts :abandon_process [Boolean] Abandon the jamfHelper process so that your code can exit. # This is mostly used so that a policy can finish while a dialog is waiting # (possibly forever) for user response. When true, the returned value is the # process id of the abandoned jamfHelper process. # # @option opts :output_file [String, Pathname] Save the output of jamfHelper # (the exit code) into this file. This is useful when using abandon_process. # The output file can be examined later to see what happened. If this option # is not provided, no output is saved. # # @option opts :arg_string [String] The jamfHelper commandline args as a single # String, the way you'd specify them in a shell. This is appended to any # Ruby options provided when calling the method. So calling: # JSS::Client.jamf_helper :hud, title: 'This is a title', arg_string: '-heading "this is a heading"' # will run # jamfHelper -windowType hud -title 'this is a title' -heading "this is a heading" # When using this, be careful not to specify the windowType, since it's generated # by the first, required, parameter of this method. # # @return [Integer] the exit status of the jamfHelper command. See above. # 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 # def self.jamf_helper end # class Client end # module