blob: fbca39fa5bbf587733b8a0381dcbfbfa581dd20c [file] [log] [blame]
Scott James Remnantd5a366e2009-07-08 17:03:05 +01001/* upstart
2 *
3 * sysv.c - System V compatibility
4 *
5 * Copyright © 2009 Canonical Ltd.
6 * Author: Scott James Remnant <scott@netsplit.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
27#include <dbus/dbus.h>
28
29#include <nih-dbus/dbus_error.h>
30#include <nih-dbus/dbus_connection.h>
31#include <nih-dbus/dbus_proxy.h>
32
33#include <nih/macros.h>
34#include <nih/alloc.h>
35#include <nih/string.h>
36#include <nih/logging.h>
37#include <nih/error.h>
38
39#include "dbus/upstart.h"
40
41#include "utmp.h"
42#include "sysv.h"
43
44#include "com.ubuntu.Upstart.h"
45
46
47/**
48 * RUNLEVEL_EVENT:
49 *
50 * Name of the event we emit on a runlevel change.
51 **/
52#define RUNLEVEL_EVENT "runlevel"
53
54
55/* Prototypes for static functions */
56static void error_handler (NihError **err, NihDBusMessage *message);
57
58
59/**
60 * dest_address:
61 *
62 * Address for private D-Bus connection.
63 **/
64const char *dest_address = DBUS_ADDRESS_UPSTART;
65
66
67/**
68 * sysv_change_runlevel:
69 * @runlevel: new runlevel,
70 * @extra_env: NULL-terminated array of additional environment.
71 *
72 * Returns: zero on success, negative value on raised error.
73 **/
74int
75sysv_change_runlevel (int runlevel,
76 char * const *extra_env,
77 const char * utmp_file,
78 const char * wtmp_file)
79{
80 int prevlevel;
81 DBusError dbus_error;
82 DBusConnection * connection;
83 nih_local NihDBusProxy *upstart = NULL;
84 nih_local char ** env = NULL;
85 char * e;
86 DBusPendingCall * pending_call;
87 NihError * err;
88
89 nih_assert (runlevel > 0);
90
91 /* Get the previous runlevel from the environment or utmp */
92 prevlevel = utmp_get_runlevel (utmp_file, NULL);
93 if (prevlevel < 0) {
94 nih_free (nih_error_get ());
95
Scott James Remnanta849b0a2009-07-09 00:36:46 +010096 prevlevel = 'N';
Scott James Remnantd5a366e2009-07-08 17:03:05 +010097 }
98
99 /* Connect to Upstart via the private socket, establish a proxy and
100 * drop the initial connection reference since the proxy will hold
101 * one.
102 */
103 dbus_error_init (&dbus_error);
104 connection = dbus_connection_open (dest_address, &dbus_error);
105 if (! connection) {
106 nih_dbus_error_raise (dbus_error.name, dbus_error.message);
107 dbus_error_free (&dbus_error);
108 return -1;
109 }
110 dbus_error_free (&dbus_error);
111
112 upstart = nih_dbus_proxy_new (NULL, connection,
113 NULL, DBUS_PATH_UPSTART,
114 NULL, NULL);
115 if (! upstart) {
116 dbus_connection_unref (connection);
117 return -1;
118 }
119
120 upstart->auto_start = FALSE;
121
122 dbus_connection_unref (connection);
123
124 /* Construct the environment to the event, which must include the
125 * new runlevel and previous runlevel as the first two arguments
126 * followed by any additional environment.
127 */
128 env = nih_str_array_new (NULL);
129 if (! env)
130 nih_return_no_memory_error (-1);
131
132 e = nih_sprintf (NULL, "RUNLEVEL=%c", runlevel);
133 if (! e)
134 nih_return_no_memory_error (-1);
135
136 if (! nih_str_array_addp (&env, NULL, NULL, e)) {
137 nih_error_raise_no_memory ();
138 nih_free (e);
139 return -1;
140 }
141
142 e = nih_sprintf (NULL, "PREVLEVEL=%c", prevlevel);
143 if (! e)
144 nih_return_no_memory_error (-1);
145
146 if (! nih_str_array_addp (&env, NULL, NULL, e)) {
147 nih_error_raise_no_memory ();
148 nih_free (e);
149 return -1;
150 }
151
152 if (extra_env) {
153 if (! nih_str_array_append (&env, NULL, NULL, extra_env))
154 nih_return_no_memory_error (-1);
155 }
156
157 /* Write out the new runlevel record to utmp and wtmp, do this
158 * before calling EmitEvent so that the records are correct.
159 */
160 if (utmp_write_runlevel (utmp_file, wtmp_file,
161 runlevel, prevlevel) < 0)
162 nih_free (nih_error_get ());
163
164 /* Make the EmitEvent call, we don't wait for the event to finish
165 * because sysvinit never did.
166 */
167 err = NULL;
168 pending_call = NIH_SHOULD (upstart_emit_event (
169 upstart, "runlevel", env, FALSE,
170 NULL,
171 (NihDBusErrorHandler)error_handler,
172 &err,
173 NIH_DBUS_TIMEOUT_NEVER));
174 if (! pending_call)
175 return -1;
176
177 dbus_pending_call_block (pending_call);
178 dbus_pending_call_unref (pending_call);
179
180 if (err) {
181 nih_error_raise_error (err);
182 return -1;
183 }
184
185 return 0;
186}
187
188/**
189 * error_handler:
190 * @err: pointer to store error into,
191 * @message: D-Bus message received.
192 *
193 * This function is called in the event of an error from a D-Bus method
194 * call, it stashes the raised error in @err.
195 **/
196static void
197error_handler (NihError ** err,
198 NihDBusMessage *message)
199{
200 *err = nih_error_steal ();
201}