depot3 3.0.9 → 3.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -42,8 +42,8 @@ module D3
42
42
  db_ro_user = D3::CONFIG.client_db_ro_user
43
43
  db_ro_user ||= JSS::CONFIG.db_username
44
44
 
45
- JSS::DB_CNX.connect :server => JSS::CONFIG.db_server_name, :user => db_ro_user, :pw => D3::Client.get_ro_pass(:db), :connect_timeout => 10
46
- JSS::API.connect :server => JSS::CONFIG.api_server_name, :user => jss_ro_user, :pw => D3::Client.get_ro_pass(:jss), :open_timeout => 10
45
+ JSS::DB_CNX.connect :server => JSS::CONFIG.db_server_name, :user => db_ro_user, :pw => D3::Client.get_ro_pass(:db)
46
+ JSS::API.connect :server => JSS::CONFIG.api_server_name, :user => jss_ro_user, :pw => D3::Client.get_ro_pass(:jss)
47
47
 
48
48
  D3::Database.check_schema_version
49
49
  end # connect
@@ -57,6 +57,10 @@ module D3
57
57
  raise JSS::NoSuchItemError, "No d3 package matching #{pkg_to_search}" unless desired_pkg
58
58
  raise D3::InstallError, "The package for #{desired_pkg.edition} is missing from the JSS" if desired_pkg.missing?
59
59
 
60
+ if options.custom_expiration
61
+ raise "Sorry #{desired_pkg.edition} is not expirable. A d3 admin needs to add an expiration path." if desired_pkg.expiration_paths.empty?
62
+ end
63
+
60
64
  curr_rcpt = D3::Client::Receipt.all[desired_pkg.basename]
61
65
 
62
66
  # many things can be forced
@@ -101,14 +105,13 @@ module D3
101
105
  D3.log "Finished installing #{desired_pkg.edition}(#{desired_pkg.status})", :info
102
106
 
103
107
  rescue JSS::MissingDataError, JSS::NoSuchItemError, JSS::InvalidDataError, D3::InstallError
104
- D3.log "Skipping installation of #{pkg_to_search}:\n #{$!}", :error
108
+ D3.log "Skipping installation of #{pkg_to_search}: #{$!}", :error
105
109
  D3.log_backtrace
106
110
  rescue D3::PreInstallError
107
- D3.log "There was an error with the pre-install script for #{desired_pkg.edition}:\n #{$!}", :error
111
+ D3.log "There was an error with the pre-install script for #{desired_pkg.edition}: #{$!}", :error
108
112
  D3.log_backtrace
109
113
  rescue D3::PostInstallError
110
- D3.log "There was an error with the post-install script for #{desired_pkg.edition}:\n #{$!}", :error
111
- D3.log " NOTE: it was installed, but may have problems.", :error
114
+ D3.log "There was an error with the post-install script for #{desired_pkg.edition}: #{$!} NOTE: it was installed, but may have problems.", :error
112
115
  D3.log_backtrace
113
116
  end # begin
114
117
  end # args.each
@@ -133,7 +136,7 @@ module D3
133
136
  D3.log "Finished uninstalling #{rcpt.edition}.", :info
134
137
 
135
138
  rescue JSS::MissingDataError, D3::UninstallError, JSS::InvalidDataError
136
- D3.log "Skipping uninstall of #{rcpt_to_remove}:\n #{$!}", :error
139
+ D3.log "Skipping uninstall of #{rcpt_to_remove}: #{$!}", :error
137
140
  D3.log_backtrace
138
141
  next
139
142
  end # begin
@@ -223,6 +226,13 @@ module D3
223
226
  # expirations
224
227
  do_expirations
225
228
 
229
+ # removie receipts w/ missing packages on the server
230
+ # This must happen AFTER update_installed_pkgs
231
+ # so that the basename gets any updates on the server
232
+ # before removing the recetip (which wouild prevent
233
+ # updates)
234
+ clean_missing_receipts
235
+
226
236
  D3.log "Finished sync", :warn
227
237
  ensure
228
238
  D3::Client.unset_env :sync
@@ -256,27 +266,35 @@ module D3
256
266
  rcpt.update
257
267
  next
258
268
  end
269
+ need_update = false
270
+
271
+ # Are we rolling back to a prev version?
272
+ # If the pkgdata[:status] is :pilot and the
273
+ # rcpt.status is NOT :pilot, then we are.
274
+ rolling_back = (pkgdata[:status] == :pilot) && (rcpt.status != :pilot)
259
275
 
260
276
  # status
261
- if rcpt.status != pkgdata[:status]
262
- # update the status
263
- rcpt.status = pkgdata[:status]
264
- D3.log "Updating status for #{rcpt.edition} to #{pkgdata[:status]}", :info
265
- rcpt.update
266
- end # if
277
+ unless rolling_back
278
+ if rcpt.status != pkgdata[:status]
279
+ # update the status
280
+ rcpt.status = pkgdata[:status]
281
+ D3.log "Updating status for #{rcpt.edition} to #{pkgdata[:status]}", :info
282
+ need_update = true
283
+ end # if
284
+ end # unless
267
285
 
268
286
  # pre-remove script
269
287
  if rcpt.pre_remove_script_id != pkgdata[:pre_remove_script_id]
270
288
  rcpt.pre_remove_script_id = pkgdata[:pre_remove_script_id]
271
289
  D3.log "Updating pre-remove script for #{rcpt.edition}", :info
272
- rcpt.update
290
+ need_update = true
273
291
  end # if
274
292
 
275
293
  # post-remove script
276
294
  if rcpt.post_remove_script_id != pkgdata[:post_remove_script_id]
277
295
  rcpt.post_remove_script_id = pkgdata[:post_remove_script_id]
278
296
  D3.log "Updating post-remove script for #{rcpt.edition}", :info
279
- rcpt.update
297
+ need_update = true
280
298
  end # if
281
299
 
282
300
  # removability
@@ -287,22 +305,22 @@ module D3
287
305
  rcpt.expiration = 0
288
306
  D3.log "#{rcpt.edition} is not expirable now that it's not removable", :info
289
307
  end
290
- rcpt.update
308
+ need_update = true
291
309
  end # if
292
310
 
293
311
  # expiration
294
312
  if rcpt.removable?
295
313
 
296
- if rcpt.expiration_path.to_s != pkgdata[:expiration_path].to_s
297
- rcpt.expiration_path = pkgdata[:expiration_path]
298
- D3.log "Updating expiration path for #{rcpt.edition}", :info
299
- rcpt.update
314
+ unless rcpt.expiration_paths_match? pkgdata[:expiration_paths]
315
+ rcpt.expiration_paths = pkgdata[:expiration_paths]
316
+ D3.log "Updating expiration path(s) for #{rcpt.edition}", :info
317
+ need_update = true
300
318
  end # if
301
-
319
+
302
320
  if (rcpt.expiration != pkgdata[:expiration].to_i) and (not rcpt.custom_expiration)
303
321
  rcpt.expiration = pkgdata[:expiration].to_i
304
322
  D3.log "Updating expiration for #{rcpt.edition}", :info
305
- rcpt.update
323
+ need_update = true
306
324
  end # if
307
325
  end # if removable
308
326
 
@@ -310,18 +328,12 @@ module D3
310
328
  if rcpt.prohibiting_process.to_s != pkgdata[:prohibiting_process].to_s
311
329
  rcpt.prohibiting_process = pkgdata[:prohibiting_process]
312
330
  D3.log "Updating prohibiting_process for #{rcpt.edition}", :info
313
- rcpt.update
331
+ need_update = true
314
332
  end # if
315
333
 
316
- # last usage
317
- # this will update the last_usage value stored in the rcpt (for reporting only)
318
- # (expiration only looks at current usage data)
319
- if rcpt.expiration_path
320
- rcpt.last_usage
321
- rcpt.update
322
- end
323
334
 
324
- end # each do basename, rcpt
335
+ rcpt.update if need_update
336
+ end # each do basename, rcpt
325
337
  end # update
326
338
 
327
339
  ### remove any invalid puppies from the queue
@@ -399,14 +411,13 @@ module D3
399
411
  )
400
412
  D3.log "Auto-installed #{new_pkg.basename}", :warn
401
413
  rescue JSS::MissingDataError, JSS::InvalidDataError, D3::InstallError
402
- D3.log "Skipping auto-install of #{new_pkg.edition}:\n #{$!}", :error
414
+ D3.log "Skipping auto-install of #{new_pkg.edition}: #{$!}", :error
403
415
  D3.log_backtrace
404
416
  rescue D3::PreInstallError
405
- D3.log "There was an error with the pre-install script for #{new_pkg.edition}:\n #{$!}", :error
417
+ D3.log "There was an error with the pre-install script for #{new_pkg.edition}: #{$!}", :error
406
418
  D3.log_backtrace
407
419
  rescue D3::PostInstallError
408
- D3.log "There was an error with the post-install script for #{new_pkg.edition}:\n #{$!}", :error
409
- D3.log " NOTE: #{new_pkg.edition} was installed, but may not work.", :error
420
+ D3.log "There was an error with the post-install script for #{new_pkg.edition}: #{$!} NOTE: #{new_pkg.edition} was installed, but may not work.", :error
410
421
  D3.log_backtrace
411
422
  end #begin
412
423
  end # live_ids_for_group.each do |live_id|
@@ -416,6 +427,18 @@ module D3
416
427
  end
417
428
  end
418
429
 
430
+ ### remove any receipts for packages that are missing from the server
431
+ ###
432
+ ###
433
+ def self.clean_missing_receipts
434
+ D3.log "Checking for receipts no longer in d3", :warn
435
+ D3::Client::Receipt.all.values.select{|r| r.status == :missing}.each do |mrcpt|
436
+ D3.log "Removing receipt for missing edition #{mrcpt.edition}", :info
437
+ D3::Client::Receipt.remove_receipt mrcpt.basename
438
+ D3.log "Removed receipt for missing edition #{mrcpt.edition}", :info
439
+ end
440
+ end
441
+
419
442
  ### Update any currently installed basenames to the currently live one
420
443
  ### skipping any basenames currently frozen
421
444
  ###
@@ -480,7 +503,9 @@ module D3
480
503
 
481
504
  # mention rollbacks
482
505
  if rollback
483
- D3.log "Rolling back #{rcpt.edition} (#{rcpt.status}) to older live #{ live_pkg_data[:edition]}.", :warn
506
+ D3.log "Rolling back #{rcpt.edition} (#{rcpt.status}) to older live #{live_pkg_data[:edition]}.", :warn
507
+ else
508
+ D3.log "Updating #{rcpt.edition} (#{rcpt.status}) to #{live_pkg_data[:edition]} (#{live_pkg_data[:status]})", :warn
484
509
  end
485
510
 
486
511
  # are we bringing over a custom expiration period?
@@ -488,7 +513,6 @@ module D3
488
513
 
489
514
  # heres the pkg
490
515
  live_pkg = D3::Package.new :id => live_basenames_to_ids[rcpt.basename]
491
- D3.log "Updating #{rcpt.edition} (#{rcpt.status}) to #{live_pkg.edition} (#{live_pkg.status})", :warn
492
516
 
493
517
  begin
494
518
  live_pkg.install(
@@ -501,14 +525,13 @@ module D3
501
525
  )
502
526
  D3.log "Done updating #{rcpt.edition} (#{rcpt.status}) to #{live_pkg.edition} (#{live_pkg.status})", :info
503
527
  rescue JSS::MissingDataError, JSS::InvalidDataError, D3::InstallError
504
- D3.log "Skipping update of #{rcpt.edition} to #{live_pkg.edition}:\n #{$!}", :error
528
+ D3.log "Skipping update of #{rcpt.edition} to #{live_pkg.edition}: #{$!}", :error
505
529
  D3.log_backtrace
506
530
  rescue D3::PreInstallError
507
- D3.log "There was an error with the pre-install script for #{live_pkg.edition}:\n #{$!}", :error
531
+ D3.log "There was an error with the pre-install script for #{live_pkg.edition}: #{$!}", :error
508
532
  D3.log_backtrace
509
533
  rescue D3::PostInstallError
510
- D3.log "There was an error with the post-install script for #{live_pkg.edition}:\n #{$!}", :error
511
- D3.log " NOTE: #{live_pkg.edition} was installed, but may not work.", :error
534
+ D3.log "There was an error with the post-install script for #{live_pkg.edition}: #{$!} NOTE: #{live_pkg.edition} was installed, but may not work.", :error
512
535
  D3.log_backtrace
513
536
  end # begin
514
537
  end # D3::Client::Receipt.all.values.each
@@ -555,7 +578,23 @@ module D3
555
578
  rcpt.update
556
579
  D3.log "Thawing receipt for #{rcpt.edition}, will resume auto-update during sync", :warn
557
580
  end
558
- end # freeze receipts
581
+ end # thaw_receipts
582
+
583
+ ### forget one or more receipts, and their matching apple pkg receipts
584
+ ###
585
+ ### @param basenames[Array] the basenames of the rcpts to forget
586
+ ###
587
+ ### @return [void]
588
+ ###
589
+ def self.forget_receipts (basenames)
590
+ basenames.each do |bn|
591
+ rcpt = D3::Client::Receipt.all[bn]
592
+ next unless rcpt
593
+ rcpt.apple_pkg_ids.each{|ar| system "/usr/sbin/pkgutil --forget '#{ar}'" }
594
+ D3::Client::Receipt.remove_receipt bn
595
+ D3.log "Receipt for #{rcpt.edition} has been forgotten", :warn
596
+ end
597
+ end # thaw_receipts
559
598
 
560
599
  ### Do any pending puppy installs right now, because we're
561
600
  ### syncing and --puppies option was given
@@ -583,14 +622,13 @@ module D3
583
622
  D3.log_backtrace
584
623
  D3::PUPPY_Q - puppy
585
624
  rescue JSS::MissingDataError, JSS::InvalidDataError, D3::InstallError
586
- D3.log "Skipping install of #{new_pkg.edition} from queue:\n #{$!}", :error
625
+ D3.log "Skipping install of #{new_pkg.edition} from queue: #{$!}", :error
587
626
  D3.log_backtrace
588
627
  rescue D3::PreInstallError
589
- D3.log "There was an error with the pre-install script for #{new_pkg.edition}:\n #{$!}", :error
628
+ D3.log "There was an error with the pre-install script for #{new_pkg.edition}: #{$!}", :error
590
629
  D3.log_backtrace
591
630
  rescue D3::PostInstallError
592
- D3.log "There was an error with the post-install script for #{new_pkg.edition}:\n #{$!}", :error
593
- D3.log " NOTE: #{new_pkg.edition} was installed, but may not work.", :error
631
+ D3.log "There was an error with the post-install script for #{new_pkg.edition}: #{$!} NOTE: #{new_pkg.edition} was installed, but may not work.", :error
594
632
  D3.log_backtrace
595
633
  D3::PUPPY_Q - puppy
596
634
  end # begin
@@ -634,7 +672,7 @@ module D3
634
672
  expired_edition = rcpt.expire verbose, force
635
673
  @@editions_expired << expired_edition if expired_edition
636
674
  rescue
637
- D3.log "There was an error expiring #{rcpt.edition}:\n #{$!}", :error
675
+ D3.log "There was an error expiring #{rcpt.edition}: #{$!}", :error
638
676
  D3.log_backtrace
639
677
  end
640
678
  end
data/lib/d3/client/cli.rb CHANGED
@@ -67,6 +67,12 @@ module D3
67
67
  :needs_connection => false,
68
68
  :arg => :basename
69
69
  },
70
+ forget: {
71
+ :aka => :fg,
72
+ :help => "Remove receipt but don't try uninstalling.",
73
+ :needs_connection => false,
74
+ :arg => :basename
75
+ },
70
76
  list_available: {
71
77
  :aka => :la,
72
78
  :help => "list all available live installers on the server",
@@ -97,6 +103,11 @@ module D3
97
103
  :help => "list any queued pkgs awaiting puppytime at logout",
98
104
  :needs_root => false
99
105
  },
106
+ list_queue: {
107
+ :aka => :lq,
108
+ :help => "list any queued pkgs awaiting puppytime at logout",
109
+ :needs_root => false
110
+ },
100
111
  list_details: {
101
112
  :aka => :ld,
102
113
  :help => "show detailed info about packages in d3",
@@ -58,6 +58,7 @@ Actions:
58
58
  uninstall u <basename> - Uninstall packages
59
59
  freeze f <basename> - Stop auto-updates of this basename
60
60
  thaw t <basename> - Resume auto-updates of this basename
61
+ forget fg <basename> - Remove receipt without uninstalling
61
62
  dequeue dq <basename> - Remove a pending logout install
62
63
  sync s - Update receipt data, do auto-installs
63
64
  update installed software & uninstall
@@ -105,7 +105,7 @@ module D3
105
105
  :pre_remove_script_id,
106
106
  :post_remove_script_id,
107
107
  :expiration,
108
- :expiration_path,
108
+ :expiration_paths,
109
109
  :prohibiting_process
110
110
  ]
111
111
 
@@ -148,6 +148,7 @@ module D3
148
148
  self.get_datastore_lock(lock_timeout) if rw
149
149
 
150
150
  @@installed_rcpts = DATASTORE.file? ? YAML.load(DATASTORE.read) : {}
151
+ @@installed_rcpts ||= {}
151
152
 
152
153
  D3.log "Receipts loaded", :debug
153
154
  end # seld.load_receipts
@@ -162,7 +163,7 @@ module D3
162
163
  ###
163
164
  ### @return [void]
164
165
  ###
165
- def self.reload_receipts(rw = false, lock_timeout = DATASTORE_LOCK_TIMEOUT)
166
+ def self.reload_receipts (rw = false, lock_timeout = DATASTORE_LOCK_TIMEOUT)
166
167
 
167
168
  # if we haven't loaded them at all yet, just do that.
168
169
  unless @@installed_rcpts
@@ -183,6 +184,7 @@ module D3
183
184
 
184
185
  # reload it
185
186
  @@installed_rcpts = DATASTORE.file? ? YAML.load(DATASTORE.read) : {}
187
+ @@installed_rcpts ||= {}
186
188
  D3.log "Receipts reloaded", :debug
187
189
  end # self.reload_receipts
188
190
 
@@ -470,8 +472,8 @@ module D3
470
472
  :removable => d3_pkg.removable,
471
473
  :pre_remove_script_id => d3_pkg.pre_remove_script_id,
472
474
  :post_remove_script_id => d3_pkg.post_remove_script_id,
473
- :expiraation => d3_pkg.expiraation,
474
- :expiraation_path => d3_pkg.expiraation_path
475
+ :expiration => d3_pkg.expiration,
476
+ :expiration_paths => d3_pkg.expiration_paths
475
477
  )
476
478
 
477
479
  end # .each do |d3_pkg|
@@ -519,11 +521,11 @@ module D3
519
521
  attr_accessor :frozen
520
522
 
521
523
  # @return [Time, nil] When was this app last used.
522
- # nil if never checked, or no @expiration_path
524
+ # nil if never checked, or no @expiration_paths
523
525
  attr_reader :last_usage
524
526
 
525
527
  # @return [Time, nil] When was @last_usage updated?
526
- # nil if never checked, or no @expiration_path
528
+ # nil if never checked, or no @expiration_paths
527
529
  attr_reader :last_usage_as_of
528
530
 
529
531
  ################# Constructor #################
@@ -579,7 +581,7 @@ module D3
579
581
  @post_remove_script_id = args[:post_remove_script_id]
580
582
 
581
583
  @expiration = args[:expiration].to_i
582
- @expiration_path = args[:expiration_path]
584
+ @expiration_paths = args[:expiration_paths]
583
585
  @custom_expiration = args[:custom_expiration]
584
586
 
585
587
  @manually_installed = (@admin != D3::AUTO_INSTALL_ADMIN)
@@ -807,7 +809,7 @@ module D3
807
809
  @manually_installed = (@admin != D3::AUTO_INSTALL_ADMIN)
808
810
  @package_type = @jamf_rcpt_file.end_with?(".dmg") ? :dmg : :pkg
809
811
  @expiration = d3_pkg.expiration
810
- @expiration_path = d3_pkg.expiration_path
812
+ @expiration_paths = d3_pkg.expiration_paths
811
813
 
812
814
  end # repair rcpt
813
815
 
@@ -860,8 +862,8 @@ module D3
860
862
  ###
861
863
  ### @return [void]
862
864
  ###
863
- def expiration_path= (new_val)
864
- @expiration_path = Pathname.new new_val
865
+ def expiration_paths= (new_val)
866
+ @expiration_paths = new_val
865
867
  end
866
868
 
867
869
  ### Set a new prohibiting process
@@ -930,7 +932,7 @@ Post-remove script: #{post_name}
930
932
  Apple.pkg ids: #{@apple_pkg_ids.join(', ')}
931
933
  END_DEETS
932
934
  end
933
- if @expiration_path
935
+ if @expiration_paths
934
936
  if @expiration.to_i > 0
935
937
  lu = last_usage
936
938
  if lu.nil?
@@ -943,7 +945,7 @@ Apple.pkg ids: #{@apple_pkg_ids.join(', ')}
943
945
 
944
946
  deets += <<-END_DEETS
945
947
  Expiration period: #{@expiration} days#{@custom_expiration ? ' (custom)' : ''}
946
- Expiration path: #{@expiration_path}
948
+ Expiration path(s): #{D3::Database::ARRAY_OF_PATHNAMES_TO_COMMA_STRING.call @expiration_paths}
947
949
  Last brought to foreground: #{last_usage_display}
948
950
  END_DEETS
949
951
  end # if exp > 0
@@ -970,8 +972,8 @@ Last brought to foreground: #{last_usage_display}
970
972
  return false if @expiration.nil? or @expiration == 0
971
973
 
972
974
  # gotta have an expiration path
973
- unless @expiration_path
974
- D3.log "Not expiring #{edition} because: No Expiration Path for #{edition}", :debug
975
+ if @expiration_paths.empty?
976
+ D3.log "Not expiring #{edition} because: No Expiration Path(s) for #{edition}", :debug
975
977
  return false
976
978
  end
977
979
 
@@ -1048,7 +1050,7 @@ Last brought to foreground: #{last_usage_display}
1048
1050
  return deleted? ? edition : nil
1049
1051
  end # expire
1050
1052
 
1051
- ### Return the number of days since the last usage for the @expiration_path
1053
+ ### Return the number of days since the last usage for the @expiration_paths
1052
1054
  ### for this receipt
1053
1055
  ###s
1054
1056
  ### Returns nil if last_usage is nil
@@ -1079,19 +1081,19 @@ Last brought to foreground: #{last_usage_display}
1079
1081
  ### expiration path or the data wasn't retrievable.
1080
1082
  ###
1081
1083
  def last_usage
1082
- return nil unless @expiration_path
1084
+ return nil unless @expiration_paths
1083
1085
 
1084
1086
  now = Time.now
1085
1087
 
1086
1088
  # if it's in the foreground right now, return [now, 0]
1087
1089
  fgnd_path = D3::Client.foreground_executable_path
1088
1090
  if fgnd_path
1089
- now_in_forground = (fgnd_path.to_s == @expiration_path.to_s.chomp('/'))
1091
+ now_in_foreground = @expiration_paths.select{|p| fgnd_path == p}.length > 0
1090
1092
  else
1091
- now_in_forground = nil
1093
+ now_in_foreground = nil
1092
1094
  end
1093
1095
 
1094
- if now_in_forground
1096
+ if now_in_foreground
1095
1097
  @last_usage = now
1096
1098
  @last_usage_as_of = now
1097
1099
  return @last_usage
@@ -1117,13 +1119,14 @@ Last brought to foreground: #{last_usage_display}
1117
1119
  return nil
1118
1120
  end
1119
1121
 
1120
- # loop through the plists, get the newest usage time for this
1122
+ # loop through the plists, get the newest usage time for this
1121
1123
  # expiration path, and append it to all_usages
1122
1124
  all_usages = []
1123
1125
  plists.each do |plist|
1124
1126
  usage_times = D3.parse_plist plist
1125
- my_usage_keys = usage_times.keys.select{|k| k.start_with? @expiration_path.to_s }
1126
- all_usages << my_usage_keys.map{|k| usage_times[k].to_time }.max
1127
+ my_usage_keys = usage_times.keys.map{|p| Pathname.new(p)}
1128
+ exp_paths_with_usage = @expiration_paths & my_usage_keys
1129
+ exp_paths_with_usage.each{|p| all_usages << usage_times[p] }
1127
1130
  end # do plist
1128
1131
 
1129
1132
  @last_usage = all_usages.compact.max
data/lib/d3/database.rb CHANGED
@@ -42,7 +42,7 @@ module D3
42
42
  MIN_SCHEMA_VERSION = "9.4"
43
43
 
44
44
  # the minimum JSS schema version allower
45
- MAX_SCHEMA_VERSION = "9.82"
45
+ MAX_SCHEMA_VERSION = "9.93"
46
46
 
47
47
  ### these Proc objects allow us to encapsulate and pass around various
48
48
  ### blocks of code more easily for converting data between their mysql
@@ -59,6 +59,10 @@ module D3
59
59
 
60
60
  ### Some values are stored as comma-separated strings, but used as Arrays
61
61
  COMMA_STRING_TO_ARRAY = Proc.new{|v| JSS.to_s_and_a(v)[:arrayform] }
62
+
63
+ ### Some values are stored as comma-separated strings, but used as Arrays of Pathnames
64
+ COMMA_STRING_TO_ARRAY_OF_PATHNAMES = Proc.new{|v| JSS.to_s_and_a(v)[:arrayform].map{|p| Pathname.new(p)} }
65
+ ARRAY_OF_PATHNAMES_TO_COMMA_STRING = Proc.new{|v| v.join(", ")}
62
66
 
63
67
  ### Some values are used as Arrays but stored as comma-separated strings
64
68
  ARRAY_TO_COMMA_STRING = Proc.new{|v| JSS.to_s_and_a(v)[:stringform] }
@@ -325,12 +329,12 @@ module D3
325
329
  :to_ruby => STRING_TO_INT
326
330
  },
327
331
 
328
- :expiration_path => {
332
+ :expiration_paths => {
329
333
  :field_name => "expiration_app_path",
330
334
  :sql_type => 'varchar(300)',
331
335
  :index => nil,
332
- :to_sql => PATHNAME_TO_STRING,
333
- :to_ruby => STRING_TO_PATHNAME
336
+ :to_sql => ARRAY_OF_PATHNAMES_TO_COMMA_STRING,
337
+ :to_ruby => COMMA_STRING_TO_ARRAY_OF_PATHNAMES
334
338
  }
335
339
  },
336
340
 
@@ -404,7 +408,8 @@ module D3
404
408
  ### Raise an exception if JSS schema is to old or too new
405
409
  def self.check_schema_version
406
410
  raw = JSS::DB_CNX.db.query("SELECT version FROM #{SCHEMA_TABLE}").fetch[0]
407
- current = JSS.parse_jss_version(raw)[:version]
411
+ simmered = raw.split('.')[0..1].join('.')
412
+ current = JSS.parse_jss_version(simmered)[:version]
408
413
  min = JSS.parse_jss_version(MIN_SCHEMA_VERSION)[:version]
409
414
  max = JSS.parse_jss_version(MAX_SCHEMA_VERSION)[:version]
410
415
  raise JSS::InvalidConnectionError, "Invalid JSS database schema version: #{raw}, min: #{MIN_SCHEMA_VERSION}, max: #{MAX_SCHEMA_VERSION}" if current < min or current > max
data/lib/d3/log.rb CHANGED
@@ -28,13 +28,16 @@ module D3
28
28
 
29
29
  ### Log a message to the d3 log, possibly sending it to stderr as well.
30
30
  ###
31
- ### The message will appear in the log based upon its severity level,
32
- ### and the current D3::Log.level. Any message more severe than the log level
33
- ### will be logged.
31
+ ### The message will appear in the log:
32
+ ### - if the log is writable by the current user
33
+ ### - based upon its severity level, and the current D3::Log.level.
34
+ ### Any message more severe than the log level will be logged.
34
35
  ###
35
- ### The message will appear on stderr if the message severity is
36
- ### at or higher than the current @@verbosity. If the @@verbosity is :debug
37
- ### the messages to stderr will be prefixed with the message severity.
36
+ ### The message will also appear on stderr if the message severity is
37
+ ### at or higher than the current @@verbosity.
38
+ ###
39
+ ### If the @@verbosity is :debug the messages to stderr will be prefixed with
40
+ ### the message severity.
38
41
  ###
39
42
  ### In the d3 command, @@verbosity is controlled with the -v, -q and -d
40
43
  ### options
@@ -62,12 +65,8 @@ module D3
62
65
  end
63
66
  end #
64
67
 
65
- # can't write to the log unless we're super user
66
- return unless JSS.superuser?
67
-
68
68
  # send to the logger
69
69
  D3::Log.instance.log msg, severity
70
-
71
70
  end
72
71
 
73
72
  ### Log the lines of backtrace from the most recent exception
@@ -173,31 +172,25 @@ module D3
173
172
  @level = D3::CONFIG.log_level if D3::CONFIG.log_level
174
173
  @timestamp_format = D3::CONFIG.log_timestamp_format if D3::CONFIG.log_timestamp_format
175
174
 
176
- # the logger will be created when it's needed
177
- @logger = nil
178
-
175
+ # the logger will be created if the file is writable
176
+ writable = if @log_file.file?
177
+ @log_file.writable?
178
+ else
179
+ @log_file.parent.writable?
180
+ end
179
181
 
182
+ if writable
183
+ @logger = Logger.new @log_file
184
+ @logger.level = D3::Log.check_level(@level)
185
+ set_format
186
+ else
187
+ @logger = nil
188
+ end
180
189
 
181
190
  end # init
182
191
 
183
192
  ################# Public Instance Methods #################
184
193
 
185
- ### Access the logger, creating it if needed.
186
- ### We don't make it when the instance is created because it
187
- ### might want to write to a place we don't have permissions.
188
- ### So anything that uses it will call this to create it when
189
- ### its needed.
190
- def the_logger
191
- return @logger if @logger
192
- # make logger if needed
193
- unless @logger
194
- @logger = Logger.new @log_file
195
- set_format
196
- end
197
- @logger.level = D3::Log.check_level @level
198
- return @logger
199
- end
200
-
201
194
  ### Send a message to be logged
202
195
  ### If the severity is less severe than the current level,
203
196
  ### the message won't be written to the log.
@@ -215,7 +208,8 @@ module D3
215
208
  ### @return [Boolean] the message was handled appropriately, or not
216
209
  ###
217
210
  def log (msg, severity = DFT_LOG_LEVEL)
218
- the_logger.add(D3::Log.check_level(severity), msg, @progname)
211
+ return nil unless @logger
212
+ @logger.add(D3::Log.check_level(severity), msg, @progname)
219
213
  end
220
214
 
221
215
  ### Set a new severity-level for logging.
@@ -226,8 +220,9 @@ module D3
226
220
  ### @return [void]
227
221
  ###
228
222
  def level= (new_level)
229
- the_logger.level = D3::Log.check_level(new_level)
230
- @level = new_level
223
+ return nil unless @logger
224
+ @level = D3::Log.check_level(new_level)
225
+ @logger.level = @level
231
226
  end
232
227
 
233
228
 
@@ -250,20 +245,21 @@ module D3
250
245
  ### @return [void]
251
246
  ###
252
247
  def timestamp_format= (new_format)
253
- new_format = new_format.to_s
254
- the_logger.datetime_format = new_format
255
- @timestamp_format = new_format
248
+ return nil unless @logger
249
+ @timestamp_format = new_format.to_s
250
+ @logger.datetime_format = @timestamp_format
256
251
  end # timestamp_format=
257
252
 
258
253
  private
259
254
 
260
255
  ### set up the log line format
261
256
  def set_format
262
- # set the line format
263
- the_logger.formatter = proc do |severity, datetime, progname, msg|
257
+ return nil unless @logger
258
+ @logger.formatter = proc do |severity, datetime, progname, msg|
264
259
  "#{datetime.strftime @timestamp_format} #{progname} [#{$$}]: #{severity}: #{msg}\n"
265
260
  end #
266
- end
261
+ end # set format
262
+
267
263
  end # class Log
268
264
 
269
265
  # the singleton instance of our logger
@@ -167,7 +167,7 @@ module D3
167
167
 
168
168
  # if we have a setter method for this key, call it to set the attribute.
169
169
  setter = "#{fld_key}=".to_sym
170
- send(setter, fld_val) if self.respond_to?(setter, true) # the 'true' makes respond_to? look at private methods also
170
+ self.send(setter, fld_val) if self.respond_to?(setter, true) # the 'true' makes respond_to? look at private methods also
171
171
 
172
172
  end # PFIELDS.each
173
173
  end # if d3pkg_data
@@ -175,6 +175,9 @@ module D3
175
175
  # some nil-values shouldn't be nil
176
176
  @auto_groups ||= []
177
177
  @excluded_groups ||= []
178
+
179
+ # expiration_paths should always be an array
180
+ @expiration_paths ||= []
178
181
 
179
182
  # these don't come from the table def.
180
183
  @admin = args[:admin]