1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <stdlib.h>
4 
5 #include "analyze.h"
6 #include "analyze-condition.h"
7 #include "analyze-verify-util.h"
8 #include "condition.h"
9 #include "conf-parser.h"
10 #include "load-fragment.h"
11 #include "service.h"
12 
parse_condition(Unit * u,const char * line)13 static int parse_condition(Unit *u, const char *line) {
14         assert(u);
15         assert(line);
16 
17         for (ConditionType t = 0; t < _CONDITION_TYPE_MAX; t++) {
18                 ConfigParserCallback callback;
19                 Condition **target;
20                 const char *p, *name;
21 
22                 name = condition_type_to_string(t);
23                 p = startswith(line, name);
24                 if (p)
25                         target = &u->conditions;
26                 else {
27                         name = assert_type_to_string(t);
28                         p = startswith(line, name);
29                         if (!p)
30                                 continue;
31 
32                         target = &u->asserts;
33                 }
34 
35                 p += strspn(p, WHITESPACE);
36 
37                 if (*p != '=')
38                         continue;
39                 p++;
40 
41                 p += strspn(p, WHITESPACE);
42 
43                 if (condition_takes_path(t))
44                         callback = config_parse_unit_condition_path;
45                 else
46                         callback = config_parse_unit_condition_string;
47 
48                 return callback(NULL, "(cmdline)", 0, NULL, 0, name, t, p, target, u);
49         }
50 
51         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot parse \"%s\".", line);
52 }
53 
54 _printf_(7, 8)
log_helper(void * userdata,int level,int error,const char * file,int line,const char * func,const char * format,...)55 static int log_helper(void *userdata, int level, int error, const char *file, int line, const char *func, const char *format, ...) {
56         Unit *u = userdata;
57         va_list ap;
58         int r;
59 
60         assert(u);
61 
62         /* "upgrade" debug messages */
63         level = MIN(LOG_INFO, level);
64 
65         va_start(ap, format);
66         r = log_object_internalv(level, error, file, line, func,
67                                  NULL,
68                                  u->id,
69                                  NULL,
70                                  NULL,
71                                  format, ap);
72         va_end(ap);
73 
74         return r;
75 }
76 
verify_conditions(char ** lines,LookupScope scope,const char * unit,const char * root)77 static int verify_conditions(char **lines, LookupScope scope, const char *unit, const char *root) {
78         _cleanup_(manager_freep) Manager *m = NULL;
79         Unit *u;
80         int r, q = 1;
81 
82         if (unit) {
83                 _cleanup_strv_free_ char **filenames = NULL;
84                 _cleanup_free_ char *var = NULL;
85 
86                 filenames = strv_new(unit);
87                 if (!filenames)
88                         return log_oom();
89 
90                 r = verify_generate_path(&var, filenames);
91                 if (r < 0)
92                         return log_error_errno(r, "Failed to generate unit load path: %m");
93 
94                 assert_se(set_unit_path(var) >= 0);
95         }
96 
97         r = manager_new(scope, MANAGER_TEST_RUN_MINIMAL, &m);
98         if (r < 0)
99                 return log_error_errno(r, "Failed to initialize manager: %m");
100 
101         log_debug("Starting manager...");
102         r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root);
103         if (r < 0)
104                 return r;
105 
106         if (unit) {
107                 _cleanup_free_ char *prepared = NULL;
108 
109                 r = verify_prepare_filename(unit, &prepared);
110                 if (r < 0)
111                         return log_error_errno(r, "Failed to prepare filename %s: %m", unit);
112 
113                 r = manager_load_startable_unit_or_warn(m, NULL, prepared, &u);
114                 if (r < 0)
115                         return r;
116         } else {
117                 r = unit_new_for_name(m, sizeof(Service), "test.service", &u);
118                 if (r < 0)
119                         return log_error_errno(r, "Failed to create test.service: %m");
120 
121                 STRV_FOREACH(line, lines) {
122                         r = parse_condition(u, *line);
123                         if (r < 0)
124                                 return r;
125                 }
126         }
127 
128         r = condition_test_list(u->asserts, environ, assert_type_to_string, log_helper, u);
129         if (u->asserts)
130                 log_notice("Asserts %s.", r > 0 ? "succeeded" : "failed");
131 
132         q = condition_test_list(u->conditions, environ, condition_type_to_string, log_helper, u);
133         if (u->conditions)
134                 log_notice("Conditions %s.", q > 0 ? "succeeded" : "failed");
135 
136         return r > 0 && q > 0 ? 0 : -EIO;
137 }
138 
verb_condition(int argc,char * argv[],void * userdata)139 int verb_condition(int argc, char *argv[], void *userdata) {
140         return verify_conditions(strv_skip(argv, 1), arg_scope, arg_unit, arg_root);
141 }
142