Merge lp:~xnox/upstart/overrides into lp:upstart
- overrides
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 1423 |
Proposed branch: | lp:~xnox/upstart/overrides |
Merge into: | lp:upstart |
Diff against target: |
814 lines (+484/-160) 4 files modified
ChangeLog (+8/-0) init/conf.c (+212/-157) init/tests/test_conf.c (+119/-1) init/tests/test_conf_static.c (+145/-2) |
To merge this branch: | bzr merge lp:~xnox/upstart/overrides |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Hunt | Approve | ||
Review via email: mp+141914@code.launchpad.net |
Commit message
Description of the change
This branch implements override files from any directory as specified here [1].
Instead of checking for a known path to .override file, a helper function `conf_get_
Next, I refactored another helper function conf_load_
For the handlers we now have the following logic:
- if a .conf file is created/modified: simply call conf_load_
- if a .override file is created/
- if a .conf file is deleted: the behaviour is the same, unref the file from the ConfSource.
For system init, there shouldn't be much performance impact since there is only one directory to do lookups in.
For session init, most jobs will be in the lowest priority config_source. Thus resulting in lstat call in each config_source for each job to check for an .override file. But in practice, those higher priority config_sources may not even exist and lstat calls on non-existing directories is cheap. Potentially this can be micro-optimised by storing hashes of all found .override files while walking the config_sources and then instead of lstat check do a hash lookup...
- 1424. By Dimitri John Ledkov
-
Review comments
Dimitri John Ledkov (xnox) wrote : | # |
Addressed all comments.
Checking return status of nih_sprintf with NIH_MUST.
Converted conf_source_
- 1425. By Dimitri John Ledkov
-
;
James Hunt (jamesodhunt) wrote : | # |
Thanks. LGTM - merged!
Preview Diff
1 | === modified file 'ChangeLog' |
2 | --- ChangeLog 2012-12-18 09:02:24 +0000 |
3 | +++ ChangeLog 2013-01-08 16:18:21 +0000 |
4 | @@ -1,3 +1,11 @@ |
5 | +2013-01-04 Dmitrijs Ledkovs <xnox@ubuntu.com> |
6 | + |
7 | + * init/conf.c: add ability to apply override files from higher |
8 | + priority configuration sources. |
9 | + * init/tests/test_conf.c: test that multiple override files are |
10 | + correctly applied and removed. |
11 | + * init/tests/test_conf_static.c: test override file detection. |
12 | + |
13 | 2012-12-17 James Hunt <james.hunt@ubuntu.com> |
14 | |
15 | * init/man/init.5: Document that User Jobs are not supported |
16 | |
17 | === modified file 'init/conf.c' |
18 | --- init/conf.c 2012-12-19 12:46:46 +0000 |
19 | +++ init/conf.c 2013-01-08 16:18:21 +0000 |
20 | @@ -2,7 +2,7 @@ |
21 | * |
22 | * conf.c - configuration management |
23 | * |
24 | - * Copyright © 2009,2010,2011 Canonical Ltd. |
25 | + * Copyright © 2009,2010,2011,2012,2013 Canonical Ltd. |
26 | * Author: Scott James Remnant <scott@netsplit.com>. |
27 | * |
28 | * This program is free software; you can redistribute it and/or modify |
29 | @@ -86,6 +86,14 @@ |
30 | static inline char *toggle_conf_name (const void *parent, const char *path) |
31 | __attribute__ ((warn_unused_result, malloc)); |
32 | |
33 | +static inline char * conf_to_job_name (const char *source_path, |
34 | + const char *conf_path) |
35 | + __attribute__ ((malloc, warn_unused_result)); |
36 | + |
37 | +static char * conf_get_best_override (const char *name, |
38 | + const ConfSource *last_source) |
39 | + __attribute__ ((malloc, warn_unused_result)); |
40 | + |
41 | /** |
42 | * conf_sources: |
43 | * |
44 | @@ -110,6 +118,8 @@ |
45 | static inline int |
46 | is_conf_file_std (const char *path) |
47 | { |
48 | + nih_assert (path != NULL); |
49 | + |
50 | char *ptr = strrchr (path, '.'); |
51 | |
52 | if (ptr && IS_CONF_EXT_STD (ptr)) |
53 | @@ -132,6 +142,8 @@ |
54 | static inline int |
55 | is_conf_file_override (const char *path) |
56 | { |
57 | + nih_assert (path != NULL); |
58 | + |
59 | char *ptr = strrchr (path, '.'); |
60 | |
61 | if (ptr && IS_CONF_EXT_OVERRIDE (ptr)) |
62 | @@ -154,6 +166,8 @@ |
63 | static inline int |
64 | is_conf_file (const char *path) |
65 | { |
66 | + nih_assert (path != NULL); |
67 | + |
68 | char *ptr = strrchr (path, '.'); |
69 | |
70 | if (ptr && (ptr > path) && (ptr[-1] != '/') && IS_CONF_EXT (ptr)) |
71 | @@ -163,6 +177,99 @@ |
72 | } |
73 | |
74 | /** |
75 | + * conf_to_job_name: |
76 | + * @source_path: path to ConfSource |
77 | + * @conf_path: path to configuration file |
78 | + * |
79 | + * Constructs the job name for a given @conf_path. Removes |
80 | + * @source_path directory name from the front of @conf_path and |
81 | + * extension from the end. |
82 | + * |
83 | + * Returns: newly-allocated name. |
84 | + * |
85 | + **/ |
86 | +static inline char * |
87 | +conf_to_job_name (const char * source_path, const char * conf_path) |
88 | +{ |
89 | + const char *start, *end; |
90 | + char *name = NULL; |
91 | + int source_len; |
92 | + |
93 | + nih_assert (source_path != NULL); |
94 | + nih_assert (conf_path != NULL); |
95 | + |
96 | + start = conf_path; |
97 | + source_len = strlen (source_path); |
98 | + |
99 | + if (! strncmp (start, source_path, source_len)) |
100 | + start += source_len; |
101 | + |
102 | + while (*start == '/') |
103 | + start++; |
104 | + |
105 | + end = strrchr (start, '.'); |
106 | + if (end && IS_CONF_EXT (end)) |
107 | + name = NIH_MUST (nih_strndup (NULL, start, end - start)); |
108 | + else |
109 | + name = NIH_MUST (nih_strdup (NULL, start)); |
110 | + |
111 | + return name; |
112 | +} |
113 | + |
114 | + |
115 | +/** |
116 | + * conf_get_best_override: |
117 | + * @conf_file: conf_file object |
118 | + * |
119 | + * Given a @conf_file iterate over all config sources & find the |
120 | + * first applicable override file. It will not look for override files |
121 | + * beyond the @conf_file's ConfSource. |
122 | + * |
123 | + * Returns: newly allocated path to override file or NULL. |
124 | + **/ |
125 | +static char * |
126 | +conf_get_best_override (const char *name, const ConfSource *last_source) |
127 | +{ |
128 | + char *try_path=NULL; |
129 | + struct stat statbuf; |
130 | + |
131 | + nih_assert (name != NULL); |
132 | + nih_assert (last_source != NULL); |
133 | + |
134 | + NIH_LIST_FOREACH (conf_sources, iter) { |
135 | + |
136 | + ConfSource *source = (ConfSource *)iter; |
137 | + |
138 | + /* Look at directories only */ |
139 | + if (source->type == CONF_FILE) |
140 | + continue; |
141 | + |
142 | + /* Reclaim memory */ |
143 | + if (try_path) |
144 | + nih_free (try_path); |
145 | + |
146 | + /* construct path */ |
147 | + try_path = NIH_MUST (nih_sprintf (NULL, "%s/%s%s", source->path, name, CONF_EXT_OVERRIDE)); |
148 | + |
149 | + /* Found it! */ |
150 | + if (lstat (try_path, &statbuf) == 0 && S_ISREG (statbuf.st_mode)) |
151 | + return try_path; |
152 | + |
153 | + /* Warning, you have reached the end of the conveyor! |
154 | + * Ignore overrides beyond .conf itself. |
155 | + */ |
156 | + if (source == last_source) |
157 | + break; |
158 | + } |
159 | + |
160 | + if (try_path) |
161 | + nih_free (try_path); |
162 | + |
163 | + return NULL; |
164 | +} |
165 | + |
166 | + |
167 | +/** |
168 | * Convert a configuration file name to an override file name and vice |
169 | * versa. |
170 | * |
171 | @@ -511,7 +618,7 @@ |
172 | |
173 | override_path = toggle_conf_name (NULL, source->path); |
174 | |
175 | - if (stat (override_path, &statbuf) != 0) |
176 | + if (lstat (override_path, &statbuf) != 0) |
177 | return 0; |
178 | |
179 | nih_debug ("Updating configuration for %s from %s", |
180 | @@ -670,6 +777,66 @@ |
181 | } |
182 | |
183 | /** |
184 | + * conf_load_path_with_override: |
185 | + * @source: configuration source |
186 | + * @conf_path: path to config file |
187 | + * |
188 | + * Loads given @conf_path as a config file in a given @source. Then it |
189 | + * finds an override file. If an override file is found it applies it |
190 | + * as well. |
191 | + **/ |
192 | +static void |
193 | +conf_load_path_with_override (ConfSource *source, |
194 | + const char *conf_path) |
195 | +{ |
196 | + int ret = 0; |
197 | + const char *error_path = NULL; |
198 | + char *override_path = NULL; |
199 | + nih_local char *job_name = NULL; |
200 | + |
201 | + nih_assert (source != NULL); |
202 | + nih_assert (conf_path != NULL); |
203 | + |
204 | + /* reload conf file */ |
205 | + nih_debug ("Loading configuration file %s", conf_path); |
206 | + ret = conf_reload_path (source, conf_path, NULL); |
207 | + if (ret < 0) { |
208 | + error_path = conf_path; |
209 | + goto error; |
210 | + } |
211 | + |
212 | + job_name = conf_to_job_name (source->path, conf_path); |
213 | + override_path = conf_get_best_override (job_name, source); |
214 | + if (! override_path) |
215 | + return; |
216 | + |
217 | + /* overlay override settings */ |
218 | + nih_debug ("Loading override file %s for %s", conf_path, override_path); |
219 | + ret = conf_reload_path (source, conf_path, override_path); |
220 | + if (ret < 0) { |
221 | + error_path = override_path; |
222 | + goto error; |
223 | + } |
224 | + nih_free (override_path); |
225 | + return; |
226 | + |
227 | +error: |
228 | + { |
229 | + NihError *err; |
230 | + |
231 | + err = nih_error_get (); |
232 | + nih_error ("%s: %s: %s", error_path, |
233 | + _("Error while loading configuration file"), |
234 | + err->message); |
235 | + nih_free (err); |
236 | + if (override_path) |
237 | + nih_free (override_path); |
238 | + return; |
239 | + } |
240 | +} |
241 | + |
242 | + |
243 | +/** |
244 | * conf_create_modify_handler: |
245 | * @source: configuration source, |
246 | * @watch: NihWatch for source, |
247 | @@ -693,85 +860,45 @@ |
248 | struct stat *statbuf) |
249 | { |
250 | ConfFile *file = NULL; |
251 | - const char *error_path = path; |
252 | - nih_local char *new_path = NULL; |
253 | - int ret; |
254 | + char *config_path = NULL; |
255 | + nih_local char *job_name = NULL; |
256 | |
257 | nih_assert (source != NULL); |
258 | nih_assert (watch != NULL); |
259 | nih_assert (path != NULL); |
260 | - nih_assert (statbuf != NULL); |
261 | |
262 | /* note that symbolic links are ignored */ |
263 | - if (! S_ISREG (statbuf->st_mode)) |
264 | - return; |
265 | - |
266 | - new_path = toggle_conf_name (NULL, path); |
267 | - file = (ConfFile *)nih_hash_lookup (source->files, new_path); |
268 | - |
269 | - if (is_conf_file_override (path)) { |
270 | - if (! file) { |
271 | - /* override file has no corresponding conf file */ |
272 | - nih_debug ("Ignoring orphan override file %s", path); |
273 | - return; |
274 | - } |
275 | - |
276 | - /* reload conf file */ |
277 | - nih_debug ("Loading configuration file %s", new_path); |
278 | - ret = conf_reload_path (source, new_path, NULL); |
279 | - if (ret < 0) { |
280 | - error_path = new_path; |
281 | - goto error; |
282 | - } |
283 | - |
284 | - /* overlay override settings */ |
285 | - nih_debug ("Loading override file %s for %s", path, new_path); |
286 | - ret = conf_reload_path (source, new_path, path); |
287 | - if (ret < 0) { |
288 | - error_path = path; |
289 | - goto error; |
290 | - } |
291 | - } else { |
292 | - nih_debug ("Loading configuration and override files for %s", path); |
293 | - |
294 | - /* load conf file */ |
295 | - nih_debug ("Loading configuration file %s", path); |
296 | - ret = conf_reload_path (source, path, NULL); |
297 | - if (ret < 0) { |
298 | - error_path = path; |
299 | - goto error; |
300 | - } |
301 | - |
302 | - /* ensure we ignore directory changes (which won't have overrides. */ |
303 | - if (is_conf_file_std (path)) { |
304 | - struct stat st; |
305 | - if (stat (new_path, &st) == 0) { |
306 | - /* overlay override settings */ |
307 | - nih_debug ("Loading override file %s for %s", new_path, path); |
308 | - ret = conf_reload_path (source, path, new_path); |
309 | - if (ret < 0) { |
310 | - error_path = new_path; |
311 | - goto error; |
312 | - } |
313 | - } |
314 | - |
315 | - } |
316 | + if (statbuf && ! S_ISREG (statbuf->st_mode)) |
317 | + return; |
318 | + |
319 | + /* ignore non-config file changes */ |
320 | + if (! is_conf_file (path)) |
321 | + return; |
322 | + |
323 | + /* For config file, load it and it's override file */ |
324 | + if (is_conf_file_std (path)) { |
325 | + conf_load_path_with_override (source, path); |
326 | + return; |
327 | + } |
328 | + |
329 | + /* For override files, reload all matching conf+override combos */ |
330 | + job_name = conf_to_job_name (source->path, path); |
331 | + NIH_LIST_FOREACH (conf_sources, iter) { |
332 | + ConfSource *source = (ConfSource *)iter; |
333 | + |
334 | + if (source->type == CONF_FILE) |
335 | + continue; |
336 | + |
337 | + config_path = NIH_MUST (nih_sprintf (NULL, "%s/%s%s", source->path, job_name, CONF_EXT_STD)); |
338 | + file = (ConfFile *)nih_hash_lookup (source->files, config_path); |
339 | + if (file) { |
340 | + /* Find its override file and reload both */ |
341 | + conf_load_path_with_override (source, config_path); |
342 | + } |
343 | + nih_free (config_path); |
344 | } |
345 | |
346 | return; |
347 | - |
348 | -error: |
349 | - { |
350 | - NihError *err; |
351 | - |
352 | - err = nih_error_get (); |
353 | - nih_error ("%s: %s: %s", error_path, |
354 | - _("Error while loading configuration file"), |
355 | - err->message); |
356 | - nih_free (err); |
357 | - if (file) |
358 | - nih_unref (file, source); |
359 | - } |
360 | } |
361 | |
362 | /** |
363 | @@ -823,28 +950,19 @@ |
364 | |
365 | /* non-override files (and directories) are the simple case, so handle |
366 | * them and leave. |
367 | - */ |
368 | + */ |
369 | if (! is_conf_file_override (path)) { |
370 | nih_unref (file, source); |
371 | return; |
372 | } |
373 | |
374 | - /* if an override file is deleted for which there is a corresponding |
375 | - * conf file, reload the conf file to remove any modifications |
376 | - * introduced by the override file. |
377 | + /* Deleting override file is about the same as changing one. |
378 | + * We need to iterate across all matching jobs and reload them |
379 | + * with new "best" override file, if any. |
380 | */ |
381 | - new_path = toggle_conf_name (NULL, path); |
382 | - file = (ConfFile *)nih_hash_lookup (source->files, new_path); |
383 | - |
384 | - if (file) { |
385 | - nih_debug ("Reloading configuration for %s on deletion of overide (%s)", |
386 | - new_path, path); |
387 | - |
388 | - if ( conf_reload_path (source, new_path, NULL) < 0 ) { |
389 | - nih_warn ("%s: %s", new_path, |
390 | - _("Unable to reload configuration after override deletion")); |
391 | - } |
392 | - } |
393 | + nih_debug ("Reloading configuration for matching configs on deletion of override (%s)", |
394 | + path); |
395 | + conf_create_modify_handler (source, watch, path, NULL); |
396 | } |
397 | |
398 | /** |
399 | @@ -867,64 +985,18 @@ |
400 | const char *path, |
401 | struct stat *statbuf) |
402 | { |
403 | - ConfFile *file = NULL; |
404 | - nih_local char *new_path = NULL; |
405 | - |
406 | nih_assert (source != NULL); |
407 | nih_assert (dirname != NULL); |
408 | nih_assert (path != NULL); |
409 | nih_assert (statbuf != NULL); |
410 | |
411 | - /* We assume that CONF_EXT_STD files are visited before |
412 | - * CONF_EXT_OVERRIDE files. Happily, this assumption is currently |
413 | - * valid since CONF_EXT_STD comes before CONF_EXT_OVERRIDE if ordered |
414 | - * alphabetically. |
415 | - * |
416 | - * If this were ever to change (for example if we decided to |
417 | - * rename the CONF_EXT_OVERRIDE files to end in ".abc", say), the logic |
418 | - * in this function would be erroneous since it would never be possible when |
419 | - * visiting an override file (before a conf file) to lookup a conf file |
420 | - * in the hash, since the conf file would not yet have been seen and thus would |
421 | - * not exist in the hash (yet). |
422 | - */ |
423 | - nih_assert (CONF_EXT_STD[1] < CONF_EXT_OVERRIDE[1]); |
424 | - |
425 | if (! S_ISREG (statbuf->st_mode)) |
426 | return 0; |
427 | |
428 | - if (is_conf_file_std (path)) { |
429 | - if (conf_reload_path (source, path, NULL) < 0) { |
430 | - NihError *err; |
431 | - |
432 | - err = nih_error_get (); |
433 | - nih_error ("%s: %s: %s", path, |
434 | - _("Error while loading configuration file"), |
435 | - err->message); |
436 | - nih_free (err); |
437 | - } |
438 | - return 0; |
439 | - } |
440 | - |
441 | - new_path = toggle_conf_name (NULL, path); |
442 | - file = (ConfFile *)nih_hash_lookup (source->files, new_path); |
443 | - |
444 | - if (file) { |
445 | - /* we're visiting an override file with an associated conf file that |
446 | - * has already been loaded, so just overlay the override file. If |
447 | - * there is no corresponding conf file, we ignore the override file. |
448 | - */ |
449 | - if (conf_reload_path (source, new_path, path) < 0) { |
450 | - NihError *err; |
451 | - |
452 | - err = nih_error_get (); |
453 | - nih_error ("%s: %s: %s", new_path, |
454 | - _("Error while reloading configuration file"), |
455 | - err->message); |
456 | - nih_free (err); |
457 | - } |
458 | - } |
459 | - |
460 | - return 0; |
461 | + if (is_conf_file_std (path)) |
462 | + conf_load_path_with_override (source, path); |
463 | + |
464 | + return 0; |
465 | } |
466 | |
467 | |
468 | @@ -957,7 +1029,6 @@ |
469 | { |
470 | ConfFile *file = NULL; |
471 | nih_local char *buf = NULL; |
472 | - const char *start, *end; |
473 | nih_local char *name = NULL; |
474 | size_t len, pos, lineno; |
475 | NihError *err = NULL; |
476 | @@ -1014,23 +1085,8 @@ |
477 | |
478 | break; |
479 | case CONF_JOB_DIR: |
480 | - /* Construct the job name by taking the path and removing |
481 | - * the directory name from the front and the extension |
482 | - * from the end. |
483 | - */ |
484 | - start = path; |
485 | - if (! strncmp (start, source->path, strlen (source->path))) |
486 | - start += strlen (source->path); |
487 | - |
488 | - while (*start == '/') |
489 | - start++; |
490 | - |
491 | - end = strrchr (start, '.'); |
492 | - if (end && IS_CONF_EXT (end)) { |
493 | - name = NIH_MUST (nih_strndup (NULL, start, end - start)); |
494 | - } else { |
495 | - name = NIH_MUST (nih_strdup (NULL, start)); |
496 | - } |
497 | + |
498 | + name = conf_to_job_name (source->path, path); |
499 | |
500 | /* Create a new job item and parse the buffer to produce |
501 | * the job definition. |
502 | @@ -1388,4 +1444,3 @@ |
503 | } |
504 | |
505 | #endif /* DEBUG */ |
506 | - |
507 | |
508 | === modified file 'init/tests/test_conf.c' |
509 | --- init/tests/test_conf.c 2012-12-19 12:46:46 +0000 |
510 | +++ init/tests/test_conf.c 2013-01-08 16:18:21 +0000 |
511 | @@ -2424,7 +2424,8 @@ |
512 | FILE *f; |
513 | int ret, fd[4096], i = 0; |
514 | char dirname[PATH_MAX]; |
515 | - char filename[PATH_MAX], override[PATH_MAX]; |
516 | + char filename[PATH_MAX], override[PATH_MAX], override2[PATH_MAX]; |
517 | + char *dir; |
518 | JobClass *job; |
519 | NihError *err; |
520 | |
521 | @@ -3424,6 +3425,123 @@ |
522 | unlink (filename); |
523 | TEST_EQ (rmdir (dirname), 0); |
524 | |
525 | + TEST_FEATURE ("create two watches, two overrides, conf, then delete override files one by one"); |
526 | + TEST_ENSURE_CLEAN_ENV (); |
527 | + TEST_FILENAME (dirname); |
528 | + TEST_EQ (mkdir (dirname, 0755), 0); |
529 | + |
530 | + strcpy (override, dirname); |
531 | + strcat (override, "/peter/foo.override"); |
532 | + strcpy (override2, dirname); |
533 | + strcat (override2, "/paul/foo.override"); |
534 | + strcpy (filename, dirname); |
535 | + strcat (filename, "/paul/foo.conf"); |
536 | + |
537 | + const char *sources[] = {"peter", "paul", NULL}; |
538 | + |
539 | + for (const char **src = sources; *src; src++) { |
540 | + dir = nih_sprintf (NULL, "%s/%s", dirname, *src); |
541 | + TEST_EQ (mkdir (dir, 0755), 0); |
542 | + source = conf_source_new (NULL, dir, CONF_JOB_DIR); |
543 | + TEST_NE_P (source, NULL); |
544 | + ret = conf_source_reload (source); |
545 | + TEST_EQ (ret, 0); |
546 | + nih_free (dir); |
547 | + } |
548 | + |
549 | + TEST_FORCE_WATCH_UPDATE(); |
550 | + |
551 | + /* create override */ |
552 | + f = fopen (override, "w"); |
553 | + TEST_NE_P (f, NULL); |
554 | + fprintf (f, "manual\n"); |
555 | + fprintf (f, "author \"peter\"\n"); |
556 | + fclose (f); |
557 | + |
558 | + TEST_FORCE_WATCH_UPDATE(); |
559 | + |
560 | + /* create override */ |
561 | + f = fopen (override2, "w"); |
562 | + TEST_NE_P (f, NULL); |
563 | + fprintf (f, "manual\n"); |
564 | + fprintf (f, "author \"paul\"\n"); |
565 | + fprintf (f, "env wibble=wobble\n"); |
566 | + fclose (f); |
567 | + |
568 | + TEST_FORCE_WATCH_UPDATE(); |
569 | + |
570 | + /* create conf */ |
571 | + f = fopen (filename, "w"); |
572 | + TEST_NE_P (f, NULL); |
573 | + fprintf (f, "start on started\n"); |
574 | + fprintf (f, "emits hello\n"); |
575 | + fprintf (f, "author \"mary\"\n"); |
576 | + fclose (f); |
577 | + |
578 | + TEST_FORCE_WATCH_UPDATE(); |
579 | + |
580 | + /* ensure conf loaded */ |
581 | + file = (ConfFile *)nih_hash_lookup (source->files, filename); |
582 | + TEST_NE_P (file, NULL); |
583 | + job = (JobClass *)nih_hash_lookup (job_classes, "foo"); |
584 | + TEST_NE_P (job, NULL); |
585 | + TEST_EQ_P (file->job, job); |
586 | + TEST_EQ_STR ((job->emits)[0], "hello"); |
587 | + |
588 | + /* should pick up the top-priority override, *NOT* conf */ |
589 | + TEST_EQ_P (job->start_on, NULL); |
590 | + TEST_EQ_STR (job->author, "peter"); |
591 | + |
592 | + /* delete override */ |
593 | + unlink (override); |
594 | + |
595 | + TEST_FORCE_WATCH_UPDATE(); |
596 | + |
597 | + /* ensure conf reloaded and updated with the second override */ |
598 | + file = (ConfFile *)nih_hash_lookup (source->files, filename); |
599 | + TEST_NE_P (file, NULL); |
600 | + job = (JobClass *)nih_hash_lookup (job_classes, "foo"); |
601 | + TEST_NE_P (job, NULL); |
602 | + TEST_EQ_P (file->job, job); |
603 | + TEST_EQ_STR ((job->emits)[0], "hello"); |
604 | + |
605 | + /* should pick up the second override, *NOT* conf */ |
606 | + TEST_EQ_P (job->start_on, NULL); |
607 | + TEST_EQ_STR (job->author, "paul"); |
608 | + TEST_EQ_STR ((job->env)[0], "wibble=wobble"); |
609 | + |
610 | + TEST_FORCE_WATCH_UPDATE(); |
611 | + |
612 | + /* delete override */ |
613 | + unlink (override2); |
614 | + |
615 | + TEST_FORCE_WATCH_UPDATE(); |
616 | + |
617 | + /* ensure conf loaded */ |
618 | + file = (ConfFile *)nih_hash_lookup (source->files, filename); |
619 | + TEST_NE_P (file, NULL); |
620 | + job = (JobClass *)nih_hash_lookup (job_classes, "foo"); |
621 | + TEST_NE_P (job, NULL); |
622 | + TEST_EQ_P (file->job, job); |
623 | + TEST_EQ_STR (job->author, "mary"); |
624 | + TEST_EQ_STR ((job->emits)[0], "hello"); |
625 | + TEST_NE_P (job->start_on, NULL); |
626 | + TEST_EQ_P (job->env, NULL); |
627 | + |
628 | + unlink (filename); |
629 | + |
630 | + for (const char **src = sources; *src; src++) { |
631 | + dir = nih_sprintf (NULL, "%s/%s", dirname, *src); |
632 | + TEST_EQ (rmdir (dir), 0); |
633 | + nih_free (dir); |
634 | + } |
635 | + |
636 | + TEST_EQ (rmdir (dirname), 0); |
637 | + |
638 | + NIH_LIST_FOREACH_SAFE (conf_sources, iter) { |
639 | + ConfSource *source = (ConfSource *)iter; |
640 | + nih_free (source); |
641 | + } |
642 | |
643 | TEST_FEATURE ("create conf, watch, then create invalid override, delete override"); |
644 | TEST_ENSURE_CLEAN_ENV (); |
645 | |
646 | === modified file 'init/tests/test_conf_static.c' |
647 | --- init/tests/test_conf_static.c 2012-12-19 12:46:46 +0000 |
648 | +++ init/tests/test_conf_static.c 2013-01-08 16:18:21 +0000 |
649 | @@ -34,8 +34,8 @@ |
650 | char *f; |
651 | char *p; |
652 | |
653 | - TEST_FUNCTION_FEATURE ("toggle_conf_name", |
654 | - "changing conf to override"); |
655 | + TEST_FUNCTION ("toggle_conf_name"); |
656 | + TEST_FEATURE ("changing conf to override"); |
657 | |
658 | TEST_FILENAME (dirname); |
659 | strcpy (filename, dirname); |
660 | @@ -71,11 +71,154 @@ |
661 | nih_free (job); |
662 | } |
663 | |
664 | +void |
665 | +test_conf_to_job_name (void) |
666 | +{ |
667 | + char dirname[PATH_MAX]; |
668 | + char *filename; |
669 | + char *name; |
670 | + |
671 | + TEST_FUNCTION ("conf_to_job_name"); |
672 | + TEST_FEATURE ("with .conf file"); |
673 | + TEST_FILENAME (dirname); |
674 | + filename = nih_sprintf (NULL, "%s/foo.conf", dirname); |
675 | + name = conf_to_job_name (dirname, filename); |
676 | + TEST_EQ_STR (name, "foo"); |
677 | + nih_free (filename); |
678 | + nih_free (name); |
679 | + |
680 | + TEST_FEATURE ("with .override file"); |
681 | + filename = nih_sprintf (NULL, "%s/foo.override", dirname); |
682 | + name = conf_to_job_name (dirname, filename); |
683 | + TEST_EQ_STR (name, "foo"); |
684 | + nih_free (filename); |
685 | + nih_free (name); |
686 | + |
687 | + TEST_FEATURE ("with .conf in a sub-directory"); |
688 | + filename = nih_sprintf (NULL, "%s/foo/bar.conf", dirname); |
689 | + name = conf_to_job_name (dirname, filename); |
690 | + TEST_EQ_STR (name, "foo/bar"); |
691 | + nih_free (filename); |
692 | + nih_free (name); |
693 | + |
694 | + TEST_FEATURE ("without extension"); |
695 | + filename = nih_sprintf (NULL, "%s/foo", dirname); |
696 | + name = conf_to_job_name (dirname, filename); |
697 | + TEST_EQ_STR (name, "foo"); |
698 | + nih_free (filename); |
699 | + nih_free (name); |
700 | + |
701 | +} |
702 | + |
703 | +void |
704 | +test_conf_get_best_override (void) |
705 | +{ |
706 | + const char *sources[] = {"peter", "paul", "mary", NULL}; |
707 | + FILE *f; |
708 | + char dirname[PATH_MAX]; |
709 | + char *dir; |
710 | + char *expected; |
711 | + char *path; |
712 | + |
713 | + TEST_FUNCTION ("conf_get_best_override"); |
714 | + |
715 | + TEST_FILENAME (dirname); |
716 | + mkdir (dirname, 0755); |
717 | + |
718 | + for (const char **src = sources; *src; src++) { |
719 | + dir = nih_sprintf (NULL, "%s/%s", dirname, *src); |
720 | + TEST_EQ (mkdir (dir, 0755), 0); |
721 | + NIH_MUST (conf_source_new (NULL, dir, CONF_JOB_DIR)); |
722 | + nih_free (dir); |
723 | + } |
724 | + |
725 | + TEST_FEATURE ("with no overrides"); |
726 | + NIH_LIST_FOREACH (conf_sources, iter) { |
727 | + ConfSource *source = (ConfSource *)iter; |
728 | + path = conf_get_best_override ("foo", source); |
729 | + TEST_EQ_P (path, NULL); |
730 | + } |
731 | + |
732 | + TEST_FEATURE ("with single highest priority override"); |
733 | + expected = nih_sprintf (NULL, "%s/%s/foo.override", dirname, sources[0]); |
734 | + f = fopen (expected, "w"); |
735 | + TEST_NE_P (f, NULL); |
736 | + TEST_EQ (fclose (f), 0); |
737 | + |
738 | + NIH_LIST_FOREACH (conf_sources, iter) { |
739 | + ConfSource *source = (ConfSource *)iter; |
740 | + path = conf_get_best_override ("foo", source); |
741 | + TEST_EQ_STR (path, expected); |
742 | + nih_free (path); |
743 | + } |
744 | + TEST_EQ (unlink (expected), 0); |
745 | + |
746 | + TEST_FEATURE ("with single middle priority override"); |
747 | + expected = nih_sprintf (NULL, "%s/%s/foo.override", dirname, sources[1]); |
748 | + f = fopen (expected, "w"); |
749 | + TEST_NE_P (f, NULL); |
750 | + TEST_EQ (fclose (f), 0); |
751 | + |
752 | + NIH_LIST_FOREACH (conf_sources, iter) { |
753 | + ConfSource *source = (ConfSource *)iter; |
754 | + path = conf_get_best_override ("foo", source); |
755 | + |
756 | + /* Poor-man's basename(1) */ |
757 | + dir = conf_to_job_name (dirname, source->path); |
758 | + if (strcmp (dir, sources[0]) == 0) { |
759 | + TEST_EQ_P (path, NULL); |
760 | + } else { |
761 | + TEST_EQ_STR (path, expected); |
762 | + nih_free (path); |
763 | + } |
764 | + nih_free (dir); |
765 | + } |
766 | + TEST_EQ (unlink (expected), 0); |
767 | + |
768 | + TEST_FEATURE ("with single lowest priority override"); |
769 | + expected = nih_sprintf (NULL, "%s/%s/foo.override", dirname, sources[2]); |
770 | + f = fopen (expected, "w"); |
771 | + TEST_NE_P (f, NULL); |
772 | + TEST_EQ (fclose (f), 0); |
773 | + |
774 | + NIH_LIST_FOREACH (conf_sources, iter) { |
775 | + ConfSource *source = (ConfSource *)iter; |
776 | + path = conf_get_best_override ("foo", source); |
777 | + |
778 | + /* Poor-man's basename(1) */ |
779 | + dir = conf_to_job_name (dirname, source->path); |
780 | + if (strcmp (dir, sources[2]) == 0) { |
781 | + TEST_EQ_STR (path, expected); |
782 | + nih_free (path); |
783 | + } else { |
784 | + TEST_EQ_P (path, NULL); |
785 | + } |
786 | + nih_free (dir); |
787 | + } |
788 | + TEST_EQ (unlink (expected), 0); |
789 | + |
790 | + /* Clean up */ |
791 | + for (const char **src = sources; *src; src++) { |
792 | + dir = nih_sprintf (NULL, "%s/%s", dirname, *src); |
793 | + TEST_EQ (rmdir (dir), 0); |
794 | + nih_free (dir); |
795 | + } |
796 | + |
797 | + TEST_EQ (rmdir (dirname), 0); |
798 | + |
799 | + NIH_LIST_FOREACH_SAFE (conf_sources, iter) { |
800 | + ConfSource *source = (ConfSource *)iter; |
801 | + nih_free (source); |
802 | + } |
803 | +} |
804 | + |
805 | int |
806 | main (int argc, |
807 | char *argv[]) |
808 | { |
809 | test_toggle_conf_name (); |
810 | + test_conf_to_job_name (); |
811 | + test_conf_get_best_override (); |
812 | |
813 | return 0; |
814 | } |
Hi Dmitrijs,
* Changelog: Need extra indent for 'priority' and 'correctly'. best_override( ): reload_ file() is still using stat(2) so path_with_ override( ): modify_ handler( ): handler( ):
* init/conf.c:
- Might just as well add ',2013' to copyright now :)
- conf_to_job_name():
- Typo: 'ConfigSource' should be 'ConfSource'.
- Typo: 'from then' should be 'from the'.
- You could drop the braces for the final if/else test, but they
were there originally I guess :)
- conf_get_
- Typo: '& finds the' should be '& find the'.
- Typo: 'for override file' should be 'for override files'.
- Typo: 'ConfigSource' should be 'ConfSource'.
- @name and @last_source are not asserted.
- Using lstat(2) seems correct since we don't support symlinks on
purpose, but conf_source_
there is a discrepancy.
- conf_load_
- Need space around '=' in initial assignments.
- 'if (override_path)' at line 818 technically redundant since we already know it
cannot be NULL.
- conf_create_
- Missing check on return from nih_sprintf().
- conf_delete_
- Typo: 'overide' rather than 'override'.
Regarding your comments on the lstat issue, we need to keep an eye on this. As you say, the impact should not be great, but it would still be worth performing some tests on a slow system to see if the new code makes any appreciable difference to performance.