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