class Jamf::Package

A Package in the JSS

Also the API provides no access to the package’s file list (index), so indexing must be done separately (usually via Casper Admin)

Constants

CATEGORY_DATA_TYPE

How is the category stored in the API data?

CATEGORY_SUBSET

Where is the Category in the API JSON?

CHECKSUM_HASH_TYPES

Mapping of the hash types to the maching Digest modules See {#calculate_checksum}

CHECKSUM_HASH_TYPE_MD5

The hash_type value in the API for md5

CHECKSUM_HASH_TYPE_SHA512

The hash_type value in the API for sha512

CPU_TYPES

The possible values for cpu_type (required_processor) in a JSS package

DB_TABLE

The table in the database for this object

DEFAULT_CHECKSUM_HASH_TYPE
DEFAULT_PRIORITY

the default priority, since one is needed for making new pkgs

DEFAULT_PROCESSOR

by default, no processor requirement

DIST_POINT_PKGS_FOLDER

The pkg storage folder on the distribution point

DO_NOT_INSTALL

When we shouldn’t install anything (e.g. switch w/package)

OBJECT_HISTORY_OBJECT_TYPE

the object type for this object in the object history table. See {APIObject#add_object_history_entry}

PRIORITIES

the possible priorities

RSRC_BASE

The base for REST resources of this class

RSRC_LIST_KEY

the hash key used for the JSON list output of all objects in the JSS

RSRC_OBJECT_KEY

The hash key used for the JSON object output. It’s also used in various error messages

Attributes

allow_uninstalled[R]

@return [Boolean] can this item be uninstalled? Some, e.g. OS Updates, can’t

boot[R]

@return [Boolean] should this pkg be installed on the boot volume during imaging

boot?[R]

@return [Boolean] should this pkg be installed on the boot volume during imaging

boot_volume_required[R]

@return [Boolean] should this pkg be installed on the boot volume during imaging

checksum[R]

@return [String, nil] the checksum value for the package file on the

dist. point, if it's been calculated.
checksum_type[R]

@ @return [Symbol] The checksum hash type used to generate the checksum value,

either :md5 or :sha512, defaults to :sha512 if there is no checksum yet.
cpu_type[R]

@return [String] limit installation to these architectures: ‘x86’, ‘ppc’, ‘None’

feu[R]

@return [Boolean] does this item ‘Fill Existing Users’ when jamf installs it?

feu?[R]

@return [Boolean] does this item ‘Fill Existing Users’ when jamf installs it?

filename[R]

@return [String] the filename of the .pkg, .mpkg, or .dmg on the Casper server

fill_existing_users[R]

@return [Boolean] does this item ‘Fill Existing Users’ when jamf installs it?

fill_user_template[R]

@return [Boolean] does this pkg also get install in the OS user homedir template

fut[R]

@return [Boolean] does this pkg also get install in the OS user homedir template

fut?[R]

@return [Boolean] does this pkg also get install in the OS user homedir template

if_in_swupdate[R]

@return [Boolean] only install this pkg if it’s available in the commandline softwareupdate.

if_in_swupdate?[R]

@return [Boolean] only install this pkg if it’s available in the commandline softwareupdate.

info[R]

@return [String] the info field for this pkg - stores d3’s basename & swupdate values

install_if_reported_available[R]

@return [Boolean] only install this pkg if it’s available in the commandline softwareupdate.

notes[R]

@return [String] the notes field for this pkg

notify[R]

@return [Boolean] does this pkg cause a notification to be sent on self-heal?

os_requirements[R]

@return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x

oses[R]

@return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x

priority[R]

@return [Integer] Priority to use for deploying or uninstalling the package

reboot[R]

@return [Boolean] does this item require a reboot after installation?

reboot?[R]

@return [Boolean] does this item require a reboot after installation?

reboot_required[R]

@return [Boolean] does this item require a reboot after installation?

receipt[R]

@return [Pathname] the local receipt when this pkg is installed

removable[R]

@return [Boolean] can this item be uninstalled? Some, e.g. OS Updates, can’t

removable?[R]

@return [Boolean] can this item be uninstalled? Some, e.g. OS Updates, can’t

required_processor[R]

@return [String] limit installation to these architectures: ‘x86’, ‘ppc’, ‘None’

send_notification[R]

@return [Boolean] does this pkg cause a notification to be sent on self-heal?

switch_with_package[R]

@return [String] the name of a pkg to install (or “Do Not Install”) when this pkg can’t be installed

Public Class Methods

all_filenames(api: nil, cnx: Jamf.cnx) click to toggle source

An array of all dist-point filenames used by all JSS packages

Slow cuz we have to instantiate every pkg

@param cnx [Jamf::Connection] an API connection to use

Defaults to the corrently active API. See {Jamf::Connection}

@return [Array<String>] The current file names

    # File lib/jamf/api/classic/api_objects/package.rb
123 def self.all_filenames(api: nil, cnx: Jamf.cnx)
124   cnx = api if api
125   all_filenames_by(:id, cnx: cnx).values
126 end
all_filenames_by(key, api: nil, cnx: Jamf.cnx) click to toggle source

A Hash of all dist-point filenames used by all JSS packages, keyed by package name or id

Slow cuz we have to instantiate every pkg

@param key either :id, or :name

@param cnx [Jamf::Connection] an API connection to use

Defaults to the corrently active API. See {Jamf::Connection}

@return [Hash{Ingeter,String => String}] The current file names by key

    # File lib/jamf/api/classic/api_objects/package.rb
140 def self.all_filenames_by(key, api: nil, cnx: Jamf.cnx)
141   cnx = api if api
142 
143   raise ArgumentError, 'key must be :id or :name' unless %i[id name].include? key
144 
145   files_in_use = {}
146   all_ids(:refresh, cnx: cnx).each do |pkg_id|
147     pkg = fetch id: pkg_id, cnx: cnx
148     files_in_use[pkg.send(key)] = pkg.filename
149   end
150 
151   files_in_use
152 end
calculate_checksum(filepath, type = DEFAULT_CHECKSUM_HASH_TYPE ) click to toggle source

Given a file path, and hash type, generate the checksum for an arbitrary file.

@param filepath [String, Pathname] The file to checksum

@param type [String ] One of the keys of CHECKSUM_HASH_TYPES, either

CHECKSUM_HASH_TYPE_MD5 or CHECKSUM_HASH_TYPE_SHA512

@return [String] The checksum of the file

    # File lib/jamf/api/classic/api_objects/package.rb
223 def self.calculate_checksum(filepath, type = DEFAULT_CHECKSUM_HASH_TYPE )
224   raise ArgumentError, 'Unknown checksum hash type' unless CHECKSUM_HASH_TYPES.key? type
225   CHECKSUM_HASH_TYPES[type].file(filepath).hexdigest
226 end
fetch_dist_point(dist_point, api: nil, cnx: Jamf.cnx) click to toggle source

@param dist_point [String,Integer] the name or id of the distribution

point to use. Defaults to the Master Dist. Point

@return [Jamf::DistributionPoint]

    # File lib/jamf/api/classic/api_objects/package.rb
232 def self.fetch_dist_point(dist_point, api: nil, cnx: Jamf.cnx)
233   cnx = api if api
234 
235   if dist_point
236     Jamf::DistributionPoint.fetch dist_point, cnx: cnx
237   else
238     Jamf::DistributionPoint.master_distribution_point cnx: cnx
239   end
240 end
missing_files(ro_pw, unmount = true, dist_point: nil, api: nil, cnx: Jamf.cnx) click to toggle source

An array of String filenames for all filenames in any Jamf::Package that don’t exist on DIST_POINT_PKGS_FOLDER

Slow cuz we have to instantiate every pkg

@param ro_pw the password for the readonly account

on the master Distribution Point,

@param unmount whether or not ot unount the

distribution point when finished.

@param cnx [Jamf::Connection] an API connection to use

Defaults to the corrently active API. See {Jamf::Connection}

@param dist_point [String,Integer] the name or id of the distribution

point to use. Defaults to the Master Dist. Point

@return [Array<String>] The orphaned files

    # File lib/jamf/api/classic/api_objects/package.rb
203 def self.missing_files(ro_pw, unmount = true, dist_point: nil, api: nil, cnx: Jamf.cnx)
204   cnx = api if api
205 
206   dp = fetch_dist_point(dist_point, cnx: cnx)
207   pkgs_dir = dp.mount(ro_pw, :ro) + DIST_POINT_PKGS_FOLDER
208   files_on_dp = pkgs_dir.children.map { |f| f.basename.to_s }
209   dp.unmount if unmount
210   all_filenames(cnx: cnx) - files_on_dp
211 end
new(**args) click to toggle source

@see Jamf::APIObject#initialize

Calls superclass method Jamf::APIObject::new
    # File lib/jamf/api/classic/api_objects/package.rb
300 def initialize(**args)
301   super
302 
303   # now we have pkg_data with something in it, so fill out the instance vars
304   @allow_uninstalled = @init_data[:allow_uninstalled]
305   @boot_volume_required = @init_data[:boot_volume_required]
306   @filename = @init_data[:filename] || @init_data[:name]
307   @fill_existing_users = @init_data[:fill_existing_users]
308   @fill_user_template = @init_data[:fill_user_template]
309   @info = @init_data[:info]
310   @install_if_reported_available = @init_data[:install_if_reported_available]
311   @notes = @init_data[:notes]
312   @os_requirements = @init_data[:os_requirements].split(/\s*,\s*/) if @init_data[:os_requirements]
313   @os_requirements ||= []
314 
315   @priority = @init_data[:priority] || DEFAULT_PRIORITY
316   @reboot_required = @init_data[:reboot_required]
317   @required_processor = @init_data[:required_processor] || DEFAULT_PROCESSOR
318   @required_processor = nil if @required_processor.to_s.casecmp('none').zero?
319   @send_notification = @init_data[:send_notification]
320   @switch_with_package = @init_data[:switch_with_package] || DO_NOT_INSTALL
321 
322   @checksum = @init_data[:hash_value] #ill be nil if no checksum
323   @checksum_type = @checksum ? @init_data[:hash_type] : DEFAULT_CHECKSUM_HASH_TYPE
324 
325 
326   # the receipt is the filename with any .zip extension removed.
327   @receipt = @filename ? (Jamf::Client::RECEIPTS_FOLDER + @filename.to_s.sub(/.zip$/, '')) : nil
328 end
orphaned_files(ro_pw, unmount = true, dist_point: nil, api: nil, cnx: Jamf.cnx) click to toggle source

An array of String filenames for all files DIST_POINT_PKGS_FOLDER that aren’t used by a Jamf::Package

Slow cuz we have to instantiate every pkg

@param ro_pw the password for the readonly account

on the master Distribution Point,

@param unmount whether or not ot unount the

distribution point when finished.

@param cnx [Jamf::Connection] an API connection to use

Defaults to the corrently active API. See {Jamf::Connection}

@param dist_point [String,Integer] the name or id of the distribution

point to use. Defaults to the Master Dist. Point

@return [Array<String>] The orphaned files

    # File lib/jamf/api/classic/api_objects/package.rb
173 def self.orphaned_files(ro_pw, unmount = true, dist_point: nil, api: nil, cnx: Jamf.cnx)
174   cnx = api if api
175 
176   dp = fetch_dist_point(dist_point, cnx: cnx)
177   pkgs_dir = dp.mount(ro_pw, :ro) + DIST_POINT_PKGS_FOLDER
178   files_on_dp = pkgs_dir.children.map { |f| f.basename.to_s }
179   dp.unmount if unmount
180   files_on_dp - all_filenames(cnx: cnx)
181 end

Public Instance Methods

allow_uninstalled=(new_val) click to toggle source

Change the ‘allow to be uninstalled’ field in the JSS NOTE The package must be indexed before this works. Right now, that means using CasperAdmin.app

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
338 def allow_uninstalled=(new_val)
339   return nil if new_val == @allow_uninstalled
340 
341   # removable? defaults to false
342   # even though we usually want to be able to ununstall things, it would be
343   # dangerous to do on things like OS updates, so it must be turned on explicitly.
344   # packages must be indexed with Casper Admin in order to be uninstalled.
345   new_val = false if new_val.to_s.empty?
346   raise Jamf::InvalidDataError, "allow_uninstalled must be boolean 'true' or 'false'" unless Jamf::TRUE_FALSE.include? new_val
347 
348   @allow_uninstalled = new_val
349   @need_to_update = true
350 end
Also aliased as: removable=
boot=(new_val)
boot_volume_required=(new_val) click to toggle source

Change the boot volume required field in the JSS

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
358 def boot_volume_required=(new_val)
359   return nil if new_val == @boot_volume_required
360   new_val = false if new_val.to_s.empty?
361   raise Jamf::InvalidDataError, 'install_if_reported_available must be boolean true or false' unless Jamf::TRUE_FALSE.include? new_val
362   @boot_volume_required = new_val
363   @need_to_update = true
364 end
Also aliased as: boot=
calculate_checksum(type: nil, local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true, dist_point: nil ) click to toggle source

Caclulate and return the checksum hash for a given local file, or the file on the master dist point if no local file is given.

@param type [String] The checksum hash type, one of the keys of

CHECKSUM_HASH_TYPES

@param local_file [String, Pathname] A local copy of the pkg file. BE SURE

it's identical to the one on the server. If omitted, the master dist.
point will be mounted and the file read from there.

@param rw_pw [String] The read-write password for mounting the master dist

point. Either this or the ro_pw must be provided if no local_file

@param ro_pw [String] The read-onlypassword for mounting the master dist

point. Either this or the rw_pw must be provided if no local_file

@param unmount [Boolean] Unmount the master dist point after using it.

Only used if the dist point is mounted. default: true

@param dist_point [String,Integer] the name or id of the distribution

point to use. Defaults to the Master Dist. Point

@return [String] The calculated checksum

    # File lib/jamf/api/classic/api_objects/package.rb
757 def calculate_checksum(type: nil, local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true, dist_point: nil )
758   type ||= DEFAULT_CHECKSUM_HASH_TYPE
759   dp = self.class.fetch_dist_point(dist_point, cnx: @cnx)
760 
761   if local_file
762     file_to_calc = local_file
763   else
764     if rw_pw
765       dppw = rw_pw
766       mnt = :rw
767     elsif ro_pw
768       dppw = ro_pw
769       mnt = :ro
770     else
771       raise ArgumentError, 'Either rw_pw: or ro_pw: must be provided'
772     end
773     file_to_calc = dp.mount(dppw, mnt) + "#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
774   end
775   new_checksum = self.class.calculate_checksum(file_to_calc, type)
776   dp.unmount if unmount && dp.mounted?
777   new_checksum
778 end
checksum=(new_val) click to toggle source

Change the checksum type

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
473 def checksum=(new_val)
474   return if new_val == @checksum
475   raise Jamf::InvalidDataError, 'Checksum must be a String or nil' unless new_val.is_a?(String) || new_val.nil?
476 
477   @checksum = new_val
478   @need_to_update = true
479 end
checksum_type=(new_val) click to toggle source

Change the checksum type

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
459 def checksum_type=(new_val)
460   return if new_val == @checksum_type
461   raise Jamf::InvalidDataError, "Checksum type must be one of: #{CHECKSUM_HASH_TYPES.keys.join ', '} " unless CHECKSUM_HASH_TYPES.key? new_val
462 
463   @checksum_type = new_val
464   @need_to_update = true
465 end
checksum_valid?(local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true, dist_point: nil ) click to toggle source

Is the checksum for this pkg is valid?

@param local_file [String, Pathname] A local copy of the pkg file. BE SURE

it's identical to the one on the server. If omitted, the master dist.
point will be mounted and the file read from there.

@param rw_pw [String] The read-write password for mounting the master dist

point. Either this or the ro_pw must be provided if no local_file

@param ro_pw [String] The read-onlypassword for mounting the master dist

point. Either this or the rw_pw must be provided if no local_file

@param unmount [Boolean] Unmount the master dist point after using it.

Only used if the dist point is mounted. default: true

@param dist_point [String,Integer] the name or id of the distribution

point to use. Defaults to the Master Dist. Point

@return [Boolean] false if there is no checksum for this pkg, otherwise,

does the calculated checksum match the one stored for the pkg?
    # File lib/jamf/api/classic/api_objects/package.rb
801 def checksum_valid?(local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true, dist_point: nil )
802   return false unless @checksum
803   new_checksum = calculate_checksum(
804     type: @checksum_type,
805     local_file: local_file,
806     rw_pw: rw_pw,
807     ro_pw: ro_pw,
808     unmount: unmount,
809     dist_point: dist_point
810   )
811   new_checksum == @checksum
812 end
cpu_type=(new_val)
Alias for: required_processor=
delete(delete_file: false, rw_pw: nil, unmount: true, dist_point: nil) click to toggle source

Delete this package from the JSS, optionally deleting the master dist point file also.

@param delete_file should the master dist point file be deleted?

@param rw_pw the password for the read/write account on the master Distribution Point

or :prompt, or :stdin# where # is the line of stdin containing the password. See {Jamf::DistributionPoint#mount}

@param unmount whether or not ot unount the distribution point when finished.

@param dist_point [String,Integer] the name or id of the distribution

point to use. Defaults to the Master Dist. Point

@return [void]

Calls superclass method Jamf::APIObject::delete
    # File lib/jamf/api/classic/api_objects/package.rb
892 def delete(delete_file: false, rw_pw: nil, unmount: true, dist_point: nil)
893   super()
894   delete_master_file(rw_pw, unmount, dist_point: dist_point) if delete_file
895 end
delete_master_file(rw_pw, unmount = true, dist_point: nil) click to toggle source

Delete the filename from the master distribution point, if it exists.

If you’ll be uploading several files you can specify unmount as false, and do it manually when all are finished.

@param rw_pw the password for the read/write account on the master Distribution Point

or :prompt, or :stdin# where # is the line of stdin containing the password. See {Jamf::DistributionPoint#mount}

@param unmount whether or not ot unount the distribution point when finished.

@param dist_point [String,Integer] the name or id of the distribution

point to use. Defaults to the Master Dist. Point

@return [Boolean] was the file deleted?

    # File lib/jamf/api/classic/api_objects/package.rb
865 def delete_master_file(rw_pw, unmount = true, dist_point: nil)
866   dp = self.class.fetch_dist_point(dist_point, cnx: @cnx)
867   file = dp.mount(rw_pw, :rw) + "#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
868   if file.exist?
869     file.delete
870     did_it = true
871   else
872     did_it = false
873   end # if exists
874   dp.unmount if unmount
875   did_it
876 end
feu=(new_val)
filename=(new_val) click to toggle source

Change the package filename. Setting it to nil or empty will make it match the display name

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
373 def filename=(new_val)
374   new_val = nil if new_val == ''
375   new_val ||= @name
376   return nil if new_val == @filename
377 
378   @filename = new_val
379   @need_to_update = true
380 end
fill_existing_users=(new_val) click to toggle source

Change the Fill Existing Users value

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
388 def fill_existing_users=(new_val)
389   return nil if new_val == @fill_existing_users
390   new_val = false if new_val.to_s.empty?
391   raise Jamf::InvalidDataError, "fill_existing_users must be boolean 'true' or 'false'" unless Jamf::TRUE_FALSE.include? new_val
392   @fill_existing_users = new_val
393   @need_to_update = true
394 end
Also aliased as: feu=
fill_user_template=(new_val) click to toggle source

Change the fill_user_template value

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
402 def fill_user_template=(new_val)
403   return nil if new_val == @fill_user_template
404   new_val = false if new_val.to_s.empty?
405   raise Jamf::InvalidDataError, "fill_user_template must be boolean 'true' or 'false'" unless Jamf::TRUE_FALSE.include? new_val
406   @fill_user_template = new_val
407   @need_to_update = true
408 end
Also aliased as: fut=
fut=(new_val)
Alias for: fill_user_template=
if_in_swupdate=(new_val)
info=(new_val) click to toggle source

Change the info field in the JSS.

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
416 def info=(new_val)
417   return nil if new_val == @info
418   # line breaks should be \r
419   new_val = new_val.to_s.tr("\n", "\r")
420   @info = new_val
421   @need_to_update = true
422 end
install(**args) click to toggle source

Install this package via the jamf binary ‘install’ command from the distribution point for this machine. See {Jamf::DistributionPoint.my_distribution_point}

@note This code must be run as root to install packages

The read-only or http passwd for the dist. point must be provided, except for non-authenticated http downloads)

@param args the arguments for installation

@option args :ro_pw the read-only or http password for the

distribution point for the local machine
(http will be used if available, and may not need a pw)

@option args :target The drive on which to install

the package, defaults to '/'

@option args :verbose [Boolean] be verbose to stdout, defaults to false

@option args :feu fill existing users, defaults to false

@option args :fut fill user template, defaults to false

@option args :unmount unmount the distribution point when

finished?(if we mounted it), defaults to false

@option args :no_http don’t use http downloads even if they

are enabled for the dist. point.

@option args :alt_download_url [String] Use this url for an http

download, regardless of distribution point settings. This can be used
to access Cloud Distribution Points if the fileshare isn't available.
The URL should already be ur
The package filename will be removed or appended as needed.

@return [Boolean] did the jamf install succeed?

@todo deal with cert-based https authentication in dist points

     # File lib/jamf/api/classic/api_objects/package.rb
 937 def install(**args)
 938   raise Jamf::UnsupportedError, 'You must have root privileges to install packages' unless JSS.superuser?
 939 
 940   args[:target] ||= '/'
 941 
 942   ro_pw = args[:ro_pw]
 943 
 944   # as of Casper 9.72, with http downloads, the jamf binary requires
 945   # the filename must be at the  end of the -path url, but before 9.72
 946   # it can't be.
 947   # e.g.
 948   #    in  <9.72:  jamf install  -package foo.pkg -path http://mycasper.myorg.edu/CasperShare/Packages
 949   # but
 950   #    in >=9.72:  jamf install  -package foo.pkg -path http://mycasper.myorg.edu/CasperShare/Packages/foo.pkg
 951   #
 952   append_at_vers = JSS.parse_jss_version('9.72')[:version]
 953   our_vers = JSS.parse_jss_version(@cnx.server.raw_version)[:version]
 954   no_filename_in_url = (our_vers < append_at_vers)
 955 
 956   # use a provided alternative url for an http download
 957   if args[:alt_download_url]
 958 
 959     # we'll re-add the filename below if needed.
 960     src_path = args[:alt_download_url].chomp "/#{@filename}"
 961     using_http = true
 962   # use our appropriate dist. point for download
 963   else
 964     mdp = Jamf::DistributionPoint.my_distribution_point cnx: @cnx
 965 
 966     # how do we access our dist. point? with http?
 967     if mdp.http_downloads_enabled && !(args[:no_http])
 968       using_http = true
 969       src_path = mdp.http_url
 970       if mdp.username_password_required
 971         raise Jamf::MissingDataError, 'No password provided for http download' unless ro_pw
 972         raise Jamf::InvaldDatatError, 'Incorrect password for http access to distribution point.' unless mdp.check_pw(:http, ro_pw)
 973         # insert the name and pw into the uri
 974         # reserved_chars = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}]") # we'll escape all the chars that aren't unreserved
 975         src_path = src_path.sub(%r{(https?://)(\S)}, "#{Regexp.last_match(1)}#{CGI.escape mdp.http_username.to_s}:#{CGI.escape ro_pw.to_s}@#{Regexp.last_match(2)}")
 976       end
 977 
 978     # or with filesharing?
 979     else
 980       using_http = false
 981       src_path = mdp.mount(ro_pw)
 982     end
 983 
 984     # look at the pkgs folder
 985     src_path += "#{DIST_POINT_PKGS_FOLDER}/"
 986   end # if args[:alt_download_url]
 987 
 988   if using_http
 989     src_path += @filename.to_s unless no_filename_in_url
 990   end
 991 
 992   # are we doing "fill existing users" or "fill user template"?
 993   do_feu = args[:feu] ? '-feu' : ''
 994   do_fut = args[:fut] ? '-fut' : ''
 995 
 996   # the install args for jamf
 997   command_args = "-package '#{@filename}' -path '#{src_path}'  -target '#{args[:target]}' #{do_feu} #{do_fut} -showProgress -verbose"
 998 
 999   # run it via a client cmd
1000   install_out = Jamf::Client.run_jamf :install, command_args, args[:verbose]
1001 
1002   install_out =~ %r{<exitCode>(\d+)</exitCode>}
1003   install_exit = Regexp.last_match(1) ? Regexp.last_match(1).to_i : nil
1004   install_exit ||= $CHILD_STATUS.exitstatus
1005 
1006   if args.include? :unmount
1007     mdp.unmount unless using_http
1008   end
1009 
1010   install_exit.zero? ? true : false
1011 end
install_if_reported_available=(new_val) click to toggle source

Change the if_in_swupdate field in the JSS

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
430 def install_if_reported_available=(new_val)
431   return nil if new_val == @install_if_reported_available
432   new_val = false if new_val.to_s.empty?
433   raise Jamf::InvalidDataError, 'install_if_reported_available must be boolean true or false' unless Jamf::TRUE_FALSE.include? new_val
434   @install_if_reported_available = new_val
435   @need_to_update = true
436 end
Also aliased as: if_in_swupdate=
installed?() click to toggle source

Is this packaged installed on the current machine (via casper)? We just look for the receipt, which is the @filename less any possible .zip extension.

@return [Boolean]

    # File lib/jamf/api/classic/api_objects/package.rb
619 def installed?
620   @receipt.file?
621 end
notes=(new_val) click to toggle source

Change the notes field in the JSS.NewLines are converted r.

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
444 def notes=(new_val)
445   return nil if new_val == @notes
446 
447   # line breaks should be \r
448   new_val = new_val.to_s.tr("\n", "\r")
449   @notes = new_val
450   @need_to_update = true
451 end
notify=(new_val)
Alias for: send_notification=
os_ok?(os = nil) click to toggle source

Is a given OS OK for this package based on its @os_requirements?

@param os the os to check, defaults to

the os of the current machine.

@return [Boolean] can this pkg be installed with the os

given?
    # File lib/jamf/api/classic/api_objects/package.rb
522 def os_ok?(os = nil)
523   JSS.os_ok? @os_requirements, os
524 end
os_requirements=(new_val) click to toggle source

Change the os_requirements field in the JSS E.g. 10.5, 10.5.3, 10.6.x

@param new_val comma-separated string, or array of os versions

@return [void]

Extra feature: Minumum OS’s can now be specified as a string using the notation “>=10.6.7”.

@see JSS.expand_min_os

    # File lib/jamf/api/classic/api_objects/package.rb
493 def os_requirements=(new_val)
494   # nil should be an empty array
495   new_val = [] if new_val.to_s.empty?
496 
497   # if any value starts with >=, expand it
498   case new_val
499   when String
500     new_val = JSS.expand_min_os(new_val) if new_val =~ /^>=/
501   when Array
502     new_val.map! { |a| a =~ /^>=/ ? JSS.expand_min_os(a) : a }
503     new_val.flatten!
504     new_val.uniq!
505   else
506     raise Jamf::InvalidDataError, 'os_requirements must be a String or an Array of strings'
507   end
508   # get the array version
509   @os_requirements = JSS.to_s_and_a(new_val)[:arrayform]
510   @need_to_update = true
511 end
Also aliased as: oses=
oses=(new_val)
Alias for: os_requirements=
priority=(new_val) click to toggle source

Change the priority field in the JSS

@param new_val one of PRIORITIES

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
532 def priority=(new_val)
533   return nil if new_val == @priority
534   new_val = DEFAULT_PRIORITY if new_val.to_s.empty?
535   raise Jamf::InvalidDataError, ':priority must be an integer from 1-20' unless PRIORITIES.include? new_val
536   @priority = new_val
537   @need_to_update = true
538 end
processor_ok?(processor = nil) click to toggle source

Is a given processor OK for this package based on its @required_processor?

@param processor the processor to check, defaults to

the processor of the current machine.

@return [Boolean] can this pkg be installed with the processor

given?
    # File lib/jamf/api/classic/api_objects/package.rb
579 def processor_ok?(processor = nil)
580   JSS.processor_ok? @required_processor, processor
581 end
reboot=(new_val)
Alias for: reboot_required=
reboot_required=(new_val) click to toggle source

Change the reboot-required field in the JSS

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
546 def reboot_required=(new_val)
547   return nil if new_val == @reboot_required
548   new_val = false if new_val.to_s.empty?
549   raise Jamf::InvalidDataError, "reboot must be boolean 'true' or 'false'" unless Jamf::TRUE_FALSE.include? new_val
550   @reboot_required = new_val
551   @need_to_update = true
552 end
Also aliased as: reboot=
removable=(new_val)
Alias for: allow_uninstalled=
required_processor=(new_val) click to toggle source

Change the required processor field in the JSS

@param new_val one of {CPU_TYPES}

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
560 def required_processor=(new_val)
561   return nil if new_val == @required_processor
562 
563   new_val = DEFAULT_PROCESSOR if new_val.to_s.empty?
564   raise Jamf::InvalidDataError, "Required_processor must be one of: #{CPU_TYPES.join ', '}" unless CPU_TYPES.include? new_val
565 
566   @required_processor = new_val
567   @need_to_update = true
568 end
Also aliased as: cpu_type=
reset_checksum(type: nil, local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true, dist_point: nil ) click to toggle source

Using either a local file, or the file on the master dist. point, re-set the checksum for this package. Call update to save the new one to the JSS.

BE VERY CAREFUL if using a local copy of the file - make sure its identical to the one on the dist point.

This can be used to change the checksum type, and by default will use DEFAULT_CHECKSUM_HASH_TYPE (‘SHA_512’)

@param @see calculate_checksum

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
715 def reset_checksum(type: nil, local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true, dist_point: nil )
716   type ||= DEFAULT_CHECKSUM_HASH_TYPE
717 
718   new_checksum = calculate_checksum(
719     type: type,
720     local_file: local_file,
721     rw_pw: rw_pw,
722     ro_pw: ro_pw,
723     unmount: unmount,
724     dist_point: dist_point
725   )
726   return if @checksum == new_checksum
727 
728   @checksum_type = type
729   @checksum = new_checksum
730   @need_to_update = true
731 end
send_notification=(new_val) click to toggle source

Change the notify field in the JSS

@param new_val

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
589 def send_notification=(new_val)
590   return nil if new_val == @send_notification
591   new_val = false if new_val.to_s.empty?
592   raise Jamf::InvalidDataError, 'send_notification must be boolean true or false' unless Jamf::TRUE_FALSE.include? new_val
593   @send_notification = new_val
594   @need_to_update = true
595 end
Also aliased as: notify=
switch_with_package=(new_val) click to toggle source

Change which pkg should be installed if this one can’t.

@param new_val the name of an existing package or “Do Not Install”

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
603 def switch_with_package=(new_val)
604   return nil if new_val == @switch_with_package
605   new_val = nil if new_val.to_s.empty?
606 
607   raise Jamf::NoSuchItemError, "No package named '#{new_val}' exists in the JSS" if new_val && (!self.class.all_names(cnx: @cnx).include? new_val)
608 
609   new_val ||= DO_NOT_INSTALL
610   @switch_with_package = new_val
611   @need_to_update = true
612 end
type() click to toggle source

What type of package is this?

@return [Symbol] :pkg or :dmg or:unknown

     # File lib/jamf/api/classic/api_objects/package.rb
1054 def type
1055   case @filename
1056   when /\.m?pkg(\.zip)?$/ then :pkg
1057   when /\.dmg$/ then :dmg
1058   else :unknown
1059   end
1060 end
uninstall(**args) click to toggle source

Uninstall this pkg via the jamf command.

@param args the arguments for installation

@option args :target The drive from which to uninstall the package, defaults to ‘/’

@option args :verbose be verbose to stdout, defaults to false

@option args :feu fill existing users, defaults to false

@option args :fut fill user template, defaults to false

@return [Process::Status] the result of the ‘jamf uninstall’ command

@note This code must be run as root to uninstall packages

     # File lib/jamf/api/classic/api_objects/package.rb
1029 def uninstall(**args)
1030   unless removable?
1031     raise Jamf::UnsupportedError, \
1032           'This package cannot be uninstalled. Please use CasperAdmin to index it and allow uninstalls'
1033   end
1034   raise Jamf::UnsupportedError, 'You must have root privileges to uninstall packages' unless JSS.superuser?
1035   args[:target] ||= '/'
1036 
1037   # are we doing "fill existing users" or "fill user template"?
1038   do_feu = args[:feu] ? '-feu' : ''
1039   do_fut = args[:fut] ? '-fut' : ''
1040 
1041   # use jamf binary to uninstall the pkg
1042   jamf_opts = "-target '#{args[:target]}' -id '#{@id}' #{do_feu} #{do_fut}"
1043 
1044   # run it via a client
1045   Jamf::Client.run_jamf 'uninstall', jamf_opts, args[:verbose]
1046 
1047   $CHILD_STATUS
1048 end
update_master_filename(old_file_name, new_file_name, rw_pw, unmount = true, dist_point: nil) click to toggle source

Change the name of a package file on the master distribution point.

@param new_file_name

@param old_file_name[default: @filename, String]

@param unmount whether or not ot unount the distribution point when finished.

@param rw_pw the password for the read/write account on the master Distribution Point,

or :prompt, or :stdin# where # is the line of stdin containing the password See {Jamf::DistributionPoint#mount}

@param dist_point [String,Integer] the name or id of the distribution

point to use. Defaults to the Master Dist. Point

@return [nil]

    # File lib/jamf/api/classic/api_objects/package.rb
830 def update_master_filename(old_file_name, new_file_name, rw_pw, unmount = true, dist_point: nil)
831   raise Jamf::NoSuchItemError, "#{old_file_name} does not exist in the jss." unless @in_jss
832   dp = self.class.fetch_dist_point(dist_point, cnx: @cnx)
833 
834   pkgs_dir = dp.mount(rw_pw, :rw) + DIST_POINT_PKGS_FOLDER.to_s
835   old_file = pkgs_dir + old_file_name
836   raise Jamf::NoSuchItemError, "File not found on the master distribution point at #{DIST_POINT_PKGS_FOLDER}/#{old_file_name}." unless \
837     old_file.exist?
838 
839   new_file = pkgs_dir + new_file_name
840   # use the extension of the original file.
841   new_file = pkgs_dir + (new_file_name + old_file.extname) if new_file.extname.empty?
842 
843   old_file.rename new_file
844   dp.unmount if unmount
845   nil
846 end
upload_master_file(local_file_path, rw_pw, unmount = true, chksum: DEFAULT_CHECKSUM_HASH_TYPE, dist_point: nil) click to toggle source

Upload a locally-readable file to the master distribution point. If the file is a directory (like a bundle .pk/.mpkg) it will be zipped before uploading and the @filename will be adjusted accordingly by adding a .zip extension

The name of the local file doesn’t matter, the file on the dist. point will use the @filename (possibly with .zip)

If you’ll be uploading several files you can specify unmount as false, and do it manually when all are finished with Jamf::DistributionPoint.master_distribution_point.unmount

@param local_file_path the local path to the file to be uploaded

@param rw_pw the password for the read/write account on the master Distribution Point,

or :prompt, or :stdin# where # is the line of stdin containing the password See {Jamf::DistributionPoint#mount}

@param unmount whether or not ot unount the distribution point when finished.

@param chksum [String] the constants CHECKSUM_HASH_TYPE_SHA512 or

CHECKSUM_HASH_TYPE_MD5. Anything else means don't calc.

@param dist_point [String,Integer] the name or id of the distribution

point to use. Defaults to the Master Dist. Point

@return [void]

    # File lib/jamf/api/classic/api_objects/package.rb
648 def upload_master_file(local_file_path, rw_pw, unmount = true, chksum: DEFAULT_CHECKSUM_HASH_TYPE, dist_point: nil)
649   raise Jamf::NoSuchItemError, 'Please create this package in the JSS before uploading it.' unless @in_jss
650 
651   dp = self.class.fetch_dist_point(dist_point, cnx: @cnx)
652 
653   destination = dp.mount(rw_pw, :rw) + "#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
654 
655   local_path = Pathname.new local_file_path
656   raise Jamf::NoSuchItemError, "Local file '#{@local_file}' doesn't exist" unless local_path.exist?
657 
658   # should we zip it?
659   if local_path.directory?
660     begin
661       # go to the same dir as the local file
662       wd = Dir.pwd
663       Dir.chdir local_path.parent
664 
665       # the contents of the zip file have to have the same name as the zip file itself (minus the .zip)
666       # so temporarily rename the source
667       local_path_to_upload = local_path.parent + @filename
668       local_path.rename local_path_to_upload unless local_path_to_upload == local_path
669 
670       zipdir = Pathname.new "/tmp/rubyjsstmp-#{Time.new.strftime '%Y%m%d%H%M%S'}-#{$PROCESS_ID}"
671       zipdir.mkpath
672       zipdir.chmod 0o700
673       zipfile = zipdir + (local_path_to_upload.basename.to_s + '.zip')
674 
675       raise 'There was a problem zipping the pkg bundle' unless system "/usr/bin/zip -qr '#{zipfile}' '#{local_path_to_upload}'"
676     ensure
677       # rename the source to the original name
678       local_path_to_upload.rename local_path if local_path_to_upload.exist? && local_path_to_upload != local_path
679       # go back where we started
680       Dir.chdir wd
681     end # begin
682 
683     # update our info
684     local_path = zipfile
685     destination = destination.to_s + '.zip'
686     @filename = zipfile.basename.to_s
687     @need_to_update = true
688   end # if directory
689 
690   FileUtils.copy_entry local_path, destination
691 
692   if CHECKSUM_HASH_TYPES.keys.include? chksum
693     @checksum_type = chksum
694     @checksum = calculate_checksum local_file: local_path, type: chksum, unmount: false, dist_point: dist_point
695     @need_to_update = true
696   end
697   update if @need_to_update
698   dp.unmount if unmount
699 end

Private Instance Methods

rest_xml() click to toggle source

Return the REST XML for this pkg, with the current values, for saving or updating

     # File lib/jamf/api/classic/api_objects/package.rb
1101 def rest_xml
1102   doc = REXML::Document.new Jamf::Connection::XML_HEADER
1103   pkg = doc.add_element 'package'
1104   pkg.add_element('allow_uninstalled').text = @allow_uninstalled
1105   pkg.add_element('boot_volume_required').text = @boot_volume_required
1106   pkg.add_element('filename').text = @filename
1107   pkg.add_element('fill_existing_users').text = @fill_existing_users
1108   pkg.add_element('fill_user_template').text = @fill_user_template
1109   pkg.add_element('info').text = @info
1110   pkg.add_element('install_if_reported_available').text = @install_if_reported_available
1111   pkg.add_element('name').text = @name
1112   pkg.add_element('notes').text = @notes
1113   pkg.add_element('os_requirements').text = JSS.to_s_and_a(@os_requirements)[:stringform]
1114   pkg.add_element('priority').text = @priority
1115   pkg.add_element('reboot_required').text = @reboot_required
1116   pkg.add_element('required_processor').text = @required_processor.to_s.empty? ? 'None' : @required_processor
1117   pkg.add_element('send_notification').text = @send_notification
1118   pkg.add_element('switch_with_package').text = @switch_with_package
1119 
1120   pkg.add_element('hash_type').text = @checksum_type
1121   pkg.add_element('hash_value').text = @checksum.to_s
1122 
1123   add_category_to_xml(doc)
1124   doc.to_s
1125 end