From 01fa4133d8456400cbe651b1ae7854616018ff3a Mon Sep 17 00:00:00 2001 From: "pr.jung" Date: Fri, 10 Mar 2017 16:25:33 +0900 Subject: [PATCH 2/2] Create initial commit Change-Id: I8a3f42cc179eb266a502dbf1c756a32a64b91f79 Signed-off-by: pr.jung Signed-off-by: taeyoung --- CMakeLists.txt | 54 + LICENSE | 204 +++ packaging/storaged.manifest | 5 + packaging/storaged.spec | 127 ++ scripts/storaged.conf | 23 + src/block/CMakeLists.txt | 51 + src/block/block.c | 3313 +++++++++++++++++++++++++++++++++++++++++++ src/block/block.conf | 7 + src/block/block.h | 177 +++ src/block/ext4.c | 175 +++ src/block/mmc-smack-label | 12 + src/block/mmc.c | 123 ++ src/block/utils.c | 189 +++ src/block/utils.h | 31 + src/block/vfat.c | 158 +++ src/core/dbus_main.c | 71 + src/core/dbus_main.h | 26 + src/core/main.c | 103 ++ src/core/modules.c | 165 +++ src/core/modules.h | 26 + src/shared/common.c | 144 ++ src/shared/common.h | 45 + src/shared/config-parser.c | 127 ++ src/shared/config-parser.h | 43 + src/shared/dbus.c | 1053 ++++++++++++++ src/shared/dbus.h | 276 ++++ src/shared/dbus_macro.h | 94 ++ src/shared/fd_handler.c | 133 ++ src/shared/fd_handler.h | 69 + src/shared/list.h | 62 + src/shared/log.h | 45 + src/shared/module-intf.h | 34 + src/shared/udev.c | 299 ++++ src/shared/udev.h | 52 + src/storage/CMakeLists.txt | 38 + src/storage/storage.c | 468 ++++++ src/storage/storage.conf | 7 + systemd/storaged.service | 14 + 38 files changed, 8043 insertions(+) create mode 100755 CMakeLists.txt create mode 100644 LICENSE create mode 100644 packaging/storaged.manifest create mode 100644 packaging/storaged.spec create mode 100644 scripts/storaged.conf create mode 100644 src/block/CMakeLists.txt create mode 100644 src/block/block.c create mode 100644 src/block/block.conf create mode 100644 src/block/block.h create mode 100644 src/block/ext4.c create mode 100755 src/block/mmc-smack-label create mode 100644 src/block/mmc.c create mode 100644 src/block/utils.c create mode 100644 src/block/utils.h create mode 100644 src/block/vfat.c create mode 100644 src/core/dbus_main.c create mode 100644 src/core/dbus_main.h create mode 100644 src/core/main.c create mode 100644 src/core/modules.c create mode 100644 src/core/modules.h create mode 100644 src/shared/common.c create mode 100644 src/shared/common.h create mode 100644 src/shared/config-parser.c create mode 100644 src/shared/config-parser.h create mode 100644 src/shared/dbus.c create mode 100644 src/shared/dbus.h create mode 100755 src/shared/dbus_macro.h create mode 100644 src/shared/fd_handler.c create mode 100644 src/shared/fd_handler.h create mode 100644 src/shared/list.h create mode 100644 src/shared/log.h create mode 100644 src/shared/module-intf.h create mode 100644 src/shared/udev.c create mode 100644 src/shared/udev.h create mode 100644 src/storage/CMakeLists.txt create mode 100755 src/storage/storage.c create mode 100644 src/storage/storage.conf create mode 100644 systemd/storaged.service diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..753ad5b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,54 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(storaged C) + +SET(CMAKE_VERBOSE_MAKEFILE OFF) + +SET(SRCS + src/core/dbus_main.c + src/core/main.c + src/core/modules.c +) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src/shared) + +SET(PKG_MODULES + argos_watchdog + capi-base-common + capi-system-info + dbus-1 + dlog + gio-2.0 + libsystemd + libtzplatform-config + libudev + vconf +) + +INCLUDE(FindPkgConfig) +pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED ${PKG_MODULES}) + +FOREACH(flag ${${PROJECT_NAME}_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -Werror -rdynamic") +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -fno-omit-frame-pointer -finstrument-functions") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -lrt -fPIE") +SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie") + +ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${${PROJECT_NAME}_pkgs_LDFLAGS} "-ldl" "-lm") +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin) + +INSTALL(FILES ${CMAKE_SOURCE_DIR}/scripts/storaged.conf DESTINATION /etc/dbus-1/system.d) +INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/systemd/ DESTINATION lib/systemd/system + FILES_MATCHING + PATTERN "storaged.service") + +IF(BLOCK_MODULE STREQUAL on) + ADD_SUBDIRECTORY(src/block) +ENDIF() +IF(STORAGE_MODULE STREQUAL on) + ADD_SUBDIRECTORY(src/storage) +ENDIF() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8f17f50 --- /dev/null +++ b/LICENSE @@ -0,0 +1,204 @@ +Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + diff --git a/packaging/storaged.manifest b/packaging/storaged.manifest new file mode 100644 index 0000000..97e8c31 --- /dev/null +++ b/packaging/storaged.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/storaged.spec b/packaging/storaged.spec new file mode 100644 index 0000000..3555d1a --- /dev/null +++ b/packaging/storaged.spec @@ -0,0 +1,127 @@ +#These options are DEACTIVATED by default. +%bcond_with emulator + +Name: storaged +Summary: Storaged +Version: 1.0.0 +Release: 1 +Group: System/Management +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +Source1: %{name}.manifest + +BuildRequires: cmake +BuildRequires: libattr-devel +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(capi-base-common) +BuildRequires: pkgconfig(dbus-1) +BuildRequires: pkgconfig(libudev) +BuildRequires: pkgconfig(gio-2.0) +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(libtzplatform-config) +BuildRequires: pkgconfig(capi-system-info) +BuildRequires: pkgconfig(argos_watchdog) +BuildRequires: pkgconfig(libsystemd) +BuildRequires: pkgconfig(storage) +BuildRequires: pkgconfig(app2sd) +BuildRequires: pkgconfig(blkid) +BuildRequires: pkgconfig(mount) +#BuildRequires: pkgconfig(deviced) +#BuildRequires: deviced-profile_mobile +Requires(post): /usr/bin/vconftool + +Requires: /usr/bin/fsck_msdosfs +Requires: /usr/bin/newfs_msdos + +%description +storage daemon. + +%package module_block +Summary: block module plugin +Requires: %{name} = %{version}-%{release} +%description module_block +block module plugin + +%package module_storage +Summary: storage module plugin +Requires: %{name} = %{version}-%{release} +%description module_storage +storage module plugin + +%prep +%setup -q +%if %{with emulator} + %define ARCH emulator +%else + %ifarch %{arm} aarch64 + %define ARCH arm + %else + %define ARCH x86 + %endif +%endif + +%ifarch %{arm} %ix86 + %define ARCH_BIT 32 +%else + %define ARCH_BIT 64 +%endif + +%if 0%{?tizen_build_devel_mode} == 1 +%define engineer_mode on +%else +%define engineer_mode off +%endif + +%cmake . \ + -DTZ_SYS_ETC=%TZ_SYS_ETC \ + -DCMAKE_INSTALL_PREFIX=%{_prefix} \ + -DARCH=%{ARCH} \ + -DARCH_BIT=%{ARCH_BIT} \ + -DENGINEER_MODE=%{engineer_mode} \ + -DPROFILE=mobile \ + -DBLOCK_MODULE=on \ + -DBLOCK_TMPFS=on \ + -DSTORAGE_MODULE=on \ + #eol + +%build +cp %{SOURCE1} . + +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install + +%install_service multi-user.target.wants storaged.service + +%post +systemctl daemon-reload +if [ "$1" == "1" ]; then + systemctl restart storaged.service +fi + +%postun +systemctl daemon-reload + +%files +%manifest %{name}.manifest +%license LICENSE +%config %{_sysconfdir}/dbus-1/system.d/storaged.conf +%{_unitdir}/multi-user.target.wants/storaged.service +%{_unitdir}/storaged.service +%{_bindir}/storaged + +%files module_block +%manifest %{name}.manifest +%license LICENSE +%{_libdir}/storaged/module_block.so +%{_bindir}/mmc-smack-label +%config %{_sysconfdir}/storaged/block.conf + +%files module_storage +%manifest %{name}.manifest +%license LICENSE +%{_libdir}/storaged/module_storage.so +%config %{_sysconfdir}/storaged/storage.conf diff --git a/scripts/storaged.conf b/scripts/storaged.conf new file mode 100644 index 0000000..ecd3199 --- /dev/null +++ b/scripts/storaged.conf @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + diff --git a/src/block/CMakeLists.txt b/src/block/CMakeLists.txt new file mode 100644 index 0000000..301980f --- /dev/null +++ b/src/block/CMakeLists.txt @@ -0,0 +1,51 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(module_block C) + +INCLUDE(FindPkgConfig) +pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED + app2sd + capi-system-info + blkid + dlog + dbus-1 + gio-2.0 + glib-2.0 + libudev + mount + storage + libtzplatform-config + vconf +) + +FOREACH(flag ${${PROJECT_NAME}_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wall -Werror -fvisibility=hidden -rdynamic") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g") + +IF(BLOCK_TMPFS STREQUAL on) + ADD_DEFINITIONS("-DBLOCK_TMPFS") +ENDIF(BLOCK_TMPFS STREQUAL on) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/share) + +FILE(GLOB ALL_SRCS "*.c") +SET(SRCS ${ALL_SRCS}) +SET(SHARED_SRCS + ../shared/common.c + ../shared/config-parser.c + ../shared/dbus.c + ../shared/fd_handler.c + ../shared/udev.c +) + +ADD_LIBRARY(${PROJECT_NAME} ${SRCS} ${SHARED_SRCS}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${${PROJECT_NAME}_pkgs_LDFLAGS}) + +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES PREFIX "") +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR}/storaged COMPONENT RuntimeLibraries) + +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/block.conf DESTINATION /etc/storaged) +INSTALL(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/mmc-smack-label DESTINATION bin) diff --git a/src/block/block.c b/src/block/block.c new file mode 100644 index 0000000..25e7e5e --- /dev/null +++ b/src/block/block.c @@ -0,0 +1,3313 @@ +/* + * storaged + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "config-parser.h" +#include "module-intf.h" +#include "udev.h" +#include "list.h" +#include "block.h" +#include "dbus.h" +#include "fd_handler.h" +#include "utils.h" + +/** + * TODO Assume root device is always mmcblk0*. + */ +#define MMC_PATH "*/mmcblk[0-9]*" +#define MMC_PARTITION_PATH "mmcblk[0-9]p[0-9]" +/* Emulator send devlink for sdcard as \*\/sdcard\/\* */ +#define MMC_LINK_PATH "*/sdcard/*" +#define SCSI_PATH "*/sd[a-z]*" +#define SCSI_PARTITION_PATH "sd[a-z][0-9]" +#define SCSI_PARTITION_LENGTH 9 + +#define FILESYSTEM "filesystem" + +#define DEV_PREFIX "/dev/" +#define ROOT_DIR "/" + +#define UNMOUNT_RETRY 5 +#define TIMEOUT_MAKE_OBJECT 500 /* milliseconds */ + +#define SIGNAL_BOOTING_DONE "BootingDone" +#define SIGNAL_POWEROFF_STATE "ChangeState" + +#define BLOCK_DEVICE_ADDED "DeviceAdded" +#define BLOCK_DEVICE_REMOVED "DeviceRemoved" +#define BLOCK_DEVICE_BLOCKED "DeviceBlocked" +#define BLOCK_DEVICE_CHANGED "DeviceChanged" +#define BLOCK_DEVICE_CHANGED_2 "DeviceChanged2" + +#define BLOCK_TYPE_MMC "mmc" +#define BLOCK_TYPE_SCSI "scsi" +#define BLOCK_TYPE_ALL "all" + +#define BLOCK_MMC_NODE_PREFIX "SDCard" +#define BLOCK_SCSI_NODE_PREFIX "USBDrive" + +#define BLOCK_CONF_FILE "/etc/storaged/block.conf" + +#define EXTERNAL_STORAGE_PATH "/run/external-storage" +#define PATH_LEN 55 + +/* Minimum value of block id */ +#define BLOCK_ID_MIN 10 +/* For 2.4 Backward Compatibility */ +#define EXT_PRIMARY_SD_FIXID 1 + +/* Maximum number of thread */ +#define THREAD_MAX 5 + +#define PKGDIR_BUS_NAME "org.tizen.pkgdir_tool" +#define PKGDIR_PATH "/org/tizen/pkgdir_tool" +#define PKGDIR_INTERFACE "org.tizen.pkgdir_tool" + +#define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0])) + +enum block_dev_operation { + BLOCK_DEV_MOUNT, + BLOCK_DEV_UNMOUNT, + BLOCK_DEV_FORMAT, + BLOCK_DEV_INSERT, + BLOCK_DEV_REMOVE, +}; + +enum private_operation_state { + REQ_NORMAL, + REQ_PRIVATE, + REQ_PRIVATE_FORMAT, +}; + +struct operation_queue { + enum block_dev_operation op; + dbus_method_reply_handle_h reply_handle; + void *data; + bool done; +}; + +struct block_device { + struct block_data *data; + dd_list *op_queue; + int thread_id; /* Current thread ID */ + bool removed; /* True when device is physically removed but operation is not precessed yet */ + enum private_operation_state on_private_op; + bool mount_point_updated; + pid_t private_pid; +}; + +struct format_data { + struct block_device *bdev; + char *fs_type; + enum unmount_operation option; +}; + +struct pipe_data { + enum block_dev_operation op; + struct block_device *bdev; + int result; +}; + +static struct block_conf { + bool multimount; +} block_conf[BLOCK_MMC_DEV + 1]; + +static struct manage_thread { + dd_list *th_node_list; /* List of devnode which thread dealt with. Only main thread access */ + dd_list *block_dev_list; /* Use thread mutex */ + pthread_t th; + pthread_mutex_t mutex; + pthread_cond_t cond; + int num_dev; /* Number of devices which thread holds. Only main thread access */ + int op_len; /* Number of operation of thread. Use thread mutex */ + int thread_id; /* Never changed */ + bool start_th; +} th_manager[THREAD_MAX]; + +static dd_list *fs_head; +static dd_list *block_ops_list; +static bool smack; +static int pfds[2]; +static fd_handler_h phandler; +static bool block_control = false; +static bool block_boot = false; + +/* Assume there is only one physical internal storage */ +static int dev_internal = -1; +static char dev_internal_scsi = '\0'; +static char dev_internal_emul = '\0'; + +static int add_operation(struct block_device *bdev, + enum block_dev_operation operation, + dbus_method_reply_handle_h reply_handle, void *data); +static void remove_operation(struct block_device *bdev); +static void check_removed(struct block_device *bdev, dd_list **queue, struct operation_queue **op); +static bool check_unmount(struct block_device *bdev, dd_list **queue, struct operation_queue **op); +static int change_mount_point(struct block_device *bdev, const char *mount_point); + +static void uevent_block_handler(struct udev_device *dev); +static struct uevent_handler uh = { + .subsystem = BLOCK_SUBSYSTEM, + .uevent_func = uevent_block_handler, +}; + +static void __CONSTRUCTOR__ smack_check(void) +{ + FILE *fp; + char buf[128]; + + fp = fopen("/proc/filesystems", "r"); + if (!fp) + return; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (strstr(buf, "smackfs")) { + smack = true; + break; + } + } + + fclose(fp); +} + +void add_fs(const struct block_fs_ops *fs) +{ + DD_LIST_APPEND(fs_head, (void *)fs); +} + +void remove_fs(const struct block_fs_ops *fs) +{ + DD_LIST_REMOVE(fs_head, (void *)fs); +} + +const struct block_fs_ops *find_fs(enum block_fs_type type) +{ + struct block_fs_ops *fs; + dd_list *elem; + + DD_LIST_FOREACH(fs_head, elem, fs) { + if (fs->type == type) + return fs; + } + return NULL; +} + +void add_block_dev(const struct block_dev_ops *ops) +{ + DD_LIST_APPEND(block_ops_list, (void *)ops); +} + +void remove_block_dev(const struct block_dev_ops *ops) +{ + DD_LIST_REMOVE(block_ops_list, (void *)ops); +} + +static void broadcast_block_info(enum block_dev_operation op, + struct block_data *data, int result) +{ + struct block_dev_ops *ops; + dd_list *elem; + + DD_LIST_FOREACH(block_ops_list, elem, ops) { + if (ops->block_type != data->block_type) + continue; + if (op == BLOCK_DEV_MOUNT) + ops->mounted(data, result); + else if (op == BLOCK_DEV_UNMOUNT) + ops->unmounted(data, result); + else if (op == BLOCK_DEV_FORMAT) + ops->formatted(data, result); + else if (op == BLOCK_DEV_INSERT) + ops->inserted(data); + else if (op == BLOCK_DEV_REMOVE) + ops->removed(data); + } +} + +// Called by MainThread - Insert +static int block_get_new_id(void) +{ + static int id = BLOCK_ID_MIN; + struct block_device *bdev; + dd_list *elem; + bool found; + int i, j; + + for (i = 0 ; i < INT_MAX ; i++) { + found = false; + for (j = 0; j < THREAD_MAX; j++) { + pthread_mutex_lock(&(th_manager[j].mutex)); + DD_LIST_FOREACH(th_manager[j].block_dev_list, elem, bdev) { + if (bdev->data->id == id) { + found = true; + break; + } + } + pthread_mutex_unlock(&(th_manager[j].mutex)); + if (found) + break; + } + + if (!found) + return id++; + + if (++id == INT_MAX) + id = BLOCK_ID_MIN; + } + + return -ENOENT; +} + +static void remove_file(int id) +{ + char file_name[PATH_LEN]; + int ret; + + if (id < 0) + return; + + snprintf(file_name, sizeof(file_name), EXTERNAL_STORAGE_PATH"/%d", id); + + ret = remove(file_name); + if (ret < 0) + _E("Fail to remove %s. errno: %d", file_name, errno); +} + +static void create_file(int id, char *mount_point) +{ + FILE *fp; + char file_name[PATH_LEN]; + + if (id < 0) + return; + + snprintf(file_name, PATH_LEN, EXTERNAL_STORAGE_PATH"/%d", id); + + fp = fopen(file_name, "w+"); + if (fp) { + fprintf(fp, "%s", mount_point); + fclose(fp); + } else + _E("Fail to open %s", file_name); +} + +static void signal_device_blocked(struct block_device *bdev) +{ + struct block_data *data; + char *arr[13]; + char str_block_type[32]; + char str_readonly[32]; + char str_state[32]; + char str_primary[32]; + char str_flags[32]; + char str_id[32]; + char *str_null = ""; + int flags; + + if (!bdev || !bdev->data) + return; + + data = bdev->data; + flags = 0; + + /* Broadcast outside with BlockManager iface */ + snprintf(str_block_type, sizeof(str_block_type), + "%d", data->block_type); + arr[0] = str_block_type; + arr[1] = (data->devnode ? data->devnode : str_null); + arr[2] = (data->syspath ? data->syspath : str_null); + arr[3] = (data->fs_usage ? data->fs_usage : str_null); + arr[4] = (data->fs_type ? data->fs_type : str_null); + arr[5] = (data->fs_version ? data->fs_version : str_null); + arr[6] = (data->fs_uuid_enc ? data->fs_uuid_enc : str_null); + snprintf(str_readonly, sizeof(str_readonly), + "%d", data->readonly); + arr[7] = str_readonly; + arr[8] = (data->mount_point ? data->mount_point : str_null); + snprintf(str_state, sizeof(str_state), + "%d", data->state); + arr[9] = str_state; + snprintf(str_primary, sizeof(str_primary), + "%d", data->primary); + arr[10] = str_primary; + snprintf(str_flags, sizeof(str_flags), "%d", flags); + arr[11] = str_flags; + snprintf(str_id, sizeof(str_id), "%d", data->id); + arr[12] = str_id; + + broadcast_dbus_signal(STORAGED_PATH_BLOCK_MANAGER, + STORAGED_INTERFACE_BLOCK_MANAGER, + BLOCK_DEVICE_BLOCKED, + "issssssisibii", arr); +} + +static void signal_device_changed(struct block_device *bdev, + enum block_dev_operation op) +{ + struct block_data *data; + char *arr[13]; + char str_block_type[32]; + char str_readonly[32]; + char str_state[32]; + char str_primary[32]; + char str_flags[32]; + char str_id[32]; + char *str_null = ""; + int flags; + + if (!bdev || !bdev->data) + return; + + data = bdev->data; + + switch (op) { + case BLOCK_DEV_MOUNT: + BLOCK_GET_MOUNT_FLAGS(data, flags); + break; + case BLOCK_DEV_UNMOUNT: + BLOCK_GET_UNMOUNT_FLAGS(data, flags); + break; + case BLOCK_DEV_FORMAT: + BLOCK_GET_FORMAT_FLAGS(data, flags); + break; + default: + flags = 0; + break; + } + + /* Broadcast outside with BlockManager iface */ + snprintf(str_block_type, sizeof(str_block_type), + "%d", data->block_type); + arr[0] = str_block_type; + arr[1] = (data->devnode ? data->devnode : str_null); + arr[2] = (data->syspath ? data->syspath : str_null); + arr[3] = (data->fs_usage ? data->fs_usage : str_null); + arr[4] = (data->fs_type ? data->fs_type : str_null); + arr[5] = (data->fs_version ? data->fs_version : str_null); + arr[6] = (data->fs_uuid_enc ? data->fs_uuid_enc : str_null); + snprintf(str_readonly, sizeof(str_readonly), + "%d", data->readonly); + arr[7] = str_readonly; + arr[8] = (data->mount_point ? data->mount_point : str_null); + snprintf(str_state, sizeof(str_state), + "%d", data->state); + arr[9] = str_state; + snprintf(str_primary, sizeof(str_primary), + "%d", data->primary); + arr[10] = str_primary; + snprintf(str_flags, sizeof(str_flags), "%d", flags); + arr[11] = str_flags; + snprintf(str_id, sizeof(str_id), "%d", data->id); + arr[12] = str_id; + + if (op == BLOCK_DEV_INSERT) + broadcast_dbus_signal(STORAGED_PATH_BLOCK_MANAGER, + STORAGED_INTERFACE_BLOCK_MANAGER, + BLOCK_DEVICE_ADDED, + "issssssisibii", arr); + else if (op == BLOCK_DEV_REMOVE) + broadcast_dbus_signal(STORAGED_PATH_BLOCK_MANAGER, + STORAGED_INTERFACE_BLOCK_MANAGER, + BLOCK_DEVICE_REMOVED, + "issssssisibii", arr); + else { + broadcast_dbus_signal(STORAGED_PATH_BLOCK_MANAGER, + STORAGED_INTERFACE_BLOCK_MANAGER, + BLOCK_DEVICE_CHANGED, + "issssssisibii", arr); + broadcast_dbus_signal(STORAGED_PATH_BLOCK_MANAGER, + STORAGED_INTERFACE_BLOCK_MANAGER, + BLOCK_DEVICE_CHANGED_2, + "issssssisibi", arr); + } +} + +static int get_mmc_mount_node(char *devnode, char *node, size_t len) +{ + char *name = devnode; + int dev = -1, part = -1; + char emul[32] = { 0, }; + int i; + + if (!name) + return -EINVAL; + + /* Check Target */ + sscanf(name, "mmcblk%dp%d", &dev, &part); + if (dev >= 0) { + if (part < 0) + snprintf(node, len, "%s%c", BLOCK_MMC_NODE_PREFIX, dev + 'A' - 1); + else + snprintf(node, len, "%s%c%d", BLOCK_MMC_NODE_PREFIX, dev + 'A' - 1, part); + return 0; + } + + /* Check Emulator */ + sscanf(name, "vd%s", emul); + if (emul[0] == '\0') + return -EINVAL; + for (i = 0 ; i < strlen(emul) ; i++) + emul[i] = toupper(emul[i]); + snprintf(node, len, "%s%s", BLOCK_MMC_NODE_PREFIX, emul); + return 0; +} + +static int get_scsi_mount_node(char *devnode, char *node, size_t len) +{ + char dev[64], *name; + int i; + + if (!devnode) + return -EINVAL; + + snprintf(dev, sizeof(dev), "%s", devnode); + + if (!strstr(dev, "sd")) + return -EINVAL; + + name = dev; + name += strlen("sd"); + + for (i = 0 ; i < strlen(name) ; i++) + name[i] = toupper(name[i]); + snprintf(node, len, "%s%s", BLOCK_SCSI_NODE_PREFIX, name); + + return 0; +} + +static char *generate_mount_path(struct block_data *data) +{ + const char *str; + char *name, node[64]; + int ret; + + if (!data || !data->devnode) + return NULL; + + name = strrchr(data->devnode, '/'); + if (!name) + goto out; + name++; + + switch (data->block_type) { + case BLOCK_MMC_DEV: + ret = get_mmc_mount_node(name, node, sizeof(node)); + break; + case BLOCK_SCSI_DEV: + ret = get_scsi_mount_node(name, node, sizeof(node)); + break; + default: + _E("Invalid block type (%d)", data->block_type); + return NULL; + } + if (ret < 0) + goto out; + + str = tzplatform_mkpath(TZ_SYS_MEDIA, node); + if (!str) + return NULL; + return strdup(str); + +out: + _E("Invalid devnode (%s)", data->devnode ? data->devnode : "NULL"); + return NULL; +} + +static bool check_primary_partition(const char *devnode) +{ + struct block_fs_ops *fs; + blkid_probe probe; + dd_list *elem; + const char *filesystem = NULL; + char *temp; + char str[PATH_MAX]; + char str2[PATH_MAX]; + size_t fs_len; + int len; + int ret; + int i; + bool found = false; + + if (fnmatch(MMC_LINK_PATH, devnode, 0) && + fnmatch(MMC_PATH, devnode, 0) && + fnmatch(SCSI_PATH, devnode, 0)) + return false; + + temp = strrchr(devnode, '/'); + if (!temp) + return false; + if (fnmatch("/"SCSI_PARTITION_PATH, temp, 0) && + fnmatch("/"MMC_PARTITION_PATH, temp, 0)) + return true; + + /* Emulator support only one partition */ + if (is_emulator()) + return true; + + snprintf(str, sizeof(str), "%s", devnode); + + len = strlen(str); + str[len - 1] = '\0'; + + for (i = 1; i <= 9; ++i) { + snprintf(str2, sizeof(str2), "%s%d", str, i); + if (access(str2, R_OK) != 0) + continue; + + probe = blkid_new_probe_from_filename(str2); + if (!probe) + continue; + if (blkid_do_probe(probe) != 0) + continue; + + ret = blkid_probe_lookup_value(probe, "TYPE", &filesystem, &fs_len); + if (ret < 0) { + blkid_free_probe(probe); + continue; + } + DD_LIST_FOREACH(fs_head, elem, fs) { + if (!strncmp(fs->name, filesystem, fs_len)) { + found = true; + break; + } + } + blkid_free_probe(probe); + if (!found) + continue; + break; + } + + if (found && !strncmp(devnode, str2, strlen(str2) + 1)) + return true; + + return false; +} + +/* Whole data in struct block_data should be freed. */ +static struct block_data *make_block_data(const char *devnode, + const char *syspath, + const char *fs_usage, + const char *fs_type, + const char *fs_version, + const char *fs_uuid_enc, + const char *readonly) +{ + struct block_data *data; + + /* devnode is unique value so it should exist. */ + if (!devnode) + return NULL; + + if (!fs_type) { + _E("Not support extended partition"); + return NULL; + } + + data = calloc(1, sizeof(struct block_data)); + if (!data) { + _E("calloc() failed"); + return NULL; + } + + data->devnode = strdup(devnode); + if (syspath) + data->syspath = strdup(syspath); + if (fs_usage) + data->fs_usage = strdup(fs_usage); + if (fs_type) + data->fs_type = strdup(fs_type); + if (fs_version) + data->fs_version = strdup(fs_version); + if (fs_uuid_enc) + data->fs_uuid_enc = strdup(fs_uuid_enc); + if (readonly) + data->readonly = atoi(readonly); + data->primary = check_primary_partition(devnode); + + /* TODO should we know block dev type? */ + if (!fnmatch(MMC_LINK_PATH, devnode, 0)) + data->block_type = BLOCK_MMC_DEV; + else if (!fnmatch(MMC_PATH, devnode, 0)) + data->block_type = BLOCK_MMC_DEV; + else if (!fnmatch(SCSI_PATH, devnode, 0)) + data->block_type = BLOCK_SCSI_DEV; + else + data->block_type = -1; + + data->mount_point = generate_mount_path(data); + BLOCK_FLAG_CLEAR_ALL(data); + + /* for 2.4 backward compatibility */ + if (data->primary == true && data->block_type == BLOCK_MMC_DEV) + data->id = EXT_PRIMARY_SD_FIXID; + else + data->id = block_get_new_id(); + + return data; +} + +static void free_block_data(struct block_data *data) +{ + if (!data) + return; + free(data->devnode); + free(data->syspath); + free(data->fs_usage); + free(data->fs_type); + free(data->fs_version); + free(data->fs_uuid_enc); + free(data->mount_point); + free(data); +} + +static int update_block_data(struct block_data *data, + const char *fs_usage, + const char *fs_type, + const char *fs_version, + const char *fs_uuid_enc, + const char *readonly, + bool mount_point_updated) +{ + if (!data) + return -EINVAL; + + free(data->fs_usage); + data->fs_usage = NULL; + if (fs_usage) + data->fs_usage = strdup(fs_usage); + + free(data->fs_type); + data->fs_type = NULL; + if (fs_type) + data->fs_type = strdup(fs_type); + + free(data->fs_version); + data->fs_version = NULL; + if (fs_version) + data->fs_version = strdup(fs_version); + + free(data->fs_uuid_enc); + data->fs_uuid_enc = NULL; + if (fs_uuid_enc) + data->fs_uuid_enc = strdup(fs_uuid_enc); + + /* generate_mount_path function should be invoked + * after fs_uuid_enc is updated */ + if (!mount_point_updated) { + free(data->mount_point); + data->mount_point = generate_mount_path(data); + } + + data->readonly = false; + if (readonly) + data->readonly = atoi(readonly); + + BLOCK_FLAG_MOUNT_CLEAR(data); + + return 0; +} + +static struct block_device *make_block_device(struct block_data *data) +{ + struct block_device *bdev; + + if (!data) + return NULL; + + bdev = calloc(1, sizeof(struct block_device)); + if (!bdev) + return NULL; + + bdev->data = data; + bdev->thread_id = -1; + bdev->removed = false; + bdev->on_private_op = REQ_NORMAL; + bdev->private_pid = 0; + bdev->mount_point_updated = false; + + return bdev; +} + +// Called by MainThread - Remove DevNode +static void free_block_device(struct block_device *bdev) +{ + dd_list *l, *next; + struct operation_queue *op; + int thread_id; + + if (!bdev) + return; + + thread_id = bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) + return; + + /* Remove file for block device /run/external-storage/id */ + remove_file(bdev->data->id); + + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + + th_manager[thread_id].num_dev--; + DD_LIST_REMOVE(th_manager[thread_id].block_dev_list, bdev); + free_block_data(bdev->data); + + DD_LIST_FOREACH_SAFE(bdev->op_queue, l, next, op) { + if (!op->done) + th_manager[thread_id].op_len--; + DD_LIST_REMOVE(bdev->op_queue, op); + free(op); + } + pthread_mutex_unlock(&(th_manager[thread_id].mutex)); + + free(bdev); +} + +// Called By MainThread - Remove Device +static struct block_device *find_block_device(const char *devnode) +{ + struct block_device *bdev; + dd_list *elem; + int len; + int i; + + len = strlen(devnode) + 1; + for (i = 0; i < THREAD_MAX; i++) { + pthread_mutex_lock(&(th_manager[i].mutex)); + DD_LIST_FOREACH(th_manager[i].block_dev_list, elem, bdev) { + if (bdev->data && !bdev->removed && + !strncmp(bdev->data->devnode, devnode, len)) { + pthread_mutex_unlock(&(th_manager[i].mutex)); + return bdev; + } + } + pthread_mutex_unlock(&(th_manager[i].mutex)); + } + + return NULL; +} + +// Called By MainThread - Mount,Unmount,Format,GetInfo +static struct block_device *find_block_device_by_id(int id) +{ + struct block_device *bdev; + dd_list *elem; + int i; + + for (i = 0; i < THREAD_MAX; i++) { + pthread_mutex_lock(&(th_manager[i].mutex)); + DD_LIST_FOREACH(th_manager[i].block_dev_list, elem, bdev) { + if (!bdev->data) + continue; + if (bdev->removed) + continue; + if (bdev->data->id == id) { + pthread_mutex_unlock(&(th_manager[i].mutex)); + return bdev; + } + } + pthread_mutex_unlock(&(th_manager[i].mutex)); + } + + return NULL; +} + +static char *get_operation_char(enum block_dev_operation op, + char *name, unsigned int len) +{ + char *str = "unknown"; + + if (!name) + return NULL; + + switch (op) { + case BLOCK_DEV_MOUNT: + str = "MOUNT"; + break; + case BLOCK_DEV_UNMOUNT: + str = "UNMOUNT"; + break; + case BLOCK_DEV_FORMAT: + str = "FORMAT"; + break; + case BLOCK_DEV_INSERT: + str = "INSERT"; + break; + case BLOCK_DEV_REMOVE: + str = "REMOVE"; + break; + default: + _E("invalid operation (%d)", op); + break; + } + + snprintf(name, len, "%s", str); + return name; +} + +static void create_external_apps_directory(void) +{ + int ret; + + ret = call_dbus_method_async(PKGDIR_BUS_NAME, PKGDIR_PATH, + PKGDIR_INTERFACE, "CreateExternalDirsForAllPkgs", + NULL, NULL, NULL, DBUS_TIMEOUT_USE_DEFAULT, NULL); + if (ret) + _E("Fail to create external directory"); +} + +static int pipe_trigger(enum block_dev_operation op, + struct block_device *bdev, int result) +{ + struct pipe_data pdata = { op, bdev, result }; + int n; + char name[16]; + + _D("op : %s, bdev : %p, result : %d", + get_operation_char(pdata.op, name, sizeof(name)), + pdata.bdev, pdata.result); + + n = write(pfds[1], &pdata, sizeof(struct pipe_data)); + + return (n != sizeof(struct pipe_data)) ? -EPERM : 0; +} + +static bool pipe_cb(int fd, void *data) +{ + struct pipe_data pdata = {0,}; + int n; + int thread_id; + int ret; + char name[16]; + + n = read(fd, &pdata, sizeof(pdata)); + if (n != sizeof(pdata) || !pdata.bdev) { + _E("fail to read struct pipe data"); + goto out; + } + + _I("op : %s, bdev : %p, result : %d", + get_operation_char(pdata.op, name, sizeof(name)), + pdata.bdev, pdata.result); + + if (pdata.op == BLOCK_DEV_MOUNT && pdata.result < 0) { + if (pdata.bdev->data->state == BLOCK_UNMOUNT) { + ret = change_mount_point(pdata.bdev, ""); + /* Modify /run/external-storage/id file */ + if (ret == 0) + create_file(pdata.bdev->data->id, pdata.bdev->data->mount_point); + } + goto out; + } + if (pdata.op == BLOCK_DEV_MOUNT && + pdata.bdev->data->state == BLOCK_MOUNT && + pdata.bdev->data->block_type == BLOCK_MMC_DEV && + pdata.bdev->data->primary) + create_external_apps_directory(); + + /* Broadcast to mmc and usb storage module */ + broadcast_block_info(pdata.op, pdata.bdev->data, pdata.result); + + /* Broadcast outside with Block iface */ + if (pdata.bdev->on_private_op == REQ_NORMAL) + signal_device_changed(pdata.bdev, pdata.op); + else if (pdata.bdev->on_private_op == REQ_PRIVATE) { + if (pdata.op == BLOCK_DEV_UNMOUNT) { + pdata.bdev->on_private_op = REQ_NORMAL; + _D("Private operation state: %d", pdata.bdev->on_private_op); + } + } else { + if (pdata.op == BLOCK_DEV_MOUNT) { + pdata.bdev->on_private_op = REQ_PRIVATE; + _D("Private operation state: %d", pdata.bdev->on_private_op); + } + } + + if (pdata.op == BLOCK_DEV_REMOVE) { + thread_id = pdata.bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) + return true; + free_block_device(pdata.bdev); + } + +out: + return true; +} + +static int pipe_init(void) +{ + int ret; + + ret = pipe2(pfds, O_CLOEXEC); + if (ret == -1) + return -errno; + + ret = add_fd_read_handler(pfds[0], pipe_cb, + NULL, NULL, &phandler); + if (ret < 0) { + _E("Failed to add pipe handler (%d)", ret); + return ret; + } + + return 0; +} + +static void pipe_exit(void) +{ + if (phandler) { + remove_fd_read_handler(&phandler); + phandler = NULL; + } + + if (pfds[0]) + close(pfds[0]); + if (pfds[1]) + close(pfds[1]); +} + +static int mmc_check_and_unmount(const char *path) +{ + int ret = 0; + int retry = 0; + + if (!path) + return 0; + + while (mount_check(path)) { + ret = umount(path); + if (ret < 0) { + retry++; + if (retry > UNMOUNT_RETRY) + return -errno; + } + } + return ret; +} + +static bool check_rw_mount(const char *szPath) +{ + struct statvfs mount_stat; + + if (!statvfs(szPath, &mount_stat)) { + if ((mount_stat.f_flag & ST_RDONLY) == ST_RDONLY) + return false; + } + return true; +} + +static int retrieve_udev_device(struct block_data *data, bool mount_point_updated) +{ + struct udev *udev; + struct udev_device *dev; + int r; + + if (!data) + return -EINVAL; + + udev = udev_new(); + if (!udev) { + _E("fail to create udev library context"); + return -EPERM; + } + + dev = udev_device_new_from_syspath(udev, data->syspath); + if (!dev) { + _E("fail to create new udev device"); + udev_unref(udev); + return -EPERM; + } + + r = update_block_data(data, + udev_device_get_property_value(dev, "ID_FS_USAGE"), + udev_device_get_property_value(dev, "ID_FS_TYPE"), + udev_device_get_property_value(dev, "ID_FS_VERSION"), + udev_device_get_property_value(dev, "ID_FS_UUID_ENC"), + udev_device_get_sysattr_value(dev, "ro"), + mount_point_updated); + if (r < 0) + _E("fail to update block data for %s", data->devnode); + + udev_device_unref(dev); + udev_unref(udev); + return r; +} + +static int block_mount(struct block_data *data) +{ + struct block_fs_ops *fs; + dd_list *elem; + int r; + int len; + + if (!data || !data->devnode || !data->mount_point) + return -EINVAL; + + /* check existing mounted */ + if (mount_check(data->mount_point)) + return -EEXIST; + + /* create mount point */ + if (access(data->mount_point, R_OK) != 0) { + if (mkdir(data->mount_point, 0755) < 0) + return -errno; + } + + /* check matched file system */ + if (!data->fs_usage || + strncmp(data->fs_usage, FILESYSTEM, + sizeof(FILESYSTEM)) != 0) { + r = -ENODEV; + goto out; + } + + if (!data->fs_type) { + _E("There is no file system"); + BLOCK_FLAG_SET(data, FS_EMPTY); + r = -ENODATA; + goto out; + } + + fs = NULL; + len = strlen(data->fs_type) + 1; + DD_LIST_FOREACH(fs_head, elem, fs) { + if (!strncmp(fs->name, data->fs_type, len)) + break; + } + + if (!fs) { + _E("Not supported file system (%s)", data->fs_type); + BLOCK_FLAG_SET(data, FS_NOT_SUPPORTED); + r = -ENOTSUP; + goto out; + } + + r = fs->mount(smack, data->devnode, data->mount_point); + + if (r == -EIO) + BLOCK_FLAG_SET(data, FS_BROKEN); + + if (r < 0) + goto out; + + r = check_rw_mount(data->mount_point); + if (!r) + return -EROFS; + + return 0; + +out: + rmdir(data->mount_point); + return r; +} + +static int mount_start(struct block_device *bdev) +{ + struct block_data *data; + int ret; + int r; + + assert(bdev); + assert(bdev->data); + + data = bdev->data; + _I("Mount Start : (%s -> %s)", + data->devnode, data->mount_point); + + /* mount operation */ + r = block_mount(data); + if (r != -EROFS && r < 0) { + _E("fail to mount %s device : %d", data->devnode, r); + goto out; + } + + if (r == -EROFS) { + data->readonly = true; + BLOCK_FLAG_SET(data, MOUNT_READONLY); + } + + data->state = BLOCK_MOUNT; + + if (data->block_type == BLOCK_MMC_DEV) { + /* app2ext_migrate_legacy_all has dbus method call to deviced */ + ret = app2ext_migrate_legacy_all(); + if (ret < 0) + _E("app2ext failed"); + } + +out: + if (r < 0 && r != -EROFS) + data->state = BLOCK_UNMOUNT; + + _I("%s result : %s, %d", __func__, data->devnode, r); + + if (pipe_trigger(BLOCK_DEV_MOUNT, bdev, r) < 0) + _E("fail to trigger pipe"); + + return r; +} + +static int change_mount_point(struct block_device *bdev, + const char *mount_point) +{ + struct block_data *data; + + if (!bdev) + return -EINVAL; + + data = bdev->data; + free(data->mount_point); + + /* If the mount path already exists, the path cannot be used */ + if (mount_point && + access(mount_point, F_OK) != 0) { + data->mount_point = strdup(mount_point); + bdev->mount_point_updated = true; + } else { + data->mount_point = generate_mount_path(data); + bdev->mount_point_updated = false; + } + + return 0; +} + +static int mount_block_device(struct block_device *bdev) +{ + struct block_data *data; + int r; + + if (!bdev || !bdev->data) + return -EINVAL; + + data = bdev->data; + if (data->state == BLOCK_MOUNT) { + _I("%s is already mounted", data->devnode); + return 0; + } + + if (!block_conf[data->block_type].multimount && + !data->primary) { + _I("Not support multi mount by config info"); + return 0; + } + + r = mount_start(bdev); + if (r < 0) { + _E("Failed to mount (%s)", data->devnode); + return r; + } + + return 0; +} + +static int block_unmount(struct block_device *bdev, + enum unmount_operation option) +{ + struct block_data *data; + int r, retry = 0; + struct timespec time = {0,}; + + if (!bdev || !bdev->data || !bdev->data->mount_point) + return -EINVAL; + + data = bdev->data; + + if (bdev->on_private_op == REQ_NORMAL) + signal_device_blocked(bdev); + + /* it must called before unmounting mmc */ + r = mmc_check_and_unmount(data->mount_point); + if (!r) + goto out; + if (option == UNMOUNT_NORMAL) { + _I("Failed to unmount with normal option : %d", r); + return r; + } + + _I("Execute force unmount!"); + /* Force Unmount Scenario */ + while (1) { + switch (retry++) { + case 0: + /* Mobile specific: + * should unmount the below vconf key. */ + if (data->block_type == BLOCK_MMC_DEV && data->primary) { + /* At first, notify to other app + * who already access sdcard */ + _I("Notify to other app who already access sdcard"); + vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, + VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED); + } + break; + case 1: + /* Second, kill app with SIGTERM */ + _I("Kill app with SIGTERM"); + terminate_process(data->mount_point, false); + break; + case 2: + /* Last time, kill app with SIGKILL */ + _I("Kill app with SIGKILL"); + terminate_process(data->mount_point, true); + break; + default: + if (umount2(data->mount_point, MNT_DETACH) != 0) { + _I("Failed to unmount with lazy option : %d", + errno); + return -errno; + } + goto out; + } + + /* it takes some seconds til other app completely clean up */ + time.tv_nsec = 500 * NANO_SECOND_MULTIPLIER; + nanosleep(&time, NULL); + + print_open_files(data->mount_point); + + r = mmc_check_and_unmount(data->mount_point); + if (!r) { + _D("Success to unmount (%s)", data->mount_point); + break; + } + } + +out: + data->state = BLOCK_UNMOUNT; + + if (rmdir(data->mount_point) < 0) + _E("fail to remove %s directory", data->mount_point); + + return r; +} + +static int unmount_block_device(struct block_device *bdev, + enum unmount_operation option) +{ + struct block_data *data; + int r; + + if (!bdev || !bdev->data) + return -EINVAL; + + data = bdev->data; + if (data->state == BLOCK_UNMOUNT) { + _I("%s is already unmounted", data->devnode); + r = mmc_check_and_unmount(data->mount_point); + if (r < 0) + _E("The path was existed, but could not delete it(%s)", + data->mount_point); + return 0; + } + + _I("Unmount Start : (%s -> %s)", + data->devnode, data->mount_point); + + r = block_unmount(bdev, option); + if (r < 0) { + _E("fail to unmount %s device : %d", data->devnode, r); + goto out; + } + + BLOCK_FLAG_MOUNT_CLEAR(data); + +out: + _I("%s result : %s, %d", __func__, data->devnode, r); + + if (pipe_trigger(BLOCK_DEV_UNMOUNT, bdev, r) < 0) + _E("fail to trigger pipe"); + + return r; +} + +static int block_format(struct block_data *data, + const char *fs_type, bool mount_point_updated) +{ + const struct block_fs_ops *fs; + dd_list *elem; + int len; + int r; + + if (!data || !data->devnode || !data->mount_point) + return -EINVAL; + + if (!fs_type) + fs_type = data->fs_type; + + fs = NULL; + len = strlen(fs_type); + DD_LIST_FOREACH(fs_head, elem, fs) { + if (!strncmp(fs->name, fs_type, len)) + break; + } + + if (!fs) { + BLOCK_FLAG_SET(data, FS_NOT_SUPPORTED); + _E("not supported file system(%s)", fs_type); + return -ENOTSUP; + } + + _I("format path : %s", data->devnode); + fs->check(data->devnode); + r = fs->format(data->devnode); + if (r < 0) { + _E("fail to format block data for %s", data->devnode); + goto out; + } + + /* need to update the partition data. + * It can be changed in doing format. */ + retrieve_udev_device(data, mount_point_updated); + +out: + return r; +} + +static int format_block_device(struct block_device *bdev, + const char *fs_type, + enum unmount_operation option) +{ + struct block_data *data; + int r; + + assert(bdev); + assert(bdev->data); + + data = bdev->data; + + _I("Format Start : (%s -> %s)", + data->devnode, data->mount_point); + + if (data->state == BLOCK_MOUNT) { + r = block_unmount(bdev, option); + if (r < 0) { + _E("fail to unmount %s device : %d", data->devnode, r); + goto out; + } + } + + r = block_format(data, fs_type, bdev->mount_point_updated); + if (r < 0) + _E("fail to format %s device : %d", data->devnode, r); + +out: + _I("%s result : %s, %d", __func__, data->devnode, r); + + r = pipe_trigger(BLOCK_DEV_FORMAT, bdev, r); + if (r < 0) + _E("fail to trigger pipe"); + + return r; +} + +static struct format_data *get_format_data( + const char *fs_type, enum unmount_operation option) +{ + struct format_data *fdata; + + fdata = (struct format_data *)malloc(sizeof(struct format_data)); + if (!fdata) { + _E("fail to allocate format data"); + return NULL; + } + + if (fs_type) + fdata->fs_type = strdup(fs_type); + else + fdata->fs_type = NULL; + fdata->option = option; + + return fdata; +} + +static void release_format_data(struct format_data *data) +{ + if (data) { + free(data->fs_type); + free(data); + } +} + +// Called by BlockThread - Real Mount Op +static int block_mount_device(struct block_device *bdev, void *data) +{ + dd_list *l; + int ret; + int thread_id; + + if (!bdev) + return -EINVAL; + + thread_id = bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) + return -EINVAL; + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + l = DD_LIST_FIND(th_manager[thread_id].block_dev_list, bdev); + pthread_mutex_unlock(&(th_manager[thread_id].mutex)); + if (!l) { + _E("(%d) does not exist in the device list", bdev->data->devnode); + return -ENOENT; + } + + /* mount automatically */ + ret = mount_block_device(bdev); + if (ret < 0) + _E("fail to mount block device for %s", bdev->data->devnode); + + return ret; +} + +// Called by BlockThread - Real Format Op +static int block_format_device(struct block_device *bdev, void *data) +{ + dd_list *l; + int ret; + int thread_id; + struct format_data *fdata = (struct format_data *)data; + + if (!bdev || !fdata) { + ret = -EINVAL; + goto out; + } + + thread_id = bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) + return -EINVAL; + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + l = DD_LIST_FIND(th_manager[thread_id].block_dev_list, bdev); + pthread_mutex_unlock(&(th_manager[thread_id].mutex)); + if (!l) { + _E("(%d) does not exist in the device list", bdev->data->devnode); + ret = -ENOENT; + goto out; + } + + ret = format_block_device(bdev, fdata->fs_type, fdata->option); + if (ret < 0) + _E("fail to mount block device for %s", bdev->data->devnode); + +out: + release_format_data(fdata); + + return ret; +} + +// Called by BlockThread - Real Unmount Op +static int block_unmount_device(struct block_device *bdev, void *data) +{ + int ret; + long option = (long)data; + + if (!bdev) + return -EINVAL; + + ret = unmount_block_device(bdev, option); + if (ret < 0) { + _E("Failed to unmount block device (%s)", bdev->data->devnode); + return ret; + } + + return 0; +} + +/* Called by BlockThread - Remove Operation + Direct Call at BlockThread + Previously this function was called by MainThread. However, it will increase complexity. + Need thread lock before to call remove_operation +*/ +static void remove_operation(struct block_device *bdev) +{ + struct operation_queue *op; + dd_list *l, *next; + char name[16]; + int thread_id; + + assert(bdev); + + thread_id = bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) + return; + + DD_LIST_FOREACH_SAFE(bdev->op_queue, l, next, op) { + if (op->done) { + _D("Remove operation (%s, %s)", + get_operation_char(op->op, name, sizeof(name)), + bdev->data->devnode); + + DD_LIST_REMOVE(bdev->op_queue, op); + free(op); + } + } +} + +static void block_send_dbus_reply(dbus_method_reply_handle_h reply_handle, int result) +{ + DBusMessage *rep; + + if (!reply_handle) + return; + + rep = make_dbus_reply_message_simple(reply_handle, result); + reply_dbus_method_result(reply_handle, rep); +} + +// Called by BlockThread +static void check_removed(struct block_device *bdev, dd_list **queue, struct operation_queue **op) +{ + struct operation_queue *temp; + dd_list *l; + int thread_id; + + if (!bdev) + return; + + if (!queue) + return; + + if (!op) + return; + + thread_id = bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) + return; + + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + + DD_LIST_FOREACH(*queue, l, temp) { + if (temp->op == BLOCK_DEV_REMOVE) { + *op = temp; + break; + } + temp->done = true; + th_manager[thread_id].op_len--; + block_send_dbus_reply((*op)->reply_handle, 0); + } + + remove_operation(bdev); + pthread_mutex_unlock(&(th_manager[thread_id].mutex)); +} + +// Called by BlockThread +static bool check_unmount(struct block_device *bdev, dd_list **queue, struct operation_queue **op) +{ + struct operation_queue *temp; + dd_list *l; + int thread_id; + bool unmounted = false; + + if (!bdev) + return false; + + if (!queue) + return false; + + if (!op) + return false; + + thread_id = bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) + return false; + + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + DD_LIST_FOREACH(*queue, l, temp) { + if (temp->op == BLOCK_DEV_UNMOUNT) { + unmounted = true; + _D("Operation queue has unmount operation"); + break; + } + } + pthread_mutex_unlock(&(th_manager[thread_id].mutex)); + + if (!unmounted) + return unmounted; + + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + + DD_LIST_FOREACH(*queue, l, temp) { + if (temp->op == BLOCK_DEV_UNMOUNT) { + *op = temp; + break; + } + temp->done = true; + th_manager[thread_id].op_len--; + block_send_dbus_reply((*op)->reply_handle, 0); + } + + remove_operation(bdev); + pthread_mutex_unlock(&(th_manager[thread_id].mutex)); + + return unmounted; +} + +// Called by BlockThread +static void trigger_operation(struct block_device *bdev, dd_list *queue, struct operation_queue *op) +{ + int ret = 0; + int thread_id; + char devnode[PATH_MAX]; + char name[16]; + enum block_dev_operation operation; + bool unmounted = false; + + assert(bdev); + + if (!queue) + return; + + thread_id = bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) + return; + + snprintf(devnode, sizeof(devnode), "%s", bdev->data->devnode); + + do { + if (!op) + return; + if (op->done) + return; + + operation = op->op; + + _D("Thread id %d Trigger operation (%s, %s)", thread_id, + get_operation_char(operation, name, sizeof(name)), devnode); + + unmounted = false; + if (operation == BLOCK_DEV_INSERT && bdev->removed) { + check_removed(bdev, &queue, &op); + operation = op->op; + _D("Trigger operation again (%s, %s)", + get_operation_char(operation, name, sizeof(name)), devnode); + } + if (operation == BLOCK_DEV_MOUNT) { + unmounted = check_unmount(bdev, &queue, &op); + if (unmounted) { + operation = op->op; + _D("Trigger operation again (%s, %s)", + get_operation_char(operation, name, sizeof(name)), devnode); + } + } + + switch (operation) { + case BLOCK_DEV_INSERT: + break; + case BLOCK_DEV_MOUNT: + ret = block_mount_device(bdev, op->data); + _D("Mount (%s) result:(%d)", devnode, ret); + break; + case BLOCK_DEV_FORMAT: + ret = block_format_device(bdev, op->data); + _D("Format (%s) result:(%d)", devnode, ret); + break; + case BLOCK_DEV_UNMOUNT: + ret = block_unmount_device(bdev, op->data); + _D("Unmount (%s) result:(%d)", devnode, ret); + break; + case BLOCK_DEV_REMOVE: + /* Do nothing */ + break; + default: + _E("Operation type is invalid (%d)", op->op); + ret = -EINVAL; + break; + } + + /* LOCK + * during checking the queue length */ + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + + op->done = true; + th_manager[thread_id].op_len--; + + block_send_dbus_reply(op->reply_handle, ret); + + queue = bdev->op_queue; + if (queue != NULL) { + queue = DD_LIST_NEXT(queue); + if (queue != NULL) + op = DD_LIST_NTH(queue, 0); + else + op = NULL; + } else + op = NULL; + + remove_operation(bdev); + + pthread_mutex_unlock(&(th_manager[thread_id].mutex)); + /* UNLOCK */ + + + if (operation == BLOCK_DEV_INSERT || operation == BLOCK_DEV_REMOVE) { + if (pipe_trigger(operation, bdev, 0) < 0) + _E("fail to trigger pipe"); + } + + } while (true); + +} + +// Called by BlockThread +static void *block_th_start(void *arg) +{ + struct block_device *temp; + struct manage_thread *th = (struct manage_thread *)arg; + struct operation_queue *op = NULL; + dd_list *elem; + dd_list *queue = NULL; + int thread_id; + + assert(th); + + thread_id = th->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) { + _E("Thread Number: %d", th->thread_id); + return NULL; + } + + do { + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + if (th_manager[thread_id].op_len == 0) { + _D("Operation queue of thread is empty"); + pthread_cond_wait(&(th_manager[thread_id].cond), &(th_manager[thread_id].mutex)); + _D("Wake up %d", thread_id); + } + + DD_LIST_FOREACH(th_manager[thread_id].block_dev_list, elem, temp) { + queue = temp->op_queue; + do { + op = DD_LIST_NTH(queue, 0); + if (!op) { + _D("Operation queue for device %s is Empty", temp->data->devnode); + break; + } + if (op->done) { + queue = DD_LIST_NEXT(queue); + continue; + } + break; + } while (true); + if (op) + break; + } + pthread_mutex_unlock(&(th_manager[thread_id].mutex)); + + if (op && !op->done) + trigger_operation(temp, queue, op); + + } while (true); +} + +// This function will be refactored later +// Especially, we don't need to keep th_node_list. +static int find_thread(char *devnode) +{ + dd_list *elem; + char str[PATH_MAX]; + char *th_node; + char *temp; + char dev_scsi; + int i, len, min, min_num; + int dev_mmc = -1, part = -1, num; + + len = 0; + if (!fnmatch("*/"MMC_PARTITION_PATH, devnode, 0)) { + sscanf(devnode, "/dev/mmcblk%dp%d", &dev_mmc, &part); + num = dev_mmc; + while (num > 0) { + num = num / 10; + len++; + } + len = len + 12; + snprintf(str, len, "/dev/mmcblk%d", dev_mmc); + th_node = strdup(str); + } else if (!fnmatch("*/"SCSI_PARTITION_PATH, devnode, 0)) { + sscanf(devnode, "/dev/sd%c%d", &dev_scsi, &part); + snprintf(str, SCSI_PARTITION_LENGTH, "/dev/sd%c", dev_scsi); + th_node = strdup(str); + } else + th_node = devnode; + + len = strlen(th_node) + 1; + min_num = 1000; + min = -1; + for (i = 0; i < THREAD_MAX; i++) { + DD_LIST_FOREACH(th_manager[i].th_node_list, elem, temp) { + if (!strncmp(temp, th_node, len)) + return i; + } + if (th_manager[i].num_dev < min_num) { + min_num = th_manager[i].num_dev; + min = i; + } + } + + if (min >= 0 && min < THREAD_MAX) { + DD_LIST_APPEND(th_manager[min].th_node_list, th_node); + return min; + } + + _E("Finding thread is failed"); + DD_LIST_APPEND(th_manager[0].th_node_list, th_node); + return 0; +} + +/* Only Main thread is permmited */ +// Called by MainThread +static int add_operation(struct block_device *bdev, + enum block_dev_operation operation, + dbus_method_reply_handle_h reply_handle, void *data) +{ + struct operation_queue *op; + int ret; + int thread_id; + bool start_th; + char name[16]; + + if (!bdev) + return -EINVAL; + + + _I("Add operation (%s, %s)", + get_operation_char(operation, name, sizeof(name)), + bdev->data->devnode); + + thread_id = bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) { + _E("Fail to find thread to add"); + return -EPERM; + } + + op = (struct operation_queue *)malloc(sizeof(struct operation_queue)); + if (!op) { + _E("malloc failed"); + return -ENOMEM; + } + + op->op = operation; + op->data = data; + op->reply_handle = reply_handle; + + /* LOCK + * during adding queue and checking the queue length */ + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + + /* Only modified between lock and unlock of mutex */ + op->done = false; + + start_th = th_manager[thread_id].start_th; + DD_LIST_APPEND(bdev->op_queue, op); + th_manager[thread_id].op_len++; + + if (th_manager[thread_id].op_len == 1 && !start_th) + pthread_cond_signal(&(th_manager[thread_id].cond)); + + pthread_mutex_unlock(&(th_manager[thread_id].mutex)); + /* UNLOCK */ + + /* Need to disble app2ext whenever unmounting mmc */ + if (op->op == BLOCK_DEV_UNMOUNT && + bdev->data->state == BLOCK_MOUNT && + bdev->data->block_type == BLOCK_MMC_DEV && + bdev->data->primary) + if (app2ext_disable_all_external_pkgs() < 0) + _E("app2ext_disable_all_external_pkgs() failed"); + + + if (start_th) { + _D("Start New thread for block device"); + th_manager[thread_id].start_th = false; + ret = pthread_create(&(th_manager[thread_id].th), NULL, block_th_start, &th_manager[thread_id]); + if (ret != 0) { + _E("fail to create thread for %s", bdev->data->devnode); + return -EPERM; + } + + pthread_detach(th_manager[thread_id].th); + } + + return 0; +} + +static bool disk_is_partitioned_by_kernel(struct udev_device *dev) +{ + DIR *dp; + struct dirent *dir; + const char *syspath; + bool ret = false; + + syspath = udev_device_get_syspath(dev); + if (!syspath) + goto out; + + dp = opendir(syspath); + if (!dp) { + _E("fail to open %s", syspath); + goto out; + } + + /* TODO compare devname and d_name */ + while ((dir = readdir(dp))) { + if (!fnmatch(MMC_PARTITION_PATH, dir->d_name, 0) || + !fnmatch(SCSI_PARTITION_PATH, dir->d_name, 0)) { + ret = true; + break; + } + } + + closedir(dp); + +out: + return ret; +} + +static bool check_partition(struct udev_device *dev) +{ + const char *devtype; + const char *part_table_type; + const char *fs_usage; + bool ret = false; + + /* only consider disk type, never partitions */ + devtype = udev_device_get_devtype(dev); + if (!devtype) + goto out; + + if (strncmp(devtype, BLOCK_DEVTYPE_DISK, + sizeof(BLOCK_DEVTYPE_DISK)) != 0) + goto out; + + part_table_type = udev_device_get_property_value(dev, + "ID_PART_TABLE_TYPE"); + if (part_table_type) { + fs_usage = udev_device_get_property_value(dev, + "ID_FS_USAGE"); + if (fs_usage && + strncmp(fs_usage, FILESYSTEM, sizeof(FILESYSTEM)) == 0) { + if (!disk_is_partitioned_by_kernel(dev)) + goto out; + } + ret = true; + goto out; + } + + if (disk_is_partitioned_by_kernel(dev)) { + ret = true; + goto out; + } + +out: + return ret; +} + +// Called by MainThread +static int add_block_device(struct udev_device *dev, const char *devnode) +{ + struct block_data *data; + struct block_device *bdev; + bool partition; + int ret; + int thread_id; + + partition = check_partition(dev); + if (partition) { + /* if there is a partition, skip this request */ + _I("%s device has partitions, skip this time", devnode); + return 0; + } + + data = make_block_data(devnode, + udev_device_get_syspath(dev), + udev_device_get_property_value(dev, "ID_FS_USAGE"), + udev_device_get_property_value(dev, "ID_FS_TYPE"), + udev_device_get_property_value(dev, "ID_FS_VERSION"), + udev_device_get_property_value(dev, "ID_FS_UUID_ENC"), + udev_device_get_sysattr_value(dev, "ro")); + if (!data) { + _E("fail to make block data for %s", devnode); + return -EPERM; + } + + if (!block_conf[data->block_type].multimount && !data->primary) { + _D("Not support multi mount by config info"); + return -EPERM; + } + + bdev = make_block_device(data); + if (!bdev) { + _E("fail to make block device for %s", devnode); + free_block_data(data); + return -EPERM; + } + + thread_id = find_thread(bdev->data->devnode); + if (thread_id < 0 || thread_id >= THREAD_MAX) { + _E("Fail to find thread to add"); + free_block_data(data); + free_block_device(bdev); + return -EPERM; + } + bdev->thread_id = thread_id; + + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + th_manager[thread_id].num_dev++; + DD_LIST_APPEND(th_manager[thread_id].block_dev_list, bdev); + + /* Create file for block device /run/external-storage/id */ + create_file(bdev->data->id, bdev->data->mount_point); + + pthread_mutex_unlock(&(th_manager[thread_id].mutex)); + + ret = add_operation(bdev, BLOCK_DEV_INSERT, NULL, (void *)data); + if (ret < 0) { + _E("Failed to add operation (insert %s)", devnode); + free_block_data(data); + free_block_device(bdev); + return ret; + } + + ret = add_operation(bdev, BLOCK_DEV_MOUNT, NULL, NULL); + if (ret < 0) { + _E("Failed to add operation (mount %s)", devnode); + free_block_data(data); + free_block_device(bdev); + return ret; + } + + return 0; +} + +static int remove_block_device(struct udev_device *dev, const char *devnode) +{ + struct block_device *bdev; + int ret; + + bdev = find_block_device(devnode); + if (!bdev) { + _E("fail to find block data for %s", devnode); + return -ENODEV; + } + + BLOCK_FLAG_SET(bdev->data, UNMOUNT_UNSAFE); + + bdev->removed = true; + if (bdev->on_private_op != REQ_NORMAL) { + bdev->on_private_op = REQ_NORMAL; + _D("Private operation state: %d", bdev->on_private_op); + } + + ret = add_operation(bdev, BLOCK_DEV_UNMOUNT, NULL, (void *)UNMOUNT_FORCE); + if (ret < 0) { + _E("Failed to add operation (unmount %s)", devnode); + return ret; + } + + ret = add_operation(bdev, BLOCK_DEV_REMOVE, NULL, NULL); + if (ret < 0) { + _E("Failed to add operation (remove %s)", devnode); + return ret; + } + + return 0; +} + +static int get_internal_storage_number(void) +{ + struct libmnt_table *t = NULL; + struct libmnt_fs *fs; + const char *temp; + char *name; + int r = 0, dev_temp; + + if ((!is_emulator() && (dev_internal >= 0 || dev_internal_scsi != '\0')) || + (is_emulator() && dev_internal_emul != '\0')) + return 0; + + t = mnt_new_table(); + if (!t) + return -EPERM; + + r = mnt_table_parse_mtab(t, NULL); + if (r < 0) { + mnt_free_table(t); + return -EPERM; + } + + fs = mnt_table_find_target(t, ROOT_DIR, MNT_ITER_BACKWARD); + + if (!fs) { + mnt_free_table(t); + return -EPERM; + } + temp = mnt_fs_get_srcpath(fs); + name = strrchr(temp, '/'); + if (!name) + return -EPERM; + name++; + /* Boot from USB is not handled */ + if (!is_emulator()) { + if (!fnmatch(MMC_PATH, temp, 0)) + sscanf(name, "mmcblk%d", &dev_internal); + else if (!fnmatch(SCSI_PATH, temp, 0)) + sscanf(name, "sd%c", &dev_internal_scsi); + } else { + if (!fnmatch(MMC_LINK_PATH, temp, 0)) + sscanf(name, "vd%c%d", &dev_internal_emul, &dev_temp); + else + dev_internal_emul = '\0'; + } + + mnt_free_table(t); + + return 0; +} + +static int check_external_storage(const char* devnode) +{ + char dev_scsi = '\0'; + char *name; + char emul = '\0'; + int dev_num = -1, dev_temp; + + if (!devnode) + return -EPERM; + + name = strrchr(devnode, '/'); + if (!name) + return -EPERM; + name++; + if (!is_emulator()) { + if (!fnmatch(MMC_PATH, devnode, 0)) { + sscanf(name, "mmcblk%d", &dev_num); + if (dev_internal == dev_num) { + _D("%s is internal storage", devnode); + return 0; + } + } else if (!fnmatch(SCSI_PATH, devnode, 0)) { + sscanf(name, "sd%c", &dev_scsi); + if (dev_internal_scsi == dev_scsi) { + _D("%s is internal storage", devnode); + return 0; + } + } + } else { + if (!fnmatch(MMC_LINK_PATH, devnode, 0)) { + sscanf(name, "vd%c%d", &emul, &dev_temp); + if (dev_internal_emul == emul) { + _D("%s is internal storage", devnode); + return 0; + } + } + } + + return 1; +} + +static int check_already_handled(const char* devnode) +{ + struct block_device *bdev; + struct block_data *data; + dd_list *elem; + int i; + + for (i = 0; i < THREAD_MAX; i++) { + pthread_mutex_lock(&(th_manager[i].mutex)); + DD_LIST_FOREACH(th_manager[i].block_dev_list, elem, bdev) { + data = bdev->data; + if (!data) + continue; + if (bdev->removed) + continue; + if (!strncmp(data->devnode, devnode, sizeof(devnode) + 1)) { + pthread_mutex_unlock(&(th_manager[i].mutex)); + return -1; + } + } + pthread_mutex_unlock(&(th_manager[i].mutex)); + } + + return 0; +} + +static int block_init_from_udev_enumerate(void) +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *list_entry, *list_sub_entry; + struct udev_device *dev; + const char *syspath; + const char *devnode; + int r = 0; + + udev = udev_new(); + if (!udev) { + _E("fail to create udev library context"); + return -EPERM; + } + + /* create a list of the devices in the 'usb' subsystem */ + enumerate = udev_enumerate_new(udev); + if (!enumerate) { + _E("fail to create an enumeration context"); + return -EPERM; + } + + if ((dev_internal < 0 && !is_emulator() && dev_internal_scsi == '\0') || + (is_emulator() && dev_internal_emul == '\0')) { + r = get_internal_storage_number(); + if (r < 0) + return -EPERM; + } + + udev_enumerate_add_match_subsystem(enumerate, BLOCK_SUBSYSTEM); + udev_enumerate_add_match_property(enumerate, + UDEV_DEVTYPE, BLOCK_DEVTYPE_DISK); + udev_enumerate_add_match_property(enumerate, + UDEV_DEVTYPE, BLOCK_DEVTYPE_PARTITION); + udev_enumerate_scan_devices(enumerate); + + udev_list_entry_foreach(list_entry, + udev_enumerate_get_list_entry(enumerate)) { + syspath = udev_list_entry_get_name(list_entry); + if (!syspath) + continue; + + dev = udev_device_new_from_syspath( + udev_enumerate_get_udev(enumerate), + syspath); + if (!dev) + continue; + + devnode = NULL; + udev_list_entry_foreach(list_sub_entry, + udev_device_get_devlinks_list_entry(dev)) { + const char *devlink = udev_list_entry_get_name(list_sub_entry); + if (!fnmatch(MMC_LINK_PATH, devlink, 0)) { + devnode = devlink; + break; + } + } + + if (!devnode) { + devnode = udev_device_get_devnode(dev); + if (!devnode) + continue; + + if (fnmatch(MMC_PATH, devnode, 0) && + fnmatch(SCSI_PATH, devnode, 0)) + continue; + } + + r = check_external_storage(devnode); + if (r <= 0) + continue; + + r = check_already_handled(devnode); + if (r < 0) { + _I("%s is already handled", devnode); + continue; + } + + _I("%s device add", devnode); + add_block_device(dev, devnode); + + udev_device_unref(dev); + } + + udev_enumerate_unref(enumerate); + udev_unref(udev); + return 0; +} + +// Called by MainThread +static void show_block_device_list(void) +{ + struct block_device *bdev; + struct block_data *data; + dd_list *elem; + int i; + + for (i = 0; i < THREAD_MAX; i++) { + pthread_mutex_lock(&(th_manager[i].mutex)); + DD_LIST_FOREACH(th_manager[i].block_dev_list, elem, bdev) { + data = bdev->data; + if (!data) + continue; + if (bdev->removed) + continue; + _D("%s:", data->devnode); + _D("\tSyspath: %s", data->syspath); + _D("\tBlock type: %s", + (data->block_type == BLOCK_MMC_DEV ? + BLOCK_TYPE_MMC : BLOCK_TYPE_SCSI)); + _D("\tFs type: %s", data->fs_type); + _D("\tFs usage: %s", data->fs_usage); + _D("\tFs version: %s", data->fs_version); + _D("\tFs uuid enc: %s", data->fs_uuid_enc); + _D("\tReadonly: %s", + (data->readonly ? "true" : "false")); + _D("\tMount point: %s", data->mount_point); + _D("\tMount state: %s", + (data->state == BLOCK_MOUNT ? + "mount" : "unmount")); + _D("\tPrimary: %s", + (data->primary ? "true" : "false")); + _D("\tID: %d", data->id); + } + pthread_mutex_unlock(&(th_manager[i].mutex)); + } +} + +// Called by MainThread +static void remove_whole_block_device(void) +{ + struct block_device *bdev; + dd_list *elem; + dd_list *next; + int r; + int i; + + for (i = 0; i < THREAD_MAX; i++) { + do { + pthread_mutex_lock(&(th_manager[i].mutex)); + DD_LIST_FOREACH_SAFE(th_manager[i].block_dev_list, elem, next, bdev) { + if (bdev->removed == false) + break; + } + pthread_mutex_unlock(&(th_manager[i].mutex)); + + if (bdev && bdev->removed == false) { + bdev->removed = true; + r = add_operation(bdev, BLOCK_DEV_UNMOUNT, NULL, (void *)UNMOUNT_NORMAL); + if (r < 0) + _E("Failed to add operation (unmount %s)", bdev->data->devnode); + + r = add_operation(bdev, BLOCK_DEV_REMOVE, NULL, NULL); + if (r < 0) + _E("Failed to add operation (remove %s)", bdev->data->devnode); + } else + break; + } while (true); + } +} + +static void booting_done(const char *sender_name, + const char *object_path, const char *interface_name, + const char *signal_name, DBusMessage *msg, + void *data) +{ + static int done = 0; + if (done > 0) + return; + done = 1; + _I("Booting done"); + /* if there is the attached device, try to mount */ + block_init_from_udev_enumerate(); + block_control = true; + block_boot = true; +} + +static void block_poweroff(const char *sender_name, + const char *object_path, const char *interface_name, + const char *signal_name, DBusMessage *msg, + void *data) +{ + static int status = 0; + if (status > 0) + return; + status = 1; + _I("Power off"); + /* unregister mmc uevent control routine */ + unregister_udev_uevent_control(&uh); + remove_whole_block_device(); +} + +static void uevent_block_handler(struct udev_device *dev) +{ + const char *devnode = NULL; + const char *action; + struct udev_list_entry *list_entry; + int r; + + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { + const char *devlink = udev_list_entry_get_name(list_entry); + if (!fnmatch(MMC_LINK_PATH, devlink, 0)) { + devnode = devlink; + break; + } + } + + if (!devnode) { + devnode = udev_device_get_devnode(dev); + if (!devnode) + return; + + if (fnmatch(MMC_PATH, devnode, 0) && + fnmatch(SCSI_PATH, devnode, 0)) + return; + } + + r = check_external_storage(devnode); + if (r <= 0) + return; + + action = udev_device_get_action(dev); + if (!action) + return; + + _I("%s device %s", devnode, action); + if (!strncmp(action, UDEV_ADD, sizeof(UDEV_ADD))) + add_block_device(dev, devnode); + else if (!strncmp(action, UDEV_REMOVE, sizeof(UDEV_REMOVE))) + remove_block_device(dev, devnode); +} + +static DBusMessage *request_mount_block(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg, bool onprivate) +{ + struct block_device *bdev; + char *mount_point; + int id; + int ret = -EBADMSG; + + if (!reply_handle || !msg) + goto out; + + ret = dbus_message_get_args(msg, NULL, + DBUS_TYPE_INT32, &id, + DBUS_TYPE_STRING, &mount_point, + DBUS_TYPE_INVALID); + if (!ret) + goto out; + + bdev = find_block_device_by_id(id); + if (!bdev) { + _E("Failed to find (%d) in the device list", id); + ret = -ENOENT; + goto out; + } + if (bdev->on_private_op != REQ_NORMAL) { + ret = -EPERM; + goto out; + } + + if (bdev->data->state == BLOCK_MOUNT) { + _I("%s is already mounted", bdev->data->devnode); + ret = -EALREADY; + goto out; + } + + if (onprivate) { + bdev->on_private_op = REQ_PRIVATE; + bdev->private_pid = get_dbus_method_sender_pid(reply_handle); + _D("Private operation state: %d", bdev->on_private_op); + } else { + if (bdev->on_private_op != REQ_NORMAL) { + _E("Failed to process mount operation"); + ret = -EPERM; + goto out; + } + } + + /* if requester want to use a specific mount point */ + if (mount_point && strncmp(mount_point, "", 1) != 0) { + ret = change_mount_point(bdev, mount_point); + if (ret < 0) { + ret = -EPERM; + goto out; + } + + /* Modify /run/external-storage/id file */ + create_file(bdev->data->id, bdev->data->mount_point); + } + + ret = add_operation(bdev, BLOCK_DEV_MOUNT, reply_handle, NULL); + if (ret < 0) { + _E("Failed to add operation (mount %s)", bdev->data->devnode); + goto out; + } + + return NULL; + +out: + return make_dbus_reply_message_simple(reply_handle, ret); +} + +static DBusMessage *request_public_mount_block(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg) +{ + return request_mount_block(reply_handle, msg, false); +} + +static DBusMessage *request_private_mount_block(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg) +{ + return request_mount_block(reply_handle, msg, true); +} + +static DBusMessage *request_unmount_block(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg, bool onprivate) +{ + struct block_device *bdev; + pid_t pid; + long option; + int id; + int ret = -EBADMSG; + + if (!reply_handle || !msg) + goto out; + + ret = dbus_message_get_args(msg, NULL, + DBUS_TYPE_INT32, &id, + DBUS_TYPE_INT32, &option, + DBUS_TYPE_INVALID); + if (!ret) + goto out; + + bdev = find_block_device_by_id(id); + if (!bdev) { + _E("Failed to find (%d) in the device list", id); + ret = -ENOENT; + goto out; + } + + if (onprivate) { + pid = get_dbus_method_sender_pid(reply_handle); + if (bdev->on_private_op == REQ_NORMAL || (bdev->on_private_op != REQ_NORMAL && pid != bdev->private_pid)) { + _E("Failed to process private unmount operation"); + ret = -EPERM; + goto out; + } + } else { + if (bdev->on_private_op != REQ_NORMAL) { + _E("Failed to process unmount operation"); + ret = -EPERM; + goto out; + } + } + + ret = add_operation(bdev, BLOCK_DEV_UNMOUNT, reply_handle, (void *)option); + if (ret < 0) { + _E("Failed to add operation (unmount %s)", bdev->data->devnode); + goto out; + } + + return NULL; + +out: + return make_dbus_reply_message_simple(reply_handle, ret); +} + +static DBusMessage *request_public_unmount_block(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg) +{ + return request_unmount_block(reply_handle, msg, false); +} + +static DBusMessage *request_private_unmount_block(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg) +{ + return request_unmount_block(reply_handle, msg, true); +} + +static DBusMessage *request_format_block(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg) +{ + struct block_device *bdev; + struct format_data *fdata; + pid_t pid; + int id; + int option; + int ret = -EBADMSG; + int prev_state; + + if (!reply_handle || !msg) + goto out; + + ret = dbus_message_get_args(msg, NULL, + DBUS_TYPE_INT32, &id, + DBUS_TYPE_INT32, &option, + DBUS_TYPE_INVALID); + if (!ret) + goto out; + + bdev = find_block_device_by_id(id); + if (!bdev) { + _E("Failed to find (%d) in the device list", id); + goto out; + } + + pid = get_dbus_method_sender_pid(reply_handle); + if (bdev->on_private_op != REQ_NORMAL && pid != bdev->private_pid) { + _E("Failed to format on private state"); + ret = -EPERM; + goto out; + } + + fdata = get_format_data(NULL, option); + if (!fdata) { + _E("Failed to get format data"); + goto out; + } + + prev_state = bdev->data->state; + if (prev_state == BLOCK_MOUNT) { + if (bdev->on_private_op == REQ_PRIVATE) { + bdev->on_private_op = REQ_PRIVATE_FORMAT; + _D("Private operation state: %d", bdev->on_private_op); + } + ret = add_operation(bdev, BLOCK_DEV_UNMOUNT, NULL, (void *)UNMOUNT_FORCE); + if (ret < 0) { + _E("Failed to add operation (unmount %s)", bdev->data->devnode); + release_format_data(fdata); + goto out; + } + } + + ret = add_operation(bdev, BLOCK_DEV_FORMAT, reply_handle, (void *)fdata); + if (ret < 0) { + _E("Failed to add operation (format %s)", bdev->data->devnode); + release_format_data(fdata); + } + + /* Maintain previous state of mount/unmount */ + if (prev_state == BLOCK_MOUNT) { + if (add_operation(bdev, BLOCK_DEV_MOUNT, NULL, NULL) < 0) { + _E("Failed to add operation (mount %s)", bdev->data->devnode); + goto out; + } + } + + return NULL; + +out: + return make_dbus_reply_message_simple(reply_handle, ret); +} + +static int add_device_to_iter(struct block_data *data, DBusMessageIter *piter) +{ + char *str_null = ""; + + if (!data || !piter) + return -EINVAL; + + dbus_message_iter_append_basic(piter, DBUS_TYPE_INT32, + &(data->block_type)); + dbus_message_iter_append_basic(piter, DBUS_TYPE_STRING, + data->devnode ? &(data->devnode) : &str_null); + dbus_message_iter_append_basic(piter, DBUS_TYPE_STRING, + data->syspath ? &(data->syspath) : &str_null); + dbus_message_iter_append_basic(piter, DBUS_TYPE_STRING, + data->fs_usage ? &(data->fs_usage) : &str_null); + dbus_message_iter_append_basic(piter, DBUS_TYPE_STRING, + data->fs_type ? &(data->fs_type) : &str_null); + dbus_message_iter_append_basic(piter, DBUS_TYPE_STRING, + data->fs_version ? &(data->fs_version) : &str_null); + dbus_message_iter_append_basic(piter, DBUS_TYPE_STRING, + data->fs_uuid_enc ? &(data->fs_uuid_enc) : &str_null); + dbus_message_iter_append_basic(piter, DBUS_TYPE_INT32, + &(data->readonly)); + dbus_message_iter_append_basic(piter, DBUS_TYPE_STRING, + data->mount_point ? &(data->mount_point) : &str_null); + dbus_message_iter_append_basic(piter, DBUS_TYPE_INT32, + &(data->state)); + dbus_message_iter_append_basic(piter, DBUS_TYPE_BOOLEAN, + &(data->primary)); + dbus_message_iter_append_basic(piter, DBUS_TYPE_INT32, + &(data->flags)); + dbus_message_iter_append_basic(piter, DBUS_TYPE_INT32, + &(data->id)); + + return 0; +} + + +static int add_device_to_struct_iter(struct block_data *data, DBusMessageIter *iter) +{ + DBusMessageIter piter; + + if (!data || !iter) + return -EINVAL; + + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &piter); + add_device_to_iter(data, &piter); + dbus_message_iter_close_container(iter, &piter); + + return 0; +} + +static int add_device_to_iter_2(struct block_data *data, DBusMessageIter *iter) +{ + DBusMessageIter piter; + char *str_null = ""; + + if (!data || !iter) + return -EINVAL; + + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &piter); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_INT32, + &(data->block_type)); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, + data->devnode ? &(data->devnode) : &str_null); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, + data->syspath ? &(data->syspath) : &str_null); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, + data->fs_usage ? &(data->fs_usage) : &str_null); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, + data->fs_type ? &(data->fs_type) : &str_null); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, + data->fs_version ? &(data->fs_version) : &str_null); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, + data->fs_uuid_enc ? &(data->fs_uuid_enc) : &str_null); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_INT32, + &(data->readonly)); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, + data->mount_point ? &(data->mount_point) : &str_null); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_INT32, + &(data->state)); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_BOOLEAN, + &(data->primary)); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_INT32, + &(data->flags)); + dbus_message_iter_close_container(iter, &piter); + + return 0; +} + +static DBusMessage *request_get_device_info(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + struct block_device *bdev; + struct block_data *data; + int ret, id; + + if (!reply_handle || !msg) + return NULL; + + reply = make_dbus_reply_message(reply_handle); + if (!reply) + goto out; + + ret = dbus_message_get_args(msg, NULL, + DBUS_TYPE_INT32, &id, + DBUS_TYPE_INVALID); + if (!ret) + goto out; + + bdev = find_block_device_by_id(id); + if (!bdev) + goto out; + data = bdev->data; + if (!data) + goto out; + + dbus_message_iter_init_append(reply, &iter); + add_device_to_iter(data, &iter); + +out: + return reply; +} + +static DBusMessage *request_show_device_list(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg) +{ + show_block_device_list(); + return make_dbus_reply_message(reply_handle); +} + +// Called by MainThread +static DBusMessage *request_get_device_list(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessageIter aiter; + DBusMessage *reply; + struct block_device *bdev; + struct block_data *data; + dd_list *elem; + char *type = NULL; + int ret = -EBADMSG; + int block_type; + int i; + + reply = make_dbus_reply_message(reply_handle); + + ret = dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &type, + DBUS_TYPE_INVALID); + if (!ret) { + _E("Failed to get args"); + goto out; + } + + if (!type) { + _E("Delivered type is NULL"); + goto out; + } + + _D("Block (%s) device list is requested", type); + + if (!strncmp(type, BLOCK_TYPE_SCSI, sizeof(BLOCK_TYPE_SCSI))) + block_type = BLOCK_SCSI_DEV; + else if (!strncmp(type, BLOCK_TYPE_MMC, sizeof(BLOCK_TYPE_MMC))) + block_type = BLOCK_MMC_DEV; + else if (!strncmp(type, BLOCK_TYPE_ALL, sizeof(BLOCK_TYPE_ALL))) + block_type = -1; + else { + _E("Invalid type (%s) is requested", type); + goto out; + } + + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(issssssisibii)", &aiter); + + for (i = 0; i < THREAD_MAX; i++) { + pthread_mutex_lock(&(th_manager[i].mutex)); + DD_LIST_FOREACH(th_manager[i].block_dev_list, elem, bdev) { + data = bdev->data; + if (!data) + continue; + if (bdev->removed) + continue; + + switch (block_type) { + case BLOCK_SCSI_DEV: + case BLOCK_MMC_DEV: + if (data->block_type != block_type) + continue; + break; + default: + break; + } + add_device_to_struct_iter(data, &aiter); + } + pthread_mutex_unlock(&(th_manager[i].mutex)); + } + dbus_message_iter_close_container(&iter, &aiter); + +out: + return reply; +} + +// Called by MainThread +static DBusMessage *request_get_device_list_2(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessageIter aiter; + DBusMessage *reply; + struct block_device *bdev; + struct block_data *data; + dd_list *elem; + char *type = NULL; + int ret = -EBADMSG; + int block_type; + int i; + + reply = make_dbus_reply_message(reply_handle); + + ret = dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &type, + DBUS_TYPE_INVALID); + if (!ret) { + _E("Failed to get args"); + goto out; + } + + if (!type) { + _E("Delivered type is NULL"); + goto out; + } + + _D("Block (%s) device list is requested", type); + + if (!strncmp(type, BLOCK_TYPE_SCSI, sizeof(BLOCK_TYPE_SCSI))) + block_type = BLOCK_SCSI_DEV; + else if (!strncmp(type, BLOCK_TYPE_MMC, sizeof(BLOCK_TYPE_MMC))) + block_type = BLOCK_MMC_DEV; + else if (!strncmp(type, BLOCK_TYPE_ALL, sizeof(BLOCK_TYPE_ALL))) + block_type = -1; + else { + _E("Invalid type (%s) is requested", type); + goto out; + } + + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(issssssisibi)", &aiter); + + for (i = 0; i < THREAD_MAX; i++) { + pthread_mutex_lock(&(th_manager[i].mutex)); + DD_LIST_FOREACH(th_manager[i].block_dev_list, elem, bdev) { + data = bdev->data; + if (!data) + continue; + if (bdev->removed) + continue; + + switch (block_type) { + case BLOCK_SCSI_DEV: + case BLOCK_MMC_DEV: + if (data->block_type != block_type) + continue; + break; + default: + break; + } + + add_device_to_iter_2(data, &aiter); + } + pthread_mutex_unlock(&(th_manager[i].mutex)); + } + dbus_message_iter_close_container(&iter, &aiter); + +out: + return reply; +} + +static DBusMessage *request_get_mmc_primary(dbus_method_reply_handle_h reply_handle, + DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + struct block_device *bdev; + struct block_data *data, nodata = {0,}; + dd_list *elem; + bool found; + int i; + + if (!reply_handle || !msg) + return NULL; + + reply = make_dbus_reply_message(reply_handle); + if (!reply) + goto out; + + found = false; + for (i = 0; i < THREAD_MAX; i++) { + pthread_mutex_lock(&(th_manager[i].mutex)); + DD_LIST_FOREACH(th_manager[i].block_dev_list, elem, bdev) { + data = bdev->data; + if (!data) + continue; + if (bdev->removed) + continue; + if (data->block_type != BLOCK_MMC_DEV) + continue; + if (!data->primary) + continue; + found = true; + break; + } + pthread_mutex_unlock(&(th_manager[i].mutex)); + if (found) + break; + } + + dbus_message_iter_init_append(reply, &iter); + if (found) + add_device_to_iter(data, &iter); + else { + nodata.id = -ENODEV; + add_device_to_iter(&nodata, &iter); + } + +out: + return reply; +} + +/* + Method name Method call format string Reply format string +{ "ShowDeviceList", NULL, NULL, request_show_device_list }, +{ "GetDeviceList" , "s", "a(issssssisibii)", request_get_device_list }, +{ "GetDeviceList2", "s", "a(issssssisibi)", request_get_device_list_2 }, +{ "Mount", "is", "i", request_public_mount_block }, +{ "Unmount", "ii", "i", request_public_unmount_block }, +{ "Format", "ii", "i", request_format_block }, +{ "GetDeviceInfo", "i", "(issssssisibii)", request_get_device_info }, +{ "GetMmcPrimary" , NULL, "(issssssisibii)" , request_get_mmc_primary }, +{ "PrivateMount", "is", "i", request_private_mount_block }, +{ "PrivateUnmount", "ii", "i", request_private_unmount_block }, +*/ + +static const dbus_method_s manager_methods[] = { + { "ShowDeviceList", NULL, request_show_device_list }, + { "GetDeviceList" , "s", request_get_device_list }, + { "GetDeviceList2", "s", request_get_device_list_2 }, + { "Mount", "is", request_public_mount_block }, + { "Unmount", "ii", request_public_unmount_block }, + { "Format", "ii", request_format_block }, + { "GetDeviceInfo", "i", request_get_device_info }, + { "GetMmcPrimary" , NULL, request_get_mmc_primary }, + { "PrivateMount", "is", request_private_mount_block }, + { "PrivateUnmount", "ii", request_private_unmount_block }, +}; + +static dbus_interface_s block_interface = { + .name = STORAGED_INTERFACE_BLOCK_MANAGER, + .methods = manager_methods, + .nr_methods = ARRAY_SIZE(manager_methods), +}; + +static int load_config(struct parse_result *result, void *user_data) +{ + int index; + + if (MATCH(result->section, "Block")) + return 0; + + if (MATCH(result->section, "SCSI")) + index = BLOCK_SCSI_DEV; + else if (MATCH(result->section, "MMC")) + index = BLOCK_MMC_DEV; + else + return -EINVAL; + + if (MATCH(result->name, "Multimount")) + block_conf[index].multimount = + (MATCH(result->value, "yes") ? true : false); + + return 0; +} + +#ifdef BLOCK_TMPFS +static int mount_root_path_tmpfs(void) +{ + int ret; + const char *root; + + root = tzplatform_getenv(TZ_SYS_MEDIA); + if (!root) + return -ENOTSUP; + + if (access(root, F_OK) != 0) + return -ENODEV; + + if (mount_check(root)) + return 0; + + ret = mount("tmpfs", root, "tmpfs", 0, "smackfsroot=System::Shared"); + if (ret < 0) { + ret = -errno; + _E("tmpfs mount failed (%d)", ret); + return ret; + } + + return 0; +} +#else +#define mount_root_path_tmpfs() 0 +#endif + +static void block_init(void *data) +{ + struct stat buf; + int ret; + int i; + dbus_handle_h handle; + + udev_init(NULL); + + /* load config */ + ret = config_parse(BLOCK_CONF_FILE, load_config, NULL); + if (ret < 0) + _E("fail to load %s, Use default value", BLOCK_CONF_FILE); + + ret = mount_root_path_tmpfs(); + if (ret < 0) + _E("Failed to mount tmpfs to root mount path (%d)", ret); + + ret = dbus_get_connection(&handle); + if (ret < 0) + _E("Failed to get dbus connection(%d)", ret); + + /* register block manager object and interface */ + ret = register_dbus_methods(handle, + STORAGED_PATH_BLOCK_MANAGER, &block_interface, + NULL, NULL); + if (ret < 0) + _E("Failed to register block interface and methods (%d)", ret); + + /* init pipe */ + ret = pipe_init(); + if (ret < 0) + _E("fail to init pipe"); + + /* register mmc uevent control routine */ + ret = register_udev_uevent_control(&uh); + if (ret < 0) + _E("fail to register block uevent : %d", ret); + + /* register notifier for below each event */ + register_dbus_signal(DEVICED_PATH_CORE, + DEVICED_INTERFACE_CORE, + SIGNAL_BOOTING_DONE, + booting_done, NULL, NULL); + + register_dbus_signal(DEVICED_PATH_POWEROFF, + DEVICED_INTERFACE_POWEROFF, + SIGNAL_POWEROFF_STATE, + block_poweroff, NULL, NULL); + + for (i = 0; i < THREAD_MAX; i++) { + th_manager[i].num_dev = 0; + th_manager[i].op_len = 0; + th_manager[i].start_th = true; + th_manager[i].thread_id = i; + pthread_mutex_init(&(th_manager[i].mutex), NULL); + pthread_cond_init(&(th_manager[i].cond), NULL); + } + + ret = stat(EXTERNAL_STORAGE_PATH, &buf); + if (ret < 0) { + ret = mkdir(EXTERNAL_STORAGE_PATH, 0755); + if (ret < 0) + _E("Failed to make directory: %d", errno); + } else if (!S_ISDIR(buf.st_mode)) { + ret = remove(EXTERNAL_STORAGE_PATH); + if (ret < 0) + _E("Fail to remove %s. errno: %d", EXTERNAL_STORAGE_PATH, errno); + ret = mkdir(EXTERNAL_STORAGE_PATH, 0755); + if (ret < 0) + _E("Failed to make directory: %d", errno); + } else { + ret = chmod(EXTERNAL_STORAGE_PATH, 644); + if (ret < 0) + _E("Fail to change permissions of a file"); + } +} + +static void block_exit(void *data) +{ + int ret, i; + + udev_exit(NULL); + + /* unregister notifier for below each event */ + unregister_dbus_signal(DEVICED_PATH_CORE, + DEVICED_INTERFACE_CORE, + SIGNAL_BOOTING_DONE, booting_done); + + unregister_dbus_signal(DEVICED_PATH_POWEROFF, + DEVICED_INTERFACE_POWEROFF, + SIGNAL_POWEROFF_STATE, block_poweroff); + + /* exit pipe */ + pipe_exit(); + + /* unregister mmc uevent control routine */ + ret = unregister_udev_uevent_control(&uh); + if (ret < 0) + _E("fail to unregister block uevent : %d", ret); + + /* remove remaining blocks */ + remove_whole_block_device(); + + for (i = 0; i < THREAD_MAX; i++) { + if (!th_manager[i].start_th) + pthread_cancel(th_manager[i].th); + } + + block_control = false; +} + +static int block_start(void *data) +{ + int ret; + + if (!block_boot) { + _E("Cannot be started. Booting is not ready"); + return -ENODEV; + } + + if (block_control) { + _I("Already started"); + return 0; + } + + /* register mmc uevent control routine */ + ret = register_udev_uevent_control(&uh); + if (ret < 0) + _E("fail to register block uevent : %d", ret); + + block_init_from_udev_enumerate(); + + block_control = true; + + _I("start"); + return 0; +} + +static int block_stop(void *data) +{ + if (!block_boot) { + _E("Cannot be stopped. Booting is not ready"); + return -ENODEV; + } + + if (!block_control) { + _I("Already stopped"); + return 0; + } + + /* unregister mmc uevent control routine */ + unregister_udev_uevent_control(&uh); + + /* remove the existing blocks */ + remove_whole_block_device(); + + block_control = false; + + _I("stop"); + return 0; +} + +static storaged_module_interface block_module = { + .name = "block", + .init = block_init, + .exit = block_exit, + .start = block_start, + .stop = block_stop, +}; + +__attribute__ ((visibility("default")))storaged_module_interface * +storaged_get_module_interface(void) +{ + return &block_module; +} diff --git a/src/block/block.conf b/src/block/block.conf new file mode 100644 index 0000000..8898468 --- /dev/null +++ b/src/block/block.conf @@ -0,0 +1,7 @@ +[Block] + +[MMC] +Multimount=no # yes or no + +[SCSI] +Multimount=yes # yes or no diff --git a/src/block/block.h b/src/block/block.h new file mode 100644 index 0000000..5b36326 --- /dev/null +++ b/src/block/block.h @@ -0,0 +1,177 @@ +/* + * storaged + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#ifndef __BLOCK_H__ +#define __BLOCK_H__ + +#include +#include "common.h" + +#define SMACKFS_MOUNT_OPT "smackfsroot=*,smackfsdef=*" + +#define RETRY_COUNT 10 + +enum block_fs_type { + FS_TYPE_VFAT = 0, + FS_TYPE_EXT4, +}; + +struct block_fs_ops { + enum block_fs_type type; + const char *name; + bool (*match) (const char *); + int (*check) (const char *); + int (*mount) (bool, const char *, const char *); + int (*format) (const char *); +}; + +struct fs_check { + int type; + char *name; + unsigned int offset; + unsigned int magic_sz; + char magic[4]; +}; + +void add_fs(const struct block_fs_ops *fs); +void remove_fs(const struct block_fs_ops *fs); + +enum block_device_type { + BLOCK_SCSI_DEV, + BLOCK_MMC_DEV, +}; + +enum mount_state { + BLOCK_UNMOUNT, + BLOCK_MOUNT, +}; + +enum unmount_operation { + UNMOUNT_NORMAL, + UNMOUNT_FORCE, +}; + +struct block_data { + enum block_device_type block_type; + char *devnode; + char *syspath; + char *fs_usage; + char *fs_type; + char *fs_version; + char *fs_uuid_enc; + bool readonly; + char *mount_point; + enum mount_state state; + bool primary; /* the first partition */ + int flags; + int id; /* libstorage uses the id as the storage_id */ +}; + +struct block_dev_ops { + const char *name; + enum block_device_type block_type; + void (*mounted) (struct block_data *data, int result); + void (*unmounted) (struct block_data *data, int result); + void (*formatted) (struct block_data *data, int result); + void (*inserted) (struct block_data *data); + void (*removed) (struct block_data *data); +}; + +void add_block_dev(const struct block_dev_ops *ops); +void remove_block_dev(const struct block_dev_ops *ops); + +#define BLOCK_DEVICE_OPS_REGISTER(dev) \ +static void __CONSTRUCTOR__ block_dev_init(void) \ +{ \ + add_block_dev(dev); \ +} \ +static void __DESTRUCTOR__ block_dev_exit(void) \ +{ \ + remove_block_dev(dev); \ +} + +enum block_flags { + FLAG_NONE = 0, + UNMOUNT_UNSAFE = 1 << 0, + FS_BROKEN = 1 << 1, + FS_EMPTY = 1 << 2, + FS_NOT_SUPPORTED = 1 << 3, + MOUNT_READONLY = 1 << 4, +}; + +/* a: struct block_data + * b: enum block_flags */ +#define BLOCK_FLAG_SET(a, b) \ + do { \ + if (a) { \ + (((a)->flags) |= (b)); \ + } \ + } while (0) + +#define BLOCK_FLAG_UNSET(a, b) \ + do { \ + if (a) { \ + (((a)->flags) &= ~(b)); \ + } \ + } while (0) + +#define BLOCK_IS_FLAG_SET(a, b) \ + ((a) ? ((((a)->flags) & (b)) ? true : false) : false) + +#define BLOCK_FLAG_CLEAR_ALL(a) \ + do { \ + if (a) { \ + ((a)->flags) = FLAG_NONE; \ + } \ + } while (0) + +#define BLOCK_FLAG_MOUNT_CLEAR(a) \ + do { \ + BLOCK_FLAG_UNSET((a), FS_BROKEN); \ + BLOCK_FLAG_UNSET((a), FS_EMPTY); \ + BLOCK_FLAG_UNSET((a), FS_NOT_SUPPORTED); \ + BLOCK_FLAG_UNSET((a), MOUNT_READONLY); \ + } while (0) + +#define BLOCK_FLAG_UNMOUNT_CLEAR(a) \ + do { \ + BLOCK_FLAG_UNSET((a), UNMOUNT_UNSAFE); \ + } while (0) + +#define BLOCK_GET_MOUNT_FLAGS(a, c) \ + do { \ + (c) = (a)->flags; \ + (c) &= ~UNMOUNT_UNSAFE; \ + } while (0) + +#define BLOCK_GET_UNMOUNT_FLAGS(a, c) \ + do { \ + (c) = 0; \ + if (BLOCK_IS_FLAG_SET((a), UNMOUNT_UNSAFE)) \ + (c) |= UNMOUNT_UNSAFE; \ + } while (0) + +#define BLOCK_GET_FORMAT_FLAGS(a, c) \ + do { \ + (c) = 0; \ + if (BLOCK_IS_FLAG_SET((a), FS_NOT_SUPPORTED)) \ + (c) |= FS_NOT_SUPPORTED; \ + } while (0) + +#endif /* __BLOCK_H__ */ diff --git a/src/block/ext4.c b/src/block/ext4.c new file mode 100644 index 0000000..3d1551a --- /dev/null +++ b/src/block/ext4.c @@ -0,0 +1,175 @@ +/* + * storaged + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "log.h" +#include "block.h" + +#define FS_EXT4_NAME "ext4" + +static const char *check_smack_arg[] = { + "/usr/bin/mmc-smack-label", + NULL, NULL, +}; + +static const char *ext4_arg[] = { + "/sbin/mkfs.ext4", + NULL, NULL, +}; + +static const char *ext4_check_arg[] = { + "/sbin/fsck.ext4", + "-f", "-y", NULL, NULL, +}; + +static struct fs_check ext4_info = { + FS_TYPE_EXT4, + "ext4", + 0x438, + 2, + {0x53, 0xef}, +}; + +static bool ext4_match(const char *devpath) +{ + char buf[4]; + int fd, r; + + fd = open(devpath, O_RDONLY); + if (fd < 0) { + _E("failed to open fd(%s) : %d", devpath, errno); + return false; + } + + /* check fs type with magic code */ + r = lseek(fd, ext4_info.offset, SEEK_SET); + if (r < 0) + goto error; + + r = read(fd, buf, 2); + if (r < 0) + goto error; + + _I("ext4 search magic : 0x%2x, 0x%2x", buf[0], buf[1]); + if (memcmp(buf, ext4_info.magic, ext4_info.magic_sz)) + goto error; + + close(fd); + _I("Storage type : %s", ext4_info.name); + return true; + +error: + close(fd); + _E("failed to match with ext4(%s)", devpath); + return false; +} + +static int ext4_check(const char *devpath) +{ + int argc; + argc = ARRAY_SIZE(ext4_check_arg); + ext4_check_arg[argc - 2] = devpath; + return run_child(argc, ext4_check_arg); +} + +static int mmc_check_smack(const char *mount_point) +{ + char buf[NAME_MAX] = {0,}; + int argc; + int ret; + + snprintf(buf, sizeof(buf), "%s", mount_point); + + argc = ARRAY_SIZE(check_smack_arg); + check_smack_arg[argc - 2] = buf; + ret = run_child(argc, check_smack_arg); + + return ret; +} + +static int check_smack_popup(void) +{ + /* TODO: show smack popup */ + return 0; +} + +static int ext4_mount(bool smack, const char *devpath, const char *mount_point) +{ + int r, retry = RETRY_COUNT; + struct timespec time = {0,}; + unsigned long mountflags = MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_DIRSYNC; + + do { + r = mount(devpath, mount_point, "ext4", mountflags, NULL); + if (!r) { + _I("Storage Mounted[ext4]"); + if (smack) { + check_smack_popup(); + mmc_check_smack(mount_point); + } + return 0; + } + _I("mount fail : r = %d, err = %d", r, errno); + time.tv_nsec = 100 * NANO_SECOND_MULTIPLIER; + nanosleep(&time, NULL); + if (r < 0 && errno == EROFS) + mountflags |= MS_RDONLY; + } while (r < 0 && (errno == ENOENT || errno == EROFS) && retry-- > 0); + + return -errno; +} + +static int ext4_format(const char *devpath) +{ + int argc; + argc = ARRAY_SIZE(ext4_arg); + ext4_arg[argc - 2] = devpath; + return run_child(argc, ext4_arg); +} + +static const struct block_fs_ops ext4_ops = { + .type = FS_TYPE_EXT4, + .name = "ext4", + .match = ext4_match, + .check = ext4_check, + .mount = ext4_mount, + .format = ext4_format, +}; + +static void __CONSTRUCTOR__ module_init(void) +{ + add_fs(&ext4_ops); +} +/* +static void __DESTRUCTOR__ module_exit(void) +{ + remove_fs(&ext4_ops); +} +*/ diff --git a/src/block/mmc-smack-label b/src/block/mmc-smack-label new file mode 100755 index 0000000..c206758 --- /dev/null +++ b/src/block/mmc-smack-label @@ -0,0 +1,12 @@ +#!/bin/bash + +PATH=/bin:/usr/bin:/sbin:/usr/sbin + +source /etc/tizen-platform.conf +MOUNT_DIRECTORY=$1 +find $MOUNT_DIRECTORY -type d | xargs chsmack -a '*' -t +find $MOUNT_DIRECTORY -type f | xargs chsmack -a '*' +find $MOUNT_DIRECTORY -type d | xargs chmod 770 +find $MOUNT_DIRECTORY -type f | xargs chmod 660 +find $MOUNT_DIRECTORY -type d | xargs chown root:priv_externalstorage +find $MOUNT_DIRECTORY -type f | xargs chown root:priv_externalstorage diff --git a/src/block/mmc.c b/src/block/mmc.c new file mode 100644 index 0000000..674cdcc --- /dev/null +++ b/src/block/mmc.c @@ -0,0 +1,123 @@ +/* + * storaged + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#include +#include +#include +#include + +#include "log.h" +#include "common.h" +#include "block.h" + +static void mmc_update_state(int state) +{ + static int old = -1; + + if (old == state) + return; + + if (vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, state) == 0) + old = state; +} + +static void mmc_update_mount_state(int state) +{ + static int old = -1; + + if (old == state) + return; + + if (vconf_set_int(VCONFKEY_SYSMAN_MMC_MOUNT, state) == 0) + old = state; +} + +static void mmc_mount(struct block_data *data, int result) +{ + int r; + + /* Only the primary partition is valid. */ + if (!data || !data->primary) + return; + + if (result < 0) { + mmc_update_state(VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED); + mmc_update_mount_state(VCONFKEY_SYSMAN_MMC_MOUNT_FAILED); + return; + } + + /* Give a transmutable attribute to mount_point */ + r = setxattr(data->mount_point, "security.SMACK64TRANSMUTE", + "TRUE", strlen("TRUE"), 0); + if (r < 0) + _E("setxattr error : %d", errno); + + mmc_update_state(VCONFKEY_SYSMAN_MMC_MOUNTED); + mmc_update_mount_state(VCONFKEY_SYSMAN_MMC_MOUNT_COMPLETED); +} + +static void mmc_unmount(struct block_data *data, int result) +{ + /* Only the primary partition is valid. */ + if (!data || !data->primary) + return; + + if (result == 0) + mmc_update_state(VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED); + else + mmc_update_state(VCONFKEY_SYSMAN_MMC_MOUNTED); +} + +static void mmc_format(struct block_data *data, int result) +{ + /* Only the primary partition is valid. */ + if (!data || !data->primary) + return; + + if (data->state == BLOCK_MOUNT) { + mmc_update_state(VCONFKEY_SYSMAN_MMC_MOUNTED); + mmc_update_mount_state(VCONFKEY_SYSMAN_MMC_MOUNT_COMPLETED); + } +} + +static void mmc_insert(struct block_data *data) +{ + /* Do nothing */ +} + +static void mmc_remove(struct block_data *data) +{ + /* Only the primary partition is valid. */ + if (!data || !data->primary) + return; + + mmc_update_state(VCONFKEY_SYSMAN_MMC_REMOVED); +} + +const struct block_dev_ops mmc_block_ops = { + .name = "mmc", + .block_type = BLOCK_MMC_DEV, + .mounted = mmc_mount, + .unmounted = mmc_unmount, + .formatted = mmc_format, + .inserted = mmc_insert, + .removed = mmc_remove, +}; + +BLOCK_DEVICE_OPS_REGISTER(&mmc_block_ops) diff --git a/src/block/utils.c b/src/block/utils.c new file mode 100644 index 0000000..c12ece7 --- /dev/null +++ b/src/block/utils.c @@ -0,0 +1,189 @@ +/* + * storaged + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "common.h" + +int print_open_files(const char *mount_point) +{ + DIR *dp; + struct dirent *dentry; + + DIR *dp_child; + struct dirent *dentry_child; + + int pid = -1, fd; + int ret; + char buf[PATH_MAX]; + char buf2[PATH_MAX]; + + char cmdline[PATH_MAX]; + char check_path[PATH_MAX]; + + int len = strlen(mount_point); + + dp = opendir("/proc"); + if (!dp) { + _E("FAIL: open /proc"); + return -1; + } + + while (1) { + dentry = readdir(dp); + if (dentry == NULL) + break; + + if (!isdigit(dentry->d_name[0])) + continue; + + pid = atoi(dentry->d_name); + snprintf(buf, PATH_MAX, "/proc/%d/cmdline", pid); + + fd = open(buf, O_RDONLY); + if (fd < 0) + continue; + ret = read(fd, cmdline, PATH_MAX); + close(fd); + + if (ret < 0 || ret >= PATH_MAX) + continue; + cmdline[ret] = '\0'; + + snprintf(buf, PATH_MAX, "/proc/%d/fd", pid); + dp_child = opendir(buf); + if (!dp_child) + continue; + while (1) { + dentry_child = readdir(dp_child); + if (dentry_child == NULL) + break; + + snprintf(check_path, PATH_MAX, "%s/%s", buf, dentry_child->d_name); + + if (readlink(check_path, buf2, PATH_MAX) < 0) + continue; + + if (strncmp(buf2, mount_point, len) == 0) + _D("Process %s : Opened files - %s", cmdline, buf2); + } + closedir(dp_child); + } + + closedir(dp); + return 0; +} + +int terminate_process(const char *partition, bool force) +{ + const char *argv[7] = {"/usr/bin/fuser", "-m", "-k", "-s", NULL, NULL, NULL}; + int argc; + + if (force) + argv[4] = "-SIGKILL"; + else + argv[4] = "-SIGTERM"; + argv[5] = partition; + argc = sizeof(argv) / sizeof(argv[0]); + return run_child(argc, argv); +} + +int mount_check(const char *path) +{ + int ret = false; + struct mntent *mnt; + const char *table = "/etc/mtab"; + FILE *fp; + int len; + + fp = setmntent(table, "r"); + if (!fp) + return ret; + + len = strlen(path) + 1; + while (1) { + mnt = getmntent(fp); + if (mnt == NULL) + break; + if (!strncmp(mnt->mnt_dir, path, len)) { + ret = true; + break; + } + } + endmntent(fp); + return ret; +} + +int umount_partition(const char *path, const bool force) +{ + int retry = 0, ret = -1; + struct timespec time = {0,}; + + sync(); + while (ret && retry < 2) { + switch (retry++) { + case 0: + /* Second, kill app with SIGTERM */ + _I("Kill app with SIGTERM"); + terminate_process(path, false); + time.tv_nsec = 500 * NANO_SECOND_MULTIPLIER; + nanosleep(&time, NULL); + ret = umount2(path, 0); + break; + case 1: + /* Last time, kill app with SIGKILL */ + _I("Kill app with SIGKILL"); + terminate_process(path, true); + time.tv_nsec = 200 * NANO_SECOND_MULTIPLIER; + nanosleep(&time, NULL); + ret = umount2(path, 0); + break; + } + _I("ret %d retry %d", ret, retry); + } + if (ret) { + if (force) + ret = umount2(path, MNT_DETACH); + else + ret = umount2(path, 0); + } + if (ret) + _I("Failed to unmount %s", path); + else + _I("%s unmounted successfully", path); + return ret; +} diff --git a/src/block/utils.h b/src/block/utils.h new file mode 100644 index 0000000..8bf2a9b --- /dev/null +++ b/src/block/utils.h @@ -0,0 +1,31 @@ +/* + * storaged + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef __STORAGED_BLOCK_UTIL_H__ +#define __STORAGED_BLOCK_UTIL_H__ + +#include +#include "log.h" +#include "common.h" + +int print_open_files(const char *mount_point); +int terminate_process(const char *partition, bool force); +int mount_check(const char *path); +int umount_partition(const char *path, const bool force); + +#endif /* __STORAGED_BLOCK_UTIL_H__ */ diff --git a/src/block/vfat.c b/src/block/vfat.c new file mode 100644 index 0000000..997b446 --- /dev/null +++ b/src/block/vfat.c @@ -0,0 +1,158 @@ +/* + * storaged + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "log.h" +#include "block.h" + +#define FS_VFAT_NAME "mkdosfs" + +/* guid 10001 - group priv_externalstorage */ +#define FS_VFAT_MOUNT_OPT "uid=0,gid=10001,dmask=0007,fmask=0117,iocharset=iso8859-1,utf8,shortname=mixed" + +static const char *vfat_arg[] = { + "/usr/bin/newfs_msdos", + "-F", "32", "-O", "tizen", "-c", "8", NULL, NULL, +}; + +static const char *vfat_check_arg[] = { + "/usr/bin/fsck_msdosfs", + "-pf", NULL, NULL, +}; + +static struct fs_check vfat_info = { + FS_TYPE_VFAT, + "vfat", + 0x1fe, + 2, + {0x55, 0xAA}, +}; + +static int vfat_check(const char *devpath) +{ + int argc, r, pass = 0; + + argc = ARRAY_SIZE(vfat_check_arg); + vfat_check_arg[argc - 2] = devpath; + + do { + r = run_child(argc, vfat_check_arg); + + switch (r) { + case 0: + _I("filesystem check completed OK"); + return 0; + case 2: + _I("file system check failed (not a FAT filesystem)"); + errno = ENODATA; + return -1; + case 4: + if (pass++ <= 2) { + _I("filesystem modified - rechecking (pass : %d)", pass); + continue; + } + _I("failing check after rechecks, but file system modified"); + errno = EIO; + return -1; + default: + _I("filesystem check failed (unknown exit code %d)", r); + errno = EIO; + return -1; + } + } while (1); +} + +static bool vfat_match(const char *devpath) +{ + int r; + + r = vfat_check(devpath); + if (r < 0) { + _E("failed to match with vfat(%s)", devpath); + return false; + } + + _I("Storage type : %s", vfat_info.name); + return true; +} + +static int vfat_mount(bool smack, const char *devpath, const char *mount_point) +{ + char options[NAME_MAX]; + int r, retry = RETRY_COUNT; + struct timespec time = {0,}; + unsigned long mountflags = MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_DIRSYNC; + + if (smack) + snprintf(options, sizeof(options), "%s,%s", FS_VFAT_MOUNT_OPT, SMACKFS_MOUNT_OPT); + else + snprintf(options, sizeof(options), "%s", FS_VFAT_MOUNT_OPT); + + do { + r = mount(devpath, mount_point, "vfat", mountflags, options); + if (!r) { + _I("Storage Mounted [vfat]"); + return 0; + } + _I("mount fail : r = %d, err = %d", r, errno); + time.tv_nsec = 100 * NANO_SECOND_MULTIPLIER; + nanosleep(&time, NULL); + if (r < 0 && errno == EROFS) + mountflags |= MS_RDONLY; + } while (r < 0 && (errno == ENOENT || errno == EROFS) && retry-- > 0); + + return -errno; +} + +static int vfat_format(const char *devpath) +{ + int argc; + argc = ARRAY_SIZE(vfat_arg); + vfat_arg[argc - 2] = devpath; + return run_child(argc, vfat_arg); +} + +static const struct block_fs_ops vfat_ops = { + .type = FS_TYPE_VFAT, + .name = "vfat", + .match = vfat_match, + .check = vfat_check, + .mount = vfat_mount, + .format = vfat_format, +}; + +static void __CONSTRUCTOR__ module_init(void) +{ + add_fs(&vfat_ops); +} +/* +static void __DESTRUCTOR__ module_exit(void) +{ + _I("module exit"); + remove_fs(&vfat_ops); +} +*/ diff --git a/src/core/dbus_main.c b/src/core/dbus_main.c new file mode 100644 index 0000000..f7cc658 --- /dev/null +++ b/src/core/dbus_main.c @@ -0,0 +1,71 @@ +/* + * storaged + * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include "fd_handler.h" +#include "dbus.h" +#include "log.h" + +/* -1 is a default timeout value, it's converted to 25*1000 internally. */ +#define DBUS_REPLY_TIMEOUT (-1) + +int set_dbus_connection(const char *bus) +{ + static DBusConnection *conn; + static int already; + DBusError err; + int ret; + + if (conn && already) + return 0; + + dbus_error_init(&err); + + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (!conn) { + if (dbus_error_is_set(&err)) { + _E("dbus_bus_get error(%s, %s)", err.name, err.message); + dbus_error_free(&err); + } else + _E("dbus_bus_get error"); + return -ECOMM; + } + + if (!already) { + already = 1; + dbus_error_init(&err); + + ret = dbus_bus_request_name(conn, bus, + DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE, + &err); + if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + if (dbus_error_is_set(&err)) { + _E("Failed to request dbus name (%s, %s, %s)", bus, err.name, err.message); + dbus_error_free(&err); + } else + _E("Failed to request dbus name (%s)", bus); + return -ECOMM; + } + } + + return 0; +} diff --git a/src/core/dbus_main.h b/src/core/dbus_main.h new file mode 100644 index 0000000..ce2a14a --- /dev/null +++ b/src/core/dbus_main.h @@ -0,0 +1,26 @@ +/* + * storaged + * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + + +#ifndef __STORAGED_DBUS_MAIN_H__ +#define __STORAGED_DBUS_MAIN_H__ + +#include "dbus_macro.h" + +int set_dbus_connection(const char *bus); + +#endif /* __STORAGED_BUS_MAIN_H__ */ diff --git a/src/core/main.c b/src/core/main.c new file mode 100644 index 0000000..db63353 --- /dev/null +++ b/src/core/main.c @@ -0,0 +1,103 @@ +/* + * storaged + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include + +#include "dbus_main.h" +#include "log.h" +#include "modules.h" + +#define WATCHDOG_TIMEOUT 15 /* Seconds */ + +static GMainLoop *loop; + +static void sig_quit(int signo) +{ + _D("received SIGTERM signal %d", signo); + g_main_loop_quit(loop); +} + +static void sig_usr1(int signo) +{ + _D("received SIGUSR1 signal %d, storaged'll be finished!", signo); + + g_main_loop_quit(loop); +} + +void watchdog_notify(void) +{ + int ret = aw_notify(); + if (ret < 0) + _E("aw_notify failed(%d)", ret); +} + +static gboolean watchdog_cb(gpointer data) +{ + watchdog_notify(); + return G_SOURCE_CONTINUE; +} + +static gboolean storaged_notify(gpointer data) +{ + _I("sd_notify(READY=1)"); + sd_notify(0, "READY=1"); + return G_SOURCE_REMOVE; +} + +int main(int argc, char **argv) +{ + int ret; + guint timer; + + loop = g_main_loop_new(NULL, TRUE); + if (!loop) { + _E("Failed to make main loop"); + return -ENOMEM; + } + + ret = set_dbus_connection(STORAGED_BUS_NAME); + if (ret < 0) { + _E("Failed to set dbus connection (%s, %d)", STORAGED_BUS_NAME, ret); + return ret; + } + + modules_init(NULL); + signal(SIGTERM, sig_quit); + signal(SIGUSR1, sig_usr1); + + timer = g_timeout_add(WATCHDOG_TIMEOUT * 1000, watchdog_cb, NULL); + if (timer > 0) { + ret = aw_register(WATCHDOG_TIMEOUT * 2); + if (ret < 0) + _E("aw_register failed"); + } + + g_idle_add(storaged_notify, NULL); + + g_main_loop_run(loop); + + modules_deinit(NULL); + + return 0; +} diff --git a/src/core/modules.c b/src/core/modules.c new file mode 100644 index 0000000..6e65246 --- /dev/null +++ b/src/core/modules.c @@ -0,0 +1,165 @@ +/* + * storaged + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include "modules.h" +#include "module-intf.h" +#include "log.h" + +#define PLUGIN_PATH "/usr/lib/storaged" + +struct storaged_module { + char *name; + char *so_name; + storaged_module_interface *plugin; +}; + +static GList *module_list; + +static int storaged_open_module(char *name, storaged_module_interface **module) +{ + int ret; + void *handle; + storaged_module_interface *plugin; + storaged_module_interface *(*get_interface)(void); + + if (!name || !module) + return -EINVAL; + + if (access(name, F_OK) != 0) { + _E("(%s) module does not exist !", name); + return -ENOENT; + } + + handle = dlopen(name, RTLD_NOW); + if (!handle) { + _E("dlopen (%s) failed (%s)", name, dlerror()); + ret = -EPERM; + goto out; + } + + get_interface = dlsym(handle, "storaged_get_module_interface"); + if (!get_interface) { + _E("dlsym failed(%s)", dlerror()); + ret = -EPERM; + goto out; + } + + plugin = get_interface(); + if (!plugin) { + _E("invalid plugin function"); + ret = -EINVAL; + goto out; + } + + plugin->dso = handle; + + *module = plugin; + + return 0; + +out: + if (handle) + dlclose(handle); + + return ret; +} + +static int storaged_close_module(storaged_module_interface *module) +{ + void *handle; + + if (!module) + return -EINVAL; + + handle = module->dso; + if (handle) + dlclose(handle); + return 0; +} + +int modules_init(void *data) +{ + storaged_module_interface *plugin; + struct storaged_module *module; + DIR *dp; + struct dirent *dentry; + int ret; + char path[256]; + + dp = opendir(PLUGIN_PATH); + if (!dp) { + ret = -errno; + _E("Failed to open plugin path %s (%d)", PLUGIN_PATH, ret); + return ret; + } + + while ((dentry = readdir(dp))) { + if (dentry->d_type != DT_REG) + continue; + + snprintf(path, sizeof(path), "%s/%s", PLUGIN_PATH, dentry->d_name); + ret = storaged_open_module(path, &plugin); + if (ret < 0) { + _E("Failed to open module (%s: %d)", path, ret); + continue; + } + + _I("%s opened", path); + + module = calloc(1, sizeof(struct storaged_module)); + module->plugin = plugin; + module->name = strdup(plugin->name); + module->so_name = strdup(dentry->d_name); + + module_list = g_list_append(module_list, module); + + if (plugin->init) + plugin->init(NULL); + } + + if (dp) + closedir(dp); + + return 0; +} + +void modules_deinit(void *data) +{ + GList *l, *l_next; + struct storaged_module *module; + + for (l = module_list, l_next = g_list_next(l) ; + l && (module = l->data) ; + l = l_next, l_next = g_list_next(l), module = NULL) { + module_list = g_list_remove(module_list, module); + if (module->plugin && module->plugin->exit) + module->plugin->exit(NULL); + storaged_close_module(module->plugin); + free(module->name); + free(module->so_name); + free(module); + } +} diff --git a/src/core/modules.h b/src/core/modules.h new file mode 100644 index 0000000..6ee1d00 --- /dev/null +++ b/src/core/modules.h @@ -0,0 +1,26 @@ +/* + * storaged + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#ifndef __STORAGED_MODULES_H__ +#define __STORAGED_MODULES_H__ + +int modules_init(void *data); +void modules_deinit(void *data); + +#endif /* __STORAGED_MODULES_H__ */ diff --git a/src/shared/common.c b/src/shared/common.c new file mode 100644 index 0000000..21a7ddc --- /dev/null +++ b/src/shared/common.c @@ -0,0 +1,144 @@ +/* + * storaged + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "common.h" + + +#define MODEL_NAME "http://tizen.org/system/model_name" +#define MODEL_EMULATOR "Emulator" + +bool is_emulator(void) +{ + int ret; + char *model_name = NULL; + static bool emul = false; + static int set = 0; + + if (set) + return emul; + + ret = system_info_get_platform_string(MODEL_NAME, &model_name); + if (ret < 0) { + _E("Cannot get model name(%d)", ret); + return emul; + } + + if (!strncmp(MODEL_EMULATOR, model_name, strlen(model_name) + 1)) + emul = true; + + set = 1; + free(model_name); + + return emul; +} + +static int parent(pid_t pid) +{ + int status; + + /* wait for child */ + if (waitpid(pid, &status, 0) != -1) { + /* terminated normally */ + if (WIFEXITED(status)) { + _I("%d terminated by exit(%d)", pid, WEXITSTATUS(status)); + return WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) + _I("%d terminated by signal %d", pid, WTERMSIG(status)); + else if (WIFSTOPPED(status)) + _I("%d stopped by signal %d", pid, WSTOPSIG(status)); + } else + _I("%d waitpid() failed : %d", pid, errno); + + return -EAGAIN; +} + +static void child(int argc, const char *argv[]) +{ + int i, r; + + for (i = 0; i < _NSIG; ++i) + signal(i, SIG_DFL); + + r = execv(argv[0], (char **)argv); + if (r < 0) + exit(EXIT_FAILURE); +} + +int run_child(int argc, const char *argv[]) +{ + pid_t pid; + struct sigaction act, oldact; + int r = 0; + FILE *fp; + + if (!argv) + return -EINVAL; + + fp = fopen(argv[0], "r"); + if (fp == NULL) { + _E("fail %s (%d)", argv[0], errno); + return -errno; + } + fclose(fp); + + /* Use default signal handler */ + act.sa_handler = SIG_DFL; + act.sa_sigaction = NULL; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + + if (sigaction(SIGCHLD, &act, &oldact) < 0) + return -errno; + + pid = fork(); + if (pid < 0) { + _E("failed to fork"); + r = -errno; + } else if (pid == 0) { + child(argc, argv); + } else + r = parent(pid); + + if (sigaction(SIGCHLD, &oldact, NULL) < 0) + _E("failed to restore sigaction"); + + return r; +} diff --git a/src/shared/common.h b/src/shared/common.h new file mode 100644 index 0000000..e0db5d4 --- /dev/null +++ b/src/shared/common.h @@ -0,0 +1,45 @@ +/* + * storaged + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#ifndef __STORAGED_COMMON_H__ +#define __STORAGED_COMMON_H__ + +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0])) + +#ifndef __CONSTRUCTOR__ +#define __CONSTRUCTOR__ __attribute__ ((constructor)) +#endif + +#ifndef __DESTRUCTOR__ +#define __DESTRUCTOR__ __attribute__ ((destructor)) +#endif + +#define NANO_SECOND_MULTIPLIER 1000000 /* 1ms = 1,000,000 nsec */ + +int run_child(int argc, const char *argv[]); +bool is_emulator(void); + +#endif /* __STORAGED_COMMON_H__ */ + diff --git a/src/shared/config-parser.c b/src/shared/config-parser.c new file mode 100644 index 0000000..af05f81 --- /dev/null +++ b/src/shared/config-parser.c @@ -0,0 +1,127 @@ +/* + * storaged + * + * Copyright (c) 2013 - 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include "log.h" +#include "config-parser.h" + +#define MAX_LINE 512 +#define MAX_SECTION 64 +#define WHITESPACE " \t" +#define NEWLINE "\n\r" +#define COMMENT '#' + +static inline char *trim_str(char *s) +{ + char *t; + /* left trim */ + s += strspn(s, WHITESPACE); + + /* right trim */ + for (t = strchr(s, 0); t > s; t--) + if (!strchr(WHITESPACE, t[-1])) + break; + *t = 0; + return s; +} + +int config_parse(const char *file_name, int cb(struct parse_result *result, + void *user_data), void *user_data) +{ + FILE *f = NULL; + struct parse_result result; + /* use stack for parsing */ + char line[MAX_LINE]; + char section[MAX_SECTION]; + char *start, *end, *name, *value; + int lineno = 0, ret = 0; + + if (!file_name || !cb) { + ret = -EINVAL; + goto error; + } + + /* open conf file */ + f = fopen(file_name, "r"); + if (!f) { + _E("Failed to open file %s", file_name); + ret = -EIO; + goto error; + } + + /* parsing line by line */ + while (fgets(line, MAX_LINE, f) != NULL) { + lineno++; + + start = line; + start[strcspn(start, NEWLINE)] = '\0'; + start = trim_str(start); + + if (*start == COMMENT) { + continue; + } else if (*start == '[') { + /* parse section */ + end = strchr(start, ']'); + if (!end || *end != ']') { + ret = -EBADMSG; + goto error; + } + + *end = '\0'; + strncpy(section, start + 1, sizeof(section)); + section[MAX_SECTION-1] = '\0'; + } else if (*start) { + /* parse name & value */ + end = strchr(start, '='); + if (!end || *end != '=') { + ret = -EBADMSG; + goto error; + } + *end = '\0'; + name = trim_str(start); + value = trim_str(end + 1); + end = strchr(value, COMMENT); + if (end && *end == COMMENT) { + *end = '\0'; + value = trim_str(value); + } + + result.section = section; + result.name = name; + result.value = value; + /* callback with parse result */ + ret = cb(&result, user_data); + if (ret < 0) { + ret = -EBADMSG; + goto error; + } + } + } + _D("Success to load %s", file_name); + fclose(f); + return 0; + +error: + if (f) + fclose(f); + _E("Failed to read %s:%d!", file_name, lineno); + return ret; +} + diff --git a/src/shared/config-parser.h b/src/shared/config-parser.h new file mode 100644 index 0000000..4263727 --- /dev/null +++ b/src/shared/config-parser.h @@ -0,0 +1,43 @@ +/* + * storaged + * + * Copyright (c) 2013 - 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#ifndef __CONFIG_PARSER_H__ +#define __CONFIG_PARSER_H__ + +#define MATCH(a, b) (!strncmp(a, b, strlen(a))) +#define SET_CONF(a, b) (a = (b > 0.0 ? b : a)) + +struct parse_result { + char *section; + char *name; + char *value; +}; + +/** + * @brief Parse config file and call callback\n + * @param[in] file_name conf file. + * @param[in] cb cb is called when conf file is parsed line by line. + * @param[in] user_data user data is passed to cb. + * @return 0 on success, negative if failed + */ +int config_parse(const char *file_name, int cb(struct parse_result *result, + void *user_data), void *user_data); + +#endif + diff --git a/src/shared/dbus.c b/src/shared/dbus.c new file mode 100644 index 0000000..15b30ed --- /dev/null +++ b/src/shared/dbus.c @@ -0,0 +1,1053 @@ +/* + * storaged + * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include "fd_handler.h" +#include "dbus.h" +#include "log.h" + +/* -1 is a default timeout value, it's converted to 25*1000 internally. */ +#define DBUS_REPLY_TIMEOUT (-1) + +struct dbus_handle_s { + DBusConnection *conn; + char *name; + GList *method_handle_list; +}; + +struct dbus_method_reply_handle_s { + struct dbus_handle_s *handle; + DBusMessage *msg; +}; + +struct dbus_method_handle_s { + struct dbus_handle_s *handle; + const char *path; + const dbus_interface_s *iface; + void (*method_handle_received)(void *method_handle, void *data); + void *data; +}; + +struct signal_info { + const char *path; + const char *iface; + const char *name; + dbus_signal_received cb; + destroy_notified free_func; + void *data; + guint handler; +}; + +struct pending_call_data { + dbus_pending_cb func; + void *data; +}; + +static GList *connection_handle_list; +static GList *signal_handler_list; + +static DBusConnection *get_dbus_connection(void) +{ + static DBusConnection *conn; + DBusError err; + + if (conn) + return conn; + + dbus_error_init(&err); + + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (!conn) { + if (dbus_error_is_set(&err)) { + _E("dbus_bus_get error(%s, %s)", err.name, err.message); + dbus_error_free(&err); + } else + _E("dbus_bus_get error"); + } + + return conn; +} + +int dbus_get_connection(dbus_handle_h *handle) +{ + static DBusConnection *conn; + struct dbus_handle_s *h; + int ret; + + if (!handle) + return -EINVAL; + + h = calloc(1, sizeof(struct dbus_handle_s)); + if (!h) { + _E("calloc() failed"); + return -ENOMEM; + } + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + ret = -ECOMM; + goto out; + } + + h->conn = conn; + *handle = h; + + connection_handle_list = g_list_append(connection_handle_list, h); + + return 0; + +out: + free(h); + return ret; +} + +int get_dbus_method_sender_name(dbus_method_reply_handle_h reply_handle, + char *name, size_t len) +{ + struct dbus_method_reply_handle_s *reply = reply_handle; + const char *sender; + + if (!reply || !reply->msg || !name) + return -EINVAL; + + sender = dbus_message_get_sender(reply->msg); + snprintf(name, len, "%s", sender); + + return 0; +} + +int get_dbus_method_sender_pid(dbus_method_reply_handle_h reply_handle) +{ + char *param[1]; + pid_t pid; + char name[NAME_MAX]; + DBusMessage *msg; + DBusError err; + int ret; + + ret = get_dbus_method_sender_name(reply_handle, name, sizeof(name)); + if (ret < 0) + return ret; + + param[0] = name; + ret = call_dbus_method_sync(DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixProcessID", + "s", param, DBUS_REPLY_TIMEOUT, + &msg); + if (ret < 0) { + _E("Faild to get pid of sender(%s)", name); + return ret; + } + + dbus_error_init(&err); + + ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID); + dbus_message_unref(msg); + if (dbus_error_is_set(&err)) { + _E("no message : [%s:%s]", err.name, err.message); + dbus_error_free(&err); + return -ENOMSG; + } + if (!ret) { + _E("no message"); + return -ENOMSG; + } + + return pid; +} + +DBusMessage *make_dbus_reply_message(dbus_method_reply_handle_h reply_handle) +{ + struct dbus_method_reply_handle_s *reply = reply_handle; + + if (!reply || !(reply->msg)) { + _E("Invalid parameter"); + return NULL; + } + + return dbus_message_new_method_return(reply->msg); +} + +DBusMessage *make_dbus_reply_message_simple(dbus_method_reply_handle_h reply_handle, int result) +{ + DBusMessage *rep; + DBusMessageIter iter; + rep = make_dbus_reply_message(reply_handle); + dbus_message_iter_init_append(rep, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &result); + return rep; +} + +void reply_dbus_method_result(dbus_method_reply_handle_h reply_handle, DBusMessage *reply_msg) +{ + struct dbus_method_reply_handle_s *reply = reply_handle; + + if (!reply || !reply_msg) + return; + + dbus_connection_send(reply->handle->conn, reply_msg, NULL); + dbus_message_unref(reply_msg); + if (reply) + free(reply); +} + +static DBusHandlerResult method_call_handler(DBusConnection *connection, + DBusMessage *msg, void *user_data) +{ + int i; + struct dbus_method_handle_s *mh = user_data; + struct dbus_method_reply_handle_s *reply; + const dbus_interface_s *iface; + const dbus_method_s *methods; + DBusMessage *result = NULL; + + _D("Method call handler"); + + if (!msg || !mh) { + return DBUS_HANDLER_RESULT_HANDLED; + } + + iface = mh->iface; + methods = iface->methods; + + for (i = 0 ; i < iface->nr_methods ; i++) { + if (dbus_message_is_method_call(msg, iface->name, methods[i].member) != TRUE) + continue; + + reply = calloc(1, sizeof(struct dbus_method_reply_handle_s)); + if (!reply) { + _E("calloc() failed"); + return DBUS_HANDLER_RESULT_HANDLED; + } + + reply->handle = mh->handle; + reply->msg = msg; + result = methods[i].func(reply, msg); + if (!result) + _E("result == NULL"); + break; + } + + if (result) + reply_dbus_method_result(reply, result); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static const DBusObjectPathVTable path_vtable = { + NULL, + method_call_handler, + NULL, NULL, NULL, NULL, +}; + +static gboolean register_methods(gpointer data) +{ + dbus_bool_t ret; + struct dbus_method_handle_s *mh = data; + + if (!mh) + return FALSE; + + ret = dbus_connection_register_object_path(mh->handle->conn, + mh->path, &path_vtable, mh); + if (ret == FALSE) { + _E("Failed to register methods (%s)", mh->path); + return FALSE; + } + + if (mh->method_handle_received) + mh->method_handle_received(mh, mh->data); + + _I("object path(%s)", mh->path); + + return FALSE; +} + +struct watch_info { + DBusWatch *watch; + void *data; +}; + +static fd_handler_h watch_handler; + +static bool watch_changed_cb(int fd, void *data) +{ + struct watch_info *winfo = data; + + dbus_watch_handle(winfo->watch, DBUS_WATCH_READABLE | DBUS_WATCH_ERROR); + + return true; +} + +static void free_watch_info(void *data) +{ + if (data) + free(data); +} + +static dbus_bool_t add_watch_cb(DBusWatch *watch, void *data) +{ + int fd; + int ret; + struct watch_info *winfo; + dbus_bool_t enabled; + + _I("add_watch_cb %s", (dbus_watch_get_enabled(watch) == 0 ? "disabled": "enabled")); + + enabled = dbus_watch_get_enabled(watch); + + if (watch_handler || !enabled) + return TRUE; + + winfo = calloc(1, sizeof(struct watch_info)); + if (!winfo) { + _E("calloc() failed"); + return TRUE; + } + + winfo->watch = watch; + winfo->data = NULL; + + fd = dbus_watch_get_unix_fd(watch); + ret = add_fd_read_handler(fd, watch_changed_cb, winfo, free_watch_info, &watch_handler); + if (ret < 0) + _E("Failed to add fd handler (%d)", ret); + + return TRUE; +} + +static void remove_watch_cb(DBusWatch *watch, void *data) +{ + int ret; + + if (!watch_handler) + return; + + ret = remove_fd_read_handler(&watch_handler); + if (ret < 0) + _E("Failed to remove fd handler (%d)", ret); + watch_handler = NULL; +} + +static void toggle_watch_cb(DBusWatch *watch, void *data) +{ + return; +} + +static dbus_bool_t add_timeout_cb(DBusTimeout *timeout, void *data) +{ + return TRUE; +} + +static void remove_timeout_cb(DBusTimeout *timeout, void *data) +{ + return; +} + +static void toggle_timeout_cb(DBusTimeout *timeout, void *data) +{ + return; +} + +static gboolean dispatch_idler_cb(gpointer data) +{ + DBusConnection *conn = data; + + if (!conn) { + _E("conn == NULL"); + return FALSE; + } + + dbus_connection_ref(conn); + + while (dbus_connection_get_dispatch_status(conn) == DBUS_DISPATCH_DATA_REMAINS) { + dbus_connection_dispatch(conn); + } + + dbus_connection_unref(conn); + + return FALSE; +} + +static void dispatch_status_cb(DBusConnection *conn, DBusDispatchStatus new_status, void *data) +{ + if (new_status == DBUS_DISPATCH_DATA_REMAINS) + g_idle_add(dispatch_idler_cb, conn); +} + +int register_dbus_methods(dbus_handle_h handle, + const char *object_path, const dbus_interface_s *interface, + void (*method_handle_received)(dbus_method_handle_h method_handle, void *data), + void *data) +{ + struct dbus_method_handle_s *mh; + struct dbus_handle_s *h = handle; + static bool set_dispatch = false; + + if (!h || !(h->conn) || !object_path || !interface) + return -EINVAL; + + mh = calloc(1, sizeof(struct dbus_method_handle_s)); + if (!mh) { + _E("calloc() failed"); + return -ENOMEM; + } + + mh->handle = h; + mh->path = object_path; + mh->iface = interface; + mh->method_handle_received = method_handle_received; + mh->data = data; + + h->method_handle_list = g_list_append(h->method_handle_list, mh); + + if (!set_dispatch) { + set_dispatch = true; + + dbus_connection_set_watch_functions(h->conn, + add_watch_cb, remove_watch_cb, toggle_watch_cb, + NULL, NULL); + + dbus_connection_set_timeout_functions(h->conn, + add_timeout_cb, remove_timeout_cb, toggle_timeout_cb, + NULL, NULL); + + dbus_connection_set_dispatch_status_function(h->conn, + dispatch_status_cb, NULL, NULL); + } + + dispatch_status_cb(h->conn, dbus_connection_get_dispatch_status(h->conn), NULL); + + register_methods(mh); + + return 0; +} + +void unregister_dbus_methods(dbus_handle_h handle, dbus_method_handle_h method_handle) +{ + GList *l; + struct dbus_method_handle_s *item; + struct dbus_handle_s *h = handle; + struct dbus_method_handle_s *mh = method_handle; + + if (!h || !h->conn) + return; + if (!mh) + return; + + for (l = h->method_handle_list ; l && (item = l->data) ; + l = g_list_next(l), item = NULL) { + if (item == mh) { + h->method_handle_list = g_list_remove(h->method_handle_list, item); + break; + } + } + + if (!item) + return; + + dbus_connection_unregister_object_path(h->conn, item->path); + free(item); +} + +static int append_variant(DBusMessageIter *iter, const char *sig, char *param[]) +{ + char *ch; + int i; + int int_type; + dbus_bool_t bool_type; + unsigned long long int64_type; + DBusMessageIter arr; + struct dbus_byte *byte; + + if (!sig || !param) + return 0; + + for (ch = (char *)sig, i = 0; *ch != '\0'; ++i, ++ch) { + switch (*ch) { + case 'b': + bool_type = (atoi(param[i]) == 0 ? FALSE : TRUE); + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &bool_type); + break; + case 'i': + int_type = atoi(param[i]); + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &int_type); + break; + case 'u': + int_type = strtoul(param[i], NULL, 10); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &int_type); + break; + case 't': + int64_type = atoll(param[i]); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &int64_type); + break; + case 's': + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, ¶m[i]); + break; + case 'a': + ++ch; + switch (*ch) { + case 'y': + ++i; + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &arr); + byte = (struct dbus_byte *)param[i]; + dbus_message_iter_append_fixed_array(&arr, DBUS_TYPE_BYTE, &(byte->data), byte->size); + dbus_message_iter_close_container(iter, &arr); + break; + default: + break; + } + break; + default: + return -EINVAL; + } + } + + return 0; +} + +int broadcast_dbus_signal(const char *path, const char *iface, + const char *name, const char *sig, char *param[]) +{ + DBusConnection *conn; + DBusMessage *msg; + DBusMessageIter iter; + int ret; + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + return -ECOMM; + } + + msg = dbus_message_new_signal(path, iface, name); + if (!msg) { + _E("fail to allocate new %s.%s signal", iface, name); + return -EPERM; + } + + dbus_message_iter_init_append(msg, &iter); + ret = append_variant(&iter, sig, param); + if (ret < 0) { + _E("append_variant error(%d)", ret); + return -EPERM; + } + + ret = dbus_connection_send(conn, msg, NULL); + dbus_message_unref(msg); + if (ret != TRUE) { + _E("dbus_connection_send error(%s:%s-%s)", path, iface, name); + return -ECOMM; + } + + return 0; +} + +static void release_signal_info(struct signal_info *info) +{ + if (info) { + if (info->free_func) + info->free_func(info->data); + free(info); + } +} + +static struct signal_info *find_signal_info(const char *path, + const char *iface, const char *name, dbus_signal_received cb) +{ + GList *l; + size_t path_len, iface_len, name_len; + struct signal_info *info; + + path_len = strlen(path) + 1; + iface_len = strlen(iface) + 1; + name_len = strlen(name) + 1; + + for (l = signal_handler_list ; l && (info = l->data) ; l = g_list_next(l), info = NULL) { + if (strncmp(info->path, path, path_len)) + continue; + if (strncmp(info->iface, iface, iface_len)) + continue; + if (strncmp(info->name, name, name_len)) + continue; + if (cb && info->cb != cb) + continue; + return info; + } + + return NULL; +} + +static void call_signal_callbacks(const char *path, + const char *iface, const char *name, DBusMessage *msg) +{ + GList *l; + size_t path_len, iface_len, name_len; + struct signal_info *info; + + path_len = strlen(path) + 1; + iface_len = strlen(iface) + 1; + name_len = strlen(name) + 1; + + for (l = signal_handler_list ; l && (info = l->data) ; l = g_list_next(l), info = NULL) { + _I("path(%s), info(%s), name(%s)", info->path, info->iface, info->name); + if (strncmp(info->path, path, path_len)) + continue; + if (strncmp(info->iface, iface, iface_len)) + continue; + if (strncmp(info->name, name, name_len)) + continue; + if (info->cb) + info->cb(NULL, path, iface, name, msg, info->data); + } +} + +static int make_match(const char *path, const char *iface, + const char *name, char *buf, size_t len) +{ + if (!path || !iface || ! name || !buf) + return -EINVAL; + + snprintf(buf, len, + "type='signal',path=%s,interface=%s,member=%s", + path, iface, name); + + return 0; +} + +void unregister_dbus_signal_all(void) +{ + GList *l, *l_next; + struct signal_info *info; + char match[256]; + int ret; + DBusConnection *conn; + + for (l = signal_handler_list, l_next = g_list_next(l) ; + l && (info = l->data) ; + l = l_next, l_next = g_list_next(l), info = NULL) { + signal_handler_list = g_list_remove(signal_handler_list, info); + + ret = make_match(info->path, info->iface, info->name, + match, sizeof(match)); + if (ret == 0) { + conn = get_dbus_connection(); + if (conn) + dbus_bus_remove_match(conn, match, NULL); + } + + release_signal_info(info); + } +} + +static DBusHandlerResult signal_filter(DBusConnection *conn, DBusMessage *msg, void *data) +{ + const char *path, *iface, *name; + + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + path = dbus_message_get_path(msg); + iface = dbus_message_get_interface(msg); + name = dbus_message_get_member(msg); + + call_signal_callbacks(path, iface, name, msg); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +int register_dbus_signal(const char *path, + const char *interface, const char *name, + dbus_signal_received cb, void *data, + destroy_notified free_func) +{ + struct signal_info *info; + DBusConnection *conn; + static int add_filter = 0; + char match[256]; + int ret; + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + return -ECOMM; + } + + info = find_signal_info(path, interface, name, cb); + if (info) + return -EEXIST; + + info = calloc(1, sizeof(struct signal_info)); + if (!info) { + _E("calloc failed"); + return -ENOMEM; + } + + info->path = path; + info->iface = interface; + info->name = name; + info->cb = cb; + info->free_func = free_func; + info->data = data; + + if (add_filter == 0) { + add_filter = 1; + dbus_connection_add_filter(conn, signal_filter, NULL, NULL); + } + + ret = make_match(path, interface, name, match, sizeof(match)); + if (ret < 0) { + _E("Failed to make match (%d)", ret); + free(info); + return ret; + } + dbus_bus_add_match(conn, match, NULL); + + signal_handler_list = g_list_append(signal_handler_list, info); + + return 0; +} + +int unregister_dbus_signal(const char *path, + const char *interface, const char *name, + dbus_signal_received cb) +{ + struct signal_info *info; + DBusConnection *conn; + char match[256]; + int ret; + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + return -ECOMM; + } + + info = find_signal_info(path, interface, name, cb); + if (!info) + return -ENOENT; + + signal_handler_list = g_list_remove(signal_handler_list, info); + release_signal_info(info); + + ret = make_match(path, interface, name, match, sizeof(match)); + if (ret < 0) { + _E("Failed to make match (%d)", ret); + return ret; + } + + dbus_bus_remove_match(conn, match, NULL); + + return 0; +} + +int call_dbus_method_sync(const char *dest, const char *path, + const char *interface, const char *method, + const char *sig, char *param[], int timeout, + DBusMessage **reply) +{ + DBusConnection *conn; + DBusMessage *msg; + DBusMessageIter iter; + DBusMessage *rep; + DBusError err; + int r; + + if (!dest || !path || !interface || !method || !reply) + return -EINVAL; + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + return -ECOMM; + } + + msg = dbus_message_new_method_call(dest, path, interface, method); + if (!msg) { + _E("dbus_message_new_method_call(%s:%s-%s)", + path, interface, method); + return -EPERM; + } + + dbus_message_iter_init_append(msg, &iter); + r = append_variant(&iter, sig, param); + if (r < 0) { + _E("append_variant error(%d) %s %s:%s-%s", + r, dest, path, interface, method); + dbus_message_unref(msg); + return r; + } + + dbus_error_init(&err); + + rep = dbus_connection_send_with_reply_and_block(conn, msg, timeout, &err); + dbus_message_unref(msg); + if (dbus_error_is_set(&err)) { + _E("dbus_connection_send error(%s:%s) %s %s:%s-%s", + err.name, err.message, dest, path, interface, method); + dbus_error_free(&err); + return -EPERM; + } + if (!rep) { + _E("dbus_connection_send error(No reply) %s %s:%s-%s", + dest, path, interface, method); + return -EPERM; + } + + *reply = rep; + + return 0; +} + +int call_dbus_method_sync_pairs(const char *dest, + const char *path, const char *interface, const char *method, + int num, va_list args) +{ + DBusConnection *conn; + DBusMessage *msg; + DBusMessageIter iter; + DBusMessageIter aiter, piter; + DBusMessage *reply; + DBusError err; + char *key, *value; + int ret, result; + int i; + + if (!dest || !path || !interface || !method) + return -EINVAL; + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + return -ECOMM; + } + + msg = dbus_message_new_method_call(dest, path, interface, method); + if (!msg) { + _E("dbus_message_new_method_call(%s:%s-%s)", + path, interface, method); + return -EPERM; + } + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{ss}", &aiter); + + for (i = 0 ; i < num ; i = i + 2) { + key = va_arg(args, char *); + value = va_arg(args, char *); + _I("key(%s), value(%s)", key, value); + dbus_message_iter_open_container(&aiter, DBUS_TYPE_DICT_ENTRY, NULL, &piter); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, &key); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, &value); + dbus_message_iter_close_container(&aiter, &piter); + } + + dbus_message_iter_close_container(&iter, &aiter); + + dbus_error_init(&err); + + reply = dbus_connection_send_with_reply_and_block(conn, msg, DBUS_REPLY_TIMEOUT, &err); + if (!reply) { + _E("dbus_connection_send error(No reply) %s %s:%s-%s", + dest, path, interface, method); + } + + if (dbus_error_is_set(&err)) { + _E("dbus_connection_send error(%s:%s) %s %s:%s-%s", + err.name, err.message, dest, path, interface, method); + dbus_error_free(&err); + reply = NULL; + } + dbus_message_unref(msg); + + if (!reply) + return -EPERM; + + ret = dbus_message_get_args(reply, &err, DBUS_TYPE_INT32, &result, DBUS_TYPE_INVALID); + dbus_message_unref(reply); + if (!ret) { + _E("no message : [%s:%s] %s %s:%s-%s", + err.name, err.message, dest, path, interface, method); + dbus_error_free(&err); + return -ENOMSG; + } + + return result; +} + +int call_dbus_method_async_pairs(const char *dest, + const char *path, const char *interface, const char *method, + int num, va_list args) +{ + DBusConnection *conn; + DBusMessage *msg; + DBusMessageIter iter; + DBusMessageIter aiter, piter; + char *key, *value; + int ret, i; + + if (!dest || !path || !interface || !method) + return -EINVAL; + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + return -ECOMM; + } + + msg = dbus_message_new_method_call(dest, path, interface, method); + if (!msg) { + _E("dbus_message_new_method_call(%s:%s-%s)", + path, interface, method); + return -EPERM; + } + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{ss}", &aiter); + + for (i = 0 ; i < num ; i = i + 2) { + key = va_arg(args, char *); + value = va_arg(args, char *); + _I("key(%s), value(%s)", key, value); + dbus_message_iter_open_container(&aiter, DBUS_TYPE_DICT_ENTRY, NULL, &piter); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, &key); + dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, &value); + dbus_message_iter_close_container(&aiter, &piter); + } + + dbus_message_iter_close_container(&iter, &aiter); + + ret = dbus_connection_send(conn, msg, NULL); + dbus_message_unref(msg); + if (ret != TRUE) { + _E("dbus_connection_send error(%s %s:%s-%s)", + dest, path, interface, method); + return -ECOMM; + } + + return 0; +} + +static void cb_pending(DBusPendingCall *pending, void *user_data) +{ + DBusMessage *msg; + struct pending_call_data *data = user_data; + int ret; + + ret = dbus_pending_call_get_completed(pending); + if (!ret) { + _I("dbus_pending_call_get_completed() fail"); + dbus_pending_call_unref(pending); + return; + } + + msg = dbus_pending_call_steal_reply(pending); + if (!msg) { + _E("message is NULL"); + if (data->func) + data->func(data->data, NULL, -ECOMM); + return; + } + + if (data->func) + data->func(data->data, msg, 0); + + dbus_message_unref(msg); + dbus_pending_call_unref(pending); +} + +int call_dbus_method_async(const char *dest, const char *path, + const char *interface, const char *method, + const char *sig, char *param[], + dbus_pending_cb cb, int timeout, void *data) +{ + DBusConnection *conn; + DBusMessage *msg; + DBusMessageIter iter; + DBusPendingCall *pending = NULL; + struct pending_call_data *pdata; + int ret; + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + return -ECOMM; + } + + pdata = calloc(1, sizeof(struct pending_call_data)); + if (!pdata) { + _E("malloc error : %s-%s", interface, method); + return -ENOMEM; + } + + pdata->func = cb; + pdata->data = data; + + msg = dbus_message_new_method_call(dest, path, interface, method); + if (!msg) { + _E("dbus_message_new_method_call(%s:%s-%s)", + path, interface, method); + return -EBADMSG; + } + + dbus_message_iter_init_append(msg, &iter); + ret = append_variant(&iter, sig, param); + if (ret < 0) { + _E("append_variant error(%d)%s %s:%s-%s", + ret, dest, path, interface, method); + dbus_message_unref(msg); + return ret; + } + + ret = dbus_connection_send_with_reply(conn, msg, &pending, timeout); + if (!ret) { + dbus_message_unref(msg); + _E("dbus_connection_send error(%s %s:%s-%s)", + dest, path, interface, method); + return -ECOMM; + } + + dbus_message_unref(msg); + + if (cb && pending) { + pdata = calloc(1, sizeof(struct pending_call_data)); + if (!pdata) { + _E("malloc error : %s-%s", interface, method); + return -ENOMEM; + } + + pdata->func = cb; + pdata->data = data; + + ret = dbus_pending_call_set_notify(pending, cb_pending, pdata, free); + if (!ret) { + free(pdata); + dbus_pending_call_cancel(pending); + return -ECOMM; + } + } + + return 0; +} diff --git a/src/shared/dbus.h b/src/shared/dbus.h new file mode 100644 index 0000000..10bc475 --- /dev/null +++ b/src/shared/dbus.h @@ -0,0 +1,276 @@ +/* + * storaged + * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + + +#ifndef __STORAGED_DBUS_H__ +#define __STORAGED_DBUS_H__ + +#include +#include +#include "dbus_macro.h" + +/** + * @brief Structure for byte array + * @since_tizen 4.0 + */ +struct dbus_byte { + const unsigned char *data; + int size; +}; + +/** + * @brief Structure for integer array + * @since_tizen 4.0 + */ +struct dbus_int { + int *list; + int size; +}; + +/** + * @brief Dbus handler which is used to register and call methods + + * @since_tizen 4.0 + */ +typedef void *dbus_handle_h; + +/** + * @brief Dbus method handler which is used to unregister dbus methods + * @since_tizen 4.0 + */ +typedef void *dbus_method_handle_h; + +/** + * @brief Dbus reply handler which is used to reply for dbus methods + * @since_tizen 4.0 + */ +typedef void *dbus_method_reply_handle_h; + +/** + * @brief Structure which contains the dbus method name and callback function. + * @since_tizen 4.0 + */ +typedef struct dbus_method { + const char *member; + const char *sig; + DBusMessage *(*func)(dbus_method_reply_handle_h reply, DBusMessage *msg); +} dbus_method_s; + +/** + * @brief Callback function which is called when the specified signal is delivered. + * @since_tizen 4.0 + */ +typedef void (*dbus_signal_received)(const char *sender_name, + const char *object_path, const char *interface_name, + const char *signal_name, DBusMessage *msg, + void *data); + +/** + * @brief Type for the callback function of the Async dbus method calls + * @since_tizen 4.0 + */ +typedef void (*dbus_pending_cb)(void *data, DBusMessage *msg, int err); + +/** + * @brief Callback function which is called when the user data needs to be released. + * @since_tizen 4.0 + */ +typedef void (*destroy_notified)(void *data); + +/** + * @brief Structure which contains the dbus interface information and its methods.i + * @since_tizen 4.0 + */ +typedef struct dbus_interface { + const char *name; + const dbus_method_s *methods; + int nr_methods; +} dbus_interface_s; + +/** + * @brief Get dbus handle for dbus connection + * @since_tizen 4.0 + * @param[in] bus The dbus bus name + * @param[out] handle The dbus handle + * @return @c 0 on success, + * otherwise a negative error value + */ +int dbus_get_connection(dbus_handle_h *handle); + +/** + * @brief Register dbus methods. + * @since_tizen 4.0 + * @param[in] handle The dbus handle obtained by syscommon_get_dbus_connection(). + * @param[in] object_path The dbus object path. + * @param[in] interface The dbus interface object. + * @param[in] interface_handle_received The callback function called if the interface is registered. + * @param[in] data The user data which is delivered to the interface_handle_received function. + * @return @c 0 on success, + * otherwise a negative error value + */ +int register_dbus_methods(dbus_handle_h handle, + const char *object_path, const dbus_interface_s *interface, + void (*method_handle_received)(dbus_method_handle_h method_handle, void *data), + void *data); + +/** + * @brief Deliver the return value of the dbus methods + * @since_tizen 4.0 + * @param[in] invocation The invocation information which is delivered to the callback function registered by syscommon_register_dbus_methods(). + * @param[in] result The gvariant value which contains the result. + */ +void reply_dbus_method_result(dbus_method_reply_handle_h reply_handle, DBusMessage *reply_msg); + +DBusMessage *make_dbus_reply_message(dbus_method_reply_handle_h reply_handle); + +DBusMessage *make_dbus_reply_message_simple(dbus_method_reply_handle_h reply_handle, int result); + +int get_dbus_method_sender_name(dbus_method_reply_handle_h reply_handle, char *name, size_t len); + +int get_dbus_method_sender_pid(dbus_method_reply_handle_h reply_handle); + +/** + * @brief Unregister dbus methods + * @since_tizen 4.0 + * @param[in] handle The dbus handle obtained by syscommon_get_dbus_connection(). + * @param[in] id The method regist ID which is obtained by the id_received callback of the syscommon_register_dbus_methods(). + */ +void unregister_dbus_methods(dbus_handle_h handle, dbus_method_handle_h method_handle); + +/** + * @brief Register dbus signal + * @since_tizen 4.0 + * @param[in] path The dbus object path. + * @param[in] interface The dbus interface name. + * @param[in] name The dbus signal name. + * @param[in] cb The callback function which will called when the signal is delivered. + * @param[in] data The user data which is delivered to the callback function. + * @param[in] free_func The function which is called when the signal handler is removed + * @return @c 0 on success, + * otherwise a negative error value + */ +int register_dbus_signal(const char *path, + const char *interface, const char *name, + dbus_signal_received cb, void *data, + destroy_notified free_func); + +/** + * @brief Unregister dbus signal + * @since_tizen 4.0 + * @param[in] path The dbus object path. + * @param[in] interface The dbus interface name. + * @param[in] name The dbus signal name. + * @return @c 0 on success, + * otherwise a negative error value + */ +int unregister_dbus_signal(const char *path, + const char *interface, const char *name, + dbus_signal_received cb); + +/** + * @brief Unregister all dbus signals + * @since_tizen 4.0 + */ +void unregister_dbus_signal_all(void); + +/** + * @brief Broadcast dbus signal + * @since_tizen 4.0 + * @param[in] path The dbus object path. + * @param[in] iface The dbus interface name. + * @param[in] name The dbus signal name. + * @param[in] sig The format string of parameter. + * @param[in] param The parameter array. + * @return @c 0 on success, + * otherwise a negative error value + */ +int broadcast_dbus_signal(const char *path, + const char *iface, const char *name, + const char *sig, char *param[]); + + +/** + * @brief Call dbus method synchronously. + * @remarks The function expects that the return value is just single integer. + * @since_tizen 4.0 + * @param[in] dest The dbus bus name. + * @param[in] path The dbus object path. + * @param[in] iface The dbus interface name. + * @param[in] method The dbus method name. + * @param[in] sig The format string of parameter. + * @param[in] param The parameter array. + * @return return value of the method + */ +int call_dbus_method_sync(const char *dest, + const char *path, const char *iface, + const char *method, const char *sig, char *param[], + int timeout, DBusMessage **info); + +/** + * @brief Call dbus method Asynchronously and get return value from the callback function. + * @remarks The return value with the DBusMessage type is delivered to the callback function. + * @since_tizen 4.0 + * @param[in] dest The dbus bus name. + * @param[in] path The dbus object path. + * @param[in] iface The dbus interface name. + * @param[in] method The dbus method name. + * @param[in] sig The format string of parameter. + * @param[in] param The parameter array. + * @param[in] cb The callback function which is called when the dbus method returns. + * @param[in] timeout The timeout for calling the callback function. + * @param[in] data The user data which is delivered to the callback function. + * @return @c 0 on success, + * otherwise a negative error value + */ +int call_dbus_method_async(const char *dest, + const char *path, const char *iface, const char *method, + const char *sig, char *param[], + dbus_pending_cb cb, int timeout, void *data); + +/** + * @brief Call dbus method for lauching apps synchronously + * @since_tizen 4.0 + * @param[in] dest The dbus bus name. + * @param[in] path The dbus object path. + * @param[in] iface The dbus interface name. + * @param[in] method The dbus method name. + * @param[in] num The number of parameters constained in args + * @param[in] args The va_list variable which contains the parameters + * @return return value of the method + */ +int call_dbus_method_sync_pairs(const char *dest, + const char *path, const char *iface, const char *method, + int num, va_list args); + +/** + * @brief Call dbus method for lauching apps Asynchronously + * @since_tizen 4.0 + * @param[in] dest The dbus bus name. + * @param[in] path The dbus object path. + * @param[in] iface The dbus interface name. + * @param[in] method The dbus method name. + * @param[in] num The number of parameters constained in args + * @param[in] args The va_list variable which contains the parameters + * @return @c 0 on success, + * otherwise a negative error value + */ + +int call_dbus_method_async_pairs(const char *dest, + const char *path, const char *iface, const char *method, + int num, va_list args); + +#endif /* __STORAGED_BUS_H__ */ diff --git a/src/shared/dbus_macro.h b/src/shared/dbus_macro.h new file mode 100755 index 0000000..093c02e --- /dev/null +++ b/src/shared/dbus_macro.h @@ -0,0 +1,94 @@ +/* + * storaged + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#ifndef __STORAGED_DBUS_MACRO_H__ +#define __STORAGED_DBUS_MACRO_H__ + +/* + * Template + * +#define XXX_BUS_NAME "org.tizen.system.XXX" +#define XXX_OBJECT_PATH "/Org/Tizen/System/XXX" +#define XXX_INTERFACE_NAME XXX_BUS_NAME +#define XXX_PATH_YYY XXX_OBJECT_PATH"/YYY" +#define XXX_INTERFACE_YYY XXX_INTERFACE_NAME".YYY" +#define XXX_SIGNAL_ZZZ "ZZZ" +#define XXX_METHOD_ZZZ "ZZZ" + */ + +/* + * DBus daemon + */ +#define DBUS_BUS_NAME "org.freedesktop.DBus" +#define DBUS_OBJECT_PATH "/org/freedesktop/DBus" +#define DBUS_INTERFACE_NAME DBUS_BUS_NAME + +/* + * Device daemon + */ +#define DEVICED_BUS_NAME "org.tizen.system.deviced" +#define DEVICED_OBJECT_PATH "/Org/Tizen/System/DeviceD" +#define DEVICED_INTERFACE_NAME DEVICED_BUS_NAME +/* Core service: get/set device status operations about device */ +#define DEVICED_PATH_CORE DEVICED_OBJECT_PATH"/Core" +#define DEVICED_INTERFACE_CORE DEVICED_INTERFACE_NAME".core" +/* Power service: set resetkey disable operations about power */ +#define DEVICED_PATH_POWER DEVICED_OBJECT_PATH"/Power" +#define DEVICED_INTERFACE_POWER DEVICED_INTERFACE_NAME".power" +/* Poweroff service: get power off status operations about Poweroff */ +#define DEVICED_PATH_POWEROFF DEVICED_OBJECT_PATH"/PowerOff" +#define DEVICED_INTERFACE_POWEROFF DEVICED_INTERFACE_NAME".PowerOff" +/* Block service: manage block device */ +#define STORAGED_BUS_NAME "org.tizen.system.storage" +#define STORAGED_OBJECT_PATH "/Org/Tizen/System/Storage" +#define STORAGED_INTERFACE_NAME STORAGED_BUS_NAME +#define STORAGED_PATH_BLOCK STORAGED_OBJECT_PATH"/Block" +#define STORAGED_PATH_BLOCK_MANAGER STORAGED_PATH_BLOCK"/Manager" +#define STORAGED_INTERFACE_BLOCK_MANAGER STORAGED_INTERFACE_NAME".BlockManager" +/* Storage service: get storage size operatioins about storage */ +#define STORAGED_PATH_STORAGE STORAGED_OBJECT_PATH"/Storage" +#define STORAGED_INTERFACE_STORAGE STORAGED_INTERFACE_NAME".storage" +/* Lowmem service: get critical low status operations about Lowmem */ +#define STORAGED_PATH_LOWMEM STORAGED_OBJECT_PATH"/Lowmem" +#define STORAGED_INTERFACE_LOWMEM STORAGED_INTERFACE_NAME".lowmem" + +/* + * Popup launcher + */ +#define POPUP_BUS_NAME "org.tizen.system.popup" +#define POPUP_OBJECT_PATH "/Org/Tizen/System/Popup" +#define POPUP_INTERFACE_NAME POPUP_BUS_NAME +/* Low memory */ +#define POPUP_PATH_LOWMEM POPUP_OBJECT_PATH"/Lowmem" +#define POPUP_INTERFACE_LOWMEM POPUP_INTERFACE_NAME".Lowmem" +/* MMC */ +#define POPUP_PATH_MMC POPUP_OBJECT_PATH"/Mmc" +#define POPUP_INTERFACE_MMC POPUP_INTERFACE_NAME".Mmc" +/* System */ +#define POPUP_PATH_SYSTEM POPUP_OBJECT_PATH"/System" +#define POPUP_INTERFACE_SYSTEM POPUP_INTERFACE_NAME".System" + +#define POPUP_PATH_APP POPUP_OBJECT_PATH"/Apps" +#define POPUP_IFACE_APP POPUP_BUS_NAME".Apps" + +#define POPUP_METHOD_LAUNCH "PopupLaunch" +#define POPUP_METHOD_TERMINATE "AppTerminateByPid" +#define POPUP_KEY_CONTENT "_SYSPOPUP_CONTENT_" + +#endif /* __STORAGED_DBUS_MACRO_H__ */ diff --git a/src/shared/fd_handler.c b/src/shared/fd_handler.c new file mode 100644 index 0000000..c95742e --- /dev/null +++ b/src/shared/fd_handler.c @@ -0,0 +1,133 @@ +/* + * storaged + * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. +*/ + +#include +#include +#include +#include +#include + +#include "log.h" +#include "fd_handler.h" + +struct fd_handler_s { + int fd; + GIOChannel *ch; + guint id; + fd_changed_cb changed; + void *data; + release_cb free_func; + fd_handler_h *handler_p; +}; + +int remove_fd_read_handler(fd_handler_h *handler) +{ + struct fd_handler_s *h; + + if (!handler || !(*handler)) + return -EINVAL; + + h = *handler; + + if (h->free_func) + h->free_func(h->data); + + if (h->id) + g_source_remove(h->id); + + if (h->ch) + g_io_channel_unref(h->ch); + + *(h->handler_p) = NULL; + free(h); + + return 0; +} + +static gboolean channel_changed(GIOChannel *source, + GIOCondition condition, gpointer data) +{ + struct fd_handler_s *h = data; + bool ret; + + if (!h) + return TRUE; + + if (h->ch != source) + return TRUE; + + if (h->fd != g_io_channel_unix_get_fd(source)) + return TRUE; + + if (condition != G_IO_IN) + return TRUE; + + ret = true; + if (h->changed) + ret = h->changed(h->fd, h->data); + + if (ret) + return TRUE; + + remove_fd_read_handler((fd_handler_h *)&h); + + return FALSE; +} + +int add_fd_read_handler(int fd, + fd_changed_cb callback, void *data, + release_cb free_func, fd_handler_h *handler) +{ + GIOChannel *ch; + struct fd_handler_s *h; + guint id; + + if (fd < 0 || !callback) + return -EINVAL; + + h = calloc(1, sizeof(struct fd_handler_s)); + if (!h) + return -ENOMEM; + + ch = g_io_channel_unix_new(fd); + if (!ch) { + _E("Failed to create GIOChannel"); + free(h); + return -ENOMEM; + } + + id = g_io_add_watch(ch, G_IO_IN | G_IO_ERR, + channel_changed, h); + if (id == 0) { + _E("Failed to add watch for GIOChannel"); + free(h); + g_io_channel_unref(ch); + return -ENOMEM; + } + + h->fd = fd; + h->changed = callback; + h->data = data; + h->free_func = free_func; + h->ch = ch; + h->id = id; + h->handler_p = handler; + + *handler = h; + + return 0; +} diff --git a/src/shared/fd_handler.h b/src/shared/fd_handler.h new file mode 100644 index 0000000..6bf5812 --- /dev/null +++ b/src/shared/fd_handler.h @@ -0,0 +1,69 @@ +/* + * storaged + * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + + +#ifndef __STORAGED_FD_HANDLER_H__ +#define __STORAGED_FD_HANDLER_H__ + +#include +#include +#include + +/** + * @brief File descriptor handler + * @since_tizen 4.0 + */ +typedef void *fd_handler_h; + +/** + * @brief Type for the file descriptor change callback function + * @since_tizen 4.0 + */ +typedef bool (*fd_changed_cb)(int fd, void *data); + +/** + * @brief Type of the callback function to release user data + * @since_tizen 4.0 + */ +typedef void (*release_cb)(void *data); + +/** + * @brief Register file descriptor callback function + * @since_tizen 4.0 + * @param[in] fd The file descriptor to notify the changes. + * @param[in] callback The callback function which will be called when the fd is changed. + * @param[in] data The user data which is delivered to the callback function. + * @param[in] free_func The callback function to release the user data which is called when the handler is removed by remove_fd_read_handler(). + * @param[out] handler The handler which will be used to remove the fd handler. + * @return @c 0 on success, + * otherwise a negative error value + * @remarks If the callback function returns false, the fd handler is removed automatically and the callback will not be called again. + */ +int add_fd_read_handler(int fd, fd_changed_cb callback, + void *data, release_cb free_func, + fd_handler_h *handler); + +/** + * @brief Unregister file descriptor callback function + * @since_tizen 4.0 + * @param[in] handler The fd handler which is obtained by add_fd_read_handler(). + * @return @c 0 on success, + * otherwise a negative error value + */ +int remove_fd_read_handler(fd_handler_h *handler); + +#endif /* __STORAGED_FD_HANDLER_H__ */ diff --git a/src/shared/list.h b/src/shared/list.h new file mode 100644 index 0000000..c355a0a --- /dev/null +++ b/src/shared/list.h @@ -0,0 +1,62 @@ +/* + * storaged + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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. + */ + +#ifndef __LIST_H__ +#define __LIST_H__ + +#include +typedef GList dd_list; + +#define DD_LIST_PREPEND(a, b) \ + a = g_list_prepend(a, (gpointer)b) +#define DD_LIST_APPEND(a, b) \ + a = g_list_append(a, (gpointer)b) +#define DD_LIST_REMOVE(a, b) \ + a = g_list_remove(a, (gpointer)b) +#define DD_LIST_REMOVE_LIST(a, b) \ + a = g_list_delete_link(a, b) +#define DD_LIST_LENGTH(a) \ + g_list_length(a) +#define DD_LIST_NTH(a, b) \ + g_list_nth_data(a, b) +#define DD_LIST_FIND(a, b) \ + g_list_find(a, (gpointer)b) +#define DD_LIST_FREE_LIST(a) \ + g_list_free(a) +#define DD_LIST_SORT(a, func) \ + a = g_list_sort(a, func) +#define DD_LIST_FOREACH(head, elem, node) \ + for (elem = head, node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = elem->next, node = NULL) +#define DD_LIST_FOREACH_SAFE(head, elem, elem_next, node) \ + for (elem = head, elem_next = g_list_next(elem), node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = elem_next, elem_next = g_list_next(elem), node = NULL) +#define DD_LIST_REVERSE_FOREACH(head, elem, node) \ + for (elem = g_list_last(head), node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = g_list_previous(elem), node = NULL) +#define DD_LIST_REVERSE_FOREACH_SAFE(head, elem, elem_next, node) \ + for (elem = g_list_last(head), elem_next = g_list_previous(elem), node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = elem_next, elem_next = g_list_previous(elem), node = NULL) +#define DD_LIST_NEXT(a) \ + g_list_next(a) + +#endif diff --git a/src/shared/log.h b/src/shared/log.h new file mode 100644 index 0000000..d9da05d --- /dev/null +++ b/src/shared/log.h @@ -0,0 +1,45 @@ +/* + * storaged + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#ifndef __STORAGED_LOG_H__ +#define __STORAGED_LOG_H__ + +#include + +#undef LOG_TAG +#define LOG_TAG "STORAGED" + +#define _D(fmt, arg...) \ + do { SLOGD(fmt, ##arg); } while (0) +#define _I(fmt, arg...) \ + do { SLOGI(fmt, ##arg); } while (0) +#define _W(fmt, arg...) \ + do { SLOGW(fmt, ##arg); } while (0) +#define _E(fmt, arg...) \ + do { SLOGE(fmt, ##arg); } while (0) +#define _SD(fmt, arg...) \ + do { SECURE_SLOGD(fmt, ##arg); } while (0) +#define _SI(fmt, arg...) \ + do { SECURE_SLOGI(fmt, ##arg); } while (0) +#define _SW(fmt, arg...) \ + do { SECURE_SLOGW(fmt, ##arg); } while (0) +#define _SE(fmt, arg...) \ + do { SECURE_SLOGE(fmt, ##arg); } while (0) + +#endif /* __STORAGED_LOG_H__ */ diff --git a/src/shared/module-intf.h b/src/shared/module-intf.h new file mode 100644 index 0000000..fe4a897 --- /dev/null +++ b/src/shared/module-intf.h @@ -0,0 +1,34 @@ +/* + * storaged + * + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#ifndef __STORAGED_MODULE_INTF_H__ +#define __STORAGED_MODULE_INTF_H__ + + +typedef struct { + void *dso; + const char *name; + void (*init)(void *data); + void (*exit)(void *data); + int (*start)(void *data); + int (*stop)(void *data); + int (*execute)(void *data); +} storaged_module_interface; + +#endif /* __STORAGED_MODULE_INTF_H__ */ diff --git a/src/shared/udev.c b/src/shared/udev.c new file mode 100644 index 0000000..de5a927 --- /dev/null +++ b/src/shared/udev.c @@ -0,0 +1,299 @@ +/* + * storaged + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#include +#include +#include + +#include "log.h" +#include "fd_handler.h" +#include "udev.h" +#include "list.h" +#include "dbus.h" + +#define KERNEL "kernel" +#define UDEV "udev" + +#define SIGNAL_POWEROFF_STATE "ChangeState" + +#define UDEV_MONITOR_SIZE (10*1024) + +struct uevent_info { + struct udev_monitor *mon; + fd_handler_h handler; + dd_list *event_list; +}; + +/* Uevent */ +static struct udev *udev; + +static struct uevent_info kevent; /* kernel */ +static struct uevent_info uevent; /* udev */ + +static bool uevent_control_cb(int fd, void *data) +{ + struct uevent_info *info = data; + struct udev_device *dev; + struct uevent_handler *l; + dd_list *elem; + const char *subsystem; + int len; + + assert(info); + + dev = udev_monitor_receive_device(info->mon); + if (!dev) + return true; + + subsystem = udev_device_get_subsystem(dev); + if (!subsystem) + goto out; + + len = strlen(subsystem); + DD_LIST_FOREACH(info->event_list, elem, l) { + if (!strncmp(l->subsystem, subsystem, len) && + l->uevent_func) + l->uevent_func(dev); + } + +out: + udev_device_unref(dev); + return true; +} + +static int uevent_control_stop(struct uevent_info *info) +{ + struct udev_device *dev; + + if (!info) + return -EINVAL; + + if (info->handler) { + remove_fd_read_handler(&(info->handler)); + info->handler = NULL; + } + if (info->mon) { + dev = udev_monitor_receive_device(info->mon); + if (dev) + udev_device_unref(dev); + udev_monitor_unref(info->mon); + info->mon = NULL; + } + if (udev) + udev = udev_unref(udev); + return 0; +} + +static int uevent_control_start(const char *type, + struct uevent_info *info) +{ + struct uevent_handler *l; + dd_list *elem; + int fd; + int ret; + + if (!info) + return -EINVAL; + + if (info->mon) { + _E("%s uevent control routine is alreay started", type); + return -EINVAL; + } + + if (!udev) { + udev = udev_new(); + if (!udev) { + _E("error create udev"); + return -EINVAL; + } + } else + udev = udev_ref(udev); + + info->mon = udev_monitor_new_from_netlink(udev, type); + if (info->mon == NULL) { + _E("error udev_monitor create"); + goto stop; + } + + ret = udev_monitor_set_receive_buffer_size(info->mon, + UDEV_MONITOR_SIZE); + if (ret != 0) { + _E("fail to set receive buffer size"); + goto stop; + } + + DD_LIST_FOREACH(info->event_list, elem, l) { + ret = udev_monitor_filter_add_match_subsystem_devtype( + info->mon, + l->subsystem, NULL); + if (ret < 0) { + _E("error apply subsystem filter"); + goto stop; + } + } + + ret = udev_monitor_filter_update(info->mon); + if (ret < 0) + _E("error udev_monitor_filter_update"); + + fd = udev_monitor_get_fd(info->mon); + if (fd == -1) { + _E("error udev_monitor_get_fd"); + goto stop; + } + + ret = add_fd_read_handler(fd, uevent_control_cb, info, NULL, &(info->handler)); + if (ret < 0) { + _E("Failed to add fd handler (%d)", ret); + goto stop; + } + + if (udev_monitor_enable_receiving(info->mon) < 0) { + _E("error unable to subscribe to udev events"); + goto stop; + } + + return 0; +stop: + uevent_control_stop(info); + return -EINVAL; +} + +static int register_uevent_control(struct uevent_info *info, + const struct uevent_handler *uh) +{ + struct uevent_handler *l; + dd_list *elem; + int r; + bool matched = false; + int len; + + if (!info || !uh || !uh->subsystem) + return -EINVAL; + + /* if udev is not initialized, it just will be added list */ + if (!udev || !info->mon) + goto add_list; + + len = strlen(uh->subsystem); + /* check if the same subsystem is already added */ + DD_LIST_FOREACH(info->event_list, elem, l) { + if (!strncmp(l->subsystem, uh->subsystem, len)) { + matched = true; + break; + } + } + + /* the first request to add subsystem */ + if (!matched) { + r = udev_monitor_filter_add_match_subsystem_devtype(info->mon, + uh->subsystem, NULL); + if (r < 0) { + _E("fail to add %s subsystem : %d", uh->subsystem, r); + return -EPERM; + } + } + + r = udev_monitor_filter_update(info->mon); + if (r < 0) + _E("fail to update udev monitor filter : %d", r); + +add_list: + DD_LIST_APPEND(info->event_list, uh); + return 0; +} + +static int unregister_uevent_control(struct uevent_info *info, + const struct uevent_handler *uh) +{ + struct uevent_handler *l; + dd_list *n, *next; + int len; + + if (!info || !uh || !uh->subsystem) + return -EINVAL; + + len = strlen(uh->subsystem); + DD_LIST_FOREACH_SAFE(info->event_list, n, next, l) { + if (!strncmp(l->subsystem, uh->subsystem, len) && + l->uevent_func == uh->uevent_func) { + DD_LIST_REMOVE(info->event_list, l); + return 0; + } + } + + return -ENOENT; +} + +int register_kernel_uevent_control(const struct uevent_handler *uh) +{ + return register_uevent_control(&kevent, uh); +} + +int unregister_kernel_uevent_control(const struct uevent_handler *uh) +{ + return unregister_uevent_control(&kevent, uh); +} + +int register_udev_uevent_control(const struct uevent_handler *uh) +{ + return register_uevent_control(&uevent, uh); +} + +int unregister_udev_uevent_control(const struct uevent_handler *uh) +{ + return unregister_uevent_control(&uevent, uh); +} + +static void device_change_poweroff(const char *sender_name, + const char *object_path, const char *interface_name, + const char *signal_name, DBusMessage *msg, + void *data) +{ + static int status = 0; + if (status > 0) + return; + status = 1; + _I("Power off"); + uevent_control_stop(&kevent); + uevent_control_stop(&uevent); +} + +void udev_init(void *data) +{ + register_dbus_signal(DEVICED_PATH_POWEROFF, + DEVICED_INTERFACE_POWEROFF, + SIGNAL_POWEROFF_STATE, + device_change_poweroff, + NULL, NULL); + + if (uevent_control_start(KERNEL, &kevent) != 0) + _E("fail uevent kernel control init"); + + if (uevent_control_start(UDEV, &uevent) != 0) + _E("fail uevent udev control init"); +} + +void udev_exit(void *data) +{ + unregister_dbus_signal(DEVICED_PATH_POWEROFF, + DEVICED_INTERFACE_POWEROFF, + SIGNAL_POWEROFF_STATE, + device_change_poweroff); +} diff --git a/src/shared/udev.h b/src/shared/udev.h new file mode 100644 index 0000000..7fabf15 --- /dev/null +++ b/src/shared/udev.h @@ -0,0 +1,52 @@ +/* + * storaged + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + + +#ifndef __UDEV_H__ +#define __UDEV_H__ + +#include + +#define UDEV_CHANGE "change" +#define UDEV_ADD "add" +#define UDEV_REMOVE "remove" + +#define UDEV_DEVPATH "DEVPATH" +#define UDEV_DEVTYPE "DEVTYPE" + +/* block */ +#define BLOCK_SUBSYSTEM "block" +#define BLOCK_DEVTYPE_DISK "disk" +#define BLOCK_DEVTYPE_PARTITION "partition" + +struct uevent_handler { + char *subsystem; + void (*uevent_func)(struct udev_device *dev); + void *data; +}; + +void udev_init(void *data); +void udev_exit(void *data); + +int register_kernel_uevent_control(const struct uevent_handler *uh); +int unregister_kernel_uevent_control(const struct uevent_handler *uh); + +int register_udev_uevent_control(const struct uevent_handler *uh); +int unregister_udev_uevent_control(const struct uevent_handler *uh); + +#endif /* __UDEV_H__ */ diff --git a/src/storage/CMakeLists.txt b/src/storage/CMakeLists.txt new file mode 100644 index 0000000..e2aea8b --- /dev/null +++ b/src/storage/CMakeLists.txt @@ -0,0 +1,38 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(module_storage C) + +INCLUDE(FindPkgConfig) +pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED + dlog + dbus-1 + gio-2.0 + glib-2.0 + storage + vconf +) + +FOREACH(flag ${${PROJECT_NAME}_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wall -Werror -fvisibility=hidden -rdynamic") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g") + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/share) + +FILE(GLOB ALL_SRCS "*.c") +SET(SRCS ${ALL_SRCS}) +SET(SHARED_SRCS + ../shared/config-parser.c + ../shared/dbus.c + ../shared/fd_handler.c +) + +ADD_LIBRARY(${PROJECT_NAME} ${SRCS} ${SHARED_SRCS}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${${PROJECT_NAME}_pkgs_LDFLAGS}) + +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES PREFIX "") +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR}/storaged COMPONENT RuntimeLibraries) + +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/storage.conf DESTINATION /etc/storaged) diff --git a/src/storage/storage.c b/src/storage/storage.c new file mode 100755 index 0000000..2f4e3cf --- /dev/null +++ b/src/storage/storage.c @@ -0,0 +1,468 @@ +/* + * storaged + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "config-parser.h" +#include "dbus.h" +#include "module-intf.h" + +#define MEMORY_STATUS_TMP_PATH "/tmp" +#define MEMNOTI_TMP_CRITICAL_VALUE (20) + +#define MEMORY_MEGABYTE_VALUE 1048576 + +#define MEMNOTI_WARNING_VALUE (5) /* 5% under */ +#define MEMNOTI_CRITICAL_VALUE (0.1) /* 0.1% under */ +#define MEMNOTI_FULL_VALUE (0.0) /* 0.0% under */ + +#define SIGNAL_LOWMEM_STATE "ChangeState" +#define SIGNAL_LOWMEM_FULL "Full" +#define MEMNOTI_TIMER_INTERVAL 5000 /* milliseconds */ + +#define SIGNAL_BOOTING_DONE "BootingDone" +#define SIGNAL_POWEROFF_STATE "ChangeState" + +#define STORAGE_CONF_FILE "/etc/storaged/storage.conf" + +#define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0])) + +enum memnoti_level { + MEMNOTI_LEVEL_CRITICAL = 0, + MEMNOTI_LEVEL_WARNING, + MEMNOTI_LEVEL_NORMAL, + MEMNOTI_LEVEL_FULL, +}; + +enum memnoti_status { + MEMNOTI_DISABLE, + MEMNOTI_ENABLE, +}; + +struct storage_config_info { + enum memnoti_level current_noti_level; + double warning_level; + double critical_level; + double full_level; +}; + +static guint memnoti_timer; + +static struct storage_config_info storage_internal_info = { + .current_noti_level = MEMNOTI_LEVEL_NORMAL, + .warning_level = MEMNOTI_WARNING_VALUE, + .critical_level = MEMNOTI_CRITICAL_VALUE, + .full_level = MEMNOTI_FULL_VALUE, +}; + +static struct storage_config_info storage_tmp_info = { + .current_noti_level = MEMNOTI_LEVEL_NORMAL, + .warning_level = MEMNOTI_TMP_CRITICAL_VALUE, + .critical_level = MEMNOTI_TMP_CRITICAL_VALUE, + .full_level = MEMNOTI_FULL_VALUE, +}; + +static void memnoti_send_broadcast(char *signal, int status) +{ + char *arr[1]; + char str_status[32]; + + _I("signal %s status %d", signal, status); + snprintf(str_status, sizeof(str_status), "%d", status); + arr[0] = str_status; + broadcast_dbus_signal(STORAGED_PATH_LOWMEM, STORAGED_INTERFACE_LOWMEM, + signal, "i", arr); +} + +static int launch_memory_popup(int num, ...) +{ + int ret; + va_list args; + + va_start(args, num); + + ret = call_dbus_method_async_pairs(POPUP_BUS_NAME, + POPUP_PATH_SYSTEM, + POPUP_INTERFACE_SYSTEM, + "PopupLaunch", + num, args); + + va_end(args); + + return ret; +} + +static int memnoti_popup(enum memnoti_level level) +{ + int ret = -1; + int val = -1; + char *value = NULL; + + if (level != MEMNOTI_LEVEL_WARNING && level != MEMNOTI_LEVEL_CRITICAL) { + _E("level check error : %d", level); + return 0; + } + + if (level == MEMNOTI_LEVEL_WARNING) + value = "lowstorage_warning"; + else if (level == MEMNOTI_LEVEL_CRITICAL) + value = "lowstorage_critical"; + + ret = vconf_get_int(VCONFKEY_STARTER_SEQUENCE, &val); + if (val == 0 || ret != 0) + return -1; + + if (!value) + return 0; + + return launch_memory_popup(2, "_SYSPOPUP_CONTENT_", value); +} + +static void storage_status_broadcast(struct storage_config_info *info, double total, double avail) +{ + double level = (avail/total)*100; + int status = MEMNOTI_DISABLE; + + if (level <= info->full_level) { + if (info->current_noti_level == MEMNOTI_LEVEL_FULL) + return; + info->current_noti_level = MEMNOTI_LEVEL_FULL; + status = MEMNOTI_ENABLE; + memnoti_send_broadcast(SIGNAL_LOWMEM_FULL, status); + _I("current level %4.4lf w:%4.4lf c:%4.4lf f:%4.4lf", + level, info->warning_level, info->critical_level, info->full_level); + return; + } + + if (level <= info->critical_level) { + if (info->current_noti_level == MEMNOTI_LEVEL_CRITICAL) + return; + if (info->current_noti_level == MEMNOTI_LEVEL_FULL) + memnoti_send_broadcast(SIGNAL_LOWMEM_FULL, status); + info->current_noti_level = MEMNOTI_LEVEL_CRITICAL; + status = MEMNOTI_ENABLE; + memnoti_send_broadcast(SIGNAL_LOWMEM_STATE, status); + _I("current level %4.4lf w:%4.4lf c:%4.4lf f:%4.4lf", + level, info->warning_level, info->critical_level, info->full_level); + return; + } + + if (info->current_noti_level == MEMNOTI_LEVEL_FULL) + memnoti_send_broadcast(SIGNAL_LOWMEM_FULL, status); + if (info->current_noti_level == MEMNOTI_LEVEL_CRITICAL) + memnoti_send_broadcast(SIGNAL_LOWMEM_STATE, status); + if (level <= info->warning_level) + info->current_noti_level = MEMNOTI_LEVEL_WARNING; + else + info->current_noti_level = MEMNOTI_LEVEL_NORMAL; +} + +static int storage_get_memory_size(const char *path, struct statvfs *s) +{ + int ret; + + if (!path) { + _E("input param error"); + return -EINVAL; + } + + ret = statvfs(path, s); + if (ret) { + _E("fail to get storage size"); + return -errno; + } + + return 0; +} + +static void get_storage_status(const char *path, struct statvfs *s) +{ + if (strcmp(path, tzplatform_getenv(TZ_SYS_HOME)) == 0) + storage_get_internal_memory_size(s); + else + storage_get_memory_size(path, s); +} + +static void init_storage_config_info(const char *path, struct storage_config_info *info) +{ + struct statvfs s; + double dAvail = 0.0; + double dTotal = 0.0; + + get_storage_status(path, &s); + + dTotal = (double)s.f_frsize * s.f_blocks; + dAvail = (double)s.f_bsize * s.f_bavail; + + info->full_level += (MEMORY_MEGABYTE_VALUE/dTotal)*100; + + _I("%s t: %4.0lf a: %4.0lf(%4.2lf) w:%4.4lf c:%4.4lf f:%4.4lf", + path, dTotal, dAvail, (dAvail*100/dTotal), info->warning_level, info->critical_level, info->full_level); +} + +static void check_internal_storage_popup(struct storage_config_info *info) +{ + static enum memnoti_level old = MEMNOTI_LEVEL_NORMAL; + int ret; + + if (info->current_noti_level < MEMNOTI_LEVEL_NORMAL && info->current_noti_level < old) { + ret = memnoti_popup(info->current_noti_level); + if (ret != 0) + info->current_noti_level = MEMNOTI_LEVEL_NORMAL; + } + old = info->current_noti_level; +} + +static gboolean check_storage_status(gpointer data) +{ + struct statvfs s; + double dAvail = 0.0; + double dTotal = 0.0; + + /* check internal */ + storage_get_internal_memory_size(&s); + dTotal = (double)s.f_frsize * s.f_blocks; + dAvail = (double)s.f_bsize * s.f_bavail; + storage_status_broadcast(&storage_internal_info, dTotal, dAvail); + check_internal_storage_popup(&storage_internal_info); + /* check tmp */ + storage_get_memory_size(MEMORY_STATUS_TMP_PATH, &s); + dTotal = (double)s.f_frsize * s.f_blocks; + dAvail = (double)s.f_bsize * s.f_bavail; + storage_status_broadcast(&storage_tmp_info, dTotal, dAvail); + + if (memnoti_timer) { + g_source_remove(memnoti_timer); + memnoti_timer = g_timeout_add(MEMNOTI_TIMER_INTERVAL, + check_storage_status, NULL); + } + + return G_SOURCE_REMOVE; +} + +static int init_storage_config_info_all(void) +{ + init_storage_config_info(tzplatform_getenv(TZ_SYS_HOME), &storage_internal_info); + init_storage_config_info(MEMORY_STATUS_TMP_PATH, &storage_tmp_info); + memnoti_timer = g_timeout_add(MEMNOTI_TIMER_INTERVAL, + check_storage_status, NULL); + if (memnoti_timer == 0) + _E("fail mem available noti timer add"); + return 0; +} + +static DBusMessage *dbus_getstatus(dbus_method_reply_handle_h reply_handle, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + struct statvfs s; + unsigned long long dAvail = 0.0; + unsigned long long dTotal = 0.0; + + storage_get_internal_memory_size(&s); + dTotal = (unsigned long long)s.f_frsize * s.f_blocks; + dAvail = (unsigned long long)s.f_bsize * s.f_bavail; + + reply = make_dbus_reply_message(reply_handle); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT64, &dTotal); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT64, &dAvail); + return reply; +} + +static DBusMessage *dbus_get_storage_status(dbus_method_reply_handle_h reply_handle, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + DBusError err; + char *path; + struct statvfs s; + pid_t pid; + unsigned long long dAvail = 0.0; + unsigned long long dTotal = 0.0; + + dbus_error_init(&err); + if (!dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) { + _E("Bad message: [%s:%s]", err.name, err.message); + dbus_error_free(&err); + goto out; + } + + if (!strcmp(path, tzplatform_getenv(TZ_SYS_HOME))) + storage_get_internal_memory_size(&s); + else + storage_get_memory_size(path, &s); + + dTotal = (unsigned long long)s.f_frsize * s.f_blocks; + dAvail = (unsigned long long)s.f_bsize * s.f_bavail; + + pid = get_dbus_method_sender_pid(reply_handle); + + _D("[request %d] path %s total %4.0llu avail %4.0llu", pid, path, dTotal, dAvail); + +out: + reply = make_dbus_reply_message(reply_handle); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT64, &dTotal); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT64, &dAvail); + return reply; +} + +static const dbus_method_s storage_methods[] = { + { "getstorage", NULL, dbus_getstatus }, + { "GetStatus", "s", dbus_get_storage_status}, + /* Add methods here */ +}; + +static dbus_interface_s storage_interface = { + .name = STORAGED_INTERFACE_STORAGE, + .methods = storage_methods, + .nr_methods = ARRAY_SIZE(storage_methods), +}; + +static void booting_done(const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + DBusMessage *msg, + void *data) +{ + static int done; + + if (done > 0) + return; + done = 1; + _I("booting done"); + + if (init_storage_config_info_all() == -1) + _E("fail remain mem noti control fd init"); +} + +static void storage_poweroff(const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + DBusMessage *msg, + void *data) +{ + if (memnoti_timer) { + g_source_remove(memnoti_timer); + memnoti_timer = 0; + } +} + +static int load_config(struct parse_result *result, void *user_data) +{ + struct storage_config_info *info = (struct storage_config_info *)user_data; + char *name; + char *value; + + if (!info) + return -EINVAL; + + if (!MATCH(result->section, "LOWSTORAGE")) + return -EINVAL; + + _D("%s,%s,%s", result->section, result->name, result->value); + + name = result->name; + value = result->value; + + if (MATCH(name, "WARNING_LEVEL")) + info->warning_level = (double)atof(value); + else if (MATCH(name, "CRITICAL_LEVEL")) + info->critical_level = (double)atof(value); + else if (MATCH(name, "FULL_LEVEL")) + info->full_level = (double)atof(value); + + return 0; +} + +static void storage_config_load(struct storage_config_info *info) +{ + int ret; + + ret = config_parse(STORAGE_CONF_FILE, load_config, info); + if (ret < 0) + _E("Failed to load %s, %d Use default value!", STORAGE_CONF_FILE, ret); +} + +static void storage_init(void *data) +{ + int ret; + dbus_handle_h handle; + + ret = dbus_get_connection(&handle); + if (ret < 0) { + _E("Failed to get dbus connection(%d)", ret); + return; + } + + storage_config_load(&storage_internal_info); + register_dbus_signal(DEVICED_PATH_CORE, + DEVICED_INTERFACE_CORE, + SIGNAL_BOOTING_DONE, + booting_done, NULL, NULL); + + register_dbus_signal(DEVICED_PATH_POWEROFF, + DEVICED_INTERFACE_POWEROFF, + SIGNAL_POWEROFF_STATE, + storage_poweroff, NULL, NULL); + + ret = register_dbus_methods(handle, STORAGED_PATH_STORAGE, + &storage_interface, NULL, NULL); + if (ret < 0) + _E("Failed to register dbus interface and methods(%d)", ret); +} + +static void storage_exit(void *data) +{ + unregister_dbus_signal(DEVICED_PATH_CORE, + DEVICED_INTERFACE_CORE, + SIGNAL_BOOTING_DONE, booting_done); + + unregister_dbus_signal(DEVICED_PATH_POWEROFF, + DEVICED_INTERFACE_POWEROFF, + SIGNAL_POWEROFF_STATE, storage_poweroff); +} + +static storaged_module_interface storage_module = { + .name = "storage", + .init = storage_init, + .exit = storage_exit, +}; + +__attribute__ ((visibility("default")))storaged_module_interface * +storaged_get_module_interface(void) +{ + return &storage_module; +} diff --git a/src/storage/storage.conf b/src/storage/storage.conf new file mode 100644 index 0000000..331e786 --- /dev/null +++ b/src/storage/storage.conf @@ -0,0 +1,7 @@ +[LOWSTORAGE] +#5% +WARNING_LEVEL=5 +#0.1% +CRITICAL_LEVEL=0.1 +#0.0% +FULL_LEVEL=0 diff --git a/systemd/storaged.service b/systemd/storaged.service new file mode 100644 index 0000000..a0b7562 --- /dev/null +++ b/systemd/storaged.service @@ -0,0 +1,14 @@ +[Unit] +Description=System storage daemon + +[Service] +Type=notify +SmackProcessLabel=System::Privileged +ExecStart=/usr/bin/storaged +Restart=always +RestartSec=0 +KillSignal=SIGUSR1 +NotifyAccess=main + +[Install] +WantedBy=multi-user.target -- 2.7.4