Scott James Remnant | d5a366e | 2009-07-08 17:03:05 +0100 | [diff] [blame] | 1 | /* 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 */ |
| 56 | static void error_handler (NihError **err, NihDBusMessage *message); |
| 57 | |
| 58 | |
| 59 | /** |
| 60 | * dest_address: |
| 61 | * |
| 62 | * Address for private D-Bus connection. |
| 63 | **/ |
| 64 | const 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 | **/ |
| 74 | int |
| 75 | sysv_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 Remnant | a849b0a | 2009-07-09 00:36:46 +0100 | [diff] [blame] | 96 | prevlevel = 'N'; |
Scott James Remnant | d5a366e | 2009-07-08 17:03:05 +0100 | [diff] [blame] | 97 | } |
| 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 | **/ |
| 196 | static void |
| 197 | error_handler (NihError ** err, |
| 198 | NihDBusMessage *message) |
| 199 | { |
| 200 | *err = nih_error_steal (); |
| 201 | } |