b75eea344e5fae6ccba9e2b851acef9ddaf70a87
[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     // simulate process terminating, should then be restarted:
255     base_process_service_test::handle_exit(&p, 0);
256     sset.process_queues();
257
258     // Starting, restart timer should be armed:
259     assert(p.get_state() == service_state_t::STARTING);
260     assert(event_loop.active_timers.size() == 1);
261
262     event_loop.advance_time(time_val(0, 200000000));
263     assert(event_loop.active_timers.size() == 0);
264
265     sset.process_queues();
266     base_process_service_test::exec_succeeded(&p);
267     sset.process_queues();
268
269     assert(p.get_state() == service_state_t::STARTED);
270     assert(event_loop.active_timers.size() == 0);
271
272     assert(sset.count_active_services() == 2);
273
274     // Request stop, this time it should not restart:
275     p.stop(true);
276     sset.process_queues();
277     base_process_service_test::handle_exit(&p, 0);
278     sset.process_queues();
279
280     assert(p.get_state() == service_state_t::STOPPED);
281     assert(event_loop.active_timers.size() == 0);
282     assert(sset.count_active_services() == 1);
283
284     // simulate terminate dinit
285     sset.stop_all_services();
286
287     //base_process_service_test::handle_exit(&p, 0);
288     sset.process_queues();
289
290     assert(p.get_state() == service_state_t::STOPPED);
291     assert(event_loop.active_timers.size() == 0);
292     assert(sset.count_active_services() == 0);
293
294     sset.remove_service(&p);
295     sset.remove_service(&b);
296 }
297
298 // Termination via stop request
299 void test_term_via_stop()
300 {
301     using namespace std;
302
303     service_set sset;
304
305     string command = "test-command";
306     list<pair<unsigned,unsigned>> command_offsets;
307     command_offsets.emplace_back(0, command.length());
308     std::list<prelim_dep> depends;
309
310     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
311     init_service_defaults(p);
312     sset.add_service(&p);
313
314     p.start(true);
315     sset.process_queues();
316
317     base_process_service_test::exec_succeeded(&p);
318     sset.process_queues();
319
320     assert(p.get_state() == service_state_t::STARTED);
321     assert(event_loop.active_timers.size() == 0);
322
323     p.stop(true);
324     sset.process_queues();
325
326     assert(p.get_state() == service_state_t::STOPPING);
327     assert(event_loop.active_timers.size() == 1);
328
329     base_process_service_test::handle_exit(&p, 0);
330     sset.process_queues();
331
332     assert(p.get_state() == service_state_t::STOPPED);
333     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
334     assert(event_loop.active_timers.size() == 0);
335
336     sset.remove_service(&p);
337 }
338
339 // Termination via stop request, ensure reason is reset:
340 void test_term_via_stop2()
341 {
342     using namespace std;
343
344     service_set sset;
345
346     string command = "test-command";
347     list<pair<unsigned,unsigned>> command_offsets;
348     command_offsets.emplace_back(0, command.length());
349     std::list<prelim_dep> depends;
350
351     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
352     init_service_defaults(p);
353     sset.add_service(&p);
354
355     p.start(true);
356     sset.process_queues();
357
358     // first set it up with failure reason:
359
360     base_process_service_test::exec_failed(&p, ENOENT);
361     sset.process_queues();
362
363     assert(p.get_state() == service_state_t::STOPPED);
364     assert(p.get_stop_reason() == stopped_reason_t::EXECFAILED);
365
366     // now restart clean:
367
368     p.start(true);
369     sset.process_queues();
370
371     base_process_service_test::exec_succeeded(&p);
372     sset.process_queues();
373
374     assert(p.get_state() == service_state_t::STARTED);
375     assert(event_loop.active_timers.size() == 0);
376
377     p.stop(true);
378     sset.process_queues();
379     assert(p.get_state() == service_state_t::STOPPING);
380
381     base_process_service_test::handle_exit(&p, 0);
382     sset.process_queues();
383     assert(p.get_state() == service_state_t::STOPPED);
384     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
385     assert(event_loop.active_timers.size() == 0);
386
387     sset.remove_service(&p);
388 }
389
390 // Time-out during start
391 void test_proc_start_timeout()
392 {
393     using namespace std;
394
395     service_set sset;
396
397     string command = "test-command";
398     list<pair<unsigned,unsigned>> command_offsets;
399     command_offsets.emplace_back(0, command.length());
400     std::list<prelim_dep> depends;
401
402     scripted_service p {&sset, "testproc", std::move(command), command_offsets, depends};
403     init_service_defaults(p);
404     p.set_start_timeout(time_val(10,0));
405     sset.add_service(&p);
406
407     p.start(true);
408     sset.process_queues();
409
410     assert(p.get_state() == service_state_t::STARTING);
411
412     event_loop.advance_time(time_val(10,0));
413     sset.process_queues();
414
415     assert(p.get_state() == service_state_t::STOPPING);
416
417     base_process_service_test::handle_signal_exit(&p, SIGTERM);
418     sset.process_queues();
419
420     // We set no stop script, so state should now be STOPPED with no timer set
421     assert(p.get_state() == service_state_t::STOPPED);
422     assert(p.get_stop_reason() == stopped_reason_t::TIMEDOUT);
423     assert(event_loop.active_timers.size() == 0);
424
425     sset.remove_service(&p);
426 }
427
428 // Test that a timeout doesn't stop a "waits for" dependent to fail to start
429 void test_proc_start_timeout2()
430 {
431     using namespace std;
432
433     service_set sset;
434
435     string command = "test-command";
436     list<pair<unsigned,unsigned>> command_offsets;
437     command_offsets.emplace_back(0, command.length());
438     std::list<prelim_dep> depends;
439
440     scripted_service p {&sset, "testproc", std::move(command), command_offsets, depends};
441     p.set_start_timeout(time_val {1,0});
442     init_service_defaults(p);
443     sset.add_service(&p);
444
445     service_record ts {&sset, "test-service-1", service_type_t::INTERNAL,
446         {{&p, dependency_type::WAITS_FOR}} };
447
448     ts.start(true);
449     sset.process_queues();
450
451     assert(p.get_state() == service_state_t::STARTING);
452     assert(ts.get_state() == service_state_t::STARTING);
453
454     event_loop.advance_time(time_val {1,0}); // start timer should expire
455     sset.process_queues();
456
457     assert(p.get_state() == service_state_t::STOPPING);
458
459     base_process_service_test::handle_exit(&p, 0);
460     sset.process_queues();
461
462     assert(p.get_state() == service_state_t::STOPPED);
463     assert(p.get_stop_reason() == stopped_reason_t::TIMEDOUT);
464     assert(ts.get_state() == service_state_t::STARTED);
465     assert(event_loop.active_timers.size() == 0);
466
467     sset.remove_service(&p);
468 }
469
470 // Test exec() failure for process service start.
471 void test_proc_start_execfail()
472 {
473     using namespace std;
474
475     service_set sset;
476
477     string command = "test-command";
478     list<pair<unsigned,unsigned>> command_offsets;
479     command_offsets.emplace_back(0, command.length());
480     std::list<prelim_dep> depends;
481
482     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
483     init_service_defaults(p);
484     sset.add_service(&p);
485
486     p.start(true);
487     sset.process_queues();
488
489     assert(p.get_state() == service_state_t::STARTING);
490
491     base_process_service_test::exec_failed(&p, ENOENT);
492     sset.process_queues();
493
494     assert(p.get_state() == service_state_t::STOPPED);
495     assert(p.get_stop_reason() == stopped_reason_t::EXECFAILED);
496     assert(event_loop.active_timers.size() == 0);
497
498     sset.remove_service(&p);
499 }
500
501 // Test no ready notification before process terminates
502 void test_proc_notify_fail()
503 {
504     using namespace std;
505
506     service_set sset;
507
508     string command = "test-command";
509     list<pair<unsigned,unsigned>> command_offsets;
510     command_offsets.emplace_back(0, command.length());
511     std::list<prelim_dep> depends;
512
513     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
514     init_service_defaults(p);
515     p.set_notification_fd(3);
516     sset.add_service(&p);
517
518     p.start(true);
519     sset.process_queues();
520
521     assert(p.get_state() == service_state_t::STARTING);
522
523     base_process_service_test::exec_succeeded(&p);
524     sset.process_queues();
525
526     assert(p.get_state() == service_state_t::STARTING);
527
528     int nfd = base_process_service_test::get_notification_fd(&p);
529     assert(nfd > 0);
530
531     // Signal EOF on notify fd:
532     event_loop.regd_fd_watchers[nfd]->fd_event(event_loop, nfd, dasynq::IN_EVENTS);
533
534     assert(p.get_state() == service_state_t::STOPPING);
535
536     base_process_service_test::handle_exit(&p, 0);
537     sset.process_queues();
538
539     assert(p.get_state() == service_state_t::STOPPED);
540     assert(event_loop.active_timers.size() == 0);
541
542     sset.remove_service(&p);
543 }
544
545 // Test stop timeout
546 void test_proc_stop_timeout()
547 {
548     using namespace std;
549
550     service_set sset;
551
552     string command = "test-command";
553     list<pair<unsigned,unsigned>> command_offsets;
554     command_offsets.emplace_back(0, command.length());
555     std::list<prelim_dep> depends;
556
557     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
558     init_service_defaults(p);
559     p.set_stop_timeout(time_val {10, 0});
560     sset.add_service(&p);
561
562     p.start(true);
563     sset.process_queues();
564
565     assert(p.get_state() == service_state_t::STARTING);
566
567     base_process_service_test::exec_succeeded(&p);
568     sset.process_queues();
569
570     assert(p.get_state() == service_state_t::STARTED);
571
572     p.stop(true);
573     sset.process_queues();
574
575     assert(p.get_state() == service_state_t::STOPPING);
576     assert(bp_sys::last_sig_sent == SIGTERM);
577
578     event_loop.advance_time(time_val {10, 0}); // expire stop timer
579     sset.process_queues();
580
581     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
582     assert(p.get_state() == service_state_t::STOPPING);
583     assert(bp_sys::last_sig_sent == SIGKILL);
584
585     base_process_service_test::handle_exit(&p, 0);
586     sset.process_queues();
587
588     assert(p.get_state() == service_state_t::STOPPED);
589     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
590
591     assert(event_loop.active_timers.size() == 0);
592
593     sset.remove_service(&p);
594 }
595
596 // Smooth recovery
597 void test_proc_smooth_recovery1()
598 {
599     using namespace std;
600
601     service_set sset;
602
603     string command = "test-command";
604     list<pair<unsigned,unsigned>> command_offsets;
605     command_offsets.emplace_back(0, command.length());
606     std::list<prelim_dep> depends;
607
608     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
609     init_service_defaults(p);
610     p.set_smooth_recovery(true);
611     p.set_restart_delay(time_val {0, 1000});
612     sset.add_service(&p);
613
614     p.start(true);
615     sset.process_queues();
616
617     base_process_service_test::exec_succeeded(&p);
618     sset.process_queues();
619
620     pid_t first_instance = bp_sys::last_forked_pid;
621
622     assert(p.get_state() == service_state_t::STARTED);
623
624     base_process_service_test::handle_exit(&p, 0);
625     sset.process_queues();
626
627     // since time hasn't been changed, we expect that the process has not yet been re-launched:
628     assert(first_instance == bp_sys::last_forked_pid);
629     assert(p.get_state() == service_state_t::STARTED);
630
631     event_loop.advance_time(time_val {0, 1000});
632     sset.process_queues();
633
634     // Now a new process should've been launched:
635     assert(first_instance + 1 == bp_sys::last_forked_pid);
636     assert(p.get_state() == service_state_t::STARTED);
637
638     assert(event_loop.active_timers.size() == 0);
639
640     sset.remove_service(&p);
641 }
642
643 // Smooth recovery without restart delay
644 void test_proc_smooth_recovery2()
645 {
646     using namespace std;
647
648     service_set sset;
649
650     string command = "test-command";
651     list<pair<unsigned,unsigned>> command_offsets;
652     command_offsets.emplace_back(0, command.length());
653     std::list<prelim_dep> depends;
654
655     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
656     init_service_defaults(p);
657     p.set_smooth_recovery(true);
658     p.set_restart_delay(time_val(0, 0));
659     sset.add_service(&p);
660
661     p.start(true);
662     sset.process_queues();
663
664     base_process_service_test::exec_succeeded(&p);
665     sset.process_queues();
666
667     pid_t first_instance = bp_sys::last_forked_pid;
668
669     assert(p.get_state() == service_state_t::STARTED);
670     assert(event_loop.active_timers.size() == 0);
671
672     base_process_service_test::handle_exit(&p, 0);
673     sset.process_queues();
674
675     // no restart delay, process should restart immediately:
676     assert(first_instance + 1 == bp_sys::last_forked_pid);
677     assert(p.get_state() == service_state_t::STARTED);
678     assert(event_loop.active_timers.size() == 0);
679
680     sset.remove_service(&p);
681 }
682
683 // Test stop timeout
684 void test_scripted_stop_timeout()
685 {
686     using namespace std;
687
688     service_set sset;
689
690     string command = "test-command";
691     string stopcommand = "stop-command";
692     list<pair<unsigned,unsigned>> command_offsets;
693     command_offsets.emplace_back(0, command.length());
694     std::list<prelim_dep> depends;
695
696     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
697     init_service_defaults(p);
698     p.set_stop_command(stopcommand, command_offsets);
699     p.set_stop_timeout(time_val {10, 0});
700     sset.add_service(&p);
701
702     p.start(true);
703     sset.process_queues();
704
705     assert(p.get_state() == service_state_t::STARTING);
706
707     base_process_service_test::exec_succeeded(&p);
708     sset.process_queues();
709     base_process_service_test::handle_exit(&p, 0);
710     sset.process_queues();
711
712     assert(p.get_state() == service_state_t::STARTED);
713
714     p.stop(true);
715     sset.process_queues();
716
717     assert(p.get_state() == service_state_t::STOPPING);
718
719     base_process_service_test::exec_succeeded(&p);
720     sset.process_queues();
721
722     // should still be stopping:
723     assert(p.get_state() == service_state_t::STOPPING);
724
725     event_loop.advance_time(time_val {10, 0}); // expire stop timer
726     sset.process_queues();
727
728     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
729     assert(p.get_state() == service_state_t::STOPPING);
730     assert(bp_sys::last_sig_sent == SIGKILL);
731
732     base_process_service_test::handle_exit(&p, SIGKILL);
733     sset.process_queues();
734
735     assert(p.get_state() == service_state_t::STOPPED);
736     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
737
738     assert(event_loop.active_timers.size() == 0);
739
740     sset.remove_service(&p);
741 }
742
743 void test_scripted_start_fail()
744 {
745     using namespace std;
746
747     service_set sset;
748
749     string command = "test-command";
750     string stopcommand = "stop-command";
751     list<pair<unsigned,unsigned>> command_offsets;
752     command_offsets.emplace_back(0, command.length());
753     std::list<prelim_dep> depends;
754
755     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
756     init_service_defaults(p);
757     p.set_stop_command(stopcommand, command_offsets);
758     sset.add_service(&p);
759
760     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
761     service_record *s3 = new service_record(&sset, "test-service-3",
762             service_type_t::INTERNAL, {{&p, REG}, {s2, REG}});
763     sset.add_service(s2);
764     sset.add_service(s3);
765
766     s3->start(true);
767     sset.process_queues();
768
769     assert(p.get_state() == service_state_t::STARTING);
770
771     base_process_service_test::exec_succeeded(&p);
772     sset.process_queues();
773     base_process_service_test::handle_exit(&p, 0x1);  // exit fail
774     sset.process_queues();
775
776     // failed to start:
777     assert(p.get_state() == service_state_t::STOPPED);
778     assert(s2->get_state() == service_state_t::STOPPED);
779     assert(s3->get_state() == service_state_t::STOPPED);
780     assert(p.get_stop_reason() == stopped_reason_t::FAILED);
781     assert(s2->get_stop_reason() == stopped_reason_t::DEPFAILED);
782     assert(s3->get_stop_reason() == stopped_reason_t::DEPFAILED);
783
784     event_loop.active_timers.clear();
785     sset.remove_service(&p);
786
787     assert(sset.count_active_services() == 0);
788 }
789
790 void test_scripted_stop_fail()
791 {
792     using namespace std;
793
794     service_set sset;
795
796     string command = "test-command";
797     string stopcommand = "stop-command";
798     list<pair<unsigned,unsigned>> command_offsets;
799     command_offsets.emplace_back(0, command.length());
800     std::list<prelim_dep> depends;
801
802     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
803     init_service_defaults(p);
804     p.set_stop_command(stopcommand, command_offsets);
805     sset.add_service(&p);
806
807     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {});
808     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL,
809             {{s2, REG}, {&p, REG}});
810     service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL,
811             {{&p, REG}, {s3, REG}});
812     sset.add_service(s2);
813     sset.add_service(s3);
814     sset.add_service(s4);
815
816     s4->start(true);
817     sset.process_queues();
818
819     base_process_service_test::exec_succeeded(&p);
820     sset.process_queues();
821     base_process_service_test::handle_exit(&p, 0x0);  // success
822     sset.process_queues();
823
824     assert(p.get_state() == service_state_t::STARTED);
825     assert(s2->get_state() == service_state_t::STARTED);
826     assert(s3->get_state() == service_state_t::STARTED);
827     assert(s4->get_state() == service_state_t::STARTED);
828
829     pid_t last_forked = bp_sys::last_forked_pid;
830
831     s4->stop(true);
832     sset.process_queues();
833
834     base_process_service_test::exec_succeeded(&p);
835     sset.process_queues();
836     base_process_service_test::handle_exit(&p, 0x1);  // failure
837     sset.process_queues();
838
839     // The stop command should be executed once:
840     assert((bp_sys::last_forked_pid - last_forked) == 1);
841
842     assert(p.get_state() == service_state_t::STOPPED);
843     assert(s2->get_state() == service_state_t::STOPPED);
844     assert(s3->get_state() == service_state_t::STOPPED);
845     assert(s4->get_state() == service_state_t::STOPPED);
846
847     event_loop.active_timers.clear();
848     sset.remove_service(&p);
849 }
850
851 void test_scripted_start_skip()
852 {
853     using namespace std;
854
855     service_set sset;
856
857     string command = "test-command";
858     list<pair<unsigned,unsigned>> command_offsets;
859     command_offsets.emplace_back(0, command.length());
860     std::list<prelim_dep> depends;
861
862     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
863     init_service_defaults(p);
864     service_flags_t sflags;
865     sflags.skippable = true;
866     p.set_flags(sflags);
867     sset.add_service(&p);
868
869     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
870     sset.add_service(s2);
871
872     s2->start(true);
873     sset.process_queues();
874     assert(p.get_state() == service_state_t::STARTING);
875
876     base_process_service_test::exec_succeeded(&p);
877     sset.process_queues();
878     assert(p.get_state() == service_state_t::STARTING);
879
880     base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
881     sset.process_queues();
882
883     assert(p.get_state() == service_state_t::STARTED);
884     assert(s2->get_state() == service_state_t::STARTED);
885     assert(p.was_start_skipped());
886     assert(! s2->was_start_skipped());
887     assert(sset.count_active_services() == 2);
888
889     s2->stop(true);
890     sset.process_queues();
891
892     assert(p.get_state() == service_state_t::STOPPED);
893     assert(s2->get_state() == service_state_t::STOPPED);
894     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
895     assert(s2->get_stop_reason() == stopped_reason_t::NORMAL);
896     assert(sset.count_active_services() == 0);
897
898     event_loop.active_timers.clear();
899     sset.remove_service(&p);
900 }
901
902 // Test interrupting start of a service marked skippable
903 void test_scripted_start_skip2()
904 {
905     using namespace std;
906
907     service_set sset;
908
909     string command = "test-command";
910     list<pair<unsigned,unsigned>> command_offsets;
911     command_offsets.emplace_back(0, command.length());
912     std::list<prelim_dep> depends;
913
914     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
915     init_service_defaults(p);
916     service_flags_t sflags;
917     sflags.skippable = true;
918     sflags.start_interruptible = true;
919     p.set_flags(sflags);
920     sset.add_service(&p);
921
922     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
923     sset.add_service(s2);
924
925     s2->start(true);
926     sset.process_queues();
927     assert(p.get_state() == service_state_t::STARTING);
928
929     base_process_service_test::exec_succeeded(&p);
930     sset.process_queues();
931     assert(p.get_state() == service_state_t::STARTING);
932
933     s2->stop(true);  // abort startup; p should be cancelled
934     sset.process_queues();
935
936     assert(p.get_state() == service_state_t::STOPPING);
937
938     base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
939     sset.process_queues();
940
941     assert(p.get_state() == service_state_t::STOPPED);
942     assert(s2->get_state() == service_state_t::STOPPED);
943     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
944     assert(s2->get_stop_reason() == stopped_reason_t::NORMAL);
945     assert(sset.count_active_services() == 0);
946
947     event_loop.active_timers.clear();
948     sset.remove_service(&p);
949 }
950
951 // Test that starting a service with a waits-for dependency on another - currently stopping - service,
952 // causes that service to re-start.
953 void test_waitsfor_restart()
954 {
955     using namespace std;
956
957     service_set sset;
958
959     string command = "test-command";
960     list<pair<unsigned,unsigned>> command_offsets;
961     command_offsets.emplace_back(0, command.length());
962     std::list<prelim_dep> depends;
963
964     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
965     init_service_defaults(p);
966     sset.add_service(&p);
967
968     service_record tp {&sset, "test-service", service_type_t::INTERNAL, {{&p, WAITS}}};
969     sset.add_service(&tp);
970
971     // start p:
972
973     p.start(true);
974     sset.process_queues();
975
976     assert(p.get_state() == service_state_t::STARTING);
977
978     base_process_service_test::exec_succeeded(&p);
979     sset.process_queues();
980
981     assert(p.get_state() == service_state_t::STARTED);
982     assert(event_loop.active_timers.size() == 0);
983
984     // begin stopping p:
985
986     p.stop(true);
987     sset.process_queues();
988
989     assert(p.get_state() == service_state_t::STOPPING);
990
991     // start tp (which waits-for p):
992
993     tp.start(true);
994     sset.process_queues();
995
996     assert(tp.get_state() == service_state_t::STARTING);
997     assert(p.get_state() == service_state_t::STOPPING);
998
999     // p terminates (finishes stopping). Then it should re-start...
1000     base_process_service_test::handle_signal_exit(&p, SIGTERM);
1001     sset.process_queues();
1002
1003     assert(tp.get_state() == service_state_t::STARTING);
1004     assert(p.get_state() == service_state_t::STARTING);
1005
1006     base_process_service_test::exec_succeeded(&p);
1007     sset.process_queues();
1008
1009     assert(tp.get_state() == service_state_t::STARTED);
1010     assert(p.get_state() == service_state_t::STARTED);
1011
1012     sset.remove_service(&tp);
1013     sset.remove_service(&p);
1014 }
1015
1016
1017 #define RUN_TEST(name, spacing) \
1018     std::cout << #name "..." spacing << std::flush; \
1019     name(); \
1020     std::cout << "PASSED" << std::endl;
1021
1022 int main(int argc, char **argv)
1023 {
1024     RUN_TEST(test_proc_service_start, "   ");
1025     RUN_TEST(test_proc_notify_start, "    ");
1026     RUN_TEST(test_proc_unexpected_term, " ");
1027     RUN_TEST(test_proc_term_restart, "    ");
1028     RUN_TEST(test_proc_term_restart2, "   ");
1029     RUN_TEST(test_term_via_stop, "        ");
1030     RUN_TEST(test_term_via_stop2, "       ");
1031     RUN_TEST(test_proc_start_timeout, "   ");
1032     RUN_TEST(test_proc_start_timeout2, "  ");
1033     RUN_TEST(test_proc_start_execfail, "  ");
1034     RUN_TEST(test_proc_notify_fail, "     ");
1035     RUN_TEST(test_proc_stop_timeout, "    ");
1036     RUN_TEST(test_proc_smooth_recovery1, "");
1037     RUN_TEST(test_proc_smooth_recovery2, "");
1038     RUN_TEST(test_scripted_stop_timeout, "");
1039     RUN_TEST(test_scripted_start_fail, "  ");
1040     RUN_TEST(test_scripted_stop_fail, "   ");
1041     RUN_TEST(test_scripted_start_skip, "  ");
1042     RUN_TEST(test_scripted_start_skip2, " ");
1043     RUN_TEST(test_waitsfor_restart, "     ");
1044 }