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