my app installer uses the standard open DMG, drag to 'Applications' for installation, but I want to update $PATH so my app can be used from the command line.
I think the right way to do this is to call a script on the first time my application runs that creates a file myapp in /etc/paths.d with the text /Applications/myapp/bin followed by a newline(ascii 13):
rm /etc/paths.d/myapp
echo "/Applications/myapp/bin" > /etc/paths.d/myapp
currently I'm getting errors;
rm: /etc/paths.d/myapp: No such file or directory
./myapp.sh: line 2: /etc/paths.d/myapp: Permission denied
I need to trigger a request for the user to type the admin password but I'm not sure how to do that in a way this clearly inform the user what changes I am making to their system and why. (I can add it to the manual but who reads that)
Any suggestions?
PS I need to do the same on linux(hopefully similar) and Windows, but if I can get MacOS sorted hopefully I'll know where to start.
AuthorizationExecuteWithPrivileges is deprecated for a long time, but still present and working in macOS 11 (Big Sur / 10.16). STPrivilegedTask demonstrates how to call the function in a "safe" manner - that is, to properly handle the case where the function might be removed in a future version of the OS.
Usage is something like this (error checking etc is omitted for brevity). This will create a symlink of your executable in /usr/local/bin with the name "my-app":
AuthorizationRef authorizationRef;
OSStatus err;
const char* tool = "/bin/ln";
char *args[] = {
"-sf",
[[[NSBundle mainBundle] executablePath] UTF8String],
"/usr/local/bin/my-app",
nil
};
AuthorizationCreate(nil, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);
err = AuthorizationExecuteWithPrivileges(authorizationRef, tool, kAuthorizationFlagDefaults, args, nil);
switch (err)
{
case errAuthorizationCanceled:
// user cancelled prompt
break;
case errAuthorizationSuccess:
// success
break;
default:
// an error occurred
break;
}
AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
How you use that, is up to you - you could place it behind a menu item ("Install Command Line Tools") like cmake does. If you want to install this at launch time, I'd suggest prompting the user first (and allowing them the option to "don't ask me again").
Related
I'm new to Golang, and i'm trying out my first CLI application, using the Cobra framework.
My plan is to have few commands, with many flags.
These flags, don't have to have a value attached to them, since they can simply be -r to restart the device.
Currently, i have the following working, but i keep thinking, that this cannot be the correct way to do it.
So any help is appreciated.
The logic is currently, that each command, get's a default value attached to it, and then i look for this, in the run command, and triggers my function, once it captures it.
My "working code" looks like below.
My init function, in the command contains the following.
chargerCmd.Flags().StringP("UpdateFirmware", "u", "", "Updeates the firmware of the charger")
chargerCmd.Flags().Lookup("UpdateFirmware").NoOptDefVal = "yes"
chargerCmd.Flags().StringP("reboot", "r", "", "Reboots the charger")
chargerCmd.Flags().Lookup("reboot").NoOptDefVal = "yes"
And the run section looks like this.
Run: func(cmd *cobra.Command, args []string) {
input, _ := cmd.Flags().GetString("UpdateFirmware")
if input == "yes" {
fmt.Println("Updating firmware")
UpdateFirmware(os.Getenv("Test"), os.Getenv("Test2"))
}
input, _ = cmd.Flags().GetString("reboot")
if input == "yes" {
fmt.Println("Rebooting Charger")
}
},
Maybe to make the usage a bit cleaner, as stated in the comment from Burak - you can better differentiate between commands and flags. With cobra you have the root command and sub-commands attached to the root command. Additionaly each command can accept flags.
In your case, charger is the root commands and you want two sub-commands: update_firmware and reboot.
So as an example to reboot the charger, you would execute the command:
$ charger reboot
In the code above, you are trying to define sub-commands as flags, which is possible, but likely not good practice.
Instead, the project should be set-up something like this: https://github.com/hesamchobanlou/stackoverflow/tree/main/74934087
You can then move the UpdateFirmware(...) operation within the respective command definition under cmd/update_firmware.go instead of trying to check each flag variation on the root chargerCmd.
If that does not help, provide some more details on why you think your approach might not be correct?
Edit:
Here is a link to the actual Xcode Test Project:
https://github.com/briggsm/StdErrTest
With this, I think it will be very easy to reproduce & see this issue.
Original Post:
I have an executable (chmod +x) bash script (myScript.sh) like so:
#!/bin/sh
# auto login disabled
ald=$(defaults read /Library/Preferences/com.apple.loginwindow autoLoginUser 2>&1)
echo "ald: $ald"
Command-line Works
If I run this script from the command line, it works perfectly:
$ ./myScript.sh
ald: 2016-11-25 17:13:14.467 defaults[26645:619328]
The domain/default pair of (/Library/Preferences/com.apple.loginwindow, autoLoginUser) does not exist
The output came from stderr.
Xcode/Swift - Process/NSTask Does Not Work
But if I run this same script from an App in Xcode, using Swift 3 and the Process class, it does not work - the stderr does not seem to be getting redirected to stdout, and the variable (ald) is just an empty string.
For reference, here is my ViewController.swift code:
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func runProcessClicked(_ sender: NSButton) {
// Make sure we can find the script file. Return if not.
guard let path = Bundle.main.path(forResource: "myScript", ofType:".sh") else {
print("\n Unable to locate: myScript.sh!")
return
}
// Init outputPipe
let outputPipe = Pipe()
// Setup & Launch our process
let ps: Process = Process()
ps.launchPath = path
//ps.arguments = arguments
ps.standardOutput = outputPipe
ps.launch()
ps.waitUntilExit()
// Read everything the outputPipe captured from stdout
let data = outputPipe.fileHandleForReading.readDataToEndOfFile()
var outputString = String(data: data, encoding: String.Encoding.utf8) ?? ""
outputString = outputString.trimmingCharacters(in: .whitespacesAndNewlines)
// Return the output
print("[output: \(outputString)]")
}
}
After clicking the button, the output I see is:
[output: ald:]
If myScript.sh runs a command which simply outputs to stdout, that value DOES get stored in ald and I can see it both from the command-line AND Xcode.
So it appears to me that the issue is that stderr is not getting redirected to stdout in the context of a process/task being run via Swift.
Can anybody shed some light on this issue, and help me solve this problem? Thank you so much for trying!
Hmm, maybe not so much an "answer" as a "discovery ", but I'll post here in case others find it useful.
If Xcode App is run in Debug mode, it does not work. But if I "Archive" the App and export it to a "macOS App", it DOES work!
Maybe has something to do with App's sandbox environment in the Release version vs the Debug version. I wouldn't think so but apparently it does.
I'd still love to know why it doesn't work in Xcode (in Debug mode) though, because debugging within Xcode is obviously very useful!
Any additional thoughts would be greatly appreciated!
I don't know if this helps, but I ran into a similar issue trying to re-direct stderror to a NSTask my program is launching (while debugging). I found that if I edit the Scheme and under the Options tab change the Console setting from 'Use Xcode' to 'Use Terminal' I am able to successfully redirect stderror and register for notifications while in the debugger.
I am trying to write a script-like D program, that would have different behaviour based on availability of certain tools on user's system.
I'd like to test if a given program is available from command line (in this case it is unison-gtk) or if it is installed (I care only about Ubuntu systems, which use apt)
For the record, there is a walk around using e.g. tryRun:
bool checkIfUnisonGTK()
{
import scriptlike;
return = tryRun("unison-gtk -version")==0;
}
Instead of tryRun, I propose you grab the PATH environment variable, parse it (it is trivial to parse it), and look for specific executable inside those directories:
module which1;
import std.process; // environment
import std.algorithm; // splitter
import std.file; // exists
import std.stdio;
/**
* Use this function to find out whether given executable exists or not.
* It behaves like the `which` command in Linux shell.
* If executable is found, it will return absolute path to it, or an empty string.
*/
string which(string executableName) {
string res = "";
auto path = environment["PATH"];
auto dirs = splitter(path, ":");
foreach (dir; dirs) {
auto tmpPath = dir ~ "/" ~ executableName;
if (exists(tmpPath)) {
return tmpPath;
}
}
return res;
} // which() function
int main(string[] args) {
writeln(which("wget")); // output: /usr/bin/wget
writeln(which("non-existent")); // output:
return 0;
}
A natural improvement to the which() function is to check whether tmpPath is an executable, or not, and return only when it found an executable with given name...
There can't be any «native D solution» because you are trying to detect something in the system environment, not inside your program itself. So no solution will be «native».
By the way, if you are really concerned about Ubuntu only, you can parse output of command dpkg --status unison-gtk. But for me it prints that package 'unison-gtk' is not installed and no information is available (I suppose that I don't have enabled some repo that you have). So I think that C1sc0's answer is the most universal one: you should try to run which unison-gtk (or whatever the command you want to run is) and check if it prints anything. This way will work even if user has installed unison-gtk from anywhere else than a repository, e.g. has built it from source or copied a binary directly into /usr/bin, etc.
Linux command to list all available commands and aliases
In short: run auto r = std.process.executeShell("compgen -c"). Each line in r.output is an available command. Requires bash to be installed.
man which
man whereis
man find
man locate
I am trying to delete a text file in haskell while working in winhugs with help of removeFile function.But it is giving an error that
Program error: price.txt: Directory.removeFile: permission denied
What can be the reason?
According to the Hackage Docs for removeFile, the operation may fail with:
isPermissionError / PermissionDenied The process has insufficient privileges to perform the operation. [EROFS, EACCES, EPERM]
Also, according to the source code there, removeFile is just a thin wrapper around deleteFile in the Win32 API:
removeFile :: FilePath -> IO ()
removeFile path =
#if mingw32_HOST_OS
Win32.deleteFile path
#else
Posix.removeLink path
#endif
Update
After digging around the source code for winhugs, it seems the Windows API function unlink is actually being used to delete a file in Hugs:
primFun(primRemoveFile) { /* remove a file */
int rc;
String s = evalName(IOArg(1));
if (!s) {
IOFail(mkIOError(NULL,
nameIllegal,
"Directory.removeFile",
"illegal file name",
&IOArg(1)));
}
rc = unlink(s);
if (rc != 0)
throwErrno("Directory.removeFile", TRUE, NO_HANDLE, &IOArg(1));
IOReturn(nameUnit);
}
In any case, the previous answer is going to hold up in the sense that any permissions constraint is not introduced by Haskell. Rather, any permissions error would be due to the underlying OS environment (user accounts, open files, permissions, etc).
i am trying to run a code in c++ which will result in an .exe file running at startup using registry...but the problem is that the code results fails without showing any errors...i compiled the code in devcpp...
the code is
void createkey(char *path)
{
int reg;
HKEY hkey,Hkey1;
DWORD ptr;
reg=RegOpenKeyEx(HKEY_LOCAL_MACHINE,TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"),0,KEY_SET_VALUE,&hkey);
if(reg=ERROR_SUCCESS)
cout<<"success"<<endl;
else
cout<<"failure"; //(a)
cout<<reg<<endl; //(b)
if(reg==0)
{
RegSetValueEx(hkey,TEXT("key"),0,REG_SZ,(BYTE*)path,strlen(path));
}
}
in the command line failure and 0 got printed as a result of (a) and (b)...(dont know how as the two mean completely opposite things )....the char *path passed to regsetvalueex was "c:/Dev-Cpp/bin/Untitled2.exe"...i am sure that the functions are not working as key doesnt appear in run key(i checked using regedit)...
if(reg=ERROR_SUCCESS)
That's an assignment, you need to use the == operator. Most modern compilers warn about this, be sure to update yours. You probably got an access denied error, can't write to HKLM\Software without elevation.
Standard users don't have write access to HKLM. You need to run this process elevated.