GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/handle.c Lines: 101 106 95.3 %
Date: 2020-02-19 21:21:49 Branches: 27 42 64.3 %

Line Branch Exec Source
1
/**
2
 * handle.c : wrapper class around alpm_handle_t
3
 *
4
 *  Copyright (c) 2011 Rémy Oudompheng <remy@archlinux.org>
5
 *
6
 *  This file is part of pyalpm.
7
 *
8
 *  pyalpm is free software: you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation, either version 3 of the License, or
11
 *  (at your option) any later version.
12
 *
13
 *  pyalpm is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
19
 *  along with pyalpm.  If not, see <http://www.gnu.org/licenses/>.
20
 *
21
 */
22
23
#include <pyconfig.h>
24
#include <alpm.h>
25
#include <Python.h>
26
27
#include "handle.h"
28
#include "package.h"
29
#include "db.h"
30
#include "options.h"
31
#include "util.h"
32
33
PyTypeObject AlpmHandleType;
34
35
108
static PyObject *pyalpm_handle_from_pmhandle(void* data) {
36
108
  alpm_handle_t *handle = (alpm_handle_t*)data;
37
108
  AlpmHandle *self;
38
108
  self = (AlpmHandle*)AlpmHandleType.tp_alloc(&AlpmHandleType, 0);
39
108
  if (self == NULL) {
40
    PyErr_SetString(PyExc_RuntimeError, "unable to create pyalpm.Handle object");
41
    return NULL;
42
  }
43
44
108
  self->c_data = handle;
45
  /* memset(self->py_callbacks, 0, N_CALLBACKS * sizeof(PyObject*)); */
46
108
  return (PyObject *)self;
47
}
48
49
/*pyalpm functions*/
50
111
PyObject* pyalpm_initialize(PyTypeObject *subtype, PyObject *args, PyObject *kwargs)
51
{
52
111
  const char *root;
53
111
  const char *dbpath;
54
111
  alpm_handle_t *h;
55
111
  enum _alpm_errno_t errcode = 0;
56
111
  if(!PyArg_ParseTuple(args, "ss", &root, &dbpath)) {
57
    return NULL;
58
  }
59
60
111
  h = alpm_initialize(root, dbpath, &errcode);
61
111
  if (h) {
62
108
    return pyalpm_handle_from_pmhandle((void*)h);
63
  } else {
64
3
    RET_ERR("could not create a libalpm handle", errcode, NULL);
65
  }
66
}
67
68
/* Database getters/setters */
69
70
39
static PyObject* pyalpm_get_localdb(PyObject *self, PyObject *dummy) {
71
39
  alpm_handle_t *handle = ALPM_HANDLE(self);
72
39
  return pyalpm_db_from_pmdb(alpm_get_localdb(handle));
73
}
74
75
24
static PyObject* pyalpm_get_syncdbs(PyObject *self, PyObject *dummy) {
76
24
  alpm_handle_t *handle = ALPM_HANDLE(self);
77
24
  return alpmlist_to_pylist(alpm_get_syncdbs(handle),
78
			    pyalpm_db_from_pmdb);
79
}
80
81
27
static PyObject* pyalpm_register_syncdb(PyObject *self, PyObject *args) {
82
27
  alpm_handle_t *handle = ALPM_HANDLE(self);
83
27
  const char *dbname;
84
27
  alpm_db_t *result;
85
27
  int pgp_level;
86
87
27
  if (!PyArg_ParseTuple(args, "si", &dbname, &pgp_level)) {
88
6
    PyErr_Format(PyExc_TypeError, "%s() takes a string and an integer", __func__);
89
6
    return NULL;
90
  }
91
92
21
  result = alpm_register_syncdb(handle, dbname, pgp_level);
93
21
  if (! result) {
94
3
    PyErr_Format(alpm_error, "unable to register sync database %s", dbname);
95
3
    return NULL;
96
  }
97
98
18
  return pyalpm_db_from_pmdb(result);
99
}
100
101
3
static PyObject* pyalpm_set_pkgreason(PyObject* self, PyObject* args) {
102
3
  alpm_handle_t *handle = ALPM_HANDLE(self);
103
3
  alpm_pkg_t *pmpkg = NULL;
104
3
  PyObject *pkg = NULL;
105
3
  alpm_pkgreason_t reason;
106
3
  int ret;
107
3
  if (!PyArg_ParseTuple(args, "O!i:set_pkgreason", &AlpmPackageType, &pkg, &reason)) {
108
    return NULL;
109
  }
110
3
  pmpkg = ALPM_PACKAGE(pkg);
111
3
  ret = alpm_pkg_set_reason(pmpkg, reason);
112
113
3
  if (ret == -1) RET_ERR("failed setting install reason", alpm_errno(handle), NULL);
114
  Py_RETURN_NONE;
115
}
116
117
/* String attributes get/setters */
118
struct _alpm_str_getset {
119
  const char *(*getter)(alpm_handle_t *);
120
  int (*setter)(alpm_handle_t *, const char *);
121
};
122
123
9
static PyObject *_get_string_attr(PyObject *self, const struct _alpm_str_getset *closure) {
124
9
  alpm_handle_t *handle = ALPM_HANDLE(self);
125
9
  const char *str = closure->getter(handle);
126
9
  if(str == NULL)
127
    RET_ERR("failed getting option value", alpm_errno(handle), NULL);
128
9
  return Py_BuildValue("s", str);
129
}
130
131
9
static int _set_string_attr(PyObject *self, PyObject *value, const struct _alpm_str_getset *closure) {
132
9
  alpm_handle_t *handle = ALPM_HANDLE(self);
133
9
  char *path = NULL;
134
9
  int ret;
135
9
  if (PyBytes_Check(value)) {
136
3
    path = strdup(PyBytes_AS_STRING(value));
137
6
  } else if (PyUnicode_Check(value)) {
138
3
    PyObject* utf8 = PyUnicode_AsUTF8String(value);
139
3
    path = strdup(PyBytes_AS_STRING(utf8));
140
3
    Py_DECREF(utf8);
141
  } else {
142
3
    PyErr_SetString(PyExc_TypeError, "logfile path must be a string");
143
3
    return -1;
144
  }
145
146
6
  ret = closure->setter(handle, path);
147
6
  free(path);
148
6
  if (ret == -1) RET_ERR("failed setting option value", alpm_errno(handle), -1);
149
  return 0;
150
}
151
152
static struct _alpm_str_getset root_getset    = { alpm_option_get_root, NULL };
153
static struct _alpm_str_getset dbpath_getset  = { alpm_option_get_dbpath, NULL };
154
static struct _alpm_str_getset lockfile_getset  = { alpm_option_get_lockfile, NULL };
155
static struct _alpm_str_getset logfile_getset = { alpm_option_get_logfile, alpm_option_set_logfile };
156
static struct _alpm_str_getset gpgdir_getset = { alpm_option_get_gpgdir, alpm_option_set_gpgdir };
157
static struct _alpm_str_getset arch_getset = { alpm_option_get_arch, alpm_option_set_arch };
158
159
/* Callback attributes get/setters */
160
typedef int (*alpm_cb_setter)(alpm_handle_t*, void*);
161
struct _alpm_cb_getset {
162
  alpm_cb_setter setter;
163
  void *cb_wrapper;
164
  pyalpm_callback_id id;
165
};
166
167
void pyalpm_eventcb(alpm_event_t event, void* data1, void *data2);
168
void pyalpm_questioncb(alpm_question_t question,
169
    void* data1, void *data2, void* data3, int* retcode);
170
void pyalpm_progresscb(alpm_progress_t op,
171
    const char* target_name, int percentage, size_t n_targets, size_t cur_target);
172
173
static struct _alpm_cb_getset cb_getsets[N_CALLBACKS] = {
174
  { (alpm_cb_setter)alpm_option_set_logcb, pyalpm_logcb, CB_LOG },
175
  { (alpm_cb_setter)alpm_option_set_dlcb, pyalpm_dlcb, CB_DOWNLOAD },
176
  { (alpm_cb_setter)alpm_option_set_fetchcb, pyalpm_fetchcb, CB_FETCH },
177
  { (alpm_cb_setter)alpm_option_set_totaldlcb, pyalpm_totaldlcb, CB_TOTALDL },
178
  { (alpm_cb_setter)alpm_option_set_eventcb, pyalpm_eventcb, CB_EVENT },
179
  { (alpm_cb_setter)alpm_option_set_questioncb, pyalpm_questioncb, CB_QUESTION },
180
  { (alpm_cb_setter)alpm_option_set_progresscb, pyalpm_progresscb, CB_PROGRESS },
181
};
182
183
/** Callback options
184
 * We use Python callable objects as callbacks: they are
185
 * stored in static variables, and the reference count is
186
 * increased accordingly.
187
 *
188
 * These Python functions are wrapped into C functions
189
 * that are passed to libalpm.
190
 */
191
PyObject *global_py_callbacks[N_CALLBACKS];
192
193
6
static PyObject* _get_cb_attr(PyObject *self, const struct _alpm_cb_getset *closure) {
194
  /* AlpmHandle *it = self; */
195
6
  PyObject *pycb = global_py_callbacks[closure->id];
196
6
  if (pycb == NULL) Py_RETURN_NONE;
197
3
  Py_INCREF(pycb);
198
3
  return pycb;
199
}
200
201
12
static int _set_cb_attr(PyObject *self, PyObject *value, const struct _alpm_cb_getset *closure) {
202
12
  AlpmHandle *it = (AlpmHandle *)self;
203
12
  if (value == Py_None) {
204

3
    Py_CLEAR(global_py_callbacks[closure->id]);
205
3
    closure->setter(it->c_data, NULL);
206
9
  } else if (PyCallable_Check(value)) {
207

6
    Py_CLEAR(global_py_callbacks[closure->id]);
208
6
    Py_INCREF(value);
209
6
    global_py_callbacks[closure->id] = value;
210
6
    closure->setter(it->c_data, closure->cb_wrapper);
211
  } else {
212
3
    PyErr_SetString(PyExc_TypeError, "value must be None or a function");
213
3
    return -1;
214
  }
215
216
  return 0;
217
}
218
219
struct PyGetSetDef pyalpm_handle_getset[] = {
220
  /** filepaths */
221
  { "root",
222
    (getter)_get_string_attr,
223
    NULL,
224
    "system root directory", &root_getset } ,
225
  { "dbpath",
226
    (getter)_get_string_attr,
227
    NULL,
228
    "alpm database directory", &dbpath_getset } ,
229
  { "logfile",
230
    (getter)_get_string_attr,
231
    (setter)_set_string_attr,
232
    "alpm logfile path", &logfile_getset } ,
233
  { "lockfile",
234
    (getter)_get_string_attr,
235
    NULL,
236
    "alpm lockfile path", &lockfile_getset } ,
237
  { "gpgdir",
238
    (getter)_get_string_attr,
239
    (setter)_set_string_attr,
240
    "alpm GnuPG home directory", &gpgdir_getset } ,
241
242
  /** strings */
243
  { "arch",
244
    (getter)_get_string_attr,
245
    (setter)_set_string_attr,
246
    "Target archichecture", &arch_getset } ,
247
248
  /** booleans */
249
  { "usesyslog",
250
    (getter)option_get_usesyslog_alpm,
251
    (setter)option_set_usesyslog_alpm,
252
    "use syslog (an integer, 0 = False, 1 = True)", NULL } ,
253
 { "checkspace",
254
    (getter)option_get_checkspace_alpm,
255
    (setter)option_set_checkspace_alpm,
256
    "check disk space before transactions (an integer, 0 = False, 1 = True)", NULL } ,
257
258
  /** lists */
259
  { "cachedirs",
260
    (getter)option_get_cachedirs_alpm,
261
    (setter)option_set_cachedirs_alpm,
262
    "list of package cache directories", NULL },
263
  { "noupgrades",
264
    (getter)option_get_noupgrades_alpm,
265
    (setter)option_set_noupgrades_alpm,
266
    "list of ...", NULL },
267
  { "noextracts",
268
    (getter)option_get_noextracts_alpm,
269
    (setter)option_set_noextracts_alpm,
270
    "list of ...", NULL },
271
  { "ignorepkgs",
272
    (getter)option_get_ignorepkgs_alpm,
273
    (setter)option_set_ignorepkgs_alpm,
274
    "list of ignored packages", NULL },
275
  { "ignoregrps",
276
    (getter)option_get_ignoregrps_alpm,
277
    (setter)option_set_ignoregrps_alpm,
278
    "list of ignored groups", NULL },
279
280
  /** callbacks */
281
  { "logcb",
282
    (getter)_get_cb_attr, (setter)_set_cb_attr,
283
    "logging callback, with arguments (loglevel, format string, tuple)",
284
    &cb_getsets[CB_LOG] },
285
  { "dlcb",
286
    (getter)_get_cb_attr, (setter)_set_cb_attr,
287
    "download status callback (a function)\n"
288
    "args: filename    :: str\n"
289
    "      transferred :: int\n"
290
    "      total       :: int\n",
291
    &cb_getsets[CB_DOWNLOAD] },
292
  { "totaldlcb",
293
    (getter)_get_cb_attr, (setter)_set_cb_attr,
294
    "total download size callback: totaldlcb(total_size)",
295
    &cb_getsets[CB_TOTALDL] },
296
  { "fetchcb",
297
    (getter)_get_cb_attr, (setter)_set_cb_attr,
298
    "download function\n"
299
    "args: url              :: string\n"
300
    "      destination path :: string\n"
301
    "      overwrite        :: bool\n"
302
    "returns: 0 on success, 1 if file exists, -1 on error",
303
    &cb_getsets[CB_FETCH] },
304
  { "eventcb",
305
    (getter)_get_cb_attr, (setter)_set_cb_attr,
306
    "  a function called when an event occurs\n"
307
    "    -- args: (event ID, event string, (object 1, object 2))\n",
308
    &cb_getsets[CB_EVENT] },
309
  { "questioncb",
310
    (getter)_get_cb_attr, (setter)_set_cb_attr,
311
    "  a function called to get user input\n",
312
    &cb_getsets[CB_QUESTION] },
313
  { "progresscb",
314
    (getter)_get_cb_attr, (setter)_set_cb_attr,
315
    "  -- a function called to indicate progress\n"
316
    "    -- args: (target name, percentage, number of targets, target number)\n",
317
    &cb_getsets[CB_PROGRESS] },
318
319
  /** terminator */
320
  { NULL }
321
};
322
323
static PyMethodDef pyalpm_handle_methods[] = {
324
  /* Transaction initialization */
325
  {"init_transaction",    (PyCFunction)pyalpm_trans_init, METH_VARARGS | METH_KEYWORDS,
326
    "Initializes a transaction.\n"
327
    "Arguments:\n"
328
    "  nodeps, force, nosave, nodepversion, cascade, recurse,\n"
329
    "  dbonly, alldeps, downloadonly, noscriptlet, noconflicts,\n"
330
    "  needed, allexplicit, unneeded, recurseall, nolock\n"
331
    "    -- the transaction options (booleans)\n"
332
  },
333
334
  /* Package load */
335
  {"load_pkg", (PyCFunction)pyalpm_package_load, METH_VARARGS | METH_KEYWORDS,
336
    "loads package information from a tarball"},
337
338
  /* Database members */
339
  {"register_syncdb", pyalpm_register_syncdb, METH_VARARGS,
340
   "registers the database with the given name\n"
341
   "returns the new database on success"},
342
  {"get_localdb", pyalpm_get_localdb, METH_NOARGS, "returns an object representing the local DB"},
343
  {"get_syncdbs", pyalpm_get_syncdbs, METH_NOARGS, "returns a list of sync DBs"},
344
  {"set_pkgreason", pyalpm_set_pkgreason, METH_VARARGS,
345
    "set install reason for a package (PKG_REASON_DEPEND, PKG_REASON_EXPLICIT)\n"},
346
347
  /* Option modifiers */
348
  {"add_noupgrade", option_add_noupgrade_alpm, METH_VARARGS, "add a noupgrade package."},
349
  {"remove_noupgrade", option_remove_noupgrade_alpm, METH_VARARGS, "removes a noupgrade package."},
350
351
  {"add_cachedir", option_add_cachedir_alpm, METH_VARARGS, "adds a cachedir."},
352
  {"remove_cachedir", option_remove_cachedir_alpm, METH_VARARGS, "removes a cachedir."},
353
354
  {"add_noextract", option_add_noextract_alpm, METH_VARARGS, "add a noextract package."},
355
  {"remove_noextract", option_remove_noextract_alpm, METH_VARARGS, "remove a noextract package."},
356
357
  {"add_ignorepkg", option_add_ignorepkg_alpm, METH_VARARGS, "add an ignorepkg."},
358
  {"remove_ignorepkg", option_remove_ignorepkg_alpm, METH_VARARGS, "remove an ignorepkg."},
359
360
  {"add_ignoregrp", option_add_ignoregrp_alpm, METH_VARARGS, "add an ignoregrp."},
361
  {"remove_ignoregrp", option_remove_ignoregrp_alpm, METH_VARARGS, "remove an ignoregrp."},
362
  {NULL, NULL, 0, NULL},
363
};
364
365
108
static void pyalpm_dealloc(PyObject* self) {
366
108
  alpm_handle_t *handle = ALPM_HANDLE(self);
367
108
  int ret = alpm_release(handle);
368
108
  if (ret == -1) {
369
    PyErr_Format(alpm_error, "unable to release alpm handle");
370
  }
371
108
  handle = NULL;
372
108
  Py_TYPE(self)->tp_free((PyObject *)self);
373
108
}
374
375
PyTypeObject AlpmHandleType = {
376
  PyVarObject_HEAD_INIT(NULL, 0)
377
  "alpm.Handle",       /*tp_name*/
378
  sizeof(AlpmHandle),  /*tp_basicsize*/
379
  0,                   /*tp_itemsize*/
380
  .tp_flags = Py_TPFLAGS_DEFAULT,
381
  .tp_doc = "An object wrapping a libalpm handle. Arguments: root path, DB path.",
382
  .tp_methods = pyalpm_handle_methods,
383
  .tp_getset = pyalpm_handle_getset,
384
  .tp_new = pyalpm_initialize,
385
  .tp_dealloc = (destructor) pyalpm_dealloc,
386
};
387
388
/** Initializes Handle class in module */
389
3
int init_pyalpm_handle(PyObject *module) {
390
3
  PyObject *type;
391
3
  if (PyType_Ready(&AlpmHandleType) < 0)
392
    return -1;
393
3
  type = (PyObject*)&AlpmHandleType;
394
3
  Py_INCREF(type);
395
3
  PyModule_AddObject(module, "Handle", type);
396
397
3
  PyModule_AddIntConstant(module, "LOG_ERROR", ALPM_LOG_ERROR);
398
3
  PyModule_AddIntConstant(module, "LOG_WARNING", ALPM_LOG_WARNING);
399
3
  PyModule_AddIntConstant(module, "LOG_DEBUG", ALPM_LOG_DEBUG);
400
3
  PyModule_AddIntConstant(module, "LOG_FUNCTION", ALPM_LOG_FUNCTION);
401
402
3
  return 0;
403
}
404
405
/* vim: set ts=2 sw=2 et: */