2013-08-27 2 views
2

Я работаю над приложением терминала, которое позволяет людям выполнять команды bash из графического интерфейса Swing. Я бег в следующей проблему при попытке выполнить команду с Sudo:Sudo In Java Process

Суда кд/Users/{MyName}/Desktop

Суд: нет терминала присутствует и не программа 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); } 
    } 
} 
+0

а как насчет gksudo? –

+0

Кстати, 'sudo cd ...' - это бессмысленная команда. 'cd' - это встроенная оболочка, и текущий каталог влияет только на оболочку, в которой он запущен. Вы не можете повышать привилегии вашей оболочки _current_; Unix не работает. –

+0

Я просто хотел протестировать, используя ключевое слово 'sudo'. Любой лучший пример, который не является разрушительным: P – nrubin29

ответ

3

Поскольку это приложение Качелей, там действительно нет терминала (терминал) присутствуют, даже если вы используете JTerminal (который выглядит как терминал, но на самом деле не берет на себя ваш tty). Вместо этого вам нужно будет настроить программу askpass (как сказано в сообщении об ошибке), которая будет запрашивать у пользователя пароль.

Чтобы настроить askpass программы, вы можете установить переменные окружения SUDO_ASKPASS, либо установить его в sudoers с помощью Path askpass .... Дополнительную информацию см. В разделе manual page.

В качестве альтернативы, если ваш пароль не очень секретен, и вы не возражаете, чтобы он отображался на экране, запустите sudo с опцией -S.

+1

Если я хочу установить переменную окружения, как мне это сделать? Как это? 'ProcessBuilder bashBuilder = new ProcessBuilder (" bash "). Directory (workingFolder); bashBuilder.environment().put («SUDO_ASKPASS», «SUDO_ASKPASS»); Процесс bash = bashBuilder.start(); ' – nrubin29

+0

Да, но, конечно, _value_' SUDO_ASKPASS' должен быть программой, которая запрашивает пароль, а не буквально 'SUDO_ASKPASS'. –

+0

И программа будет написана в bash? Есть ли программа, которая поставляется с системой, которую я мог бы использовать, или один, который я мог бы получить легко. Извините за вопросы noob, но я не слишком хорошо разбираюсь в Unix, не говоря уже о попытке использовать его с Java. – nrubin29

1

Я извинил простой 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; 
    } 
} 

Это мини-api, чтобы получить пароль root (пользователь должен написать правильно). Пример использования:

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

Sample возвращение в моем тесте (на польском языке):

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. 

Это апите создать и записать Process пароля. Таким образом, вы можете запускать команду с разрешения sudo.

P.S. Я тестирую вашу программу. И есть одна ошибка: вы должны добавить конец - я должен уничтожить и запустить. Запись в консоли «ping google.com» - программа не отвечает. Вы должны создать новый поток.

+0

И добавить: UIManager.setLookAndFeel (UIManager.getSystemLookAndFeelClassName()); - это ремонт вашей полосы прокрутки. – barwnikk