| /* |
| * 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); |
| } |
| } |