Implemented force install
[spkg.git] / src / taction.c
blobf1c3a3eafd8471cc42814e47f60b61b18a4bb1d3
1 /*----------------------------------------------------------------------*\
2 |* spkg - The Unofficial Slackware Linux Package Manager *|
3 |* designed by Ondøej Jirman, 2005 *|
4 |*----------------------------------------------------------------------*|
5 |* No copy/usage restrictions are imposed on anybody. *|
6 \*----------------------------------------------------------------------*/
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <time.h>
13 #include <string.h>
15 #include "taction.h"
16 #include "message.h"
17 #include "sys.h"
19 #ifdef __WIN32__
20 #include <windows.h>
21 extern int link(const char *existing, const char *newfile);
22 #endif
24 /* private
25 ************************************************************************/
27 #define e_set(n, fmt, args...) e_add(_ta.err, "taction", __func__, n, fmt, ##args)
28 #define _e_set(e, n, fmt, args...) e_add(e, "taction", __func__, n, fmt, ##args)
30 struct transaction {
31 gboolean active;
32 gboolean dryrun;
33 struct error* err;
34 GSList* list;
35 GSList* endlist;
38 static struct transaction _ta = {
39 .active = 0,
40 .err = 0,
41 .list = 0,
42 .dryrun = 0
45 /* action list handling */
46 typedef enum {
47 MOVE, KEEP, LINK, FORCELINK, SYMLINK, CHPERM, FORCESYMLINK, REMOVE,
48 NOTHING
49 } t_action;
51 struct action {
52 t_action on_finalize;
53 t_action on_rollback;
54 gchar* path1;
55 gchar* path2;
56 gboolean is_dir;
57 gint mode;
58 gint owner;
59 gint group;
62 static struct action* _ta_insert(
63 GSList** list,
64 t_action on_finalize,
65 t_action on_rollback
68 g_assert(list != NULL);
69 struct action* a = g_new0(struct action, 1);
70 a->on_finalize = on_finalize;
71 a->on_rollback = on_rollback;
72 *list = g_slist_prepend(*list, a);
73 return a;
76 static void _ta_free_action(struct action* a)
78 g_free(a->path1);
79 g_free(a->path2);
80 g_free(a);
83 /* public
84 ************************************************************************/
86 gint ta_initialize(gboolean dryrun, struct error* e)
88 g_assert(e != 0);
90 if (_ta.active)
92 _e_set(e, E_ERROR|TA_ACTIVE, "Can't start transaction while another transaction is still in progress.");
93 return 1;
95 _ta.err = e;
96 _ta.active = 1;
97 _ta.dryrun = dryrun;
98 _ta.list = 0;
99 return 0;
102 void ta_keep_remove(gchar* path, gboolean is_dir)
104 g_assert(path != 0);
105 struct action* a = _ta_insert(&_ta.list, KEEP, REMOVE);
106 a->path1 = path;
107 a->is_dir = is_dir;
110 void ta_move_remove(gchar* path, gchar* dst_path)
112 g_assert(path != 0);
113 g_assert(dst_path != 0);
114 struct action* a = _ta_insert(&_ta.list, MOVE, REMOVE);
115 a->path1 = path;
116 a->path2 = dst_path;
119 void ta_link_nothing(gchar* path, gchar* tgt_path)
121 g_assert(path != 0);
122 g_assert(tgt_path != 0);
123 struct action* a = _ta_insert(&_ta.list, LINK, NOTHING);
124 a->path1 = path;
125 a->path2 = tgt_path;
128 void ta_symlink_nothing(gchar* path, gchar* tgt_path)
130 g_assert(path != 0);
131 g_assert(tgt_path != 0);
132 struct action* a = _ta_insert(&_ta.list, SYMLINK, NOTHING);
133 a->path1 = path;
134 a->path2 = tgt_path;
137 void ta_forcesymlink_nothing(gchar* path, gchar* tgt_path)
139 g_assert(path != 0);
140 g_assert(tgt_path != 0);
141 struct action* a = _ta_insert(&_ta.list, FORCESYMLINK, NOTHING);
142 a->path1 = path;
143 a->path2 = tgt_path;
146 void ta_forcelink_nothing(gchar* path, gchar* tgt_path)
148 g_assert(path != 0);
149 g_assert(tgt_path != 0);
150 struct action* a = _ta_insert(&_ta.list, FORCELINK, NOTHING);
151 a->path1 = path;
152 a->path2 = tgt_path;
155 void ta_chperm_nothing(gchar* path, gint mode, gint owner, gint group)
157 g_assert(path != 0);
158 struct action* a = _ta_insert(&_ta.list, CHPERM, NOTHING);
159 a->path1 = path;
160 a->mode = mode;
161 a->owner = owner;
162 a->group = group;
165 void ta_remove_nothing(gchar* path, gint is_dir)
167 g_assert(path != 0);
168 if (is_dir)
170 struct action* a = _ta_insert(&_ta.endlist, REMOVE, NOTHING);
171 a->path1 = path;
172 a->is_dir = is_dir;
174 else
176 struct action* a = _ta_insert(&_ta.list, REMOVE, NOTHING);
177 a->path1 = path;
178 a->is_dir = is_dir;
182 gint ta_finalize()
184 GSList* l;
186 if (!_ta.active)
188 e_set(E_ERROR|TA_NACTIVE, "Can't finalize transaction, because no transaction was started.");
189 return 1;
192 _ta.list = g_slist_reverse(_ta.list);
193 for (l=_ta.list; l!=0; l=l->next)
195 struct action* a = l->data;
196 if (a->on_finalize == MOVE)
198 _debug("Moving %s -> %s", a->path1, a->path2);
199 if (!_ta.dryrun)
201 #ifdef __WIN32__
202 if (access(a->path2, 0) == 0)
203 remove(a->path2);
204 #endif
205 if (rename(a->path1, a->path2) == -1)
207 _warning("Failed to move %s -> %s (%s)", a->path1, a->path2, strerror(errno));
211 else if (a->on_finalize == LINK)
213 _notice("Creating hardlink %s -> %s", a->path1, a->path2);
214 if (!_ta.dryrun)
216 if (link(a->path2, a->path1) == -1)
218 _warning("Failed to create hardlink %s -> %s (%s)", a->path1, a->path2, strerror(errno));
222 else if (a->on_finalize == FORCELINK)
224 _debug("Removing path %s", a->path1);
225 if (!_ta.dryrun)
227 if (sys_rm_rf(a->path1))
229 _warning("Failed to remove path %s", a->path1);
230 continue;
233 _notice("Creating hardlink %s -> %s", a->path1, a->path2);
234 if (!_ta.dryrun)
236 if (link(a->path2, a->path1) == -1)
238 _warning("Failed to create hardlink %s -> %s (%s)", a->path1, a->path2, strerror(errno));
242 else if (a->on_finalize == SYMLINK)
244 _notice("Creating symlink %s -> %s", a->path1, a->path2);
245 if (!_ta.dryrun)
247 #ifndef __WIN32__
248 if (symlink(a->path2, a->path1) == -1)
250 _warning("Failed to create symlink %s -> %s (%s)", a->path1, a->path2, strerror(errno));
252 #else
253 _warning("Failed to create symlink %s -> %s (%s)", a->path1, a->path2, strerror(errno));
254 #endif
257 else if (a->on_finalize == FORCESYMLINK)
259 _debug("Removing path %s", a->path1);
260 if (!_ta.dryrun)
262 if (sys_rm_rf(a->path1))
264 _warning("Failed to remove path %s", a->path1);
265 continue;
268 _notice("Creating symlink %s -> %s", a->path1, a->path2);
269 if (!_ta.dryrun)
271 #ifndef __WIN32__
272 if (symlink(a->path2, a->path1) == -1)
274 _warning("Failed to create symlink %s -> %s (%s)", a->path1, a->path2, strerror(errno));
276 #else
277 _warning("Failed to create symlink %s -> %s (%s)", a->path1, a->path2, strerror(errno));
278 #endif
281 else if (a->on_finalize == CHPERM)
283 /* chmod */
284 _notice("Changing mode on %s to %04o", a->path1, a->mode);
285 if (!_ta.dryrun)
287 if (chmod(a->path1, a->mode) == -1)
289 _warning("Failed to cange mode on %s to %04o (%s)", a->path1, a->mode, strerror(errno));
292 /* chown */
293 _notice("Changing owner on %s to %d:%d", a->path1, a->owner, a->group);
294 if (!_ta.dryrun)
296 #ifndef __WIN32__
297 if (chown(a->path1, a->owner, a->group) == -1)
299 _warning("Failed to cange owner on %s to %d:%d (%s)", a->path1, a->owner, a->group, strerror(errno));
301 #endif
304 else if (a->on_finalize == REMOVE)
306 if (!a->is_dir)
308 _notice("Removing file %s", a->path1);
309 if (!_ta.dryrun)
311 if (unlink(a->path1))
313 _warning("Failed to remove file %s. (%s)", a->path1, strerror(errno));
320 for (l=_ta.endlist; l!=0; l=l->next)
322 struct action* a = l->data;
323 if (a->on_finalize == REMOVE)
325 if (a->is_dir)
327 _notice("Removing directory %s", a->path1);
328 if (!_ta.dryrun)
330 if (rmdir(a->path1))
332 _warning("Failed to remove directory %s. (%s)", a->path1, strerror(errno));
339 g_slist_foreach(_ta.endlist, (GFunc)_ta_free_action, 0);
340 g_slist_free(_ta.endlist);
341 g_slist_foreach(_ta.list, (GFunc)_ta_free_action, 0);
342 g_slist_free(_ta.list);
343 memset(&_ta, 0, sizeof(_ta));
344 return 0;
347 gint ta_rollback()
349 GSList* l;
351 if (!_ta.active)
353 e_set(E_ERROR|TA_NACTIVE, "Can't rollback transaction, because no transaction was started.");
354 return 1;
357 for (l=_ta.list; l!=0; l=l->next)
359 struct action* a = l->data;
360 if (a->on_rollback == REMOVE)
362 if (a->is_dir)
364 _debug("Removing directory %s", a->path1);
365 if (!_ta.dryrun)
367 if (rmdir(a->path1) == -1)
369 _warning("Failed to remove directory %s (%s)", a->path1, strerror(errno));
373 else
375 _debug("Removing file %s", a->path1);
376 if (!_ta.dryrun)
378 if (unlink(a->path1) == -1)
380 _warning("Failed to remove file %s (%s)", a->path1, strerror(errno));
387 g_slist_foreach(_ta.endlist, (GFunc)_ta_free_action, 0);
388 g_slist_free(_ta.endlist);
389 g_slist_foreach(_ta.list, (GFunc)_ta_free_action, 0);
390 g_slist_free(_ta.list);
391 memset(&_ta, 0, sizeof(_ta));
392 return 0;