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