fetch.c 4.62 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* fetch.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
 * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
Natanael Copa's avatar
Natanael Copa committed
15
#include <errno.h>
16 17 18 19 20 21 22 23

#include "apk_applet.h"
#include "apk_database.h"
#include "apk_state.h"
#include "apk_io.h"

#define FETCH_RECURSIVE		1
#define FETCH_STDOUT		2
Natanael Copa's avatar
Natanael Copa committed
24
#define FETCH_LINK		4
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

struct fetch_ctx {
	unsigned int flags;
	const char *outdir;
};

static int fetch_parse(void *ctx, int optch, int optindex, const char *optarg)
{
	struct fetch_ctx *fctx = (struct fetch_ctx *) ctx;

	switch (optch) {
	case 'R':
		fctx->flags |= FETCH_RECURSIVE;
		break;
	case 's':
		fctx->flags |= FETCH_STDOUT;
		break;
Natanael Copa's avatar
Natanael Copa committed
42 43 44
	case 'L':
		fctx->flags |= FETCH_LINK;
		break;
45 46 47 48 49 50 51 52 53 54 55 56 57 58
	case 'o':
		fctx->outdir = optarg;
		break;
	default:
		return -1;
	}
	return 0;
}

static int fetch_package(struct fetch_ctx *fctx,
			 struct apk_database *db,
			 struct apk_package *pkg)
{
	struct apk_istream *is;
Natanael Copa's avatar
Natanael Copa committed
59 60
	char infile[256];
	char outfile[256];
61 62
	int i, r, fd;

Natanael Copa's avatar
Natanael Copa committed
63
	if (!(fctx->flags & FETCH_STDOUT)) {
64
		struct stat st;
65

Natanael Copa's avatar
Natanael Copa committed
66
		snprintf(outfile, sizeof(outfile), "%s/%s-%s.apk",
67 68 69
			 fctx->outdir ? fctx->outdir : ".",
			 pkg->name->name, pkg->version);

70
		if (lstat(outfile, &st) == 0 && st.st_size == pkg->size)
71 72 73 74 75 76 77 78 79 80 81 82 83 84
			return 0;
	}
	apk_message("Downloading %s-%s", pkg->name->name, pkg->version);

	for (i = 0; i < APK_MAX_REPOS; i++)
		if (pkg->repos & BIT(i))
			break;

	if (i >= APK_MAX_REPOS) {
		apk_error("%s-%s: not present in any repository",
			  pkg->name->name, pkg->version);
		return -1;
	}

Natanael Copa's avatar
Natanael Copa committed
85
	snprintf(infile, sizeof(infile), "%s/%s-%s.apk",
86
		 db->repos[i].url, pkg->name->name, pkg->version);
Natanael Copa's avatar
Natanael Copa committed
87 88 89 90 91

	if (fctx->flags & FETCH_STDOUT) {
		fd = STDOUT_FILENO;
	} else {
		if ((fctx->flags & FETCH_LINK) && apk_url_local_file(infile)) {
92
			char real_infile[256];
93 94 95 96
			int n;
			n = readlink(infile, real_infile, sizeof(real_infile));
			if (n > 0 && n < sizeof(real_infile))
				real_infile[n] = '\0';
97
			if (link(real_infile, outfile) == 0)
Natanael Copa's avatar
Natanael Copa committed
98 99 100 101 102 103 104 105 106 107
				return 0;
		}
		fd = creat(outfile, 0644);
		if (fd < 0) {
			apk_error("%s: %s", outfile, strerror(errno));
			return -1;
		}
	}

	is = apk_istream_from_url(infile);
108
	if (is == NULL) {
Natanael Copa's avatar
Natanael Copa committed
109
		apk_error("Unable to download '%s'", infile);
110 111 112 113
		return -1;
	}

	r = apk_istream_splice(is, fd, pkg->size, NULL, NULL);
Natanael Copa's avatar
Natanael Copa committed
114
	is->close(is);
115 116
	if (fd != STDOUT_FILENO)
		close(fd);
117
	if (r != pkg->size) {
Natanael Copa's avatar
Natanael Copa committed
118 119
		apk_error("Unable to download '%s'", infile);
		unlink(outfile);
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
		return -1;
	}

	return 0;
}

static int fetch_main(void *ctx, int argc, char **argv)
{
	struct fetch_ctx *fctx = (struct fetch_ctx *) ctx;
	struct apk_database db;
	int i, j, r;

	r = apk_db_open(&db, apk_root, APK_OPENF_EMPTY_STATE);
	if (r != 0)
		return r;

	for (i = 0; i < argc; i++) {
		struct apk_dependency dep = (struct apk_dependency) {
			.name = apk_db_get_name(&db, APK_BLOB_STR(argv[i])),
			.result_mask = APK_DEPMASK_REQUIRE,
		};

		if (fctx->flags & FETCH_RECURSIVE) {
			struct apk_state *state;
			struct apk_change *change;

			state = apk_state_new(&db);
			r = apk_state_lock_dependency(state, &dep);
			if (r != 0) {
				apk_state_unref(state);
				apk_error("Unable to install '%s'",
					  dep.name->name);
				goto err;
			}

			list_for_each_entry(change, &state->change_list_head, change_list) {
				r = fetch_package(fctx, &db, change->newpkg);
				if (r != 0)
					goto err;
			}

			apk_state_unref(state);
		} else if (dep.name->pkgs != NULL) {
			struct apk_package *pkg = NULL;

			for (j = 0; j < dep.name->pkgs->num; j++)
				if (pkg == NULL ||
				    apk_version_compare(APK_BLOB_STR(dep.name->pkgs->item[j]->version),
							APK_BLOB_STR(pkg->version))
				    == APK_VERSION_GREATER)
					pkg = dep.name->pkgs->item[j];

			r = fetch_package(fctx, &db, pkg);
			if (r != 0)
				goto err;
		} else {
			apk_message("Unable to get '%s'", dep.name->name);
			r = -1;
			break;
		}
	}

err:
	apk_db_close(&db);
	return r;
}

static struct option fetch_options[] = {
	{ "recursive",	no_argument,		NULL, 'R' },
	{ "stdout",	no_argument,		NULL, 's' },
Natanael Copa's avatar
Natanael Copa committed
190
	{ "link",	no_argument,		NULL, 'L' },
191 192 193 194 195
	{ "output",	required_argument,	NULL, 'o' },
};

static struct apk_applet apk_fetch = {
	.name = "fetch",
Natanael Copa's avatar
Natanael Copa committed
196
	.usage = "[-R|--recursive|--stdout] [--link|-L]  [-o dir] apkname...",
197 198 199 200 201 202 203 204 205
	.context_size = sizeof(struct fetch_ctx),
	.num_options = ARRAY_SIZE(fetch_options),
	.options = fetch_options,
	.parse = fetch_parse,
	.main = fetch_main,
};

APK_DEFINE_APPLET(apk_fetch);