通过Java管道smbpasswd

时间:2016-07-07 13:58:46

标签: java ubuntu automation samba

我试图通过Java应用程序将密码管道输入smbpassword,这是我通过终端管道的方式:

(echo newPassword; echo confirmPassword) | smbpasswd -a -s client1

并且输出显示命令很好:

Added user client1

但是,我无法通过Java应用程序实现这一点,这里是我使用的代码:

public void run(String command, String[] prompt) {
    try {
        String[] args = new String[] {"/bin/bash","-c","echo " + rootPassword + "| sudo -S " + command};
        Process proc = new ProcessBuilder(args).start();

        if (prompt != null && prompt.length > 0) {
            for (int i = 0; i < prompt.length; i++) {
                proc.getOutputStream().write((prompt[i] + "\r\n").getBytes());
                proc.getOutputStream().flush();
            }
        }

        proc.waitFor();

        String output = "";
        String line;
        BufferedReader input = new BufferedReader(new InputStreamReader(proc.getInputStream()));
        while ((line = input.readLine()) != null) {
            System.out.println(line);
            output += line + "\n";
        }
        System.out.println(output);
        input.close();
    }
    catch (Exception ex) {
        System.out.println(null, ex.getMessage());
    }
}

我尝试使用OutputStream:

String[] pipe = { password, password };
run("smbpasswd -s -a " + username, pipe);

或管道:

run("(echo " + password + "; echo " + password + ") | smbpasswd -s -a " + username, null);

但两个都不起作用,我没有输出,并且我通过pdbedit -L检查后似乎没有创建用户。但是,我能够通过该函数使用管道(例如echo username:password | chpasswd)执行另一个命令。

任何想法?
提前谢谢。

1 个答案:

答案 0 :(得分:0)

主要的概念问题是shell代码,例如您正在尝试运行:

(echo newPassword; echo confirmPassword) | smbpasswd -a -s client1

不是sudo所要求的“命令”。事实上,即使在shell术语中,它也不是“命令”;相反,它是一个管道,由复合命令和简单命令组成。 sudo希望您提供一个简单的命令。

但这实际上有点搁置。您的问题并非真正针对sudo;相反,这是一个shell问题。在失败的情况下,您的run()方法调用bash来执行以下shell代码:

echo rootPassword | sudo -S (echo newPassword; echo confirmPassword) | smbpasswd -a -s client1

这至少有两个原因:

  1. 子shell调用不能出现在这样的简单命令(即sudo)参数列表中。您可以在那里放置一个命令替换($(echo ...)),但这不符合您的目的,因为内部换行符不会被保留。

  2. 即使您解决了(1),您的第二个管道处于错误的级别:它会将sudo的输出传输到smbpasswd。这在数据方面很好,但它会让smbpasswd无特权地运行 - sudo不适用于它。

  3. 您可以通过指示sudo通过shell运行代码来解决问题。您可以通过在内部bash -c命令中嵌入最终命令来实现这一点,但使用sudo的{​​{1}}选项可能更容易(但要注意{{1}如果允许,可以选择与-s不同的shell 。如果要在Java源代码中的单个String中编码整个操作,那么这些内容可能是最常用的解决方案:

    <击>

    <击>
    -s

    <击>

    bash

    然而,您的大部分问题都源于您依靠 String[] args = new String[] { "/bin/bash", "-c", "echo " + rootPassword + " | sudo -S -s '" + command + "'" }; 向您的命令提供数据。您应该考虑使用 String[] args = new String[] { "/bin/bash", "-c", "echo " + rootPassword + " | sudo -S bash -c '" + command + "'" }; 的{​​{1}}来从Java程序向其提供数据。这不仅仅是一个简单的Java端API,但我认为你在欺骗自己的API有多简单。正如您所发现的那样,它隐藏了陷阱,即使我上面提供的修订版仍然至少有一个:如果提交给它的命令包含单引号字符(echo),它可能会中断。

    更新

    以下是通过Process

    为流程提供所需数据的一种方法
    OutputStream

    您可以在其类中使用

    '

    请特别注意,在您编写了要写入的所有内容之后,OutputStream的输出流通常必须关闭。如果您不这样做,您可以运行的某些命令将不会退出。

    还请注意

    对于一般情况,您必须阅读每个 public void run2(String command, String commandInput) throws IOException, InterruptedException { String[] args = new String[] { "sudo", "-S", "bash", "-c", command }; Process proc = new ProcessBuilder(args).start(); Writer toProc = new OutputStreamWriter(proc.getOutputStream()); toProc.write(rootPassword, 0, rootPassword.length()); toProc.write('\n'); toProc.write(commandInput, 0, commandInput.length()); toProc.close(); proc.waitFor(); BufferedReader input = new BufferedReader(new InputStreamReader(proc.getInputStream())); StringBuilder output = new StringBuilder(); String line; while ((line = input.readLine()) != null) { output.append(line).append('\n'); } System.out.println("Command output:"); System.out.println(output.toString()); input.close(); } 的输入流和错误流(或者只有前者,如果您将它们组合在一起),并行与流程本身。如果您还没有组合流,那么您必须彼此并行读取它们。如果您正在写入流程的输出流,那么它也必须并行。如果您没有安排通过自己的线程处理每个流,那么该进程可能会卡住。

    另一方面,你需要一个单独的线程来运行run2("smbpasswd -a -s client1", "newPassword\nnewPassword"); 本身 - 毕竟,它代表了一个完整的独立过程。只需推迟等待它,直到您关闭其输出流并读取其输入和错误流的结束。