chiark / gitweb /
fix job merging
[elogind.git] / job.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <assert.h>
4 #include <errno.h>
5
6 #include "macro.h"
7 #include "job.h"
8
9 Job* job_new(Manager *m, JobType type, Name *name) {
10         Job *j;
11
12         assert(m);
13         assert(type < _JOB_TYPE_MAX);
14         assert(name);
15
16         if (!(j = new0(Job, 1)))
17                 return NULL;
18
19         j->manager = m;
20         j->id = m->current_job_id++;
21         j->type = type;
22         j->name = name;
23
24         /* We don't link it here, that's what job_dependency() is for */
25
26         return j;
27 }
28
29 void job_free(Job *j) {
30         assert(j);
31
32         /* Detach from next 'bigger' objects */
33
34         if (j->linked) {
35                 if (j->name->meta.job == j)
36                         j->name->meta.job = NULL;
37
38                 hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
39         }
40
41         manager_transaction_unlink_job(j->manager, j);
42
43         free(j);
44 }
45
46 JobDependency* job_dependency_new(Job *subject, Job *object, bool matters) {
47         JobDependency *l;
48
49         assert(object);
50
51         /* Adds a new job link, which encodes that the 'subject' job
52          * needs the 'object' job in some way. If 'subject' is NULL
53          * this means the 'anchor' job (i.e. the one the user
54          * explcitily asked for) is the requester. */
55
56         if (!(l = new0(JobDependency, 1)))
57                 return NULL;
58
59         l->subject = subject;
60         l->object = object;
61         l->matters = matters;
62
63         if (subject) {
64                 l->subject_next = subject->subject_list;
65                 subject->subject_list = l;
66         } else {
67                 l->subject_next = object->manager->transaction_anchor;
68                 object->manager->transaction_anchor = l;
69         }
70
71         if (l->subject_next)
72                 l->subject_next->subject_prev = l;
73         l->subject_prev = NULL;
74
75         if ((l->object_next = object->object_list))
76                 l->object_next->object_prev = l;
77         l->object_prev = NULL;
78         object->object_list = l;
79
80         return l;
81 }
82
83 void job_dependency_free(JobDependency *l) {
84         assert(l);
85
86         if (l->subject_prev)
87                 l->subject_prev->subject_next = l->subject_next;
88         else if (l->subject)
89                 l->subject->subject_list = l->subject_next;
90         else
91                 l->object->manager->transaction_anchor = l->subject_next;
92
93         if (l->subject_next)
94                 l->subject_next->subject_prev = l->subject_prev;
95
96         if (l->object_prev)
97                 l->object_prev->object_next = l->object_next;
98         else
99                 l->object->object_list = l->object_next;
100
101         if (l->object_next)
102                 l->object_next->object_prev = l->object_prev;
103
104         free(l);
105 }
106
107 void job_dependency_delete(Job *subject, Job *object, bool *matters) {
108         JobDependency *l;
109
110         assert(object);
111
112         for (l = object->object_list; l; l = l->object_next) {
113                 assert(l->object == object);
114
115                 if (l->subject == subject)
116                         break;
117         }
118
119         if (!l) {
120                 if (matters)
121                         *matters = false;
122                 return;
123         }
124
125         if (matters)
126                 *matters = l->matters;
127
128         job_dependency_free(l);
129 }
130
131 const char* job_type_to_string(JobType t) {
132
133         static const char* const job_type_table[_JOB_TYPE_MAX] = {
134                 [JOB_START] = "start",
135                 [JOB_STOP] = "stop",
136                 [JOB_VERIFY_STARTED] = "verify-started",
137                 [JOB_RELOAD] = "reload",
138                 [JOB_RELOAD_OR_START] = "reload-or-start",
139                 [JOB_RESTART] = "restart",
140                 [JOB_TRY_RESTART] = "try-restart",
141         };
142
143
144         if (t < 0 || t >= _JOB_TYPE_MAX)
145                 return "n/a";
146
147         return job_type_table[t];
148 }
149
150 void job_dump(Job *j, FILE*f, const char *prefix) {
151
152         static const char* const job_state_table[_JOB_STATE_MAX] = {
153                 [JOB_WAITING] = "waiting",
154                 [JOB_RUNNING] = "running",
155                 [JOB_DONE] = "done"
156         };
157
158         assert(j);
159         assert(f);
160
161         fprintf(f,
162                 "%sJob %u:\n"
163                 "%s\tAction: %s → %s\n"
164                 "%s\tState: %s\n",
165                 prefix, j->id,
166                 prefix, name_id(j->name), job_type_to_string(j->type),
167                 prefix, job_state_table[j->state]);
168 }
169
170 bool job_is_anchor(Job *j) {
171         JobDependency *l;
172
173         assert(j);
174
175         for (l = j->object_list; l; l = l->object_next)
176                 if (!l->subject)
177                         return true;
178
179         return false;
180 }
181
182 static bool types_match(JobType a, JobType b, JobType c, JobType d) {
183         return
184                 (a == c && b == d) ||
185                 (a == d && b == c);
186 }
187
188 int job_type_merge(JobType *a, JobType b) {
189         if (*a == b)
190                 return 0;
191
192         /* Merging is associative! a merged with b merged with c is
193          * the same as a merged with c merged with b. */
194
195         /* Mergeability is transitive! if a can be merged with b and b
196          * with c then a also with c */
197
198         /* Also, if a merged with b cannot be merged with c, then
199          * either a or b cannot be merged with c either */
200
201         if (types_match(*a, b, JOB_START, JOB_VERIFY_STARTED))
202                 *a = JOB_START;
203         else if (types_match(*a, b, JOB_START, JOB_RELOAD) ||
204                  types_match(*a, b, JOB_START, JOB_RELOAD_OR_START) ||
205                  types_match(*a, b, JOB_VERIFY_STARTED, JOB_RELOAD_OR_START) ||
206                  types_match(*a, b, JOB_RELOAD, JOB_RELOAD_OR_START))
207                 *a = JOB_RELOAD_OR_START;
208         else if (types_match(*a, b, JOB_START, JOB_RESTART) ||
209                  types_match(*a, b, JOB_START, JOB_TRY_RESTART) ||
210                  types_match(*a, b, JOB_VERIFY_STARTED, JOB_RESTART) ||
211                  types_match(*a, b, JOB_RELOAD, JOB_RESTART) ||
212                  types_match(*a, b, JOB_RELOAD_OR_START, JOB_RESTART) ||
213                  types_match(*a, b, JOB_RELOAD_OR_START, JOB_TRY_RESTART) ||
214                  types_match(*a, b, JOB_RESTART, JOB_TRY_RESTART))
215                 *a = JOB_RESTART;
216         else if (types_match(*a, b, JOB_VERIFY_STARTED, JOB_RELOAD))
217                 *a = JOB_RELOAD;
218         else if (types_match(*a, b, JOB_VERIFY_STARTED, JOB_TRY_RESTART) ||
219                  types_match(*a, b, JOB_RELOAD, JOB_TRY_RESTART))
220                 *a = JOB_TRY_RESTART;
221         else
222                 return -EEXIST;
223
224         return 0;
225 }
226
227 bool job_type_mergeable(JobType a, JobType b) {
228         return job_type_merge(&a, b) >= 0;
229 }
230
231 bool job_type_is_superset(JobType a, JobType b) {
232
233         /* Checks whether operation a is a "superset" of b */
234
235         if (a == b)
236                 return true;
237
238         switch (a) {
239                 case JOB_START:
240                         return b == JOB_VERIFY_STARTED;
241
242                 case JOB_RELOAD:
243                         return b == JOB_VERIFY_STARTED;
244
245                 case JOB_RELOAD_OR_START:
246                         return
247                                 b == JOB_RELOAD ||
248                                 b == JOB_START;
249
250                 case JOB_RESTART:
251                         return
252                                 b == JOB_START ||
253                                 b == JOB_VERIFY_STARTED ||
254                                 b == JOB_RELOAD ||
255                                 b == JOB_RELOAD_OR_START ||
256                                 b == JOB_TRY_RESTART;
257
258                 case JOB_TRY_RESTART:
259                         return
260                                 b == JOB_VERIFY_STARTED ||
261                                 b == JOB_RELOAD;
262                 default:
263                         return false;
264
265         }
266 }