blob: af7e1892ed63c8ae9adbffffb7d7ccfd805b4018 [file] [log] [blame]
Ken Sumralle3aeeb42011-03-07 23:29:42 -08001/*
2 * Copyright 2011, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Yusuke Sato0df08272015-07-08 14:57:07 -070017#include <errno.h>
Ken Sumralle3aeeb42011-03-07 23:29:42 -080018#include <fcntl.h>
Yabin Cuid6bd9bf2015-01-02 14:02:14 -080019#include <mntent.h>
Yusuke Sato0df08272015-07-08 14:57:07 -070020#include <stdbool.h>
Ken Sumralle3aeeb42011-03-07 23:29:42 -080021#include <stdio.h>
Yusuke Sato0df08272015-07-08 14:57:07 -070022#include <stdlib.h>
Ken Sumralle3aeeb42011-03-07 23:29:42 -080023#include <string.h>
Yusuke Sato0df08272015-07-08 14:57:07 -070024#include <sys/cdefs.h>
25#include <sys/mount.h>
26#include <sys/reboot.h>
27#include <sys/stat.h>
28#include <sys/syscall.h>
29#include <sys/types.h>
30#include <unistd.h>
Ken Sumralle3aeeb42011-03-07 23:29:42 -080031
32#include <cutils/android_reboot.h>
Yusuke Sato0df08272015-07-08 14:57:07 -070033#include <cutils/klog.h>
34#include <cutils/list.h>
Ken Sumralle3aeeb42011-03-07 23:29:42 -080035
Yusuke Sato0df08272015-07-08 14:57:07 -070036#define TAG "android_reboot"
37#define READONLY_CHECK_MS 5000
38#define READONLY_CHECK_TIMES 50
Mark Salyzyn2b94cc22013-11-22 07:36:45 -080039
Yusuke Sato0df08272015-07-08 14:57:07 -070040typedef struct {
41 struct listnode list;
42 struct mntent entry;
43} mntent_list;
44
45static bool has_mount_option(const char* opts, const char* opt_to_find)
46{
47 bool ret = false;
48 char* copy = NULL;
49 char* opt;
50 char* rem;
51
52 while ((opt = strtok_r(copy ? NULL : (copy = strdup(opts)), ",", &rem))) {
53 if (!strcmp(opt, opt_to_find)) {
54 ret = true;
55 break;
56 }
57 }
58
59 free(copy);
60 return ret;
61}
62
63static bool is_block_device(const char* fsname)
64{
65 return !strncmp(fsname, "/dev/block", 10);
66}
67
68/* Find all read+write block devices in /proc/mounts and add them to
69 * |rw_entries|.
Ken Sumralle3aeeb42011-03-07 23:29:42 -080070 */
Yusuke Sato0df08272015-07-08 14:57:07 -070071static void find_rw(struct listnode* rw_entries)
Ken Sumralle3aeeb42011-03-07 23:29:42 -080072{
Yabin Cuid6bd9bf2015-01-02 14:02:14 -080073 FILE* fp;
74 struct mntent* mentry;
Ken Sumralle3aeeb42011-03-07 23:29:42 -080075
Yabin Cuid6bd9bf2015-01-02 14:02:14 -080076 if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
Yusuke Sato0df08272015-07-08 14:57:07 -070077 KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
78 return;
Ken Sumralle3aeeb42011-03-07 23:29:42 -080079 }
Yabin Cuid6bd9bf2015-01-02 14:02:14 -080080 while ((mentry = getmntent(fp)) != NULL) {
Yusuke Sato0df08272015-07-08 14:57:07 -070081 if (is_block_device(mentry->mnt_fsname) &&
82 has_mount_option(mentry->mnt_opts, "rw")) {
83 mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list));
84 item->entry = *mentry;
85 item->entry.mnt_fsname = strdup(mentry->mnt_fsname);
86 item->entry.mnt_dir = strdup(mentry->mnt_dir);
87 item->entry.mnt_type = strdup(mentry->mnt_type);
88 item->entry.mnt_opts = strdup(mentry->mnt_opts);
89 list_add_tail(rw_entries, &item->list);
Ken Sumralle3aeeb42011-03-07 23:29:42 -080090 }
Yabin Cuid6bd9bf2015-01-02 14:02:14 -080091 }
92 endmntent(fp);
Yusuke Sato0df08272015-07-08 14:57:07 -070093}
Ken Sumralle3aeeb42011-03-07 23:29:42 -080094
Yusuke Sato0df08272015-07-08 14:57:07 -070095static void free_entries(struct listnode* entries)
96{
97 struct listnode* node;
98 struct listnode* n;
99 list_for_each_safe(node, n, entries) {
100 mntent_list* item = node_to_item(node, mntent_list, list);
101 free(item->entry.mnt_fsname);
102 free(item->entry.mnt_dir);
103 free(item->entry.mnt_type);
104 free(item->entry.mnt_opts);
105 free(item);
106 }
107}
108
109static mntent_list* find_item(struct listnode* rw_entries, const char* fsname_to_find)
110{
111 struct listnode* node;
112 list_for_each(node, rw_entries) {
113 mntent_list* item = node_to_item(node, mntent_list, list);
114 if (!strcmp(item->entry.mnt_fsname, fsname_to_find)) {
115 return item;
116 }
117 }
118 return NULL;
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800119}
120
121/* Remounting filesystems read-only is difficult when there are files
122 * opened for writing or pending deletes on the filesystem. There is
123 * no way to force the remount with the mount(2) syscall. The magic sysrq
124 * 'u' command does an emergency remount read-only on all writable filesystems
125 * that have a block device (i.e. not tmpfs filesystems) by calling
126 * emergency_remount(), which knows how to force the remount to read-only.
127 * Unfortunately, that is asynchronous, and just schedules the work and
128 * returns. The best way to determine if it is done is to read /proc/mounts
129 * repeatedly until there are no more writable filesystems mounted on
130 * block devices.
131 */
Yusuke Sato0df08272015-07-08 14:57:07 -0700132static void remount_ro(void (*cb_on_remount)(const struct mntent*))
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800133{
Yusuke Sato0df08272015-07-08 14:57:07 -0700134 int fd, cnt;
135 FILE* fp;
136 struct mntent* mentry;
137 struct listnode* node;
138
139 list_declare(rw_entries);
140 list_declare(ro_entries);
141
142 sync();
143 find_rw(&rw_entries);
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800144
145 /* Trigger the remount of the filesystems as read-only,
146 * which also marks them clean.
147 */
Yusuke Sato0df08272015-07-08 14:57:07 -0700148 fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY));
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800149 if (fd < 0) {
Yusuke Sato0df08272015-07-08 14:57:07 -0700150 KLOG_WARNING(TAG, "Failed to open sysrq-trigger.\n");
151 /* TODO: Try to remount each rw parition manually in readonly mode.
152 * This may succeed if no process is using the partition.
153 */
154 goto out;
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800155 }
Yusuke Sato0df08272015-07-08 14:57:07 -0700156 if (TEMP_FAILURE_RETRY(write(fd, "u", 1)) != 1) {
157 close(fd);
158 KLOG_WARNING(TAG, "Failed to write to sysrq-trigger.\n");
159 /* TODO: The same. Manually remount the paritions. */
160 goto out;
161 }
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800162 close(fd);
163
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800164 /* Now poll /proc/mounts till it's done */
Yusuke Sato0df08272015-07-08 14:57:07 -0700165 cnt = 0;
166 while (cnt < READONLY_CHECK_TIMES) {
167 if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
168 /* If we can't read /proc/mounts, just give up. */
169 KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
170 goto out;
171 }
172 while ((mentry = getmntent(fp)) != NULL) {
173 if (!is_block_device(mentry->mnt_fsname) ||
174 !has_mount_option(mentry->mnt_opts, "ro")) {
175 continue;
176 }
177 mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname);
178 if (item) {
179 /* |item| has now been ro remounted. */
180 list_remove(&item->list);
181 list_add_tail(&ro_entries, &item->list);
182 }
183 }
184 endmntent(fp);
185 if (list_empty(&rw_entries)) {
186 /* All rw block devices are now readonly. */
187 break;
188 }
189 TEMP_FAILURE_RETRY(
190 usleep(READONLY_CHECK_MS * 1000 / READONLY_CHECK_TIMES));
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800191 cnt++;
192 }
193
Yusuke Sato0df08272015-07-08 14:57:07 -0700194 list_for_each(node, &rw_entries) {
195 mntent_list* item = node_to_item(node, mntent_list, list);
196 KLOG_WARNING(TAG, "Failed to remount %s in readonly mode.\n",
197 item->entry.mnt_fsname);
198 }
199
200 if (cb_on_remount) {
201 list_for_each(node, &ro_entries) {
202 mntent_list* item = node_to_item(node, mntent_list, list);
203 cb_on_remount(&item->entry);
204 }
205 }
206
207out:
208 free_entries(&rw_entries);
209 free_entries(&ro_entries);
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800210}
211
Yusuke Sato0df08272015-07-08 14:57:07 -0700212int android_reboot_with_callback(
213 int cmd, int flags __unused, const char *arg,
214 void (*cb_on_remount)(const struct mntent*))
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800215{
216 int ret;
Yusuke Sato0df08272015-07-08 14:57:07 -0700217 remount_ro(cb_on_remount);
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800218 switch (cmd) {
219 case ANDROID_RB_RESTART:
220 ret = reboot(RB_AUTOBOOT);
221 break;
222
223 case ANDROID_RB_POWEROFF:
224 ret = reboot(RB_POWER_OFF);
225 break;
226
227 case ANDROID_RB_RESTART2:
Pavel Chupindccdb942013-11-21 23:56:37 +0400228 ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800229 LINUX_REBOOT_CMD_RESTART2, arg);
230 break;
231
232 default:
233 ret = -1;
234 }
235
236 return ret;
237}
238
Yusuke Sato0df08272015-07-08 14:57:07 -0700239int android_reboot(int cmd, int flags, const char *arg)
240{
241 return android_reboot_with_callback(cmd, flags, arg, NULL);
242}