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