1 /* 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 17 #include <errno.h> 18 #include <fcntl.h> 19 #include <mntent.h> 20 #include <stdbool.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #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> 31 32 #include <cutils/android_reboot.h> 33 #include <cutils/klog.h> 34 #include <cutils/list.h> 35 36 #define TAG "android_reboot" 37 #define READONLY_CHECK_MS 5000 38 #define READONLY_CHECK_TIMES 50 39 40 typedef struct { 41 struct listnode list; 42 struct mntent entry; 43 } mntent_list; 44 45 static 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 63 static 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|. 70 */ 71 static void find_rw(struct listnode* rw_entries) 72 { 73 FILE* fp; 74 struct mntent* mentry; 75 76 if ((fp = setmntent("/proc/mounts", "r")) == NULL) { 77 KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n"); 78 return; 79 } 80 while ((mentry = getmntent(fp)) != NULL) { 81 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); 90 } 91 } 92 endmntent(fp); 93 } 94 95 static 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 109 static 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; 119 } 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 */ 132 static void remount_ro(void (*cb_on_remount)(const struct mntent*)) 133 { 134 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); 144 145 /* Trigger the remount of the filesystems as read-only, 146 * which also marks them clean. 147 */ 148 fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY)); 149 if (fd < 0) { 150 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; 155 } 156 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 } 162 close(fd); 163 164 /* Now poll /proc/mounts till it's done */ 165 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)); 191 cnt++; 192 } 193 194 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 207 out: 208 free_entries(&rw_entries); 209 free_entries(&ro_entries); 210 } 211 212 int android_reboot_with_callback( 213 int cmd, int flags __unused, const char *arg, 214 void (*cb_on_remount)(const struct mntent*)) 215 { 216 int ret; 217 remount_ro(cb_on_remount); 218 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: 228 ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, 229 LINUX_REBOOT_CMD_RESTART2, arg); 230 break; 231 232 default: 233 ret = -1; 234 } 235 236 return ret; 237 } 238 239 int android_reboot(int cmd, int flags, const char *arg) 240 { 241 return android_reboot_with_callback(cmd, flags, arg, NULL); 242 } 243