GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/transaction.c Lines: 94 242 38.8 %
Date: 2019-02-09 19:11:25 Branches: 24 103 23.3 %

Line Branch Exec Source
1
/**
2
 * transaction.c : wrapper class around libalpm transactions
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 <string.h>
25
#include <alpm.h>
26
#include <Python.h>
27
#include "package.h"
28
#include "handle.h"
29
#include "util.h"
30
31
/** Transaction callbacks */
32
extern PyObject *global_py_callbacks[N_CALLBACKS];
33
34
void pyalpm_eventcb(alpm_event_t *event) {
35
  const char *eventstr;
36
  switch(event->type) {
37
    case ALPM_EVENT_CHECKDEPS_START:
38
      eventstr = "Checking dependencies";
39
      break;
40
    case ALPM_EVENT_CHECKDEPS_DONE:
41
      eventstr = "Done checking dependencies";
42
      break;
43
    case ALPM_EVENT_FILECONFLICTS_START:
44
      eventstr = "Checking file conflicts";
45
      break;
46
    case ALPM_EVENT_FILECONFLICTS_DONE:
47
      eventstr = "Done checking file conflicts";
48
      break;
49
    case ALPM_EVENT_RESOLVEDEPS_START:
50
      eventstr = "Resolving dependencies";
51
      break;
52
    case ALPM_EVENT_RESOLVEDEPS_DONE:
53
      eventstr = "Done resolving dependencies";
54
      break;
55
    case ALPM_EVENT_INTERCONFLICTS_START:
56
      eventstr = "Checking inter conflicts";
57
      break;
58
    case ALPM_EVENT_INTERCONFLICTS_DONE:
59
      eventstr = "Done checking inter conflicts";
60
      break;
61
    case ALPM_EVENT_PACKAGE_OPERATION_START:
62
      eventstr = "Operating on a package";
63
      switch(((alpm_event_package_operation_t*)event)->operation) {
64
      case ALPM_PACKAGE_INSTALL:
65
        eventstr = "Adding a package";
66
        break;
67
      case ALPM_PACKAGE_UPGRADE:
68
        eventstr = "Upgrading a package";
69
        break;
70
      case ALPM_PACKAGE_REINSTALL:
71
        eventstr = "Reinstalling a package";
72
        break;
73
      case ALPM_PACKAGE_DOWNGRADE:
74
        eventstr = "Downgrading a package";
75
        break;
76
      case ALPM_PACKAGE_REMOVE:
77
        eventstr = "Removing a package";
78
        break;
79
      }
80
      break;
81
    case ALPM_EVENT_PACKAGE_OPERATION_DONE:
82
      eventstr = "Done operating on a package";
83
      switch(((alpm_event_package_operation_t*)event)->operation) {
84
      case ALPM_PACKAGE_INSTALL:
85
        eventstr = "Done adding a package";
86
        break;
87
      case ALPM_PACKAGE_UPGRADE:
88
        eventstr = "Done upgrading a package";
89
        break;
90
      case ALPM_PACKAGE_REINSTALL:
91
        eventstr = "Done reinstalling a package";
92
        break;
93
      case ALPM_PACKAGE_DOWNGRADE:
94
        eventstr = "Done downgrading a package";
95
        break;
96
      case ALPM_PACKAGE_REMOVE:
97
        eventstr = "Done removing a package";
98
        break;
99
      }
100
      break;
101
    case ALPM_EVENT_INTEGRITY_START:
102
      eventstr = "Checking integrity";
103
      break;
104
    case ALPM_EVENT_INTEGRITY_DONE:
105
      eventstr = "Done checking integrity";
106
      break;
107
    case ALPM_EVENT_LOAD_START:
108
    case ALPM_EVENT_LOAD_DONE:
109
    case ALPM_EVENT_DELTA_INTEGRITY_START:
110
    case ALPM_EVENT_DELTA_INTEGRITY_DONE:
111
    case ALPM_EVENT_DELTA_PATCHES_START:
112
    case ALPM_EVENT_DELTA_PATCHES_DONE:
113
    case ALPM_EVENT_DELTA_PATCH_START:
114
      /* info here */
115
    case ALPM_EVENT_DELTA_PATCH_DONE:
116
    case ALPM_EVENT_DELTA_PATCH_FAILED:
117
    case ALPM_EVENT_SCRIPTLET_INFO:
118
      /* info here */
119
    case ALPM_EVENT_RETRIEVE_START:
120
    case ALPM_EVENT_RETRIEVE_DONE:
121
    case ALPM_EVENT_RETRIEVE_FAILED:
122
      /* info here */
123
      eventstr = "event not implemented";
124
      break;
125
    case ALPM_EVENT_DISKSPACE_START:
126
      eventstr = "Checking disk space";
127
      break;
128
    case ALPM_EVENT_DISKSPACE_DONE:
129
      eventstr = "Done checking disk space";
130
      break;
131
    case ALPM_EVENT_OPTDEP_REMOVAL:
132
    case ALPM_EVENT_DATABASE_MISSING:
133
      eventstr = "event not implemented";
134
      break;
135
    case ALPM_EVENT_KEYRING_START:
136
      eventstr = "Checking keys in keyring";
137
      break;
138
    case ALPM_EVENT_KEYRING_DONE:
139
      eventstr = "Done checking keys in keyring";
140
      break;
141
    case ALPM_EVENT_KEY_DOWNLOAD_START:
142
    case ALPM_EVENT_KEY_DOWNLOAD_DONE:
143
    case ALPM_EVENT_PACNEW_CREATED:
144
    case ALPM_EVENT_PACSAVE_CREATED:
145
    case ALPM_EVENT_HOOK_START:
146
    case ALPM_EVENT_HOOK_DONE:
147
    case ALPM_EVENT_HOOK_RUN_START:
148
    case ALPM_EVENT_HOOK_RUN_DONE:
149
    default:
150
      eventstr = "unknown event";
151
  }
152
  {
153
    PyObject *result = NULL;
154
    if (global_py_callbacks[CB_PROGRESS]) {
155
      result = PyObject_CallFunction(global_py_callbacks[CB_EVENT], "is", event->type, eventstr);
156
    } else {
157
      PyErr_SetString(PyExc_RuntimeError, "event callback was called but it's not set!");
158
    }
159
    if (PyErr_Occurred()) PyErr_Print();
160
    Py_CLEAR(result);
161
  }
162
}
163
164
void pyalpm_questioncb(alpm_question_t question,
165
        void* data1, void *data2, void* data3, int* retcode) {
166
}
167
168
void pyalpm_progresscb(alpm_progress_t op,
169
        const char* target_name, int percentage, size_t n_targets, size_t cur_target) {
170
  PyObject *result = NULL;
171
  if (global_py_callbacks[CB_PROGRESS]) {
172
    result = PyObject_CallFunction(global_py_callbacks[CB_PROGRESS], "sinn",
173
      target_name, percentage, n_targets, cur_target);
174
  } else {
175
    PyErr_SetString(PyExc_RuntimeError, "progress callback was called but it's not set!");
176
  }
177
  if (PyErr_Occurred()) {
178
    PyErr_Print();
179
    /* alpm_trans_interrupt(handle); */
180
  }
181
  Py_CLEAR(result);
182
}
183
184
/** Transaction info translation */
185
17
static PyObject* pyobject_from_pmdepmissing(void *item) {
186
17
  alpm_depmissing_t* miss = (alpm_depmissing_t*)item;
187
17
  char* needed = alpm_dep_compute_string(miss->depend);
188
17
  PyObject *result = Py_BuildValue("(sss)",
189
      miss->target,
190
      needed,
191
      miss->causingpkg);
192
17
  free(needed);
193
17
  return result;
194
}
195
196
static PyObject* pyobject_from_pmfileconflict(void *item) {
197
  alpm_fileconflict_t* conflict = (alpm_fileconflict_t*)item;
198
  const char *target = conflict->target;
199
  const char *filename = conflict->file;
200
  switch(conflict->type) {
201
  case ALPM_FILECONFLICT_TARGET:
202
    return Py_BuildValue("(sss)", target, filename, conflict->ctarget);
203
  case ALPM_FILECONFLICT_FILESYSTEM:
204
    return Py_BuildValue("(ssO)", target, filename, Py_None);
205
  default:
206
    PyErr_Format(PyExc_RuntimeError, "invalid type %d for alpm_fileconflict_t object", conflict->type);
207
    return NULL;
208
  }
209
}
210
211
/* Standard methods */
212
const char* flagnames[19] = {
213
  "nodeps",
214
  "force",
215
  "nosave",
216
  "nodepversion",
217
  "cascade",
218
  "recurse",
219
  "dbonly",
220
  NULL,
221
  "alldeps",
222
  "downloadonly",
223
  "noscriptlet",
224
  "noconflicts",
225
  NULL,
226
  "needed",
227
  "allexplicit",
228
  "unneeded",
229
  "recurseall",
230
  "nolock",
231
  NULL
232
};
233
234
1
static PyObject *pyalpm_trans_get_flags(PyObject *self, void *closure)
235
{
236
1
  PyObject *result;
237
1
  alpm_handle_t *handle = ALPM_HANDLE(self);
238
1
  int flags = alpm_trans_get_flags(handle);
239
1
  int i;
240
1
  if (flags == -1) RET_ERR("no transaction defined", alpm_errno(handle), NULL);
241
1
  result = PyDict_New();
242
19
  for (i = 0; i < 18; i++) {
243
18
    if(flagnames[i])
244
32
      PyDict_SetItemString(result, flagnames[i], flags & (1 << i) ? Py_True : Py_False);
245
  }
246
  return result;
247
}
248
249
2
static PyObject *pyalpm_trans_get_add(PyObject *self, void *closure)
250
{
251
2
  alpm_handle_t *handle = ALPM_HANDLE(self);
252
2
  alpm_list_t *to_add;
253
  /* sanity check */
254
2
  int flags = alpm_trans_get_flags(handle);
255
2
  if (flags == -1) RET_ERR("no transaction defined", alpm_errno(handle), NULL);
256
257
2
  to_add = alpm_trans_get_add(handle);
258
2
  return alpmlist_to_pylist(to_add, pyalpm_package_from_pmpkg);
259
}
260
261
2
static PyObject *pyalpm_trans_get_remove(PyObject *self, void *closure)
262
{
263
2
  alpm_handle_t *handle = ALPM_HANDLE(self);
264
2
  alpm_list_t *to_remove;
265
  /* sanity check */
266
2
  int flags = alpm_trans_get_flags(handle);
267
2
  if (flags == -1) RET_ERR("no transaction defined", alpm_errno(handle), NULL);
268
269
2
  to_remove = alpm_trans_get_remove(handle);
270
2
  return alpmlist_to_pylist(to_remove, pyalpm_package_from_pmpkg);
271
}
272
273
/** Transaction flow */
274
#define INDEX_FLAGS(array) \
275
  array[0], array[1], array[2], \
276
  array[3], array[4], array[5], \
277
  array[6], array[8], array[9], \
278
  array[10], array[11], array[13], \
279
  array[14], array[15], array[16], \
280
  array[17]
281
282
/** Initializes a transaction
283
 * @param self a Handle object
284
 * ...
285
 * @return a Transaction object with the same underlying object
286
 */
287
7
PyObject* pyalpm_trans_init(PyObject *self, PyObject *args, PyObject *kwargs) {
288
7
  alpm_handle_t *handle = ALPM_HANDLE(self);
289
7
  PyObject *result;
290
7
  const char* keywords[] = { INDEX_FLAGS(flagnames), NULL };
291
7
  char flags[18] = "\0\0\0\0\0" /* 5 */ "\0\0\0\0\0" /* 10 */ "\0\0\0\0\0" /* 15 */ "\0\0\0";
292
293
  /* check all arguments */
294
7
  if (!PyArg_ParseTupleAndKeywords(args, kwargs,
295
        "|bbbbbbbbbbbbbbbbOOO", (char**)keywords,
296
        INDEX_FLAGS(&flags))) {
297
    return NULL;
298
  }
299
300
  /* run alpm_trans_init() */
301
  {
302
    alpm_transflag_t flag_int = 0;
303
    int i, ret;
304
133
    for (i = 0; i < 18; i++) {
305
126
      if (flags[i]) flag_int |= 1U << i;
306
    }
307
7
    ret = alpm_trans_init(handle, flag_int);
308
7
    if (ret == -1) {
309
      RET_ERR("transaction could not be initialized", alpm_errno(handle), NULL);
310
    }
311
  }
312
7
  result = pyalpm_transaction_from_pmhandle(handle);
313
7
  return result;
314
}
315
316
1
static PyObject* pyalpm_trans_prepare(PyObject *self, PyObject *args) {
317
1
  alpm_handle_t *handle = ALPM_HANDLE(self);
318
1
  alpm_list_t *data;
319
320
1
  int ret = alpm_trans_prepare(handle, &data);
321
1
  if (ret == -1) {
322
    /* return the list of package conflicts in the exception */
323
1
    PyObject *info = alpmlist_to_pylist(data, pyobject_from_pmdepmissing);
324
1
    if (!info) return NULL;
325
1
    RET_ERR_DATA("transaction preparation failed", alpm_errno(handle), info, NULL);
326
  }
327
328
  Py_RETURN_NONE;
329
}
330
331
static PyObject* pyalpm_trans_commit(PyObject *self, PyObject *args) {
332
  alpm_handle_t *handle = ALPM_HANDLE(self);
333
  alpm_list_t *data = NULL;
334
  int ret;
335
  enum _alpm_errno_t err;
336
  PyObject *err_info = NULL;
337
338
  ret = alpm_trans_commit(handle, &data);
339
  if (ret == 0) Py_RETURN_NONE;
340
  if (ret != -1) {
341
    PyErr_Format(PyExc_RuntimeError,
342
        "unexpected return value %d from alpm_trans_commit()", ret);
343
    return NULL;
344
  }
345
346
  err = alpm_errno(handle);
347
  switch(err) {
348
    case ALPM_ERR_FILE_CONFLICTS:
349
      /* return the list of file conflicts in the exception */
350
      err_info = alpmlist_to_pylist(data, pyobject_from_pmfileconflict);
351
      break;
352
    case ALPM_ERR_PKG_INVALID:
353
    case ALPM_ERR_PKG_INVALID_CHECKSUM:
354
    case ALPM_ERR_PKG_INVALID_SIG:
355
    case ALPM_ERR_DLT_INVALID:
356
      err_info = alpmlist_to_pylist(data, pyobject_from_string);
357
      break;
358
    default:
359
      break;
360
  }
361
  if (err_info)
362
    RET_ERR_DATA("transaction failed", err, err_info, NULL);
363
  else
364
    RET_ERR("transaction failed", err, NULL);
365
}
366
367
static PyObject* pyalpm_trans_interrupt(PyObject *self, PyObject *args) {
368
  alpm_handle_t *handle = ALPM_HANDLE(self);
369
  int ret = alpm_trans_interrupt(handle);
370
  if (ret == -1) RET_ERR("unable to interrupt transaction", alpm_errno(handle), NULL);
371
  Py_RETURN_NONE;
372
}
373
374
7
PyObject* pyalpm_trans_release(PyObject *self, PyObject *args) {
375
7
  alpm_handle_t *handle = ALPM_HANDLE(self);
376
7
  int ret = alpm_trans_release(handle);
377
7
  if (ret == -1) RET_ERR("unable to release transaction", alpm_errno(handle), NULL);
378
7
  Py_RETURN_NONE;
379
}
380
381
/** Transaction contents */
382
2
static PyObject* pyalpm_trans_add_pkg(PyObject *self, PyObject *args) {
383
2
  alpm_handle_t *handle = ALPM_HANDLE(self);
384
2
  alpm_pkg_t *pmpkg;
385
2
  PyObject *pkg;
386
2
  int ret;
387
388
2
  if (!PyArg_ParseTuple(args, "O!", &AlpmPackageType, &pkg)) {
389
    return NULL;
390
  }
391
392
1
  pmpkg = pmpkg_from_pyalpm_pkg(pkg);
393
1
  ret = alpm_add_pkg(handle, pmpkg);
394
1
  if (ret == -1) RET_ERR("unable to update transaction", alpm_errno(handle), NULL);
395
  /* alpm_add_pkg eats the reference to pkg */
396
1
  pyalpm_pkg_unref(pkg);
397
1
  Py_RETURN_NONE;
398
}
399
400
1
static PyObject* pyalpm_trans_remove_pkg(PyObject *self, PyObject *args) {
401
1
  alpm_handle_t *handle = ALPM_HANDLE(self);
402
1
  PyObject *pkg;
403
1
  alpm_pkg_t *pmpkg;
404
1
  int ret;
405
406
1
  if (!PyArg_ParseTuple(args, "O!", &AlpmPackageType, &pkg)) {
407
    return NULL;
408
  }
409
410
  pmpkg = pmpkg_from_pyalpm_pkg(pkg);
411
  ret = alpm_remove_pkg(handle, pmpkg);
412
  if (ret == -1) RET_ERR("unable to update transaction", alpm_errno(handle), NULL);
413
  Py_RETURN_NONE;
414
}
415
416
1
static PyObject* pyalpm_trans_sysupgrade(PyObject *self, PyObject *args, PyObject *kwargs) {
417
1
  alpm_handle_t *handle = ALPM_HANDLE(self);
418
1
  char* keyword[] = {"downgrade", NULL};
419
1
  PyObject *downgrade;
420
1
  int do_downgrade, ret;
421
422
1
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", keyword, &PyBool_Type, &downgrade))
423
    return NULL;
424
425
1
  do_downgrade = (downgrade == Py_True) ? 1 : 0;
426
1
  ret = alpm_sync_sysupgrade(handle, do_downgrade);
427
1
  if (ret == -1) RET_ERR("unable to update transaction", alpm_errno(handle), NULL);
428
1
  Py_RETURN_NONE;
429
}
430
431
/** Properties and methods */
432
433
static struct PyGetSetDef pyalpm_trans_getset[] = {
434
  /** filepaths */
435
  { "flags", (getter)pyalpm_trans_get_flags, NULL, "Transaction flags", NULL } ,
436
  { "to_add", (getter)pyalpm_trans_get_add, NULL, "Packages added by the transaction", NULL },
437
  { "to_remove", (getter)pyalpm_trans_get_remove, NULL, "Packages added by the transaction", NULL },
438
  { NULL }
439
};
440
441
static struct PyMethodDef pyalpm_trans_methods[] = {
442
  /* Execution flow */
443
  {"prepare", pyalpm_trans_prepare,    METH_NOARGS, "prepare" },
444
  {"commit",  pyalpm_trans_commit,     METH_NOARGS, "commit" },
445
  {"interrupt", pyalpm_trans_interrupt,METH_NOARGS,  "Interrupt the transaction." },
446
  {"release", pyalpm_trans_release,    METH_NOARGS,  "Release the transaction." },
447
448
  /* Transaction contents */
449
  {"add_pkg",    pyalpm_trans_add_pkg,    METH_VARARGS,
450
    "append a package addition to transaction"},
451
  {"remove_pkg", pyalpm_trans_remove_pkg, METH_VARARGS,
452
    "append a package removal to transaction"},
453
  {"sysupgrade", (PyCFunction)pyalpm_trans_sysupgrade, METH_VARARGS | METH_KEYWORDS,
454
    "set the transaction to perform a system upgrade\n"
455
    "args:\n"
456
    "  transaction (boolean) : whether to enable downgrades\n" },
457
  { NULL }
458
};
459
460
/* The Transaction object have the same underlying C structure
461
 * as the Handle objects. Only the method table changes.
462
 */
463
static PyTypeObject AlpmTransactionType = {
464
  PyVarObject_HEAD_INIT(NULL, 0)
465
  "alpm.Transaction",    /*tp_name*/
466
  sizeof(AlpmHandle),  /*tp_basicsize*/
467
  .tp_flags = Py_TPFLAGS_DEFAULT,
468
  .tp_doc = "This class is the main interface to get/set libalpm options",
469
  .tp_methods = pyalpm_trans_methods,
470
  .tp_getset = pyalpm_trans_getset,
471
};
472
473
7
PyObject *pyalpm_transaction_from_pmhandle(void* data) {
474
7
  alpm_handle_t *handle = (alpm_handle_t*)data;
475
7
  AlpmHandle *self;
476
7
  self = (AlpmHandle*)AlpmTransactionType.tp_alloc(&AlpmTransactionType, 0);
477
7
  if (self == NULL) {
478
    PyErr_SetString(PyExc_RuntimeError, "unable to create pyalpm.Transaction object");
479
    return NULL;
480
  }
481
482
7
  self->c_data = handle;
483
7
  return (PyObject *)self;
484
}
485
486
/* Initialization */
487
1
int init_pyalpm_transaction(PyObject *module) {
488
1
  if (PyType_Ready(&AlpmTransactionType) < 0)
489
    return -1;
490
1
  Py_INCREF(&AlpmTransactionType);
491
1
  PyModule_AddObject(module, "Transaction", (PyObject*)(&AlpmTransactionType));
492
1
  return 0;
493
}
494
495
/* vim: set ts=2 sw=2 tw=0 et: */
496