• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.os;
18 
19 import android.util.Slog;
20 import com.android.internal.util.FastPrintWriter;
21 
22 import java.io.BufferedInputStream;
23 import java.io.FileDescriptor;
24 import java.io.FileInputStream;
25 import java.io.FileOutputStream;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.io.PrintWriter;
29 
30 /**
31  * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}.
32  * @hide
33  */
34 public abstract class ShellCommand {
35     static final String TAG = "ShellCommand";
36     static final boolean DEBUG = false;
37 
38     private Binder mTarget;
39     private FileDescriptor mIn;
40     private FileDescriptor mOut;
41     private FileDescriptor mErr;
42     private String[] mArgs;
43     private ShellCallback mShellCallback;
44     private ResultReceiver mResultReceiver;
45 
46     private String mCmd;
47     private int mArgPos;
48     private String mCurArgData;
49 
50     private FileInputStream mFileIn;
51     private FileOutputStream mFileOut;
52     private FileOutputStream mFileErr;
53 
54     private FastPrintWriter mOutPrintWriter;
55     private FastPrintWriter mErrPrintWriter;
56     private InputStream mInputStream;
57 
init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, int firstArgPos)58     public void init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
59             String[] args, ShellCallback callback, int firstArgPos) {
60         mTarget = target;
61         mIn = in;
62         mOut = out;
63         mErr = err;
64         mArgs = args;
65         mShellCallback = callback;
66         mResultReceiver = null;
67         mCmd = null;
68         mArgPos = firstArgPos;
69         mCurArgData = null;
70         mFileIn = null;
71         mFileOut = null;
72         mFileErr = null;
73         mOutPrintWriter = null;
74         mErrPrintWriter = null;
75         mInputStream = null;
76     }
77 
exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)78     public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
79             String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
80         String cmd;
81         int start;
82         if (args != null && args.length > 0) {
83             cmd = args[0];
84             start = 1;
85         } else {
86             cmd = null;
87             start = 0;
88         }
89         init(target, in, out, err, args, callback, start);
90         mCmd = cmd;
91         mResultReceiver = resultReceiver;
92 
93         if (DEBUG) Slog.d(TAG, "Starting command " + mCmd + " on " + mTarget);
94         int res = -1;
95         try {
96             res = onCommand(mCmd);
97             if (DEBUG) Slog.d(TAG, "Executed command " + mCmd + " on " + mTarget);
98         } catch (SecurityException e) {
99             PrintWriter eout = getErrPrintWriter();
100             eout.println("Security exception: " + e.getMessage());
101             eout.println();
102             e.printStackTrace(eout);
103         } catch (Throwable e) {
104             // Unlike usual calls, in this case if an exception gets thrown
105             // back to us we want to print it back in to the dump data, since
106             // that is where the caller expects all interesting information to
107             // go.
108             PrintWriter eout = getErrPrintWriter();
109             eout.println();
110             eout.println("Exception occurred while executing:");
111             e.printStackTrace(eout);
112         } finally {
113             if (DEBUG) Slog.d(TAG, "Flushing output streams on " + mTarget);
114             if (mOutPrintWriter != null) {
115                 mOutPrintWriter.flush();
116             }
117             if (mErrPrintWriter != null) {
118                 mErrPrintWriter.flush();
119             }
120             if (DEBUG) Slog.d(TAG, "Sending command result on " + mTarget);
121             mResultReceiver.send(res, null);
122         }
123         if (DEBUG) Slog.d(TAG, "Finished command " + mCmd + " on " + mTarget);
124         return res;
125     }
126 
127     /**
128      * Return direct raw access (not buffered) to the command's output data stream.
129      */
getRawOutputStream()130     public OutputStream getRawOutputStream() {
131         if (mFileOut == null) {
132             mFileOut = new FileOutputStream(mOut);
133         }
134         return mFileOut;
135     }
136 
137     /**
138      * Return a PrintWriter for formatting output to {@link #getRawOutputStream()}.
139      */
getOutPrintWriter()140     public PrintWriter getOutPrintWriter() {
141         if (mOutPrintWriter == null) {
142             mOutPrintWriter = new FastPrintWriter(getRawOutputStream());
143         }
144         return mOutPrintWriter;
145     }
146 
147     /**
148      * Return direct raw access (not buffered) to the command's error output data stream.
149      */
getRawErrorStream()150     public OutputStream getRawErrorStream() {
151         if (mFileErr == null) {
152             mFileErr = new FileOutputStream(mErr);
153         }
154         return mFileErr;
155     }
156 
157     /**
158      * Return a PrintWriter for formatting output to {@link #getRawErrorStream()}.
159      */
getErrPrintWriter()160     public PrintWriter getErrPrintWriter() {
161         if (mErr == null) {
162             return getOutPrintWriter();
163         }
164         if (mErrPrintWriter == null) {
165             mErrPrintWriter = new FastPrintWriter(getRawErrorStream());
166         }
167         return mErrPrintWriter;
168     }
169 
170     /**
171      * Return direct raw access (not buffered) to the command's input data stream.
172      */
getRawInputStream()173     public InputStream getRawInputStream() {
174         if (mFileIn == null) {
175             mFileIn = new FileInputStream(mIn);
176         }
177         return mFileIn;
178     }
179 
180     /**
181      * Return buffered access to the command's {@link #getRawInputStream()}.
182      */
getBufferedInputStream()183     public InputStream getBufferedInputStream() {
184         if (mInputStream == null) {
185             mInputStream = new BufferedInputStream(getRawInputStream());
186         }
187         return mInputStream;
188     }
189 
190     /**
191      * Helper for just system services to ask the shell to open an output file.
192      * @hide
193      */
openOutputFileForSystem(String path)194     public ParcelFileDescriptor openOutputFileForSystem(String path) {
195         try {
196             ParcelFileDescriptor pfd = getShellCallback().openOutputFile(path,
197                     "u:r:system_server:s0");
198             if (pfd != null) {
199                 return pfd;
200             }
201         } catch (RuntimeException e) {
202             getErrPrintWriter().println("Failure opening file: " + e.getMessage());
203         }
204         getErrPrintWriter().println("Error: Unable to open file: " + path);
205         getErrPrintWriter().println("Consider using a file under /data/local/tmp/");
206         return null;
207     }
208 
209     /**
210      * Return the next option on the command line -- that is an argument that
211      * starts with '-'.  If the next argument is not an option, null is returned.
212      */
getNextOption()213     public String getNextOption() {
214         if (mCurArgData != null) {
215             String prev = mArgs[mArgPos - 1];
216             throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
217         }
218         if (mArgPos >= mArgs.length) {
219             return null;
220         }
221         String arg = mArgs[mArgPos];
222         if (!arg.startsWith("-")) {
223             return null;
224         }
225         mArgPos++;
226         if (arg.equals("--")) {
227             return null;
228         }
229         if (arg.length() > 1 && arg.charAt(1) != '-') {
230             if (arg.length() > 2) {
231                 mCurArgData = arg.substring(2);
232                 return arg.substring(0, 2);
233             } else {
234                 mCurArgData = null;
235                 return arg;
236             }
237         }
238         mCurArgData = null;
239         return arg;
240     }
241 
242     /**
243      * Return the next argument on the command line, whatever it is; if there are
244      * no arguments left, return null.
245      */
getNextArg()246     public String getNextArg() {
247         if (mCurArgData != null) {
248             String arg = mCurArgData;
249             mCurArgData = null;
250             return arg;
251         } else if (mArgPos < mArgs.length) {
252             return mArgs[mArgPos++];
253         } else {
254             return null;
255         }
256     }
257 
peekNextArg()258     public String peekNextArg() {
259         if (mCurArgData != null) {
260             return mCurArgData;
261         } else if (mArgPos < mArgs.length) {
262             return mArgs[mArgPos];
263         } else {
264             return null;
265         }
266     }
267 
268     /**
269      * Return the next argument on the command line, whatever it is; if there are
270      * no arguments left, throws an IllegalArgumentException to report this to the user.
271      */
getNextArgRequired()272     public String getNextArgRequired() {
273         String arg = getNextArg();
274         if (arg == null) {
275             String prev = mArgs[mArgPos - 1];
276             throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
277         }
278         return arg;
279     }
280 
281     /**
282      * Return the {@link ShellCallback} for communicating back with the calling shell.
283      */
getShellCallback()284     public ShellCallback getShellCallback() {
285         return mShellCallback;
286     }
287 
handleDefaultCommands(String cmd)288     public int handleDefaultCommands(String cmd) {
289         if ("dump".equals(cmd)) {
290             String[] newArgs = new String[mArgs.length-1];
291             System.arraycopy(mArgs, 1, newArgs, 0, mArgs.length-1);
292             mTarget.doDump(mOut, getOutPrintWriter(), newArgs);
293             return 0;
294         } else if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) {
295             onHelp();
296         } else {
297             getOutPrintWriter().println("Unknown command: " + cmd);
298         }
299         return -1;
300     }
301 
302     /**
303      * Implement parsing and execution of a command.  If it isn't a command you understand,
304      * call {@link #handleDefaultCommands(String)} and return its result as a last resort.
305      * Use {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
306      * to process additional command line arguments.  Command output can be written to
307      * {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}.
308      *
309      * <p class="caution">Note that no permission checking has been done before entering this function,
310      * so you need to be sure to do your own security verification for any commands you
311      * are executing.  The easiest way to do this is to have the ShellCommand contain
312      * only a reference to your service's aidl interface, and do all of your command
313      * implementations on top of that -- that way you can rely entirely on your executing security
314      * code behind that interface.</p>
315      *
316      * @param cmd The first command line argument representing the name of the command to execute.
317      * @return Return the command result; generally 0 or positive indicates success and
318      * negative values indicate error.
319      */
onCommand(String cmd)320     public abstract int onCommand(String cmd);
321 
322     /**
323      * Implement this to print help text about your command to {@link #getOutPrintWriter()}.
324      */
onHelp()325     public abstract void onHelp();
326 }
327