Create initial commit 92/118392/13 accepted/tizen_common accepted/tizen_ivi accepted/tizen_mobile accepted/tizen_tv accepted/tizen_wearable accepted/tizen/common/20170403.190742 accepted/tizen/ivi/20170403.130344 accepted/tizen/mobile/20170403.130241 accepted/tizen/tv/20170403.130316 accepted/tizen/unified/20170403.130359 accepted/tizen/wearable/20170403.130328 submit/tizen/20170403.054337
authorpr.jung <pr.jung@samsung.com>
Fri, 10 Mar 2017 07:25:33 +0000 (16:25 +0900)
committerpr.jung <pr.jung@samsung.com>
Mon, 3 Apr 2017 05:12:21 +0000 (14:12 +0900)
Change-Id: I8a3f42cc179eb266a502dbf1c756a32a64b91f79
Signed-off-by: pr.jung <pr.jung@samsung.com>
Signed-off-by: taeyoung <ty317.kim@samsung.com>
38 files changed:
CMakeLists.txt [new file with mode: 0755]
LICENSE [new file with mode: 0644]
packaging/storaged.manifest [new file with mode: 0644]
packaging/storaged.spec [new file with mode: 0644]
scripts/storaged.conf [new file with mode: 0644]
src/block/CMakeLists.txt [new file with mode: 0644]
src/block/block.c [new file with mode: 0644]
src/block/block.conf [new file with mode: 0644]
src/block/block.h [new file with mode: 0644]
src/block/ext4.c [new file with mode: 0644]
src/block/mmc-smack-label [new file with mode: 0755]
src/block/mmc.c [new file with mode: 0644]
src/block/utils.c [new file with mode: 0644]
src/block/utils.h [new file with mode: 0644]
src/block/vfat.c [new file with mode: 0644]
src/core/dbus_main.c [new file with mode: 0644]
src/core/dbus_main.h [new file with mode: 0644]
src/core/main.c [new file with mode: 0644]
src/core/modules.c [new file with mode: 0644]
src/core/modules.h [new file with mode: 0644]
src/shared/common.c [new file with mode: 0644]
src/shared/common.h [new file with mode: 0644]
src/shared/config-parser.c [new file with mode: 0644]
src/shared/config-parser.h [new file with mode: 0644]
src/shared/dbus.c [new file with mode: 0644]
src/shared/dbus.h [new file with mode: 0644]
src/shared/dbus_macro.h [new file with mode: 0755]
src/shared/fd_handler.c [new file with mode: 0644]
src/shared/fd_handler.h [new file with mode: 0644]
src/shared/list.h [new file with mode: 0644]
src/shared/log.h [new file with mode: 0644]
src/shared/module-intf.h [new file with mode: 0644]
src/shared/udev.c [new file with mode: 0644]
src/shared/udev.h [new file with mode: 0644]
src/storage/CMakeLists.txt [new file with mode: 0644]
src/storage/storage.c [new file with mode: 0755]
src/storage/storage.conf [new file with mode: 0644]
systemd/storaged.service [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..753ad5b
--- /dev/null
@@ -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 (file)
index 0000000..8f17f50
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,204 @@
+Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.\r
+
+                                 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.
+\r
diff --git a/packaging/storaged.manifest b/packaging/storaged.manifest
new file mode 100644 (file)
index 0000000..97e8c31
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+       <request>
+               <domain name="_"/>
+       </request>
+</manifest>
diff --git a/packaging/storaged.spec b/packaging/storaged.spec
new file mode 100644 (file)
index 0000000..3555d1a
--- /dev/null
@@ -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 (file)
index 0000000..ecd3199
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="org.tizen.system.storage"/>
+        <allow send_destination="org.tizen.system.storage"/>
+    </policy>
+
+    <policy context="default">
+        <allow send_destination="org.tizen.system.storage"/>
+
+        <deny send_destination="org.tizen.system.storage"
+                send_interface="org.tizen.system.storage.storage"/>
+
+        <check send_destination="org.tizen.system.storage"
+                send_interface="org.tizen.system.storage.storage"
+                privilege="http://tizen.org/privilege/externalstorage"/>
+        <check send_destination="org.tizen.system.storage"
+                send_interface="org.tizen.system.storage.storage"
+                privilege="http://tizen.org/privilege/mediastorage"/>
+
+    </policy>
+</busconfig>
diff --git a/src/block/CMakeLists.txt b/src/block/CMakeLists.txt
new file mode 100644 (file)
index 0000000..301980f
--- /dev/null
@@ -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 (file)
index 0000000..25e7e5e
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/statvfs.h>
+#include <fnmatch.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/statfs.h>
+#include <stdbool.h>
+#include <pthread.h>
+#include <time.h>
+#include <assert.h>
+#include <vconf.h>
+#include <ctype.h>
+#include <tzplatform_config.h>
+#include <app2ext_interface.h>
+#include <libmount.h>
+#include <blkid/blkid.h>
+
+#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 (file)
index 0000000..8898468
--- /dev/null
@@ -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 (file)
index 0000000..5b36326
--- /dev/null
@@ -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 <stdbool.h>
+#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 (file)
index 0000000..3d1551a
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <limits.h>
+#include <vconf.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#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 (executable)
index 0000000..c206758
--- /dev/null
@@ -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 (file)
index 0000000..674cdcc
--- /dev/null
@@ -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 <stdlib.h>
+#include <vconf.h>
+#include <sys/types.h>
+#include <attr/xattr.h>
+
+#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 (file)
index 0000000..c12ece7
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <poll.h>
+#include <mntent.h>
+#include <system_info.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#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 (file)
index 0000000..8bf2a9b
--- /dev/null
@@ -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 <stdbool.h>
+#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 (file)
index 0000000..997b446
--- /dev/null
@@ -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 <stdio.h>
+#include <sys/mount.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#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 (file)
index 0000000..f7cc658
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <dbus/dbus.h>
+#include <stdbool.h>
+#include <limits.h>
+#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 (file)
index 0000000..ce2a14a
--- /dev/null
@@ -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 (file)
index 0000000..db63353
--- /dev/null
@@ -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 <stdio.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <argos.h>
+#include <systemd/sd-daemon.h>
+#include <glib.h>
+
+#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 (file)
index 0000000..6e65246
--- /dev/null
@@ -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 <stdio.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <dlog.h>
+#include <dirent.h>
+#include <glib.h>
+#include <stdlib.h>
+#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 (file)
index 0000000..6ee1d00
--- /dev/null
@@ -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 (file)
index 0000000..21a7ddc
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <poll.h>
+#include <mntent.h>
+#include <system_info.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#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 (file)
index 0000000..e0db5d4
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <error.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#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 (file)
index 0000000..af05f81
--- /dev/null
@@ -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 <stdio.h>
+#include <string.h>
+#include <errno.h>
+#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 (file)
index 0000000..4263727
--- /dev/null
@@ -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 (file)
index 0000000..15b30ed
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <dbus/dbus.h>
+#include <stdbool.h>
+#include <limits.h>
+#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, &param[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 (file)
index 0000000..10bc475
--- /dev/null
@@ -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 <glib.h>
+#include <dbus/dbus.h>
+#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 (executable)
index 0000000..093c02e
--- /dev/null
@@ -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 (file)
index 0000000..c95742e
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+#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 (file)
index 0000000..6bf5812
--- /dev/null
@@ -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 <glib.h>
+#include <gio/gio.h>
+#include <stdbool.h>
+
+/**
+ * @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 (file)
index 0000000..c355a0a
--- /dev/null
@@ -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 <glib.h>
+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 (file)
index 0000000..d9da05d
--- /dev/null
@@ -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 <dlog.h>
+
+#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 (file)
index 0000000..fe4a897
--- /dev/null
@@ -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 (file)
index 0000000..de5a927
--- /dev/null
@@ -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 <stdio.h>
+#include <errno.h>
+#include <assert.h>
+
+#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 (file)
index 0000000..7fabf15
--- /dev/null
@@ -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 <libudev.h>
+
+#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 (file)
index 0000000..e2aea8b
--- /dev/null
@@ -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 (executable)
index 0000000..2f4e3cf
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <limits.h>
+#include <vconf.h>
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include <sys/stat.h>
+#include <sys/shm.h>
+#include <time.h>
+#include <storage.h>
+#include <tzplatform_config.h>
+#include <glib.h>
+
+#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 (file)
index 0000000..331e786
--- /dev/null
@@ -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 (file)
index 0000000..a0b7562
--- /dev/null
@@ -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