I need to use Start-Process in an exe to call itself so that when called part of the app has elevated permissions. I was using env::current_exe().unwrap(); the problem is that for whatever reason Command is not unescaping the file path when passed to .args(&[...]) Ive found raw_args https://doc.rust-lang.org/std/os/windows/process/trait.CommandExt.html#tymethod.raw_arg in the docs but cant figure out how to call it and don't think it will solve the issue, though could be wrong.
The expected command must look like Start-Process 'C:\path\from\env::current_exe().unwrap()' -Verb RunAs -ArgumentList 'arg1' 'arg2'
Any ideas?
It appears that raw_args :
This trait is sealed: it cannot be implemented outside the standard
library. This is so that future additional methods are not breaking
changes.
Any other ideas??
roughly :
fn call_self(){
let file_path = env::current_exe().unwrap();
let path_str = file_path.as_str().unwrap();
let _ = Command::new("Start-Process")
.args(&[path_str, "-Verb", "RunAs"])
.spawn()
.expect("failed to execute process");
}
call with raw using cargo build
fn call_self() {
use std::os::windows::process::CommandExt;
use std::ffi::OsStr;
// keep getting wrong path so trying this but fails
let mut os_str = OsStr::new("'.\\app.exe' -Verb RunAs");
let mut command = Command::new("Start-Service");
let mut command = CommandExt::raw_arg(&mut command, &mut os_str).spawn().unwrap();
}
Related
I consistently get the following error on Windows (both local and inside Github Actions). I do not get this error on macOS and Linux.
Error: Custom { kind: Other, error: PathError { path: "C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\.tmpi45reT", err: Os { code: 32, kind: Other, message: "The process cannot access the file because it is being used by another process." } } }
The code is basically creating a temporary dir with tempfile crate and then doing a bunch of File::creates in that directory (and some other, probably irrelevant stuff). After all writes each file is explicitly dropped and the dir handle is explicitly closed. Here's the rough version of the code:
Rust playground
use fs::File;
use std::env;
use std::fs;
use std::io::Write as _;
use tempfile::tempdir;
fn main() -> std::io::Result<()> {
let dir = tempdir()?;
let dir_path = dir.path();
let pkg_path = dir_path.join("package.json");
let mut pkg_json = File::create(pkg_path)?;
pkg_json.write_all(
r#"
{
"name": "testpackage"
}
"#
.as_bytes(),
)?;
let _a = File::create(dir_path.join("yarn.lock"))?;
let _b = File::create(dir_path.join(".gitignore"))?;
env::set_current_dir(&dir)?;
drop(pkg_json);
drop(_a);
drop(_b);
dir.close()?;
Ok(())
}
This is a part of integration tests, where each test does something similar to the code above.
What could be causing this error?
I found that the reason was not changing the current directory back to where it was after manually setting it to the tmp directory.
I want to write a simple app that puts the result of the command
/usr/bin/strings myfile
into a label in the normal MacOS interface. I am receiving this error
xcrun: error: cannot be used within an App Sandbox.
I tried to use the info here:
https://forums.developer.apple.com/thread/73554
and here:
ANY possible way to run the clang compiler from a Sandboxed app?
The actual piece of code invoking the instruction is:
let path = "/usr/bin/strings"
let arguments = ["/path/to/my/file"]
let task = Process()
task.arguments = arguments
task.executableURL = URL(fileURLWithPath: path)
let outputPipe = Pipe()
let errorPipe = Pipe()
task.standardOutput = outputPipe
task.standardError = errorPipe
do {
try task.run()
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
let output = String(decoding: outputData, as: UTF8.self)
let error = String(decoding: errorData, as: UTF8.self)
testo.stringValue="OUTPUT: \(output)\nERROR: \(error)"
} catch {
lbl.stringValue="Error somewhere"
}
Anyone does know how to solve this?
The problem is not the command (/usr/bin/strings) but the access to the filesystem. In fact, replacing the first two lines with:
let path = "/sbin/ping"
let arguments = ["-c", "1", "www.google.com"]
for instance, and allowing for outbound connections, would work fine. Hence, it is important that the application is allowed to access the path of the file to be processed, by means of the Sandbox.
I have a sub-app in my main Swift app. I made it so it's copied automatically in the Resources folder of the main app when building it. That way, I want to be able to launch an instance of the sub-app from the main app.
The thing is, I'm having an error that is hard to debug/find answers about.
Here is my code :
let args = ["--args", "-admin_url", site.url, "-login", site.login, "-pass", site.password]
let helperPath = (NSBundle.mainBundle().pathForResource("App Helper", ofType: "app"))!
let task = NSTask.init()
task.launchPath = helperPath
task.arguments = args
task.launch()
And the error :
[56490:7218926] Couldn't posix_spawn: error 13
I have no idea where to look, what to search for. I don't know what I'm doing wrong.
I'm wondering if the issue is related to the sub-app itself. That sub-app is empty for now. I set Application is Agent to YES. And in MainMenu.xib, I set the Visible at launch option to no.
That sub-app needs to do some work in the background and doesn't need any UI at all.
Thanks !
Don't use NSTask for this, use NSWorkspace:
let helperAppURL = NSBundle.mainBundle().URLForResource("App Helper",
withExtension:"app")!
_ = try? NSWorkspace.sharedWorkspace().openURL(helperAppURL,
options:[.Default],
configuration:[NSWorkspaceLaunchConfigurationArguments :
["--args", "-admin_url", site.url, "-login",
site.login, "-pass", site.password]])
In the above code, for brevity, I ignored the result of the openURL() command, but in reality it can return an instance of NSRunningApplication which represents the task.
To keep track of the instances of your helper app you launch, you could keep references to this NSRunningApplication in an appropriate kind of collection class, and when the time comes, call its terminate() method.
the launch() function is deprecated, using run()
func shell(_ command: String) -> String {
let task = Process()
task.launchPath = "/usr/bin/"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
if #available(macOS 10.13, *) {
try? task.run()
} else {
task.launch()
}
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
return output
}
or using swift-commands
import Commands
Commands.Bash.run("say hello")
I'm working on an alternative to the wireless network organization and diagnostics utilities provided by Apple. So far it's going fairly well. I'm able to pull and store the current network, some network details, password, and location data and store it in an encrypted realm.
The area I'm running into issues with is being able to access a list of the networks that are currently in range of the computer. I know this can be accessed by using the "airport -s" command from the terminal. My initial plan was to call this command from an NSTask and parse and store the results. However, after hours of tinkering and googling and coming up empty, I finally came upon Objective-C: NSCommand "airport -s" returning empty which seems to reflect what I'm seeing.
I've to get around this by writing a quick bash script to store in my bundle resources and call from an NSTask and return the output, or even to call a script that will write the output from "airport -s" to a .txt file in the bundle resources... but these all seem to fail when initiated from an NSTask even though they all run fine from the terminal.
func runCommand(cmd : String, args : String...) -> (output: [String], error: [String], exitCode: Int32) {
var output : [String] = []
var error : [String] = []
let task = NSTask()
task.launchPath = cmd
task.arguments = args
let outpipe = NSPipe()
task.standardOutput = outpipe
let errpipe = NSPipe()
task.standardError = errpipe
task.launch()
let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String.fromCString(UnsafePointer(outdata.bytes)) {
string = string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
output = string.componentsSeparatedByString("\n")
}
let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String.fromCString(UnsafePointer(errdata.bytes)) {
string = string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
error = string.componentsSeparatedByString("\n")
}
task.waitUntilExit()
let status = task.terminationStatus
return (output, error, status)
}
Is the method I'm using elsewhere to utilize NSTask. I'm attempting get a list of nearby networks with the following code:
func findNearby() -> String {
var checkNearby = sb.runCommand("/bin/sh", args: "-c", "sh '/Users/<username>/Library/Containers/com.user.app/Data/Library/Application Support/getnearby.sh'")
if checkNearby.output != [] {
return checkNearby.output[0]
}
return "could not access airport"
}
Is there an issue with my implementation of NSTask that is causing these scripts to time out, or is Apple really going through all the trouble to stop me from calling "airport -s" from NSTask?
Are there any alternatives to "airport -s" I could try?
Note: I know I'm not really storing the .sh file in my Resources, this location was just temporary while I tried to figure out a solution.
I'm using D as a scripting language for Windows 7 console stuff to automate boring tasks. One of my scripts (open.exe) is supposed to allow me to open stuff from the command line without me having to specify which program I use (I have a configuration file with this stuff). Now, I use executeShell to do this, and call something like start [name of program I want to use] [name of input file]. If I do this directly from the shell, it returns immediately, but if I do it using my D script, it doesn't return until the program that it opens is closed. What should I do to allow it to return immediately?
For reference purposes, this is the business logic of my script (the main method just does some argument parsing for piping purposes):
immutable path = "some//path//going//to//config//file.conf";
void process(string input) {
string extension = split(input,".")[1]; //get file extension from input
auto config = File(path,"r"); auto found = false;
while (!config.eof()){
auto line = chomp(config.readln());
if (line[0]!='#') { //skip comment lines
auto divided = split(line, ":");
if (divided[0] == extension) {
found = true;
auto command = "start " ~ divided[1] ~ " " ~ input;
auto result = executeShell(command);
//test for error code and output if necessary
writeln(result.output);
}
}
}
if (!found)
writeln("ERROR: Don't know how to open " ~ input);
}
From the top of the std.process documentation:
Execute and wait for completion, collect output - executeShell
The Windows start program spawns a process and exits immediately. D's executeShell does something else. If you'd like to spawn another program, use the appropriate functions: spawnProcess or spawnShell.