1 /*
2 * Copyright (C) 2008 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 #include <stdlib.h>
18 #include <sys/socket.h>
19 #include <sys/types.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <dirent.h>
23 #include <errno.h>
24 #include <fcntl.h>
25
26 #define LOG_TAG "VoldCmdListener"
27 #include <cutils/log.h>
28
29 #include <sysutils/SocketClient.h>
30
31 #include "CommandListener.h"
32 #include "VolumeManager.h"
33 #include "ResponseCode.h"
34 #include "Process.h"
35 #include "Xwarp.h"
36 #include "Loop.h"
37 #include "Devmapper.h"
38
CommandListener()39 CommandListener::CommandListener() :
40 FrameworkListener("vold") {
41 registerCmd(new DumpCmd());
42 registerCmd(new VolumeCmd());
43 registerCmd(new AsecCmd());
44 registerCmd(new ObbCmd());
45 registerCmd(new ShareCmd());
46 registerCmd(new StorageCmd());
47 registerCmd(new XwarpCmd());
48 }
49
dumpArgs(int argc,char ** argv,int argObscure)50 void CommandListener::dumpArgs(int argc, char **argv, int argObscure) {
51 char buffer[4096];
52 char *p = buffer;
53
54 memset(buffer, 0, sizeof(buffer));
55 int i;
56 for (i = 0; i < argc; i++) {
57 int len = strlen(argv[i]) + 1; // Account for space
58 if (i == argObscure) {
59 len += 2; // Account for {}
60 }
61 if (((p - buffer) + len) < (sizeof(buffer)-1)) {
62 if (i == argObscure) {
63 *p++ = '{';
64 *p++ = '}';
65 *p++ = ' ';
66 continue;
67 }
68 strcpy(p, argv[i]);
69 p+= strlen(argv[i]);
70 if (i != (argc -1)) {
71 *p++ = ' ';
72 }
73 }
74 }
75 SLOGD("%s", buffer);
76 }
77
DumpCmd()78 CommandListener::DumpCmd::DumpCmd() :
79 VoldCommand("dump") {
80 }
81
runCommand(SocketClient * cli,int argc,char ** argv)82 int CommandListener::DumpCmd::runCommand(SocketClient *cli,
83 int argc, char **argv) {
84 cli->sendMsg(0, "Dumping loop status", false);
85 if (Loop::dumpState(cli)) {
86 cli->sendMsg(ResponseCode::CommandOkay, "Loop dump failed", true);
87 }
88 cli->sendMsg(0, "Dumping DM status", false);
89 if (Devmapper::dumpState(cli)) {
90 cli->sendMsg(ResponseCode::CommandOkay, "Devmapper dump failed", true);
91 }
92 cli->sendMsg(0, "Dumping mounted filesystems", false);
93 FILE *fp = fopen("/proc/mounts", "r");
94 if (fp) {
95 char line[1024];
96 while (fgets(line, sizeof(line), fp)) {
97 line[strlen(line)-1] = '\0';
98 cli->sendMsg(0, line, false);;
99 }
100 fclose(fp);
101 }
102
103 cli->sendMsg(ResponseCode::CommandOkay, "dump complete", false);
104 return 0;
105 }
106
107
VolumeCmd()108 CommandListener::VolumeCmd::VolumeCmd() :
109 VoldCommand("volume") {
110 }
111
runCommand(SocketClient * cli,int argc,char ** argv)112 int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
113 int argc, char **argv) {
114 dumpArgs(argc, argv, -1);
115
116 if (argc < 2) {
117 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
118 return 0;
119 }
120
121 VolumeManager *vm = VolumeManager::Instance();
122 int rc = 0;
123
124 if (!strcmp(argv[1], "list")) {
125 return vm->listVolumes(cli);
126 } else if (!strcmp(argv[1], "debug")) {
127 if (argc != 3 || (argc == 3 && (strcmp(argv[2], "off") && strcmp(argv[2], "on")))) {
128 cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume debug <off/on>", false);
129 return 0;
130 }
131 vm->setDebug(!strcmp(argv[2], "on") ? true : false);
132 } else if (!strcmp(argv[1], "mount")) {
133 if (argc != 3) {
134 cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false);
135 return 0;
136 }
137 rc = vm->mountVolume(argv[2]);
138 } else if (!strcmp(argv[1], "unmount")) {
139 if (argc < 3 || argc > 4 || (argc == 4 && strcmp(argv[3], "force"))) {
140 cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force]", false);
141 return 0;
142 }
143
144 bool force = false;
145 if (argc >= 4 && !strcmp(argv[3], "force")) {
146 force = true;
147 }
148 rc = vm->unmountVolume(argv[2], force);
149 } else if (!strcmp(argv[1], "format")) {
150 if (argc != 3) {
151 cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path>", false);
152 return 0;
153 }
154 rc = vm->formatVolume(argv[2]);
155 } else if (!strcmp(argv[1], "share")) {
156 if (argc != 4) {
157 cli->sendMsg(ResponseCode::CommandSyntaxError,
158 "Usage: volume share <path> <method>", false);
159 return 0;
160 }
161 rc = vm->shareVolume(argv[2], argv[3]);
162 } else if (!strcmp(argv[1], "unshare")) {
163 if (argc != 4) {
164 cli->sendMsg(ResponseCode::CommandSyntaxError,
165 "Usage: volume unshare <path> <method>", false);
166 return 0;
167 }
168 rc = vm->unshareVolume(argv[2], argv[3]);
169 } else if (!strcmp(argv[1], "shared")) {
170 bool enabled = false;
171 if (argc != 4) {
172 cli->sendMsg(ResponseCode::CommandSyntaxError,
173 "Usage: volume shared <path> <method>", false);
174 return 0;
175 }
176
177 if (vm->shareEnabled(argv[2], argv[3], &enabled)) {
178 cli->sendMsg(
179 ResponseCode::OperationFailed, "Failed to determine share enable state", true);
180 } else {
181 cli->sendMsg(ResponseCode::ShareEnabledResult,
182 (enabled ? "Share enabled" : "Share disabled"), false);
183 }
184 return 0;
185 } else {
186 cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume cmd", false);
187 }
188
189 if (!rc) {
190 cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded", false);
191 } else {
192 int erno = errno;
193 rc = ResponseCode::convertFromErrno();
194 cli->sendMsg(rc, "volume operation failed", true);
195 }
196
197 return 0;
198 }
199
ShareCmd()200 CommandListener::ShareCmd::ShareCmd() :
201 VoldCommand("share") {
202 }
203
runCommand(SocketClient * cli,int argc,char ** argv)204 int CommandListener::ShareCmd::runCommand(SocketClient *cli,
205 int argc, char **argv) {
206 dumpArgs(argc, argv, -1);
207
208 if (argc < 2) {
209 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
210 return 0;
211 }
212
213 VolumeManager *vm = VolumeManager::Instance();
214 int rc = 0;
215
216 if (!strcmp(argv[1], "status")) {
217 bool avail = false;
218
219 if (vm->shareAvailable(argv[2], &avail)) {
220 cli->sendMsg(
221 ResponseCode::OperationFailed, "Failed to determine share availability", true);
222 } else {
223 cli->sendMsg(ResponseCode::ShareStatusResult,
224 (avail ? "Share available" : "Share unavailable"), false);
225 }
226 } else {
227 cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown share cmd", false);
228 }
229
230 return 0;
231 }
232
StorageCmd()233 CommandListener::StorageCmd::StorageCmd() :
234 VoldCommand("storage") {
235 }
236
runCommand(SocketClient * cli,int argc,char ** argv)237 int CommandListener::StorageCmd::runCommand(SocketClient *cli,
238 int argc, char **argv) {
239 dumpArgs(argc, argv, -1);
240
241 if (argc < 2) {
242 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
243 return 0;
244 }
245
246 if (!strcmp(argv[1], "users")) {
247 DIR *dir;
248 struct dirent *de;
249
250 if (!(dir = opendir("/proc"))) {
251 cli->sendMsg(ResponseCode::OperationFailed, "Failed to open /proc", true);
252 return 0;
253 }
254
255 while ((de = readdir(dir))) {
256 int pid = Process::getPid(de->d_name);
257
258 if (pid < 0) {
259 continue;
260 }
261
262 char processName[255];
263 Process::getProcessName(pid, processName, sizeof(processName));
264
265 if (Process::checkFileDescriptorSymLinks(pid, argv[2]) ||
266 Process::checkFileMaps(pid, argv[2]) ||
267 Process::checkSymLink(pid, argv[2], "cwd") ||
268 Process::checkSymLink(pid, argv[2], "root") ||
269 Process::checkSymLink(pid, argv[2], "exe")) {
270
271 char msg[1024];
272 snprintf(msg, sizeof(msg), "%d %s", pid, processName);
273 cli->sendMsg(ResponseCode::StorageUsersListResult, msg, false);
274 }
275 }
276 closedir(dir);
277 cli->sendMsg(ResponseCode::CommandOkay, "Storage user list complete", false);
278 } else {
279 cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
280 }
281 return 0;
282 }
283
AsecCmd()284 CommandListener::AsecCmd::AsecCmd() :
285 VoldCommand("asec") {
286 }
287
runCommand(SocketClient * cli,int argc,char ** argv)288 int CommandListener::AsecCmd::runCommand(SocketClient *cli,
289 int argc, char **argv) {
290 if (argc < 2) {
291 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
292 return 0;
293 }
294
295 VolumeManager *vm = VolumeManager::Instance();
296 int rc = 0;
297
298 if (!strcmp(argv[1], "list")) {
299 dumpArgs(argc, argv, -1);
300 DIR *d = opendir(Volume::SEC_ASECDIR);
301
302 if (!d) {
303 cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
304 return 0;
305 }
306
307 struct dirent *dent;
308 while ((dent = readdir(d))) {
309 if (dent->d_name[0] == '.')
310 continue;
311 if (!strcmp(&dent->d_name[strlen(dent->d_name)-5], ".asec")) {
312 char id[255];
313 memset(id, 0, sizeof(id));
314 strncpy(id, dent->d_name, strlen(dent->d_name) -5);
315 cli->sendMsg(ResponseCode::AsecListResult, id, false);
316 }
317 }
318 closedir(d);
319 } else if (!strcmp(argv[1], "create")) {
320 dumpArgs(argc, argv, 5);
321 if (argc != 7) {
322 cli->sendMsg(ResponseCode::CommandSyntaxError,
323 "Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid>", false);
324 return 0;
325 }
326
327 unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
328 rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]));
329 } else if (!strcmp(argv[1], "finalize")) {
330 dumpArgs(argc, argv, -1);
331 if (argc != 3) {
332 cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec finalize <container-id>", false);
333 return 0;
334 }
335 rc = vm->finalizeAsec(argv[2]);
336 } else if (!strcmp(argv[1], "destroy")) {
337 dumpArgs(argc, argv, -1);
338 if (argc < 3) {
339 cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id> [force]", false);
340 return 0;
341 }
342 bool force = false;
343 if (argc > 3 && !strcmp(argv[3], "force")) {
344 force = true;
345 }
346 rc = vm->destroyAsec(argv[2], force);
347 } else if (!strcmp(argv[1], "mount")) {
348 dumpArgs(argc, argv, 3);
349 if (argc != 5) {
350 cli->sendMsg(ResponseCode::CommandSyntaxError,
351 "Usage: asec mount <namespace-id> <key> <ownerUid>", false);
352 return 0;
353 }
354 rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]));
355 } else if (!strcmp(argv[1], "unmount")) {
356 dumpArgs(argc, argv, -1);
357 if (argc < 3) {
358 cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id> [force]", false);
359 return 0;
360 }
361 bool force = false;
362 if (argc > 3 && !strcmp(argv[3], "force")) {
363 force = true;
364 }
365 rc = vm->unmountAsec(argv[2], force);
366 } else if (!strcmp(argv[1], "rename")) {
367 dumpArgs(argc, argv, -1);
368 if (argc != 4) {
369 cli->sendMsg(ResponseCode::CommandSyntaxError,
370 "Usage: asec rename <old_id> <new_id>", false);
371 return 0;
372 }
373 rc = vm->renameAsec(argv[2], argv[3]);
374 } else if (!strcmp(argv[1], "path")) {
375 dumpArgs(argc, argv, -1);
376 if (argc != 3) {
377 cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec path <container-id>", false);
378 return 0;
379 }
380 char path[255];
381
382 if (!(rc = vm->getAsecMountPath(argv[2], path, sizeof(path)))) {
383 cli->sendMsg(ResponseCode::AsecPathResult, path, false);
384 return 0;
385 }
386 } else {
387 dumpArgs(argc, argv, -1);
388 cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown asec cmd", false);
389 }
390
391 if (!rc) {
392 cli->sendMsg(ResponseCode::CommandOkay, "asec operation succeeded", false);
393 } else {
394 rc = ResponseCode::convertFromErrno();
395 cli->sendMsg(rc, "asec operation failed", true);
396 }
397
398 return 0;
399 }
400
ObbCmd()401 CommandListener::ObbCmd::ObbCmd() :
402 VoldCommand("obb") {
403 }
404
runCommand(SocketClient * cli,int argc,char ** argv)405 int CommandListener::ObbCmd::runCommand(SocketClient *cli,
406 int argc, char **argv) {
407 if (argc < 2) {
408 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
409 return 0;
410 }
411
412 VolumeManager *vm = VolumeManager::Instance();
413 int rc = 0;
414
415 if (!strcmp(argv[1], "list")) {
416 dumpArgs(argc, argv, -1);
417
418 rc = vm->listMountedObbs(cli);
419 } else if (!strcmp(argv[1], "mount")) {
420 dumpArgs(argc, argv, 3);
421 if (argc != 5) {
422 cli->sendMsg(ResponseCode::CommandSyntaxError,
423 "Usage: obb mount <filename> <key> <ownerUid>", false);
424 return 0;
425 }
426 rc = vm->mountObb(argv[2], argv[3], atoi(argv[4]));
427 } else if (!strcmp(argv[1], "unmount")) {
428 dumpArgs(argc, argv, -1);
429 if (argc < 3) {
430 cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb unmount <source file> [force]", false);
431 return 0;
432 }
433 bool force = false;
434 if (argc > 3 && !strcmp(argv[3], "force")) {
435 force = true;
436 }
437 rc = vm->unmountObb(argv[2], force);
438 } else if (!strcmp(argv[1], "path")) {
439 dumpArgs(argc, argv, -1);
440 if (argc != 3) {
441 cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb path <source file>", false);
442 return 0;
443 }
444 char path[255];
445
446 if (!(rc = vm->getObbMountPath(argv[2], path, sizeof(path)))) {
447 cli->sendMsg(ResponseCode::AsecPathResult, path, false);
448 return 0;
449 }
450 } else {
451 dumpArgs(argc, argv, -1);
452 cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown obb cmd", false);
453 }
454
455 if (!rc) {
456 cli->sendMsg(ResponseCode::CommandOkay, "obb operation succeeded", false);
457 } else {
458 rc = ResponseCode::convertFromErrno();
459 cli->sendMsg(rc, "obb operation failed", true);
460 }
461
462 return 0;
463 }
464
XwarpCmd()465 CommandListener::XwarpCmd::XwarpCmd() :
466 VoldCommand("xwarp") {
467 }
468
runCommand(SocketClient * cli,int argc,char ** argv)469 int CommandListener::XwarpCmd::runCommand(SocketClient *cli,
470 int argc, char **argv) {
471 if (argc < 2) {
472 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
473 return 0;
474 }
475
476 if (!strcmp(argv[1], "enable")) {
477 if (Xwarp::enable()) {
478 cli->sendMsg(ResponseCode::OperationFailed, "Failed to enable xwarp", true);
479 return 0;
480 }
481
482 cli->sendMsg(ResponseCode::CommandOkay, "Xwarp mirroring started", false);
483 } else if (!strcmp(argv[1], "disable")) {
484 if (Xwarp::disable()) {
485 cli->sendMsg(ResponseCode::OperationFailed, "Failed to disable xwarp", true);
486 return 0;
487 }
488
489 cli->sendMsg(ResponseCode::CommandOkay, "Xwarp disabled", false);
490 } else if (!strcmp(argv[1], "status")) {
491 char msg[255];
492 bool r;
493 unsigned mirrorPos, maxSize;
494
495 if (Xwarp::status(&r, &mirrorPos, &maxSize)) {
496 cli->sendMsg(ResponseCode::OperationFailed, "Failed to get xwarp status", true);
497 return 0;
498 }
499 snprintf(msg, sizeof(msg), "%s %u %u", (r ? "ready" : "not-ready"), mirrorPos, maxSize);
500 cli->sendMsg(ResponseCode::XwarpStatusResult, msg, false);
501 } else {
502 cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
503 }
504
505 return 0;
506 }
507
508