58a880770baf44b0bed0e155e372af109bd30619
[platform/core/system/deviced.git] / src / power / power-handler.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <unistd.h>
20 #include <time.h>
21 #include <limits.h>
22 #include <fcntl.h>
23 #include <dirent.h>
24 #include <vconf.h>
25 #include <assert.h>
26 #include <sys/syscall.h>
27 #include <linux/reboot.h>
28 #include <sys/time.h>
29 #include <mntent.h>
30 #include <sys/mount.h>
31 #include <bundle.h>
32 #include <eventsystem.h>
33 #include <stdbool.h>
34 #include <signal.h>
35 #include <systemd/sd-daemon.h>
36 #include <libsyscommon/dbus-system.h>
37 #include <libsyscommon/dbus-systemd.h>
38 #include <libsyscommon/ini-parser.h>
39
40 #include "dd-deviced.h"
41 #include "core/log.h"
42 #include "core/launch.h"
43 #include "core/device-notifier.h"
44 #include "core/device-idler.h"
45 #include "core/common.h"
46 #include "core/devices.h"
47 #include "core/launch.h"
48 #include "display/poll.h"
49 #include "display/setting.h"
50 #include "display/core.h"
51 #include "display/display-ops.h"
52 #include "power-handler.h"
53 #include "apps/apps.h"
54 #include "boot.h"
55 #include "shared/plugin.h"
56
57 #define POWEROFF_DURATION           4
58 #define POWEROFF_WAIT_RESOURCED     (0.5*1000) /* 0.5 seconds */
59 #define POWEROFF_WAIT_MAX           10 /* 10 seconds */
60 #define POWEROFF_WAIT_SYSTEMD_MS    (30 * 1000/*ms*/) /* 30 seconds */
61 #define SIGNAL_POWEROFF_STATE       "ChangeState"
62 #define POWER_CONF_FILE             "/etc/deviced/power.conf"
63
64 static struct display_plugin *disp_plgn;
65
66 static struct timeval tv_start_poweroff;
67 static GList *poweroff_options;
68 static struct power_option poweroff_opt;
69 static GList *poweroff_handles;
70
71 static const char *poweroff_type_flagpaths[] = { // index denotes type
72         [POWEROFF_TYPE_POWEROFF] = POWER_FLAG_POWEROFF,
73         [POWEROFF_TYPE_REBOOT] = POWER_FLAG_REBOOT,
74         [POWEROFF_TYPE_EXIT] = POWER_FLAG_EXIT,
75 };
76
77 static const char *poweroff_type_names[] = { // index denotes type
78         [POWEROFF_TYPE_POWEROFF] = POWER_POWEROFF,
79         [POWEROFF_TYPE_REBOOT] = POWER_REBOOT,
80         [POWEROFF_TYPE_EXIT] = POWER_EXIT,
81 };
82
83 enum poweroff_stage {
84         POWEROFF_DEFAULT,           /* Default stage, poweroff has not been triggered */
85         POWEROFF_TRIGGERED,         /* Poweroff is triggered. Wait timer can be added up to this stage */
86         POWEROFF_WAIT_OTHERS,       /* Wait for other processes to clean up their resources */
87 };
88
89 static enum poweroff_stage poweroff_stage;
90
91 static const char *poweroff_type_to_name(enum poweroff_type type)
92 {
93         if (type <= 0 || type >= ARRAY_SIZE(poweroff_type_names))
94                 return NULL;
95
96         return poweroff_type_names[type];
97 }
98
99 static enum poweroff_type poweroff_name_to_type(const char *name)
100 {
101         if (!name)
102                 goto out;
103
104         for (int i = 0; i < ARRAY_SIZE(poweroff_type_names); i++) {
105                 if (poweroff_type_names[i] && strcmp(poweroff_type_names[i], name) == 0)
106                         return i;
107         }
108 out:
109         return POWEROFF_TYPE_INVALID;
110 }
111
112 static void poweroff_start_animation(void)
113 {
114         int ret;
115
116         ret = systemd_start_unit_async("shutdown-animation.service", NULL);
117         if (ret < 0)
118                 _E("Failed to start shutdown animation.");
119
120         gettimeofday(&tv_start_poweroff, NULL);
121 }
122
123 static void poweroff_notify_resourced(void)
124 {
125         _I("Request to stop systemd service to resourced.");
126         dbus_handle_method_sync_with_reply_var_timeout(RESOURCED_BUS_NAME,
127                                 RESOURCED_PATH_PROCESS,
128                                 RESOURCED_INTERFACE_PROCESS,
129                                 "PrePoweroff",
130                                 NULL,
131                                 NULL,
132                                 POWEROFF_WAIT_RESOURCED);
133 }
134
135 static void disable_display(void)
136 {
137         const struct device_ops *display_device_ops = NULL;
138         FIND_DEVICE_VOID(display_device_ops, "display");
139         display_device_ops->stop(NORMAL_MODE);
140 }
141
142 static int disable_systemd_journald(void)
143 {
144         int ret;
145
146         ret = systemd_stop_unit_async("systemd-journald.socket", NULL);
147         if (ret < 0) {
148                 _E("Failed to stop 'systemd-journald.socket'.");
149                 return ret;
150         }
151
152         ret = systemd_stop_unit_async("systemd-journald.service", NULL);
153         if (ret < 0) {
154                 _E("Failed to stop 'systemd-journald.service'.");
155                 return ret;
156         }
157         return 0;
158 }
159
160 /* processes might start to fail abnormally after we kill them via
161  * umount_partition_by_kill. Failing services can trigger coredump
162  * handler, which might have trouble handling core in inconsistent
163  * system state (eg. lack of /opt).  Due to this we disable the
164  * coredump handler for the shutdown period.
165  */
166 static bool disable_coredump_handler(void)
167 {
168         int ret = 0;
169         int fd = open("/proc/sys/kernel/core_pattern", O_WRONLY);
170         if (fd >= 0) {
171                 ret = write(fd, "/dev/null", sizeof("/dev/null") - 1);
172                 close(fd);
173         }
174
175         bool is_ok = ret > 0;
176         _I("Disabling core dumps %s.", is_ok  ? "succeeded" : "failed");
177
178         return is_ok;
179 }
180
181 void poweroff_request_shutdown(void)
182 {
183         const char *method;
184
185         if (poweroff_opt.type == POWEROFF_TYPE_REBOOT)
186                 method = "Reboot";
187         else if (poweroff_opt.type == POWEROFF_TYPE_POWEROFF)
188                 method = "PowerOff";
189         else if (poweroff_opt.type == POWEROFF_TYPE_EXIT)
190                 method = "Exit";
191         else {
192                 _E("Poweroff invalid type(%d).", poweroff_opt.type);
193                 return;
194         }
195
196         CRITICAL_LOG("Requested %s via systemd.", method);
197         dbus_handle_method_sync_with_reply_var_timeout(SYSTEMD_DBUS_DEST,
198                                         SYSTEMD_DBUS_PATH,
199                                         SYSTEMD_DBUS_IFACE_MANAGER,
200                                         method,
201                                         NULL,
202                                         NULL,
203                                         POWEROFF_WAIT_SYSTEMD_MS);
204
205         raise(SIGUSR1);
206 }
207
208 static void poweroff_wait_for_seconds(void)
209 {
210         static int wait;
211         struct timeval now;
212         int poweroff_duration = POWEROFF_DURATION;
213         int check_duration = 0;
214
215         watchdog_notify();
216
217         gettimeofday(&now, NULL);
218         check_duration = now.tv_sec - tv_start_poweroff.tv_sec;
219
220         while (check_duration < poweroff_duration) {
221                 if (wait == 0) {
222                         _I("Wait poweroff %d %d.", check_duration, poweroff_duration);
223                         wait = 1;
224                 }
225                 usleep(100000);
226
227                 gettimeofday(&now, NULL);
228                 check_duration = now.tv_sec - tv_start_poweroff.tv_sec;
229
230                 if (check_duration < 0)
231                         break;
232         }
233
234         watchdog_notify();
235 }
236
237 void poweroff_prepare(void)
238 {
239         int off = poweroff_opt.type;
240
241         if (off == POWEROFF_TYPE_POWEROFF)
242                 CRITICAL_LOG("Prepare PowerOff.");
243         else if (off == POWEROFF_TYPE_REBOOT)
244                 CRITICAL_LOG("Prepare Reboot.");
245
246         poweroff_notify_resourced();
247         disable_systemd_journald();
248         disable_coredump_handler();
249
250         poweroff_wait_for_seconds();
251
252         disable_display();
253
254         /* Below functions follow after notifying DEVICE_NOTIFIER_POWEROFF
255            1. pmlock
256            - pmlock_detector_poweroff_cb()
257            - cleanup_pmlock_statistics()
258            - do_copy_force()
259            - save_display_log()
260            2. tzip
261            - tzip_poweroff()
262            - tzip_server_exit()
263            3. udev
264            - device_change_poweroff()
265            - uevent_control_stop()
266         */
267         device_notify(DEVICE_NOTIFIER_POWEROFF, &off);
268 }
269
270 int check_power_flag(void)
271 {
272         for (int i = 0; i < ARRAY_SIZE(poweroff_type_flagpaths); i++) {
273                 if (access(poweroff_type_flagpaths[i], F_OK) == 0) {
274                         poweroff_opt.type = i;
275                         poweroff_opt.option = NULL;
276                         return 1;
277                 }
278         }
279
280         return 0;
281 }
282
283 static void make_power_flag(enum poweroff_type type, const char *option)
284 {
285         const char *path;
286         int fd;
287         ssize_t len;
288
289         if (type <= 0 || type >= ARRAY_SIZE(poweroff_type_flagpaths))
290                 return;
291
292         path = poweroff_type_flagpaths[type];
293         if (!path)
294                 return;
295
296         fd = open(path, O_RDWR|O_CREAT, S_IRUSR | S_IWUSR);
297         if (fd < 0) {
298                 _E("Failed to create '%s'.", path);
299                 return;
300         }
301
302         if (option) {
303                 len = write(fd, option, strlen(option));
304                 if (len <= 0)
305                         _E("Failed to store option: %zd", len < 0 ? errno : len);
306         }
307
308         close(fd);
309 }
310
311 static void poweroff_remove_handle(pid_t pid)
312 {
313         struct poweroff_handle *handle;
314         GList *elem;
315
316         SYS_G_LIST_FOREACH(poweroff_handles, elem, handle) {
317                 if (handle->pid == pid)
318                         break;
319         }
320
321         assert(handle);
322
323         _D("Remove handle pid=%d timeout=%d timeout_id=%d.", handle->pid, handle->timeout, handle->timeout_id);
324         SYS_G_LIST_REMOVE(poweroff_handles, handle);
325
326         if (handle->timeout_id) {
327                 g_source_remove(handle->timeout_id);
328                 handle->timeout = 0;
329                 handle->timeout_id = 0;
330         }
331
332         free(handle);
333 }
334
335 static gboolean poweroff_wait_timeout_cb(void *data)
336 {
337         char timeout[50] = {0,};
338         pid_t pid = (pid_t)((intptr_t)data);
339
340         poweroff_remove_handle(pid);
341
342         /* All other processes finished cleanup. Poweroff is now on standby */
343         if (poweroff_stage == POWEROFF_WAIT_OTHERS && SYS_G_LIST_LENGTH(poweroff_handles) == 0) {
344                 _D("The last poweroff wait timer for pid %d is expired. Poweroff is now on standby.", pid);
345
346                 CRITICAL_LOG("Starting poweroff sequence.");
347
348                 // Watchdog timeout 90 -> 30 sec to reduce delay from unexpected poweroff failure.
349                 snprintf(timeout, sizeof(timeout), "WATCHDOG_USEC=%llu", (unsigned long long)POWEROFF_WAIT_SYSTEMD_MS*1000);
350                 sd_notify(0, timeout);
351
352                 make_power_flag(poweroff_opt.type, poweroff_opt.option);
353
354                 if (disp_plgn->pm_lock_internal)
355                         disp_plgn->pm_lock_internal(INTERNAL_LOCK_POWEROFF, LCD_OFF, STAY_CUR_STATE, 0);
356
357                 poweroff_prepare();
358                 poweroff_request_shutdown();
359         } else {
360                 _D("Poweroff wait timer for pid %d is expired, but keep waiting for others...", pid);
361         }
362
363         return G_SOURCE_REMOVE;
364 }
365
366 static gboolean poweroff_start_timers(void *data)
367 {
368         struct poweroff_handle *handle = NULL;
369         GList *l;
370         bool timer_exist = false;
371         int pid_alive = 0;
372
373         poweroff_stage = POWEROFF_WAIT_OTHERS;
374
375         SYS_G_LIST_FOREACH(poweroff_handles, l, handle) {
376                 pid_alive = kill(handle->pid, 0);
377                 if (pid_alive == -1) {
378                         _D("Pid(%d) is dead.", handle->pid);
379                         handle->timeout = 0;
380                 }
381
382                 _D("Run timer, pid=%d timeout=%d timeout_id=%d.", handle->pid, handle->timeout, handle->timeout_id);
383
384                 handle->timeout_id = g_timeout_add_seconds(handle->timeout,
385                                 poweroff_wait_timeout_cb,
386                                 (void *)((intptr_t)(handle->pid)));
387
388                 timer_exist = true;
389         }
390
391         if (timer_exist) {
392                 return G_SOURCE_REMOVE;
393         } else {
394                 _D("Handle is NULL.");
395
396                 handle = (struct poweroff_handle *)malloc(sizeof(struct poweroff_handle));
397                 if (handle == NULL) {
398                         _E("Not enough memory.");
399                         return G_SOURCE_REMOVE;
400                 }
401
402                 handle->pid = getpid();
403                 handle->timeout = 0;
404                 handle->timeout_id = g_timeout_add_seconds(handle->timeout,
405                                 poweroff_wait_timeout_cb,
406                                 (void *)((intptr_t)(handle->pid)));
407
408                 if (!handle->timeout_id) {
409                         _E("Failed to timer_add.");
410                         free(handle);
411                         return G_SOURCE_REMOVE;
412                 }
413
414                 SYS_G_LIST_APPEND(poweroff_handles, handle);
415         }
416
417         _D("Last Timer: timer_id=%d pid=%d timeout=%d", handle->timeout_id, handle->pid, handle->timeout);
418
419         return G_SOURCE_REMOVE;
420 }
421
422 static void system_shutdown_send_system_event(void)
423 {
424         bundle *b;
425
426         b = bundle_create();
427         bundle_add_str(b, EVT_KEY_SYSTEM_SHUTDOWN, EVT_VAL_SYSTEM_SHUTDOWN_TRUE);
428         eventsystem_send_system_event(SYS_EVENT_SYSTEM_SHUTDOWN, b);
429         bundle_free(b);
430 }
431
432 static int poweroff_option_valid(enum poweroff_type type_e, const char *option)
433 {
434         GList *l;
435         struct power_option *elem;
436
437         SYS_G_LIST_FOREACH(poweroff_options, l, elem) {
438                 if (elem->type != type_e)
439                         continue;
440
441                 /* Do not match option
442                 if (option) {
443                         if (elem->option == NULL)
444                                 continue;
445                         if (strncmp(elem->option, option, strlen(elem->option)))
446                                 continue;
447                 } else {
448                         if (elem->option != NULL)
449                                 continue;
450                 } */
451                 return 1;
452         }
453
454         return 0;
455 }
456
457 static void poweroff_send_broadcast(int status)
458 {
459         static int old = 0;
460         int ret;
461
462         if (old == status)
463                 return;
464
465         _D("Broadcast poweroff %d.", status);
466
467         old = status;
468
469         /* Need to notify to deviced-vibrator. deviced-vibrator receives ChangeState signal for POWEROFF_TYPE_DIRECT and POWEROFF_TYPE_RESTART */
470         ret = dbus_handle_emit_dbus_signal(NULL,
471                                                 DEVICED_PATH_POWEROFF,
472                                                 DEVICED_INTERFACE_POWEROFF,
473                                                 SIGNAL_POWEROFF_STATE,
474                                                 g_variant_new("(i)", status));
475         if (ret < 0)
476                 _E("Failed to send dbus signal(%s)", SIGNAL_POWEROFF_STATE);
477 }
478
479 static int power_execute_pid(const char *typename, const char *option)
480 {
481         int ret;
482
483         if (poweroff_stage >= POWEROFF_TRIGGERED) {
484                 _E("Duplicate poweroff request. Poweroff was already triggered.");
485                 return -EINVAL;
486         }
487
488         enum poweroff_type type_e = poweroff_name_to_type(typename);
489         if (type_e == POWEROFF_TYPE_INVALID) {
490                 _E("Failed to get type enum value(%d).", type_e);
491                 return -EINVAL;
492         }
493
494         if (poweroff_option_valid(type_e, option)) {
495                 poweroff_opt.type = type_e;
496                 free(poweroff_opt.option);
497                 poweroff_opt.option = NULL;
498                 if (option)
499                         poweroff_opt.option = strdup(option);
500         } else {
501                 _E("Failed to find supported type(%s). option=%s", typename, (option ? option : "NULL"));
502                 return -EINVAL;
503         }
504
505         ret = vconf_set_int(VCONFKEY_SYSMAN_POWER_OFF_STATUS, poweroff_opt.type);
506         if (ret < 0)
507                 _E("Failed to set vconf value for power off status: %d", vconf_get_ext_errno());
508
509         poweroff_stage = POWEROFF_TRIGGERED;
510         if (disp_plgn->update_pm_setting)
511                 disp_plgn->update_pm_setting(SETTING_POWEROFF, poweroff_opt.type);
512
513         /* Poweroff event broadcasting */
514         system_shutdown_send_system_event();
515         poweroff_send_broadcast(poweroff_opt.type);
516
517         /* Skip running animation if option is silent */
518         if (poweroff_opt.option != NULL && !strcmp(poweroff_opt.option, "silent"))
519                 _D("Skip running poweroff animation.");
520         else
521                 poweroff_start_animation();
522
523         /* Spare time for AddPowerOffWait requests */
524         g_timeout_add_seconds(1, poweroff_start_timers, NULL);
525
526         return 0;
527 }
528
529 static int power_execute(void *data)
530 {
531         return power_execute_pid((char *)data, NULL);
532 }
533
534 static int check_sender_process(GDBusConnection *conn, const char *sender)
535 {
536         pid_t pid;
537
538         if (sender == NULL || g_dbus_is_name(sender) == FALSE) {
539                 _E("Invalid sender");
540                 return -EINVAL;
541         }
542
543         pid = dbus_connection_get_sender_pid(conn, sender);
544         if (pid == -1 || kill(pid, 0) == -1) {
545                 _E("Process(%d) does not exist, dbus ignored.", pid);
546                 return -ESRCH;
547         }
548
549         return pid;
550 }
551
552 static GVariant *dbus_power_handler(GDBusConnection *conn,
553         const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
554         GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
555 {
556         int ret;
557         char *type_str;
558
559         g_variant_get(param, "(s)", &type_str);
560
561         ret = check_sender_process(conn, sender);
562         if (ret < 0)
563                 goto out;
564
565
566         CRITICAL_LOG("Poweroff PID(%d) requests %s.", ret, type_str);
567         ret = power_execute_pid(type_str, NULL);
568
569 out:
570         g_free(type_str);
571         return g_variant_new("(i)", ret);
572 }
573
574 static GVariant *dbus_power_option_handler(GDBusConnection *conn,
575         const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
576         GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
577 {
578         int ret;
579         char *type, *option;
580
581         g_variant_get(param, "(ss)", &type, &option);
582
583         ret = check_sender_process(conn, sender);
584         if (ret < 0)
585                 goto out;
586
587
588         CRITICAL_LOG("Poweroff PID(%d) requests type=%s option=%s.", ret, type, option);
589         ret = power_execute_pid(type, option);
590
591 out:
592         g_free(type);
593         g_free(option);
594         return g_variant_new("(i)", ret);
595 }
596
597 /* timer can be added before the stage POWEROFF_WAIT_OTHERS */
598 static GVariant *add_poweroff_time(GDBusConnection *conn,
599         const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
600         GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
601 {
602         struct poweroff_handle *handle;
603         int ret;
604         pid_t pid;
605         GList *l;
606
607         if (poweroff_stage >= POWEROFF_WAIT_OTHERS) {
608                 _E("It's too late. Poweroff is already in waiting stage.");
609                 ret = -1;
610                 goto out;
611         }
612
613         ret = check_sender_process(conn, sender);
614         if (ret < 0)
615                 goto out;
616
617         pid = (pid_t)ret;
618
619         CRITICAL_LOG("PID %d requested to a poweroff timer.", pid);
620
621         SYS_G_LIST_FOREACH(poweroff_handles, l, handle) {
622                 if (handle->pid == pid)
623                         break;
624         }
625
626         if (handle)
627                 poweroff_remove_handle(pid);
628
629         _D("Make a new handle.");
630         handle = (struct poweroff_handle *)malloc(sizeof(struct poweroff_handle));
631         if (handle == NULL) {
632                 _E("Not enough memory.");
633                 ret = -1;
634                 goto out;
635         }
636
637         handle->pid = pid;
638         handle->timeout_id = 0;
639         handle->timeout = POWEROFF_WAIT_MAX;
640
641         SYS_G_LIST_APPEND(poweroff_handles, handle);
642         _D("Add a new poweroff timer. pid=%d timeout=%d timeout_id=%d)", handle->pid, handle->timeout, handle->timeout_id);
643
644         ret = 0;
645
646 out:
647         return g_variant_new("(i)", ret);
648 }
649
650 static GVariant *remove_poweroff_time(GDBusConnection *conn,
651         const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
652         GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
653 {
654         struct poweroff_handle *handle;
655         GList *l;
656         int ret = 0;
657         pid_t pid;
658
659         ret = check_sender_process(conn, sender);
660         if (ret < 0)
661                 goto out;
662
663         pid = (pid_t)ret;
664
665         CRITICAL_LOG("PID %d requested to remove poweroff timer.", pid);
666
667         SYS_G_LIST_FOREACH(poweroff_handles, l, handle) {
668                 if (handle->pid == pid)
669                         break;
670         }
671
672         if (handle) {
673                 if (handle->timeout_id)
674                         g_source_remove(handle->timeout_id);
675                 handle->timeout = 0;
676                 handle->timeout_id = g_timeout_add_seconds(handle->timeout,
677                                 poweroff_wait_timeout_cb,
678                                 (void *)((intptr_t)(handle->pid)));
679         } else {
680                 _E("Invalid pid(%d).", pid);
681                 ret = -1;
682         }
683
684 out:
685         return g_variant_new("(i)", ret);
686 }
687
688 static const dbus_method_s dbus_methods[] = {
689         { "PowerOff"          , "s" , "i", dbus_power_handler },
690         { "PowerOffWithOption", "ss", "i", dbus_power_option_handler },
691         /* Public API device_power_reboot() calls this dbus method. */
692         { "AddPowerOffWait"   , NULL, "i", add_poweroff_time },
693         { "RemovePowerOffWait", NULL, "i", remove_poweroff_time },
694         /* Add methods here */
695 };
696
697 static const dbus_interface_u dbus_interface = {
698         .oh = NULL,
699         .name = DEVICED_INTERFACE_POWEROFF,
700         .methods = dbus_methods,
701         .nr_methods = ARRAY_SIZE(dbus_methods),
702 };
703
704 static int add_poweroff_option(enum poweroff_type type, const char *option)
705 {
706         struct power_option *opt;
707         const char *name;
708
709         name = poweroff_type_to_name(type);
710         if (!name) {
711                 _E("Invalid type(%d).", type);
712                 return -EINVAL;
713         }
714
715         opt = calloc(1, sizeof(struct power_option));
716         if (!opt) {
717                 _E("Failed to calloc().");
718                 return -ENOMEM;
719         }
720
721         opt->type = type;
722         opt->option = (option ? strdup(option) : NULL);
723
724         SYS_G_LIST_APPEND(poweroff_options, opt);
725
726         _D("Add %s option=%s", name, opt->option);
727
728         return 0;
729 }
730
731 static int load_config(struct parse_result *result, void *user_data)
732 {
733         enum poweroff_type type;
734         int ret;
735
736         if (MATCH(result->section, "PowerOff"))
737                 type = POWEROFF_TYPE_DIRECT;
738         else if (MATCH(result->section, "Reboot"))
739                 type = POWEROFF_TYPE_RESTART;
740         else
741                 return 0;
742
743         if (!MATCH(result->name, "Option"))
744                 return 0;
745
746         ret = add_poweroff_option(type, result->value);
747         if (ret < 0) {
748                 _E("Failed to add %s option=%s", result->section, result->value);
749                 return ret;
750         }
751
752         return 0;
753 }
754
755 static int booting_done(void *data)
756 {
757         static int done;
758
759         if (data == NULL)
760                 goto out;
761
762         done = *(int *)data;
763 out:
764         return done;
765 }
766
767 static void power_init(void *data)
768 {
769         int ret;
770
771         /* init dbus interface */
772         ret = dbus_handle_add_dbus_object(NULL, DEVICED_PATH_POWEROFF, &dbus_interface);
773         if (ret < 0)
774                 _E("Failed to init dbus method: %d", ret);
775
776         add_booting_done_handler(NULL);
777
778         register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done);
779
780         add_poweroff_option(POWEROFF_TYPE_POWEROFF, NULL);
781         add_poweroff_option(POWEROFF_TYPE_RESTART, NULL);
782         add_poweroff_option(POWEROFF_TYPE_EXIT, NULL);
783
784         ret = config_parse(POWER_CONF_FILE, load_config, NULL);
785         if (ret < 0)
786                 _E("Failed to load power off config: %d", ret);
787
788         poweroff_stage = POWEROFF_DEFAULT;
789 }
790
791 static const struct device_ops power_device_ops = {
792         DECLARE_NAME_LEN(POWER_OPS_NAME),
793         .init     = power_init,
794         .execute  = power_execute,
795 };
796
797 DEVICE_OPS_REGISTER(&power_device_ops)
798
799 static void __CONSTRUCTOR__ initialize(void)
800 {
801         disp_plgn = get_var_display_plugin();
802         if (!disp_plgn)
803                 _E("Failed to get display plugin variable.");
804 }