CommandListener.cpp revision 736910ca99a40b9add4353bf619e778c40938948
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 || (argc == 4 && strcmp(argv[3], "force"))) {
142            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force]", false);
143            return 0;
144        }
145
146        bool force = false;
147        if (argc >= 4 && !strcmp(argv[3], "force")) {
148            force = true;
149        }
150        rc = vm->unmountVolume(argv[2], force);
151    } else if (!strcmp(argv[1], "format")) {
152        if (argc != 3) {
153            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path>", false);
154            return 0;
155        }
156        rc = vm->formatVolume(argv[2]);
157    } else if (!strcmp(argv[1], "share")) {
158        if (argc != 4) {
159            cli->sendMsg(ResponseCode::CommandSyntaxError,
160                    "Usage: volume share <path> <method>", false);
161            return 0;
162        }
163        rc = vm->shareVolume(argv[2], argv[3]);
164    } else if (!strcmp(argv[1], "unshare")) {
165        if (argc != 4) {
166            cli->sendMsg(ResponseCode::CommandSyntaxError,
167                    "Usage: volume unshare <path> <method>", false);
168            return 0;
169        }
170        rc = vm->unshareVolume(argv[2], argv[3]);
171    } else if (!strcmp(argv[1], "shared")) {
172        bool enabled = false;
173        if (argc != 4) {
174            cli->sendMsg(ResponseCode::CommandSyntaxError,
175                    "Usage: volume shared <path> <method>", false);
176            return 0;
177        }
178
179        if (vm->shareEnabled(argv[2], argv[3], &enabled)) {
180            cli->sendMsg(
181                    ResponseCode::OperationFailed, "Failed to determine share enable state", true);
182        } else {
183            cli->sendMsg(ResponseCode::ShareEnabledResult,
184                    (enabled ? "Share enabled" : "Share disabled"), false);
185        }
186        return 0;
187    } else {
188        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume cmd", false);
189    }
190
191    if (!rc) {
192        cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded", false);
193    } else {
194        int erno = errno;
195        rc = ResponseCode::convertFromErrno();
196        cli->sendMsg(rc, "volume operation failed", true);
197    }
198
199    return 0;
200}
201
202CommandListener::StorageCmd::StorageCmd() :
203                 VoldCommand("storage") {
204}
205
206int CommandListener::StorageCmd::runCommand(SocketClient *cli,
207                                                      int argc, char **argv) {
208    dumpArgs(argc, argv, -1);
209
210    if (argc < 2) {
211        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
212        return 0;
213    }
214
215    if (!strcmp(argv[1], "users")) {
216        DIR *dir;
217        struct dirent *de;
218
219        if (!(dir = opendir("/proc"))) {
220            cli->sendMsg(ResponseCode::OperationFailed, "Failed to open /proc", true);
221            return 0;
222        }
223
224        while ((de = readdir(dir))) {
225            int pid = Process::getPid(de->d_name);
226
227            if (pid < 0) {
228                continue;
229            }
230
231            char processName[255];
232            Process::getProcessName(pid, processName, sizeof(processName));
233
234            if (Process::checkFileDescriptorSymLinks(pid, argv[2]) ||
235                Process::checkFileMaps(pid, argv[2]) ||
236                Process::checkSymLink(pid, argv[2], "cwd") ||
237                Process::checkSymLink(pid, argv[2], "root") ||
238                Process::checkSymLink(pid, argv[2], "exe")) {
239
240                char msg[1024];
241                snprintf(msg, sizeof(msg), "%d %s", pid, processName);
242                cli->sendMsg(ResponseCode::StorageUsersListResult, msg, false);
243            }
244        }
245        closedir(dir);
246        cli->sendMsg(ResponseCode::CommandOkay, "Storage user list complete", false);
247    } else {
248        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
249    }
250    return 0;
251}
252
253CommandListener::AsecCmd::AsecCmd() :
254                 VoldCommand("asec") {
255}
256
257int CommandListener::AsecCmd::runCommand(SocketClient *cli,
258                                                      int argc, char **argv) {
259    if (argc < 2) {
260        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
261        return 0;
262    }
263
264    VolumeManager *vm = VolumeManager::Instance();
265    int rc = 0;
266
267    if (!strcmp(argv[1], "list")) {
268        dumpArgs(argc, argv, -1);
269        DIR *d = opendir(Volume::SEC_ASECDIR);
270
271        if (!d) {
272            cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
273            return 0;
274        }
275
276        struct dirent *dent;
277        while ((dent = readdir(d))) {
278            if (dent->d_name[0] == '.')
279                continue;
280            if (!strcmp(&dent->d_name[strlen(dent->d_name)-5], ".asec")) {
281                char id[255];
282                memset(id, 0, sizeof(id));
283                strncpy(id, dent->d_name, strlen(dent->d_name) -5);
284                cli->sendMsg(ResponseCode::AsecListResult, id, false);
285            }
286        }
287        closedir(d);
288    } else if (!strcmp(argv[1], "create")) {
289        dumpArgs(argc, argv, 5);
290        if (argc != 7) {
291            cli->sendMsg(ResponseCode::CommandSyntaxError,
292                    "Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid>", false);
293            return 0;
294        }
295
296        unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
297        rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]));
298    } else if (!strcmp(argv[1], "finalize")) {
299        dumpArgs(argc, argv, -1);
300        if (argc != 3) {
301            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec finalize <container-id>", false);
302            return 0;
303        }
304        rc = vm->finalizeAsec(argv[2]);
305    } else if (!strcmp(argv[1], "destroy")) {
306        dumpArgs(argc, argv, -1);
307        if (argc < 3) {
308            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id> [force]", false);
309            return 0;
310        }
311        bool force = false;
312        if (argc > 3 && !strcmp(argv[3], "force")) {
313            force = true;
314        }
315        rc = vm->destroyAsec(argv[2], force);
316    } else if (!strcmp(argv[1], "mount")) {
317        dumpArgs(argc, argv, 3);
318        if (argc != 5) {
319            cli->sendMsg(ResponseCode::CommandSyntaxError,
320                    "Usage: asec mount <namespace-id> <key> <ownerUid>", false);
321            return 0;
322        }
323        rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]));
324    } else if (!strcmp(argv[1], "unmount")) {
325        dumpArgs(argc, argv, -1);
326        if (argc < 3) {
327            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id> [force]", false);
328            return 0;
329        }
330        bool force = false;
331        if (argc > 3 && !strcmp(argv[3], "force")) {
332            force = true;
333        }
334        rc = vm->unmountAsec(argv[2], force);
335    } else if (!strcmp(argv[1], "rename")) {
336        dumpArgs(argc, argv, -1);
337        if (argc != 4) {
338            cli->sendMsg(ResponseCode::CommandSyntaxError,
339                    "Usage: asec rename <old_id> <new_id>", false);
340            return 0;
341        }
342        rc = vm->renameAsec(argv[2], argv[3]);
343    } else if (!strcmp(argv[1], "path")) {
344        dumpArgs(argc, argv, -1);
345        if (argc != 3) {
346            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec path <container-id>", false);
347            return 0;
348        }
349        char path[255];
350
351        if (!(rc = vm->getAsecMountPath(argv[2], path, sizeof(path)))) {
352            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
353            return 0;
354        }
355    } else if (!strcmp(argv[1], "fspath")) {
356        dumpArgs(argc, argv, -1);
357        if (argc != 3) {
358            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fspath <container-id>", false);
359            return 0;
360        }
361        char path[255];
362
363        if (!(rc = vm->getAsecFilesystemPath(argv[2], path, sizeof(path)))) {
364            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
365            return 0;
366        }
367    } else {
368        dumpArgs(argc, argv, -1);
369        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown asec cmd", false);
370    }
371
372    if (!rc) {
373        cli->sendMsg(ResponseCode::CommandOkay, "asec operation succeeded", false);
374    } else {
375        rc = ResponseCode::convertFromErrno();
376        cli->sendMsg(rc, "asec operation failed", true);
377    }
378
379    return 0;
380}
381
382CommandListener::ObbCmd::ObbCmd() :
383                 VoldCommand("obb") {
384}
385
386int CommandListener::ObbCmd::runCommand(SocketClient *cli,
387                                                      int argc, char **argv) {
388    if (argc < 2) {
389        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
390        return 0;
391    }
392
393    VolumeManager *vm = VolumeManager::Instance();
394    int rc = 0;
395
396    if (!strcmp(argv[1], "list")) {
397        dumpArgs(argc, argv, -1);
398
399        rc = vm->listMountedObbs(cli);
400    } else if (!strcmp(argv[1], "mount")) {
401            dumpArgs(argc, argv, 3);
402            if (argc != 5) {
403                cli->sendMsg(ResponseCode::CommandSyntaxError,
404                        "Usage: obb mount <filename> <key> <ownerUid>", false);
405                return 0;
406            }
407            rc = vm->mountObb(argv[2], argv[3], atoi(argv[4]));
408    } else if (!strcmp(argv[1], "unmount")) {
409        dumpArgs(argc, argv, -1);
410        if (argc < 3) {
411            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb unmount <source file> [force]", false);
412            return 0;
413        }
414        bool force = false;
415        if (argc > 3 && !strcmp(argv[3], "force")) {
416            force = true;
417        }
418        rc = vm->unmountObb(argv[2], force);
419    } else if (!strcmp(argv[1], "path")) {
420        dumpArgs(argc, argv, -1);
421        if (argc != 3) {
422            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb path <source file>", false);
423            return 0;
424        }
425        char path[255];
426
427        if (!(rc = vm->getObbMountPath(argv[2], path, sizeof(path)))) {
428            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
429            return 0;
430        }
431    } else {
432        dumpArgs(argc, argv, -1);
433        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown obb cmd", false);
434    }
435
436    if (!rc) {
437        cli->sendMsg(ResponseCode::CommandOkay, "obb operation succeeded", false);
438    } else {
439        rc = ResponseCode::convertFromErrno();
440        cli->sendMsg(rc, "obb operation failed", true);
441    }
442
443    return 0;
444}
445
446CommandListener::XwarpCmd::XwarpCmd() :
447                 VoldCommand("xwarp") {
448}
449
450int CommandListener::XwarpCmd::runCommand(SocketClient *cli,
451                                                      int argc, char **argv) {
452    if (argc < 2) {
453        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
454        return 0;
455    }
456
457    if (!strcmp(argv[1], "enable")) {
458        if (Xwarp::enable()) {
459            cli->sendMsg(ResponseCode::OperationFailed, "Failed to enable xwarp", true);
460            return 0;
461        }
462
463        cli->sendMsg(ResponseCode::CommandOkay, "Xwarp mirroring started", false);
464    } else if (!strcmp(argv[1], "disable")) {
465        if (Xwarp::disable()) {
466            cli->sendMsg(ResponseCode::OperationFailed, "Failed to disable xwarp", true);
467            return 0;
468        }
469
470        cli->sendMsg(ResponseCode::CommandOkay, "Xwarp disabled", false);
471    } else if (!strcmp(argv[1], "status")) {
472        char msg[255];
473        bool r;
474        unsigned mirrorPos, maxSize;
475
476        if (Xwarp::status(&r, &mirrorPos, &maxSize)) {
477            cli->sendMsg(ResponseCode::OperationFailed, "Failed to get xwarp status", true);
478            return 0;
479        }
480        snprintf(msg, sizeof(msg), "%s %u %u", (r ? "ready" : "not-ready"), mirrorPos, maxSize);
481        cli->sendMsg(ResponseCode::XwarpStatusResult, msg, false);
482    } else {
483        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
484    }
485
486    return 0;
487}
488
489CommandListener::CryptfsCmd::CryptfsCmd() :
490                 VoldCommand("cryptfs") {
491}
492
493int CommandListener::CryptfsCmd::runCommand(SocketClient *cli,
494                                                      int argc, char **argv) {
495    if (argc < 2) {
496        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
497        return 0;
498    }
499
500    int rc = 0;
501
502    if (!strcmp(argv[1], "checkpw")) {
503        if (argc != 3) {
504            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs checkpw <passwd>", false);
505            return 0;
506        }
507        dumpArgs(argc, argv, 2);
508        rc = cryptfs_check_passwd(argv[2]);
509    } else if (!strcmp(argv[1], "restart")) {
510        if (argc != 2) {
511            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs restart", false);
512            return 0;
513        }
514        dumpArgs(argc, argv, -1);
515        rc = cryptfs_restart();
516    } else if (!strcmp(argv[1], "cryptocomplete")) {
517        if (argc != 2) {
518            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs cryptocomplete", false);
519            return 0;
520        }
521        dumpArgs(argc, argv, -1);
522        rc = cryptfs_crypto_complete();
523    } else if (!strcmp(argv[1], "enablecrypto")) {
524        if ( (argc != 4) || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) {
525            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs enablecrypto <wipe|inplace> <passwd>", false);
526            return 0;
527        }
528        dumpArgs(argc, argv, 3);
529        rc = cryptfs_enable(argv[2], argv[3]);
530    } else if (!strcmp(argv[1], "changepw")) {
531        if (argc != 3) {
532            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs changepw <newpasswd>", false);
533            return 0;
534        }
535        SLOGD("cryptfs changepw {}");
536        rc = cryptfs_changepw(argv[2]);
537    } else {
538        dumpArgs(argc, argv, -1);
539        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false);
540    }
541
542    // Always report that the command succeeded and return the error code.
543    // The caller will check the return value to see what the error was.
544    char msg[255];
545    snprintf(msg, sizeof(msg), "%d", rc);
546    cli->sendMsg(ResponseCode::CommandOkay, msg, false);
547
548    return 0;
549}
550
551