~jamesodhunt/upstart/test-quiesce-cleanup

« back to all changes in this revision

Viewing changes to init/quiesce.c

  • Committer: Scott James Remnant
  • Date: 2009-07-08 19:43:16 UTC
  • Revision ID: scott@netsplit.com-20090708194316-t6rw4e8auuza6qju
* conf/control-alt-delete.conf: Default job for Control-Alt-Delete
* conf/rc-sysinit.conf: Default job for system initialisation
* conf/rc.conf: A fully wacky instance job that runs the rc script
for runlevel changes
* conf/rcS.conf: And a job for single-user-mode, which calls back
to rc-sysinit
* conf/Makefile.am (dist_init_DATA): Install the default files
into the /etc/init directory
* configure.ac (AC_CONFIG_FILES): Create conf/Makefile
* Makefile.am (SUBDIRS): Recurse into the conf directory.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* upstart
2
 
 *
3
 
 * quiesce.c - shutdown handling.
4
 
 *
5
 
 * Copyright © 2013 Canonical Ltd.
6
 
 * Author: James Hunt <james.hunt@canonical.com>
7
 
 *
8
 
 * This program is free software; you can redistribute it and/or modify
9
 
 * it under the terms of the GNU General Public License version 2, as
10
 
 * published by the Free Software Foundation.
11
 
 *
12
 
 * This program is distributed in the hope that it will be useful,
13
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
 * GNU General Public License for more details.
16
 
 *
17
 
 * You should have received a copy of the GNU General Public License along
18
 
 * with this program; if not, write to the Free Software Foundation, Inc.,
19
 
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
 
 */
21
 
 
22
 
#ifdef HAVE_CONFIG_H
23
 
# include <config.h>
24
 
#endif /* HAVE_CONFIG_H */
25
 
 
26
 
#include "quiesce.h"
27
 
#include "events.h"
28
 
#include "environ.h"
29
 
#include "conf.h"
30
 
#include "job_process.h"
31
 
#include "control.h"
32
 
 
33
 
#include <nih/main.h>
34
 
 
35
 
/**
36
 
 * quiesce_requester:
37
 
 *
38
 
 * Where the quiesce request originated. This determines
39
 
 * the shutdown behaviour.
40
 
 **/
41
 
static QuiesceRequester quiesce_requester = QUIESCE_REQUESTER_INVALID;
42
 
 
43
 
/**
44
 
 * quiesce_phase:
45
 
 *
46
 
 * Current phase of shutdown.
47
 
 **/
48
 
static QuiescePhase quiesce_phase = QUIESCE_PHASE_NOT_QUIESCED;
49
 
 
50
 
/**
51
 
 * max_kill_timeout:
52
 
 *
53
 
 * Maxiumum kill_timout value calculated from all running jobs used to
54
 
 * determine how long to wait before exiting.
55
 
 **/
56
 
static time_t max_kill_timeout = 0;
57
 
 
58
 
/**
59
 
 * quiesce_phase_time:
60
 
 *
61
 
 * Time that a particular phase started.
62
 
 **/
63
 
static time_t quiesce_phase_time = 0;
64
 
 
65
 
/* External definitions */
66
 
extern int disable_respawn;
67
 
 
68
 
/**
69
 
 * quiesce:
70
 
 *
71
 
 * @requester: where the quiesce request originated.
72
 
 *
73
 
 * Commence Session Init shutdown.
74
 
 **/
75
 
void
76
 
quiesce (QuiesceRequester requester)
77
 
{
78
 
        nih_local char  **env = NULL;
79
 
        const char       *reason;
80
 
 
81
 
        job_class_init ();
82
 
 
83
 
        /* Quiesce already in progress */
84
 
        if (quiesce_phase != QUIESCE_PHASE_NOT_QUIESCED)
85
 
                return;
86
 
 
87
 
        quiesce_requester = requester;
88
 
 
89
 
        /* System shutdown skips the wait phase to ensure all running
90
 
         * jobs get signalled.
91
 
         *
92
 
         * Note that jobs which choose to start on SESSION_END_EVENT may
93
 
         * not complete (or even start), but no guarantee is possible in
94
 
         * the system shutdown scenario since Session Inits must not
95
 
         * hold up the system.
96
 
         */
97
 
        quiesce_phase = (requester == QUIESCE_REQUESTER_SYSTEM)
98
 
                ? QUIESCE_PHASE_KILL
99
 
                : QUIESCE_PHASE_WAIT;
100
 
 
101
 
        reason = (requester == QUIESCE_REQUESTER_SESSION)
102
 
                ? _("logout") : _("shutdown");
103
 
 
104
 
        nih_info (_("Quiescing due to %s request"), reason);
105
 
 
106
 
        quiesce_phase_time = time (NULL);
107
 
 
108
 
        /* Stop existing jobs from respawning */
109
 
        disable_respawn = TRUE;
110
 
 
111
 
        /* Signal that the session is ending. This may start new jobs.
112
 
         *
113
 
         * Note that the event doesn't actually get emitted until the
114
 
         * next time the main loop gets a chance to run.
115
 
         */
116
 
        env = NIH_MUST (nih_str_array_new (NULL));
117
 
 
118
 
        NIH_MUST (environ_set (&env, NULL, NULL, TRUE,
119
 
                                "TYPE=%s", reason));
120
 
 
121
 
        NIH_MUST (event_new (NULL, SESSION_END_EVENT, env));
122
 
 
123
 
        if (requester == QUIESCE_REQUESTER_SYSTEM) {
124
 
                /* We'll attempt to wait for this long, but system
125
 
                 * policy may prevent it such that we just get killed
126
 
                 * and job processes reparented to PID 1.
127
 
                 */
128
 
                max_kill_timeout = job_class_max_kill_timeout ();
129
 
 
130
 
                job_process_stop_all ();
131
 
        }
132
 
 
133
 
        /* Check every second to see if all jobs have finished. If so,
134
 
         * we can exit early.
135
 
         */
136
 
        NIH_MUST (nih_timer_add_periodic (NULL, 1,
137
 
                                (NihTimerCb)quiesce_wait_callback, NULL));
138
 
}
139
 
 
140
 
/**
141
 
 * quiesce_wait_callback:
142
 
 *
143
 
 * @data: not used,
144
 
 * @timer: timer that caused us to be called.
145
 
 *
146
 
 * Callback used to check if all jobs have finished and if so
147
 
 * finalise Session Init shutdown.
148
 
 **/
149
 
void
150
 
quiesce_wait_callback (void *data, NihTimer *timer)
151
 
{
152
 
        time_t now;
153
 
 
154
 
        nih_assert (timer);
155
 
        nih_assert (quiesce_phase_time);
156
 
 
157
 
        now = time (NULL);
158
 
 
159
 
        nih_assert (quiesce_requester != QUIESCE_REQUESTER_INVALID);
160
 
 
161
 
        if (quiesce_requester == QUIESCE_REQUESTER_SYSTEM) {
162
 
                nih_assert (quiesce_phase == QUIESCE_PHASE_KILL);
163
 
 
164
 
                if ((now - quiesce_phase_time) > max_kill_timeout)
165
 
                        goto out;
166
 
 
167
 
        } else if (quiesce_phase == QUIESCE_PHASE_WAIT) {
168
 
 
169
 
                if ((now - quiesce_phase_time) > QUIESCE_DEFAULT_JOB_RUNTIME) {
170
 
                        quiesce_phase = QUIESCE_PHASE_KILL;
171
 
 
172
 
                        /* reset for new phase */
173
 
                        quiesce_phase_time = time (NULL);
174
 
 
175
 
                        max_kill_timeout = job_class_max_kill_timeout ();
176
 
                        job_process_stop_all ();
177
 
                }
178
 
        } else if (quiesce_phase == QUIESCE_PHASE_KILL) {
179
 
 
180
 
                if ((now - quiesce_phase_time) > max_kill_timeout)
181
 
                        goto out;
182
 
        } else {
183
 
                nih_assert_not_reached ();
184
 
        }
185
 
 
186
 
        if (! job_process_jobs_running ())
187
 
                goto out;
188
 
 
189
 
        return;
190
 
 
191
 
out:
192
 
        quiesce_show_slow_jobs ();
193
 
 
194
 
        /* Note that we might skip the kill phase for the session
195
 
         * requestor if no jobs are actually running at this point.
196
 
         */
197
 
        quiesce_phase = QUIESCE_PHASE_CLEANUP;
198
 
        quiesce_finalise ();
199
 
 
200
 
        /* Deregister */
201
 
        nih_free (timer);
202
 
}
203
 
 
204
 
/**
205
 
 * quiesce_show_slow_jobs:
206
 
 *
207
 
 * List jobs that are still running after their expected end time.
208
 
 **/
209
 
void
210
 
quiesce_show_slow_jobs (void)
211
 
{
212
 
        job_class_init ();
213
 
 
214
 
        NIH_HASH_FOREACH (job_classes, iter) {
215
 
                JobClass *class = (JobClass *)iter;
216
 
 
217
 
                /* Note that instances get killed in a random order */
218
 
                NIH_HASH_FOREACH (class->instances, job_iter) {
219
 
                        const char  *name;
220
 
                        Job         *job;
221
 
                       
222
 
                        job = (Job *)job_iter;
223
 
 
224
 
                        name = job_name (job);
225
 
 
226
 
                        nih_warn ("job %s failed to stop", name);
227
 
                }
228
 
        }
229
 
}
230
 
 
231
 
 
232
 
/**
233
 
 * quiesce_finalise:
234
 
 *
235
 
 * Request shutdown.
236
 
 **/
237
 
void
238
 
quiesce_finalise (void)
239
 
{
240
 
        nih_assert (quiesce_phase == QUIESCE_PHASE_CLEANUP);
241
 
 
242
 
        nih_main_loop_exit (0);
243
 
}