Home | History | Annotate | Download | only in Launcher
      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 
     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 
     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 
    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 
    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 
    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 
    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