--- upstart-1.4.orig/ChangeLog +++ upstart-1.4/ChangeLog @@ -1,3 +1,140 @@ +2012-02-03 James Hunt + + * init/job_process.c: job_process_spawn(): + - Set close-on-exec for pty_master. + - Ensure stdio buffers flushed prior to forking to ensure no data + leakage to child (should init be run with '--debug', or the tests + be run with redirected output, for example). + - Free log object if child process fails to exec(3) rather than just + closing fd. This ensures io watch is removed correctly. (LP: #922754) + - Only need to remap pty_master if CONSOLE_LOG in operation. + * init/tests/test_job_process: + - fd_valid(): New helper function to determine if specified fd is valid. + - child(): Added new 'TEST_FDS' test to ensure no fd leakage to child + processes. + - test_run(): + - New tests: + - "ensure sane fds with no console, no script" + - "ensure sane fds with no console, and script" + - "ensure sane fds with console log, no script" + - "ensure sane fds with console log, and script" + - "with single-line command running an invalid command, \ + then a 1-line post-stop script" + - "with single-line command running an invalid command, \ + then a 2-line post-stop script" + - "with single-line command running an invalid command, \ + then a post-stop command" + - "with single-line command running an invalid command, \ + then an invalid post-stop command" + - "with single-line command running a valid command, \ + then a 1-line invalid post-stop command" + - Test "with single-line command running an invalid command": + - now diverts stderr output for less chatty test-run experience. + - Improved checking. + - Test "with setuid me" now diverts stderr output for less chatty + test-run experience. + - test_spawn(): + - New tests: + - "with no such file, no shell and console log" + - "ensure sane fds with no console" + - "ensure sane fds with console log" + * init/tests/test_log.c: test_log_destroy(): + - New test "ensure watch freed when log destroyed" + +2012-01-27 James Hunt + + * init/tests/test_job_process.c: test_run(): Fixed typo in + test "with setuid me" where uid value was being set to gid. + +2012-01-25 James Hunt + + * init/job_process.c: job_process_terminated(): Free log to ensure data + written as soon as _any_ process ends (consider respawn jobs). + * init/log.c: + - log_destroy(): + - Improved documentation. + - Now calls new function log_flush(). + - log_flush(): New function to ensure no lingering buffered job data + remains. Now considers EBADF (LP: #912558). + - log_io_reader(): + - Added missing assert for @len. + - Simplified ENOSPC handling. + - Ensure log->io set to NULL to allow other routines to detect it + really has gone. + - log_file_write(): Added @len checks. + - log_read_watch(): New function to drain data from a watch descriptor + (which also must consider EBADF). + * init/log.h: Added define for LOG_READ_SIZE. + * init/tests/test_job_process.c: + - test_run(): + - Added some extra pointer checks. + - Free class *before* checking file to ensure destructor invoked at + correct point. + - Added test "with single-line command running an invalid command" + (for scenario bug 912558 exposed). + - Added test "with single-line command writing fast and exiting". + * init/tests/test_log.c: Changed all tests to use openpty(3) rather than + pipe(2) for semantic parity with actual code. + * util/tests/test_user_sessions.sh: + - ensure_no_output(): Now calls check_job_output() and delete_job() to + simplify logic. + - delete_job(): Call get_job_file() rather than doing it long-hand. + - check_job_output(): New function. + - start_job(): Added allow_failure parameter. + - test_ensure_no_unexpected_output(): New test + "ensure command job does not create log file with invalid command". + +2012-01-05 James Hunt + + * init/man/init.5: Explain that all job processes affected + by 'setuid' and 'setgid' stanzas. + +2011-12-22 James Hunt + + * init/job_process.c: job_process_spawn(): + - Set child handler to default rather than explicit ignore + to avoid test failures in environments that disallow + ignoring SIGCHLD. + * init/tests/test_job_process.c: test_run(): + - Changed timeout for test feature "ensure that no log + file written for CONSOLE_NONE". + +2011-12-15 James Hunt + + * Makefile.am: Add missing TESTING.sessions to distribution. + * contrib/vim/syntax/upstart.vim: Meta-data update and addition + of more standard (Ubuntu Upstart) events. + * extra/man/upstart-udev-bridge.8: Ensure literal dashes used + for all command-line options. + * extra/upstart-udev-bridge.c: + - udev_monitor_watcher(): Fix leak when obtaining udev value. + - make_safe_string(): Don't realloc since overhead too high + considering size of strings. + * init/job_class.c: Typo. + * init/job_process.c: job_process_spawn(): + - Correct ignoring of SIGCHLD prior to grantpt(3) call. + - Removed redundant close(2) calls. + - Move declarations to top of block for + getpwnam(3)/getgrnam(3). + * init/log.c: + - log_file_open(): Comments. + - log_file_write(): Added missing cast on + nih_io_buffer_shrink() call. + * init/main.c: console_type_setter(): NihOptionSetter's should + return 0 on success. + * init/man/init.5: lower-case all references to system jobs + and user jobs. + * init/tests/test_job_process.c: Add missing include for + fnmatch.h. + +2011-12-15 James Hunt + + * init/tests/test_job_process.c: test_run(): + - Ensure process group killed for multi-process shell scripts. + - Change 'command-not-found' tests to use regex matching rather + than literal to allow for minor differences in /bin/sh variants + error output. + 2011-12-13 James Hunt * NEWS: Release 1.4 --- upstart-1.4.orig/TESTING.sessions +++ upstart-1.4/TESTING.sessions @@ -0,0 +1,289 @@ +.. contents:: + +============================= +Testing Sessions with Upstart +============================= + +Upstart now has support for two types of "sessions": + +* user sessions + +* chroots sessions + +Such sessions are implemented by asociating a separate Upstart session +with every chroot environment and every non-privileged user. + +The session support does not yet provide full tests (as would be run by +"``make check``"). This is mostly due to the complexity of automatically +testing some of the scenarios below. + +This document attempts to outline the minimum set of tests that should +be performed to ensure that session support works as expected. + +Assumptions +=========== + +You are running an Ubuntu or Debian system. + +User Sessions +============= + +User sessions can be minimally tested using the script provided:: + + util/tests/test_user_sessions.sh + +Notes: + +- Run "``test_user_sessions.sh -h``" to understand what this script does. + +- that this script needs to be run on a system where the version of + ``/sbin/init`` has session support since the ``/sbin/init`` binary + itself will be tested. For this reason, the script cannot be run as part + of the "``make check``" run. + +- The script will complain loudly if any of the tests fail and tell you + how to raise a bug. + +Chroot Sessions +=============== + +Setup +----- + +#. Install a chroot environement (such as ``schroot(1)``) and configure it. +#. run, "``debootstrap(8)``" to install the same release as you are running into your chroot. +#. Create the following files *outside* your chroot. + + - ``/etc/init/foo.conf``:: + + start on wibble + + script + exec 2>>/tmp/foo.$$.log + set -x + echo "foo: stat=`stat /`" + echo "foo: env=`env`" + sleep 999 + end script + + - ``/etc/init/outside_chroot.conf``:: + + start on A + + script + exec 2>>/tmp/outside_chroot.$$.log + set -x + echo "in_chroot: stat=`stat /`" + echo "in_chroot: env=`env`" + sleep 999 + end script + +#. Create following files inside chroot environment. + + - ``/path/to/chroot/etc/init/foo.conf``:: + + start on wibble + + script + exec 2>>/tmp/foo.$$.log + set -x + echo "foo: stat=`stat /`" + echo "foo: env=`env`" + sleep 999 + end script + + - ``/path/to/chroot/etc/init/in_chroot.conf``:: + + start on A + + script + exec 2>>/tmp/in_chroot.$$.log + set -x + echo "in_chroot: stat=`stat /`" + echo "in_chroot: env=`env`" + sleep 999 + end script + + +Run as non-``root`` with no chroot +---------------------------------- + +- ``initctl list`` + + Ensure list is contents of ``/etc/init/``: the command below should + return no output:: + + cmp <(initctl list|awk '{print $1}'|sort -u) <(cd /etc/init && ls *.conf|cut -d: -f2-|sed 's/\.conf//g'|sort -u) + +- ``initctl status cron`` + + Should work. + +- ``initctl show-config`` + + Should work. + +- ``initctl check-config`` + + Should work. + +- ``start foo`` + + Should fail ("``initctl: Rejected send message``"). + +- ``stop cron`` + + Should fail ("``initctl: Rejected send message``"). + +- ``restart cron`` + + Should fail ("``initctl: Rejected send message``"). + +- ``initctl emit bar`` + + Should fail ("``initctl: Rejected send message``"). + +Run as ``root`` with no chroot +------------------------------ + +- ``initctl list`` + + Ensure list is contents of ``/etc/init/``: command below should return no output:: + + cmp <(initctl list|awk '{print $1}'|sort -u) <(cd /etc/init && ls *.conf|cut -d: -f2-|sed 's/\.conf//g'|sort -u) + +- ``initctl status outside_chroot`` + + Should work. + +- ``initctl status in_chroot`` + + Should fail with message:: + + initct: Unknown job: in_chroot + +- ``initctl show-config`` + + Should work. + +- ``initctl check-config`` + + Should work. + +- ``start in_chroot`` + + Should fail with message:: + + initct: Unknown job: in_chroot + +- ``start outside_chroot`` + + Should work. + +- ``stop in_chroot`` + + Should fail with message:: + + initct: Unknown job: in_chroot + +- ``restart outside_chroot`` + + Should work. + +- ``stop outside_chroot`` + + Should work. + +- ``initctl emit wibble`` + + - Ensure ``/etc/init/foo.conf`` runs by looking at the log and ensuring + the inode for the stat of '``/``' is the same as running "``stat /``" on + the command-line. + + * Ensure ``/path/to/chroot/etc/init/foo.conf`` does *NOT* run. + +- ``initctl emit A`` + + - Ensure ``/etc/init/outside_chroot.conf`` runs. + + - Ensure ``/path/to/chroot/etc/init/in_chroot.conf`` does *NOT* run. + +Run as ``root`` inside a chroot +------------------------------- + +- ``initctl list`` + + Ensure list is contents of ``/path/to/chroot/etc/init/``: command below should return no output:: + + cmp <(initctl list|awk '{print $1}'|sort -u) <(cd /etc/init && ls *.conf|cut -d: -f2-|sed 's/\.conf//g'|sort -u) + +- ``initctl status in_chroot`` + + Should work. + +- ``initctl status outside_chroot`` + + Should fail with message:: + + initct: Unknown job: outside_chroot + +- ``initctl show-config`` + + Should work. + +- ``initctl check-config`` + + Should work. + +- ``start outside_chroot`` + + Should fail with message:: + + initct: Unknown job: outside_chroot + +- ``stop outside_chroot`` + + Should fail with message:: + + initct: Unknown job: outside_chroot + +- ``start in_chroot`` + + Should work. + +- ``restart in_chroot`` + + Should work. + +- ``stop in_chroot`` + + Should work. + +- ``initctl emit wibble`` + + - Ensure ``/path/to/chroot/etc/init/foo.conf`` runs by looking at the log and ensuring + the inode for the stat of '``/``' is the same as running "``stat /``" on the command-line. + + - Ensure ``/etc/init/foo.conf`` does *NOT* run. + +- ``initctl emit A`` + + - Ensure ``/path/to/chroot/etc/init/in_chroot.conf`` runs. + + - Ensure ``/etc/init/outside_chroot.conf`` does *NOT* run. + +Run as non-root inside a chroot +------------------------------- + +Not supported. + +Run with ``--no-sessions`` +-------------------------- + +The "``--no-sessions``" option makes Upstart behave as it used to prior to +the introduction of session support. + +#. Reboot system and specify "``--no-sessions``" on kernel command-line. + +#. Ensure "``initctl list``" within a chroot displays the same list as + "``initctl list``" run outside of any chroot environment. --- upstart-1.4.orig/Makefile.am +++ upstart-1.4/Makefile.am @@ -2,6 +2,6 @@ SUBDIRS = intl dbus init util extra conf doc contrib po scripts -EXTRA_DIST = HACKING +EXTRA_DIST = HACKING TESTING.sessions ACLOCAL_AMFLAGS = --install -I m4 --- upstart-1.4.orig/Makefile.in +++ upstart-1.4/Makefile.in @@ -303,7 +303,7 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = intl dbus init util extra conf doc contrib po scripts -EXTRA_DIST = HACKING +EXTRA_DIST = HACKING TESTING.sessions ACLOCAL_AMFLAGS = --install -I m4 all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive --- upstart-1.4.orig/util/man/initctl.8 +++ upstart-1.4/util/man/initctl.8 @@ -16,6 +16,12 @@ .BR init (8) daemon. +If D\-Bus has been configured to allow non\-privileged users to invoke all +Upstart D\-Bus methods, this command is also able to manage user jobs. +See +.BR init (5) +for further details. + When run as .BR initctl , the first non\-option argument is the --- upstart-1.4.orig/util/tests/test_user_sessions.sh +++ upstart-1.4/util/tests/test_user_sessions.sh @@ -0,0 +1,1091 @@ +#!/bin/sh +#--------------------------------------------------------------------- +# Script to run minimal Upstart user session tests. +# +# Note that this script _cannot_ be run as part of the "make check" +# tests since those tests stimulate functions and features of the +# as-yet-uninstalled version of Upstart. However, this script needs to +# run on a system where the version of Upstart under test has _already_ +# been fully installed. +#--------------------------------------------------------------------- +# +# Copyright (C) 2011 Canonical Ltd. +# +# Author: James Hunt +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +#--------------------------------------------------------------------- + +script_name=${0##*/} +sys_job_dir="/etc/init" +user_job_dir="$HOME/.init" +user_log_dir="$HOME/.cache/upstart/log" +sys_log_dir="/var/log/upstart" +bug_url="https://bugs.launchpad.net/upstart/+filebug" +test_dir= +test_dir_suffix= +user_to_create= +uid= +gid= +opt= +OPTARG= +debug_enabled=0 +feature= + +# allow non-priv users to find 'initctl' +export PATH=$PATH:/sbin + +# for assertions +die() +{ + msg="$*" + echo "ERROR: $msg" >&2 + exit 1 +} + +debug() +{ + str="$1" + [ "$debug_enabled" = 1 ] && echo "DEBUG: $str" +} + +get_job_pid() +{ + job="$1" + [ -z "$job" ] && die "need job" + + pid=$(initctl status "$job"|grep process|awk '{print $NF}') + [ -z "$pid" ] && die "job $job has no pid" + + echo "$pid" +} + +# take a string and convert it into a valid job name +make_job_name() +{ + str="$1" + + echo "$str" |\ + sed -e 's/>/ gt /g' -e 's//dev/null 2>&1 + rc=$? + TEST_EQ "$cmd" $rc 0 +} + +# Note that if the specified job is *not* as task, it is expected to run +# indefinately. This allows us to perform PID checks, etc. +run_user_job_tests() +{ + job_name="$1" + job_file="$2" + task="$3" + env="$4" + + # XXX: env can be empty + [ -z "$job_name" ] && die "no job name" + [ -z "$job_file" ] && die "no job file" + [ -z "$task" ] && die "no task value" + + job="${test_dir_suffix}/${job_name}" + + [ -f "$job_file" ] || TEST_FAILED "job file '$job_file' does not exist" + + ensure_job_known "$job" "$job_name" + + TEST_FEATURE "ensure job can be started" + cmd="start ${job} ${env}" + output=$(eval "$cmd") + rc=$? + TEST_EQ "$cmd" $rc 0 + + if [ "$task" = no ] + then + TEST_FEATURE "ensure 'start' shows job pid" + pid=$(echo "$output"|awk '{print $4}') + TEST_NE "pid" "$pid" "" + + TEST_FEATURE "ensure 'initctl' shows job is running with pid" + initctl list|grep -q "^$job start/running, process $pid" || \ + TEST_FAILED "job $job_name did not start" + + TEST_FEATURE "ensure 'status' shows job is running with pid" + cmd="status ${job}" + output=$(eval "$cmd") + echo "$output"|while read job_tmp state ignored status_pid + do + state=$(echo $state|tr -d ',') + TEST_EQ "job name" "$job_tmp" "$job" + TEST_EQ "job state" "$state" "start/running" + TEST_EQ "job pid" "$status_pid" "$pid" + done + + TEST_FEATURE "ensure job pid is running with correct uids" + pid_uids=$(ps --no-headers -p $pid -o euid,ruid) + for pid_uid in $pid_uids + do + TEST_EQ "pid uid" "$pid_uid" "$uid" + done + + TEST_FEATURE "ensure job pid is running with correct gids" + pid_gids=$(ps --no-headers -p $pid -o egid,rgid) + for pid_gid in $pid_gids + do + TEST_EQ "pid gid" "$pid_gid" "$gid" + done + + TEST_FEATURE "ensure process is running in correct directory" + cwd=$(readlink /proc/$pid/cwd) + TEST_EQ "cwd" "$cwd" "$HOME" + + TEST_FEATURE "ensure job can be stopped" + cmd="stop ${job}" + output=$(eval "$cmd") + rc=$? + TEST_EQ "$cmd" $rc 0 + + TEST_FEATURE "ensure job pid no longer exists" + pid_ids=$(ps --no-headers -p $pid -o euid,ruid,egid,rgid) + TEST_EQ "pid uids+gids" "$pid_ids" "" + fi + + remove_job_file "$job_file" + ensure_job_gone "$job" "$job_name" "$env" +} + +remove_job_file() +{ + job_file="$1" + + [ -z "$job_file" ] && die "no job file" + [ ! -f "$job_file" ] && TEST_FAILED "job file '$job_file' does not exist" + + cmd="rm $job_file" + eval "$cmd" + TEST_EQ "$cmd" $? 0 +} + +ensure_job_gone() +{ + job="$1" + job_name="$2" + env="$3" + + # XXX: no check on env since it can be empty + [ -z "$job" ] && die "no job" + [ -z "$job_name" ] && die "no job name" + + TEST_FEATURE "ensure 'initctl' no longer recognises job" + initctl list|grep -q "^$job " && \ + TEST_FAILED "deleted job $job_name still known to initctl" + + TEST_FEATURE "ensure 'status' no longer recognises job" + cmd="status ${job}" + eval "$cmd" >/dev/null 2>&1 + rc=$? + TEST_NE "$cmd" $rc 0 +} + +test_user_job() +{ + test_group="$1" + job_name="$2" + script="$3" + task="$4" + env="$5" + + # XXX: no test on script or env since they might be empty + [ -z "$test_group" ] && die "no test group" + [ -z "$job_name" ] && die "no job name" + [ -z "$task" ] && die "no task" + + TEST_GROUP "$test_group" + + job_file="${test_dir}/${job_name}.conf" + + echo "$script" > $job_file + + run_user_job_tests "$job_name" "$job_file" "$task" "$env" +} + +test_user_job_binary() +{ + group="user job running a binary" + job_name="binary_test" + script="exec sleep 999" + test_user_job "$group" "$job_name" "$script" no "" +} + +test_user_job_binary_task() +{ + group="user job running a binary task" + job_name="binary_task_test" + OUTFILE=$(mktemp) + + script="\ +task +exec /bin/true > $OUTFILE" + + test_user_job "$group" "$job_name" "$script" yes "OUTFILE=$OUTFILE" + rm -f $OUTFILE +} + +test_user_job_single_line_script() +{ + group="user job running a single-line script" + job_name="single_line_script_test" + script="\ +script + sleep 999 +end script" + test_user_job "$group" "$job_name" "$script" no "" +} + +test_user_job_single_line_script_task() +{ + group="user job running a single-line script task" + job_name="single_line_script_task_test" + OUTFILE=$(mktemp) + + script="\ +task +script + exec /bin/true > $OUTFILE +end script" + test_user_job "$group" "$job_name" "$script" yes "OUTFILE=$OUTFILE" + rm -f $OUTFILE +} + +test_user_job_multi_line_script() +{ + group="user job running a multi-line script" + job_name="multi_line_script_test" + script="\ +script + + /bin/true + /bin/true;/bin/true + sleep 999 + +end script" + test_user_job "$group" "$job_name" "$script" no "" +} + +test_user_job_multi_line_script_task() +{ + group="user job running a multi-line script task" + job_name="multi_line_script_task_test" + OUTFILE=$(mktemp) + + script="\ +task +script + + /bin/true + /bin/true + /bin/true + +end script" + test_user_job "$group" "$job_name" "$script" yes "OUTFILE=$OUTFILE" + rm -f $OUTFILE +} + +test_user_emit_events() +{ + job_name="start_on_foo" + + TEST_GROUP "user emitting an event" + initctl emit foo || TEST_FAILED "failed to emit event as user" + + TEST_GROUP "user emitting an event to start a job" + script="\ + start on foo BAR=2 + stop on baz cow=moo or hello + exec sleep 999" + + job_file="${test_dir}/${job_name}.conf" + job="${test_dir_suffix}/${job_name}" + + echo "$script" > $job_file + + ensure_job_known "$job" "$job_name" + + initctl list|grep -q "^$job stop/waiting" || \ + TEST_FAILED "job $job_name not stopped" + + TEST_FEATURE "ensure job can be started with event" + initctl emit foo BAR=2 || \ + TEST_FAILED "failed to emit event for user job" + + initctl status "$job"|grep -q "^$job start/running" || \ + TEST_FAILED "job $job_name failed to start" + + TEST_FEATURE "ensure job can be stopped with event" + initctl emit baz cow=moo || \ + TEST_FAILED "failed to emit event for user job" + + initctl list|grep -q "^$job stop/waiting" || \ + TEST_FAILED "job $job_name not stopped" + + rm -f "$job_file" +} + +test_user_job_setuid_setgid() +{ + group="user job with setuid and setgid me" + job_name="setuid_setgid_me_test" + script="\ +setuid $(id -un) +setgid $(id -gn) +exec sleep 999" + test_user_job "$group" "$job_name" "$script" no "" + + TEST_GROUP "user job with setuid and setgid root" + script="\ +setuid root +setgid root +exec sleep 999" + + job_name="setuid_setgid_root_test" + job_file="${test_dir}/${job_name}.conf" + job="${test_dir_suffix}/${job_name}" + + echo "$script" > $job_file + + ensure_job_known "$job" "$job_name" + + TEST_FEATURE "ensure job fails to start as root" + cmd="start ${job}" + output=$(eval "$cmd" 2>&1) + rc=$? + TEST_EQ "$cmd" $rc 1 + + TEST_FEATURE "ensure 'start' indicates job failure" + error=$(echo "$output"|grep failed) + TEST_NE "error" "$error" "" + + TEST_FEATURE "ensure 'initctl' does not list job" + initctl list|grep -q "^$job stop/waiting" || \ + TEST_FAILED "job $job_name not listed as stopped" + + delete_job "$job_name" +} + +get_job_file() +{ + job_name="$1" + + [ -z "$job_name" ] && die "no job name" + echo "${test_dir}/${job_name}.conf" +} + +ensure_no_output() +{ + job_name="$1" + script="$2" + instance="$3" + + job="${test_dir_suffix}/${job_name}" + + create_job "$job_name" "$script" + start_job "$job" "$job_name" "$instance" + + check_job_output "$job_name" + delete_job "$job_name" +} + +create_job() +{ + job_name="$1" + script="$2" + + # XXX: script could be empty + [ -z "$job_name" ] && die "no job name" + + debug "create_job: job_name='$job_name'" + debug "create_job: script='$script'" + + # Not currently possible to have a user job with the + # same name as a system job. + # + # XXX: Note that this test assumes that user has *not* specified + # XXX: an alternate configuration directory using the + # XXX: '--confdir' option. + [ -e "${sys_job_dir}/${job_name}.conf" ] && \ + die "job '$job_name' already exists as a system job" + + job_file="${test_dir}/${job_name}.conf" + job="${test_dir_suffix}/${job_name}" + + echo "$script" > "$job_file" + sync +} + +delete_job() +{ + job_name="$1" + + [ -z "$job_name" ] && die "no job name" + + job_file="$(get_job_file $job_name)" + + rm "$job_file" || TEST_FAILED "unable to remove job file '$job_file'" +} + +check_job_output() +{ + job_name="$1" + + [ ! -z "$(ls $user_log_dir 2>/dev/null)" ] && \ + TEST_FAILED "job $job_name created logfile unexpectedly in '$user_log_dir'" + + # XXX: note that it might appear that checking in $sys_log_dir + # could result in false positives, but this isn't so since + # (currently) it is not possible for a user job to have the + # same name as a system job. start_job() will detect this + # scenario. + for dir in "$user_log_dir" "$sys_log_dir" + do + log_file="${dir}/${job_name}.log" + [ -f "$log_file" ] && \ + TEST_FAILED "job $job_name created logfile unexpectedly as '$log_file'" + done +} + +start_job() +{ + job="$1" + job_file="$2" + instance="$3" + allow_failure="$4" + + # XXX: instance may be blank + [ -z "$job" ] && die "no job" + [ -z "$job_file" ] && die "no job file" + + debug "start_job: job='$job'" + debug "start_job: job_file='$job_file'" + debug "start_job: instance='$instance'" + debug "start_job: allow_failure='$allow_failure'" + + eval output=$(mktemp) + + # XXX: Don't quote instance as we don't want to pass a null instance to + # start(8). + cmd="start \"$job\" $instance >${output} 2>&1" + debug "start_job: running '$cmd'" + eval "$cmd" + rc=$? + + if [ $rc -ne 0 -a -z "$allow_failure" ] + then + TEST_FAILED "job $job_file not started: $(cat $output)" + fi + + rm -f "$output" +} + +get_job_logfile_name() +{ + job_name="$1" + instance_value="$2" + + # XXX: instance may be null + [ -z "$job_name" ] && die "no job name" + + encoded_test_dir_suffix=$(upstart_encode "${test_dir_suffix}/") + file_name="${encoded_test_dir_suffix}$(make_log_name $job_name)" + + if [ ! -z "$instance_value" ] + then + log_file="${user_log_dir}/${file_name}-${instance_value}.log" + else + log_file="${user_log_dir}/${file_name}.log" + fi + + echo "$log_file" +} + +run_job() +{ + job="$1" + job_name="$2" + script="$3" + instance="$4" + + # XXX: script, instance might be blank + [ -z "$job" ] && die "no job" + [ -z "$job_name" ] && die "no job name" + + debug "run_job: job='$job'" + debug "run_job: job_name='$job_name'" + debug "run_job: script='$script'" + debug "run_job: instance='$instance'" + + create_job "$job_name" "$script" + start_job "$job" "$job_name" "$instance" +} + +ensure_file_meta() +{ + file="$1" + expected_owner="$2" + expected_group="$3" + expected_perms="$4" + + [ -z "$file" ] && die "no file" + [ -z "$expected_owner" ] && die "no expected owner" + [ -z "$expected_group" ] && die "no expected group" + [ -z "$expected_perms" ] && die "no expected perms" + + [ ! -f "$file" ] && die "file $file does not exist" + + expected_perms="640" + umask_value=$(umask) + umask_expected=0022 + + if [ "$umask_value" != "$umask_expected" ] + then + msg="umask value is $umask_value -" + msg="${msg} changing it to $umask_expected." + echo "WARNING: $msg" + umask "$umask_expected" || TEST_FAILED "unable to change umask" + fi + + owner=$(ls -l "$file"|awk '{print $3}') + group=$(ls -l "$file"|awk '{print $4}') + perms=$(stat --printf "%a\n" "$file") + + [ "$owner" = "$expected_owner" ] || TEST_FAILED \ + "file $file has wrong owner (expected $expected_owner, got $owner)" + + [ "$group" = "$expected_group" ] || TEST_FAILED \ + "file $file has wrong group (expected $expected_group, got $group)" + + [ "$perms" = "$expected_perms" ] || TEST_FAILED \ + "file $file has wrong group (expected $expected_perms, got $perms)" +} + + +ensure_output() +{ + job_name="$1" + script="$2" + expected_output="$3" + instance="$4" + instance_value="$5" + options="$6" + + # XXX: remaining args could be null + [ -z "$job_name" ] && die "no job name" + + debug "ensure_output: job_name='$job_name'" + debug "ensure_output: script='$script'" + debug "ensure_output: expected_ouput='$expected_ouput'" + debug "ensure_output: instance='$instance'" + debug "ensure_output: instance_value='$instance_value'" + debug "ensure_output: options='$options'" + + regex=n + retain=n + unique="" + use_od=n + + for opt in $options + do + case "$opt" in + regex) + regex=y + ;; + retain) + retain=y + ;; + unique) + unique='|sort -u' + ;; + use_od) + use_od=y + ;; + esac + done + + debug "ensure_output: regex='$regex'" + debug "ensure_output: retain='$retain'" + debug "ensure_output: unique='$unique'" + debug "ensure_output: use_od='$use_od'" + + expected_owner=$(id -un) + expected_group=$(id -gn) + expected_perms="640" + + job="${test_dir_suffix}/${job_name}" + + run_job "$job" "$job_name" "$script" "$instance" + + debug "ensure_output: user_log_dir='$user_log_dir'" + debug "ensure_output: test_dir='$test_dir'" + debug "ensure_output: test_dir_suffix='$test_dir_suffix'" + + log_file=$(get_job_logfile_name "$job_name" "$instance_value") + + debug "ensure_output: log_file='$log_file'" + + # Give Upstart a chance to parse the file + count=1 + while ! status "$job" >/dev/null 2>&1 + do + sleep 1 + count=$((count+1)) + [ "$count" -eq 5 ] && break + done + + # give job a chance to start + count=1 + while [ ! -f "$log_file" ] + do + sleep 1 + count=$((count+1)) + [ "$count" -eq 5 ] && break + done + + [ ! -f "$log_file" ] && \ + TEST_FAILED "job '$job_name' failed to create logfile" + + ensure_file_meta \ + "$log_file" \ + "$expected_owner" \ + "$expected_group" \ + "$expected_perms" + + # XXX: note we have to remove carriage returns added by the line + # discipline + if [ "$regex" = y ] + then + log=$(eval "cat $log_file|tr -d '\r' $unique") + msg="job '$job_name' failed to log correct data\n" + msg="${msg}\texpected regex: '$expected_output'\n" + msg="${msg}\tgot : '$log'" + cat "$log_file" | egrep "$expected_output" || TEST_FAILED "$msg" + elif [ "$use_od" = y ] + then + log=$(eval "cat $log_file|tr -d '\r' $unique|od -x") + msg="job '$job_name' failed to log correct data\n" + msg="${msg}\texpected hex: '$expected_output'\n" + msg="${msg}\tgot : '$log'" + [ "$expected_output" != "$log" ] && TEST_FAILED "$msg" + else + log=$(eval "cat $log_file|tr -d '\r' $unique") + msg="job '$job_name' failed to log correct data\n" + msg="${msg}\texpected text: '$expected_output'\n" + msg="${msg}\tgot : '$log'" + [ "$expected_output" != "$log" ] && TEST_FAILED "$msg" + fi + + if [ "$retain" = n ] + then + delete_job "$job_name" + rm "$log_file" || TEST_FAILED "unable to remove log file '$log_file'" + fi +} + +test_ensure_no_unexpected_output() +{ + #--------------------------------------------------------------------- + feature="ensure command job does not create log file with no console" + TEST_FEATURE "$feature" + + job_name=$(make_job_name "$feature") + + script="\ + console none + exec echo hello world" + + ensure_no_output "$job_name" "$script" "" + + #--------------------------------------------------------------------- + feature="ensure 1-line script job does not create log file with no console" + TEST_FEATURE "$feature" + + job_name=$(make_job_name "$feature") + + script="\ + console none + script + echo hello world + end script + " + + ensure_no_output "$job_name" "$script" "" + + #--------------------------------------------------------------------- + feature="ensure multi-line script job does not create log file with no console" + TEST_FEATURE "$feature" + + job_name=$(make_job_name "$feature") + + script="\ + console none + script + /bin/true + echo hello world + end script + " + + ensure_no_output "$job_name" "$script" "" + + #--------------------------------------------------------------------- + feature="ensure no output if log directory does not exist" + TEST_FEATURE "$feature" + + rmdir "${user_log_dir}" || \ + TEST_FAILED "unable to delete log directory '$user_log_dir'" + + job_name=$(make_job_name "$feature") + string="hello world" + script="\ + console log + script + /bin/true + /bin/echo hello world + end script + " + + ensure_no_output "$job_name" "$script" "" + + mkdir "${user_log_dir}" || \ + TEST_FAILED "unable to recreate log directory '$user_log_dir'" + + #--------------------------------------------------------------------- + feature="ensure command job does not create log file with invalid command" + TEST_FEATURE "$feature" + + job_name=$(make_job_name "$feature") + + script="\ + console log + exec /this/command/does/not/exist" + + job="${test_dir_suffix}/${job_name}" + create_job "$job_name" "$script" + start_job "$job" "$job_name" "" 1 + check_job_output "$job_name" + delete_job "$job_name" +} + +test_output_logged() +{ + # XXX: upstart won't create this + mkdir -p "$user_log_dir" + + test_ensure_no_unexpected_output +} + +test_user_jobs() +{ + test_user_job_binary + test_user_job_single_line_script + test_user_job_multi_line_script + + test_user_job_binary_task + test_user_job_single_line_script_task + test_user_job_multi_line_script_task + + test_user_job_setuid_setgid + + test_user_emit_events + + test_output_logged +} + +tests() +{ + echo + echo -n "Running Upstart user session tests as user '`whoami`'" + echo " (uid $uid, gid $gid) in directory '$test_dir'" + echo + + test_user_jobs + + echo + echo "All tests completed successfully" + echo +} + +usage() +{ +cat < : Specify name of test user to create. + +DESCRIPTION: + +Run simple set of Upstart user session tests. + +PREREQUISITE: + +For this test to run, non-root users must be allowed to invoke all D-Bus +methods on Upstart via configuration file: + + /etc/dbus-1/system.d/Upstart.conf + +See dbus-daemon(1) for further details. + +WARNING: Note that this script is unavoidably invasive, so read what +WARNING: follows before running! + +If run as a non-root user, this script will create a uniquely-named +subdirectory below "\$HOME/.init/" to run its tests in. On successful +completion of these tests, the unique subdirectory and its contents will +be removed. + +If however, this script is invoked as the root user, the script will +refuse to run until given the name of a test user to create via the "-u" +option. If the user specified to this option already exists, this script +will exit with an error. If the user does not already exist, it will be +created, the script then run *as that user* and assuming successful +completion of the tests, the test user and their home directory will +then be deleted. + +EOT +} + +#--------------------------------------------------------------------- +# main +#--------------------------------------------------------------------- + +while getopts "dhu:" opt +do + case "$opt" in + d) + debug_enabled=1 + ;; + + h) + usage + exit 0 + ;; + + u) + user_to_create="$OPTARG" + ;; + esac +done + +setup +tests +cleanup +exit 0 --- upstart-1.4.orig/po/en@boldquot.po +++ upstart-1.4/po/en@boldquot.po @@ -32,8 +32,8 @@ msgstr "" "Project-Id-Version: upstart 1.4\n" "Report-Msgid-Bugs-To: new@bugs.launchpad.net\n" -"POT-Creation-Date: 2011-12-12 11:29+0000\n" -"PO-Revision-Date: 2011-12-12 11:29+0000\n" +"POT-Creation-Date: 2011-12-14 11:53+0000\n" +"PO-Revision-Date: 2011-12-14 11:53+0000\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: en\n" @@ -88,7 +88,7 @@ msgid "Disconnected from system bus" msgstr "Disconnected from system bus" -#: init/control.c:370 init/main.c:694 +#: init/control.c:370 init/main.c:830 msgid "Reloading configuration" msgstr "Reloading configuration" @@ -517,43 +517,43 @@ msgid "Failed to write to log file" msgstr "Failed to write to log file" -#: init/main.c:131 +#: init/main.c:135 msgid "specify alternative directory to load configuration files from" msgstr "specify alternative directory to load configuration files from" -#: init/main.c:134 +#: init/main.c:138 msgid "default value for console stanza" msgstr "default value for console stanza" -#: init/main.c:137 +#: init/main.c:141 msgid "specify alternative directory to store job output logs in" msgstr "specify alternative directory to store job output logs in" -#: init/main.c:140 +#: init/main.c:144 msgid "disable job logging" msgstr "disable job logging" -#: init/main.c:143 +#: init/main.c:147 msgid "disable user and chroot sessions" msgstr "disable user and chroot sessions" -#: init/main.c:146 +#: init/main.c:150 msgid "do not emit any startup event (for testing)" msgstr "do not emit any startup event (for testing)" -#: init/main.c:151 +#: init/main.c:155 msgid "use D-Bus session bus rather than system bus (for testing)" msgstr "use D-Bus session bus rather than system bus (for testing)" -#: init/main.c:154 +#: init/main.c:158 msgid "specify an alternative initial event (for testing)" msgstr "specify an alternative initial event (for testing)" -#: init/main.c:174 +#: init/main.c:178 msgid "Process management daemon." msgstr "Process management daemon." -#: init/main.c:176 +#: init/main.c:180 msgid "" "This daemon is normally executed by the kernel and given process id 1 to " "denote its special status. When executed by a user process, it will " @@ -563,65 +563,75 @@ "denote its special status. When executed by a user process, it will " "actually run /sbin/telinit." -#: init/main.c:197 util/reboot.c:166 util/shutdown.c:363 util/telinit.c:148 +#: init/main.c:201 util/reboot.c:166 util/shutdown.c:363 util/telinit.c:148 msgid "Need to be root" msgstr "Need to be root" -#: init/main.c:206 +#: init/main.c:210 msgid "Not being executed as init" msgstr "Not being executed as init" -#: init/main.c:247 +#: init/main.c:251 msgid "Unable to initialize console, will try /dev/null" msgstr "Unable to initialize console, will try /dev/null" -#: init/main.c:253 +#: init/main.c:257 msgid "Unable to initialize console as /dev/null" msgstr "Unable to initialize console as /dev/null" -#: init/main.c:269 init/main.c:604 +#: init/main.c:273 init/main.c:687 msgid "Unable to set root directory" msgstr "Unable to set root directory" -#: init/main.c:280 +#: init/main.c:284 msgid "Unable to mount /proc filesystem" msgstr "Unable to mount /proc filesystem" -#: init/main.c:289 +#: init/main.c:293 msgid "Unable to mount /sys filesystem" msgstr "Unable to mount /sys filesystem" -#: init/main.c:392 init/main.c:398 +#: init/main.c:403 init/main.c:409 msgid "Unable to set default oom score" msgstr "Unable to set default oom score" -#: init/main.c:417 +#: init/main.c:428 msgid "Unable to listen for private connections" msgstr "Unable to listen for private connections" -#: init/main.c:450 +#: init/main.c:461 msgid "Unable to setup standard file descriptors" msgstr "Unable to setup standard file descriptors" -#: init/main.c:621 +#: init/main.c:704 #, c-format msgid "Caught %s, core dumped" msgstr "Caught %s, core dumped" -#: init/main.c:625 +#: init/main.c:708 #, c-format msgid "Caught %s, unable to dump core" msgstr "Caught %s, unable to dump core" -#: init/main.c:711 +#: init/main.c:737 +#, c-format +msgid "Re-executing %s" +msgstr "Re-executing %s" + +#: init/main.c:762 +#, c-format +msgid "Failed to re-execute %s: %s" +msgstr "Failed to re-execute %s: %s" + +#: init/main.c:847 msgid "Reconnecting to system bus" msgstr "Reconnecting to system bus" -#: init/main.c:717 +#: init/main.c:853 msgid "Unable to connect to the system bus" msgstr "Unable to connect to the system bus" -#: init/main.c:793 +#: init/main.c:929 msgid "invalid console type specified" msgstr "invalid console type specified" --- upstart-1.4.orig/po/en@quot.po +++ upstart-1.4/po/en@quot.po @@ -29,8 +29,8 @@ msgstr "" "Project-Id-Version: upstart 1.4\n" "Report-Msgid-Bugs-To: new@bugs.launchpad.net\n" -"POT-Creation-Date: 2011-12-12 11:29+0000\n" -"PO-Revision-Date: 2011-12-12 11:29+0000\n" +"POT-Creation-Date: 2011-12-14 11:53+0000\n" +"PO-Revision-Date: 2011-12-14 11:53+0000\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: en\n" @@ -85,7 +85,7 @@ msgid "Disconnected from system bus" msgstr "Disconnected from system bus" -#: init/control.c:370 init/main.c:694 +#: init/control.c:370 init/main.c:830 msgid "Reloading configuration" msgstr "Reloading configuration" @@ -514,43 +514,43 @@ msgid "Failed to write to log file" msgstr "Failed to write to log file" -#: init/main.c:131 +#: init/main.c:135 msgid "specify alternative directory to load configuration files from" msgstr "specify alternative directory to load configuration files from" -#: init/main.c:134 +#: init/main.c:138 msgid "default value for console stanza" msgstr "default value for console stanza" -#: init/main.c:137 +#: init/main.c:141 msgid "specify alternative directory to store job output logs in" msgstr "specify alternative directory to store job output logs in" -#: init/main.c:140 +#: init/main.c:144 msgid "disable job logging" msgstr "disable job logging" -#: init/main.c:143 +#: init/main.c:147 msgid "disable user and chroot sessions" msgstr "disable user and chroot sessions" -#: init/main.c:146 +#: init/main.c:150 msgid "do not emit any startup event (for testing)" msgstr "do not emit any startup event (for testing)" -#: init/main.c:151 +#: init/main.c:155 msgid "use D-Bus session bus rather than system bus (for testing)" msgstr "use D-Bus session bus rather than system bus (for testing)" -#: init/main.c:154 +#: init/main.c:158 msgid "specify an alternative initial event (for testing)" msgstr "specify an alternative initial event (for testing)" -#: init/main.c:174 +#: init/main.c:178 msgid "Process management daemon." msgstr "Process management daemon." -#: init/main.c:176 +#: init/main.c:180 msgid "" "This daemon is normally executed by the kernel and given process id 1 to " "denote its special status. When executed by a user process, it will " @@ -560,65 +560,75 @@ "denote its special status. When executed by a user process, it will " "actually run /sbin/telinit." -#: init/main.c:197 util/reboot.c:166 util/shutdown.c:363 util/telinit.c:148 +#: init/main.c:201 util/reboot.c:166 util/shutdown.c:363 util/telinit.c:148 msgid "Need to be root" msgstr "Need to be root" -#: init/main.c:206 +#: init/main.c:210 msgid "Not being executed as init" msgstr "Not being executed as init" -#: init/main.c:247 +#: init/main.c:251 msgid "Unable to initialize console, will try /dev/null" msgstr "Unable to initialize console, will try /dev/null" -#: init/main.c:253 +#: init/main.c:257 msgid "Unable to initialize console as /dev/null" msgstr "Unable to initialize console as /dev/null" -#: init/main.c:269 init/main.c:604 +#: init/main.c:273 init/main.c:687 msgid "Unable to set root directory" msgstr "Unable to set root directory" -#: init/main.c:280 +#: init/main.c:284 msgid "Unable to mount /proc filesystem" msgstr "Unable to mount /proc filesystem" -#: init/main.c:289 +#: init/main.c:293 msgid "Unable to mount /sys filesystem" msgstr "Unable to mount /sys filesystem" -#: init/main.c:392 init/main.c:398 +#: init/main.c:403 init/main.c:409 msgid "Unable to set default oom score" msgstr "Unable to set default oom score" -#: init/main.c:417 +#: init/main.c:428 msgid "Unable to listen for private connections" msgstr "Unable to listen for private connections" -#: init/main.c:450 +#: init/main.c:461 msgid "Unable to setup standard file descriptors" msgstr "Unable to setup standard file descriptors" -#: init/main.c:621 +#: init/main.c:704 #, c-format msgid "Caught %s, core dumped" msgstr "Caught %s, core dumped" -#: init/main.c:625 +#: init/main.c:708 #, c-format msgid "Caught %s, unable to dump core" msgstr "Caught %s, unable to dump core" -#: init/main.c:711 +#: init/main.c:737 +#, c-format +msgid "Re-executing %s" +msgstr "Re-executing %s" + +#: init/main.c:762 +#, c-format +msgid "Failed to re-execute %s: %s" +msgstr "Failed to re-execute %s: %s" + +#: init/main.c:847 msgid "Reconnecting to system bus" msgstr "Reconnecting to system bus" -#: init/main.c:717 +#: init/main.c:853 msgid "Unable to connect to the system bus" msgstr "Unable to connect to the system bus" -#: init/main.c:793 +#: init/main.c:929 msgid "invalid console type specified" msgstr "invalid console type specified" --- upstart-1.4.orig/po/upstart.pot +++ upstart-1.4/po/upstart.pot @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: upstart 1.4\n" "Report-Msgid-Bugs-To: new@bugs.launchpad.net\n" -"POT-Creation-Date: 2011-12-12 11:29+0000\n" +"POT-Creation-Date: 2011-12-14 11:53+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -63,7 +63,7 @@ msgid "Disconnected from system bus" msgstr "" -#: init/control.c:370 init/main.c:694 +#: init/control.c:370 init/main.c:830 msgid "Reloading configuration" msgstr "" @@ -492,108 +492,118 @@ msgid "Failed to write to log file" msgstr "" -#: init/main.c:131 +#: init/main.c:135 msgid "specify alternative directory to load configuration files from" msgstr "" -#: init/main.c:134 +#: init/main.c:138 msgid "default value for console stanza" msgstr "" -#: init/main.c:137 +#: init/main.c:141 msgid "specify alternative directory to store job output logs in" msgstr "" -#: init/main.c:140 +#: init/main.c:144 msgid "disable job logging" msgstr "" -#: init/main.c:143 +#: init/main.c:147 msgid "disable user and chroot sessions" msgstr "" -#: init/main.c:146 +#: init/main.c:150 msgid "do not emit any startup event (for testing)" msgstr "" -#: init/main.c:151 +#: init/main.c:155 msgid "use D-Bus session bus rather than system bus (for testing)" msgstr "" -#: init/main.c:154 +#: init/main.c:158 msgid "specify an alternative initial event (for testing)" msgstr "" -#: init/main.c:174 +#: init/main.c:178 msgid "Process management daemon." msgstr "" -#: init/main.c:176 +#: init/main.c:180 msgid "" "This daemon is normally executed by the kernel and given process id 1 to " "denote its special status. When executed by a user process, it will " "actually run /sbin/telinit." msgstr "" -#: init/main.c:197 util/reboot.c:166 util/shutdown.c:363 util/telinit.c:148 +#: init/main.c:201 util/reboot.c:166 util/shutdown.c:363 util/telinit.c:148 msgid "Need to be root" msgstr "" -#: init/main.c:206 +#: init/main.c:210 msgid "Not being executed as init" msgstr "" -#: init/main.c:247 +#: init/main.c:251 msgid "Unable to initialize console, will try /dev/null" msgstr "" -#: init/main.c:253 +#: init/main.c:257 msgid "Unable to initialize console as /dev/null" msgstr "" -#: init/main.c:269 init/main.c:604 +#: init/main.c:273 init/main.c:687 msgid "Unable to set root directory" msgstr "" -#: init/main.c:280 +#: init/main.c:284 msgid "Unable to mount /proc filesystem" msgstr "" -#: init/main.c:289 +#: init/main.c:293 msgid "Unable to mount /sys filesystem" msgstr "" -#: init/main.c:392 init/main.c:398 +#: init/main.c:403 init/main.c:409 msgid "Unable to set default oom score" msgstr "" -#: init/main.c:417 +#: init/main.c:428 msgid "Unable to listen for private connections" msgstr "" -#: init/main.c:450 +#: init/main.c:461 msgid "Unable to setup standard file descriptors" msgstr "" -#: init/main.c:621 +#: init/main.c:704 #, c-format msgid "Caught %s, core dumped" msgstr "" -#: init/main.c:625 +#: init/main.c:708 #, c-format msgid "Caught %s, unable to dump core" msgstr "" -#: init/main.c:711 +#: init/main.c:737 +#, c-format +msgid "Re-executing %s" +msgstr "" + +#: init/main.c:762 +#, c-format +msgid "Failed to re-execute %s: %s" +msgstr "" + +#: init/main.c:847 msgid "Reconnecting to system bus" msgstr "" -#: init/main.c:717 +#: init/main.c:853 msgid "Unable to connect to the system bus" msgstr "" -#: init/main.c:793 +#: init/main.c:929 msgid "invalid console type specified" msgstr "" --- upstart-1.4.orig/init/log.c +++ upstart-1.4/init/log.c @@ -35,6 +35,8 @@ static int log_file_open (Log *log); static int log_file_write (Log *log, const char *buf, size_t len); +static void log_read_watch (Log *log); +static void log_flush (Log *log); /** * log_new: @@ -140,11 +142,54 @@ * @log: Log. * * Called automatically when Log is being destroyed. + * + * XXX: Note that the fd associated with the jobs stdout and stderr (as + * passed to log_new()) *MUST* be closed by the time this function is + * called since it will continue to read from the fd until an error is + * detected. This behaviour is required to ensure all job output is + * read. + * + * Returns: 0 always. **/ int log_destroy (Log *log) { + nih_assert (log); + + /* User job logging not currently available */ + nih_assert (log->uid == 0); + + log_flush (log); + + /* Force file to flush */ + if (log->fd != -1) + close (log->fd); + + log->fd = -1; + + return 0; +} + +/** + * log_flush: + * + * @log: Log. + * + * Ensure that no job output data is buffered and attempt to flush all + * unflushed data to disk. + * + * It is safe to call this function multiple times and may in fact be + * necessary if the log file cannot be written for any reason. + * + * Note no return value since there isn't much that can be done at + * the point this function is called should the flushing operations + * fail. + **/ +static void +log_flush (Log *log) +{ int ret; + int flags; nih_assert (log); @@ -162,21 +207,49 @@ goto out; ret = log_file_write (log, NULL, 0); - if (ret < 0) + if (ret < 0) { + close (log->fd); + log->fd = -1; goto out; + } } - /* Force file to flush - * - * Only attempt this for jobs which the current process is - * in control of. - */ - if (log->fd > 0) + if (log->io) { + nih_assert (log->io->watch); + + /* If the job associated with this log produces output _after_ + * nih_io_handle_fds() has been called in any loop of the main + * loop and just before the job is destroyed, we will miss it. + * + * Therefore, attempt to read from the watch fd until we get an error. + */ + log_read_watch (log); + + flags = fcntl (log->io->watch->fd, F_GETFL); + + if (flags < 0 && errno == EBADF) { + /* The watch fd is now known to be invalid, so disable + * the error handler to avoid an infinite loop where the + * error handler attempts to free the NihIo, which would + * error, causing the error handler to be called + * ad infinitum. + * + * Note that the NihIo is freed via + * nih_io_destroy(). + */ + log->io->error_handler = NULL; + + nih_free (log->io); + log->io = NULL; + } + } + + /* Force file to flush */ + if (log->fd != -1) close (log->fd); out: log->fd = -1; - return 0; } /** @@ -191,7 +264,7 @@ * encapsulated in @io. * * Notes for user jobs: - + * * User jobs by necessity are handled differently to system jobs. Since * a user job must log their data to files owned by a non-root user, the * safest technique is for a process running as that user to create the @@ -223,45 +296,37 @@ nih_assert (io); nih_assert (log->io == io); nih_assert (buf); + nih_assert (len); /* User job logging not currently available */ nih_assert (log->uid == 0); - /* Note we don't assert @len in case we are being called after - * an error is detected (where there is no new data, but may be - * unflushed data). - */ - /* Just in case we try to write more than read can inform us * about (this should really be a build-time assertion). */ nih_assert (sizeof (size_t) == sizeof (ssize_t)); if (log_file_open (log) < 0) { - if (errno == ENOSPC) { - /* Always discard when out of space */ - nih_io_buffer_shrink (io->recv_buf, len); - return; - } else { + if (errno != ENOSPC) { /* Add new data to unflushed buffer */ if (nih_io_buffer_push (log->unflushed, buf, len) < 0) return; - - nih_io_buffer_shrink (io->recv_buf, len); - /* No point attempting to write if we cannot - * open the file. - */ - return; } + + /* Note that we always discard when out of space */ + nih_io_buffer_shrink (io->recv_buf, len); + + /* No point attempting to write if we cannot + * open the file. + */ + return; } ret = log_file_write (log, buf, len); - if (ret < 0) { + if (ret < 0) nih_warn ("%s %s", _("Failed to write to log file"), log->path); - } } - /** * log_io_error_handler: * @@ -289,12 +354,12 @@ err = nih_error_get (); nih_assert (err->number == EIO); + nih_free (err); - if (log->io) { - /* Close the connection */ - nih_free (log->io); - } + /* Ensure the NihIo is closed */ + nih_free (log->io); + log->io = NULL; } /** @@ -333,6 +398,9 @@ * unlinked file, but it *is* a problem for * users who expect to see some data. Therefore, * close the file and attempt to rewrite it. + * + * This behaviour also allows tools such as logrotate(8) + * to operate without disrupting the logger. */ if (log->fd > -1 && ! statbuf.st_nlink) { close (log->fd); @@ -445,16 +513,18 @@ * Note that data is always discarded when out of * space. */ - if (saved != ENOSPC && nih_io_buffer_push (log->unflushed, buf, len) < 0) + if (saved != ENOSPC && len + && nih_io_buffer_push (log->unflushed, buf, len) < 0) goto error; - nih_io_buffer_shrink (io->recv_buf, len); + if (len) + nih_io_buffer_shrink (io->recv_buf, len); /* Still need to indicate that the write failed */ goto error; } - nih_io_buffer_shrink (log->unflushed, wlen); + nih_io_buffer_shrink (log->unflushed, (size_t)wlen); } /* Only managed a partial write for the unflushed data, @@ -463,6 +533,9 @@ * next time. */ if (log->unflushed->len) { + if (! len) + goto error; + /* Save new data */ if (nih_io_buffer_push (log->unflushed, buf, len) < 0) goto error; @@ -501,3 +574,62 @@ return -1; } +/** + * log_read_watch: + * + * @log: Log. + * + * Attempt a final read from the watch descriptor to ensure we've + * drained all the data from the job. + **/ +void +log_read_watch (Log *log) +{ + NihIo *io; + ssize_t len; + int saved; + + nih_assert (log); + + /* Must not be called if there is unflushed data as the log + * would then not be written in order . + */ + nih_assert (! log->unflushed->len); + + io = log->io; + + if (! io) + return; + + while (1) { + /* Ensure we have some space to read data from the job */ + if (nih_io_buffer_resize (io->recv_buf, LOG_READ_SIZE) < 0) + break; + + /* Append to buffer */ + len = read (io->watch->fd, + io->recv_buf->buf + io->recv_buf->len, + io->recv_buf->size - io->recv_buf->len); + saved = errno; + + if (len > 0) + io->recv_buf->len += len; + + if (io->recv_buf->len) + log_io_reader (log, io, io->recv_buf->buf, io->recv_buf->len); + + /* If an error occurs, it is likely to be EIO or EBADF. + * But erring on the side of caution, any unusual error + * causes the loop to be exited. + */ + if ((len < 0 && saved != EAGAIN && saved != EWOULDBLOCK) || len == 0) { + /* Either the job process end of the pty has + * been closed, or there really + * is no (more) data to be read. + */ + close (log->fd); + log->fd = -1; + break; + } + } +} --- upstart-1.4.orig/init/job_class.c +++ upstart-1.4/init/job_class.c @@ -1423,7 +1423,7 @@ * job_class_console_type: * @console: string representing console type. * - * Returns: ConsoleType equivalent of @string, or -1 on invalid @string. + * Returns: ConsoleType equivalent of @string, or -1 on invalid @console. **/ ConsoleType job_class_console_type (const char *console) --- upstart-1.4.orig/init/log.h +++ upstart-1.4/init/log.h @@ -41,6 +41,12 @@ **/ #define LOG_DEFAULT_MODE (S_IRWXU | S_IRGRP) +/** LOG_READ_SIZE: + * + * Minimum buffer size for reading log data. + **/ +#define LOG_READ_SIZE 1024 + /** * Log: * --- upstart-1.4.orig/init/main.c +++ upstart-1.4/init/main.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -65,6 +66,9 @@ #ifndef DEBUG static int logger_kmsg (NihLogLevel priority, const char *message); static void crash_handler (int signum); +#endif /* DEBUG */ +static void term_handler (void *data, NihSignal *signal); +#ifndef DEBUG static void cad_handler (void *data, NihSignal *signal); static void kbd_handler (void *data, NihSignal *signal); static void pwr_handler (void *data, NihSignal *signal); @@ -117,6 +121,7 @@ extern int disable_sessions; extern int disable_job_logging; +int force_logging; extern int use_session_bus; extern int default_console; extern char *log_dir; @@ -134,6 +139,9 @@ { 0, "default-console", N_("default value for console stanza"), NULL, "VALUE", NULL, console_type_setter }, + { 0, "log", N_("enable job logging"), + NULL, NULL, &force_logging, NULL }, + { 0, "logdir", N_("specify alternative directory to store job output logs in"), NULL, "DIR", &log_dir, NULL }, @@ -177,6 +185,9 @@ "process id 1 to denote its special status. When executed " "by a user process, it will actually run /sbin/telinit.")); + /* Temporarily disable job logging (bug 912558) */ + disable_job_logging = 1; + args = nih_option_parser (NULL, argc, argv, options, FALSE); if (! args) exit (1); @@ -184,6 +195,9 @@ handle_confdir (); handle_logdir (); + if (force_logging) + disable_job_logging = 0; + if (disable_job_logging) nih_debug ("Job logging disabled"); @@ -356,6 +370,13 @@ /* SIGUSR1 instructs us to reconnect to D-Bus */ nih_signal_set_handler (SIGUSR1, nih_signal_handler); NIH_MUST (nih_signal_add_handler (NULL, SIGUSR1, usr1_handler, NULL)); + + /* SIGTERM instructs us to re-exec ourselves; this should be the + * last in the list to ensure that all other signals are handled + * before a SIGTERM. + */ + nih_signal_set_handler (SIGTERM, nih_signal_handler); + NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, term_handler, NULL)); } #endif /* DEBUG */ @@ -463,6 +484,16 @@ * init daemon that exec'd us */ if (! restart) { + DIR *piddir; + + /* Look in well-known locations for pid files. + * + * Try /run (the newer) location first, but fall back to + * the original location for older systems. + */ + const char * const pid_paths[] = { "/run/initramfs/", "/dev/.initramfs/", NULL }; + const char * const *pid_path; + if (disable_startup_event) { nih_debug ("Startup event disabled"); } else { @@ -471,6 +502,68 @@ ? initial_event : STARTUP_EVENT, NULL)); + } + + for (pid_path = pid_paths; pid_path && *pid_path; pid_path++) { + struct dirent *ent; + + /* Total hack, look for .pid files in known + * locations - if there's a job config for them pretend + * that we started it and it has that pid. + */ + piddir = opendir (*pid_path); + if (! piddir) + continue; + + while ((ent = readdir (piddir)) != NULL) { + char path[PATH_MAX]; + char * ptr; + FILE * pidfile; + pid_t pid; + JobClass *class; + Job * job; + + if (ent->d_name[0] == '.') + continue; + + strcpy (path, *pid_path); + strcat (path, ent->d_name); + + ptr = strrchr (ent->d_name, '.'); + if ((! ptr) || strcmp (ptr, ".pid")) + continue; + + *ptr = '\0'; + pidfile = fopen (path, "r"); + if (! pidfile) + continue; + + pid = -1; + if (fscanf (pidfile, "%d", &pid)) + ; + fclose (pidfile); + + if ((pid < 0) || (kill (pid, 0) < 0)) + continue; + + class = (JobClass *)nih_hash_lookup (job_classes, ent->d_name); + if (! class) + continue; + if (! class->process[PROCESS_MAIN]) + continue; + if (strlen (class->instance)) + continue; + + job = NIH_MUST (job_new (class, "")); + job->goal = JOB_START; + job->state = JOB_RUNNING; + job->pid[PROCESS_MAIN] = pid; + + nih_debug ("%s inherited from initramfs with pid %d", class->name, pid); + } + + closedir (piddir); + break; } } else { @@ -630,7 +723,60 @@ /* Goodbye, cruel world. */ exit (signum); } +#endif + +/** + * term_handler: + * @data: unused, + * @signal: signal caught. + * + * This is called when we receive the TERM signal, which instructs us + * to reexec ourselves. + **/ +static void +term_handler (void *data, + NihSignal *signal) +{ + NihError *err; + const char *loglevel; + sigset_t mask, oldmask; + + nih_assert (argv0 != NULL); + nih_assert (signal != NULL); + + nih_warn (_("Re-executing %s"), argv0); + + /* Block signals while we work. We're the last signal handler + * installed so this should mean that they're all handled now. + * + * The child must make sure that it unblocks these again when + * it's ready. + */ + sigfillset (&mask); + sigprocmask (SIG_BLOCK, &mask, &oldmask); + + /* Argument list */ + if (nih_log_priority <= NIH_LOG_DEBUG) { + loglevel = "--debug"; + } else if (nih_log_priority <= NIH_LOG_INFO) { + loglevel = "--verbose"; + } else if (nih_log_priority >= NIH_LOG_ERROR) { + loglevel = "--error"; + } else { + loglevel = NULL; + } + execl (argv0, argv0, "--restart", loglevel, NULL); + nih_error_raise_system (); + + err = nih_error_get (); + nih_error (_("Failed to re-execute %s: %s"), argv0, err->message); + nih_free (err); + + sigprocmask (SIG_SETMASK, &oldmask, NULL); +} + +#ifndef DEBUG /** * cad_handler: * @data: unused, @@ -780,7 +926,7 @@ * NihOption setter function to handle selection of default console * type. * - * Returns 1 on success, -1 on invalid console type. + * Returns 0 on success, -1 on invalid console type. **/ static int console_type_setter (NihOption *option, const char *arg) @@ -794,5 +940,5 @@ return -1; } - return 1; + return 0; } --- upstart-1.4.orig/init/job_process.c +++ upstart-1.4/init/job_process.c @@ -465,6 +465,11 @@ nih_return_system_error (-1); } + /* Stop any process created _before_ the log object below is + * freed from inheriting this fd. + */ + nih_io_set_cloexec (pty_master); + /* pty_master will be closed by log_destroy() */ job->log = log_new (job, log_path, pty_master, 0); if (! job->log) { @@ -482,6 +487,13 @@ sigfillset (&child_set); sigprocmask (SIG_BLOCK, &child_set, &orig_set); + /* Ensure that any lingering data in stdio buffers is flushed + * to avoid the child getting a copy of it. + * If not done, CONSOLE_LOG jobs may end up with unexpected data + * in their logs if we run with for example '--debug'. + */ + fflush (NULL); + /* Fork the child process, handling success and failure by resetting * the signal mask and returning the new process id or a raised error. */ @@ -497,8 +509,13 @@ /* Read error from the pipe, return if one is raised */ if (job_process_error_read (fds[0]) < 0) { - if (class->console == CONSOLE_LOG) - close (pty_master); + if (class->console == CONSOLE_LOG) { + /* Ensure the pty_master watch gets + * removed and the fd closed. + */ + nih_free (job->log); + job->log = NULL; + } close (fds[0]); return -1; } @@ -514,8 +531,10 @@ sigprocmask (SIG_SETMASK, &orig_set, NULL); close (fds[0]); close (fds[1]); - if (class->console == CONSOLE_LOG) - close (pty_master); + if (class->console == CONSOLE_LOG) { + nih_free (job->log); + job->log = NULL; + } return -1; } @@ -535,40 +554,40 @@ job_process_remap_fd (&fds[1], JOB_PROCESS_SCRIPT_FD, fds[1]); nih_io_set_cloexec (fds[1]); - job_process_remap_fd (&pty_master, JOB_PROCESS_SCRIPT_FD, fds[1]); - if (class->console == CONSOLE_LOG) { struct sigaction act; + struct sigaction ignore; + + job_process_remap_fd (&pty_master, JOB_PROCESS_SCRIPT_FD, fds[1]); /* Child is the slave, so won't need this */ nih_io_set_cloexec (pty_master); - /* Save old handler as grantpt disallows child handler - * to be in effect + /* Temporarily disable child handler as grantpt(3) disallows one + * being in effect when called. */ - if (sigaction (SIGCHLD, NULL, &act) < 0) { - close (pty_master); + ignore.sa_handler = SIG_DFL; + ignore.sa_flags = 0; + sigemptyset (&ignore.sa_mask); + + if (sigaction (SIGCHLD, &ignore, &act) < 0) { job_process_error_abort (fds[1], JOB_PROCESS_ERROR_OPENPT_MASTER, 0); } if (grantpt (pty_master) < 0) { - close (pty_master); job_process_error_abort (fds[1], JOB_PROCESS_ERROR_OPENPT_MASTER, 0); } - /* Restore handler */ + /* Restore child handler */ if (sigaction (SIGCHLD, &act, NULL) < 0) { - close (pty_master); job_process_error_abort (fds[1], JOB_PROCESS_ERROR_OPENPT_MASTER, 0); } if (unlockpt (pty_master) < 0) { - close (pty_master); job_process_error_abort (fds[1], JOB_PROCESS_ERROR_UNLOCKPT, 0); } if (ptsname_r (pty_master, pts_name, sizeof(pts_name)) < 0) { - close (pty_master); job_process_error_abort (fds[1], JOB_PROCESS_ERROR_PTSNAME, 0); } @@ -801,11 +820,12 @@ * session jobs and jobs with a chroot stanza. */ if (class->setuid) { + struct passwd *pwd; /* Without resetting errno, it's impossible to * distinguish between a non-existent user and and * error during lookup */ errno = 0; - struct passwd *pwd = getpwnam (class->setuid); + pwd = getpwnam (class->setuid); if (! pwd) { if (errno != 0) { nih_error_raise_system (); @@ -823,8 +843,9 @@ } if (class->setgid) { + struct group *grp; errno = 0; - struct group *grp = getgrnam (class->setgid); + grp = getgrnam (class->setgid); if (! grp) { if (errno != 0) { nih_error_raise_system (); @@ -1569,6 +1590,18 @@ job->kill_process = -1; } + if (job->class->console == CONSOLE_LOG) { + /* It is imperative that we free the log at this stage to ensure + * that jobs which respawn have their log written _now_ + * (and not just when the overall Job object is freed at + * some distant future point). + */ + if (job->log) { + nih_free (job->log); + job->log = NULL; + } + } + /* Find existing utmp entry for the process pid */ setutxent(); while ((utmptr = getutxent()) != NULL) { --- upstart-1.4.orig/init/man/init.8 +++ upstart-1.4/init/man/init.8 @@ -48,6 +48,10 @@ and .BR stopped (7) events emitted as jobs change state. + +See +.BR upstart-events (7) +for a summary of well-known events. .\" .SS System V compatibility The Upstart @@ -157,3 +161,5 @@ .BR stopping (7) .BR stopped (7) .BR telinit (8) +.BR upstart-events (7) +.BR telinit (8) --- upstart-1.4.orig/init/man/init.5 +++ upstart-1.4/init/man/init.5 @@ -11,7 +11,6 @@ .TP .B $HOME/.init/ Default location of user job configuration files. -.RE .\" .SH DESCRIPTION On startup, the Upstart @@ -638,6 +637,9 @@ .BR none and all subsequent job output will be discarded. +If the logger detects that the file it is about to write to was deleted, +it will re-open the file first. + Care should be taken if the log directory is a mount point since any job that starts before that mount is available and which produces output will then attempt to write logs to the mount point, not to the mounted @@ -759,27 +761,31 @@ .B setuid \fIUSERNAME Changes to the user .I USERNAME -before running the job's process. +before running any job process. -If this stanza is unspecified, the job will run as root in the case of -system jobs, and as the user in the case of User Jobs. +If this stanza is unspecified, all job processes will run as root in the +case of system jobs, and as the user in the case of user jobs. -Note that System jobs using the setuid stanza are still system jobs, -and can not be controlled by an unprivileged user, even if the setuid +Note that system jobs using the +.B setuid +stanza are still system jobs, and can not be controlled by an +unprivileged user, even if the +.B setuid stanza specifies that user. .\" .TP .B setgid \fIGROUPNAME Changes to the group .I GROUPNAME -before running the job's process. +before running any job process. If this stanza is unspecified, the primary group of the user specified in the .B setuid -block is used. If both stanzas are unspecified, the job will run with -its group ID set to 0 in the case of system jobs, and as the primary -group of the user in the case of User Jobs. +block is used for all job processes. If both stanzas are unspecified, +all job processes will run with its group ID set to 0 in the case of +system jobs, and as the primary group of the user in the case of User +Jobs. .\" .SS Override File Handling Override files allow a jobs environment to be changed without modifying @@ -959,4 +965,5 @@ .BR init (8) .BR initctl (8) .BR sh (1) +.BR upstart-events (7) .BR pty (7) --- upstart-1.4.orig/init/tests/test_job_process.c +++ upstart-1.4/init/tests/test_job_process.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -146,13 +147,42 @@ TEST_PWD, TEST_ENVIRONMENT, TEST_OUTPUT, - TEST_SIGNALS + TEST_SIGNALS, + TEST_FDS }; static char *argv0; static int get_available_pty_count (void) __attribute__((unused)); + +/** + * fd_valid: + * @fd: file descriptor. + * + * Return 1 if @fd is valid, else 0. + **/ +static int +fd_valid (int fd) +{ + int flags = 0; + + if (fd < 0) + return 0; + + errno = 0; + flags = fcntl (fd, F_GETFL); + + if (flags < 0) + return 0; + + /* redundant really */ + if (errno == EBADF) + return 0; + + return 1; +} + static void child (enum child_tests test, const char *filename) @@ -162,7 +192,7 @@ char tmpname[PATH_MAX], path[PATH_MAX]; int i; char buffer[1024]; - + int ret = EXIT_SUCCESS; strcpy (tmpname, filename); strcat (tmpname, ".tmp"); @@ -220,14 +250,103 @@ fclose(in); break; + case TEST_FDS: + /* Establish list of open (valid) and closed (invalid) + * file descriptors. + * + * XXX: Note that if you attempt to run this program + * through gdb, the TEST_FDS tests will probably fail. + * This seems to be due to gdb creating/leaking atleast 1 fd. + * + * To work around this issue, either comment out all + * TEST_FDS tests to allow you to debug the _actual_ failing + * test(s), or if it is one of the TEST_FDS tests which is + * failing either use an alternative technique to debug the + * failing test(s) (such as strace(1)), or force the TEST_FDS + * tests to pass in gdb by setting the appropriate flag + * variable to indicate the test(s) passed. + */ + { + DIR *dir; + struct dirent *ent; + struct stat statbuf; + char *prefix_path = "/proc/self/fd"; + char path[PATH_MAX]; + char link[PATH_MAX]; + int saved_errno; + ssize_t len; + int valid; + + dir = opendir(prefix_path); + + if (!dir) + { + saved_errno = errno; + fprintf (out, "failed to open '%s'" + " (errno=%d [%s])", + prefix_path, + saved_errno, strerror(saved_errno)); + + ret = EXIT_FAILURE; + goto out; + } + + while ((ent=readdir(dir)) != NULL) + { + int fd; + + valid = 0; + + if ( !strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") ) + continue; + + sprintf (path, "%s/%s", prefix_path, ent->d_name); + fd = atoi (ent->d_name); + + len = readlink (path, link, sizeof (link)); + TEST_GT (len, 0); + link[len] = '\0'; + + if (fd == fileno (out)) { + /* we (have to) pretend the log file that + * we write is invisible. + */ + valid = 0; + } else if (link[0] == '/') { + sprintf (path, "/proc/%d/fd", getpid ()); + + if (stat (link, &statbuf) < 0) + valid = 0; + else if (S_ISDIR (statbuf.st_mode) && ! strcmp (path, link)) { + /* Ignore the last entry which is a link to the + * /proc/self/fd/ directory. + */ + valid = 0; + } else { + valid = 1; + } + } else { + valid = fd_valid (fd); + } + + fprintf (out, "fd %d: %svalid (link=%s)\n", + fd, + valid ? "" : "in", + link); + } + + closedir(dir); + } + break; } +out: fsync (fileno (out)); fclose (out); rename (tmpname, filename); - exit (0); + exit (ret); } /* FIXME: @@ -287,6 +406,7 @@ FILE *output; struct stat statbuf; char filename[PATH_MAX], buf[80]; + char function[PATH_MAX]; int ret = -1, status, first; siginfo_t info; char filebuf[1024]; @@ -294,13 +414,22 @@ struct group *grp; char *p; int ok; - + char buffer[1024]; + pid_t pid; TEST_FUNCTION ("job_process_run"); TEST_FILENAME (filename); program_name = "test"; + TEST_FILENAME (dirname); + TEST_EQ (mkdir (dirname, 0755), 0); + + /* Override default location to ensure job output goes to a + * writeable location + */ + TEST_EQ (setenv ("UPSTART_LOGDIR", dirname, 1), 0); + /* Check that we can run a simple command, and have the process id * and state filled in. We should be able to wait for the pid to * finish and see that it has been run as expected. @@ -976,6 +1105,274 @@ TEST_EQ (setenv ("UPSTART_LOGDIR", dirname, 1), 0); /************************************************************/ + TEST_FEATURE ("ensure sane fds with no console, no script"); + + class = job_class_new (NULL, "prism", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/prism.log", dirname), 0); + + sprintf (function, "%d", TEST_FDS); + + class->console = CONSOLE_NONE; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_sprintf ( + class->process[PROCESS_MAIN], + "%s %s %s", + argv0, function, filename); + class->process[PROCESS_MAIN]->script = FALSE; + + job = job_new (class, ""); + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_EQ (ret, 0); + + TEST_NE (job->pid[PROCESS_MAIN], 0); + + waitpid (job->pid[PROCESS_MAIN], &status, 0); + TEST_TRUE (WIFEXITED (status)); + TEST_EQ (WEXITSTATUS (status), 0); + + TEST_EQ (stat (filename, &statbuf), 0); + + output = fopen (filename, "r"); + + { + char state[32]; + int fd; + int ret; + int valid; + + while (fgets (filebuf, sizeof(filebuf), output) != NULL) { + ret = sscanf (filebuf, "fd %d: %s ", &fd, state); + TEST_EQ (ret, 2); + + if (! strcmp ("invalid", state)) + valid = 0; + else + valid = 1; + + /* 0, 1, 2 */ + if (fd < 3) { + if (! valid) + TEST_FAILED ("fd %d is unexpected invalid", fd); + } else { + if (valid) + TEST_FAILED ("fd %d is unexpected valid", fd); + } + } + } + + fclose (output); + TEST_EQ (unlink (filename), 0); + + nih_free (class); + + /************************************************************/ + TEST_FEATURE ("ensure sane fds with no console, and script"); + + class = job_class_new (NULL, "prism", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/prism.log", dirname), 0); + + sprintf (function, "%d", TEST_FDS); + + class->console = CONSOLE_NONE; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_sprintf ( + class->process[PROCESS_MAIN], + "%s %s %s", + argv0, function, filename); + class->process[PROCESS_MAIN]->script = TRUE; + + job = job_new (class, ""); + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_EQ (ret, 0); + + TEST_NE (job->pid[PROCESS_MAIN], 0); + + waitpid (job->pid[PROCESS_MAIN], &status, 0); + TEST_TRUE (WIFEXITED (status)); + TEST_EQ (WEXITSTATUS (status), 0); + + TEST_EQ (stat (filename, &statbuf), 0); + + output = fopen (filename, "r"); + + { + char state[32]; + int fd; + int ret; + int valid; + + while (fgets (filebuf, sizeof(filebuf), output) != NULL) { + ret = sscanf (filebuf, "fd %d: %s ", &fd, state); + TEST_EQ (ret, 2); + + if (! strcmp ("invalid", state)) + valid = 0; + else + valid = 1; + + /* 0, 1, 2 */ + if (fd < 3) { + if (! valid) + TEST_FAILED ("fd %d is unexpected invalid", fd); + } else { + if (valid) + TEST_FAILED ("fd %d is unexpected valid", fd); + } + } + } + + fclose (output); + TEST_EQ (unlink (filename), 0); + + nih_free (class); + + /************************************************************/ + TEST_FEATURE ("ensure sane fds with console log, no script"); + + class = job_class_new (NULL, "prism", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/prism.log", dirname), 0); + + sprintf (function, "%d", TEST_FDS); + + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_sprintf ( + class->process[PROCESS_MAIN], + "%s %s %s", + argv0, function, filename); + class->process[PROCESS_MAIN]->script = FALSE; + + job = job_new (class, ""); + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_EQ (ret, 0); + + TEST_NE (job->pid[PROCESS_MAIN], 0); + + waitpid (job->pid[PROCESS_MAIN], &status, 0); + TEST_TRUE (WIFEXITED (status)); + TEST_EQ (WEXITSTATUS (status), 0); + + TEST_EQ (stat (filename, &statbuf), 0); + + output = fopen (filename, "r"); + + { + char state[32]; + int fd; + int ret; + int valid; + + while (fgets (filebuf, sizeof(filebuf), output) != NULL) { + ret = sscanf (filebuf, "fd %d: %s ", &fd, state); + TEST_EQ (ret, 2); + + if (! strcmp ("invalid", state)) + valid = 0; + else + valid = 1; + + /* 0, 1, 2 */ + if (fd < 3) { + if (! valid) + TEST_FAILED ("fd %d is unexpected invalid", fd); + } else { + if (valid) + TEST_FAILED ("fd %d is unexpected valid", fd); + } + } + } + + fclose (output); + TEST_EQ (unlink (filename), 0); + + nih_free (class); + + /************************************************************/ + TEST_FEATURE ("ensure sane fds with console log, and script"); + + class = job_class_new (NULL, "prism", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/prism.log", dirname), 0); + + sprintf (function, "%d", TEST_FDS); + + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_sprintf ( + class->process[PROCESS_MAIN], + "%s %s %s", + argv0, function, filename); + class->process[PROCESS_MAIN]->script = TRUE; + + job = job_new (class, ""); + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_EQ (ret, 0); + + TEST_NE (job->pid[PROCESS_MAIN], 0); + + waitpid (job->pid[PROCESS_MAIN], &status, 0); + TEST_TRUE (WIFEXITED (status)); + TEST_EQ (WEXITSTATUS (status), 0); + + TEST_EQ (stat (filename, &statbuf), 0); + + output = fopen (filename, "r"); + + { + char state[32]; + int fd; + int ret; + int valid; + + while (fgets (filebuf, sizeof(filebuf), output) != NULL) { + ret = sscanf (filebuf, "fd %d: %s ", &fd, state); + TEST_EQ (ret, 2); + + if (! strcmp ("invalid", state)) + valid = 0; + else + valid = 1; + + /* 0, 1, 2 */ + if (fd < 3) { + if (! valid) + TEST_FAILED ("fd %d is unexpected invalid", fd); + } else { + if (valid) + TEST_FAILED ("fd %d is unexpected valid", fd); + } + } + } + + fclose (output); + TEST_EQ (unlink (filename), 0); + + nih_free (class); + + /************************************************************/ TEST_FEATURE ("ensure that no log file written for single-line no-output script"); class = job_class_new (NULL, "test", NULL); @@ -1081,7 +1478,8 @@ { struct timeval t; /* be generous */ - t.tv_usec = 500000; + t.tv_sec = 2; + t.tv_usec = 0; TEST_FORCE_WATCH_UPDATE_TIMEOUT (t); } @@ -1227,7 +1625,7 @@ /* wait for read from pty allowing logger to write to log file */ TEST_FORCE_WATCH_UPDATE (); - TEST_EQ (kill (job->pid[PROCESS_MAIN], SIGKILL), 0); + TEST_EQ (kill (-job->pid[PROCESS_MAIN], SIGKILL), 0); waitpid (job->pid[PROCESS_MAIN], &status, 0); TEST_TRUE (WIFSIGNALED (status)); TEST_EQ (WTERMSIG (status), SIGKILL); @@ -1274,6 +1672,8 @@ class->process[PROCESS_MAIN]->script = FALSE; job = job_new (class, ""); + TEST_NE_P (job, NULL); + job->goal = JOB_START; job->state = JOB_SPAWNED; @@ -1290,6 +1690,9 @@ TEST_TRUE (WIFSIGNALED (status)); TEST_EQ (WTERMSIG (status), SIGKILL); + /* allow destructor to write any lingering unflushed data */ + nih_free (class); + TEST_EQ (stat (filename, &statbuf), 0); TEST_TRUE (S_ISREG (statbuf.st_mode)); @@ -1330,7 +1733,6 @@ fclose (output); TEST_EQ (unlink (filename), 0); - nih_free (class); /************************************************************/ TEST_FEATURE ("with multi-line script that is killed"); @@ -1372,7 +1774,7 @@ /* XXX: call 2: wait for read from pty allowing logger to write to log file */ TEST_FORCE_WATCH_UPDATE (); - TEST_EQ (kill (job->pid[PROCESS_MAIN], SIGKILL), 0); + TEST_EQ (kill (-job->pid[PROCESS_MAIN], SIGKILL), 0); waitpid (job->pid[PROCESS_MAIN], &status, 0); TEST_TRUE (WIFSIGNALED (status)); TEST_EQ (WTERMSIG (status), SIGKILL); @@ -1430,7 +1832,7 @@ /* wait for read from pty allowing logger to write to log file */ TEST_FORCE_WATCH_UPDATE (); - TEST_EQ (kill (job->pid[PROCESS_MAIN], SIGKILL), 0); + TEST_EQ (kill (-job->pid[PROCESS_MAIN], SIGKILL), 0); waitpid (job->pid[PROCESS_MAIN], &status, 0); TEST_TRUE (WIFSIGNALED (status)); TEST_EQ (WTERMSIG (status), SIGKILL); @@ -2510,7 +2912,9 @@ output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_FILE_EQ (output, "/bin/sh: 1: /this/command/does/not/exist: not found\r\n"); + TEST_TRUE (fgets (buffer, sizeof(buffer), output)); + TEST_EQ (fnmatch ("*sh*/this/command/does/not/exist*not found*", buffer, 0), 0); + TEST_FILE_END (output); fclose (output); @@ -2518,57 +2922,518 @@ nih_free (class); /************************************************************ - * No point in running a test for: - * - * TEST_FEATURE ("with single-line command running an invalid command"); - * - * Since as such commands are exec'ed directly, there is no shell to report - * an error back - exec just fails. + * Superficially, there seems little point in running a test for + * this scenario since if Upstart attempts to exec(2) directly a + * command that does not exist, the exec simply fails (since + * there is no shell to report the error). * + * And yet -- ironically -- bug 912558 would have been prevented + * had we originally tested this scenario! ************************************************************/ + TEST_FEATURE ("with single-line command running an invalid command"); - /************************************************************/ - TEST_FEATURE ("with multi-line script running an invalid command"); - - class = job_class_new (NULL, "blah", NULL); + class = job_class_new (NULL, "buzz", NULL); TEST_NE_P (class, NULL); TEST_FILENAME (filename); - TEST_GT (sprintf (filename, "%s/blah.log", dirname), 0); + TEST_GT (sprintf (filename, "%s/buzz.log", dirname), 0); class->console = CONSOLE_LOG; class->process[PROCESS_MAIN] = process_new (class); class->process[PROCESS_MAIN]->command = nih_strdup ( class->process[PROCESS_MAIN], - "true\n/this/command/does/not/exist"); - class->process[PROCESS_MAIN]->script = TRUE; + "/this/command/does/not/exist"); + class->process[PROCESS_MAIN]->script = FALSE; + + /* Stranger things have happened at sea */ + TEST_EQ (stat (class->process[PROCESS_MAIN]->command, &statbuf), -1); + TEST_EQ (errno, ENOENT); job = job_new (class, ""); job->goal = JOB_START; job->state = JOB_SPAWNED; - ret = job_process_run (job, PROCESS_MAIN); - TEST_EQ (ret, 0); + output = tmpfile (); + TEST_NE_P (output, NULL); + TEST_DIVERT_STDERR (output) { + ret = job_process_run (job, PROCESS_MAIN); + TEST_LT (ret, 0); + } + fclose (output); - TEST_NE (job->pid[PROCESS_MAIN], 0); + /* We don't expect a logfile to be written since there is no + * accompanying shell to write the error. + */ + TEST_EQ (stat (filename, &statbuf), -1); + TEST_EQ (errno, ENOENT); - TEST_FORCE_WATCH_UPDATE (); - waitpid (job->pid[PROCESS_MAIN], &status, 0); - TEST_TRUE (WIFEXITED (status)); - TEST_NE (WEXITSTATUS (status), 0); - TEST_FORCE_WATCH_UPDATE (); + nih_free (class); - TEST_EQ (stat (filename, &statbuf), 0); + /************************************************************/ + TEST_FEATURE ("with single-line command running an invalid command, then a 1-line post-stop script"); - TEST_TRUE (S_ISREG (statbuf.st_mode)); + class = job_class_new (NULL, "asterix", NULL); + TEST_NE_P (class, NULL); - TEST_TRUE (statbuf.st_mode & S_IRUSR); - TEST_TRUE (statbuf.st_mode & S_IWUSR); - TEST_FALSE (statbuf.st_mode & S_IXUSR); + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/asterix.log", dirname), 0); - TEST_TRUE (statbuf.st_mode & S_IRGRP); - TEST_FALSE (statbuf.st_mode & S_IWGRP); - TEST_FALSE (statbuf.st_mode & S_IXGRP); + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_strdup ( + class->process[PROCESS_MAIN], + "/this/command/does/not/exist"); + class->process[PROCESS_MAIN]->script = FALSE; + + class->process[PROCESS_POST_STOP] = process_new (class); + class->process[PROCESS_POST_STOP]->command = nih_sprintf ( + class->process[PROCESS_POST_STOP], + "echo hello"); + class->process[PROCESS_POST_STOP]->script = TRUE; + + /* Stranger things have happened at sea */ + TEST_EQ (stat (class->process[PROCESS_MAIN]->command, &statbuf), -1); + TEST_EQ (errno, ENOENT); + + job = job_new (class, ""); + + output = tmpfile (); + TEST_NE_P (output, NULL); + TEST_DIVERT_STDERR (output) { + + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_LT (ret, 0); + + /* We don't expect a logfile to be written since there is no + * accompanying shell to write the error. + */ + TEST_EQ (stat (filename, &statbuf), -1); + TEST_EQ (errno, ENOENT); + + job->goal = JOB_STOP; + job->state = JOB_POST_STOP; + + ret = job_process_run (job, PROCESS_POST_STOP); + TEST_EQ (ret, 0); + + TEST_NE (job->pid[PROCESS_POST_STOP], 0); + + /* Flush the io so that the shell on the client side + * gets the data (the script to execute). + */ + TEST_FORCE_WATCH_UPDATE (); + + waitpid (job->pid[PROCESS_POST_STOP], &status, 0); + TEST_TRUE (WIFEXITED (status)); + TEST_EQ (WEXITSTATUS (status), 0); + + /* .. but the post stop should have written data */ + TEST_EQ (stat (filename, &statbuf), 0); + } + fclose (output); + + /* check file contents */ + output = fopen (filename, "r"); + TEST_FILE_EQ (output, "hello\r\n"); + TEST_FILE_END (output); + fclose (output); + + TEST_EQ (unlink (filename), 0); + + nih_free (class); + + /************************************************************/ + TEST_FEATURE ("with single-line command running an invalid command, then a 2-line post-stop script"); + + class = job_class_new (NULL, "asterix", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/asterix.log", dirname), 0); + + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_strdup ( + class->process[PROCESS_MAIN], + "/this/command/does/not/exist"); + class->process[PROCESS_MAIN]->script = FALSE; + + class->process[PROCESS_POST_STOP] = process_new (class); + class->process[PROCESS_POST_STOP]->command = nih_sprintf ( + class->process[PROCESS_POST_STOP], + "echo hello\necho world"); + class->process[PROCESS_POST_STOP]->script = TRUE; + + /* Stranger things have happened at sea */ + TEST_EQ (stat (class->process[PROCESS_MAIN]->command, &statbuf), -1); + TEST_EQ (errno, ENOENT); + + job = job_new (class, ""); + + output = tmpfile (); + TEST_NE_P (output, NULL); + TEST_DIVERT_STDERR (output) { + + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_LT (ret, 0); + + /* We don't expect a logfile to be written since there is no + * accompanying shell to write the error. + */ + TEST_EQ (stat (filename, &statbuf), -1); + TEST_EQ (errno, ENOENT); + + job->goal = JOB_STOP; + job->state = JOB_POST_STOP; + + ret = job_process_run (job, PROCESS_POST_STOP); + TEST_EQ (ret, 0); + + TEST_NE (job->pid[PROCESS_POST_STOP], 0); + + /* Flush the io so that the shell on the client side + * gets the data (the script to execute). + */ + TEST_FORCE_WATCH_UPDATE (); + + waitpid (job->pid[PROCESS_POST_STOP], &status, 0); + TEST_TRUE (WIFEXITED (status)); + TEST_EQ (WEXITSTATUS (status), 0); + + /* Allow the log to be written */ + TEST_FORCE_WATCH_UPDATE (); + + /* .. but the post stop should have written data */ + TEST_EQ (stat (filename, &statbuf), 0); + } + fclose (output); + + /* check file contents */ + output = fopen (filename, "r"); + TEST_FILE_EQ (output, "hello\r\n"); + TEST_FILE_EQ (output, "world\r\n"); + TEST_FILE_END (output); + fclose (output); + + TEST_EQ (unlink (filename), 0); + + nih_free (class); + + /************************************************************/ + TEST_FEATURE ("with single-line command running an invalid command, then a post-stop command"); + + class = job_class_new (NULL, "asterix", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/asterix.log", dirname), 0); + + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_strdup ( + class->process[PROCESS_MAIN], + "/this/command/does/not/exist"); + class->process[PROCESS_MAIN]->script = FALSE; + + class->process[PROCESS_POST_STOP] = process_new (class); + class->process[PROCESS_POST_STOP]->command = nih_sprintf ( + class->process[PROCESS_POST_STOP], + "echo hello"); + class->process[PROCESS_POST_STOP]->script = FALSE; + + /* Stranger things have happened at sea */ + TEST_EQ (stat (class->process[PROCESS_MAIN]->command, &statbuf), -1); + TEST_EQ (errno, ENOENT); + + job = job_new (class, ""); + + output = tmpfile (); + TEST_NE_P (output, NULL); + TEST_DIVERT_STDERR (output) { + + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_LT (ret, 0); + + /* We don't expect a logfile to be written since there is no + * accompanying shell to write the error. + */ + TEST_EQ (stat (filename, &statbuf), -1); + TEST_EQ (errno, ENOENT); + + job->goal = JOB_STOP; + job->state = JOB_POST_STOP; + + ret = job_process_run (job, PROCESS_POST_STOP); + TEST_EQ (ret, 0); + + TEST_NE (job->pid[PROCESS_POST_STOP], 0); + + /* Flush the io so that the shell on the client side + * gets the data (the script to execute). + */ + TEST_FORCE_WATCH_UPDATE (); + + waitpid (job->pid[PROCESS_POST_STOP], &status, 0); + TEST_TRUE (WIFEXITED (status)); + TEST_EQ (WEXITSTATUS (status), 0); + + /* .. but the post stop should have written data */ + TEST_EQ (stat (filename, &statbuf), 0); + } + fclose (output); + + /* check file contents */ + output = fopen (filename, "r"); + TEST_FILE_EQ (output, "hello\r\n"); + TEST_FILE_END (output); + fclose (output); + + TEST_EQ (unlink (filename), 0); + + nih_free (class); + + /************************************************************/ + TEST_FEATURE ("with single-line command running an invalid command, then an invalid post-stop command"); + + class = job_class_new (NULL, "asterix", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/asterix.log", dirname), 0); + + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_strdup ( + class->process[PROCESS_MAIN], + "/this/command/does/not/exist"); + class->process[PROCESS_MAIN]->script = FALSE; + + class->process[PROCESS_POST_STOP] = process_new (class); + class->process[PROCESS_POST_STOP]->command = nih_sprintf ( + class->process[PROCESS_POST_STOP], + "/this/command/does/not/exist"); + class->process[PROCESS_POST_STOP]->script = FALSE; + + /* Stranger things have happened at sea */ + TEST_EQ (stat (class->process[PROCESS_MAIN]->command, &statbuf), -1); + TEST_EQ (errno, ENOENT); + + job = job_new (class, ""); + + output = tmpfile (); + TEST_NE_P (output, NULL); + TEST_DIVERT_STDERR (output) { + + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_LT (ret, 0); + + /* We don't expect a logfile to be written since there is no + * accompanying shell to write the error. + */ + TEST_EQ (stat (filename, &statbuf), -1); + TEST_EQ (errno, ENOENT); + + job->goal = JOB_STOP; + job->state = JOB_POST_STOP; + + ret = job_process_run (job, PROCESS_POST_STOP); + TEST_LT (ret, 0); + + /* Again, no file expected */ + TEST_EQ (stat (filename, &statbuf), -1); + TEST_EQ (errno, ENOENT); + } + fclose (output); + nih_free (class); + + /************************************************************/ + TEST_FEATURE ("with single-line command running a valid command, then a 1-line invalid post-stop command"); + + class = job_class_new (NULL, "obelix", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/obelix.log", dirname), 0); + + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_sprintf ( + class->process[PROCESS_MAIN], + "%s hello world", TEST_CMD_ECHO); + class->process[PROCESS_MAIN]->script = FALSE; + + class->process[PROCESS_POST_STOP] = process_new (class); + class->process[PROCESS_POST_STOP]->command = nih_strdup ( + class->process[PROCESS_POST_STOP], + "/this/command/does/not/exist"); + class->process[PROCESS_POST_STOP]->script = FALSE; + + /* Stranger things have happened at sea */ + TEST_EQ (stat (class->process[PROCESS_POST_STOP]->command, &statbuf), -1); + TEST_EQ (errno, ENOENT); + + job = job_new (class, ""); + + output = tmpfile (); + TEST_NE_P (output, NULL); + TEST_DIVERT_STDERR (output) { + + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_EQ (ret, 0); + + TEST_NE (job->pid[PROCESS_MAIN], 0); + + waitpid (job->pid[PROCESS_MAIN], &status, 0); + TEST_TRUE (WIFEXITED (status)); + TEST_EQ (WEXITSTATUS (status), 0); + + /* Flush the io so that the shell on the client side + * gets the data (the script to execute). + */ + TEST_FORCE_WATCH_UPDATE (); + + /* Expect a log file */ + TEST_EQ (stat (filename, &statbuf), 0); + + job->goal = JOB_STOP; + job->state = JOB_POST_STOP; + + ret = job_process_run (job, PROCESS_POST_STOP); + TEST_LT (ret, 0); + + TEST_EQ (job->pid[PROCESS_POST_STOP], 0); + } + fclose (output); + + /* check file contents */ + output = fopen (filename, "r"); + TEST_FILE_EQ (output, "hello world\r\n"); + TEST_FILE_END (output); + fclose (output); + + TEST_EQ (unlink (filename), 0); + + nih_free (class); + + /************************************************************/ + TEST_FEATURE ("with multi-line script running an invalid command"); + + class = job_class_new (NULL, "blah", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/blah.log", dirname), 0); + + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_strdup ( + class->process[PROCESS_MAIN], + "true\n/this/command/does/not/exist"); + class->process[PROCESS_MAIN]->script = TRUE; + + job = job_new (class, ""); + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_EQ (ret, 0); + + TEST_NE (job->pid[PROCESS_MAIN], 0); + + TEST_FORCE_WATCH_UPDATE (); + waitpid (job->pid[PROCESS_MAIN], &status, 0); + TEST_TRUE (WIFEXITED (status)); + TEST_NE (WEXITSTATUS (status), 0); + TEST_FORCE_WATCH_UPDATE (); + + TEST_EQ (stat (filename, &statbuf), 0); + + TEST_TRUE (S_ISREG (statbuf.st_mode)); + + TEST_TRUE (statbuf.st_mode & S_IRUSR); + TEST_TRUE (statbuf.st_mode & S_IWUSR); + TEST_FALSE (statbuf.st_mode & S_IXUSR); + + TEST_TRUE (statbuf.st_mode & S_IRGRP); + TEST_FALSE (statbuf.st_mode & S_IWGRP); + TEST_FALSE (statbuf.st_mode & S_IXGRP); + + TEST_FALSE (statbuf.st_mode & S_IROTH); + TEST_FALSE (statbuf.st_mode & S_IWOTH); + TEST_FALSE (statbuf.st_mode & S_IXOTH); + + output = fopen (filename, "r"); + TEST_NE_P (output, NULL); + + TEST_TRUE (fgets (buffer, sizeof(buffer), output)); + TEST_EQ (fnmatch ("/proc/self/fd/9*/this/command/does/not/exist*not found*", buffer, 0), 0); + + TEST_FILE_END (output); + fclose (output); + + TEST_EQ (unlink (filename), 0); + nih_free (class); + + /************************************************************/ + TEST_FEATURE ("with multi-line script that writes 1 line to stdout then 1 line to stderr"); + + class = job_class_new (NULL, "blah", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/blah.log", dirname), 0); + + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_sprintf ( + class->process[PROCESS_MAIN], + "%s stdout\n%s stderr >&2\n", + TEST_CMD_ECHO, TEST_CMD_ECHO); + class->process[PROCESS_MAIN]->script = TRUE; + + job = job_new (class, ""); + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_EQ (ret, 0); + + TEST_NE (job->pid[PROCESS_MAIN], 0); + + TEST_FORCE_WATCH_UPDATE (); + + waitpid (job->pid[PROCESS_MAIN], &status, 0); + TEST_TRUE (WIFEXITED (status)); + TEST_EQ (WEXITSTATUS (status), 0); + + TEST_FORCE_WATCH_UPDATE (); + + TEST_EQ (stat (filename, &statbuf), 0); + + TEST_TRUE (S_ISREG (statbuf.st_mode)); + + TEST_TRUE (statbuf.st_mode & S_IRUSR); + TEST_TRUE (statbuf.st_mode & S_IWUSR); + TEST_FALSE (statbuf.st_mode & S_IXUSR); + + TEST_TRUE (statbuf.st_mode & S_IRGRP); + TEST_FALSE (statbuf.st_mode & S_IWGRP); + TEST_FALSE (statbuf.st_mode & S_IXGRP); TEST_FALSE (statbuf.st_mode & S_IROTH); TEST_FALSE (statbuf.st_mode & S_IWOTH); @@ -2577,7 +3442,8 @@ output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_FILE_EQ (output, "/proc/self/fd/9: 3: /this/command/does/not/exist: not found\r\n"); + TEST_FILE_EQ (output, "stdout\r\n"); + TEST_FILE_EQ (output, "stderr\r\n"); TEST_FILE_END (output); fclose (output); @@ -2585,7 +3451,7 @@ nih_free (class); /************************************************************/ - TEST_FEATURE ("with multi-line script that writes 1 line to stdout then 1 line to stderr"); + TEST_FEATURE ("with multi-line script that writes 1 line to stderr then 1 line to stdout"); class = job_class_new (NULL, "blah", NULL); TEST_NE_P (class, NULL); @@ -2597,7 +3463,7 @@ class->process[PROCESS_MAIN] = process_new (class); class->process[PROCESS_MAIN]->command = nih_sprintf ( class->process[PROCESS_MAIN], - "%s stdout\n%s stderr >&2\n", + "%s stderr >&2\n%s stdout\n", TEST_CMD_ECHO, TEST_CMD_ECHO); class->process[PROCESS_MAIN]->script = TRUE; @@ -2637,8 +3503,8 @@ output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_FILE_EQ (output, "stdout\r\n"); TEST_FILE_EQ (output, "stderr\r\n"); + TEST_FILE_EQ (output, "stdout\r\n"); TEST_FILE_END (output); fclose (output); @@ -2646,21 +3512,29 @@ nih_free (class); /************************************************************/ - TEST_FEATURE ("with multi-line script that writes 1 line to stderr then 1 line to stdout"); + /* XXX: Note that we don't force a watch update here to simulate + * a job that writes data _after_ Upstart has run nih_io_handle_fds() + * in the main loop and just _before_ it exits _in the same main + * loop iteration_. + */ + TEST_FEATURE ("with single line command writing fast and exiting"); - class = job_class_new (NULL, "blah", NULL); + class = job_class_new (NULL, "budapest", NULL); TEST_NE_P (class, NULL); TEST_FILENAME (filename); - TEST_GT (sprintf (filename, "%s/blah.log", dirname), 0); + TEST_GT (sprintf (filename, "%s/budapest.log", dirname), 0); class->console = CONSOLE_LOG; class->process[PROCESS_MAIN] = process_new (class); + /* program to run "fast", so directly exec a program with + * no shell intervention. + */ class->process[PROCESS_MAIN]->command = nih_sprintf ( class->process[PROCESS_MAIN], - "%s stderr >&2\n%s stdout\n", - TEST_CMD_ECHO, TEST_CMD_ECHO); - class->process[PROCESS_MAIN]->script = TRUE; + "%s hello\n", + TEST_CMD_ECHO); + class->process[PROCESS_MAIN]->script = FALSE; job = job_new (class, ""); job->goal = JOB_START; @@ -2669,15 +3543,8 @@ ret = job_process_run (job, PROCESS_MAIN); TEST_EQ (ret, 0); - TEST_NE (job->pid[PROCESS_MAIN], 0); - - TEST_FORCE_WATCH_UPDATE (); - - waitpid (job->pid[PROCESS_MAIN], &status, 0); - TEST_TRUE (WIFEXITED (status)); - TEST_EQ (WEXITSTATUS (status), 0); - - TEST_FORCE_WATCH_UPDATE (); + /* allow destructor to write any lingering unflushed data */ + nih_free (class); TEST_EQ (stat (filename, &statbuf), 0); @@ -2698,19 +3565,199 @@ output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_FILE_EQ (output, "stderr\r\n"); - TEST_FILE_EQ (output, "stdout\r\n"); + TEST_FILE_EQ (output, "hello\r\n"); TEST_FILE_END (output); fclose (output); TEST_EQ (unlink (filename), 0); + + /************************************************************/ + TEST_FEATURE ("with single line command writing lots of data fast and exiting"); + + class = job_class_new (NULL, "foo", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/foo.log", dirname), 0); + + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + /* program must run "fast", so directly exec with + * no shell intervention. + * + * Writes large number of nulls (3MB). + */ +#define EXPECTED_1K_BLOCKS (1024*3) +#define TEST_BLOCKSIZE 1024 + + class->process[PROCESS_MAIN]->command = nih_sprintf ( + class->process[PROCESS_MAIN], + "%s if=/dev/zero bs=%d count=%d", + TEST_CMD_DD, TEST_BLOCKSIZE, EXPECTED_1K_BLOCKS); + class->process[PROCESS_MAIN]->script = FALSE; + + NIH_MUST (nih_child_add_watch (NULL, + -1, + NIH_CHILD_ALL, + job_process_handler, + NULL)); + + job = job_new (class, ""); + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + ret = job_process_run (job, PROCESS_MAIN); + TEST_EQ (ret, 0); + + pid = job->pid[PROCESS_MAIN]; + + /* job will block until something reads the other end of the pty */ + TEST_EQ (kill (pid, 0), 0); + + { + size_t bytes; + size_t expected_bytes = TEST_BLOCKSIZE * EXPECTED_1K_BLOCKS; + off_t filesize = (off_t)-1; + + /* Check repeatedly for job log output jobs until + * we've either read the expected number of nulls, or we + * timed out. + */ + while ( 1 ) { + size_t length; + size_t i; + struct timeval t; + nih_local char *file = NULL; + + t.tv_sec = 1; + t.tv_usec = 0; + + TEST_FORCE_WATCH_UPDATE_TIMEOUT (t); + + TEST_EQ (stat (filename, &statbuf), 0); + + /* We expect the file size to change */ + if (statbuf.st_size == filesize) { + break; + } + + filesize = statbuf.st_size; + + file = nih_file_read (NULL, filename, &length); + TEST_NE_P (file, NULL); + + bytes = 0; + for (i=0; i < length; ++i) { + if (file[i] == '\0') + bytes++; + } + + if (bytes == expected_bytes) { + break; + } + } + + TEST_EQ (bytes, expected_bytes); + } + + TEST_EQ (kill (pid, 0), 0); + nih_child_poll (); + + /* The process should now be dead */ + TEST_EQ (kill (pid, 0), -1); + TEST_EQ (errno, ESRCH); + + nih_free (class); + TEST_EQ (stat (filename, &statbuf), 0); + + TEST_TRUE (S_ISREG (statbuf.st_mode)); + + TEST_TRUE (statbuf.st_mode & S_IRUSR); + TEST_TRUE (statbuf.st_mode & S_IWUSR); + TEST_FALSE (statbuf.st_mode & S_IXUSR); + + TEST_TRUE (statbuf.st_mode & S_IRGRP); + TEST_FALSE (statbuf.st_mode & S_IWGRP); + TEST_FALSE (statbuf.st_mode & S_IXGRP); + + TEST_FALSE (statbuf.st_mode & S_IROTH); + TEST_FALSE (statbuf.st_mode & S_IWOTH); + TEST_FALSE (statbuf.st_mode & S_IXOTH); + + TEST_EQ (unlink (filename), 0); + +#undef EXPECTED_1K_BLOCKS +#undef TEST_BLOCKSIZE + + /************************************************************/ + /* Applies to respawn jobs too */ + + TEST_FEATURE ("with log object freed on process exit"); + + class = job_class_new (NULL, "acorn", NULL); + TEST_NE_P (class, NULL); + + TEST_FILENAME (filename); + TEST_GT (sprintf (filename, "%s/acorn.log", dirname), 0); + + class->console = CONSOLE_LOG; + class->process[PROCESS_MAIN] = process_new (class); + class->process[PROCESS_MAIN]->command = nih_sprintf ( + class->process[PROCESS_MAIN], + "%s hello", + TEST_CMD_ECHO); + class->process[PROCESS_MAIN]->script = FALSE; + + /* XXX: Manually add the class so job_process_find() works */ + nih_hash_add (job_classes, &class->entry); + + NIH_MUST (nih_child_add_watch (NULL, + -1, + NIH_CHILD_ALL, + job_process_handler, + NULL)); + + job = job_new (class, ""); + job->goal = JOB_START; + job->state = JOB_SPAWNED; + + TEST_EQ_P (job->log, NULL); + + ret = job_process_run (job, PROCESS_MAIN); + TEST_EQ (ret, 0); + + pid = job->pid[PROCESS_MAIN]; + + job->goal = JOB_STOP; + job->state = JOB_KILLED; + + TEST_NE (job->pid[PROCESS_MAIN], 0); + + TEST_NE_P (job->log, NULL); + + TEST_FREE_TAG (job); + TEST_FREE_TAG (job->log); + + TEST_FORCE_WATCH_UPDATE (); + + nih_child_poll (); + + /* Should have been destroyed now */ + TEST_FREE (job); + TEST_FREE (job->log); + nih_free (class); + unlink (filename); + + /************************************************************/ /* Check that we can succesfully setuid and setgid to * ourselves. This should always work, privileged or * otherwise. */ TEST_FEATURE ("with setuid me"); + + TEST_NE_P (output, NULL); TEST_ALLOC_FAIL { TEST_ALLOC_SAFE { class = job_class_new (NULL, "test", NULL); @@ -2725,15 +3772,20 @@ grp = getgrgid (getgid ()); TEST_NE (grp, NULL); - class->setuid = nih_strdup (class, grp->gr_name); + class->setgid = nih_strdup (class, grp->gr_name); job = job_new (class, ""); job->goal = JOB_START; job->state = JOB_SPAWNED; + + output = tmpfile (); } - ret = job_process_run (job, PROCESS_MAIN); - TEST_EQ (ret, 0); + TEST_DIVERT_STDERR (output) { + ret = job_process_run (job, PROCESS_MAIN); + TEST_EQ (ret, 0); + } + fclose (output); TEST_NE (job->pid[PROCESS_MAIN], 0); @@ -2779,10 +3831,25 @@ NihError *err; JobProcessError *perr; int status; + struct stat statbuf; + + /* Override default location to ensure job output goes to a + * writeable location + */ + TEST_EQ (setenv ("UPSTART_LOGDIR", dirname, 1), 0); TEST_FUNCTION ("job_process_spawn"); TEST_FILENAME (filename); +#if 0 + perr = NULL; + info.si_signo = 0; + if ((int)getuid() == (int)getpid()) { + perr = NULL; + buf[0] = filebuf[0] = '\0'; + printf("%p %s %p %p" ,perr, buf, env, &info); + } +#else args[0] = argv0; args[1] = function; args[2] = filename; @@ -3036,6 +4103,37 @@ TEST_EQ (perr->errnum, ENOENT); nih_free (perr); +#endif + + /************************************************************/ + TEST_FEATURE ("with no such file, no shell and console log"); + + args[0] = "does-not-exist"; + args[1] = NULL; + + class = job_class_new (NULL, "test", NULL); + class->console = CONSOLE_LOG; + job = job_new (class, ""); + + TEST_EQ_P (job->log, NULL); + pid = job_process_spawn (job, args, NULL, FALSE, -1); + TEST_LT (pid, 0); + + /* The log should have been allocated in job_process_spawn, + * but then freed on error. + */ + TEST_EQ_P (job->log, NULL); + + err = nih_error_get (); + TEST_EQ (err->number, JOB_PROCESS_ERROR); + TEST_ALLOC_SIZE (err, sizeof (JobProcessError)); + + perr = (JobProcessError *)err; + TEST_EQ (perr->type, JOB_PROCESS_ERROR_EXEC); + TEST_EQ (perr->arg, 0); + TEST_EQ (perr->errnum, ENOENT); + nih_free (perr); + /* Check that we can spawn a job and pause it */ TEST_FEATURE ("with debug enabled"); @@ -3117,6 +4215,7 @@ nih_free (class); + /********************************************************************/ TEST_FEATURE ("ensure sane signal state with log console"); sprintf (function, "%d", TEST_SIGNALS); @@ -3159,6 +4258,114 @@ nih_free (class); + /********************************************************************/ + TEST_FEATURE ("ensure sane fds with no console"); + + sprintf (function, "%d", TEST_FDS); + + args[0] = argv0; + args[1] = function; + args[2] = filename; + args[3] = NULL; + + class = job_class_new (NULL, "test", NULL); + class->console = CONSOLE_NONE; + job = job_new (class, ""); + + pid = job_process_spawn (job, args, NULL, FALSE, -1); + TEST_GT (pid, 0); + + waitpid (pid, NULL, 0); + TEST_EQ (stat (filename, &statbuf), 0); + output = fopen (filename, "r"); + + TEST_NE_P (output, NULL); + + { + char state[32]; + int fd; + int ret; + int valid; + + while (fgets (filebuf, sizeof(filebuf), output) != NULL) { + ret = sscanf (filebuf, "fd %d: %s ", &fd, state); + TEST_EQ (ret, 2); + + if (! strcmp ("invalid", state)) + valid = 0; + else + valid = 1; + + /* 0, 1, 2 */ + if (fd < 3) { + if (! valid) + TEST_FAILED ("fd %d is unexpected invalid", fd); + } else { + if (valid) + TEST_FAILED ("fd %d is unexpected valid", fd); + } + } + } + + fclose (output); + unlink (filename); + + nih_free (class); + + /********************************************************************/ + TEST_FEATURE ("ensure sane fds with console log"); + + sprintf (function, "%d", TEST_FDS); + + args[0] = argv0; + args[1] = function; + args[2] = filename; + args[3] = NULL; + + class = job_class_new (NULL, "test", NULL); + class->console = CONSOLE_LOG; + job = job_new (class, ""); + + pid = job_process_spawn (job, args, NULL, FALSE, -1); + TEST_GT (pid, 0); + + waitpid (pid, NULL, 0); + TEST_EQ (stat (filename, &statbuf), 0); + output = fopen (filename, "r"); + + TEST_NE_P (output, NULL); + + { + char state[32]; + int fd; + int ret; + int valid; + + while (fgets (filebuf, sizeof(filebuf), output) != NULL) { + ret = sscanf (filebuf, "fd %d: %s ", &fd, state); + TEST_EQ (ret, 2); + + if (! strcmp ("invalid", state)) + valid = 0; + else + valid = 1; + + /* 0, 1, 2 */ + if (fd < 3) { + if (! valid) + TEST_FAILED ("fd %d is unexpected invalid", fd); + } else { + if (valid) + TEST_FAILED ("fd %d is unexpected valid", fd); + } + } + } + + fclose (output); + unlink (filename); + + nih_free (class); + /************************************************************/ TEST_FEATURE ("simple test"); @@ -3200,6 +4407,8 @@ TEST_LT (pid, 0); } else { TEST_GT (pid, 0); + TEST_EQ (unlink (script), 0); + unlink (filename); } } @@ -3308,7 +4517,7 @@ TEST_EQ (fclose (output), 0); - unlink (filename); + TEST_EQ (unlink (filename), 0); TEST_EQ (rmdir (dirname), 0); TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0); @@ -3402,6 +4611,9 @@ TEST_FORCE_WATCH_UPDATE (); + /* This will eventually call the log destructor */ + nih_free (class); + output = fopen (filename, "r"); TEST_NE_P (output, NULL); @@ -3416,8 +4628,6 @@ TEST_EQ (rmdir (dirname), 0); TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0); - nih_free (job); - /************************************************************/ TEST_FEATURE ("read data from daemon process"); @@ -3449,10 +4659,14 @@ pid = job_process_spawn (job, args, NULL, FALSE, -1); TEST_GT (pid, 0); + TEST_FORCE_WATCH_UPDATE (); + TEST_EQ (waitpid (pid, &status, 0), pid); TEST_TRUE (WIFEXITED (status)); + TEST_EQ (WEXITSTATUS (status), 0); - TEST_FORCE_WATCH_UPDATE (); + /* This will eventually call the log destructor */ + nih_free (class); output = fopen (filename, "r"); TEST_NE_P (output, NULL); @@ -3468,9 +4682,6 @@ TEST_EQ (rmdir (dirname), 0); TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0); - nih_free (job); - - /* FIXME */ #if 0 /************************************************************/ @@ -3531,14 +4742,13 @@ TEST_EQ (err->number, ENOMEM); nih_free (err); } - nih_free (job); + nih_free (class); } #else /* FIXME */ TEST_FEATURE ("WARNING: FIXME: test 'when no free ptys' disabled due to kernel bug"); #endif - nih_free (class); TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0); } --- upstart-1.4.orig/init/tests/test_log.c +++ upstart-1.4/init/tests/test_log.c @@ -91,7 +91,6 @@ test_log_new (void) { Log *log; - int fds[2] = { -1, -1 }; char path[] = "/foo"; char str[] = "hello, world!"; char str2[] = "The end?"; @@ -104,6 +103,8 @@ FILE *output; mode_t old_perms; off_t old_size; + int pty_master; + int pty_slave; TEST_FUNCTION ("log_new"); @@ -130,8 +131,8 @@ TEST_FEATURE ("object checks with uid 0"); TEST_ALLOC_FAIL { - TEST_EQ (pipe (fds), 0); - log = log_new (NULL, path, fds[0], 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); + log = log_new (NULL, path, pty_master, 0); /* Handle all alloc failures where the alloc calls were * initiated by log_new(). @@ -140,8 +141,8 @@ (test_alloc_failed <= LOG_NEW_ALLOC_CALLS)) { TEST_EQ_P (log, NULL); - close (fds[0]); - close (fds[1]); + close (pty_master); + close (pty_slave); continue; } @@ -153,11 +154,11 @@ TEST_ALLOC_PARENT (log->path, log); TEST_EQ_STR (log->path, path); - TEST_EQ (log->io->watch->fd, fds[0]); + TEST_EQ (log->io->watch->fd, pty_master); TEST_EQ (log->uid, 0); TEST_LT (log->fd, 0); - close (fds[1]); + close (pty_slave); /* frees fds[0] */ nih_free (log); @@ -168,13 +169,13 @@ /* XXX: No support for logging of user job output currently */ TEST_FEATURE ("ensure logging disallowed for uid >0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); - log = log_new (NULL, path, fds[0], 1); + log = log_new (NULL, path, pty_master, 1); TEST_EQ (log, NULL); - close (fds[0]); - close (fds[1]); + close (pty_master); + close (pty_slave); /************************************************************/ TEST_FEATURE ("parent check"); @@ -185,14 +186,15 @@ string = NIH_MUST (nih_strdup (NULL, str)); } - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); - log = log_new (string, path, fds[0], 0); + log = log_new (string, path, pty_master, 0); - if (test_alloc_failed) { + if (test_alloc_failed && + (test_alloc_failed <= LOG_NEW_ALLOC_CALLS)) { TEST_EQ_P (log, NULL); - close (fds[0]); - close (fds[1]); + close (pty_master); + close (pty_slave); nih_free (string); continue; } @@ -201,7 +203,7 @@ TEST_ALLOC_PARENT (log, string); TEST_FREE_TAG (log); - close (fds[1]); + close (pty_slave); /* Freeing the parent should free the child */ nih_free (string); @@ -215,9 +217,9 @@ TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); TEST_LT (stat (filename, &statbuf), 0); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); /* First time through at this point only log_new() has been called. * But by the end of the first loop, log_io_reader() will have @@ -232,16 +234,16 @@ if (test_alloc_failed && (test_alloc_failed <= LOG_NEW_ALLOC_CALLS)) { TEST_EQ_P (log, NULL); - close (fds[0]); - close (fds[1]); + close (pty_master); + close (pty_slave); continue; } TEST_NE_P (log, NULL); - ret = write (fds[1], str, strlen (str)); + ret = write (pty_slave, str, strlen (str)); TEST_GT (ret, 0); - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); TEST_FORCE_WATCH_UPDATE (); @@ -251,12 +253,13 @@ */ if (test_alloc_failed == 1+LOG_NEW_ALLOC_CALLS) { TEST_NE_P (log, NULL); - close (fds[1]); + close (pty_slave); nih_free (log); + TEST_EQ (unlink (filename), 0); continue; } - close (fds[1]); + close (pty_slave); nih_free (log); TEST_EQ (stat (filename, &statbuf), 0); @@ -277,7 +280,7 @@ output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_FILE_EQ (output, "hello, world!\n"); + TEST_FILE_EQ (output, "hello, world!\r\n"); TEST_FILE_END (output); fclose (output); @@ -287,17 +290,17 @@ /************************************************************/ TEST_FEATURE ("same logger appending to file with uid 0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); TEST_LT (stat (filename, &statbuf), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); - ret = write (fds[1], str, strlen (str)); + ret = write (pty_slave, str, strlen (str)); TEST_GT (ret, 0); - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); TEST_FORCE_WATCH_UPDATE (); @@ -321,11 +324,11 @@ output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_FILE_EQ (output, "hello, world!\n"); + TEST_FILE_EQ (output, "hello, world!\r\n"); TEST_FILE_END (output); fclose (output); - ret = write (fds[1], str2, strlen (str2)); + ret = write (pty_slave, str2, strlen (str2)); TEST_GT (ret, 0); TEST_FORCE_WATCH_UPDATE (); @@ -351,31 +354,32 @@ output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_FILE_EQ (output, "hello, world!\n"); + TEST_FILE_EQ (output, "hello, world!\r\n"); TEST_FILE_EQ (output, str2); TEST_FILE_END (output); fclose (output); TEST_EQ (unlink (filename), 0); - close (fds[1]); + close (pty_slave); nih_free (log); /************************************************************/ TEST_FEATURE ("different logger appending to file with uid 0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); bytes = 0; - ret = write (fds[1], str, strlen (str)); + ret = write (pty_slave, str, strlen (str)); TEST_GT (ret, 0); bytes += ret; - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); - bytes += ret; + /* XXX: '+1' for '\r' */ + bytes += (ret+1); TEST_FORCE_WATCH_UPDATE (); @@ -400,16 +404,16 @@ output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_FILE_EQ (output, "hello, world!\n"); + TEST_FILE_EQ (output, "hello, world!\r\n"); TEST_FILE_END (output); fclose (output); - close (fds[1]); + close (pty_slave); nih_free (log); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); TEST_EQ (stat (filename, &statbuf), 0); @@ -430,16 +434,17 @@ TEST_EQ (statbuf.st_size, old_size); bytes = 0; - ret = write (fds[1], str2, strlen (str2)); + ret = write (pty_slave, str2, strlen (str2)); TEST_GT (ret, 0); bytes += ret; - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); - bytes += ret; + /* '+1' for '\r' */ + bytes += (1+ret); TEST_FORCE_WATCH_UPDATE (); - close (fds[1]); + close (pty_slave); nih_free (log); TEST_EQ (stat (filename, &statbuf), 0); @@ -462,8 +467,8 @@ output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_FILE_EQ (output, "hello, world!\n"); - TEST_FILE_EQ (output, "The end?\n"); + TEST_FILE_EQ (output, "hello, world!\r\n"); + TEST_FILE_EQ (output, "The end?\r\n"); TEST_FILE_END (output); fclose (output); @@ -472,15 +477,15 @@ /************************************************************/ TEST_FEATURE ("ensure logging resumes when file made accessible with uid 0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); - ret = write (fds[1], str, strlen (str)); + ret = write (pty_slave, str, strlen (str)); TEST_GT (ret, 0); - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); TEST_FORCE_WATCH_UPDATE (); @@ -508,7 +513,7 @@ output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_FILE_EQ (output, "hello, world!\n"); + TEST_FILE_EQ (output, "hello, world!\r\n"); TEST_FILE_END (output); fclose (output); @@ -516,9 +521,9 @@ TEST_EQ (chmod (filename, 0x0), 0); /* Send more data to logger */ - ret = write (fds[1], str2, strlen (str2)); + ret = write (pty_slave, str2, strlen (str2)); TEST_GT (ret, 0); - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); /* File shouldn't have changed */ @@ -531,12 +536,12 @@ /* Further data should cause previous data that could not be * written to be flushed to the file. */ - ret = write (fds[1], "foo\n", 4); + ret = write (pty_slave, "foo\n", 4); TEST_EQ (ret, 4); TEST_FORCE_WATCH_UPDATE (); - close (fds[1]); + close (pty_slave); nih_free (log); TEST_EQ (stat (filename, &statbuf), 0); @@ -559,9 +564,9 @@ TEST_NE_P (output, NULL); /* Re-check entire file contents */ - TEST_FILE_EQ (output, "hello, world!\n"); - TEST_FILE_EQ (output, "The end?\n"); - TEST_FILE_EQ (output, "foo\n"); + TEST_FILE_EQ (output, "hello, world!\r\n"); + TEST_FILE_EQ (output, "The end?\r\n"); + TEST_FILE_EQ (output, "foo\r\n"); TEST_FILE_END (output); fclose (output); @@ -570,18 +575,18 @@ /************************************************************/ TEST_FEATURE ("ensure logger flushes when destroyed with uid 0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); TEST_EQ (rmdir (dirname), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); - ret = write (fds[1], str, strlen (str)); + ret = write (pty_slave, str, strlen (str)); TEST_GT (ret, 0); - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); TEST_FORCE_WATCH_UPDATE (); @@ -591,7 +596,7 @@ umask (old_perms); /* No more data sent to ensure logger writes it on log destroy */ - close (fds[1]); + close (pty_slave); nih_free (log); output = fopen (filename, "r"); @@ -611,7 +616,7 @@ TEST_FALSE (statbuf.st_mode & S_IROTH); TEST_FALSE (statbuf.st_mode & S_IWOTH); TEST_FALSE (statbuf.st_mode & S_IXOTH); - TEST_FILE_EQ (output, "hello, world!\n"); + TEST_FILE_EQ (output, "hello, world!\r\n"); TEST_FILE_END (output); fclose (output); @@ -620,35 +625,31 @@ /************************************************************/ TEST_FEATURE ("ensure log written when directory created accessible with uid 0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); TEST_EQ (rmdir (dirname), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); - ret = write (fds[1], str, strlen (str)); + ret = write (pty_slave, str, strlen (str)); TEST_GT (ret, 0); - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); - TEST_FORCE_WATCH_UPDATE (); - old_perms = umask (0); TEST_EQ (mkdir (dirname, 0755), 0); umask (old_perms); /* Send more data */ - ret = write (fds[1], str2, strlen (str2)); + ret = write (pty_slave, str2, strlen (str2)); TEST_GT (ret, 0); - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); - TEST_FORCE_WATCH_UPDATE (); - - close (fds[1]); + close (pty_slave); nih_free (log); output = fopen (filename, "r"); @@ -668,8 +669,8 @@ TEST_FALSE (statbuf.st_mode & S_IROTH); TEST_FALSE (statbuf.st_mode & S_IWOTH); TEST_FALSE (statbuf.st_mode & S_IXOTH); - TEST_FILE_EQ (output, "hello, world!\n"); - TEST_FILE_EQ (output, "The end?\n"); + TEST_FILE_EQ (output, "hello, world!\r\n"); + TEST_FILE_EQ (output, "The end?\r\n"); TEST_FILE_END (output); fclose (output); @@ -678,16 +679,16 @@ /************************************************************/ TEST_FEATURE ("ensure remainder of log written when file deleted with uid 0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); - ret = write (fds[1], str, strlen (str)); + ret = write (pty_slave, str, strlen (str)); TEST_GT (ret, 0); - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); TEST_FORCE_WATCH_UPDATE (); @@ -711,7 +712,7 @@ TEST_FALSE (statbuf.st_mode & S_IXOTH); TEST_EQ (fstat (log->fd, &statbuf), 0); - TEST_FILE_EQ (output, "hello, world!\n"); + TEST_FILE_EQ (output, "hello, world!\r\n"); TEST_FILE_END (output); fclose (output); @@ -720,9 +721,9 @@ TEST_EQ (fstat (log->fd, &statbuf), 0); /* Send more data */ - ret = write (fds[1], str2, strlen (str2)); + ret = write (pty_slave, str2, strlen (str2)); TEST_GT (ret, 0); - ret = write (fds[1], "\n", 1); + ret = write (pty_slave, "\n", 1); TEST_EQ (ret, 1); TEST_FORCE_WATCH_UPDATE (); @@ -744,31 +745,31 @@ TEST_FALSE (statbuf.st_mode & S_IROTH); TEST_FALSE (statbuf.st_mode & S_IWOTH); TEST_FALSE (statbuf.st_mode & S_IXOTH); - TEST_FILE_EQ (output, "The end?\n"); + TEST_FILE_EQ (output, "The end?\r\n"); TEST_FILE_END (output); fclose (output); - close (fds[1]); + close (pty_slave); nih_free (log); TEST_EQ (unlink (filename), 0); /************************************************************/ TEST_FEATURE ("writing 1 null with uid 0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); TEST_LT (stat (filename, &statbuf), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); - ret = write (fds[1], "\000", 1); + ret = write (pty_slave, "\000", 1); TEST_EQ (ret, 1); TEST_FORCE_WATCH_UPDATE (); - close (fds[1]); + close (pty_slave); nih_free (log); TEST_EQ (stat (filename, &statbuf), 0); @@ -799,20 +800,20 @@ /************************************************************/ TEST_FEATURE ("writing >1 null with uid 0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); TEST_LT (stat (filename, &statbuf), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); - ret = write (fds[1], "\000\000\000", 3); + ret = write (pty_slave, "\000\000\000", 3); TEST_EQ (ret, 3); TEST_FORCE_WATCH_UPDATE (); - close (fds[1]); + close (pty_slave); nih_free (log); TEST_EQ (stat (filename, &statbuf), 0); @@ -843,20 +844,20 @@ /************************************************************/ TEST_FEATURE ("writing 1 non-printable only with uid 0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); TEST_LT (stat (filename, &statbuf), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); - ret = write (fds[1], " ", 1); + ret = write (pty_slave, " ", 1); TEST_EQ (ret, 1); TEST_FORCE_WATCH_UPDATE (); - close (fds[1]); + close (pty_slave); nih_free (log); TEST_EQ (stat (filename, &statbuf), 0); @@ -887,20 +888,20 @@ /************************************************************/ TEST_FEATURE ("writing >1 non-printable only with uid 0"); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); TEST_GT (sprintf (filename, "%s/test.log", dirname), 0); TEST_LT (stat (filename, &statbuf), 0); - log = log_new (NULL, filename, fds[0], 0); + log = log_new (NULL, filename, pty_master, 0); TEST_NE_P (log, NULL); - ret = write (fds[1], "\n \t", 3); + ret = write (pty_slave, "\n \t", 3); TEST_EQ (ret, 3); TEST_FORCE_WATCH_UPDATE (); - close (fds[1]); + close (pty_slave); nih_free (log); TEST_EQ (stat (filename, &statbuf), 0); @@ -917,15 +918,18 @@ TEST_FALSE (statbuf.st_mode & S_IROTH); TEST_FALSE (statbuf.st_mode & S_IWOTH); TEST_FALSE (statbuf.st_mode & S_IXOTH); - TEST_EQ (statbuf.st_size, 3); + + /* '\r', '\n', ' ', '\t' */ + TEST_EQ (statbuf.st_size, 4); output = fopen (filename, "r"); TEST_NE_P (output, NULL); - TEST_EQ (fread (buffer, 1, 3, output), 3); - TEST_EQ (buffer[0], '\n'); - TEST_EQ (buffer[1], ' '); - TEST_EQ (buffer[2], '\t'); + TEST_EQ (fread (buffer, 1, 4, output), 4); + TEST_EQ (buffer[0], '\r'); + TEST_EQ (buffer[1], '\n'); + TEST_EQ (buffer[2], ' '); + TEST_EQ (buffer[3], '\t'); TEST_FILE_END (output); fclose (output); @@ -949,13 +953,17 @@ memset (long_path+len, 'J', sizeof(long_path)-len-1); - TEST_EQ (pipe (fds), 0); + nih_debug("long_path='%s'", long_path); - log = log_new (NULL, long_path, fds[0], 0); + pty_master = -1; pty_slave = -1; + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); + + log = log_new (NULL, long_path, pty_master, 0); TEST_NE_P (log, NULL); + close (pty_slave); + pty_slave = -1; nih_free (log); - close (fds[1]); } /************************************************************/ @@ -975,12 +983,13 @@ memset (illegal_path+len, 'z', sizeof(illegal_path)-len-1); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); - log = log_new (NULL, illegal_path, fds[0], 0); + log = log_new (NULL, illegal_path, pty_master, 0); TEST_EQ_P (log, NULL); - close (fds[1]); + close (pty_master); + close (pty_slave); } /************************************************************/ @@ -997,13 +1006,13 @@ memset (long_path+len, 'J', sizeof(long_path)-len-1); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); - log = log_new (NULL, long_path, fds[0], 0); + log = log_new (NULL, long_path, pty_master, 0); TEST_NE_P (log, NULL); + close (pty_slave); nih_free (log); - close (fds[1]); } /************************************************************/ @@ -1020,12 +1029,13 @@ memset (illegal_path+len, 'z', sizeof(illegal_path)-len-1); - TEST_EQ (pipe (fds), 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); - log = log_new (NULL, illegal_path, fds[0], 0); + log = log_new (NULL, illegal_path, pty_master, 0); TEST_EQ_P (log, NULL); - close (fds[1]); + close (pty_master); + close (pty_slave); } /************************************************************/ @@ -1035,48 +1045,49 @@ TEST_EQ (unsetenv ("UPSTART_LOGDIR"), 0); } - void +void test_log_destroy (void) { Log *log; - int fd; int ret; int flags; - int fds[2]; char str[] = "hello, world!"; + int pty_master; + int pty_slave; + int found_fd; TEST_FUNCTION ("log_destroy"); /************************************************************/ TEST_FEATURE ("ensure log fd closed with uid 0"); - fd = dup (2); - TEST_GT (fd, 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); - flags = fcntl (fd, F_GETFL); + flags = fcntl (pty_master, F_GETFL); TEST_NE (flags, -1); - log = log_new (NULL, "/foo", fd, 0); + log = log_new (NULL, "/foo", pty_master, 0); TEST_NE_P (log, NULL); + close (pty_slave); nih_free (log); - flags = fcntl (fd, F_GETFL); + flags = fcntl (pty_master, F_GETFL); TEST_EQ (flags, -1); TEST_EQ (errno, EBADF); /************************************************************/ TEST_FEATURE ("ensure path and io elements freed with uid 0"); - fd = dup (2); - TEST_GT (fd, 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); - log = log_new (NULL, "/bar", fd, 0); + log = log_new (NULL, "/bar", pty_master, 0); TEST_NE_P (log, NULL); TEST_FREE_TAG (log->path); TEST_FREE_TAG (log->io); + close (pty_slave); nih_free (log); TEST_FREE (log->path); @@ -1085,13 +1096,12 @@ /************************************************************/ TEST_FEATURE ("ensure unflushed data freed with uid 0"); - TEST_EQ (pipe (fds), 0); - TEST_GT (fd, 0); + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); - log = log_new (NULL, "/bar", fds[0], 0); + log = log_new (NULL, "/bar", pty_master, 0); TEST_NE_P (log, NULL); - ret = write (fds[1], str, strlen (str)); + ret = write (pty_slave, str, strlen (str)); TEST_GT (ret, 0); TEST_FORCE_WATCH_UPDATE (); @@ -1100,8 +1110,57 @@ TEST_EQ_STR (log->unflushed->buf, str); TEST_FREE_TAG (log->unflushed); + close (pty_slave); nih_free (log); TEST_FREE (log->unflushed); + + /************************************************************/ + TEST_FEATURE ("ensure watch freed when log destroyed"); + + TEST_EQ (openpty (&pty_master, &pty_slave, NULL, NULL, NULL), 0); + + log = log_new (NULL, "/bar", pty_master, 0); + TEST_NE_P (log, NULL); + + found_fd = 0; + NIH_LIST_FOREACH (nih_io_watches, iter) { + NihIoWatch *watch = (NihIoWatch *)iter; + if (watch->fd == pty_master) { + found_fd = pty_master; + break; + } + } + + /* fd passed to log_new() should have resulted in a watch being + * created. + */ + TEST_EQ (found_fd, pty_master); + + ret = write (pty_slave, str, strlen (str)); + TEST_GT (ret, 0); + + TEST_FORCE_WATCH_UPDATE (); + TEST_NE_P (log->unflushed, NULL); + TEST_EQ (log->unflushed->len, strlen(str)); + TEST_EQ_STR (log->unflushed->buf, str); + TEST_FREE_TAG (log->unflushed); + + close (pty_slave); + nih_free (log); + + found_fd = 0; + NIH_LIST_FOREACH (nih_io_watches, iter) { + NihIoWatch *watch = (NihIoWatch *)iter; + if (watch->fd == pty_master) { + found_fd = pty_master; + break; + } + } + + /* Freeing the log object should have removed the watch */ + TEST_EQ (found_fd, 0); + + TEST_FREE (log->unflushed); } --- upstart-1.4.orig/conf/rc.conf +++ upstart-1.4/conf/rc.conf @@ -6,13 +6,18 @@ description "System V runlevel compatibility" author "Scott James Remnant " +emits deconfiguring-networking +emits unmounted-remote-filesystems + start on runlevel [0123456] stop on runlevel [!$RUNLEVEL] export RUNLEVEL export PREVLEVEL +console output +env INIT_VERBOSE + task -console output exec /etc/init.d/rc $RUNLEVEL --- upstart-1.4.orig/conf/rc-sysinit.conf +++ upstart-1.4/conf/rc-sysinit.conf @@ -6,7 +6,7 @@ description "System V initialisation compatibility" author "Scott James Remnant " -start on startup +start on (filesystem and static-network-up) or failsafe-boot stop on runlevel # Default runlevel, this may be overriden on the kernel command-line @@ -23,9 +23,11 @@ env RUNLEVEL= env PREVLEVEL= +console output +env INIT_VERBOSE + task -console owner script # Check for default runlevel in /etc/inittab if [ -r /etc/inittab ] --- upstart-1.4.orig/debian/rules +++ upstart-1.4/debian/rules @@ -0,0 +1,26 @@ +#!/usr/bin/make -f +%: + dh --with bash-completion $@ + + +CFLAGS = -Wall -g -fstack-protector -fPIE +LDFLAGS = -Wl,--as-needed -Wl,-z,relro -Wl,-z,now -pie + +# Disable optimisations if noopt found in $DEB_BUILD_OPTIONS +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 + LDFLAGS += -Wl,-O0 +else + CFLAGS += -Os + LDFLAGS += -Wl,-O1 +endif + +override_dh_auto_configure: + dh_auto_configure -- CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + --exec-prefix= + +override_dh_fixperms: + dh_fixperms + chmod 755 debian/upstart/lib/init/upstart-job + chmod 755 debian/upstart/lib/init/apparmor-profile-load + chmod 755 debian/upstart/usr/lib/upstart/migrate-inittab.pl --- upstart-1.4.orig/debian/upstart.docs +++ upstart-1.4/debian/upstart.docs @@ -0,0 +1,2 @@ +AUTHORS +NEWS --- upstart-1.4.orig/debian/changelog +++ upstart-1.4/debian/changelog @@ -0,0 +1,1079 @@ +upstart (1.4-0ubuntu5) precise; urgency=low + + * Merge of important upstream log fixes to handle scenario attempts + are made to exec(3) directly non-existent commands. (LP: #922754) + + -- James Hunt Fri, 03 Feb 2012 14:16:40 +0000 + +upstart (1.4-0ubuntu4) precise; urgency=low + + * Disable job logging by default again due to a reported regression with + plymouth-upstart-bridge and upstart itself taking 100% CPU. Use --log + at boot time again for testing. + + -- Steve Langasek Fri, 27 Jan 2012 11:04:05 -0800 + +upstart (1.4-0ubuntu3) precise; urgency=low + + [ 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. + + -- James Hunt Thu, 26 Jan 2012 15:13:25 +0000 + +upstart (1.4-0ubuntu2) precise; urgency=low + + * init/main.c: Temporarily disable default job logging whilst + investigating bug 912558 (can be re-enabled with + _temporary_ '--log' option). + + -- James Hunt Fri, 06 Jan 2012 16:11:23 +0000 + +upstart (1.4-0ubuntu1) precise; urgency=low + + * New upstream 1.4 release. + * debian/upstart.dirs: Create /var/log/upstart to hold job logs. + * debian/upstart.logrotate: Logrotate script for job logs. + * po/en@boldquot.gmo, po/en@quot.gmo: Overwrite with upstream versions to + keep bzr happy. This makes the files technically "wrong" (since the + Ubuntu Upstart code has additional messages not in upstream), but they get + re-genrated on build so it all works out. + * debian/manpages/upstart-events.7: Add missing 'static-network-up' + event (LP: #889047). + * debian/manpages/upstart-events.7: Add missing + 'deconfiguring-networking' event (LP: #904175). + * Upstream merge for minor test fixes. + * Upstream merge for review fixes. + * Merge of upstream fix for running tests in environment not + supporting full POSIX SIGCHLD semantics. + + -- James Hunt Thu, 22 Dec 2011 16:51:43 +0000 + +upstart (1.3-0ubuntu11) oneiric-proposed; urgency=low + + * debian/conf/failsafe.conf: stop on static-network-up, so that a slow + runlevel switch doesn't leave a confusing message on screen about + starting up without networking. LP: #881079. + + -- Steve Langasek Wed, 26 Oct 2011 12:05:47 -0700 + +upstart (1.3-0ubuntu10) oneiric; urgency=low + + [ Clint Byrum ] + * debian/conf/failsafe.conf: do delay as script, not pre-start, so + that the messages/delays are stopped on runlevel immediately. emit + as an event, failsafe-boot, to provide finer grained control. + (LP: #863864) + * conf/rc-sysinit.conf: change from 'started failsafe' to 'failsafe-boot' + to accomodate changes to failsafe.conf. + + [ Steve Langasek ] + * Document device-not-ready in upstart-events(7). LP: #805510. + * Document desktop-shutdown in upstart-events(7). LP: #854329. + * Mention lightdm in the list of example DMs in upstart-events(7), since + it's now the default... + + -- Steve Langasek Mon, 03 Oct 2011 18:37:04 -0700 + +upstart (1.3-0ubuntu9) oneiric; urgency=low + + * Update rcS.conf to only start single user mode but not friendly-recovery. + (LP: #575469) + * Mark upstart as breaking friendly-recovery << 0.2.13 + + -- Stéphane Graber Thu, 15 Sep 2011 16:04:17 -0400 + +upstart (1.3-0ubuntu8) oneiric; urgency=low + + * debian/conf/failsafe.conf: Add plymouth messages to help users + understand why the system boot takes longer when they have + static network interfaces defined. (LP: #847782) + + -- Clint Byrum Wed, 14 Sep 2011 18:53:10 -0700 + +upstart (1.3-0ubuntu7) oneiric; urgency=low + + * debian/conf/failsafe.conf: raise timeout to 120 seconds to + allow for very slow DHCP interfaces to come up on servers. + (LP: #839595) + + -- Clint Byrum Sun, 04 Sep 2011 09:29:27 -0700 + +upstart (1.3-0ubuntu6) oneiric; urgency=low + + [ Steve Langasek ] + * Fix maintainer field to be compliant with policy definition + + [ Clint Byrum ] + * conf/rc.conf: document events that are emitted by sysvinit + jobs to quiet 'initctl check-config' + * extra/conf/upstart-udev-bridge.conf: narrow definition to + only the events actually emitted. (LP: #819928) + * debian/conf/failsafe.conf: new job for critical services to + start on. + * conf/rc-sysinit.conf: start after static-network-up or failsafe + so that runlevel 2 is only entered with all static net interfaces + up. (LP: #580319) + + -- Clint Byrum Wed, 10 Aug 2011 08:44:43 -0500 + +upstart (1.3-0ubuntu5) oneiric; urgency=low + + * Upstream cherry-pick for user session fixes + ("bzr merge -r 1318..1324 lp:upstart"). + + -- James Hunt Mon, 25 Jul 2011 17:09:47 +0100 + +upstart (1.3-0ubuntu4) oneiric; urgency=low + + * init/main.c: main(): Add support for "/run" directory with fallback to + old location for initramfs state passing (LP: #810956). + * Upstream cherry-pick to get dbus fix ("bzr merge -r 1314.. lp:upstart"). + + -- James Hunt Wed, 20 Jul 2011 14:51:04 +0100 + +upstart (1.3-0ubuntu3) oneiric; urgency=low + + * init/paths.h: Syned with upstream since Ubuntu file appears to have + diff applied twice, causing redefines. + + -- James Hunt Fri, 17 Jun 2011 09:59:49 +0100 + +upstart (1.3-0ubuntu2) oneiric; urgency=low + + * init/Makefile.am: Sync up from upstream, dropping TEST define which is + no longer required now we set UPSTART_NO_SESSIONS for init/tests/*.c + and specify "--no-sessions" for util/tests/*.c. + + -- James Hunt Fri, 17 Jun 2011 09:47:36 +0100 + +upstart (1.3-0ubuntu1) oneiric; urgency=low + + * Merge of Upstart 1.3 (lp:upstart @ 1.3 release tag). + + -- James Hunt Thu, 16 Jun 2011 15:09:41 +0100 + +upstart (0.9.7-3) oneiric; urgency=low + + * Merge of upstream lp:~upstart-devel/upstart/0.9: Updates for + init-checkconf. + + -- James Hunt Wed, 27 Apr 2011 15:19:47 +0100 + +upstart (0.9.7-2) oneiric; urgency=low + + * Add wait-for-state generic "wait job". + + -- Clint Byrum Wed, 04 May 2011 08:32:04 -0700 + +upstart (0.9.7-1) natty; urgency=low + + * New upstream release 0.9.7: Important session fix (LP: #767053). + + -- James Hunt Wed, 20 Apr 2011 17:44:41 +0100 + +upstart (0.9.6-1ubuntu1) natty; urgency=low + + [ James Hunt ] + * init/man/init.5: Remove mention of user jobs since facility is + disabled. + + [ Clint Byrum ] + * Noting bugs fixed by 0.9.6 release of upstart: (LP: #728531 , LP: #766206) + + -- Clint Byrum Tue, 19 Apr 2011 13:16:46 -0700 + +upstart (0.9.6-1) natty; urgency=low + + * New upstream release 0.9.6: Important session+chroot fixes. + + -- James Hunt Fri, 15 Apr 2011 15:36:40 +0100 + +upstart (0.9.5-1ubuntu1) natty; urgency=low + + [ Clint Byrum ] + * debian/upstart-job: change behavior to reload job configuration on + restart, which more closely matches expected sysvinit script + behavior. (LP: #707479) + + -- James Hunt Wed, 06 Apr 2011 17:50:53 +0100 + +upstart (0.9.5-1) natty; urgency=low + + * New upstream release 0.9.5. + + -- James Hunt Wed, 06 Apr 2011 17:45:38 +0100 + +upstart (0.9.4-1ubuntu1) natty; urgency=low + + * debian/manpages/upstart-events.7: + - Corrected reference to Upstart man page (actually init). + - Changed to using proper troff quotes. + - Escaped dashes in event names. + - Updated date. + - Table 1: + - Improved name. + - Sorted columns: Events, References, and Notes. + - Added unmounted-remote-filesystems event. + + -- James Hunt Thu, 24 Mar 2011 14:34:12 +0000 + +upstart (0.9.4-1) natty; urgency=low + + * New upstream release 0.9.4: + - scripts/initctl2dot.py: Fixes to handle 'emits' glob syntax. + + -- James Hunt Thu, 24 Mar 2011 14:31:26 +0000 + +upstart (0.9.3-1) natty; urgency=low + + * New upstream release 0.9.3: + - Added missing emits stanzas for supplied .conf files. + - Added wildcard/globbing facility to initctl.c (for check-config + command). + - Updated man page on emits stanza syntax. + + -- James Hunt Tue, 15 Mar 2011 11:57:11 +0000 + +upstart (0.9.2-1) natty; urgency=low + + * Merge of upstream lp:~upstart-devel/upstart/0.9. + + -- James Hunt Fri, 11 Mar 2011 10:34:39 +0000 + +upstart (0.9.1-1ubuntu5) natty; urgency=low + + * Merge of upstream lp:~upstart-devel/upstart/0.9. + + -- James Hunt Thu, 10 Mar 2011 14:23:28 +0000 + +upstart (0.9.1-1ubuntu4) natty; urgency=low + + * Merge of upstream lp:~upstart-devel/upstart/0.9. + + -- James Hunt Mon, 07 Mar 2011 15:08:35 +0000 + +upstart (0.9.1-1ubuntu3) natty; urgency=low + + * debian/upstart.bash-completion: Fixed path so dh_bash-completion + considers it a file, not a snippet. + + -- James Hunt Fri, 04 Mar 2011 21:26:27 +0000 + +upstart (0.9.1-1ubuntu2) natty; urgency=low + + * debian/control: Added Suggests for python, graphviz and + bash-completion. + * debian/rules: Invoke bash-completion add-on explicitly. + * debian/manpages/upstart-events.7: New man page. + * debian/upstart.bash-completion: Install bash completion. + * debian/upstart.manpages: Install upstart-events.7. + + -- James Hunt Fri, 04 Mar 2011 17:16:26 +0000 + +upstart (0.9.1-1ubuntu1) natty; urgency=low + + * Merge of upstream lp:~upstart-devel/upstart/0.9. + + -- James Hunt Fri, 04 Mar 2011 15:13:35 +0000 + +upstart (0.9.1-1) natty; urgency=low + + * Merge of upstream lp:~upstart-devel/upstart/0.9. + + -- James Hunt Thu, 03 Mar 2011 20:52:16 +0000 + +upstart (0.9.0-1ubuntu3) natty; urgency=low + + * Revert dbus/Upstart.conf to the version in 0.6.7, disabling user session + support until such time as it has comprehensive test suite coverage. + + -- Colin Watson Mon, 28 Feb 2011 20:29:01 +0000 + +upstart (0.9.0-1ubuntu2) natty; urgency=low + + * Added extra/Makefile.in to avoid build failing. These files need removing + at a later date and debian/rules updating to call dh-autoreconf. + + -- James Hunt Thu, 24 Feb 2011 16:39:28 +0000 + +upstart (0.9.0-1ubuntu1) natty; urgency=low + + * debian/control: Updated for nih version 1.0.3 (required for nih-dbus-tool + and DBUS_TYPE_UNIX_FD). + * removed binary message catalog files causing build issues: + - po/en@boldquot.gmo + - po/en@quot.gmo + + -- James Hunt Thu, 24 Feb 2011 16:06:13 +0000 + +upstart (0.9.0-1) natty; urgency=low + + * New upstream release: + - Session support (chroots, sessions and user-session). + - Socket bridge. + - Override file support. + + -- James Hunt Thu, 24 Feb 2011 14:22:14 +0000 + +upstart (0.6.7-7) natty; urgency=low + + * Re-add upstream r977 to allow proper re-exec on shutdown (LP: #672177) + * debian/control: adding Breaks on eglibc version that disables + telinit u to avoid accidentally installing a version of libc6 that + will cause upstart to re-exec and lose its state. + + -- Clint Byrum Fri, 21 Jan 2011 08:39:13 -0800 + +upstart (0.6.7-6) natty; urgency=low + + * debian/apparmor-profile-load: check for correct AppArmor profile loading + interface file (LP: #710649). + + -- Kees Cook Thu, 03 Feb 2011 13:45:32 -0800 + +upstart (0.6.7-5) natty; urgency=low + + * debian/upstart-job: properly handle jobs that are in state 'start/running' + with no PID, by checking only if the goal is 'start'. LP: #603934, + #707971. + + -- Steve Langasek Wed, 26 Jan 2011 14:05:43 -0800 + +upstart (0.6.7-4) natty; urgency=low + + * debian/apparmor-profile-load: allow profiles to be missing for saner + packaging integration. + + -- Kees Cook Fri, 14 Jan 2011 13:46:12 -0800 + +upstart (0.6.7-3) natty; urgency=low + + * debian/rules: make sure apparmor-profile-load is executable. + + -- Kees Cook Wed, 22 Dec 2010 10:55:09 -0800 + +upstart (0.6.7-2) natty; urgency=low + + * debian/apparmor-profile-load: common AppArmor profile loading helper + which can be used by any upstart services, regardless of the state + of AppArmor (LP: #692801). + + -- Kees Cook Mon, 20 Dec 2010 16:03:33 -0800 + +upstart (0.6.7-1) natty; urgency=low + + * New upstream release: + - Added manual stanza. + - Added debug stanza. + - Added start_on, stop_on and emits properties. + - Added GoalChanged, StateChanged and Failed signals. + - Documentation updates. + + * Added myself as a maintainer. + + -- James Hunt Tue, 14 Dec 2010 17:15:57 +0000 + +upstart (0.6.6-3) maverick; urgency=low + + * Ubuntu seems to have stopped installing Recommends of Build-Depends, + add a Build-Depend on dbus. LP: #602130. + + -- Scott James Remnant Thu, 12 Aug 2010 16:38:05 -0400 + +upstart (0.6.6-2) maverick; urgency=low + + * Apply patch from trunk to use /dev/null when /dev/console is unavailable + due to kernel bugs. This isn't a fix for those bugs, but it does work + around it for now. LP: #554172. + + -- Scott James Remnant Thu, 12 Aug 2010 09:52:07 -0400 + +upstart (0.6.6-1ubuntu1) maverick; urgency=low + + * Try buying with -fPIE/-pie on armel again; we have a shiny new armel + toolchain and this part of the rules was broken in 0.6.6-1 anyway. + + -- Loïc Minier Fri, 23 Jul 2010 14:54:11 +0200 + +upstart (0.6.6-1) maverick; urgency=low + + * New upstream release: + - All changes were previously merged into this package. + + * debian/upstart-job: + - Fix output for the force-reload command to only refer to reload(8). + LP: #532862. + + * Add debian/source/format with "1.0" to be future compatible. + * Add missing ${misc:Depends}. + * Bump standards version. + * Convert rules to dh7 format. + + -- Scott James Remnant Tue, 27 Apr 2010 13:41:18 -0700 + +upstart (0.6.5-6) lucid; urgency=low + + * Merge fixes from trunk: + - double-quoting of NIH_CFLAGS and NIH_DBUS_CFLAGS on --with-local-libnih + - document "env KEY" behaviour + * conf/rc.conf, conf/rc-sysinit.conf: + - enable console output. LP: #548954. + - pass value of INIT_VERBOSE from kernel command-line. + + -- Scott James Remnant Thu, 01 Apr 2010 19:25:36 +0100 + +upstart (0.6.5-5) lucid; urgency=low + + * init/main.c: + - Don't change the settings of the foreground console, this is often + owned by plymouth and not supposed to be in Canonical Mode; all other + paths have stty sane settings anyway (which these are not), so there + really isn't need for init to do this. LP: #540256. + + -- Scott James Remnant Wed, 17 Mar 2010 22:34:55 +0000 + +upstart (0.6.5-4) lucid; urgency=low + + * debian/control: + - change Pre-Depends back to Depends, this was a holdover from when we + attempted to make Upstart Essential to solve early sysvinit→upstart + upgrade issues, we backed out the Essential bit but never the use of + Pre-Depends. LP: #527722. + - add versioned-dependencies on ifupdown for loopback fix that can + prevent initscripts from being run. LP: #527830. + + * Merge patches from trunk to use /proc/self/fd instead of /dev/fd, and + to always mount /proc and /sys on boot. + + -- Scott James Remnant Fri, 26 Feb 2010 15:40:58 +0000 + +upstart (0.6.5-3) lucid; urgency=low + + * udev/upstart-udev-bridge.c: use right variable name, fixing a build + failure. (LP: #524484) + + -- Scott Moser Fri, 19 Feb 2010 10:21:33 -0500 + +upstart (0.6.5-2) lucid; urgency=low + + * udev/upstart-udev-bridge.c: + - Increase receiving buffer size for uevents so we don't miss any. + LP: #504883. + + -- Scott James Remnant Wed, 17 Feb 2010 15:50:40 +0000 + +upstart (0.6.5-1) lucid; urgency=low + + * New upstream release: + - libnih has been separated out into its own project. + - "start on" and "stop on" now support != matches. LP: #513035. + - Fixed crash in child when unable to spawn job. LP: #451917. + - No longer holds /dev/console open so SAK won't kill init. LP: #486005. + - Added missing OPTIONS section to init(8). LP: #449883. + + [ Scott James Remnant ] + * Build-depend on libnih-dev, libnih-dbus-dev and nih-dbus-tool to use + the separated out libnih. + - This has the fix for LP: #436758. + - Remove changelog.nih from the doc directory. + * Bump udev build-dependency to 147 to match upstream. + * udev/Makefile.am: Update to use external libnih + + [ Johan Kiviniemi ] + * udev/upstart-udev-bridge.c: Change -device-remove to -device-removed to + match -device-added and -device-changed. LP: #516698. + + -- Scott James Remnant Thu, 04 Feb 2010 16:30:10 -0800 + +upstart (0.6.3-11build1) lucid; urgency=low + + * Rebuild to pick up relaxed dependency on libc6, after checking that + __abort_msg is available with the same signature in eglibc 2.11. + LP: #508702. + + -- Matthias Klose Mon, 18 Jan 2010 16:10:11 +0100 + +upstart (0.6.3-11) karmic-proposed; urgency=low + + * Make rc-sysinit.conf wait on the loopback interface, to ensure that the + interface is up before we process the scripts in /etc/rc?.d. LP: #461725. + + -- Steve Langasek Tue, 08 Dec 2009 12:58:37 -0800 + +upstart (0.6.3-10) karmic; urgency=low + + * Retain the "telinit u" for the case when we're upgrading from pre-0.6 + (ie. hardy or jaunty). Whups. LP: #451556. + + -- Scott James Remnant Thu, 15 Oct 2009 17:48:47 +0100 + +upstart (0.6.3-9) karmic; urgency=low + + * Restore the call to sync() in reboot, have been observing some issues + and it looks like ext4 might not be explicitly flushing the disk when + remounting read-only. + + -- Scott James Remnant Wed, 14 Oct 2009 16:40:32 +0100 + +upstart (0.6.3-8) karmic; urgency=low + + * Rather than calling "telinit u" after upgrade, which will lose state, + have the umountroot initscript take care of it for us by setting a + flag. LP: #441796. + * Don't lose the original default runlevel if /etc/inittab exists without + an initdefault line. LP: #405847. + * Fix "unhandled error" in shutdown when unable to change runlevel, + e.g. due to previous Ubiquity bug. LP: #426332. + * Merge change from trunk that makes it possible to build Upstart using + a previously built copy of nih-dbus-tool, especially useful when + cross-compiling. LP: #426740. + * Merge change from libnih to store our assertion messages in the + glibc __abort_msg symbol so apport can pick them up. LP: #429411. + * Merge change from libnih to fix compilation issue with eglibc due + to changed alphasort() prototype. + + -- Scott James Remnant Wed, 14 Oct 2009 05:34:13 +0100 + +upstart (0.6.3-7) karmic; urgency=low + + * Ignore initramfs pids that don't exist. LP: #440071. + - you still need to ensure that the pid's parent is init, there's no + cheap way to test for that. + * Remove "console owner" and "console output" from rc scripts. + * Try harder to remove dbus-reconnect.conf + + -- Scott James Remnant Fri, 02 Oct 2009 21:09:03 +0100 + +upstart (0.6.3-6) karmic; urgency=low + + * Don't use "telinit q" to reconnect to D-Bus, since that breaks + lots of things. Invent another secret way instead. + + [ Steve Langasek ] + * upstart-job's restart target must also not fail when the service is not + yet started. LP: #430883. + + -- Scott James Remnant Thu, 01 Oct 2009 15:26:19 +0100 + +upstart (0.6.3-5) karmic; urgency=low + + * Update autoconf and automake files. LP: #435252. + + -- Scott James Remnant Wed, 23 Sep 2009 14:16:34 -0700 + +upstart (0.6.3-4) karmic; urgency=low + + [ Scott James Remnant ] + * Reduce the priority of the stopped by/continued by messages so that + they are only shown when --verbose on the kernel command-line. + LP: #401333. + * Add a hack to look for /dev/.initramfs/*.pid files on startup and + "fake" start jobs of those names. Basically this means that "status" + and "stop" work for things like bootchart and usplash. + * Implement a "reload" command in initctl that retrieves the current pid + of the job and sends it the HUP signal. LP: #433544. + + [ Steve Langasek ] + * debian/upstart-job: + - give proper policy-compliant behavior of the start command: detect if + the job is already running using upstart status, and if so return success. + - same for the stop command: return success if the job is already stopped. + - when $DPKG_MAINTSCRIPT_PACKAGE is set, don't spit warnings out because + it's not the user's fault - we're being invoked by a maintainer script. + + -- Scott James Remnant Tue, 22 Sep 2009 13:56:48 -0700 + +upstart (0.6.3-3) karmic; urgency=low + + * debian/upstart-job: + - force-reload should only send a HUP signal, since it may not be wise + to actually restart (cf. dbus) + + -- Scott James Remnant Wed, 16 Sep 2009 00:10:13 +0100 + +upstart (0.6.3-2) karmic; urgency=low + + FFE LP: #427356. + + * debian/upstart-job: + - Remove trailing "s" from file + - Support direct invocation better. + * udev/upstart-udev-bridge.c: + - New tool to capture events from the udev netlink socket and + convert into upstart events. + * conf/rc-sysinit.conf: + - Run once all filesystems are mounted, rather than on startup + * debian/control: + - Add dependency on mountall for the filesystem event. + + -- Scott James Remnant Tue, 15 Sep 2009 03:19:09 +0100 + +upstart (0.6.3-1) karmic; urgency=low + + * New upstream release: + - Fixed assertion when a job exits while stopping. LP: #406408. + - Fixed compilation on ia64. + - nih-dbus-tool(1) manpage no longer installed. + + -- Scott James Remnant Mon, 03 Aug 2009 23:58:47 +0100 + +upstart (0.6.2-1) karmic; urgency=low + + * New upstream release: + - Fixed assertion when stopping a job during its starting event. + - Fixed fork following to not stop on exec() before fork() + - Fixed missing chdir() in crash handler. + + -- Scott James Remnant Wed, 22 Jul 2009 10:39:50 +0100 + +upstart (0.6.1-1) karmic; urgency=low + + * New upstream release: + - Fixed race condition in ptrace() code. LP: #264711. + - Fixed runlevel to output "unknown" not "N N". LP: #400248. + - Fixed runlevel to prefix error messages with filename. LP: #400241. + + * Provide/Conflict/Replace the agreed "upstart-job" meta-package. + LP: #399799. + * Bump dpkg dependency to 1.2.16 + + -- Scott James Remnant Thu, 16 Jul 2009 18:26:23 +0100 + +upstart (0.6.0-5) karmic; urgency=low + + * Cherry-pick patch from -r1188 to fix "expect fork" and "expect daemon" + LP: #264711. + + -- Scott James Remnant Tue, 14 Jul 2009 15:19:17 +0100 + +upstart (0.6.0-4) karmic; urgency=low + + * Don't build the testsuite with -fPIE on armel; LP: #398403. + + -- Loïc Minier Mon, 13 Jul 2009 22:12:34 +0200 + +upstart (0.6.0-3) karmic; urgency=low + + * Add Conflicts on older Upstart packages to make update-manager's + job easier. + + -- Scott James Remnant Fri, 10 Jul 2009 10:11:21 +0100 + +upstart (0.6.0-2) karmic; urgency=low + + * Bump D-Bus build dependency to ensure we get the container abandonment + patches, and the GIT version bump. + * Actually ship /lib/init/upstart-job + + -- Scott James Remnant Thu, 09 Jul 2009 17:29:59 +0100 + +upstart (0.6.0-1) karmic; urgency=low + + * New upstream release ("How appropriate, you fight like a cow") + - my customary changes list since pointless, it's basically a + complete rewrite. + - Handles /bin/sh symlink disappearing. LP: #65024. + - Boot parameters may be passed to init scripts. LP: #74664. + - reboot implies --force during shutdown. LP: #388738. + - reboot no longer iterates /proc/ide. LP: #92685. + - much improved documentation. LP: #60429, #72058, #388715. + + * Merge the various upstart packages into a single package, it makes + little sense to have it all spread out. + + -- Scott James Remnant Wed, 08 Jul 2009 23:12:03 +0100 + +upstart (0.3.10-2) karmic; urgency=low + + * debian/upstart.postinst: Use telinit u to re-exec, rather than + kill just in case it's not Upstart that's running. LP: #92177. + * debian/event.d/system-services/tty*: Run getty in 8-bit clean + mode. LP: #273189. + * debian/event.d/upstart-compat-sysv/rc-default: + - Don't use grep -w, instead split on $IFS and iterate. LP: #385911. + - Check for any valid runlevel, not just S. LP: #85014. + - Make console owner, since it may spawn sulogin. + * debian/event.d/upstart-compat-sysv/rcS: + - Spawn sulogin if given -b or "emergency". LP: #193810. + * debian/event.d/upstart-compat-sysv/rcS: + - Make console owner. LP: #211402. + * debian/event.d/upstart-compat-sysv/rcS-sulogin: + - Place the telinit code in post-stop, checking $UPSTART_EVENT first so + we don't change the runlevel if we were stopped due to a runlevel + change. LP: #66002. + + -- Scott James Remnant Thu, 18 Jun 2009 16:19:34 +0100 + +upstart (0.3.10-1) karmic; urgency=low + + * Compilation fixes. + * Fixed assertion caused by the post-start or pre-stop scripts + exiting after the main process of a respawning job had exited. + LP: #381048. + + -- Scott James Remnant Wed, 17 Jun 2009 13:33:40 +0100 + +upstart (0.3.9-8) intrepid; urgency=low + + * Do not attempt to continue communicating with the restarted upstart + (LP: #273761). + + -- Kees Cook Mon, 29 Sep 2008 13:35:21 -0700 + +upstart (0.3.9-7) intrepid; urgency=low + + * Implement "telinit u" by just sending Upstart SIGTERM with a slightly + different patch than Fedora. LP: #188925. + + -- Scott James Remnant Tue, 23 Sep 2008 09:01:09 -0700 + +upstart (0.3.9-6) intrepid; urgency=low + + * Really fix LP: #237276 properly this time, lost the change while mucking + around with bzr. + + -- Scott James Remnant Wed, 04 Jun 2008 22:29:48 +0100 + +upstart (0.3.9-5) intrepid; urgency=low + + * Correct build problem on amd64 and ia64 by only building libnih and + libupstart statically. The shared objects were unwanted, and conflict + with -fPIE. + + -- Scott James Remnant Wed, 04 Jun 2008 17:07:12 +0100 + +upstart (0.3.9-4) intrepid; urgency=low + + * Add missing limits.h, required to build with current libc. + + -- Scott James Remnant Wed, 04 Jun 2008 13:09:32 +0100 + +upstart (0.3.9-3) intrepid; urgency=low + + * Change dependency from sysvutils to sysvinit-utils. LP: #237276. + * Compile with stack -fstack-protector, -fPIE, -z relro, -z now and -pie + (MMmm, pie) + + -- Scott James Remnant Wed, 04 Jun 2008 12:59:11 +0100 + +upstart (0.3.9-2) hardy; urgency=low + + * Start the getty on tty1 after the rc script has stopped rather then + at the same time it starts to avoid overwriting by console messages. + tty2..6 will still be active if you want an early login. LP: #65230. + * If the recovery menu is available start that instead of sulogin when + entering single-user-mode. + + -- Scott James Remnant Fri, 11 Apr 2008 13:38:50 +0100 + +upstart (0.3.9-1) hardy; urgency=low + + * New upstream release: + - many bug fixes. + + * Update reference to "edgy" in README.Debian to "hardy". LP: #140037. + + -- Scott James Remnant Sun, 28 Oct 2007 10:51:59 -0400 + +upstart (0.3.8-2) gutsy; urgency=low + + * Fix broken migration of old-style 'respawn process' stanzas which + produced corrupted 'exec' stanzas. Try to fix up files previously + corrupted by this. LP: #95210 + + -- Scott James Remnant Sun, 28 Oct 2007 10:50:36 -0400 + +upstart (0.3.8-1) feisty; urgency=low + + * New upstream release: + - much improved initctl tool. + + * Update my standard prep_/undo_/rm_conffile functions to take into account + current dpkg behaviour wrt obsolete conffiles. The conffile is now moved + out of the way in preinst and the moved file deleted in postinst, or moved + back in postrm abort-upgrade. This means it's not there when dpkg + configures the new version, so the conffile is not left in the list. + * Purge backups of modified obsolete conffiles when the package is purged. + + * Update runlevel and respawn rule generated in migrate-inittab.pl + LP: #89314 + + * Drop 00-libnih-update.patch and 01-libnih-sparc-ftbfs.patch; new upstream + release includes an up-to-date libnih which contains both patches. + * Drop 10-cant-stop-execless-job.patch; included upstream. + * Drop 20-complex-event-config.patch; this is going to be significantly + changed upstream, and we don't want to ship something strange. + * Drop 30-fix-warnings.patch; included upstream. + + -- Scott James Remnant Sun, 11 Mar 2007 19:19:00 +0000 + +upstart (0.3.5-2) feisty; urgency=low + + * Changed "start script" to "pre-start script" in sulogin event, the former + is no longer recognised. + + * Applied 01-libnih-sparc-ftbfs.patch; this updates the signal name list + to exclude signals not available on that architecture, and add one that's + unique to it. + * Applied 30-fix-warnings.patch; this corrects a few warnings that spoiled + an otherwise clean build log. + + -- Scott James Remnant Tue, 13 Feb 2007 15:56:33 +0000 + +upstart (0.3.5-1) feisty; urgency=low + + * New upstream release: + - inotify file descriptor leak fixed. LP: #83099. + - inotify support is no longer required. LP: #68904. + - new job state machine + - new event structure, can now include arguments and environment + + * Applied 00-libnih-update.patch; this updates the libnih library to the + latest bzr trunk version, required for the complex-event-config patch. + * Applied 10-cant-stop-execless-job.patch from upstream; this corrects a + bug where jobs without an "exec" or "script" stanza cannot be stopped. + * Applied 20-complex-event-config.patch from upstream; this is an + experimental implementation of the "on" keyword that allows definition + of complex system states. + + * System V compatibility jobs updated to match new event names. + * rcS job now sets PREVLEVEL and RUNLEVEL. LP: #76304. + + * NOTE: After this upgrade, init will appear to have "forgotten" the + process ids of your gettys, etc. This is not a critical problem and + will be fixed before release. Shutdown will still work as normal. + + -- Scott James Remnant Mon, 12 Feb 2007 13:51:40 +0000 + +upstart (0.3.1-1) feisty; urgency=low + + * New upstream release: + - start, stop and status are now symlinks to initctl, not to a + different, separate utility. + - initctl completely rewritten to behave properly. + - some upstart-specific options to shutdown and reboot dropped, as + these are considered SysV-compat tools. + - "console none" fixed. LP: #70782. + - improved documentation. LP: #68805. + + * shutdown and reboot moved to upstart-compat-sysv. + + * Replace the /usr/share/doc/* directory in upstart-logd, + upstart-compat-sysv, system-services and startup-tasks with a symlink to + /usr/share/doc/upstart. This was actually done in a previous package, + but the migration missed. LP: #70895. + + -- Scott James Remnant Wed, 13 Dec 2006 17:27:37 +0000 + +upstart (0.2.7-7) edgy; urgency=low + + * Don't abort the postinst if we can't send init SIGTERM. Ubuntu: #64499. + + -- Scott James Remnant Tue, 10 Oct 2006 10:13:05 +0100 + +upstart (0.2.7-6) edgy; urgency=low + + * Don't start gettys on tty2 thru tty6 in runlevels 4 and 5 (matches + our sysvinit configuration). + * Migrate common changes made to /etc/inittab to /etc/event.d by + adjusting the installed conffiles. Ubuntu: #61539. + + * Include missing AUTHORS and NEWS file in the upstart package. + * Include README.Debian which answers common questions. Ubuntu: #60429. + + -- Scott James Remnant Thu, 5 Oct 2006 16:08:34 +0100 + +upstart (0.2.7-5) edgy; urgency=low + + * Don't set the current runlevel in /var/run/utmp to 0 or 6 if it is + already either of those two values. That way we don't end up with + either 0 or 6 in the PREVLEVEL variable, which can cause + /etc/init.d/rc to be "efficient" and not bother doing + anything. Ubuntu: #63852. + + -- Scott James Remnant Wed, 4 Oct 2006 14:06:18 +0100 + +upstart (0.2.7-4) edgy; urgency=low + + * Can't just start rc-default once in single-user mode, because if we + boot into that, that will just return us back to sulogin again. Copy + the script out of rc-default into rcS-sulogin to call telinit with the + right default runlevel. Ubuntu: #62189. + + * Add Build-Depend on dpkg-dev (>= 1.13.19) due to our use of + ${binary:Version}. Ubuntu: #61693. + + -- Scott James Remnant Tue, 26 Sep 2006 17:20:42 +0100 + +upstart (0.2.7-3) edgy; urgency=low + + * Set the runlevel to "S" when we enter sulogin so that it appears + in utmp. + + -- Scott James Remnant Thu, 21 Sep 2006 05:37:25 +0100 + +upstart (0.2.7-2) edgy; urgency=low + + * Ensure that the same version of upstart is installed as the version of + upstart-compat-sysv and upstart-logd; as the IPC protocol may change + between releases. + + * Adjust the rcS-sulogin job so that if sulogin exits the default runlevel + is entered; but if the job is stopped (e.g. by shutdown) it isn't. The + solves the regression introduced in the previous release. + + * Revert upstream logd/"quiet" change in favour of doing it in our + lsb logging functions instead; seems to work better (fsvo better). + + -- Scott James Remnant Thu, 21 Sep 2006 03:12:33 +0100 + +upstart (0.2.7-1) edgy; urgency=low + + * New upstream release: + - logd now writes to the console unless "quiet" is specified + - runaway jobs caught when they start rather than respawn. Ubuntu: #59807 + + * Fix failure to shutdown while in single-user mode, however this means + that for edgy you can't exit the sulogin shell to enter the default + runlevel; explicitly say what works. Ubuntu: #60626. + * Drop unnecessary dependency on util-linux. + * Drop sulogin hack, instead depend on the version of sysvutils that + includes the real one. Ubuntu: #60965. + + -- Scott James Remnant Wed, 20 Sep 2006 05:39:16 +0100 + +upstart (0.2.6-1) edgy; urgency=low + + * New upstream release: + - fix infinite loop caused by bad waitid() call. Ubuntu: #59459. + - halt now behaves as "shutdown -h now". Ubuntu: #59720. + + -- Scott James Remnant Wed, 13 Sep 2006 22:16:17 +0100 + +upstart (0.2.5-1) edgy; urgency=low + + * New upstream release: + - no longer spins when no stalled event handler. Ubuntu: #59170. + - shutdown works when under sysvinit. Ubuntu: #58523; + - shutdown -k implemented. Ubuntu: #58723. + - telinit sends shutdown event for 0, 1 and 6. Ubuntu: #58913. + - basic manual pages included. Ubuntu: #58724. + + * upstart-compat-sysv Replaces: sysvinit. Ubuntu: #59427. + * upstart Recommends: upstart-compat-sysv, startup-tasks & system-services. + + * New upstart-logd package includes the logd daemon that can will log + output of jobs with "console logged" (the default) in their description + to /var/log/boot. + + * Add /etc/event.d/rc0 that is run on the "halt" event (neither -H or -P + given), and modify rc0-halt to run on "system-halt" (-H given) and + rc0-poweroff to run on "power-off" (-P given). Ubuntu: #59134. + * Fix the control-alt-delete job to run on the "ctrlaltdel" event so + that it's triggered properly. Ubuntu: #59398. + * Fix single-user mode. + + -- Scott James Remnant Sat, 9 Sep 2006 05:10:12 +0100 + +upstart (0.2.1-7) edgy; urgency=low + + * Remove the Essential tags again, they didn't solve the problem we + hoped they would (dpkg/apt still won't remove sysvinit without + serious persuasion) and I don't think these packages should be. + + -- Scott James Remnant Thu, 7 Sep 2006 02:42:33 +0100 + +upstart (0.2.1-6) edgy; urgency=low + + * Make packages Essential, and change Depends to Pre-Depends so that the + packages work when unconfigured (nothing interesting is performed in + postinst). Ubuntu: #59005. + * Sync priority in debian/control with that in the archive (required) + * Drop warning of dire consequences if you install upstart, seeing as it's + installed by default. + + * Add new startup-tasks and system-services packages which will contain + the /etc/event.d files themselves (other than the main ones). + * Move tty definitions into system-services. + * Modify tty definitions to start when the rcS task has finished. This + puts them in the "right" place when compared to gdm. Ubuntu: #58630. + + * Correct rcS compatibility script to ignore any information in utmp so + that all scripts are always run. Ubuntu: #59203. + * Make rcS the console owner while it runs, temporary fix for + Ubuntu: #58609, #58794, #58796 + * Include default control-alt-delete handler that reboots the machine. + + -- Scott James Remnant Wed, 6 Sep 2006 21:52:48 +0100 + +upstart (0.2.1-5) edgy; urgency=low + + * upstart-compat-sysv Depends: initscripts (closes: Malone #58979). + + -- Colin Watson Tue, 5 Sep 2006 12:22:50 +0100 + +upstart (0.2.1-4) edgy; urgency=low + + * Add missing #! line to top of postrm + + -- Scott James Remnant Mon, 4 Sep 2006 08:11:16 +0100 + +upstart (0.2.1-3) edgy; urgency=low + + * Remove the rc0 configuration file shipped in 0.1.0 that causes all + reboots to become shutdowns. Ubuntu: #58557. + + -- Scott James Remnant Sun, 3 Sep 2006 23:24:41 +0100 + +upstart (0.2.1-2) edgy; urgency=low + + * Don't send the SIGTERM signal unless we're upgrading from a version + of upstart that supports re-exec; older versions would cause a kernel + PANIC and change from sysvinit does nothing. + + -- Scott James Remnant Sat, 2 Sep 2006 17:18:38 +0100 + +upstart (0.2.1-1) edgy; urgency=low + + * New upstream release: + - compilation fixes. + + -- Scott James Remnant Fri, 1 Sep 2006 19:51:41 +0100 + +upstart (0.2.0-1) edgy; urgency=low + + * New upstream release: + - upstart includes shutdown, reboot, halt, poweroff, start, stop, status, + runlevel and telinit utilities. + - "initctl list" will list active jobs. + - Events vastly simplified to just simple strings. + + * Compatibility tasks for old rc scripts, along with runlevel and telinit + utilities now shipped in upstart-compat-sysv package. + + -- Scott James Remnant Fri, 1 Sep 2006 02:38:44 +0100 + +upstart (0.1.1-1) edgy; urgency=low + + * New upstream release: + - set PATH and TERM in processes + + -- Scott James Remnant Fri, 25 Aug 2006 16:17:52 +0200 + +upstart (0.1.0-2) edgy; urgency=low + + * Oops, rename /sbin/init to /sbin/upstart as documented. Lost this + while battling bzr. + + -- Scott James Remnant Thu, 24 Aug 2006 16:30:54 +0200 + +upstart (0.1.0-1) edgy; urgency=low + + * Initial release. + + -- Scott James Remnant Thu, 24 Aug 2006 14:27:47 +0200 --- upstart-1.4.orig/debian/upstart.dirs +++ upstart-1.4/debian/upstart.dirs @@ -0,0 +1 @@ +/var/log/upstart --- upstart-1.4.orig/debian/upstart.postinst +++ upstart-1.4/debian/upstart.postinst @@ -0,0 +1,62 @@ +#!/bin/sh -e +# This script can be called in the following ways: +# +# After the package was installed: +# configure +# +# +# If prerm fails during upgrade or fails on failed upgrade: +# abort-upgrade +# +# If prerm fails during deconfiguration of a package: +# abort-deconfigure in-favour +# removing +# +# If prerm fails during replacement due to conflict: +# abort-remove in-favour + + +# Remove a no-longer used conffile +rm_conffile() +{ + CONFFILE="$1" + + if [ -e "$CONFFILE".dpkg-obsolete ]; then + echo "Removing obsolete conffile $CONFFILE" + rm -f "$CONFFILE".dpkg-obsolete + fi +} + + +case "$1" in + configure) + if dpkg --compare-versions "$2" lt-nl 0.6.0; then + # We're upgrading from a version of Upstart that doesn't use + # D-Bus for its IPC. We have to tell it to re-exec into one + # that does. It'll lose all state, but we didn't keep much + # in those days. + telinit u + else + # Before we shutdown or reboot, we need to re-exec so that we + # can safely remount the root filesystem; we can't just do that + # here because we lose state. + touch /var/run/init.upgraded || : + fi + + # Upgrade from karmic development version + if dpkg --compare-versions "$2" lt-nl 0.6.3-7; then + rm_conffile /etc/init/dbus-reconnect.conf + fi + ;; + + abort-upgrade|abort-deconfigure|abort-remove) + ;; + + *) + echo "$0 called with unknown argument \`$1'" 1>&2 + exit 1 + ;; +esac + +#DEBHELPER# +exit 0 --- upstart-1.4.orig/debian/upstart.manpages +++ upstart-1.4/debian/upstart.manpages @@ -0,0 +1 @@ +debian/manpages/* --- upstart-1.4.orig/debian/control +++ upstart-1.4/debian/control @@ -0,0 +1,22 @@ +Source: upstart +Section: admin +Priority: required +Maintainer: James Hunt +Uploaders: Scott James Remnant +Standards-Version: 3.8.4.0 +Build-Depends: debhelper (>= 7.3.15ubuntu2), pkg-config (>= 0.22), libnih-dev (>= 1.0.3), libnih-dbus-dev (>= 1.0.3), nih-dbus-tool (>= 1.0.3), libdbus-1-dev (>= 1.2.16), libexpat1-dev (>= 2.0.0), libudev-dev (>= 151-5), dbus, bash-completion +Homepage: http://upstart.ubuntu.com/ + +Package: upstart +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, sysvinit-utils, sysv-rc, initscripts, mountall, ifupdown (>= 0.6.10ubuntu5) +Suggests: python, graphviz, bash-completion +Replaces: upstart-job, sysvinit, upstart-compat-sysv, startup-tasks, system-services +Conflicts: upstart-job, sysvinit, upstart-compat-sysv, startup-tasks, system-services +Provides: upstart-job, upstart-compat-sysv, startup-tasks, system-services +Breaks: libc6 (<< 2.12.1-0ubuntu12), friendly-recovery (<< 0.2.13) +Multi-Arch: foreign +Description: event-based init daemon + upstart is a replacement for the /sbin/init daemon which handles + starting of tasks and services during boot, stopping them during + shutdown and supervising them while the system is running. --- upstart-1.4.orig/debian/upstart.bash-completion +++ upstart-1.4/debian/upstart.bash-completion @@ -0,0 +1 @@ +contrib/bash_completion/upstart --- upstart-1.4.orig/debian/compat +++ upstart-1.4/debian/compat @@ -0,0 +1 @@ +7 --- upstart-1.4.orig/debian/upstart.logrotate +++ upstart-1.4/debian/upstart.logrotate @@ -0,0 +1,7 @@ +/var/log/upstart/*.log { + daily + missingok + rotate 7 + compress + notifempty +} --- upstart-1.4.orig/debian/README.Debian +++ upstart-1.4/debian/README.Debian @@ -0,0 +1,200 @@ +upstart +======= + +Upstart is a replacement for the traditional sysvinit package, and +runs as process #1. Through upstart, we are able to have an +event-driven process, whilst at the same time retaining compatibility +for the original sysvinit behaviour. + +This file documents how to do a few common operations with the new +system. + + +Where are initscripts installed? +-------------------------------- + +This has not changed, they are installed in /etc/init.d. See +/etc/init.d/README. + +Important system jobs are no longer shipped as initscripts, but as +upstart jobs. These are installed in /etc/init + + +How are initscripts started and stopped? +---------------------------------------- + +This has not changed, symlinks are made from the initscript in the +/etc/init.d directory to the /etc/rc?.d directories. See +/etc/init.d/README and /etc/rc?.d/README. + + +What order are initscripts started and stopped in? +-------------------------------------------------- + +This has not changed, the symlinks are named SNNname or KNNname, where +NN is a number from 00 to 99. The K scripts are run first in +numerical order, followed by the S scripts in numerical order. + + +How do I find the current/previous runlevel? +-------------------------------------------- + +This has not changed, use the "runlevel" command. See runlevel(8). + + +How do I change the runlevel? +----------------------------- + +This has not changed, use the "telinit" command or just invoke "init" +directly. See telinit(8). + + +How do I change the default runlevel? +------------------------------------- + +If you have an /etc/inittab file, edit it. Locate the following line: + + id:N:initdefault: + +Where N is the default runlevel, change this to match. + +Most people won't have that file, you can edit /etc/init/rc-sysinit.conf +and change the following line: + + env DEFAULT_RUNLEVEL=2 + + +How do I shutdown the machine? +------------------------------ + +This has not changed, use the "shutdown" command provided by the +upstart package; you may also use the "reboot"/"halt"/"poweroff" +commands as a short-cut. See shutdown(8) and reboot(8). + +You can also press Control-Alt-Delete on a console to reboot the +machine. + + +How do I change the behaviour of Control-Alt-Delete? +---------------------------------------------------- + +Edit the /etc/init/control-alt-delete.conf file, the line beginning +"exec" is what upstart will run when this key combination is pressed. + +To not do anything, you can simply delete this file. + + +How do I enter single-user mode? +-------------------------------- + +This hasn't changed, choose the "(recovery mode)" option from GRUB; +add "-s", "S" or "single" to the kernel command-line; or from a +running machine, run "telinit 1" or "shutdown now". + + +How do I reduce the number of gettys? +------------------------------------- + +Also see "How do I change which runlevels gettys are run in?" + +In /etc/init there is a file named ttyN.conf for each getty that will be +started, where N is numbered 1 to 6. Remove any that you do not +want. + +This will not take immediate effect, however you can run "stop ttyN" +to stop one that is running. + + +How do I change getty parameters? +--------------------------------- + +In /etc/init there is a file named ttyN.conf for each getty that will be +started, where N is numbered 1 to 6. Edit these files, the line +beginning "respawn" is what upstart will run. + +This will not take immediate effect, run "stop ttyN" followed by +"start ttyN" or just kill the running getty to respawn with the new +parameters. + + +How do I change which runlevels gettys are run in? +-------------------------------------------------- + +In /etc/init there is a file named ttyN.conf for each getty that will be +started, where N is numbered 1 to 6. Edit these files, there are two +lines: + + start on runlevel [2345] + stop on runlevel [!2345] + +Change the set of runlevels to match your taste. + +This will not take immediate effect, however you can run "stop ttyN" +to stop one that is running or "start ttyN" to start one that isn't. + + +How do I increase the number of gettys? +--------------------------------------- + +In /etc/init there is a file named ttyN.conf for each getty that will be +started, where N is numbered 1 to 6. + +Copy one of these files to a new name, we suggest you simply name it +after the tty, e.g. "ttyS0". + +Edit that file, change the "respawn" line to match your requirements; +in particular you'll need to change the tty the getty should be run +on. + +This will not take immediate effect, however you can run "start ttyN" +to start the getty. + + +How do I add a serial console? +------------------------------ + +See "How do I increase the number of gettys?" + + +How can I see boot messages on the console? +------------------------------------------- + +This is nothing to do with upstart, but I'll answer this anyway. +Remove "quiet" from the kernel command-line. + +To make this permanent, edit /boot/grub/menu.lst and edit the line +that begins "# defoptions=" (yes, it looks like a comment). + +This will change both usplash and the LSB init logging. + + +Upstart isn't working, how do I debug it? +----------------------------------------- + +Add "--debug" to the kernel command-line, and be sure to remove "quiet" +and "splash". You'll now see debugging messages as upstart works. + + +Can I query upstart for a list of jobs? +--------------------------------------- + +Yes, "initctl list" will list the known jobs and their status. + + +How do I manually start or stop a job? +-------------------------------------- + +Use "start JOB" or "stop JOB". + + +How do I find the status of a job? +---------------------------------- + +Use "status JOB". + + +Can I emit an event by hand? +---------------------------- + +Yes, "initctl emit EVENT" will emit the named event and cause any +jobs waiting for it to be started or stopped as appropriate. --- upstart-1.4.orig/debian/copyright +++ upstart-1.4/debian/copyright @@ -0,0 +1,18 @@ +This is the Ubuntu package of upstart, the event-based init daemon. + +Copyright © 2009 Canonical Ltd. +Copyright © 2009 Scott James Remnant + +Licence: + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2, as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +On Ubuntu systems, the complete text of the GNU General Public License +can be found in ‘/usr/share/common-licenses/GPL’. --- upstart-1.4.orig/debian/upstart.install +++ upstart-1.4/debian/upstart.install @@ -0,0 +1,4 @@ +debian/conf/*.conf etc/init/ +debian/upstart-job lib/init/ +debian/apparmor-profile-load lib/init/ +debian/migrate-inittab.pl usr/lib/upstart/ --- upstart-1.4.orig/debian/upstart-job +++ upstart-1.4/debian/upstart-job @@ -0,0 +1,85 @@ +#!/bin/sh -e +# upstart-job +# +# Symlink target for initscripts that have been converted to Upstart. + +set -e + +INITSCRIPT="$(basename "$0")" +JOB="${INITSCRIPT%.sh}" + +if [ "$JOB" = "upstart-job" ]; then + if [ -z "$1" ]; then + echo "Usage: upstart-job JOB COMMAND" 1>&2 + exit 1 + fi + + JOB="$1" + INITSCRIPT="$1" + shift +else + if [ -z "$1" ]; then + echo "Usage: $0 COMMAND" 1>&2 + exit 1 + fi +fi + +COMMAND="$1" +shift + + +if [ -z "$DPKG_MAINTSCRIPT_PACKAGE" ]; then + ECHO=echo +else + ECHO=: +fi + +$ECHO "Rather than invoking init scripts through /etc/init.d, use the service(8)" +$ECHO "utility, e.g. service $INITSCRIPT $COMMAND" + +case $COMMAND in +status) + $ECHO + $ECHO "Since the script you are attempting to invoke has been converted to an" + $ECHO "Upstart job, you may also use the $COMMAND(8) utility, e.g. $COMMAND $JOB" + $COMMAND "$JOB" + ;; +start|stop) + $ECHO + $ECHO "Since the script you are attempting to invoke has been converted to an" + $ECHO "Upstart job, you may also use the $COMMAND(8) utility, e.g. $COMMAND $JOB" + if status "$JOB" 2>/dev/null | grep -q ' start/'; then + RUNNING=1 + fi + if [ -z "$RUNNING" ] && [ "$COMMAND" = "stop" ]; then + exit 0 + elif [ -n "$RUNNING" ] && [ "$COMMAND" = "start" ]; then + exit 0 + fi + $COMMAND "$JOB" + ;; +restart) + $ECHO + $ECHO "Since the script you are attempting to invoke has been converted to an" + $ECHO "Upstart job, you may also use the stop(8) and then start(8) utilities," + $ECHO "e.g. stop $JOB ; start $JOB. The restart(8) utility is also available." + if status "$JOB" 2>/dev/null | grep -q ' start/'; then + RUNNING=1 + fi + if [ -n "$RUNNING" ] ; then + stop "$JOB" + fi + start "$JOB" + ;; +reload|force-reload) + $ECHO + $ECHO "Since the script you are attempting to invoke has been converted to an" + $ECHO "Upstart job, you may also use the reload(8) utility, e.g. reload $JOB" + reload "$JOB" + ;; +*) + $ECHO + $ECHO "The script you are attempting to invoke has been converted to an Upstart" 1>&2 + $ECHO "job, but $COMMAND is not supported for Upstart jobs." 1>&2 + exit 1 +esac --- upstart-1.4.orig/debian/migrate-inittab.pl +++ upstart-1.4/debian/migrate-inittab.pl @@ -0,0 +1,143 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my %gettys; +my $have_cad = 0; + + +#-----------------------------------------------------------------------------# +# Parse /etc/inittab +#-----------------------------------------------------------------------------# + +open INITTAB, "/etc/inittab" + or die "Unable to open /etc/inittab: $!"; + +while () { + chomp; + s/^\s*//; + + next if /^\#/; + next unless length; + + my ($id, $rlevel, $action, $process) = split /:/, $_, 4; + + warn "missing id field" and next + unless defined $id and length $id; + warn "missing runlevel field" and next + unless defined $rlevel; + warn "missing action field" and next + unless defined $action and length $action; + warn "missing process field" and next + unless defined $process; + + + $have_cad = 1 if $action eq "ctrlaltdel"; + $gettys{$1} = [ $rlevel, $process ] if $process =~ /getty.*\b(tty\w+)/; +} + +close INITTAB + or warn "Error while closing /etc/inittab: $!"; + + +#-----------------------------------------------------------------------------# +# Alter /etc/event.d +#-----------------------------------------------------------------------------# + +unlink "/etc/init/control-alt-delete.conf" + unless $have_cad; + +foreach (qw/tty1 tty2 tty3 tty4 tty5 tty6/) { + unlink "/etc/init/$_.conf" + unless exists $gettys{$_}; +} + +foreach (sort keys %gettys) { + my ($rlevel, $process) = @{$gettys{$_}}; + + my @job; + if (-f "/etc/event.d/$_") { + open JOB, "/etc/event.d/$_" + or warn "Unable to open /etc/event.d/$_: $!" and next; + @job = ; + chomp @job; + close JOB + or warn "Error while closing /etc/event,d/$_: $!" and next; + + foreach my $rl (qw/2 3 4 5/) { + my $idx; + for ($idx = 0; $idx < @job; $idx++) { + last if $job[$idx] =~ /^\s*(start|stop)\s+on\s+runlevel\s+$rl\b/; + } + + if ($idx < @job) { + if ($rlevel =~ /$rl/) { + $job[$idx] =~ s/^(\s*)stop(\s+)/$1start$2/; + } else { + $job[$idx] =~ s/^(\s*)start(\s+)/$1stop$2/; + } + } else { + if ($rlevel =~ /$rl/) { + push @job, "start on runlevel $rl"; + } else { + push @job, "stop on runlevel $rl"; + } + } + } + + my $idx; + for ($idx = 0; $idx < @job; $idx++) { + last if $job[$idx] =~ /^\s*respawn\s*/; # match bare 'respawn' too + } + + if ($idx < @job) { + # only match old-style 'respawn process', not bare 'respawn' + $job[$idx] =~ s/^(\s*respawn\s+).*/$1$process/; + } else { + push @job, "respawn"; + push @job, "exec $process"; + } + + # Try to fix up effects of previous broken migrations + if (@job and $job[$#job] =~ /.*(.+?)exec (\1)$/) { + $job[$#job] = "exec $1"; + } + + } else { + push @job, "# $_ - getty"; + push @job, "#"; + push @job, "# Converted from /etc/inittab entry"; + push @job, ""; + + foreach my $rl (qw/2 3 4 5/) { + if ($rlevel =~ /$rl/) { + push @job, "start on runlevel $rl"; + } else { + push @job, "stop on runlevel $rl"; + } + } + push @job, ""; + + push @job, "stop on shutdown"; + push @job, ""; + + push @job, "respawn"; + push @job, "exec $process"; + } + + open JOB, ">/etc/event.d/.$_" + or warn "Unable to write to /etc/event.d/.$_: $!" and next; + print JOB map { "$_\n" } @job; + unless (close JOB) { + warn "Error while closing /etc/event.d/.$_: $!"; + unlink "/etc/event.d/.$_"; + next; + } + + unless (rename "/etc/event.d/.$_", "/etc/event.d/$_") { + warn "Unable to replace /etc/event.d/$_: $!"; + unlink "/etc/event.d/.$_"; + next; + } +} --- upstart-1.4.orig/debian/upstart.preinst +++ upstart-1.4/debian/upstart.preinst @@ -0,0 +1,54 @@ +#!/bin/sh -e +# This script can be called in the following ways: +# +# Before the package is installed: +# install +# +# Before removed package is upgraded: +# install +# +# Before the package is upgraded: +# upgrade +# +# +# If postrm fails during upgrade or fails on failed upgrade: +# abort-upgrade + + +# Prepare to remove a no-longer used conffile +prep_rm_conffile() +{ + CONFFILE="$1" + + if [ -e "$CONFFILE" ]; then + md5sum="`md5sum \"$CONFFILE\" | sed -e \"s/ .*//\"`" + old_md5sum="`sed -n -e \"/^Conffiles:/,/^[^ ]/{\\\\' $CONFFILE '{s/ obsolete$//;s/.* //;p}}\" /var/lib/dpkg/status`" + if [ "$md5sum" != "$old_md5sum" ]; then + echo "Obsolete conffile $CONFFILE has been modified by you, renaming to .dpkg-bak" + mv -f "$CONFFILE" "$CONFFILE".dpkg-bak + else + mv -f "$CONFFILE" "$CONFFILE".dpkg-obsolete + fi + fi +} + + +case "$1" in + install|upgrade) + # Upgrade from karmic development version + if dpkg --compare-versions "$2" lt-nl 0.6.3-7; then + prep_rm_conffile /etc/init/dbus-reconnect.conf + fi + ;; + + abort-upgrade) + ;; + + *) + echo "$0 called with unknown argument \`$1'" 1>&2 + exit 1 + ;; +esac + +#DEBHELPER# +exit 0 --- upstart-1.4.orig/debian/apparmor-profile-load +++ upstart-1.4/debian/apparmor-profile-load @@ -0,0 +1,27 @@ +#!/bin/sh +# apparmor-profile-load +# +# Helper for loading an AppArmor profile in pre-start scripts. + +[ -z "$1" ] && exit 1 # require a profile name + +[ -d /rofs/etc/apparmor.d ] && exit 0 # do not load if running liveCD + +profile=/etc/apparmor.d/"$1" +[ -e "$profile" ] || exit 0 # skip when missing profile + +module=/sys/module/apparmor +[ -d $module ] || exit 0 # do not load without AppArmor in kernel + +[ -x /sbin/apparmor_parser ] || exit 0 # do not load without parser + +aafs=/sys/kernel/security/apparmor +[ -d $aafs ] || exit 0 # do not load if unmounted +[ -w $aafs/.load ] || exit 1 # fail if cannot load profiles + +params=$module/parameters +[ -r $params/enabled ] || exit 0 # do not load if missing +read enabled < $params/enabled || exit 1 # if this fails, something went wrong +[ "$enabled" = "Y" ] || exit 0 # do not load if disabled + +/sbin/apparmor_parser -r -W "$profile" --- upstart-1.4.orig/debian/upstart.postrm +++ upstart-1.4/debian/upstart.postrm @@ -0,0 +1,81 @@ +#!/bin/sh -e +# This script can be called in the following ways: +# +# After the package was removed: +# remove +# +# After the package was purged: +# purge +# +# After the package was upgraded: +# upgrade +# if that fails: +# failed-upgrade +# +# +# After all of the packages files have been replaced: +# disappear +# +# +# If preinst fails during install: +# abort-install +# +# If preinst fails during upgrade of removed package: +# abort-install +# +# If preinst fails during upgrade: +# abort-upgrade + + +# Undo removal of a no-longer used conffile +undo_rm_conffile() +{ + CONFFILE="$1" + + if [ ! -e "$CONFFILE" ]; then + if [ -e "$CONFFILE".dpkg-bak ]; then + echo "Restoring modified conffile $CONFFILE" + mv -f "$CONFFILE".dpkg-bak "$CONFFILE" + elif [ -e "$CONFFILE".dpkg-obsolete ]; then + mv -f "$CONFFILE".dpkg-obsolete "$CONFFILE" + fi + fi +} + +# Finish removal of a no-longer used conffile +finish_rm_conffile() +{ + CONFFILE="$1" + + if [ -e "$CONFFILE".dpkg-bak ]; then + rm -f "$CONFFILE".dpkg-bak + fi +} + + +case "$1" in + remove) + ;; + + purge) + finish_rm_conffile /etc/init/dbus-reconnect.conf + ;; + + upgrade|failed-upgrade|disappear) + ;; + + abort-install|abort-upgrade) + # Abort upgrade from karmic development version + if dpkg --compare-versions "$2" lt-nl 0.6.3-7; then + undo_rm_conffile /etc/init/dbus-reconnect.conf + fi + ;; + + *) + echo "$0 called with unknown argument \`$1'" 1>&2 + exit 1 + ;; +esac + +#DEBHELPER# +exit 0 --- upstart-1.4.orig/debian/manpages/upstart-events.7 +++ upstart-1.4/debian/manpages/upstart-events.7 @@ -0,0 +1,287 @@ +'\" t +.TH upstart-events 7 2011-03-24 upstart +.\" +.SH NAME +upstart-events \- Well-known Upstart events summary +.\" +.SH Event Summary + +This manual page summarizes well-known events generated by the Upstart +.BR init (8) +daemon. +It is not an exhaustive list of all possible events, but rather details +a standard set of events expected to be generated on any Ubuntu system +running Upstart. + +The primary table, \fBTable 1\fP, encodes the well-known events, along +with the type of each event (listed in \fBTable 2\fP), the emitter of +the event (see \fBTable 3\fP) and the approximate time at which the +event could be generated. Additionally, the \fINote\fP column indexes +into \fBTable 4\fP for further details on a particular event. + +The \fIRef\fP (Reference) column is used to refer to individual +events succinctly in the \fITime\fP column. + +Note that the \(aq<\(aq and \(aq>\(aq characters in the \fITime\fP column denote +that the event in the \fIEvent\fP column occurs respectively before or +after the event specified in the \fITime\fP column (for example, the +\fBmounting\fP(7) event occurs "at some time" after the \fBstartup\fP(7) +event, and the \fBvirtual\-filesystems\fP(7) event occurs after the last +\fBmounted\fP(7) event relating to a virtual filesystem has been emitted). + +For further details on events, consult the manual pages and the job +configuration files, usually located in \fI/etc/init\fP. +.\" + +.\" Flush-left to allow table to be viewed on 80-col display without +.\" wrapping. +.nr old_po .po +.nr old_in .in +.po 0 +.in 0 +.sp 1 +\fBTable 1: Well-Known Event Summary.\fP +.TS +box, tab (@); +c | c | c | c | c | c +c | l | c | c | l | c. +Ref@Event@Type@Emit@Time@Note += + @\fBall\-swaps\fP@S@M@> (5)@ + @\fBcontrol\-alt\-delete\fP(7)@S@A@> (5)@A + @dbus\-activation@S@B@> D\-Bus client request@ + @deconfiguring\-networking@H@V@< non-local IFs down@P + @desktop\-session\-start@H@D@> \fBX\fP(7) session created@B + @desktop\-shutdown@H@D@> \fBX\fP(7) session ended@O + @device\-not\-ready@H@M@> (2)@N + @drm\-device\-added@S@U@> (5)@C + @\fBfilesystem\fP@S@M@After last (1)@D + @graphics\-device\-added@S@U@> (5)@C + @\fBkeyboard\-request\fP(7)@S@A@> (5)@E + @\fBlocal\-filesystems\fP(7)@S@M@> (6)@ + @login\-session\-start@H@D@< DM running@F +1@\fBmounted\fP(7)@H@M@> associated (2)@G +2@\fBmounting\fP(7)@H@M@> (5)@H +3@net\-device\-added@S@U@> (5)@C + @net\-device\-changed@S@U@> (5)@C + @net\-device\-down@S@F@< (4)@C +4@net\-device\-removed@S@U@> (5)@C + @net\-device\-up@S@F,N@> (3)@C + @\fBpower\-\%status\-\%changed\fP(7)@S@I@> (5)@I + @\fBremote\-\%filesystems\fP(7)@S@M@> (6)@ + @\fBrunlevel\fP(7)@M@T@> (5)@ + @\fBsocket\fP(7)@S@S@> socket connection@ +5@\fBstartup\fP(7)@S@I@Boot@J + @\fBstarted\fP(7)@S@I@> job started@K + @\fBstarting\fP(7)@H@I@< job starts@K + @static\-network\-up@S@I@> last static IF up@ + @\fBstopped\fP(7)@S@I@> job stopped@K + @\fBstopping\fP(7)@H@I@< job stops@K + @T{ +unmounted\-\:remote\-\:filesystems +T}@H@V@T{ +> last remote FS unmounted +T}@L +6@\fBvirtual\-\:filesystems\fP(7)@S@M@> last virtual FS (1)@M +.TE +.po \n[old_po] +.in \n[old_in] +.P +Key: + \(aqDM\(aq is an abbreviation for Display Manager. + \(aqFS\(aq is an abbreviation for filesystem. + \(aqIF\(aq is an abbreviation for Network Interface. + +.\" +.P +.sp 1 +.nr old_po .po +.nr old_in .in +.po 0 +.in 0 +\fBTable 2: Event Types.\fP +.TS +box, tab (@); +c | l |l +c | l |l. +Ref@Event Type@Notes += +H@Hook@T{ +Blocking. Waits for events that \fBstart on\fP or \fBstop on\fP this +event. +T} +M@Method@Blocking task. +S@Signal@Non-blocking. +.TE +.po \n[old_po] +.in \n[old_in] + +.\" +.P +.nr old_po .po +.nr old_in .in +.po 0 +.in 0 +.sp 1 +\fBTable 3: Event Emitters.\fP +.TS +box, tab (@); +c | l |l +c | l |l. +Ref@Emitter@Notes += +A@System Administrator (initiator)@Technically emitted by init(8). +B@\fBdbus\-daemon\fP(1)@Run with "\fI\-\-activation=upstart"\fP +D@Display Manager@e.g. lightdm/gdm/kdm/xdm. +F@\fBifup\fP(8) or \fBifdown\fP(8)@See \fI/etc/network/\fP. +I@\fBinit\fP(8)@ +M@\fBmountall\fP(8)@ +N@network\-interface job@ +S@\fBupstart\-socket\-bridge\fP(8)@ +T@\fBtelinit\fP(8), \fBshutdown\fP(8)@ +U@\fBupstart\-udev\-bridge\fP(8)@ +V@System V init system@ +.TE +.po \n[old_po] +.in \n[old_in] + +.\" +.P +.nr old_po .po +.nr old_in .in +.po 0 +.in 0 +\fBTable 4: Event Summary Notes.\fP +.TS +box, tab (@); +c | l +c | l. +Note@Detail += +A@T{ +Requires administrator to press Control-Alt-Delete key +combination on the console. +T} +B@Event generated when user performs graphical login. +C@T{ +These are specific examples. \fBupstart\-udev\-bridge\fP(8) will emit +events which match the pattern, "\fIS\fP\-device\-\fIA\fP" where +\(aqS\(aq is the udev \fIsubsystem\fP and \(aqA\(aq is the udev \fIaction\fP. See +\fBudev\fP(7) and for further details. If you have sysfs +mounted, you can look in \fI/sys/class/\fP for possible values for subsystem. +T} +D@Note this is in the singular - there is no \(aqfilesystems\(aq event. +E@T{ +Emitted when administrator presses Alt-UpArrow key combination on +the console. +T} +F@T{ +Denotes Display Manager running (about to be displayed), but no users +logged in yet. +T} +G@Generated for each mount that completes successfully. +H@T{ +Emitted when mount attempt for single entry from \fBfstab\fP(5) +for any filesystem type is about to begin. +T} +I@Emitted when Upstart receives the SIGPWR signal. +J@Initial event. +K@T{ +Although the events are emmitted by \fBinit\fP(8), the instigator may be +\fBinitctl\fP(8) if a System Administrator has manually started or +stopped a job. +T} +L@\fI/etc/init/umountnfs.sh\fP. +M@Emitted when all virtual filesystems (such as \fI/proc\fR) mounted. +N@T{ +Emitted when the \fI\-\-dev\-wait\-time\fP timeout is exceeded for +\fBmountall\fP(8). This defaults to 30 seconds. +T} +O@T{ +Emitted when the \fIX\fP(7) display manager exits at shutdown or reboot, to +hand off to the shutdown splash manager. +T} +P@T{ +Emitted by /etc/init.d/networking just prior to stopping all non-local +network interfaces. +T} +.TE +.po \n[old_po] +.in \n[old_in] + +.SH Job lifecycle +.\" +.SS Starting a Job +.nr step 1 1 +.IP \n[step] 3 +Upstart emits the \fBstarting\fP(7) event denoting the job is +"about to start". The \fBstarting\fP(7) event completes. +.IP \n+[step] 3 +If the \fBpre\-start\fP stanza exists, the pre\-start process is +spawned. +.IP \n+[step] 3 +Upstart spawns the main process. +.sp +It then ascertains the \fIfinal\fP PID for the job which may be a +descendent of the immediate child process if \fBexpect fork\fP or +\fBexpect daemon\fP has been specified. +.IP \n+[step] 3 +If the \fBpost\-start\fP stanza exists, the post\-start process is +spawned. +.IP \n+[step] 3 +Upstart emits the \fBstarted\fP(7) event. +.sp 1 +For services, when this event completes the main process will now be fully +running. If the job refers to a task, it will now have completed. + +.SS Stopping a Job + +.nr step 1 1 +.IP \n[step] 3 +If the \fBpre\-stop\fP stanza exists, the pre\-stop process is +spawned. +.IP \n+[step] 3 +The main process is stopped: +.RS +.nr step2 1 1 +.af step2 i +.IP \n[step2] 3 +The SIGTERM signal is sent to the main process. +.sp 1 +See \fBsignal\fP(7). +.IP \n+[step2] 3 +Upstart waits for up to "kill timeout" seconds (default 5 seconds) for +the process to end. +.IP \n+[step2] 3 +If the process is still running after the timeout, a SIGKILL is sent to the process. +.RE +.IP \n+[step] 3 +Upstart emits the \fBstopping\fP(7) event. +.IP \n+[step] 3 +If the \fBpost\-stop\fP stanza exists, the post\-stop process is +spawned. +.IP \n+[step] 3 +Upstart emits the \fBstopped\fP(7) event. +.sp 1 +When this event completes, the job is fully stopped. + +.SH AUTHOR +Manual page written by James Hunt +.RB < james.hunt@ubuntu.com > +.\" +.SH REPORTING BUGS +Report bugs at +.RB < https://launchpad.net/ubuntu/+source/upstart/+bugs > +.\" +.SH COPYRIGHT +Copyright \(co 2011 Canonical Ltd. +.br +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +.\" +.SH SEE ALSO +.BR init (5) +.BR init (8) +.BR initctl (8) +.BR mountall (8) +.BR telinit (8) --- upstart-1.4.orig/debian/conf/tty5.conf +++ upstart-1.4/debian/conf/tty5.conf @@ -0,0 +1,10 @@ +# tty5 - getty +# +# This service maintains a getty on tty5 from the point the system is +# started until it is shut down again. + +start on runlevel [23] +stop on runlevel [!23] + +respawn +exec /sbin/getty -8 38400 tty5 --- upstart-1.4.orig/debian/conf/tty6.conf +++ upstart-1.4/debian/conf/tty6.conf @@ -0,0 +1,10 @@ +# tty6 - getty +# +# This service maintains a getty on tty6 from the point the system is +# started until it is shut down again. + +start on runlevel [23] +stop on runlevel [!23] + +respawn +exec /sbin/getty -8 38400 tty6 --- upstart-1.4.orig/debian/conf/failsafe.conf +++ upstart-1.4/debian/conf/failsafe.conf @@ -0,0 +1,42 @@ +# failsafe + +description "Failsafe Boot Delay" +author "Clint Byrum " + +start on filesystem and net-device-up IFACE=lo +stop on static-network-up or runlevel + +console output + +script + # Determine if plymouth is available + if [ -x /bin/plymouth ] && /bin/plymouth --ping ; then + PLYMOUTH=/bin/plymouth + else + PLYMOUTH=":" + fi + + # The point here is to wait for 2 minutes before forcibly booting + # the system. Anything that is in an "or" condition with 'started + # failsafe' in rc-sysinit deserves consideration for mentioning in + # these messages. currently only static-network-up counts for that. + + sleep 20 + + # Plymouth errors should not stop the script because we *must* reach + # the end of this script to avoid letting the system spin forever + # waiting on it to start. + $PLYMOUTH message --text="Waiting for network configuration..." || : + sleep 40 + + $PLYMOUTH message --text="Waiting up to 60 more seconds for network configuration..." || : + sleep 59 + $PLYMOUTH message --text="Booting system without full network configuration..." || : + + # give user 1 second to see this message since plymouth will go + # away as soon as failsafe starts. + sleep 1 + exec initctl emit --no-wait failsafe-boot +end script + +post-start exec logger -t 'failsafe' -p daemon.warning "Failsafe of 120 seconds reached." --- upstart-1.4.orig/debian/conf/tty2.conf +++ upstart-1.4/debian/conf/tty2.conf @@ -0,0 +1,10 @@ +# tty2 - getty +# +# This service maintains a getty on tty2 from the point the system is +# started until it is shut down again. + +start on runlevel [23] +stop on runlevel [!23] + +respawn +exec /sbin/getty -8 38400 tty2 --- upstart-1.4.orig/debian/conf/tty1.conf +++ upstart-1.4/debian/conf/tty1.conf @@ -0,0 +1,10 @@ +# tty1 - getty +# +# This service maintains a getty on tty1 from the point the system is +# started until it is shut down again. + +start on stopped rc RUNLEVEL=[2345] +stop on runlevel [!2345] + +respawn +exec /sbin/getty -8 38400 tty1 --- upstart-1.4.orig/debian/conf/tty3.conf +++ upstart-1.4/debian/conf/tty3.conf @@ -0,0 +1,10 @@ +# tty3 - getty +# +# This service maintains a getty on tty3 from the point the system is +# started until it is shut down again. + +start on runlevel [23] +stop on runlevel [!23] + +respawn +exec /sbin/getty -8 38400 tty3 --- upstart-1.4.orig/debian/conf/tty4.conf +++ upstart-1.4/debian/conf/tty4.conf @@ -0,0 +1,10 @@ +# tty4 - getty +# +# This service maintains a getty on tty4 from the point the system is +# started until it is shut down again. + +start on runlevel [23] +stop on runlevel [!23] + +respawn +exec /sbin/getty -8 38400 tty4 --- upstart-1.4.orig/debian/conf/rcS.conf +++ upstart-1.4/debian/conf/rcS.conf @@ -0,0 +1,28 @@ +# rcS - System V single-user mode compatibility +# +# This task handles the old System V-style single-user mode, this is +# distinct from the other runlevels since running the rc script would +# be bad. + +description "System V single-user mode compatibility" +author "Scott James Remnant " + +start on runlevel S +stop on runlevel [!S] + +console owner +script + exec /sbin/sulogin +end script + +post-stop script + # Don't switch runlevels if we were stopped by an event, since that + # means we're already switching runlevels + if [ -n "${UPSTART_STOP_EVENTS}" ] + then + exit 0 + fi + + # Switch, passing a magic flag + start --no-wait rc-sysinit FROM_SINGLE_USER_MODE=y +end script --- upstart-1.4.orig/debian/conf/wait-for-state.conf +++ upstart-1.4/debian/conf/wait-for-state.conf @@ -0,0 +1,60 @@ +author "Clint Byrum " +description "Waiting for state" + +task +normal exit 2 + +stop on started $WAIT_FOR or stopped $WAIT_FOR + +# These are all arguments for use influencing how this job waits +env TIMEOUT=30 +env MANUAL_OVERRIDE="N" +env WAIT_FOREVER="N" +env WAIT_STATE="started" +env TARGET_GOAL="start" + +# Required args w/ no sensible default, the tests at the beginning of +# the script are just to guard against WAITER="" or WAIT_FOR="", as the +# instance line will fail if they are not set, since they have no env +instance $WAITER$WAIT_FOR + +script + test -n "$WAIT_FOR" || exit 1 + test -n "$WAITER" || exit 1 + + # We don't want to override the manual stanza + # XXX: initctl show-config should share manual w/ us too + case $MANUAL_OVERRIDE in + N|n|0) + if grep -q "^\s*manual\s*$" /etc/init/$WAIT_FOR.conf ; then + exit 0 + fi + ;; + esac + + if [ "$WAIT_STATE" = "stopped" ] ; then + TARGET_GOAL="stop" + fi + + # Already running/stopped? + status $WAIT_FOR | grep -q "$TARGET_GOAL/$WAIT_STATE" && exit 0 + + # Give it a push + $TARGET_GOAL $WAIT_FOR || : + + # upstart will kill this shell on started/stopped $WAIT_FOR + while sleep $TIMEOUT ; do + case $WAIT_FOREVER in + N|n|0) + exit 100 + ;; + Y|y|1) + ;; + *) + exit 1 + ;; + esac + done + # Very strange, sleep returned non-zero? + exit 1 +end script --- upstart-1.4.orig/debian/source/format +++ upstart-1.4/debian/source/format @@ -0,0 +1 @@ +1.0 --- upstart-1.4.orig/dbus/Upstart.conf +++ upstart-1.4/dbus/Upstart.conf @@ -9,14 +9,12 @@ - - - + + + + + + + + + + + + + + + + + + --- upstart-1.4.orig/extra/upstart-udev-bridge.c +++ upstart-1.4/extra/upstart-udev-bridge.c @@ -282,21 +282,24 @@ for (struct udev_list_entry *list_entry = udev_device_get_properties_list_entry (udev_device); list_entry != NULL; list_entry = udev_list_entry_get_next (list_entry)) { - nih_local char *key = NULL; + nih_local char *udev_name = NULL; + nih_local char *udev_value = NULL; nih_local char *var = NULL; - key = copy_string (NULL, udev_list_entry_get_name (list_entry)); - if (! strcmp (key, "DEVPATH")) + udev_name = copy_string (NULL, udev_list_entry_get_name (list_entry)); + + if (! strcmp (udev_name, "DEVPATH")) continue; - if (! strcmp (key, "DEVNAME")) + if (! strcmp (udev_name, "DEVNAME")) continue; - if (! strcmp (key, "SUBSYSTEM")) + if (! strcmp (udev_name, "SUBSYSTEM")) continue; - if (! strcmp (key, "ACTION")) + if (! strcmp (udev_name, "ACTION")) continue; - var = NIH_MUST (nih_sprintf (NULL, "%s=%s", key, - copy_string (NULL, udev_list_entry_get_value (list_entry)))); + udev_value = copy_string (NULL, udev_list_entry_get_value (list_entry)); + + var = NIH_MUST (nih_sprintf (NULL, "%s=%s", udev_name, udev_value)); NIH_MUST (nih_str_array_addp (&env, NULL, &env_len, var)); } @@ -411,6 +414,10 @@ if (i != j) nih_debug ("removed unexpected bytes from udev message data"); - /* If substitutions were necessary, shrink the string */ - return i == j ? cleaned : nih_realloc (cleaned, parent, j + 1); + /* Note that strictly we should realloc the string if + * bogus bytes were found (since it will now be shorter). + * However, since all the strings are short (and short-lived) we + * do not do this to avoid the associated overhead. + */ + return cleaned; } --- upstart-1.4.orig/extra/conf/upstart-udev-bridge.conf +++ upstart-1.4/extra/conf/upstart-udev-bridge.conf @@ -5,7 +5,13 @@ description "Bridge udev events into upstart" -emits *-device-* +# From upstart-udev-bridge itself +emits *-device-added +emits *-device-removed +emits *-device-changed +# From http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/libudev-udev-device.html +emits *-device-online +emits *-device-offline start on starting udev stop on stopped udev --- upstart-1.4.orig/extra/man/upstart-udev-bridge.8 +++ upstart-1.4/extra/man/upstart-udev-bridge.8 @@ -51,7 +51,7 @@ .I without specifying this option but with the -.B --debug +.B \-\-debug option. .\" .TP --- upstart-1.4.orig/contrib/vim/syntax/upstart.vim +++ upstart-1.4/contrib/vim/syntax/upstart.vim @@ -2,9 +2,9 @@ " Language: Upstart job files " Maintainer: Michael Biebl " James Hunt -" Last Change: 2011 Oct 27 +" Last Change: 2011 Dec 15 " License: GPL v2 -" Version: 0.4 +" Version: 0.5 " Remark: Syntax highlighting for Upstart (init(8)) job files. " " It is inspired by the initng syntax file and includes sh.vim to do the @@ -33,7 +33,7 @@ " one argument syn keyword upstartStatement description author version instance expect syn keyword upstartStatement pid kill normal console env exit export -syn keyword upstartStatement umask nice oom chroot chdir exec setiud setgid +syn keyword upstartStatement umask nice oom chroot chdir exec setuid setgid " two arguments syn keyword upstartStatement limit @@ -78,19 +78,30 @@ " D-Bus syn match upstartEvent /dbus-activation/ -" Display Manager (ie gdm) +" Display Manager (ie gdm/lightdm) syn match upstartEvent /desktop-session-start/ syn match upstartEvent /login-session-start/ +syn match upstartEvent /desktop-shutdown/ " mountall syn keyword upstartEvent filesystem syn keyword upstartEvent mounted syn keyword upstartEvent mounting +syn match upstartEvent /device-not-ready/ syn match upstartEvent /\(\\|\\|\\)-filesystems/ syn match upstartEvent /all-swaps/ " upstart-udev-bridge and ifup/down syn match upstartEvent /\<\i\{-1,}-device-\(\\|\\|\\|\\)/ +syn match upstartEvent /static-network-up/ + +" SysV handling +syn match upstartEvent /unmounted-remote-filesystems/ +syn match upstartEvent /deconfiguring-networking/ + +" misc +syn match upstartEvent /failsafe-boot/ +syn match upstartEvent /recovery/ " upstart-socket-bridge syn keyword upstartEvent socket