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