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