catalog.c revision b826ab586c9e0a9c0d438a75c28cf3a8ab485929
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer This file is part of systemd.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer Copyright 2012 Lennart Poettering
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer systemd is free software; you can redistribute it and/or modify it
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer under the terms of the GNU Lesser General Public License as published by
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt the Free Software Foundation; either version 2.1 of the License, or
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt (at your option) any later version.
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt systemd is distributed in the hope that it will be useful, but
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt WITHOUT ANY WARRANTY; without even the implied warranty of
4be4833ece2856e0cacc09f8f8b2c02b320751faMartin Pitt MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier Lesser General Public License for more details.
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier You should have received a copy of the GNU Lesser General Public License
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier along with systemd; If not, see <http://www.gnu.org/licenses/>.
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalierconst char * const catalog_file_dirs[] = {
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' }
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevaliertypedef struct CatalogHeader {
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevaliertypedef struct CatalogItem {
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalierstatic void catalog_hash_func(const void *p, struct siphash *state) {
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier siphash24_compress(&i->id, sizeof(i->id), state);
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier siphash24_compress(i->language, strlen(i->language), state);
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalierstatic int catalog_compare_func(const void *a, const void *b) {
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier const CatalogItem *i = a, *j = b;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) {
8a8332f77e61d41f3bb28b8f929ed41e0ffaf721Zbigniew Jędrzejewski-Szmek if (i->id.bytes[k] > j->id.bytes[k])
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier const char *payload) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier offset = strbuf_add_string(sb, payload, strlen(payload));
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier assert(strlen(language) > 1 && strlen(language) < 32);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.",
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier SD_ID128_FORMAT_VAL(id), language ? language : "C");
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier } else if (r < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierint catalog_file_lang(const char* filename, char **lang) {
b8667ee4162cd2510363602b417cecede9fd2ccaZbigniew Jędrzejewski-Szmek end = endswith(filename, ".catalog");
b8667ee4162cd2510363602b417cecede9fd2ccaZbigniew Jędrzejewski-Szmek while (beg > filename && *beg != '.' && *beg != '/' && end - beg < 32)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int catalog_entry_lang(const char* filename, int line,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier const char* t, const char* deflang, char **lang) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (c == 0) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_error("[%s:%u] Language too short.", filename, line);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (c > 31) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_error("[%s:%u] language too long.", filename, line);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_warning("[%s:%u] language specified unnecessarily",
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_warning("[%s:%u] language differs from default for file",
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierint catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier unsigned n = 0;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier _cleanup_free_ char *deflang = NULL, *lang = NULL;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier return log_error_errno(errno, "Failed to open file %s: %m", path);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_error_errno(errno, "Failed to determine language for file %s: %m", path);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_debug("File %s has language %s.", path, deflang);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_error_errno(errno, "Failed to read file %s: %m", path);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (line[0] == 0) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier (line[2+1+32] == ' ' || line[2+1+32] == '\0')) {
cffae62bcb6912fbaf1b7b282d9d170c9d308897Martin Pitt /* New entry */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
3486cb6cfa3d32a95c0daf02c7510fdf372507bfMartin Pitt r = finish_item(h, sb, id, lang ?: deflang, payload);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = catalog_entry_lang(path, n, t, deflang, &lang);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Payload */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier log_error("[%s:%u] Got payload before ID.", path, n);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier t[a+b+2] = 0;
53d90f9582f96208b3674da823ad1a3d2c3b1aa4Martin Pitt t[a+b] = '\n';
53d90f9582f96208b3674da823ad1a3d2c3b1aa4Martin Pitt t[a+b+1] = 0;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = finish_item(h, sb, id, lang ?: deflang, payload);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return log_error_errno(r, "Recursive mkdir %s: %m", d);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return log_error_errno(r, "Failed to open database for writing: %s: %m",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer header.catalog_item_size = htole64(sizeof(CatalogItem));
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (k != sizeof(header)) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer k = fwrite(items, 1, n * sizeof(CatalogItem), w);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (k != n * sizeof(CatalogItem)) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error_errno(r, "%s: failed to write database: %m", p);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint catalog_update(const char* database, const char* root, const char* const* dirs) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (!h || !sb) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = conf_files_list_strv(&files, ".catalog", root, dirs);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error_errno(r, "Failed to get catalog files: %m");
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error_errno(r, "Failed to import file '%s': %m", *f);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (hashmap_size(h) <= 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_debug("Found %u items in catalog.", hashmap_size(h));
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_debug("Found " SD_ID128_FORMAT_STR ", language %s",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error_errno(r, "Failed to write %s: %m", database);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.",
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return r < 0 ? r : 0;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (st.st_size < (off_t) sizeof(CatalogHeader)) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 ||
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer le64toh(h->header_size) < sizeof(CatalogHeader) ||
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer le64toh(h->catalog_item_size) < sizeof(CatalogItem) ||
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic const char *find_id(void *p, sd_id128_t id) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *loc;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer strncpy(key.language, loc, sizeof(key.language));
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return (const char*) p +
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer le64toh(h->n_items) * le64toh(h->catalog_item_size) +
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint catalog_get(const char* database, sd_id128_t id, char **_text) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *s;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic char *find_header(const char *s, const char *header) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *v, *e;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* End of text */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer /* End of header */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerstatic void dump_catalog_entry(FILE *f, sd_id128_t id, const char *s, bool oneline) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_free_ char *subject = NULL, *defined_by = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint catalog_list(FILE *f, const char *database, bool oneline) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer items = (const CatalogItem*) ((const uint8_t*) p + le64toh(h->header_size));
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *s;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (last_id_set && sd_id128_equal(last_id, items[n].id))
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer dump_catalog_entry(f, items[n].id, s, oneline);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint catalog_list_items(FILE *f, const char *database, bool oneline, char **items) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (k < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_error_errno(k, "Failed to parse id128 '%s': %m", *item);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (k < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer log_full_errno(k == -ENOENT ? LOG_NOTICE : LOG_ERR, k,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer "Failed to retrieve catalog entry for '%s': %m", *item);