In older RB/RS, I could use the following logic in Linux to run elevated processes (pseudo code …):
theShell.Mode = 0
theShell.Execute "echo " + escPasswd + " | sudo -S /usr/bin/true" // start the sudo timer
theShell.Mode = 2
theShell.Executed "sudo " + someElevatedProcess // sudo clock is running, so no sudo password prompt for this command
Do
theShell.Poll
// Do Something with ReadAll
Loop Until not theShell.IsRunning
Now, under Xojo 15r4, I get prompted when executing the second command - but only in the Xojo shell. Executing the same sequence from an actual terminal works as expected.
Did the Xojo shell backend change, or have I missed a change in Linux sudo and pseudoTTYs? Is there something that I can configure for the Linux shell backend to get the live results again in the Xojo shell?
Sorted this - more code than it used to be (or on OS X).
You need to create an interactive shell session and use Write instead of Execute and then use a thread to keep the UI responsive and handle the shelled task and a Timer to update the output TextArea. Here’s where I am so far:
// Call this from a Thread's Run method
Dim theShell As New Shell
Dim escPasswd As String
Dim Completed As Boolean
escPasswd = BuildUserPassword
theShell.Canonical = True
theShell.Backend = "/bin/bash"
theShell.Mode = 2
Timer1.Period = 25
Timer1.Mode = 2
theShell.Execute "/bin/bash -i" // the -i is important so that the shell attaches to a TTY
theShell.WriteLine "PS1=input:\\ " // Makes it easier to catch end of command since we can't depend
// on IsRunning using a shell in this manner
theShell.WriteLine "sudo -v" // Authenticate and start the sudo clock
Do
theShell.Poll
App.SleepCurrentThread(25)
Loop Until InStr(theShell.Result, "[sudo] password for ") <> 0
Call theShell.ReadAll // throw it away
theShell.WriteLine escPasswd
Do
theShell.Poll
App.SleepCurrentThread(25)
Loop Until InStr(theShell.Result, "input: ") <> 0
theShell.WriteLine "sudo su -" // set the shell to root so the prompts stop
Do
theShell.Poll
App.SleepCurrentThread(25)
Completed = InStr(theShell.Result, "root@") <> 0
Loop Until Completed
Completed = False
theShell.WriteLine "PS1=input:\\ " // repeated since the su - now has a new shell environment
Do
theShell.Poll
App.SleepCurrentThread(25)
Loop Until InStr(theShell.Result, "input: ") <> 0
Call theShell.ReadAll
App.SleepCurrentThread(100)
theShell.WriteLine "tar -czvvf /tmp/tar_test.tgz -b 256 /etc"
Do
theShell.Poll
Completed = InStr(theShell.Result, "input: ") <> 0
App.SleepCurrentThread(25)
bruOut = theShell.ReadAll
Loop Until Completed
App.SleepCurrentThread(250)
Timer1.Mode = 0
theShell.WriteLine "sudo -K"
theShell.WriteLine "exit"
BTW - the use of /etc as the tar content is a proof that the su is working since there are many files in the /etc folder that can’t be read by a normal user.
I seems that the team at Apple have now adopted the requiretty restriction on sudo starting with Sierra. However, as expected with Apple’s low-level changes, there is no documentation about this and the manpage still includes the section"
Therefore, the logic above seems to be the only proven successful way to run a background shelled task as UID root instead of EUID.
If someone else comes up with a more elegant solution, I’d be very excited to spread the news far and wide.