blob: 06026d125eb50813ce7507550f4a728f11a8f8b7 [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
Yusuke Sato0df08272015-07-08 14:57:07 -070045static bool is_block_device(const char* fsname)
46{
47 return !strncmp(fsname, "/dev/block", 10);
48}
49
50/* Find all read+write block devices in /proc/mounts and add them to
51 * |rw_entries|.
Ken Sumralle3aeeb42011-03-07 23:29:42 -080052 */
Yusuke Sato0df08272015-07-08 14:57:07 -070053static void find_rw(struct listnode* rw_entries)
Ken Sumralle3aeeb42011-03-07 23:29:42 -080054{
Yabin Cuid6bd9bf2015-01-02 14:02:14 -080055 FILE* fp;
56 struct mntent* mentry;
Ken Sumralle3aeeb42011-03-07 23:29:42 -080057
Yabin Cuid6bd9bf2015-01-02 14:02:14 -080058 if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
Yusuke Sato0df08272015-07-08 14:57:07 -070059 KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
60 return;
Ken Sumralle3aeeb42011-03-07 23:29:42 -080061 }
Yabin Cuid6bd9bf2015-01-02 14:02:14 -080062 while ((mentry = getmntent(fp)) != NULL) {
Elliott Hughes8c183882016-11-30 09:37:17 -080063 if (is_block_device(mentry->mnt_fsname) && hasmntopt(mentry, "rw")) {
Yusuke Sato0df08272015-07-08 14:57:07 -070064 mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list));
65 item->entry = *mentry;
66 item->entry.mnt_fsname = strdup(mentry->mnt_fsname);
67 item->entry.mnt_dir = strdup(mentry->mnt_dir);
68 item->entry.mnt_type = strdup(mentry->mnt_type);
69 item->entry.mnt_opts = strdup(mentry->mnt_opts);
70 list_add_tail(rw_entries, &item->list);
Ken Sumralle3aeeb42011-03-07 23:29:42 -080071 }
Yabin Cuid6bd9bf2015-01-02 14:02:14 -080072 }
73 endmntent(fp);
Yusuke Sato0df08272015-07-08 14:57:07 -070074}
Ken Sumralle3aeeb42011-03-07 23:29:42 -080075
Yusuke Sato0df08272015-07-08 14:57:07 -070076static void free_entries(struct listnode* entries)
77{
78 struct listnode* node;
79 struct listnode* n;
80 list_for_each_safe(node, n, entries) {
81 mntent_list* item = node_to_item(node, mntent_list, list);
82 free(item->entry.mnt_fsname);
83 free(item->entry.mnt_dir);
84 free(item->entry.mnt_type);
85 free(item->entry.mnt_opts);
86 free(item);
87 }
88}
89
90static mntent_list* find_item(struct listnode* rw_entries, const char* fsname_to_find)
91{
92 struct listnode* node;
93 list_for_each(node, rw_entries) {
94 mntent_list* item = node_to_item(node, mntent_list, list);
95 if (!strcmp(item->entry.mnt_fsname, fsname_to_find)) {
96 return item;
97 }
98 }
99 return NULL;
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800100}
101
102/* Remounting filesystems read-only is difficult when there are files
103 * opened for writing or pending deletes on the filesystem. There is
104 * no way to force the remount with the mount(2) syscall. The magic sysrq
105 * 'u' command does an emergency remount read-only on all writable filesystems
106 * that have a block device (i.e. not tmpfs filesystems) by calling
107 * emergency_remount(), which knows how to force the remount to read-only.
108 * Unfortunately, that is asynchronous, and just schedules the work and
109 * returns. The best way to determine if it is done is to read /proc/mounts
110 * repeatedly until there are no more writable filesystems mounted on
111 * block devices.
112 */
Yusuke Sato0df08272015-07-08 14:57:07 -0700113static void remount_ro(void (*cb_on_remount)(const struct mntent*))
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800114{
Yusuke Sato0df08272015-07-08 14:57:07 -0700115 int fd, cnt;
116 FILE* fp;
117 struct mntent* mentry;
118 struct listnode* node;
119
120 list_declare(rw_entries);
121 list_declare(ro_entries);
122
123 sync();
124 find_rw(&rw_entries);
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800125
126 /* Trigger the remount of the filesystems as read-only,
127 * which also marks them clean.
128 */
Yusuke Sato0df08272015-07-08 14:57:07 -0700129 fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY));
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800130 if (fd < 0) {
Yusuke Sato0df08272015-07-08 14:57:07 -0700131 KLOG_WARNING(TAG, "Failed to open sysrq-trigger.\n");
132 /* TODO: Try to remount each rw parition manually in readonly mode.
133 * This may succeed if no process is using the partition.
134 */
135 goto out;
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800136 }
Yusuke Sato0df08272015-07-08 14:57:07 -0700137 if (TEMP_FAILURE_RETRY(write(fd, "u", 1)) != 1) {
138 close(fd);
139 KLOG_WARNING(TAG, "Failed to write to sysrq-trigger.\n");
140 /* TODO: The same. Manually remount the paritions. */
141 goto out;
142 }
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800143 close(fd);
144
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800145 /* Now poll /proc/mounts till it's done */
Yusuke Sato0df08272015-07-08 14:57:07 -0700146 cnt = 0;
147 while (cnt < READONLY_CHECK_TIMES) {
148 if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
149 /* If we can't read /proc/mounts, just give up. */
150 KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
151 goto out;
152 }
153 while ((mentry = getmntent(fp)) != NULL) {
Elliott Hughes8c183882016-11-30 09:37:17 -0800154 if (!is_block_device(mentry->mnt_fsname) || !hasmntopt(mentry, "ro")) {
Yusuke Sato0df08272015-07-08 14:57:07 -0700155 continue;
156 }
157 mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname);
158 if (item) {
159 /* |item| has now been ro remounted. */
160 list_remove(&item->list);
161 list_add_tail(&ro_entries, &item->list);
162 }
163 }
164 endmntent(fp);
165 if (list_empty(&rw_entries)) {
166 /* All rw block devices are now readonly. */
167 break;
168 }
169 TEMP_FAILURE_RETRY(
170 usleep(READONLY_CHECK_MS * 1000 / READONLY_CHECK_TIMES));
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800171 cnt++;
172 }
173
Yusuke Sato0df08272015-07-08 14:57:07 -0700174 list_for_each(node, &rw_entries) {
175 mntent_list* item = node_to_item(node, mntent_list, list);
176 KLOG_WARNING(TAG, "Failed to remount %s in readonly mode.\n",
177 item->entry.mnt_fsname);
178 }
179
180 if (cb_on_remount) {
181 list_for_each(node, &ro_entries) {
182 mntent_list* item = node_to_item(node, mntent_list, list);
183 cb_on_remount(&item->entry);
184 }
185 }
186
187out:
188 free_entries(&rw_entries);
189 free_entries(&ro_entries);
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800190}
191
Todd Poynoreac33da2017-01-30 17:28:55 -0800192static void save_reboot_reason(int cmd, const char *arg)
193{
194 FILE *fp;
195 const char *reason = NULL;
196
197 fp = fopen(LAST_REBOOT_REASON_FILE, "w");
198 if (fp == NULL) {
199 KLOG_WARNING(TAG, "Error creating " LAST_REBOOT_REASON_FILE
200 ": %s\n", strerror(errno));
201 return;
202 }
203 switch (cmd) {
204 case ANDROID_RB_RESTART:
205 reason = "restart";
206 break;
207
208 case ANDROID_RB_POWEROFF:
209 reason = "power-off";
210 break;
211
212 case ANDROID_RB_RESTART2:
213 reason = arg && strlen(arg) ? arg : "restart";
214 break;
215
216 case ANDROID_RB_THERMOFF:
217 reason = "thermal-shutdown";
218 break;
219
220 default:
221 fprintf(fp,"0x%08X\n", cmd);
222 break;
223 }
224
225 if (reason) {
226 if (fprintf(fp, "%s\n", reason) < 0) {
227 KLOG_WARNING(TAG, "Error writing " LAST_REBOOT_REASON_FILE
228 ": %s\n", strerror(errno));
229 }
230 }
231
232 fclose(fp);
233}
234
Yusuke Sato0df08272015-07-08 14:57:07 -0700235int android_reboot_with_callback(
236 int cmd, int flags __unused, const char *arg,
237 void (*cb_on_remount)(const struct mntent*))
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800238{
239 int ret;
Todd Poynoreac33da2017-01-30 17:28:55 -0800240
241 save_reboot_reason(cmd, arg);
Yusuke Sato0df08272015-07-08 14:57:07 -0700242 remount_ro(cb_on_remount);
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800243 switch (cmd) {
244 case ANDROID_RB_RESTART:
245 ret = reboot(RB_AUTOBOOT);
246 break;
247
248 case ANDROID_RB_POWEROFF:
249 ret = reboot(RB_POWER_OFF);
250 break;
251
252 case ANDROID_RB_RESTART2:
Pavel Chupindccdb942013-11-21 23:56:37 +0400253 ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800254 LINUX_REBOOT_CMD_RESTART2, arg);
255 break;
256
Todd Poynor37bba3b2017-01-30 17:27:39 -0800257 case ANDROID_RB_THERMOFF:
258 ret = reboot(RB_POWER_OFF);
259 break;
260
Ken Sumralle3aeeb42011-03-07 23:29:42 -0800261 default:
262 ret = -1;
263 }
264
265 return ret;
266}
267
Yusuke Sato0df08272015-07-08 14:57:07 -0700268int android_reboot(int cmd, int flags, const char *arg)
269{
270 return android_reboot_with_callback(cmd, flags, arg, NULL);
271}