catalog.c revision 709f6e46a35ec492b70eb92943d82a8d838ce918
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering This file is part of systemd.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Copyright 2012 Lennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering systemd is free software; you can redistribute it and/or modify it
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering under the terms of the GNU Lesser General Public License as published by
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (at your option) any later version.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering systemd is distributed in the hope that it will be useful, but
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Lesser General Public License for more details.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering You should have received a copy of the GNU Lesser General Public License
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
1b12a7b5896f94bdf33b3a6661ebabd761ea6adcHarald Hoyerconst char * const catalog_file_dirs[] = {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' }
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sieverstypedef struct CatalogItem {
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sieversstatic void catalog_hash_func(const void *p, struct siphash *state) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering siphash24_compress(&i->id, sizeof(i->id), state);
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers siphash24_compress(i->language, strlen(i->language), state);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int catalog_compare_func(const void *a, const void *b) {
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landden const CatalogItem *i = a, *j = b;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) {
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poetteringconst struct hash_ops catalog_hash_ops = {
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landden const char *payload) {
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landden offset = strbuf_add_string(sb, payload, strlen(payload));
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers assert(strlen(language) > 1 && strlen(language) < 32);
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek i->offset = htole64((uint64_t) offset);
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.",
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering SD_ID128_FORMAT_VAL(id), language ? language : "C");
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering } else if (r < 0)
b90930c73b1c82a3dc4d4f2603799993f042aaffLennart Poetteringint catalog_file_lang(const char* filename, char **lang) {
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek end = endswith(filename, ".catalog");
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers while (beg > filename && *beg != '.' && *beg != '/' && end - beg < 32)
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sieversstatic int catalog_entry_lang(const char* filename, int line,
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers const char* t, const char* deflang, char **lang) {
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers if (c == 0) {
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers log_error("[%s:%u] Language too short.", filename, line);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error("[%s:%u] language too long.", filename, line);
adacb9575a09981fcf11279f2f661e3fc21e58ffLennart Poettering log_warning("[%s:%u] language specified unnecessarily",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_warning("[%s:%u] language differs from default for file",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringint catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering unsigned n = 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering _cleanup_free_ char *deflang = NULL, *lang = NULL;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen return log_error_errno(errno, "Failed to open file %s: %m", path);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen r = catalog_file_lang(path, &deflang);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen log_error_errno(r, "Failed to determine language for file %s: %m", path);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen log_debug("File %s has language %s.", path, deflang);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen if (!fgets(line, sizeof(line), f)) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error_errno(errno, "Failed to read file %s: %m", path);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen if (strchr(COMMENTS "\n", line[0]))
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering (line[2+1+32] == ' ' || line[2+1+32] == '\0')) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* New entry */
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen r = finish_item(h, sb, id, lang ?: deflang, payload);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen t = strstrip(line + 2 + 1 + 32 + 1);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen r = catalog_entry_lang(path, n, t, deflang, &lang);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error("[%s:%u] Got payload before ID.", path, n);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen a = payload ? strlen(payload) : 0;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen c = a + (empty_line ? 1 : 0) + b + 1 + 1;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering t[a+b] = '\n';
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = finish_item(h, sb, id, lang ?: deflang, payload);
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmekstatic int64_t write_catalog(const char *database, struct strbuf *sb,
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek CatalogItem *items, size_t n) {
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek _cleanup_fclose_ FILE *w = NULL;
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek _cleanup_free_ char *d, *p = NULL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return log_error_errno(r, "Recursive mkdir %s: %m", d);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return log_error_errno(r, "Failed to open database for writing: %s: %m",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering header.catalog_item_size = htole64(sizeof(CatalogItem));
c978343015c787713651dff571acb5207367f5f2Lennart Poettering k = fwrite(&header, 1, sizeof(header), w);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen if (k != sizeof(header)) {
c978343015c787713651dff571acb5207367f5f2Lennart Poettering log_error("%s: failed to write header.", p);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering k = fwrite(items, 1, n * sizeof(CatalogItem), w);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (k != n * sizeof(CatalogItem)) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error("%s: failed to write database.", p);
601185b43da638b1c74153deae01dbd518680889Zbigniew Jędrzejewski-Szmek k = fwrite(sb->buf, 1, sb->len, w);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error("%s: failed to write strings.", p);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error_errno(r, "%s: failed to write database: %m", p);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen if (rename(p, database) < 0) {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database);
546158bc6f46f8004cc11e81d19d223e0da56730Jan Janssenint catalog_update(const char* database, const char* root, const char* const* dirs) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering _cleanup_hashmap_free_free_ Hashmap *h = NULL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering _cleanup_free_ CatalogItem *items = NULL;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (!h || !sb) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = conf_files_list_strv(&files, ".catalog", root, dirs);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error_errno(r, "Failed to get catalog files: %m");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error_errno(r, "Failed to import file '%s': %m", *f);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("Found %u items in catalog.", hashmap_size(h));
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering items = new(CatalogItem, hashmap_size(h));
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("Found " SD_ID128_FORMAT_STR ", language %s",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering isempty(i->language) ? "C" : i->language);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sz = write_catalog(database, sb, items, n);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = log_error_errno(sz, "Failed to write %s: %m", database);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("%s: wrote %u items, with %zu bytes of strings, %"PRIi64" total size.",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (st.st_size < (off_t) sizeof(CatalogHeader)) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 ||
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering le64toh(h->header_size) < sizeof(CatalogHeader) ||
84f6181c2ac99a0514ca5e0c8fc8c8e284caf789Lennart Poettering le64toh(h->catalog_item_size) < sizeof(CatalogItem) ||
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) {
const CatalogHeader *h = p;
const char *loc;
f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
return NULL;
void *p = NULL;
r = -ENOENT;
goto finish;
if (!text) {
r = -ENOMEM;
goto finish;
return NULL;
return NULL;
if (oneline) {
void *p = NULL;
const CatalogHeader *h;
bool last_id_set = false;
last_id_set = true;
char **item;