Merge lp:~jamesodhunt/ubuntu/precise/upstart/fix-for-bug-912558 into lp:ubuntu/precise/upstart

Proposed by James Hunt
Status: Merged
Merged at revision: 1352
Proposed branch: lp:~jamesodhunt/ubuntu/precise/upstart/fix-for-bug-912558
Merge into: lp:ubuntu/precise/upstart
Diff against target: 3355 lines (+685/-766)
10 files modified
ChangeLog (+43/-0)
debian/changelog (+8/-2)
init/job_process.c (+12/-0)
init/log.c (+165/-36)
init/log.h (+6/-0)
init/main.c (+0/-10)
init/man/init.5 (+8/-7)
init/tests/test_job_process.c (+293/-18)
init/tests/test_log.c (+150/-140)
util/tests/test_user_sessions.sh (+0/-553)
To merge this branch: bzr merge lp:~jamesodhunt/ubuntu/precise/upstart/fix-for-bug-912558
Reviewer Review Type Date Requested Status
Colin Watson Approve
Review via email: mp+90278@code.launchpad.net

Description of the change

  [ Stéphane Graber ]
  * Mark upstart Multi-Arch:foreign

  [ James Hunt ]
  * Merge of important logger fixes from upstream lp:upstart
    (LP: #912558).
  * Reverted temporary fix to disable job logging.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2011-12-22 16:52:22 +0000
3+++ ChangeLog 2012-01-26 15:59:26 +0000
4@@ -1,3 +1,46 @@
5+2012-01-25 James Hunt <james.hunt@ubuntu.com>
6+
7+ * init/job_process.c: job_process_terminated(): Free log to ensure data
8+ written as soon as _any_ process ends (consider respawn jobs).
9+ * init/log.c:
10+ - log_destroy():
11+ - Improved documentation.
12+ - Now calls new function log_flush().
13+ - log_flush(): New function to ensure no lingering buffered job data
14+ remains. Now considers EBADF (LP: #912558).
15+ - log_io_reader():
16+ - Added missing assert for @len.
17+ - Simplified ENOSPC handling.
18+ - Ensure log->io set to NULL to allow other routines to detect it
19+ really has gone.
20+ - log_file_write(): Added @len checks.
21+ - log_read_watch(): New function to drain data from a watch descriptor
22+ (which also must consider EBADF).
23+ * init/log.h: Added define for LOG_READ_SIZE.
24+ * init/tests/test_job_process.c:
25+ - test_run():
26+ - Added some extra pointer checks.
27+ - Free class *before* checking file to ensure destructor invoked at
28+ correct point.
29+ - Added test "with single-line command running an invalid command"
30+ (for scenario bug 912558 exposed).
31+ - Added test "with single-line command writing fast and exiting".
32+ * init/tests/test_log.c: Changed all tests to use openpty(3) rather than
33+ pipe(2) for semantic parity with actual code.
34+ * util/tests/test_user_sessions.sh:
35+ - ensure_no_output(): Now calls check_job_output() and delete_job() to
36+ simplify logic.
37+ - delete_job(): Call get_job_file() rather than doing it long-hand.
38+ - check_job_output(): New function.
39+ - start_job(): Added allow_failure parameter.
40+ - test_ensure_no_unexpected_output(): New test
41+ "ensure command job does not create log file with invalid command".
42+
43+2012-01-05 James Hunt <james.hunt@ubuntu.com>
44+
45+ * init/man/init.5: Explain that all job processes affected
46+ by 'setuid' and 'setgid' stanzas.
47+
48 2011-12-22 James Hunt <james.hunt@ubuntu.com>
49
50 * init/job_process.c: job_process_spawn():
51
52=== modified file 'debian/changelog'
53--- debian/changelog 2012-01-09 10:10:08 +0000
54+++ debian/changelog 2012-01-26 15:59:26 +0000
55@@ -1,8 +1,14 @@
56-upstart (1.4-0ubuntu3) UNRELEASED; urgency=low
57+upstart (1.4-0ubuntu3) precise; urgency=low
58
59+ [ Stéphane Graber ]
60 * Mark upstart Multi-Arch:foreign
61
62- -- Stéphane Graber <stgraber@ubuntu.com> Mon, 09 Jan 2012 11:08:27 +0100
63+ [ James Hunt ]
64+ * Merge of important logger fixes from upstream lp:upstart
65+ (LP: #912558).
66+ * Reverted temporary fix to disable job logging.
67+
68+ -- James Hunt <james.hunt@ubuntu.com> Thu, 26 Jan 2012 15:13:25 +0000
69
70 upstart (1.4-0ubuntu2) precise; urgency=low
71
72
73=== modified file 'init/job_process.c'
74--- init/job_process.c 2011-12-22 16:52:22 +0000
75+++ init/job_process.c 2012-01-26 15:59:26 +0000
76@@ -1571,6 +1571,18 @@
77 job->kill_process = -1;
78 }
79
80+ if (job->class->console == CONSOLE_LOG) {
81+ /* It is imperative that we free the log at this stage to ensure
82+ * that jobs which respawn have their log written _now_
83+ * (and not just when the overall Job object is freed at
84+ * some distant future point).
85+ */
86+ if (job->log) {
87+ nih_free (job->log);
88+ job->log = NULL;
89+ }
90+ }
91+
92 /* Find existing utmp entry for the process pid */
93 setutxent();
94 while ((utmptr = getutxent()) != NULL) {
95
96=== modified file 'init/log.c'
97--- init/log.c 2011-12-15 17:50:14 +0000
98+++ init/log.c 2012-01-26 15:59:26 +0000
99@@ -35,6 +35,8 @@
100
101 static int log_file_open (Log *log);
102 static int log_file_write (Log *log, const char *buf, size_t len);
103+static void log_read_watch (Log *log);
104+static void log_flush (Log *log);
105
106 /**
107 * log_new:
108@@ -140,11 +142,54 @@
109 * @log: Log.
110 *
111 * Called automatically when Log is being destroyed.
112+ *
113+ * XXX: Note that the fd associated with the jobs stdout and stderr (as
114+ * passed to log_new()) *MUST* be closed by the time this function is
115+ * called since it will continue to read from the fd until an error is
116+ * detected. This behaviour is required to ensure all job output is
117+ * read.
118+ *
119+ * Returns: 0 always.
120 **/
121 int
122 log_destroy (Log *log)
123 {
124+ nih_assert (log);
125+
126+ /* User job logging not currently available */
127+ nih_assert (log->uid == 0);
128+
129+ log_flush (log);
130+
131+ /* Force file to flush */
132+ if (log->fd != -1)
133+ close (log->fd);
134+
135+ log->fd = -1;
136+
137+ return 0;
138+}
139+
140+/**
141+ * log_flush:
142+ *
143+ * @log: Log.
144+ *
145+ * Ensure that no job output data is buffered and attempt to flush all
146+ * unflushed data to disk.
147+ *
148+ * It is safe to call this function multiple times and may in fact be
149+ * necessary if the log file cannot be written for any reason.
150+ *
151+ * Note no return value since there isn't much that can be done at
152+ * the point this function is called should the flushing operations
153+ * fail.
154+ **/
155+static void
156+log_flush (Log *log)
157+{
158 int ret;
159+ int flags;
160
161 nih_assert (log);
162
163@@ -162,21 +207,49 @@
164 goto out;
165
166 ret = log_file_write (log, NULL, 0);
167- if (ret < 0)
168+ if (ret < 0) {
169+ close (log->fd);
170+ log->fd = -1;
171 goto out;
172- }
173-
174- /* Force file to flush
175- *
176- * Only attempt this for jobs which the current process is
177- * in control of.
178- */
179- if (log->fd > 0)
180+ }
181+ }
182+
183+ if (log->io) {
184+ nih_assert (log->io->watch);
185+
186+ /* If the job associated with this log produces output _after_
187+ * nih_io_handle_fds() has been called in any loop of the main
188+ * loop and just before the job is destroyed, we will miss it.
189+ *
190+ * Therefore, attempt to read from the watch fd until we get an error.
191+ */
192+ log_read_watch (log);
193+
194+ flags = fcntl (log->io->watch->fd, F_GETFL);
195+
196+ if (flags < 0 && errno == EBADF) {
197+ /* The watch fd is now known to be invalid, so disable
198+ * the error handler to avoid an infinite loop where the
199+ * error handler attempts to free the NihIo, which would
200+ * error, causing the error handler to be called
201+ * ad infinitum.
202+ *
203+ * Note that the NihIo is freed via
204+ * nih_io_destroy().
205+ */
206+ log->io->error_handler = NULL;
207+
208+ nih_free (log->io);
209+ log->io = NULL;
210+ }
211+ }
212+
213+ /* Force file to flush */
214+ if (log->fd != -1)
215 close (log->fd);
216
217 out:
218 log->fd = -1;
219- return 0;
220 }
221
222 /**
223@@ -191,7 +264,7 @@
224 * encapsulated in @io.
225 *
226 * Notes for user jobs:
227-
228+ *
229 * User jobs by necessity are handled differently to system jobs. Since
230 * a user job must log their data to files owned by a non-root user, the
231 * safest technique is for a process running as that user to create the
232@@ -223,45 +296,37 @@
233 nih_assert (io);
234 nih_assert (log->io == io);
235 nih_assert (buf);
236+ nih_assert (len);
237
238 /* User job logging not currently available */
239 nih_assert (log->uid == 0);
240
241- /* Note we don't assert @len in case we are being called after
242- * an error is detected (where there is no new data, but may be
243- * unflushed data).
244- */
245-
246 /* Just in case we try to write more than read can inform us
247 * about (this should really be a build-time assertion).
248 */
249 nih_assert (sizeof (size_t) == sizeof (ssize_t));
250
251 if (log_file_open (log) < 0) {
252- if (errno == ENOSPC) {
253- /* Always discard when out of space */
254- nih_io_buffer_shrink (io->recv_buf, len);
255- return;
256- } else {
257+ if (errno != ENOSPC) {
258 /* Add new data to unflushed buffer */
259 if (nih_io_buffer_push (log->unflushed, buf, len) < 0)
260 return;
261-
262- nih_io_buffer_shrink (io->recv_buf, len);
263- /* No point attempting to write if we cannot
264- * open the file.
265- */
266- return;
267 }
268+
269+ /* Note that we always discard when out of space */
270+ nih_io_buffer_shrink (io->recv_buf, len);
271+
272+ /* No point attempting to write if we cannot
273+ * open the file.
274+ */
275+ return;
276 }
277
278 ret = log_file_write (log, buf, len);
279- if (ret < 0) {
280+ if (ret < 0)
281 nih_warn ("%s %s", _("Failed to write to log file"), log->path);
282- }
283 }
284
285-
286 /**
287 * log_io_error_handler:
288 *
289@@ -289,12 +354,12 @@
290 err = nih_error_get ();
291
292 nih_assert (err->number == EIO);
293+
294 nih_free (err);
295
296- if (log->io) {
297- /* Close the connection */
298- nih_free (log->io);
299- }
300+ /* Ensure the NihIo is closed */
301+ nih_free (log->io);
302+ log->io = NULL;
303 }
304
305 /**
306@@ -448,10 +513,12 @@
307 * Note that data is always discarded when out of
308 * space.
309 */
310- if (saved != ENOSPC && nih_io_buffer_push (log->unflushed, buf, len) < 0)
311+ if (saved != ENOSPC && len
312+ && nih_io_buffer_push (log->unflushed, buf, len) < 0)
313 goto error;
314
315- nih_io_buffer_shrink (io->recv_buf, len);
316+ if (len)
317+ nih_io_buffer_shrink (io->recv_buf, len);
318
319 /* Still need to indicate that the write failed */
320 goto error;
321@@ -466,6 +533,9 @@
322 * next time.
323 */
324 if (log->unflushed->len) {
325+ if (! len)
326+ goto error;
327+
328 /* Save new data */
329 if (nih_io_buffer_push (log->unflushed, buf, len) < 0)
330 goto error;
331@@ -504,3 +574,62 @@
332 return -1;
333 }
334
335+/**
336+ * log_read_watch:
337+ *
338+ * @log: Log.
339+ *
340+ * Attempt a final read from the watch descriptor to ensure we've
341+ * drained all the data from the job.
342+ **/
343+void
344+log_read_watch (Log *log)
345+{
346+ NihIo *io;
347+ ssize_t len;
348+ int saved;
349+
350+ nih_assert (log);
351+
352+ /* Must not be called if there is unflushed data as the log
353+ * would then not be written in order .
354+ */
355+ nih_assert (! log->unflushed->len);
356+
357+ io = log->io;
358+
359+ if (! io)
360+ return;
361+
362+ while (1) {
363+ /* Ensure we have some space to read data from the job */
364+ if (nih_io_buffer_resize (io->recv_buf, LOG_READ_SIZE) < 0)
365+ break;
366+
367+ /* Append to buffer */
368+ len = read (io->watch->fd,
369+ io->recv_buf->buf + io->recv_buf->len,
370+ io->recv_buf->size - io->recv_buf->len);
371+ saved = errno;
372+
373+ if (len > 0)
374+ io->recv_buf->len += len;
375+
376+ if (io->recv_buf->len)
377+ log_io_reader (log, io, io->recv_buf->buf, io->recv_buf->len);
378+
379+ /* If an error occurs, it is likely to be EIO or EBADF.
380+ * But erring on the side of caution, any unusual error
381+ * causes the loop to be exited.
382+ */
383+ if ((len < 0 && saved != EAGAIN && saved != EWOULDBLOCK) || len == 0) {
384+ /* Either the job process end of the pty has
385+ * been closed, or there really
386+ * is no (more) data to be read.
387+ */
388+ close (log->fd);
389+ log->fd = -1;
390+ break;
391+ }
392+ }
393+}
394
395=== modified file 'init/log.h'
396--- init/log.h 2011-12-09 14:07:11 +0000
397+++ init/log.h 2012-01-26 15:59:26 +0000
398@@ -41,6 +41,12 @@
399 **/
400 #define LOG_DEFAULT_MODE (S_IRWXU | S_IRGRP)
401
402+/** LOG_READ_SIZE:
403+ *
404+ * Minimum buffer size for reading log data.
405+ **/
406+#define LOG_READ_SIZE 1024
407+
408 /**
409 * Log:
410 *
411
412=== modified file 'init/main.c'
413--- init/main.c 2012-01-06 16:12:52 +0000
414+++ init/main.c 2012-01-26 15:59:26 +0000
415@@ -121,7 +121,6 @@
416
417 extern int disable_sessions;
418 extern int disable_job_logging;
419-int force_logging;
420 extern int use_session_bus;
421 extern int default_console;
422 extern char *log_dir;
423@@ -139,9 +138,6 @@
424 { 0, "default-console", N_("default value for console stanza"),
425 NULL, "VALUE", NULL, console_type_setter },
426
427- { 0, "log", N_("enable job logging"),
428- NULL, NULL, &force_logging, NULL },
429-
430 { 0, "logdir", N_("specify alternative directory to store job output logs in"),
431 NULL, "DIR", &log_dir, NULL },
432
433@@ -185,9 +181,6 @@
434 "process id 1 to denote its special status. When executed "
435 "by a user process, it will actually run /sbin/telinit."));
436
437- /* Temporarily disable job logging (bug 912558) */
438- disable_job_logging = 1;
439-
440 args = nih_option_parser (NULL, argc, argv, options, FALSE);
441 if (! args)
442 exit (1);
443@@ -195,9 +188,6 @@
444 handle_confdir ();
445 handle_logdir ();
446
447- if (force_logging)
448- disable_job_logging = 0;
449-
450 if (disable_job_logging)
451 nih_debug ("Job logging disabled");
452
453
454=== modified file 'init/man/init.5'
455--- init/man/init.5 2011-12-15 17:50:14 +0000
456+++ init/man/init.5 2012-01-26 15:59:26 +0000
457@@ -761,10 +761,10 @@
458 .B setuid \fIUSERNAME
459 Changes to the user
460 .I USERNAME
461-before running the job's process.
462+before running any job process.
463
464-If this stanza is unspecified, the job will run as root in the case of
465-system jobs, and as the user in the case of user jobs.
466+If this stanza is unspecified, all job processes will run as root in the
467+case of system jobs, and as the user in the case of user jobs.
468
469 Note that system jobs using the
470 .B setuid
471@@ -777,14 +777,15 @@
472 .B setgid \fIGROUPNAME
473 Changes to the group
474 .I GROUPNAME
475-before running the job's process.
476+before running any job process.
477
478 If this stanza is unspecified, the primary group of the user specified
479 in the
480 .B setuid
481-block is used. If both stanzas are unspecified, the job will run with
482-its group ID set to 0 in the case of system jobs, and as the primary
483-group of the user in the case of User Jobs.
484+block is used for all job processes. If both stanzas are unspecified,
485+all job processes will run with its group ID set to 0 in the case of
486+system jobs, and as the primary group of the user in the case of User
487+Jobs.
488 .\"
489 .SS Override File Handling
490 Override files allow a jobs environment to be changed without modifying
491
492=== modified file 'init/tests/test_job_process.c'
493--- init/tests/test_job_process.c 2011-12-22 16:52:22 +0000
494+++ init/tests/test_job_process.c 2012-01-26 15:59:26 +0000
495@@ -296,13 +296,21 @@
496 char *p;
497 int ok;
498 char buffer[1024];
499-
500+ pid_t pid;
501
502 TEST_FUNCTION ("job_process_run");
503
504 TEST_FILENAME (filename);
505 program_name = "test";
506
507+ TEST_FILENAME (dirname);
508+ TEST_EQ (mkdir (dirname, 0755), 0);
509+
510+ /* Override default location to ensure job output goes to a
511+ * writeable location
512+ */
513+ TEST_EQ (setenv ("UPSTART_LOGDIR", dirname, 1), 0);
514+
515 /* Check that we can run a simple command, and have the process id
516 * and state filled in. We should be able to wait for the pid to
517 * finish and see that it has been run as expected.
518@@ -1277,6 +1285,8 @@
519 class->process[PROCESS_MAIN]->script = FALSE;
520
521 job = job_new (class, "");
522+ TEST_NE_P (job, NULL);
523+
524 job->goal = JOB_START;
525 job->state = JOB_SPAWNED;
526
527@@ -1293,6 +1303,9 @@
528 TEST_TRUE (WIFSIGNALED (status));
529 TEST_EQ (WTERMSIG (status), SIGKILL);
530
531+ /* allow destructor to write any lingering unflushed data */
532+ nih_free (class);
533+
534 TEST_EQ (stat (filename, &statbuf), 0);
535
536 TEST_TRUE (S_ISREG (statbuf.st_mode));
537@@ -1333,7 +1346,6 @@
538 fclose (output);
539
540 TEST_EQ (unlink (filename), 0);
541- nih_free (class);
542
543 /************************************************************/
544 TEST_FEATURE ("with multi-line script that is killed");
545@@ -2523,14 +2535,36 @@
546 nih_free (class);
547
548 /************************************************************
549- * No point in running a test for:
550- *
551- * TEST_FEATURE ("with single-line command running an invalid command");
552- *
553- * Since as such commands are exec'ed directly, there is no shell to report
554- * an error back - exec just fails.
555- *
556+ * Superficially, there seems little point in running a test for
557+ * this scenario since if Upstart attempts to exec(2) directly a
558+ * command that does not exist, the exec simply fails (since
559+ * there is no shell to report the error).
560+ *
561+ * And yet -- ironically -- bug 912558 would have been prevented
562+ * had we originally tested this scenario!
563 ************************************************************/
564+ TEST_FEATURE ("with single-line command running an invalid command");
565+
566+ class = job_class_new (NULL, "buzz", NULL);
567+ TEST_NE_P (class, NULL);
568+
569+ TEST_FILENAME (filename);
570+ TEST_GT (sprintf (filename, "%s/buzz.log", dirname), 0);
571+
572+ class->console = CONSOLE_LOG;
573+ class->process[PROCESS_MAIN] = process_new (class);
574+ class->process[PROCESS_MAIN]->command = nih_strdup (
575+ class->process[PROCESS_MAIN],
576+ "/this/command/does/not/exist");
577+ class->process[PROCESS_MAIN]->script = FALSE;
578+
579+ job = job_new (class, "");
580+ job->goal = JOB_START;
581+ job->state = JOB_SPAWNED;
582+
583+ ret = job_process_run (job, PROCESS_MAIN);
584+ TEST_LT (ret, 0);
585+ nih_free (class);
586
587 /************************************************************/
588 TEST_FEATURE ("with multi-line script running an invalid command");
589@@ -2713,6 +2747,246 @@
590 TEST_EQ (unlink (filename), 0);
591 nih_free (class);
592
593+ /************************************************************/
594+ /* XXX: Note that we don't force a watch update here to simulate
595+ * a job that writes data _after_ Upstart has run nih_io_handle_fds()
596+ * in the main loop and just _before_ it exits _in the same main
597+ * loop iteration_.
598+ */
599+ TEST_FEATURE ("with single line command writing fast and exiting");
600+
601+ class = job_class_new (NULL, "budapest", NULL);
602+ TEST_NE_P (class, NULL);
603+
604+ TEST_FILENAME (filename);
605+ TEST_GT (sprintf (filename, "%s/budapest.log", dirname), 0);
606+
607+ class->console = CONSOLE_LOG;
608+ class->process[PROCESS_MAIN] = process_new (class);
609+ /* program to run "fast", so directly exec a program with
610+ * no shell intervention.
611+ */
612+ class->process[PROCESS_MAIN]->command = nih_sprintf (
613+ class->process[PROCESS_MAIN],
614+ "%s hello\n",
615+ TEST_CMD_ECHO);
616+ class->process[PROCESS_MAIN]->script = FALSE;
617+
618+ job = job_new (class, "");
619+ job->goal = JOB_START;
620+ job->state = JOB_SPAWNED;
621+
622+ ret = job_process_run (job, PROCESS_MAIN);
623+ TEST_EQ (ret, 0);
624+
625+ /* allow destructor to write any lingering unflushed data */
626+ nih_free (class);
627+
628+ TEST_EQ (stat (filename, &statbuf), 0);
629+
630+ TEST_TRUE (S_ISREG (statbuf.st_mode));
631+
632+ TEST_TRUE (statbuf.st_mode & S_IRUSR);
633+ TEST_TRUE (statbuf.st_mode & S_IWUSR);
634+ TEST_FALSE (statbuf.st_mode & S_IXUSR);
635+
636+ TEST_TRUE (statbuf.st_mode & S_IRGRP);
637+ TEST_FALSE (statbuf.st_mode & S_IWGRP);
638+ TEST_FALSE (statbuf.st_mode & S_IXGRP);
639+
640+ TEST_FALSE (statbuf.st_mode & S_IROTH);
641+ TEST_FALSE (statbuf.st_mode & S_IWOTH);
642+ TEST_FALSE (statbuf.st_mode & S_IXOTH);
643+
644+ output = fopen (filename, "r");
645+ TEST_NE_P (output, NULL);
646+
647+ TEST_FILE_EQ (output, "hello\r\n");
648+ TEST_FILE_END (output);
649+ fclose (output);
650+
651+ TEST_EQ (unlink (filename), 0);
652+
653+ /************************************************************/
654+ TEST_FEATURE ("with single line command writing lots of data fast and exiting");
655+
656+ class = job_class_new (NULL, "foo", NULL);
657+ TEST_NE_P (class, NULL);
658+
659+ TEST_FILENAME (filename);
660+ TEST_GT (sprintf (filename, "%s/foo.log", dirname), 0);
661+
662+ class->console = CONSOLE_LOG;
663+ class->process[PROCESS_MAIN] = process_new (class);
664+ /* program must run "fast", so directly exec with
665+ * no shell intervention.
666+ *
667+ * Writes large number of nulls (3MB).
668+ */
669+#define EXPECTED_1K_BLOCKS (1024*3)
670+#define TEST_BLOCKSIZE 1024
671+
672+ class->process[PROCESS_MAIN]->command = nih_sprintf (
673+ class->process[PROCESS_MAIN],
674+ "%s if=/dev/zero bs=%d count=%d",
675+ TEST_CMD_DD, TEST_BLOCKSIZE, EXPECTED_1K_BLOCKS);
676+ class->process[PROCESS_MAIN]->script = FALSE;
677+
678+ NIH_MUST (nih_child_add_watch (NULL,
679+ -1,
680+ NIH_CHILD_ALL,
681+ job_process_handler,
682+ NULL));
683+
684+ job = job_new (class, "");
685+ job->goal = JOB_START;
686+ job->state = JOB_SPAWNED;
687+
688+ ret = job_process_run (job, PROCESS_MAIN);
689+ TEST_EQ (ret, 0);
690+
691+ pid = job->pid[PROCESS_MAIN];
692+
693+ /* job will block until something reads the other end of the pty */
694+ TEST_EQ (kill (pid, 0), 0);
695+
696+ {
697+ size_t bytes;
698+ size_t expected_bytes = TEST_BLOCKSIZE * EXPECTED_1K_BLOCKS;
699+ off_t filesize = (off_t)-1;
700+
701+ /* Check repeatedly for job log output jobs until
702+ * we've either read the expected number of nulls, or we
703+ * timed out.
704+ */
705+ while ( 1 ) {
706+ size_t length;
707+ size_t i;
708+ struct timeval t;
709+ nih_local char *file = NULL;
710+
711+ t.tv_sec = 1;
712+ t.tv_usec = 0;
713+
714+ TEST_FORCE_WATCH_UPDATE_TIMEOUT (t);
715+
716+ TEST_EQ (stat (filename, &statbuf), 0);
717+
718+ /* We expect the file size to change */
719+ if (statbuf.st_size == filesize) {
720+ break;
721+ }
722+
723+ filesize = statbuf.st_size;
724+
725+ file = nih_file_read (NULL, filename, &length);
726+ TEST_NE_P (file, NULL);
727+
728+ bytes = 0;
729+ for (i=0; i < length; ++i) {
730+ if (file[i] == '\0')
731+ bytes++;
732+ }
733+
734+ if (bytes == expected_bytes) {
735+ break;
736+ }
737+ }
738+
739+ TEST_EQ (bytes, expected_bytes);
740+ }
741+
742+ TEST_EQ (kill (pid, 0), 0);
743+ nih_child_poll ();
744+
745+ /* The process should now be dead */
746+ TEST_EQ (kill (pid, 0), -1);
747+ TEST_EQ (errno, ESRCH);
748+
749+ nih_free (class);
750+ TEST_EQ (stat (filename, &statbuf), 0);
751+
752+ TEST_TRUE (S_ISREG (statbuf.st_mode));
753+
754+ TEST_TRUE (statbuf.st_mode & S_IRUSR);
755+ TEST_TRUE (statbuf.st_mode & S_IWUSR);
756+ TEST_FALSE (statbuf.st_mode & S_IXUSR);
757+
758+ TEST_TRUE (statbuf.st_mode & S_IRGRP);
759+ TEST_FALSE (statbuf.st_mode & S_IWGRP);
760+ TEST_FALSE (statbuf.st_mode & S_IXGRP);
761+
762+ TEST_FALSE (statbuf.st_mode & S_IROTH);
763+ TEST_FALSE (statbuf.st_mode & S_IWOTH);
764+ TEST_FALSE (statbuf.st_mode & S_IXOTH);
765+
766+ TEST_EQ (unlink (filename), 0);
767+
768+#undef EXPECTED_1K_BLOCKS
769+#undef TEST_BLOCKSIZE
770+
771+ /************************************************************/
772+ /* Applies to respawn jobs too */
773+
774+ TEST_FEATURE ("with log object freed on process exit");
775+
776+ class = job_class_new (NULL, "acorn", NULL);
777+ TEST_NE_P (class, NULL);
778+
779+ TEST_FILENAME (filename);
780+ TEST_GT (sprintf (filename, "%s/acorn.log", dirname), 0);
781+
782+ class->console = CONSOLE_LOG;
783+ class->process[PROCESS_MAIN] = process_new (class);
784+ class->process[PROCESS_MAIN]->command = nih_sprintf (
785+ class->process[PROCESS_MAIN],
786+ "%s hello",
787+ TEST_CMD_ECHO);
788+ class->process[PROCESS_MAIN]->script = FALSE;
789+
790+ /* XXX: Manually add the class so job_process_find() works */
791+ nih_hash_add (job_classes, &class->entry);
792+
793+ NIH_MUST (nih_child_add_watch (NULL,
794+ -1,
795+ NIH_CHILD_ALL,
796+ job_process_handler,
797+ NULL));
798+
799+ job = job_new (class, "");
800+ job->goal = JOB_START;
801+ job->state = JOB_SPAWNED;
802+
803+ TEST_EQ_P (job->log, NULL);
804+
805+ ret = job_process_run (job, PROCESS_MAIN);
806+ TEST_EQ (ret, 0);
807+
808+ pid = job->pid[PROCESS_MAIN];
809+
810+ job->goal = JOB_STOP;
811+ job->state = JOB_KILLED;
812+
813+ TEST_NE (job->pid[PROCESS_MAIN], 0);
814+
815+ TEST_NE_P (job->log, NULL);
816+
817+ TEST_FREE_TAG (job);
818+ TEST_FREE_TAG (job->log);
819+
820+ TEST_FORCE_WATCH_UPDATE ();
821+
822+ nih_child_poll ();
823+
824+ /* Should have been destroyed now */
825+ TEST_FREE (job);
826+ TEST_FREE (job->log);
827+
828+ nih_free (class);
829+ unlink (filename);
830+
831+ /************************************************************/
832+
833 /* Check that we can succesfully setuid and setgid to
834 * ourselves. This should always work, privileged or
835 * otherwise.
836@@ -3315,7 +3589,7 @@
837
838 TEST_EQ (fclose (output), 0);
839
840- unlink (filename);
841+ TEST_EQ (unlink (filename), 0);
842
843 TEST_EQ (rmdir (dirname), 0);
844 TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0);
845@@ -3409,6 +3683,9 @@
846
847 TEST_FORCE_WATCH_UPDATE ();
848
849+ /* This will eventually call the log destructor */
850+ nih_free (class);
851+
852 output = fopen (filename, "r");
853 TEST_NE_P (output, NULL);
854
855@@ -3423,8 +3700,6 @@
856 TEST_EQ (rmdir (dirname), 0);
857 TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0);
858
859- nih_free (job);
860-
861 /************************************************************/
862 TEST_FEATURE ("read data from daemon process");
863
864@@ -3456,10 +3731,14 @@
865 pid = job_process_spawn (job, args, NULL, FALSE, -1);
866 TEST_GT (pid, 0);
867
868+ TEST_FORCE_WATCH_UPDATE ();
869+
870 TEST_EQ (waitpid (pid, &status, 0), pid);
871 TEST_TRUE (WIFEXITED (status));
872+ TEST_EQ (WEXITSTATUS (status), 0);
873
874- TEST_FORCE_WATCH_UPDATE ();
875+ /* This will eventually call the log destructor */
876+ nih_free (class);
877
878 output = fopen (filename, "r");
879 TEST_NE_P (output, NULL);
880@@ -3475,9 +3754,6 @@
881 TEST_EQ (rmdir (dirname), 0);
882 TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0);
883
884- nih_free (job);
885-
886-
887 /* FIXME */
888 #if 0
889 /************************************************************/
890@@ -3538,14 +3814,13 @@
891 TEST_EQ (err->number, ENOMEM);
892 nih_free (err);
893 }
894- nih_free (job);
895+ nih_free (class);
896 }
897 #else
898 /* FIXME */
899 TEST_FEATURE ("WARNING: FIXME: test 'when no free ptys' disabled due to kernel bug");
900 #endif
901
902- nih_free (class);
903 TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0);
904 }
905
906
907=== modified file 'init/tests/test_log.c'
908--- init/tests/test_log.c 2011-12-09 14:07:11 +0000
909+++ init/tests/test_log.c 2012-01-26 15:59:26 +0000
910@@ -91,7 +91,6 @@
911 test_log_new (void)
912 {
913 Log *log;
914- int fds[2] = { -1, -1 };
915 char path[] = "/foo";
916 char str[] = "hello, world!";
917 char str2[] = "The end?";
918@@ -104,6 +103,8 @@
919 FILE *output;
920 mode_t old_perms;
921 off_t old_size;
922+ int pty_master;
923+ int pty_slave;
924
925 TEST_FUNCTION ("log_new");
926
927@@ -130,8 +131,8 @@
928 TEST_FEATURE ("object checks with uid 0");
929
930 TEST_ALLOC_FAIL {
931- TEST_EQ (pipe (fds), 0);
932- log = log_new (NULL, path, fds[0], 0);
933+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
934+ log = log_new (NULL, path, pty_master, 0);
935
936 /* Handle all alloc failures where the alloc calls were
937 * initiated by log_new().
938@@ -140,8 +141,8 @@
939 (test_alloc_failed <= LOG_NEW_ALLOC_CALLS)) {
940
941 TEST_EQ_P (log, NULL);
942- close (fds[0]);
943- close (fds[1]);
944+ close (pty_master);
945+ close (pty_slave);
946 continue;
947 }
948
949@@ -153,11 +154,11 @@
950 TEST_ALLOC_PARENT (log->path, log);
951
952 TEST_EQ_STR (log->path, path);
953- TEST_EQ (log->io->watch->fd, fds[0]);
954+ TEST_EQ (log->io->watch->fd, pty_master);
955 TEST_EQ (log->uid, 0);
956 TEST_LT (log->fd, 0);
957
958- close (fds[1]);
959+ close (pty_slave);
960
961 /* frees fds[0] */
962 nih_free (log);
963@@ -168,13 +169,13 @@
964 /* XXX: No support for logging of user job output currently */
965 TEST_FEATURE ("ensure logging disallowed for uid >0");
966
967- TEST_EQ (pipe (fds), 0);
968+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
969
970- log = log_new (NULL, path, fds[0], 1);
971+ log = log_new (NULL, path, pty_master, 1);
972 TEST_EQ (log, NULL);
973
974- close (fds[0]);
975- close (fds[1]);
976+ close (pty_master);
977+ close (pty_slave);
978
979 /************************************************************/
980 TEST_FEATURE ("parent check");
981@@ -185,14 +186,15 @@
982 string = NIH_MUST (nih_strdup (NULL, str));
983 }
984
985- TEST_EQ (pipe (fds), 0);
986-
987- log = log_new (string, path, fds[0], 0);
988-
989- if (test_alloc_failed) {
990+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
991+
992+ log = log_new (string, path, pty_master, 0);
993+
994+ if (test_alloc_failed &&
995+ (test_alloc_failed <= LOG_NEW_ALLOC_CALLS)) {
996 TEST_EQ_P (log, NULL);
997- close (fds[0]);
998- close (fds[1]);
999+ close (pty_master);
1000+ close (pty_slave);
1001 nih_free (string);
1002 continue;
1003 }
1004@@ -201,7 +203,7 @@
1005 TEST_ALLOC_PARENT (log, string);
1006 TEST_FREE_TAG (log);
1007
1008- close (fds[1]);
1009+ close (pty_slave);
1010
1011 /* Freeing the parent should free the child */
1012 nih_free (string);
1013@@ -215,9 +217,9 @@
1014
1015 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1016 TEST_LT (stat (filename, &statbuf), 0);
1017- TEST_EQ (pipe (fds), 0);
1018+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1019
1020- log = log_new (NULL, filename, fds[0], 0);
1021+ log = log_new (NULL, filename, pty_master, 0);
1022
1023 /* First time through at this point only log_new() has been called.
1024 * But by the end of the first loop, log_io_reader() will have
1025@@ -232,16 +234,16 @@
1026 if (test_alloc_failed &&
1027 (test_alloc_failed <= LOG_NEW_ALLOC_CALLS)) {
1028 TEST_EQ_P (log, NULL);
1029- close (fds[0]);
1030- close (fds[1]);
1031+ close (pty_master);
1032+ close (pty_slave);
1033 continue;
1034 }
1035
1036 TEST_NE_P (log, NULL);
1037
1038- ret = write (fds[1], str, strlen (str));
1039+ ret = write (pty_slave, str, strlen (str));
1040 TEST_GT (ret, 0);
1041- ret = write (fds[1], "\n", 1);
1042+ ret = write (pty_slave, "\n", 1);
1043 TEST_EQ (ret, 1);
1044
1045 TEST_FORCE_WATCH_UPDATE ();
1046@@ -251,12 +253,13 @@
1047 */
1048 if (test_alloc_failed == 1+LOG_NEW_ALLOC_CALLS) {
1049 TEST_NE_P (log, NULL);
1050- close (fds[1]);
1051+ close (pty_slave);
1052 nih_free (log);
1053+ TEST_EQ (unlink (filename), 0);
1054 continue;
1055 }
1056
1057- close (fds[1]);
1058+ close (pty_slave);
1059 nih_free (log);
1060
1061 TEST_EQ (stat (filename, &statbuf), 0);
1062@@ -277,7 +280,7 @@
1063 output = fopen (filename, "r");
1064 TEST_NE_P (output, NULL);
1065
1066- TEST_FILE_EQ (output, "hello, world!\n");
1067+ TEST_FILE_EQ (output, "hello, world!\r\n");
1068 TEST_FILE_END (output);
1069 fclose (output);
1070
1071@@ -287,17 +290,17 @@
1072 /************************************************************/
1073 TEST_FEATURE ("same logger appending to file with uid 0");
1074
1075- TEST_EQ (pipe (fds), 0);
1076+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1077
1078 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1079 TEST_LT (stat (filename, &statbuf), 0);
1080
1081- log = log_new (NULL, filename, fds[0], 0);
1082+ log = log_new (NULL, filename, pty_master, 0);
1083 TEST_NE_P (log, NULL);
1084
1085- ret = write (fds[1], str, strlen (str));
1086+ ret = write (pty_slave, str, strlen (str));
1087 TEST_GT (ret, 0);
1088- ret = write (fds[1], "\n", 1);
1089+ ret = write (pty_slave, "\n", 1);
1090 TEST_EQ (ret, 1);
1091
1092 TEST_FORCE_WATCH_UPDATE ();
1093@@ -321,11 +324,11 @@
1094 output = fopen (filename, "r");
1095 TEST_NE_P (output, NULL);
1096
1097- TEST_FILE_EQ (output, "hello, world!\n");
1098+ TEST_FILE_EQ (output, "hello, world!\r\n");
1099 TEST_FILE_END (output);
1100 fclose (output);
1101
1102- ret = write (fds[1], str2, strlen (str2));
1103+ ret = write (pty_slave, str2, strlen (str2));
1104 TEST_GT (ret, 0);
1105
1106 TEST_FORCE_WATCH_UPDATE ();
1107@@ -351,31 +354,32 @@
1108 output = fopen (filename, "r");
1109 TEST_NE_P (output, NULL);
1110
1111- TEST_FILE_EQ (output, "hello, world!\n");
1112+ TEST_FILE_EQ (output, "hello, world!\r\n");
1113 TEST_FILE_EQ (output, str2);
1114 TEST_FILE_END (output);
1115 fclose (output);
1116
1117 TEST_EQ (unlink (filename), 0);
1118- close (fds[1]);
1119+ close (pty_slave);
1120 nih_free (log);
1121
1122 /************************************************************/
1123 TEST_FEATURE ("different logger appending to file with uid 0");
1124
1125- TEST_EQ (pipe (fds), 0);
1126+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1127
1128 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1129- log = log_new (NULL, filename, fds[0], 0);
1130+ log = log_new (NULL, filename, pty_master, 0);
1131 TEST_NE_P (log, NULL);
1132
1133 bytes = 0;
1134- ret = write (fds[1], str, strlen (str));
1135+ ret = write (pty_slave, str, strlen (str));
1136 TEST_GT (ret, 0);
1137 bytes += ret;
1138- ret = write (fds[1], "\n", 1);
1139+ ret = write (pty_slave, "\n", 1);
1140 TEST_EQ (ret, 1);
1141- bytes += ret;
1142+ /* XXX: '+1' for '\r' */
1143+ bytes += (ret+1);
1144
1145 TEST_FORCE_WATCH_UPDATE ();
1146
1147@@ -400,16 +404,16 @@
1148 output = fopen (filename, "r");
1149 TEST_NE_P (output, NULL);
1150
1151- TEST_FILE_EQ (output, "hello, world!\n");
1152+ TEST_FILE_EQ (output, "hello, world!\r\n");
1153 TEST_FILE_END (output);
1154 fclose (output);
1155
1156- close (fds[1]);
1157+ close (pty_slave);
1158 nih_free (log);
1159
1160- TEST_EQ (pipe (fds), 0);
1161+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1162
1163- log = log_new (NULL, filename, fds[0], 0);
1164+ log = log_new (NULL, filename, pty_master, 0);
1165 TEST_NE_P (log, NULL);
1166
1167 TEST_EQ (stat (filename, &statbuf), 0);
1168@@ -430,16 +434,17 @@
1169 TEST_EQ (statbuf.st_size, old_size);
1170
1171 bytes = 0;
1172- ret = write (fds[1], str2, strlen (str2));
1173+ ret = write (pty_slave, str2, strlen (str2));
1174 TEST_GT (ret, 0);
1175 bytes += ret;
1176- ret = write (fds[1], "\n", 1);
1177+ ret = write (pty_slave, "\n", 1);
1178 TEST_EQ (ret, 1);
1179- bytes += ret;
1180+ /* '+1' for '\r' */
1181+ bytes += (1+ret);
1182
1183 TEST_FORCE_WATCH_UPDATE ();
1184
1185- close (fds[1]);
1186+ close (pty_slave);
1187 nih_free (log);
1188
1189 TEST_EQ (stat (filename, &statbuf), 0);
1190@@ -462,8 +467,8 @@
1191 output = fopen (filename, "r");
1192 TEST_NE_P (output, NULL);
1193
1194- TEST_FILE_EQ (output, "hello, world!\n");
1195- TEST_FILE_EQ (output, "The end?\n");
1196+ TEST_FILE_EQ (output, "hello, world!\r\n");
1197+ TEST_FILE_EQ (output, "The end?\r\n");
1198 TEST_FILE_END (output);
1199 fclose (output);
1200
1201@@ -472,15 +477,15 @@
1202 /************************************************************/
1203 TEST_FEATURE ("ensure logging resumes when file made accessible with uid 0");
1204
1205- TEST_EQ (pipe (fds), 0);
1206+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1207
1208 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1209- log = log_new (NULL, filename, fds[0], 0);
1210+ log = log_new (NULL, filename, pty_master, 0);
1211 TEST_NE_P (log, NULL);
1212
1213- ret = write (fds[1], str, strlen (str));
1214+ ret = write (pty_slave, str, strlen (str));
1215 TEST_GT (ret, 0);
1216- ret = write (fds[1], "\n", 1);
1217+ ret = write (pty_slave, "\n", 1);
1218 TEST_EQ (ret, 1);
1219
1220 TEST_FORCE_WATCH_UPDATE ();
1221@@ -508,7 +513,7 @@
1222 output = fopen (filename, "r");
1223 TEST_NE_P (output, NULL);
1224
1225- TEST_FILE_EQ (output, "hello, world!\n");
1226+ TEST_FILE_EQ (output, "hello, world!\r\n");
1227 TEST_FILE_END (output);
1228 fclose (output);
1229
1230@@ -516,9 +521,9 @@
1231 TEST_EQ (chmod (filename, 0x0), 0);
1232
1233 /* Send more data to logger */
1234- ret = write (fds[1], str2, strlen (str2));
1235+ ret = write (pty_slave, str2, strlen (str2));
1236 TEST_GT (ret, 0);
1237- ret = write (fds[1], "\n", 1);
1238+ ret = write (pty_slave, "\n", 1);
1239 TEST_EQ (ret, 1);
1240
1241 /* File shouldn't have changed */
1242@@ -531,12 +536,12 @@
1243 /* Further data should cause previous data that could not be
1244 * written to be flushed to the file.
1245 */
1246- ret = write (fds[1], "foo\n", 4);
1247+ ret = write (pty_slave, "foo\n", 4);
1248 TEST_EQ (ret, 4);
1249
1250 TEST_FORCE_WATCH_UPDATE ();
1251
1252- close (fds[1]);
1253+ close (pty_slave);
1254 nih_free (log);
1255
1256 TEST_EQ (stat (filename, &statbuf), 0);
1257@@ -559,9 +564,9 @@
1258 TEST_NE_P (output, NULL);
1259
1260 /* Re-check entire file contents */
1261- TEST_FILE_EQ (output, "hello, world!\n");
1262- TEST_FILE_EQ (output, "The end?\n");
1263- TEST_FILE_EQ (output, "foo\n");
1264+ TEST_FILE_EQ (output, "hello, world!\r\n");
1265+ TEST_FILE_EQ (output, "The end?\r\n");
1266+ TEST_FILE_EQ (output, "foo\r\n");
1267 TEST_FILE_END (output);
1268 fclose (output);
1269
1270@@ -570,18 +575,18 @@
1271 /************************************************************/
1272 TEST_FEATURE ("ensure logger flushes when destroyed with uid 0");
1273
1274- TEST_EQ (pipe (fds), 0);
1275+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1276
1277 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1278
1279 TEST_EQ (rmdir (dirname), 0);
1280
1281- log = log_new (NULL, filename, fds[0], 0);
1282+ log = log_new (NULL, filename, pty_master, 0);
1283 TEST_NE_P (log, NULL);
1284
1285- ret = write (fds[1], str, strlen (str));
1286+ ret = write (pty_slave, str, strlen (str));
1287 TEST_GT (ret, 0);
1288- ret = write (fds[1], "\n", 1);
1289+ ret = write (pty_slave, "\n", 1);
1290 TEST_EQ (ret, 1);
1291
1292 TEST_FORCE_WATCH_UPDATE ();
1293@@ -591,7 +596,7 @@
1294 umask (old_perms);
1295
1296 /* No more data sent to ensure logger writes it on log destroy */
1297- close (fds[1]);
1298+ close (pty_slave);
1299 nih_free (log);
1300
1301 output = fopen (filename, "r");
1302@@ -611,7 +616,7 @@
1303 TEST_FALSE (statbuf.st_mode & S_IROTH);
1304 TEST_FALSE (statbuf.st_mode & S_IWOTH);
1305 TEST_FALSE (statbuf.st_mode & S_IXOTH);
1306- TEST_FILE_EQ (output, "hello, world!\n");
1307+ TEST_FILE_EQ (output, "hello, world!\r\n");
1308 TEST_FILE_END (output);
1309 fclose (output);
1310
1311@@ -620,35 +625,31 @@
1312 /************************************************************/
1313 TEST_FEATURE ("ensure log written when directory created accessible with uid 0");
1314
1315- TEST_EQ (pipe (fds), 0);
1316+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1317
1318 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1319
1320 TEST_EQ (rmdir (dirname), 0);
1321
1322- log = log_new (NULL, filename, fds[0], 0);
1323+ log = log_new (NULL, filename, pty_master, 0);
1324 TEST_NE_P (log, NULL);
1325
1326- ret = write (fds[1], str, strlen (str));
1327+ ret = write (pty_slave, str, strlen (str));
1328 TEST_GT (ret, 0);
1329- ret = write (fds[1], "\n", 1);
1330+ ret = write (pty_slave, "\n", 1);
1331 TEST_EQ (ret, 1);
1332
1333- TEST_FORCE_WATCH_UPDATE ();
1334-
1335 old_perms = umask (0);
1336 TEST_EQ (mkdir (dirname, 0755), 0);
1337 umask (old_perms);
1338
1339 /* Send more data */
1340- ret = write (fds[1], str2, strlen (str2));
1341+ ret = write (pty_slave, str2, strlen (str2));
1342 TEST_GT (ret, 0);
1343- ret = write (fds[1], "\n", 1);
1344+ ret = write (pty_slave, "\n", 1);
1345 TEST_EQ (ret, 1);
1346
1347- TEST_FORCE_WATCH_UPDATE ();
1348-
1349- close (fds[1]);
1350+ close (pty_slave);
1351 nih_free (log);
1352
1353 output = fopen (filename, "r");
1354@@ -668,8 +669,8 @@
1355 TEST_FALSE (statbuf.st_mode & S_IROTH);
1356 TEST_FALSE (statbuf.st_mode & S_IWOTH);
1357 TEST_FALSE (statbuf.st_mode & S_IXOTH);
1358- TEST_FILE_EQ (output, "hello, world!\n");
1359- TEST_FILE_EQ (output, "The end?\n");
1360+ TEST_FILE_EQ (output, "hello, world!\r\n");
1361+ TEST_FILE_EQ (output, "The end?\r\n");
1362 TEST_FILE_END (output);
1363 fclose (output);
1364
1365@@ -678,16 +679,16 @@
1366 /************************************************************/
1367 TEST_FEATURE ("ensure remainder of log written when file deleted with uid 0");
1368
1369- TEST_EQ (pipe (fds), 0);
1370+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1371
1372 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1373
1374- log = log_new (NULL, filename, fds[0], 0);
1375+ log = log_new (NULL, filename, pty_master, 0);
1376 TEST_NE_P (log, NULL);
1377
1378- ret = write (fds[1], str, strlen (str));
1379+ ret = write (pty_slave, str, strlen (str));
1380 TEST_GT (ret, 0);
1381- ret = write (fds[1], "\n", 1);
1382+ ret = write (pty_slave, "\n", 1);
1383 TEST_EQ (ret, 1);
1384
1385 TEST_FORCE_WATCH_UPDATE ();
1386@@ -711,7 +712,7 @@
1387 TEST_FALSE (statbuf.st_mode & S_IXOTH);
1388 TEST_EQ (fstat (log->fd, &statbuf), 0);
1389
1390- TEST_FILE_EQ (output, "hello, world!\n");
1391+ TEST_FILE_EQ (output, "hello, world!\r\n");
1392 TEST_FILE_END (output);
1393 fclose (output);
1394
1395@@ -720,9 +721,9 @@
1396 TEST_EQ (fstat (log->fd, &statbuf), 0);
1397
1398 /* Send more data */
1399- ret = write (fds[1], str2, strlen (str2));
1400+ ret = write (pty_slave, str2, strlen (str2));
1401 TEST_GT (ret, 0);
1402- ret = write (fds[1], "\n", 1);
1403+ ret = write (pty_slave, "\n", 1);
1404 TEST_EQ (ret, 1);
1405
1406 TEST_FORCE_WATCH_UPDATE ();
1407@@ -744,31 +745,31 @@
1408 TEST_FALSE (statbuf.st_mode & S_IROTH);
1409 TEST_FALSE (statbuf.st_mode & S_IWOTH);
1410 TEST_FALSE (statbuf.st_mode & S_IXOTH);
1411- TEST_FILE_EQ (output, "The end?\n");
1412+ TEST_FILE_EQ (output, "The end?\r\n");
1413 TEST_FILE_END (output);
1414 fclose (output);
1415
1416- close (fds[1]);
1417+ close (pty_slave);
1418 nih_free (log);
1419 TEST_EQ (unlink (filename), 0);
1420
1421 /************************************************************/
1422 TEST_FEATURE ("writing 1 null with uid 0");
1423
1424- TEST_EQ (pipe (fds), 0);
1425+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1426
1427 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1428 TEST_LT (stat (filename, &statbuf), 0);
1429
1430- log = log_new (NULL, filename, fds[0], 0);
1431+ log = log_new (NULL, filename, pty_master, 0);
1432 TEST_NE_P (log, NULL);
1433
1434- ret = write (fds[1], "\000", 1);
1435+ ret = write (pty_slave, "\000", 1);
1436 TEST_EQ (ret, 1);
1437
1438 TEST_FORCE_WATCH_UPDATE ();
1439
1440- close (fds[1]);
1441+ close (pty_slave);
1442 nih_free (log);
1443
1444 TEST_EQ (stat (filename, &statbuf), 0);
1445@@ -799,20 +800,20 @@
1446 /************************************************************/
1447 TEST_FEATURE ("writing >1 null with uid 0");
1448
1449- TEST_EQ (pipe (fds), 0);
1450+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1451
1452 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1453 TEST_LT (stat (filename, &statbuf), 0);
1454
1455- log = log_new (NULL, filename, fds[0], 0);
1456+ log = log_new (NULL, filename, pty_master, 0);
1457 TEST_NE_P (log, NULL);
1458
1459- ret = write (fds[1], "\000\000\000", 3);
1460+ ret = write (pty_slave, "\000\000\000", 3);
1461 TEST_EQ (ret, 3);
1462
1463 TEST_FORCE_WATCH_UPDATE ();
1464
1465- close (fds[1]);
1466+ close (pty_slave);
1467 nih_free (log);
1468
1469 TEST_EQ (stat (filename, &statbuf), 0);
1470@@ -843,20 +844,20 @@
1471 /************************************************************/
1472 TEST_FEATURE ("writing 1 non-printable only with uid 0");
1473
1474- TEST_EQ (pipe (fds), 0);
1475+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1476
1477 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1478 TEST_LT (stat (filename, &statbuf), 0);
1479
1480- log = log_new (NULL, filename, fds[0], 0);
1481+ log = log_new (NULL, filename, pty_master, 0);
1482 TEST_NE_P (log, NULL);
1483
1484- ret = write (fds[1], " ", 1);
1485+ ret = write (pty_slave, " ", 1);
1486 TEST_EQ (ret, 1);
1487
1488 TEST_FORCE_WATCH_UPDATE ();
1489
1490- close (fds[1]);
1491+ close (pty_slave);
1492 nih_free (log);
1493
1494 TEST_EQ (stat (filename, &statbuf), 0);
1495@@ -887,20 +888,20 @@
1496 /************************************************************/
1497 TEST_FEATURE ("writing >1 non-printable only with uid 0");
1498
1499- TEST_EQ (pipe (fds), 0);
1500+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1501
1502 TEST_GT (sprintf (filename, "%s/test.log", dirname), 0);
1503 TEST_LT (stat (filename, &statbuf), 0);
1504
1505- log = log_new (NULL, filename, fds[0], 0);
1506+ log = log_new (NULL, filename, pty_master, 0);
1507 TEST_NE_P (log, NULL);
1508
1509- ret = write (fds[1], "\n \t", 3);
1510+ ret = write (pty_slave, "\n \t", 3);
1511 TEST_EQ (ret, 3);
1512
1513 TEST_FORCE_WATCH_UPDATE ();
1514
1515- close (fds[1]);
1516+ close (pty_slave);
1517 nih_free (log);
1518
1519 TEST_EQ (stat (filename, &statbuf), 0);
1520@@ -917,15 +918,18 @@
1521 TEST_FALSE (statbuf.st_mode & S_IROTH);
1522 TEST_FALSE (statbuf.st_mode & S_IWOTH);
1523 TEST_FALSE (statbuf.st_mode & S_IXOTH);
1524- TEST_EQ (statbuf.st_size, 3);
1525+
1526+ /* '\r', '\n', ' ', '\t' */
1527+ TEST_EQ (statbuf.st_size, 4);
1528
1529 output = fopen (filename, "r");
1530 TEST_NE_P (output, NULL);
1531
1532- TEST_EQ (fread (buffer, 1, 3, output), 3);
1533- TEST_EQ (buffer[0], '\n');
1534- TEST_EQ (buffer[1], ' ');
1535- TEST_EQ (buffer[2], '\t');
1536+ TEST_EQ (fread (buffer, 1, 4, output), 4);
1537+ TEST_EQ (buffer[0], '\r');
1538+ TEST_EQ (buffer[1], '\n');
1539+ TEST_EQ (buffer[2], ' ');
1540+ TEST_EQ (buffer[3], '\t');
1541
1542 TEST_FILE_END (output);
1543 fclose (output);
1544@@ -949,13 +953,17 @@
1545
1546 memset (long_path+len, 'J', sizeof(long_path)-len-1);
1547
1548- TEST_EQ (pipe (fds), 0);
1549-
1550- log = log_new (NULL, long_path, fds[0], 0);
1551+ nih_debug("long_path='%s'", long_path);
1552+
1553+ pty_master = -1; pty_slave = -1;
1554+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1555+
1556+ log = log_new (NULL, long_path, pty_master, 0);
1557 TEST_NE_P (log, NULL);
1558
1559+ close (pty_slave);
1560+ pty_slave = -1;
1561 nih_free (log);
1562- close (fds[1]);
1563 }
1564
1565 /************************************************************/
1566@@ -975,12 +983,13 @@
1567
1568 memset (illegal_path+len, 'z', sizeof(illegal_path)-len-1);
1569
1570- TEST_EQ (pipe (fds), 0);
1571+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1572
1573- log = log_new (NULL, illegal_path, fds[0], 0);
1574+ log = log_new (NULL, illegal_path, pty_master, 0);
1575 TEST_EQ_P (log, NULL);
1576
1577- close (fds[1]);
1578+ close (pty_master);
1579+ close (pty_slave);
1580 }
1581
1582 /************************************************************/
1583@@ -997,13 +1006,13 @@
1584
1585 memset (long_path+len, 'J', sizeof(long_path)-len-1);
1586
1587- TEST_EQ (pipe (fds), 0);
1588+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1589
1590- log = log_new (NULL, long_path, fds[0], 0);
1591+ log = log_new (NULL, long_path, pty_master, 0);
1592 TEST_NE_P (log, NULL);
1593
1594+ close (pty_slave);
1595 nih_free (log);
1596- close (fds[1]);
1597 }
1598
1599 /************************************************************/
1600@@ -1020,12 +1029,13 @@
1601
1602 memset (illegal_path+len, 'z', sizeof(illegal_path)-len-1);
1603
1604- TEST_EQ (pipe (fds), 0);
1605+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1606
1607- log = log_new (NULL, illegal_path, fds[0], 0);
1608+ log = log_new (NULL, illegal_path, pty_master, 0);
1609 TEST_EQ_P (log, NULL);
1610
1611- close (fds[1]);
1612+ close (pty_master);
1613+ close (pty_slave);
1614 }
1615
1616 /************************************************************/
1617@@ -1035,48 +1045,48 @@
1618 TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0);
1619 }
1620
1621- void
1622+void
1623 test_log_destroy (void)
1624 {
1625 Log *log;
1626- int fd;
1627 int ret;
1628 int flags;
1629- int fds[2];
1630 char str[] = "hello, world!";
1631+ int pty_master;
1632+ int pty_slave;
1633
1634 TEST_FUNCTION ("log_destroy");
1635
1636 /************************************************************/
1637 TEST_FEATURE ("ensure log fd closed with uid 0");
1638
1639- fd = dup (2);
1640- TEST_GT (fd, 0);
1641+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1642
1643- flags = fcntl (fd, F_GETFL);
1644+ flags = fcntl (pty_master, F_GETFL);
1645 TEST_NE (flags, -1);
1646
1647- log = log_new (NULL, "/foo", fd, 0);
1648+ log = log_new (NULL, "/foo", pty_master, 0);
1649 TEST_NE_P (log, NULL);
1650
1651+ close (pty_slave);
1652 nih_free (log);
1653
1654- flags = fcntl (fd, F_GETFL);
1655+ flags = fcntl (pty_master, F_GETFL);
1656 TEST_EQ (flags, -1);
1657 TEST_EQ (errno, EBADF);
1658
1659 /************************************************************/
1660 TEST_FEATURE ("ensure path and io elements freed with uid 0");
1661
1662- fd = dup (2);
1663- TEST_GT (fd, 0);
1664+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1665
1666- log = log_new (NULL, "/bar", fd, 0);
1667+ log = log_new (NULL, "/bar", pty_master, 0);
1668 TEST_NE_P (log, NULL);
1669
1670 TEST_FREE_TAG (log->path);
1671 TEST_FREE_TAG (log->io);
1672
1673+ close (pty_slave);
1674 nih_free (log);
1675
1676 TEST_FREE (log->path);
1677@@ -1085,13 +1095,12 @@
1678 /************************************************************/
1679 TEST_FEATURE ("ensure unflushed data freed with uid 0");
1680
1681- TEST_EQ (pipe (fds), 0);
1682- TEST_GT (fd, 0);
1683+ TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0);
1684
1685- log = log_new (NULL, "/bar", fds[0], 0);
1686+ log = log_new (NULL, "/bar", pty_master, 0);
1687 TEST_NE_P (log, NULL);
1688
1689- ret = write (fds[1], str, strlen (str));
1690+ ret = write (pty_slave, str, strlen (str));
1691 TEST_GT (ret, 0);
1692
1693 TEST_FORCE_WATCH_UPDATE ();
1694@@ -1100,6 +1109,7 @@
1695 TEST_EQ_STR (log->unflushed->buf, str);
1696 TEST_FREE_TAG (log->unflushed);
1697
1698+ close (pty_slave);
1699 nih_free (log);
1700 TEST_FREE (log->unflushed);
1701 }
1702
1703=== added file 'util/tests/test_user_sessions.sh'
1704--- util/tests/test_user_sessions.sh 1970-01-01 00:00:00 +0000
1705+++ util/tests/test_user_sessions.sh 2012-01-26 15:59:26 +0000
1706@@ -0,0 +1,1091 @@
1707+#!/bin/sh
1708+#---------------------------------------------------------------------
1709+# Script to run minimal Upstart user session tests.
1710+#
1711+# Note that this script _cannot_ be run as part of the "make check"
1712+# tests since those tests stimulate functions and features of the
1713+# as-yet-uninstalled version of Upstart. However, this script needs to
1714+# run on a system where the version of Upstart under test has _already_
1715+# been fully installed.
1716+#---------------------------------------------------------------------
1717+#
1718+# Copyright (C) 2011 Canonical Ltd.
1719+#
1720+# Author: James Hunt <james.hunt@canonical.com>
1721+#
1722+# This program is free software: you can redistribute it and/or modify
1723+# it under the terms of the GNU General Public License as published by
1724+# the Free Software Foundation, version 3 of the License.
1725+#
1726+# This program is distributed in the hope that it will be useful,
1727+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1728+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1729+# GNU General Public License for more details.
1730+#
1731+# You should have received a copy of the GNU General Public License
1732+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1733+#
1734+#---------------------------------------------------------------------
1735+
1736+script_name=${0##*/}
1737+sys_job_dir="/etc/init"
1738+user_job_dir="$HOME/.init"
1739+user_log_dir="$HOME/.cache/upstart/log"
1740+sys_log_dir="/var/log/upstart"
1741+bug_url="https://bugs.launchpad.net/upstart/+filebug"
1742+test_dir=
1743+test_dir_suffix=
1744+user_to_create=
1745+uid=
1746+gid=
1747+opt=
1748+OPTARG=
1749+debug_enabled=0
1750+feature=
1751+
1752+# allow non-priv users to find 'initctl'
1753+export PATH=$PATH:/sbin
1754+
1755+# for assertions
1756+die()
1757+{
1758+ msg="$*"
1759+ echo "ERROR: $msg" >&2
1760+ exit 1
1761+}
1762+
1763+debug()
1764+{
1765+ str="$1"
1766+ [ "$debug_enabled" = 1 ] && echo "DEBUG: $str"
1767+}
1768+
1769+get_job_pid()
1770+{
1771+ job="$1"
1772+ [ -z "$job" ] && die "need job"
1773+
1774+ pid=$(initctl status "$job"|grep process|awk '{print $NF}')
1775+ [ -z "$pid" ] && die "job $job has no pid"
1776+
1777+ echo "$pid"
1778+}
1779+
1780+# take a string and convert it into a valid job name
1781+make_job_name()
1782+{
1783+ str="$1"
1784+
1785+ echo "$str" |\
1786+ sed -e 's/>/ gt /g' -e 's/</ lt /g' -e 's/+/ and /g' |\
1787+ sed -e 's/[[:punct:]]//g' -e 's/ */ /g' |\
1788+ tr ' ' '-'
1789+}
1790+
1791+upstart_encode()
1792+{
1793+ str="$1"
1794+
1795+ echo "$str" | sed 's!/!_!g'
1796+}
1797+
1798+# take a string and convert it into a valid job log file name
1799+make_log_name()
1800+{
1801+ str="$1"
1802+ upstart_encode "$str"
1803+}
1804+
1805+TEST_FAILED()
1806+{
1807+ args="$*"
1808+
1809+ [ -z "$args" ] && die "need args"
1810+
1811+ echo
1812+ echo "ERROR: TEST FAILED ('$feature')"
1813+ echo
1814+ printf "BAD: ${args}\n"
1815+ printf "\nPlease report a bug at $bug_url including the following details:\n"
1816+ printf "\nUpstart:\n"
1817+ /sbin/init --version|head -n1
1818+ /sbin/initctl --version|head -n1
1819+ echo
1820+ printf "cmdline:\n"
1821+ cat /proc/cmdline
1822+ echo
1823+ printf "Upstart Env:\n"
1824+ set|grep UPSTART_
1825+ echo
1826+ printf "lsb:\n"
1827+ lsb_release -a
1828+ printf "\nuname:\n"
1829+ uname -a
1830+ echo
1831+ sync
1832+ echo "ERROR: TEST FAILED ('$feature')"
1833+ echo
1834+ exit 1
1835+}
1836+
1837+TEST_GROUP()
1838+{
1839+ name="$1"
1840+
1841+ [ -z "$name" ] && die "need name"
1842+
1843+ printf "Testing %s\n" "$name"
1844+}
1845+
1846+TEST_FEATURE()
1847+{
1848+ feature="$1"
1849+
1850+ [ -z "$feature" ] && die "need feature"
1851+
1852+ printf "...%s\n" "$feature"
1853+}
1854+
1855+TEST_NE()
1856+{
1857+ cmd="$1"
1858+ value="$2"
1859+ expected="$3"
1860+
1861+ # XXX: no checks on value or expected since they might be blank
1862+ [ -z "$cmd" ] && die "need cmd"
1863+
1864+ [ "$value" = "$expected" ] && TEST_FAILED \
1865+ "wrong value for '$cmd', expected $expected got $value"
1866+}
1867+
1868+TEST_EQ()
1869+{
1870+ cmd="$1"
1871+ value="$2"
1872+ expected="$3"
1873+
1874+ # XXX: no checks on value or expected since they might be blank
1875+ [ -z "$cmd" ] && die "need cmd"
1876+
1877+ [ "$value" != "$expected" ] && TEST_FAILED \
1878+ "wrong value for '$cmd', expected '$expected' got '$value'"
1879+}
1880+
1881+checks()
1882+{
1883+ cmd=initctl
1884+ [ -z "$(command -v $cmd)" ] && die "cannot find command $cmd"
1885+
1886+ [ "$(id -u)" = 0 ] && die "ERROR: should not run this function as root"
1887+
1888+ # This will fail for a non-root user unless D-Bus is correctly
1889+ # configured
1890+ $cmd emit foo || die \
1891+ "You do not appear to have configured D-Bus for Upstart user sessions. See usage."
1892+}
1893+
1894+setup()
1895+{
1896+ uid=$(id -u)
1897+ gid=$(id -g)
1898+
1899+ if [ "$uid" = 0 ]
1900+ then
1901+ [ -z "$user_to_create" ] && die "need '-u' option when running as root"
1902+
1903+ getent passwd "$user_to_create" && \
1904+ die "user '$user_to_create' already exists"
1905+
1906+ echo "Creating user '$user_to_create'"
1907+ cmd="useradd -mU -c 'Upstart Test User' $user_to_create"
1908+ eval "$cmd"
1909+ TEST_EQ "$cmd" $? 0
1910+
1911+ echo "Locking account for user '$user_to_create'"
1912+ cmd="usermod -L $user_to_create"
1913+ eval "$cmd"
1914+ TEST_EQ "$cmd" $? 0
1915+
1916+ # Run ourselves again as the new user
1917+ su -c "$0 -a" "$user_to_create"
1918+ test_run_rc=$?
1919+
1920+ if [ $test_run_rc -eq 0 ]
1921+ then
1922+ echo "Deleting user '$user_to_create'"
1923+ cmd="userdel -r \"$user_to_create\""
1924+ eval "$cmd"
1925+ TEST_EQ "$cmd" $? 0
1926+ fi
1927+
1928+ exit $test_run_rc
1929+ fi
1930+
1931+ checks
1932+
1933+ # setup
1934+ if [ ! -d "$user_job_dir" ]
1935+ then
1936+ cmd="mkdir -p \"$user_job_dir\""
1937+ eval $cmd
1938+ TEST_EQ "$cmd" $? 0
1939+
1940+ cmd="chmod 755 \"$user_job_dir\""
1941+ eval "$cmd"
1942+ TEST_EQ "$cmd" $? 0
1943+ fi
1944+
1945+ # create somewhere to store user jobs
1946+ cmd="mktemp -d --tmpdir=\"$user_job_dir\""
1947+ test_dir=$(eval "$cmd")
1948+ TEST_EQ "$cmd" $? 0
1949+ TEST_NE "$test_dir" "$test_dir" ""
1950+ test_dir_suffix=${test_dir#${user_job_dir}/}
1951+
1952+ # ensure files in this directory are accessible since
1953+ # mktemp sets directory perms to 0700 regardless of umask.
1954+ cmd="chmod 755 \"$test_dir\""
1955+ eval "$cmd"
1956+ TEST_EQ "$cmd" $? 0
1957+
1958+ TEST_NE "HOME" "$HOME" ""
1959+}
1960+
1961+cleanup()
1962+{
1963+ if [ -d "$test_dir" ]
1964+ then
1965+ echo "Removing test directory '$test_dir'"
1966+ cmd="rmdir \"$test_dir\""
1967+ eval "$cmd"
1968+ TEST_EQ "$cmd" $? 0
1969+ fi
1970+}
1971+
1972+ensure_job_known()
1973+{
1974+ job="$1"
1975+ job_name="$2"
1976+
1977+ [ -z "$job" ] && die "no job"
1978+ [ -z "$job_name" ] && die "no job name"
1979+
1980+ TEST_FEATURE "ensure 'initctl' recognises job"
1981+ initctl list|grep -q "^$job " || \
1982+ TEST_FAILED "job $job_name not known to initctl"
1983+
1984+ TEST_FEATURE "ensure 'status' recognises job"
1985+ cmd="status ${job}"
1986+ eval "$cmd" >/dev/null 2>&1
1987+ rc=$?
1988+ TEST_EQ "$cmd" $rc 0
1989+}
1990+
1991+# Note that if the specified job is *not* as task, it is expected to run
1992+# indefinately. This allows us to perform PID checks, etc.
1993+run_user_job_tests()
1994+{
1995+ job_name="$1"
1996+ job_file="$2"
1997+ task="$3"
1998+ env="$4"
1999+
2000+ # XXX: env can be empty
2001+ [ -z "$job_name" ] && die "no job name"
2002+ [ -z "$job_file" ] && die "no job file"
2003+ [ -z "$task" ] && die "no task value"
2004+
2005+ job="${test_dir_suffix}/${job_name}"
2006+
2007+ [ -f "$job_file" ] || TEST_FAILED "job file '$job_file' does not exist"
2008+
2009+ ensure_job_known "$job" "$job_name"
2010+
2011+ TEST_FEATURE "ensure job can be started"
2012+ cmd="start ${job} ${env}"
2013+ output=$(eval "$cmd")
2014+ rc=$?
2015+ TEST_EQ "$cmd" $rc 0
2016+
2017+ if [ "$task" = no ]
2018+ then
2019+ TEST_FEATURE "ensure 'start' shows job pid"
2020+ pid=$(echo "$output"|awk '{print $4}')
2021+ TEST_NE "pid" "$pid" ""
2022+
2023+ TEST_FEATURE "ensure 'initctl' shows job is running with pid"
2024+ initctl list|grep -q "^$job start/running, process $pid" || \
2025+ TEST_FAILED "job $job_name did not start"
2026+
2027+ TEST_FEATURE "ensure 'status' shows job is running with pid"
2028+ cmd="status ${job}"
2029+ output=$(eval "$cmd")
2030+ echo "$output"|while read job_tmp state ignored status_pid
2031+ do
2032+ state=$(echo $state|tr -d ',')
2033+ TEST_EQ "job name" "$job_tmp" "$job"
2034+ TEST_EQ "job state" "$state" "start/running"
2035+ TEST_EQ "job pid" "$status_pid" "$pid"
2036+ done
2037+
2038+ TEST_FEATURE "ensure job pid is running with correct uids"
2039+ pid_uids=$(ps --no-headers -p $pid -o euid,ruid)
2040+ for pid_uid in $pid_uids
2041+ do
2042+ TEST_EQ "pid uid" "$pid_uid" "$uid"
2043+ done
2044+
2045+ TEST_FEATURE "ensure job pid is running with correct gids"
2046+ pid_gids=$(ps --no-headers -p $pid -o egid,rgid)
2047+ for pid_gid in $pid_gids
2048+ do
2049+ TEST_EQ "pid gid" "$pid_gid" "$gid"
2050+ done
2051+
2052+ TEST_FEATURE "ensure process is running in correct directory"
2053+ cwd=$(readlink /proc/$pid/cwd)
2054+ TEST_EQ "cwd" "$cwd" "$HOME"
2055+
2056+ TEST_FEATURE "ensure job can be stopped"
2057+ cmd="stop ${job}"
2058+ output=$(eval "$cmd")
2059+ rc=$?
2060+ TEST_EQ "$cmd" $rc 0
2061+
2062+ TEST_FEATURE "ensure job pid no longer exists"
2063+ pid_ids=$(ps --no-headers -p $pid -o euid,ruid,egid,rgid)
2064+ TEST_EQ "pid uids+gids" "$pid_ids" ""
2065+ fi
2066+
2067+ remove_job_file "$job_file"
2068+ ensure_job_gone "$job" "$job_name" "$env"
2069+}
2070+
2071+remove_job_file()
2072+{
2073+ job_file="$1"
2074+
2075+ [ -z "$job_file" ] && die "no job file"
2076+ [ ! -f "$job_file" ] && TEST_FAILED "job file '$job_file' does not exist"
2077+
2078+ cmd="rm $job_file"
2079+ eval "$cmd"
2080+ TEST_EQ "$cmd" $? 0
2081+}
2082+
2083+ensure_job_gone()
2084+{
2085+ job="$1"
2086+ job_name="$2"
2087+ env="$3"
2088+
2089+ # XXX: no check on env since it can be empty
2090+ [ -z "$job" ] && die "no job"
2091+ [ -z "$job_name" ] && die "no job name"
2092+
2093+ TEST_FEATURE "ensure 'initctl' no longer recognises job"
2094+ initctl list|grep -q "^$job " && \
2095+ TEST_FAILED "deleted job $job_name still known to initctl"
2096+
2097+ TEST_FEATURE "ensure 'status' no longer recognises job"
2098+ cmd="status ${job}"
2099+ eval "$cmd" >/dev/null 2>&1
2100+ rc=$?
2101+ TEST_NE "$cmd" $rc 0
2102+}
2103+
2104+test_user_job()
2105+{
2106+ test_group="$1"
2107+ job_name="$2"
2108+ script="$3"
2109+ task="$4"
2110+ env="$5"
2111+
2112+ # XXX: no test on script or env since they might be empty
2113+ [ -z "$test_group" ] && die "no test group"
2114+ [ -z "$job_name" ] && die "no job name"
2115+ [ -z "$task" ] && die "no task"
2116+
2117+ TEST_GROUP "$test_group"
2118+
2119+ job_file="${test_dir}/${job_name}.conf"
2120+
2121+ echo "$script" > $job_file
2122+
2123+ run_user_job_tests "$job_name" "$job_file" "$task" "$env"
2124+}
2125+
2126+test_user_job_binary()
2127+{
2128+ group="user job running a binary"
2129+ job_name="binary_test"
2130+ script="exec sleep 999"
2131+ test_user_job "$group" "$job_name" "$script" no ""
2132+}
2133+
2134+test_user_job_binary_task()
2135+{
2136+ group="user job running a binary task"
2137+ job_name="binary_task_test"
2138+ OUTFILE=$(mktemp)
2139+
2140+ script="\
2141+task
2142+exec /bin/true > $OUTFILE"
2143+
2144+ test_user_job "$group" "$job_name" "$script" yes "OUTFILE=$OUTFILE"
2145+ rm -f $OUTFILE
2146+}
2147+
2148+test_user_job_single_line_script()
2149+{
2150+ group="user job running a single-line script"
2151+ job_name="single_line_script_test"
2152+ script="\
2153+script
2154+ sleep 999
2155+end script"
2156+ test_user_job "$group" "$job_name" "$script" no ""
2157+}
2158+
2159+test_user_job_single_line_script_task()
2160+{
2161+ group="user job running a single-line script task"
2162+ job_name="single_line_script_task_test"
2163+ OUTFILE=$(mktemp)
2164+
2165+ script="\
2166+task
2167+script
2168+ exec /bin/true > $OUTFILE
2169+end script"
2170+ test_user_job "$group" "$job_name" "$script" yes "OUTFILE=$OUTFILE"
2171+ rm -f $OUTFILE
2172+}
2173+
2174+test_user_job_multi_line_script()
2175+{
2176+ group="user job running a multi-line script"
2177+ job_name="multi_line_script_test"
2178+ script="\
2179+script
2180+
2181+ /bin/true
2182+ /bin/true;/bin/true
2183+ sleep 999
2184+
2185+end script"
2186+ test_user_job "$group" "$job_name" "$script" no ""
2187+}
2188+
2189+test_user_job_multi_line_script_task()
2190+{
2191+ group="user job running a multi-line script task"
2192+ job_name="multi_line_script_task_test"
2193+ OUTFILE=$(mktemp)
2194+
2195+ script="\
2196+task
2197+script
2198+
2199+ /bin/true
2200+ /bin/true
2201+ /bin/true
2202+
2203+end script"
2204+ test_user_job "$group" "$job_name" "$script" yes "OUTFILE=$OUTFILE"
2205+ rm -f $OUTFILE
2206+}
2207+
2208+test_user_emit_events()
2209+{
2210+ job_name="start_on_foo"
2211+
2212+ TEST_GROUP "user emitting an event"
2213+ initctl emit foo || TEST_FAILED "failed to emit event as user"
2214+
2215+ TEST_GROUP "user emitting an event to start a job"
2216+ script="\
2217+ start on foo BAR=2
2218+ stop on baz cow=moo or hello
2219+ exec sleep 999"
2220+
2221+ job_file="${test_dir}/${job_name}.conf"
2222+ job="${test_dir_suffix}/${job_name}"
2223+
2224+ echo "$script" > $job_file
2225+
2226+ ensure_job_known "$job" "$job_name"
2227+
2228+ initctl list|grep -q "^$job stop/waiting" || \
2229+ TEST_FAILED "job $job_name not stopped"
2230+
2231+ TEST_FEATURE "ensure job can be started with event"
2232+ initctl emit foo BAR=2 || \
2233+ TEST_FAILED "failed to emit event for user job"
2234+
2235+ initctl status "$job"|grep -q "^$job start/running" || \
2236+ TEST_FAILED "job $job_name failed to start"
2237+
2238+ TEST_FEATURE "ensure job can be stopped with event"
2239+ initctl emit baz cow=moo || \
2240+ TEST_FAILED "failed to emit event for user job"
2241+
2242+ initctl list|grep -q "^$job stop/waiting" || \
2243+ TEST_FAILED "job $job_name not stopped"
2244+
2245+ rm -f "$job_file"
2246+}
2247+
2248+test_user_job_setuid_setgid()
2249+{
2250+ group="user job with setuid and setgid me"
2251+ job_name="setuid_setgid_me_test"
2252+ script="\
2253+setuid $(id -un)
2254+setgid $(id -gn)
2255+exec sleep 999"
2256+ test_user_job "$group" "$job_name" "$script" no ""
2257+
2258+ TEST_GROUP "user job with setuid and setgid root"
2259+ script="\
2260+setuid root
2261+setgid root
2262+exec sleep 999"
2263+
2264+ job_name="setuid_setgid_root_test"
2265+ job_file="${test_dir}/${job_name}.conf"
2266+ job="${test_dir_suffix}/${job_name}"
2267+
2268+ echo "$script" > $job_file
2269+
2270+ ensure_job_known "$job" "$job_name"
2271+
2272+ TEST_FEATURE "ensure job fails to start as root"
2273+ cmd="start ${job}"
2274+ output=$(eval "$cmd" 2>&1)
2275+ rc=$?
2276+ TEST_EQ "$cmd" $rc 1
2277+
2278+ TEST_FEATURE "ensure 'start' indicates job failure"
2279+ error=$(echo "$output"|grep failed)
2280+ TEST_NE "error" "$error" ""
2281+
2282+ TEST_FEATURE "ensure 'initctl' does not list job"
2283+ initctl list|grep -q "^$job stop/waiting" || \
2284+ TEST_FAILED "job $job_name not listed as stopped"
2285+
2286+ delete_job "$job_name"
2287+}
2288+
2289+get_job_file()
2290+{
2291+ job_name="$1"
2292+
2293+ [ -z "$job_name" ] && die "no job name"
2294+ echo "${test_dir}/${job_name}.conf"
2295+}
2296+
2297+ensure_no_output()
2298+{
2299+ job_name="$1"
2300+ script="$2"
2301+ instance="$3"
2302+
2303+ job="${test_dir_suffix}/${job_name}"
2304+
2305+ create_job "$job_name" "$script"
2306+ start_job "$job" "$job_name" "$instance"
2307+
2308+ check_job_output "$job_name"
2309+ delete_job "$job_name"
2310+}
2311+
2312+create_job()
2313+{
2314+ job_name="$1"
2315+ script="$2"
2316+
2317+ # XXX: script could be empty
2318+ [ -z "$job_name" ] && die "no job name"
2319+
2320+ debug "create_job: job_name='$job_name'"
2321+ debug "create_job: script='$script'"
2322+
2323+ # Not currently possible to have a user job with the
2324+ # same name as a system job.
2325+ #
2326+ # XXX: Note that this test assumes that user has *not* specified
2327+ # XXX: an alternate configuration directory using the
2328+ # XXX: '--confdir' option.
2329+ [ -e "${sys_job_dir}/${job_name}.conf" ] && \
2330+ die "job '$job_name' already exists as a system job"
2331+
2332+ job_file="${test_dir}/${job_name}.conf"
2333+ job="${test_dir_suffix}/${job_name}"
2334+
2335+ echo "$script" > "$job_file"
2336+ sync
2337+}
2338+
2339+delete_job()
2340+{
2341+ job_name="$1"
2342+
2343+ [ -z "$job_name" ] && die "no job name"
2344+
2345+ job_file="$(get_job_file $job_name)"
2346+
2347+ rm "$job_file" || TEST_FAILED "unable to remove job file '$job_file'"
2348+}
2349+
2350+check_job_output()
2351+{
2352+ job_name="$1"
2353+
2354+ [ ! -z "$(ls $user_log_dir 2>/dev/null)" ] && \
2355+ TEST_FAILED "job $job_name created logfile unexpectedly in '$user_log_dir'"
2356+
2357+ # XXX: note that it might appear that checking in $sys_log_dir
2358+ # could result in false positives, but this isn't so since
2359+ # (currently) it is not possible for a user job to have the
2360+ # same name as a system job. start_job() will detect this
2361+ # scenario.
2362+ for dir in "$user_log_dir" "$sys_log_dir"
2363+ do
2364+ log_file="${dir}/${job_name}.log"
2365+ [ -f "$log_file" ] && \
2366+ TEST_FAILED "job $job_name created logfile unexpectedly as '$log_file'"
2367+ done
2368+}
2369+
2370+start_job()
2371+{
2372+ job="$1"
2373+ job_file="$2"
2374+ instance="$3"
2375+ allow_failure="$4"
2376+
2377+ # XXX: instance may be blank
2378+ [ -z "$job" ] && die "no job"
2379+ [ -z "$job_file" ] && die "no job file"
2380+
2381+ debug "start_job: job='$job'"
2382+ debug "start_job: job_file='$job_file'"
2383+ debug "start_job: instance='$instance'"
2384+ debug "start_job: allow_failure='$allow_failure'"
2385+
2386+ eval output=$(mktemp)
2387+
2388+ # XXX: Don't quote instance as we don't want to pass a null instance to
2389+ # start(8).
2390+ cmd="start \"$job\" $instance >${output} 2>&1"
2391+ debug "start_job: running '$cmd'"
2392+ eval "$cmd"
2393+ rc=$?
2394+
2395+ if [ $rc -ne 0 -a -z "$allow_failure" ]
2396+ then
2397+ TEST_FAILED "job $job_file not started: $(cat $output)"
2398+ fi
2399+
2400+ rm -f "$output"
2401+}
2402+
2403+get_job_logfile_name()
2404+{
2405+ job_name="$1"
2406+ instance_value="$2"
2407+
2408+ # XXX: instance may be null
2409+ [ -z "$job_name" ] && die "no job name"
2410+
2411+ encoded_test_dir_suffix=$(upstart_encode "${test_dir_suffix}/")
2412+ file_name="${encoded_test_dir_suffix}$(make_log_name $job_name)"
2413+
2414+ if [ ! -z "$instance_value" ]
2415+ then
2416+ log_file="${user_log_dir}/${file_name}-${instance_value}.log"
2417+ else
2418+ log_file="${user_log_dir}/${file_name}.log"
2419+ fi
2420+
2421+ echo "$log_file"
2422+}
2423+
2424+run_job()
2425+{
2426+ job="$1"
2427+ job_name="$2"
2428+ script="$3"
2429+ instance="$4"
2430+
2431+ # XXX: script, instance might be blank
2432+ [ -z "$job" ] && die "no job"
2433+ [ -z "$job_name" ] && die "no job name"
2434+
2435+ debug "run_job: job='$job'"
2436+ debug "run_job: job_name='$job_name'"
2437+ debug "run_job: script='$script'"
2438+ debug "run_job: instance='$instance'"
2439+
2440+ create_job "$job_name" "$script"
2441+ start_job "$job" "$job_name" "$instance"
2442+}
2443+
2444+ensure_file_meta()
2445+{
2446+ file="$1"
2447+ expected_owner="$2"
2448+ expected_group="$3"
2449+ expected_perms="$4"
2450+
2451+ [ -z "$file" ] && die "no file"
2452+ [ -z "$expected_owner" ] && die "no expected owner"
2453+ [ -z "$expected_group" ] && die "no expected group"
2454+ [ -z "$expected_perms" ] && die "no expected perms"
2455+
2456+ [ ! -f "$file" ] && die "file $file does not exist"
2457+
2458+ expected_perms="640"
2459+ umask_value=$(umask)
2460+ umask_expected=0022
2461+
2462+ if [ "$umask_value" != "$umask_expected" ]
2463+ then
2464+ msg="umask value is $umask_value -"
2465+ msg="${msg} changing it to $umask_expected."
2466+ echo "WARNING: $msg"
2467+ umask "$umask_expected" || TEST_FAILED "unable to change umask"
2468+ fi
2469+
2470+ owner=$(ls -l "$file"|awk '{print $3}')
2471+ group=$(ls -l "$file"|awk '{print $4}')
2472+ perms=$(stat --printf "%a\n" "$file")
2473+
2474+ [ "$owner" = "$expected_owner" ] || TEST_FAILED \
2475+ "file $file has wrong owner (expected $expected_owner, got $owner)"
2476+
2477+ [ "$group" = "$expected_group" ] || TEST_FAILED \
2478+ "file $file has wrong group (expected $expected_group, got $group)"
2479+
2480+ [ "$perms" = "$expected_perms" ] || TEST_FAILED \
2481+ "file $file has wrong group (expected $expected_perms, got $perms)"
2482+}
2483+
2484+
2485+ensure_output()
2486+{
2487+ job_name="$1"
2488+ script="$2"
2489+ expected_output="$3"
2490+ instance="$4"
2491+ instance_value="$5"
2492+ options="$6"
2493+
2494+ # XXX: remaining args could be null
2495+ [ -z "$job_name" ] && die "no job name"
2496+
2497+ debug "ensure_output: job_name='$job_name'"
2498+ debug "ensure_output: script='$script'"
2499+ debug "ensure_output: expected_ouput='$expected_ouput'"
2500+ debug "ensure_output: instance='$instance'"
2501+ debug "ensure_output: instance_value='$instance_value'"
2502+ debug "ensure_output: options='$options'"
2503+
2504+ regex=n
2505+ retain=n
2506+ unique=""
2507+ use_od=n
2508+
2509+ for opt in $options
2510+ do
2511+ case "$opt" in
2512+ regex)
2513+ regex=y
2514+ ;;
2515+ retain)
2516+ retain=y
2517+ ;;
2518+ unique)
2519+ unique='|sort -u'
2520+ ;;
2521+ use_od)
2522+ use_od=y
2523+ ;;
2524+ esac
2525+ done
2526+
2527+ debug "ensure_output: regex='$regex'"
2528+ debug "ensure_output: retain='$retain'"
2529+ debug "ensure_output: unique='$unique'"
2530+ debug "ensure_output: use_od='$use_od'"
2531+
2532+ expected_owner=$(id -un)
2533+ expected_group=$(id -gn)
2534+ expected_perms="640"
2535+
2536+ job="${test_dir_suffix}/${job_name}"
2537+
2538+ run_job "$job" "$job_name" "$script" "$instance"
2539+
2540+ debug "ensure_output: user_log_dir='$user_log_dir'"
2541+ debug "ensure_output: test_dir='$test_dir'"
2542+ debug "ensure_output: test_dir_suffix='$test_dir_suffix'"
2543+
2544+ log_file=$(get_job_logfile_name "$job_name" "$instance_value")
2545+
2546+ debug "ensure_output: log_file='$log_file'"
2547+
2548+ # Give Upstart a chance to parse the file
2549+ count=1
2550+ while ! status "$job" >/dev/null 2>&1
2551+ do
2552+ sleep 1
2553+ count=$((count+1))
2554+ [ "$count" -eq 5 ] && break
2555+ done
2556+
2557+ # give job a chance to start
2558+ count=1
2559+ while [ ! -f "$log_file" ]
2560+ do
2561+ sleep 1
2562+ count=$((count+1))
2563+ [ "$count" -eq 5 ] && break
2564+ done
2565+
2566+ [ ! -f "$log_file" ] && \
2567+ TEST_FAILED "job '$job_name' failed to create logfile"
2568+
2569+ ensure_file_meta \
2570+ "$log_file" \
2571+ "$expected_owner" \
2572+ "$expected_group" \
2573+ "$expected_perms"
2574+
2575+ # XXX: note we have to remove carriage returns added by the line
2576+ # discipline
2577+ if [ "$regex" = y ]
2578+ then
2579+ log=$(eval "cat $log_file|tr -d '\r' $unique")
2580+ msg="job '$job_name' failed to log correct data\n"
2581+ msg="${msg}\texpected regex: '$expected_output'\n"
2582+ msg="${msg}\tgot : '$log'"
2583+ cat "$log_file" | egrep "$expected_output" || TEST_FAILED "$msg"
2584+ elif [ "$use_od" = y ]
2585+ then
2586+ log=$(eval "cat $log_file|tr -d '\r' $unique|od -x")
2587+ msg="job '$job_name' failed to log correct data\n"
2588+ msg="${msg}\texpected hex: '$expected_output'\n"
2589+ msg="${msg}\tgot : '$log'"
2590+ [ "$expected_output" != "$log" ] && TEST_FAILED "$msg"
2591+ else
2592+ log=$(eval "cat $log_file|tr -d '\r' $unique")
2593+ msg="job '$job_name' failed to log correct data\n"
2594+ msg="${msg}\texpected text: '$expected_output'\n"
2595+ msg="${msg}\tgot : '$log'"
2596+ [ "$expected_output" != "$log" ] && TEST_FAILED "$msg"
2597+ fi
2598+
2599+ if [ "$retain" = n ]
2600+ then
2601+ delete_job "$job_name"
2602+ rm "$log_file" || TEST_FAILED "unable to remove log file '$log_file'"
2603+ fi
2604+}
2605+
2606+test_ensure_no_unexpected_output()
2607+{
2608+ #---------------------------------------------------------------------
2609+ feature="ensure command job does not create log file with no console"
2610+ TEST_FEATURE "$feature"
2611+
2612+ job_name=$(make_job_name "$feature")
2613+
2614+ script="\
2615+ console none
2616+ exec echo hello world"
2617+
2618+ ensure_no_output "$job_name" "$script" ""
2619+
2620+ #---------------------------------------------------------------------
2621+ feature="ensure 1-line script job does not create log file with no console"
2622+ TEST_FEATURE "$feature"
2623+
2624+ job_name=$(make_job_name "$feature")
2625+
2626+ script="\
2627+ console none
2628+ script
2629+ echo hello world
2630+ end script
2631+ "
2632+
2633+ ensure_no_output "$job_name" "$script" ""
2634+
2635+ #---------------------------------------------------------------------
2636+ feature="ensure multi-line script job does not create log file with no console"
2637+ TEST_FEATURE "$feature"
2638+
2639+ job_name=$(make_job_name "$feature")
2640+
2641+ script="\
2642+ console none
2643+ script
2644+ /bin/true
2645+ echo hello world
2646+ end script
2647+ "
2648+
2649+ ensure_no_output "$job_name" "$script" ""
2650+
2651+ #---------------------------------------------------------------------
2652+ feature="ensure no output if log directory does not exist"
2653+ TEST_FEATURE "$feature"
2654+
2655+ rmdir "${user_log_dir}" || \
2656+ TEST_FAILED "unable to delete log directory '$user_log_dir'"
2657+
2658+ job_name=$(make_job_name "$feature")
2659+ string="hello world"
2660+ script="\
2661+ console log
2662+ script
2663+ /bin/true
2664+ /bin/echo hello world
2665+ end script
2666+ "
2667+
2668+ ensure_no_output "$job_name" "$script" ""
2669+
2670+ mkdir "${user_log_dir}" || \
2671+ TEST_FAILED "unable to recreate log directory '$user_log_dir'"
2672+
2673+ #---------------------------------------------------------------------
2674+ feature="ensure command job does not create log file with invalid command"
2675+ TEST_FEATURE "$feature"
2676+
2677+ job_name=$(make_job_name "$feature")
2678+
2679+ script="\
2680+ console log
2681+ exec /this/command/does/not/exist"
2682+
2683+ job="${test_dir_suffix}/${job_name}"
2684+ create_job "$job_name" "$script"
2685+ start_job "$job" "$job_name" "" 1
2686+ check_job_output "$job_name"
2687+ delete_job "$job_name"
2688+}
2689+
2690+test_output_logged()
2691+{
2692+ # XXX: upstart won't create this
2693+ mkdir -p "$user_log_dir"
2694+
2695+ test_ensure_no_unexpected_output
2696+}
2697+
2698+test_user_jobs()
2699+{
2700+ test_user_job_binary
2701+ test_user_job_single_line_script
2702+ test_user_job_multi_line_script
2703+
2704+ test_user_job_binary_task
2705+ test_user_job_single_line_script_task
2706+ test_user_job_multi_line_script_task
2707+
2708+ test_user_job_setuid_setgid
2709+
2710+ test_user_emit_events
2711+
2712+ test_output_logged
2713+}
2714+
2715+tests()
2716+{
2717+ echo
2718+ echo -n "Running Upstart user session tests as user '`whoami`'"
2719+ echo " (uid $uid, gid $gid) in directory '$test_dir'"
2720+ echo
2721+
2722+ test_user_jobs
2723+
2724+ echo
2725+ echo "All tests completed successfully"
2726+ echo
2727+}
2728+
2729+usage()
2730+{
2731+cat <<EOT
2732+USAGE: $script_name [options]
2733+
2734+OPTIONS:
2735+
2736+ -a : Actually run this script.
2737+ -h : Show this help.
2738+ -u <user> : Specify name of test user to create.
2739+
2740+DESCRIPTION:
2741+
2742+Run simple set of Upstart user session tests.
2743+
2744+PREREQUISITE:
2745+
2746+For this test to run, non-root users must be allowed to invoke all D-Bus
2747+methods on Upstart via configuration file:
2748+
2749+ /etc/dbus-1/system.d/Upstart.conf
2750+
2751+See dbus-daemon(1) for further details.
2752+
2753+WARNING: Note that this script is unavoidably invasive, so read what
2754+WARNING: follows before running!
2755+
2756+If run as a non-root user, this script will create a uniquely-named
2757+subdirectory below "\$HOME/.init/" to run its tests in. On successful
2758+completion of these tests, the unique subdirectory and its contents will
2759+be removed.
2760+
2761+If however, this script is invoked as the root user, the script will
2762+refuse to run until given the name of a test user to create via the "-u"
2763+option. If the user specified to this option already exists, this script
2764+will exit with an error. If the user does not already exist, it will be
2765+created, the script then run *as that user* and assuming successful
2766+completion of the tests, the test user and their home directory will
2767+then be deleted.
2768+
2769+EOT
2770+}
2771+
2772+#---------------------------------------------------------------------
2773+# main
2774+#---------------------------------------------------------------------
2775+
2776+while getopts "dhu:" opt
2777+do
2778+ case "$opt" in
2779+ d)
2780+ debug_enabled=1
2781+ ;;
2782+
2783+ h)
2784+ usage
2785+ exit 0
2786+ ;;
2787+
2788+ u)
2789+ user_to_create="$OPTARG"
2790+ ;;
2791+ esac
2792+done
2793+
2794+setup
2795+tests
2796+cleanup
2797+exit 0
2798
2799=== removed file 'util/tests/test_user_sessions.sh'
2800--- util/tests/test_user_sessions.sh 2011-07-26 01:09:47 +0000
2801+++ util/tests/test_user_sessions.sh 1970-01-01 00:00:00 +0000
2802@@ -1,553 +0,0 @@
2803-#!/bin/sh -u
2804-#---------------------------------------------------------------------
2805-# Script to run minimal Upstart user session tests.
2806-#
2807-# Note that this script _cannot_ be run as part of the "make check"
2808-# tests since those tests stimulate functions and features of the
2809-# as-yet-uninstalled version of Upstart. However, this script needs to
2810-# run on a system where the version of Upstart under test has _already_
2811-# been fully installed.
2812-#---------------------------------------------------------------------
2813-#
2814-# Copyright (C) 2011 Canonical Ltd.
2815-#
2816-# Author: James Hunt <james.hunt@canonical.com>
2817-#
2818-# This program is free software: you can redistribute it and/or modify
2819-# it under the terms of the GNU General Public License as published by
2820-# the Free Software Foundation, version 3 of the License.
2821-#
2822-# This program is distributed in the hope that it will be useful,
2823-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2824-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2825-# GNU General Public License for more details.
2826-#
2827-# You should have received a copy of the GNU General Public License
2828-# along with this program. If not, see <http://www.gnu.org/licenses/>.
2829-#
2830-#---------------------------------------------------------------------
2831-
2832-script_name=${0##*/}
2833-user_job_dir="$HOME/.init"
2834-bug_url="https://bugs.launchpad.net/upstart/+filebug"
2835-test_dir=
2836-test_dir_suffix=
2837-user_to_create=
2838-uid=
2839-gid=
2840-opt=
2841-OPTARG=
2842-
2843-# allow non-priv users to find 'initctl'
2844-export PATH=$PATH:/sbin
2845-
2846-# for assertions
2847-die()
2848-{
2849- msg="$*"
2850- echo "ERROR: $msg" >&2
2851- exit 1
2852-}
2853-
2854-TEST_FAILED()
2855-{
2856- args="$*"
2857-
2858- [ -z "$args" ] && die "need args"
2859-
2860- printf "BAD: %s\n" "$args"
2861- printf "\nPlease report a bug at $bug_url including the following details:\n"
2862- printf "\nUpstart:\n"
2863- /sbin/init --version|head -n1
2864- /sbin/initctl --version|head -n1
2865- echo
2866- printf "cmdline:\n"
2867- cat /proc/cmdline
2868- echo
2869- printf "Upstart Env:\n"
2870- set|grep UPSTART_
2871- echo
2872- printf "lsb:\n"
2873- lsb_release -a
2874- printf "\nuname:\n"
2875- uname -a
2876- echo
2877- sync
2878- exit 1
2879-}
2880-
2881-TEST_GROUP()
2882-{
2883- name="$1"
2884-
2885- [ -z "$name" ] && die "need name"
2886-
2887- printf "Testing %s\n", "$name"
2888-}
2889-
2890-TEST_FEATURE()
2891-{
2892- feature="$1"
2893-
2894- [ -z "$feature" ] && die "need feature"
2895-
2896- printf "...%s\n" "$feature"
2897-}
2898-
2899-TEST_NE()
2900-{
2901- cmd="$1"
2902- value="$2"
2903- expected="$3"
2904-
2905- # XXX: no checks on value or expected since they might be blank
2906- [ -z "$cmd" ] && die "need cmd"
2907-
2908- [ "$value" = "$expected" ] && TEST_FAILED \
2909- "wrong value for '$cmd', expected $expected got $value"
2910-}
2911-
2912-TEST_EQ()
2913-{
2914- cmd="$1"
2915- value="$2"
2916- expected="$3"
2917-
2918- # XXX: no checks on value or expected since they might be blank
2919- [ -z "$cmd" ] && die "need cmd"
2920-
2921- [ "$value" != "$expected" ] && TEST_FAILED \
2922- "wrong value for '$cmd', expected '$expected' got '$value'"
2923-}
2924-
2925-setup()
2926-{
2927- uid=$(id -u)
2928- gid=$(id -g)
2929-
2930- if [ "$uid" = 0 ]
2931- then
2932- [ -z "$user_to_create" ] && die "need '-u' option when running as root"
2933-
2934- getent passwd "$user_to_create" && \
2935- die "user '$user_to_create' already exists"
2936-
2937- echo "Creating user '$user_to_create'"
2938- cmd="useradd -mU -c 'Upstart Test User' $user_to_create"
2939- eval "$cmd"
2940- TEST_EQ "$cmd" $? 0
2941-
2942- echo "Locking account for user '$user_to_create'"
2943- cmd="usermod -L $user_to_create"
2944- eval "$cmd"
2945- TEST_EQ "$cmd" $? 0
2946-
2947- # Run ourselves again as the new user
2948- su -c "$0 -a" "$user_to_create"
2949- test_run_rc=$?
2950-
2951- if [ $test_run_rc -eq 0 ]
2952- then
2953- echo "Deleting user '$user_to_create'"
2954- cmd="userdel -r \"$user_to_create\""
2955- eval "$cmd"
2956- TEST_EQ "$cmd" $? 0
2957- fi
2958-
2959- exit $test_run_rc
2960- fi
2961-
2962- # setup
2963- if [ ! -d "$user_job_dir" ]
2964- then
2965- cmd="mkdir -p \"$user_job_dir\""
2966- eval $cmd
2967- TEST_EQ "$cmd" $? 0
2968-
2969- cmd="chmod 755 \"$user_job_dir\""
2970- eval "$cmd"
2971- TEST_EQ "$cmd" $? 0
2972- fi
2973-
2974- # create somewhere to store user jobs
2975- cmd="mktemp -d --tmpdir=\"$user_job_dir\""
2976- test_dir=$(eval "$cmd")
2977- TEST_EQ "$cmd" $? 0
2978- TEST_NE "$test_dir" "$test_dir" ""
2979- test_dir_suffix=${test_dir#${user_job_dir}/}
2980-
2981- # ensure files in this directory are accessible since
2982- # mktemp sets directory perms to 0700 regardless of umask.
2983- cmd="chmod 755 \"$test_dir\""
2984- eval "$cmd"
2985- TEST_EQ "$cmd" $? 0
2986-
2987- TEST_NE "HOME" "$HOME" ""
2988-}
2989-
2990-cleanup()
2991-{
2992- if [ -d "$test_dir" ]
2993- then
2994- echo "Removing test directory '$test_dir'"
2995- cmd="rmdir \"$test_dir\""
2996- eval "$cmd"
2997- TEST_EQ "$cmd" $? 0
2998- fi
2999-}
3000-
3001-ensure_job_known()
3002-{
3003- job="$1"
3004- job_name="$2"
3005-
3006- [ -z "$job" ] && die "no job"
3007- [ -z "$job_name" ] && die "no job name"
3008-
3009- TEST_FEATURE "ensure 'initctl' recognises job"
3010- initctl list|grep -q "^$job " || \
3011- TEST_FAILED "job $job_name not known to initctl"
3012-
3013- TEST_FEATURE "ensure 'status' recognises job"
3014- cmd="status ${job}"
3015- eval "$cmd"
3016- rc=$?
3017- TEST_EQ "$cmd" $rc 0
3018-}
3019-
3020-run_user_job_tests()
3021-{
3022- job_name="$1"
3023- job_file="$2"
3024- task="$3"
3025- env="$4"
3026-
3027- # XXX: env can be empty
3028- [ -z "$job_name" ] && die "no job name"
3029- [ -z "$job_file" ] && die "no job file"
3030- [ -z "$task" ] && die "no task value"
3031-
3032- job="${test_dir_suffix}/${job_name}"
3033-
3034- [ -f "$job_file" ] || TEST_FAILED "job file '$job_file' does not exist"
3035-
3036- ensure_job_known "$job" "$job_name"
3037-
3038- TEST_FEATURE "ensure job can be started"
3039- cmd="start ${job} ${env}"
3040- output=$(eval "$cmd")
3041- rc=$?
3042- TEST_EQ "$cmd" $rc 0
3043-
3044- if [ "$task" = no ]
3045- then
3046- TEST_FEATURE "ensure 'start' shows job pid"
3047- pid=$(echo "$output"|awk '{print $4}')
3048- TEST_NE "pid" "$pid" ""
3049-
3050- TEST_FEATURE "ensure 'initctl' shows job is running with pid"
3051- initctl list|grep -q "^$job start/running, process $pid" || \
3052- TEST_FAILED "job $job_name did not start"
3053-
3054- TEST_FEATURE "ensure 'status' shows job is running with pid"
3055- cmd="status ${job}"
3056- output=$(eval "$cmd")
3057- echo "$output"|while read job_tmp state ignored status_pid
3058- do
3059- state=$(echo $state|tr -d ',')
3060- TEST_EQ "job name" "$job_tmp" "$job"
3061- TEST_EQ "job state" "$state" "start/running"
3062- TEST_EQ "job pid" "$status_pid" "$pid"
3063- done
3064-
3065- TEST_FEATURE "ensure job pid is running with correct uids"
3066- pid_uids=$(ps --no-headers -p $pid -o euid,ruid)
3067- for pid_uid in $pid_uids
3068- do
3069- TEST_EQ "pid uid" "$pid_uid" "$uid"
3070- done
3071-
3072- TEST_FEATURE "ensure job pid is running with correct gids"
3073- pid_gids=$(ps --no-headers -p $pid -o egid,rgid)
3074- for pid_gid in $pid_gids
3075- do
3076- TEST_EQ "pid gid" "$pid_gid" "$gid"
3077- done
3078-
3079- TEST_FEATURE "ensure process is running in correct directory"
3080- cwd=$(readlink /proc/$pid/cwd)
3081- TEST_EQ "cwd" "$cwd" "$HOME"
3082-
3083- TEST_FEATURE "ensure job can be stopped"
3084- cmd="stop ${job}"
3085- output=$(eval "$cmd")
3086- rc=$?
3087- TEST_EQ "$cmd" $rc 0
3088-
3089- TEST_FEATURE "ensure job pid no longer exists"
3090- pid_ids=$(ps --no-headers -p $pid -o euid,ruid,egid,rgid)
3091- TEST_EQ "pid uids+gids" "$pid_ids" ""
3092- fi
3093-
3094- remove_job_file "$job_file"
3095- ensure_job_gone "$job" "$job_name" "$env"
3096-}
3097-
3098-remove_job_file()
3099-{
3100- job_file="$1"
3101-
3102- [ -z "$job_file" ] && die "no job file"
3103- [ ! -f "$job_file" ] && TEST_FAILED "job file '$job_file' does not exist"
3104-
3105- cmd="rm $job_file"
3106- eval "$cmd"
3107- TEST_EQ "$cmd" $? 0
3108-}
3109-
3110-ensure_job_gone()
3111-{
3112- job="$1"
3113- job_name="$2"
3114- env="$3"
3115-
3116- # XXX: no check on env since it can be empty
3117- [ -z "$job" ] && die "no job"
3118- [ -z "$job_name" ] && die "no job name"
3119-
3120- TEST_FEATURE "ensure 'initctl' no longer recognises job"
3121- initctl list|grep -q "^$job " && \
3122- TEST_FAILED "deleted job $job_name still known to initctl"
3123-
3124- TEST_FEATURE "ensure 'status' no longer recognises job"
3125- cmd="status ${job}"
3126- eval "$cmd"
3127- rc=$?
3128- TEST_NE "$cmd" $rc 0
3129-}
3130-
3131-test_user_job()
3132-{
3133- test_group="$1"
3134- job_name="$2"
3135- script="$3"
3136- task="$4"
3137- env="$5"
3138-
3139- # XXX: no test on script or env since they might be empty
3140- [ -z "$test_group" ] && die "no test group"
3141- [ -z "$job_name" ] && die "no job name"
3142- [ -z "$task" ] && die "no task"
3143-
3144- TEST_GROUP "$test_group"
3145-
3146- job_file="${test_dir}/${job_name}.conf"
3147-
3148- echo "$script" > $job_file
3149-
3150- run_user_job_tests "$job_name" "$job_file" "$task" "$env"
3151-}
3152-
3153-test_user_job_binary()
3154-{
3155- group="user job running a binary"
3156- job_name="binary_test"
3157- script="exec sleep 999"
3158- test_user_job "$group" "$job_name" "$script" no ""
3159-}
3160-
3161-test_user_job_binary_task()
3162-{
3163- group="user job running a binary task"
3164- job_name="binary_task_test"
3165- OUTFILE=$(mktemp)
3166-
3167- script="\
3168-task
3169-exec true > $OUTFILE"
3170-
3171- test_user_job "$group" "$job_name" "$script" yes "OUTFILE=$OUTFILE"
3172- rm -f $OUTFILE
3173-}
3174-
3175-test_user_job_single_line_script()
3176-{
3177- group="user job running a single-line script"
3178- job_name="single_line_script_test"
3179- script="\
3180-script
3181- sleep 999
3182-end script"
3183- test_user_job "$group" "$job_name" "$script" no ""
3184-}
3185-
3186-test_user_job_single_line_script_task()
3187-{
3188- group="user job running a single-line script task"
3189- job_name="single_line_script_task_test"
3190- OUTFILE=$(mktemp)
3191-
3192- script="\
3193-task
3194-script
3195- exec true > $OUTFILE
3196-end script"
3197- test_user_job "$group" "$job_name" "$script" yes "OUTFILE=$OUTFILE"
3198- rm -f $OUTFILE
3199-}
3200-
3201-test_user_job_multi_line_script()
3202-{
3203- group="user job running a multi-line script"
3204- job_name="multi_line_script_test"
3205- script="\
3206-script
3207-
3208- true
3209- true;true
3210- sleep 999
3211-
3212-end script"
3213- test_user_job "$group" "$job_name" "$script" no ""
3214-}
3215-
3216-test_user_job_multi_line_script_task()
3217-{
3218- group="user job running a multi-line script task"
3219- job_name="multi_line_script_task_test"
3220- OUTFILE=$(mktemp)
3221-
3222- script="\
3223-task
3224-script
3225-
3226- true
3227- true
3228- true
3229-
3230-end script"
3231- test_user_job "$group" "$job_name" "$script" yes "OUTFILE=$OUTFILE"
3232- rm -f $OUTFILE
3233-}
3234-
3235-test_user_emit_events()
3236-{
3237- job_name="start_on_foo"
3238-
3239- TEST_GROUP "user emitting an event"
3240- initctl emit foo || TEST_FAILED "failed to emit event as user"
3241-
3242- TEST_GROUP "user emitting an event to start a job"
3243- script="\
3244- start on foo BAR=2
3245- stop on baz cow=moo or hello
3246- exec sleep 999"
3247-
3248- job_file="${test_dir}/${job_name}.conf"
3249- job="${test_dir_suffix}/${job_name}"
3250-
3251- echo "$script" > $job_file
3252-
3253- ensure_job_known "$job" "$job_name"
3254-
3255- initctl list|grep -q "^$job stop/waiting" || \
3256- TEST_FAILED "job $job_name not stopped"
3257-
3258- TEST_FEATURE "ensure job can be started with event"
3259- initctl emit foo BAR=2 || \
3260- TEST_FAILED "failed to emit event for user job"
3261-
3262- initctl status "$job"|grep -q "^$job start/running" || \
3263- TEST_FAILED "job $job_name failed to start"
3264-
3265- TEST_FEATURE "ensure job can be stopped with event"
3266- initctl emit baz cow=moo || \
3267- TEST_FAILED "failed to emit event for user job"
3268-
3269- initctl list|grep -q "^$job stop/waiting" || \
3270- TEST_FAILED "job $job_name not stopped"
3271-
3272- rm -f "$job_file"
3273-}
3274-
3275-test_user_jobs()
3276-{
3277- test_user_job_binary
3278- test_user_job_single_line_script
3279- test_user_job_multi_line_script
3280-
3281- test_user_job_binary_task
3282- test_user_job_single_line_script_task
3283- test_user_job_multi_line_script_task
3284-
3285- test_user_emit_events
3286-}
3287-
3288-tests()
3289-{
3290- echo
3291- echo -n "Running user session tests as user '`whoami`'"
3292- echo " (uid $uid, gid $gid) in directory '$test_dir'"
3293- echo
3294-
3295- test_user_jobs
3296-
3297- echo
3298- echo "All tests completed successfully"
3299- echo
3300-}
3301-
3302-usage()
3303-{
3304-cat <<EOT
3305-USAGE: $script_name [options]
3306-
3307-OPTIONS:
3308-
3309- -a : Actually run this script.
3310- -h : Show this help.
3311- -u <user> : Specify name of test user to create.
3312-
3313-DESCRIPTION: Run simple set of Upstart user session tests.
3314-
3315-WARNING: Note that this script is unavoidably invasive, so read what
3316-WARNING: follows before running!
3317-
3318-If run as a non-root user, this script will create a uniquely-named
3319-subdirectory below "\$HOME/.init/" to run its tests in. On successful
3320-completion of these tests, the unique subdirectory and its contents will
3321-be removed.
3322-
3323-If however, this script is invoked as the root user, the script will
3324-refuse to run until given the name of a test user to create via the "-u"
3325-option. If the user specified to this option already exists, this script
3326-will exit with an error. If the user does not already exist, it will be
3327-created, the script then run *as that user* and assuming successful
3328-completion of the tests, the test user and their home directory will
3329-then be deleted.
3330-
3331-EOT
3332-}
3333-
3334-#---------------------------------------------------------------------
3335-# main
3336-#---------------------------------------------------------------------
3337-
3338-while getopts "hu:" opt
3339-do
3340- case "$opt" in
3341- h)
3342- usage
3343- exit 0
3344- ;;
3345-
3346- u)
3347- user_to_create="$OPTARG"
3348- ;;
3349- esac
3350-done
3351-
3352-setup
3353-tests
3354-cleanup
3355-exit 0

Subscribers

People subscribed via source and target branches

to all changes: