Merge lp:~jamesodhunt/upstart/bug-1157713-inotify-test-failures into lp:upstart

Proposed by James Hunt
Status: Merged
Merged at revision: 1474
Proposed branch: lp:~jamesodhunt/upstart/bug-1157713-inotify-test-failures
Merge into: lp:upstart
Diff against target: 563 lines (+305/-74)
5 files modified
ChangeLog (+14/-0)
init/Makefile.am (+19/-7)
init/tests/test_conf.c (+31/-67)
init/tests/test_conf_preload.sh.in (+64/-0)
init/tests/wrap_inotify.c (+177/-0)
To merge this branch: bzr merge lp:~jamesodhunt/upstart/bug-1157713-inotify-test-failures
Reviewer Review Type Date Requested Status
Dimitri John Ledkov Approve
Review via email: mp+159334@code.launchpad.net

Description of the change

* init/Makefile.am: Build wrap_inotify library and run test_conf
  via test_conf_preload.sh.
* init/tests/test_conf.c: Communicate with wrap_inotify library by
  setting INOTIFY_DISABLE to reliably disable inotify rather than trying
  to exhaust inotify instances (LP: #1157713).
* init/tests/test_conf_preload.sh.in: Script to run test_conf within
  LD_PRELOAD environment.
* init/tests/wrap_inotify.c: Wrapper library that provides the inotify
  API and allows test_conf to believe inotify is disabled by
  conditionally failing all inotify calls, depending on whether
  INOTIFY_DISABLE is set.

To post a comment you must log in.
1474. By James Hunt

* init/tests/wrap_inotify.c: Use RTLD_NEXT to avoid having to call dlopen.

Revision history for this message
Dimitri John Ledkov (xnox) wrote :

lgtm, resolve changelog conflict and merge.

review: Approve
1475. By James Hunt

Sync with upstream.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2013-04-22 10:30:09 +0000
3+++ ChangeLog 2013-04-30 23:28:25 +0000
4@@ -2,6 +2,20 @@
5
6 * Typo and doc changes.
7
8+2013-04-17 James Hunt <james.hunt@ubuntu.com>
9+
10+ * init/Makefile.am: Build wrap_inotify library and run test_conf
11+ via test_conf_preload.sh.
12+ * init/tests/test_conf.c: Communicate with wrap_inotify library by
13+ setting INOTIFY_DISABLE to reliably disable inotify rather than trying
14+ to exhaust inotify instances (LP: #1157713).
15+ * init/tests/test_conf_preload.sh.in: Script to run test_conf within
16+ LD_PRELOAD environment.
17+ * init/tests/wrap_inotify.c: Wrapper library that provides the inotify
18+ API and allows test_conf to believe inotify is disabled by
19+ conditionally failing all inotify calls, depending on whether
20+ INOTIFY_DISABLE is set.
21+
22 2013-04-02 James Hunt <james.hunt@ubuntu.com>
23
24 * extra/man/file-event.7: Correct EVENT values in examples.
25
26=== modified file 'init/Makefile.am'
27--- init/Makefile.am 2013-02-14 17:24:05 +0000
28+++ init/Makefile.am 2013-04-30 23:28:25 +0000
29@@ -136,8 +136,6 @@
30 TEST_DATA_FILES = \
31 $(TEST_DATA_DIR)/upstart-1.6.json
32
33-EXTRA_DIST = init.supp $(TEST_DATA_FILES)
34-
35 test_util_SOURCES = \
36 tests/test_util.c tests/test_util.h
37
38@@ -155,14 +153,23 @@
39 test_blocked \
40 test_parse_job \
41 test_parse_conf \
42- test_conf \
43 test_conf_static \
44 test_xdg \
45 test_control
46
47-check_PROGRAMS = $(TESTS)
48-
49-tests: $(BUILT_SOURCES) $(check_PROGRAMS)
50+EXTRA_DIST = init.supp $(TEST_DATA_FILES) tests/test_conf_preload.sh.in
51+
52+test_conf_preload.sh: tests/test_conf_preload.sh.in Makefile test_conf
53+ sed -e 's|[@]abs_builddir[@]|$(abs_builddir)|g' \
54+ -e 's|[@]inotify_preload_library[@]|$(check_LTLIBRARIES)|g' \
55+ $< > $@
56+ chmod +x $@
57+
58+check_PROGRAMS = $(TESTS) test_conf
59+check_SCRIPTS = test_conf_preload.sh
60+CLEANFILES += $(check_SCRIPTS)
61+
62+tests: $(BUILT_SOURCES) $(check_PROGRAMS) $(check_LTLIBRARIES)
63
64 test_system_SOURCES = tests/test_system.c
65 test_system_LDADD = \
66@@ -328,7 +335,12 @@
67 $(JSON_LIBS) \
68 -lrt
69
70-test_conf_SOURCES = tests/test_conf.c $(test_util_SOURCES)
71+check_LTLIBRARIES = tests/libwrap_inotify.la
72+tests_libwrap_inotify_la_SOURCES = tests/wrap_inotify.c
73+tests_libwrap_inotify_la_LIBADD = -lrt -ldl $(AM_LIBADD)
74+tests_libwrap_inotify_la_LDFLAGS = avoid-version -module -shared -export-dynamic -rpath /nowhere -ldl
75+
76+test_conf_SOURCES = tests/test_conf.c $(test_util_SOURCES) $(check_LTLIBRARIES)
77 test_conf_LDADD = \
78 system.o environ.o process.o \
79 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
80
81=== modified file 'init/tests/test_conf.c'
82--- init/tests/test_conf.c 2013-01-25 09:01:00 +0000
83+++ init/tests/test_conf.c 2013-04-30 23:28:25 +0000
84@@ -126,7 +126,7 @@
85 JobClass *job, *old_job;
86 Job *instance;
87 FILE *f;
88- int ret, fd[4096], i = 0, nfds;
89+ int ret, fd, nfds;
90 char dirname[PATH_MAX];
91 char tmpname[PATH_MAX], filename[PATH_MAX];
92 fd_set readfds, writefds, exceptfds;
93@@ -171,11 +171,11 @@
94 fclose (f);
95
96 /* Make sure that we have inotify before performing some tests... */
97- if ((fd[0] = inotify_init ()) < 0) {
98+ if ((fd = inotify_init ()) < 0) {
99 printf ("SKIP: inotify not available\n");
100 goto no_inotify;
101 }
102- close (fd[0]);
103+ close (fd);
104
105
106 /* Check that we can load a job directory source for the first time.
107@@ -1083,15 +1083,10 @@
108 unlink (filename);
109 rmdir (dirname);
110
111-
112- /* Consume all available inotify instances so that the following
113- * tests run without inotify.
114- */
115- for (i = 0; i < 4096; i++)
116- if ((fd[i] = inotify_init ()) < 0)
117- break;
118 no_inotify:
119
120+ /* Disable inotify for the following tests */
121+ assert0 (putenv ("INOTIFY_DISABLE=1"));
122
123 TEST_FILENAME (dirname);
124 mkdir (dirname, 0755);
125@@ -1614,13 +1609,8 @@
126
127 nih_log_set_priority (NIH_LOG_MESSAGE);
128
129- /* Release consumed instances */
130- for (i = 0; i < 4096; i++) {
131- if (fd[i] < 0)
132- break;
133-
134- close (fd[i]);
135- }
136+ /* Re-enable inotify */
137+ assert0 (unsetenv ("INOTIFY_DISABLE"));
138 }
139
140
141@@ -1630,7 +1620,7 @@
142 ConfSource *source;
143 ConfFile *file, *old_file;
144 FILE *f;
145- int ret, fd[4096], i = 0, nfds;
146+ int ret, fd, nfds;
147 char dirname[PATH_MAX];
148 char filename[PATH_MAX];
149 fd_set readfds, writefds, exceptfds;
150@@ -1671,11 +1661,11 @@
151 fclose (f);
152
153 /* Make sure that we have inotify before performing some tests... */
154- if ((fd[0] = inotify_init ()) < 0) {
155+ if ((fd = inotify_init ()) < 0) {
156 printf ("SKIP: inotify not available\n");
157 goto no_inotify;
158 }
159- close (fd[0]);
160+ close (fd);
161
162
163 /* Check that we can load a conf directory source for the first time.
164@@ -2047,15 +2037,10 @@
165
166 nih_free (source);
167
168-
169- /* Consume all available inotify instances so that the following
170- * tests run without inotify.
171- */
172- for (i = 0; i < 4096; i++)
173- if ((fd[i] = inotify_init ()) < 0)
174- break;
175 no_inotify:
176
177+ /* Disable inotify for the following tests */
178+ assert0 (putenv ("INOTIFY_DISABLE=1"));
179
180 TEST_FILENAME (dirname);
181 mkdir (dirname, 0755);
182@@ -2409,13 +2394,8 @@
183
184 nih_log_set_priority (NIH_LOG_MESSAGE);
185
186- /* Release consumed instances */
187- for (i = 0; i < 4096; i++) {
188- if (fd[i] < 0)
189- break;
190-
191- close (fd[i]);
192- }
193+ /* Re-enable inotify */
194+ assert0 (unsetenv ("INOTIFY_DISABLE"));
195 }
196
197 void
198@@ -2424,7 +2404,7 @@
199 ConfSource *source;
200 ConfFile *file;
201 FILE *f;
202- int ret, fd[4096], i = 0;
203+ int ret, fd;
204 char dirname[PATH_MAX];
205 char filename[PATH_MAX], override[PATH_MAX], override2[PATH_MAX];
206 char *dir;
207@@ -2438,11 +2418,11 @@
208 TEST_GROUP ("override files");
209
210 /* Make sure that we have inotify before performing some tests... */
211- if ((fd[0] = inotify_init ()) < 0) {
212+ if ((fd = inotify_init ()) < 0) {
213 printf ("SKIP: inotify not available\n");
214 goto no_inotify;
215 }
216- close (fd[0]);
217+ close (fd);
218
219
220 /* Explicit test of behaviour prior to introduction of override files.
221@@ -3665,14 +3645,11 @@
222 unlink (override);
223 TEST_EQ (rmdir (dirname), 0);
224
225- /* Consume all available inotify instances so that the following
226- * tests run without inotify.
227- */
228- for (i = 0; i < 4096; i++)
229- if ((fd[i] = inotify_init ()) < 0)
230- break;
231-
232 no_inotify:
233+
234+ /* Disable inotify for the following tests */
235+ assert0 (putenv ("INOTIFY_DISABLE=1"));
236+
237 /* If you don't have inotify, any override file must exist
238 * before the system boots.
239 */
240@@ -3729,13 +3706,8 @@
241
242 nih_log_set_priority (NIH_LOG_MESSAGE);
243
244- /* Release consumed instances */
245- for (i = 0; i < 4096; i++) {
246- if (fd[i] < 0)
247- break;
248-
249- close (fd[i]);
250- }
251+ /* Re-enable inotify */
252+ assert0 (unsetenv ("INOTIFY_DISABLE"));
253 }
254
255 void
256@@ -3744,7 +3716,7 @@
257 ConfSource *source;
258 ConfFile *file, *old_file;
259 FILE *f;
260- int ret, fd[4096], i = 0, nfds;
261+ int ret, fd, nfds;
262 char dirname[PATH_MAX];
263 char tmpname[PATH_MAX], filename[PATH_MAX];
264 fd_set readfds, writefds, exceptfds;
265@@ -3773,11 +3745,11 @@
266 fclose (f);
267
268 /* Make sure that we have inotify before performing some tests... */
269- if ((fd[0] = inotify_init ()) < 0) {
270+ if ((fd = inotify_init ()) < 0) {
271 printf ("SKIP: inotify not available\n");
272 goto no_inotify;
273 }
274- close (fd[0]);
275+ close (fd);
276
277
278 /* Check that we can load a file source for the first time. An
279@@ -4289,14 +4261,11 @@
280 TEST_HASH_EMPTY (source->files);
281
282 nih_free (source);
283- /* Consume all available inotify instances so that the following
284- * tests run without inotify.
285- */
286- for (i = 0; i < 4096; i++)
287- if ((fd[i] = inotify_init ()) < 0)
288- break;
289+
290 no_inotify:
291
292+ /* Disable inotify for the following tests */
293+ assert0 (putenv ("INOTIFY_DISABLE=1"));
294
295 TEST_FILENAME (dirname);
296 mkdir (dirname, 0755);
297@@ -4524,13 +4493,8 @@
298
299 nih_log_set_priority (NIH_LOG_MESSAGE);
300
301- /* Release consumed instances */
302- for (i = 0; i < 4096; i++) {
303- if (fd[i] < 0)
304- break;
305-
306- close (fd[i]);
307- }
308+ /* Re-enable inotify */
309+ assert0 (unsetenv ("INOTIFY_DISABLE"));
310 }
311
312
313
314=== added file 'init/tests/test_conf_preload.sh.in'
315--- init/tests/test_conf_preload.sh.in 1970-01-01 00:00:00 +0000
316+++ init/tests/test_conf_preload.sh.in 2013-04-30 23:28:25 +0000
317@@ -0,0 +1,64 @@
318+#!/bin/sh -e
319+#---------------------------------------------------------------------
320+# Script to run a test in a modified environment where inotify can be
321+# disabled.
322+#---------------------------------------------------------------------
323+# Copyright (C) 2013 Canonical Ltd.
324+#
325+# Author: James Hunt <james.hunt@canonical.com>
326+#
327+# This program is free software: you can redistribute it and/or modify
328+# it under the terms of the GNU General Public License as published by
329+# the Free Software Foundation, version 3 of the License.
330+#
331+# This program is distributed in the hope that it will be useful,
332+# but WITHOUT ANY WARRANTY; without even the implied warranty of
333+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
334+# GNU General Public License for more details.
335+#
336+# You should have received a copy of the GNU General Public License
337+# along with this program. If not, see <http://www.gnu.org/licenses/>.
338+#
339+#---------------------------------------------------------------------
340+
341+die()
342+{
343+ msg="$*"
344+ echo "ERROR: $msg" >&2
345+ exit 1
346+}
347+
348+# Name of test to run in modified environment
349+test_name=test_conf
350+
351+build_dir="@abs_builddir@"
352+test_path="$build_dir/$test_name"
353+
354+preload_library="$build_dir/@inotify_preload_library@"
355+
356+for file in "$test_path" "$preload_library"
357+do
358+ [ -e "$file" ] || die "file $file not found"
359+done
360+
361+# Install the preload library to a temporary location since we need the
362+# full path to the shared library for LD_PRELOAD.
363+install_dir=$(mktemp -d --tmpdir="$builddir")
364+libtool --mode=install install \
365+ "$preload_library" "$install_dir" >/dev/null 2>&1
366+
367+installed_lib=$(basename "$preload_library")
368+installed_so="$install_dir/$(echo "$installed_lib"|cut -d\. -f1).so"
369+
370+[ -f "$installed_so" ] || die "cannot find $installed_so"
371+
372+echo
373+echo "INFO: Running test $test_name in LD_PRELOAD environment which allows inotify to be disabled/enabled"
374+echo
375+
376+# Run test in modified environment
377+LD_PRELOAD=$installed_so $test_path
378+
379+# clean up
380+libtool --mode=uninstall \
381+ rm "$install_dir/$installed_lib" >/dev/null 2>&1
382
383=== added file 'init/tests/wrap_inotify.c'
384--- init/tests/wrap_inotify.c 1970-01-01 00:00:00 +0000
385+++ init/tests/wrap_inotify.c 2013-04-30 23:28:25 +0000
386@@ -0,0 +1,177 @@
387+/* upstart
388+ *
389+ * wrap_inotify.c - library to subvert inotify calls.
390+ *
391+ * Copyright © Â 2013 Canonical Ltd.
392+ * Author: James Hunt <james.hunt@canonical.com>
393+ *
394+ * This program is free software; you can redistribute it and/or modify
395+ * it under the terms of the GNU General Public License version 2, as
396+ * published by the Free Software Foundation.
397+ *
398+ * This program is distributed in the hope that it will be useful,
399+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
400+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
401+ * GNU General Public License for more details.
402+ *
403+ * You should have received a copy of the GNU General Public License along
404+ * with this program; if not, write to the Free Software Foundation, Inc.,
405+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
406+ */
407+
408+/**
409+ * = Description =
410+ *
411+ * The test_conf upstart test requires certain test scenarios to run in
412+ * an environment where inotify(7) is not available/functional to force the
413+ * underlying NIH library to perform a manual filesystem tree traversal.
414+ *
415+ * Since inotify limits are _per user_ and not _per process_, it is not
416+ * possible to disable inotify on a system reliabliy for the duration
417+ * of a test run since the test is at the mercy of other processes that
418+ * are making inotify calls too.
419+ *
420+ * The only reliable method therefore is to "fake" the inotify calls
421+ * using this library.
422+ *
423+ * To use this library:
424+ *
425+ * 1) Have the test code set the environment variable "INOTIFY_DISABLE" to
426+ * any value to disable inotify, and unset the variable to leave it
427+ * enabled.
428+ *
429+ * 2) Run the test code using LD_PRELOAD to force the dynamic
430+ * link-loader to use these inotify definitions rather than those
431+ * provided by libc:
432+ *
433+ * (LD_PRELOAD=/path/to/this/libary.so test_code)
434+ *
435+ * To convince yourself this library is being used, set "INOTIFY_DEBUG"
436+ * to any value for some stdout debug messages.
437+ **/
438+
439+/* to ensure we get RTLD_NEXT */
440+#define _GNU_SOURCE
441+
442+#include <stdio.h>
443+#include <stdlib.h>
444+#include <errno.h>
445+#include <assert.h>
446+#include <sys/inotify.h>
447+#include <dlfcn.h>
448+
449+/**
450+ * Determine if inotify should be disabled.
451+ **/
452+#define disable_inotify() \
453+ (getenv ("INOTIFY_DISABLE"))
454+
455+/**
456+ * Determine if inotify debug should be displayed to stdout.
457+ **/
458+#define debug_inotify() \
459+ (getenv ("INOTIFY_DEBUG"))
460+
461+/**
462+ * debug_msg:
463+ *
464+ * If debug is enabled, display a message to stdout stating if inotify
465+ * is enabled along with details of the called function.
466+ **/
467+#define debug_msg() \
468+ do { \
469+ if (debug_inotify ()) { \
470+ printf ("DEBUG:%s:%d: inotify %s\n", \
471+ __func__, __LINE__, \
472+ disable_inotify () \
473+ ? "disabled" : "enabled"); \
474+ fflush (NULL); \
475+ } \
476+ } while (0)
477+
478+int __wrap_inotify_init (void)
479+ __attribute ((warn_unused_result, no_instrument_function));
480+
481+int __wrap_inotify_add_watch (int fd, const char *pathname, uint32_t mask)
482+ __attribute ((warn_unused_result, no_instrument_function));
483+
484+int __wrap_inotify_rm_watch (int fd, int wd)
485+ __attribute ((warn_unused_result, no_instrument_function));
486+
487+int (*real_inotify_init_addr) (void);
488+int (*real_inotify_add_watch_addr) (int fd, const char *pathname, uint32_t mask);
489+int (*real_inotify_rm_watch_addr) (int fd, int wd);
490+
491+int
492+__wrap_inotify_init (void)
493+{
494+ if (disable_inotify ()) {
495+ /* simulate reaching inotify instances user limit */
496+ errno = EMFILE;
497+ return -1;
498+ }
499+
500+ *(void **)(&real_inotify_init_addr) = dlsym (RTLD_NEXT, "inotify_init");
501+
502+ assert (! dlerror ());
503+ assert (real_inotify_init_addr);
504+
505+ return real_inotify_init_addr ();
506+}
507+
508+int
509+__wrap_inotify_add_watch (int fd, const char *pathname, uint32_t mask)
510+{
511+ if (disable_inotify ()) {
512+ /* simulate reaching inotify watches user limit */
513+ errno = ENOSPC;
514+ return -1;
515+ }
516+
517+ *(void **)(&real_inotify_add_watch_addr) = dlsym (RTLD_NEXT, "inotify_add_watch");
518+
519+ assert (! dlerror ());
520+ assert (real_inotify_add_watch_addr);
521+
522+ return real_inotify_add_watch_addr (fd, pathname, mask);
523+}
524+
525+int
526+__wrap_inotify_rm_watch (int fd, int wd)
527+{
528+ if (disable_inotify ()) {
529+ ; /* not meaningful, so just pass through */
530+ }
531+
532+ *(void **)(&real_inotify_rm_watch_addr) = dlsym (RTLD_NEXT, "inotify_rm_watch");
533+
534+ assert (! dlerror ());
535+ assert (real_inotify_rm_watch_addr);
536+
537+ return real_inotify_rm_watch_addr (fd, wd);
538+}
539+
540+int
541+inotify_init (void)
542+{
543+ debug_msg ();
544+
545+ return __wrap_inotify_init ();
546+}
547+
548+int
549+inotify_add_watch (int fd, const char *pathname, uint32_t mask)
550+{
551+ debug_msg ();
552+
553+ return __wrap_inotify_add_watch (fd, pathname, mask);
554+
555+}
556+
557+int
558+inotify_rm_watch (int fd, int wd)
559+{
560+ debug_msg ();
561+
562+ return __wrap_inotify_rm_watch (fd, wd);
563+}

Subscribers

People subscribed via source and target branches