Re-work restart function.
[oweals/dinit.git] / src / tests / proctests.cc
1 #include <cassert>
2 #include <iostream>
3 #include <list>
4 #include <utility>
5 #include <string>
6
7 #include "service.h"
8 #include "proc-service.h"
9
10 // Tests of process-service related functionality.
11 //
12 // These tests work mostly by completely mocking out the base_process_service class. The mock
13 // implementations can be found in test-baseproc.cc.
14
15 extern eventloop_t event_loop;
16
17 constexpr static auto REG = dependency_type::REGULAR;
18 constexpr static auto WAITS = dependency_type::WAITS_FOR;
19
20 // Friend interface to access base_process_service private/protected members.
21 class base_process_service_test
22 {
23     public:
24     static void exec_succeeded(base_process_service *bsp)
25     {
26         bsp->waiting_for_execstat = false;
27         bsp->exec_succeeded();
28     }
29
30     static void exec_failed(base_process_service *bsp, int errcode)
31     {
32         run_proc_err err;
33         err.stage = exec_stage::DO_EXEC;
34         err.st_errno = errcode;
35         bsp->waiting_for_execstat = false;
36         bsp->exec_failed(err);
37     }
38
39     static void handle_exit(base_process_service *bsp, int exit_status)
40     {
41         bsp->pid = -1;
42         bsp->handle_exit_status(bp_sys::exit_status(true, false, exit_status));
43     }
44
45     static void handle_signal_exit(base_process_service *bsp, int signo)
46     {
47         bsp->pid = -1;
48         bsp->handle_exit_status(bp_sys::exit_status(false, true, signo));
49     }
50
51     static int get_notification_fd(base_process_service *bsp)
52     {
53         return bsp->notification_fd;
54     }
55 };
56
57 namespace bp_sys {
58     // last signal sent:
59     extern int last_sig_sent;
60     extern pid_t last_forked_pid;
61 }
62
63 static void init_service_defaults(base_process_service &ps)
64 {
65     ps.set_restart_interval(time_val(10,0), 3);
66     ps.set_restart_delay(time_val(0, 200000000)); // 200 milliseconds
67     ps.set_stop_timeout(time_val(10,0));
68 }
69
70 // Regular service start
71 void test_proc_service_start()
72 {
73     using namespace std;
74
75     service_set sset;
76
77     string command = "test-command";
78     list<pair<unsigned,unsigned>> command_offsets;
79     command_offsets.emplace_back(0, command.length());
80     std::list<prelim_dep> depends;
81
82     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
83     init_service_defaults(p);
84     sset.add_service(&p);
85
86     p.start(true);
87     sset.process_queues();
88
89     assert(p.get_state() == service_state_t::STARTING);
90
91     base_process_service_test::exec_succeeded(&p);
92     sset.process_queues();
93
94     assert(p.get_state() == service_state_t::STARTED);
95     assert(event_loop.active_timers.size() == 0);
96
97     sset.remove_service(&p);
98 }
99
100 // Test start with readiness notification
101 void test_proc_notify_start()
102 {
103     using namespace std;
104
105     service_set sset;
106
107     string command = "test-command";
108     list<pair<unsigned,unsigned>> command_offsets;
109     command_offsets.emplace_back(0, command.length());
110     std::list<prelim_dep> depends;
111
112     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
113     init_service_defaults(p);
114     p.set_notification_fd(3);
115     sset.add_service(&p);
116
117     p.start(true);
118     sset.process_queues();
119
120     assert(p.get_state() == service_state_t::STARTING);
121
122     base_process_service_test::exec_succeeded(&p);
123     sset.process_queues();
124
125     assert(p.get_state() == service_state_t::STARTING);
126
127     int nfd = base_process_service_test::get_notification_fd(&p);
128     assert(nfd > 0);
129
130     char notifystr[] = "ok started\n";
131     std::vector<char> rnotifystr;
132     rnotifystr.insert(rnotifystr.end(), notifystr, notifystr + sizeof(notifystr));
133     bp_sys::supply_read_data(nfd, std::move(rnotifystr));
134
135     event_loop.regd_fd_watchers[nfd]->fd_event(event_loop, nfd, dasynq::IN_EVENTS);
136
137     assert(p.get_state() == service_state_t::STARTED);
138     assert(event_loop.active_timers.size() == 0);
139
140     sset.remove_service(&p);
141 }
142
143 // Unexpected termination
144 void test_proc_unexpected_term()
145 {
146     using namespace std;
147
148     service_set sset;
149
150     string command = "test-command";
151     list<pair<unsigned,unsigned>> command_offsets;
152     command_offsets.emplace_back(0, command.length());
153     std::list<prelim_dep> depends;
154
155     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
156     init_service_defaults(p);
157     sset.add_service(&p);
158
159     p.start(true);
160     sset.process_queues();
161
162     base_process_service_test::exec_succeeded(&p);
163     sset.process_queues();
164
165     assert(p.get_state() == service_state_t::STARTED);
166
167     base_process_service_test::handle_exit(&p, 0);
168
169     assert(p.get_state() == service_state_t::STOPPED);
170     assert(p.get_stop_reason() == stopped_reason_t::TERMINATED);
171     assert(event_loop.active_timers.size() == 0);
172
173     sset.remove_service(&p);
174 }
175
176 // Unexpected termination with restart
177 void test_proc_term_restart()
178 {
179     using namespace std;
180
181     service_set sset;
182
183     string command = "test-command";
184     list<pair<unsigned,unsigned>> command_offsets;
185     command_offsets.emplace_back(0, command.length());
186     std::list<prelim_dep> depends;
187
188     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
189     init_service_defaults(p);
190     p.set_auto_restart(true);
191     sset.add_service(&p);
192
193     p.start(true);
194     sset.process_queues();
195
196     base_process_service_test::exec_succeeded(&p);
197     sset.process_queues();
198
199     assert(p.get_state() == service_state_t::STARTED);
200     assert(event_loop.active_timers.size() == 0);
201
202     base_process_service_test::handle_exit(&p, 0);
203     sset.process_queues();
204
205     // Starting, restart timer should be armed:
206     assert(p.get_state() == service_state_t::STARTING);
207     assert(event_loop.active_timers.size() == 1);
208
209     event_loop.advance_time(time_val(0, 200000000));
210     assert(event_loop.active_timers.size() == 0);
211
212     sset.process_queues();
213     base_process_service_test::exec_succeeded(&p);
214     sset.process_queues();
215
216     assert(p.get_state() == service_state_t::STARTED);
217     assert(event_loop.active_timers.size() == 0);
218
219     sset.remove_service(&p);
220 }
221
222 void test_proc_term_restart2()
223 {
224     using namespace std;
225
226     service_set sset;
227
228     string command = "test-command";
229     list<pair<unsigned,unsigned>> command_offsets;
230     command_offsets.emplace_back(0, command.length());
231     std::list<prelim_dep> depends;
232
233     service_record b {&sset, "boot"};
234     sset.add_service(&b);
235
236     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
237     init_service_defaults(p);
238     p.set_auto_restart(true);
239     sset.add_service(&p);
240
241     b.add_dep(&p, WAITS);
242
243     b.start(true);
244     sset.process_queues();
245
246     assert(p.get_state() == service_state_t::STARTING);
247
248     base_process_service_test::exec_succeeded(&p);
249     sset.process_queues();
250
251     assert(p.get_state() == service_state_t::STARTED);
252     assert(event_loop.active_timers.size() == 0);
253
254     base_process_service_test::handle_exit(&p, 0);
255     sset.process_queues();
256
257     // Starting, restart timer should be armed:
258     assert(p.get_state() == service_state_t::STARTING);
259     assert(event_loop.active_timers.size() == 1);
260
261     event_loop.advance_time(time_val(0, 200000000));
262     assert(event_loop.active_timers.size() == 0);
263
264     sset.process_queues();
265     base_process_service_test::exec_succeeded(&p);
266     sset.process_queues();
267
268     assert(p.get_state() == service_state_t::STARTED);
269     assert(event_loop.active_timers.size() == 0);
270
271     sset.remove_service(&p);
272     sset.remove_service(&b);
273 }
274
275
276 // Termination via stop request
277 void test_term_via_stop()
278 {
279     using namespace std;
280
281     service_set sset;
282
283     string command = "test-command";
284     list<pair<unsigned,unsigned>> command_offsets;
285     command_offsets.emplace_back(0, command.length());
286     std::list<prelim_dep> depends;
287
288     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
289     init_service_defaults(p);
290     sset.add_service(&p);
291
292     p.start(true);
293     sset.process_queues();
294
295     base_process_service_test::exec_succeeded(&p);
296     sset.process_queues();
297
298     assert(p.get_state() == service_state_t::STARTED);
299     assert(event_loop.active_timers.size() == 0);
300
301     p.stop(true);
302     sset.process_queues();
303
304     assert(p.get_state() == service_state_t::STOPPING);
305     assert(event_loop.active_timers.size() == 1);
306
307     base_process_service_test::handle_exit(&p, 0);
308     sset.process_queues();
309
310     assert(p.get_state() == service_state_t::STOPPED);
311     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
312     assert(event_loop.active_timers.size() == 0);
313
314     sset.remove_service(&p);
315 }
316
317 // Termination via stop request, ensure reason is reset:
318 void test_term_via_stop2()
319 {
320     using namespace std;
321
322     service_set sset;
323
324     string command = "test-command";
325     list<pair<unsigned,unsigned>> command_offsets;
326     command_offsets.emplace_back(0, command.length());
327     std::list<prelim_dep> depends;
328
329     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
330     init_service_defaults(p);
331     sset.add_service(&p);
332
333     p.start(true);
334     sset.process_queues();
335
336     // first set it up with failure reason:
337
338     base_process_service_test::exec_failed(&p, ENOENT);
339     sset.process_queues();
340
341     assert(p.get_state() == service_state_t::STOPPED);
342     assert(p.get_stop_reason() == stopped_reason_t::EXECFAILED);
343
344     // now restart clean:
345
346     p.start(true);
347     sset.process_queues();
348
349     base_process_service_test::exec_succeeded(&p);
350     sset.process_queues();
351
352     assert(p.get_state() == service_state_t::STARTED);
353     assert(event_loop.active_timers.size() == 0);
354
355     p.stop(true);
356     sset.process_queues();
357     assert(p.get_state() == service_state_t::STOPPING);
358
359     base_process_service_test::handle_exit(&p, 0);
360     sset.process_queues();
361     assert(p.get_state() == service_state_t::STOPPED);
362     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
363     assert(event_loop.active_timers.size() == 0);
364
365     sset.remove_service(&p);
366 }
367
368 // Time-out during start
369 void test_proc_start_timeout()
370 {
371     using namespace std;
372
373     service_set sset;
374
375     string command = "test-command";
376     list<pair<unsigned,unsigned>> command_offsets;
377     command_offsets.emplace_back(0, command.length());
378     std::list<prelim_dep> depends;
379
380     scripted_service p {&sset, "testproc", std::move(command), command_offsets, depends};
381     init_service_defaults(p);
382     p.set_start_timeout(time_val(10,0));
383     sset.add_service(&p);
384
385     p.start(true);
386     sset.process_queues();
387
388     assert(p.get_state() == service_state_t::STARTING);
389
390     event_loop.advance_time(time_val(10,0));
391     sset.process_queues();
392
393     assert(p.get_state() == service_state_t::STOPPING);
394
395     base_process_service_test::handle_signal_exit(&p, SIGTERM);
396     sset.process_queues();
397
398     // We set no stop script, so state should now be STOPPED with no timer set
399     assert(p.get_state() == service_state_t::STOPPED);
400     assert(p.get_stop_reason() == stopped_reason_t::TIMEDOUT);
401     assert(event_loop.active_timers.size() == 0);
402
403     sset.remove_service(&p);
404 }
405
406 // Test that a timeout doesn't stop a "waits for" dependent to fail to start
407 void test_proc_start_timeout2()
408 {
409     using namespace std;
410
411     service_set sset;
412
413     string command = "test-command";
414     list<pair<unsigned,unsigned>> command_offsets;
415     command_offsets.emplace_back(0, command.length());
416     std::list<prelim_dep> depends;
417
418     scripted_service p {&sset, "testproc", std::move(command), command_offsets, depends};
419     p.set_start_timeout(time_val {1,0});
420     init_service_defaults(p);
421     sset.add_service(&p);
422
423     service_record ts {&sset, "test-service-1", service_type_t::INTERNAL,
424         {{&p, dependency_type::WAITS_FOR}} };
425
426     ts.start(true);
427     sset.process_queues();
428
429     assert(p.get_state() == service_state_t::STARTING);
430     assert(ts.get_state() == service_state_t::STARTING);
431
432     event_loop.advance_time(time_val {1,0}); // start timer should expire
433     sset.process_queues();
434
435     assert(p.get_state() == service_state_t::STOPPING);
436
437     base_process_service_test::handle_exit(&p, 0);
438     sset.process_queues();
439
440     assert(p.get_state() == service_state_t::STOPPED);
441     assert(p.get_stop_reason() == stopped_reason_t::TIMEDOUT);
442     assert(ts.get_state() == service_state_t::STARTED);
443     assert(event_loop.active_timers.size() == 0);
444
445     sset.remove_service(&p);
446 }
447
448 // Test exec() failure for process service start.
449 void test_proc_start_execfail()
450 {
451     using namespace std;
452
453     service_set sset;
454
455     string command = "test-command";
456     list<pair<unsigned,unsigned>> command_offsets;
457     command_offsets.emplace_back(0, command.length());
458     std::list<prelim_dep> depends;
459
460     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
461     init_service_defaults(p);
462     sset.add_service(&p);
463
464     p.start(true);
465     sset.process_queues();
466
467     assert(p.get_state() == service_state_t::STARTING);
468
469     base_process_service_test::exec_failed(&p, ENOENT);
470     sset.process_queues();
471
472     assert(p.get_state() == service_state_t::STOPPED);
473     assert(p.get_stop_reason() == stopped_reason_t::EXECFAILED);
474     assert(event_loop.active_timers.size() == 0);
475
476     sset.remove_service(&p);
477 }
478
479 // Test no ready notification before process terminates
480 void test_proc_notify_fail()
481 {
482     using namespace std;
483
484     service_set sset;
485
486     string command = "test-command";
487     list<pair<unsigned,unsigned>> command_offsets;
488     command_offsets.emplace_back(0, command.length());
489     std::list<prelim_dep> depends;
490
491     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
492     init_service_defaults(p);
493     p.set_notification_fd(3);
494     sset.add_service(&p);
495
496     p.start(true);
497     sset.process_queues();
498
499     assert(p.get_state() == service_state_t::STARTING);
500
501     base_process_service_test::exec_succeeded(&p);
502     sset.process_queues();
503
504     assert(p.get_state() == service_state_t::STARTING);
505
506     int nfd = base_process_service_test::get_notification_fd(&p);
507     assert(nfd > 0);
508
509     // Signal EOF on notify fd:
510     event_loop.regd_fd_watchers[nfd]->fd_event(event_loop, nfd, dasynq::IN_EVENTS);
511
512     assert(p.get_state() == service_state_t::STOPPING);
513
514     base_process_service_test::handle_exit(&p, 0);
515     sset.process_queues();
516
517     assert(p.get_state() == service_state_t::STOPPED);
518     assert(event_loop.active_timers.size() == 0);
519
520     sset.remove_service(&p);
521 }
522
523 // Test stop timeout
524 void test_proc_stop_timeout()
525 {
526     using namespace std;
527
528     service_set sset;
529
530     string command = "test-command";
531     list<pair<unsigned,unsigned>> command_offsets;
532     command_offsets.emplace_back(0, command.length());
533     std::list<prelim_dep> depends;
534
535     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
536     init_service_defaults(p);
537     p.set_stop_timeout(time_val {10, 0});
538     sset.add_service(&p);
539
540     p.start(true);
541     sset.process_queues();
542
543     assert(p.get_state() == service_state_t::STARTING);
544
545     base_process_service_test::exec_succeeded(&p);
546     sset.process_queues();
547
548     assert(p.get_state() == service_state_t::STARTED);
549
550     p.stop(true);
551     sset.process_queues();
552
553     assert(p.get_state() == service_state_t::STOPPING);
554     assert(bp_sys::last_sig_sent == SIGTERM);
555
556     event_loop.advance_time(time_val {10, 0}); // expire stop timer
557     sset.process_queues();
558
559     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
560     assert(p.get_state() == service_state_t::STOPPING);
561     assert(bp_sys::last_sig_sent == SIGKILL);
562
563     base_process_service_test::handle_exit(&p, 0);
564     sset.process_queues();
565
566     assert(p.get_state() == service_state_t::STOPPED);
567     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
568
569     assert(event_loop.active_timers.size() == 0);
570
571     sset.remove_service(&p);
572 }
573
574 // Smooth recovery
575 void test_proc_smooth_recovery1()
576 {
577     using namespace std;
578
579     service_set sset;
580
581     string command = "test-command";
582     list<pair<unsigned,unsigned>> command_offsets;
583     command_offsets.emplace_back(0, command.length());
584     std::list<prelim_dep> depends;
585
586     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
587     init_service_defaults(p);
588     p.set_smooth_recovery(true);
589     p.set_restart_delay(time_val {0, 1000});
590     sset.add_service(&p);
591
592     p.start(true);
593     sset.process_queues();
594
595     base_process_service_test::exec_succeeded(&p);
596     sset.process_queues();
597
598     pid_t first_instance = bp_sys::last_forked_pid;
599
600     assert(p.get_state() == service_state_t::STARTED);
601
602     base_process_service_test::handle_exit(&p, 0);
603     sset.process_queues();
604
605     // since time hasn't been changed, we expect that the process has not yet been re-launched:
606     assert(first_instance == bp_sys::last_forked_pid);
607     assert(p.get_state() == service_state_t::STARTED);
608
609     event_loop.advance_time(time_val {0, 1000});
610     sset.process_queues();
611
612     // Now a new process should've been launched:
613     assert(first_instance + 1 == bp_sys::last_forked_pid);
614     assert(p.get_state() == service_state_t::STARTED);
615
616     assert(event_loop.active_timers.size() == 0);
617
618     sset.remove_service(&p);
619 }
620
621 // Smooth recovery without restart delay
622 void test_proc_smooth_recovery2()
623 {
624     using namespace std;
625
626     service_set sset;
627
628     string command = "test-command";
629     list<pair<unsigned,unsigned>> command_offsets;
630     command_offsets.emplace_back(0, command.length());
631     std::list<prelim_dep> depends;
632
633     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
634     init_service_defaults(p);
635     p.set_smooth_recovery(true);
636     p.set_restart_delay(time_val(0, 0));
637     sset.add_service(&p);
638
639     p.start(true);
640     sset.process_queues();
641
642     base_process_service_test::exec_succeeded(&p);
643     sset.process_queues();
644
645     pid_t first_instance = bp_sys::last_forked_pid;
646
647     assert(p.get_state() == service_state_t::STARTED);
648     assert(event_loop.active_timers.size() == 0);
649
650     base_process_service_test::handle_exit(&p, 0);
651     sset.process_queues();
652
653     // no restart delay, process should restart immediately:
654     assert(first_instance + 1 == bp_sys::last_forked_pid);
655     assert(p.get_state() == service_state_t::STARTED);
656     assert(event_loop.active_timers.size() == 0);
657
658     sset.remove_service(&p);
659 }
660
661 // Test stop timeout
662 void test_scripted_stop_timeout()
663 {
664     using namespace std;
665
666     service_set sset;
667
668     string command = "test-command";
669     string stopcommand = "stop-command";
670     list<pair<unsigned,unsigned>> command_offsets;
671     command_offsets.emplace_back(0, command.length());
672     std::list<prelim_dep> depends;
673
674     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
675     init_service_defaults(p);
676     p.set_stop_command(stopcommand, command_offsets);
677     p.set_stop_timeout(time_val {10, 0});
678     sset.add_service(&p);
679
680     p.start(true);
681     sset.process_queues();
682
683     assert(p.get_state() == service_state_t::STARTING);
684
685     base_process_service_test::exec_succeeded(&p);
686     sset.process_queues();
687     base_process_service_test::handle_exit(&p, 0);
688     sset.process_queues();
689
690     assert(p.get_state() == service_state_t::STARTED);
691
692     p.stop(true);
693     sset.process_queues();
694
695     assert(p.get_state() == service_state_t::STOPPING);
696
697     base_process_service_test::exec_succeeded(&p);
698     sset.process_queues();
699
700     // should still be stopping:
701     assert(p.get_state() == service_state_t::STOPPING);
702
703     event_loop.advance_time(time_val {10, 0}); // expire stop timer
704     sset.process_queues();
705
706     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
707     assert(p.get_state() == service_state_t::STOPPING);
708     assert(bp_sys::last_sig_sent == SIGKILL);
709
710     base_process_service_test::handle_exit(&p, SIGKILL);
711     sset.process_queues();
712
713     assert(p.get_state() == service_state_t::STOPPED);
714     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
715
716     assert(event_loop.active_timers.size() == 0);
717
718     sset.remove_service(&p);
719 }
720
721 void test_scripted_start_fail()
722 {
723     using namespace std;
724
725     service_set sset;
726
727     string command = "test-command";
728     string stopcommand = "stop-command";
729     list<pair<unsigned,unsigned>> command_offsets;
730     command_offsets.emplace_back(0, command.length());
731     std::list<prelim_dep> depends;
732
733     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
734     init_service_defaults(p);
735     p.set_stop_command(stopcommand, command_offsets);
736     sset.add_service(&p);
737
738     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
739     service_record *s3 = new service_record(&sset, "test-service-3",
740             service_type_t::INTERNAL, {{&p, REG}, {s2, REG}});
741     sset.add_service(s2);
742     sset.add_service(s3);
743
744     s3->start(true);
745     sset.process_queues();
746
747     assert(p.get_state() == service_state_t::STARTING);
748
749     base_process_service_test::exec_succeeded(&p);
750     sset.process_queues();
751     base_process_service_test::handle_exit(&p, 0x1);  // exit fail
752     sset.process_queues();
753
754     // failed to start:
755     assert(p.get_state() == service_state_t::STOPPED);
756     assert(s2->get_state() == service_state_t::STOPPED);
757     assert(s3->get_state() == service_state_t::STOPPED);
758     assert(p.get_stop_reason() == stopped_reason_t::FAILED);
759     assert(s2->get_stop_reason() == stopped_reason_t::DEPFAILED);
760     assert(s3->get_stop_reason() == stopped_reason_t::DEPFAILED);
761
762     event_loop.active_timers.clear();
763     sset.remove_service(&p);
764
765     assert(sset.count_active_services() == 0);
766 }
767
768 void test_scripted_stop_fail()
769 {
770     using namespace std;
771
772     service_set sset;
773
774     string command = "test-command";
775     string stopcommand = "stop-command";
776     list<pair<unsigned,unsigned>> command_offsets;
777     command_offsets.emplace_back(0, command.length());
778     std::list<prelim_dep> depends;
779
780     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
781     init_service_defaults(p);
782     p.set_stop_command(stopcommand, command_offsets);
783     sset.add_service(&p);
784
785     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {});
786     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL,
787             {{s2, REG}, {&p, REG}});
788     service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL,
789             {{&p, REG}, {s3, REG}});
790     sset.add_service(s2);
791     sset.add_service(s3);
792     sset.add_service(s4);
793
794     s4->start(true);
795     sset.process_queues();
796
797     base_process_service_test::exec_succeeded(&p);
798     sset.process_queues();
799     base_process_service_test::handle_exit(&p, 0x0);  // success
800     sset.process_queues();
801
802     assert(p.get_state() == service_state_t::STARTED);
803     assert(s2->get_state() == service_state_t::STARTED);
804     assert(s3->get_state() == service_state_t::STARTED);
805     assert(s4->get_state() == service_state_t::STARTED);
806
807     pid_t last_forked = bp_sys::last_forked_pid;
808
809     s4->stop(true);
810     sset.process_queues();
811
812     base_process_service_test::exec_succeeded(&p);
813     sset.process_queues();
814     base_process_service_test::handle_exit(&p, 0x1);  // failure
815     sset.process_queues();
816
817     // The stop command should be executed once:
818     assert((bp_sys::last_forked_pid - last_forked) == 1);
819
820     assert(p.get_state() == service_state_t::STOPPED);
821     assert(s2->get_state() == service_state_t::STOPPED);
822     assert(s3->get_state() == service_state_t::STOPPED);
823     assert(s4->get_state() == service_state_t::STOPPED);
824
825     event_loop.active_timers.clear();
826     sset.remove_service(&p);
827 }
828
829 void test_scripted_start_skip()
830 {
831     using namespace std;
832
833     service_set sset;
834
835     string command = "test-command";
836     list<pair<unsigned,unsigned>> command_offsets;
837     command_offsets.emplace_back(0, command.length());
838     std::list<prelim_dep> depends;
839
840     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
841     init_service_defaults(p);
842     service_flags_t sflags;
843     sflags.skippable = true;
844     p.set_flags(sflags);
845     sset.add_service(&p);
846
847     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
848     sset.add_service(s2);
849
850     s2->start(true);
851     sset.process_queues();
852     assert(p.get_state() == service_state_t::STARTING);
853
854     base_process_service_test::exec_succeeded(&p);
855     sset.process_queues();
856     assert(p.get_state() == service_state_t::STARTING);
857
858     base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
859     sset.process_queues();
860
861     assert(p.get_state() == service_state_t::STARTED);
862     assert(s2->get_state() == service_state_t::STARTED);
863     assert(p.was_start_skipped());
864     assert(! s2->was_start_skipped());
865     assert(sset.count_active_services() == 2);
866
867     s2->stop(true);
868     sset.process_queues();
869
870     assert(p.get_state() == service_state_t::STOPPED);
871     assert(s2->get_state() == service_state_t::STOPPED);
872     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
873     assert(s2->get_stop_reason() == stopped_reason_t::NORMAL);
874     assert(sset.count_active_services() == 0);
875
876     event_loop.active_timers.clear();
877     sset.remove_service(&p);
878 }
879
880 // Test interrupting start of a service marked skippable
881 void test_scripted_start_skip2()
882 {
883     using namespace std;
884
885     service_set sset;
886
887     string command = "test-command";
888     list<pair<unsigned,unsigned>> command_offsets;
889     command_offsets.emplace_back(0, command.length());
890     std::list<prelim_dep> depends;
891
892     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
893     init_service_defaults(p);
894     service_flags_t sflags;
895     sflags.skippable = true;
896     sflags.start_interruptible = true;
897     p.set_flags(sflags);
898     sset.add_service(&p);
899
900     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
901     sset.add_service(s2);
902
903     s2->start(true);
904     sset.process_queues();
905     assert(p.get_state() == service_state_t::STARTING);
906
907     base_process_service_test::exec_succeeded(&p);
908     sset.process_queues();
909     assert(p.get_state() == service_state_t::STARTING);
910
911     s2->stop(true);  // abort startup; p should be cancelled
912     sset.process_queues();
913
914     assert(p.get_state() == service_state_t::STOPPING);
915
916     base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
917     sset.process_queues();
918
919     assert(p.get_state() == service_state_t::STOPPED);
920     assert(s2->get_state() == service_state_t::STOPPED);
921     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
922     assert(s2->get_stop_reason() == stopped_reason_t::NORMAL);
923     assert(sset.count_active_services() == 0);
924
925     event_loop.active_timers.clear();
926     sset.remove_service(&p);
927 }
928
929 // Test that starting a service with a waits-for dependency on another - currently stopping - service,
930 // causes that service to re-start.
931 void test_waitsfor_restart()
932 {
933     using namespace std;
934
935     service_set sset;
936
937     string command = "test-command";
938     list<pair<unsigned,unsigned>> command_offsets;
939     command_offsets.emplace_back(0, command.length());
940     std::list<prelim_dep> depends;
941
942     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
943     init_service_defaults(p);
944     sset.add_service(&p);
945
946     service_record tp {&sset, "test-service", service_type_t::INTERNAL, {{&p, WAITS}}};
947     sset.add_service(&tp);
948
949     // start p:
950
951     p.start(true);
952     sset.process_queues();
953
954     assert(p.get_state() == service_state_t::STARTING);
955
956     base_process_service_test::exec_succeeded(&p);
957     sset.process_queues();
958
959     assert(p.get_state() == service_state_t::STARTED);
960     assert(event_loop.active_timers.size() == 0);
961
962     // begin stopping p:
963
964     p.stop(true);
965     sset.process_queues();
966
967     assert(p.get_state() == service_state_t::STOPPING);
968
969     // start tp (which waits-for p):
970
971     tp.start(true);
972     sset.process_queues();
973
974     assert(tp.get_state() == service_state_t::STARTING);
975     assert(p.get_state() == service_state_t::STOPPING);
976
977     // p terminates (finishes stopping). Then it should re-start...
978     base_process_service_test::handle_signal_exit(&p, SIGTERM);
979     sset.process_queues();
980
981     assert(tp.get_state() == service_state_t::STARTING);
982     assert(p.get_state() == service_state_t::STARTING);
983
984     base_process_service_test::exec_succeeded(&p);
985     sset.process_queues();
986
987     assert(tp.get_state() == service_state_t::STARTED);
988     assert(p.get_state() == service_state_t::STARTED);
989
990     sset.remove_service(&tp);
991     sset.remove_service(&p);
992 }
993
994
995 #define RUN_TEST(name, spacing) \
996     std::cout << #name "..." spacing << std::flush; \
997     name(); \
998     std::cout << "PASSED" << std::endl;
999
1000 int main(int argc, char **argv)
1001 {
1002     RUN_TEST(test_proc_service_start, "   ");
1003     RUN_TEST(test_proc_notify_start, "    ");
1004     RUN_TEST(test_proc_unexpected_term, " ");
1005     RUN_TEST(test_proc_term_restart, "    ");
1006     RUN_TEST(test_proc_term_restart2, "   ");
1007     RUN_TEST(test_term_via_stop, "        ");
1008     RUN_TEST(test_term_via_stop2, "       ");
1009     RUN_TEST(test_proc_start_timeout, "   ");
1010     RUN_TEST(test_proc_start_timeout2, "  ");
1011     RUN_TEST(test_proc_start_execfail, "  ");
1012     RUN_TEST(test_proc_notify_fail, "     ");
1013     RUN_TEST(test_proc_stop_timeout, "    ");
1014     RUN_TEST(test_proc_smooth_recovery1, "");
1015     RUN_TEST(test_proc_smooth_recovery2, "");
1016     RUN_TEST(test_scripted_stop_timeout, "");
1017     RUN_TEST(test_scripted_start_fail, "  ");
1018     RUN_TEST(test_scripted_stop_fail, "   ");
1019     RUN_TEST(test_scripted_start_skip, "  ");
1020     RUN_TEST(test_scripted_start_skip2, " ");
1021     RUN_TEST(test_waitsfor_restart, "     ");
1022 }