Reputation: 1612
I am working on a Terminal application that allows people to execute bash commands from a Swing GUI. I run into the following problem when trying to execute a command with sudo:
sudo cd /Users/{myname}/Desktop
sudo: no tty present and no askpass program specified
Here's my code:
package me.nrubin29.jterminal;
import javax.swing.*;
import javax.swing.filechooser.FileSystemView;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.*;
import java.util.ArrayList;
public class JTerminal extends JFrame {
private JTextPane area = new JTextPane();
private JTextField input = new JTextField("Input");
private SimpleAttributeSet inputSAS = new SimpleAttributeSet(), output = new SimpleAttributeSet(), error = new SimpleAttributeSet();
private File workingFolder = FileSystemView.getFileSystemView().getDefaultDirectory();
public JTerminal() throws IOException {
super("JTerminal");
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
StyleConstants.setForeground(inputSAS, Color.GREEN);
StyleConstants.setBackground(inputSAS, Color.BLACK);
StyleConstants.setForeground(output, Color.WHITE);
StyleConstants.setBackground(output, Color.BLACK);
StyleConstants.setForeground(error, Color.RED);
StyleConstants.setBackground(error, Color.BLACK);
input.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
try {
String command = input.getText();
if (command.equals("")) return;
setTitle("JTerminal (" + command.split(" ")[0] + ")");
input.setText("");
input.setEditable(false);
write(inputSAS, command);
Process bash = new ProcessBuilder("bash").directory(workingFolder).start();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(bash.getOutputStream());
outputStreamWriter.write(command);
outputStreamWriter.close();
int code = bash.waitFor();
writeStream(bash.getErrorStream(), error);
writeStream(bash.getInputStream(), output);
input.setEditable(true);
setTitle("JTerminal");
if (code == 0 && command.split(" ").length > 1) workingFolder = new File(command.split(" ")[1]);
} catch (Exception ex) { error(ex); }
}
}
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
});
area.setBackground(Color.black);
area.setCaretColor(Color.green);
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14));
area.setEditable(false);
JScrollPane pane = new JScrollPane(area);
pane.setBorder(BorderFactory.createLineBorder(Color.GREEN));
pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
pane.setPreferredSize(new Dimension(640, 460));
input.setBackground(Color.black);
input.setForeground(Color.green);
input.setCaretColor(Color.green);
input.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14));
input.setBorder(BorderFactory.createLineBorder(Color.GREEN));
add(pane);
add(input);
Dimension DIM = new Dimension(640, 480);
setPreferredSize(DIM);
setSize(DIM);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setResizable(true);
pack();
setVisible(true);
input.requestFocus();
}
public static void main(String[] args) throws IOException {
new JTerminal();
}
private void write(SimpleAttributeSet attributeSet, String... lines) {
try {
if (lines.length == 0) return;
for (String line : lines) {
area.getStyledDocument().insertString(area.getStyledDocument().getLength(), line + "\n", attributeSet);
}
area.getStyledDocument().insertString(area.getStyledDocument().getLength(), "\n", attributeSet);
}
catch (Exception e) { error(e); }
}
private void error(Exception e) {
write(error, "An error has occured: " + e.getLocalizedMessage());
e.printStackTrace(); //TODO: temp.
}
private void writeStream(InputStream s, SimpleAttributeSet color) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(s));
ArrayList<String> strs = new ArrayList<String>();
while(reader.ready()) strs.add(reader.readLine());
if (strs.size() > 0) write(color, strs.toArray(new String[strs.size()]));
}
catch (Exception e) { error(e); }
}
}
Upvotes: 2
Views: 6425
Reputation: 976
I writed simple api: package me.barwnikk.library.linuxcommandroot;
import java.awt.BorderLayout;
import java.io.IOException;
import java.io.InputStream;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
public class LinuxCommand {
static InputStream is;
static byte[] buff = new byte[8192];
static int n;
public static String getPasswdForRoot() throws IOException {
Process p = Runtime.getRuntime().exec(new String[]{"sh","-c","sudo -S id"});
is = p.getErrorStream();
n = is.read(buff, 0, 8192);
String text = new String(buff,0,n);
if(text.contains("root"))return null; //not set password
JPanel panel = new JPanel(new BorderLayout());
JLabel lab = new JLabel(text);
panel.add(lab,BorderLayout.NORTH);
JPasswordField password = new JPasswordField();
panel.add(password,BorderLayout.SOUTH);
JOptionPane.showMessageDialog(null, panel);
byte[] passwd = (new String(password.getPassword())+"\r\n").getBytes();
p.getOutputStream().write(passwd);
p.getOutputStream().flush();
n = is.read(buff, 0, 8192);
if(n==-1) return new String(password.getPassword());
text = new String(buff,0,n);
while(true) {
lab.setText(text);
JOptionPane.showMessageDialog(null, panel);
p = Runtime.getRuntime().exec(new String[]{"sh","-c","sudo -S id"});
is = p.getErrorStream();
n = is.read(buff, 0, 8192);
passwd = (new String(password.getPassword())+"\n").getBytes();
p.getOutputStream().write(passwd);
p.getOutputStream().flush();
n = is.read(buff, 0, 8192);
if(n==-1) return new String(password.getPassword());
text = new String(buff,0,n);
}
}
public static Process runFromRoot(String command, String password) throws IOException {
byte[] passwd = (password+"\n").getBytes(); //for OutputStream better is byte[]
Process p = Runtime.getRuntime().exec(new String[]{"sh","-c","sudo -S "+command});
p.getOutputStream().write(passwd);
p.getOutputStream().flush();
return p;
}
}
It's a mini api to get root password (user must write correct). Sample of usage:
public static void main(String[] args) throws IOException, InterruptedException {
String password = LinuxCommand.getPasswdForRoot();
System.out.println("stdout of 'id':");
Process p = LinuxCommand.runFromRoot("id",password);
System.out.print(streamToString(p.getInputStream()));
System.out.println("stdout of 'fdisk -l':");
p = LinuxCommand.runFromRoot("fdisk -l",password);
System.out.print(streamToString(p.getInputStream()));
}
Method streamToString:
public static String streamToString(InputStream stream) {
String read = "";
try {
while((n=stream.read(buff, 0, 8192))!=-1) {
read+=new String(buff,0,n);
}
} catch (IOException e) {
e.printStackTrace();
}
return read;
}
Sample return in my test (in polish):
stdout of 'id':
uid=0(root) gid=0(root) grupy=0(root)
stdout of 'fdisk -l':
Disk /dev/sda: 640.1 GB, 640135028736 bytes
głowic: 255, sektorów/ścieżkę: 63, cylindrów: 77825, w sumie sektorów: 1250263728
Jednostka = sektorów, czyli 1 * 512 = 512 bajtów
Rozmiar sektora (logiczny/fizyczny) w bajtach: 512 / 4096
Rozmiar we/wy (minimalny/optymalny) w bajtach: 4096 / 4096
Identyfikator dysku: 0xc56b9eef
Urządzenie Rozruch Początek Koniec Bloków ID System
/dev/sda1 2048 37064703 18531328 27 Hidden NTFS WinRE
/dev/sda2 * 37064704 37269503 102400 7 HPFS/NTFS/exFAT
/dev/sda3 37269504 456711884 209721190+ 7 HPFS/NTFS/exFAT
/dev/sda4 456711946 1250258624 396773339+ f W95 Rozsz. (LBA)
Partycja 4 nie zaczyna się na granicy bloku fizycznego.
/dev/sda5 456711948 810350729 176819391 7 HPFS/NTFS/exFAT
Partycja 5 nie zaczyna się na granicy bloku fizycznego.
/dev/sda6 810350793 862802954 26226081 7 HPFS/NTFS/exFAT
Partycja 6 nie zaczyna się na granicy bloku fizycznego.
/dev/sda7 862803018 1020078408 78637695+ 83 Linux
Partycja 7 nie zaczyna się na granicy bloku fizycznego.
/dev/sda8 1020079368 1229791814 104856223+ 7 HPFS/NTFS/exFAT
/dev/sda9 1229791878 1250258624 10233373+ 7 HPFS/NTFS/exFAT
Partycja 9 nie zaczyna się na granicy bloku fizycznego.
This api create and write to Process password. So, you can run command with permission sudo.
P.S. I test your program. And there are one bug: you must add terminate - I must destroy and run. Write in console "ping google.com" - program doesn't response. You must create new thread.
Upvotes: 1
Reputation: 223193
Since this is a Swing application, there is indeed no terminal (tty) present, even if you're using JTerminal (which looks like a terminal but doesn't actually take over your tty). You will instead need to set up an askpass
program (as the error message says), which will prompt the user for a password.
To set up an askpass
program, you can either set the SUDO_ASKPASS
environment variable, or else set it up in sudoers
using Path askpass ...
. See the manual page for more details.
Alternatively, if your password isn't very secret and you don't mind seeing it echoed on screen, run sudo with the -S
option.
Upvotes: 4