Compare commits

..
19 Commits
Author SHA1 Message Date
Tudor Marcu 75039ad6b9 Release v3.3.2
This release includes changes across various areas of server, specifically:
- Fixing fallthrough detection logic to detect files and directories correctly
  when checking if they are "state" files.
- Removing stale and unused signature creation code
- Adding functional tests and enabling travis-ci integration
- Create alternative input layout to save IO for some cases(backward compatible)
- Update parallelism code to make it more versatile and editable
- Fix log call and add logging to stdout instead of just logfiles
- Honor proxy and cert checking settings
- Fix extracting files with bsdtar
- Enable locales in all programs

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-12-08 11:04:15 -08:00
Patrick Ohly 9b316bf95c swupd-create-update: alternative input layout
In Ostro OS, we already have a "full" directory with all files.
Splitting it up into bundles just so that swupd-create-update can
reconstruct the "full" directory is a waste of IO, and noticably slow
when run under pseudo.

To streamline the required work, a new layout for the "image" input
directory gets introduced:
- The "full" directory gets created by the caller before invoking
  swupd-create-update.
- For each bundle, instead of a <bundle> directory, there is a
  <bundle>.content.txt file, listing all entries (including directories)
  of the bundle.

The traditional mode of operation still works as before because each operation
which normally works with a bundle directory checks whether there is such a
directory and if not, switches to the new mode.

That way it is even possible to mix the two modes, i.e. replacing only
some bundles with a content list, although that's probably not all
that useful.

This revised commit fixes the use of an uninitialized newversiondircontent
pointer in populate_dirs().

Fixes: swupd-server/#54

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-08 10:53:45 -08:00
Patrick Ohly b618516167 enable locales in all programs
This is a pre-condition for using libarchive directly: libarchive
needs to know what the encoding of filenames is, and it uses the
current locale for that. Without setlocale(), the locale is "C", which
only supports ASCII filenames, leading to warnings about "Can't
encode..." from libarchive when it is forced to fall back to copying
strings verbatim when writing archives that require UTF-8 encoding.

As a side effect, error messages from libc will get translated
according to the user's environment.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-08 10:57:36 -08:00
Patrick Ohly 7542d2152a swupd_make_pack: fix extracting files with bsdtar
TAR_XATTR_ARGS is no longer used as part of a plain string. Embedding
the empty "" value for bsdtar inside an argv argument list passes an
empty parameter to bsdtar, leading to:
  bsdtar: Must specify one of -c, -r, -t, -u, -x

To allow the the "no parameter" case, it has to be argument list: that
can be empty. If not empty, it has to end with a comma.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-08 10:49:02 -08:00
Patrick McCarty 490326f2b9 packfsck: honor proxy settings and cert checking
In case a proxy is used for downloading the manifest/pack, make sure to
honor those settings. And also, make sure certificate verification
occurs for the downloads.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 11:59:11 -08:00
Tudor Marcu 307d2427a1 Revert "swupd-create-update: alternative input layout"
This reverts commit f01d9ca6c8.

Upon further testing, this patch causes a double free/corruption with the
current master branch and crashes two of the tests. We need to investigate
more before fully enabling it to ensure we don't regress.
2016-12-01 11:43:57 -08:00
Patrick Ohly 72dd27a886 add logging to stdout
When a CI system (like the one from Ostro) captures the output of
commands, but not necessarily intermediate log files, then it is
useful to also log to stdout. Another use case is calling the tools
interactively during development.

The new --log-stdout option in all three commands enables logging to
stdout in addition to the traditional log files.

The implementation recycles the existing init_log_stdout() (not used
before) and gives it the slightly different meaning of "also log to
stdout".

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-01 11:20:40 -08:00
Patrick Ohly 7e38f013ef fullfiles.c: fix invalid LOG() call
LOG() takes an additional fixed string before the format string.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-01 11:20:05 -08:00
Patrick Ohly 4e0fdd4193 update control over parallelism
The SWUPD_NUM_THREADS env variable is now understood by all three
commands and overrides the default number of threads. Setting it to 1
is useful while debugging the code that runs inside threads (only one
thread hits breakpoints there). If SWUPD_NUM_THREADS is invalid, a
warning is printed and the variable gets ignored, i.e. the default
parallelism is used.

The hard-coded parallelism of 12 threads when analysing the file system
gets replaced with n, where n is the number of available CPUs. The default
is the same as before elsewhere (n for packing, 3 * n for fullfiles).

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-01 11:19:35 -08:00
Patrick Ohly f01d9ca6c8 swupd-create-update: alternative input layout
In Ostro OS, we already have a "full" directory with all files.
Splitting it up into bundles just so that swupd-create-update can
reconstruct the "full" directory is a waste of IO, and noticably slow
when run under pseudo.

To streamline the required work, a new layout for the "image" input
directory gets introduced:
- The "full" directory gets created by the caller before invoking
  swupd-create-update.
- For each bundle, instead of a <bundle> directory, there is a
  <bundle>.content.txt file, listing all entries (including directories)
  of the bundle.

The traditional mode of operation still works as before because each operation
which normally works with a bundle directory checks whether there is such a
directory and if not, switches to the new mode.

That way it is even possible to mix the two modes, i.e. replacing only
some bundles with a content list, although that's probably not all
that useful.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-01 11:16:21 -08:00
Patrick McCarty 944dfa1d93 Add travis-ci integration for functional testing
Right now, the travis-ci config simply installs required build
dependencies for swupd-server and works around the umask discrepancy to
proper run the functional test suite.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 11:15:46 -08:00
Patrick McCarty 963e8117b4 Skip some tests if run as root
Three functional tests depend on the effective UID being non-zero
(non-root), so skip the tests if running as root.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 11:15:46 -08:00
Patrick McCarty 57292a5a03 Add functional test for state file tagging
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 11:05:36 -08:00
Patrick McCarty fe6f47e4c3 Fix fallthrough state detection logic
This conditional checks for state *directories* that are generally
installed by default, and the conditional immediately below this one
checks for state files within these directories. So, if we do strncmp()
instead of strcmp(), the fallthrough logic doesn't occur, and state
files are not marked as such.

This reverts commit 63fb5fb61b.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 11:05:36 -08:00
Patrick McCarty 13189dd4d4 Remove all signature creation code
Since the enablement of signature verification in swupd-client, the
signature creation step has been decoupled from swupd-server, and is
instead performed as a separate step in a DevOps flow.  As a result of
this decoupling, the signature code in swupd-server has remained unused.

This commit removes all the signature creation code with the assumption
that the separate DevOps step is going to work better long-term. Also,
the existing signature creation support does not accord with
swupd-client's verification support.

An example of how Manifest.MoM files can be signed is found in the
https://github.com/clearlinux/mixer-tools repo.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 10:15:42 -08:00
Tudor Marcu b417fc4391 Release v3.3.1
This release fixes some git tagging errors that occured in the previous release.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-11-17 11:09:56 -08:00
Tudor Marcu 478cdc5272 Merge tag 'v3.3.0'
swupd-server release 3.3.0
2016-11-17 11:07:58 -08:00
Tudor Marcu 2992dc1978 Release v3.3.0
This release contains various changes to fix segfaults and memory misuse,
simplifying logic when iterating directories and populating file structs
from manifests, and changing the secondary sort to be lexographically sorted
filenames for version-sorted manifests.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-11-17 01:21:31 -08:00
Patrick McCarty 9148ea8a8e Switch secondary sort order for version-sorted manifests
To ease human readability of manifests, but without impacting manifest
delta efficiency, use a lexicographic filename secondary sort order when
sorting manifests by version.

Below is an example of how this commit changes the sorted order (the
first column is the version, and the second column is the filename).

 # Before
 10	zyxw
 10	abcd
 20	test2
 20	test1
 20	abba
 20	aaaa
 30	zzyy

 # After
 10	abcd
 10	zyxw
 20	aaaa
 20	abba
 20	test1
 20	test2
 30	zzyy

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-11-16 22:28:26 -08:00
23 changed files with 320 additions and 363 deletions
+25
View File
@@ -0,0 +1,25 @@
sudo: required
dist: trusty
language: c
# Pre-install missing build dependencies:
# - libmagic (pull from repo)
# - libcheck 0.9.10 is slightly too old, since 0.9.12 adds TAP support
# - bsdiff 1.* is the Clear Linux OS fork
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y libmagic-dev
install:
- wget http://downloads.sourceforge.net/project/check/check/0.10.0/check-0.10.0.tar.gz
- tar -xvf check-0.10.0.tar.gz
- pushd check-0.10.0 && ./configure --prefix=/usr && make -j48 && sudo make install && popd
- wget https://github.com/clearlinux/bsdiff/releases/download/v1.0.2/bsdiff-1.0.2.tar.xz
- tar -xvf bsdiff-1.0.2.tar.xz
- pushd bsdiff-1.0.2 && ./configure --prefix=/usr --disable-tests && make -j48 && sudo make install && popd
# Ubuntu's default umask is 0002, but this break's swupd hash calculations.
script:
- sudo find test/functional -exec chmod g-w {} \;
- autoreconf --verbose --warnings=none --install --force && ./configure && make -j48 && sudo sh -c 'umask 0022 && make -j48 check'
after_failure: cat test-suite.log
+2 -4
View File
@@ -26,7 +26,6 @@ swupd_create_update_SOURCES = \
src/manifest.c \
src/pack.c \
src/rename.c \
src/signature.c \
src/stats.c \
src/type_change.c \
src/versions.c \
@@ -44,7 +43,6 @@ swupd_make_pack_SOURCES = \
src/manifest.c \
src/pack.c \
src/rename.c \
src/signature.c \
src/stats.c \
src/xattrs.c
@@ -61,7 +59,6 @@ swupd_make_fullfiles_SOURCES = \
src/manifest.c \
src/pack.c \
src/rename.c \
src/signature.c \
src/stats.c \
src/xattrs.c
@@ -118,7 +115,8 @@ dist_check_SCRIPTS = \
test/functional/pack/test.bats \
test/functional/full-run/test.bats \
test/functional/full-run-delta/test.bats \
test/functional/file-name-blacklisted/test.bats
test/functional/file-name-blacklisted/test.bats \
test/functional/state-file/test.bats
endif
if COVERAGE
+1 -1
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.66])
AC_INIT(swupd-server, 3.3.0, timothy.c.pepper@linux.intel.com)
AC_INIT(swupd-server, 3.3.2, tudor.marcu@intel.com)
AM_INIT_AUTOMAKE([foreign -Wall -W subdir-objects])
AM_SILENT_RULES([yes])
AC_PROG_CC
+3 -7
View File
@@ -20,12 +20,12 @@
#define TAR_COMMAND "bsdtar"
#define TAR_XATTR_ARGS ""
#define TAR_XATTR_ARGS_STRLIST
#define TAR_WARN_ARGS ""
#define TAR_WARN_ARGS_STRLIST
#else
#define TAR_COMMAND "tar"
#define TAR_XATTR_ARGS "--xattrs --xattrs-include='*'"
#define TAR_XATTR_ARGS_STRLIST "--xattrs", "--xattrs-include='*'",
#define TAR_WARN_ARGS "--warning=no-timestamp"
#define TAR_WARN_ARGS_STRLIST "--warning=no-timestamp",
#endif
#if SWUPD_WITH_SELINUX
@@ -143,7 +143,6 @@ extern int current_version;
extern int newversion;
extern int minversion;
extern unsigned long long int format;
extern bool enable_signing;
extern char *state_dir;
extern char *packstage_dir;
@@ -258,9 +257,6 @@ extern int system_argv(char *const argv[]);
extern int system_argv_fd(char *const argv[], int newstdin, int newstdout, int newstderr);
extern int system_argv_pipe(char *const argvp1[], int stdinp1, int stderrp1,
char *const argvp2[], int stdoutp2, int stderrp2);
extern bool signature_initialize(void);
extern void signature_terminate(void);
extern bool signature_sign(const char *filename);
extern int num_threads(float scaling);
#endif
+2 -2
View File
@@ -7,7 +7,7 @@
my $target = $ARGV[0];
system("rm /tmp/Manifest");
system("wget --quiet --no-proxy --no-check-certificate --output-document=/tmp/Manifest https://download.clearlinux.org/update/$target/Manifest.os-core");
system("wget --quiet --output-document=/tmp/Manifest https://download.clearlinux.org/update/$target/Manifest.os-core");
my $from = $target;
@@ -15,7 +15,7 @@ while ($from > $target - 100) {
$from = $from - 10;
print "Testing the $from-$target pack\n";
system("rm /tmp/pack.tar");
system("wget --quiet --no-proxy --no-check-certificate --output-document=/tmp/pack.tar https://download.clearlinux.org/update/$target/pack-os-core-from-$from.tar");
system("wget --quiet ---output-document=/tmp/pack.tar https://download.clearlinux.org/update/$target/pack-os-core-from-$from.tar");
+134 -45
View File
@@ -275,7 +275,7 @@ static void get_hash(gpointer data, gpointer user_data)
/* disallow characters which can do unexpected things when the filename is
* used on a tar command line via system("tar [args] filename [more args]");
*/
static bool illegal_characters(char *filename)
static bool illegal_characters(const char *filename)
{
char c;
int i;
@@ -301,27 +301,151 @@ static bool illegal_characters(char *filename)
return false;
}
static struct file *add_file(struct manifest *manifest,
const char *entry_name,
char *sub_filename,
char *fullname,
bool do_hash)
{
GError *err = NULL;
struct file *file;
if (illegal_characters(entry_name)) {
printf("WARNING: Filename %s includes illegal character(s) ...skipping.\n", sub_filename);
free(sub_filename);
free(fullname);
return NULL;
}
file = calloc(1, sizeof(struct file));
assert(file);
file->last_change = manifest->version;
file->filename = sub_filename;
populate_file_struct(file, fullname);
if (file->is_deleted) {
/*
* populate_file_struct() logs a stat() failure, but
* does not abort. When adding files that should
* exist, this case is an error.
*/
LOG(NULL, "file not found", "%s", fullname);
assert(0);
}
/* if for some reason there is a file in the official build
* which should not be included in the Manifest, then open a bug
* to get it removed, and work around its presence by
* excluding it here, eg:
if (strncmp(file->filename, "/dev/", 5) == 0) {
continue;
}
*/
if (do_hash) {
/* compute the hash from a thread */
int ret;
ret = g_thread_pool_push(threadpool, file, &err);
if (ret == FALSE) {
printf("GThread hash computation push error\n");
printf("%s\n", err->message);
assert(0);
}
}
manifest->files = g_list_prepend(manifest->files, file);
manifest->count++;
return file;
}
static void iterate_directory(struct manifest *manifest, char *pathprefix,
char *subpath, bool do_hash)
{
DIR *dir;
struct dirent *entry;
char *fullpath;
int ret;
GError *err = NULL;
string_or_die(&fullpath, "%s/%s", pathprefix, subpath);
dir = opendir(fullpath);
if (!dir) {
bool fatal_error = errno != ENOENT;
FILE *content;
free(fullpath);
if (fatal_error) {
return;
}
/*
* If there is a <dir>.content.txt instead of
* the actual directory, then read that
* file. It has a list of path names,
* including all directories. The
* corresponding file system entry is then
* expected to be in a pre-populated "full"
* directory.
*
* Only supported at top level (i.e. empty
* subpath) to keep the code and testing
* simpler.
*/
assert(!subpath[0]);
string_or_die(&fullpath, "%s.content.txt", pathprefix);
content = fopen(fullpath, "r");
free(fullpath);
fullpath = NULL;
if (content) {
char *line = NULL;
size_t len = 0;
ssize_t read;
const char *full;
int full_len;
/*
* determine path to "full" directory: it is assumed to be alongside
* "pathprefix", i.e. pathprefix/../full. But pathprefix does not exit,
* so we have to strip the last path component.
*/
full = strrchr(pathprefix, '/');
if (full) {
full_len = full - pathprefix + 1;
full = pathprefix;
} else {
full = "";
full_len = 0;
}
while ((read = getline(&line, &len, content)) != -1) {
if (read) {
const char *entry_name = strrchr(line, '/');
if (entry_name) {
entry_name++;
} else {
entry_name = line;
}
if (line[read - 1] == '\n') {
line[read - 1] = 0;
}
string_or_die(&fullpath, "%.*sfull/%s", full_len, full, line);
add_file(manifest,
entry_name,
strdup(line),
fullpath,
do_hash);
}
}
free(line);
}
// If both directory and content file are missing, silently (?)
// don't add anything to the manifest.
return;
}
while (dir) {
struct file *file;
char *sub_filename;
char *fullname;
struct file *file;
entry = readdir(dir);
if (!entry) {
@@ -334,50 +458,14 @@ static void iterate_directory(struct manifest *manifest, char *pathprefix,
}
string_or_die(&sub_filename, "%s/%s", subpath, entry->d_name);
if (illegal_characters(entry->d_name)) {
printf("WARNING: Filename %s includes illegal character(s) ...skipping.\n", sub_filename);
free(sub_filename);
continue;
}
file = calloc(1, sizeof(struct file));
if (!file) {
break;
}
file->last_change = manifest->version;
file->filename = sub_filename;
string_or_die(&fullname, "%s/%s", fullpath, entry->d_name);
populate_file_struct(file, fullname);
free(fullname);
if (file->is_dir) {
iterate_directory(manifest, pathprefix, file->filename, do_hash);
}
/* takes ownership of the strings, so we don't need to free it */
file = add_file(manifest, entry->d_name, sub_filename, fullname, do_hash);
/* if for some reason there is a file in the official build
* which should not be included in the Manifest, then open a bug
* to get it removed, and work around its presence by
* excluding it here, eg:
if (strncmp(file->filename, "/dev/", 5) == 0) {
continue;
if (file && file->is_dir) {
iterate_directory(manifest, pathprefix, file->filename, do_hash);
}
*/
if (do_hash) {
/* compute the hash from a thread */
ret = g_thread_pool_push(threadpool, file, &err);
if (ret == FALSE) {
printf("GThread hash computation push error\n");
printf("%s\n", err->message);
closedir(dir);
return;
}
}
manifest->files = g_list_prepend(manifest->files, file);
manifest->count++;
}
closedir(dir);
free(fullpath);
@@ -387,6 +475,7 @@ struct manifest *full_manifest_from_directory(int version)
{
struct manifest *manifest;
char *dir;
int numthreads = num_threads(1.0);
LOG(NULL, "Computing hashes", "for %i/full", version);
@@ -394,7 +483,7 @@ struct manifest *full_manifest_from_directory(int version)
string_or_die(&dir, "%s/%i/full", image_dir, version);
threadpool = g_thread_pool_new(get_hash, dir, 12, FALSE, NULL);
threadpool = g_thread_pool_new(get_hash, dir, numthreads, FALSE, NULL);
iterate_directory(manifest, dir, "", true);
+16 -8
View File
@@ -39,15 +39,21 @@ void chroot_create_full(int newversion)
char *full_dir;
string_or_die(&full_dir, "%s/%i/full/", image_dir, newversion);
if (!access(full_dir, R_OK|X_OK)) {
free(full_dir);
return;
}
g_mkdir_with_parents(full_dir, S_IRWXU);
/* start with base */
LOG(NULL, "Copying chroot os-core to full", "");
string_or_die(&param, "%s/%i/os-core/", image_dir, newversion);
char *const rsynccmd[] = { "rsync", "-aAX", param, full_dir, NULL };
if (system_argv(rsynccmd) != 0) {
assert(0);
if (!access(param, F_OK)) {
LOG(NULL, "Copying chroot os-core to full", "");
char *const rsynccmd[] = { "rsync", "-aAX", param, full_dir, NULL };
if (system_argv(rsynccmd) != 0) {
assert(0);
}
}
free(param);
@@ -58,11 +64,13 @@ void chroot_create_full(int newversion)
break;
}
LOG(NULL, "Overlaying bundle chroot onto full", "%s", group);
string_or_die(&param, "%s/%i/%s/", image_dir, newversion, group);
char *const rsynccmd[] = { "rsync", "-aAX", "--ignore-existing", param, full_dir, NULL };
if (system_argv(rsynccmd) != 0) {
assert(0);
if (!access(param, F_OK)) {
LOG(NULL, "Overlaying bundle chroot onto full", "%s", group);
char *const rsynccmd[] = { "rsync", "-aAX", "--ignore-existing", param, full_dir, NULL };
if (system_argv(rsynccmd) != 0) {
assert(0);
}
}
free(param);
}
+17 -14
View File
@@ -29,6 +29,7 @@
#include <errno.h>
#include <getopt.h>
#include <glib.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -49,12 +50,12 @@ static void banner(void)
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "version", no_argument, 0, 'v' },
{ "log-stdout", no_argument, 0, 'l' },
{ "osversion", required_argument, 0, 'o' },
{ "minversion", required_argument, 0, 'm' },
{ "format", required_argument, 0, 'F' },
{ "getformat", no_argument, 0, 'g' },
{ "statedir", required_argument, 0, 'S' },
{ "signcontent", no_argument, 0, 's' },
{ 0, 0, 0, 0 }
};
@@ -67,12 +68,12 @@ static void print_help(const char *name)
printf(" -v, --version Show software version\n");
printf("\n");
printf("Application Options:\n");
printf(" -l, --log-stdout Write log messages also to stdout\n");
printf(" -o, --osversion The OS version for which to create an update\n");
printf(" -m, --minversion Optional minimum file version to write into manifests per file\n");
printf(" -F, --format Format number for the update\n");
printf(" -g, --getformat Print current format string and exit\n");
printf(" -S, --statedir Optional directory to use for state [ default:=%s ]\n", SWUPD_SERVER_STATE_DIR);
printf(" -s, --signcontent Enables cryptographic signing of update content\n");
printf("\n");
}
@@ -80,12 +81,15 @@ static bool parse_options(int argc, char **argv)
{
int opt;
while ((opt = getopt_long(argc, argv, "hvo:m:F:g:S:s", prog_opts, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "hvo:m:F:g:S:", prog_opts, NULL)) != -1) {
switch (opt) {
case '?':
case 'h':
print_help(argv[0]);
return false;
case 'l':
init_log_stdout();
break;
case 'v':
banner();
return false;
@@ -124,9 +128,6 @@ static bool parse_options(int argc, char **argv)
free_globals();
}
exit(0);
case 's':
enable_signing = true;
break;
}
}
@@ -140,6 +141,7 @@ static bool parse_options(int argc, char **argv)
static void populate_dirs(int version)
{
char *newversiondir;
char *newversiondircontent = NULL;
string_or_die(&newversiondir, "%s/%d", image_dir, version);
@@ -181,9 +183,11 @@ static void populate_dirs(int version)
}
string_or_die(&newversiondir, "%s/%d/%s", image_dir, version, group);
string_or_die(&newversiondircontent, "%s/%d/%s.content.txt", image_dir, version, group);
/* Create the bundle directory(s) as needed */
if (access(newversiondir, F_OK | R_OK) != 0) {
if (access(newversiondir, F_OK | R_OK) != 0 &&
access(newversiondircontent, F_OK | R_OK) != 0) {
printf("%s does not exist...creating\n", group);
if (mkdir(newversiondir, 0755) != 0) {
printf("Failed to create %s subdirectory\n", group);
@@ -192,6 +196,7 @@ static void populate_dirs(int version)
}
}
free(newversiondir);
free(newversiondircontent);
}
static int check_build_env(void)
@@ -259,6 +264,11 @@ int main(int argc, char **argv)
/* keep valgrind working well */
setenv("G_SLICE", "always-malloc", 0);
if (!setlocale(LC_ALL, "")) {
fprintf(stderr, "%s: setlocale() failed\n", argv[0]);
return EXIT_FAILURE;
}
if (!parse_options(argc, argv)) {
free_globals();
return EXIT_FAILURE;
@@ -272,12 +282,6 @@ int main(int argc, char **argv)
goto exit;
}
/* Initilize the crypto signature module */
if (!signature_initialize()) {
printf("Can't initialize the crypto signature module!\n");
goto exit;
}
string_or_die(&file_path, "%s/server.ini", state_dir);
if (!read_configuration_file(file_path)) {
printf("Failed to read %s configuration file!\n", state_dir);
@@ -530,7 +534,6 @@ exit:
}
release_configuration_data();
release_group_file();
signature_terminate();
g_list_free(manifests_last_versions_list);
close_log(newversion, exit_status);
+4 -3
View File
@@ -72,7 +72,7 @@ static void create_fullfile(struct file *file)
string_or_die(&origin, "%s/%i/full/%s", indir, file->last_change, file->filename);
if (lstat(origin, &sbuf) < 0) {
/* no input file: means earlier phase of update creation failed */
LOG(NULL, "Failed to stat %s\n", origin);
LOG(NULL, "Failed to stat", "%s: %s", origin, strerror(errno));
assert(0);
}
@@ -291,10 +291,11 @@ static void submit_fullfile_tasks(GList *files)
int ret;
int count = 0;
GError *err = NULL;
int numthreads = num_threads(3.0);
LOG(NULL, "fullfile threadpool", "%d threads", sysconf(_SC_NPROCESSORS_ONLN) * 3);
LOG(NULL, "fullfile threadpool", "%d threads", numthreads);
threadpool = g_thread_pool_new(create_fullfile_task, NULL,
sysconf(_SC_NPROCESSORS_ONLN) * 3,
numthreads,
TRUE, NULL);
printf("Starting downloadable fullfiles data creation\n");
-1
View File
@@ -34,7 +34,6 @@
int newversion = -1;
int minversion = 0;
unsigned long long int format = 0;
bool enable_signing = false;
char *state_dir = NULL;
char *packstage_dir = NULL;
+25
View File
@@ -284,3 +284,28 @@ void check_root(void)
exit(EXIT_FAILURE);
}
}
int num_threads(float scaling)
{
const char *var = getenv("SWUPD_NUM_THREADS");
int result = sysconf(_SC_NPROCESSORS_ONLN) * scaling;
if (var && *var) {
char *endptr;
long int value;
errno = 0;
value = strtol(var, &endptr, 0);
if ((errno != 0 && value == 0) || *endptr) {
LOG(NULL, "SWUPD_NUM_THREADS must be an integer", "%s", var);
} else if ((errno == ERANGE && (value == LONG_MAX || value == LONG_MIN)) ||
value < 1 || value > INT_MAX) {
LOG(NULL, "SWUPD_NUM_THREADS out of range", "%s", var);
} else {
result = (int)value;
}
}
return result;
}
+9 -9
View File
@@ -44,15 +44,15 @@ static void runtime_state_heuristics(struct file *file)
{
/* these are shipped directories that are not themselves state,
* rather only their contents are state */
if ((strncmp(file->filename, "/usr/src/debug", 14) == 0) ||
(strncmp(file->filename, "/dev", 4) == 0) ||
(strncmp(file->filename, "/home", 5) == 0) ||
(strncmp(file->filename, "/proc", 5) == 0) ||
(strncmp(file->filename, "/root", 5) == 0) ||
(strncmp(file->filename, "/run", 4) == 0) ||
(strncmp(file->filename, "/sys", 4) == 0) ||
(strncmp(file->filename, "/tmp", 4) == 0) ||
(strncmp(file->filename, "/var", 4) == 0)) {
if ((strcmp(file->filename, "/usr/src/debug") == 0) ||
(strcmp(file->filename, "/dev") == 0) ||
(strcmp(file->filename, "/home") == 0) ||
(strcmp(file->filename, "/proc") == 0) ||
(strcmp(file->filename, "/root") == 0) ||
(strcmp(file->filename, "/run") == 0) ||
(strcmp(file->filename, "/sys") == 0) ||
(strcmp(file->filename, "/tmp") == 0) ||
(strcmp(file->filename, "/var") == 0)) {
return;
}
+17 -10
View File
@@ -33,7 +33,7 @@
#include "swupd.h"
static FILE *logfile;
static FILE *logfile[2];
static struct timeval start_time;
@@ -41,13 +41,13 @@ void init_log(const char *prefix, const char *bundle, int start, int end)
{
char *filename;
string_or_die(&filename, "%s%s-from-%i-to-%i.log", prefix, bundle, start, end);
logfile = fopen(filename, "w");
logfile[0] = fopen(filename, "w");
free(filename);
gettimeofday(&start_time, NULL);
}
void init_log_stdout(void)
{
logfile = stdout;
logfile[1] = stdout;
gettimeofday(&start_time, NULL);
}
@@ -91,8 +91,9 @@ void __log_message(struct file *file, char *msg, char *filename, int linenr, con
char *logstring = NULL;
char filebuf[4096];
char filebuf2[4096];
int i;
if (!logfile) {
if (!logfile[0] && !logfile[1]) {
return;
}
@@ -119,12 +120,16 @@ void __log_message(struct file *file, char *msg, char *filename, int linenr, con
strcat(filebuf2, " ");
}
fprintf(logfile, "%3i.%03i %5s %s:%03i\t| %s\t| %s\t| %s\n",
(int)current_time.tv_sec, (int)current_time.tv_usec / 1000, logstring, filebuf, linenr, filebuf2, msg, buf);
for (i = 0; i < 2; i++) {
if (logfile[i]) {
fprintf(logfile[i], "%3i.%03i %5s %s:%03i\t| %s\t| %s\t| %s\n",
(int)current_time.tv_sec, (int)current_time.tv_usec / 1000, logstring, filebuf, linenr, filebuf2, msg, buf);
fflush(logfile[i]);
}
}
free(logstring);
free(buf);
fflush(logfile);
}
void close_log(int version, int exit_status)
@@ -133,7 +138,7 @@ void close_log(int version, int exit_status)
int t_sec;
int t_msec;
if (!logfile) {
if (!logfile[0] && !logfile[1]) {
return;
}
@@ -159,6 +164,8 @@ void close_log(int version, int exit_status)
printf("Update build failed for version %i\n", version);
}
fclose(logfile);
logfile = NULL;
if (logfile[0]) {
fclose(logfile[0]);
logfile[0] = NULL;
}
}
+11
View File
@@ -23,6 +23,7 @@
#define _GNU_SOURCE
#include <assert.h>
#include <getopt.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -32,6 +33,7 @@
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "log-stdout", no_argument, 0, 'l' },
{ "statedir", required_argument, 0, 'S' },
{ 0, 0, 0, 0 }
};
@@ -42,6 +44,7 @@ static void usage(const char *name)
printf(" %s <version>\n\n", name);
printf("Help options:\n");
printf(" -h, --help Show help options\n");
printf(" -l, --log-stdout Write log messages also to stdout\n");
printf(" -S, --statedir Optional directory to use for state [ default:=%s ]\n", SWUPD_SERVER_STATE_DIR);
printf("\n");
}
@@ -56,6 +59,9 @@ static bool parse_options(int argc, char **argv)
case 'h':
usage(argv[0]);
return false;
case 'l':
init_log_stdout();
break;
case 'S':
if (!optarg || !set_state_dir(optarg)) {
printf("Invalid --statedir argument '%s'\n\n", optarg);
@@ -88,6 +94,11 @@ int main(int argc, char **argv)
/* keep valgrind working well */
setenv("G_SLICE", "always-malloc", 0);
if (!setlocale(LC_ALL, "")) {
fprintf(stderr, "%s: setlocale() failed\n", argv[0]);
return EXIT_FAILURE;
}
if (!parse_options(argc, argv)) {
free_state_globals();
return EXIT_FAILURE;
+11 -13
View File
@@ -27,6 +27,7 @@
#include <getopt.h>
#include <getopt.h>
#include <glib.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -45,8 +46,8 @@ static void banner(void)
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "log-stdout", no_argument, 0, 'l' },
{ "statedir", required_argument, 0, 'S' },
{ "signcontent", no_argument, 0, 's' },
{ 0, 0, 0, 0 }
};
@@ -56,8 +57,8 @@ static void usage(const char *name)
printf(" %s <start version> <latest version> <bundle>\n\n", name);
printf("Help options:\n");
printf(" -h, --help Show help options\n");
printf(" -l, --log-stdout Write log messages also to stdout\n");
printf(" -S, --statedir Optional directory to use for state [ default:=%s ]\n", SWUPD_SERVER_STATE_DIR);
printf(" -s, --signcontent Enables cryptographic signing of update content\n");
printf("\n");
}
@@ -71,15 +72,15 @@ static bool parse_options(int argc, char **argv)
case 'h':
usage(argv[0]);
return false;
case 'l':
init_log_stdout();
break;
case 'S':
if (!optarg || !set_state_dir(optarg)) {
printf("Invalid --statedir argument ''%s'\n\n", optarg);
return false;
}
break;
case 's':
enable_signing = true;
break;
}
}
@@ -101,6 +102,11 @@ int main(int argc, char **argv)
int exit_status = EXIT_FAILURE;
char *file_path = NULL;
if (!setlocale(LC_ALL, "")) {
fprintf(stderr, "%s: setlocale() failed\n", argv[0]);
return EXIT_FAILURE;
}
if (!parse_options(argc, argv)) {
free_state_globals();
return EXIT_FAILURE;
@@ -114,12 +120,6 @@ int main(int argc, char **argv)
banner();
check_root();
/* Initilize the crypto signature module */
if (!signature_initialize()) {
printf("Can't initialize the crypto signature module!\n");
return exit_status;
}
string_or_die(&file_path, "%s/server.ini", state_dir);
read_configuration_file(file_path);
free(file_path);
@@ -152,8 +152,6 @@ int main(int argc, char **argv)
exit_status = EXIT_SUCCESS;
}
signature_terminate();
printf("Pack creation %s (pack-%s %i to %li)\n",
exit_status == EXIT_SUCCESS ? "complete" : "failed",
module, start_version, end_version);
+6 -43
View File
@@ -64,7 +64,7 @@ int file_sort_version(gconstpointer a, gconstpointer b)
return 1;
}
return strcmp(B->filename, A->filename);
return strcmp(A->filename, B->filename);
}
int file_sort_filename(gconstpointer a, gconstpointer b)
@@ -695,29 +695,6 @@ static void compute_content_size(struct manifest *manifest)
}
}
/* Returns 0 == success, -1 == failure */
static int write_manifest_signature(struct manifest *manifest, const char *suffix)
{
char *conf = config_output_dir();
char *filename = NULL;
int ret = -1;
if (conf == NULL) {
assert(0);
}
string_or_die(&filename, "%s/%i/Manifest.%s%s", conf, manifest->version,
manifest->component, suffix);
if (!signature_sign(filename)) {
fprintf(stderr, "Creating signature for '%s' failed\n", filename);
goto exit;
}
ret = 0;
exit:
free(filename);
free(conf);
return ret;
}
/* Returns 0 == success, -1 == failure */
static int write_manifest_plain(struct manifest *manifest)
{
@@ -851,7 +828,7 @@ exit:
static int write_manifest_tar(struct manifest *manifest)
{
char *conf = config_output_dir();
char *directory, *manifesttar, *manifestcomp, *manifestsigned;
char *directory, *manifesttar, *manifestcomp;
int ret = 0;
if (conf == NULL) {
@@ -861,19 +838,11 @@ static int write_manifest_tar(struct manifest *manifest)
string_or_die(&directory, "--directory=%s/%i", conf, manifest->version);
string_or_die(&manifesttar, "%s/%i/Manifest.%s.tar", conf, manifest->version, manifest->component);
string_or_die(&manifestcomp, "Manifest.%s", manifest->component);
string_or_die(&manifestsigned, "Manifest.%s.signed", manifest->component);
/* now, tar the thing up for efficient full file download */
/* and put the signature of the plain manifest into the archive, too */
if (enable_signing) {
char *const tarcmd[] = { TAR_COMMAND, directory, TAR_PERM_ATTR_ARGS_STRLIST, "-Jcf",
manifesttar, manifestcomp, manifestsigned, NULL };
ret = system_argv(tarcmd);
} else {
char *const tarcmd[] = { TAR_COMMAND, directory, TAR_PERM_ATTR_ARGS_STRLIST, "-Jcf",
manifesttar, manifestcomp, NULL };
ret = system_argv(tarcmd);
}
char *const tarcmd[] = { TAR_COMMAND, directory, TAR_PERM_ATTR_ARGS_STRLIST, "-Jcf",
manifesttar, manifestcomp, NULL };
ret = system_argv(tarcmd);
if (ret) {
fprintf(stderr, "Creation of Manifest.tar failed\n");
}
@@ -881,7 +850,6 @@ static int write_manifest_tar(struct manifest *manifest)
free(directory);
free(manifesttar);
free(manifestcomp);
free(manifestsigned);
free(conf);
return ret;
}
@@ -911,9 +879,7 @@ bool compute_hash_with_xattrs(const char *filename)
int write_manifest(struct manifest *manifest)
{
if (write_manifest_plain(manifest) == 0 &&
write_manifest_signature(manifest, "") == 0 &&
write_manifest_tar(manifest) == 0 &&
write_manifest_signature(manifest, ".tar") == 0) {
write_manifest_tar(manifest) == 0) {
return 0;
}
return -1;
@@ -1115,9 +1081,6 @@ void create_manifest_delta(int oldversion, int newversion, char *module)
}
LOG(NULL, "Failed to rename", "");
}
if (!signature_sign(outfile)) {
fprintf(stderr, "Creating signature for '%s' failed\n", outfile);
}
} else {
sleep(1); /* we raced. whatever. sleep for a bit to get the other guy to make progress */
}
+4 -28
View File
@@ -116,7 +116,7 @@ static void explode_pack_stage(int from_version, int to_version, char *module)
* time on the client...
*/
string_or_die(&param, "%s/%s/%i_to_%i/staged", packstage_dir, module, from_version, to_version);
char *const tarcmd[] = { TAR_COMMAND, "-C", param, TAR_WARN_ARGS, TAR_PERM_ATTR_ARGS_STRLIST, "-xf", path, NULL };
char *const tarcmd[] = { TAR_COMMAND, "-C", param, TAR_WARN_ARGS_STRLIST TAR_PERM_ATTR_ARGS_STRLIST, "-xf", path, NULL };
if (system_argv(tarcmd) == 0) {
unlink(path);
}
@@ -285,10 +285,11 @@ static void make_pack_deltas(GList *files)
struct file *file;
int ret;
GError *err = NULL;
int numthreads = num_threads(1.0);
LOG(NULL, "pack deltas threadpool", "%d threads", sysconf(_SC_NPROCESSORS_ONLN));
LOG(NULL, "pack deltas threadpool", "%d threads", numthreads);
threadpool = g_thread_pool_new(create_delta, NULL,
sysconf(_SC_NPROCESSORS_ONLN), FALSE, NULL);
numthreads, FALSE, NULL);
item = g_list_first(files);
while (item) {
@@ -307,24 +308,6 @@ static void make_pack_deltas(GList *files)
g_thread_pool_free(threadpool, FALSE, TRUE);
}
/* Returns 0 == success, -1 == failure */
static int write_pack_signature(struct packdata *pack)
{
char *filename = NULL;
int ret = -1;
string_or_die(&filename, "%s/%i/pack-%s-from-%i.tar",
staging_dir, pack->to, pack->module, pack->from);
if (!signature_sign(filename)) {
fprintf(stderr, "Creating signature for '%s' failed\n", filename);
goto exit;
}
ret = 0;
exit:
free(filename);
return ret;
}
/* Returns 0 == success, other == failure */
static int make_final_pack(struct packdata *pack)
{
@@ -507,13 +490,6 @@ static int make_final_pack(struct packdata *pack)
if ((ret != 0) && (ret != 1)) {
fprintf(stderr, "Unexpected return value (%d) creating tar of pack %s from %i to %i\n",
ret, pack->module, pack->from, pack->to);
} else {
/* Write the signature file */
ret = write_pack_signature(pack);
if (ret != 0) {
fprintf(stderr, "Failure creating signature of pack %s from %i to %i\n",
pack->module, pack->from, pack->to);
}
}
/* and clean up */
-173
View File
@@ -1,173 +0,0 @@
/*
* Software Updater - server side
*
* Copyright © 2012-2016 Intel Corporation.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 or later of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Tom Keel <thomas.keel@intel.com>
*
*/
#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "swupd.h"
static char *make_filename(const char *, const char *, const char *);
static char *leaf_key = NULL;
static char *leaf_cert = NULL;
static char *ca_chain_cert = NULL;
static char *passphrase = NULL;
static bool initialized = false;
/*
* Initialize this module.
* @return true <=> success
*/
bool signature_initialize(void)
{
if (!enable_signing) {
return true;
}
char *cdir;
char *pphr;
struct stat s;
if (initialized) {
return true;
}
cdir = getenv("SWUPD_CERTS_DIR");
if (cdir == NULL || cdir[0] == '\0') {
printf("No certificates directory specified\n");
goto err;
}
if (stat(cdir, &s)) {
printf("Can't stat certificates directory '%s' (%s)\n", cdir,
strerror(errno));
goto err;
}
leaf_key = make_filename(cdir, "LEAF_KEY", "leaf key");
if (leaf_key == NULL) {
goto err;
}
leaf_cert = make_filename(cdir, "LEAF_CERT", "leaf certificate");
if (leaf_cert == NULL) {
goto err;
}
ca_chain_cert = make_filename(cdir, "CA_CHAIN_CERT", "CA chain certificate");
if (ca_chain_cert == NULL) {
goto err;
}
pphr = getenv("PASSPHRASE");
if (pphr == NULL || (passphrase = strdup(pphr)) == NULL) {
goto err;
}
if (stat(passphrase, &s)) {
printf("Can't stat '%s' (%s)\n", passphrase,
strerror(errno));
goto err;
}
initialized = true;
return true;
err:
signature_terminate();
return false;
}
/* Make filename from dir name and env variable containing basename */
static char *make_filename(const char *dir, const char *env, const char *desc)
{
char *fn = getenv(env);
char *result = NULL;
struct stat s;
if (fn == NULL || fn[0] == '\0') {
printf("No %s file specified\n", desc);
return NULL;
}
string_or_die(&result, "%s/%s", dir, fn);
if (stat(result, &s)) {
printf("Can't stat %s '%s' (%s)\n", desc, result, strerror(errno));
free(result);
return NULL;
}
return result;
}
/*
* Terminate this module, free resources.
*/
void signature_terminate(void)
{
if (!enable_signing) {
return;
}
free(leaf_key);
free(leaf_cert);
free(ca_chain_cert);
free(passphrase);
leaf_key = NULL;
leaf_cert = NULL;
ca_chain_cert = NULL;
passphrase = NULL;
initialized = false;
}
/*
* Write the signature file corresponding to the given data file.
* The name of the signature file is the name of the data file with suffix
* ".signed" appended.
*/
bool signature_sign(const char *filename)
{
char *param1, *param2;
int status;
if (!enable_signing) {
return true;
}
if (!initialized) {
return false;
}
string_or_die(&param1, "%s.signed", filename);
string_or_die(&param2, "file:%s", passphrase);
char *const opensslcmd[] = { "openssl", "smime", "-sign", "-in", (char *)filename, "-binary",
"-out", param1, "-outform", " PEM", "-md", "sha256", "-inkey",
leaf_key, "-signer", leaf_cert, "-certfile", ca_chain_cert,
"-passin", param2, NULL };
status = system_argv(opensslcmd);
if (status != 0) {
printf("Bad status %d from signing command\n", status);
}
free(param1);
free(param2);
return status == 0;
}
+1
View File
@@ -14,6 +14,7 @@ load "../swupdlib"
}
@test "make_fullfiles root priv check" {
[ $EUID -eq 0 ] && skip "test can only be run as non-root"
run $MAKE_FULLFILES foo
[ "$status" -eq 1 ]
[[ "$output" =~ "not being run as root.. exiting" ]]
+1
View File
@@ -20,6 +20,7 @@ load "../swupdlib"
}
@test "make_pack root priv check" {
[ $EUID -eq 0 ] && skip "test can only be run as non-root"
run $MAKE_PACK foo bar foo
[ "$status" -eq 1 ]
[[ "$output" =~ "not being run as root.. exiting" ]]
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
clean_test_dir
init_test_dir
init_server_ini
set_latest_ver 0
init_groups_ini os-core
set_os_release 10 os-core
track_bundle 10 os-core
gen_file_plain 10 os-core "/var/lib/test"
}
@test "state file marked in manifest" {
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
# a file and dir installed in /var should be marked state
grep '^D\.s\..*/var/lib$' $DIR/www/10/Manifest.os-core
grep '^F\.s\..*/var/lib/test$' $DIR/www/10/Manifest.os-core
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+2 -2
View File
@@ -91,7 +91,7 @@ gen_file_plain() {
local name="$3"
# Add plain text file into a bundle
mkdir -p $DIR/image/$ver/$bundle
mkdir -p $DIR/image/$ver/$bundle/$(dirname "$name")
echo "$name" > $DIR/image/$ver/$bundle/"$name"
}
@@ -101,7 +101,7 @@ gen_file_plain_change() {
local name="$3"
# Add plain text file into a bundle
mkdir -p $DIR/image/$ver/$bundle
mkdir -p $DIR/image/$ver/$bundle/$(dirname "$name")
echo "$ver $name" > $DIR/image/$ver/$bundle/"$name"
}
+1
View File
@@ -15,6 +15,7 @@ load "../swupdlib"
}
@test "create_update root priv check" {
[ $EUID -eq 0 ] && skip "test can only be run as non-root"
run $CREATE_UPDATE -F 3 -o 10
[ "$status" -eq 1 ]
[[ "$output" =~ "not being run as root.. exiting" ]]