tldr; I need to run a command as admin and retrieve the output
I've already created a bat file that does the job but I want to translate it to pure ruby as a learning exercise.
Using backticks I can get the output from the command, unfortunately the output tells me I lack the user privileges. Through this site I've found how to poke UAC in the right way, but I can't seem to get it to recognise the command I want which is (bcdedit /v)
I've read through https://learn.microsoft.com/en-us/windows/desktop/shell/shell-shellexecute & Detect if running with administrator privileges under Windows XP
and numerous stack overflow posts with similar questions but haven't found clear guidelines to do this.
def running_in_admin_mode?
(`reg query HKU\\S-1-5-19 2>&1` =~ /ERROR/).nil?
end
unless running_in_admin_mode?
shell = WIN32OLE.new('Shell.Application')
shell.ShellExecute("ruby", File.expand_path(__FILE__), nil, 'runas')
exit
end
update: so this sort of works. It flashes up the output I want in an IRB shell, but opens infinite new shells which locks up the computer.
require 'win32ole'
shell = WIN32OLE.new('Shell.Application')
shell.ShellExecute("ruby", File.expand_path(__FILE__), nil, 'runas')
response = `bcdedit /v`
p response
exit
Update: giving up doing it for now. Instead just checking the responses. Code is not completely functional yet, but here is where I am incase someone wants to pitch in.
require 'win32/registry'
require 'win32ole'
p 'Device Guard v0.1'
p 'Checking for requisite permissions'
def disable
p 'Would you like me to disable Docker and enable VMWare? [Yes] or [No]'
case (gets.downcase.chomp)
when 'yes'
puts "Disabling docker"
`bcdedit /set hypervisorlaunchtype off` # seems to work ok
Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\Microsoft\Windows\CurrentVersion\Run') do |reg|
value = reg['Docker for Windows'] # need to save the location of the executable for next time
reg.delete_value('Docker for Windows') # Access denied even when run as admin :()
end
when 'no'
recheck
else
puts 'Instructions unclear'
disable
end
end
def enable
p 'Would you like me to disable VMWare and enable Docker? [Yes] or [No]'
case (gets.downcase.chomp)
when 'yes'
Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\Microsoft\Windows\CurrentVersion\Run') do |reg|
docker_location = reg['Docker for Windows']
p docker_location
`bcdedit /set hypervisorlaunchtype Auto`
end
when 'no'
recheck
else
puts "Instructions unclear"
enable
end
end
def recheck
p 'Well what do you want me to do then? [Recheck] or [Exit]'
case (gets.downcase.chomp)
when 'recheck'
startup
when 'exit'
puts "bye then"
exit
else
puts "Instructions unclear"
recheck
end
end
def check_hypervisor(response)
edited_response = response.match('hypervisorlaunchtype(.*)')[1]
if edited_response.end_with?(" Off")
p 'Hypervisor Launch Type is disabled'
p 'This means VMWare should run and Docker is disabled'
enable
elsif edited_response.end_with?(" Auto")
p 'Hypervisor Launch Type is enabled'
p 'This means that Docker should run and VMWare is disabled'
disable
end
end
def startup
response = `bcdedit /v`
unless response.include?("Windows Boot Manager")
no_access
end
unless response.include?("Access is denied")
check_hypervisor(response)
end
end
def no_access
p 'I am not running as admin. Please run me from a shell with admin privilages'
exit
end
startup
Related
I'm trying to create an asynchronous connection to a device that broadcasts packets when something changes, like a door is opened. I want to process the feedback in realtime to the control system. I also need to try to do this with the standard library due to the limitations on some controllers.
Right now I've been using cURL inside ruby to keep a connection open and reconnect if it disconnects. That has worked fine, but on macOS Big Sur after a few days terminal stops working due to the requests. I have not been able to figure out why.
I've rewritten most of my script to use net/http instead of cURL, but I can't figure out keeping a connection open and then real-time sending data to another function.
cURL Ruby Code:
def httpBcast(cmd, url, header,valuesListID,valuesListFID)
fullCommand = "curl -s -k -X #{cmd} '#{url}' #{header}"
loop do
begin
PTY.spawn( fullCommand ) do |stdout, stdin, pid|
begin
# Send Output to Savant
stdout.each { |line| parseBcast(line,valuesListID,valuesListFID)}
rescue Errno::EIO
puts "Errno:EIO error, but this probably just means " +
"that the process has finished giving output"
end
end
rescue PTY::ChildExited
puts "The child process exited!"
end
end
end
def parseBcast(msg='',valuesListID,valuesListFID)
if msg.start_with?("data:")
msg.gsub!(/\n+/, '')
msg.gsub!(/\s+/, "")
msg.gsub!("data:","")
msg.to_json
msgP = JSON.parse(msg)
if valuesListFID.include?(msgP['result']['deviceId'])
id = valuesListFID.index(msgP['result']['deviceId'])
id +=1
else
id = "Device Doesn't Exist"
end
msgP['result']['deviceId'] = id
send_savant msgP.to_json
end
end
Any guidance anyone can offer would be most appreciated.
Turns out the vendor needed to make a firmware update. All Good Now.
In my vagrantfile I use two lines of rudy code to get the username and password from the console and pass it on to the shell script for creating an additional user account instead of the default vagrant
This works well for vagrant up but it beats me by prompting the same when I run any other vagrant commands like vagrant status. Is there a way to catch if the vagrant file is called with up command?
require 'io/console'
print "Please enter username: "
#username = STDIN.gets
print "Please enter password: "
#password = STDIN.noecho(&:gets)
Vagrant.configure("2") do |config|
config.vm.box = "generic/debian10"
["Node 01"].each do |node_name|
config.vm.define node_name do |node|
node.vm.provision "shell" do |p|
p.args = [#username, #password]
p.path = "create_user.sh"
end
end
end
end
UPDATE
After #Matt Schuchard's suggestion, I've tried triggers; However, the global variables that are captured inside the trigger block are not shared with the place where I pass them into the shell script (p.args = [#username, #password])
config.trigger.before :up do |trigger|
if #credential_captured == nil # I've used this to prevent executing the following block per each vm
print "Please enter username: "
#username = STDIN.gets
print "Please enter password: "
#password = STDIN.noecho(&:gets)
#credential_captured = true
end
end
Sprinkle Some Plain Ruby into the Vagrantfile
The best practice would really be to put an idempotent provisioning block at the end that picks up your information from the environment or some other fixed location rather than trying to do this as interactive user input within the Vagrantfile. However, if you really want to do it the way you're doing it, just remember that a Vagrantfile is mostly just a DSL on top of Ruby.
You can get the status of a given virtual machine with vagrant status. For a single node, you could just wrap your current prompts at the top in a conditional. For example:
# assuming a single node named "default"
if %x(vagrant status default) =~ /running/
# prompt here
end
If you're running multiple nodes, then I'd suggest wrapping your prompts into a method and moving your conditional down into the body of the shell provisioning block you already have. For example:
def prompt_user
# prompt here
end
# ...
if `vagrant status #{node_name}` =~ running
prompt_user unless #username && #password
node.vm.provision 'shell' do |p|
# provision your node here
end
end
There are likely ways to make this code more efficient, and probably other ways to do what you want, but this approach is fairly straightforward and should at least get you headed in the right direction.
In my recipe I have following resource that restart the computer but I want to apply guard on this resource.
powershell_script "Restart Computer" do
code <<-EOH
$computer_name=$env:COMPUTERNAME
Restart-Computer -ComputerName $computer_name
EOH
guard_interpreter :powershell_script
only_if reboot_pending?
end
I googled and found following ruby script for checking machine required reboot or not
def reboot_pending?
# don't break when used as a mixin in contexts without #node (e.g. specs).
if self.respond_to?(:node, true) && node.run_context.reboot_requested?
true
elsif platform?("windows")
# PendingFileRenameOperations contains pairs (REG_MULTI_SZ) of filenames that cannot be updated
# due to a file being in use (usually a temporary file and a system file)
# \??\c:\temp\test.sys!\??\c:\winnt\system32\test.sys
# http://technet.microsoft.com/en-us/library/cc960241.aspx
registry_value_exists?('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => 'PendingFileRenameOperations' }) ||
# RebootRequired key contains Update IDs with a value of 1 if they require a reboot.
# The existence of RebootRequired alone is sufficient on my Windows 8.1 workstation in Windows Update
registry_key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired') ||
# Vista + Server 2008 and newer may have reboots pending from CBS
registry_key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired') ||
# The mere existence of the UpdateExeVolatile key should indicate a pending restart for certain updates
# http://support.microsoft.com/kb/832475
(registry_key_exists?('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile') &&
!registry_get_values('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').select { |v| v[:name] == "Flags" }[0].nil? &&
[1,2,3].include?(registry_get_values('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').select { |v| v[:name] == "Flags" }[0][:data]))
elsif platform?("ubuntu")
# This should work for Debian as well if update-notifier-common happens to be installed. We need an API for that.
File.exists?('/var/run/reboot-required')
else
false
end
end
I tried to add this method inside guard but its not working.Is there any way to write multiline statement inside chef guard?
Chef guards take in a block, like the first example here. Blocks can be multi line, but can't have methods declared inside of them. If you copy the contents of the method you found and put them inside the only_if that should work.
powershell_script "Restart Computer" do
...
only_if do
<contents of method here>
end
end
While this will work, the cleaner way to implement such an extensive guard is to put the reboot_pending method in a library and call that method from your recipe.
So, I've been working on a Ruby script that blocks reddit during my school hours (useful stuff). Here's the code:
require 'fileutils'
puts "-----------------------------------"
puts "Welcome to the hosts file modifier!"
puts "-----------------------------------"
puts "Option A: Use modified hosts"
puts "Option B: Use original hosts"
puts "Option C: Do nothing"
puts "Please enter your choice: "
input = gets.chomp.downcase
t = Time.now
# Time.now is used is conjunction with function 'original', in option 'b'
def modified
# This function copies the modified (redditblocking) hosts file from Documents to /etc
puts "Moving original hosts file out of /etc"
FileUtils.mv('/etc/hosts', '/Users/(usernameobscured)/Documents/OriginalHosts/hosts')
puts "Done. Now copying modified hosts to /etc"
FileUtils.cp('/Users/(usernameobscured)/Documents/ModifiedHosts/hosts', '/etc/hosts')
puts "Done"
end
def original
# This function deletes the modified hosts file from /etc (since we have a copy in Documents)
# and then moves the original hosts file back to /etc
puts "Deleting modified hosts file from /etc"
FileUtils.rm_rf('etc/hosts')
puts "Done. Now copying original hosts to /etc"
FileUtils.mv('/Users/(usernameobscured)/Documents/OriginalHosts/hosts', '/etc/hosts')
puts "Done"
end
def nothing
# This does... nothing. Literally.
puts "Doing nothing"
end
if input == 'a'
modified
end
if input == 'b'
# Here's when using Time.now becomes helpful: if the hour of the day is less than 5PM,
# then the original hosts file can't be moved back (don't wanna be on reddit during school hours!)
if t.hour > 17
original
elsif t.hour < 17
puts "Too early to use original hosts file. Come back at 5PM"
end
end
if input == 'c'
# Nothing...
nothing
end
As you can see, it moves a modified hosts file from my Documents folder to /etc. The problem I'm having though, as per OS X/Unix security measures, is that I have to run the script via sudo or logged in as root. This is a minor nuisance, however, it's one that I believe can be fixed within the code. How can I get superuser privileges, OR write access to /etc temporarily, via my ruby script, so that I can simply run the script without sudo/root?
Per Unix security model, it is not possible to gain root access without some sort of external intervention (setuid set to the executable, running as root user). Otherwise we would have a gaping security hole.
I am not clear what is exactly your issue of using sudo or rvmsudo or against setting the script setuid (it is possible to configure sudo to not require password for narrowly defined set of commands).
I would just suggest making the various versions of host files group writable by a group that you are member of.
According to this site : http://ruby.about.com/od/rubyversionmanager/qt/Rvm-And-Sudo.htm
you can start executing the script using the rvmsudo command. In your terminal window or shell script:
rvmsudo ruby blockreddit.rb
I want make interactive application where user launches it and can do various task by typing commands (some kind of shell)
example:
./myapp.rb
App says Hi
Commands:
help - display help about command
open - open task
do - do action
Start>help open
open <TaskName>
opens specified task
Start>open Something
Something>do SomeAction
Success!
Something> (blinking cursor here)
I searched but couldn't find any ruby gems that I could use specially for console interaction, so I'm about to my make my own...
I looked at Thor, but that's not exactly as I want, maybe I could use it, but not sure...
it could look something like:
class Tasks
attr_reader :opened_task
desc "open <TaskName>", "opens specified task"
def open(params)
end
desc "do <ActionName>", "do specified action"
def do(params)
end
end
tasks = Tasks.new
# theoretical Console class
console = Console.new
console.addCommand("open",tasks.method(:open),"open task")
console.addCommand("do",tasks.method(:do),"do action")
console.start("%s>",[*tasks.opened_task])
so my question is, what gems I could use to make such console class? maybe someone have already made something similar?
I plan using HighLine for input/output, but any other suggestion what could I use?
What you want is a REPL – Read → Evaluate → Print Loop.
IRB, for example, implements a REPL for the Ruby language.
Here's a very simple implementation of your application's REPL:
loop do
Application::Console.prompt.display
input = gets.chomp
command, *params = input.split /\s/
case command
when /\Ahelp\z/i
puts Application::Console.help_text
when /\Aopen\z/i
Application::Task.open params.first
when /\Ado\z/i
Application::Action.perform *params
else puts 'Invalid command'
end
end
\A and \z match the start of the string and the end of the string, respectively.
You could also try ripl. (from the documentation):
Creating and starting a custom shell is as simple as:
require 'ripl'
# Define plugins, load files, etc...
Ripl.start
There is a comprehensive list of plugins for ripl as well as list of console applications using ripl on the projects website.
ok, so I made this library for creating console applications in ruby. Actually it was some while ago, but only just decided to release it. It does support auto-completion if used with HighLine and Readline.
When I wrote it there wasn't any documentation nor tests/specs, but now I made some. Still not much but for beginning should be ok.
So gem cli-console and
code is at GitHub, here's usage example
TTY is a really good gem for easily doing this sort of things. You have plenty of tools which can work alone or with the full toolKit. You can use colors, prompts, execute shell natives, interact with the screen, print tables, progressbars and many other useful elements of command lines whith the easy of a goop api.
Particularly tty-prompt is really useful for asking for user input.
A brief example for the case you proposed:
require 'tty-prompt'
require 'pastel'
prompt = TTY::Prompt.new
loop do
cmd, parms* = prompt.ask('user#machine$ ').split /\s/
case cmd
when "hola"
puts "Hola amigo " parms
when "exit"
break if prompt.yes?('Do you really want to exit?')
end
end
Take a look at cliqr ruby gem. It looks like exactly what you need. Here is the github link with a descriptive readme: https://github.com/anshulverma/cliqr
It can execute the commands directly or within a inbuilt shell.
Here is a test case from its git repo:
it 'can execute a sub action from shell' do
cli = Cliqr.interface do
name 'my-command'
handler do
puts 'base command executed'
end
action :foo do
handler do
puts 'foo executed'
end
action :bar do
handler do
puts 'bar executed'
end
end
end
end
with_input(['', 'my-command', 'foo', 'foo bar', 'foo bar help']) do
result = cli.execute %w(my-command shell), output: :buffer
expect(result[:stdout]).to eq <<-EOS
Starting shell for command "my-command"
my-command > .
base command executed
my-command > my-command.
base command executed
my-command > foo.
foo executed
my-command > foo bar.
bar executed
my-command > foo bar help.
my-command foo bar
USAGE:
my-command foo bar [actions] [options] [arguments]
Available options:
--help, -h : Get helpful information for action "my-command foo bar" along with its usage information.
Available actions:
[ Type "my-command foo bar help [action-name]" to get more information about that action ]
help -- The help action for command "my-command foo bar" which provides details and usage information on how to use the command.
my-command > exit.
shell exited with code 0
EOS
end
end
class MyAPI
def self.__is__(text)
#__is__ = text
end
def self.method_added(method)
#__help__ ||= {}
#__help__[method.to_s] = #__is__
#__is__ = nil
end
def self.help(of)
#__help__[of]
end
__is__ "open file <file>"
def open(file)
#...
end
__is__ "do X"
def do(*params)
#...
end
__is__ "calls help, use help <command>"
def help(*args, &block)
self.class.help(*args, &block)
end
end
MyAPI.new(...).pry
Or you could use pry commands, but that defeats the
turing-completeness. Help might be implemented using commands, as I'm
not sure how well my approach works out. Those methods need to be
coded defensive. I can't remember how to use class variables :-/