• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 //
18 // Strictly to deal with reboot into system after OTA after /data
19 // mounts to pull the last pmsg file data and place it
20 // into /data/misc/recovery/ directory, rotating it in.
21 //
22 // Usage: recovery-persist [--force-persist]
23 //
24 //    On systems without /cache mount, all file content representing in the
25 //    recovery/ directory stored in /sys/fs/pstore/pmsg-ramoops-0 in logger
26 //    format that reside in the LOG_ID_SYSTEM buffer at ANDROID_LOG_INFO
27 //    priority or higher is transfered to the /data/misc/recovery/ directory.
28 //    The content is matched and rotated in as need be.
29 //
30 //    --force-persist  ignore /cache mount, always rotate in the contents.
31 //
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 #include <string>
39 
40 #include <android-base/file.h>
41 #include <android-base/logging.h>
42 #include <private/android_logger.h> /* private pmsg functions */
43 
44 #include "recovery_utils/logging.h"
45 #include "recovery_utils/parse_install_logs.h"
46 
47 constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log";
48 constexpr const char* LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0";
49 constexpr const char* LAST_KMSG_FILE = "/data/misc/recovery/last_kmsg";
50 constexpr const char* LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops-0";
51 constexpr const char* ALT_LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops";
52 
53 // close a file, log an error if the error indicator is set
check_and_fclose(FILE * fp,const char * name)54 static void check_and_fclose(FILE *fp, const char *name) {
55     fflush(fp);
56     if (ferror(fp)) {
57         PLOG(ERROR) << "Error in " << name;
58     }
59     fclose(fp);
60 }
61 
copy_file(const char * source,const char * destination)62 static void copy_file(const char* source, const char* destination) {
63   FILE* dest_fp = fopen(destination, "we");
64   if (dest_fp == nullptr) {
65     PLOG(ERROR) << "Can't open " << destination;
66   } else {
67     FILE* source_fp = fopen(source, "re");
68     if (source_fp != nullptr) {
69       char buf[4096];
70       size_t bytes;
71       while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
72         fwrite(buf, 1, bytes, dest_fp);
73       }
74       check_and_fclose(source_fp, source);
75     }
76     check_and_fclose(dest_fp, destination);
77   }
78 }
79 
file_exists(const char * filename)80 static bool file_exists(const char* filename) {
81   return access(filename, R_OK) == 0;
82 }
83 
84 static bool rotated = false;
85 
logsave(log_id_t,char,const char * filename,const char * buf,size_t len,void *)86 ssize_t logsave(
87         log_id_t /* logId */,
88         char /* prio */,
89         const char *filename,
90         const char *buf, size_t len,
91         void * /* arg */) {
92 
93     std::string destination("/data/misc/");
94     destination += filename;
95 
96     std::string buffer(buf, len);
97 
98     {
99         std::string content;
100         android::base::ReadFileToString(destination, &content);
101 
102         if (buffer.compare(content) == 0) {
103             return len;
104         }
105     }
106 
107     // ToDo: Any others that match? Are we pulling in multiple
108     // already-rotated files? Algorithm thus far is KISS: one file,
109     // one rotation allowed.
110 
111     rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE);
112     rotated = true;
113 
114     return android::base::WriteStringToFile(buffer, destination.c_str());
115 }
116 
main(int argc,char ** argv)117 int main(int argc, char **argv) {
118 
119     /* Is /cache a mount?, we have been delivered where we are not wanted */
120     /*
121      * Following code halves the size of the executable as compared to:
122      *
123      *    load_volume_table();
124      *    has_cache = volume_for_path(CACHE_ROOT) != nullptr;
125      */
126     bool has_cache = false;
127     static const char mounts_file[] = "/proc/mounts";
128     FILE* fp = fopen(mounts_file, "re");
129     if (!fp) {
130         PLOG(ERROR) << "failed to open " << mounts_file;
131     } else {
132         char *line = NULL;
133         size_t len = 0;
134         ssize_t read;
135         while ((read = getline(&line, &len, fp)) != -1) {
136             if (strstr(line, " /cache ")) {
137                 has_cache = true;
138                 break;
139             }
140         }
141         free(line);
142         fclose(fp);
143     }
144 
145     if (has_cache) {
146       // Collects and reports the non-a/b update metrics from last_install; and removes the file
147       // to avoid duplicate report.
148       if (file_exists(LAST_INSTALL_FILE_IN_CACHE) && unlink(LAST_INSTALL_FILE_IN_CACHE) == -1) {
149         PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE_IN_CACHE;
150       }
151 
152       // TBD: Future location to move content from /cache/recovery to /data/misc/recovery/
153       // if --force-persist flag, then transfer pmsg data anyways
154       if ((argc <= 1) || !argv[1] || strcmp(argv[1], "--force-persist")) {
155         return 0;
156       }
157     }
158 
159     /* Is there something in pmsg? If not, no need to proceed. */
160     if (!file_exists(LAST_PMSG_FILE)) {
161       return 0;
162     }
163 
164     // Take last pmsg file contents and send it off to the logsave
165     __android_log_pmsg_file_read(
166         LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", logsave, NULL);
167 
168     // For those device without /cache, the last_install file has been copied to
169     // /data/misc/recovery from pmsg. Looks for the sideload history only.
170     if (!has_cache) {
171       if (file_exists(LAST_INSTALL_FILE) && unlink(LAST_INSTALL_FILE) == -1) {
172         PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE;
173       }
174     }
175 
176     /* Is there a last console log too? */
177     if (rotated) {
178       if (file_exists(LAST_CONSOLE_FILE)) {
179         copy_file(LAST_CONSOLE_FILE, LAST_KMSG_FILE);
180       } else if (file_exists(ALT_LAST_CONSOLE_FILE)) {
181         copy_file(ALT_LAST_CONSOLE_FILE, LAST_KMSG_FILE);
182       }
183     }
184 
185     return 0;
186 }
187