~jamesodhunt/upstart/file-bridge-MP

« back to all changes in this revision

Viewing changes to init/quiesce.c

  • Committer: James Hunt
  • Date: 2013-02-25 09:28:24 UTC
  • mfrom: (1427.3.4 upstart-shutdown)
  • Revision ID: james.hunt@ubuntu.com-20130225092824-o1ju9qoryd53fijv
* Merge of lp:~jamesodhunt/upstart/upstart-shutdown.

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
                        nih_local const char  *name = NULL;
 
220
                        Job                   *job;
 
221
                       
 
222
                        job = (Job *)job_iter;
 
223
 
 
224
                        name = job_name (job);
 
225
 
 
226
                        nih_message ("job %s failed to stop", name);
 
227
                }
 
228
        }
 
229
}
 
230
 
 
231
 
 
232
/**
 
233
 * quiesce_finalise:
 
234
 *
 
235
 * Perform final shutdown operations.
 
236
 **/
 
237
void
 
238
quiesce_finalise (void)
 
239
{
 
240
        nih_assert (quiesce_phase == QUIESCE_PHASE_CLEANUP);
 
241
 
 
242
        /* Cleanup */
 
243
        conf_destroy ();
 
244
        session_destroy ();
 
245
        control_cleanup ();
 
246
 
 
247
        nih_main_loop_exit (0);
 
248
}