@push.rocks/smartdaemon 2.4.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdaemon',
6
- version: '2.4.1',
6
+ version: '2.6.0',
7
7
  description: 'Start scripts as long running daemons and manage them.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx5QkFBeUI7SUFDL0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHdEQUF3RDtDQUN0RSxDQUFBIn0=
@@ -1,3 +1,5 @@
1
+ import { type ISystemdServiceStartup, type ISystemdServiceDependencies, type ISystemdServiceInstallTargets } from './systemd.definition.js';
2
+ export type { ISystemdServiceStartup, ISystemdServiceDependencies, ISystemdServiceInstallTargets } from './systemd.definition.js';
1
3
  export interface ISystemdServiceDefinitionOptions {
2
4
  unitName: string;
3
5
  description: string;
@@ -9,6 +11,9 @@ export interface ISystemdServiceDefinitionOptions {
9
11
  restart: 'no' | 'on-failure' | 'always';
10
12
  killMode: 'mixed' | 'control-group';
11
13
  timeoutStopSeconds: number;
14
+ startup?: ISystemdServiceStartup;
15
+ dependencies?: ISystemdServiceDependencies;
16
+ install?: ISystemdServiceInstallTargets;
12
17
  }
13
18
  export declare class SystemdServiceDefinitionError extends Error {
14
19
  constructor();
@@ -1,4 +1,5 @@
1
1
  import * as plugins from './smartdaemon.plugins.js';
2
+ import { renderServiceRelations } from './systemd.definition.js';
2
3
  export class SystemdServiceDefinitionError extends Error {
3
4
  constructor() {
4
5
  super('Invalid direct systemd service definition.');
@@ -28,8 +29,9 @@ export class SystemdServiceDefinition {
28
29
  'killMode',
29
30
  'timeoutStopSeconds',
30
31
  ];
31
- requireValue(options && Object.getPrototypeOf(options) === Object.prototype &&
32
- Reflect.ownKeys(options).length === keys.length && keys.every((key) => {
32
+ requireValue(options && !plugins.util.types.isProxy(options) && Object.getPrototypeOf(options) === Object.prototype &&
33
+ Reflect.ownKeys(options).every(key => typeof key === 'string' && [...keys, 'startup', 'dependencies', 'install'].includes(key)
34
+ && Object.getOwnPropertyDescriptor(options, key)?.enumerable && 'value' in Object.getOwnPropertyDescriptor(options, key)) && keys.every((key) => {
33
35
  const descriptor = Object.getOwnPropertyDescriptor(options, key);
34
36
  return descriptor && 'value' in descriptor;
35
37
  }));
@@ -42,7 +44,7 @@ export class SystemdServiceDefinition {
42
44
  absolute(options.workingDirectory) &&
43
45
  options.workingDirectory.trim() === options.workingDirectory &&
44
46
  !options.workingDirectory.includes('\\'));
45
- requireValue(Array.isArray(options.args) && Object.getPrototypeOf(options.args) === Array.prototype &&
47
+ requireValue(Array.isArray(options.args) && !plugins.util.types.isProxy(options.args) && Object.getPrototypeOf(options.args) === Array.prototype &&
46
48
  options.args.length <= 128 &&
47
49
  Reflect.ownKeys(options.args).length === options.args.length + 1 &&
48
50
  Array.from({ length: options.args.length }, (_, index) => Object.getOwnPropertyDescriptor(options.args, String(index)))
@@ -57,14 +59,15 @@ export class SystemdServiceDefinition {
57
59
  .replaceAll('\\', '\\\\').replaceAll('"', '\\"').replaceAll('%', '%%')
58
60
  .replaceAll('$', () => command ? '$$' : '$') +
59
61
  '"';
62
+ const relations = renderServiceRelations(options, options.unitName, options.restart, () => { throw new SystemdServiceDefinitionError(); });
60
63
  this.unitName = options.unitName;
61
64
  this.content = [
62
65
  '[Unit]',
63
66
  `Description=${options.description.replaceAll('%', '%%')}`,
64
- 'After=local-fs.target',
67
+ ...relations.unit,
65
68
  '',
66
69
  '[Service]',
67
- 'Type=exec',
70
+ ...relations.service,
68
71
  `User=${options.user}`,
69
72
  `Group=${options.group}`,
70
73
  `ExecStart=${[quote(options.executable), ...options.args.map((value) => quote(value, true))].join(' ')}`,
@@ -82,7 +85,7 @@ export class SystemdServiceDefinition {
82
85
  'StandardError=journal',
83
86
  '',
84
87
  '[Install]',
85
- 'WantedBy=multi-user.target',
88
+ ...relations.install,
86
89
  '',
87
90
  ].join('\n');
88
91
  requireValue(Buffer.byteLength(this.content) <= 16384);
@@ -90,4 +93,4 @@ export class SystemdServiceDefinition {
90
93
  Object.freeze(this);
91
94
  }
92
95
  }
93
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zeXN0ZW1kc2VydmljZWRlZmluaXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLnN5c3RlbWRzZXJ2aWNlZGVmaW5pdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLDBCQUEwQixDQUFDO0FBZXBELE1BQU0sT0FBTyw2QkFBOEIsU0FBUSxLQUFLO0lBQ3REO1FBQ0UsS0FBSyxDQUFDLDRDQUE0QyxDQUFDLENBQUM7UUFDcEQsSUFBSSxDQUFDLElBQUksR0FBRywrQkFBK0IsQ0FBQztJQUM5QyxDQUFDO0NBQ0Y7QUFFRCw4RkFBOEY7QUFDOUYsTUFBTSxPQUFPLHdCQUF3QjtJQUtuQyxZQUFZLE9BQXlDO1FBQ25ELE1BQU0sWUFBWSxHQUFHLENBQUMsS0FBYyxFQUFRLEVBQUU7WUFDNUMsSUFBSSxDQUFDLEtBQUs7Z0JBQUUsTUFBTSxJQUFJLDZCQUE2QixFQUFFLENBQUM7UUFDeEQsQ0FBQyxDQUFDO1FBQ0YsTUFBTSxJQUFJLEdBQUcsQ0FBQyxLQUFjLEVBQUUsT0FBTyxHQUFHLElBQUksRUFBbUIsRUFBRSxDQUMvRCxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLE1BQU0sSUFBSSxPQUFPLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDO1lBQ3RGLENBQUMseUVBQXlFLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3pGLE1BQU0sUUFBUSxHQUFHLENBQUMsS0FBYyxFQUFFLEVBQUUsQ0FDbEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQztZQUM3QyxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsS0FBSyxLQUFLLENBQUM7UUFDMUMsTUFBTSxJQUFJLEdBQUc7WUFDWCxVQUFVO1lBQ1YsYUFBYTtZ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
96
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zeXN0ZW1kc2VydmljZWRlZmluaXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLnN5c3RlbWRzZXJ2aWNlZGVmaW5pdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLDBCQUEwQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxzQkFBc0IsRUFDTyxNQUFNLHlCQUF5QixDQUFDO0FBbUJ0RSxNQUFNLE9BQU8sNkJBQThCLFNBQVEsS0FBSztJQUN0RDtRQUNFLEtBQUssQ0FBQyw0Q0FBNEMsQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxJQUFJLEdBQUcsK0JBQStCLENBQUM7SUFDOUMsQ0FBQztDQUNGO0FBRUQsOEZBQThGO0FBQzlGLE1BQU0sT0FBTyx3QkFBd0I7SUFLbkMsWUFBWSxPQUF5QztRQUNuRCxNQUFNLFlBQVksR0FBRyxDQUFDLEtBQWMsRUFBUSxFQUFFO1lBQzVDLElBQUksQ0FBQyxLQUFLO2dCQUFFLE1BQU0sSUFBSSw2QkFBNkIsRUFBRSxDQUFDO1FBQ3hELENBQUMsQ0FBQztRQUNGLE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBYyxFQUFFLE9BQU8sR0FBRyxJQUFJLEVBQW1CLEVBQUUsQ0FDL0QsT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssQ0FBQyxNQUFNLElBQUksT0FBTyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQztZQUN0RixDQUFDLHlFQUF5RSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN6RixNQUFNLFFBQVEsR0FBRyxDQUFDLEtBQWMsRUFBRSxFQUFFLENBQ2xDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUM7WUFDN0MsT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLEtBQUssS0FBSyxD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
@@ -25,11 +25,21 @@ export interface ISystemdUnitConfigurationState {
25
25
  readonly dropInPaths: string;
26
26
  readonly needsReload: boolean;
27
27
  }
28
+ export interface ISystemdUnitRelationships {
29
+ readonly unitName: string;
30
+ readonly before: readonly string[];
31
+ readonly after: readonly string[];
32
+ readonly requires: readonly string[];
33
+ readonly wants: readonly string[];
34
+ readonly conflicts: readonly string[];
35
+ readonly requiredBy: readonly string[];
36
+ readonly wantedBy: readonly string[];
37
+ }
28
38
  export declare class SystemdUnitError extends Error {
29
39
  readonly code: 'invalid_options' | 'command_failed' | 'invalid_state';
30
40
  constructor(code: 'invalid_options' | 'command_failed' | 'invalid_state');
31
41
  }
32
- /** Controls an existing system unit without creating files, changing enablement or escalating privileges. */
42
+ /** Controls an existing system unit without rewriting its definition or escalating privileges. */
33
43
  export declare class SystemdUnit {
34
44
  private readonly unitName;
35
45
  private readonly executable;
@@ -39,6 +49,11 @@ export declare class SystemdUnit {
39
49
  inspect(): Promise<ISystemdUnitState>;
40
50
  /** Await the systemd job, then inspect. This does not enable or unmask the unit. */
41
51
  start(): Promise<ISystemdUnitState>;
52
+ /** Persistently enables the existing definition, then inspects. Never starts, unmasks or forces links.
53
+ * Owning installers must separately verify exact file/configuration and required relationships. */
54
+ enable(): Promise<ISystemdUnitState>;
55
+ /** Loaded manager graph, including reverse edges. Enablement alone does not prove every required link. */
56
+ inspectRelationships(): Promise<ISystemdUnitRelationships>;
42
57
  /** Await the systemd job, then inspect. The snapshot alone is not an application data-drain proof. */
43
58
  stop(): Promise<ISystemdUnitState>;
44
59
  /** Reports the manager's selected definition without reading or changing its contents. */
@@ -18,7 +18,7 @@ const properties = [
18
18
  'KillMode',
19
19
  'Job',
20
20
  ];
21
- /** Controls an existing system unit without creating files, changing enablement or escalating privileges. */
21
+ /** Controls an existing system unit without rewriting its definition or escalating privileges. */
22
22
  export class SystemdUnit {
23
23
  constructor(optionsArg) {
24
24
  this.unitName = optionsArg?.unitName;
@@ -70,6 +70,40 @@ export class SystemdUnit {
70
70
  await this.command(['start', this.unitName]);
71
71
  return this.inspect();
72
72
  }
73
+ /** Persistently enables the existing definition, then inspects. Never starts, unmasks or forces links.
74
+ * Owning installers must separately verify exact file/configuration and required relationships. */
75
+ async enable() {
76
+ await this.command(['enable', this.unitName]);
77
+ return this.inspect();
78
+ }
79
+ /** Loaded manager graph, including reverse edges. Enablement alone does not prove every required link. */
80
+ async inspectRelationships() {
81
+ const keys = ['Id', 'Before', 'After', 'Requires', 'Wants', 'Conflicts', 'RequiredBy', 'WantedBy'];
82
+ const stdout = await this.command(['show', this.unitName, `--property=${keys.join(',')}`, '--all']);
83
+ const fields = new Map();
84
+ for (const line of (stdout.endsWith('\n') ? stdout.slice(0, -1) : stdout).split('\n')) {
85
+ const separator = line.indexOf('='), key = line.slice(0, separator);
86
+ if (separator < 1 || !keys.includes(key) || fields.has(key))
87
+ throw new SystemdUnitError('invalid_state');
88
+ fields.set(key, line.slice(separator + 1));
89
+ }
90
+ if (fields.size !== keys.length || fields.get('Id') !== this.unitName)
91
+ throw new SystemdUnitError('invalid_state');
92
+ const units = (key) => {
93
+ const text = fields.get(key);
94
+ const values = text === '' ? [] : text.split(' ');
95
+ // Unit names are ASCII; retain systemd's literal hex escapes without decoding
96
+ // them into separators. Output and every individual name remain bounded.
97
+ if (values.some(value => value.length > 255 ||
98
+ !/^(?:[a-zA-Z0-9_.:@-]|\\x[0-9a-fA-F]{2})+\.(?:service|socket|device|mount|automount|swap|target|path|timer|slice|scope)$/.test(value)) ||
99
+ new Set(values).size !== values.length)
100
+ throw new SystemdUnitError('invalid_state');
101
+ return Object.freeze(values.sort());
102
+ };
103
+ return Object.freeze({ unitName: this.unitName, before: units('Before'), after: units('After'),
104
+ requires: units('Requires'), wants: units('Wants'), conflicts: units('Conflicts'),
105
+ requiredBy: units('RequiredBy'), wantedBy: units('WantedBy') });
106
+ }
73
107
  /** Await the systemd job, then inspect. The snapshot alone is not an application data-drain proof. */
74
108
  async stop() {
75
109
  await this.command(['stop', this.unitName]);
@@ -122,4 +156,4 @@ export class SystemdUnit {
122
156
  });
123
157
  }
124
158
  }
125
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zeXN0ZW1kdW5pdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMuc3lzdGVtZHVuaXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSwwQkFBMEIsQ0FBQztBQWdDcEQsTUFBTSxPQUFPLGdCQUFpQixTQUFRLEtBQUs7SUFDekMsWUFBNEIsSUFBNEQ7UUFDdEYsS0FBSyxDQUFDLGtDQUFrQyxJQUFJLEdBQUcsQ0FBQyxDQUFDO1FBRHZCLFNBQUksR0FBSixJQUFJLENBQXdEO1FBRXRGLElBQUksQ0FBQyxJQUFJLEdBQUcsa0JBQWtCLENBQUM7SUFDakMsQ0FBQztDQUNGO0FBRUQsTUFBTSxVQUFVLEdBQUc7SUFDakIsSUFBSTtJQUNKLFdBQVc7SUFDWCxhQUFhO0lBQ2IsVUFBVTtJQUNWLGVBQWU7SUFDZixTQUFTO0lBQ1QsWUFBWTtJQUNaLGNBQWM7SUFDZCxVQUFVO0lBQ1YsS0FBSztDQUNHLENBQUM7QUFFWCw2R0FBNkc7QUFDN0csTUFBTSxPQUFPLFdBQVc7SUFLdEIsWUFBWSxVQUErQjtRQUN6QyxJQUFJLENBQUMsUUFBUSxHQUFHLFVBQVUsRUFBRSxRQUFRLENBQUM7UUFDckMsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLEVBQUUsYUFBYSxJQUFJLG9CQUFvQixDQUFDO1FBQ3BFLElBQUksQ0FBQyxPQUFPLEdBQUcsVUFBVSxFQUFFLGdCQUFnQixJQUFJLE1BQU0sQ0FBQztRQUN0RCxJQUNFLE9BQU8sSUFBSSxDQUFDLFFBQVEsS0FBSyxRQUFRO1lBQ2pDLENBQUMsK0NBQStDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUM7WUFDcEUsT0FBTyxJQUFJLENBQUMsVUFBVSxLQUFLLFFBQVEsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUM7WUFDaEYsSUFBSSxDQUFDLFVBQVUsQ0FBQyxR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
159
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zeXN0ZW1kdW5pdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMuc3lzdGVtZHVuaXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSwwQkFBMEIsQ0FBQztBQTJDcEQsTUFBTSxPQUFPLGdCQUFpQixTQUFRLEtBQUs7SUFDekMsWUFBNEIsSUFBNEQ7UUFDdEYsS0FBSyxDQUFDLGtDQUFrQyxJQUFJLEdBQUcsQ0FBQyxDQUFDO1FBRHZCLFNBQUksR0FBSixJQUFJLENBQXdEO1FBRXRGLElBQUksQ0FBQyxJQUFJLEdBQUcsa0JBQWtCLENBQUM7SUFDakMsQ0FBQztDQUNGO0FBRUQsTUFBTSxVQUFVLEdBQUc7SUFDakIsSUFBSTtJQUNKLFdBQVc7SUFDWCxhQUFhO0lBQ2IsVUFBVTtJQUNWLGVBQWU7SUFDZixTQUFTO0lBQ1QsWUFBWTtJQUNaLGNBQWM7SUFDZCxVQUFVO0lBQ1YsS0FBSztDQUNHLENBQUM7QUFFWCxrR0FBa0c7QUFDbEcsTUFBTSxPQUFPLFdBQVc7SUFLdEIsWUFBWSxVQUErQjtRQUN6QyxJQUFJLENBQUMsUUFBUSxHQUFHLFVBQVUsRUFBRSxRQUFRLENBQUM7UUFDckMsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLEVBQUUsYUFBYSxJQUFJLG9CQUFvQixDQUFDO1FBQ3BFLElBQUksQ0FBQyxPQUFPLEdBQUcsVUFBVSxFQUFFLGdCQUFnQixJQUFJLE1BQU0sQ0FBQztRQUN0RCxJQUNFLE9BQU8sSUFBSSxDQUFDLFFBQVEsS0FBSyxRQUFRO1lBQ2pDLENBQUMsK0NBQStDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUM7WUFDcEUsT0FBTyxJQUFJLENBQUMsVUFBVSxLQUFLLFFBQVEsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUM7WUFDaEYsSUFBSSxDQUFDLFVBQVUsQ0FBQyxR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
@@ -3,8 +3,9 @@ import * as childProcess from 'node:child_process';
3
3
  import * as fs from 'node:fs/promises';
4
4
  import { constants as fsConstants } from 'node:fs';
5
5
  import * as crypto from 'node:crypto';
6
+ import * as util from 'node:util';
6
7
  export type { Stats } from 'node:fs';
7
- export { path, childProcess, fs, fsConstants, crypto };
8
+ export { path, childProcess, fs, fsConstants, crypto, util };
8
9
  import * as lik from '@push.rocks/lik';
9
10
  import * as smartfm from '@push.rocks/smartfm';
10
11
  import * as smartfs from '@push.rocks/smartfs';
@@ -4,7 +4,8 @@ import * as childProcess from 'node:child_process';
4
4
  import * as fs from 'node:fs/promises';
5
5
  import { constants as fsConstants } from 'node:fs';
6
6
  import * as crypto from 'node:crypto';
7
- export { path, childProcess, fs, fsConstants, crypto };
7
+ import * as util from 'node:util';
8
+ export { path, childProcess, fs, fsConstants, crypto, util };
8
9
  // @pushrocks scope
9
10
  import * as lik from '@push.rocks/lik';
10
11
  import * as smartfm from '@push.rocks/smartfm';
@@ -16,4 +17,4 @@ import * as smartshell from '@push.rocks/smartshell';
16
17
  import * as smartsystem from '@push.rocks/smartsystem';
17
18
  export { lik, smartfm, smartfs, smartlog, smartlogDestinationLocal, smartpath, smartshell, smartsystem, };
18
19
  // third party
19
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYWVtb24ucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0ZGFlbW9uLnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxZQUFZLE1BQU0sb0JBQW9CLENBQUM7QUFDbkQsT0FBTyxLQUFLLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUN2QyxPQUFPLEVBQUUsU0FBUyxJQUFJLFdBQVcsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNuRCxPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUd0QyxPQUFPLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxFQUFFLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxDQUFDO0FBRXZELG1CQUFtQjtBQUNuQixPQUFPLEtBQUssR0FBRyxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZDLE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFDL0MsT0FBTyxLQUFLLE9BQU8sTUFBTSxxQkFBcUIsQ0FBQztBQUMvQyxPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sS0FBSyx3QkFBd0IsTUFBTSx3Q0FBd0MsQ0FBQztBQUNuRixPQUFPLEtBQUssU0FBUyxNQUFNLHVCQUF1QixDQUFDO0FBQ25ELE9BQU8sS0FBSyxVQUFVLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxLQUFLLFdBQVcsTUFBTSx5QkFBeUIsQ0FBQztBQUV2RCxPQUFPLEVBQ0wsR0FBRyxFQUNILE9BQU8sRUFDUCxPQUFPLEVBQ1AsUUFBUSxFQUNSLHdCQUF3QixFQUN4QixTQUFTLEVBQ1QsVUFBVSxFQUNWLFdBQVcsR0FDWixDQUFDO0FBRUYsY0FBYyJ9
20
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYWVtb24ucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0ZGFlbW9uLnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxZQUFZLE1BQU0sb0JBQW9CLENBQUM7QUFDbkQsT0FBTyxLQUFLLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUN2QyxPQUFPLEVBQUUsU0FBUyxJQUFJLFdBQVcsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNuRCxPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUN0QyxPQUFPLEtBQUssSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUdsQyxPQUFPLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxFQUFFLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsQ0FBQztBQUU3RCxtQkFBbUI7QUFDbkIsT0FBTyxLQUFLLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQztBQUN2QyxPQUFPLEtBQUssT0FBTyxNQUFNLHFCQUFxQixDQUFDO0FBQy9DLE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFDL0MsT0FBTyxLQUFLLFFBQVEsTUFBTSxzQkFBc0IsQ0FBQztBQUNqRCxPQUFPLEtBQUssd0JBQXdCLE1BQU0sd0NBQXdDLENBQUM7QUFDbkYsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBQ3JELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFFdkQsT0FBTyxFQUNMLEdBQUcsRUFDSCxPQUFPLEVBQ1AsT0FBTyxFQUNQLFFBQVEsRUFDUix3QkFBd0IsRUFDeEIsU0FBUyxFQUNULFVBQVUsRUFDVixXQUFXLEdBQ1osQ0FBQztBQUVGLGNBQWMifQ==
@@ -0,0 +1,29 @@
1
+ export interface ISystemdServiceStartup {
2
+ type: 'oneshot';
3
+ remainAfterExit: boolean;
4
+ timeoutStartSeconds: number;
5
+ }
6
+ /** Complete literal relations. After=local-fs.target is always retained. */
7
+ export interface ISystemdServiceDependencies {
8
+ defaultDependencies: boolean;
9
+ after: readonly string[];
10
+ before: readonly string[];
11
+ requires: readonly string[];
12
+ wants: readonly string[];
13
+ conflicts: readonly string[];
14
+ }
15
+ /** Install metadata only; rendering and file installation do not enable these links. */
16
+ export interface ISystemdServiceInstallTargets {
17
+ wantedBy: readonly string[];
18
+ requiredBy: readonly string[];
19
+ }
20
+ /** Internal validated renderer; no systemd/process/filesystem effects. */
21
+ export declare const renderServiceRelations: (options: {
22
+ startup?: ISystemdServiceStartup;
23
+ dependencies?: ISystemdServiceDependencies;
24
+ install?: ISystemdServiceInstallTargets;
25
+ }, unitName: string, restart: string, fail: () => never) => {
26
+ unit: string[];
27
+ service: string[];
28
+ install: string[];
29
+ };
@@ -0,0 +1,57 @@
1
+ import * as plugins from './smartdaemon.plugins.js';
2
+ /** Internal validated renderer; no systemd/process/filesystem effects. */
3
+ export const renderServiceRelations = (options, unitName, restart, fail) => {
4
+ const requireValue = (value) => { if (!value)
5
+ fail(); };
6
+ const record = (value, keys) => {
7
+ requireValue(value !== null && typeof value === 'object' && !plugins.util.types.isProxy(value)
8
+ && Object.getPrototypeOf(value) === Object.prototype);
9
+ const actual = Reflect.ownKeys(value);
10
+ requireValue(actual.length === keys.length && actual.every(key => typeof key === 'string' && keys.includes(key)
11
+ && Object.getOwnPropertyDescriptor(value, key)?.enumerable && 'value' in Object.getOwnPropertyDescriptor(value, key)));
12
+ };
13
+ const units = (value) => {
14
+ requireValue(Array.isArray(value) && !plugins.util.types.isProxy(value)
15
+ && Object.getPrototypeOf(value) === Array.prototype && value.length <= 32
16
+ && Reflect.ownKeys(value).length === value.length + 1);
17
+ const result = [];
18
+ for (let index = 0; index < value.length; index++) {
19
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
20
+ requireValue(descriptor?.enumerable && 'value' in descriptor);
21
+ const item = descriptor.value;
22
+ requireValue(typeof item === 'string' && item !== unitName && item.length <= 255
23
+ && /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,230}\.(service|target|socket|mount|automount|swap|timer|path|slice|scope|device)$/.test(item));
24
+ result.push(item);
25
+ }
26
+ requireValue(new Set(result).size === result.length);
27
+ return result.sort();
28
+ };
29
+ const line = (key, value) => value.length ? [`${key}=${value.join(' ')}`] : [];
30
+ const result = { unit: ['After=local-fs.target'], service: ['Type=exec'], install: ['WantedBy=multi-user.target'] };
31
+ if (options.startup !== undefined) {
32
+ const value = options.startup;
33
+ record(value, ['type', 'remainAfterExit', 'timeoutStartSeconds']);
34
+ requireValue(value.type === 'oneshot' && typeof value.remainAfterExit === 'boolean' && restart !== 'always'
35
+ && Number.isSafeInteger(value.timeoutStartSeconds) && value.timeoutStartSeconds >= 1 && value.timeoutStartSeconds <= 600);
36
+ result.service = ['Type=oneshot', `RemainAfterExit=${value.remainAfterExit ? 'yes' : 'no'}`, `TimeoutStartSec=${value.timeoutStartSeconds}s`];
37
+ }
38
+ if (options.dependencies !== undefined) {
39
+ const value = options.dependencies;
40
+ record(value, ['defaultDependencies', 'after', 'before', 'requires', 'wants', 'conflicts']);
41
+ requireValue(typeof value.defaultDependencies === 'boolean');
42
+ const after = [...new Set(['local-fs.target', ...units(value.after)])].sort();
43
+ const before = units(value.before), requires = units(value.requires), wants = units(value.wants), conflicts = units(value.conflicts);
44
+ requireValue(before.every(item => !after.includes(item)) && conflicts.every(item => !requires.includes(item) && !wants.includes(item)));
45
+ result.unit = [`DefaultDependencies=${value.defaultDependencies ? 'yes' : 'no'}`, ...line('After', after),
46
+ ...line('Before', before), ...line('Requires', requires), ...line('Wants', wants), ...line('Conflicts', conflicts)];
47
+ }
48
+ if (options.install !== undefined) {
49
+ const value = options.install;
50
+ record(value, ['wantedBy', 'requiredBy']);
51
+ const wanted = units(value.wantedBy), required = units(value.requiredBy);
52
+ requireValue(wanted.length + required.length > 0 && wanted.every(item => !required.includes(item)));
53
+ result.install = [...line('WantedBy', wanted), ...line('RequiredBy', required)];
54
+ }
55
+ return result;
56
+ };
57
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3lzdGVtZC5kZWZpbml0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc3lzdGVtZC5kZWZpbml0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sMEJBQTBCLENBQUM7QUF3QnBELDBFQUEwRTtBQUMxRSxNQUFNLENBQUMsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLE9BSXRDLEVBQUUsUUFBZ0IsRUFBRSxPQUFlLEVBQUUsSUFBaUIsRUFFckQsRUFBRTtJQUNGLE1BQU0sWUFBWSxHQUFHLENBQUMsS0FBYyxFQUFRLEVBQUUsR0FBRyxJQUFJLENBQUMsS0FBSztRQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3ZFLE1BQU0sTUFBTSxHQUFHLENBQUMsS0FBYyxFQUFFLElBQXVCLEVBQVEsRUFBRTtRQUMvRCxZQUFZLENBQUMsS0FBSyxLQUFLLElBQUksSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO2VBQ3pGLE1BQU0sQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLEtBQUssTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3hELE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBZSxDQUFDLENBQUM7UUFDaEQsWUFBWSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEtBQUssSUFBSSxDQUFDLE1BQU0sSUFBSSxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsT0FBTyxHQUFHLEtBQUssUUFBUSxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDO2VBQzFHLE1BQU0sQ0FBQyx3QkFBd0IsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLEVBQUUsVUFBVSxJQUFJLE9BQU8sSUFBSSxNQUFNLENBQUMsd0JBQXdCLENBQUMsS0FBSyxF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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartdaemon",
3
- "version": "2.4.1",
3
+ "version": "2.6.0",
4
4
  "private": false,
5
5
  "description": "Start scripts as long running daemons and manage them.",
6
6
  "main": "dist_ts/index.js",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "homepage": "https://code.foss.global/push.rocks/smartdaemon",
28
28
  "devDependencies": {
29
- "@git.zone/tsbuild": "^4.4.2",
29
+ "@git.zone/tsbuild": "^4.4.3",
30
30
  "@git.zone/tsrun": "^2.0.6",
31
31
  "@git.zone/tstest": "^6.0.0",
32
32
  "@types/node": "^26.4.1"
package/readme.md CHANGED
@@ -191,10 +191,27 @@ list and `needsReload` flag. `reloadConfiguration()` explicitly reloads the mana
191
191
  unit definitions globally and returns a fresh configuration snapshot. Neither
192
192
  operation changes enablement or masks.
193
193
 
194
+ `enable()` persistently enables the existing unit definition and returns a fresh
195
+ state snapshot. It does not rewrite the definition, start the unit, unmask it or
196
+ force conflicting links. Run it inside the owning installer's cross-process
197
+ installation scope, after any required offline commissioning. A failed or timed
198
+ out command can leave partial enablement; inspect and retry the exact operation
199
+ without assuming cancellation or undoing the application's retained protection.
200
+
201
+ `inspectRelationships()` returns immutable sorted arrays for `before`, `after`,
202
+ `requires`, `wants`, `conflicts`, `requiredBy` and `wantedBy` from the loaded manager
203
+ graph. Literal systemd hex escapes are preserved. The bounded snapshot rejects
204
+ missing fields, foreign identities, duplicate relationships and malformed names.
205
+ `unitFileState === 'enabled'` proves only that some enablement links exist. For a
206
+ boot guard, verify that its `requiredBy` and `before` both include every required
207
+ target and the actual network provider; inspect the provider's `requires` and
208
+ `after` as well. Recheck the exact file digest, selected fragment, absence of
209
+ drop-ins and `needsReload === false` before permitting dependent activation.
210
+
194
211
  ### Installing a direct systemd service definition
195
212
 
196
213
  `SystemdServiceDefinition` encodes a literal executable and argument list, with
197
- explicit user, group, restart policy, kill mode and stop timeout. It uses `Type=exec`,
214
+ explicit user, group, restart policy, kill mode and stop timeout. It defaults to `Type=exec`,
198
215
  journal output and null stdin. It introduces no shell; percent specifiers and
199
216
  argument environment expansion are escaped. Paths must be absolute and normalized;
200
217
  control characters are rejected. Descriptions and working directories cannot have
@@ -223,6 +240,71 @@ const installed = await file.install(definition, previous?.sha256 ?? null);
223
240
  console.log(installed.changed, installed.masked, installed.record.sha256);
224
241
  ```
225
242
 
243
+ For a boot prerequisite that must finish before dependent services start, supply
244
+ `startup: { type: 'oneshot', remainAfterExit: true, timeoutStartSeconds: 120 }`.
245
+ Systemd waits for successful process exit; `RemainAfterExit` keeps the completed
246
+ unit active. The start timeout is an integer from 1 to 600 seconds. Oneshot
247
+ services reject `restart: 'always'`; the default exec definition remains unchanged.
248
+
249
+ Optional `dependencies` describes the complete literal runtime relationships:
250
+
251
+ ```typescript
252
+ dependencies: {
253
+ defaultDependencies: false,
254
+ after: [],
255
+ before: ['network-pre.target', 'shutdown.target', 'systemd-networkd.service'],
256
+ requires: [],
257
+ wants: ['network-pre.target'],
258
+ conflicts: ['shutdown.target'],
259
+ },
260
+ install: { wantedBy: [], requiredBy: ['network-pre.target', 'systemd-networkd.service'] },
261
+ ```
262
+
263
+ `After=local-fs.target` is always retained. Disable default dependencies only for
264
+ an intentionally early boot or late shutdown service. Every list is bounded to
265
+ 32 distinct literal unit names, sorted deterministically; templates, specifiers,
266
+ self references and contradictory direct relationships are rejected. These local
267
+ checks do not prove the complete host dependency graph is free of cycles.
268
+
269
+ Consumers must declare both `requires: ['the-bootstrap.service']` and
270
+ `after: ['the-bootstrap.service']` for a failed bootstrap to prevent their start.
271
+ Ordering alone does not propagate failure. A prerequisite must actually fail its
272
+ start on failure; a skipped condition is not equivalent. Reverse stop ordering
273
+ keeps the prerequisite active until its ordered consumers stop.
274
+ The example assumes the host uses `systemd-networkd.service`: requiring the guard
275
+ only from `network-pre.target` does not block a network manager that merely orders
276
+ itself after that target. Select and verify the actual host network owner's edges.
277
+
278
+ `install` supplies only `WantedBy`/`RequiredBy` metadata, with at least one target.
279
+ The privileged installer must separately enable and verify the intended links;
280
+ rendering or installing the file does not activate them. A successful oneshot
281
+ proves only that its executable returned success, so that executable must finish
282
+ and verify all required work before exiting. See the upstream
283
+ [service readiness](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html)
284
+ and [dependency semantics](https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html).
285
+
286
+ `test/native/qualify-systemd.py` exercises the generated units under Ubuntu systemd
287
+ PID 1 across provisioning, successful cold boot and failed cold boot. It checks
288
+ actual networkd failure coupling and reverse shutdown order in a disposable QEMU
289
+ overlay without an external network backend. The fixture requires QEMU/KVM,
290
+ OVMF, xorriso, Python PyYAML and global `tsx`. Supply the unmodified
291
+ [Ubuntu Noble 20260826 image](https://cloud-images.ubuntu.com/noble/20260826/noble-server-cloudimg-amd64.img)
292
+ with SHA-256 `d0fe84bb5f80853425fa6be28e2c106f30104c3cfe8611933f2e65c9b63f0e30`:
293
+
294
+ ```sh
295
+ python3 test/native/qualify-systemd.py --image /path/to/noble-server-cloudimg-amd64.img \
296
+ --work-root /tmp/smartdaemon-systemd-qualification-local
297
+ ```
298
+
299
+ To exercise the public enablement and relationship APIs in the same guest, bundle
300
+ `test/native/activate-systemd.ts` as a self-contained Node ESM file and also supply
301
+ `--node /path/to/node --activation-bundle /path/to/activate.mjs`. Any library absent
302
+ from the minimal image can be supplied explicitly with repeatable
303
+ `--node-library /path/to/libname.so.N`. These hashed test inputs travel on a
304
+ read-only ISO. The API fixture checks enablement without implicit start, oneshot
305
+ readiness, unchanged definition/configuration, detection of a missing provider
306
+ link despite enabled state, exact enable retry and preservation of a mask.
307
+
226
308
  Installation requires Linux, root and a quiescent exact unit with no process,
227
309
  cgroup or job. It rejects effective drop-ins and shadowing fragments. Definitions
228
310
  default to `/usr/local/lib/systemd/system`, below administrator configuration in
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdaemon',
6
- version: '2.4.1',
6
+ version: '2.6.0',
7
7
  description: 'Start scripts as long running daemons and manage them.'
8
8
  }
@@ -1,4 +1,7 @@
1
1
  import * as plugins from './smartdaemon.plugins.js';
2
+ import { renderServiceRelations, type ISystemdServiceStartup, type ISystemdServiceDependencies,
3
+ type ISystemdServiceInstallTargets } from './systemd.definition.js';
4
+ export type { ISystemdServiceStartup, ISystemdServiceDependencies, ISystemdServiceInstallTargets } from './systemd.definition.js';
2
5
 
3
6
  export interface ISystemdServiceDefinitionOptions {
4
7
  unitName: string;
@@ -11,6 +14,9 @@ export interface ISystemdServiceDefinitionOptions {
11
14
  restart: 'no' | 'on-failure' | 'always';
12
15
  killMode: 'mixed' | 'control-group';
13
16
  timeoutStopSeconds: number;
17
+ startup?: ISystemdServiceStartup;
18
+ dependencies?: ISystemdServiceDependencies;
19
+ install?: ISystemdServiceInstallTargets;
14
20
  }
15
21
 
16
22
  export class SystemdServiceDefinitionError extends Error {
@@ -49,8 +55,9 @@ export class SystemdServiceDefinition {
49
55
  'timeoutStopSeconds',
50
56
  ];
51
57
  requireValue(
52
- options && Object.getPrototypeOf(options) === Object.prototype &&
53
- Reflect.ownKeys(options).length === keys.length && keys.every((key) => {
58
+ options && !plugins.util.types.isProxy(options) && Object.getPrototypeOf(options) === Object.prototype &&
59
+ Reflect.ownKeys(options).every(key => typeof key === 'string' && [...keys, 'startup', 'dependencies', 'install'].includes(key)
60
+ && Object.getOwnPropertyDescriptor(options, key)?.enumerable && 'value' in Object.getOwnPropertyDescriptor(options, key)!) && keys.every((key) => {
54
61
  const descriptor = Object.getOwnPropertyDescriptor(options, key);
55
62
  return descriptor && 'value' in descriptor;
56
63
  }),
@@ -71,7 +78,7 @@ export class SystemdServiceDefinition {
71
78
  !options.workingDirectory.includes('\\'),
72
79
  );
73
80
  requireValue(
74
- Array.isArray(options.args) && Object.getPrototypeOf(options.args) === Array.prototype &&
81
+ Array.isArray(options.args) && !plugins.util.types.isProxy(options.args) && Object.getPrototypeOf(options.args) === Array.prototype &&
75
82
  options.args.length <= 128 &&
76
83
  Reflect.ownKeys(options.args).length === options.args.length + 1 &&
77
84
  Array.from(
@@ -97,14 +104,15 @@ export class SystemdServiceDefinition {
97
104
  .replaceAll('\\', '\\\\').replaceAll('"', '\\"').replaceAll('%', '%%')
98
105
  .replaceAll('$', () => command ? '$$' : '$') +
99
106
  '"';
107
+ const relations = renderServiceRelations(options, options.unitName, options.restart, () => { throw new SystemdServiceDefinitionError(); });
100
108
  this.unitName = options.unitName;
101
109
  this.content = [
102
110
  '[Unit]',
103
111
  `Description=${options.description.replaceAll('%', '%%')}`,
104
- 'After=local-fs.target',
112
+ ...relations.unit,
105
113
  '',
106
114
  '[Service]',
107
- 'Type=exec',
115
+ ...relations.service,
108
116
  `User=${options.user}`,
109
117
  `Group=${options.group}`,
110
118
  `ExecStart=${
@@ -124,7 +132,7 @@ export class SystemdServiceDefinition {
124
132
  'StandardError=journal',
125
133
  '',
126
134
  '[Install]',
127
- 'WantedBy=multi-user.target',
135
+ ...relations.install,
128
136
  '',
129
137
  ].join('\n');
130
138
  requireValue(Buffer.byteLength(this.content) <= 16384);
@@ -30,6 +30,17 @@ export interface ISystemdUnitConfigurationState {
30
30
  readonly needsReload: boolean;
31
31
  }
32
32
 
33
+ export interface ISystemdUnitRelationships {
34
+ readonly unitName: string;
35
+ readonly before: readonly string[];
36
+ readonly after: readonly string[];
37
+ readonly requires: readonly string[];
38
+ readonly wants: readonly string[];
39
+ readonly conflicts: readonly string[];
40
+ readonly requiredBy: readonly string[];
41
+ readonly wantedBy: readonly string[];
42
+ }
43
+
33
44
  export class SystemdUnitError extends Error {
34
45
  constructor(public readonly code: 'invalid_options' | 'command_failed' | 'invalid_state') {
35
46
  super(`Systemd unit operation failed: ${code}.`);
@@ -50,7 +61,7 @@ const properties = [
50
61
  'Job',
51
62
  ] as const;
52
63
 
53
- /** Controls an existing system unit without creating files, changing enablement or escalating privileges. */
64
+ /** Controls an existing system unit without rewriting its definition or escalating privileges. */
54
65
  export class SystemdUnit {
55
66
  private readonly unitName: string;
56
67
  private readonly executable: string;
@@ -113,6 +124,39 @@ export class SystemdUnit {
113
124
  return this.inspect();
114
125
  }
115
126
 
127
+ /** Persistently enables the existing definition, then inspects. Never starts, unmasks or forces links.
128
+ * Owning installers must separately verify exact file/configuration and required relationships. */
129
+ public async enable(): Promise<ISystemdUnitState> {
130
+ await this.command(['enable', this.unitName]);
131
+ return this.inspect();
132
+ }
133
+
134
+ /** Loaded manager graph, including reverse edges. Enablement alone does not prove every required link. */
135
+ public async inspectRelationships(): Promise<ISystemdUnitRelationships> {
136
+ const keys = ['Id', 'Before', 'After', 'Requires', 'Wants', 'Conflicts', 'RequiredBy', 'WantedBy'];
137
+ const stdout = await this.command(['show', this.unitName, `--property=${keys.join(',')}`, '--all']);
138
+ const fields = new Map<string, string>();
139
+ for (const line of (stdout.endsWith('\n') ? stdout.slice(0, -1) : stdout).split('\n')) {
140
+ const separator = line.indexOf('='), key = line.slice(0, separator);
141
+ if (separator < 1 || !keys.includes(key) || fields.has(key)) throw new SystemdUnitError('invalid_state');
142
+ fields.set(key, line.slice(separator + 1));
143
+ }
144
+ if (fields.size !== keys.length || fields.get('Id') !== this.unitName) throw new SystemdUnitError('invalid_state');
145
+ const units = (key: string): readonly string[] => {
146
+ const text = fields.get(key)!;
147
+ const values = text === '' ? [] : text.split(' ');
148
+ // Unit names are ASCII; retain systemd's literal hex escapes without decoding
149
+ // them into separators. Output and every individual name remain bounded.
150
+ if (values.some(value => value.length > 255 ||
151
+ !/^(?:[a-zA-Z0-9_.:@-]|\\x[0-9a-fA-F]{2})+\.(?:service|socket|device|mount|automount|swap|target|path|timer|slice|scope)$/.test(value)) ||
152
+ new Set(values).size !== values.length) throw new SystemdUnitError('invalid_state');
153
+ return Object.freeze(values.sort());
154
+ };
155
+ return Object.freeze({ unitName: this.unitName, before: units('Before'), after: units('After'),
156
+ requires: units('Requires'), wants: units('Wants'), conflicts: units('Conflicts'),
157
+ requiredBy: units('RequiredBy'), wantedBy: units('WantedBy') });
158
+ }
159
+
116
160
  /** Await the systemd job, then inspect. The snapshot alone is not an application data-drain proof. */
117
161
  public async stop(): Promise<ISystemdUnitState> {
118
162
  await this.command(['stop', this.unitName]);
@@ -4,9 +4,10 @@ import * as childProcess from 'node:child_process';
4
4
  import * as fs from 'node:fs/promises';
5
5
  import { constants as fsConstants } from 'node:fs';
6
6
  import * as crypto from 'node:crypto';
7
+ import * as util from 'node:util';
7
8
  export type { Stats } from 'node:fs';
8
9
 
9
- export { path, childProcess, fs, fsConstants, crypto };
10
+ export { path, childProcess, fs, fsConstants, crypto, util };
10
11
 
11
12
  // @pushrocks scope
12
13
  import * as lik from '@push.rocks/lik';
@@ -0,0 +1,84 @@
1
+ import * as plugins from './smartdaemon.plugins.js';
2
+
3
+ export interface ISystemdServiceStartup {
4
+ type: 'oneshot';
5
+ remainAfterExit: boolean;
6
+ timeoutStartSeconds: number;
7
+ }
8
+
9
+ /** Complete literal relations. After=local-fs.target is always retained. */
10
+ export interface ISystemdServiceDependencies {
11
+ defaultDependencies: boolean;
12
+ after: readonly string[];
13
+ before: readonly string[];
14
+ requires: readonly string[];
15
+ wants: readonly string[];
16
+ conflicts: readonly string[];
17
+ }
18
+
19
+ /** Install metadata only; rendering and file installation do not enable these links. */
20
+ export interface ISystemdServiceInstallTargets {
21
+ wantedBy: readonly string[];
22
+ requiredBy: readonly string[];
23
+ }
24
+
25
+ /** Internal validated renderer; no systemd/process/filesystem effects. */
26
+ export const renderServiceRelations = (options: {
27
+ startup?: ISystemdServiceStartup;
28
+ dependencies?: ISystemdServiceDependencies;
29
+ install?: ISystemdServiceInstallTargets;
30
+ }, unitName: string, restart: string, fail: () => never): {
31
+ unit: string[]; service: string[]; install: string[];
32
+ } => {
33
+ const requireValue = (value: unknown): void => { if (!value) fail(); };
34
+ const record = (value: unknown, keys: readonly string[]): void => {
35
+ requireValue(value !== null && typeof value === 'object' && !plugins.util.types.isProxy(value)
36
+ && Object.getPrototypeOf(value) === Object.prototype);
37
+ const actual = Reflect.ownKeys(value as object);
38
+ requireValue(actual.length === keys.length && actual.every(key => typeof key === 'string' && keys.includes(key)
39
+ && Object.getOwnPropertyDescriptor(value, key)?.enumerable && 'value' in Object.getOwnPropertyDescriptor(value, key)!));
40
+ };
41
+ const units = (value: readonly string[]): string[] => {
42
+ requireValue(Array.isArray(value) && !plugins.util.types.isProxy(value)
43
+ && Object.getPrototypeOf(value) === Array.prototype && value.length <= 32
44
+ && Reflect.ownKeys(value).length === value.length + 1);
45
+ const result: string[] = [];
46
+ for (let index = 0; index < value.length; index++) {
47
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
48
+ requireValue(descriptor?.enumerable && 'value' in descriptor);
49
+ const item = descriptor!.value;
50
+ requireValue(typeof item === 'string' && item !== unitName && item.length <= 255
51
+ && /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,230}\.(service|target|socket|mount|automount|swap|timer|path|slice|scope|device)$/.test(item));
52
+ result.push(item);
53
+ }
54
+ requireValue(new Set(result).size === result.length);
55
+ return result.sort();
56
+ };
57
+ const line = (key: string, value: readonly string[]): string[] => value.length ? [`${key}=${value.join(' ')}`] : [];
58
+ const result = { unit: ['After=local-fs.target'], service: ['Type=exec'], install: ['WantedBy=multi-user.target'] };
59
+ if (options.startup !== undefined) {
60
+ const value = options.startup;
61
+ record(value, ['type', 'remainAfterExit', 'timeoutStartSeconds']);
62
+ requireValue(value.type === 'oneshot' && typeof value.remainAfterExit === 'boolean' && restart !== 'always'
63
+ && Number.isSafeInteger(value.timeoutStartSeconds) && value.timeoutStartSeconds >= 1 && value.timeoutStartSeconds <= 600);
64
+ result.service = ['Type=oneshot', `RemainAfterExit=${value.remainAfterExit ? 'yes' : 'no'}`, `TimeoutStartSec=${value.timeoutStartSeconds}s`];
65
+ }
66
+ if (options.dependencies !== undefined) {
67
+ const value = options.dependencies;
68
+ record(value, ['defaultDependencies', 'after', 'before', 'requires', 'wants', 'conflicts']);
69
+ requireValue(typeof value.defaultDependencies === 'boolean');
70
+ const after = [...new Set(['local-fs.target', ...units(value.after)])].sort();
71
+ const before = units(value.before), requires = units(value.requires), wants = units(value.wants), conflicts = units(value.conflicts);
72
+ requireValue(before.every(item => !after.includes(item)) && conflicts.every(item => !requires.includes(item) && !wants.includes(item)));
73
+ result.unit = [`DefaultDependencies=${value.defaultDependencies ? 'yes' : 'no'}`, ...line('After', after),
74
+ ...line('Before', before), ...line('Requires', requires), ...line('Wants', wants), ...line('Conflicts', conflicts)];
75
+ }
76
+ if (options.install !== undefined) {
77
+ const value = options.install;
78
+ record(value, ['wantedBy', 'requiredBy']);
79
+ const wanted = units(value.wantedBy), required = units(value.requiredBy);
80
+ requireValue(wanted.length + required.length > 0 && wanted.every(item => !required.includes(item)));
81
+ result.install = [...line('WantedBy', wanted), ...line('RequiredBy', required)];
82
+ }
83
+ return result;
84
+ };