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