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
10 #if PY_MAJOR_VERSION < 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 7)
12 #define BUP_XSTR(x) BUP_STR(x)
13 #pragma message "Python versions older than 3.7 are not supported; detected X.Y " \
14 BUP_XSTR(PY_MAJOR_VERSION) "." BUP_XSTR(PY_MINOR_VERSION)
25 #if defined(__FreeBSD__) || defined(__NetBSD__)
26 # include <sys/sysctl.h>
28 #include <sys/types.h>
31 #include "bup/compat.h"
32 #include "bup/intprops.h"
35 static int prog_argc = 0;
36 static char **prog_argv = NULL;
37 static char *orig_env_pythonpath = NULL;
40 get_argv(PyObject *self, PyObject *args)
42 if (!PyArg_ParseTuple(args, ""))
45 PyObject *result = PyList_New(prog_argc);
47 for (i = 0; i < prog_argc; i++) {
48 PyObject *s = PyBytes_FromString(prog_argv[i]);
50 die(2, "cannot convert argument to bytes: %s\n", prog_argv[i]);
51 PyList_SET_ITEM(result, i, s);
56 static PyMethodDef bup_main_methods[] = {
57 {"argv", get_argv, METH_VARARGS,
58 "Return the program's current argv array as a list of byte strings." },
62 static int setup_module(PyObject *mod)
64 if (!orig_env_pythonpath) {
65 PyObject_SetAttrString(mod, "env_pythonpath", Py_None);
67 PyObject *py_p = PyBytes_FromString(orig_env_pythonpath);
69 die(2, "cannot convert PYTHONPATH to bytes: %s\n",
71 PyObject_SetAttrString(mod, "env_pythonpath", py_p);
77 static struct PyModuleDef bup_main_module_def = {
78 .m_base = PyModuleDef_HEAD_INIT,
80 .m_doc = "Built-in bup module providing direct access to argv.",
82 .m_methods = bup_main_methods
86 PyInit_bup_main(void) {
87 PyObject *mod = PyModule_Create(&bup_main_module_def);
88 if (!setup_module(mod))
97 setup_bup_main_module(void) {
99 char *path = getenv("PYTHONPATH");
101 orig_env_pythonpath = strdup(path);
103 if (PyImport_AppendInittab("bup_main", PyInit_bup_main) == -1)
104 die(2, "unable to register bup_main module\n");
108 * Older realpath implementations (e.g. 4.4BSD) required the second
109 * argument to be non-NULL, and then POSIX added the option of NULL
110 * with the semantics of malloc'ing a big-enough buffer. Define a
111 * helper function with the NULL semantics to accomodate older
114 * gnulib has a list of systems that are known to reject NULL as the
116 * https://www.gnu.org/software/gnulib/manual/html_node/realpath.html
119 #define BUP_HAVE_POSIX_REALPATH
121 // FreeBSD < 7: bup's FreeBSD code does not use realpath(3)
122 #if defined(__NetBSD__)
123 # if !__NetBSD_Prereq__(7,0,0)
124 # undef BUP_HAVE_POSIX_REALPATH
126 // OpenBSD: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/sys/param.h.diff?r1=1.91&r2=1.92&f=h
127 #elif defined(__OpenBSD__) && __OpenBSD__ < 201111
128 # undef BUP_HAVE_POSIX_REALPATH
132 bup_realpath(const char *pathname)
134 #ifdef BUP_HAVE_POSIX_REALPATH
135 return realpath(pathname, NULL);
137 char resolvedname[PATH_MAX];
138 char *ret = realpath(pathname, resolvedname);
140 assert(ret == resolvedname);
147 #if defined(__APPLE__) && defined(__MACH__)
149 static char *exe_parent_dir(const char * const argv_0) {
152 uint32_t size = sizeof(spath);
153 int rc = _NSGetExecutablePath(spath, &size);
155 mpath = malloc(size);
156 if (!mpath) die(2, "unable to allocate memory for executable path\n");
157 rc = _NSGetExecutablePath(mpath, &size);
159 if(rc != 0) die(2, "unable to find executable path\n");
160 char *path = mpath ? mpath : spath;
161 char *abs_exe = bup_realpath(path);
163 die(2, "cannot resolve path (%s): %s\n", strerror(errno), path);
164 char * const abs_parent = strdup(dirname(abs_exe));
166 if (mpath) free(mpath);
171 #elif defined(__FreeBSD__)
173 static char *exe_path ()
175 const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
177 int rc = sysctl (mib, 4, NULL, &path_len, NULL, 0);
178 if (rc != 0) die(2, "unable to determine executable path length\n");
179 char *path = malloc (path_len);
180 if (!path) die(2, "unable to allocate memory for executable path\n");
181 rc = sysctl (mib, 4, path, &path_len, NULL, 0);
182 if (rc != 0) die(2, "unable to determine executable path via sysctl\n");
186 static char *exe_parent_dir(const char * const argv_0)
188 char * const exe = exe_path();
189 if (!exe) die(2, "unable to determine executable path\n");
190 char * const parent = strdup(dirname(exe));
191 if (!parent) die(2, "unable to determine parent directory of executable\n");
196 #else // not defined(__FreeBSD__)
198 /// Use /proc if possible, and if all else fails, search in the PATH
200 #if defined(__linux__)
201 # define PROC_SELF_EXE "/proc/self/exe"
202 #elif defined(__sun) || defined (sun)
203 # define PROC_SELF_EXE "/proc/self/path/a.out"
205 # define PROC_SELF_EXE NULL
208 static char *find_in_path(const char * const name, const char * const path)
211 char *tmp_path = strdup(path);
214 char *tok_path = tmp_path;
215 while ((elt = strtok(tok_path, ":")) != NULL) {
218 int rc = asprintf(&candidate, "%s/%s", elt, name);
221 rc = stat(candidate, &st);
224 case EACCES: case ELOOP: case ENOENT: case ENAMETOOLONG:
228 die(2, "cannot stat %s: %s\n", candidate, strerror(errno));
231 } else if (S_ISREG(st.st_mode)) {
232 if (access(candidate, X_OK) == 0) {
237 case EACCES: case ELOOP: case ENOENT: case ENAMETOOLONG:
241 die(2, "cannot determine executability of %s: %s\n",
242 candidate, strerror(errno));
252 static char *find_exe_parent(const char * const argv_0)
254 char *candidate = NULL;
255 const char * const slash = strchr(argv_0, '/');
257 candidate = strdup(argv_0);
260 const char * const env_path = getenv("PATH");
262 die(2, "no PATH and executable isn't relative or absolute: %s\n",
264 char *path_exe = find_in_path(argv_0, env_path);
266 char * abs_exe = bup_realpath(path_exe);
268 die(2, "cannot resolve path (%s): %s\n",
269 strerror(errno), path_exe);
277 char * const abs_exe = bup_realpath(candidate);
279 die(2, "cannot resolve path (%s): %s\n", strerror(errno), candidate);
281 char * const abs_parent = strdup(dirname(abs_exe));
287 static char *exe_parent_dir(const char * const argv_0)
289 if (PROC_SELF_EXE != NULL) {
292 size_t path_n = sizeof(sbuf);
295 len = readlink(PROC_SELF_EXE, path, path_n);
296 if (len == -1 || (size_t) len != path_n)
298 if (!INT_MULTIPLY_OK(path_n, 2, &path_n))
299 die(2, "memory buffer for executable path would be too big\n");
300 if (path != sbuf) free(path);
301 path = malloc(path_n);
303 die(2, "unable to allocate memory for executable path\n");
307 char *result = strdup(dirname(path));
313 case ENOENT: case EACCES: case EINVAL: case ELOOP: case ENOTDIR:
317 die(2, "cannot resolve %s: %s\n", path, strerror(errno));
323 return find_exe_parent(argv_0);
326 #endif // use /proc if possible, and if all else fails, search in the PATh
329 setenv_or_die(const char *name, const char *value)
331 int rc = setenv(name, value, 1);
333 die(2, "setenv %s=%s failed (%s)\n", name, value, strerror(errno));
337 prepend_lib_to_pythonpath(const char * const exec_path,
338 const char * const relative_path)
340 char *parent = exe_parent_dir(exec_path);
343 int rc = asprintf(&bupmodpath, "%s/%s", parent, relative_path);
346 rc = stat(bupmodpath, &st);
348 die(2, "unable find lib dir (%s): %s\n", strerror(errno), bupmodpath);
349 if (!S_ISDIR(st.st_mode))
350 die(2, "lib path is not dir: %s\n", bupmodpath);
351 char *curpypath = getenv("PYTHONPATH");
354 int rc = asprintf(&path, "%s:%s", bupmodpath, curpypath);
356 setenv_or_die("PYTHONPATH", path);
359 setenv_or_die("PYTHONPATH", bupmodpath);
366 #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 8
367 # define bup_py_main bup_py_bytes_main
369 # define bup_py_main Py_BytesMain
372 #if defined(BUP_DEV_BUP_PYTHON) && defined(BUP_DEV_BUP_EXEC)
373 # error "Both BUP_DEV_BUP_PYTHON and BUP_DEV_BUP_EXEC are defined"
376 #ifdef BUP_DEV_BUP_PYTHON
378 int main(int argc, char **argv)
383 setup_bup_main_module();
384 prepend_lib_to_pythonpath(argv[0], "../lib");
385 return bup_py_main (argc, argv);
388 #elif defined(BUP_DEV_BUP_EXEC)
390 int main(int argc, char **argv)
393 prog_argc = argc - 1;
394 prog_argv = argv + 1;
395 setup_bup_main_module();
396 prepend_lib_to_pythonpath(argv[0], "../lib");
398 return bup_py_main (1, argv);
399 // This can't handle a script with a name like "-c", but that's
400 // python's problem, not ours.
401 return bup_py_main (2, argv);
404 #else // normal bup command
406 int main(int argc, char **argv)
411 setup_bup_main_module();
412 prepend_lib_to_pythonpath(argv[0], "..");
413 char *bup_argv[] = { argv[0], "-m", "bup.main" };
414 return bup_py_main (3, bup_argv);
417 #endif // normal bup command