░▒▓████████████████████████████████▓▒░ ░▒▓█ ▓▒░ ░▒▓█ ~ S W A M P ~ ▓▒░ ░▒▓█ ▓▒░ ░▒▓████████████████████████████████▓▒░
pplmvsvm
LOCATION:
/opt/microsoft/omsagent/plugin/in_dependencyagent
☗ ROOT
↻ REFRESH
✎ CARVE FLESH
EDITING: HeartbeatUpload.rb
# Copyright (c) 2016-2017 Microsoft. All rights reserved. # frozen_string_literal: true module DependencyAgent require_relative 'version.rb' unless const_defined? 'ConnectorVersion' class HeartbeatUpload require 'socket' require 'thread' require 'base64' require 'set' def initialize @thread = nil end def start(config, &cb) raise ArgumentError, 'config is nil' if config.nil? raise ArgumentError, "config is not kind of #{Configuration}" unless config.kind_of? Configuration raise ArgumentError, 'proc required' if cb.nil? raise RuntimeError, 'already started' unless @thread.nil? @thread = PollingThread.new config.poll, config.storage_dir, config.max_upload_size, config.log, cb nil end def stop return if @thread.nil? @thread = nil if @thread.stop end def running? (! @thread.nil?) && @thread.status end class Configuration def initialize(log) raise ArgumentError unless log @poll = 10 # seconds @storage_dir = "/var/opt/microsoft/dependency-agent/storage" @max_upload_size = 10 * 1024 * 1024 @log = log end def poll=(v) raise ArgumentError unless (v.kind_of? Numeric) && (v.real?) && (v >= 1) @poll = v end def storage_dir=(v) raise ArgumentError unless (v.instance_of? String) raise ArgumentError if v.empty? raise ArgumentError unless v.start_with? File.absolute_path('/') v.each_codepoint { |c| raise ArgumentError, v unless ASCII_PRINTABLE.cover? c } @storage_dir = v end def max_upload_size=(v) raise ArgumentError unless (v.kind_of? Numeric) && (v.integer?) && (v >= 1) @max_upload_size = v end attr_reader :poll, :storage_dir, :max_upload_size, :log ASCII_PRINTABLE = (0x20...0x7f) end # class Configuration private class PollingThread < Thread def initialize(interval, storage_dir, max_upload_size, log, cb) @mutex = Mutex.new @condvar = ConditionVariable.new @run = true @log = log @max_upload_size = max_upload_size @cb = cb @file_blacklist = Set.new @saved_dir_exception = nil @saved_dir_exception_reset_time = Time.now - 1 super() { abort_on_exception = false begin @log.info "Starting polling loop at #{interval} second interval on directory #{storage_dir}" protected_yield telemetry_message ConnectorStart while @run do each(storage_dir) @mutex.synchronize { @condvar.wait(@mutex, interval) if @run } end ensure protected_yield telemetry_message ConnectorStop @log.info "Stopping polling" end } end def stop @mutex.synchronize { @run = false @condvar.broadcast } self.join(5) || self.terminate end def each(dirname) begin Dir.open(dirname) { |dir| @log.debug "Polling '#{dirname}'" new_blacklist = Set.new dir.each { |entry| next unless File.fnmatch? '*.hb', entry # always put the file on the blacklist so if anything happens, # provided the new blacklist is preserved, # we won't touch this file again begin #1 new_blacklist << entry # skip if we have processed this file before next if @file_blacklist.include? entry fname = File.join(dirname, entry) message = file_to_message fname protected_yield message rescue => std @log.error std.message protected_yield telemetry_exception __FILE__, __LINE__, std end # begin #1 } # not doing in an ensure clause because if something happens # want to keep the existing blacklist @file_blacklist = new_blacklist } rescue SystemCallError => ex return nil if (ex == @saved_dir_exception) && (Time.now < @saved_dir_exception_reset_time) @log.error ex.message protected_yield telemetry_system_error dirname, StorageDirectoryError, ex @saved_dir_exception = ex @saved_dir_exception_reset_time = Time.now + (60 * 60) rescue => std return nil if (std == @saved_dir_exception) && (Time.now < @saved_dir_exception_reset_time) @log.error std.message protected_yield telemetry_exception __FILE__, __LINE__, std @saved_dir_exception = std @saved_dir_exception_reset_time = Time.now + (60 * 60) end nil end def protected_yield(*args) @cb[*args] rescue => ex @log.error "Unexpected exception #{ex.inspect}" @log.error_backtrace ex.backtrace true # if there was an exception, assume next steps should happen so as not to have it keep happening end def file_to_message(filename) @log.debug "Processing #{filename}" # Don't delete, used in unit test process_heartbeat_file filename rescue SystemCallError => sce @log.error sce.message @log.debug_backtrace telemetry_system_error filename, HeartbeatFileAccessError, sce rescue NoMemoryError, StandardError => ex @log.error ex.message @log.debug_backtrace telemetry_exception __FILE__, __LINE__, ex end def common_message(type) { "MessageType" => type, "Time" => Time.now.strftime("%FT%T.%3N%z"), "Platform" => "Linux", "Hostname" => HOSTNAME, "ConnectorVersion" => DependencyAgent::ConnectorVersion, } end def heartbeat_message data result = common_message "Heartbeat" result["HeartbeatData"] = data result end def telemetry_message eventId result = common_message "Telemetry" result["EventId"] = eventId result end def telemetry_file_message name, eventId result = telemetry_message eventId result["Name"] = name result end def telemetry_system_error name, eventId, ex result = telemetry_file_message name, eventId result["ErrorCode"] = (ex.class)::Errno result["ErrorMessage"] = ex.to_s result end def telemetry_file_size name, size result = telemetry_file_message name, HeartbeatFileTooBig result["Size"] = size result end def telemetry_exception file, line, ex result = telemetry_message ExceptionEventId result["Name"] = ex.class.to_s result["ErrorContext"] = "#{file}(#{line})" result["ErrorMessage"] = ex.message result end def process_heartbeat_file(filename) File.open(filename, FileOpenMode) { |f| stat = f.stat raise "#{filename}: invalid file type #{stat.ftype}" unless stat.file? @log.trace 'reading ...' # Don't delete, used in unit test data = String.new # relying upon NONBLOCK having no effect on ordinary files # optimize for the expected case that files are small enough # to fit, while ensuring a growing file can't put us over the # message size limit. while (input = f.read(3 * 1024)) data.concat(Base64.strict_encode64(input)) return telemetry_file_size(filename, f.stat.size) if (data.length > @max_upload_size) end heartbeat_message data } end StorageDirectoryError = 1 HeartbeatFileAccessError = 2 HeartbeatFileDeleteError = 3 HeartbeatFileTooBig = 4 ConnectorStart = 5 ConnectorStop = 6 ExceptionEventId = 7 HOSTNAME = Socket.gethostname # hackish way to keep the code running on Windows for development & unit testing require 'fcntl' FileOpenMode = (Fcntl.const_defined? :F_GETFL) ? (File::RDONLY | File::BINARY | File::NONBLOCK) : "rb" end # class PollingThread end # class end #module
CANCEL
Name
Type
Size
Modified
Actions
↩ ..
DIR
—
—
📄 HeartbeatUpload.rb
RB
8 KB
2023-11-30 04:10
EDIT
📄 version.rb
RB
88 B
2023-11-30 04:10
EDIT