Sudo在Java过程中

时间:2013-08-27 23:09:39

标签: java swing bash process sudo

我正在开发一个终端应用程序,允许人们从Swing GUI执行bash命令。尝试使用sudo执行命令时遇到以下问题:

  

sudo cd / Users / {myname} / Desktop

     

sudo:没有tty存在且没有指定askpass程序

这是我的代码:

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); }
    }
}

2 个答案:

答案 0 :(得分:3)

由于这是一个Swing应用程序,确实没有终端(tty)存在,即使你正在使用JTerminal(看起来像一个终端但实际上没有接管你的tty) 。您将需要设置askpass程序(如错误消息所示),这将提示用户输入密码。

要设置askpass计划,您可以设置SUDO_ASKPASS环境变量,也可以使用sudoersPath askpass ...中设置它。有关详细信息,请参阅manual page

或者,如果您的密码不是很秘密,并且您不介意在屏幕上看到它,请使用-S选项运行sudo。

答案 1 :(得分:1)

我写了简单的api:     包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;
    }
}

获取root密码的迷你API(用户必须写正确)。用法示例:

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()));
}

方法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;
}

我的测试中的样品返回(抛光):

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.

这个api创建并写入Process密码。因此,您可以使用权限sudo运行命令。

P.S。我测试你的程序。并且有一个错误:你必须添加终止 - 我必须销毁并运行。 在控制台中写字&#34; ping google.com&#34; - 程序没有响应。您必须创建新主题。