]> arthur.barton.de Git - bup.git/blob - lib/cmd/bup.c
Stash the env PYTHONPATH during startup and restore it in main
[bup.git] / lib / cmd / bup.c
1
2 #define PY_SSIZE_T_CLEAN
3 #define _GNU_SOURCE  1 // asprintf
4 #undef NDEBUG
5
6 // According to Python, its header has to go first:
7 //   http://docs.python.org/2/c-api/intro.html#include-files
8 //   http://docs.python.org/3/c-api/intro.html#include-files
9 #include <Python.h>
10
11 #include <libgen.h>
12 #include <limits.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/stat.h>
18 #if defined(__FreeBSD__) || defined(__NetBSD__)
19 # include <sys/sysctl.h>
20 #endif
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 __attribute__ ((format(printf, 2, 3)))
25 static void
26 die(int exit_status, const char * const msg, ...)
27 {
28     if (fputs("bup: ", stderr) == EOF)
29         exit(3);
30     va_list ap;
31     va_start(ap, msg);;
32     if (vfprintf(stderr, msg, ap) < 0)
33         exit(3);
34     va_end(ap);
35     exit(exit_status);
36 }
37
38 static int prog_argc = 0;
39 static char **prog_argv = NULL;
40 static char *orig_env_pythonpath = NULL;
41
42 static PyObject*
43 get_argv(PyObject *self, PyObject *args)
44 {
45     if (!PyArg_ParseTuple(args, ""))
46         return NULL;
47
48     PyObject *result = PyList_New(prog_argc);
49     for (int i = 0; i < prog_argc; i++) {
50         PyObject *s = PyBytes_FromString(prog_argv[i]);
51         if (!s)
52             die(2, "cannot convert argument to bytes: %s\n", prog_argv[i]);
53         PyList_SET_ITEM(result, i, s);
54     }
55     return result;
56 }
57
58 static PyMethodDef bup_main_methods[] = {
59     {"argv", get_argv, METH_VARARGS,
60      "Return the program's current argv array as a list of byte strings." },
61     {NULL, NULL, 0, NULL}
62 };
63
64 static int setup_module(PyObject *mod)
65 {
66     if (!orig_env_pythonpath) {
67         PyObject_SetAttrString(mod, "env_pythonpath", Py_None);
68     } else {
69         PyObject *py_p = PyBytes_FromString(orig_env_pythonpath);
70         if (!py_p)
71             die(2, "cannot convert PYTHONPATH to bytes: %s\n",
72                 orig_env_pythonpath);
73         PyObject_SetAttrString(mod, "env_pythonpath", py_p);
74         Py_DECREF(py_p);
75     }
76     return 1;
77 }
78
79 #if PY_MAJOR_VERSION >= 3
80
81 static struct PyModuleDef bup_main_module_def = {
82     .m_base = PyModuleDef_HEAD_INIT,
83     .m_name = "bup_main",
84     .m_doc = "Built-in bup module providing direct access to argv.",
85     .m_size = -1,
86     .m_methods = bup_main_methods
87 };
88
89 PyObject *
90 PyInit_bup_main(void) {
91     PyObject *mod =  PyModule_Create(&bup_main_module_def);
92     if (!setup_module(mod))
93     {
94         Py_DECREF(mod);
95         return NULL;
96     }
97     return mod;
98 }
99
100 #else // PY_MAJOR_VERSION < 3
101
102 void PyInit_bup_main(void)
103 {
104     PyObject *mod = Py_InitModule("bup_main", bup_main_methods);
105     if (mod == NULL) {
106         PyErr_SetString(PyExc_RuntimeError, "bup._helpers init failed");
107         return;
108     }
109     if (!setup_module(mod))
110     {
111         PyErr_SetString(PyExc_RuntimeError, "bup._helpers set up failed");
112         Py_DECREF(mod);
113         return;
114     }
115 }
116
117 #endif // PY_MAJOR_VERSION < 3
118
119 static void
120 setup_bup_main_module(void) {
121
122     char *path = getenv("PYTHONPATH");
123     if (path)
124         orig_env_pythonpath = strdup(path);
125
126     if (PyImport_AppendInittab("bup_main", PyInit_bup_main) == -1)
127         die(2, "unable to register bup_main module\n");
128 }
129
130 #if defined(__APPLE__) && defined(__MACH__)
131
132 static char *exe_parent_dir(const char * const argv_0) {
133     char path[4096];  // FIXME
134     uint32_t size = sizeof(path);
135     if(_NSGetExecutablePath(path, &size) !=0)
136         die(2, "unable to find executable path\n");
137     char * abs_exe = realpath(path, NULL);
138     if (!abs_exe)
139         die(2, "cannot resolve path (%s): %s\n", strerror(errno), path);
140     char * const abs_parent = strdup(dirname(abs_exe));
141     assert(abs_parent);
142     free(abs_exe);
143     return abs_parent;
144 }
145
146 #elif defined(__FreeBSD__) || defined(__NetBSD__)
147
148 static char *exe_path ()
149 {
150     const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
151     size_t path_len;
152     int rc = sysctl (mib, 4, NULL, &path_len, NULL, 0);
153     if (rc != 0) die(2, "unable to determine executable path length\n");
154     char *path = malloc (path_len);
155     if (!path) die(2, "unable to allocate memory for executable path\n");
156     rc = sysctl (mib, 4, path, &path_len, NULL, 0);
157     if (rc != 0) die(2, "unable to determine executable path via sysctl\n");
158     return path;
159 }
160
161 static char *exe_parent_dir(const char * const argv_0)
162 {
163     char * const exe = exe_path();
164     if (!exe) die(2, "unable to determine executable path\n");
165     char * const parent = strdup(dirname(exe));
166     if (!parent) die(2, "unable to determine parent directory of executable\n");
167     free(exe);
168     return parent;
169 }
170
171 #else // not defined(__FreeBSD__) || defined(__NetBSD__)
172
173 /// Use /proc if possible, and if all else fails, search in the PATH
174
175 #if defined(__linux__)
176 # define PROC_SELF_EXE "/proc/self/exe"
177 #elif defined(__sun) || defined (sun)
178 # define PROC_SELF_EXE "/proc/self/path/a.out"
179 #else
180 # define PROC_SELF_EXE NULL
181 #endif
182
183 static char *find_in_path(const char * const name, const char * const path)
184 {
185     char *result = NULL;
186     char *tmp_path = strdup(path);
187     assert(tmp_path);
188     const char *elt;
189     char *tok_path = tmp_path;
190     while ((elt = strtok(tok_path, ":")) != NULL) {
191         tok_path = NULL;
192         char *candidate;
193         int rc = asprintf(&candidate, "%s/%s", elt, name);
194         assert(rc >= 0);
195         struct stat st;
196         rc = stat(candidate, &st);
197         if (rc != 0) {
198             switch (errno) {
199                 case EACCES: case ELOOP: case ENOENT: case ENAMETOOLONG:
200                 case ENOTDIR:
201                     break;
202                 default:
203                     die(2, "cannot stat %s: %s\n", candidate, strerror(errno));
204                     break;
205             }
206         } else if (S_ISREG(st.st_mode)) {
207             if (access(candidate, X_OK) == 0) {
208                 result = candidate;
209                 break;
210             }
211             switch (errno) {
212                 case EACCES: case ELOOP: case ENOENT: case ENAMETOOLONG:
213                 case ENOTDIR:
214                     break;
215                 default:
216                     die(2, "cannot determine executability of %s: %s\n",
217                         candidate, strerror(errno));
218                     break;
219             }
220         }
221         free(candidate);
222     }
223     free(tmp_path);
224     return result;
225 }
226
227 static char *find_exe_parent(const char * const argv_0)
228 {
229     char *candidate = NULL;
230     const char * const slash = index(argv_0, '/');
231     if (slash) {
232         candidate = strdup(argv_0);
233         assert(candidate);
234     } else {
235         const char * const env_path = getenv("PATH");
236         if (!env_path)
237             die(2, "no PATH and executable isn't relative or absolute: %s\n",
238                 argv_0);
239         char *path_exe = find_in_path(argv_0, env_path);
240         if (path_exe) {
241             char * abs_exe = realpath(path_exe, NULL);
242             if (!abs_exe)
243                 die(2, "cannot resolve path (%s): %s\n",
244                     strerror(errno), path_exe);
245             free(path_exe);
246             candidate = abs_exe;
247         }
248     }
249     if (!candidate)
250         return NULL;
251
252     char * const abs_exe = realpath(candidate, NULL);
253     if (!abs_exe)
254         die(2, "cannot resolve path (%s): %s\n", strerror(errno), candidate);
255     free(candidate);
256     char * const abs_parent = strdup(dirname(abs_exe));
257     assert(abs_parent);
258     free(abs_exe);
259     return abs_parent;
260 }
261
262 static char *exe_parent_dir(const char * const argv_0)
263 {
264     if (PROC_SELF_EXE != NULL) {
265         char path[4096];  // FIXME
266         int len = readlink(PROC_SELF_EXE, path, sizeof(path));
267         if (len == sizeof(path))
268             die(2, "unable to resolve symlink %s: %s\n",
269                 PROC_SELF_EXE, strerror(errno));
270         if (len != -1) {
271             path[len] = '\0';
272             return strdup(dirname(path));
273         }
274         switch (errno) {
275             case ENOENT: case EACCES: case EINVAL: case ELOOP: case ENOTDIR:
276             case ENAMETOOLONG:
277                 break;
278             default:
279                 die(2, "cannot resolve %s: %s\n", path, strerror(errno));
280                 break;
281         }
282     }
283     return find_exe_parent(argv_0);
284 }
285
286 #endif // use /proc if possible, and if all else fails, search in the PATh
287
288 static void
289 setenv_or_die(const char *name, const char *value)
290 {
291     int rc = setenv(name, value, 1);
292     if (rc != 0)
293         die(2, "setenv %s=%s failed (%s)\n", name, value, strerror(errno));
294 }
295
296 static void
297 prepend_lib_to_pythonpath(const char * const exec_path,
298                           const char * const relative_path)
299 {
300     char *parent = exe_parent_dir(exec_path);
301     assert(parent);
302     char *bupmodpath;
303     int rc = asprintf(&bupmodpath, "%s/%s", parent, relative_path);
304     assert(rc >= 0);
305     struct stat st;
306     rc = stat(bupmodpath, &st);
307     if (rc != 0)
308         die(2, "unable find lib dir (%s): %s\n", strerror(errno), bupmodpath);
309     if (!S_ISDIR(st.st_mode))
310         die(2, "lib path is not dir: %s\n", bupmodpath);
311     char *curpypath = getenv("PYTHONPATH");
312     if (curpypath) {
313         char *path;
314         int rc = asprintf(&path, "%s:%s", bupmodpath, curpypath);
315         assert(rc >= 0);
316         setenv_or_die("PYTHONPATH", path);
317         free(path);
318     } else {
319         setenv_or_die("PYTHONPATH", bupmodpath);
320     }
321
322     free(bupmodpath);
323     free(parent);
324 }
325
326 #if PY_MAJOR_VERSION > 2
327 #define bup_py_main Py_BytesMain
328 # else
329 #define bup_py_main Py_Main
330 #endif
331
332 #if defined(BUP_DEV_BUP_PYTHON) && defined(BUP_DEV_BUP_EXEC)
333 # error "Both BUP_DEV_BUP_PYTHON and BUP_DEV_BUP_EXEC are defined"
334 #endif
335
336 #ifdef BUP_DEV_BUP_PYTHON
337
338 int main(int argc, char **argv)
339 {
340     prog_argc = argc;
341     prog_argv = argv;
342     setup_bup_main_module();
343     prepend_lib_to_pythonpath(argv[0], "../lib");
344     return bup_py_main (argc, argv);
345 }
346
347 #elif defined(BUP_DEV_BUP_EXEC)
348
349 int main(int argc, char **argv)
350 {
351     prog_argc = argc - 1;
352     prog_argv = argv + 1;
353     setup_bup_main_module();
354     prepend_lib_to_pythonpath(argv[0], "../lib");
355     if (argc == 1)
356         return bup_py_main (1, argv);
357     // This can't handle a script with a name like "-c", but that's
358     // python's problem, not ours.
359     return bup_py_main (2, argv);
360 }
361
362 #else // normal bup command
363
364 int main(int argc, char **argv)
365 {
366     prog_argc = argc;
367     prog_argv = argv;
368     setup_bup_main_module();
369     prepend_lib_to_pythonpath(argv[0], "..");
370     char *bup_argv[] = { argv[0], "-m", "bup.main" };
371     return bup_py_main (3, bup_argv);
372 }
373
374 #endif // normal bup command