depot3 0.0.0a1 → 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +55 -1
  3. data/bin/d3 +323 -0
  4. data/bin/d3admin +1011 -0
  5. data/bin/d3helper +354 -0
  6. data/bin/puppytime +334 -0
  7. data/data/d3/com.pixar.d3.RepoMan.plist +23 -0
  8. data/data/d3/d3.conf.example +507 -0
  9. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftAppKit.dylib +0 -0
  10. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCore.dylib +0 -0
  11. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCoreData.dylib +0 -0
  12. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCoreGraphics.dylib +0 -0
  13. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCoreImage.dylib +0 -0
  14. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftDarwin.dylib +0 -0
  15. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftDispatch.dylib +0 -0
  16. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftFoundation.dylib +0 -0
  17. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftObjectiveC.dylib +0 -0
  18. data/data/d3/d3RepoMan.app/Contents/Info.plist +56 -0
  19. data/data/d3/d3RepoMan.app/Contents/MacOS/d3RepoMan +0 -0
  20. data/data/d3/d3RepoMan.app/Contents/PkgInfo +1 -0
  21. data/data/d3/d3RepoMan.app/Contents/Resources/Base.lproj/MainMenu.nib +0 -0
  22. data/data/d3/d3RepoMan.app/Contents/Resources/last-foreground-times-template.plist +5 -0
  23. data/data/d3/d3RepoMan.app/Contents/_CodeSignature/CodeResources +214 -0
  24. data/data/d3/puppytime/ImageLicenses.txt +165 -0
  25. data/data/d3/puppytime/notification_image +1 -0
  26. data/data/d3/puppytime/opt_out_image +1 -0
  27. data/data/d3/puppytime/slideshow/2008-07-11_White_German_Shepherd_pup_chilling_at_the_Coker_Arboretum.jpg +0 -0
  28. data/data/d3/puppytime/slideshow/2009-04-21_APBT_pup_on_deck.jpg +0 -0
  29. data/data/d3/puppytime/slideshow/A_puppy_Yorkie.jpg +0 -0
  30. data/data/d3/puppytime/slideshow/Alert_Pug_Puppy.jpg +0 -0
  31. data/data/d3/puppytime/slideshow/Australian_Cattle_Dog_puppies_04.JPG +0 -0
  32. data/data/d3/puppytime/slideshow/Beagle_puppy_Cadet.jpg +0 -0
  33. data/data/d3/puppytime/slideshow/Bernese_Mountain_Dog.jpg +0 -0
  34. data/data/d3/puppytime/slideshow/Bloodhound_Puppy.jpg +0 -0
  35. data/data/d3/puppytime/slideshow/Boston_terrier_with_toy.jpg +0 -0
  36. data/data/d3/puppytime/slideshow/Boxer_puppy_fawn_portrai.jpg +0 -0
  37. data/data/d3/puppytime/slideshow/Caracal_kitten.jpg +0 -0
  38. data/data/d3/puppytime/slideshow/Chihuahua_&_Doberman_Pup.jpg +0 -0
  39. data/data/d3/puppytime/slideshow/Cuccioli_di_Margot_a_35_gg_Basenjis.jpg +0 -0
  40. data/data/d3/puppytime/slideshow/Dalmatian_puppy_03.jpg +0 -0
  41. data/data/d3/puppytime/slideshow/GoldenRetrieverPuppyDaisyParker.JPG +0 -0
  42. data/data/d3/puppytime/slideshow/Green_eyed_beige_Chihuahua.jpg +0 -0
  43. data/data/d3/puppytime/slideshow/Let_Sleeping_Dogs_Lie.jpg +0 -0
  44. data/data/d3/puppytime/slideshow/Meatball_-_French_Bulldog_Puppy.jpg +0 -0
  45. data/data/d3/puppytime/slideshow/Oola_-_9_weeks.jpg +0 -0
  46. data/data/d3/puppytime/slideshow/Pancho0008.JPG +0 -0
  47. data/data/d3/puppytime/slideshow/Pomeranian_orange-sable_Coco.jpg +0 -0
  48. data/data/d3/puppytime/slideshow/Pug_puppy_001.jpg +0 -0
  49. data/data/d3/puppytime/slideshow/Puggle_puppy_6_weeks.JPG +0 -0
  50. data/data/d3/puppytime/slideshow/Puli_kan.jpg +0 -0
  51. data/data/d3/puppytime/slideshow/Puppy_French_Bulldog.jpg +0 -0
  52. data/data/d3/puppytime/slideshow/Rocco_the_Bulldog.jpg +0 -0
  53. data/data/d3/puppytime/slideshow/Rottweiler_Face.jpg +0 -0
  54. data/data/d3/puppytime/slideshow/Saint_Bernard_puppy.jpg +0 -0
  55. data/data/d3/puppytime/slideshow/Scottish_froment.jpg +0 -0
  56. data/data/d3/puppytime/slideshow/Shar_pei_puppy_(age_2_months).jpg +0 -0
  57. data/data/d3/puppytime/slideshow/Shiba-Inu_beim_Spielen_im_Schnee.JPG +0 -0
  58. data/data/d3/puppytime/slideshow/Smooth-coat_Border_Collie_puppy..jpg +0 -0
  59. data/data/d3/puppytime/slideshow/Smooth_Dachshund_puppies.jpg +0 -0
  60. data/data/d3/puppytime/slideshow/Snow_dog.jpg +0 -0
  61. data/data/d3/puppytime/slideshow/Taylor_the_Pembroke_Welsh_Corgi.png +0 -0
  62. data/data/d3/puppytime/slideshow/Weim_Pups_001.jpg +0 -0
  63. data/data/d3/puppytime/slideshow/Westie_pups.jpg +0 -0
  64. data/data/d3/puppytime/slideshow/Yellow_Labrador_puppies_(4165737325).jpg +0 -0
  65. data/lib/d3/admin/add.rb +451 -0
  66. data/lib/d3/admin/auth.rb +470 -0
  67. data/lib/d3/admin/edit.rb +297 -0
  68. data/lib/d3/admin/help.rb +396 -0
  69. data/lib/d3/admin/interactive.rb +972 -0
  70. data/lib/d3/admin/options.rb +454 -0
  71. data/lib/d3/admin/prefs.rb +204 -0
  72. data/lib/d3/admin/report.rb +727 -0
  73. data/lib/d3/admin/state.rb +42 -0
  74. data/lib/d3/admin/validate.rb +413 -0
  75. data/lib/d3/admin.rb +42 -0
  76. data/lib/d3/basename.rb +217 -0
  77. data/lib/d3/client/auth.rb +108 -0
  78. data/lib/d3/client/class_methods.rb +766 -0
  79. data/lib/d3/client/class_variables.rb +47 -0
  80. data/lib/d3/client/cli.rb +187 -0
  81. data/lib/d3/client/environment.rb +134 -0
  82. data/lib/d3/client/help.rb +110 -0
  83. data/lib/d3/client/lists.rb +314 -0
  84. data/lib/d3/client/receipt.rb +1173 -0
  85. data/lib/d3/client.rb +45 -0
  86. data/lib/d3/configuration.rb +319 -0
  87. data/lib/d3/constants.rb +60 -0
  88. data/lib/d3/database.rb +488 -0
  89. data/lib/d3/exceptions.rb +44 -0
  90. data/lib/d3/log.rb +271 -0
  91. data/lib/d3/package/aliases.rb +80 -0
  92. data/lib/d3/package/attributes.rb +97 -0
  93. data/lib/d3/package/class_methods.rb +817 -0
  94. data/lib/d3/package/class_variables.rb +46 -0
  95. data/lib/d3/package/client_actions.rb +293 -0
  96. data/lib/d3/package/constants.rb +58 -0
  97. data/lib/d3/package/constructor.rb +191 -0
  98. data/lib/d3/package/getters.rb +164 -0
  99. data/lib/d3/package/mixins.rb +39 -0
  100. data/lib/d3/package/private_methods.rb +227 -0
  101. data/lib/d3/package/questions.rb +95 -0
  102. data/lib/d3/package/server_actions.rb +683 -0
  103. data/lib/d3/package/setters.rb +326 -0
  104. data/lib/d3/package/validate.rb +448 -0
  105. data/lib/d3/package.rb +51 -0
  106. data/lib/d3/puppytime/pending_puppy.rb +108 -0
  107. data/lib/d3/puppytime/puppy_queue.rb +274 -0
  108. data/lib/d3/puppytime.rb +68 -0
  109. data/lib/d3/state.rb +105 -0
  110. data/lib/d3/utility.rb +325 -0
  111. data/lib/d3/version.rb +1 -1
  112. metadata +162 -9
data/bin/d3helper ADDED
@@ -0,0 +1,354 @@
1
+ #!/usr/bin/ruby
2
+ ### Copyright 2016 Pixar
3
+ ###
4
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ ### with the following modification; you may not use this file except in
6
+ ### compliance with the Apache License and the following modification to it:
7
+ ### Section 6. Trademarks. is deleted and replaced with:
8
+ ###
9
+ ### 6. Trademarks. This License does not grant permission to use the trade
10
+ ### names, trademarks, service marks, or product names of the Licensor
11
+ ### and its affiliates, except as required to comply with Section 4(c) of
12
+ ### the License and to reproduce the content of the NOTICE file.
13
+ ###
14
+ ### You may obtain a copy of the Apache License at
15
+ ###
16
+ ### http://www.apache.org/licenses/LICENSE-2.0
17
+ ###
18
+ ### Unless required by applicable law or agreed to in writing, software
19
+ ### distributed under the Apache License with the above modification is
20
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ ### KIND, either express or implied. See the Apache License for the specific
22
+ ### language governing permissions and limitations under the Apache License.
23
+ ###
24
+ ###
25
+
26
+ ######################################
27
+ # == Synopsis
28
+ # d3helper [action]
29
+ #
30
+ # d3helper: perform various helper tasks for d3
31
+ #
32
+ # Copyright Pixar Animation Studios
33
+ #
34
+ # Author: Chris Lasell chrisl@pixar.com
35
+ #
36
+ #
37
+ ######################################
38
+
39
+
40
+ ############
41
+ # Modules, Libraries, etc
42
+ ############
43
+ # Load libraries
44
+ require 'd3'
45
+
46
+ ############
47
+ # The App Object
48
+ ############
49
+
50
+ class App
51
+
52
+ ############################################
53
+ ### Constants - paths, settings, and other values used throughout
54
+
55
+ ACTIONS = {
56
+ "--display-puppy-notification" => :display_puppy_notification,
57
+ "--rcpts-for-ea" => :rcpts_for_ea,
58
+ "--puppyq-for-ea" => :puppyq_for_ea,
59
+ "--import-casper-receipts" => :import_receipts,
60
+ "--help" => :help,
61
+ "--version" => :show_version
62
+ }
63
+
64
+ USAGE = "#{File.basename(__FILE__)} [--help] [action]\nwhere action is one of: #{ACTIONS.keys.join(', ')}"
65
+
66
+
67
+ attr_reader :debug
68
+
69
+ ############################################
70
+ ### Setup
71
+ ### Initialization should only be used to set variables
72
+ ### and set up the infrasctructure. No actual processing/error checking
73
+ ### until the run method is called.
74
+ ###
75
+ def initialize(args)
76
+ opts = GetoptLong.new(
77
+ [ '--help', '-h', '-H', GetoptLong::NO_ARGUMENT ],
78
+ [ '--debug', '-d', GetoptLong::NO_ARGUMENT ],
79
+ [ '--import-casper-receipts', GetoptLong::NO_ARGUMENT ],
80
+ [ '--display-puppy-notification', GetoptLong::NO_ARGUMENT ],
81
+ [ '--rcpts-for-ea', GetoptLong::NO_ARGUMENT ],
82
+ [ '--puppyq-for-ea', GetoptLong::NO_ARGUMENT ],
83
+ [ '--version', '-v', GetoptLong::NO_ARGUMENT ],
84
+ [ '--configure', GetoptLong::NO_ARGUMENT ]
85
+ )
86
+
87
+ opts.each do |opt, arg|
88
+ case opt
89
+ when '--help'
90
+ @action = ACTIONS[opt]
91
+ when '--version'
92
+ @action = ACTIONS[opt]
93
+ when '--debug'
94
+ @debug = true
95
+ when '--import-casper-receipts'
96
+ @action = ACTIONS[opt]
97
+ when '--display-puppy-notification'
98
+ @action = ACTIONS[opt]
99
+ when '--rcpts-for-ea'
100
+ @action = ACTIONS[opt]
101
+ when '--puppyq-for-ea'
102
+ @action = ACTIONS[opt]
103
+ when '--configure'
104
+ @action = ACTIONS[opt]
105
+ end # case
106
+ end # opts.each
107
+
108
+ unless @action
109
+ puts USAGE
110
+ exit 1
111
+ end
112
+
113
+ D3::LOG.progname = File.basename(__FILE__)
114
+ end # init
115
+
116
+ ############################################
117
+ ### Run
118
+ def run
119
+ if @action == :help or @action == :show_version
120
+ self.send @action
121
+ return
122
+ end
123
+
124
+ # Must be root to do these things
125
+ raise D3::PermissionError, "Only root can use this program" unless JSS.superuser?
126
+
127
+ # connect to the server servernames are defined in JSS::CONFIG
128
+ D3::Client.connect
129
+
130
+ # do something
131
+ self.send @action
132
+
133
+ end # run
134
+
135
+ ############################################
136
+ ### Instance Methods
137
+
138
+ ############
139
+ # spew out help
140
+ def help
141
+
142
+ puts <<-USAGEBLURB
143
+ #{USAGE}
144
+
145
+ --display-puppy-notification Tell users that puppies need walking.
146
+ --import-casper-receipts Import local Casper package receipts into
147
+ d3 receipts. Can be run repeatedly to
148
+ import for packages new to d3.
149
+ --rcpts-for-ea Generate Extension Attribute data about
150
+ the d3 receipts on this machine.
151
+ --help, -h, -H Show this help.
152
+
153
+ USAGEBLURB
154
+
155
+ end # show_help
156
+
157
+
158
+ def show_version
159
+ puts <<-ENDVERS
160
+ D3 module version: #{D3::VERSION}
161
+ JSS module version: #{JSS::VERSION}
162
+ ENDVERS
163
+ end
164
+
165
+ ###
166
+ def display_puppy_notification
167
+ puppy_notification_env = D3::Client::ENV_STATES[:puppytime_notification]
168
+
169
+ pending_puppy_names = ENV[puppy_notification_env]
170
+ pending_puppy_names ||= "(No Puppies to walk! why are you seeing this?)"
171
+
172
+ description = "Please log out ASAP for software installs and updates.
173
+ Watch a parade of cute puppies while these items are installed:
174
+ #{pending_puppy_names}"
175
+
176
+ image_path = D3::CONFIG.puppy_notify_image_path ? Pathname.new(D3::CONFIG.puppy_notify_image_path) : nil
177
+
178
+ image_path = D3::PuppyTime::DFT_NOTIFY_IMAGE unless image_path && image_path.file?
179
+
180
+ # fork off the jamf helper, then detach it so the policy running this doesn't
181
+ # hang aound
182
+ alert_pid = Process.fork do
183
+ JSS::Client.jamf_helper( :hud, :lock_hud => true,
184
+ :window_position => :ur,
185
+ :title => "The puppies await you",
186
+ :align_heading => :center,
187
+ :heading => "It's PuppyTime!",
188
+ :description => description,
189
+ :align_description => :left,
190
+ :icon => image_path.to_s,
191
+ :icon_size => 300,
192
+ :button1 => "OK",
193
+ :default_button => 1
194
+ )
195
+ end # fork
196
+ Process.detach(alert_pid)
197
+
198
+ end
199
+
200
+
201
+ ### output a JSON string with miminal data about the receipts on this machine
202
+ ### wrapped in a <result> tag, to be used in the CONFIG.report_receipts_ext_attr_name
203
+ ### extension attribute
204
+ ###
205
+ def rcpts_for_ea
206
+ begin
207
+ ea_data = {}
208
+ D3::Client::Receipt.all.values.each { |r|
209
+ this_r = {}
210
+ this_r[:version] = r.version
211
+ this_r[:revision] = r.revision
212
+ this_r[:status] = r.status
213
+ this_r[:installed_at] = r.installed_at
214
+ this_r[:admin] = r.admin
215
+ this_r[:frozen] = r.frozen?
216
+ this_r[:manual] = r.manually_installed
217
+ this_r[:custom_expiration] = r.custom_expiration
218
+ this_r[:last_usage] = r.last_usage
219
+ ea_data[r.basename] = this_r
220
+ } # D3::Client::Receipt.all.values do |r|
221
+
222
+ res = JSON.dump ea_data
223
+ rescue
224
+ res = "ERROR:#{$!}:#{$@.first}"
225
+ end
226
+ puts "<result>#{res}</result>"
227
+ end
228
+
229
+
230
+ ### output a JSON string with data about the pending puppytime (logout)
231
+ ### installs on this machine wrapped in a <result> tag, to be used in the
232
+ ### CONFIG.report_puppyq_ext_attr_name extension attribute
233
+ ###
234
+ ### The data is a hash of hashes, first keys are basenames
235
+ ### second keys are
236
+ ### :version
237
+ ### :revision
238
+ ### :status
239
+ ### :queued_at (Time)
240
+ ### :admin
241
+ ### :custom_expiration
242
+ ###
243
+ ### @return [void]
244
+ ###
245
+ def puppyq_for_ea
246
+ begin
247
+ ea_data = {}
248
+ D3::PUPPY_Q.queue.values.each { |pup|
249
+ this_pup = {}
250
+ this_pup[:version] = pup.version
251
+ this_pup[:revision] = pup.revision
252
+ this_pup[:status] = pup.status
253
+ this_pup[:queued_at] = pup.queued_at
254
+ this_pup[:admin] = pup.admin
255
+ this_pup[:custom_expiration] = pup.custom_expiration
256
+ ea_data[pup.basename] = this_pup
257
+ } # D3::Client::Receipt.all.values do |r|
258
+
259
+ res = JSON.dump ea_data
260
+ rescue
261
+ res = "ERROR:#{$!}:#{$@.first}"
262
+ end
263
+ puts "<result>#{res}</result>"
264
+ end
265
+
266
+
267
+ ###
268
+ def import_receipts
269
+
270
+ D3.log "Importing Casper Receipts for packages in d3", :warn
271
+
272
+ # this gets us a hash of jss package filenames -> pkg ids
273
+ d3_pkg_filenames = D3::Package.all_filenames.invert
274
+
275
+ # this is an array of current d3 receipt ids
276
+ d3_rcpt_ids = D3::Client::Receipt.all.values.map{|r| r.id}
277
+
278
+ now = Time.now
279
+
280
+ JSS::Client.receipts.each do |jss_rcpt|
281
+
282
+
283
+ # jss_rcpt is a Pathname object, the rcpt name is the paths basename
284
+ jss_rcpt_name = jss_rcpt.basename.to_s
285
+
286
+ # do we have a match?
287
+ pkg_id = d3_pkg_filenames[jss_rcpt_name]
288
+ # if not, the filename might end with .zip, but the rcpt name doesnt
289
+ pkg_id ||= d3_pkg_filenames[jss_rcpt_name + ".zip"]
290
+ # if still not, this jss recipt doesn't point to a d3 package.
291
+ next unless pkg_id
292
+
293
+ # If we're here, the jss receipt might need to be imported, unless its
294
+ # already been imported
295
+ next if d3_rcpt_ids.include? pkg_id
296
+
297
+ # If we're here, we need to import the receipt, so get the matching D3
298
+ # package and make a receipt from it.
299
+ D3.log "Importing Casper Receipt #{jss_rcpt_name}", :warn
300
+ begin
301
+ d3_pkg = D3::Package.new id: pkg_id
302
+
303
+ # do we already have a rcpt for the package's basename? if so,
304
+ # delete it - we can only have on rcpt per basename
305
+
306
+ if D3::Client::Receipt.all(:refresh).keys.include? d3_pkg.basename
307
+ D3.log "Deleting previous d3 receipt for basename #{d3_pkg.basename}", :warn
308
+ D3::Client::Receipt.all[d3_pkg.basename].delete
309
+ end
310
+
311
+ d3_rcpt = D3::Client::Receipt.new(
312
+ :basename => d3_pkg.basename,
313
+ :version => d3_pkg.version,
314
+ :revision => d3_pkg.revision,
315
+ :admin => D3::AUTO_INSTALL_ADMIN,
316
+ :id => d3_pkg.id,
317
+ :status => d3_pkg.status ,
318
+ :jamf_rcpt_file => jss_rcpt,
319
+ :apple_pkg_ids => nil,
320
+ :installed_at => jss_rcpt.mtime,
321
+ :removable => d3_pkg.removable,
322
+ :expiration => d3_pkg.expiration,
323
+ :expiration_path => d3_pkg.expiration_path,
324
+ :prohibiting_process => d3_pkg.prohibiting_process,
325
+ :pre_remove_script_id => d3_pkg.pre_remove_script_id,
326
+ :post_remove_script_id => d3_pkg.post_remove_script_id
327
+ )
328
+ D3::Client::Receipt.add_receipt d3_rcpt, :replace
329
+ D3.log "Done. Imported Casper Receipt #{jss_rcpt_name}", :warn
330
+ rescue
331
+ D3.log "ERROR importing Casper Receipt #{jss_rcpt_name}: #{$!}", :error
332
+ end
333
+
334
+ D3.log "Done importing Casper Receipts for packages in d3", :warn
335
+
336
+ end #JSS::Client.receipts.each do |jss_rcpt|
337
+ end #import_receipts
338
+
339
+ end # class App
340
+
341
+
342
+ ############
343
+ # Go
344
+ ############
345
+ begin
346
+ app = nil
347
+ app = App.new(ARGV)
348
+ app.run
349
+ exit 0
350
+ rescue
351
+ $stderr.puts "An error occurred: #{$!}"
352
+ $stderr.puts $@ if D3::CONFIG.log_level == :debug or (app and app.debug)
353
+ exit 1
354
+ end # begin
data/bin/puppytime ADDED
@@ -0,0 +1,334 @@
1
+ #!/usr/bin/ruby
2
+ ### Copyright 2016 Pixar
3
+ ###
4
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ ### with the following modification; you may not use this file except in
6
+ ### compliance with the Apache License and the following modification to it:
7
+ ### Section 6. Trademarks. is deleted and replaced with:
8
+ ###
9
+ ### 6. Trademarks. This License does not grant permission to use the trade
10
+ ### names, trademarks, service marks, or product names of the Licensor
11
+ ### and its affiliates, except as required to comply with Section 4(c) of
12
+ ### the License and to reproduce the content of the NOTICE file.
13
+ ###
14
+ ### You may obtain a copy of the Apache License at
15
+ ###
16
+ ### http://www.apache.org/licenses/LICENSE-2.0
17
+ ###
18
+ ### Unless required by applicable law or agreed to in writing, software
19
+ ### distributed under the Apache License with the above modification is
20
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ ### KIND, either express or implied. See the Apache License for the specific
22
+ ### language governing permissions and limitations under the Apache License.
23
+ ###
24
+ ###
25
+
26
+
27
+ # PuppyTime! Install all d3 packages listed in the puppy-queue while displaying
28
+ # a slideshow.
29
+ #
30
+ # The default images are of cute puppies, but they can be customized.
31
+ # See http://some/url/ for details.
32
+ #
33
+ # If there are no items in the queue, the script exits immediately.
34
+ #
35
+ # This script is intended to be executed at logout. This is best accomplished
36
+ # with a Casper policy triggered at logout. The policy can either run this script
37
+ # as a Casper script (in which case you need to add it to the JSS) or
38
+ # can call it locally using the "execute command" field of the
39
+ # "files and processes" section of the policy
40
+ #
41
+ # This script also expects a policy to be defined in
42
+ # D3::CONFIG.puppy_reboot_policy, which can contain policy name, custom-trigger
43
+ # or JSS id number.
44
+ #
45
+ # That policy may do any other actions after installing the packages in the
46
+ # queue, but before the reboot. It shouldn't to too much, however, since the
47
+ # system may be in an unstable state after the installs. The policy itself
48
+ # *must* do the actual reboot.
49
+ #
50
+ # If the D3::CONFIG.puppy_reboot_policy is not defined, or doesn't exist exist
51
+ # in the JSS, this script will reboot with 'shutdown -r now'
52
+ #
53
+ #
54
+ require 'd3'
55
+ require 'ostruct'
56
+
57
+ class App
58
+
59
+ VERSION = "3.0.0"
60
+
61
+ # app attributes
62
+ attr_reader :slideshow_pid
63
+
64
+ # Set up
65
+ def initialize(args)
66
+
67
+ @show_version = (args.include? "--version") || (args.include? "-v")
68
+
69
+ # debugging?
70
+ if args.include? "--debug"
71
+ @debug = true
72
+ D3::LOG.level = :debug
73
+ D3::Client.set_env :debug
74
+ args.delete_if{|f| f == "--debug" }
75
+ end
76
+
77
+ # These come from the JAMF binary and how it runs scripts
78
+ # the first arg is the target drive
79
+ # the second arg is the computer name
80
+ # the third arg is always the user.
81
+ @target_drive = args[0]
82
+ @comp_name = args[1]
83
+ @user = args[2]
84
+
85
+
86
+ # use config values or defaults
87
+ @optout_text = D3::CONFIG.puppy_optout_text || D3::PuppyTime::DFT_OPTOUT_TEXT
88
+
89
+ @optout_image_path = Pathname.new(D3::CONFIG.puppy_optout_image_path || D3::PuppyTime::DFT_OPTOUT_IMAGE)
90
+
91
+ @optout_seconds = D3::CONFIG.puppy_optout_seconds || D3::PuppyTime::DFT_OPTOUT_SECS
92
+
93
+ @slideshow_folder_path = Pathname.new(D3::CONFIG.puppy_slideshow_folder_path || D3::PuppyTime::DFT_SLIDESHOW_DIR)
94
+
95
+ @image_size = D3::CONFIG.puppy_image_size || D3::PuppyTime::DFT_IMG_SIZE
96
+
97
+ @title = D3::CONFIG.puppy_title || D3::PuppyTime::DFT_TITLE
98
+
99
+ @display_secs = D3::CONFIG.puppy_display_secs || D3::PuppyTime::DFT_DISPLAY_SECS
100
+
101
+ @show_captions = D3::CONFIG.puppy_display_captions
102
+
103
+ @dft_caption = D3::PuppyTime::DFT_CAPTION || D3::PuppyTime::DFT_CAPTION
104
+
105
+ # paths must exist, or use defaults
106
+ @optout_image_path = D3::PuppyTime::DFT_OPTOUT_IMAGE unless @optout_image_path.file?
107
+ @slideshow_folder_path = D3::PuppyTime::DFT_SLIDESHOW_DIR unless @slideshow_folder_path.directory?
108
+
109
+
110
+ end # init
111
+
112
+ ### walk them puppies!
113
+ def run
114
+
115
+ if @show_version
116
+ show_version
117
+ return
118
+ end
119
+
120
+ D3.log "Starting PuppyTime", :warn
121
+
122
+ # show the opt-out window
123
+ button_clicked = JSS::Client.jamf_helper( :hud,
124
+ :lock_hud => true,
125
+ :title => @title,
126
+ :heading => "It's PuppyTime!",
127
+ :align_heading => D3::PuppyTime::CAPTION_POSITION,
128
+ :description => @optout_text,
129
+ :align_description => D3::PuppyTime::TEXT_POSITION,
130
+ :icon => @optout_image_path.to_s,
131
+ :icon_size => @image_size,
132
+ :button1 => "OK",
133
+ :button2 => "Cancel",
134
+ :default_button => 1,
135
+ :cancel_button => 2,
136
+ :timeout => @optout_seconds,
137
+ :countdown => true,
138
+ :align_countdown => D3::PuppyTime::COUNTDOWN_POSITION
139
+ )
140
+
141
+ # TODO test if we need #startlaunchd
142
+
143
+ # did the user cancel in time?
144
+ if button_clicked == 2
145
+ D3.log "User cancelled puppytime.", :warn
146
+ return true
147
+ end
148
+
149
+ D3::Client.connect
150
+
151
+ # start the slideshow
152
+ # this sets up @write_to_slideshow as our end of the pipe to
153
+ # update the text on the slideshow
154
+ show_puppies
155
+
156
+ # now loop through the puppy queue, installing
157
+ # each one with a call to d3 itself
158
+ D3::PUPPY_Q.queue.each do |pupname,pup|
159
+
160
+ # make sure the pkg exists
161
+ unless D3::Package.all_editions.include? pup.edition
162
+ D3::PUPPY_Q - pup
163
+ D3.log "Removed invalid puppy #{pup.edition} from the queue.", :info
164
+ next
165
+ end
166
+
167
+ write_to_slideshow "Installing: #{pup.edition}"
168
+ pup.install
169
+
170
+ end # D3::PUPPY_Q.queue.each
171
+
172
+ write_to_slideshow "All Done with installs!\nThis computer will reboot in a moment.\nThanks for playing!"
173
+ sleep @display_secs
174
+
175
+ # end the slideshow
176
+ stop_puppies
177
+ D3.log "Puppies have been walked, rebooting.", :warn
178
+ do_reboot
179
+
180
+ end # run
181
+
182
+
183
+ def show_version
184
+ puts <<-ENDVERS
185
+ D3 module version: #{D3::VERSION}
186
+ JSS module version: #{JSS::VERSION}
187
+ ENDVERS
188
+ end
189
+
190
+ ### Update the slideshow with a new message
191
+ ###
192
+ ### @param message[String] the new message to display
193
+ ###
194
+ ### @return [void]
195
+ ###
196
+ def write_to_slideshow (message)
197
+ @write_to_slideshow.puts message if @write_to_slideshow
198
+ end
199
+
200
+ ### Start the puppy slideshow!
201
+ ### This forks off a process to show the puppy pics while we
202
+ ### do the installs. It just loops indefinitely through all
203
+ ### the puppy pics.
204
+ ### It returns the PID of the subprocess
205
+ ### which we'll kill when we're done with the installs
206
+ ###
207
+ ### @return [Integer] the pid of the slideshow process
208
+ ###
209
+ def show_puppies
210
+
211
+ # these pipe-ends will be duplicated in the forked child.
212
+ # we'll use the write_to_slideshow end to send the text of the latest
213
+ # thing we're installing.
214
+ #
215
+ # The forked child will use the read_from_main_process end to get that text from us
216
+ @read_from_main_process, @write_to_slideshow = IO.pipe
217
+
218
+ @slideshow_pid = Process.fork do
219
+
220
+ # the slideshow doesn't use this end of the pipe
221
+ @write_to_slideshow.close
222
+
223
+ # collect all the slideshow images and shuffle them into a random order
224
+ image_paths = @slideshow_folder_path.children.shuffle
225
+
226
+ # Start with this window description
227
+ description = "Preparing Installers."
228
+
229
+ while true do
230
+ image_paths.each do |slide_img|
231
+
232
+ caption = @show_captions ? slide_img.basename.to_s.chomp(slide_img.extname) : @dft_caption
233
+
234
+ # Read a new description, if one was sent, up to 3000 chars
235
+ begin
236
+ raw_read = @read_from_main_process.read_nonblock 3000
237
+ description = raw_read.lines.first.chomp
238
+ rescue
239
+ # this catches the error when read_from_main_process has nothing to give us
240
+ end
241
+
242
+ # Show this puppy
243
+ JSS::Client.jamf_helper( :hud,
244
+ :lock_hud => true,
245
+ :title => @title,
246
+ :heading => caption,
247
+ :align_heading => D3::PuppyTime::CAPTION_POSITION,
248
+ :description => description,
249
+ :align_description => D3::PuppyTime::TEXT_POSITION,
250
+ :icon => slide_img,
251
+ :icon_size => @image_size,
252
+ :timeout => @display_secs
253
+ )
254
+ end # each slide_img
255
+ end # while true
256
+
257
+ # the slideshow is done reading if we get here.
258
+ @read_from_main_process.close
259
+
260
+ end # fork
261
+
262
+ # the main process doesn't use this end of the pipe
263
+ @read_from_main_process.close
264
+
265
+ return @slideshow_pid
266
+ end #show_puppies
267
+
268
+ ### Stop the puppy slideshow
269
+ ###
270
+ ### @return [void]
271
+ ###
272
+ def stop_puppies
273
+ return if @puppies_stopped
274
+ @write_to_slideshow.close if @write_to_slideshow
275
+ Process.kill("TERM", @slideshow_pid) if @slideshow_pid
276
+ @slideshow_pid = nil
277
+ @puppies_stopped = true
278
+ end
279
+
280
+ ### Reboot the machine
281
+ ### If a reboot policy is defined and exists, use it
282
+ ### and expect it to do the reboot.
283
+ ### otherwise use 'shutdown -r now'
284
+ ###
285
+ ### @return [void]
286
+ ###
287
+ def do_reboot
288
+
289
+ # get the policy name or id from the d3 config
290
+ policy = D3::CONFIG.puppy_reboot_policy
291
+
292
+ unless policy
293
+ D3.log "No puppy reboot policy in configuration, using 'shutdown -r now'", :debug
294
+ system "/sbin/shutdown -r now"
295
+ return
296
+ end
297
+ unless D3.run_policy policy, :puppy_reboot, :verbose
298
+ D3.log "Invalid reboot policy '#{policy_config}' in configuration, using 'shutdown -r now'.", :warn
299
+ system "/sbin/shutdown -r now"
300
+ end
301
+ end # do reboot
302
+
303
+ end # class App
304
+
305
+ ############
306
+ begin
307
+ # exit asap if there are not puppies in the queue
308
+ exit 0 if D3::PUPPY_Q.pups.empty?
309
+
310
+ D3::LOG.progname = "puppytime"
311
+ D3::Client.set_env :puppytime
312
+
313
+ if app = App.new(ARGV)
314
+ app.run
315
+ # make sure we don't leave zombie puppies running around.
316
+ Process.wait if app.respond_to? :slideshow_pid and app.slideshow_pid
317
+ end
318
+
319
+ rescue
320
+ if defined?(app)
321
+ app.write_to_slideshow "*** AN ERROR OCCURED ***\nStopping puppies for now" if app.respond_to? :write_to_slideshow
322
+ app.stop_puppies if app.respond_to? :stop_puppies
323
+ end
324
+ # handle exceptions not handled elsewhere
325
+ D3.log "#{$!.class}: #{$!}", :fatal
326
+ D3.log_backtrace
327
+ puts $@
328
+ exit 12
329
+ ensure
330
+ if JSS::API.connected?
331
+ JSS::DistributionPoint.my_distribution_point.unmount if JSS::DistributionPoint.my_distribution_point.mounted?
332
+ D3::Client.disconnect
333
+ end # if
334
+ end
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <!--
4
+ This should be installed as a top-level launch agent, in /Library/LaunchAgents.
5
+ It should have owner:group 'root:wheel' and permissions 0644
6
+ It will be launched automatically when each user logs in, and will record
7
+ timestamps for applications coming to the foreground into a plist in
8
+ /Library/Application Support/d3/Usage/
9
+ -->
10
+ <plist version="1.0">
11
+ <dict>
12
+ <key>Label</key>
13
+ <string>com.pixar.d3.RepoMan</string>
14
+ <key>ProgramArguments</key>
15
+ <array>
16
+ <string>/Library/Application Support/d3/d3RepoMan.app/Contents/MacOS/d3RepoMan</string>
17
+ </array>
18
+ <key>RunAtLoad</key>
19
+ <true/>
20
+ <key>KeepAlive</key>
21
+ <true/>
22
+ </dict>
23
+ </plist>