aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-util/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-util/src')
-rw-r--r--packages/taler-util/src/logging.ts10
-rw-r--r--packages/taler-util/src/talerconfig.ts193
2 files changed, 166 insertions, 37 deletions
diff --git a/packages/taler-util/src/logging.ts b/packages/taler-util/src/logging.ts
index c4b2a3da0..79fc49cdd 100644
--- a/packages/taler-util/src/logging.ts
+++ b/packages/taler-util/src/logging.ts
@@ -32,13 +32,17 @@ export enum LogLevel {
None = "none",
}
-export let globalLogLevel = LogLevel.Info;
+let globalLogLevel = LogLevel.Info;
+const byTagLogLevel: Record<string, LogLevel> = {};
+
+export function getGlobalLogLevel(): string {
+ return globalLogLevel;
+}
export function setGlobalLogLevelFromString(logLevelStr: string): void {
globalLogLevel = getLevelForString(logLevelStr);
}
-export const byTagLogLevel: Record<string, LogLevel> = {};
export function setLogLevelFromString(tag: string, logLevelStr: string): void {
byTagLogLevel[tag] = getLevelForString(logLevelStr);
}
@@ -98,7 +102,7 @@ function writeNodeLog(
* and uses the corresponding console.* method to log in the browser.
*/
export class Logger {
- constructor(private tag: string) {}
+ constructor(private tag: string) { }
shouldLogTrace(): boolean {
const level = byTagLogLevel[this.tag] ?? globalLogLevel;
diff --git a/packages/taler-util/src/talerconfig.ts b/packages/taler-util/src/talerconfig.ts
index 59c789cae..d86c58678 100644
--- a/packages/taler-util/src/talerconfig.ts
+++ b/packages/taler-util/src/talerconfig.ts
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
+ (C) 2020-2023 Taler Systems S.A.
GNU Taler 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
@@ -39,10 +39,30 @@ export class ConfigError extends Error {
}
}
+enum EntryOrigin {
+ /**
+ * From a default file.
+ */
+ DefaultFile = 1,
+ /**
+ * From a system/installation specific default value.
+ */
+ DefaultSystem = 2,
+ /**
+ * Loaded from file or string
+ */
+ Loaded = 3,
+ /**
+ * Changed after loading
+ */
+ Changed = 4,
+}
+
interface Entry {
value: string;
sourceLine: number;
sourceFile: string;
+ origin: EntryOrigin;
}
interface Section {
@@ -159,12 +179,12 @@ export function pathsub(
const r = lookup(inner, depth + 1);
if (r !== undefined) {
- s = s.substr(0, start) + r + s.substr(p + 1);
+ s = s.substring(0, start) + r + s.substring(p + 1);
l = start + r.length;
continue;
} else if (defaultValue !== undefined) {
const resolvedDefault = pathsub(defaultValue, lookup, depth + 1);
- s = s.substr(0, start) + resolvedDefault + s.substr(p + 1);
+ s = s.substring(0, start) + resolvedDefault + s.substring(p + 1);
l = start + resolvedDefault.length;
continue;
}
@@ -176,7 +196,7 @@ export function pathsub(
if (m && m[0]) {
const r = lookup(m[0], depth + 1);
if (r !== undefined) {
- s = s.substr(0, l) + r + s.substr(l + 1 + m[0].length);
+ s = s.substring(0, l) + r + s.substring(l + 1 + m[0].length);
l = l + r.length;
continue;
}
@@ -195,6 +215,7 @@ export interface LoadOptions {
export interface StringifyOptions {
diagnostics?: boolean;
+ excludeDefaults?: boolean;
}
export interface LoadedFile {
@@ -282,7 +303,11 @@ export class Configuration {
private nestLevel = 0;
- private loadFromFilename(filename: string, opts: LoadOptions = {}): void {
+ private loadFromFilename(
+ filename: string,
+ isDefaultSource: boolean,
+ opts: LoadOptions = {},
+ ): void {
filename = expandPath(filename);
const checkCycle = () => {
@@ -309,7 +334,7 @@ export class Configuration {
const oldNestLevel = this.nestLevel;
this.nestLevel += 1;
try {
- this.loadFromString(s, {
+ this.internalLoadFromString(s, isDefaultSource, {
...opts,
filename: filename,
});
@@ -318,7 +343,11 @@ export class Configuration {
}
}
- private loadGlob(parentFilename: string, fileglob: string): void {
+ private loadGlob(
+ parentFilename: string,
+ isDefaultSource: boolean,
+ fileglob: string,
+ ): void {
const resolvedParent = nodejs_fs.realpathSync(parentFilename);
const parentDir = nodejs_path.dirname(resolvedParent);
@@ -339,12 +368,16 @@ export class Configuration {
for (const f of files) {
if (globMatch(tail, f)) {
const fullPath = nodejs_path.join(head, f);
- this.loadFromFilename(fullPath);
+ this.loadFromFilename(fullPath, isDefaultSource);
}
}
}
- private loadSecret(sectionName: string, filename: string): void {
+ private loadSecret(
+ sectionName: string,
+ filename: string,
+ isDefaultSource: boolean,
+ ): void {
const sec = this.provideSection(sectionName);
sec.secretFilename = filename;
const otherCfg = new Configuration();
@@ -354,7 +387,7 @@ export class Configuration {
sec.inaccessible = true;
return;
}
- otherCfg.loadFromFilename(filename, {
+ otherCfg.loadFromFilename(filename, isDefaultSource, {
banDirectives: true,
});
const otherSec = otherCfg.provideSection(sectionName);
@@ -363,7 +396,11 @@ export class Configuration {
}
}
- loadFromString(s: string, opts: LoadOptions = {}): void {
+ private internalLoadFromString(
+ s: string,
+ isDefaultSource: boolean,
+ opts: LoadOptions = {},
+ ): void {
let lineNo = 0;
const fn = opts.filename ?? "<input>";
const reComment = /^\s*#.*$/;
@@ -399,7 +436,10 @@ export class Configuration {
);
}
const arg = directiveMatch[2].trim();
- this.loadFromFilename(normalizeInlineFilename(opts.filename, arg));
+ this.loadFromFilename(
+ normalizeInlineFilename(opts.filename, arg),
+ isDefaultSource,
+ );
break;
}
case "inline-secret": {
@@ -419,7 +459,7 @@ export class Configuration {
opts.filename,
sp[1],
);
- this.loadSecret(sp[0], secretFilename);
+ this.loadSecret(sp[0], secretFilename, isDefaultSource);
break;
}
case "inline-matching": {
@@ -429,7 +469,7 @@ export class Configuration {
`invalid configuration, @inline-matching@ directive in ${fn}:${lineNo} can only be used from a file`,
);
}
- this.loadGlob(opts.filename, arg);
+ this.loadGlob(opts.filename, isDefaultSource, arg);
break;
}
default:
@@ -462,6 +502,9 @@ export class Configuration {
value: val,
sourceFile: opts.filename ?? "<unknown>",
sourceLine: lineNo,
+ origin: isDefaultSource
+ ? EntryOrigin.DefaultFile
+ : EntryOrigin.Loaded,
};
continue;
}
@@ -471,6 +514,10 @@ export class Configuration {
}
}
+ loadFromString(s: string, opts: LoadOptions = {}): void {
+ return this.internalLoadFromString(s, false, opts);
+ }
+
private provideSection(section: string): Section {
const secNorm = section.toUpperCase();
if (this.sectionMap[secNorm]) {
@@ -496,6 +543,24 @@ export class Configuration {
value,
sourceLine: 0,
sourceFile: "<unknown>",
+ origin: EntryOrigin.Changed,
+ };
+ }
+
+ /**
+ * Set a string value to a value from default locations.
+ */
+ private setStringSystemDefault(
+ section: string,
+ option: string,
+ value: string,
+ ): void {
+ const sec = this.provideSection(section);
+ sec.entries[option.toUpperCase()] = {
+ value,
+ sourceLine: 0,
+ sourceFile: "<unknown>",
+ origin: EntryOrigin.DefaultSystem,
};
}
@@ -559,7 +624,7 @@ export class Configuration {
lookupVariable(x: string, depth: number = 0): string | undefined {
// We loop up options in PATHS in upper case, as option names
// are case insensitive
- const val = this.findEntry("PATHS", x)?.value;
+ let val = this.findEntry("PATHS", x)?.value;
if (val !== undefined) {
return pathsub(val, (v, d) => this.lookupVariable(v, d), depth);
}
@@ -578,30 +643,63 @@ export class Configuration {
);
}
- loadFrom(dirname: string): void {
+ loadDefaultsFromDir(dirname: string): void {
const files = nodejs_fs.readdirSync(dirname);
for (const f of files) {
const fn = nodejs_path.join(dirname, f);
- this.loadFromFilename(fn);
+ this.loadFromFilename(fn, true);
}
}
private loadDefaults(): void {
- let bc = process.env["TALER_BASE_CONFIG"];
- if (!bc) {
+ let baseConfigDir = process.env["TALER_BASE_CONFIG"];
+ if (!baseConfigDir) {
/* Try to locate the configuration based on the location
* of the taler-config binary. */
const path = which("taler-config");
if (path) {
- bc = nodejs_fs.realpathSync(
+ baseConfigDir = nodejs_fs.realpathSync(
nodejs_path.dirname(path) + "/../share/taler/config.d",
);
}
}
- if (!bc) {
- bc = "/usr/share/taler/config.d";
+ if (!baseConfigDir) {
+ baseConfigDir = "/usr/share/taler/config.d";
}
- this.loadFrom(bc);
+
+ let installPrefix = process.env["TALER_PREFIX"];
+ if (!installPrefix) {
+ /* Try to locate install path based on the location
+ * of the taler-config binary. */
+ const path = which("taler-config");
+ if (path) {
+ installPrefix = nodejs_fs.realpathSync(
+ nodejs_path.dirname(path) + "/..",
+ );
+ }
+ }
+ if (!installPrefix) {
+ installPrefix = "/usr";
+ }
+
+ this.setStringSystemDefault(
+ "PATHS",
+ "LIBEXECDIR",
+ `${installPrefix}/taler/libexec/`,
+ );
+ this.setStringSystemDefault(
+ "PATHS",
+ "DOCDIR",
+ `${installPrefix}/share/doc/taler/`,
+ );
+ this.setStringSystemDefault("PATHS", "ICONDIR", `${installPrefix}/share/icons/`);
+ this.setStringSystemDefault("PATHS", "LOCALEDIR", `${installPrefix}/share/locale/`);
+ this.setStringSystemDefault("PATHS", "PREFIX", `${installPrefix}/`);
+ this.setStringSystemDefault("PATHS", "BINDIR", `${installPrefix}/bin`);
+ this.setStringSystemDefault("PATHS", "LIBDIR", `${installPrefix}/lib/taler/`);
+ this.setStringSystemDefault("PATHS", "DATADIR", `${installPrefix}/share/taler/`);
+
+ this.loadDefaultsFromDir(baseConfigDir);
}
getDefaultConfigFilename(): string | undefined {
@@ -631,11 +729,13 @@ export class Configuration {
const cfg = new Configuration();
cfg.loadDefaults();
if (filename) {
- cfg.loadFromFilename(filename);
+ cfg.loadFromFilename(filename, false);
} else {
const fn = cfg.getDefaultConfigFilename();
if (fn) {
- cfg.loadFromFilename(fn);
+ // It's the default filename for the main config file,
+ // but we don't consider the values default values.
+ cfg.loadFromFilename(fn, false);
}
}
cfg.hintEntrypoint = filename;
@@ -657,26 +757,51 @@ export class Configuration {
}
for (const sectionName of Object.keys(this.sectionMap)) {
const sec = this.sectionMap[sectionName];
- if (opts.diagnostics && sec.secretFilename) {
- s += `# Secret section from ${sec.secretFilename}\n`;
- s += `# Secret accessible: ${!sec.inaccessible}\n`;
- }
- s += `[${sectionName}]\n`;
+ let headerWritten = false;
for (const optionName of Object.keys(sec.entries)) {
const entry = this.sectionMap[sectionName].entries[optionName];
+ if (
+ opts.excludeDefaults &&
+ (entry.origin === EntryOrigin.DefaultSystem ||
+ entry.origin === EntryOrigin.DefaultFile)
+ ) {
+ continue;
+ }
+ if (!headerWritten) {
+ if (opts.diagnostics && sec.secretFilename) {
+ s += `# Secret section from ${sec.secretFilename}\n`;
+ s += `# Secret accessible: ${!sec.inaccessible}\n`;
+ }
+ s += `[${sectionName}]\n`;
+ headerWritten = true;
+ }
if (entry !== undefined) {
if (opts.diagnostics) {
- s += `# ${entry.sourceFile}:${entry.sourceLine}\n`;
+ switch (entry.origin) {
+ case EntryOrigin.DefaultFile:
+ case EntryOrigin.Changed:
+ case EntryOrigin.Loaded:
+ s += `# ${entry.sourceFile}:${entry.sourceLine}\n`;
+ break;
+ case EntryOrigin.DefaultSystem:
+ s += `# (system/installation default)\n`;
+ break;
+ }
}
s += `${optionName} = ${entry.value}\n`;
}
}
- s += "\n";
+ if (headerWritten) {
+ s += "\n";
+ }
}
return s;
}
- write(filename: string): void {
- nodejs_fs.writeFileSync(filename, this.stringify());
+ write(filename: string, opts: { excludeDefaults?: boolean } = {}): void {
+ nodejs_fs.writeFileSync(
+ filename,
+ this.stringify({ excludeDefaults: opts.excludeDefaults }),
+ );
}
}