source: mainline/uspace/srv/sysman/units/unit_cfg.c@ 8ae8262

Last change on this file since 8ae8262 was 8ae8262, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

sysman: Support for anonymous services

  • Daemons that only call task_retval and don't exit are recorded as anonymous services.
  • Split unit run-state and repository state.
  • Partial support for removal of units from repository (needs refcounting yet).
  • Property mode set to 100644
File size: 6.3 KB
Line 
1/*
2 * Copyright (c) 2015 Michal Koutny
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <adt/list.h>
30#include <assert.h>
31#include <conf/configuration.h>
32#include <conf/ini.h>
33#include <conf/text_parse.h>
34#include <dirent.h>
35#include <errno.h>
36#include <stdlib.h>
37#include <str.h>
38#include <sysman/unit.h>
39
40#include "repo.h"
41#include "log.h"
42#include "unit.h"
43#include "util.h"
44
45static const char *section_name = "Configuration";
46
47static config_item_t unit_configuration[] = {
48 {"Path", &config_parse_string, offsetof(unit_cfg_t, path), NULL},
49 CONFIGURATION_ITEM_SENTINEL
50};
51
52/**
53 * TODO refactor path handling and rename to 'load from file'
54 *
55 * @param[out] unit_ptr Unit loaded from the file. Undefined when function fails.
56 */
57static int cfg_parse_file(const char *dirname, const char *filename,
58 unit_t **unit_ptr)
59{
60 int rc = EOK;
61 unit_t *new_unit = NULL;
62 char *fn = NULL;
63 ini_configuration_t ini_conf;
64 text_parse_t text_parse;
65
66 ini_configuration_init(&ini_conf);
67 text_parse_init(&text_parse);
68
69 const char *last_sep = str_rchr(filename, UNIT_NAME_SEPARATOR);
70 if (last_sep == NULL) {
71 rc = EINVAL;
72 goto finish;
73 }
74
75 const char *unit_name = filename;
76 const char *unit_type_name = last_sep + 1;
77
78 unit_type_t unit_type = unit_type_name_to_type(unit_type_name);
79 if (unit_type == UNIT_TYPE_INVALID) {
80 rc = EINVAL;
81 goto finish;
82 }
83
84 unit_t *u = repo_find_unit_by_name(unit_name);
85 if (u != NULL) {
86 // TODO allow updating configuration of existing unit
87 rc = EEXISTS;
88 goto finish;
89 } else {
90 new_unit = u = unit_create(unit_type);
91 new_unit->name = str_dup(unit_name);
92 if (new_unit->name == NULL) {
93 rc = ENOMEM;
94 goto finish;
95 }
96 }
97 if (u == NULL) {
98 rc = ENOMEM;
99 goto finish;
100 }
101 assert(u->type == unit_type);
102
103 fn = util_compose_path(dirname, filename);
104 if (fn == NULL) {
105 rc = ENOMEM;
106 goto finish;
107 }
108
109 /* Parse INI file to ini_conf structure */
110 rc = ini_parse_file(fn, &ini_conf, &text_parse);
111 switch (rc) {
112 case EOK:
113 /* This is fine */
114 break;
115 case EINVAL:
116 goto dump_parse;
117 break;
118 default:
119 sysman_log(LVL_WARN,
120 "Cannot parse '%s' (%i).", fn, rc);
121 goto finish;
122 }
123
124 /* Parse ini structure */
125 rc = unit_load(u, &ini_conf, &text_parse);
126 *unit_ptr = u;
127
128 /*
129 * Here we just continue undisturbed by errors, they'll be returned in
130 * 'finish' block and potential parse errors (or none) will be logged
131 * in 'dump_parse' block.
132 */
133
134dump_parse:
135 list_foreach(text_parse.errors, link, text_parse_error_t, err) {
136 sysman_log(LVL_WARN,
137 "Error (%i) when parsing '%s' on line %i.",
138 err->parse_errno, fn, err->lineno);
139 }
140
141finish:
142 free(fn);
143 ini_configuration_deinit(&ini_conf);
144 text_parse_deinit(&text_parse);
145 if (rc != EOK) {
146 unit_destroy(&new_unit);
147 }
148
149 return rc;
150}
151
152static int cfg_load_configuration(const char *path)
153{
154 DIR *dir;
155 struct dirent *de;
156
157 dir = opendir(path);
158 if (dir == NULL) {
159 sysman_log(LVL_ERROR,
160 "Cannot open configuration directory '%s'", path);
161 return EIO;
162 }
163
164 repo_begin_update();
165
166 while ((de = readdir(dir))) {
167 unit_t *unit = NULL;
168 int rc = cfg_parse_file(path, de->d_name, &unit);
169 if (rc != EOK) {
170 sysman_log(LVL_WARN, "Cannot load unit from file %s/%s",
171 path, de->d_name);
172 /*
173 * Ignore error for now, we'll fail only when we're
174 * unable to resolve dependency names.
175 */
176 continue;
177 }
178
179 assert(unit->repo_state == REPO_EMBRYO);
180 repo_add_unit(unit);
181 }
182 closedir(dir);
183
184 int rc = repo_resolve_references();
185 if (rc != EOK) {
186 repo_rollback();
187 return rc;
188 }
189
190 repo_commit();
191 return EOK;
192}
193
194static void unit_cfg_init(unit_t *unit)
195{
196 unit_cfg_t *u_cfg = CAST_CFG(unit);
197 assert(u_cfg);
198}
199
200static void unit_cfg_destroy(unit_t *unit)
201{
202 unit_cfg_t *u_cfg = CAST_CFG(unit);
203 assert(u_cfg);
204
205 free(u_cfg->path);
206}
207
208static int unit_cfg_load(unit_t *unit, ini_configuration_t *ini_conf,
209 text_parse_t *text_parse)
210{
211 unit_cfg_t *u_cfg = CAST_CFG(unit);
212 assert(u_cfg);
213
214 ini_section_t *section = ini_get_section(ini_conf, section_name);
215 if (section == NULL) {
216 sysman_log(LVL_ERROR,
217 "Expected section '%s' in configuration of unit '%s'",
218 section_name, unit_name(unit));
219 return ENOENT;
220 }
221
222 return config_load_ini_section(unit_configuration, section, u_cfg,
223 text_parse);
224}
225
226static int unit_cfg_start(unit_t *unit)
227{
228 unit_cfg_t *u_cfg = CAST_CFG(unit);
229 assert(u_cfg);
230
231 int rc = cfg_load_configuration(u_cfg->path);
232
233 if (rc == EOK) {
234 unit->state = STATE_STARTED;
235 } else {
236 unit->state = STATE_FAILED;
237 }
238
239 return rc;
240}
241
242static int unit_cfg_stop(unit_t *unit)
243{
244 unit_cfg_t *u_cfg = CAST_CFG(unit);
245 assert(u_cfg);
246
247 /*
248 * It makes no sense to stop configuration (i.e. unload it), however,
249 * lets virtually stop it not to make obstructions for potential
250 * restart = reload of configuration.
251 */
252 unit->state = STATE_STOPPED;
253 return EOK;
254}
255
256static void unit_cfg_exposee_created(unit_t *unit)
257{
258 /* Configuration has no exposees. */
259 assert(false);
260}
261
262static void unit_cfg_fail(unit_t *unit)
263{
264 /* Configuration cannot async fail. */
265 assert(false);
266}
267
268DEFINE_UNIT_VMT(unit_cfg)
269
Note: See TracBrowser for help on using the repository browser.