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");
100 * Older realpath implementations (e.g. 4.4BSD) required the second
101 * argument to be non-NULL, and then POSIX added the option of NULL
102 * with the semantics of malloc'ing a big-enough buffer. Define a
103 * helper function with the NULL semantics to accomodate older
106 * gnulib has a list of systems that are known to reject NULL as the
108 * https://www.gnu.org/software/gnulib/manual/html_node/realpath.html
111 #define BUP_HAVE_POSIX_REALPATH
113 // FreeBSD < 7: bup's FreeBSD code does not use realpath(3)
114 #if defined(__NetBSD__)
115 # if !__NetBSD_Prereq__(7,0,0)
116 # undef BUP_HAVE_POSIX_REALPATH
118 // OpenBSD: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/sys/param.h.diff?r1=1.91&r2=1.92&f=h
119 #elif defined(__OpenBSD__) && __OpenBSD__ < 201111
120 # undef BUP_HAVE_POSIX_REALPATH
124 bup_realpath(const char *pathname)
126 #ifdef BUP_HAVE_POSIX_REALPATH
127 return realpath(pathname, NULL);
129 char resolvedname[PATH_MAX];
130 char *ret = realpath(pathname, resolvedname);
132 assert(ret == resolvedname);
139 #if defined(__APPLE__) && defined(__MACH__)
141 static char *exe_parent_dir(const char * const argv_0) {
144 uint32_t size = sizeof(spath);
145 int rc = _NSGetExecutablePath(spath, &size);
147 mpath = malloc(size);
148 if (!mpath) die(2, "unable to allocate memory for executable path\n");
149 rc = _NSGetExecutablePath(mpath, &size);
151 if(rc != 0) die(2, "unable to find executable path\n");
152 char *path = mpath ? mpath : spath;
153 char *abs_exe = bup_realpath(path);
155 die(2, "cannot resolve path (%s): %s\n", strerror(errno), path);
156 char * const abs_parent = strdup(dirname(abs_exe));
158 if (mpath) free(mpath);
163 #elif defined(__FreeBSD__)
165 static char *exe_path ()
167 const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
169 int rc = sysctl (mib, 4, NULL, &path_len, NULL, 0);
170 if (rc != 0) die(2, "unable to determine executable path length\n");
171 char *path = malloc (path_len);
172 if (!path) die(2, "unable to allocate memory for executable path\n");
173 rc = sysctl (mib, 4, path, &path_len, NULL, 0);
174 if (rc != 0) die(2, "unable to determine executable path via sysctl\n");
178 static char *exe_parent_dir(const char * const argv_0)
180 char * const exe = exe_path();
181 if (!exe) die(2, "unable to determine executable path\n");
182 char * const parent = strdup(dirname(exe));
183 if (!parent) die(2, "unable to determine parent directory of executable\n");
188 #else // not defined(__FreeBSD__)
190 /// Use /proc if possible, and if all else fails, search in the PATH
192 #if defined(__linux__)
193 # define PROC_SELF_EXE "/proc/self/exe"
194 #elif defined(__sun) || defined (sun)
195 # define PROC_SELF_EXE "/proc/self/path/a.out"
197 # define PROC_SELF_EXE NULL
200 static char *find_in_path(const char * const name, const char * const path)
203 char *tmp_path = strdup(path);
206 char *tok_path = tmp_path;
207 while ((elt = strtok(tok_path, ":")) != NULL) {
210 int rc = asprintf(&candidate, "%s/%s", elt, name);
213 rc = stat(candidate, &st);
216 case EACCES: case ELOOP: case ENOENT: case ENAMETOOLONG:
220 die(2, "cannot stat %s: %s\n", candidate, strerror(errno));
223 } else if (S_ISREG(st.st_mode)) {
224 if (access(candidate, X_OK) == 0) {
229 case EACCES: case ELOOP: case ENOENT: case ENAMETOOLONG:
233 die(2, "cannot determine executability of %s: %s\n",
234 candidate, strerror(errno));
244 static char *find_exe_parent(const char * const argv_0)
246 char *candidate = NULL;
247 const char * const slash = strchr(argv_0, '/');
249 candidate = strdup(argv_0);
252 const char * const env_path = getenv("PATH");
254 die(2, "no PATH and executable isn't relative or absolute: %s\n",
256 char *path_exe = find_in_path(argv_0, env_path);
258 char * abs_exe = bup_realpath(path_exe);
260 die(2, "cannot resolve path (%s): %s\n",
261 strerror(errno), path_exe);
269 char * const abs_exe = bup_realpath(candidate);
271 die(2, "cannot resolve path (%s): %s\n", strerror(errno), candidate);
273 char * const abs_parent = strdup(dirname(abs_exe));
279 static char *exe_parent_dir(const char * const argv_0)
281 if (PROC_SELF_EXE != NULL) {
284 size_t path_n = sizeof(sbuf);
287 len = readlink(PROC_SELF_EXE, path, path_n);
288 if (len == -1 || (size_t) len != path_n)
290 if (!INT_MULTIPLY_OK(path_n, 2, &path_n))
291 die(2, "memory buffer for executable path would be too big\n");
292 if (path != sbuf) free(path);
293 path = malloc(path_n);
295 die(2, "unable to allocate memory for executable path\n");
299 char *result = strdup(dirname(path));
305 case ENOENT: case EACCES: case EINVAL: case ELOOP: case ENOTDIR:
309 die(2, "cannot resolve %s: %s\n", path, strerror(errno));
315 return find_exe_parent(argv_0);
318 #endif // use /proc if possible, and if all else fails, search in the PATh
321 setenv_or_die(const char *name, const char *value)
323 int rc = setenv(name, value, 1);
325 die(2, "setenv %s=%s failed (%s)\n", name, value, strerror(errno));
329 prepend_lib_to_pythonpath(const char * const exec_path,
330 const char * const relative_path)
332 char *parent = exe_parent_dir(exec_path);
335 int rc = asprintf(&bupmodpath, "%s/%s", parent, relative_path);
338 rc = stat(bupmodpath, &st);
340 die(2, "unable find lib dir (%s): %s\n", strerror(errno), bupmodpath);
341 if (!S_ISDIR(st.st_mode))
342 die(2, "lib path is not dir: %s\n", bupmodpath);
343 char *curpypath = getenv("PYTHONPATH");
346 int rc = asprintf(&path, "%s:%s", bupmodpath, curpypath);
348 setenv_or_die("PYTHONPATH", path);
351 setenv_or_die("PYTHONPATH", bupmodpath);
358 #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 8
359 # define bup_py_main bup_py_bytes_main
361 # define bup_py_main Py_BytesMain
364 #if defined(BUP_DEV_BUP_PYTHON) && defined(BUP_DEV_BUP_EXEC)
365 # error "Both BUP_DEV_BUP_PYTHON and BUP_DEV_BUP_EXEC are defined"
368 #ifdef BUP_DEV_BUP_PYTHON
370 int main(int argc, char **argv)
375 setup_bup_main_module();
376 prepend_lib_to_pythonpath(argv[0], "../lib");
377 return bup_py_main (argc, argv);
380 #elif defined(BUP_DEV_BUP_EXEC)
382 int main(int argc, char **argv)
385 prog_argc = argc - 1;
386 prog_argv = argv + 1;
387 setup_bup_main_module();
388 prepend_lib_to_pythonpath(argv[0], "../lib");
390 return bup_py_main (1, argv);
391 // This can't handle a script with a name like "-c", but that's
392 // python's problem, not ours.
393 return bup_py_main (2, argv);
396 #else // normal bup command
398 int main(int argc, char **argv)
403 setup_bup_main_module();
404 prepend_lib_to_pythonpath(argv[0], "..");
405 char *bup_argv[] = { argv[0], "-m", "bup.main" };
406 return bup_py_main (3, bup_argv);
409 #endif // normal bup command