--- /dev/null
+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()
--- /dev/null
+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
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
--- /dev/null
+#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
--- /dev/null
+<!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>
--- /dev/null
+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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+[Block]
+
+[MMC]
+Multimount=no # yes or no
+
+[SCSI]
+Multimount=yes # yes or no
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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);
+}
+*/
--- /dev/null
+#!/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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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);
+}
+*/
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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, ¶m[i]);
+ break;
+ case 'a':
+ ++ch;
+ switch (*ch) {
+ case 'y':
+ ++i;
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &arr);
+ byte = (struct dbus_byte *)param[i];
+ dbus_message_iter_append_fixed_array(&arr, DBUS_TYPE_BYTE, &(byte->data), byte->size);
+ dbus_message_iter_close_container(iter, &arr);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int broadcast_dbus_signal(const char *path, const char *iface,
+ const char *name, const char *sig, char *param[])
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ int ret;
+
+ conn = get_dbus_connection();
+ if (!conn) {
+ _E("Failed to get dbus connection");
+ return -ECOMM;
+ }
+
+ msg = dbus_message_new_signal(path, iface, name);
+ if (!msg) {
+ _E("fail to allocate new %s.%s signal", iface, name);
+ return -EPERM;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ ret = append_variant(&iter, sig, param);
+ if (ret < 0) {
+ _E("append_variant error(%d)", ret);
+ return -EPERM;
+ }
+
+ ret = dbus_connection_send(conn, msg, NULL);
+ dbus_message_unref(msg);
+ if (ret != TRUE) {
+ _E("dbus_connection_send error(%s:%s-%s)", path, iface, name);
+ return -ECOMM;
+ }
+
+ return 0;
+}
+
+static void release_signal_info(struct signal_info *info)
+{
+ if (info) {
+ if (info->free_func)
+ info->free_func(info->data);
+ free(info);
+ }
+}
+
+static struct signal_info *find_signal_info(const char *path,
+ const char *iface, const char *name, dbus_signal_received cb)
+{
+ GList *l;
+ size_t path_len, iface_len, name_len;
+ struct signal_info *info;
+
+ path_len = strlen(path) + 1;
+ iface_len = strlen(iface) + 1;
+ name_len = strlen(name) + 1;
+
+ for (l = signal_handler_list ; l && (info = l->data) ; l = g_list_next(l), info = NULL) {
+ if (strncmp(info->path, path, path_len))
+ continue;
+ if (strncmp(info->iface, iface, iface_len))
+ continue;
+ if (strncmp(info->name, name, name_len))
+ continue;
+ if (cb && info->cb != cb)
+ continue;
+ return info;
+ }
+
+ return NULL;
+}
+
+static void call_signal_callbacks(const char *path,
+ const char *iface, const char *name, DBusMessage *msg)
+{
+ GList *l;
+ size_t path_len, iface_len, name_len;
+ struct signal_info *info;
+
+ path_len = strlen(path) + 1;
+ iface_len = strlen(iface) + 1;
+ name_len = strlen(name) + 1;
+
+ for (l = signal_handler_list ; l && (info = l->data) ; l = g_list_next(l), info = NULL) {
+ _I("path(%s), info(%s), name(%s)", info->path, info->iface, info->name);
+ if (strncmp(info->path, path, path_len))
+ continue;
+ if (strncmp(info->iface, iface, iface_len))
+ continue;
+ if (strncmp(info->name, name, name_len))
+ continue;
+ if (info->cb)
+ info->cb(NULL, path, iface, name, msg, info->data);
+ }
+}
+
+static int make_match(const char *path, const char *iface,
+ const char *name, char *buf, size_t len)
+{
+ if (!path || !iface || ! name || !buf)
+ return -EINVAL;
+
+ snprintf(buf, len,
+ "type='signal',path=%s,interface=%s,member=%s",
+ path, iface, name);
+
+ return 0;
+}
+
+void unregister_dbus_signal_all(void)
+{
+ GList *l, *l_next;
+ struct signal_info *info;
+ char match[256];
+ int ret;
+ DBusConnection *conn;
+
+ for (l = signal_handler_list, l_next = g_list_next(l) ;
+ l && (info = l->data) ;
+ l = l_next, l_next = g_list_next(l), info = NULL) {
+ signal_handler_list = g_list_remove(signal_handler_list, info);
+
+ ret = make_match(info->path, info->iface, info->name,
+ match, sizeof(match));
+ if (ret == 0) {
+ conn = get_dbus_connection();
+ if (conn)
+ dbus_bus_remove_match(conn, match, NULL);
+ }
+
+ release_signal_info(info);
+ }
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+ const char *path, *iface, *name;
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ path = dbus_message_get_path(msg);
+ iface = dbus_message_get_interface(msg);
+ name = dbus_message_get_member(msg);
+
+ call_signal_callbacks(path, iface, name, msg);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int register_dbus_signal(const char *path,
+ const char *interface, const char *name,
+ dbus_signal_received cb, void *data,
+ destroy_notified free_func)
+{
+ struct signal_info *info;
+ DBusConnection *conn;
+ static int add_filter = 0;
+ char match[256];
+ int ret;
+
+ conn = get_dbus_connection();
+ if (!conn) {
+ _E("Failed to get dbus connection");
+ return -ECOMM;
+ }
+
+ info = find_signal_info(path, interface, name, cb);
+ if (info)
+ return -EEXIST;
+
+ info = calloc(1, sizeof(struct signal_info));
+ if (!info) {
+ _E("calloc failed");
+ return -ENOMEM;
+ }
+
+ info->path = path;
+ info->iface = interface;
+ info->name = name;
+ info->cb = cb;
+ info->free_func = free_func;
+ info->data = data;
+
+ if (add_filter == 0) {
+ add_filter = 1;
+ dbus_connection_add_filter(conn, signal_filter, NULL, NULL);
+ }
+
+ ret = make_match(path, interface, name, match, sizeof(match));
+ if (ret < 0) {
+ _E("Failed to make match (%d)", ret);
+ free(info);
+ return ret;
+ }
+ dbus_bus_add_match(conn, match, NULL);
+
+ signal_handler_list = g_list_append(signal_handler_list, info);
+
+ return 0;
+}
+
+int unregister_dbus_signal(const char *path,
+ const char *interface, const char *name,
+ dbus_signal_received cb)
+{
+ struct signal_info *info;
+ DBusConnection *conn;
+ char match[256];
+ int ret;
+
+ conn = get_dbus_connection();
+ if (!conn) {
+ _E("Failed to get dbus connection");
+ return -ECOMM;
+ }
+
+ info = find_signal_info(path, interface, name, cb);
+ if (!info)
+ return -ENOENT;
+
+ signal_handler_list = g_list_remove(signal_handler_list, info);
+ release_signal_info(info);
+
+ ret = make_match(path, interface, name, match, sizeof(match));
+ if (ret < 0) {
+ _E("Failed to make match (%d)", ret);
+ return ret;
+ }
+
+ dbus_bus_remove_match(conn, match, NULL);
+
+ return 0;
+}
+
+int call_dbus_method_sync(const char *dest, const char *path,
+ const char *interface, const char *method,
+ const char *sig, char *param[], int timeout,
+ DBusMessage **reply)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ DBusMessage *rep;
+ DBusError err;
+ int r;
+
+ if (!dest || !path || !interface || !method || !reply)
+ return -EINVAL;
+
+ conn = get_dbus_connection();
+ if (!conn) {
+ _E("Failed to get dbus connection");
+ return -ECOMM;
+ }
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ _E("dbus_message_new_method_call(%s:%s-%s)",
+ path, interface, method);
+ return -EPERM;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ r = append_variant(&iter, sig, param);
+ if (r < 0) {
+ _E("append_variant error(%d) %s %s:%s-%s",
+ r, dest, path, interface, method);
+ dbus_message_unref(msg);
+ return r;
+ }
+
+ dbus_error_init(&err);
+
+ rep = dbus_connection_send_with_reply_and_block(conn, msg, timeout, &err);
+ dbus_message_unref(msg);
+ if (dbus_error_is_set(&err)) {
+ _E("dbus_connection_send error(%s:%s) %s %s:%s-%s",
+ err.name, err.message, dest, path, interface, method);
+ dbus_error_free(&err);
+ return -EPERM;
+ }
+ if (!rep) {
+ _E("dbus_connection_send error(No reply) %s %s:%s-%s",
+ dest, path, interface, method);
+ return -EPERM;
+ }
+
+ *reply = rep;
+
+ return 0;
+}
+
+int call_dbus_method_sync_pairs(const char *dest,
+ const char *path, const char *interface, const char *method,
+ int num, va_list args)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ DBusMessageIter aiter, piter;
+ DBusMessage *reply;
+ DBusError err;
+ char *key, *value;
+ int ret, result;
+ int i;
+
+ if (!dest || !path || !interface || !method)
+ return -EINVAL;
+
+ conn = get_dbus_connection();
+ if (!conn) {
+ _E("Failed to get dbus connection");
+ return -ECOMM;
+ }
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ _E("dbus_message_new_method_call(%s:%s-%s)",
+ path, interface, method);
+ return -EPERM;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{ss}", &aiter);
+
+ for (i = 0 ; i < num ; i = i + 2) {
+ key = va_arg(args, char *);
+ value = va_arg(args, char *);
+ _I("key(%s), value(%s)", key, value);
+ dbus_message_iter_open_container(&aiter, DBUS_TYPE_DICT_ENTRY, NULL, &piter);
+ dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, &key);
+ dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, &value);
+ dbus_message_iter_close_container(&aiter, &piter);
+ }
+
+ dbus_message_iter_close_container(&iter, &aiter);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, DBUS_REPLY_TIMEOUT, &err);
+ if (!reply) {
+ _E("dbus_connection_send error(No reply) %s %s:%s-%s",
+ dest, path, interface, method);
+ }
+
+ if (dbus_error_is_set(&err)) {
+ _E("dbus_connection_send error(%s:%s) %s %s:%s-%s",
+ err.name, err.message, dest, path, interface, method);
+ dbus_error_free(&err);
+ reply = NULL;
+ }
+ dbus_message_unref(msg);
+
+ if (!reply)
+ return -EPERM;
+
+ ret = dbus_message_get_args(reply, &err, DBUS_TYPE_INT32, &result, DBUS_TYPE_INVALID);
+ dbus_message_unref(reply);
+ if (!ret) {
+ _E("no message : [%s:%s] %s %s:%s-%s",
+ err.name, err.message, dest, path, interface, method);
+ dbus_error_free(&err);
+ return -ENOMSG;
+ }
+
+ return result;
+}
+
+int call_dbus_method_async_pairs(const char *dest,
+ const char *path, const char *interface, const char *method,
+ int num, va_list args)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ DBusMessageIter aiter, piter;
+ char *key, *value;
+ int ret, i;
+
+ if (!dest || !path || !interface || !method)
+ return -EINVAL;
+
+ conn = get_dbus_connection();
+ if (!conn) {
+ _E("Failed to get dbus connection");
+ return -ECOMM;
+ }
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ _E("dbus_message_new_method_call(%s:%s-%s)",
+ path, interface, method);
+ return -EPERM;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{ss}", &aiter);
+
+ for (i = 0 ; i < num ; i = i + 2) {
+ key = va_arg(args, char *);
+ value = va_arg(args, char *);
+ _I("key(%s), value(%s)", key, value);
+ dbus_message_iter_open_container(&aiter, DBUS_TYPE_DICT_ENTRY, NULL, &piter);
+ dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, &key);
+ dbus_message_iter_append_basic(&piter, DBUS_TYPE_STRING, &value);
+ dbus_message_iter_close_container(&aiter, &piter);
+ }
+
+ dbus_message_iter_close_container(&iter, &aiter);
+
+ ret = dbus_connection_send(conn, msg, NULL);
+ dbus_message_unref(msg);
+ if (ret != TRUE) {
+ _E("dbus_connection_send error(%s %s:%s-%s)",
+ dest, path, interface, method);
+ return -ECOMM;
+ }
+
+ return 0;
+}
+
+static void cb_pending(DBusPendingCall *pending, void *user_data)
+{
+ DBusMessage *msg;
+ struct pending_call_data *data = user_data;
+ int ret;
+
+ ret = dbus_pending_call_get_completed(pending);
+ if (!ret) {
+ _I("dbus_pending_call_get_completed() fail");
+ dbus_pending_call_unref(pending);
+ return;
+ }
+
+ msg = dbus_pending_call_steal_reply(pending);
+ if (!msg) {
+ _E("message is NULL");
+ if (data->func)
+ data->func(data->data, NULL, -ECOMM);
+ return;
+ }
+
+ if (data->func)
+ data->func(data->data, msg, 0);
+
+ dbus_message_unref(msg);
+ dbus_pending_call_unref(pending);
+}
+
+int call_dbus_method_async(const char *dest, const char *path,
+ const char *interface, const char *method,
+ const char *sig, char *param[],
+ dbus_pending_cb cb, int timeout, void *data)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ DBusPendingCall *pending = NULL;
+ struct pending_call_data *pdata;
+ int ret;
+
+ conn = get_dbus_connection();
+ if (!conn) {
+ _E("Failed to get dbus connection");
+ return -ECOMM;
+ }
+
+ pdata = calloc(1, sizeof(struct pending_call_data));
+ if (!pdata) {
+ _E("malloc error : %s-%s", interface, method);
+ return -ENOMEM;
+ }
+
+ pdata->func = cb;
+ pdata->data = data;
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ _E("dbus_message_new_method_call(%s:%s-%s)",
+ path, interface, method);
+ return -EBADMSG;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ ret = append_variant(&iter, sig, param);
+ if (ret < 0) {
+ _E("append_variant error(%d)%s %s:%s-%s",
+ ret, dest, path, interface, method);
+ dbus_message_unref(msg);
+ return ret;
+ }
+
+ ret = dbus_connection_send_with_reply(conn, msg, &pending, timeout);
+ if (!ret) {
+ dbus_message_unref(msg);
+ _E("dbus_connection_send error(%s %s:%s-%s)",
+ dest, path, interface, method);
+ return -ECOMM;
+ }
+
+ dbus_message_unref(msg);
+
+ if (cb && pending) {
+ pdata = calloc(1, sizeof(struct pending_call_data));
+ if (!pdata) {
+ _E("malloc error : %s-%s", interface, method);
+ return -ENOMEM;
+ }
+
+ pdata->func = cb;
+ pdata->data = data;
+
+ ret = dbus_pending_call_set_notify(pending, cb_pending, pdata, free);
+ if (!ret) {
+ free(pdata);
+ dbus_pending_call_cancel(pending);
+ return -ECOMM;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+[LOWSTORAGE]
+#5%
+WARNING_LEVEL=5
+#0.1%
+CRITICAL_LEVEL=0.1
+#0.0%
+FULL_LEVEL=0
--- /dev/null
+[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