Class: Aspera::Environment

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/aspera/environment.rb

Overview

detect OS, architecture, and specific stuff

Constant Summary collapse

USER_INTERFACES =
%i[text graphical].freeze
OS_WINDOWS =
:windows
OS_MACOS =
:osx
OS_LINUX =
:linux
OS_AIX =
:aix
OS_LIST =
[OS_WINDOWS, OS_MACOS, OS_LINUX, OS_AIX].freeze
CPU_X86_64 =
:x86_64
CPU_ARM64 =
:arm64
CPU_PPC64 =
:ppc64
CPU_PPC64LE =
:ppc64le
CPU_S390 =
:s390
CPU_LIST =
[CPU_X86_64, CPU_ARM64, CPU_PPC64, CPU_PPC64LE, CPU_S390].freeze
BITS_PER_BYTE =
8
MEBI =
1024 * 1024
BYTES_PER_MEBIBIT =
MEBI / BITS_PER_BYTE

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeEnvironment

Returns a new instance of Environment.

[View source]

206
207
208
209
# File 'lib/aspera/environment.rb', line 206

def initialize
  @url_method = self.class.default_gui_mode
  @terminal_supports_unicode = nil
end

Instance Attribute Details

#url_methodObject

Returns the value of attribute url_method.


204
205
206
# File 'lib/aspera/environment.rb', line 204

def url_method
  @url_method
end

Class Method Details

.architectureObject

normalized architecture name see constants: OS_* and CPU_*

[View source]

70
71
72
# File 'lib/aspera/environment.rb', line 70

def architecture
  return "#{os}-#{cpu}"
end

.cpuObject

[View source]

53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/aspera/environment.rb', line 53

def cpu
  case RbConfig::CONFIG['host_cpu']
  when /x86_64/, /x64/
    return CPU_X86_64
  when /powerpc/, /ppc64/
    return CPU_PPC64LE if os.eql?(OS_LINUX)
    return CPU_PPC64
  when /s390/
    return CPU_S390
  when /arm/, /aarch64/
    return CPU_ARM64
  else Aspera.error_unexpected_value(RbConfig::CONFIG['host_cpu']){'host_cpu'}
  end
end

.default_gui_modeObject

Returns :text or :graphical depending on the environment.

Returns:

  • :text or :graphical depending on the environment

[View source]

173
174
175
176
177
178
179
# File 'lib/aspera/environment.rb', line 173

def default_gui_mode
  # assume not remotely connected on macos and windows
  return :graphical if [Environment::OS_WINDOWS, Environment::OS_MACOS].include?(Environment.os)
  # unix family
  return :graphical if ENV.key?('DISPLAY') && !ENV['DISPLAY'].empty?
  return :text
end

.empty_bindingObject

empty variable binding for secure eval

[View source]

89
90
91
# File 'lib/aspera/environment.rb', line 89

def empty_binding
  return Kernel.binding
end

.exe_file(name = '') ⇒ Object

executable file extension for current OS

[View source]

75
76
77
78
# File 'lib/aspera/environment.rb', line 75

def exe_file(name='')
  return "#{name}.exe" if os.eql?(OS_WINDOWS)
  return name
end

.fix_homeObject

on Windows, the env var %USERPROFILE% provides the path to user’s home more reliably than %HOMEDRIVE%%HOMEPATH% so, tell Ruby the right way

[View source]

82
83
84
85
86
# File 'lib/aspera/environment.rb', line 82

def fix_home
  return unless os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV.fetch('USERPROFILE', nil))
  ENV['HOME'] = ENV.fetch('USERPROFILE', nil)
  Log.log.debug{"Windows: set HOME to USERPROFILE: #{Dir.home}"}
end

.log_spawn(env:, exec:, args:) ⇒ Object

[View source]

98
99
100
101
102
103
104
105
# File 'lib/aspera/environment.rb', line 98

def log_spawn(env:, exec:, args:)
  [
    'execute:'.red,
    env.map{|k, v| "#{k}=#{Shellwords.shellescape(v)}"},
    Shellwords.shellescape(exec),
    args.map{|a|Shellwords.shellescape(a)}
  ].flatten.join(' ')
end

.open_editor(file_path) ⇒ Object

open a file in an editor

[View source]

194
195
196
197
198
199
200
201
202
# File 'lib/aspera/environment.rb', line 194

def open_editor(file_path)
  if ENV.key?('EDITOR')
    system(ENV['EDITOR'], file_path.to_s)
  elsif Environment.os.eql?(Environment::OS_WINDOWS)
    system('notepad.exe', %Q{"#{file_path}"})
  else
    open_uri_graphical(file_path.to_s)
  end
end

.open_uri_graphical(uri) ⇒ Object

open a URI in a graphical browser command must be non blocking

[View source]

183
184
185
186
187
188
189
190
191
# File 'lib/aspera/environment.rb', line 183

def open_uri_graphical(uri)
  case Environment.os
  when Environment::OS_MACOS then return system('open', uri.to_s)
  when Environment::OS_WINDOWS then return system('start', 'explorer', %Q{"#{uri}"})
  when Environment::OS_LINUX   then return system('xdg-open', uri.to_s)
  else
    raise "no graphical open method for #{Environment.os}"
  end
end

.osObject

[View source]

39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/aspera/environment.rb', line 39

def os
  case RbConfig::CONFIG['host_os']
  when /mswin/, /msys/, /mingw/, /cygwin/, /bccwin/, /wince/, /emc/
    return OS_WINDOWS
  when /darwin/, /mac os/
    return OS_MACOS
  when /linux/
    return OS_LINUX
  when /aix/
    return OS_AIX
  else Aspera.error_unexpected_value(RbConfig::CONFIG['host_os']){'host_os'}
  end
end

.restrict_file_access(path, mode: nil) ⇒ Object

restrict access to a file or folder to user only

[View source]

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/aspera/environment.rb', line 151

def restrict_file_access(path, mode: nil)
  if mode.nil?
    # or FileUtils ?
    if File.file?(path)
      mode = 0o600
    elsif File.directory?(path)
      mode = 0o700
    else
      Log.log.debug{"No restriction can be set for #{path}"}
    end
  end
  File.chmod(mode, path) unless mode.nil?
rescue => e
  Log.log.warn(e.message)
end

.ruby_versionObject

[View source]

35
36
37
# File 'lib/aspera/environment.rb', line 35

def ruby_version
  return RbConfig::CONFIG['RUBY_PROGRAM_VERSION']
end

.secure_capture(exec:, args: [], **opts) ⇒ Object

Returns stdout of executable or raise expcetion.

Parameters:

  • exec (String)

    path to executable

  • args (Array) (defaults to: [])

    arguments to executable

  • opts (Hash)

    options to capture3

Returns:

  • stdout of executable or raise expcetion

[View source]

121
122
123
124
125
126
127
128
129
130
131
# File 'lib/aspera/environment.rb', line 121

def secure_capture(exec:, args: [], **opts)
  Aspera.assert_type(exec, String)
  Aspera.assert_type(args, Array)
  Aspera.assert_type(opts, Hash)
  Log.log.debug {log_spawn(env: {}, exec: exec, args: args)}
  stdout, stderr, status = Open3.capture3(exec, *args, **opts)
  Log.log.debug{"status=#{status}, stderr=#{stderr}"}
  Log.log.trace1{"stdout=#{stdout}"}
  raise "process failed: #{status.exitstatus} : #{stderr}" unless status.success?
  return stdout
end

.secure_eval(code, file, line) ⇒ Object

secure execution of Ruby code

[View source]

94
95
96
# File 'lib/aspera/environment.rb', line 94

def secure_eval(code, file, line)
  Kernel.send('lave'.reverse, code, empty_binding, file, line)
end

.secure_spawn(exec:, args: [], env: []) ⇒ Object

start process in background, or raise exception caller can call Process.wait on returned value

[View source]

109
110
111
112
113
114
115
# File 'lib/aspera/environment.rb', line 109

def secure_spawn(exec:, args: [], env: [])
  Log.log.debug {log_spawn(env: env, exec: exec, args: args)}
  # start ascp in separate process
  ascp_pid = Process.spawn(env, [exec, exec], *args, close_others: true)
  Log.log.debug{"pid: #{ascp_pid}"}
  return ascp_pid
end

.terminal?Boolean

Returns true if we are in a terminal.

Returns:

  • (Boolean)

    true if we are in a terminal

[View source]

168
169
170
# File 'lib/aspera/environment.rb', line 168

def terminal?
  $stdout.tty?
end

.write_file_restricted(path, force: false, mode: nil) ⇒ Object

Write content to a file, with restricted access

Parameters:

  • path (String)

    the file path

  • force (Boolean) (defaults to: false)

    if true, overwrite the file

  • mode (Integer) (defaults to: nil)

    the file mode (permissions)

[View source]

138
139
140
141
142
143
144
145
146
147
148
# File 'lib/aspera/environment.rb', line 138

def write_file_restricted(path, force: false, mode: nil)
  Aspera.assert(block_given?, exception_class: Aspera::InternalError)
  if force || !File.exist?(path)
    # Windows may give error
    File.unlink(path) rescue nil
    # content provided by block
    File.write(path, yield)
    restrict_file_access(path, mode: mode)
  end
  return path
end

Instance Method Details

#open_uri(the_url) ⇒ Object

Allows a user to open a Url if method is “text”, then URL is displayed on terminal if method is “graphical”, then the URL will be opened with the default browser. this is non blocking

[View source]

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/aspera/environment.rb', line 223

def open_uri(the_url)
  case @url_method
  when :graphical
    self.class.open_uri_graphical(the_url)
  when :text
    case the_url.to_s
    when /^http/
      puts "USER ACTION: please enter this url in a browser:\n#{the_url.to_s.red}\n"
    else
      puts "USER ACTION: open this:\n#{the_url.to_s.red}\n"
    end
  else
    raise StandardError, "unsupported url open method: #{@url_method}"
  end
end

#terminal_supports_unicode?Boolean

Returns:

  • (Boolean)

    true if we can display Unicode characters

[View source]

214
215
216
217
# File 'lib/aspera/environment.rb', line 214

def terminal_supports_unicode?
  @terminal_supports_unicode = self.class.terminal? && %w(LC_ALL LC_CTYPE LANG).any?{|var|ENV[var]&.include?('UTF-8')} if @terminal_supports_unicode.nil?
  return @terminal_supports_unicode
end