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