class Jamf::Package
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 aJSS
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
@return [Boolean] can this item be uninstalled? Some, e.g. OS Updates, can’t
@return [Boolean] should this pkg be installed on the boot volume during imaging
@return [Boolean] should this pkg be installed on the boot volume during imaging
@return [Boolean] should this pkg be installed on the boot volume during imaging
@return [String, nil] the checksum value for the package file on the
dist. point, if it's been calculated.
@ @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.
@return [String] limit installation to these architectures: ‘x86’, ‘ppc’, ‘None’
@return [Boolean] does this item ‘Fill Existing Users’ when jamf installs it?
@return [Boolean] does this item ‘Fill Existing Users’ when jamf installs it?
@return [String] the filename of the .pkg, .mpkg, or .dmg on the Casper server
@return [Boolean] does this item ‘Fill Existing Users’ when jamf installs it?
@return [Boolean] does this pkg also get install in the OS user homedir template
@return [Boolean] does this pkg also get install in the OS user homedir template
@return [Boolean] does this pkg also get install in the OS user homedir template
@return [Boolean] only install this pkg if it’s available in the commandline softwareupdate.
@return [Boolean] only install this pkg if it’s available in the commandline softwareupdate.
@return [String] the info field for this pkg - stores d3’s basename & swupdate values
@return [Boolean] only install this pkg if it’s available in the commandline softwareupdate.
@return [String] the notes field for this pkg
@return [Boolean] does this pkg cause a notification to be sent on self-heal?
@return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x
@return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x
@return [Integer] Priority to use for deploying or uninstalling the package
@return [Boolean] does this item require a reboot after installation?
@return [Boolean] does this item require a reboot after installation?
@return [Boolean] does this item require a reboot after installation?
@return [Pathname] the local receipt when this pkg is installed
@return [Boolean] can this item be uninstalled? Some, e.g. OS Updates, can’t
@return [Boolean] can this item be uninstalled? Some, e.g. OS Updates, can’t
@return [String] limit installation to these architectures: ‘x86’, ‘ppc’, ‘None’
@return [Boolean] does this pkg cause a notification to be sent on self-heal?
@return [String] the name of a pkg to install (or “Do Not Install”) when this pkg can’t be installed
Public Class Methods
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
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
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
@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
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
@see Jamf::APIObject#initialize
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
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
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
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
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
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
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
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
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]
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 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
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
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
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
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 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
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
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
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
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
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
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
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
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
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
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
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
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
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 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
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 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
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