### Copyright 2016 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 D3 class Package < JSS::Package ### Run the make_live script, if any. We do this by creating and runngin ### a tmp file, rather than using the jamf binary, because this wont' ### be done as root, so the jamf binary can't be run. ### ### @return [Process::Status] the status of the finished script. ### def run_make_live_script # Run the make_live script if any if script = D3::CONFIG.admin_make_live_script if JSS::Script.all_names.include? script code = JSS::Script.new(name: script).code elsif JSS::Script.all_ids.include? script code = JSS::Script.new(id: script).code else return nil end return nil unless code return nil unless code.start_with? "#!" tmp_file = Pathname.new Tempfile.new("mklive") tmp_file.jss_touch tmp_file.chmod 0700 tmp_file.jss_save code ENV['D3_MAKE_LIVE_EDITION'] = edition ENV['D3_MAKE_LIVE_ADMIN'] = @admin ENV['D3_MAKE_LIVE_DESC'] = description ENV['D3_MAKE_LIVE_AUTO_GROUPS'] = auto_groups.join(',') ENV['D3_MAKE_LIVE_EXCL_GROUPS'] = excluded_groups.join(',') system tmp_file.to_s tmp_file.delete ENV['D3_MAKE_LIVE_EDITION'] = nil ENV['D3_MAKE_LIVE_ADMIN'] = nil ENV['D3_MAKE_LIVE_DESC'] = nil ENV['D3_MAKE_LIVE_AUTO_GROUPS'] = nil ENV['D3_MAKE_LIVE_EXCL_GROUPS'] = nil end end ### Install this pkg on this machine. D3 pkgs are more involved than plain JSS pkgs. ### ### @param args[Hash] The options for installation, see also the options to JSS::Package#install, which are accepted here. ### ### @options args :admin [String] the name of the person doing the install ### ### @options args :force [Boolean] should the installtion be forced to act unnaturally? ### ### @options args :puppywalk [Boolean] if true, we're installing logout-installs immediately, not queueing them ### ### @options args :expiration [Integer] Override any server-defined expiration period, value is days unlaunched. ### Zero = don't expire. The pkg must be removable to be expired. ### ### @options args :verbose [Boolean] Be loud to stdout, defaults to false ### ### @return [String] the output of the jamf install command ### def install(args = {}) # force can't get around these: raise D3::InstallError, "This package is missing from the JSS, cannot install." if @status == :missing check_oses check_cpu begin # for the ensure below # if this item is now being installed by puppies at logout, # the PuppyQ has our admin name, force setting, and expiration. # It if isn't in the puppyQ, then those values should # have come from the args if args[:puppywalk] and D3::PUPPY_Q.q[@basename] then args[:admin] = D3::PUPPY_Q.q[@basename].admin ? D3::PUPPY_Q.q[@basename].admin : "puppy-install" args[:force] = D3::PUPPY_Q.q[@basename].force args[:expiration] = D3::PUPPY_Q.q[@basename].expiration else # if this is a manual install, we should know # who's doing it args[:admin] ||= D3.admin end forced = args[:force] or D3::forced? # excluded pkgs check_for_exclusions unless forced @admin = args[:admin] # pilot? D3::Client.set_env :installing, edition D3::Client.set_env :pkg_status, @status # force? @using_force = forced ? " with force" : "" # pilot installs need an admin from the args or the queue raise JSS::MissingDataError, "Missing :admin for pilot install." if pilot? and @admin == D3::AUTO_INSTALL_ADMIN # If we aren't actually installing the puppy queue items # and there's already a member of this basename in the queue # raise an exception, if D3::PUPPY_Q.pups.include? @basename && (not args[:puppywalk]) && (not forced) raise D3::InstallError, "#{@basename} (#{D3::PUPPY_Q.q[@basename].edition}) is already queued for puppies. Use force if needed." end if removable? if args[:expiration] && @expiration != args[:expiration] @expiration_to_apply = args[:expiration] @custom_expiration = args[:expiration] else @expiration_to_apply = @expiration @custom_expiration = false end else # not removable, can't expire @expiration_to_apply = 0 end # if remmovable ### ### Queue for Puppies ### if reboot? && (not args[:puppywalk]) queue_for_puppies forced ### ### Regular Install... ### else unless forced raise D3::InstallError, "#{edition} cannot be installed now because one or more of the following processes is running: #{D3::Admin::OPTIONS[:prohibiting_processes][:display_conversion].call @prohibiting_processes}." if install_prohibited_by_process? end # unless forced remove_previous_installs_if_needed (args[:verbose]) D3.log "Installing: #{edition} (#{@status})#{@using_force}", :warn # pre-install script pre_install_status = run_pre_install_script(args[:verbose]) # exit 111 means write receipts, but don't acutally install if pre_install_status == 111 then D3.log "Pre-install script for #{edition} exited with status '111'; Not installing but writing receipt.", :info write_rcpt # if this was a puppy install, remove it from the queue D3::PUPPY_Q - @basename return pre_install_status elsif pre_install_status != 0 then D3.log "Pre_install script for #{edition} failed, exit status: #{pre_install_status}, not installing.", :error raise D3::PreInstallError, "Pre_install script for #{edition} failed, exit status: #{pre_install_status}, not installing." end # pre_install_status == 111 # if forced, make the os forget this has been installed before if forced and @apple_receipt_data.is_a? Array @apple_receipt_data.each do |r| D3.log "Forcing OS to forget installer receipt for: #{r[:apple_pkg_id]}", :info system "#{JSS::Composer::PKG_UTIL} --forget '#{r[:apple_pkg_id]}' &>/dev/null" end # each do r end # if force # get the read-only passwd for the dist point, if needed args[:ro_pw] = D3::Client.get_ro_pass :dist # Install It Already! D3.log "Running 'jamf install' of #{edition}", :info if install_result = super(args) # install was good... D3.log "Finished 'jamf install' of #{edition}", :debug # write our receipt write_rcpt # if this was a puppy install, remove it from the queue D3::PUPPY_Q - @basename # run a postflight if needed post_install_status = run_post_install_script(args[:verbose]) if post_install_status != 0 D3.log "Post_install script for #{edition} failed, exit status: #{post_install_status}", :error raise D3::PostInstallError, "Post_install script for #{edition} failed, exit status: #{post_install_status}" end D3.log "Done installing #{edition}#{@using_force}", :warn else # bad install raise D3::InstallError, "There was a problem installing #{edition}, 'jamf install' failed" end # if super args return install_result end # if reboot? ensure D3::Client.unset_env :installing D3::Client.unset_env :pkg_status end # begin...ensure end #install ### This just queues this installer for installation at the next puppywalk ### For now, we're intentionally NOT caching the installer for off-line installation. ### The puppy installer will only run when the machine can talk to the JSS ### def queue_for_puppies (force = D3::forced?) # create the puppy new_pup = D3::PuppyTime::PendingPuppy.new( :basename => @basename, :version => @version, :revision => @revision, :admin => @admin, :force => force, :custom_expiration => @custom_expiration, :status => @status ) # add it to the queue - this will return true or false added_2_q = D3::PUPPY_Q + new_pup # tell someone D3.log "Added #{edition} (#{@status}) to the puppy queue#{@using_force}", :warn if added_2_q return true end ### Run the pre-install script, if any. ### ### @param verbose[Boolean] Should the script be run verbosely? ### ### @return [Integer, nil] The exitstatus of the script, 0 if no script ### def run_pre_install_script(verbose = false) return 0 unless pre_install_script? begin D3::Client.set_env :pre_install, edition D3.log "Running pre_install script for #{edition}", :info (exit_status, output) = JSS::Script.new(:id => @pre_install_script_id).run :verbose => verbose, :show_output => verbose D3.log "Finished pre_install script for #{edition}", :debug rescue D3::ScriptError raise PreInstallError, $! ensure D3::Client.unset_env :pre_install end # begin return exit_status end # run pre install ### Run the post-install script, if any. ### ### @param verbose[Boolean] Should the script be run verbosely? ### ### @return [Integer, nil] The exitstatus of the script, 0 if no script ### def run_post_install_script(verbose = false) return 0 unless post_install_script? begin D3::Client.set_env :post_install, edition D3.log "Running post_install script for #{edition}", :info (exit_status, output) = JSS::Script.new(:id => @post_install_script_id).run :verbose => verbose, :show_output => verbose D3.log "Finished post_install script for #{edition}", :debug rescue D3::ScriptError raise PostInstallError, $! ensure D3::Client.unset_env :post_install end # begin return exit_status end # run post install end # class Package end # module D3