Move some data/functions from service_record to base_process_service.
[oweals/dinit.git] / src / service.cc
1 #include <cstring>
2 #include <cerrno>
3 #include <iterator>
4 #include <memory>
5 #include <cstddef>
6
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <sys/ioctl.h>
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include <termios.h>
13
14 #include "dinit.h"
15 #include "service.h"
16 #include "dinit-log.h"
17 #include "dinit-socket.h"
18 #include "dinit-util.h"
19
20 /*
21  * service.cc - Service management.
22  * See service.h for details.
23  */
24
25 // Find the requested service by name
26 static service_record * find_service(const std::list<service_record *> & records,
27                                     const char *name) noexcept
28 {
29     using std::list;
30     list<service_record *>::const_iterator i = records.begin();
31     for ( ; i != records.end(); i++ ) {
32         if (strcmp((*i)->get_name().c_str(), name) == 0) {
33             return *i;
34         }
35     }
36     return nullptr;
37 }
38
39 service_record * service_set::find_service(const std::string &name) noexcept
40 {
41     return ::find_service(records, name.c_str());
42 }
43
44 void service_set::stop_service(const std::string & name) noexcept
45 {
46     service_record *record = find_service(name);
47     if (record != nullptr) {
48         record->stop();
49         process_queues();
50     }
51 }
52
53 // Called when a service has actually stopped; dependents have stopped already, unless this stop
54 // is due to an unexpected process termination.
55 void service_record::stopped() noexcept
56 {
57     if (onstart_flags.runs_on_console) {
58         tcsetpgrp(0, getpgrp());
59         discard_console_log_buffer();
60         release_console();
61     }
62
63     force_stop = false;
64
65     // If we are a soft dependency of another target, break the acquisition from that target now:
66     for (auto & dependent : dependents) {
67         if (dependent->dep_type != dependency_type::REGULAR) {
68             if (dependent->holding_acq) {
69                 dependent->holding_acq = false;
70                 release();
71             }
72         }
73     }
74
75     bool will_restart = (desired_state == service_state_t::STARTED)
76             && services->get_auto_restart();
77
78     for (auto dependency : depends_on) {
79         // we signal dependencies in case they are waiting for us to stop:
80         dependency.get_to()->dependent_stopped();
81     }
82
83     service_state = service_state_t::STOPPED;
84
85     if (will_restart) {
86         // Desired state is "started".
87         restarting = true;
88         start(false);
89     }
90     else {
91         becoming_inactive();
92         
93         if (start_explicit) {
94             start_explicit = false;
95             release();
96         }
97         else if (required_by == 0) {
98             services->service_inactive(this);
99         }
100     }
101
102     log_service_stopped(service_name);
103     notify_listeners(service_event_t::STOPPED);
104 }
105
106 bool service_record::do_auto_restart() noexcept
107 {
108     if (auto_restart) {
109         return services->get_auto_restart();
110     }
111     return false;
112 }
113
114 void service_record::require() noexcept
115 {
116     if (required_by++ == 0) {
117         prop_require = !prop_release;
118         prop_release = false;
119         services->add_prop_queue(this);
120     }
121 }
122
123 void service_record::release(bool issue_stop) noexcept
124 {
125     if (--required_by == 0) {
126         desired_state = service_state_t::STOPPED;
127
128         // Can stop, and can release dependencies now. We don't need to issue a release if
129         // the require was pending though:
130         prop_release = !prop_require;
131         prop_require = false;
132         services->add_prop_queue(this);
133
134         if (service_state == service_state_t::STOPPED) {
135             services->service_inactive(this);
136         }
137         else if (issue_stop) {
138             do_stop();
139         }
140     }
141 }
142
143 void service_record::release_dependencies() noexcept
144 {
145     for (auto & dependency : depends_on) {
146         service_record * dep_to = dependency.get_to();
147         if (dependency.holding_acq) {
148             dep_to->release();
149             dependency.holding_acq = false;
150         }
151     }
152 }
153
154 void service_record::start(bool activate) noexcept
155 {
156     if (activate && ! start_explicit) {
157         require();
158         start_explicit = true;
159     }
160     
161     if (desired_state == service_state_t::STARTED && service_state != service_state_t::STOPPED) return;
162
163     bool was_active = service_state != service_state_t::STOPPED || desired_state != service_state_t::STOPPED;
164     desired_state = service_state_t::STARTED;
165     
166     if (service_state != service_state_t::STOPPED) {
167         // We're already starting/started, or we are stopping and need to wait for
168         // that the complete.
169         if (service_state != service_state_t::STOPPING || ! can_interrupt_stop()) {
170             return;
171         }
172         // We're STOPPING, and that can be interrupted. Our dependencies might be STOPPING,
173         // but if so they are waiting (for us), so they too can be instantly returned to
174         // STARTING state.
175         notify_listeners(service_event_t::STOPCANCELLED);
176     }
177     else if (! was_active) {
178         services->service_active(this);
179     }
180
181     service_state = service_state_t::STARTING;
182     waiting_for_deps = true;
183
184     if (start_check_dependencies()) {
185         services->add_transition_queue(this);
186     }
187 }
188
189 void service_record::do_propagation() noexcept
190 {
191     if (prop_require) {
192         // Need to require all our dependencies
193         for (auto & dep : depends_on) {
194             dep.get_to()->require();
195             dep.holding_acq = true;
196         }
197         prop_require = false;
198     }
199     
200     if (prop_release) {
201         release_dependencies();
202         prop_release = false;
203     }
204     
205     if (prop_failure) {
206         prop_failure = false;
207         failed_to_start(true);
208     }
209     
210     if (prop_start) {
211         prop_start = false;
212         start(false);
213     }
214
215     if (prop_stop) {
216         prop_stop = false;
217         do_stop();
218     }
219 }
220
221 void service_record::execute_transition() noexcept
222 {
223     // state is STARTED with restarting set true if we are running a smooth recovery.
224     if (service_state == service_state_t::STARTING || (service_state == service_state_t::STARTED
225             && restarting)) {
226         if (check_deps_started()) {
227             all_deps_started();
228         }
229     }
230     else if (service_state == service_state_t::STOPPING) {
231         if (stop_check_dependents()) {
232             waiting_for_deps = false;
233             bring_down();
234         }
235     }
236 }
237
238 void service_record::do_start() noexcept
239 {
240     if (pinned_stopped) return;
241     
242     if (service_state != service_state_t::STARTING) {
243         return;
244     }
245     
246     service_state = service_state_t::STARTING;
247
248     waiting_for_deps = true;
249
250     // Ask dependencies to start, mark them as being waited on.
251     if (check_deps_started()) {
252         // Once all dependencies are started, we start properly:
253         all_deps_started();
254     }
255 }
256
257 void service_record::dependency_started() noexcept
258 {
259     if ((service_state == service_state_t::STARTING || service_state == service_state_t::STARTED)
260             && waiting_for_deps) {
261         services->add_transition_queue(this);
262     }
263 }
264
265 bool service_record::start_check_dependencies() noexcept
266 {
267     bool all_deps_started = true;
268
269     for (auto & dep : depends_on) {
270         service_record * to = dep.get_to();
271         if (to->service_state != service_state_t::STARTED) {
272             if (to->service_state != service_state_t::STARTING) {
273                 to->prop_start = true;
274                 services->add_prop_queue(to);
275             }
276             dep.waiting_on = true;
277             all_deps_started = false;
278         }
279     }
280     
281     return all_deps_started;
282 }
283
284 bool service_record::check_deps_started() noexcept
285 {
286     for (auto & dep : depends_on) {
287         if (dep.waiting_on) {
288             return false;
289         }
290     }
291
292     return true;
293 }
294
295 void service_record::all_deps_started() noexcept
296 {
297     if (onstart_flags.starts_on_console && ! have_console) {
298         queue_for_console();
299         return;
300     }
301     
302     waiting_for_deps = false;
303
304     if (! can_proceed_to_start()) {
305         waiting_for_deps = true;
306         return;
307     }
308
309     bool start_success = bring_up();
310     if (! start_success) {
311         failed_to_start();
312     }
313 }
314
315 void service_record::acquired_console() noexcept
316 {
317     waiting_for_console = false;
318     have_console = true;
319
320     if (service_state != service_state_t::STARTING) {
321         // We got the console but no longer want it.
322         release_console();
323     }
324     else if (check_deps_started()) {
325         all_deps_started();
326     }
327     else {
328         // We got the console but can't use it yet.
329         release_console();
330     }
331 }
332
333
334 void service_record::started() noexcept
335 {
336     // If we start on console but don't keep it, release it now:
337     if (have_console && ! onstart_flags.runs_on_console) {
338         tcsetpgrp(0, getpgrp());
339         release_console();
340     }
341
342     log_service_started(get_name());
343     service_state = service_state_t::STARTED;
344     notify_listeners(service_event_t::STARTED);
345
346     if (onstart_flags.rw_ready) {
347         open_control_socket();
348     }
349     if (onstart_flags.log_ready) {
350         setup_external_log();
351     }
352
353     if (force_stop || desired_state == service_state_t::STOPPED) {
354         // We must now stop.
355         do_stop();
356         return;
357     }
358
359     // Notify any dependents whose desired state is STARTED:
360     for (auto dept : dependents) {
361         dept->get_from()->dependency_started();
362         dept->waiting_on = false;
363     }
364 }
365
366 void service_record::failed_to_start(bool depfailed) noexcept
367 {
368     if (have_console) {
369         tcsetpgrp(0, getpgrp());
370         release_console();
371     }
372     if (waiting_for_console) {
373         services->unqueue_console(this);
374         waiting_for_console = false;
375     }
376     
377     log_service_failed(get_name());
378     service_state = service_state_t::STOPPED;
379     if (start_explicit) {
380         start_explicit = false;
381         release(false);
382     }
383     notify_listeners(service_event_t::FAILEDSTART);
384     
385     // Cancel start of dependents:
386     for (auto & dept : dependents) {
387         switch (dept->dep_type) {
388         case dependency_type::REGULAR:
389         case dependency_type::MILESTONE:
390             if (dept->get_from()->service_state == service_state_t::STARTING) {
391                 dept->get_from()->prop_failure = true;
392                 services->add_prop_queue(dept->get_from());
393             }
394             break;
395         case dependency_type::WAITS_FOR:
396         case dependency_type::SOFT:
397             if (dept->waiting_on) {
398                 dept->waiting_on = false;
399                 dept->get_from()->dependency_started();
400             }
401             if (dept->holding_acq) {
402                 dept->holding_acq = false;
403                 release();
404             }
405         }
406     }
407 }
408
409 bool service_record::bring_up() noexcept
410 {
411     // default implementation: there is no process, so we are started.
412     started();
413     return true;
414 }
415
416 // Mark this and all dependent services as force-stopped.
417 void service_record::forced_stop() noexcept
418 {
419     if (service_state != service_state_t::STOPPED) {
420         force_stop = true;
421         services->add_transition_queue(this);
422     }
423 }
424
425 void service_record::dependent_stopped() noexcept
426 {
427     if (service_state == service_state_t::STOPPING && waiting_for_deps) {
428         services->add_transition_queue(this);
429     }
430 }
431
432 void service_record::stop(bool bring_down) noexcept
433 {
434     if (start_explicit) {
435         start_explicit = false;
436         release();
437     }
438
439     if (bring_down) {
440         do_stop();
441     }
442 }
443
444 void service_record::do_stop() noexcept
445 {
446     if (pinned_started) return;
447
448     if (start_explicit && ! do_auto_restart()) {
449         start_explicit = false;
450         release(false);
451     }
452
453     bool all_deps_stopped = stop_dependents();
454
455     if (service_state != service_state_t::STARTED) {
456         if (service_state == service_state_t::STARTING) {
457             // If waiting for a dependency, or waiting for the console, we can interrupt start. Otherwise,
458             // we need to delegate to can_interrupt_start() (which can be overridden).
459             if (! waiting_for_deps && ! waiting_for_console) {
460                 if (! can_interrupt_start()) {
461                     // Well this is awkward: we're going to have to continue starting. We can stop once we've
462                     // reached the started state.
463                     return;
464                 }
465
466                 if (! interrupt_start()) {
467                     // Now wait for service startup to actually end; we don't need to handle it here.
468                     return;
469                 }
470             }
471             else if (waiting_for_console) {
472                 services->unqueue_console(this);
473                 waiting_for_console = false;
474             }
475
476             // We must have had desired_state == STARTED.
477             notify_listeners(service_event_t::STARTCANCELLED);
478
479             // Reaching this point, we are starting interruptibly - so we
480             // stop now (by falling through to below).
481         }
482         else {
483             // If we're starting we need to wait for that to complete.
484             // If we're already stopping/stopped there's nothing to do.
485             return;
486         }
487     }
488
489     service_state = service_state_t::STOPPING;
490     waiting_for_deps = true;
491     if (all_deps_stopped) {
492         services->add_transition_queue(this);
493     }
494 }
495
496 bool service_record::stop_check_dependents() noexcept
497 {
498     bool all_deps_stopped = true;
499     for (auto dept : dependents) {
500         if (dept->dep_type == dependency_type::REGULAR && ! dept->get_from()->is_stopped()) {
501             all_deps_stopped = false;
502             break;
503         }
504     }
505     
506     return all_deps_stopped;
507 }
508
509 bool service_record::stop_dependents() noexcept
510 {
511     bool all_deps_stopped = true;
512     for (auto dept : dependents) {
513         if (dept->dep_type == dependency_type::REGULAR) {
514             if (! dept->get_from()->is_stopped()) {
515                 // Note we check *first* since if the dependent service is not stopped,
516                 // 1. We will issue a stop to it shortly and
517                 // 2. It will notify us when stopped, at which point the stop_check_dependents()
518                 //    check is run anyway.
519                 all_deps_stopped = false;
520             }
521
522             if (force_stop) {
523                 // If this service is to be forcefully stopped, dependents must also be.
524                 dept->get_from()->forced_stop();
525             }
526
527             dept->get_from()->prop_stop = true;
528             services->add_prop_queue(dept->get_from());
529         }
530     }
531
532     return all_deps_stopped;
533 }
534
535 // All dependents have stopped; we can stop now, too. Only called when STOPPING.
536 void service_record::bring_down() noexcept
537 {
538     waiting_for_deps = false;
539     stopped();
540 }
541
542 void service_record::unpin() noexcept
543 {
544     if (pinned_started) {
545         pinned_started = false;
546         if (desired_state == service_state_t::STOPPED || force_stop) {
547             do_stop();
548             services->process_queues();
549         }
550     }
551     if (pinned_stopped) {
552         pinned_stopped = false;
553         if (desired_state == service_state_t::STARTED) {
554             do_start();
555             services->process_queues();
556         }
557     }
558 }
559
560 void service_record::queue_for_console() noexcept
561 {
562     waiting_for_console = true;
563     services->append_console_queue(this);
564 }
565
566 void service_record::release_console() noexcept
567 {
568     have_console = false;
569     services->pull_console_queue();
570 }
571
572 bool service_record::interrupt_start() noexcept
573 {
574     services->unqueue_console(this);
575     return true;
576 }
577
578 void service_set::service_active(service_record *sr) noexcept
579 {
580     active_services++;
581 }
582
583 void service_set::service_inactive(service_record *sr) noexcept
584 {
585     active_services--;
586 }