]> arthur.barton.de Git - bup.git/blob - lib/cmd/bup.c
e61f10610c4bb98eb673c86cd947e01584329c10
[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/3/c-api/intro.html#include-files
8 #include <Python.h>
9
10 #if PY_MAJOR_VERSION < 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 7)
11 #define BUP_STR(x) #x
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)
15 #error "Halting"
16 #endif
17
18 #include <libgen.h>
19 #include <limits.h>
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #if defined(__FreeBSD__) || defined(__NetBSD__)
26 # include <sys/sysctl.h>
27 #endif
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 #include "bup/compat.h"
32 #include "bup/intprops.h"
33 #include "bup/io.h"
34
35 static int prog_argc = 0;
36 static char **prog_argv = NULL;
37 static char *orig_env_pythonpath = NULL;
38
39 static PyObject*
40 get_argv(PyObject *self, PyObject *args)
41 {
42     if (!PyArg_ParseTuple(args, ""))
43         return NULL;
44
45     PyObject *result = PyList_New(prog_argc);
46     int i;
47     for (i = 0; i < prog_argc; i++) {
48         PyObject *s = PyBytes_FromString(prog_argv[i]);
49         if (!s)
50             die(2, "cannot convert argument to bytes: %s\n", prog_argv[i]);
51         PyList_SET_ITEM(result, i, s);
52     }
53     return result;
54 }
55
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." },
59     {NULL, NULL, 0, NULL}
60 };
61
62 static int setup_module(PyObject *mod)
63 {
64     if (!orig_env_pythonpath) {
65         PyObject_SetAttrString(mod, "env_pythonpath", Py_None);
66     } else {
67         PyObject *py_p = PyBytes_FromString(orig_env_pythonpath);
68         if (!py_p)
69             die(2, "cannot convert PYTHONPATH to bytes: %s\n",
70                 orig_env_pythonpath);
71         PyObject_SetAttrString(mod, "env_pythonpath", py_p);
72         Py_DECREF(py_p);
73     }
74     return 1;
75 }
76
77 static struct PyModuleDef bup_main_module_def = {
78     .m_base = PyModuleDef_HEAD_INIT,
79     .m_name = "bup_main",
80     .m_doc = "Built-in bup module providing direct access to argv.",
81     .m_size = -1,
82     .m_methods = bup_main_methods
83 };
84
85 PyObject *
86 PyInit_bup_main(void) {
87     PyObject *mod =  PyModule_Create(&bup_main_module_def);
88     if (!setup_module(mod))
89     {
90         Py_DECREF(mod);
91         return NULL;
92     }
93     return mod;
94 }
95
96 static void
97 setup_bup_main_module(void) {
98
99     char *path = getenv("PYTHONPATH");
100     if (path)
101         orig_env_pythonpath = strdup(path);
102
103     if (PyImport_AppendInittab("bup_main", PyInit_bup_main) == -1)
104         die(2, "unable to register bup_main module\n");
105 }
106
107 /*
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
112  * platforms.
113  *
114  * gnulib has a list of systems that are known to reject NULL as the
115  * 2nd argument:
116  *   https://www.gnu.org/software/gnulib/manual/html_node/realpath.html
117  */
118
119 #define BUP_HAVE_POSIX_REALPATH
120
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
125 #  endif
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
129 #endif
130
131 char *
132 bup_realpath(const char *pathname)
133 {
134 #ifdef BUP_HAVE_POSIX_REALPATH
135     return realpath(pathname, NULL);
136 #else
137     char resolvedname[PATH_MAX];
138     char *ret = realpath(pathname, resolvedname);
139     if (ret != NULL) {
140         assert(ret == resolvedname);
141         ret = strdup(ret);
142     }
143     return ret;
144 #endif
145 }
146
147 #if defined(__APPLE__) && defined(__MACH__)
148
149 static char *exe_parent_dir(const char * const argv_0) {
150     char *mpath = NULL;
151     char spath[2048];
152     uint32_t size = sizeof(spath);
153     int rc = _NSGetExecutablePath(spath, &size);
154     if (rc == -1) {
155         mpath = malloc(size);
156         if (!mpath) die(2, "unable to allocate memory for executable path\n");
157         rc = _NSGetExecutablePath(mpath, &size);
158     }
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);
162     if (!abs_exe)
163         die(2, "cannot resolve path (%s): %s\n", strerror(errno), path);
164     char * const abs_parent = strdup(dirname(abs_exe));
165     assert(abs_parent);
166     if (mpath) free(mpath);
167     free(abs_exe);
168     return abs_parent;
169 }
170
171 #elif defined(__FreeBSD__)
172
173 static char *exe_path ()
174 {
175     const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
176     size_t path_len;
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");
183     return path;
184 }
185
186 static char *exe_parent_dir(const char * const argv_0)
187 {
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");
192     free(exe);
193     return parent;
194 }
195
196 #else // not defined(__FreeBSD__)
197
198 /// Use /proc if possible, and if all else fails, search in the PATH
199
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"
204 #else
205 # define PROC_SELF_EXE NULL
206 #endif
207
208 static char *find_in_path(const char * const name, const char * const path)
209 {
210     char *result = NULL;
211     char *tmp_path = strdup(path);
212     assert(tmp_path);
213     const char *elt;
214     char *tok_path = tmp_path;
215     while ((elt = strtok(tok_path, ":")) != NULL) {
216         tok_path = NULL;
217         char *candidate;
218         int rc = asprintf(&candidate, "%s/%s", elt, name);
219         assert(rc >= 0);
220         struct stat st;
221         rc = stat(candidate, &st);
222         if (rc != 0) {
223             switch (errno) {
224                 case EACCES: case ELOOP: case ENOENT: case ENAMETOOLONG:
225                 case ENOTDIR:
226                     break;
227                 default:
228                     die(2, "cannot stat %s: %s\n", candidate, strerror(errno));
229                     break;
230             }
231         } else if (S_ISREG(st.st_mode)) {
232             if (access(candidate, X_OK) == 0) {
233                 result = candidate;
234                 break;
235             }
236             switch (errno) {
237                 case EACCES: case ELOOP: case ENOENT: case ENAMETOOLONG:
238                 case ENOTDIR:
239                     break;
240                 default:
241                     die(2, "cannot determine executability of %s: %s\n",
242                         candidate, strerror(errno));
243                     break;
244             }
245         }
246         free(candidate);
247     }
248     free(tmp_path);
249     return result;
250 }
251
252 static char *find_exe_parent(const char * const argv_0)
253 {
254     char *candidate = NULL;
255     const char * const slash = strchr(argv_0, '/');
256     if (slash) {
257         candidate = strdup(argv_0);
258         assert(candidate);
259     } else {
260         const char * const env_path = getenv("PATH");
261         if (!env_path)
262             die(2, "no PATH and executable isn't relative or absolute: %s\n",
263                 argv_0);
264         char *path_exe = find_in_path(argv_0, env_path);
265         if (path_exe) {
266             char * abs_exe = bup_realpath(path_exe);
267             if (!abs_exe)
268                 die(2, "cannot resolve path (%s): %s\n",
269                     strerror(errno), path_exe);
270             free(path_exe);
271             candidate = abs_exe;
272         }
273     }
274     if (!candidate)
275         return NULL;
276
277     char * const abs_exe = bup_realpath(candidate);
278     if (!abs_exe)
279         die(2, "cannot resolve path (%s): %s\n", strerror(errno), candidate);
280     free(candidate);
281     char * const abs_parent = strdup(dirname(abs_exe));
282     assert(abs_parent);
283     free(abs_exe);
284     return abs_parent;
285 }
286
287 static char *exe_parent_dir(const char * const argv_0)
288 {
289     if (PROC_SELF_EXE != NULL) {
290         char sbuf[2048];
291         char *path = sbuf;
292         size_t path_n = sizeof(sbuf);
293         ssize_t len;
294         while (1) {
295             len = readlink(PROC_SELF_EXE, path, path_n);
296             if (len == -1 || (size_t) len != path_n)
297                 break;
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);
302             if (!path)
303                 die(2, "unable to allocate memory for executable path\n");
304         }
305         if (len != -1) {
306             path[len] = '\0';
307             char *result = strdup(dirname(path));
308             if (path != sbuf)
309                 free(path);
310             return result;
311         }
312         switch (errno) {
313         case ENOENT: case EACCES: case EINVAL: case ELOOP: case ENOTDIR:
314         case ENAMETOOLONG:
315             break;
316         default:
317             die(2, "cannot resolve %s: %s\n", path, strerror(errno));
318             break;
319         }
320         if (path != sbuf)
321             free(path);
322     }
323     return find_exe_parent(argv_0);
324 }
325
326 #endif // use /proc if possible, and if all else fails, search in the PATh
327
328 static void
329 setenv_or_die(const char *name, const char *value)
330 {
331     int rc = setenv(name, value, 1);
332     if (rc != 0)
333         die(2, "setenv %s=%s failed (%s)\n", name, value, strerror(errno));
334 }
335
336 static void
337 prepend_lib_to_pythonpath(const char * const exec_path,
338                           const char * const relative_path)
339 {
340     char *parent = exe_parent_dir(exec_path);
341     assert(parent);
342     char *bupmodpath;
343     int rc = asprintf(&bupmodpath, "%s/%s", parent, relative_path);
344     assert(rc >= 0);
345     struct stat st;
346     rc = stat(bupmodpath, &st);
347     if (rc != 0)
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");
352     if (curpypath) {
353         char *path;
354         int rc = asprintf(&path, "%s:%s", bupmodpath, curpypath);
355         assert(rc >= 0);
356         setenv_or_die("PYTHONPATH", path);
357         free(path);
358     } else {
359         setenv_or_die("PYTHONPATH", bupmodpath);
360     }
361
362     free(bupmodpath);
363     free(parent);
364 }
365
366 #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 8
367 # define bup_py_main bup_py_bytes_main
368 #else
369 # define bup_py_main Py_BytesMain
370 #endif
371
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"
374 #endif
375
376 #ifdef BUP_DEV_BUP_PYTHON
377
378 int main(int argc, char **argv)
379 {
380     assert(argc > 0);
381     prog_argc = argc;
382     prog_argv = argv;
383     setup_bup_main_module();
384     prepend_lib_to_pythonpath(argv[0], "../lib");
385     return bup_py_main (argc, argv);
386 }
387
388 #elif defined(BUP_DEV_BUP_EXEC)
389
390 int main(int argc, char **argv)
391 {
392     assert(argc > 0);
393     prog_argc = argc - 1;
394     prog_argv = argv + 1;
395     setup_bup_main_module();
396     prepend_lib_to_pythonpath(argv[0], "../lib");
397     if (argc == 1)
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);
402 }
403
404 #else // normal bup command
405
406 int main(int argc, char **argv)
407 {
408     assert(argc > 0);
409     prog_argc = argc;
410     prog_argv = 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);
415 }
416
417 #endif // normal bup command