catalog.c revision e53fc357a9bb9d0a5362ccc4246d598cb0febd5e
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering This file is part of systemd.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering Copyright 2012 Lennart Poettering
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering systemd is free software; you can redistribute it and/or modify it
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering under the terms of the GNU Lesser General Public License as published by
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering (at your option) any later version.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering systemd is distributed in the hope that it will be useful, but
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering Lesser General Public License for more details.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering You should have received a copy of the GNU Lesser General Public License
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekconst char * const catalog_file_dirs[] = {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering#define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' }
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidtstatic unsigned long catalog_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering memcpy(mempcpy(v, &i->id, sizeof(i->id)), i->language, l);
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering siphash24((uint8_t*) &u, v, sz, hash_key);
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering return (unsigned long) u;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidtstatic int catalog_compare_func(const void *a, const void *b) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering const CatalogItem *i = a, *j = b;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) {
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek return strcmp(i->language, j->language);
e3b9d9c8027a7c4c55cf1614e0fe9423fad69e8fZbigniew Jędrzejewski-Szmek _cleanup_free_ CatalogItem *i = NULL;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering offset = strbuf_add_string(sb, payload, strlen(payload));
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek assert(strlen(language) > 1 && strlen(language) < 32);
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.",
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek SD_ID128_FORMAT_VAL(id), language ? language : "C");
e3b9d9c8027a7c4c55cf1614e0fe9423fad69e8fZbigniew Jędrzejewski-Szmek } else if (r < 0)
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmekint catalog_file_lang(const char* filename, char **lang) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek end = endswith(filename, ".catalog");
4b8268f843b0da1cfe1995d93a0b1f95faccc454Zbigniew Jędrzejewski-Szmek while (beg > filename && *beg != '.' && *beg != '/' && end - beg < 32)
4b8268f843b0da1cfe1995d93a0b1f95faccc454Zbigniew Jędrzejewski-Szmek if (*beg != '.' || end <= beg + 1)
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek _lang = strndup(beg + 1, end - beg - 1);
baf167ee0a2953f98e4e7d4c35752ef737832674Zbigniew Jędrzejewski-Szmekstatic int catalog_entry_lang(const char* filename, int line,
baf167ee0a2953f98e4e7d4c35752ef737832674Zbigniew Jędrzejewski-Szmek const char* t, const char* deflang, char **lang) {
baf167ee0a2953f98e4e7d4c35752ef737832674Zbigniew Jędrzejewski-Szmek log_error("[%s:%u] Language too short.", filename, line);
baf167ee0a2953f98e4e7d4c35752ef737832674Zbigniew Jędrzejewski-Szmek log_error("[%s:%u] language too long.", filename, line);
baf167ee0a2953f98e4e7d4c35752ef737832674Zbigniew Jędrzejewski-Szmek log_warning("[%s:%u] language specified unnecessarily",
baf167ee0a2953f98e4e7d4c35752ef737832674Zbigniew Jędrzejewski-Szmek log_warning("[%s:%u] language differs from default for file",
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekint catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering unsigned n = 0;
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek _cleanup_free_ char *deflang = NULL, *lang = NULL;
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt return log_error_errno(errno, "Failed to open file %s: %m", path);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek r = catalog_file_lang(path, &deflang);
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt log_error_errno(errno, "Failed to determine language for file %s: %m", path);
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek log_debug("File %s has language %s.", path, deflang);
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt log_error_errno(errno, "Failed to read file %s: %m", path);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (line[0] == 0) {
d3b6d0c21ea5a0d15ec6dbd8b8d179138b7463bcZbigniew Jędrzejewski-Szmek if (strchr(COMMENTS "\n", line[0]))
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek (line[2+1+32] == ' ' || line[2+1+32] == '\0')) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* New entry */
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek with_language = line[2+1+32] != '\0';
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek r = finish_item(h, sb, id, lang ?: deflang, payload);
baf167ee0a2953f98e4e7d4c35752ef737832674Zbigniew Jędrzejewski-Szmek r = catalog_entry_lang(path, n, t, deflang, &lang);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering log_error("[%s:%u] Got payload before ID.", path, n);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering c = a + (empty_line ? 1 : 0) + b + 1 + 1;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering t[a+b] = '\n';
c7332b0844e28d9b70c3c763b929f105c1056fe8Zbigniew Jędrzejewski-Szmek r = finish_item(h, sb, id, lang ?: deflang, payload);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekstatic long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek _cleanup_fclose_ FILE *w = NULL;
eb56eb9b40950f1edcffdb7313f8de4f8572a6d5Michal Schmidt return log_error_errno(r, "Recursive mkdir %s: %m", d);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = fopen_temporary(database, &w, &p);
eb56eb9b40950f1edcffdb7313f8de4f8572a6d5Michal Schmidt return log_error_errno(r, "Failed to open database for writing: %s: %m",
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek header.catalog_item_size = htole64(sizeof(CatalogItem));
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek header.n_items = htole64(hashmap_size(h));
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek k = fwrite(&header, 1, sizeof(header), w);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek if (k != sizeof(header)) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek log_error("%s: failed to write header.", p);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek k = fwrite(items, 1, n * sizeof(CatalogItem), w);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek if (k != n * sizeof(CatalogItem)) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek log_error("%s: failed to write database.", p);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek k = fwrite(sb->buf, 1, sb->len, w);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek log_error("%s: failed to write strings.", p);
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering log_error_errno(r, "%s: failed to write database: %m", p);
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekint catalog_update(const char* database, const char* root, const char* const* dirs) {
e3b9d9c8027a7c4c55cf1614e0fe9423fad69e8fZbigniew Jędrzejewski-Szmek _cleanup_hashmap_free_free_ Hashmap *h = NULL;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering _cleanup_free_ CatalogItem *items = NULL;
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = conf_files_list_strv(&files, ".catalog", root, dirs);
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt log_error_errno(r, "Failed to get catalog files: %m");
e3b9d9c8027a7c4c55cf1614e0fe9423fad69e8fZbigniew Jędrzejewski-Szmek log_debug("Reading file '%s'", *f);
e3b9d9c8027a7c4c55cf1614e0fe9423fad69e8fZbigniew Jędrzejewski-Szmek r = catalog_import_file(h, sb, *f);
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering log_error_errno(r, "Failed to import file '%s': %m", *f);
80343dc19a9bcd703275ad2ad88f43e5310559d6Zbigniew Jędrzejewski-Szmek log_debug("Found %u items in catalog.", hashmap_size(h));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering items = new(CatalogItem, hashmap_size(h));
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek log_debug("Found " SD_ID128_FORMAT_STR ", language %s",
18cd5fe99f70a55a2d6f2303d6ee0624942695b1Zbigniew Jędrzejewski-Szmek isempty(i->language) ? "C" : i->language);
7ff7394d9e4e9189c30fd018235e6b1728c6f2d0Zbigniew Jędrzejewski-Szmek qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = write_catalog(database, h, sb, items, n);
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt log_error_errno(r, "Failed to write %s: %m", database);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.",
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek return r < 0 ? r : 0;
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekstatic int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek fd = open(database, O_RDONLY|O_CLOEXEC);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (st.st_size < (off_t) sizeof(CatalogHeader)) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 ||
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering le64toh(h->header_size) < sizeof(CatalogHeader) ||
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering le64toh(h->catalog_item_size) < sizeof(CatalogItem) ||
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic const char *find_id(void *p, sd_id128_t id) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering strncpy(key.language, loc, sizeof(key.language));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering key.language[strcspn(key.language, ".@")] = 0;
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return (const char*) p +
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering le64toh(h->n_items) * le64toh(h->catalog_item_size) +
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekint catalog_get(const char* database, sd_id128_t id, char **_text) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering const char *s;
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = open_mmap(database, &fd, &st, &p);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic char *find_header(const char *s, const char *header) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering const char *v, *e;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* End of text */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* End of header */
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmekstatic void dump_catalog_entry(FILE *f, sd_id128_t id, const char *s, bool oneline) {
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek _cleanup_free_ char *subject = NULL, *defined_by = NULL;
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek subject = find_header(s, "Subject:");
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek defined_by = find_header(s, "Defined-By:");
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n",
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek strna(defined_by), strna(subject));
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek fprintf(f, "-- " SD_ID128_FORMAT_STR "\n%s\n",
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekint catalog_list(FILE *f, const char *database, bool oneline) {
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek r = open_mmap(database, &fd, &st, &p);
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering items = (const CatalogItem*) ((const uint8_t*) p + le64toh(h->header_size));
83f6936a018b08880670838756e0f4e9ea98b4a7Lennart Poettering for (n = 0; n < le64toh(h->n_items); n++) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering const char *s;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (last_id_set && sd_id128_equal(last_id, items[n].id))
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek dump_catalog_entry(f, items[n].id, s, oneline);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmekint catalog_list_items(FILE *f, const char *database, bool oneline, char **items) {
54b7254c1fa629937f92fd6fa34bdf127b696a00Zbigniew Jędrzejewski-Szmek k = sd_id128_from_string(*item, &id);
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering log_error_errno(k, "Failed to parse id128 '%s': %m", *item);
844ec79b3c2f246114ea316ebe1f36044bdb688eZbigniew Jędrzejewski-Szmek k = catalog_get(database, id, &msg);
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering log_full_errno(k == -ENOENT ? LOG_NOTICE : LOG_ERR, k,
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering "Failed to retrieve catalog entry for '%s': %m", *item);