Home | History | Annotate | Download | only in src
      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 #define LOG_TAG "FrameworkListener"
     18 
     19 #include <errno.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 #include <unistd.h>
     23 
     24 #include <log/log.h>
     25 #include <sysutils/FrameworkCommand.h>
     26 #include <sysutils/FrameworkListener.h>
     27 #include <sysutils/SocketClient.h>
     28 
     29 static const int CMD_BUF_SIZE = 1024;
     30 
     31 #define UNUSED __attribute__((unused))
     32 
     33 FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
     34                             SocketListener(socketName, true, withSeq) {
     35     init(socketName, withSeq);
     36 }
     37 
     38 FrameworkListener::FrameworkListener(const char *socketName) :
     39                             SocketListener(socketName, true, false) {
     40     init(socketName, false);
     41 }
     42 
     43 FrameworkListener::FrameworkListener(int sock) :
     44                             SocketListener(sock, true) {
     45     init(NULL, false);
     46 }
     47 
     48 void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
     49     mCommands = new FrameworkCommandCollection();
     50     errorRate = 0;
     51     mCommandCount = 0;
     52     mWithSeq = withSeq;
     53     mSkipToNextNullByte = false;
     54 }
     55 
     56 bool FrameworkListener::onDataAvailable(SocketClient *c) {
     57     char buffer[CMD_BUF_SIZE];
     58     int len;
     59 
     60     len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
     61     if (len < 0) {
     62         SLOGE("read() failed (%s)", strerror(errno));
     63         return false;
     64     } else if (!len) {
     65         return false;
     66     } else if (buffer[len-1] != '\0') {
     67         SLOGW("String is not zero-terminated");
     68         android_errorWriteLog(0x534e4554, "29831647");
     69         c->sendMsg(500, "Command too large for buffer", false);
     70         mSkipToNextNullByte = true;
     71         return true;
     72     }
     73 
     74     int offset = 0;
     75     int i;
     76 
     77     for (i = 0; i < len; i++) {
     78         if (buffer[i] == '\0') {
     79             /* IMPORTANT: dispatchCommand() expects a zero-terminated string */
     80             if (mSkipToNextNullByte) {
     81                 mSkipToNextNullByte = false;
     82             } else {
     83                 dispatchCommand(c, buffer + offset);
     84             }
     85             offset = i + 1;
     86         }
     87     }
     88 
     89     mSkipToNextNullByte = false;
     90     return true;
     91 }
     92 
     93 void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
     94     mCommands->push_back(cmd);
     95 }
     96 
     97 void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
     98     FrameworkCommandCollection::iterator i;
     99     int argc = 0;
    100     char *argv[FrameworkListener::CMD_ARGS_MAX];
    101     char tmp[CMD_BUF_SIZE];
    102     char *p = data;
    103     char *q = tmp;
    104     char *qlimit = tmp + sizeof(tmp) - 1;
    105     bool esc = false;
    106     bool quote = false;
    107     bool haveCmdNum = !mWithSeq;
    108 
    109     memset(argv, 0, sizeof(argv));
    110     memset(tmp, 0, sizeof(tmp));
    111     while(*p) {
    112         if (*p == '\\') {
    113             if (esc) {
    114                 if (q >= qlimit)
    115                     goto overflow;
    116                 *q++ = '\\';
    117                 esc = false;
    118             } else
    119                 esc = true;
    120             p++;
    121             continue;
    122         } else if (esc) {
    123             if (*p == '"') {
    124                 if (q >= qlimit)
    125                     goto overflow;
    126                 *q++ = '"';
    127             } else if (*p == '\\') {
    128                 if (q >= qlimit)
    129                     goto overflow;
    130                 *q++ = '\\';
    131             } else {
    132                 cli->sendMsg(500, "Unsupported escape sequence", false);
    133                 goto out;
    134             }
    135             p++;
    136             esc = false;
    137             continue;
    138         }
    139 
    140         if (*p == '"') {
    141             if (quote)
    142                 quote = false;
    143             else
    144                 quote = true;
    145             p++;
    146             continue;
    147         }
    148 
    149         if (q >= qlimit)
    150             goto overflow;
    151         *q = *p++;
    152         if (!quote && *q == ' ') {
    153             *q = '\0';
    154             if (!haveCmdNum) {
    155                 char *endptr;
    156                 int cmdNum = (int)strtol(tmp, &endptr, 0);
    157                 if (endptr == NULL || *endptr != '\0') {
    158                     cli->sendMsg(500, "Invalid sequence number", false);
    159                     goto out;
    160                 }
    161                 cli->setCmdNum(cmdNum);
    162                 haveCmdNum = true;
    163             } else {
    164                 if (argc >= CMD_ARGS_MAX)
    165                     goto overflow;
    166                 argv[argc++] = strdup(tmp);
    167             }
    168             memset(tmp, 0, sizeof(tmp));
    169             q = tmp;
    170             continue;
    171         }
    172         q++;
    173     }
    174 
    175     *q = '\0';
    176     if (argc >= CMD_ARGS_MAX)
    177         goto overflow;
    178     argv[argc++] = strdup(tmp);
    179 #if 0
    180     for (int k = 0; k < argc; k++) {
    181         SLOGD("arg[%d] = '%s'", k, argv[k]);
    182     }
    183 #endif
    184 
    185     if (quote) {
    186         cli->sendMsg(500, "Unclosed quotes error", false);
    187         goto out;
    188     }
    189 
    190     if (errorRate && (++mCommandCount % errorRate == 0)) {
    191         /* ignore this command - let the timeout handler handle it */
    192         SLOGE("Faking a timeout");
    193         goto out;
    194     }
    195 
    196     for (i = mCommands->begin(); i != mCommands->end(); ++i) {
    197         FrameworkCommand *c = *i;
    198 
    199         if (!strcmp(argv[0], c->getCommand())) {
    200             if (c->runCommand(cli, argc, argv)) {
    201                 SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
    202             }
    203             goto out;
    204         }
    205     }
    206     cli->sendMsg(500, "Command not recognized", false);
    207 out:
    208     int j;
    209     for (j = 0; j < argc; j++)
    210         free(argv[j]);
    211     return;
    212 
    213 overflow:
    214     cli->sendMsg(500, "Command too long", false);
    215     goto out;
    216 }
    217