diff --git a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java index 1114b9968..db7762d7e 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java +++ b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java @@ -1,5 +1,6 @@ package com.taobao.arthas.core.command; +import com.taobao.arthas.core.command.basic1000.Base64Command; import com.taobao.arthas.core.command.basic1000.CatCommand; import com.taobao.arthas.core.command.basic1000.ClsCommand; import com.taobao.arthas.core.command.basic1000.EchoCommand; @@ -101,6 +102,7 @@ public class BuiltinCommandPack implements CommandResolver { commands.add(Command.create(LoggerCommand.class)); commands.add(Command.create(HistoryCommand.class)); commands.add(Command.create(CatCommand.class)); + commands.add(Command.create(Base64Command.class)); commands.add(Command.create(EchoCommand.class)); commands.add(Command.create(PwdCommand.class)); commands.add(Command.create(MBeanCommand.class)); diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/Base64Command.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/Base64Command.java new file mode 100644 index 000000000..8ad843704 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/Base64Command.java @@ -0,0 +1,170 @@ +package com.taobao.arthas.core.command.basic1000; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.taobao.arthas.common.IOUtils; +import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.model.Base64Model; +import com.taobao.arthas.core.shell.cli.Completion; +import com.taobao.arthas.core.shell.cli.CompletionUtils; +import com.taobao.arthas.core.shell.command.AnnotatedCommand; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.middleware.cli.annotations.Argument; +import com.taobao.middleware.cli.annotations.Description; +import com.taobao.middleware.cli.annotations.Name; +import com.taobao.middleware.cli.annotations.Option; +import com.taobao.middleware.cli.annotations.Summary; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.base64.Base64; +import io.netty.util.CharsetUtil; + +/** + * + * @author hengyunabc 2021-01-05 + * + */ +@Name("base64") +@Summary("Encode and decode using Base64 representation") +@Description(Constants.EXAMPLE + + " base64 /tmp/test.txt\n" + + " base64 --input /tmp/test.txt --output /tmp/result.txt\n" + + " base64 -d /tmp/result.txt\n" + + Constants.WIKI + Constants.WIKI_HOME + "base64") +public class Base64Command extends AnnotatedCommand { + private static final Logger logger = LoggerFactory.getLogger(Base64Command.class); + private String file; + private Integer sizeLimit = 128 * 1024; + private int maxSizeLimit = 8 * 1024 * 1024; + + private boolean decode; + + private String input; + private String output; + + @Argument(argName = "file", index = 0, required = false) + @Description("file") + public void setFiles(String file) { + this.file = file; + } + + @Option(shortName = "d", longName = "decode", flag = true) + @Description("decodes input") + public void setDecode(boolean decode) { + this.decode = decode; + } + + @Option(shortName = "i", longName = "input") + @Description("input file") + public void setInput(String input) { + this.input = input; + } + + @Option(shortName = "o", longName = "output") + @Description("output file") + public void setOutput(String output) { + this.output = output; + } + + @Option(shortName = "M", longName = "sizeLimit") + @Description("Upper size limit in bytes for the result (128 * 1024 by default, the maximum value is 8 * 1024 * 1024)") + public void setSizeLimit(Integer sizeLimit) { + this.sizeLimit = sizeLimit; + } + + @Override + public void process(CommandProcess process) { + if (!verifyOptions(process)) { + return; + } + + // 确认输入 + if (file == null && this.input != null) { + file = input; + } + + File f = new File(file); + if (!f.exists()) { + process.end(-1, file + ": No such file or directory"); + return; + } + if (f.isDirectory()) { + process.end(-1, file + ": Is a directory"); + return; + } + + if (f.length() > sizeLimit) { + process.end(-1, file + ": Is too large, size: " + f.length()); + return; + } + + InputStream input = null; + + try { + input = new FileInputStream(f); + byte[] bytes = IOUtils.getBytes(input); + + ByteBuf convertResult = null; + if (this.decode) { + convertResult = Base64.decode(Unpooled.wrappedBuffer(bytes)); + } else { + convertResult = Base64.encode(Unpooled.wrappedBuffer(bytes)); + } + + if (this.output != null) { + int readableBytes = convertResult.readableBytes(); + OutputStream out = new FileOutputStream(this.output); + convertResult.readBytes(out, readableBytes); + process.appendResult(new Base64Model(null)); + } else { + String base64Str = convertResult.toString(CharsetUtil.UTF_8); + process.appendResult(new Base64Model(base64Str)); + } + } catch (IOException e) { + logger.error("read file error. name: " + file, e); + process.end(1, "read file error: " + e.getMessage()); + return; + } finally { + IOUtils.close(input); + } + + process.end(); + } + + private boolean verifyOptions(CommandProcess process) { + if(this.file == null && this.input == null) { + process.end(-1); + return false; + } + + if (sizeLimit > maxSizeLimit) { + process.end(-1, "sizeLimit cannot be large than: " + maxSizeLimit); + return false; + } + + // 目前不支持过滤,限制http请求执行的文件大小 + int maxSizeLimitOfNonTty = 128 * 1024; + if (!process.session().isTty() && sizeLimit > maxSizeLimitOfNonTty) { + process.end(-1, + "When executing in non-tty session, sizeLimit cannot be large than: " + maxSizeLimitOfNonTty); + return false; + } + return true; + } + + @Override + public void complete(Completion completion) { + if (!CompletionUtils.completeFilePath(completion)) { + super.complete(completion); + } + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java index 123f464d4..e6ded8dcb 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java @@ -39,6 +39,7 @@ public class ResultViewResolver { //registerView(HistoryView.class); registerView(EchoView.class); registerView(CatView.class); + registerView(Base64View.class); registerView(OptionsView.class); registerView(SystemPropertyView.class); registerView(SystemEnvView.class); diff --git a/site/src/site/sphinx/advanced-use.md b/site/src/site/sphinx/advanced-use.md index b751cc77e..393a2a246 100644 --- a/site/src/site/sphinx/advanced-use.md +++ b/site/src/site/sphinx/advanced-use.md @@ -8,6 +8,7 @@ * [cat](cat.md)——打印文件内容,和linux里的cat命令类似 * [echo](echo.md)--打印参数,和linux里的echo命令类似 * [grep](grep.md)——匹配查找,和linux里的grep命令类似 +* [base64](base64.md)——base64编码转换,和linux里的base64命令类似 * [tee](tee.md)——复制标准输入到标准输出和指定的文件,和linux里的tee命令类似 * [pwd](pwd.md)——返回当前的工作目录,和linux命令类似 * cls——清空当前屏幕区域 diff --git a/site/src/site/sphinx/commands.md b/site/src/site/sphinx/commands.md index a48066d8a..7ee246edd 100644 --- a/site/src/site/sphinx/commands.md +++ b/site/src/site/sphinx/commands.md @@ -35,6 +35,7 @@ * [cat](cat.md) * [echo](echo.md) * [grep](grep.md) +* [base64](base64.md) * [tee](tee.md) * [pwd](pwd.md) * [options](options.md) diff --git a/site/src/site/sphinx/en/advanced-use.md b/site/src/site/sphinx/en/advanced-use.md index 5b8194e74..1e2b72f62 100644 --- a/site/src/site/sphinx/en/advanced-use.md +++ b/site/src/site/sphinx/en/advanced-use.md @@ -8,6 +8,7 @@ Advanced Usage * [cat](cat.md) - Concatenate and print files * [echo](echo.md) - write arguments to the standard output * [grep](grep.md) - Pattern searcher +* [base64](base64.md) - Encode and decode using Base64 representation. * [tee](tee.md) - Copies standard input to standard output, making a copy in zero or more files. * [pwd](pwd.md) - Return working directory name * session - display current session information diff --git a/site/src/site/sphinx/en/commands.md b/site/src/site/sphinx/en/commands.md index f60729ee3..d61dadc2c 100644 --- a/site/src/site/sphinx/en/commands.md +++ b/site/src/site/sphinx/en/commands.md @@ -35,6 +35,7 @@ All Commands * [cat](cat.md) * [echo](echo.md) * [grep](grep.md) +* [base64](base64.md) * [tee](tee.md) * [pwd](pwd.md) * [options](options.md)