Merge lp:~jamesodhunt/upstart/bug-1083723 into lp:~upstart-devel/upstart/1.6

Proposed by James Hunt
Status: Merged
Merged at revision: 1393
Proposed branch: lp:~jamesodhunt/upstart/bug-1083723
Merge into: lp:~upstart-devel/upstart/1.6
Diff against target: 258 lines (+160/-6)
6 files modified
dbus/com.ubuntu.Upstart.xml (+2/-0)
init/control.c (+55/-0)
init/control.h (+3/-0)
util/Makefile.am (+2/-1)
util/man/telinit.8 (+7/-1)
util/telinit.c (+91/-4)
To merge this branch: bzr merge lp:~jamesodhunt/upstart/bug-1083723
Reviewer Review Type Date Requested Status
Steve Langasek Needs Fixing
Review via email: mp+137898@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Steve Langasek (vorlon) wrote :

+ /* Disallow users from restarting Upstart, unless they happen to
+ * own this process (which they may do in the test scenario and
+ * when running Upstart as a non-privileged user).
+ */
+ if (session && session->user && session->user != uid) {

Same comment as for https://code.launchpad.net/~jamesodhunt/upstart/bug-1079715/+merge/135388: this should be 'session && session->user != uid', and not assume that all users should be allowed to call this method for a non-null session with user==0.

+ if (init_is_upstart ()) {
+ ret = restart_upstart ();
+ }

Please get rid of the init_is_upstart() check (and function); now that restarting is done over dbus, we shouldn't open a separate connection first to check that init is upstart. I think it's reasonable and appropriate that if someone calls 'telinit u' against a non-upstart init, and as a result init does not re-exec itself, telinit should exit non-zero instead of silently failing to take the requested action. Note that this is not an issue for the library upgrade case, because the library packages sensibly do:

 telinit u 2>/dev/null || true

If there is some reason I'm missing that it's important to return 0 in the case of a non-upstart init, please at least don't call upstart_open() twice, but instead save the result so we're not setting up and tearing down the dbus connection twice.

Otherwise, this looks good.

review: Needs Fixing
lp:~jamesodhunt/upstart/bug-1083723 updated
1396. By James Hunt

* init/control.c: control_restart():
  - Simplified session check.
* util/telinit.c:
  - init_is_upstart(): Removed as redundant.
  - upstart_open(): Removed erroneous comment.
  - restart_upstart(): Changed return values for consistency with other
    calls in main() where an error is expected to set ret to < 0.
  - main(): Just call restart_upstart() in re-exec scenario to avoid the
    creation of a second D-Bus connection.

Revision history for this message
James Hunt (jamesodhunt) wrote :

> + /* Disallow users from restarting Upstart, unless they happen to
> + * own this process (which they may do in the test scenario and
> + * when running Upstart as a non-privileged user).
> + */
> + if (session && session->user && session->user != uid) {
>
> Same comment as for https://code.launchpad.net/~jamesodhunt/upstart/bug-1079715/+merge/135388: this should be 'session && session->user != uid', and not assume that all users should be allowed to call this method for a non-null session with user==0.

Fixed.

>
> + if (init_is_upstart ()) {
> + ret = restart_upstart ();
> + }
>
> Please get rid of the init_is_upstart() check (and function); now that restarting is done over dbus, we shouldn't open a separate connection first to check that init is upstart. I think it's reasonable and appropriate that if someone calls 'telinit u' against a non-upstart init, and as a result init does not re-exec itself, telinit should exit non-zero instead of silently failing to take the requested action. Note that this is not an issue for the library upgrade case, because the library packages sensibly do:
>
> telinit u 2>/dev/null || true
>
> If there is some reason I'm missing that it's important to return 0 in the case of a non-upstart init, please at least don't call upstart_open() twice, but instead save the result so we're not setting up and tearing down the dbus connection twice.

The initial init_is_upstart() check would have given us the opportunity to display a cleaner error message in the non-Upstart init scenario without more invasive code changes (something along the lines of "telinit: ERROR - init is not Upstart"). What currently happens if /sbin/init is not Upstart is this:

    # telinit u
    telinit: Failed to connect to socket /var/run/dbus/system_bus_socket: No such file or directory
    # echo $?
    1

It's not particularly pretty, but we don't anticipate this scenario happening much and of course it can be quietened down by redirecting stderr if required.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'dbus/com.ubuntu.Upstart.xml'
2--- dbus/com.ubuntu.Upstart.xml 2012-09-09 21:22:06 +0000
3+++ dbus/com.ubuntu.Upstart.xml 2012-12-06 11:40:27 +0000
4@@ -34,6 +34,8 @@
5 <arg name="jobs" type="ao" direction="out" />
6 </method>
7
8+ <method name="Restart"/>
9+
10 <!-- Signals for changes to the job list -->
11 <signal name="JobAdded">
12 <arg name="job" type="o" />
13
14=== modified file 'init/control.c'
15--- init/control.c 2012-09-20 15:16:52 +0000
16+++ init/control.c 2012-12-06 11:40:27 +0000
17@@ -949,3 +949,58 @@
18
19 return 0;
20 }
21+
22+/**
23+ * control_restart:
24+ * @data: not used,
25+ * @message: D-Bus connection and message received.
26+ *
27+ * Implements the Restart method of the com.ubuntu.Upstart
28+ * interface.
29+ *
30+ * Called to request that Upstart performs a stateful re-exec.
31+ *
32+ * Returns: zero on success, negative value on raised error.
33+ **/
34+int
35+control_restart (void *data,
36+ NihDBusMessage *message)
37+{
38+ Session *session;
39+ uid_t uid;
40+
41+ nih_assert (message != NULL);
42+
43+ uid = getuid ();
44+
45+ /* Get the relevant session */
46+ session = session_from_dbus (NULL, message);
47+
48+ /* Chroot sessions must not be able to influence
49+ * the outside system.
50+ *
51+ * Making this a NOP is safe since it is the Upstart outside the
52+ * chroot which manages all chroot jobs.
53+ */
54+ if (session && session->chroot) {
55+ nih_warn (_("Ignoring restart request from chroot session"));
56+ return 0;
57+ }
58+
59+ /* Disallow users from restarting Upstart, unless they happen to
60+ * own this process (which they may do in the test scenario and
61+ * when running Upstart as a non-privileged user).
62+ */
63+ if (session && session->user != uid) {
64+ nih_dbus_error_raise_printf (
65+ DBUS_INTERFACE_UPSTART ".Error.PermissionDenied",
66+ _("You do not have permission to request restart"));
67+ return -1;
68+ }
69+
70+ nih_info (_("Restarting"));
71+
72+ stateful_reexec ();
73+
74+ return 0;
75+}
76
77=== modified file 'init/control.h'
78--- init/control.h 2012-09-11 14:59:40 +0000
79+++ init/control.h 2012-12-06 11:40:27 +0000
80@@ -105,6 +105,9 @@
81 int control_bus_release_name (void)
82 __attribute__ ((warn_unused_result));
83
84+int control_restart (void *data, NihDBusMessage *message)
85+ __attribute__ ((warn_unused_result));
86+
87 NIH_END_EXTERN
88
89 #endif /* INIT_CONTROL_H */
90
91=== modified file 'util/Makefile.am'
92--- util/Makefile.am 2011-06-01 11:13:42 +0000
93+++ util/Makefile.am 2012-12-06 11:40:27 +0000
94@@ -71,7 +71,8 @@
95 utmp.c utmp.h \
96 sysv.c sysv.h
97 nodist_telinit_SOURCES = \
98- $(com_ubuntu_Upstart_OUTPUTS)
99+ $(com_ubuntu_Upstart_OUTPUTS) \
100+ $(com_ubuntu_Upstart_Instance_OUTPUTS)
101 telinit_LDADD = \
102 $(LTLIBINTL) \
103 $(NIH_LIBS) \
104
105=== modified file 'util/man/telinit.8'
106--- util/man/telinit.8 2012-11-07 14:28:34 +0000
107+++ util/man/telinit.8 2012-12-06 11:40:27 +0000
108@@ -71,10 +71,16 @@
109 .BR U " or " u
110 to request that the
111 .BR init (8)
112-daemon re-execute itself. This is necessary when upgrading the
113+daemon re-execute itself. This is necessary when upgrading the Upstart
114 .BR init (8)
115 daemon itself or any of its dependent system libraries
116 to ensure disks can be unmounted cleanly on shutdown.
117+
118+Note that if the init daemon is
119+.I not
120+Upstart, this option will have no effect on the running
121+.BR init (8)
122+daemon.
123 .\"
124 .SH OPTIONS
125 .TP
126
127=== modified file 'util/telinit.c'
128--- util/telinit.c 2009-07-09 11:50:19 +0000
129+++ util/telinit.c 2012-12-06 11:40:27 +0000
130@@ -38,12 +38,27 @@
131 #include <nih/logging.h>
132 #include <nih/error.h>
133
134+#include <nih-dbus/dbus_error.h>
135+#include <nih-dbus/dbus_proxy.h>
136+#include <nih-dbus/errors.h>
137+#include <nih-dbus/dbus_connection.h>
138+
139+#include "dbus/upstart.h"
140+
141+#include "com.ubuntu.Upstart.h"
142+
143 #include "sysv.h"
144
145
146 /* Prototypes for option functions */
147 int env_option (NihOption *option, const char *arg);
148
149+NihDBusProxy * upstart_open (const void *parent)
150+ __attribute__ ((warn_unused_result));
151+
152+int restart_upstart (void)
153+ __attribute__ ((warn_unused_result));
154+
155
156 /**
157 * extra_env:
158@@ -83,6 +98,79 @@
159 return 0;
160 }
161
162+/**
163+ * upstart_open:
164+ * @parent: parent object for new proxy.
165+ *
166+ * Opens a connection to the Upstart init daemon and returns a proxy
167+ * to the manager object. If @dest_name is not NULL, a connection is
168+ * instead opened to the system bus and the proxy linked to the
169+ * well-known name given.
170+ *
171+ * If @parent is not NULL, it should be a pointer to another object
172+ * which will be used as a parent for the returned proxy. When all
173+ * parents of the returned proxy are freed, the returned proxy will
174+ * also be freed.
175+ *
176+ * Returns: newly allocated D-Bus proxy or NULL on raised error.
177+ **/
178+NihDBusProxy *
179+upstart_open (const void *parent)
180+{
181+ DBusError dbus_error;
182+ DBusConnection *connection;
183+ NihDBusProxy * upstart;
184+
185+ dbus_error_init (&dbus_error);
186+
187+ connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error);
188+ if (! connection) {
189+ nih_dbus_error_raise (dbus_error.name, dbus_error.message);
190+ dbus_error_free (&dbus_error);
191+ return NULL;
192+ }
193+
194+ dbus_connection_set_exit_on_disconnect (connection, FALSE);
195+ dbus_error_free (&dbus_error);
196+
197+ upstart = nih_dbus_proxy_new (parent, connection,
198+ DBUS_SERVICE_UPSTART,
199+ DBUS_PATH_UPSTART,
200+ NULL, NULL);
201+ if (! upstart) {
202+ dbus_connection_unref (connection);
203+ return NULL;
204+ }
205+
206+ upstart->auto_start = FALSE;
207+
208+ /* Drop initial reference now the proxy holds one */
209+ dbus_connection_unref (connection);
210+
211+ return upstart;
212+}
213+
214+/**
215+ * restart_upstart:
216+ *
217+ * Request Upstart restart itself.
218+ *
219+ * Returns: 0 on SUCCESS, -1 on raised error.
220+ **/
221+int
222+restart_upstart (void)
223+{
224+ nih_local NihDBusProxy *upstart = NULL;
225+
226+ upstart = upstart_open (NULL);
227+ if (! upstart)
228+ return -1;
229+
230+ if (upstart_restart_sync (NULL, upstart) < 0)
231+ return -1;
232+
233+ return 0;
234+}
235
236 #ifndef TEST
237 /**
238@@ -107,7 +195,7 @@
239 {
240 char **args;
241 int runlevel;
242- int ret;
243+ int ret = 0;
244
245 nih_main_init (argv[0]);
246
247@@ -174,9 +262,8 @@
248 break;
249 case 'U':
250 case 'u':
251- ret = kill (1, SIGTERM);
252- if (ret < 0)
253- nih_error_raise_system ();
254+ /* If /sbin/init is not Upstart, just exit non-zero */
255+ ret = restart_upstart ();
256 break;
257 default:
258 nih_assert_not_reached ();

Subscribers

People subscribed via source and target branches

to all changes: