4 * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
28 #include <sys/types.h>
31 #include <libsyscommon/list.h>
34 #include "resourced.h"
37 #include "watchdog-cgroup.h"
41 #include "config-parser.h"
42 #include "file-helper.h"
43 #include "storage-helper.h"
46 #include "module-data.h"
47 #include "dbus-handler.h"
49 #define PROC_CONF_DIR "/etc/resourced/process.conf.d"
50 #define PROC_CONF_SUFFIX ".conf"
52 #define SYSTEMD_DBUS_DEST "org.freedesktop.systemd1"
53 #define SYSTEMD_DBUS_UNIT_PATH "/org/freedesktop/systemd1/unit/"
54 #define SYSTEMD_DBUS_SERVICE_IFACE SYSTEMD_DBUS_DEST".Service"
55 #define DBUS_IFACE_DBUS_PROPERTIES "org.freedesktop.DBus.Properties"
57 #define SYSTEMD_UNIT_ESCAPE_CHAR ".-"
59 #define BOOT_PARAM_PROC_WATCHDOG_REBOOT_DISABLED "tizen.watchdog_reboot_disable"
61 struct proc_watchdog_group {
67 int reboot_on_failure;
70 static GList *proc_watchdog_group_list;
72 /* print log on console */
73 static void proc_watchdog_print_console(const char *format, ...)
76 char buffer[256] = {0, };
80 vsnprintf(buffer, sizeof(buffer), format, ap);
83 fp = fopen("/dev/console", "a");
85 _E("[WATCHDOG] %s(failed to print log on console, %m)", buffer);
89 _D("[WATCHDOG] %s", buffer);
90 fprintf(fp, "%s\n", buffer);
95 static bool proc_watchdog_boot_param_reboot_disabled(void)
100 retval = proc_get_cmdline(0, cmdline, sizeof(cmdline));
101 if (retval != RESOURCED_ERROR_NONE)
104 return (strstr(cmdline, BOOT_PARAM_PROC_WATCHDOG_REBOOT_DISABLED) != NULL);
107 /* copy of libsyscommon/libsystemd.c: systemd_get_unit_dbus_path() */
108 static int proc_watchdog_get_escaped_name(const char *unit, char **escaped)
112 size_t p, k, prefix_len, unit_len;
113 size_t path_len, len, escape;
116 _E("[WATCHDOG] Invalid parameter.");
119 unit_len = strlen(unit);
121 for (escape = 0, p = 0; p < unit_len; escape++) {
122 k = strcspn(unit + p, SYSTEMD_UNIT_ESCAPE_CHAR);
123 if (p + k >= unit_len)
128 prefix_len = strlen(SYSTEMD_DBUS_UNIT_PATH);
129 /* assume we try to get object path of foo-bar.service then
130 * the object path will be
131 * "/org/freedesktop/systemd1/unit/foo_2dbar_2eservice\n". In
132 * this case we can find two escape characters, one of escape
133 * char('-') is changed to three of char("_2d"). So the total
135 /* (PREFIX) + (unit - escape + 3*escape) + NULL */
136 path_len = prefix_len + (unit_len - escape)
137 + (escape * 3 * sizeof(char)) + 1;
138 path = (char *)calloc(path_len, sizeof(char));
140 _E("[WATCHDOG] Not enough memory.");
146 strncpy(path, SYSTEMD_DBUS_UNIT_PATH, prefix_len + 1);
147 for (i = 0, p = 0; i <= escape; i++) {
148 k = strcspn(unit + p, SYSTEMD_UNIT_ESCAPE_CHAR);
149 strncpy(path + prefix_len, unit + p, k);
150 if (k < strlen(unit + p)) {
151 len = path_len - (prefix_len + k);
152 snprintf(path + prefix_len + k, len,
153 "_%x", *(unit + p + k) & 0xff);
162 /* get MainPID of a service. */
163 static int proc_watchdog_get_service_mainpid(const char *service, int *pid)
167 GVariant *reply = NULL;
168 GVariant *inner_reply = NULL;
173 ret = proc_watchdog_get_escaped_name(service, &escaped);
175 _E("[WATCHDOG] Failed to makeup escaped service name.");
179 ret = d_bus_call_method_sync_gvariant_with_reply(SYSTEMD_DBUS_DEST,
181 DBUS_IFACE_DBUS_PROPERTIES,
183 g_variant_new("(ss)", SYSTEMD_DBUS_SERVICE_IFACE, "ExecMainPID"),
188 if (!reply || ret < 0) {
189 _E("[WATCHDOG] Failed to get pid of service=%s", service);
191 g_variant_unref(reply);
195 do_expr_unless_g_variant_consume_typechecked(return -EINVAL, reply, "(v)", &inner_reply);
196 do_expr_unless_g_variant_consume_typechecked(return -EINVAL, inner_reply, "u", pid);
201 static bool proc_watchdog_check_all(void)
208 GList *elem1, *elem2;
209 struct proc_watchdog_group *vg;
211 SYS_G_LIST_FOREACH(proc_watchdog_group_list, elem1, vg) {
212 if (!vg->reboot_on_failure)
215 SYS_G_LIST_FOREACH(vg->process, elem2, process) {
216 pid = find_pid_from_cmdline(process);
218 proc_watchdog_print_console("%s is not running", process);
223 SYS_G_LIST_FOREACH(vg->service, elem2, service) {
224 ret_val = proc_watchdog_get_service_mainpid(service, &pid);
226 _E("[WATCHDOG] Failed to get MainPID of service=%s", service);
230 /* The property MainPID can return a positive MainPID
231 * as a pid of inactive(dead) service if the service had once
232 * been active before. Therefore, it is necessary to check
233 * whether it is still alive. */
234 if (pid == 0 || kill(pid, 0) != 0) {
235 proc_watchdog_print_console("%s is not running", service);
244 static void proc_watchdog_force_reboot(void)
246 proc_watchdog_print_console("Force reboot");
247 execl("PROC_WATCHDOG_HANDLER_PATH", "PROC_WATCHDOG_HANDLER_PATH", NULL);
249 _E("[WATCHDOG] Failed to execute %s", PROC_WATCHDOG_HANDLER_PATH);
252 static int proc_watchdog_load_config(struct parse_result *result, void *user_data)
254 char vgname[64] = {0, };
255 struct proc_watchdog_group *vg;
261 if (!strstr(result->section, PER_PROCESS_CONF))
264 snprintf(vgname, sizeof(vgname), "%s:%s", (char *)user_data, result->section);
266 SYS_G_LIST_FOREACH(proc_watchdog_group_list, elem, vg) {
267 if (!strncmp(vg->name, vgname, sizeof(vg->name)))
272 vg = calloc(1, sizeof(struct proc_watchdog_group));
275 strncpy(vg->name, vgname, sizeof(vg->name));
276 SYS_G_LIST_APPEND(proc_watchdog_group_list, vg);
279 if (!strncmp(result->name, "Process", sizeof("Process"))) {
280 SYS_G_LIST_APPEND(vg->process, strndup(result->value, PATH_MAX));
281 } else if (!strncmp(result->name, "Service", sizeof("Service"))) {
282 SYS_G_LIST_APPEND(vg->service, strndup(result->value, PATH_MAX));
283 } else if (!strncmp(result->name, "ActionOnFailure", sizeof("ActionOnFailure"))) {
284 if (!strncmp(result->value, "reboot", sizeof("reboot")))
285 vg->reboot_on_failure = 1;
287 vg->reboot_on_failure = 0;
293 static int proc_watchdog_create_sub_cgroup(const char *name, pid_t pid)
295 _cleanup_free_ char *cgroup_name = NULL;
302 r = cgroup_make_subdir(PROC_WATCHDOGCG_PATH, name, &already);
304 _E("[WATCHDOG] failed to create %s sub dir", PROC_WATCHDOGCG_PATH);
309 _D("[WATCHDOG] PID(%d) is already registered as %s sub cgroup(%s)",
310 pid, PROC_WATCHDOGCG_NAME, name);
314 r = asprintf(&cgroup_name, "%s/%s", PROC_WATCHDOGCG_PATH, name);
316 _E("[WATCHDOG] failed to allocate memory");
320 cgroup_write_tid_fullpath(cgroup_name, pid);
321 /* r = cgroup_write_node_uint32(cgroup_name, TASK_FILE_NAME, pid);
323 _E("[WATCHDOG] failed to write pid '%d' to '%s': %m",
328 _D("[WATCHDOG] PID(%d) is registered as %s sub cgroup(%s)", pid, PROC_WATCHDOGCG_NAME, name);
333 static void proc_watchdog_create_proc_name_groups(void)
335 GList *elem1, *elem2;
336 struct proc_watchdog_group *vg;
340 SYS_G_LIST_FOREACH(proc_watchdog_group_list, elem1, vg) {
341 if (!vg->reboot_on_failure)
344 SYS_G_LIST_FOREACH(vg->process, elem2, process) {
347 pid = find_pid_from_cmdline(process);
349 r = proc_watchdog_create_sub_cgroup(process, pid);
351 _E("[WATCHDOG] failed to create sub cgroup of '%s', ignoring", process);
353 _D("[WATCHDOG] failed to find pid of name: %s", process);
359 static void proc_watchdog_create_systemd_service_groups(void)
361 GList *elem1, *elem2;
362 struct proc_watchdog_group *vg;
365 SYS_G_LIST_FOREACH(proc_watchdog_group_list, elem1, vg) {
366 if (!vg->reboot_on_failure)
369 SYS_G_LIST_FOREACH(vg->service, elem2, service) {
373 ret_val = proc_watchdog_get_service_mainpid(service, &pid);
374 if (ret_val == -ECOMM)
378 ret_val = proc_watchdog_create_sub_cgroup(service, pid);
380 _E("[WATCHDOG] failed to create sub cgroup of '%s', ignoring", service);
386 static int proc_watchdog_booting_done(void *data)
388 proc_watchdog_create_proc_name_groups();
389 proc_watchdog_create_systemd_service_groups();
391 if (proc_watchdog_check_all() == false)
392 proc_watchdog_force_reboot();
397 static int proc_watchdog_process_disable(void *data)
399 _cleanup_close_ int checkfd = -1;
402 checkfd = creat(CHECK_RELEASE_PROGRESS, 0640);
404 _E("[WATCHDOG] Unable to disable cgroup release_agent - can't make file %s (%m)", CHECK_RELEASE_PROGRESS);
409 static void proc_watchdog_load_configs(void)
413 struct dirent **namelist;
415 if ((count = scandir(PROC_CONF_DIR, &namelist, NULL, alphasort)) == -1) {
416 _W("[WATCHDOG] failed to opendir (%s)", PROC_CONF_DIR);
420 for (idx = 0; idx < count; idx++) {
421 char path[PATH_MAX] = {0, };
423 if (!strstr(namelist[idx]->d_name, PROC_CONF_SUFFIX))
426 snprintf(path, sizeof(path), "%s/%s", PROC_CONF_DIR, namelist[idx]->d_name);
427 config_parse(path, proc_watchdog_load_config, (void *)namelist[idx]->d_name);
434 GList *elem1, *elem2;
435 struct proc_watchdog_group *vg;
438 SYS_G_LIST_FOREACH(proc_watchdog_group_list, elem1, vg) {
439 _D("[WATCHDOG] %s", vg->name);
440 _D("[WATCHDOG] reboot on failure=%d", vg->reboot_on_failure);
441 SYS_G_LIST_FOREACH(vg->process, elem2, name)
442 _D("[WATCHDOG] proc watchdog process: %s", name);
443 SYS_G_LIST_FOREACH(vg->service, elem2, name)
444 _D("[WATCHDOG] proc watchdog service: %s", name);
448 static int resourced_proc_watchdog_process_init(void *data)
450 _cleanup_close_ int checkfd = -1;
453 if (proc_watchdog_boot_param_reboot_disabled()) {
454 proc_watchdog_print_console("Reboot has been disabled, boot param: %s", BOOT_PARAM_PROC_WATCHDOG_REBOOT_DISABLED);
455 return RESOURCED_ERROR_NONE;
458 r = access(CHECK_RELEASE_PROGRESS, F_OK);
460 r = unlink(CHECK_RELEASE_PROGRESS);
462 _E("[WATCHDOG] failed to remove %s: %m", CHECK_RELEASE_PROGRESS);
465 proc_watchdog_load_configs();
467 if (!is_mounted(PROC_WATCHDOGCG_PATH)) {
468 r = cgroup_make_subdir(CGROUP_PATH, PROC_WATCHDOGCG_NAME, NULL);
470 _E("[WATCHDOG] failed to make %s cgroup", PROC_WATCHDOGCG_PATH);
471 return RESOURCED_ERROR_FAIL;
474 r = cgroup_mount_subsystem("cgroup", PROC_WATCHDOGCG_PATH,
475 "none,name=watchdog");
477 _E("[WATCHDOG] failed to mount %s cgroup: %m", PROC_WATCHDOGCG_PATH);
478 return RESOURCED_ERROR_FAIL;
481 r = cgroup_set_release_agent(PROC_WATCHDOGCG_NAME, PROC_WATCHDOG_HANDLER_PATH);
483 _E("[WATCHDOG] failed to set cgroup release_agent: %m");
484 return RESOURCED_ERROR_FAIL;
488 proc_watchdog_create_proc_name_groups();
489 proc_watchdog_create_systemd_service_groups();
491 register_notifier(RESOURCED_NOTIFIER_POWER_OFF,
492 proc_watchdog_process_disable);
494 r = register_notifier(RESOURCED_NOTIFIER_BOOTING_DONE,
495 proc_watchdog_booting_done);
497 _E("[WATCHDOG] failed to register notifier BootingDone");
498 return RESOURCED_ERROR_FAIL;
501 return RESOURCED_ERROR_NONE;
504 static int resourced_proc_watchdog_process_finalize(void *data)
506 GList *elem1, *elem1_n, *elem2, *elem2_n;
507 struct proc_watchdog_group *vg;
508 char *process, *service;
510 if (proc_watchdog_boot_param_reboot_disabled())
511 return RESOURCED_ERROR_NONE;
513 SYS_G_LIST_FOREACH_SAFE(proc_watchdog_group_list, elem1, elem1_n, vg) {
514 SYS_G_LIST_FOREACH_SAFE(vg->process, elem2, elem2_n, process) {
515 SYS_G_LIST_REMOVE(vg->process, process);
519 SYS_G_LIST_FOREACH_SAFE(vg->service, elem2, elem2_n, service) {
520 SYS_G_LIST_REMOVE(vg->service, service);
524 SYS_G_LIST_REMOVE(proc_watchdog_group_list, vg);
528 proc_watchdog_process_disable(NULL);
529 unregister_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, proc_watchdog_booting_done);
530 unregister_notifier(RESOURCED_NOTIFIER_POWER_OFF, proc_watchdog_process_disable);
532 return RESOURCED_ERROR_NONE;
535 static struct module_ops proc_watchdog_modules_ops = {
536 .priority = MODULE_PRIORITY_NORMAL,
537 .name = "proc-watchdog",
538 .init = resourced_proc_watchdog_process_init,
539 .exit = resourced_proc_watchdog_process_finalize,
542 MODULE_REGISTER(&proc_watchdog_modules_ops)