2 #define PY_SSIZE_T_CLEAN
3 #define _GNU_SOURCE 1 // asprintf
6 // According to Python, its header has to go first:
7 // http://docs.python.org/3/c-api/intro.html#include-files
17 #if defined(__FreeBSD__) || defined(__NetBSD__)
18 # include <sys/sysctl.h>
20 #include <sys/types.h>
23 #include "bup/compat.h"
24 #include "bup/intprops.h"
27 static int prog_argc = 0;
28 static char **prog_argv = NULL;
29 static char *orig_env_pythonpath = NULL;
32 get_argv(PyObject *self, PyObject *args)
34 if (!PyArg_ParseTuple(args, ""))
37 PyObject *result = PyList_New(prog_argc);
39 for (i = 0; i < prog_argc; i++) {
40 PyObject *s = PyBytes_FromString(prog_argv[i]);
42 die(2, "cannot convert argument to bytes: %s\n", prog_argv[i]);
43 PyList_SET_ITEM(result, i, s);
48 static PyMethodDef bup_main_methods[] = {
49 {"argv", get_argv, METH_VARARGS,
50 "Return the program's current argv array as a list of byte strings." },
54 static int setup_module(PyObject *mod)
56 if (!orig_env_pythonpath) {
57 PyObject_SetAttrString(mod, "env_pythonpath", Py_None);
59 PyObject *py_p = PyBytes_FromString(orig_env_pythonpath);
61 die(2, "cannot convert PYTHONPATH to bytes: %s\n",
63 PyObject_SetAttrString(mod, "env_pythonpath", py_p);
69 static struct PyModuleDef bup_main_module_def = {
70 .m_base = PyModuleDef_HEAD_INIT,
72 .m_doc = "Built-in bup module providing direct access to argv.",
74 .m_methods = bup_main_methods
78 PyInit_bup_main(void) {
79 PyObject *mod = PyModule_Create(&bup_main_module_def);
80 if (!setup_module(mod))
89 setup_bup_main_module(void) {
91 char *path = getenv("PYTHONPATH");
93 orig_env_pythonpath = strdup(path);
95 if (PyImport_AppendInittab("bup_main", PyInit_bup_main) == -1)
96 die(2, "unable to register bup_main module\n");
99 #if defined(__APPLE__) && defined(__MACH__)
101 static char *exe_parent_dir(const char * const argv_0) {
104 uint32_t size = sizeof(spath);
105 int rc = _NSGetExecutablePath(spath, &size);
107 mpath = malloc(size);
108 if (!mpath) die(2, "unable to allocate memory for executable path\n");
109 rc = _NSGetExecutablePath(mpath, &size);
111 if(rc != 0) die(2, "unable to find executable path\n");
112 char *path = mpath ? mpath : spath;
113 char *abs_exe = realpath(path, NULL);
115 die(2, "cannot resolve path (%s): %s\n", strerror(errno), path);
116 char * const abs_parent = strdup(dirname(abs_exe));
118 if (mpath) free(mpath);
123 #elif defined(__FreeBSD__)
125 static char *exe_path ()
127 const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
129 int rc = sysctl (mib, 4, NULL, &path_len, NULL, 0);
130 if (rc != 0) die(2, "unable to determine executable path length\n");
131 char *path = malloc (path_len);
132 if (!path) die(2, "unable to allocate memory for executable path\n");
133 rc = sysctl (mib, 4, path, &path_len, NULL, 0);
134 if (rc != 0) die(2, "unable to determine executable path via sysctl\n");
138 static char *exe_parent_dir(const char * const argv_0)
140 char * const exe = exe_path();
141 if (!exe) die(2, "unable to determine executable path\n");
142 char * const parent = strdup(dirname(exe));
143 if (!parent) die(2, "unable to determine parent directory of executable\n");
148 #else // not defined(__FreeBSD__)
150 /// Use /proc if possible, and if all else fails, search in the PATH
152 #if defined(__linux__)
153 # define PROC_SELF_EXE "/proc/self/exe"
154 #elif defined(__sun) || defined (sun)
155 # define PROC_SELF_EXE "/proc/self/path/a.out"
157 # define PROC_SELF_EXE NULL
160 static char *find_in_path(const char * const name, const char * const path)
163 char *tmp_path = strdup(path);
166 char *tok_path = tmp_path;
167 while ((elt = strtok(tok_path, ":")) != NULL) {
170 int rc = asprintf(&candidate, "%s/%s", elt, name);
173 rc = stat(candidate, &st);
176 case EACCES: case ELOOP: case ENOENT: case ENAMETOOLONG:
180 die(2, "cannot stat %s: %s\n", candidate, strerror(errno));
183 } else if (S_ISREG(st.st_mode)) {
184 if (access(candidate, X_OK) == 0) {
189 case EACCES: case ELOOP: case ENOENT: case ENAMETOOLONG:
193 die(2, "cannot determine executability of %s: %s\n",
194 candidate, strerror(errno));
204 static char *find_exe_parent(const char * const argv_0)
206 char *candidate = NULL;
207 const char * const slash = strchr(argv_0, '/');
209 candidate = strdup(argv_0);
212 const char * const env_path = getenv("PATH");
214 die(2, "no PATH and executable isn't relative or absolute: %s\n",
216 char *path_exe = find_in_path(argv_0, env_path);
218 char * abs_exe = realpath(path_exe, NULL);
220 die(2, "cannot resolve path (%s): %s\n",
221 strerror(errno), path_exe);
229 char * const abs_exe = realpath(candidate, NULL);
231 die(2, "cannot resolve path (%s): %s\n", strerror(errno), candidate);
233 char * const abs_parent = strdup(dirname(abs_exe));
239 static char *exe_parent_dir(const char * const argv_0)
241 if (PROC_SELF_EXE != NULL) {
244 size_t path_n = sizeof(sbuf);
247 len = readlink(PROC_SELF_EXE, path, path_n);
248 if (len == -1 || (size_t) len != path_n)
250 if (!INT_MULTIPLY_OK(path_n, 2, &path_n))
251 die(2, "memory buffer for executable path would be too big\n");
252 if (path != sbuf) free(path);
253 path = malloc(path_n);
255 die(2, "unable to allocate memory for executable path\n");
259 char *result = strdup(dirname(path));
265 case ENOENT: case EACCES: case EINVAL: case ELOOP: case ENOTDIR:
269 die(2, "cannot resolve %s: %s\n", path, strerror(errno));
275 return find_exe_parent(argv_0);
278 #endif // use /proc if possible, and if all else fails, search in the PATh
281 setenv_or_die(const char *name, const char *value)
283 int rc = setenv(name, value, 1);
285 die(2, "setenv %s=%s failed (%s)\n", name, value, strerror(errno));
289 prepend_lib_to_pythonpath(const char * const exec_path,
290 const char * const relative_path)
292 char *parent = exe_parent_dir(exec_path);
295 int rc = asprintf(&bupmodpath, "%s/%s", parent, relative_path);
298 rc = stat(bupmodpath, &st);
300 die(2, "unable find lib dir (%s): %s\n", strerror(errno), bupmodpath);
301 if (!S_ISDIR(st.st_mode))
302 die(2, "lib path is not dir: %s\n", bupmodpath);
303 char *curpypath = getenv("PYTHONPATH");
306 int rc = asprintf(&path, "%s:%s", bupmodpath, curpypath);
308 setenv_or_die("PYTHONPATH", path);
311 setenv_or_die("PYTHONPATH", bupmodpath);
318 #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 8
319 # define bup_py_main bup_py_bytes_main
321 # define bup_py_main Py_BytesMain
324 #if defined(BUP_DEV_BUP_PYTHON) && defined(BUP_DEV_BUP_EXEC)
325 # error "Both BUP_DEV_BUP_PYTHON and BUP_DEV_BUP_EXEC are defined"
328 #ifdef BUP_DEV_BUP_PYTHON
330 int main(int argc, char **argv)
335 setup_bup_main_module();
336 prepend_lib_to_pythonpath(argv[0], "../lib");
337 return bup_py_main (argc, argv);
340 #elif defined(BUP_DEV_BUP_EXEC)
342 int main(int argc, char **argv)
345 prog_argc = argc - 1;
346 prog_argv = argv + 1;
347 setup_bup_main_module();
348 prepend_lib_to_pythonpath(argv[0], "../lib");
350 return bup_py_main (1, argv);
351 // This can't handle a script with a name like "-c", but that's
352 // python's problem, not ours.
353 return bup_py_main (2, argv);
356 #else // normal bup command
358 int main(int argc, char **argv)
363 setup_bup_main_module();
364 prepend_lib_to_pythonpath(argv[0], "..");
365 char *bup_argv[] = { argv[0], "-m", "bup.main" };
366 return bup_py_main (3, bup_argv);
369 #endif // normal bup command