| /* | 
 |  * Copyright (C) 2008 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | /* | 
 | **	mountd server support | 
 | */ | 
 |  | 
 | #include "mountd.h" | 
 | #include "ASEC.h" | 
 |  | 
 | #include <cutils/properties.h> | 
 | #include <cutils/sockets.h> | 
 |  | 
 | #include <pthread.h> | 
 | #include <errno.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <unistd.h> | 
 | #include <string.h> | 
 | #include <sys/socket.h> | 
 |  | 
 | #include <private/android_filesystem_config.h> | 
 |  | 
 |  | 
 | // current client file descriptor | 
 | static int sFD = -1; | 
 |  | 
 | // to synchronize writing to client | 
 | static pthread_mutex_t sWriteMutex = PTHREAD_MUTEX_INITIALIZER; | 
 |  | 
 | // path for media that failed to mount before the runtime is connected | 
 | static char* sDeferredUnmountableMediaPath = NULL; | 
 |  | 
 | // last asec msg before the runtime was connected | 
 | static char* sAsecDeferredMessage = NULL; | 
 | static char* sAsecDeferredArgument = NULL; | 
 |  | 
 | static int Write(const char* message) | 
 | { | 
 |     int result = -1; | 
 |  | 
 |     pthread_mutex_lock(&sWriteMutex); | 
 |      | 
 |     LOG_SERVER("Write: %s\n", message); | 
 |     if (sFD >= 0) | 
 |         result = write(sFD, message, strlen(message) + 1); | 
 |  | 
 |     pthread_mutex_unlock(&sWriteMutex);  | 
 |      | 
 |     return result; | 
 | } | 
 |  | 
 | static int Write2(const char* message, const char* data) | 
 | { | 
 |     int result = -1; | 
 |  | 
 |     char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1); | 
 |     if (!buffer) | 
 |     { | 
 |         LOG_ERROR("alloca failed in Write2\n"); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     strcpy(buffer, message); | 
 |     strcat(buffer, data); | 
 |     return Write(buffer); | 
 | } | 
 |  | 
 | static void SendStatus() | 
 | { | 
 |     Write(IsMassStorageConnected() ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED); | 
 |     Write(IsMassStorageEnabled() ? MOUNTD_UMS_ENABLED : MOUNTD_UMS_DISABLED); | 
 | } | 
 |  | 
 | static void DoCommand(const char* command) | 
 | { | 
 |     LOG_SERVER("DoCommand %s\n", command); | 
 |      | 
 |     if (strcmp(command, MOUNTD_ENABLE_UMS) == 0) | 
 |     { | 
 |         EnableMassStorage(true); | 
 |         Write(MOUNTD_UMS_ENABLED); | 
 |      } | 
 |     else if (strcmp(command, MOUNTD_DISABLE_UMS) == 0)  | 
 |     { | 
 |         EnableMassStorage(false); | 
 |         Write(MOUNTD_UMS_DISABLED); | 
 |     } | 
 |     else if (strcmp(command, MOUNTD_SEND_STATUS) == 0) | 
 |     { | 
 |         SendStatus(); | 
 |     } | 
 |     else if (strncmp(command, MOUNTD_MOUNT_MEDIA, strlen(MOUNTD_MOUNT_MEDIA)) == 0) | 
 |     { | 
 |         const char* path = command + strlen(MOUNTD_MOUNT_MEDIA); | 
 |         MountMedia(path); | 
 |     } | 
 |     else if (strncmp(command, MOUNTD_EJECT_MEDIA, strlen(MOUNTD_EJECT_MEDIA)) == 0) | 
 |     { | 
 |         const char* path = command + strlen(MOUNTD_EJECT_MEDIA); | 
 |         UnmountMedia(path); | 
 |     }  | 
 |     else if (strncmp(command, ASEC_CMD_ENABLE, strlen(ASEC_CMD_ENABLE)) == 0) { | 
 |         LOG_ASEC("Got ASEC_CMD_ENABLE\n"); | 
 | 	// XXX: SAN: Impliment | 
 |     } | 
 |     else if (strncmp(command, ASEC_CMD_DISABLE, strlen(ASEC_CMD_DISABLE)) == 0) { | 
 |         LOG_ASEC("Got ASEC_CMD_DISABLE\n"); | 
 | 	// XXX: SAN: Impliment | 
 |     } | 
 |     else if (strncmp(command, ASEC_CMD_SEND_STATUS, strlen(ASEC_CMD_SEND_STATUS)) == 0) { | 
 |         LOG_ASEC("Got ASEC_CMD_SEND_STATUS\n"); | 
 | 	// XXX: SAN: Impliment | 
 |     } | 
 |     else | 
 |         LOGE("unknown command %s\n", command); | 
 | } | 
 |  | 
 | int RunServer() | 
 | { | 
 |     int socket = android_get_control_socket(MOUNTD_SOCKET); | 
 |     if (socket < 0) { | 
 |         LOGE("Obtaining file descriptor for socket '%s' failed: %s", | 
 |              MOUNTD_SOCKET, strerror(errno)); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     if (listen(socket, 4) < 0) { | 
 |         LOGE("Unable to listen on file descriptor '%d' for socket '%s': %s", | 
 |              socket, MOUNTD_SOCKET, strerror(errno)); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     while (1) | 
 |     { | 
 |         struct sockaddr addr; | 
 |         socklen_t alen; | 
 |         struct ucred cred; | 
 |         socklen_t size; | 
 |          | 
 |         alen = sizeof(addr); | 
 |         sFD = accept(socket, &addr, &alen); | 
 |         if (sFD < 0) | 
 |             continue; | 
 |              | 
 |         if (sDeferredUnmountableMediaPath) { | 
 |             NotifyMediaState(sDeferredUnmountableMediaPath, MEDIA_UNMOUNTABLE, false); | 
 |             free(sDeferredUnmountableMediaPath); | 
 |             sDeferredUnmountableMediaPath = NULL; | 
 |         } | 
 |  | 
 |         if (sAsecDeferredMessage) { | 
 |      | 
 |             if (Write2(sAsecDeferredMessage, sAsecDeferredArgument) < 0) | 
 |                 LOG_ERROR("Failed to deliver deferred ASEC msg to framework\n"); | 
 |             free(sAsecDeferredMessage); | 
 |             free(sAsecDeferredArgument); | 
 |             sAsecDeferredMessage = sAsecDeferredArgument = NULL; | 
 |         } | 
 |  | 
 |         while (1) | 
 |         {     | 
 |             char    buffer[101]; | 
 |             int result = read(sFD, buffer, sizeof(buffer) - 1); | 
 |             if (result > 0) | 
 |             { | 
 |                 int start = 0; | 
 |                 int i; | 
 |                 // command should be zero terminated, but just in case | 
 |                 buffer[result] = 0; | 
 |                 for (i = 0; i < result; i++)  | 
 |                 { | 
 |                     if (buffer[i] == 0)  | 
 |                     { | 
 |                         DoCommand(buffer + start); | 
 |                         start = i + 1; | 
 |                     }                    | 
 |                 } | 
 |             } | 
 |             else | 
 |             { | 
 |                 close(sFD); | 
 |                 sFD = -1; | 
 |                 break; | 
 |             } | 
 |         } | 
 |     }   | 
 |  | 
 |     // should never get here | 
 |     return 0; | 
 | } | 
 |  | 
 | void SendMassStorageConnected(boolean connected) | 
 | { | 
 |     Write(connected ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED); | 
 | } | 
 |  | 
 | void SendUnmountRequest(const char* path) | 
 | { | 
 |     Write2(MOUNTD_REQUEST_EJECT, path); | 
 | } | 
 |  | 
 | void NotifyAsecState(AsecState state, const char *argument) | 
 | { | 
 |     const char *event = NULL; | 
 |     const char *status = NULL; | 
 |     boolean deferr = true;; | 
 |  | 
 |     switch (state) { | 
 |         case ASEC_DISABLED: | 
 |             event = ASEC_EVENT_DISABLED; | 
 |             status = ASEC_STATUS_DISABLED; | 
 |             break; | 
 |         case ASEC_AVAILABLE: | 
 |             event = ASEC_EVENT_AVAILABLE; | 
 |             status = ASEC_STATUS_AVAILABLE; | 
 |             break; | 
 |         case ASEC_BUSY: | 
 |             event = ASEC_EVENT_BUSY; | 
 |             status = ASEC_STATUS_BUSY; | 
 |             deferr = false; | 
 |             break; | 
 |         case ASEC_FAILED_INTERR: | 
 |             event = ASEC_EVENT_FAILED_INTERR; | 
 |             status = ASEC_STATUS_FAILED_INTERR; | 
 |             break; | 
 |         case ASEC_FAILED_NOMEDIA: | 
 |             event = ASEC_EVENT_FAILED_NOMEDIA; | 
 |             status = ASEC_STATUS_FAILED_NOMEDIA; | 
 |             break; | 
 |         case ASEC_FAILED_BADMEDIA: | 
 |             event = ASEC_EVENT_FAILED_BADMEDIA; | 
 |             status = ASEC_STATUS_FAILED_BADMEDIA; | 
 |             break; | 
 |         case ASEC_FAILED_BADKEY: | 
 |             event = ASEC_EVENT_FAILED_BADKEY; | 
 |             status = ASEC_STATUS_FAILED_BADKEY; | 
 |             break; | 
 |         default: | 
 |             LOG_ERROR("unknown AsecState %d in NotifyAsecState\n", state); | 
 |             return; | 
 |     } | 
 |  | 
 |     property_set(ASEC_STATUS, status); | 
 |  | 
 |     int result = Write2(event, argument); | 
 |     if ((result < 0) && deferr) { | 
 |         if (sAsecDeferredMessage)  | 
 |             free(sAsecDeferredMessage); | 
 |         sAsecDeferredMessage = strdup(event); | 
 |         if (sAsecDeferredArgument) | 
 |             free(sAsecDeferredArgument); | 
 |         sAsecDeferredArgument = strdup(argument); | 
 |         LOG_ASEC("Deferring event '%s' arg '%s' until framework connects\n", event, argument); | 
 |     } | 
 | } | 
 |  | 
 | void NotifyMediaState(const char* path, MediaState state, boolean readOnly) | 
 | { | 
 |     const char* event = NULL; | 
 |     const char* propertyValue = NULL; | 
 |      | 
 |     switch (state) { | 
 |         case MEDIA_REMOVED: | 
 |             event = MOUNTD_MEDIA_REMOVED; | 
 |             propertyValue = EXTERNAL_STORAGE_REMOVED; | 
 |             break; | 
 |         case MEDIA_UNMOUNTED: | 
 |             event = MOUNTD_MEDIA_UNMOUNTED; | 
 |             propertyValue = EXTERNAL_STORAGE_UNMOUNTED; | 
 |             break; | 
 |         case MEDIA_MOUNTED: | 
 |             event = (readOnly ? MOUNTD_MEDIA_MOUNTED_READ_ONLY : MOUNTD_MEDIA_MOUNTED); | 
 |              propertyValue = (readOnly ? EXTERNAL_STORAGE_MOUNTED_READ_ONLY : EXTERNAL_STORAGE_MOUNTED); | 
 |            break; | 
 |         case MEDIA_SHARED: | 
 |             event = MOUNTD_MEDIA_SHARED; | 
 |             propertyValue = EXTERNAL_STORAGE_SHARED; | 
 |             break; | 
 |         case MEDIA_BAD_REMOVAL: | 
 |             event = MOUNTD_MEDIA_BAD_REMOVAL; | 
 |             propertyValue = EXTERNAL_STORAGE_BAD_REMOVAL; | 
 |             break; | 
 |         case MEDIA_UNMOUNTABLE: | 
 |             event = MOUNTD_MEDIA_UNMOUNTABLE; | 
 |             propertyValue = EXTERNAL_STORAGE_UNMOUNTABLE; | 
 |             break; | 
 |         default: | 
 |             LOG_ERROR("unknown MediaState %d in NotifyMediaState\n", state); | 
 |             return; | 
 |     } | 
 |      | 
 |     property_set(EXTERNAL_STORAGE_STATE, propertyValue); | 
 |     int result = Write2(event, path); | 
 |     if (result < 0 && state == MEDIA_UNMOUNTABLE) { | 
 |      | 
 |         // if we cannot communicate with the runtime, defer this message until the runtime is available | 
 |         sDeferredUnmountableMediaPath = strdup(path); | 
 |     } | 
 | } |