From: Wook Song Date: Thu, 17 Nov 2016 04:21:15 +0000 (+0900) Subject: pass: Fork from deviced X-Git-Tag: accepted/tizen/mobile/20170328.053617~118 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=65effcbd9c6db06974c9c827407cd221411398aa;p=platform%2Fcore%2Fsystem%2Fpass.git pass: Fork from deviced This patch adds a bunch of files forked from deviced. In order to make pass work as a separte systemd daemon, we start from the specific snapshot of deviced. Originates from: ssh://review.tizen.org:29418/platform/core/system/deviced (branch: accepted/tizen_3.0_mobile) commit: 41f1d655807502f88027873a4509a50180fb1196 Note: since some of files including CMakeLists.txt, packaging/deviced.spec are excluded for the technical reasons, you cannot build it as deviced. Signed-off-by: Wook Song --- diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..c06cb19 --- /dev/null +++ b/CREDITS @@ -0,0 +1,15 @@ +Thanks to the follow people for contributing to deviced by reporting +bugs, providing fixes, providing information, providing resources or +porting to new systems: + +The names are sorted alphabetically by last name. +Names taken from sources and from version control system. + +ChanWoo Choi +Byung-Soo Kim +Taeyoung Kim +Gi-Yeol Ok +Kyungmin Park +SeungHun Pi +Juho Son +Jiyoung Yun diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..247c97d --- /dev/null +++ b/LICENSE @@ -0,0 +1,203 @@ +Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.error.code b/README.error.code new file mode 100644 index 0000000..61cd9c6 --- /dev/null +++ b/README.error.code @@ -0,0 +1,9 @@ +DBus Error note + +Functions Defines Values +====================================================================== +dbus_bus_get EPERM 1 +dbus_message_get_args ENOMSG 42 +dbus_connection_send_with_reply_and_block ECOMM 70 +dbus_connection_send ECOMM 70 +dbus_message_new_method_call EBADMSG 74 diff --git a/src/apps/apps.c b/src/apps/apps.c new file mode 100755 index 0000000..3e2433b --- /dev/null +++ b/src/apps/apps.c @@ -0,0 +1,116 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "core/log.h" +#include "core/common.h" +#include "apps.h" +#include "core/edbus-handler.h" + +#define POPUP_METHOD "PopupLaunch" + +static const struct app_dbus_match { + const char *type; + const char *bus; + const char *path; + const char *iface; + const char *method; +} app_match[] = { + { APP_DEFAULT , POPUP_BUS_NAME, POPUP_PATH_SYSTEM , POPUP_INTERFACE_SYSTEM , POPUP_METHOD }, + { APP_POWERKEY, POPUP_BUS_NAME, POPUP_PATH_POWERKEY, POPUP_INTERFACE_POWERKEY, POPUP_METHOD }, +}; + +int launch_system_app(char *type, int num, ...) +{ + char *app_type; + va_list args; + int i, match, ret; + + if (type) + app_type = type; + else + app_type = APP_DEFAULT; + + match = -1; + for (i = 0 ; i < ARRAY_SIZE(app_match) ; i++) { + if (strncmp(app_type, app_match[i].type, strlen(app_type))) + continue; + match = i; + break; + } + if (match < 0) { + _E("Failed to find app matched (%s)", app_type); + return -EINVAL; + } + + va_start(args, num); + + ret = dbus_method_sync_pairs(app_match[match].bus, + app_match[match].path, + app_match[match].iface, + app_match[match].method, + num, args); + + va_end(args); + + return ret; +} + +int launch_message_post(char *type) +{ + char *param[1]; + + if (!type) + return -EINVAL; + + param[0] = type; + + return dbus_method_sync(POPUP_BUS_NAME, + POPUP_PATH_NOTI, + POPUP_INTERFACE_NOTI, + "MessagePostOn", + "s", param); +} + +int add_notification(char *type) +{ + if (!type) + return -EINVAL; + + return dbus_method_sync(POPUP_BUS_NAME, + POPUP_PATH_NOTI, + POPUP_INTERFACE_NOTI, + type, NULL, NULL); +} + +int remove_notification(char *type, int id) +{ + char *param[1]; + char id_str[16]; + + if (!type || id < 0) + return -EINVAL; + + snprintf(id_str, sizeof(id_str), "%d", id); + param[0] = id_str; + + return dbus_method_sync(POPUP_BUS_NAME, + POPUP_PATH_NOTI, + POPUP_INTERFACE_NOTI, + type, "i", param); +} diff --git a/src/apps/apps.h b/src/apps/apps.h new file mode 100755 index 0000000..2d7b35a --- /dev/null +++ b/src/apps/apps.h @@ -0,0 +1,35 @@ +/* + * deviced + * + * 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 __APPS_H__ +#define __APPS_H__ + + +#include "core/edbus-handler.h" +#include "core/common.h" + +#define APP_POWERKEY "powerkey" +#define APP_DEFAULT "system" +#define APP_KEY_TYPE "_SYSPOPUP_CONTENT_" + +int launch_system_app(char *type, int num, ...); +int launch_message_post(char *type); +int add_notification(char *type); +int remove_notification(char *type, int id); + +#endif /* __APPS_H__ */ diff --git a/src/battery/battery-time.c b/src/battery/battery-time.c new file mode 100644 index 0000000..125e658 --- /dev/null +++ b/src/battery/battery-time.c @@ -0,0 +1,407 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include + +#include "core/common.h" +#include "core/devices.h" +#include "core/log.h" +#include "core/edbus-handler.h" +#include "display/setting.h" +#include "power-supply.h" + +#define CHARGING_STATE(x) ((x) & CHRGR_FLAG) +#define FULL_CAPACITY (100) +#define BATTERY_FULL_THRESHOLD (98) +#define MAX_COUNT_UNCHARGING (10) +#define MAX_COUNT_CHARGING (10) +#define PRINT_ALL_BATT_NODE(x) /*print_all_batt_node(x)*/ +#define POLLING_TIME (30) /* seconds */ + +#define SIGNAL_TIMETOFULL "TimeToFull" +#define SIGNAL_TIMETOEMPTY "TimeToEmpty" + +enum state_b { + B_UNCHARGING = 0, + B_CHARGING = 1, + B_END = 2 +}; + +struct Batt_node { + time_t clock; + int capacity; + struct Batt_node *preview; + struct Batt_node *next; +}; + +enum state_a { + A_TIMETOEMPTY = 0, + A_TIMETOFULL = 1, + A_END = 2 +}; + +static Ecore_Timer *timeout_id; + +static struct Batt_node *batt_head[B_END]; +static struct Batt_node *batt_tail[B_END]; +static int MAX_VALUE_COUNT[B_END] = {MAX_COUNT_UNCHARGING, MAX_COUNT_CHARGING}; +static double avg_factor[B_END] = {-1.0, -1.0}; +static int old_capacity; +static int charging_state; +extern int system_wakeup_flag; +static int time_to_full = -1; +static int time_to_empty = -1; + +static int add_batt_node(enum state_b b_index, time_t clock, int capacity) +{ + struct Batt_node *node = NULL; + + PRINT_ALL_BATT_NODE(b_index); + + if (b_index < 0 || b_index >= B_END) + return -1; + + node = (struct Batt_node *) malloc(sizeof(struct Batt_node)); + if (node == NULL) { + _E("Not enough memory, add battery node fail!"); + return -1; + } + + node->clock = clock; + node->capacity = capacity; + + if (batt_head[b_index] == NULL && batt_tail[b_index] == NULL) { + batt_head[b_index] = batt_tail[b_index] = node; + node->preview = NULL; + node->next = NULL; + } else { + node->next = batt_head[b_index]; + node->preview = NULL; + batt_head[b_index]->preview = node; + batt_head[b_index] = node; + } + PRINT_ALL_BATT_NODE(b_index); + return 0; +} + +static int reap_batt_node(enum state_b b_index, int max_count) +{ + struct Batt_node *node = NULL; + struct Batt_node *tmp = NULL; + int cnt = 0; + + PRINT_ALL_BATT_NODE(b_index); + + if (b_index < 0 || b_index >= B_END) + return -1; + + if (max_count <= 0) + return -1; + + node = batt_head[b_index]; + + while (node != NULL) { + if (cnt >= max_count) break; + cnt++; + node = node->next; + } + + if (node != NULL && node != batt_tail[b_index]) { + batt_tail[b_index] = node; + node = node->next; + batt_tail[b_index]->next = NULL; + while (node != NULL) { + tmp = node; + node = node->next; + free(tmp); + } + } + PRINT_ALL_BATT_NODE(b_index); + return 0; +} + +static int del_all_batt_node(enum state_b b_index) +{ + struct Batt_node *node = NULL; + + PRINT_ALL_BATT_NODE(b_index); + + if (b_index < 0 || b_index >= B_END) + return -1; + if (batt_head[b_index] == NULL) + return 0; + + while (batt_head[b_index] != NULL) { + node = batt_head[b_index]; + batt_head[b_index] = batt_head[b_index]->next; + free(node); + } + batt_tail[b_index] = NULL; + PRINT_ALL_BATT_NODE(b_index); + return 0; +} + +static float update_factor(enum state_b b_index) +{ + struct Batt_node *node = NULL; + double factor = 0.0; + double total_factor = 0.0; + int cnt = 0; + double timediff = 0.0; + double capadiff = 0.0; + + if (b_index < 0 || b_index >= B_END) + return 0; + + if (batt_head[b_index] == NULL || batt_head[b_index]->next == NULL) + return avg_factor[b_index]; + + node = batt_head[b_index]; + while (1) { + timediff = difftime(node->clock, node->next->clock); + capadiff = node->capacity - node->next->capacity; + if (capadiff < 0) + capadiff *= (-1); + if (capadiff != 0) + factor = timediff / capadiff; + total_factor += factor; + + node = node->next; + cnt++; + + /*_I("[%d] timediff(%lf) / capadiff(%lf) = factor(%lf)", + cnt, timediff, capadiff, factor);*/ + factor = 0.0; + + if (node == NULL || node->next == NULL) + break; + if (cnt >= MAX_VALUE_COUNT[b_index]) { + reap_batt_node(b_index, MAX_VALUE_COUNT[b_index]); + break; + } + } + total_factor /= (float)cnt; + + return total_factor; +} + +static void broadcast_battery_time(char *signal, int time) +{ + char *param[1]; + char buf[10]; + + if (!signal) + return; + + snprintf(buf, 10, "%d", time); + param[0] = buf; + + broadcast_edbus_signal(DEVICED_PATH_BATTERY, DEVICED_INTERFACE_BATTERY, + signal, "i", param); +} + +static void update_time(enum state_a a_index, int seconds) +{ + if (a_index < 0 || a_index >= A_END) + return; + + if (seconds <= 0) + return; + + switch (a_index) { + case A_TIMETOFULL: + broadcast_battery_time(SIGNAL_TIMETOFULL, seconds); + time_to_full = seconds; + break; + case A_TIMETOEMPTY: + broadcast_battery_time(SIGNAL_TIMETOEMPTY, seconds); + time_to_empty = seconds; + break; + default: + break; + } +} + +static int battinfo_calculation(void) +{ + time_t clock; + int capacity = 0; + int estimated_time = 0; + int tmp = 0; + + capacity = battery.capacity; + + if (capacity <= 0) + return -1; + if (capacity == old_capacity) + return 0; + + old_capacity = capacity; + + if (get_charging_status(&tmp) == 0) + charging_state = (tmp > 0 ? EINA_TRUE : EINA_FALSE); + + clock = time(NULL); + if (charging_state == EINA_TRUE) { + del_all_batt_node(B_UNCHARGING); + if ((capacity * 100 / FULL_CAPACITY) + >= BATTERY_FULL_THRESHOLD) { + if (battery.charge_full == CHARGING_FULL) { + del_all_batt_node(B_CHARGING); + _I("battery fully charged!"); + update_time(A_TIMETOFULL, 0); + return 0; + } + } + if (batt_head[B_CHARGING] == NULL) { + add_batt_node(B_CHARGING, clock, capacity); + } else { + add_batt_node(B_CHARGING, clock, capacity); + avg_factor[B_CHARGING] = update_factor(B_CHARGING); + } + estimated_time = (float)(FULL_CAPACITY - capacity) * + avg_factor[B_CHARGING]; + update_time(A_TIMETOFULL, estimated_time); + } else { + del_all_batt_node(B_CHARGING); + if (system_wakeup_flag == true) { + del_all_batt_node(B_UNCHARGING); + system_wakeup_flag = false; + } + if (batt_head[B_UNCHARGING] == NULL) { + add_batt_node(B_UNCHARGING, clock, capacity); + } else { + add_batt_node(B_UNCHARGING, clock, capacity); + avg_factor[B_UNCHARGING] = update_factor(B_UNCHARGING); + } + estimated_time = (float)capacity * avg_factor[B_UNCHARGING]; + update_time(A_TIMETOEMPTY, estimated_time); + } + return 0; +} + +static Eina_Bool battinfo_cb(void *data) +{ + battinfo_calculation(); + return ECORE_CALLBACK_RENEW; +} + +static int start_battinfo_gathering(int timeout) +{ + _I("Start battery gathering!"); + + if (timeout <= 0) { + _E("invalid timeout value [%d]!", timeout); + return -1; + } + + old_capacity = 0; + battinfo_calculation(); + + if (timeout > 0) { + /* Using g_timer for gathering battery info */ + timeout_id = ecore_timer_add(timeout, + (Ecore_Task_Cb)battinfo_cb, NULL); + } + + return 0; +} + +static void end_battinfo_gathering(void) +{ + _I("End battery gathering!"); + + if (timeout_id) { + ecore_timer_del(timeout_id); + timeout_id = NULL; + } + + del_all_batt_node(B_UNCHARGING); + del_all_batt_node(B_CHARGING); +} + +static DBusMessage *dbus_get_timetofull(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int val; + + val = time_to_full; + + _D("get time %d", val); + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &val); + return reply; +} + +static DBusMessage *dbus_get_timetoempty(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int val; + + val = time_to_empty; + + _D("get time %d", val); + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &val); + return reply; +} + +static const struct edbus_method edbus_methods[] = { + { SIGNAL_TIMETOFULL, NULL, "i", dbus_get_timetofull }, + { SIGNAL_TIMETOEMPTY, NULL, "i", dbus_get_timetoempty }, + /* Add methods here */ +}; + +static void battery_init(void *data) +{ + int ret; + + /* init dbus interface */ + ret = register_edbus_interface_and_method(DEVICED_PATH_BATTERY, + DEVICED_INTERFACE_BATTERY, + edbus_methods, ARRAY_SIZE(edbus_methods)); + if (ret < 0) + _E("fail to init edbus interface and method(%d)", ret); + + start_battinfo_gathering(POLLING_TIME); +} + +static void battery_exit(void *data) +{ + end_battinfo_gathering(); +} + +static const struct device_ops battery_time_device_ops = { + .name = "battery-time", + .init = battery_init, + .exit = battery_exit, +}; + +DEVICE_OPS_REGISTER(&battery_time_device_ops) diff --git a/src/battery/battery.conf b/src/battery/battery.conf new file mode 100644 index 0000000..c882320 --- /dev/null +++ b/src/battery/battery.conf @@ -0,0 +1,9 @@ +[LOWBAT] +#low battery level +Normal=100 +Warning=15 +Critical=5 +PowerOff=1 +RealOff=0 +WarningMethod=warning +CriticalMethod=critical diff --git a/src/battery/battery.h b/src/battery/battery.h new file mode 100644 index 0000000..f1496bb --- /dev/null +++ b/src/battery/battery.h @@ -0,0 +1,44 @@ +/* + * deviced + * + * 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. + */ + + +#ifndef __BATTERY_H__ +#define __BATTERY_H__ + +#define BATTERY_LEVEL_CHECK_FULL 95 +#define BATTERY_LEVEL_CHECK_HIGH 15 +#define BATTERY_LEVEL_CHECK_LOW 5 +#define BATTERY_LEVEL_CHECK_CRITICAL 1 + +#define LOWBAT_OPT_WARNING 1 +#define LOWBAT_OPT_POWEROFF 2 +#define LOWBAT_OPT_CHARGEERR 3 +#define LOWBAT_OPT_CHECK 4 + +#define METHOD_NAME_MAX 32 +struct battery_config_info { + int normal; + int warning; + int critical; + int poweroff; + int realoff; +}; + +int battery_charge_err_low_act(void *data); +int battery_charge_err_high_act(void *data); +#endif /* __BATTERY_H__ */ diff --git a/src/battery/config.c b/src/battery/config.c new file mode 100644 index 0000000..4dd42c1 --- /dev/null +++ b/src/battery/config.c @@ -0,0 +1,73 @@ +/* + * deviced + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "core/log.h" +#include "core/common.h" +#include "core/config-parser.h" +#include "battery.h" +#include "config.h" + +#define BAT_CONF_FILE "/etc/deviced/battery.conf" + +static int load_config(struct parse_result *result, void *user_data) +{ + struct battery_config_info *info = user_data; + char *name; + char *value; + + _D("%s,%s,%s", result->section, result->name, result->value); + + if (!info) + return -EINVAL; + + if (!MATCH(result->section, "LOWBAT")) + return -EINVAL; + + name = result->name; + value = result->value; + if (MATCH(name, "Normal")) + info->normal = atoi(value); + else if (MATCH(name, "Warning")) + info->warning = atoi(value); + else if (MATCH(name, "Critical")) + info->critical = atoi(value); + else if (MATCH(name, "PowerOff")) + info->poweroff = atoi(value); + else if (MATCH(name, "RealOff")) + info->realoff = atoi(value); + + return 0; +} + +void battery_config_load(struct battery_config_info *info) +{ + int ret; + + ret = config_parse(BAT_CONF_FILE, load_config, info); + if (ret < 0) + _E("Failed to load %s, %d Use default value!", BAT_CONF_FILE, ret); +} diff --git a/src/battery/config.h b/src/battery/config.h new file mode 100644 index 0000000..77ca856 --- /dev/null +++ b/src/battery/config.h @@ -0,0 +1,31 @@ +/* + * deviced + * + * Copyright (c) 2014 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 __BATTERY_CONFIG_H__ +#define __BATTERY_CONFIG_H__ + +#define BATTERY_FULL 100 +#define BATTERY_NORMAL BATTERY_FULL +#define BATTERY_WARNING 15 +#define BATTERY_CRITICAL 5 +#define BATTERY_POWEROFF 1 +#define BATTERY_REALOFF 0 + +void battery_config_load(struct battery_config_info *info); +#endif /* __BATTERY_CONFIG_H__ */ diff --git a/src/battery/lowbat-handler.c b/src/battery/lowbat-handler.c new file mode 100644 index 0000000..cad1bf7 --- /dev/null +++ b/src/battery/lowbat-handler.c @@ -0,0 +1,641 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "battery.h" +#include "config.h" +#include "core/log.h" +#include "core/launch.h" +#include "core/devices.h" +#include "core/device-notifier.h" +#include "core/common.h" +#include "core/list.h" +#include "core/udev.h" +#include "device-node.h" +#include "display/setting.h" +#include "display/poll.h" +#include "core/edbus-handler.h" +#include "power/power-handler.h" +#include "apps/apps.h" +#include "power-supply.h" + +#define CHARGE_POWERSAVE_FREQ_ACT "charge_powersave_freq_act" +#define CHARGE_RELEASE_FREQ_ACT "charge_release_freq_act" + +#define POWER_OFF_CHECK_TIMER (30) + +#define BATTERY_CHARGING 65535 +#define BATTERY_UNKNOWN -1 + +#define WARNING_LOW_BAT_ACT "warning_low_bat_act" +#define CRITICAL_LOW_BAT_ACT "critical_low_bat_act" +#define POWER_OFF_BAT_ACT "power_off_bat_act" +#define CHARGE_BAT_ACT "charge_bat_act" +#define CHARGE_CHECK_ACT "charge_check_act" +#define CHARGE_ERROR_ACT "charge_error_act" +#define CHARGE_ERROR_LOW_ACT "charge_error_low_act" +#define CHARGE_ERROR_HIGH_ACT "charge_error_high_act" +#define CHARGE_ERROR_OVP_ACT "charge_error_ovp_act" +#define WAITING_INTERVAL 10 + +#define LOWBAT_CPU_CTRL_ID "id6" +#define LOWBAT_CPU_FREQ_RATE (0.7) + +#define POWER_OFF_UNLOCK 0 +#define POWER_OFF_LOCK 1 + +struct lowbat_process_entry { + int old; + int now; + int (*func) (void *data); +}; + +static int cur_bat_state = BATTERY_UNKNOWN; +static int cur_bat_capacity = -1; + +static struct battery_config_info battery_info = { + .normal = BATTERY_NORMAL, + .warning = BATTERY_WARNING, + .critical = BATTERY_CRITICAL, + .poweroff = BATTERY_POWEROFF, + .realoff = BATTERY_REALOFF, +}; + +static dd_list *lpe; +static int scenario_count; +static int power_off_lock = POWER_OFF_UNLOCK; +static Ecore_Timer *power_off_timer; + +static int lowbat_popup(char *option); + +static int lowbat_initialized(void *data) +{ + static int status; + + if (!data) + return status; + + status = *(int *)data; + return status; +} + +static int lowbat_scenario(int old, int now, void *data) +{ + dd_list *n; + struct lowbat_process_entry *scenario; + int found = 0; + + if (old == now && battery.charge_now) + return found; + DD_LIST_FOREACH(lpe, n, scenario) { + if (old != scenario->old || now != scenario->now) + continue; + if (!scenario->func) + continue; + scenario->func(data); + found = 1; + break; + } + return found; +} + +static int lowbat_add_scenario(int old, int now, int (*func)(void *data)) +{ + struct lowbat_process_entry *scenario; + + _I("%d %d, %x", old, now, func); + + if (!func) { + _E("invalid func address!"); + return -EINVAL; + } + + scenario = malloc(sizeof(struct lowbat_process_entry)); + if (!scenario) { + _E("Fail to malloc for notifier!"); + return -ENOMEM; + } + + scenario->old = old; + scenario->now = now; + scenario->func = func; + + DD_LIST_APPEND(lpe, scenario); + scenario_count++; + return 0; +} + +static void print_lowbat_state(unsigned int bat_percent) +{ +#if 0 + int i; + for (i = 0; i < BAT_MON_SAMPLES; i++) + _D("\t%d", recent_bat_percent[i]); +#endif +} + +static int power_execute(void *data) +{ + static const struct device_ops *ops; + + FIND_DEVICE_INT(ops, POWER_OPS_NAME); + + return ops->execute(data); +} + +static int booting_done(void *data) +{ + static int done; + static int popup; + + if (data == NULL) { + if (!done) + popup = 1; + goto out; + } + done = *(int *)data; + if (!done) + goto out; + _I("booting done"); + if (popup) { + popup = 0; + lowbat_popup(NULL); + } +out: + return done; +} + +static void power_off_pm_lock(void) +{ + if (power_off_lock == POWER_OFF_UNLOCK) { + pm_lock_internal(INTERNAL_LOCK_LOWBAT, LCD_OFF, STAY_CUR_STATE, 0); + power_off_lock = POWER_OFF_LOCK; + } +} + +static void power_off_pm_unlock(void) +{ + if (power_off_lock == POWER_OFF_LOCK) { + pm_unlock_internal(INTERNAL_LOCK_LOWBAT, LCD_OFF, PM_SLEEP_MARGIN); + power_off_lock = POWER_OFF_UNLOCK; + } +} + +static Eina_Bool power_off_cb(void *data) +{ + power_off_pm_unlock(); + power_execute(POWER_POWEROFF); + return EINA_FALSE; +} + +void power_off_timer_start(void) +{ + if (power_off_timer) + return; + _I("power off after %d", POWER_OFF_CHECK_TIMER); + power_off_pm_lock(); + power_off_timer = ecore_timer_add(POWER_OFF_CHECK_TIMER, + power_off_cb, NULL); + if (power_off_timer == NULL) + _E("fail to add battery init timer during booting"); +} + +void power_off_timer_stop(void) +{ + if (!power_off_timer) + return; + _I("cancel power off"); + power_off_pm_unlock(); + ecore_timer_del(power_off_timer); + power_off_timer = NULL; +} + +static int lowbat_popup(char *option) +{ + static int launched_poweroff; + static int lowbat_popup_option; + int ret; + int r_disturb, s_disturb, r_block, s_block; + static char *value; + + if (!option) { + if (!value) + return -1; + else + goto direct_launch; + } + + if (strcmp(option, POWER_OFF_BAT_ACT)) + launched_poweroff = 0; + + if (!strcmp(option, CRITICAL_LOW_BAT_ACT)) { + value = "lowbattery_critical"; + lowbat_popup_option = LOWBAT_OPT_CHECK; + } else if (!strcmp(option, WARNING_LOW_BAT_ACT)) { + value = "lowbattery_warning"; + lowbat_popup_option = LOWBAT_OPT_WARNING; + } else if (!strcmp(option, POWER_OFF_BAT_ACT)) { + value = "poweroff"; + lowbat_popup_option = LOWBAT_OPT_POWEROFF; + } else if (!strcmp(option, CHARGE_ERROR_ACT)) { + value = "chargeerr"; + lowbat_popup_option = LOWBAT_OPT_CHARGEERR; + } else if (!strcmp(option, CHARGE_ERROR_LOW_ACT)) { + value = "chargeerrlow"; + lowbat_popup_option = LOWBAT_OPT_CHARGEERR; + } else if (!strcmp(option, CHARGE_ERROR_HIGH_ACT)) { + value = "chargeerrhigh"; + lowbat_popup_option = LOWBAT_OPT_CHARGEERR; + } else if (!strcmp(option, CHARGE_ERROR_OVP_ACT)) { + value = "chargeerrovp"; + lowbat_popup_option = LOWBAT_OPT_CHARGEERR; + } else if (!strcmp(option, CHARGE_CHECK_ACT)) { + launched_poweroff = 0; + return 0; + } else + return -1; + +direct_launch: + _D("%s", value); + if (booting_done(NULL)) { + + if (launched_poweroff == 1) { + _I("will be foreced power off"); + power_execute(POWER_POWEROFF); + return 0; + } + + if (lowbat_popup_option == LOWBAT_OPT_POWEROFF) + launched_poweroff = 1; + + ret = launch_system_app(APP_DEFAULT, + 2, APP_KEY_TYPE, "remove_battery_popups"); + if (ret < 0) + _E("Failed to close all of battery popups"); + + r_disturb = vconf_get_int("memory/shealth/sleep/do_not_disturb", &s_disturb); + r_block = vconf_get_bool("db/setting/blockmode_wearable", &s_block); + if ((r_disturb != 0 && r_block != 0) || + (s_disturb == 0 && s_block == 0) || + lowbat_popup_option == LOWBAT_OPT_CHARGEERR) + pm_change_internal(getpid(), LCD_NORMAL); + else + _I("block LCD"); + + return launch_system_app(APP_DEFAULT, + 2, APP_KEY_TYPE, value); + } else { + _D("boot-animation running yet"); + } + + return 0; +} + +static int battery_check_act(void *data) +{ + power_off_timer_stop(); + lowbat_popup(CHARGE_CHECK_ACT); + return 0; +} + +static int battery_warning_low_act(void *data) +{ + power_off_timer_stop(); + lowbat_popup(WARNING_LOW_BAT_ACT); + return 0; +} + +static int battery_critical_low_act(void *data) +{ + power_off_timer_stop(); + lowbat_popup(CRITICAL_LOW_BAT_ACT); + return 0; +} + +int battery_power_off_act(void *data) +{ + lowbat_popup(CRITICAL_LOW_BAT_ACT); + power_off_timer_start(); + return 0; +} + +int battery_charge_err_act(void *data) +{ + power_off_timer_stop(); + lowbat_popup(CHARGE_ERROR_ACT); + return 0; +} + +int battery_charge_err_low_act(void *data) +{ + power_off_timer_stop(); + lowbat_popup(CHARGE_ERROR_LOW_ACT); + return 0; +} + +int battery_charge_err_high_act(void *data) +{ + power_off_timer_stop(); + lowbat_popup(CHARGE_ERROR_HIGH_ACT); + return 0; +} + +int battery_charge_err_ovp_act(void *data) +{ + power_off_timer_stop(); + lowbat_popup(CHARGE_ERROR_OVP_ACT); + return 0; +} + +static void lowbat_scenario_init(void) +{ + lowbat_add_scenario(battery_info.normal, battery_info.warning, battery_warning_low_act); + lowbat_add_scenario(battery_info.normal, battery_info.critical, battery_critical_low_act); + lowbat_add_scenario(battery_info.normal, battery_info.poweroff, battery_critical_low_act); + lowbat_add_scenario(battery_info.normal, battery_info.realoff, battery_power_off_act); + lowbat_add_scenario(battery_info.warning, battery_info.warning, battery_warning_low_act); + lowbat_add_scenario(battery_info.warning, battery_info.critical, battery_critical_low_act); + lowbat_add_scenario(battery_info.warning, battery_info.poweroff, battery_critical_low_act); + lowbat_add_scenario(battery_info.warning, battery_info.realoff, battery_power_off_act); + lowbat_add_scenario(battery_info.critical, battery_info.critical, battery_critical_low_act); + lowbat_add_scenario(battery_info.critical, battery_info.realoff, battery_power_off_act); + lowbat_add_scenario(battery_info.poweroff, battery_info.poweroff, battery_critical_low_act); + lowbat_add_scenario(battery_info.poweroff, battery_info.realoff, battery_power_off_act); + lowbat_add_scenario(battery_info.realoff, battery_info.realoff, battery_power_off_act); + lowbat_add_scenario(battery_info.realoff, battery_info.normal, battery_check_act); + lowbat_add_scenario(battery_info.realoff, battery_info.warning, battery_check_act); + lowbat_add_scenario(battery_info.realoff, battery_info.critical, battery_check_act); + lowbat_add_scenario(battery_info.realoff, battery_info.poweroff, battery_check_act); + lowbat_add_scenario(battery_info.realoff, battery_info.realoff, battery_power_off_act); +} + +static void battery_level_send_system_event(int bat_percent) +{ + bundle *b; + const char *str; + static const char *prev; + + if (bat_percent > BATTERY_LEVEL_CHECK_FULL) + str = EVT_VAL_BATTERY_LEVEL_FULL; + else if (bat_percent > BATTERY_LEVEL_CHECK_HIGH) + str = EVT_VAL_BATTERY_LEVEL_HIGH; + else if (bat_percent > BATTERY_LEVEL_CHECK_LOW) + str = EVT_VAL_BATTERY_LEVEL_LOW; + else if (bat_percent > BATTERY_LEVEL_CHECK_CRITICAL) + str = EVT_VAL_BATTERY_LEVEL_CRITICAL; + else + str = EVT_VAL_BATTERY_LEVEL_EMPTY; + + if (prev == str) + return; + + prev = str; + + _D("system_event(%s)", str); + + b = bundle_create(); + bundle_add_str(b, EVT_KEY_BATTERY_LEVEL_STATUS, str); + eventsystem_send_system_event(SYS_EVENT_BATTERY_LEVEL_STATUS, b); + bundle_free(b); +} + +static void change_lowbat_level(int bat_percent) +{ + int prev, now; + + if (cur_bat_capacity == bat_percent) + return; + + if (vconf_get_int(VCONFKEY_SYSMAN_BATTERY_LEVEL_STATUS, &prev) < 0) { + _E("vconf_get_int() failed"); + return; + } + + if (bat_percent > BATTERY_LEVEL_CHECK_FULL) + now = VCONFKEY_SYSMAN_BAT_LEVEL_FULL; + else if (bat_percent > BATTERY_LEVEL_CHECK_HIGH) + now = VCONFKEY_SYSMAN_BAT_LEVEL_HIGH; + else if (bat_percent > BATTERY_LEVEL_CHECK_LOW) + now = VCONFKEY_SYSMAN_BAT_LEVEL_LOW; + else if (bat_percent > BATTERY_LEVEL_CHECK_CRITICAL) + now = VCONFKEY_SYSMAN_BAT_LEVEL_CRITICAL; + else + now = VCONFKEY_SYSMAN_BAT_LEVEL_EMPTY; + + if (prev != now) + vconf_set_int(VCONFKEY_SYSMAN_BATTERY_LEVEL_STATUS, now); +} + +static int lowbat_process(int bat_percent, void *ad) +{ + static int online; + int new_bat_capacity; + int new_bat_state; + int vconf_state = -1; + int ret = 0; + int status = -1; + bool low_bat = false; + int result = 0; + int lock = -1; + + new_bat_capacity = bat_percent; + if (new_bat_capacity < 0) + return -EINVAL; + change_lowbat_level(new_bat_capacity); + battery_level_send_system_event(new_bat_capacity); + + if (new_bat_capacity != cur_bat_capacity) { + _D("[BAT_MON] cur = %d new = %d", cur_bat_capacity, new_bat_capacity); + if (vconf_set_int(VCONFKEY_SYSMAN_BATTERY_CAPACITY, new_bat_capacity) == 0) + cur_bat_capacity = new_bat_capacity; + power_supply_broadcast(CHARGE_CAPACITY_SIGNAL, new_bat_capacity); + } + + if (vconf_get_int(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, &vconf_state) < 0) { + _E("vconf_get_int() failed"); + result = -EIO; + goto exit; + } + + if (new_bat_capacity <= battery_info.realoff) { + if (battery.charge_now) { + new_bat_state = battery_info.poweroff; + if (vconf_state != VCONFKEY_SYSMAN_BAT_POWER_OFF) + status = VCONFKEY_SYSMAN_BAT_POWER_OFF; + } else { + new_bat_state = battery_info.realoff; + if (vconf_state != VCONFKEY_SYSMAN_BAT_REAL_POWER_OFF) + status = VCONFKEY_SYSMAN_BAT_REAL_POWER_OFF; + } + } else if (new_bat_capacity <= battery_info.poweroff) { + new_bat_state = battery_info.poweroff; + if (vconf_state != VCONFKEY_SYSMAN_BAT_POWER_OFF) + status = VCONFKEY_SYSMAN_BAT_POWER_OFF; + } else if (new_bat_capacity <= battery_info.critical) { + new_bat_state = battery_info.critical; + if (vconf_state != VCONFKEY_SYSMAN_BAT_CRITICAL_LOW) + status = VCONFKEY_SYSMAN_BAT_CRITICAL_LOW; + } else if (new_bat_capacity <= battery_info.warning) { + new_bat_state = battery_info.warning; + if (vconf_state != VCONFKEY_SYSMAN_BAT_WARNING_LOW) + status = VCONFKEY_SYSMAN_BAT_WARNING_LOW; + } else { + new_bat_state = battery_info.normal; + if (new_bat_capacity == BATTERY_FULL) { + if (battery.charge_full) { + if (vconf_state != VCONFKEY_SYSMAN_BAT_FULL) + status = VCONFKEY_SYSMAN_BAT_FULL; + } else { + if (vconf_state != VCONFKEY_SYSMAN_BAT_NORMAL) + status = VCONFKEY_SYSMAN_BAT_NORMAL; + } + } else { + if (vconf_state != VCONFKEY_SYSMAN_BAT_NORMAL) + status = VCONFKEY_SYSMAN_BAT_NORMAL; + } + } + + if (status != -1) { + lock = pm_lock_internal(INTERNAL_LOCK_BATTERY, LCD_OFF, STAY_CUR_STATE, 0); + ret = vconf_set_int(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, status); + power_supply_broadcast(CHARGE_LEVEL_SIGNAL, status); + if (update_pm_setting) + update_pm_setting(SETTING_LOW_BATT, status); + } + + if (ret < 0) { + result = -EIO; + goto exit; + } + + if (new_bat_capacity <= battery_info.warning) + low_bat = true; + + device_notify(DEVICE_NOTIFIER_LOWBAT, (void *)low_bat); + + if (battery.online == POWER_SUPPLY_TYPE_UNKNOWN) + goto exit; + if (cur_bat_state == new_bat_state && + online == battery.online) + goto exit; + online = battery.online; + if (cur_bat_state == BATTERY_UNKNOWN) + cur_bat_state = battery_info.normal; + result = lowbat_scenario(cur_bat_state, new_bat_state, NULL); + if (result) + _I("cur %d, new %d(capacity %d)", + cur_bat_state, new_bat_state, bat_percent); + cur_bat_state = new_bat_state; +exit: + if (lock == 0) + pm_unlock_internal(INTERNAL_LOCK_BATTERY, LCD_OFF, PM_SLEEP_MARGIN); + + return result; +} + +static int check_lowbat_percent(int *pct) +{ + int bat_percent; + + bat_percent = battery.capacity; + if (bat_percent < 0) { + _E("[BATMON] Cannot read battery gage. stop read fuel gage"); + return -ENODEV; + } + if (bat_percent > 100) + bat_percent = 100; + change_lowbat_level(bat_percent); + battery_level_send_system_event(bat_percent); + *pct = bat_percent; + return 0; +} + +static void lowbat_monitor(void *data) +{ + int bat_percent, r; + + r = lowbat_initialized(NULL); + if (!r) + return; + + if (data == NULL) { + r = check_lowbat_percent(&bat_percent); + if (r < 0) + return; + } else + bat_percent = *(int *)data; + print_lowbat_state(bat_percent); + lowbat_process(bat_percent, NULL); +} + +static int lowbat_monitor_init(void *data) +{ + int status = 1; + + lowbat_initialized(&status); + + /* it's called just this once. */ + unregister_notifier(DEVICE_NOTIFIER_POWER_SUPPLY, lowbat_monitor_init); + + /* load battery configuration file */ + battery_config_load(&battery_info); + _I("battery conf %d %d %d %d %d", battery_info.normal, battery_info.warning, + battery_info.critical, battery_info.poweroff, battery_info.realoff); + + lowbat_scenario_init(); + check_lowbat_percent(&battery.capacity); + lowbat_process(battery.capacity, NULL); + return 0; +} + +static void lowbat_init(void *data) +{ + register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done); + register_notifier(DEVICE_NOTIFIER_POWER_SUPPLY, lowbat_monitor_init); +} + +static void lowbat_exit(void *data) +{ + int status = 0; + + lowbat_initialized(&status); + unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done); +} + +static int lowbat_execute(void *data) +{ + lowbat_monitor(data); + return 0; +} + +static const struct device_ops lowbat_device_ops = { + .name = "lowbat", + .init = lowbat_init, + .execute = lowbat_execute, + .exit = lowbat_exit, +}; + +DEVICE_OPS_REGISTER(&lowbat_device_ops) diff --git a/src/battery/power-supply.c b/src/battery/power-supply.c new file mode 100644 index 0000000..bd98f9a --- /dev/null +++ b/src/battery/power-supply.c @@ -0,0 +1,1292 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/devices.h" +#include "core/device-notifier.h" +#include "core/udev.h" +#include "core/log.h" +#include "core/config-parser.h" +#include "display/poll.h" +#include "display/setting.h" +#include "apps/apps.h" +#include "power-supply.h" +#include "battery.h" + +#define BATTERY_NAME "battery" +#define CHARGEFULL_NAME "Full" +#define CHARGENOW_NAME "Charging" +#define DISCHARGE_NAME "Discharging" +#define NOTCHARGE_NAME "Not charging" +#define OVERHEAT_NAME "Overheat" +#define TEMPCOLD_NAME "Cold" +#define OVERVOLT_NAME "Over voltage" + +#define BUFF_MAX 255 + +#define SIGNAL_CHARGEERR_RESPONSE "ChargeErrResponse" +#define SIGNAL_TEMP_GOOD "TempGood" + +#define ABNORMAL_CHECK_TIMER_INTERVAL 60 + +#define METHOD_FULL_NOTI_ON "BatteryFullNotiOn" +#define METHOD_FULL_NOTI_OFF "BatteryFullNotiOff" +#define METHOD_CHARGE_NOTI_ON "BatteryChargeNotiOn" + +#define RETRY_MAX 5 +#define BATTERY_CHECK_TIMER_INTERVAL (0.5) + +enum power_supply_init_type { + POWER_SUPPLY_NOT_READY = 0, + POWER_SUPPLY_INITIALIZED = 1, +}; + +static void uevent_power_handler(struct udev_device *dev); +static const struct uevent_handler uh = { + .subsystem = POWER_SUBSYSTEM, + .uevent_func = uevent_power_handler, +}; + +struct battery_status battery; +static int noti_id; +static Ecore_Timer *power_timer; +static Ecore_Timer *abnormal_timer; + +static int booting_done(void *data); + +static struct battery_device *battery_dev; + +static void lowbat_execute(void *data) +{ + static const struct device_ops *lowbat_ops; + + FIND_DEVICE_VOID(lowbat_ops, "lowbat"); + device_execute(lowbat_ops, data); +} + +static void pm_check_and_change(int bInserted) +{ + static int old = -1; + + if (old == bInserted) + return; + old = bInserted; + pm_change_internal(getpid(), LCD_NORMAL); +} + +static int changed_battery_cf(enum present_type status) +{ + char *value; + + if (status == PRESENT_ABNORMAL) + value = "battdisconnect"; + else + value = "remove_battery_popups"; + + return launch_system_app(APP_DEFAULT, + 2, APP_KEY_TYPE, value); +} + +static void abnormal_popup_timer_init(void) +{ + if (abnormal_timer == NULL) + return; + ecore_timer_del(abnormal_timer); + abnormal_timer = NULL; + _I("delete health timer"); +} + +static void health_status_broadcast(void) +{ + broadcast_edbus_signal(DEVICED_PATH_BATTERY, DEVICED_INTERFACE_BATTERY, + SIGNAL_TEMP_GOOD, NULL, NULL); +} + + +static void health_timer_reset(void) +{ + abnormal_timer = NULL; +} + +static Eina_Bool health_timer_cb(void *data) +{ + health_timer_reset(); + + if (battery.health == HEALTH_GOOD) + return EINA_FALSE; + + _I("popup - Battery health status is not good"); + device_notify(DEVICE_NOTIFIER_BATTERY_HEALTH, (void *)HEALTH_BAD); + pm_change_internal(getpid(), LCD_NORMAL); + pm_lock_internal(INTERNAL_LOCK_POPUP, LCD_DIM, STAY_CUR_STATE, 0); + if (battery.temp == TEMP_LOW) + battery_charge_err_low_act(NULL); + else if (battery.temp == TEMP_HIGH) + battery_charge_err_high_act(NULL); + return EINA_FALSE; +} + +static void abnormal_popup_edbus_signal_handler(void *data, DBusMessage *msg) +{ + if (battery.health == HEALTH_GOOD) + return; + _I("restart health timer"); + abnormal_timer = ecore_timer_add(ABNORMAL_CHECK_TIMER_INTERVAL, + health_timer_cb, NULL); + if (abnormal_timer == NULL) + _E("Fail to add abnormal check timer"); +} + +static void full_noti_cb(void *data, DBusMessage *msg, DBusError *err) +{ + DBusError r_err; + int ret, id; + + if (!msg) + return; + + dbus_error_init(&r_err); + ret = dbus_message_get_args(msg, &r_err, DBUS_TYPE_INT32, &id, DBUS_TYPE_INVALID); + if (!ret) { + _E("no message [%s:%s]", r_err.name, r_err.message); + dbus_error_free(&r_err); + return; + } + + noti_id = id; + _D("Inserted battery full noti : %d", noti_id); +} + +static int check_power_supply_noti(void) +{ +#ifdef MICRO_DD + int r_disturb, s_disturb, r_block, s_block; + r_disturb = vconf_get_int("memory/shealth/sleep/do_not_disturb", &s_disturb); + r_block = vconf_get_bool("db/setting/blockmode_wearable", &s_block); + if ((r_disturb != 0 && r_block != 0) || + (s_disturb == 0 && s_block == 0)) { + return 1; + } + return 0; +#else + return 1; +#endif +} + +static int send_full_noti(enum charge_full_type state) +{ + int ret = 0; + int noti; + int retry; + char str_id[32]; + char *arr[1]; + + noti = check_power_supply_noti(); + + if (!noti) + return noti; + + switch (state) { + case CHARGING_FULL: + for (retry = RETRY_MAX; retry > 0; retry--) { + ret = dbus_method_async_with_reply(POPUP_BUS_NAME, + POPUP_PATH_NOTI, + POPUP_INTERFACE_NOTI, + METHOD_FULL_NOTI_ON, + NULL, NULL, full_noti_cb, -1, NULL); + if (ret == 0) { + _D("Created battery full noti"); + return ret; + } + } + _E("Failed to call dbus method (err: %d)", ret); + break; + case CHARGING_NOT_FULL: + if (noti_id <= 0) + return -EPERM; + snprintf(str_id, sizeof(str_id), "%d", noti_id); + arr[0] = str_id; + for (retry = RETRY_MAX; retry > 0; retry--) { + ret = dbus_method_async(POPUP_BUS_NAME, + POPUP_PATH_NOTI, + POPUP_INTERFACE_NOTI, + METHOD_FULL_NOTI_OFF, + "i", arr); + if (ret == 0) { + _D("Deleted battery full noti"); + noti_id = 0; + return ret; + } + } + _E("Failed to call dbus method (err: %d)", ret); + break; + } + return ret; +} + +static int send_charge_noti(void) +{ + int ret = 0; + int retry; + + for (retry = RETRY_MAX; retry > 0; retry--) { + ret = dbus_method_async(POPUP_BUS_NAME, + POPUP_PATH_NOTI, + POPUP_INTERFACE_NOTI, + METHOD_CHARGE_NOTI_ON, + NULL, NULL); + if (ret == 0) { + _D("Created battery charge noti"); + return ret; + } + } + _E("Failed to call dbus method (err: %d)", ret); + return ret; +} + +static void power_supply_noti(enum battery_noti_type type, enum battery_noti_status status) +{ + static int charger = CHARGER_DISCHARGING; + static int full = CHARGING_NOT_FULL; + int ret; + + if (type == DEVICE_NOTI_BATT_CHARGE) { + if (status == DEVICE_NOTI_ON && charger == CHARGER_DISCHARGING) { + send_charge_noti(); + charger = CHARGER_CHARGING; + } else if (status == DEVICE_NOTI_OFF && charger == CHARGER_CHARGING) { + charger = CHARGER_DISCHARGING; + } + } else if (type == DEVICE_NOTI_BATT_FULL) { + if (status == DEVICE_NOTI_ON && full == CHARGING_NOT_FULL) { + ret = send_full_noti(CHARGING_FULL); + if (ret == 0) + full = CHARGING_FULL; + } else if (status == DEVICE_NOTI_OFF && full == CHARGING_FULL) { + ret = send_full_noti(CHARGING_NOT_FULL); + if (ret == 0) + full = CHARGING_NOT_FULL; + } + } +} + +void power_supply_broadcast(char *sig, int status) +{ + static int old; + static char sig_old[32]; + char *arr[1]; + char str_status[32]; + + if (strcmp(sig_old, sig) == 0 && old == status) + return; + + _D("%s %d", sig, status); + + old = status; + snprintf(sig_old, sizeof(sig_old), "%s", sig); + snprintf(str_status, sizeof(str_status), "%d", status); + arr[0] = str_status; + + broadcast_edbus_signal(DEVICED_PATH_BATTERY, DEVICED_INTERFACE_BATTERY, + sig, "i", arr); +} + +static void noti_batt_full(void) +{ + static int bat_full_noti; + int noti; + + if (!battery.charge_full && bat_full_noti == 1) { + power_supply_noti(DEVICE_NOTI_BATT_FULL, DEVICE_NOTI_OFF); + bat_full_noti = 0; + /* off the full charge state */ + device_notify(DEVICE_NOTIFIER_FULLBAT, (void *)false); + } + if (battery.charge_full && bat_full_noti == 0) { + power_supply_noti(DEVICE_NOTI_BATT_FULL, DEVICE_NOTI_ON); + bat_full_noti = 1; + /* turn on LCD, if battery is full charged */ + noti = check_power_supply_noti(); + if (noti) + pm_change_internal(INTERNAL_LOCK_BATTERY_FULL, + LCD_NORMAL); + else + _I("block LCD"); + /* on the full charge state */ + device_notify(DEVICE_NOTIFIER_FULLBAT, (void *)true); + } +} + +static void check_power_supply(int state) +{ + pm_check_and_change(state); + if (update_pm_setting) + update_pm_setting(SETTING_CHARGING, state); +} + +static void charger_state_send_system_event(int state) +{ + bundle *b; + const char *str; + + switch (state) { + case CHARGE_STATUS_CHARGING: + str = EVT_VAL_BATTERY_CHARGER_CHARGING; + break; + case CHARGE_STATUS_FULL: + case CHARGE_STATUS_DISCHARGING: + str = EVT_VAL_BATTERY_CHARGER_DISCHARGING; + break; + case CHARGE_STATUS_CONNECTED: + str = EVT_VAL_BATTERY_CHARGER_CONNECTED; + break; + case CHARGE_STATUS_DISCONNECTED: + str = EVT_VAL_BATTERY_CHARGER_DISCONNECTED; + break; + default: + _E("invalid parameter(%d)", state); + return; + } + + _D("system_event(%s)", str); + + b = bundle_create(); + bundle_add_str(b, EVT_KEY_BATTERY_CHARGER_STATUS, str); + eventsystem_send_system_event(SYS_EVENT_BATTERY_CHARGER_STATUS, b); + bundle_free(b); +} + +static void update_present(enum battery_noti_status status) +{ + static int old = DEVICE_NOTI_OFF; + enum present_type present; + + if (old == status) + return; + _I("charge %d present %d", battery.charge_now, battery.present); + old = status; + pm_change_internal(getpid(), LCD_NORMAL); + if (status == DEVICE_NOTI_ON) { + present = PRESENT_ABNORMAL; + device_notify(DEVICE_NOTIFIER_BATTERY_PRESENT, (void *)PRESENT_ABNORMAL); + pm_lock_internal(INTERNAL_LOCK_POPUP, LCD_DIM, STAY_CUR_STATE, 0); + } else { + present = PRESENT_NORMAL; + device_notify(DEVICE_NOTIFIER_BATTERY_PRESENT, (void *)PRESENT_NORMAL); + pm_unlock_internal(INTERNAL_LOCK_POPUP, LCD_DIM, PM_SLEEP_MARGIN); + } + changed_battery_cf(present); +} + +static void update_health(enum battery_noti_status status) +{ + static int old = DEVICE_NOTI_OFF; + + if (old == status) + return; + _I("charge %d health %d", battery.charge_now, battery.health); + old = status; + + pm_change_internal(getpid(), LCD_NORMAL); + if (status == DEVICE_NOTI_ON) { + _I("popup - Battery health status is not good"); + device_notify(DEVICE_NOTIFIER_BATTERY_HEALTH, (void *)HEALTH_BAD); + pm_lock_internal(INTERNAL_LOCK_POPUP, LCD_DIM, STAY_CUR_STATE, 0); + if (battery.temp == TEMP_LOW) + battery_charge_err_low_act(NULL); + else if (battery.temp == TEMP_HIGH) + battery_charge_err_high_act(NULL); + } else { + device_notify(DEVICE_NOTIFIER_BATTERY_HEALTH, (void *)HEALTH_GOOD); + pm_unlock_internal(INTERNAL_LOCK_POPUP, LCD_DIM, PM_SLEEP_MARGIN); + health_status_broadcast(); + abnormal_popup_timer_init(); + } +} + +static void update_ovp(enum battery_noti_status status) +{ + static int old = DEVICE_NOTI_OFF; + + if (old == status) + return; + _I("charge %d ovp %d", battery.charge_now, battery.ovp); + old = status; + pm_change_internal(getpid(), LCD_NORMAL); + if (status == DEVICE_NOTI_ON) + device_notify(DEVICE_NOTIFIER_BATTERY_OVP, (void *)OVP_ABNORMAL); + else + device_notify(DEVICE_NOTIFIER_BATTERY_OVP, (void *)OVP_NORMAL); +} + +static void check_battery_status(void) +{ + static int old = DEVICE_CHANGE_NORMAL; + int status; + + if (battery.charge_now == CHARGER_ABNORMAL && + (battery.health == HEALTH_BAD || battery.present == PRESENT_ABNORMAL)) + status = DEVICE_CHANGE_ABNORMAL; + else if (battery.ovp == OVP_ABNORMAL) + status = DEVICE_CHANGE_ABNORMAL; + else + status = DEVICE_CHANGE_NORMAL; + if (old == status) + return; + old = status; + + if (battery.charge_now == CHARGER_ABNORMAL) { + if (battery.health == HEALTH_BAD) { + update_health(DEVICE_NOTI_ON); + return; + } else if (battery.present == PRESENT_ABNORMAL) { + update_present(DEVICE_NOTI_ON); + return; + } + } + if (battery.ovp == OVP_ABNORMAL) { + update_ovp(DEVICE_NOTI_ON); + return; + } + + if (battery.charge_now != CHARGER_ABNORMAL && + status == DEVICE_CHANGE_NORMAL) { + update_health(DEVICE_NOTI_OFF); + update_ovp(DEVICE_NOTI_OFF); + update_present(DEVICE_NOTI_OFF); + } +} + +static void check_online(void) +{ + static int old_online; + static int old_charge_status; + int charge_status; + + if (battery.charge_status == CHARGE_STATUS_FULL) + charge_status = CHARGE_STATUS_DISCHARGING; + else + charge_status = battery.charge_status; + + if (battery.online > POWER_SUPPLY_TYPE_BATTERY && + old_online == VCONFKEY_SYSMAN_CHARGER_DISCONNECTED) { + old_online = VCONFKEY_SYSMAN_CHARGER_CONNECTED; + vconf_set_int(VCONFKEY_SYSMAN_CHARGER_STATUS, old_online); + power_supply_broadcast(CHARGER_STATUS_SIGNAL, old_online); + check_power_supply(old_online); + charger_state_send_system_event(CHARGE_STATUS_CONNECTED); + if (charge_status != old_charge_status) + charger_state_send_system_event(charge_status); + + } else if (battery.online <= POWER_SUPPLY_TYPE_BATTERY && + old_online == VCONFKEY_SYSMAN_CHARGER_CONNECTED) { + old_online = VCONFKEY_SYSMAN_CHARGER_DISCONNECTED; + vconf_set_int(VCONFKEY_SYSMAN_CHARGER_STATUS, old_online); + power_supply_broadcast(CHARGER_STATUS_SIGNAL, old_online); + check_power_supply(old_online); + if (charge_status != old_charge_status) + charger_state_send_system_event(charge_status); + charger_state_send_system_event(CHARGE_STATUS_DISCONNECTED); + + } else { + if (charge_status != old_charge_status) + charger_state_send_system_event(charge_status); + } + + old_charge_status = charge_status; +} + +static void check_charge_status(const char *env_value) +{ + if (env_value == NULL) + return; + + _D("Charge Status(%s)", env_value); + + if (strncmp(env_value, CHARGEFULL_NAME, + sizeof(CHARGEFULL_NAME)) == 0) + battery.charge_status = CHARGE_STATUS_FULL; + else if (strncmp(env_value, CHARGENOW_NAME, + sizeof(CHARGENOW_NAME)) == 0) + battery.charge_status = CHARGE_STATUS_CHARGING; + else if (strncmp(env_value, DISCHARGE_NAME, + sizeof(DISCHARGE_NAME)) == 0) + battery.charge_status = CHARGE_STATUS_DISCHARGING; + else if (strncmp(env_value, NOTCHARGE_NAME, + sizeof(NOTCHARGE_NAME)) == 0) + battery.charge_status = CHARGE_STATUS_NOT_CHARGING; + else + battery.charge_status = CHARGE_STATUS_UNKNOWN; + + if (battery.charge_status == CHARGE_STATUS_FULL) { + battery.charge_full = CHARGING_FULL; + battery.charge_now = CHARGER_DISCHARGING; + } else if (battery.charge_status == CHARGE_STATUS_CHARGING) { + battery.charge_full = CHARGING_NOT_FULL; + battery.charge_now = CHARGER_CHARGING; + } else if (battery.charge_status == CHARGE_STATUS_DISCHARGING) { + battery.charge_full = CHARGING_NOT_FULL; + battery.charge_now = CHARGER_DISCHARGING; + } else if (battery.charge_status == CHARGE_STATUS_NOT_CHARGING) { + battery.charge_full = CHARGING_NOT_FULL; + battery.charge_now = CHARGER_ABNORMAL; + } else { + battery.charge_full = CHARGING_NOT_FULL; + battery.charge_now = CHARGER_DISCHARGING; + } +} + +static void check_health_status(const char *env_value) +{ + if (env_value == NULL) { + battery.health = HEALTH_GOOD; + battery.temp = TEMP_LOW; + battery.ovp = OVP_NORMAL; + return; + } + if (strncmp(env_value, OVERHEAT_NAME, + sizeof(OVERHEAT_NAME)) == 0) { + battery.health = HEALTH_BAD; + battery.temp = TEMP_HIGH; + battery.ovp = OVP_NORMAL; + } else if (strncmp(env_value, TEMPCOLD_NAME, + sizeof(TEMPCOLD_NAME)) == 0) { + battery.health = HEALTH_BAD; + battery.temp = TEMP_LOW; + battery.ovp = OVP_NORMAL; + } else if (strncmp(env_value, OVERVOLT_NAME, + sizeof(OVERVOLT_NAME)) == 0) { + battery.health = HEALTH_GOOD; + battery.temp = TEMP_LOW; + battery.ovp = OVP_ABNORMAL; + } else { + battery.health = HEALTH_GOOD; + battery.temp = TEMP_LOW; + battery.ovp = OVP_NORMAL; + } +} + +static void check_online_status(const char *env_value) +{ + if (env_value == NULL) + return; + battery.online = atoi(env_value); +} + +static void check_present_status(const char *env_value) +{ + if (env_value == NULL) { + battery.present = PRESENT_NORMAL; + return; + } + battery.present = atoi(env_value); +} + +static void check_capacity_status(const char *env_value) +{ + if (env_value == NULL) + return; + battery.capacity = atoi(env_value); +} + +static void process_power_supply(void *data) +{ + static struct battery_status old; + + if (old.charge_now != battery.charge_now || battery.charge_now == CHARGER_ABNORMAL) { + vconf_set_int(VCONFKEY_SYSMAN_BATTERY_CHARGE_NOW, battery.charge_now); + power_supply_broadcast(CHARGE_NOW_SIGNAL, battery.charge_now); + } + + lowbat_execute(data); + check_online(); + if (old.charge_full != battery.charge_full) + noti_batt_full(); + + old.capacity = battery.capacity; + old.online = battery.online; + old.charge_status = battery.charge_status; + old.charge_now = battery.charge_now; + old.charge_full = battery.charge_full; + + check_battery_status(); + device_notify(DEVICE_NOTIFIER_POWER_SUPPLY, NULL); + device_notify(DEVICE_NOTIFIER_BATTERY_CHARGING, &battery.charge_now); +} + +static void uevent_power_handler(struct udev_device *dev) +{ + struct udev_list_entry *list_entry; + const char *env_name; + const char *env_value; + bool matched = false; + int ret; + + udev_list_entry_foreach(list_entry, + udev_device_get_properties_list_entry(dev)) { + env_name = udev_list_entry_get_name(list_entry); + if (!env_name) + continue; + + if (!strncmp(env_name, CHARGE_NAME, sizeof(CHARGE_NAME))) { + env_value = udev_list_entry_get_value(list_entry); + if (!env_value) + continue; + if (!strncmp(env_value, BATTERY_NAME, + sizeof(BATTERY_NAME))) { + matched = true; + break; + } + } + } + + if (!matched) + return; + + env_value = udev_device_get_property_value(dev, CHARGE_STATUS); + check_charge_status(env_value); + env_value = udev_device_get_property_value(dev, CHARGE_ONLINE); + check_online_status(env_value); + env_value = udev_device_get_property_value(dev, CHARGE_HEALTH); + check_health_status(env_value); + env_value = udev_device_get_property_value(dev, CHARGE_PRESENT); + check_present_status(env_value); + env_value = udev_device_get_property_value(dev, CAPACITY); + check_capacity_status(env_value); + + ret = booting_done(NULL); + if (ret) { + if (battery.online > POWER_SUPPLY_TYPE_BATTERY) + power_supply_noti(DEVICE_NOTI_BATT_CHARGE, DEVICE_NOTI_ON); + else + power_supply_noti(DEVICE_NOTI_BATT_CHARGE, DEVICE_NOTI_OFF); + } + + process_power_supply(&battery.capacity); +} + +static void battery_state(struct battery_info *info) +{ + if (!info) + return; + + _I("%s(%s) %s(%d) Capa(%d) Hth(%s,%s) Pres(%d) OVP(%s) Curr(%d,%d)", + info->status, + battery.charge_now == CHARGER_CHARGING ? "Charging" + : (battery.charge_now == CHARGER_DISCHARGING ? "Discharging" : "Abnormal"), + info->power_source, + info->online, + info->capacity, + info->health, + battery.temp == TEMP_LOW ? "Low" : "High", + info->present, + battery.ovp == OVP_NORMAL ? "X" : "O", + info->current_now, + info->current_average); +} + +static void battery_changed(struct battery_info *info, void *data) +{ + int ret; + + if (!info) + return; + + if (info->status) { + snprintf(battery.status_s, sizeof(battery.status_s), + "%s", info->status); + check_charge_status(info->status); + } else + battery.status_s[0] = '\0'; + + if (info->health) { + snprintf(battery.health_s, sizeof(battery.health_s), + "%s", info->health); + check_health_status(info->health); + } else + battery.health_s[0] = '\0'; + + if (info->power_source) + snprintf(battery.power_source_s, sizeof(battery.power_source_s), + "%s", info->power_source); + else + battery.power_source_s[0] = '\0'; + + battery.online = info->online; + battery.present = info->present; + battery.capacity = info->capacity; + battery.current_now = info->current_now; + battery.current_average = info->current_average; + + battery_state(info); + + ret = booting_done(NULL); + if (ret) { + if (battery.online > POWER_SUPPLY_TYPE_BATTERY) + power_supply_noti(DEVICE_NOTI_BATT_CHARGE, DEVICE_NOTI_ON); + else + power_supply_noti(DEVICE_NOTI_BATT_CHARGE, DEVICE_NOTI_OFF); + } + + process_power_supply(&battery.capacity); + +} + +static int lowbat_read(int *val) +{ + int r; + + if (!val) + return -EINVAL; + + r = sys_get_int("/sys/class/power_supply/battery/capacity", val); + if (r < 0) + return r; + + return 0; +} + +static void battery_get_capacity(struct battery_info *info, void *data) +{ + int *capa = data; + + if (info && info->capacity >= 0) + *capa = info->capacity; +} + +static void power_supply_status_init(void) +{ + static int charge_now = -1; + static int charge_full = -1; + static int capacity = -1; + int pct; + int r; + + if (battery_dev && battery_dev->get_current_state) { + pct = -1; + r = battery_dev->get_current_state(battery_get_capacity, &pct); + if (r < 0 || pct < 0) { + _E("Failed to get battery capacity (capa:%d, ret:%d)", pct, r); + return; + } + } else { + r = lowbat_read(&pct); + if (r < 0) { + _E("fail to read capacity data : %d", r); + return; + } + } + + battery.capacity = pct; + battery.health = HEALTH_GOOD; + battery.ovp = OVP_NORMAL; + battery.present = PRESENT_NORMAL; + battery.temp = TEMP_LOW; + + if (charge_now == battery.charge_now && + charge_full == battery.charge_full && + capacity == battery.capacity) + return; + + _I("charging %d full %d capacity %d", battery.charge_now, battery.charge_full, battery.capacity); + + if (charge_now != battery.charge_now) { + vconf_set_int(VCONFKEY_SYSMAN_BATTERY_CHARGE_NOW, battery.charge_now); + power_supply_broadcast(CHARGE_NOW_SIGNAL, battery.charge_now); + } + if (capacity != battery.capacity) { + vconf_set_int(VCONFKEY_SYSMAN_BATTERY_CAPACITY, battery.capacity); + power_supply_broadcast(CHARGE_CAPACITY_SIGNAL, battery.capacity); + } + + charge_now = battery.charge_now; + charge_full = battery.charge_full; + capacity = battery.capacity; +} + +static Eina_Bool power_supply_update(void *data) +{ + power_supply_status_init(); + return EINA_TRUE; +} + +static void power_supply_timer_start(void) +{ + _D("battery init timer during booting"); + power_timer = ecore_timer_add(BATTERY_CHECK_TIMER_INTERVAL, + power_supply_update, NULL); + if (power_timer == NULL) + _E("fail to add battery init timer during booting"); +} + +static void power_supply_timer_stop(void) +{ + _D("battery init timer during booting"); + if (!power_timer) + return; + ecore_timer_del(power_timer); + power_timer = NULL; +} + +static DBusMessage *dbus_get_charger_status(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int ret; + + if (vconf_get_int(VCONFKEY_SYSMAN_CHARGER_STATUS, &ret) < 0) { + _E("vconf_get_int() failed"); + ret = -EIO; + } + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *dbus_get_charge_now(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int ret; + + ret = battery.charge_now; + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *dbus_get_charge_level(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int ret; + + if (vconf_get_int(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, &ret) < 0) { + _E("vconf_get_int() failed"); + ret = -EIO; + } + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *dbus_get_percent(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int ret; + + ret = battery.capacity; + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *dbus_get_percent_raw(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int ret; + + ret = -ENOTSUP; + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *dbus_is_full(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int ret; + + ret = battery.charge_full; + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *dbus_get_health(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int ret; + + ret = battery.health; + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *dbus_power_supply_handler(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusError err; + DBusMessageIter iter; + DBusMessage *reply; + pid_t pid; + int ret = 0; + int argc; + char *type_str; + char *argv[5]; + + dbus_error_init(&err); + + if (!dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &type_str, + DBUS_TYPE_INT32, &argc, + DBUS_TYPE_STRING, &argv[0], + DBUS_TYPE_STRING, &argv[1], + DBUS_TYPE_STRING, &argv[2], + DBUS_TYPE_STRING, &argv[3], + DBUS_TYPE_STRING, &argv[4], DBUS_TYPE_INVALID)) { + _E("there is no message"); + ret = -EINVAL; + goto out; + } + + if (argc < 0) { + _E("message is invalid!"); + ret = -EINVAL; + goto out; + } + + pid = get_edbus_sender_pid(msg); + if (kill(pid, 0) == -1) { + _E("%d process does not exist, dbus ignored!", pid); + ret = -ESRCH; + goto out; + } + check_capacity_status(argv[0]); + check_charge_status(argv[1]); + check_health_status(argv[2]); + check_online_status(argv[3]); + check_present_status(argv[4]); + _I("%d %d %d %d %d %d %d %d", + battery.capacity, + battery.charge_full, + battery.charge_now, + battery.health, + battery.online, + battery.ovp, + battery.present, + battery.temp); + + if (battery.online > POWER_SUPPLY_TYPE_BATTERY) + power_supply_noti(DEVICE_NOTI_BATT_CHARGE, DEVICE_NOTI_ON); + else + power_supply_noti(DEVICE_NOTI_BATT_CHARGE, DEVICE_NOTI_OFF); + + process_power_supply(&battery.capacity); +out: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + + return reply; +} + +static void battery_get_info(struct battery_info *info, void *data) +{ + struct battery_info *bat = data; + + if (!info || !bat) + return; + + bat->status = strdup(info->status); + bat->health = strdup(info->health); + bat->power_source = strdup(info->power_source); + bat->online = info->online; + bat->present = info->present; + bat->capacity = info->capacity; + bat->current_now = info->current_now; + bat->current_average = info->current_average; +} + +static DBusMessage *dbus_get_battery_info(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int ret, val; + const char *str; + struct battery_info info = { 0, }; + + if (battery_dev && battery_dev->get_current_state) { + ret = battery_dev->get_current_state(battery_get_info, &info); + if (ret < 0) + _E("Failed to get battery info (%d)", ret); + + battery_changed(&info, NULL); + free(info.status); + free(info.health); + free(info.power_source); + } else { + if (battery.charge_status == CHARGE_STATUS_FULL) + str = CHARGEFULL_NAME; + else if (battery.charge_status == CHARGE_STATUS_CHARGING) + str = CHARGENOW_NAME; + else if (battery.charge_status == CHARGE_STATUS_DISCHARGING) + str = DISCHARGE_NAME; + else if (battery.charge_status == CHARGE_STATUS_NOT_CHARGING) + str = NOTCHARGE_NAME; + else + str = "Unknown"; + snprintf(battery.status_s, sizeof(battery.status_s), "%s", str); + + if (battery.health == HEALTH_GOOD) { + if (battery.temp == TEMP_LOW && battery.ovp == OVP_ABNORMAL) + str = OVERVOLT_NAME; + else + str = "Good"; + } else { /* HEALTH_BAD */ + if (battery.temp == TEMP_HIGH) + str = OVERHEAT_NAME; + else /* TEMP_LOW */ + str = TEMPCOLD_NAME; + } + snprintf(battery.health_s, sizeof(battery.health_s), "%s", str); + + if (vconf_get_int(VCONFKEY_SYSMAN_USB_STATUS, &val) == 0 && + val != VCONFKEY_SYSMAN_USB_DISCONNECTED) + str = POWER_SOURCE_USB; + else if (vconf_get_int(VCONFKEY_SYSMAN_CHARGER_STATUS, &val) == 0 && + val == VCONFKEY_SYSMAN_CHARGER_CONNECTED) + str = POWER_SOURCE_AC; + else + str = POWER_SOURCE_NONE; + snprintf(battery.power_source_s, sizeof(battery.power_source_s), "%s", str); + + battery.current_now = -1; /* Not supported */ + battery.current_average = -1; /* Not supported */ + ret = 0; + } + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + str = battery.status_s; + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &str); + str = battery.health_s; + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &str); + str = battery.power_source_s; + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &str); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &(battery.online)); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &(battery.present)); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &(battery.capacity)); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &(battery.current_now)); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &(battery.current_average)); + return reply; +} + +static const struct edbus_method edbus_methods[] = { + { CHARGER_STATUS_SIGNAL, NULL, "i", dbus_get_charger_status }, + { CHARGE_NOW_SIGNAL, NULL, "i", dbus_get_charge_now }, + { CHARGE_LEVEL_SIGNAL, NULL, "i", dbus_get_charge_level }, + { CHARGE_CAPACITY_SIGNAL, NULL, "i", dbus_get_percent }, + { CHARGE_CAPACITY_LAW_SIGNAL, NULL, "i", dbus_get_percent_raw }, + { CHARGE_FULL_SIGNAL, NULL, "i", dbus_is_full }, + { CHARGE_HEALTH_SIGNAL, NULL, "i", dbus_get_health }, + { POWER_SUBSYSTEM, "sisssss", "i", dbus_power_supply_handler }, + { "GetBatteryInfo", NULL, "isssiiiii", dbus_get_battery_info }, +}; + +static int booting_done(void *data) +{ + static int done; + + if (data == NULL) + return done; + done = *(int *)data; + if (done == 0) + return done; + + _I("booting done"); + + power_supply_timer_stop(); + + /* for simple noti change cb */ + power_supply_status_init(); + process_power_supply(NULL); + + return done; +} + +static int display_changed(void *data) +{ + if (battery.charge_now != CHARGER_ABNORMAL) + return 0; + if (battery.health != HEALTH_BAD && battery.present != PRESENT_ABNORMAL) + return 0; + pm_lock_internal(INTERNAL_LOCK_POPUP, LCD_DIM, STAY_CUR_STATE, 0); + return 0; +} + +static int load_uevent(struct parse_result *result, void *user_data) +{ + struct battery_status *info = user_data; + + if (!info) + return -EINVAL; + + if (MATCH(result->name, CHARGE_STATUS)) { + if (strstr(result->value, "Charging")) { + info->charge_now = CHARGER_CHARGING; + info->charge_full = CHARGING_NOT_FULL; + } else if (strstr(result->value, "Discharging")) { + info->charge_now = CHARGER_DISCHARGING; + info->charge_full = CHARGING_NOT_FULL; + } else if (strstr(result->value, "Full")) { + info->charge_now = CHARGER_DISCHARGING; + info->charge_full = CHARGING_FULL; + } else if (strstr(result->value, "Not charging")) { + info->charge_now = CHARGER_ABNORMAL; + info->charge_full = CHARGING_NOT_FULL; + } + snprintf(info->status_s, sizeof(info->status_s), "%s", result->value); + } else if (MATCH(result->name, CAPACITY)) + info->capacity = atoi(result->value); + else if (MATCH(result->name, CHARGE_HEALTH)) + snprintf(info->health_s, sizeof(info->health_s), "%s", result->value); + return 0; +} + +static int power_supply_probe(void *data) +{ + struct hw_info *info; + int ret; + + if (battery_dev) + return 0; + + ret = hw_get_info(BATTERY_HARDWARE_DEVICE_ID, + (const struct hw_info **)&info); + + if (ret < 0) { /* There is no HAL for battery */ + if (access(POWER_PATH, R_OK) == 0) + return 0; /* Just power_supply uevent is used */ + goto out; + } + + if (!info->open) { + _E("Failed to open battery device; open(NULL)"); + return -ENODEV; + } + + ret = info->open(info, NULL, (struct hw_common**)&battery_dev); + if (ret < 0) { + _E("Failed to get battery device structure (%d)", ret); + return ret; + } + + if (!battery_dev || !battery_dev->get_current_state) { + _E("get_current_state() is not supported by the Battery HAL"); + return -ENODEV; + } + + _I("battery device structure load success"); + return 0; + +out: + /** + * Set battery vconf as -ENOTSUP + * These vconf key used by runtime-info and capi-system-device. + */ + vconf_set_int(VCONFKEY_SYSMAN_CHARGER_STATUS, -ENOTSUP); + vconf_set_int(VCONFKEY_SYSMAN_BATTERY_CHARGE_NOW, -ENOTSUP); + vconf_set_int(VCONFKEY_SYSMAN_BATTERY_LEVEL_STATUS, -ENOTSUP); + _I("There is no battery device(%d)", ret); + return -ENODEV; +} + +static void power_supply_init(void *data) +{ + int ret; + + if (battery_dev) { + if (battery_dev->register_changed_event) + battery_dev->register_changed_event(battery_changed, NULL); + + if (battery_dev->get_current_state) + battery_dev->get_current_state(battery_changed, NULL); + } else { + ret = config_parse(POWER_SUPPLY_UEVENT, load_uevent, &battery); + if (ret < 0) + _E("Failed to load %s, %d Use default value!", + POWER_SUPPLY_UEVENT, ret); + + /* register power subsystem */ + register_kernel_uevent_control(&uh); + } + + /* process check battery timer until booting done */ + power_supply_timer_start(); + + register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done); + register_notifier(DEVICE_NOTIFIER_LCD, display_changed); + + ret = register_edbus_interface_and_method(DEVICED_PATH_BATTERY, + DEVICED_INTERFACE_BATTERY, + edbus_methods, ARRAY_SIZE(edbus_methods)); + if (ret < 0) + _E("fail to init edbus interface and method(%d)", ret); + + ret = register_edbus_signal_handler(DEVICED_PATH_SYSNOTI, + DEVICED_INTERFACE_SYSNOTI, SIGNAL_CHARGEERR_RESPONSE, + abnormal_popup_edbus_signal_handler); + if (ret < 0) + _E("fail to init edbus signal(%d)", ret); +} + +static void power_supply_exit(void *data) +{ + unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done); + unregister_notifier(DEVICE_NOTIFIER_LCD, display_changed); + + /* unregister power subsystem */ + unregister_kernel_uevent_control(&uh); +} + +static const struct device_ops power_supply_ops = { + .name = "power_supply", + .probe = power_supply_probe, + .init = power_supply_init, + .exit = power_supply_exit, +}; + +DEVICE_OPS_REGISTER(&power_supply_ops) diff --git a/src/battery/power-supply.h b/src/battery/power-supply.h new file mode 100644 index 0000000..24fb949 --- /dev/null +++ b/src/battery/power-supply.h @@ -0,0 +1,107 @@ +/* + * deviced + * + * 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. + */ + + +#ifndef __POWER_SUPPLY_H__ +#define __POWER_SUPPLY_H__ + +enum device_change_type { + DEVICE_CHANGE_ABNORMAL = 0, + DEVICE_CHANGE_NORMAL = 1, +}; + +enum charge_status_type { + CHARGE_STATUS_UNKNOWN, + CHARGE_STATUS_DISCONNECTED, + CHARGE_STATUS_CONNECTED, + CHARGE_STATUS_CHARGING, + CHARGE_STATUS_DISCHARGING, + CHARGE_STATUS_NOT_CHARGING, + CHARGE_STATUS_FULL, +}; +enum charge_full_type { + CHARGING_NOT_FULL = 0, + CHARGING_FULL = 1, +}; +enum charge_now_type { + CHARGER_ABNORMAL = -1, + CHARGER_DISCHARGING = 0, + CHARGER_CHARGING = 1, +}; +enum health_type { + HEALTH_BAD = 0, + HEALTH_GOOD = 1, +}; + +enum temp_type { + TEMP_LOW = 0, + TEMP_HIGH = 1, +}; + +enum present_type { + PRESENT_ABNORMAL = 0, + PRESENT_NORMAL = 1, +}; + +enum ovp_type { + OVP_NORMAL = 0, + OVP_ABNORMAL = 1, +}; + +enum battery_noti_type { + DEVICE_NOTI_BATT_CHARGE = 0, + DEVICE_NOTI_BATT_LOW, + DEVICE_NOTI_BATT_FULL, + DEVICE_NOTI_MAX, +}; + +enum battery_noti_status { + DEVICE_NOTI_OFF = 0, + DEVICE_NOTI_ON = 1, +}; + +struct battery_status { + int capacity; + int charge_status; + int charge_full; + int charge_now; + int health; + int present; + int online; + int temp; + int ovp; + int current_now; + int current_average; + char status_s[32]; + char health_s[32]; + char power_source_s[32]; +}; + +extern struct battery_status battery; + +void power_supply_broadcast(char *sig, int status); + +#define CHARGER_STATUS_SIGNAL "ChargerStatus" +#define CHARGE_NOW_SIGNAL "ChargeNow" +#define CHARGE_LEVEL_SIGNAL "BatteryStatusLow" +#define CHARGE_CAPACITY_SIGNAL "GetPercent" +#define CHARGE_CAPACITY_LAW_SIGNAL "GetPercentRaw" +#define CHARGE_HEALTH_SIGNAL "GetHealth" +#define CHARGE_FULL_SIGNAL "IsFull" + +#endif /* __POWER_SUPPLY_H__ */ diff --git a/src/block/block.c b/src/block/block.c new file mode 100644 index 0000000..47f2e8c --- /dev/null +++ b/src/block/block.c @@ -0,0 +1,2964 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/log.h" +#include "core/config-parser.h" +#include "core/device-idler.h" +#include "core/device-notifier.h" +#include "core/devices.h" +#include "core/udev.h" +#include "core/edbus-handler.h" +#include "core/list.h" +#include "block.h" + +/** + * TODO Assume root device is always mmcblk0*. + */ +#define MMC_PATH "*/mmcblk[0-9]*" +#define MMC_PARTITION_PATH "mmcblk[0-9]p[0-9]" +#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 EMUL_PATH "*/vd[a-z]*" +#define EMUL_PARTITION_PATH "vd[a-z][0-9]" + +#define FILESYSTEM "filesystem" + +#define DEV_PREFIX "/dev/" +#define ROOT_DIR "/" + +#define UNMOUNT_RETRY 5 +#define TIMEOUT_MAKE_OBJECT 500 /* milliseconds */ + +#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/deviced/block.conf" +#define MODEL_NAME "http://tizen.org/system/model_name" +#define MODEL_EMULATOR "Emulator" + +/* 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 + +enum block_dev_operation { + BLOCK_DEV_MOUNT, + BLOCK_DEV_UNMOUNT, + BLOCK_DEV_FORMAT, + BLOCK_DEV_INSERT, + BLOCK_DEV_REMOVE, +}; + +struct operation_queue { + enum block_dev_operation op; + DBusMessage *msg; + 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 */ +}; + +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 Ecore_Fd_Handler *phandler; +static bool block_control = false; +static bool block_boot = false; + +static bool emulator = false; + +/* Assume there is only one physical internal storage */ +static int dev_internal = -1; +static char dev_emul = '\0'; +static char dev_internal_scsi = '\0'; + +static int add_operation(struct block_device *bdev, + enum block_dev_operation operation, + DBusMessage *msg, 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 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 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_block_edbus_signal(DEVICED_PATH_BLOCK_MANAGER, + DEVICED_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_block_edbus_signal(DEVICED_PATH_BLOCK_MANAGER, + DEVICED_INTERFACE_BLOCK_MANAGER, + BLOCK_DEVICE_ADDED, + "issssssisibii", arr); + else if (op == BLOCK_DEV_REMOVE) + broadcast_block_edbus_signal(DEVICED_PATH_BLOCK_MANAGER, + DEVICED_INTERFACE_BLOCK_MANAGER, + BLOCK_DEVICE_REMOVED, + "issssssisibii", arr); + else { + broadcast_block_edbus_signal(DEVICED_PATH_BLOCK_MANAGER, + DEVICED_INTERFACE_BLOCK_MANAGER, + BLOCK_DEVICE_CHANGED, + "issssssisibii", arr); + broadcast_block_edbus_signal(DEVICED_PATH_BLOCK_MANAGER, + DEVICED_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) +{ + char str[PATH_MAX]; + char str2[PATH_MAX]; + int len; + int i; + + /* if no partition */ + if (!fnmatch(MMC_LINK_PATH, devnode, 0) || + !fnmatch(MMC_PATH, devnode, 0) || + !fnmatch(SCSI_PATH, devnode, 0)) + 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) + break; + } + + if (!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; + + data = calloc(1, sizeof(struct block_data)); + if (!data) + 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) +{ + 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 */ + 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; + + 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; + + 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 && + !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->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 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 Eina_Bool pipe_cb(void *data, Ecore_Fd_Handler *fdh) +{ + struct pipe_data pdata = {0,}; + int fd; + int n; + int thread_id; + char name[16]; + + if (ecore_main_fd_handler_active_get(fdh, ECORE_FD_ERROR)) { + _E("an error has occured. Ignore it."); + goto out; + } + + fd = ecore_main_fd_handler_fd_get(fdh); + if (fd <= 0) { + _E("fail to get fd"); + goto out; + } + + n = read(fd, &pdata, sizeof(pdata)); + if (n != sizeof(pdata) || !pdata.bdev) { + _E("fail to read struct pipe data"); + goto out; + } + + _D("op : %s, bdev : %p, result : %d", + get_operation_char(pdata.op, name, sizeof(name)), + pdata.bdev, pdata.result); + + /* Broadcast to mmc and usb storage module */ + broadcast_block_info(pdata.op, pdata.bdev->data, pdata.result); + + /* Broadcast outside with Block iface */ + signal_device_changed(pdata.bdev, pdata.op); + + if (pdata.op == BLOCK_DEV_REMOVE) { + thread_id = pdata.bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) + return ECORE_CALLBACK_RENEW; + free_block_device(pdata.bdev); + } + +out: + return ECORE_CALLBACK_RENEW; + +} + +static int pipe_init(void) +{ + int ret; + + ret = pipe2(pfds, O_CLOEXEC); + if (ret == -1) + return -errno; + + phandler = ecore_main_fd_handler_add(pfds[0], + ECORE_FD_READ | ECORE_FD_ERROR, + pipe_cb, NULL, NULL, NULL); + if (!phandler) + return -EPERM; + + return 0; +} + +static void pipe_exit(void) +{ + if (phandler) { + ecore_main_fd_handler_del(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) +{ + 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")); + 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 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; + +out: + _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); + else + data->mount_point = generate_mount_path(data); + + 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 (%d)", 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; + + signal_device_blocked(bdev); + + /* Need to disble app2ext whenever unmounting mmc */ + if (data->block_type == BLOCK_MMC_DEV && data->primary) + if (app2ext_disable_all_external_pkgs() < 0) + _E("app2ext_disable_all_external_pkgs() failed"); + + /* 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: + /* At first, notify to other app + * who already access sdcard */ + _I("Notify to other app who already access sdcard"); + + /* Mobile specific: + * should unmount the below vconf key. */ + if (data->block_type == BLOCK_MMC_DEV && data->primary) + 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); + + /* Need to disble app2ext whenever unmounting mmc */ + if (data->block_type == BLOCK_MMC_DEV && data->primary) + if (app2ext_disable_all_external_pkgs() < 0) + _E("app2ext_disable_all_external_pkgs() failed"); + + r = mmc_check_and_unmount(data->mount_point); + if (!r) { + _E("Failed to unmount (%d)", 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) +{ + 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); + +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); + 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(DBusMessage *msg, int result) +{ + DBusMessage *rep; + int ret; + DBusConnection *conn = NULL; + + if (!msg) + return; + + conn = get_block_dbus_connection(); + if (!conn) { + _E("dbus_bus_get error"); + return; + } + + rep = make_reply_message(msg, result); + ret = dbus_connection_send(conn, rep, NULL); + dbus_message_unref(msg); + dbus_message_unref(rep); + + if (ret == TRUE) + _I("Success to send reply"); + else + _E("Failed to send reply"); +} + +// 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)->msg, 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)->msg, 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->msg, ret); + + queue = bdev->op_queue; + queue = DD_LIST_NEXT(queue); + op = DD_LIST_NTH(queue, 0); + 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(str) + 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 (!temp) + continue; + 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, + DBusMessage *msg, void *data) +{ + struct operation_queue *op; + int ret; + int thread_id; + bool start_th; + char name[16]; + + if (!bdev) + return -EINVAL; + + _D("Add operation (%s, %s)", + get_operation_char(operation, name, sizeof(name)), + bdev->data->devnode); + + op = (struct operation_queue *)malloc(sizeof(struct operation_queue)); + if (!op) { + _E("malloc failed"); + return -ENOMEM; + } + + op->op = operation; + op->data = data; + op->done = false; + + if (msg) + msg = dbus_message_ref(msg); + op->msg = msg; + + thread_id = bdev->thread_id; + if (thread_id < 0 || thread_id >= THREAD_MAX) { + _E("Fail to find thread to add"); + return -EPERM; + } + + /* LOCK + * during adding queue and checking the queue length */ + pthread_mutex_lock(&(th_manager[thread_id].mutex)); + + 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 */ + + 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 entry; + 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 (readdir_r(dp, &entry, &dir) == 0 && dir != NULL) { + 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; + } + + 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"); + 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); + 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 (mount %s)", devnode); + return ret; + } + + ret = add_operation(bdev, BLOCK_DEV_MOUNT, NULL, NULL); + if (ret < 0) { + _E("Failed to add operation (mount %s)", devnode); + 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; + 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; + + if (dev_internal >= 0 || dev_internal_scsi != '\0' || dev_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 (!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(EMUL_PATH, temp, 0)) + sscanf(name, "vd%c", &dev_emul); + } + + mnt_free_table(t); + + return 0; +} + +static int check_external_storage(const char* devnode) +{ + char emul = '\0'; + char dev_scsi = '\0'; + char *name; + int dev_num = -1; + + if (!devnode) + return -EPERM; + + name = strrchr(devnode, '/'); + if (!name) + return -EPERM; + name++; + if (!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(EMUL_PATH, devnode, 0)) { + sscanf(name, "vd%c", &emul); + if (emul == '\0') + return -EPERM; + if (dev_emul == emul) { + _D("%s is internal storage", devnode); + return 0; + } + } + } + + return 1; +} + +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 && dev_emul == '\0' && dev_internal_scsi == '\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; + + _D("%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 int booting_done(void *data) +{ + /* if there is the attached device, try to mount */ + block_init_from_udev_enumerate(); + block_control = true; + block_boot = true; + return 0; +} + +static int block_poweroff(void *data) +{ + /* unregister mmc uevent control routine */ + unregister_udev_uevent_control(&uh); + remove_whole_block_device(); + return 0; +} + +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; + + _D("%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(E_DBus_Object *obj, + DBusMessage *msg) +{ + struct block_device *bdev; + char *mount_point; + int id; + int ret = -EBADMSG; + + if (!obj || !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 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; + } + } + + ret = add_operation(bdev, BLOCK_DEV_MOUNT, msg, NULL); + if (ret < 0) { + _E("Failed to add operation (mount %s)", bdev->data->devnode); + goto out; + } + + return NULL; + +out: + return make_reply_message(msg, ret); +} + +static DBusMessage *request_unmount_block(E_DBus_Object *obj, + DBusMessage *msg) +{ + struct block_device *bdev; + long option; + int id; + int ret = -EBADMSG; + + if (!obj || !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; + } + + ret = add_operation(bdev, BLOCK_DEV_UNMOUNT, msg, (void *)option); + if (ret < 0) { + _E("Failed to add operation (unmount %s)", bdev->data->devnode); + goto out; + } + + return NULL; + +out: + return make_reply_message(msg, ret); +} + +static DBusMessage *request_format_block(E_DBus_Object *obj, + DBusMessage *msg) +{ + struct block_device *bdev; + struct format_data *fdata; + int id; + int option; + int ret = -EBADMSG; + int prev_state; + + if (!obj || !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; + } + + 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) { + 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, msg, (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_reply_message(msg, 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(E_DBus_Object *obj, + DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + struct block_device *bdev; + struct block_data *data; + int ret, id; + + if (!obj || !msg) + return NULL; + + reply = dbus_message_new_method_return(msg); + 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(E_DBus_Object *obj, + DBusMessage *msg) +{ + show_block_device_list(); + return dbus_message_new_method_return(msg); +} + +// Called by MainThread +static DBusMessage *request_get_device_list(E_DBus_Object *obj, + 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 = dbus_message_new_method_return(msg); + + 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(E_DBus_Object *obj, + 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 = dbus_message_new_method_return(msg); + + 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(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + struct block_device *bdev; + struct block_data *data, nodata = {0,}; + dd_list *elem; + bool found; + int i; + + if (!obj || !msg) + return NULL; + + reply = dbus_message_new_method_return(msg); + 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; +} + +static const struct edbus_method manager_methods[] = { + { "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_mount_block }, + { "Unmount", "ii", "i", request_unmount_block }, + { "Format", "ii", "i", request_format_block }, + { "GetDeviceInfo", "i", "(issssssisibii)", request_get_device_info }, + { "GetMmcPrimary" , NULL, "(issssssisibii)" , request_get_mmc_primary }, +}; + +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; + 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) +{ + char *model_name; + int ret; + int i; + + /* 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); + + /* register block manager object and interface */ + ret = register_block_edbus_interface_and_method(DEVICED_PATH_BLOCK_MANAGER, + DEVICED_INTERFACE_BLOCK_MANAGER, + manager_methods, ARRAY_SIZE(manager_methods)); + if (ret < 0) + _E("fail to init edbus interface and method(%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 */ + register_notifier(DEVICE_NOTIFIER_POWEROFF, block_poweroff); + register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done); + + ret = system_info_get_platform_string(MODEL_NAME, &model_name); + if (!strncmp(MODEL_EMULATOR, model_name, strlen(model_name))) + emulator = true; + + 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); + } +} + +static void block_exit(void *data) +{ + int ret, i; + + /* unregister notifier */ + unregister_notifier(DEVICE_NOTIFIER_POWEROFF, block_poweroff); + unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done); + + /* 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(enum device_flags flags) +{ + 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(enum device_flags flags) +{ + 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; +} + +const struct device_ops block_device_ops = { + .name = "block", + .init = block_init, + .exit = block_exit, + .start = block_start, + .stop = block_stop, +}; + +DEVICE_OPS_REGISTER(&block_device_ops) diff --git a/src/block/block.conf b/src/block/block.conf new file mode 100644 index 0000000..8898468 --- /dev/null +++ b/src/block/block.conf @@ -0,0 +1,7 @@ +[Block] + +[MMC] +Multimount=no # yes or no + +[SCSI] +Multimount=yes # yes or no diff --git a/src/block/block.h b/src/block/block.h new file mode 100644 index 0000000..22ea223 --- /dev/null +++ b/src/block/block.h @@ -0,0 +1,177 @@ +/* + * deviced + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __BLOCK_H__ +#define __BLOCK_H__ + +#include +#include "core/common.h" + +#define SMACKFS_MOUNT_OPT "smackfsroot=*,smackfsdef=*" + +#define RETRY_COUNT 10 + +enum block_fs_type { + FS_TYPE_VFAT = 0, + FS_TYPE_EXT4, +}; + +struct block_fs_ops { + enum block_fs_type type; + const char *name; + bool (*match) (const char *); + int (*check) (const char *); + int (*mount) (bool, const char *, const char *); + int (*format) (const char *); +}; + +struct fs_check { + int type; + char *name; + unsigned int offset; + unsigned int magic_sz; + char magic[4]; +}; + +void add_fs(const struct block_fs_ops *fs); +void remove_fs(const struct block_fs_ops *fs); + +enum block_device_type { + BLOCK_SCSI_DEV, + BLOCK_MMC_DEV, +}; + +enum mount_state { + BLOCK_UNMOUNT, + BLOCK_MOUNT, +}; + +enum unmount_operation { + UNMOUNT_NORMAL, + UNMOUNT_FORCE, +}; + +struct block_data { + enum block_device_type block_type; + char *devnode; + char *syspath; + char *fs_usage; + char *fs_type; + char *fs_version; + char *fs_uuid_enc; + bool readonly; + char *mount_point; + enum mount_state state; + bool primary; /* the first partition */ + int flags; + int id; /* libstorage uses the id as the storage_id */ +}; + +struct block_dev_ops { + const char *name; + enum block_device_type block_type; + void (*mounted) (struct block_data *data, int result); + void (*unmounted) (struct block_data *data, int result); + void (*formatted) (struct block_data *data, int result); + void (*inserted) (struct block_data *data); + void (*removed) (struct block_data *data); +}; + +void add_block_dev(const struct block_dev_ops *ops); +void remove_block_dev(const struct block_dev_ops *ops); + +#define BLOCK_DEVICE_OPS_REGISTER(dev) \ +static void __CONSTRUCTOR__ block_dev_init(void) \ +{ \ + add_block_dev(dev); \ +} \ +static void __DESTRUCTOR__ block_dev_exit(void) \ +{ \ + remove_block_dev(dev); \ +} + +enum block_flags { + FLAG_NONE = 0, + UNMOUNT_UNSAFE = 1 << 0, + FS_BROKEN = 1 << 1, + FS_EMPTY = 1 << 2, + FS_NOT_SUPPORTED = 1 << 3, + MOUNT_READONLY = 1 << 4, +}; + +/* a: struct block_data + * b: enum block_flags */ +#define BLOCK_FLAG_SET(a, b) \ + do { \ + if (a) { \ + (((a)->flags) |= (b)); \ + } \ + } while (0) + +#define BLOCK_FLAG_UNSET(a, b) \ + do { \ + if (a) { \ + (((a)->flags) &= ~(b)); \ + } \ + } while (0) + +#define BLOCK_IS_FLAG_SET(a, b) \ + ((a) ? ((((a)->flags) & (b)) ? true : false) : false) + +#define BLOCK_FLAG_CLEAR_ALL(a) \ + do { \ + if (a) { \ + ((a)->flags) = FLAG_NONE; \ + } \ + } while (0) + +#define BLOCK_FLAG_MOUNT_CLEAR(a) \ + do { \ + BLOCK_FLAG_UNSET((a), FS_BROKEN); \ + BLOCK_FLAG_UNSET((a), FS_EMPTY); \ + BLOCK_FLAG_UNSET((a), FS_NOT_SUPPORTED); \ + BLOCK_FLAG_UNSET((a), MOUNT_READONLY); \ + } while (0) + +#define BLOCK_FLAG_UNMOUNT_CLEAR(a) \ + do { \ + BLOCK_FLAG_UNSET((a), UNMOUNT_UNSAFE); \ + } while (0) + +#define BLOCK_GET_MOUNT_FLAGS(a, c) \ + do { \ + (c) = (a)->flags; \ + (c) &= ~UNMOUNT_UNSAFE; \ + } while (0) + +#define BLOCK_GET_UNMOUNT_FLAGS(a, c) \ + do { \ + (c) = 0; \ + if (BLOCK_IS_FLAG_SET((a), UNMOUNT_UNSAFE)) \ + (c) |= UNMOUNT_UNSAFE; \ + } while (0) + +#define BLOCK_GET_FORMAT_FLAGS(a, c) \ + do { \ + (c) = 0; \ + if (BLOCK_IS_FLAG_SET((a), FS_NOT_SUPPORTED)) \ + (c) |= FS_NOT_SUPPORTED; \ + } while (0) + +#endif /* __BLOCK_H__ */ diff --git a/src/block/ext4.c b/src/block/ext4.c new file mode 100644 index 0000000..24f7e7e --- /dev/null +++ b/src/block/ext4.c @@ -0,0 +1,178 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/common.h" +#include "core/devices.h" +#include "core/launch.h" +#include "core/log.h" +#include "block.h" + +#define FS_EXT4_NAME "ext4" + +#define FS_EXT4_SMACK_LABEL "/usr/bin/mmc-smack-label" + +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 int mmc_popup_pid; + +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("mmc 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("MMC 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,}; + + snprintf(buf, sizeof(buf), "%s", mount_point); + +#ifdef TIZEN_FEATURE_BLOCK_SET_PERMISSION + launch_evenif_exist(FS_EXT4_SMACK_LABEL, buf); +#endif + + if (mmc_popup_pid > 0) { + _E("will be killed mmc-popup(%d)", mmc_popup_pid); + kill(mmc_popup_pid, SIGTERM); + } + return 0; +} + +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("Mounted mmc card [ext4]"); + if (smack) { + check_smack_popup(); + mmc_check_smack(mount_point); + } + return 0; + } + _I("mount fail : r = %d, err = %d", r, errno); + time.tv_nsec = 100 * NANO_SECOND_MULTIPLIER; + nanosleep(&time, NULL); + if (r < 0 && errno == EROFS) + mountflags |= MS_RDONLY; + } while (r < 0 && (errno == ENOENT || errno == EROFS) && retry-- > 0); + + return -errno; +} + +static int ext4_format(const char *devpath) +{ + int argc; + argc = ARRAY_SIZE(ext4_arg); + ext4_arg[argc - 2] = devpath; + return run_child(argc, ext4_arg); +} + +static const struct block_fs_ops ext4_ops = { + .type = FS_TYPE_EXT4, + .name = "ext4", + .match = ext4_match, + .check = ext4_check, + .mount = ext4_mount, + .format = ext4_format, +}; + +static void __CONSTRUCTOR__ module_init(void) +{ + add_fs(&ext4_ops); +} +/* +static void __DESTRUCTOR__ module_exit(void) +{ + remove_fs(&ext4_ops); +} +*/ diff --git a/src/block/mmc.c b/src/block/mmc.c new file mode 100644 index 0000000..b3990cf --- /dev/null +++ b/src/block/mmc.c @@ -0,0 +1,123 @@ +/* + * deviced + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include + +#include "core/log.h" +#include "core/common.h" +#include "block/block.h" + +static void mmc_update_state(int state) +{ + static int old = -1; + + if (old == state) + return; + + if (vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, state) == 0) + old = state; +} + +static void mmc_update_mount_state(int state) +{ + static int old = -1; + + if (old == state) + return; + + if (vconf_set_int(VCONFKEY_SYSMAN_MMC_MOUNT, state) == 0) + old = state; +} + +static void mmc_mount(struct block_data *data, int result) +{ + int r; + + /* Only the primary partition is valid. */ + if (!data || !data->primary) + return; + + if (result < 0) { + mmc_update_state(VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED); + mmc_update_mount_state(VCONFKEY_SYSMAN_MMC_MOUNT_FAILED); + return; + } + + /* Give a transmutable attribute to mount_point */ + r = setxattr(data->mount_point, "security.SMACK64TRANSMUTE", + "TRUE", strlen("TRUE"), 0); + if (r < 0) + _E("setxattr error : %d", errno); + + mmc_update_state(VCONFKEY_SYSMAN_MMC_MOUNTED); + mmc_update_mount_state(VCONFKEY_SYSMAN_MMC_MOUNT_COMPLETED); +} + +static void mmc_unmount(struct block_data *data, int result) +{ + /* Only the primary partition is valid. */ + if (!data || !data->primary) + return; + + if (result == 0) + mmc_update_state(VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED); + else + mmc_update_state(VCONFKEY_SYSMAN_MMC_MOUNTED); +} + +static void mmc_format(struct block_data *data, int result) +{ + /* Only the primary partition is valid. */ + if (!data || !data->primary) + return; + + if (data->state == BLOCK_MOUNT) { + mmc_update_state(VCONFKEY_SYSMAN_MMC_MOUNTED); + mmc_update_mount_state(VCONFKEY_SYSMAN_MMC_MOUNT_COMPLETED); + } +} + +static void mmc_insert(struct block_data *data) +{ + /* Do nothing */ +} + +static void mmc_remove(struct block_data *data) +{ + /* Only the primary partition is valid. */ + if (!data || !data->primary) + return; + + mmc_update_state(VCONFKEY_SYSMAN_MMC_REMOVED); +} + +const struct block_dev_ops mmc_block_ops = { + .name = "mmc", + .block_type = BLOCK_MMC_DEV, + .mounted = mmc_mount, + .unmounted = mmc_unmount, + .formatted = mmc_format, + .inserted = mmc_insert, + .removed = mmc_remove, +}; + +BLOCK_DEVICE_OPS_REGISTER(&mmc_block_ops) diff --git a/src/block/storage.c b/src/block/storage.c new file mode 100755 index 0000000..c5fb269 --- /dev/null +++ b/src/block/storage.c @@ -0,0 +1,410 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "device-node.h" +#include "core/log.h" +#include "core/devices.h" +#include "core/common.h" +#include "core/edbus-handler.h" +#include "core/device-notifier.h" +#include "core/config-parser.h" +#include "apps/apps.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 5 + +#define STORAGE_CONF_FILE "/etc/deviced/storage.conf" + +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 Ecore_Timer *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_edbus_signal(DEVICED_PATH_LOWMEM, DEVICED_INTERFACE_LOWMEM, + signal, "i", arr); +} + +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) + goto out; + + if (value) { + ret = launch_system_app(APP_DEFAULT, + 2, APP_KEY_TYPE, value); + if (ret < 0) + _E("Failed to launch (%s) popup", value); + } + + return 0; +out: + return -1; +} + +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); + 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); + 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) c:%4.4lf f:%4.4lf", + path, dTotal, dAvail, (dAvail*100/dTotal), 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 Eina_Bool check_storage_status(void *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) + ecore_timer_interval_set(memnoti_timer, MEMNOTI_TIMER_INTERVAL); + + return EINA_TRUE; +} + +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 = ecore_timer_add(MEMNOTI_TIMER_INTERVAL, + check_storage_status, NULL); + if (memnoti_timer == NULL) + _E("fail mem available noti timer add"); + return 0; +} + +static DBusMessage *edbus_getstatus(E_DBus_Object *obj, 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 = dbus_message_new_method_return(msg); + 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 *edbus_get_storage_status(E_DBus_Object *obj, 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_edbus_sender_pid(msg); + + _D("[request %d] path %s total %4.0lf avail %4.0lf", pid, path, dTotal, dAvail); + +out: + reply = dbus_message_new_method_return(msg); + 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 struct edbus_method edbus_methods[] = { + { "getstorage", NULL, "tt", edbus_getstatus }, + { "GetStatus", "s", "tt", edbus_get_storage_status}, + /* Add methods here */ +}; + +static int booting_done(void *data) +{ + static int done; + + if (data == NULL) + return done; + done = *(int *)data; + if (done == 0) + return done; + + _I("booting done"); + + if (init_storage_config_info_all() == -1) + _E("fail remain mem noti control fd init"); + return done; +} + +static int storage_poweroff(void *data) +{ + if (memnoti_timer) { + ecore_timer_del(memnoti_timer); + memnoti_timer = NULL; + } + return 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; + + storage_config_load(&storage_internal_info); + register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done); + register_notifier(DEVICE_NOTIFIER_POWEROFF, storage_poweroff); + ret = register_edbus_method(DEVICED_PATH_STORAGE, edbus_methods, ARRAY_SIZE(edbus_methods)); + if (ret < 0) + _E("fail to init edbus method(%d)", ret); +} + +static void storage_exit(void *data) +{ + unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done); + unregister_notifier(DEVICE_NOTIFIER_POWEROFF, storage_poweroff); +} + +static const struct device_ops storage_device_ops = { + .name = "storage", + .init = storage_init, + .exit = storage_exit, +}; + +DEVICE_OPS_REGISTER(&storage_device_ops) diff --git a/src/block/storage.conf b/src/block/storage.conf new file mode 100644 index 0000000..331e786 --- /dev/null +++ b/src/block/storage.conf @@ -0,0 +1,7 @@ +[LOWSTORAGE] +#5% +WARNING_LEVEL=5 +#0.1% +CRITICAL_LEVEL=0.1 +#0.0% +FULL_LEVEL=0 diff --git a/src/block/vfat.c b/src/block/vfat.c new file mode 100644 index 0000000..5ad8e07 --- /dev/null +++ b/src/block/vfat.c @@ -0,0 +1,162 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "core/common.h" +#include "core/log.h" +#include "block.h" + +#define FS_VFAT_NAME "mkdosfs" + +#ifdef TIZEN_FEATURE_BLOCK_SET_PERMISSION +/* guid 10001 - group priv_externalstorage */ +#define FS_VFAT_MOUNT_OPT "uid=0,gid=10001,dmask=0007,fmask=0117,iocharset=iso8859-1,utf8,shortname=mixed" +#else +#define FS_VFAT_MOUNT_OPT "iocharset=iso8859-1,utf8,shortname=mixed" +#endif + +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("MMC 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("Mounted mmc card [vfat]"); + return 0; + } + _I("mount fail : r = %d, err = %d", r, errno); + time.tv_nsec = 100 * NANO_SECOND_MULTIPLIER; + nanosleep(&time, NULL); + if (r < 0 && errno == EROFS) + mountflags |= MS_RDONLY; + } while (r < 0 && (errno == ENOENT || errno == EROFS) && retry-- > 0); + + return -errno; +} + +static int vfat_format(const char *devpath) +{ + int argc; + argc = ARRAY_SIZE(vfat_arg); + vfat_arg[argc - 2] = devpath; + return run_child(argc, vfat_arg); +} + +static const struct block_fs_ops vfat_ops = { + .type = FS_TYPE_VFAT, + .name = "vfat", + .match = vfat_match, + .check = vfat_check, + .mount = vfat_mount, + .format = vfat_format, +}; + +static void __CONSTRUCTOR__ module_init(void) +{ + add_fs(&vfat_ops); +} +/* +static void __DESTRUCTOR__ module_exit(void) +{ + _I("module exit"); + remove_fs(&vfat_ops); +} +*/ diff --git a/src/control/control.c b/src/control/control.c new file mode 100644 index 0000000..ffc09d2 --- /dev/null +++ b/src/control/control.c @@ -0,0 +1,227 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include + +#include "core/log.h" +#include "core/common.h" +#include "core/devices.h" +#include "core/edbus-handler.h" + +#define CONTROL_HANDLER_NAME "control" +#define CONTROL_GETTER_NAME "getcontrol" + +static const struct control_device { + const int id; + const char *name; +} devices[] = { + /* Add id & ops to provide start/stop control */ + { DEVICE_CONTROL_MMC, "mmc" }, + { DEVICE_CONTROL_USBCLIENT, "usbclient" }, + { DEVICE_CONTROL_BLOCK, "block" }, +}; + +static int control_handler(int argc, char **argv) +{ + int i; + int pid; + int device; + bool enable; + int ret; + const struct device_ops *dev_ops = NULL; + + _I("argc : %d", argc); + for (i = 0; i < argc; ++i) + _I("[%2d] %s", i, argv[i]); + + if (argc > 5) { + _E("Invalid argument"); + errno = EINVAL; + return -1; + } + + pid = atoi(argv[0]); + device = atoi(argv[1]); + enable = atoi(argv[2]); + _I("pid : %d, device : %d, enable :%d", pid, device, enable); + + for (i = 0; i < ARRAY_SIZE(devices); i++) + if (devices[i].id == device) + break; + + if (i >= ARRAY_SIZE(devices)) + return -EINVAL; + FIND_DEVICE_INT(dev_ops, devices[i].name); + if (enable) + ret = device_start(dev_ops); + else + ret = device_stop(dev_ops); + + return ret; +} + +static int get_control_handler(int argc, char **argv) +{ + int i; + int pid; + int device; + const struct device_ops *dev_ops = NULL; + + _I("argc : %d", argc); + for (i = 0; i < argc; ++i) + _I("[%2d] %s", i, argv[i]); + + if (argc > 4) { + _E("Invalid argument"); + errno = EINVAL; + return -1; + } + + pid = atoi(argv[0]); + device = atoi(argv[1]); + _I("pid : %d, device : %d", pid, device); + + for (i = 0; i < ARRAY_SIZE(devices); i++) + if (devices[i].id == device) + break; + + if (i >= ARRAY_SIZE(devices)) + return -EINVAL; + + FIND_DEVICE_INT(dev_ops, devices[i].name); + + return device_get_status(dev_ops); +} + + +static DBusMessage *dbus_control_handler(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusError err; + DBusMessageIter iter; + DBusMessage *reply; + pid_t pid; + int ret; + int argc; + char *type_str; + char *argv[3]; + + dbus_error_init(&err); + + if (!dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &type_str, + DBUS_TYPE_INT32, &argc, + DBUS_TYPE_STRING, &argv[0], + DBUS_TYPE_STRING, &argv[1], + DBUS_TYPE_STRING, &argv[2], DBUS_TYPE_INVALID)) { + _E("there is no message"); + ret = -EINVAL; + goto out; + } + + if (argc < 0) { + _E("message is invalid!"); + ret = -EINVAL; + goto out; + } + + pid = get_edbus_sender_pid(msg); + if (kill(pid, 0) == -1) { + _E("%d process does not exist, dbus ignored!", pid); + ret = -ESRCH; + goto out; + } + + ret = control_handler(argc, (char **)&argv); +out: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + + return reply; +} + +static DBusMessage *dbus_get_control_handler(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusError err; + DBusMessageIter iter; + DBusMessage *reply; + pid_t pid; + int ret; + int argc; + char *type_str; + char *argv[2]; + + dbus_error_init(&err); + + if (!dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &type_str, + DBUS_TYPE_INT32, &argc, + DBUS_TYPE_STRING, &argv[0], + DBUS_TYPE_STRING, &argv[1], DBUS_TYPE_INVALID)) { + _E("there is no message"); + ret = -EINVAL; + goto out; + } + + if (argc < 0) { + _E("message is invalid!"); + ret = -EINVAL; + goto out; + } + + pid = get_edbus_sender_pid(msg); + if (kill(pid, 0) == -1) { + _E("%d process does not exist, dbus ignored!", pid); + ret = -ESRCH; + goto out; + } + + ret = get_control_handler(argc, (char **)&argv); +out: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + + return reply; +} + +static const struct edbus_method edbus_methods[] = { + { CONTROL_HANDLER_NAME, "sisss", "i", dbus_control_handler }, + { CONTROL_GETTER_NAME , "siss", "i", dbus_get_control_handler }, +}; + +static void control_init(void *data) +{ + int ret; + + ret = register_edbus_method(DEVICED_PATH_SYSNOTI, edbus_methods, ARRAY_SIZE(edbus_methods)); + if (ret < 0) + _E("fail to init edbus method(%d)", ret); +} + +static const struct device_ops control_device_ops = { + .name = "control", + .init = control_init, +}; + +DEVICE_OPS_REGISTER(&control_device_ops) diff --git a/src/core/common.c b/src/core/common.c new file mode 100644 index 0000000..3cb598f --- /dev/null +++ b/src/core/common.c @@ -0,0 +1,410 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "common.h" + +#define PERMANENT_DIR "/tmp/permanent" +#define VIP_DIR "/tmp/vip" +#define BUFF_MAX 255 + +#define APP_ATTR_PATH "/proc/%d/attr/current" + +/** + * Opens "/proc/$pid/oom_score_adj" file for w/r; + * Return: FILE pointer or NULL + */ +FILE *open_proc_oom_score_adj_file(int pid, const char *mode) +{ + char buf[32]; + FILE *fp; + + snprintf(buf, sizeof(buf), "/proc/%d/oom_score_adj", pid); + fp = fopen(buf, mode); + return fp; +} + +int get_exec_pid(const char *execpath) +{ + DIR *dp; + struct dirent entry; + struct dirent *dentry; + int pid = -1, fd; + int ret; + int len; + char buf[PATH_MAX]; + char buf2[PATH_MAX]; + + dp = opendir("/proc"); + if (!dp) { + _E("FAIL: open /proc"); + return -1; + } + + len = strlen(execpath) + 1; + while (1) { + ret = readdir_r(dp, &entry, &dentry); + if (ret != 0 || 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, buf2, PATH_MAX); + close(fd); + + if (ret < 0 || ret >= PATH_MAX) + continue; + + buf2[ret] = '\0'; + + if (!strncmp(buf2, execpath, len)) { + closedir(dp); + return pid; + } + } + + errno = ESRCH; + closedir(dp); + return -1; +} + +int get_cmdline_name(pid_t pid, char *cmdline, size_t cmdline_size) +{ + int fd, ret; + char buf[PATH_MAX + 1]; + char *filename; + + snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); + fd = open(buf, O_RDONLY); + if (fd < 0) { + errno = ESRCH; + return -1; + } + + ret = read(fd, buf, PATH_MAX); + close(fd); + if (ret < 0) + return -1; + + buf[PATH_MAX] = '\0'; + + filename = strrchr(buf, '/'); + if (filename == NULL) + filename = buf; + else + filename = filename + 1; + + if (cmdline_size < strlen(filename) + 1) { + errno = EOVERFLOW; + return -1; + } + + strncpy(cmdline, filename, cmdline_size - 1); + cmdline[cmdline_size - 1] = '\0'; + return 0; +} + +int is_vip(int pid) +{ + if (pid < 1) + return -1; + + char buf[PATH_MAX]; + + snprintf(buf, PATH_MAX, "%s/%d", VIP_DIR, pid); + + if (access(buf, R_OK) == 0) + return 1; + else + return 0; +} + +static int remove_dir_internal(int fd) +{ + DIR *dir; + struct dirent entry; + struct dirent *de; + int subfd, ret = 0; + + dir = fdopendir(fd); + if (!dir) + return -1; + while (1) { + ret = readdir_r(dir, &entry, &de); + if (ret != 0 || de == NULL) + break; + if (de->d_type == DT_DIR) { + if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3)) + continue; + subfd = openat(fd, de->d_name, O_RDONLY | O_DIRECTORY); + if (subfd < 0) { + _SE("Couldn't openat %s: %d\n", de->d_name, errno); + ret = -1; + continue; + } + if (remove_dir_internal(subfd)) + ret = -1; + + close(subfd); + if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { + _SE("Couldn't unlinkat %s: %d\n", de->d_name, errno); + ret = -1; + } + } else { + if (unlinkat(fd, de->d_name, 0) < 0) { + _SE("Couldn't unlinkat %s: %d\n", de->d_name, errno); + ret = -1; + } + } + } + closedir(dir); + return ret; +} + +int remove_dir(const char *path, int del_dir) +{ + int fd, ret = 0; + + if (!path) + return -1; + fd = open(path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); + if (fd < 0) { + _SE("Couldn't opendir %s: %d\n", path, errno); + return -errno; + } + ret = remove_dir_internal(fd); + close(fd); + + if (del_dir) { + if (rmdir(path)) { + _SE("Couldn't rmdir %s: %d\n", path, errno); + ret = -1; + } + } + return ret; +} + +/* + * Helper function + * - Read from sysfs entry + * - Write to sysfs entry + */ +int sys_check_node(char *path) +{ + int fd; + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + close(fd); + return 0; +} + +static int sys_read_buf(char *file, char *buf) +{ + int fd; + int r; + int ret = 0; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -ENOENT; + + r = read(fd, buf, BUFF_MAX); + if ((r >= 0) && (r < BUFF_MAX)) + buf[r] = '\0'; + else + ret = -EIO; + + close(fd); + + return ret; +} + +static int sys_write_buf(char *file, char *buf) +{ + int fd; + int r; + int ret = 0; + + fd = open(file, O_WRONLY); + if (fd == -1) + return -ENOENT; + + r = write(fd, buf, strlen(buf)); + if (r < 0) + ret = -EIO; + + close(fd); + + return ret; +} + +int sys_get_int(char *fname, int *val) +{ + char buf[BUFF_MAX]; + int ret = 0; + + if (sys_read_buf(fname, buf) == 0) { + *val = atoi(buf); + } else { + *val = -1; + ret = -EIO; + } + + return ret; +} + +int sys_set_int(char *fname, int val) +{ + char buf[BUFF_MAX]; + int ret = 0; + + snprintf(buf, sizeof(buf), "%d", val); + + if (sys_write_buf(fname, buf) != 0) + ret = -EIO; + + return ret; +} + +int sys_get_str(char *fname, char *str) +{ + char buf[BUFF_MAX] = {0}; + + if (sys_read_buf(fname, buf) == 0) { + strncpy(str, buf, strlen(buf)); + return 0; + } + + return -1; +} + +int sys_set_str(char *fname, char *val) +{ + int r = -1; + + if (val != NULL) { + if (sys_write_buf(fname, val) == 0) + r = 0; + } + + return r; +} + +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; +} + +void print_time(const char *prefix) +{ + struct timeval tv; + struct tm tm; + struct tm *ret; + gettimeofday(&tv, NULL); + ret = localtime_r(&(tv.tv_sec), &tm); + if (ret) + _D("%s --> %d:%02d:%02d %d", + prefix, tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec); +} + +int get_privilege(pid_t pid, char *name, size_t len) +{ + char path[PATH_MAX]; + char attr[BUFF_MAX]; + size_t attr_len; + FILE *fp; + + snprintf(path, sizeof(path), APP_ATTR_PATH, pid); + + fp = fopen(path, "r"); + if (!fp) + return -errno; + + attr_len = fread(attr, 1, sizeof(attr) - 1, fp); + fclose(fp); + if (attr_len == 0) + return -ENOENT; + + attr[attr_len] = '\0'; + + snprintf(name, len, "%s", attr); + return 0; +} diff --git a/src/core/common.h b/src/core/common.h new file mode 100644 index 0000000..c0096b8 --- /dev/null +++ b/src/core/common.h @@ -0,0 +1,151 @@ +/* + * deviced + * + * 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. + */ + + +#ifndef __CORE_COMMON_H__ +#define __CORE_COMMON_H__ + +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0])) + +/* + * One byte digit has 3 position in decimal representation + * 2 - 5 + * 4 - 10 + * 8 - 20 + * >8 - compile time error + * plus 1 null termination byte + * plus 1 for negative prefix + */ +#define MAX_DEC_SIZE(type) \ + (2 + (sizeof(type) <= 1 ? 3 : \ + sizeof(type) <= 2 ? 5 : \ + sizeof(type) <= 4 ? 10 : \ + sizeof(type) <= 8 ? 20 : \ + sizeof(int[-2*(sizeof(type) > 8)]))) + +#ifndef __CONSTRUCTOR__ +#define __CONSTRUCTOR__ __attribute__ ((constructor)) +#endif + +#ifndef __DESTRUCTOR__ +#define __DESTRUCTOR__ __attribute__ ((destructor)) +#endif + +#ifndef __WEAK__ +#define __WEAK__ __attribute__ ((weak)) +#endif + +#ifndef max +#define max(a, b) \ + __extension__ ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) +#endif + +#ifndef min +#define min(a, b) \ + __extension__ ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) +#endif + +#ifndef clamp +#define clamp(x, low, high) \ + __extension__ ({ \ + typeof(x) _x = (x); \ + typeof(low) _low = (low); \ + typeof(high) _high = (high); \ + ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \ + }) +#endif + +#ifndef SEC_TO_MSEC +#define SEC_TO_MSEC(x) ((x)*1000) +#endif +#ifndef MSEC_TO_USEC +#define MSEC_TO_USEC(x) ((unsigned int)(x)*1000) +#endif +#ifndef NSEC_TO_MSEC +#define NSEC_TO_MSEC(x) ((double)x/1000000) +#endif +#ifndef USEC_TO_MSEC +#define USEC_TO_MSEC(x) ((double)x/1000) +#endif + +#define NANO_SECOND_MULTIPLIER 1000000 /* 1ms = 1,000,000 nsec */ + +#ifndef safe_free +#define safe_free(x) safe_free_memory((void**)&(x)) +#endif + +static inline void safe_free_memory(void** mem) +{ + if (mem && *mem) { + free(*mem); + *mem = NULL; + } +} + +#define ret_value_if(expr, val) do { \ + if (expr) { \ + _E("(%s)", #expr); \ + return (val); \ + } \ +} while (0) + +#define ret_value_msg_if(expr, val, fmt, arg...) do { \ + if (expr) { \ + _E(fmt, ##arg); \ + return val; \ + } \ +} while (0) + +#define ret_msg_if(expr, fmt, arg...) do { \ + if (expr) { \ + _E(fmt, ##arg); \ + return; \ + } \ +} while (0) + +FILE * open_proc_oom_score_adj_file(int pid, const char *mode); +int get_exec_pid(const char *execpath); +int get_cmdline_name(pid_t pid, char *cmdline, size_t cmdline_size); +int is_vip(int pid); +int run_child(int argc, const char *argv[]); +int remove_dir(const char *path, int del_dir); +int sys_check_node(char *path); +int sys_get_int(char *fname, int *val); +int sys_set_int(char *fname, int val); +int sys_get_str(char *fname, char *str); +int sys_set_str(char *fname, char *val); +int terminate_process(const char *partition, bool force); +int mount_check(const char* path); +void print_time(const char *prefix); +int get_privilege(pid_t pid, char *name, size_t len); + +#endif /* __CORE_COMMON_H__ */ diff --git a/src/core/config-parser.c b/src/core/config-parser.c new file mode 100644 index 0000000..4018b88 --- /dev/null +++ b/src/core/config-parser.c @@ -0,0 +1,126 @@ +/* + * deviced + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "log.h" +#include "config-parser.h" + +#define MAX_LINE 128 +#define MAX_SECTION 64 +#define WHITESPACE " \t" +#define NEWLINE "\n\r" +#define COMMENT '#' + +static inline char *trim_str(char *s) +{ + char *t; + /* left trim */ + s += strspn(s, WHITESPACE); + + /* right trim */ + for (t = strchr(s, 0); t > s; t--) + if (!strchr(WHITESPACE, t[-1])) + break; + *t = 0; + return s; +} + +int config_parse(const char *file_name, int cb(struct parse_result *result, + void *user_data), void *user_data) +{ + FILE *f = NULL; + struct parse_result result; + /* use stack for parsing */ + char line[MAX_LINE]; + char section[MAX_SECTION]; + char *start, *end, *name, *value; + int lineno = 0, ret = 0; + + if (!file_name || !cb) { + ret = -EINVAL; + goto error; + } + + /* open conf file */ + f = fopen(file_name, "r"); + if (!f) { + _E("Failed to open file %s", file_name); + ret = -EIO; + goto error; + } + + /* parsing line by line */ + while (fgets(line, MAX_LINE, f) != NULL) { + lineno++; + + start = line; + start[strcspn(start, NEWLINE)] = '\0'; + start = trim_str(start); + + if (*start == COMMENT) { + continue; + } else if (*start == '[') { + /* parse section */ + end = strchr(start, ']'); + if (!end || *end != ']') { + ret = -EBADMSG; + goto error; + } + + *end = '\0'; + strncpy(section, start + 1, sizeof(section)); + section[MAX_SECTION-1] = '\0'; + } else if (*start) { + /* parse name & value */ + end = strchr(start, '='); + if (!end || *end != '=') { + ret = -EBADMSG; + goto error; + } + *end = '\0'; + name = trim_str(start); + value = trim_str(end + 1); + end = strchr(value, COMMENT); + if (end && *end == COMMENT) { + *end = '\0'; + value = trim_str(value); + } + + result.section = section; + result.name = name; + result.value = value; + /* callback with parse result */ + ret = cb(&result, user_data); + if (ret < 0) { + ret = -EBADMSG; + goto error; + } + } + } + _D("Success to load %s", file_name); + fclose(f); + return 0; + +error: + if (f) + fclose(f); + _E("Failed to read %s:%d!", file_name, lineno); + return ret; +} diff --git a/src/core/config-parser.h b/src/core/config-parser.h new file mode 100644 index 0000000..7df70e6 --- /dev/null +++ b/src/core/config-parser.h @@ -0,0 +1,42 @@ +/* + * deviced + * + * Copyright (c) 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. + */ + + +#ifndef __CONFIG_PARSER_H__ +#define __CONFIG_PARSER_H__ + +#define MATCH(a, b) (!strncmp(a, b, strlen(a))) +#define SET_CONF(a, b) (a = (b > 0.0 ? b : a)) + +struct parse_result { + char *section; + char *name; + char *value; +}; + +/** + * @brief Parse config file and call callback\n + * @param[in] file_name conf file. + * @param[in] cb cb is called when conf file is parsed line by line. + * @param[in] user_data user data is passed to cb. + * @return 0 on success, negative if failed + */ +int config_parse(const char *file_name, int cb(struct parse_result *result, + void *user_data), void *user_data); + +#endif diff --git a/src/core/device-idler.c b/src/core/device-idler.c new file mode 100644 index 0000000..e399bf2 --- /dev/null +++ b/src/core/device-idler.c @@ -0,0 +1,102 @@ +/* + * deviced + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#include "log.h" + +struct device_request { + void (*func)(void *data); + void *data; +}; + +static GQueue req_queue = G_QUEUE_INIT; +static Ecore_Idler *idler; + +static int free_request(struct device_request *req) +{ + if (!req) + return -EINVAL; + + free(req); + return 0; +} + +static Eina_Bool idler_cb(void *data) +{ + struct device_request *req; + + req = g_queue_pop_head(&req_queue); + if (req) { + if (req->func) + req->func(req->data); + free_request(req); + } + + if (g_queue_is_empty(&req_queue)) { + idler = NULL; + return ECORE_CALLBACK_CANCEL; + } + + return ECORE_CALLBACK_RENEW; +} + +static void process_next_request_in_idle(void) +{ + if (g_queue_is_empty(&req_queue)) + return; + + if (idler) + return; + + idler = ecore_idler_add(idler_cb, NULL); + /** + * if idler is null, + * it means whole system might be an abnormal state. + * so it just prints out error log. + */ + if (!idler) + _E("fail to add request to idler"); +} + +int add_idle_request(void (*func)(void *data), void *data) +{ + struct device_request *req; + + if (!func) { + _E("invalid argumet : func(NULL)"); + return -EINVAL; + } + + req = calloc(1, sizeof(struct device_request)); + if (!req) { + _E("fail to allocate request : %d", errno); + return -errno; + } + + req->func = func; + req->data = data; + + g_queue_push_tail(&req_queue, req); + process_next_request_in_idle(); + + return 0; +} diff --git a/src/core/device-idler.h b/src/core/device-idler.h new file mode 100644 index 0000000..411e4bb --- /dev/null +++ b/src/core/device-idler.h @@ -0,0 +1,29 @@ +/* + * deviced + * + * Copyright (c) 2015 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 __DEVICE_IDLER_H__ +#define __DEVICE_IDLER_H__ + +/* + * To allow for callbacks to be called when the daemon is idle state. + */ + +int add_idle_request(void (*func)(void *data), void *data); + +#endif /* __DEVICE_IDLER_H__ */ diff --git a/src/core/device-notifier.c b/src/core/device-notifier.c new file mode 100644 index 0000000..ff066c0 --- /dev/null +++ b/src/core/device-notifier.c @@ -0,0 +1,119 @@ +/* + * deviced + * + * 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 "log.h" +#include "device-notifier.h" +#include "list.h" +#include "common.h" + +struct device_notifier { + bool deleted; + enum device_notifier_type status; + int (*func)(void *data); +}; + +static dd_list *device_notifier_list; +static Ecore_Idler *idl; + +#define FIND_NOTIFIER(a, b, d, e, f) \ + DD_LIST_FOREACH(a, b, d) \ + if (e == d->e && f == (d->f)) + +int register_notifier(enum device_notifier_type status, int (*func)(void *data)) +{ + dd_list *n; + struct device_notifier *notifier; + + _I("%d, %x", status, func); + + if (!func) { + _E("invalid func address!"); + return -EINVAL; + } + + FIND_NOTIFIER(device_notifier_list, n, notifier, status, func) { + _E("function is already registered! [%d, %x]", + status, func); + return -EINVAL; + } + + notifier = calloc(1, sizeof(struct device_notifier)); + if (!notifier) { + _E("Fail to malloc for notifier!"); + return -ENOMEM; + } + + notifier->status = status; + notifier->func = func; + + DD_LIST_APPEND(device_notifier_list, notifier); + + return 0; +} + +int unregister_notifier(enum device_notifier_type status, int (*func)(void *data)) +{ + dd_list *n; + struct device_notifier *notifier; + + if (!func) { + _E("invalid func address!"); + return -EINVAL; + } + + FIND_NOTIFIER(device_notifier_list, n, notifier, status, func) { + _I("[%d, %x]", status, func); + notifier->deleted = true; + } + + return 0; +} + +static Eina_Bool delete_unused_notifier_cb(void *data) +{ + dd_list *n; + dd_list *next; + struct device_notifier *notifier; + + DD_LIST_FOREACH_SAFE(device_notifier_list, n, next, notifier) { + if (notifier->deleted) { + DD_LIST_REMOVE_LIST(device_notifier_list, n); + free(notifier); + } + } + + idl = NULL; + return ECORE_CALLBACK_CANCEL; +} + +void device_notify(enum device_notifier_type status, void *data) +{ + dd_list *n; + struct device_notifier *notifier; + + DD_LIST_FOREACH(device_notifier_list, n, notifier) { + if (!notifier->deleted && status == notifier->status) { + if (notifier->func) + notifier->func(data); + } + } + + if (!idl) + idl = ecore_idler_add(delete_unused_notifier_cb, NULL); +} diff --git a/src/core/device-notifier.h b/src/core/device-notifier.h new file mode 100644 index 0000000..567e6bf --- /dev/null +++ b/src/core/device-notifier.h @@ -0,0 +1,54 @@ +/* + * deviced + * + * 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 __DEVICE_NOTIFIER_H__ +#define __DEVICE_NOTIFIER_H__ + +enum device_notifier_type { + DEVICE_NOTIFIER_BOOTING_DONE, + DEVICE_NOTIFIER_LCD, + DEVICE_NOTIFIER_TA, + DEVICE_NOTIFIER_LOWBAT, + DEVICE_NOTIFIER_FULLBAT, + DEVICE_NOTIFIER_TOUCH_HARDKEY, + DEVICE_NOTIFIER_PROCESS_TERMINATED, + DEVICE_NOTIFIER_POWER_SUPPLY, + DEVICE_NOTIFIER_BATTERY_HEALTH, + DEVICE_NOTIFIER_BATTERY_PRESENT, + DEVICE_NOTIFIER_BATTERY_OVP, + DEVICE_NOTIFIER_BATTERY_CHARGING, + DEVICE_NOTIFIER_DISPLAY_LOCK, + DEVICE_NOTIFIER_POWER_RESUME, + DEVICE_NOTIFIER_POWEROFF, + DEVICE_NOTIFIER_POWEROFF_HAPTIC, + DEVICE_NOTIFIER_PMQOS_OOM, + DEVICE_NOTIFIER_PROCESS_BACKGROUND, + DEVICE_NOTIFIER_PROCESS_FOREGROUND, + DEVICE_NOTIFIER_USB_TETHERING_MODE, + DEVICE_NOTIFIER_MAX, +}; + +/* + * This is for internal callback method. + */ +int register_notifier(enum device_notifier_type status, int (*func)(void *data)); +int unregister_notifier(enum device_notifier_type status, int (*func)(void *data)); +void device_notify(enum device_notifier_type status, void *value); + +#endif /* __DEVICE_NOTIFIER_H__ */ diff --git a/src/core/devices.c b/src/core/devices.c new file mode 100644 index 0000000..3ff5a8d --- /dev/null +++ b/src/core/devices.c @@ -0,0 +1,116 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +#include "log.h" +#include "list.h" +#include "common.h" +#include "devices.h" +#include "edbus-handler.h" + +static const struct device_ops default_ops = { + .name = "default-ops", +}; + +static dd_list *dev_head; + +void add_device(const struct device_ops *dev) +{ + if (dev->priority == DEVICE_PRIORITY_HIGH) + DD_LIST_PREPEND(dev_head, dev); + else + DD_LIST_APPEND(dev_head, dev); +} + +void remove_device(const struct device_ops *dev) +{ + DD_LIST_REMOVE(dev_head, dev); +} + +const struct device_ops *find_device(const char *name) +{ + dd_list *elem; + const struct device_ops *dev; + + DD_LIST_FOREACH(dev_head, elem, dev) { + if (!strcmp(dev->name, name)) + return dev; + } + + dev = &default_ops; + return dev; +} + +int check_default(const struct device_ops *dev) +{ + return (dev == &default_ops); +} + +static DBusMessage *edbus_device_list(E_DBus_Object *obj, DBusMessage *msg) +{ + dd_list *elem; + const struct device_ops *dev; + + _I("device list!"); + DD_LIST_FOREACH(dev_head, elem, dev) + _I("%s", dev->name); + + return dbus_message_new_method_return(msg); +} + +static const struct edbus_method edbus_methods[] = { + { "DeviceList", NULL, NULL, edbus_device_list }, +}; + +void devices_init(void *data) +{ + dd_list *elem, *elem_n; + const struct device_ops *dev; + int ret; + + DD_LIST_FOREACH_SAFE(dev_head, elem, elem_n, dev) { + if (dev->probe && dev->probe(data) != 0) { + _E("[%s] probe fail", dev->name); + DD_LIST_REMOVE(dev_head, dev); + continue; + } + + _D("[%s] initialize", dev->name); + if (dev->init) + dev->init(data); + } + + ret = register_edbus_method(DEVICED_PATH_CORE, + edbus_methods, ARRAY_SIZE(edbus_methods)); + if (ret < 0) + _E("Failed to register edbus method! %d", ret); +} + +void devices_exit(void *data) +{ + dd_list *elem; + const struct device_ops *dev; + + DD_LIST_FOREACH(dev_head, elem, dev) { + _D("[%s] deinitialize", dev->name); + if (dev->exit) + dev->exit(data); + } +} diff --git a/src/core/devices.h b/src/core/devices.h new file mode 100644 index 0000000..c49499a --- /dev/null +++ b/src/core/devices.h @@ -0,0 +1,138 @@ +/* + * deviced + * + * 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. + */ + + +#ifndef __DEVICES_H__ +#define __DEVICES_H__ + +#include +#include "common.h" + +enum device_priority { + DEVICE_PRIORITY_NORMAL = 0, + DEVICE_PRIORITY_HIGH, +}; + +enum device_flags { + NORMAL_MODE = 0x00000001, + CORE_LOGIC_MODE = 0x00010000, + TOUCH_SCREEN_OFF_MODE = 0x00020000, + LCD_PANEL_OFF_MODE = 0x00040000, + LCD_PHASED_TRANSIT_MODE = 0x00080000, + LCD_ON_BY_GESTURE = 0x00100000, + LCD_ON_BY_POWER_KEY = 0x00200000, + LCD_ON_BY_EVENT = 0x00400000, + LCD_ON_BY_TOUCH = 0x00800000, + LCD_OFF_BY_POWER_KEY = 0x01000000, + LCD_OFF_BY_TIMEOUT = 0x02000000, + LCD_OFF_BY_EVENT = 0x04000000, + LCD_OFF_LATE_MODE = 0x08000000, +}; + +struct device_ops { + enum device_priority priority; + char *name; + int (*probe) (void *data); + void (*init) (void *data); + void (*exit) (void *data); + int (*start) (enum device_flags flags); + int (*stop) (enum device_flags flags); + int (*status) (void); + int (*execute) (void *data); +}; + +enum device_ops_status { + DEVICE_OPS_STATUS_UNINIT, + DEVICE_OPS_STATUS_START, + DEVICE_OPS_STATUS_STOP, + DEVICE_OPS_STATUS_MAX, +}; + +void devices_init(void *data); +void devices_exit(void *data); + +static inline int device_start(const struct device_ops *dev) +{ + if (dev && dev->start) + return dev->start(NORMAL_MODE); + + return -EINVAL; +} + +static inline int device_stop(const struct device_ops *dev) +{ + if (dev && dev->stop) + return dev->stop(NORMAL_MODE); + + return -EINVAL; +} + +static inline int device_exit(const struct device_ops *dev, void *data) +{ + if (dev && dev->exit) { + dev->exit(data); + return 0; + } + + return -EINVAL; +} + +static inline int device_execute(const struct device_ops *dev, void *data) +{ + if (dev && dev->execute) + return dev->execute(data); + + return -EINVAL; +} + +static inline int device_get_status(const struct device_ops *dev) +{ + if (dev && dev->status) + return dev->status(); + + return -EINVAL; +} + +#define DEVICE_OPS_REGISTER(dev) \ +static void __CONSTRUCTOR__ module_init(void) \ +{ \ + add_device(dev); \ +} \ +static void __DESTRUCTOR__ module_exit(void) \ +{ \ + remove_device(dev); \ +} + +void add_device(const struct device_ops *dev); +void remove_device(const struct device_ops *dev); + +const struct device_ops *find_device(const char *name); +int check_default(const struct device_ops *dev); + +#define NOT_SUPPORT_OPS(dev) \ + ((check_default(dev))? 1 : 0) + +#define FIND_DEVICE_INT(dev, name) do { \ + if (!dev) dev = find_device(name); if(check_default(dev)) return -ENODEV; \ +} while(0) + +#define FIND_DEVICE_VOID(dev, name) do { \ + if (!dev) dev = find_device(name); if(check_default(dev)) return; \ +} while(0) + +#endif diff --git a/src/core/edbus-handler.c b/src/core/edbus-handler.c new file mode 100644 index 0000000..5c00b89 --- /dev/null +++ b/src/core/edbus-handler.c @@ -0,0 +1,1101 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +#include "core/log.h" +#include "core/edbus-handler.h" +#include "core/common.h" +#include "core/device-idler.h" +#include "core/device-notifier.h" +#include "core/list.h" + +#define EDBUS_INIT_RETRY_COUNT 5 +#define NAME_OWNER_CHANGED "NameOwnerChanged" +#define NAME_OWNER_MATCH "type='signal',sender='org.freedesktop.DBus'," \ + "path='/org/freedesktop/DBus',interface='org.freedesktop.DBus'," \ + "member='NameOwnerChanged',arg0='%s'" + +/* -1 is a default timeout value, it's converted to 25*1000 internally. */ +#define DBUS_REPLY_TIMEOUT (-1) +#define RETRY_MAX 5 + +struct edbus_list { + char *signal_name; + E_DBus_Signal_Handler *handler; +}; + +static struct edbus_object { + char *path; + char *interface; + E_DBus_Object *obj; + E_DBus_Interface *iface; +} edbus_objects[] = { + { DEVICED_PATH_CORE , DEVICED_INTERFACE_CORE , NULL, NULL }, + { DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY, NULL, NULL }, + { DEVICED_PATH_POWER , DEVICED_INTERFACE_POWER , NULL, NULL }, + { DEVICED_PATH_STORAGE, DEVICED_INTERFACE_STORAGE, NULL, NULL }, + { DEVICED_PATH_PROCESS, DEVICED_INTERFACE_PROCESS, NULL, NULL }, + { DEVICED_PATH_KEY , DEVICED_INTERFACE_KEY , NULL, NULL }, + { DEVICED_PATH_SYSNOTI, DEVICED_INTERFACE_SYSNOTI, NULL, NULL }, + { DEVICED_PATH_USB , DEVICED_INTERFACE_USB , NULL, NULL }, + /* Add new object & interface here*/ +}; + +struct watch_func_info { + bool deleted; + void (*func)(const char *sender, void *data); + void *data; +}; + +struct watch_info { + bool deleted; + char *sender; + dd_list *func_list; +}; + +static dd_list *edbus_object_list; +static dd_list *edbus_owner_list; +static dd_list *edbus_handler_list; +static dd_list *edbus_watch_list; +static int edbus_init_val; +static DBusConnection *conn; +static DBusConnection *conn_block; +static E_DBus_Connection *edbus_conn; +static E_DBus_Connection *edbus_conn_block; +static DBusPendingCall *edbus_request_name; + +static DBusHandlerResult message_filter(DBusConnection *connection, + DBusMessage *message, void *data); + +DBusConnection *get_block_dbus_connection(void) +{ + return conn_block; +} + +E_DBus_Object *register_edbus_object(const char *object_path, void *data) +{ + E_DBus_Object *object; + + if (!object_path) { + _E("invalid parameter"); + return NULL; + } + + object = e_dbus_object_add(edbus_conn, object_path, data); + if (!object) { + _E("fail to add object for %s", object_path); + return NULL; + } + + return object; +} + +E_DBus_Object *register_block_edbus_object(const char *object_path, void *data) +{ + E_DBus_Object *object; + + if (!object_path) { + _E("invalid parameter"); + return NULL; + } + + object = e_dbus_object_add(edbus_conn_block, object_path, data); + if (!object) { + _E("fail to add object for %s", object_path); + return NULL; + } + + return object; +} + +void unregister_edbus_object(E_DBus_Object *object) +{ + if (!object) + return; + + e_dbus_object_free(object); +} + +static int register_edbus_interface(struct edbus_object *object) +{ + if (!object) { + _E("object is invalid value!"); + return -1; + } + + object->obj = e_dbus_object_add(edbus_conn, object->path, NULL); + if (!object->obj) { + _E("fail to add edbus obj"); + return -1; + } + + object->iface = e_dbus_interface_new(object->interface); + if (!object->iface) { + _E("fail to add edbus interface"); + return -1; + } + + e_dbus_object_interface_attach(object->obj, object->iface); + + return 0; +} + +static int register_block_edbus_interface(struct edbus_object *object) +{ + if (!object) { + _E("object is invalid value!"); + return -1; + } + + object->obj = e_dbus_object_add(edbus_conn_block, object->path, NULL); + if (!object->obj) { + _E("fail to add edbus obj"); + return -1; + } + + object->iface = e_dbus_interface_new(object->interface); + if (!object->iface) { + _E("fail to add edbus interface"); + return -1; + } + + e_dbus_object_interface_attach(object->obj, object->iface); + + return 0; +} + +E_DBus_Interface *get_edbus_interface(const char *path) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(edbus_objects); i++) + if (!strcmp(path, edbus_objects[i].path)) + return edbus_objects[i].iface; + + return NULL; +} + +pid_t get_edbus_sender_pid(DBusMessage *msg) +{ + const char *sender; + DBusMessage *send_msg; + DBusPendingCall *pending; + DBusMessageIter iter; + int ret; + pid_t pid; + + if (!msg) { + _E("invalid argument!"); + return -1; + } + + sender = dbus_message_get_sender(msg); + if (!sender) { + _E("invalid sender!"); + return -1; + } + + send_msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixProcessID"); + if (!send_msg) { + _E("invalid send msg!"); + return -1; + } + + ret = dbus_message_append_args(send_msg, DBUS_TYPE_STRING, + &sender, DBUS_TYPE_INVALID); + if (!ret) { + _E("fail to append args!"); + dbus_message_unref(send_msg); + return -1; + } + + pending = e_dbus_message_send(edbus_conn, send_msg, NULL, -1, NULL); + if (!pending) { + _E("pending is null!"); + dbus_message_unref(send_msg); + return -1; + } + + dbus_message_unref(send_msg); + + /* block until reply is received */ + dbus_pending_call_block(pending); + + msg = dbus_pending_call_steal_reply(pending); + dbus_pending_call_unref(pending); + if (!msg) { + _E("reply msg is null!"); + return -1; + } + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_get_basic(&iter, &pid); + dbus_message_unref(msg); + + return pid; +} + +static void unregister_edbus_signal_handle(void) +{ + dd_list *tmp, *next; + struct edbus_list *entry; + + DD_LIST_FOREACH_SAFE(edbus_handler_list, tmp, next, entry) { + if (!entry->handler) + continue; + e_dbus_signal_handler_del(edbus_conn, entry->handler); + DD_LIST_REMOVE(edbus_handler_list, entry); + free(entry->signal_name); + free(entry); + } +} + +int register_edbus_signal_handler(const char *path, const char *interface, + const char *name, E_DBus_Signal_Cb cb) +{ + dd_list *tmp; + struct edbus_list *entry; + E_DBus_Signal_Handler *handler; + + DD_LIST_FOREACH(edbus_handler_list, tmp, entry) { + if (strncmp(entry->signal_name, name, strlen(name)) == 0) + return -EEXIST; + } + + handler = e_dbus_signal_handler_add(edbus_conn, NULL, path, + interface, name, cb, NULL); + + if (!handler) { + _E("fail to add edbus handler"); + return -ENOMEM; + } + + entry = malloc(sizeof(struct edbus_list)); + + if (!entry) { + e_dbus_signal_handler_del(edbus_conn, handler); + _E("Malloc failed"); + return -ENOMEM; + } + + entry->signal_name = strndup(name, strlen(name)); + + if (!entry->signal_name) { + _E("Malloc failed"); + e_dbus_signal_handler_del(edbus_conn, handler); + free(entry); + return -ENOMEM; + } + + entry->handler = handler; + DD_LIST_PREPEND(edbus_handler_list, entry); + if (!edbus_handler_list) { + _E("dd_list_prepend failed"); + e_dbus_signal_handler_del(edbus_conn, handler); + free(entry->signal_name); + free(entry); + return -ENOMEM; + } + return 0; +} + +int unregister_edbus_signal_handler(const char *path, const char *interface, + const char *name) +{ + dd_list *tmp, *next; + struct edbus_list *entry; + + DD_LIST_FOREACH_SAFE(edbus_handler_list, tmp, next, entry) { + if (strncmp(entry->signal_name, name, strlen(name) + 1) == 0) { + e_dbus_signal_handler_del(edbus_conn, entry->handler); + DD_LIST_REMOVE(edbus_handler_list, entry); + free(entry->signal_name); + free(entry); + return 0; + } + } + + return -1; +} + +int broadcast_edbus_signal(const char *path, const char *interface, + const char *name, const char *sig, char *param[]) +{ + DBusMessage *msg; + DBusMessageIter iter; + int r; + + msg = dbus_message_new_signal(path, interface, name); + if (!msg) { + _E("fail to allocate new %s.%s signal", interface, name); + return -EPERM; + } + + dbus_message_iter_init_append(msg, &iter); + r = append_variant(&iter, sig, param); + if (r < 0) { + _E("append_variant error(%d)", r); + return -EPERM; + } + + r = dbus_connection_send(conn, msg, NULL); + dbus_message_unref(msg); + + if (r != TRUE) { + _E("dbus_connection_send error(%s:%s-%s)", + path, interface, name); + return -ECOMM; + } + + return 0; +} + +int broadcast_block_edbus_signal(const char *path, const char *interface, + const char *name, const char *sig, char *param[]) +{ + DBusMessage *msg; + DBusMessageIter iter; + int r; + + msg = dbus_message_new_signal(path, interface, name); + if (!msg) { + _E("fail to allocate new %s.%s signal", interface, name); + return -EPERM; + } + + dbus_message_iter_init_append(msg, &iter); + r = append_variant(&iter, sig, param); + if (r < 0) { + _E("append_variant error(%d)", r); + return -EPERM; + } + + r = dbus_connection_send(conn_block, msg, NULL); + dbus_message_unref(msg); + + if (r != TRUE) { + _E("dbus_connection_send error(%s:%s-%s)", + path, interface, name); + return -ECOMM; + } + + return 0; +} + +static void print_watch_item(void) +{ + struct watch_info *watch; + struct watch_func_info *finfo; + dd_list *n; + dd_list *e; + + DD_LIST_FOREACH(edbus_watch_list, n, watch) { + _D("watch sender : %s, deleted : %d", + watch->sender, watch->deleted); + DD_LIST_FOREACH(watch->func_list, e, finfo) + _D("\tfunc : %p, deleted : %d", + finfo->func, finfo->deleted); + } +} + +static bool get_valid_watch_item(void) +{ + struct watch_info *watch; + dd_list *elem; + + DD_LIST_FOREACH(edbus_watch_list, elem, watch) { + if (!watch->deleted) + return true; + } + + return false; +} + +static void watch_idler_cb(void *data) +{ + struct watch_info *watch; + struct watch_func_info *finfo; + dd_list *n; + dd_list *next; + dd_list *elem; + dd_list *enext; + char match[256]; + + DD_LIST_FOREACH_SAFE(edbus_watch_list, n, next, watch) { + if (!watch->deleted) + continue; + + /* remove dbus match */ + snprintf(match, sizeof(match), NAME_OWNER_MATCH, watch->sender); + dbus_bus_remove_match(conn, match, NULL); + + _I("%s is not watched by dbus!", watch->sender); + + /* remove watch func list */ + DD_LIST_FOREACH_SAFE(watch->func_list, elem, enext, finfo) + free(finfo); + + /* remove watch item */ + DD_LIST_FREE_LIST(watch->func_list); + DD_LIST_REMOVE_LIST(edbus_watch_list, n); + free(watch->sender); + free(watch); + } + + /* if the last request, remove message filter */ + if (!get_valid_watch_item()) + dbus_connection_remove_filter(conn, message_filter, NULL); +} + +static DBusHandlerResult message_filter(DBusConnection *connection, + DBusMessage *message, void *data) +{ + int ret; + const char *iface, *member; + const char *sender; + struct watch_info *watch; + struct watch_func_info *finfo; + dd_list *n; + int len; + + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + iface = dbus_message_get_interface(message); + member = dbus_message_get_member(message); + + if (strncmp(iface, DBUS_INTERFACE_DBUS, + sizeof(DBUS_INTERFACE_DBUS))) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (strncmp(member, NAME_OWNER_CHANGED, + sizeof(NAME_OWNER_CHANGED))) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + ret = dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &sender, + DBUS_TYPE_INVALID); + if (!ret) { + _E("no message"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + len = strlen(sender) + 1; + DD_LIST_FOREACH(edbus_watch_list, n, watch) { + if (!watch->deleted && + !strncmp(watch->sender, sender, len)) + break; + } + + if (!watch) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + DD_LIST_FOREACH(watch->func_list, n, finfo) { + if (!finfo->deleted && + finfo->func) + finfo->func(watch->sender, finfo->data); + } + + /* no interest in this item anymore */ + watch->deleted = true; + + print_watch_item(); + add_idle_request(watch_idler_cb, NULL); + return DBUS_HANDLER_RESULT_HANDLED; +} + +static struct watch_info *get_matched_watch_item(const char *sender) +{ + int len; + dd_list *n; + struct watch_info *watch; + + if (!sender) + return NULL; + + len = strlen(sender) + 1; + /* check the sender&type is already registered */ + DD_LIST_FOREACH(edbus_watch_list, n, watch) { + if (!watch->deleted && + !strncmp(watch->sender, sender, len)) + return watch; + } + + return NULL; +} + +static struct watch_info *add_watch_item(const char *sender) +{ + DBusError err; + struct watch_info *watch; + char match[256]; + int ret; + + if (!sender) + return NULL; + + watch = calloc(1, sizeof(struct watch_info)); + if (!watch) + return NULL; + + watch->sender = strdup(sender); + if (!watch->sender) + goto out; + + dbus_error_init(&err); + /* add name owner changed match string */ + snprintf(match, sizeof(match), NAME_OWNER_MATCH, watch->sender); + dbus_bus_add_match(conn, match, &err); + + if (dbus_error_is_set(&err)) { + _E("fail to add match for %s [%s:%s]", + sender, err.name, err.message); + dbus_error_free(&err); + goto out; + } + + /* if the first request, add message filter */ + if (!get_valid_watch_item()) { + ret = dbus_connection_add_filter(conn, + message_filter, NULL, NULL); + if (!ret) { + _E("fail to add message filter!"); + dbus_bus_remove_match(conn, match, NULL); + goto out; + } + _I("success to add message filter!"); + } + + /* Add watch to watch list */ + DD_LIST_APPEND(edbus_watch_list, watch); + _I("%s is watched by dbus!", sender); + return watch; + +out: + if (watch) { + free(watch->sender); + free(watch); + } + + return NULL; +} + +int register_edbus_watch(const char *sender, + void (*func)(const char *sender, void *data), void *data) +{ + struct watch_info *watch; + struct watch_func_info *finfo; + dd_list *elem; + bool isnew = false; + + if (!sender || !func) { + _E("invalid argument : sender(NULL) || func(NULL)"); + return -EINVAL; + } + + watch = get_matched_watch_item(sender); + if (!watch) { + /* create new watch item */ + watch = add_watch_item(sender); + if (!watch) { + _E("fail to add watch item"); + return -EPERM; + } + isnew = true; + } + + /* find the same callback */ + DD_LIST_FOREACH(watch->func_list, elem, finfo) { + if (finfo->func == func) { + _E("there is already the same callback"); + goto out; + } + } + + finfo = calloc(1, sizeof(struct watch_func_info)); + if (!finfo) { + _E("fail to allocate watch func info"); + goto out; + } + + finfo->func = func; + finfo->data = data; + + /* add callback function to the watch list */ + DD_LIST_APPEND(watch->func_list, finfo); + + _I("register watch func(%p) of %s", func, sender); + return 0; +out: + if (isnew) + watch->deleted = true; + + return -EPERM; +} + +int unregister_edbus_watch(const char *sender, + void (*func)(const char *sender, void *data)) +{ + struct watch_info *watch; + struct watch_func_info *finfo; + dd_list *elem; + bool matched = false; + + if (!sender || !func) { + _E("invalid argument : sender(NULL) || func(NULL)"); + return -EINVAL; + } + + watch = get_matched_watch_item(sender); + if (!watch) { + _E("fail to get matched watch item"); + return -ENODEV; + } + + /* check the no interest function */ + DD_LIST_FOREACH(watch->func_list, elem, finfo) { + if (finfo->func == func) + finfo->deleted = true; + if (!finfo->deleted) + matched = true; + } + + /* if it is the last item */ + if (!matched) + watch->deleted = true; + + _I("unregister watch func(%p) of %s", func, sender); + return 0; +} + +static void unregister_edbus_watch_all(void) +{ + dd_list *n, *next; + struct watch_info *watch; + + DD_LIST_FOREACH_SAFE(edbus_watch_list, n, next, watch) + watch->deleted = true; + + add_idle_request(watch_idler_cb, NULL); +} + +static int register_method(E_DBus_Interface *iface, + const struct edbus_method *edbus_methods, int size) +{ + int ret; + int i; + + assert(iface); + assert(edbus_methods); + + for (i = 0; i < size; i++) { + ret = e_dbus_interface_method_add(iface, + edbus_methods[i].member, + edbus_methods[i].signature, + edbus_methods[i].reply_signature, + edbus_methods[i].func); + if (!ret) { + _E("fail to add method %s!", edbus_methods[i].member); + return -EINVAL; + } + } + + return 0; +} + +int register_edbus_interface_and_method(const char *path, + const char *interface, + const struct edbus_method *edbus_methods, int size) +{ + struct edbus_object *obj; + dd_list *elem; + int ret; + + if (!path || !interface || !edbus_methods || size < 1) { + _E("invalid parameter"); + return -EINVAL; + } + + /* find matched obj */ + DD_LIST_FOREACH(edbus_object_list, elem, obj) { + if (strncmp(obj->path, path, strlen(obj->path)) == 0 && + strncmp(obj->interface, interface, strlen(obj->interface)) == 0) { + _I("found matched item : obj(%p)", obj); + break; + } + } + + /* if there is no matched obj */ + if (!obj) { + obj = malloc(sizeof(struct edbus_object)); + if (!obj) { + _E("fail to allocate %s interface", path); + return -ENOMEM; + } + + obj->path = strdup(path); + obj->interface = strdup(interface); + + ret = register_edbus_interface(obj); + if (ret < 0) { + _E("fail to register %s interface(%d)", obj->path, ret); + free(obj->path); + free(obj->interface); + free(obj); + return ret; + } + + DD_LIST_APPEND(edbus_object_list, obj); + } + + ret = register_method(obj->iface, edbus_methods, size); + if (ret < 0) { + _E("fail to register %s method(%d)", obj->path, ret); + return ret; + } + + return 0; +} + +int register_block_edbus_interface_and_method(const char *path, + const char *interface, + const struct edbus_method *edbus_methods, int size) +{ + struct edbus_object *obj; + dd_list *elem; + int ret; + + if (!path || !interface || !edbus_methods || size < 1) { + _E("invalid parameter"); + return -EINVAL; + } + + /* find matched obj */ + DD_LIST_FOREACH(edbus_object_list, elem, obj) { + if (strncmp(obj->path, path, strlen(obj->path)) == 0 && + strncmp(obj->interface, interface, strlen(obj->interface)) == 0) { + _I("found matched item : obj(%p)", obj); + break; + } + } + + /* if there is no matched obj */ + if (!obj) { + obj = malloc(sizeof(struct edbus_object)); + if (!obj) { + _E("fail to allocate %s interface", path); + return -ENOMEM; + } + + obj->path = strdup(path); + obj->interface = strdup(interface); + + ret = register_block_edbus_interface(obj); + if (ret < 0) { + _E("fail to register %s interface(%d)", obj->path, ret); + free(obj->path); + free(obj->interface); + free(obj); + return ret; + } + + DD_LIST_APPEND(edbus_object_list, obj); + } + + ret = register_method(obj->iface, edbus_methods, size); + if (ret < 0) { + _E("fail to register %s method(%d)", obj->path, ret); + return ret; + } + + return 0; +} + +int unregister_edbus_interface_all(void) +{ + struct edbus_object *obj; + dd_list *elem, *n; + + DD_LIST_FOREACH_SAFE(edbus_object_list, elem, n, obj) { + DD_LIST_REMOVE(edbus_object_list, obj); + free(obj->path); + free(obj->interface); + free(obj); + } + + return 0; +} + +int register_edbus_method(const char *path, const struct edbus_method *edbus_methods, int size) +{ + E_DBus_Interface *iface; + int ret; + + if (!path || !edbus_methods || size < 1) { + _E("invalid parameter"); + return -EINVAL; + } + + iface = get_edbus_interface(path); + if (!iface) { + _E("fail to get edbus interface!"); + return -ENODEV; + } + + ret = register_method(iface, edbus_methods, size); + if (ret < 0) { + _E("fail to register %s method(%d)", path, ret); + return ret; + } + + return 0; +} + +static void request_name_cb(void *data, DBusMessage *msg, DBusError *error) +{ + DBusError err; + unsigned int val; + int r; + + if (!msg) { + _D("invalid DBusMessage!"); + return; + } + + dbus_error_init(&err); + r = dbus_message_get_args(msg, &err, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID); + if (!r) { + _E("no message : [%s:%s]", err.name, err.message); + dbus_error_free(&err); + return; + } + + _I("Request Name reply : %d", val); +} + +static void check_owner_name(void) +{ + DBusError err; + DBusMessage *msg; + DBusMessageIter iter; + char *pa[1]; + char exe_name[PATH_MAX]; + char *entry; + dd_list *n; + int pid; + + DD_LIST_FOREACH(edbus_owner_list, n, entry) { + pa[0] = entry; + msg = dbus_method_sync_with_reply(E_DBUS_FDO_BUS, + E_DBUS_FDO_PATH, + E_DBUS_FDO_INTERFACE, + "GetConnectionUnixProcessID", "s", pa); + + if (!msg) { + _E("invalid DBusMessage!"); + return; + } + + dbus_error_init(&err); + dbus_message_iter_init(msg, &iter); + + dbus_message_iter_get_basic(&iter, &pid); + if (get_cmdline_name(pid, exe_name, PATH_MAX) != 0) + goto out; + _I("%s(%d)", exe_name, pid); + +out: + dbus_message_unref(msg); + dbus_error_free(&err); + } +} + +static void check_owner_list(void) +{ + DBusError err; + DBusMessage *msg; + DBusMessageIter array, item; + char *pa[1]; + char *name; + char *entry; + + pa[0] = DEVICED_BUS_NAME; + msg = dbus_method_sync_with_reply(E_DBUS_FDO_BUS, + E_DBUS_FDO_PATH, + E_DBUS_FDO_INTERFACE, + "ListQueuedOwners", "s", pa); + + if (!msg) { + _E("invalid DBusMessage!"); + return; + } + + dbus_error_init(&err); + dbus_message_iter_init(msg, &array); + + if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) + goto out; + dbus_message_iter_recurse(&array, &item); + while (dbus_message_iter_get_arg_type(&item) == DBUS_TYPE_STRING) { + dbus_message_iter_get_basic(&item, &name); + entry = strndup(name, strlen(name)); + DD_LIST_APPEND(edbus_owner_list, entry); + if (!edbus_owner_list) { + _E("append failed"); + free(entry); + goto out; + } + dbus_message_iter_next(&item); + } + +out: + dbus_message_unref(msg); + dbus_error_free(&err); +} + +void edbus_init(void *data) +{ + DBusError error; + int retry = 0; + int i, ret; + + dbus_threads_init_default(); + dbus_error_init(&error); + + do { + edbus_init_val = e_dbus_init(); + if (edbus_init_val) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to init edbus"); + return; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + retry = 0; + do { + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (conn) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to get dbus"); + goto out1; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + retry = 0; + do { + conn_block = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (conn_block) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to get dbus"); + goto out1; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + retry = 0; + do { + edbus_conn = e_dbus_connection_setup(conn); + if (edbus_conn) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to get edbus"); + goto out2; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + retry = 0; + do { + edbus_conn_block = e_dbus_connection_setup(conn_block); + if (edbus_conn) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to get edbus"); + goto out2; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + retry = 0; + do { + edbus_request_name = e_dbus_request_name(edbus_conn, DEVICED_BUS_NAME, + DBUS_NAME_FLAG_REPLACE_EXISTING, request_name_cb, NULL); + if (edbus_request_name) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to request edbus name"); + goto out3; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + retry = 0; + do { + edbus_request_name = e_dbus_request_name(edbus_conn_block, STORAGE_BUS_NAME, + DBUS_NAME_FLAG_REPLACE_EXISTING, request_name_cb, NULL); + if (edbus_request_name) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to request edbus name"); + goto out3; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + for (i = 0; i < ARRAY_SIZE(edbus_objects); i++) { + ret = register_edbus_interface(&edbus_objects[i]); + if (ret < 0) { + _E("fail to add obj & interface for %s", + edbus_objects[i].interface); + return; + } + _D("add new obj for %s", edbus_objects[i].interface); + } + check_owner_list(); + check_owner_name(); + return; + +out3: + e_dbus_connection_close(edbus_conn); + e_dbus_connection_close(edbus_conn_block); +out2: + dbus_connection_set_exit_on_disconnect(conn, FALSE); + dbus_connection_set_exit_on_disconnect(conn_block, FALSE); +out1: + e_dbus_shutdown(); +} + +void edbus_exit(void *data) +{ + unregister_edbus_signal_handle(); + unregister_edbus_watch_all(); + unregister_edbus_interface_all(); + e_dbus_connection_close(edbus_conn); + e_dbus_connection_close(edbus_conn_block); + e_dbus_shutdown(); +} diff --git a/src/core/edbus-handler.h b/src/core/edbus-handler.h new file mode 100644 index 0000000..17eb956 --- /dev/null +++ b/src/core/edbus-handler.h @@ -0,0 +1,73 @@ +/* + * deviced + * + * 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. + */ + + +#ifndef __EDBUS_HANDLE_H__ +#define __EDBUS_HANDLE_H__ + +#include +#include +#include "shared/dbus.h" + +struct edbus_method { + const char *member; + const char *signature; + const char *reply_signature; + E_DBus_Method_Cb func; +}; + +static inline DBusMessage *make_reply_message(DBusMessage *msg, int ret) +{ + DBusMessageIter iter; + DBusMessage *reply; + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +DBusConnection *get_block_dbus_connection(void); +E_DBus_Object *register_edbus_object(const char *object_path, void *data); +E_DBus_Object *register_block_edbus_object(const char *object_path, void *data); +void unregister_edbus_object(E_DBus_Object *object); +int register_edbus_interface_and_method(const char *path, + const char *interface, + const struct edbus_method *edbus_methods, int size); +int register_block_edbus_interface_and_method(const char *path, + const char *interface, + const struct edbus_method *edbus_methods, int size); +int register_edbus_method(const char *path, const struct edbus_method *edbus_methods, int size); +int register_edbus_signal_handler(const char *path, const char *interface, + const char *name, E_DBus_Signal_Cb cb); +int unregister_edbus_signal_handler(const char *path, const char *interface, + const char *name); +E_DBus_Interface *get_edbus_interface(const char *path); +pid_t get_edbus_sender_pid(DBusMessage *msg); +int broadcast_edbus_signal(const char *path, const char *interface, + const char *name, const char *sig, char *param[]); +int broadcast_block_edbus_signal(const char *path, const char *interface, + const char *name, const char *sig, char *param[]); +int register_edbus_watch(const char *sender, + void (*func)(const char *sender, void *data), void *data); +int unregister_edbus_watch(const char *sender, + void (*func)(const char *sender, void *data)); + +void edbus_init(void *data); +void edbus_exit(void *data); + +#endif /* __EDBUS_HANDLE_H__ */ diff --git a/src/core/execute.c b/src/core/execute.c new file mode 100755 index 0000000..1996c4d --- /dev/null +++ b/src/core/execute.c @@ -0,0 +1,102 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" + +static int parent(pid_t pid) +{ + int status; + + /* wait for child */ + if (waitpid(pid, &status, 0) != -1) { + /* terminated normally */ + if (WIFEXITED(status)) { + _I("%d terminated by exit(%d)", pid, WEXITSTATUS(status)); + return WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) + _I("%d terminated by signal %d", pid, WTERMSIG(status)); + else if (WIFSTOPPED(status)) + _I("%d stopped by signal %d", pid, WSTOPSIG(status)); + } else + _I("%d waitpid() failed : %d", pid, errno); + + return -EAGAIN; +} + +static void child(int argc, const char *argv[]) +{ + int i, r; + + for (i = 0; i < _NSIG; ++i) + signal(i, SIG_DFL); + + r = execv(argv[0], (char **)argv); + if (r < 0) + exit(EXIT_FAILURE); +} + +int run_child(int argc, const char *argv[]) +{ + pid_t pid; + struct sigaction act, oldact; + int r = 0; + FILE *fp; + + if (!argv) + return -EINVAL; + + fp = fopen(argv[0], "r"); + if (fp == NULL) { + _E("fail %s (%d)", argv[0], errno); + return -errno; + } + fclose(fp); + + /* Use default signal handler */ + act.sa_handler = SIG_DFL; + act.sa_sigaction = NULL; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + + if (sigaction(SIGCHLD, &act, &oldact) < 0) + return -errno; + + pid = fork(); + if (pid < 0) { + _E("failed to fork"); + r = -errno; + } else if (pid == 0) { + child(argc, argv); + } else + r = parent(pid); + + if (sigaction(SIGCHLD, &oldact, NULL) < 0) + _E("failed to restore sigaction"); + + return r; +} diff --git a/src/core/launch.c b/src/core/launch.c new file mode 100644 index 0000000..f481357 --- /dev/null +++ b/src/core/launch.c @@ -0,0 +1,338 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "launch.h" +#include "common.h" + +#define MAX_ARGS 255 + +#define _S(str) ((str == NULL) ? "" : str) + +static int set_current_lang(void) +{ + char *lang; + int ret; + + lang = vconf_get_str(VCONFKEY_LANGSET); + if (lang == NULL) + return -1; + ret = setenv("LANG", lang, 1); + if (ret < 0) + return -1; + free(lang); + return 0; +} + + +static void prepare_exec(void) +{ + int i; + int maxfd; + + maxfd = getdtablesize(); + for (i = 3; i < maxfd; i++) + close(i); + + for (i = 0; i < _NSIG; i++) + signal(i, SIG_DFL); +} + +static int parse_cmd(const char *cmdline, char **argv, int max_args) +{ + const char *p; + char *buf, *bufp; + int nargs = 0; + int escape = 0, squote = 0, dquote = 0; + int bufsize; + + if (cmdline == NULL || cmdline[0] == '\0') + return -1; + bufsize = strlen(cmdline)+1; + bufp = buf = malloc(bufsize); + if (bufp == NULL || buf == NULL) + return -1; + memset(buf, 0, bufsize); + p = cmdline; + + while (*p) { + if (escape) { + *bufp++ = *p; + escape = 0; + } else { + switch (*p) { + case '\\': + escape = 1; + break; + case '"': + if (squote) + *bufp++ = *p; + else + dquote = !dquote; + break; + case '\'': + if (dquote) + *bufp++ = *p; + else + squote = !squote; + break; + case ' ': + if (!squote && !dquote) { + *bufp = '\0'; + if (nargs < max_args) + argv[nargs++] = strdup(buf); + bufp = buf; + break; + } + default: + *bufp++ = *p; + break; + } + } + p++; + } + + if (bufp != buf) { + *bufp = '\0'; + if (nargs < max_args) + argv[nargs++] = strdup(buf); + } + + argv[nargs++] = NULL; + + free(buf); + return nargs; +} + +int launch_app_with_nice(const char *file, char *const argv[], pid_t *pid, int _nice) +{ + int ret; + int _pid; + + if (file == NULL || access(file, X_OK) != 0) { + _E("launch app error: Invalid input"); + errno = EINVAL; + return -1; + } + + if (pid && (*pid > 0 && kill(*pid, 0) != -1)) + return *pid; + + _pid = fork(); + + if (_pid == -1) { + _E("fork error: %d", errno); + /* keep errno */ + return -1; + } + + if (_pid > 0) { /* parent */ + if (pid) + *pid = _pid; + return _pid; + } + + /* child */ + prepare_exec(); + + ret = nice(_nice); + + if (ret == -1 && errno != 0) + _E("nice error: %d", errno); + + ret = execvp(file, argv); + + /* If failed... */ + _E("exec. error: %s", errno); + return -2; +} + +int launch_app_cmd_with_nice(const char *cmdline, int _nice) +{ + int i; + int nargs; + int ret; + char *argv[MAX_ARGS + 1]; + + nargs = parse_cmd(cmdline, argv, MAX_ARGS + 1); + if (nargs == -1) { + _E("launch app error: Invalid input"); + errno = EINVAL; + return -1; + } + + ret = launch_app_with_nice(argv[0], argv, NULL, _nice); + + for (i = 0; i < nargs; i++) + free(argv[i]); + + return ret; +} + +int launch_app_cmd(const char *cmdline) +{ + return launch_app_cmd_with_nice(cmdline, 0); +} + +int launch_if_noexist(const char *execpath, const char *arg, ...) +{ + char *buf; + int pid; + int nice_value = 0; + int flag = 0; + int buf_size = -1; + va_list argptr; + + if (execpath == NULL) { + errno = EINVAL; + return -1; + } + pid = get_exec_pid(execpath); + if (pid > 0) + return pid; + + va_start(argptr, arg); + flag = va_arg(argptr, int); + + if (flag & LAUNCH_NICE) + nice_value = va_arg(argptr, int); + + va_end(argptr); + + set_current_lang(); + arg = _S(arg); + + buf_size = strlen(execpath) + strlen(arg) + 10; + buf = malloc(buf_size); + if (buf == NULL) { + /* Do something for not enought memory error */ + _E("Malloc failed"); + return -1; + } + + snprintf(buf, buf_size, "%s %s", execpath, arg); + pid = launch_app_cmd_with_nice(buf, nice_value); + if (pid == -2) + exit(EXIT_FAILURE); + free(buf); + + return pid; +} + +int launch_evenif_exist(const char *execpath, const char *arg, ...) +{ + char *buf; + int pid; + int nice_value = 0; + int flag = 0; + int buf_size = -1; + + va_list argptr; + + if (execpath == NULL) { + errno = EINVAL; + return -1; + } + + va_start(argptr, arg); + flag = va_arg(argptr, int); + + if (flag & LAUNCH_NICE) + nice_value = va_arg(argptr, int); + + va_end(argptr); + + set_current_lang(); + + arg = _S(arg); + + buf_size = strlen(execpath) + strlen(arg) + 10; + buf = malloc(buf_size); + if (buf == NULL) { + _E("Malloc failed"); + return -1; + } + + snprintf(buf, buf_size, "%s %s", execpath, arg); + pid = launch_app_cmd_with_nice(buf, nice_value); + if (pid == -2) + exit(EXIT_FAILURE); + free(buf); + + return pid; +} + +int launch_after_kill_if_exist(const char *execpath, const char *arg, ...) +{ + char *buf; + int pid; + int flag; + int buf_size; + int exist_pid; + va_list argptr; + int nice_value = 0; + + if (execpath == NULL) { + errno = EINVAL; + return -1; + } + + exist_pid = get_exec_pid(execpath); + if (exist_pid > 0) + kill(exist_pid, SIGTERM); + + va_start(argptr, arg); + flag = va_arg(argptr, int); + + if (flag & LAUNCH_NICE) + nice_value = va_arg(argptr, int); + + va_end(argptr); + + set_current_lang(); + + arg = _S(arg); + + buf_size = strlen(execpath) + strlen(arg) + 10; + buf = malloc(buf_size); + if (buf == NULL) { + /* Do something for not enought memory error */ + _E("Malloc Failed"); + return -1; + } + + snprintf(buf, buf_size, "%s %s", execpath, arg); + pid = launch_app_cmd_with_nice(buf, nice_value); + if (pid == -2) /* It means that the 'execvp' return -1 */ + exit(EXIT_FAILURE); + free(buf); + + return pid; + +} diff --git a/src/core/launch.h b/src/core/launch.h new file mode 100644 index 0000000..7c6d0dd --- /dev/null +++ b/src/core/launch.h @@ -0,0 +1,32 @@ +/* + * deviced + * + * 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. + */ + + +#ifndef __LAUNCH_H__ +#define __LAUNCH_H__ + +#define LAUNCH_NICE 0x0002 + +int launch_if_noexist(const char *execpath, const char *arg, ...); +int launch_evenif_exist(const char *execpath, const char *arg, ...); +int launch_after_kill_if_exist(const char *execpath, const char *arg, ...); + +int launch_app_cmd(const char *cmdline); +int launch_app_cmd_with_nice(const char *cmdline, int _nice); + +#endif /* __LAUNCH_H__ */ diff --git a/src/core/list.h b/src/core/list.h new file mode 100644 index 0000000..bac4bbd --- /dev/null +++ b/src/core/list.h @@ -0,0 +1,60 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LIST_H__ +#define __LIST_H__ + +#include +typedef GList dd_list; + +#define DD_LIST_PREPEND(a, b) \ + a = g_list_prepend(a, (gpointer)b) +#define DD_LIST_APPEND(a, b) \ + a = g_list_append(a, (gpointer)b) +#define DD_LIST_REMOVE(a, b) \ + a = g_list_remove(a, (gpointer)b) +#define DD_LIST_REMOVE_LIST(a, b) \ + a = g_list_delete_link(a, b) +#define DD_LIST_LENGTH(a) \ + g_list_length(a) +#define DD_LIST_NTH(a, b) \ + g_list_nth_data(a, b) +#define DD_LIST_FIND(a, b) \ + g_list_find(a, (gpointer)b) +#define DD_LIST_FREE_LIST(a) \ + g_list_free(a) +#define DD_LIST_FOREACH(head, elem, node) \ + for (elem = head, node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = elem->next, node = NULL) +#define DD_LIST_FOREACH_SAFE(head, elem, elem_next, node) \ + for (elem = head, elem_next = g_list_next(elem), node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = elem_next, elem_next = g_list_next(elem), node = NULL) +#define DD_LIST_REVERSE_FOREACH(head, elem, node) \ + for (elem = g_list_last(head), node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = g_list_previous(elem), node = NULL) +#define DD_LIST_REVERSE_FOREACH_SAFE(head, elem, elem_next, node) \ + for (elem = g_list_last(head), elem_next = g_list_previous(elem), node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = elem_next, elem_next = g_list_previous(elem), node = NULL) +#define DD_LIST_NEXT(a) \ + g_list_next(a) + +#endif diff --git a/src/core/log.c b/src/core/log.c new file mode 100644 index 0000000..86310cd --- /dev/null +++ b/src/core/log.c @@ -0,0 +1,39 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "log.h" + +#ifdef DEBUG +void __cyg_profile_func_enter(void *, void *) + __attribute__ ((no_instrument_function)); +void __cyg_profile_func_exit(void *, void *) + __attribute__ ((no_instrument_function)); + +int g_trace_depth = -2; + +void __cyg_profile_func_enter(void *func, void *caller) +{ + g_trace_depth++; +} + +void __cyg_profile_func_exit(void *func, void *caller) +{ + g_trace_depth--; +} +#endif diff --git a/src/core/log.h b/src/core/log.h new file mode 100644 index 0000000..9684f38 --- /dev/null +++ b/src/core/log.h @@ -0,0 +1,33 @@ +/* + * deviced + * + * 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. + */ + + +#ifndef __LOG_H__ +#define __LOG_H__ + +#ifdef ENABLE_DEVICED_DLOG +#define ENABLE_DLOG +#endif + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "DEVICED" +#include "shared/log-macro.h" +#endif diff --git a/src/core/main.c b/src/core/main.c new file mode 100644 index 0000000..f0ba440 --- /dev/null +++ b/src/core/main.c @@ -0,0 +1,85 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#include "display/core.h" +#include "log.h" +#include "common.h" +#include "edbus-handler.h" +#include "devices.h" +#include "shared/dbus.h" +#include "power/boot.h" +#include "device-notifier.h" + +#define PIDFILE_PATH "/var/run/.deviced.pid" + +static void writepid(char *pidpath) +{ + FILE *fp; + + fp = fopen(pidpath, "w"); + if (fp != NULL) { + fprintf(fp, "%d", getpid()); + fclose(fp); + } +} + +static void sig_quit(int signo) +{ + _D("received SIGTERM signal %d", signo); +} + +static void sig_usr1(int signo) +{ + _D("received SIGUSR1 signal %d, deviced'll be finished!", signo); + + ecore_main_loop_quit(); +} + +static int deviced_main(int argc, char **argv) +{ + int ret; + + edbus_init(NULL); + devices_init(NULL); + ret = booting_finished(); + if (ret == 1) { + _I("notify relaunch"); + device_notify(DEVICE_NOTIFIER_BOOTING_DONE, &ret); + } + signal(SIGTERM, sig_quit); + signal(SIGUSR1, sig_usr1); + + ecore_main_loop_begin(); + + devices_exit(NULL); + edbus_exit(NULL); + ecore_shutdown(); + return 0; +} + +int main(int argc, char **argv) +{ + writepid(PIDFILE_PATH); + ecore_init(); + return deviced_main(argc, argv); +} diff --git a/src/core/sig-handler.c b/src/core/sig-handler.c new file mode 100644 index 0000000..17b0dfc --- /dev/null +++ b/src/core/sig-handler.c @@ -0,0 +1,75 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#include "log.h" +#include "devices.h" +#include "common.h" + +static struct sigaction sig_child_old_act; +static struct sigaction sig_pipe_old_act; + +static void sig_child_handler(int signo, siginfo_t *info, void *data) +{ + pid_t pid; + int status; + + if (!info || signo != SIGCHLD) + return; + + pid = waitpid(info->si_pid, &status, 0); + if (pid == -1) { + _E("SIGCHLD received\n"); + return; + } + + _D("sig child actend call - %d\n", info->si_pid); +} + +static void sig_pipe_handler(int signo, siginfo_t *info, void *data) +{ + +} + +static void signal_init(void *data) +{ + struct sigaction sig_act; + + sig_act.sa_handler = NULL; + sig_act.sa_sigaction = sig_child_handler; + sig_act.sa_flags = SA_SIGINFO; + sigemptyset(&sig_act.sa_mask); + sigaction(SIGCHLD, &sig_act, &sig_child_old_act); + + sig_act.sa_handler = NULL; + sig_act.sa_sigaction = sig_pipe_handler; + sig_act.sa_flags = SA_SIGINFO; + sigemptyset(&sig_act.sa_mask); + sigaction(SIGPIPE, &sig_act, &sig_pipe_old_act); +} + +static const struct device_ops signal_device_ops = { + .name = "signal", + .init = signal_init, +}; + +DEVICE_OPS_REGISTER(&signal_device_ops) diff --git a/src/core/udev.c b/src/core/udev.c new file mode 100644 index 0000000..f11ea9b --- /dev/null +++ b/src/core/udev.c @@ -0,0 +1,356 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#include "log.h" +#include "device-notifier.h" +#include "devices.h" +#include "udev.h" +#include "list.h" +#include "edbus-handler.h" + +#define KERNEL "kernel" +#define UDEV "udev" + +#define UDEV_MONITOR_SIZE (10*1024) + +struct uevent_info { + struct udev_monitor *mon; + Ecore_Fd_Handler *fdh; + dd_list *event_list; +}; + +/* Uevent */ +static struct udev *udev; + +static struct uevent_info kevent; /* kernel */ +static struct uevent_info uevent; /* udev */ + +static Eina_Bool uevent_control_cb(void *data, Ecore_Fd_Handler *fd_handler) +{ + 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 ECORE_CALLBACK_RENEW; + + 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 ECORE_CALLBACK_RENEW; +} + +static int uevent_control_stop(struct uevent_info *info) +{ + struct udev_device *dev; + + if (!info) + return -EINVAL; + + if (info->fdh) { + ecore_main_fd_handler_del(info->fdh); + info->fdh = 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; + } + + info->fdh = ecore_main_fd_handler_add(fd, ECORE_FD_READ, + uevent_control_cb, info, NULL, NULL); + if (!info->fdh) { + _E("error ecore_main_fd_handler_add"); + 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 DBusMessage *dbus_udev_handler(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusError err; + DBusMessageIter iter; + DBusMessage *reply; + pid_t pid; + int ret = 0; + int argc; + char *type_str; + char *argv; + + dbus_error_init(&err); + + if (!dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &type_str, + DBUS_TYPE_INT32, &argc, + DBUS_TYPE_STRING, &argv, DBUS_TYPE_INVALID)) { + _E("there is no message"); + ret = -EINVAL; + goto out; + } + + if (argc < 0) { + _E("message is invalid!"); + ret = -EINVAL; + goto out; + } + + pid = get_edbus_sender_pid(msg); + if (kill(pid, 0) == -1) { + _E("%d process does not exist, dbus ignored!", pid); + ret = -ESRCH; + goto out; + } + + if (strncmp(argv, "start", strlen("start")) == 0) { + uevent_control_start(KERNEL, &kevent); + uevent_control_start(UDEV, &uevent); + } else if (strncmp(argv, "stop", strlen("stop")) == 0) { + uevent_control_stop(&kevent); + uevent_control_stop(&uevent); + } + +out: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + + return reply; +} + +static const struct edbus_method edbus_methods[] = { + { UDEV, "sis", "i", dbus_udev_handler }, +}; + +static int device_change_poweroff(void *data) +{ + uevent_control_stop(&kevent); + uevent_control_stop(&uevent); + return 0; +} + +static void udev_init(void *data) +{ + int ret; + + register_notifier(DEVICE_NOTIFIER_POWEROFF, device_change_poweroff); + + ret = register_edbus_method(DEVICED_PATH_SYSNOTI, + edbus_methods, ARRAY_SIZE(edbus_methods)); + if (ret < 0) + _E("fail to init edbus method(%d)", ret); + + 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"); +} + +static void udev_exit(void *data) +{ + unregister_notifier(DEVICE_NOTIFIER_POWEROFF, device_change_poweroff); +} + +static const struct device_ops udev_device_ops = { + .priority = DEVICE_PRIORITY_NORMAL, + .name = "udev", + .init = udev_init, + .exit = udev_exit, +}; + +DEVICE_OPS_REGISTER(&udev_device_ops) diff --git a/src/core/udev.h b/src/core/udev.h new file mode 100644 index 0000000..494e905 --- /dev/null +++ b/src/core/udev.h @@ -0,0 +1,90 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __UDEV_H__ +#define __UDEV_H__ + +#include + +#define UDEV_CHANGE "change" +#define UDEV_ADD "add" +#define UDEV_REMOVE "remove" + +#define UDEV_DEVPATH "DEVPATH" +#define UDEV_DEVTYPE "DEVTYPE" + +/* battery device */ +#define POWER_SUBSYSTEM "power_supply" +#define POWER_PATH "/sys/class/power_supply/battery" +#define POWER_SUPPLY_UEVENT POWER_PATH"/uevent" +#define CAPACITY "POWER_SUPPLY_CAPACITY" +#define CHARGE_HEALTH "POWER_SUPPLY_HEALTH" +#define CHARGE_PRESENT "POWER_SUPPLY_PRESENT" +#define CHARGE_NAME "POWER_SUPPLY_NAME" +#define CHARGE_STATUS "POWER_SUPPLY_STATUS" +#define CHARGE_ONLINE "POWER_SUPPLY_ONLINE" + +/* extcon */ +#define EXTCON_SUBSYSTEM "extcon" + +/* usb */ +#define USB_SUBSYSTEM "usb" +#define USB_INTERFACE_DEVTYPE "usb_interface" +#define USB_DEVICE_DEVTYPE "usb_device" + +/* block */ +#define BLOCK_SUBSYSTEM "block" +#define BLOCK_DEVTYPE_DISK "disk" +#define BLOCK_DEVTYPE_PARTITION "partition" + +/* power supply status */ +enum { + POWER_SUPPLY_STATUS_UNKNOWN = 0, + POWER_SUPPLY_STATUS_CHARGING, + POWER_SUPPLY_STATUS_DISCHARGING, + POWER_SUPPLY_STATUS_NOT_CHARGING, + POWER_SUPPLY_STATUS_FULL, +}; + +enum { + POWER_SUPPLY_TYPE_UNKNOWN = 0, + POWER_SUPPLY_TYPE_BATTERY, + POWER_SUPPLY_TYPE_UPS, + POWER_SUPPLY_TYPE_MAINS, + POWER_SUPPLY_TYPE_USB, +}; + +enum dock_type { + DOCK_NONE = 0, + DOCK_SOUND = 7, +}; + +struct uevent_handler { + char *subsystem; + void (*uevent_func)(struct udev_device *dev); + void *data; +}; + +int register_kernel_uevent_control(const struct uevent_handler *uh); +int unregister_kernel_uevent_control(const struct uevent_handler *uh); + +int register_udev_uevent_control(const struct uevent_handler *uh); +int unregister_udev_uevent_control(const struct uevent_handler *uh); + +#endif /* __UDEV_H__ */ diff --git a/src/devicectl/CMakeLists.txt b/src/devicectl/CMakeLists.txt new file mode 100755 index 0000000..a99104f --- /dev/null +++ b/src/devicectl/CMakeLists.txt @@ -0,0 +1,33 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(devicectl C) + +IF("$ENV{CFLAGS}" MATCHES "-DTIZEN_ENGINEER_MODE") + OPTION(USE_ENGINEER_MODE "Use Engineer mode" ON) +ENDIF() + +SET(SRCS + devicectl.c + usb.c +) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) + +INCLUDE(FindPkgConfig) +pkg_check_modules(pkgs REQUIRED dbus-1) + +FOREACH(flag ${pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -fno-omit-frame-pointer -finstrument-functions") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") + +ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"") +ADD_DEFINITIONS("-DFACTORYFS=\"$ENV{FACTORYFS}\"") +IF( $ENV{ARCH} MATCHES "arm" ) + ADD_DEFINITIONS("-DTARGET") +ENDIF() + +ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} shared) + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin) diff --git a/src/devicectl/devicectl.c b/src/devicectl/devicectl.c new file mode 100644 index 0000000..4a06f99 --- /dev/null +++ b/src/devicectl/devicectl.c @@ -0,0 +1,422 @@ +/* + * devicectl + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "usb.h" + +/* + * devicectl [device] [action] + * ex> devicectl display stop + * devicectl pass start + */ + +enum device_type { + DEVICE_CORE, + DEVICE_DISPLAY, + DEVICE_LED, + DEVICE_PASS, + DEVICE_USB, + DEVICE_EXTCON, + DEVICE_POWER, + DEVICE_USB_HOST_TEST, + DEVICE_MAX, + DEVICE_ALL, +}; +static enum device_type arg_id; + +static const struct device { + const enum device_type id; + const char *name; + const char *path; + const char *iface; +} devices[] = { + { DEVICE_CORE, "core", DEVICED_PATH_CORE, DEVICED_INTERFACE_CORE }, + { DEVICE_DISPLAY, "display", DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY }, + { DEVICE_LED, "led", DEVICED_PATH_LED, DEVICED_INTERFACE_LED }, + { DEVICE_PASS, "pass", DEVICED_PATH_PASS, DEVICED_INTERFACE_PASS }, + { DEVICE_USB, "usb", DEVICED_PATH_USB, DEVICED_INTERFACE_USB }, + { DEVICE_EXTCON, "extcon", DEVICED_PATH_EXTCON, DEVICED_INTERFACE_EXTCON }, + { DEVICE_POWER, "power", DEVICED_PATH_POWER, DEVICED_INTERFACE_POWER }, + { DEVICE_USB_HOST_TEST, "usb-host-test", DEVICED_PATH_USB_HOST_TEST, DEVICED_INTERFACE_USB_HOST_TEST}, +}; + +static int start_device(char **args) +{ + DBusMessage *msg; + + if (!args[1]) + return -EINVAL; + + printf("start %s device!\n", args[1]); + + msg = dbus_method_sync_with_reply(DEVICED_BUS_NAME, + devices[arg_id].path, devices[arg_id].iface, + "start", NULL, NULL); + if (!msg) + return -EBADMSG; + + dbus_message_unref(msg); + + return 0; +} + +static int stop_device(char **args) +{ + DBusMessage *msg; + + if (!args[1]) + return -EINVAL; + + printf("stop %s device!\n", args[1]); + + msg = dbus_method_sync_with_reply(DEVICED_BUS_NAME, + devices[arg_id].path, devices[arg_id].iface, + "stop", NULL, NULL); + if (!msg) + return -EBADMSG; + + dbus_message_unref(msg); + + return 0; +} + +static int dump_mode(char **args) +{ + int ret; + char *arr[1]; + + if (!args[1] || !args[2] || !args[3]) + return -EINVAL; + + printf("%s (%s %s)!\n", args[1], args[2], args[3]); + + arr[0] = args[3]; + ret = dbus_method_async(DEVICED_BUS_NAME, + devices[arg_id].path, devices[arg_id].iface, + "Dumpmode", "s", arr); + if (ret < 0) + printf("failed to set dump mode (%d)", ret); + + return ret; +} + +static int save_log(char **args) +{ + int ret; + + if (!args[1]) + return -EINVAL; + + printf("save log %s device!\n", args[1]); + + ret = dbus_method_async(DEVICED_BUS_NAME, + devices[arg_id].path, devices[arg_id].iface, + "SaveLog", NULL, NULL); + if (ret < 0) + printf("failed to save log (%d)", ret); + + return ret; +} + +static void get_pname(pid_t pid, char *pname) +{ + char buf[PATH_MAX]; + int cmdline, r; + + snprintf(buf, PATH_MAX, "/proc/%d/cmdline", pid); + cmdline = open(buf, O_RDONLY); + if (cmdline < 0) { + pname[0] = '\0'; + return; + } + + r = read(cmdline, pname, PATH_MAX); + if ((r >= 0) && (r < PATH_MAX)) + pname[r] = '\0'; + else + pname[0] = '\0'; + + close(cmdline); +} + +static int save_dbus_name(char **args) +{ + DBusMessage *msg; + char **list; + int ret, size, i; + pid_t pid; + char *arr[1]; + char pname[PATH_MAX]; + + if (!args[1]) + return -EINVAL; + + printf("save dbus name!\n"); + + msg = dbus_method_sync_with_reply(DBUS_BUS_NAME, + DBUS_OBJECT_PATH, DBUS_INTERFACE_NAME, + "ListNames", NULL, NULL); + if (!msg) { + printf("failed to get list names"); + return -EBADMSG; + } + + ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING, &list, &size, DBUS_TYPE_INVALID); + dbus_message_unref(msg); + if (!ret) { + printf("invalid list name arguments!"); + return -EINVAL; + } + + printf("%d connections\n", size); + + for (i = 0; i < size; i++) { + arr[0] = list[i]; + msg = dbus_method_sync_with_reply(DBUS_BUS_NAME, + DBUS_OBJECT_PATH, DBUS_INTERFACE_NAME, + "GetConnectionUnixProcessID", "s", arr); + if (!msg) + continue; + + ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, + &pid, DBUS_TYPE_INVALID); + dbus_message_unref(msg); + if (!ret) + continue; + + get_pname(pid, pname); + printf("%6d %6s %s\n", pid, list[i], pname); + + } + + return 0; +} + +static int device_list(char **args) +{ + DBusMessage *msg; + + if (!args[1]) + return -EINVAL; + + printf("print %s to dlog!\n", args[1]); + + msg = dbus_method_sync_with_reply(DEVICED_BUS_NAME, + devices[arg_id].path, devices[arg_id].iface, + "DeviceList", NULL, NULL); + if (!msg) + return -EBADMSG; + + dbus_message_unref(msg); + + return 0; +} + +static int set_usb_mode(char **args) +{ + return load_usb_mode(args[3]); +} + +static int unset_usb_mode(char **args) +{ + return unload_usb_mode(args[3]); +} + +static int enable_device(char **args) +{ + DBusMessage *msg; + char *arr[1]; + + if (!args[3]) + return -EINVAL; + + printf("enable %s device!\n", args[3]); + + arr[0] = args[3]; + + msg = dbus_method_sync_with_reply(DEVICED_BUS_NAME, + devices[arg_id].path, devices[arg_id].iface, + "enable", "s", arr); + if (!msg) + return -EBADMSG; + + dbus_message_unref(msg); + + return 0; +} + +static int disable_device(char **args) +{ + DBusMessage *msg; + char *arr[1]; + + if (!args[3]) + return -EINVAL; + + printf("disable %s device!\n", args[3]); + + arr[0] = args[3]; + + msg = dbus_method_sync_with_reply(DEVICED_BUS_NAME, + devices[arg_id].path, devices[arg_id].iface, + "disable", "s", arr); + if (!msg) + return -EBADMSG; + + dbus_message_unref(msg); + + return 0; +} + +static int power_operation(char **args, char *type) +{ + DBusMessage *msg; + char *arr[2]; + + if (!args[1] || !type) + return -EINVAL; + + printf("Power %s device!\n", args[2]); + + arr[0] = type; + arr[1] = "0"; + + msg = dbus_method_sync_with_reply(DEVICED_BUS_NAME, + devices[arg_id].path, devices[arg_id].iface, + "reboot", "si", arr); + if (!msg) + return -EBADMSG; + + dbus_message_unref(msg); + + return 0; +} +static int power_off(char **args) +{ + return power_operation(args, "poweroff"); +} + +static int power_reboot(char **args) +{ + return power_operation(args, "reboot"); +} + +static const struct action { + const enum device_type id; + const char *action; + const int argc; + int (* const func)(char **args); + const char *option; +} actions[] = { + { DEVICE_ALL, "start", 3, start_device, "" }, + { DEVICE_ALL, "stop", 3, stop_device, "" }, + { DEVICE_DISPLAY, "dumpmode", 4, dump_mode, "[on|off]" }, + { DEVICE_LED, "dumpmode", 4, dump_mode, "[on|off]" }, + { DEVICE_DISPLAY, "savelog", 3, save_log, "" }, + { DEVICE_USB, "set", 4, set_usb_mode, "[sdb|ssh]" }, + { DEVICE_USB, "unset", 4, unset_usb_mode, "[sdb|ssh]" }, + { DEVICE_CORE, "dbusname", 3, save_dbus_name, "" }, + { DEVICE_CORE, "devicelist", 3, device_list, "" }, + { DEVICE_EXTCON, "enable", 4, enable_device, "[USB|HEADPHONE|HDMI|DOCK]" }, + { DEVICE_EXTCON, "disable", 4, disable_device, "[USB|HEADPHONE|HDMI|DOCK]" }, + { DEVICE_POWER, "off", 3, power_off, "" }, + { DEVICE_POWER, "reboot", 3, power_reboot, "" }, +}; + +static inline void usage() +{ + printf("[usage] devicectl \n"); + printf("Please use option --help to check options\n"); +} + +static void help() +{ + int i; + + printf("[usage] devicectl