1 // Copyright 2017 Google Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "launcher_internal.h"
16
17 #include <limits.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20
21 extern "C" {
22 // Cpython built-in C functions.
23 /*
24 read_directory(archive) -> files dict (new reference)
25
26 Given a path to a Zip archive, build a dict, mapping file names
27 (local to the archive, using SEP as a separator) to toc entries.
28 */
29 PyObject *read_directory(const char *archive);
30
31 /* Given a path to a Zip file and a toc_entry, return the (uncompressed)
32 data as a new reference. */
33 PyObject *get_data(const char *archive, PyObject *toc_entry);
34 }
35
36 namespace android {
37 namespace cpython2 {
38 namespace python_launcher {
39 namespace internal {
40
RunModule(const char * module,int set_argv0)41 int RunModule(const char *module, int set_argv0) {
42 PyObject *runpy, *runmodule, *runargs, *result;
43 runpy = PyImport_ImportModule("runpy");
44 if (runpy == NULL) {
45 fprintf(stderr, "Could not import runpy module\n");
46 return -1;
47 }
48 runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main");
49 if (runmodule == NULL) {
50 fprintf(stderr, "Could not access runpy._run_module_as_main\n");
51 Py_DECREF(runpy);
52 return -1;
53 }
54 runargs = Py_BuildValue("(si)", module, set_argv0);
55 if (runargs == NULL) {
56 fprintf(stderr,
57 "Could not create arguments for runpy._run_module_as_main\n");
58 Py_DECREF(runpy);
59 Py_DECREF(runmodule);
60 return -1;
61 }
62 result = PyObject_Call(runmodule, runargs, NULL);
63 if (result == NULL) {
64 PyErr_Print();
65 }
66 Py_DECREF(runpy);
67 Py_DECREF(runmodule);
68 Py_DECREF(runargs);
69 if (result == NULL) {
70 return -1;
71 }
72 Py_DECREF(result);
73 return 0;
74 }
75
GetEntryPointFilePath(const char * launcher_path)76 std::string GetEntryPointFilePath(const char *launcher_path) {
77 PyObject *files;
78 files = read_directory(launcher_path);
79 if (files == NULL) {
80 return std::string();
81 }
82 PyObject *toc_entry;
83 // Return value: Borrowed reference.
84 toc_entry = PyDict_GetItemString(files, ENTRYPOINT_FILE);
85 if (toc_entry == NULL) {
86 Py_DECREF(files);
87 return std::string();
88 }
89 PyObject *py_data;
90 py_data = get_data(launcher_path, toc_entry);
91 if (py_data == NULL) {
92 Py_DECREF(files);
93 return std::string();
94 }
95 // PyString_AsString returns a NUL-terminated representation of the "py_data",
96 // "data" must not be modified in any way. And it must not be deallocated.
97 char *data = PyString_AsString(py_data);
98 if (data == NULL) {
99 Py_DECREF(py_data);
100 Py_DECREF(files);
101 return std::string();
102 }
103
104 char *res = strdup(data); /* deep copy of data */
105 Py_DECREF(py_data);
106 Py_DECREF(files);
107
108 int i = 0;
109 /* Strip newline and other trailing whitespace. */
110 for (i = strlen(res) - 1; i >= 0 && isspace(res[i]); i--) {
111 res[i] = '\0';
112 }
113 /* Check for the file extension. */
114 i = strlen(res);
115 if (i > 3 && strcmp(res + i - 3, ".py") == 0) {
116 res[i - 3] = '\0';
117 } else {
118 PyErr_Format(PyExc_ValueError, "Invalid entrypoint in %s: %s",
119 ENTRYPOINT_FILE, res);
120 return std::string();
121 }
122 return std::string(res);
123 }
124
RunModuleNameFromEntryPoint(const char * launcher_path,std::string entrypoint)125 int RunModuleNameFromEntryPoint(const char *launcher_path, std::string entrypoint) {
126 if (entrypoint.empty()) {
127 return -1;
128 }
129 // Has to pass to free to avoid a memory leak after use.
130 char *arr = strdup(entrypoint.c_str());
131 // Replace file system path seperator with Python package/module seperator.
132 char *ch;
133 for (ch = arr; *ch; ch++) {
134 if (*ch == '/') {
135 *ch = '.';
136 }
137 }
138
139 if (AddPathToPythonSysPath(launcher_path) < 0) {
140 free(arr);
141 return -1;
142 }
143 // Calculate the runfiles path size. Extra space for '\0'.
144 size_t size = snprintf(nullptr, 0, "%s/%s", launcher_path, RUNFILES) + 1;
145 char runfiles_path[size];
146 snprintf(runfiles_path, size, "%s/%s", launcher_path, RUNFILES);
147 if (AddPathToPythonSysPath(runfiles_path) < 0) {
148 free(arr);
149 return -1;
150 }
151 int ret = RunModule(arr, 0);
152 free(arr);
153 return ret;
154 }
155
AddPathToPythonSysPath(const char * path)156 int AddPathToPythonSysPath(const char *path) {
157 if (path == NULL) {
158 return -1;
159 }
160 PyObject *py_path;
161 py_path = PyString_FromString(path);
162 if (py_path == NULL) {
163 return -1;
164 }
165 PyObject *sys_path;
166 // Return value: Borrowed reference.
167 sys_path = PySys_GetObject(const_cast<char*>("path"));
168 if (sys_path == NULL) {
169 Py_DECREF(py_path);
170 return -1;
171 }
172 PyList_Insert(sys_path, 0, py_path);
173 Py_DECREF(py_path);
174 return 0;
175 }
176
RunMainFromImporter(const char * launcher_path)177 int RunMainFromImporter(const char *launcher_path) {
178 PyObject *py_launcher_path, *importer;
179 py_launcher_path = PyString_FromString(launcher_path);
180 if (py_launcher_path == NULL) {
181 return -1;
182 }
183 importer = PyImport_GetImporter(py_launcher_path);
184 if (importer == NULL) {
185 Py_DECREF(py_launcher_path);
186 return -1;
187 }
188 if (importer != Py_None && importer->ob_type != &PyNullImporter_Type) {
189 /* Launcher path is usable as an import source, so
190 put it in sys.path[0] and import __main__ */
191 if (AddPathToPythonSysPath(launcher_path) < 0) {
192 Py_DECREF(importer);
193 Py_DECREF(py_launcher_path);
194 return -1;
195 }
196 }
197 Py_DECREF(importer);
198 Py_DECREF(py_launcher_path);
199 return RunModule("__main__", 0);
200 }
201 } // namespace internal
202
RunEntryPointOrMainModule(const char * launcher_path)203 int RunEntryPointOrMainModule(const char *launcher_path) {
204 std::string entrypoint = internal::GetEntryPointFilePath(launcher_path);
205 if (entrypoint.empty()) {
206 // If entry point can not be found or can not be executed, we try to
207 // run __main__.py within the .par file.
208 fprintf(stderr, "Cannot find valid entry point to execute par file!\n");
209 fprintf(stdout, "Start trying to run __main__ module within par file.\n");
210 return internal::RunMainFromImporter(launcher_path);
211 }
212 return internal::RunModuleNameFromEntryPoint(launcher_path, entrypoint);
213 }
214 } // namespace python_launcher
215 } // namespace cpython2
216 } // namespace android
217