1 |
/*- |
2 |
* Copyright (c) 2013-2015 Lucas Holt |
3 |
* Copyright (c) 2007-2009 Chris Reinhardt |
4 |
* All rights reserved. |
5 |
* |
6 |
* Redistribution and use in source and binary forms, with or without |
7 |
* modification, are permitted provided that the following conditions |
8 |
* are met: |
9 |
* 1. Redistributions of source code must retain the above copyright |
10 |
* notice, this list of conditions and the following disclaimer. |
11 |
* 2. Redistributions in binary form must reproduce the above copyright |
12 |
* notice, this list of conditions and the following disclaimer in the |
13 |
* documentation and/or other materials provided with the distribution. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
*/ |
27 |
|
28 |
#include <sys/cdefs.h> |
29 |
__MBSDID("$MidnightBSD$"); |
30 |
|
31 |
#include "mport.h" |
32 |
#include "mport_private.h" |
33 |
|
34 |
#include <sys/stat.h> |
35 |
#include <libgen.h> |
36 |
#include <stdlib.h> |
37 |
#include <string.h> |
38 |
#include <errno.h> |
39 |
#include <unistd.h> |
40 |
#include <syslog.h> |
41 |
#include <stdarg.h> |
42 |
#include <archive_entry.h> |
43 |
|
44 |
|
45 |
enum phase { PREINSTALL, ACTUALINSTALL, POSTINSTALL}; |
46 |
|
47 |
static int do_pre_install(mportInstance *, mportBundleRead *, mportPackageMeta *); |
48 |
static int do_actual_install(mportInstance *, mportBundleRead *, mportPackageMeta *); |
49 |
static int do_post_install(mportInstance *, mportBundleRead *, mportPackageMeta *); |
50 |
static int run_postexec(mportInstance *, mportPackageMeta *); |
51 |
static int run_pkg_install(mportInstance *, mportBundleRead *, mportPackageMeta *, const char *); |
52 |
static int run_mtree(mportInstance *, mportBundleRead *, mportPackageMeta *); |
53 |
static int display_pkg_msg(mportInstance *, mportBundleRead *, mportPackageMeta *); |
54 |
static int get_file_count(mportInstance *, char *, int *); |
55 |
static int create_package_row(mportInstance *, mportPackageMeta *); |
56 |
static int create_categories(mportInstance *mport, mportPackageMeta *pkg); |
57 |
static int create_depends(mportInstance *mport, mportPackageMeta *pkg); |
58 |
static int create_sample_file(mportInstance *mport, char *cwd, const char *file); |
59 |
static char** parse_sample(char *input); |
60 |
static int mark_complete(mportInstance *, mportPackageMeta *); |
61 |
static int mport_bundle_read_get_assetlist(mportInstance *mport, mportPackageMeta *pkg, mportAssetList **alist_p, enum phase); |
62 |
|
63 |
/** |
64 |
* This is a wrapper for all bund read install operations |
65 |
*/ |
66 |
int |
67 |
mport_bundle_read_install_pkg(mportInstance *mport, mportBundleRead *bundle, mportPackageMeta *pkg) |
68 |
{ |
69 |
if (do_pre_install(mport, bundle, pkg) != MPORT_OK) { |
70 |
RETURN_CURRENT_ERROR; |
71 |
} |
72 |
|
73 |
if (do_actual_install(mport, bundle, pkg) != MPORT_OK) { |
74 |
RETURN_CURRENT_ERROR; |
75 |
} |
76 |
|
77 |
if (do_post_install(mport, bundle, pkg) != MPORT_OK) { |
78 |
RETURN_CURRENT_ERROR; |
79 |
} |
80 |
|
81 |
syslog(LOG_NOTICE, "%s-%s installed", pkg->name, pkg->version); |
82 |
|
83 |
return MPORT_OK; |
84 |
} |
85 |
|
86 |
|
87 |
/* This does everything that has to happen before we start installing files. |
88 |
* We run mtree, pkg-install PRE-INSTALL, etc... |
89 |
*/ |
90 |
static int |
91 |
do_pre_install(mportInstance *mport, mportBundleRead *bundle, mportPackageMeta *pkg) |
92 |
{ |
93 |
char cwd[FILENAME_MAX]; |
94 |
char file[FILENAME_MAX]; |
95 |
mportAssetList *alist; |
96 |
mportAssetListEntry *e = NULL; |
97 |
|
98 |
/* run mtree */ |
99 |
if (run_mtree(mport, bundle, pkg) != MPORT_OK) |
100 |
RETURN_CURRENT_ERROR; |
101 |
|
102 |
/* run pkg-install PRE-INSTALL */ |
103 |
if (run_pkg_install(mport, bundle, pkg, "PRE-INSTALL") != MPORT_OK) |
104 |
RETURN_CURRENT_ERROR; |
105 |
|
106 |
/* Process @preexec steps */ |
107 |
if (mport_bundle_read_get_assetlist(mport, pkg, &alist, PREINSTALL) != MPORT_OK) |
108 |
goto ERROR; |
109 |
|
110 |
(void) strlcpy(cwd, pkg->prefix, sizeof(cwd)); |
111 |
|
112 |
if (mport_chdir(mport, cwd) != MPORT_OK) |
113 |
goto ERROR; |
114 |
|
115 |
STAILQ_FOREACH(e, alist, next) { |
116 |
switch (e->type) { |
117 |
case ASSET_CWD: |
118 |
(void) strlcpy(cwd, e->data == NULL ? pkg->prefix : e->data, sizeof(cwd)); |
119 |
if (mport_chdir(mport, cwd) != MPORT_OK) |
120 |
goto ERROR; |
121 |
|
122 |
break; |
123 |
case ASSET_PREEXEC: |
124 |
if (mport_run_asset_exec(mport, e->data, cwd, file) != MPORT_OK) |
125 |
goto ERROR; |
126 |
break; |
127 |
default: |
128 |
/* do nothing */ |
129 |
break; |
130 |
} |
131 |
} |
132 |
|
133 |
mport_assetlist_free(alist); |
134 |
mport_pkgmeta_logevent(mport, pkg, "preexec"); |
135 |
|
136 |
return MPORT_OK; |
137 |
|
138 |
ERROR: |
139 |
// TODO: asset list free |
140 |
RETURN_CURRENT_ERROR; |
141 |
} |
142 |
|
143 |
/* get the file count for the progress meter */ |
144 |
static int |
145 |
get_file_count(mportInstance *mport, char *pkg_name, int *file_total) |
146 |
{ |
147 |
__block sqlite3_stmt *count; |
148 |
__block int result = MPORT_OK; |
149 |
__block char *err; |
150 |
|
151 |
if (mport_db_prepare(mport->db, &count, |
152 |
"SELECT COUNT(*) FROM stub.assets WHERE (type=%i or type=%i or type=%i or type=%i) AND pkg=%Q", |
153 |
ASSET_FILE, ASSET_SAMPLE, ASSET_SHELL, ASSET_FILE_OWNER_MODE, pkg_name) != MPORT_OK) { |
154 |
sqlite3_finalize(count); |
155 |
RETURN_CURRENT_ERROR; |
156 |
} |
157 |
|
158 |
dispatch_sync(mportSQLSerial, ^{ |
159 |
switch (sqlite3_step(count)) { |
160 |
case SQLITE_ROW: |
161 |
*file_total = sqlite3_column_int(count, 0); |
162 |
sqlite3_finalize(count); |
163 |
break; |
164 |
default: |
165 |
err = (char *) sqlite3_errmsg(mport->db); |
166 |
result = MPORT_ERR_FATAL; |
167 |
sqlite3_finalize(count); |
168 |
} |
169 |
}); |
170 |
|
171 |
if (result == MPORT_ERR_FATAL) |
172 |
SET_ERRORX(result, "Error reading file count %s", err); |
173 |
return result; |
174 |
} |
175 |
|
176 |
static int |
177 |
create_package_row(mportInstance *mport, mportPackageMeta *pkg) |
178 |
{ |
179 |
/* Insert the package meta row into the packages table (We use pack here because things might have been twiddled) */ |
180 |
/* Note that this will be marked as dirty by default */ |
181 |
if (mport_db_do(mport->db, |
182 |
"INSERT INTO packages (pkg, version, origin, prefix, lang, options, comment, os_release, cpe, locked, deprecated, expiration_date, no_provide_shlib, flavor) VALUES (%Q,%Q,%Q,%Q,%Q,%Q,%Q,%Q,%Q,0,%Q,%ld,%d,%Q)", |
183 |
pkg->name, pkg->version, pkg->origin, pkg->prefix, pkg->lang, pkg->options, pkg->comment, |
184 |
pkg->os_release, pkg->cpe, pkg->deprecated, pkg->expiration_date, pkg->no_provide_shlib, pkg->flavor) != MPORT_OK) |
185 |
RETURN_CURRENT_ERROR; |
186 |
|
187 |
return MPORT_OK; |
188 |
} |
189 |
|
190 |
static int |
191 |
create_depends(mportInstance *mport, mportPackageMeta *pkg) |
192 |
{ |
193 |
/* Insert the depends into the master table */ |
194 |
if (mport_db_do(mport->db, |
195 |
"INSERT INTO depends (pkg, depend_pkgname, depend_pkgversion, depend_port) SELECT pkg,depend_pkgname,depend_pkgversion,depend_port FROM stub.depends WHERE pkg=%Q", |
196 |
pkg->name) != MPORT_OK) |
197 |
RETURN_CURRENT_ERROR; |
198 |
|
199 |
return MPORT_OK; |
200 |
} |
201 |
|
202 |
static int |
203 |
create_categories(mportInstance *mport, mportPackageMeta *pkg) |
204 |
{ |
205 |
/* Insert the categories into the master table */ |
206 |
if (mport_db_do(mport->db, |
207 |
"INSERT INTO categories (pkg, category) SELECT pkg, category FROM stub.categories WHERE pkg=%Q", |
208 |
pkg->name) != MPORT_OK) |
209 |
RETURN_CURRENT_ERROR; |
210 |
|
211 |
return MPORT_OK; |
212 |
} |
213 |
|
214 |
static char** |
215 |
parse_sample(char *input) |
216 |
{ |
217 |
char **ap, **argv; |
218 |
argv = calloc(3, sizeof(char *)); |
219 |
|
220 |
if (argv == NULL) |
221 |
return NULL; |
222 |
|
223 |
for (ap = argv; (*ap = strsep(&input, " \t")) != NULL;) { |
224 |
if (**ap != '\0') { |
225 |
if (++ap >= &argv[3]) |
226 |
break; |
227 |
} |
228 |
} |
229 |
|
230 |
return argv; |
231 |
} |
232 |
|
233 |
static int |
234 |
create_sample_file(mportInstance *mport, char *cwd, const char *file) |
235 |
{ |
236 |
char nonSample[FILENAME_MAX * 2]; |
237 |
char secondFile[FILENAME_MAX]; |
238 |
|
239 |
strlcpy(nonSample, file, FILENAME_MAX * 2); |
240 |
(void) snprintf(nonSample, FILENAME_MAX, "%s%s/%s", mport->root, cwd, file); |
241 |
char** fileargv = parse_sample(nonSample); |
242 |
|
243 |
if (fileargv[1] != '\0') { |
244 |
if (fileargv[1][0] == '/') |
245 |
strlcpy(secondFile, fileargv[1], FILENAME_MAX); |
246 |
else |
247 |
(void) snprintf(secondFile, FILENAME_MAX, "%s%s/%s", mport->root, cwd, fileargv[1]); |
248 |
|
249 |
if (!mport_file_exists(secondFile)) { |
250 |
if (mport_copy_file(fileargv[0], secondFile) != MPORT_OK) |
251 |
RETURN_CURRENT_ERROR; |
252 |
} |
253 |
} else { |
254 |
/* single file */ |
255 |
char *sptr = strcasestr(nonSample, ".sample"); |
256 |
if (sptr != NULL) { |
257 |
sptr[0] = '\0'; /* hack off .sample */ |
258 |
if (!mport_file_exists(nonSample)) { |
259 |
if (mport_copy_file(file, nonSample) != MPORT_OK) |
260 |
RETURN_CURRENT_ERROR; |
261 |
} |
262 |
} |
263 |
} |
264 |
|
265 |
free(fileargv); |
266 |
|
267 |
return MPORT_OK; |
268 |
} |
269 |
|
270 |
/** |
271 |
* Get the list of assets (plist entries) from the stub attached database (package we are installing) |
272 |
* filtered on entries that are not pre/post exec. |
273 |
*/ |
274 |
static int |
275 |
mport_bundle_read_get_assetlist(mportInstance *mport, mportPackageMeta *pkg, mportAssetList **alist_p, enum phase state) |
276 |
{ |
277 |
__block mportAssetList *alist; |
278 |
__block sqlite3_stmt *stmt = NULL; |
279 |
__block int result = MPORT_OK; |
280 |
__block char *err; |
281 |
|
282 |
if ((alist = mport_assetlist_new()) == NULL) |
283 |
RETURN_ERROR(MPORT_ERR_FATAL, "Out of memory."); |
284 |
|
285 |
*alist_p = alist; |
286 |
|
287 |
if (state == PREINSTALL) { |
288 |
if (mport_db_prepare(mport->db, &stmt, |
289 |
"SELECT type,data,checksum,owner,grp,mode FROM stub.assets WHERE pkg=%Q and type in (%d, %d)", |
290 |
pkg->name, ASSET_CWD, ASSET_PREEXEC) != MPORT_OK) { |
291 |
sqlite3_finalize(stmt); |
292 |
RETURN_CURRENT_ERROR; |
293 |
} |
294 |
} else if (state == ACTUALINSTALL) { |
295 |
if (mport_db_prepare(mport->db, &stmt, |
296 |
"SELECT type,data,checksum,owner,grp,mode FROM stub.assets WHERE pkg=%Q and type not in (%d, %d)", |
297 |
pkg->name, ASSET_PREEXEC, ASSET_POSTEXEC) != MPORT_OK) { |
298 |
sqlite3_finalize(stmt); |
299 |
RETURN_CURRENT_ERROR; |
300 |
} |
301 |
} else if (state == POSTINSTALL) { |
302 |
if (mport_db_prepare(mport->db, &stmt, |
303 |
"SELECT type,data,checksum,owner,grp,mode FROM stub.assets WHERE pkg=%Q and type in (%d, %d)", |
304 |
pkg->name, ASSET_CWD, ASSET_POSTEXEC) != MPORT_OK) { |
305 |
sqlite3_finalize(stmt); |
306 |
RETURN_CURRENT_ERROR; |
307 |
} |
308 |
} |
309 |
|
310 |
if (stmt == NULL) { |
311 |
RETURN_ERROR(MPORT_ERR_FATAL, "Statement was null"); |
312 |
} |
313 |
|
314 |
dispatch_sync(mportSQLSerial, ^{ |
315 |
while (1) { |
316 |
mportAssetListEntry *e; |
317 |
|
318 |
int ret = sqlite3_step(stmt); |
319 |
|
320 |
if (ret == SQLITE_BUSY || ret == SQLITE_LOCKED) { |
321 |
sleep(1); |
322 |
ret = sqlite3_step(stmt); |
323 |
} |
324 |
|
325 |
if (ret == SQLITE_DONE) |
326 |
break; |
327 |
|
328 |
if (ret != SQLITE_ROW) { |
329 |
err = (char *) sqlite3_errmsg(mport->db); |
330 |
result = MPORT_ERR_FATAL; |
331 |
break; // we finalize below |
332 |
} |
333 |
|
334 |
e = (mportAssetListEntry *) calloc(1, sizeof(mportAssetListEntry)); |
335 |
|
336 |
if (e == NULL) { |
337 |
err = "Out of memory"; |
338 |
result = MPORT_ERR_FATAL; |
339 |
break; // we finalize below |
340 |
} |
341 |
|
342 |
e->type = (mportAssetListEntryType) sqlite3_column_int(stmt, 0); |
343 |
const unsigned char *data = sqlite3_column_text(stmt, 1); |
344 |
const unsigned char *checksum = sqlite3_column_text(stmt, 2); |
345 |
const unsigned char *owner = sqlite3_column_text(stmt, 3); |
346 |
const unsigned char *group = sqlite3_column_text(stmt, 4); |
347 |
const unsigned char *mode = sqlite3_column_text(stmt, 5); |
348 |
|
349 |
e->data = data == NULL ? NULL : strdup((char *) data); |
350 |
if (checksum != NULL) |
351 |
e->checksum = strdup((char *) checksum); |
352 |
if (owner != NULL) |
353 |
e->owner = strdup((char *) owner); |
354 |
if (group != NULL) |
355 |
e->group = strdup((char *) group); |
356 |
if (mode != NULL) |
357 |
e->mode = strdup((char *) mode); |
358 |
|
359 |
STAILQ_INSERT_TAIL(alist, e, next); |
360 |
} |
361 |
|
362 |
sqlite3_finalize(stmt); |
363 |
}); |
364 |
|
365 |
if (result == MPORT_ERR_FATAL) |
366 |
SET_ERRORX(result, "Error reading assets %s", err); |
367 |
return result; |
368 |
} |
369 |
|
370 |
static int |
371 |
do_actual_install(mportInstance *mport, mportBundleRead *bundle, mportPackageMeta *pkg) |
372 |
{ |
373 |
mportAssetList *alist = NULL; |
374 |
__block mportAssetListEntry *e = NULL; |
375 |
int file_total; |
376 |
int file_count = 0; |
377 |
struct archive_entry *entry; |
378 |
char *orig_cwd = NULL; |
379 |
uid_t owner = 0; /* root */ |
380 |
gid_t group = 0; /* wheel */ |
381 |
mode_t *set = NULL; |
382 |
mode_t newmode; |
383 |
mode_t *dirset = NULL; |
384 |
mode_t dirnewmode; |
385 |
char *mode = NULL; |
386 |
char *mkdirp = NULL; |
387 |
struct stat sb; |
388 |
__block char file[FILENAME_MAX], cwd[FILENAME_MAX]; |
389 |
__block sqlite3_stmt *insert = NULL; |
390 |
|
391 |
/* sadly, we can't just use abs pathnames, because it will break hardlinks */ |
392 |
orig_cwd = getcwd(NULL, 0); |
393 |
|
394 |
if (get_file_count(mport, pkg->name, &file_total) != MPORT_OK) |
395 |
goto ERROR; |
396 |
|
397 |
mport_call_progress_init_cb(mport, "Installing %s-%s", pkg->name, pkg->version); |
398 |
|
399 |
if (mport_bundle_read_get_assetlist(mport, pkg, &alist, ACTUALINSTALL) != MPORT_OK) |
400 |
goto ERROR; |
401 |
|
402 |
if (create_package_row(mport, pkg) != MPORT_OK) |
403 |
goto ERROR; |
404 |
|
405 |
if (create_depends(mport, pkg) != MPORT_OK) |
406 |
goto ERROR; |
407 |
|
408 |
if (create_categories(mport, pkg) != MPORT_OK) |
409 |
goto ERROR; |
410 |
|
411 |
/* Insert the assets into the master table (We do this one by one because we want to insert file |
412 |
* assets as absolute paths. */ |
413 |
if (mport_db_prepare(mport->db, &insert, |
414 |
"INSERT INTO assets (pkg, type, data, checksum, owner, grp, mode) values (%Q,?,?,?,?,?,?)", |
415 |
pkg->name) != MPORT_OK) |
416 |
goto ERROR; |
417 |
|
418 |
(void) strlcpy(cwd, pkg->prefix, sizeof(cwd)); |
419 |
|
420 |
if (mport_chdir(mport, cwd) != MPORT_OK) |
421 |
goto ERROR; |
422 |
|
423 |
mport_db_do(mport->db, "BEGIN TRANSACTION"); |
424 |
|
425 |
STAILQ_FOREACH(e, alist, next) { |
426 |
switch (e->type) { |
427 |
case ASSET_CWD: |
428 |
(void) strlcpy(cwd, e->data == NULL ? pkg->prefix : e->data, sizeof(cwd)); |
429 |
if (mport_chdir(mport, cwd) != MPORT_OK) |
430 |
goto ERROR; |
431 |
break; |
432 |
case ASSET_CHMOD: |
433 |
if (mode != NULL) |
434 |
free(mode); |
435 |
/* TODO: should we reset the mode rather than NULL here */ |
436 |
if (e->data == NULL) |
437 |
mode = NULL; |
438 |
else |
439 |
mode = strdup(e->data); |
440 |
break; |
441 |
case ASSET_CHOWN: |
442 |
owner = mport_get_uid(e->data); |
443 |
break; |
444 |
case ASSET_CHGRP: |
445 |
group = mport_get_gid(e->data); |
446 |
break; |
447 |
case ASSET_DIR: |
448 |
case ASSET_DIRRM: |
449 |
case ASSET_DIRRMTRY: |
450 |
case ASSET_DIR_OWNER_MODE: |
451 |
mkdirp = strdup( e->data == NULL ? "" : e->data); /* need a char * here */ |
452 |
if (mkdirp == NULL || mport_mkdirp(mkdirp, S_IRWXU | S_IRWXG | S_IRWXO) == 0) { |
453 |
free(mkdirp); |
454 |
SET_ERRORX(MPORT_ERR_FATAL, "Unable to create directory %s", e->data); |
455 |
goto ERROR; |
456 |
} |
457 |
free(mkdirp); |
458 |
|
459 |
if (e->mode != NULL && e->mode[0] != '\0') { |
460 |
if ((dirset = setmode(e->mode)) == NULL) |
461 |
goto ERROR; |
462 |
dirnewmode = getmode(dirset, sb.st_mode); |
463 |
free(dirset); |
464 |
if (chmod(e->data, dirnewmode)) |
465 |
goto ERROR; |
466 |
} |
467 |
if (e->owner != NULL && e->group != NULL && e->owner[0] != '\0' && |
468 |
e->group[0] != '\0') { |
469 |
if (chown(e->data, mport_get_uid(e->owner), mport_get_gid(e->group)) == -1) { |
470 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to change owner"); |
471 |
goto ERROR; |
472 |
} |
473 |
} else if (e->owner != NULL && e->owner[0] != '\0') { |
474 |
if (chown(e->data, mport_get_uid(e->owner), group) == -1) { |
475 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to change owner"); |
476 |
goto ERROR; |
477 |
} |
478 |
} else if (e->group != NULL && e->group[0] != '\0') { |
479 |
if (chown(e->data, owner, mport_get_gid(e->group)) == -1) { |
480 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to change owner"); |
481 |
goto ERROR; |
482 |
} |
483 |
} |
484 |
|
485 |
break; |
486 |
case ASSET_EXEC: |
487 |
if (mport_run_asset_exec(mport, e->data, cwd, file) != MPORT_OK) |
488 |
goto ERROR; |
489 |
break; |
490 |
case ASSET_FILE_OWNER_MODE: |
491 |
/* FALLS THROUGH */ |
492 |
case ASSET_FILE: |
493 |
/* FALLS THROUGH */ |
494 |
case ASSET_SHELL: |
495 |
/* FALLS THROUGH */ |
496 |
case ASSET_SAMPLE: |
497 |
if (mport_bundle_read_next_entry(bundle, &entry) != MPORT_OK) |
498 |
goto ERROR; |
499 |
|
500 |
(void) snprintf(file, FILENAME_MAX, "%s%s/%s", mport->root, cwd, e->data); |
501 |
|
502 |
if (e->type == ASSET_SAMPLE) |
503 |
for (int ch = 0; ch < FILENAME_MAX; ch++) { |
504 |
if (file[ch] == '\0') |
505 |
break; |
506 |
if (file[ch] == ' ' || file[ch] == '\t') { |
507 |
file[ch] = '\0'; |
508 |
break; |
509 |
} |
510 |
} |
511 |
|
512 |
if (entry == NULL) { |
513 |
SET_ERROR(MPORT_ERR_FATAL, "Unexpected EOF with archive file"); |
514 |
goto ERROR; |
515 |
} |
516 |
|
517 |
archive_entry_set_pathname(entry, file); |
518 |
|
519 |
if (mport_bundle_read_extract_next_file(bundle, entry) != MPORT_OK) |
520 |
goto ERROR; |
521 |
|
522 |
if (lstat(file, &sb)) { |
523 |
SET_ERRORX(MPORT_ERR_FATAL, "Unable to stat file %s", file); |
524 |
goto ERROR; |
525 |
} |
526 |
|
527 |
if (S_ISREG(sb.st_mode)) { |
528 |
if (e->type == ASSET_FILE_OWNER_MODE) { |
529 |
/* Test for owner and group settings, otherwise roll with our default. */ |
530 |
if (e->owner != NULL && e->group != NULL && e->owner[0] != '\0' && |
531 |
e->group[0] != '\0') { |
532 |
#ifdef DEBUG |
533 |
fprintf(stderr, "owner %s and group %s\n", e->owner, e->group); |
534 |
#endif |
535 |
if (chown(file, mport_get_uid(e->owner), |
536 |
mport_get_gid(e->group)) == -1) { |
537 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to change owner"); |
538 |
goto ERROR; |
539 |
} |
540 |
} else if (e->owner != NULL && e->owner[0] != '\0') { |
541 |
#ifdef DEBUG |
542 |
fprintf(stderr, "owner %s\n", e->owner); |
543 |
#endif |
544 |
if (chown(file, mport_get_uid(e->owner), group) == -1) { |
545 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to change owner"); |
546 |
goto ERROR; |
547 |
} |
548 |
} else if (e->group != NULL && e->group[0] != '\0') { |
549 |
#ifdef DEBUG |
550 |
fprintf(stderr, "group %s\n", e->group); |
551 |
#endif |
552 |
if (chown(file, owner, mport_get_gid(e->group)) == -1) { |
553 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to change owner"); |
554 |
goto ERROR; |
555 |
} |
556 |
} else { |
557 |
// use default. |
558 |
if (chown(file, owner, group) == -1) { |
559 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to change owner"); |
560 |
goto ERROR; |
561 |
} |
562 |
} |
563 |
} else { |
564 |
/* Set the owner and group */ |
565 |
if (chown(file, owner, group) == -1) { |
566 |
SET_ERRORX(MPORT_ERR_FATAL, |
567 |
"Unable to set permissions on file %s", file); |
568 |
goto ERROR; |
569 |
} |
570 |
} |
571 |
|
572 |
/* Set the file permissions, assumes non NFSv4 */ |
573 |
if (mode != NULL) { |
574 |
if (stat(file, &sb)) { |
575 |
SET_ERRORX(MPORT_ERR_FATAL, "Unable to stat file %s", file); |
576 |
goto ERROR; |
577 |
} |
578 |
if (e->type == ASSET_FILE_OWNER_MODE && e->mode != NULL) { |
579 |
if ((set = setmode(e->mode)) == NULL) { |
580 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to set mode"); |
581 |
goto ERROR; |
582 |
} |
583 |
} else { |
584 |
if ((set = setmode(mode)) == NULL) { |
585 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to set mode"); |
586 |
goto ERROR; |
587 |
} |
588 |
} |
589 |
newmode = getmode(set, sb.st_mode); |
590 |
free(set); |
591 |
if (chmod(file, newmode)) { |
592 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to set file permissions"); |
593 |
goto ERROR; |
594 |
} |
595 |
} |
596 |
|
597 |
/* shell registration */ |
598 |
if (e->type == ASSET_SHELL && mport_shell_register(file) != MPORT_OK) { |
599 |
goto ERROR; |
600 |
} |
601 |
|
602 |
/* for sample files, if we don't have an existing file, make a new one */ |
603 |
if (e->type == ASSET_SAMPLE && create_sample_file(mport, cwd, e->data) != MPORT_OK) { |
604 |
SET_ERRORX(MPORT_ERR_FATAL, "Unable to create sample file from %s", |
605 |
file); |
606 |
goto ERROR; |
607 |
} |
608 |
} |
609 |
|
610 |
(mport->progress_step_cb)(++file_count, file_total, file); |
611 |
|
612 |
break; |
613 |
default: |
614 |
/* do nothing */ |
615 |
break; |
616 |
} |
617 |
|
618 |
/* insert this asset into the master database */ |
619 |
__block int code = MPORT_OK; |
620 |
__block const char *err; |
621 |
__block char *filePtr = strdup(file); |
622 |
__block char *cwdPtr = strdup(cwd); |
623 |
dispatch_sync(mportSQLSerial, ^{ |
624 |
char dir[FILENAME_MAX]; |
625 |
if (sqlite3_bind_int(insert, 1, (int) e->type) != SQLITE_OK) { |
626 |
code = MPORT_ERR_FATAL; |
627 |
err = sqlite3_errmsg(mport->db); |
628 |
return; |
629 |
} |
630 |
if (e->type == ASSET_FILE || e->type == ASSET_SAMPLE || e->type == ASSET_SHELL || |
631 |
e->type == ASSET_FILE_OWNER_MODE) { |
632 |
/* don't put the root in the database! */ |
633 |
if (sqlite3_bind_text(insert, 2, filePtr + strlen(mport->root), -1, SQLITE_STATIC) != |
634 |
SQLITE_OK) { |
635 |
code = MPORT_ERR_FATAL; |
636 |
err = sqlite3_errmsg(mport->db); |
637 |
return; |
638 |
} |
639 |
|
640 |
if (sqlite3_bind_text(insert, 3, e->checksum, -1, SQLITE_STATIC) != SQLITE_OK) { |
641 |
code = MPORT_ERR_FATAL; |
642 |
err = sqlite3_errmsg(mport->db); |
643 |
return; |
644 |
} |
645 |
|
646 |
if (e->owner != NULL) { |
647 |
if (sqlite3_bind_text(insert, 4, e->owner, -1, SQLITE_STATIC) != SQLITE_OK) { |
648 |
code = MPORT_ERR_FATAL; |
649 |
err = sqlite3_errmsg(mport->db); |
650 |
return; |
651 |
} |
652 |
} else { |
653 |
if (sqlite3_bind_null(insert, 4) != SQLITE_OK) { |
654 |
code = MPORT_ERR_FATAL; |
655 |
err = sqlite3_errmsg(mport->db); |
656 |
return; |
657 |
} |
658 |
} |
659 |
|
660 |
if (e->group != NULL) { |
661 |
if (sqlite3_bind_text(insert, 5, e->group, -1, SQLITE_STATIC) != SQLITE_OK) { |
662 |
code = MPORT_ERR_FATAL; |
663 |
err = sqlite3_errmsg(mport->db); |
664 |
return; |
665 |
} |
666 |
} else { |
667 |
if (sqlite3_bind_null(insert, 5) != SQLITE_OK) { |
668 |
code = MPORT_ERR_FATAL; |
669 |
err = sqlite3_errmsg(mport->db); |
670 |
return; |
671 |
} |
672 |
} |
673 |
|
674 |
if (e->mode != NULL) { |
675 |
if (sqlite3_bind_text(insert, 6, e->mode, -1, SQLITE_STATIC) != SQLITE_OK) { |
676 |
code = MPORT_ERR_FATAL; |
677 |
err = sqlite3_errmsg(mport->db); |
678 |
return; |
679 |
} |
680 |
} else { |
681 |
if (sqlite3_bind_null(insert, 6) != SQLITE_OK) { |
682 |
code = MPORT_ERR_FATAL; |
683 |
err = sqlite3_errmsg(mport->db); |
684 |
return; |
685 |
} |
686 |
} |
687 |
} else if (e->type == ASSET_DIR || e->type == ASSET_DIRRM || e->type == ASSET_DIRRMTRY) { |
688 |
/* if data starts with /, it's most likely an absolute path. Don't prepend cwd */ |
689 |
if (e->data != NULL && e->data[0] == '/') |
690 |
(void) snprintf(dir, FILENAME_MAX, "%s", e->data); |
691 |
else |
692 |
(void) snprintf(dir, FILENAME_MAX, "%s/%s", cwdPtr, e->data); |
693 |
|
694 |
if (sqlite3_bind_text(insert, 2, dir, -1, SQLITE_STATIC) != SQLITE_OK) { |
695 |
code = MPORT_ERR_FATAL; |
696 |
err = sqlite3_errmsg(mport->db); |
697 |
return; |
698 |
} |
699 |
|
700 |
if (sqlite3_bind_null(insert, 3) != SQLITE_OK) { |
701 |
code = MPORT_ERR_FATAL; |
702 |
err = sqlite3_errmsg(mport->db); |
703 |
return; |
704 |
} |
705 |
|
706 |
if (sqlite3_bind_null(insert, 4) != SQLITE_OK) { |
707 |
code = MPORT_ERR_FATAL; |
708 |
err = sqlite3_errmsg(mport->db); |
709 |
return; |
710 |
} |
711 |
|
712 |
if (sqlite3_bind_null(insert, 5) != SQLITE_OK) { |
713 |
code = MPORT_ERR_FATAL; |
714 |
err = sqlite3_errmsg(mport->db); |
715 |
return; |
716 |
} |
717 |
|
718 |
if (sqlite3_bind_null(insert, 6) != SQLITE_OK) { |
719 |
code = MPORT_ERR_FATAL; |
720 |
err = sqlite3_errmsg(mport->db); |
721 |
return; |
722 |
} |
723 |
} else { |
724 |
if (sqlite3_bind_text(insert, 2, e->data, -1, SQLITE_STATIC) != SQLITE_OK) { |
725 |
code = MPORT_ERR_FATAL; |
726 |
err = sqlite3_errmsg(mport->db); |
727 |
return; |
728 |
} |
729 |
|
730 |
if (sqlite3_bind_null(insert, 3) != SQLITE_OK) { |
731 |
code = MPORT_ERR_FATAL; |
732 |
err = sqlite3_errmsg(mport->db); |
733 |
return; |
734 |
} |
735 |
|
736 |
if (sqlite3_bind_null(insert, 4) != SQLITE_OK) { |
737 |
code = MPORT_ERR_FATAL; |
738 |
err = sqlite3_errmsg(mport->db); |
739 |
return; |
740 |
} |
741 |
|
742 |
if (sqlite3_bind_null(insert, 5) != SQLITE_OK) { |
743 |
code = MPORT_ERR_FATAL; |
744 |
err = sqlite3_errmsg(mport->db); |
745 |
return; |
746 |
} |
747 |
|
748 |
if (sqlite3_bind_null(insert, 6) != SQLITE_OK) { |
749 |
code = MPORT_ERR_FATAL; |
750 |
err = sqlite3_errmsg(mport->db); |
751 |
return; |
752 |
} |
753 |
} |
754 |
|
755 |
if (sqlite3_step(insert) != SQLITE_DONE) { |
756 |
code = MPORT_ERR_FATAL; |
757 |
err = sqlite3_errmsg(mport->db); |
758 |
return; |
759 |
} |
760 |
|
761 |
sqlite3_reset(insert); |
762 |
}); |
763 |
|
764 |
free(filePtr); |
765 |
free(cwdPtr); |
766 |
if (code != MPORT_OK) { |
767 |
SET_ERROR(code, err); |
768 |
goto ERROR; |
769 |
} |
770 |
} |
771 |
|
772 |
if (mport_db_do(mport->db, "COMMIT") != MPORT_OK) { |
773 |
SET_ERROR(MPORT_ERR_FATAL, sqlite3_errmsg(mport->db)); |
774 |
goto ERROR; |
775 |
} |
776 |
sqlite3_finalize(insert); |
777 |
|
778 |
mport_pkgmeta_logevent(mport, pkg, "Installed"); |
779 |
|
780 |
(mport->progress_free_cb)(); |
781 |
(void) mport_chdir(NULL, orig_cwd); |
782 |
free(orig_cwd); |
783 |
mport_assetlist_free(alist); |
784 |
return (MPORT_OK); |
785 |
|
786 |
ERROR: |
787 |
sqlite3_finalize(insert); |
788 |
(mport->progress_free_cb)(); |
789 |
free(orig_cwd); |
790 |
mport_assetlist_free(alist); |
791 |
RETURN_CURRENT_ERROR; |
792 |
} |
793 |
|
794 |
|
795 |
#define COPY_METAFILE(TYPE) (void)snprintf(from, FILENAME_MAX, "%s/%s/%s-%s/%s", bundle->tmpdir, MPORT_STUB_INFRA_DIR, pkg->name, pkg->version, TYPE); \ |
796 |
if (mport_file_exists(from)) { \ |
797 |
(void)snprintf(to, FILENAME_MAX, "%s%s/%s-%s/%s", mport->root, MPORT_INST_INFRA_DIR, pkg->name, pkg->version, TYPE); \ |
798 |
if (mport_mkdir(dirname(to)) != MPORT_OK) \ |
799 |
RETURN_CURRENT_ERROR; \ |
800 |
if (mport_copy_file(from, to) != MPORT_OK) \ |
801 |
RETURN_CURRENT_ERROR; \ |
802 |
} |
803 |
|
804 |
static int |
805 |
mark_complete(mportInstance *mport, mportPackageMeta *pkg) |
806 |
{ |
807 |
if (mport_db_do(mport->db, "UPDATE packages SET status='clean' WHERE pkg=%Q", pkg->name) != MPORT_OK) { |
808 |
SET_ERROR(MPORT_ERR_FATAL, "Unable to mark package clean"); |
809 |
RETURN_CURRENT_ERROR; |
810 |
} |
811 |
|
812 |
return MPORT_OK; |
813 |
} |
814 |
|
815 |
|
816 |
static int |
817 |
do_post_install(mportInstance *mport, mportBundleRead *bundle, mportPackageMeta *pkg) |
818 |
{ |
819 |
char to[FILENAME_MAX], from[FILENAME_MAX]; /* keep these for COPY_METAFILE */ |
820 |
|
821 |
COPY_METAFILE(MPORT_MTREE_FILE); |
822 |
COPY_METAFILE(MPORT_INSTALL_FILE); |
823 |
COPY_METAFILE(MPORT_DEINSTALL_FILE); |
824 |
COPY_METAFILE(MPORT_MESSAGE_FILE); |
825 |
|
826 |
if (run_postexec(mport, pkg) != MPORT_OK) |
827 |
RETURN_CURRENT_ERROR; |
828 |
|
829 |
if (display_pkg_msg(mport, bundle, pkg) != MPORT_OK) |
830 |
RETURN_CURRENT_ERROR; |
831 |
|
832 |
if (run_pkg_install(mport, bundle, pkg, "POST-INSTALL") != MPORT_OK) |
833 |
RETURN_CURRENT_ERROR; |
834 |
|
835 |
return mark_complete(mport, pkg); |
836 |
} |
837 |
|
838 |
|
839 |
static int |
840 |
run_postexec(mportInstance *mport, mportPackageMeta *pkg) |
841 |
{ |
842 |
mportAssetList *alist; |
843 |
mportAssetListEntry *e = NULL; |
844 |
char cwd[FILENAME_MAX]; |
845 |
|
846 |
/* Process @postexec steps */ |
847 |
if (mport_bundle_read_get_assetlist(mport, pkg, &alist, POSTINSTALL) != MPORT_OK) |
848 |
goto ERROR; |
849 |
|
850 |
(void) strlcpy(cwd, pkg->prefix, sizeof(cwd)); |
851 |
|
852 |
if (mport_chdir(mport, cwd) != MPORT_OK) |
853 |
goto ERROR; |
854 |
|
855 |
STAILQ_FOREACH(e, alist, next) { |
856 |
char file[FILENAME_MAX]; |
857 |
if (e->data == NULL) { |
858 |
snprintf(file, sizeof(file), "%s", mport->root); |
859 |
} else if (e->data[0] == '/') { |
860 |
snprintf(file, sizeof(file), "%s%s", mport->root, e->data); |
861 |
} else { |
862 |
snprintf(file, sizeof(file), "%s%s/%s", mport->root, pkg->prefix, e->data); |
863 |
} |
864 |
|
865 |
switch (e->type) { |
866 |
case ASSET_CWD: |
867 |
(void) strlcpy(cwd, e->data == NULL ? pkg->prefix : e->data, sizeof(cwd)); |
868 |
if (mport_chdir(mport, cwd) != MPORT_OK) |
869 |
goto ERROR; |
870 |
break; |
871 |
case ASSET_POSTEXEC: |
872 |
if (mport_run_asset_exec(mport, e->data, cwd, file) != MPORT_OK) |
873 |
goto ERROR; |
874 |
break; |
875 |
default: |
876 |
/* do nothing */ |
877 |
break; |
878 |
} |
879 |
} |
880 |
|
881 |
mport_assetlist_free(alist); |
882 |
mport_pkgmeta_logevent(mport, pkg, "postexec"); |
883 |
|
884 |
return MPORT_OK; |
885 |
|
886 |
ERROR: |
887 |
// TODO: asset list free |
888 |
RETURN_CURRENT_ERROR; |
889 |
} |
890 |
|
891 |
|
892 |
static int |
893 |
run_mtree(mportInstance *mport, mportBundleRead *bundle, mportPackageMeta *pkg) |
894 |
{ |
895 |
char file[FILENAME_MAX]; |
896 |
int ret; |
897 |
|
898 |
(void) snprintf(file, FILENAME_MAX, "%s/%s/%s-%s/%s", bundle->tmpdir, MPORT_STUB_INFRA_DIR, pkg->name, |
899 |
pkg->version, |
900 |
MPORT_MTREE_FILE); |
901 |
|
902 |
if (mport_file_exists(file)) { |
903 |
if ((ret = mport_xsystem(mport, "%s -U -f %s -d -e -p %s >/dev/null", MPORT_MTREE_BIN, file, |
904 |
pkg->prefix)) != 0) |
905 |
RETURN_ERRORX(MPORT_ERR_FATAL, "%s returned non-zero: %i", MPORT_MTREE_BIN, ret); |
906 |
} |
907 |
|
908 |
return MPORT_OK; |
909 |
} |
910 |
|
911 |
|
912 |
static int |
913 |
run_pkg_install(mportInstance *mport, mportBundleRead *bundle, mportPackageMeta *pkg, const char *mode) |
914 |
{ |
915 |
char file[FILENAME_MAX]; |
916 |
int ret; |
917 |
|
918 |
(void) snprintf(file, FILENAME_MAX, "%s/%s/%s-%s/%s", bundle->tmpdir, MPORT_STUB_INFRA_DIR, pkg->name, |
919 |
pkg->version, |
920 |
MPORT_INSTALL_FILE); |
921 |
|
922 |
if (mport_file_exists(file)) { |
923 |
if (chmod(file, 755) != 0) |
924 |
RETURN_ERRORX(MPORT_ERR_FATAL, "chmod(%s, 755): %s", file, strerror(errno)); |
925 |
|
926 |
if ((ret = mport_xsystem(mport, "PKG_PREFIX=%s %s %s %s", pkg->prefix, file, pkg->name, mode)) != 0) |
927 |
RETURN_ERRORX(MPORT_ERR_FATAL, "%s %s returned non-zero: %i", MPORT_INSTALL_FILE, mode, ret); |
928 |
} |
929 |
|
930 |
return MPORT_OK; |
931 |
} |
932 |
|
933 |
|
934 |
static int |
935 |
display_pkg_msg(mportInstance *mport, mportBundleRead *bundle, mportPackageMeta *pkg) |
936 |
{ |
937 |
char filename[FILENAME_MAX]; |
938 |
char *buf; |
939 |
struct stat st; |
940 |
FILE *file; |
941 |
|
942 |
(void) snprintf(filename, FILENAME_MAX, "%s/%s/%s-%s/%s", bundle->tmpdir, MPORT_STUB_INFRA_DIR, pkg->name, |
943 |
pkg->version, MPORT_MESSAGE_FILE); |
944 |
|
945 |
if (stat(filename, &st) == -1) |
946 |
/* if we couldn't stat the file, we assume there isn't a pkg-msg */ |
947 |
return MPORT_OK; |
948 |
|
949 |
if ((file = fopen(filename, "r")) == NULL) |
950 |
RETURN_ERRORX(MPORT_ERR_FATAL, "Couldn't open %s: %s", filename, strerror(errno)); |
951 |
|
952 |
if ((buf = (char *) calloc((size_t) (st.st_size + 1), sizeof(char))) == NULL) |
953 |
RETURN_ERROR(MPORT_ERR_FATAL, "Out of memory."); |
954 |
|
955 |
if (fread(buf, 1, (size_t) st.st_size, file) != (size_t) st.st_size) { |
956 |
free(buf); |
957 |
RETURN_ERRORX(MPORT_ERR_FATAL, "Read error: %s", strerror(errno)); |
958 |
} |
959 |
|
960 |
buf[st.st_size] = 0; |
961 |
|
962 |
mport_call_msg_cb(mport, buf); |
963 |
|
964 |
free(buf); |
965 |
|
966 |
return MPORT_OK; |
967 |
} |