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