Marc Deslauriers has proposed merging lp:~mdeslaur/upstart/apparmor-support 
into lp:upstart.

Requested reviews:
  Upstart Reviewers (upstart-reviewers)

For more details, see:
https://code.launchpad.net/~mdeslaur/upstart/apparmor-support/+merge/164169

This merge request adds native AppArmor support in Upstart by introducing a new 
process type that can be shared with other Mandatory Access Control frameworks.

The new AppArmor stanzas allow specifying an AppArmor profile to load at job 
start, and a profile to switch to before running the main process. One 
interesting use case for switching AppArmor profiles is to confine applications 
started with jobs in User Session mode without relying on automatic path 
attachment.

If the running kernel doesn't have AppArmor enabled, or if the AppArmor tools 
aren't installed, the stanzas are simply ignored, allowing package maintainers 
to include AppArmor profiles and stanzas in their packages that will work on 
both distros that enable AppArmor by default, and distros that don't.
-- 
https://code.launchpad.net/~mdeslaur/upstart/apparmor-support/+merge/164169
Your team Upstart Reviewers is requested to review the proposed merge of 
lp:~mdeslaur/upstart/apparmor-support into lp:upstart.
=== modified file 'ChangeLog'
--- ChangeLog	2013-05-07 08:59:05 +0000
+++ ChangeLog	2013-05-16 11:57:33 +0000
@@ -1,3 +1,28 @@
+2013-05-15  Marc Deslauriers  <[email protected]>
+
+	* init/apparmor.[ch]: AppArmor profile helper.
+	* init/Makefile.am: Added AppArmor profile helper.
+	* init/errors.h: Added SECURITY_ERROR.
+	* init/job.c:
+	  - Added new JOB_SECURITY state and PROCESS_SECURITY process.
+	  - Fix job_deserialise() for new PROCESS_SECURITY process.
+	* init/job.h: Added new JOB_SECURITY state.
+	* init/job_class.[ch]: Added apparmor_switch to hold the new
+	  "apparmor switch" stanza.
+	* init/job_process.c:
+	  - Switch to new AppArmor profile.
+	  - Handle PROCESS_SECURITY process.
+	* init/job_process.h: Added JOB_PROCESS_ERROR_SECURITY.
+	* init/man/init.5: Document new AppArmor stanzas.
+	* init/parse_job.c: Parse new "apparmor" stanzas.
+	* init/process.[ch]: Add PROCESS_SECURITY.
+	* init/tests/test_job.c: Add new tests, and adjust existing ones.
+	* init/tests/test_job_class.c: Added apparmor_switch.
+	* init/tests/test_parse_job.c: Test new AppArmor stanza parsing.
+	* init/tests/test_process.c: Added PROCESS_SECURITY tests.
+	* init/tests/test_state.c: Test apparmor_switch and
+	  PROCESS_SECURITY.
+
 2013-05-07  James Hunt  <[email protected]>
 
 	* util/man/shutdown.8: Specify default action is to bring system

=== modified file 'init/Makefile.am'
--- init/Makefile.am	2013-04-17 09:41:42 +0000
+++ init/Makefile.am	2013-05-16 11:57:33 +0000
@@ -59,7 +59,8 @@
 	control.c control.h \
 	xdg.c xdg.h \
 	quiesce.c quiesce.h \
-	errors.h
+	errors.h \
+	apparmor.c apparmor.h
 nodist_init_SOURCES = \
 	$(com_ubuntu_Upstart_OUTPUTS) \
 	$(com_ubuntu_Upstart_Job_OUTPUTS) \
@@ -186,7 +187,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -200,7 +201,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -214,7 +215,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -228,7 +229,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -242,7 +243,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -256,7 +257,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -270,7 +271,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -284,7 +285,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -298,7 +299,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -312,7 +313,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -326,7 +327,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -345,7 +346,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -359,7 +360,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \
@@ -380,7 +381,7 @@
 	system.o environ.o process.o \
 	job_class.o job_process.o job.o event.o event_operator.o blocked.o \
 	parse_job.o parse_conf.o conf.o control.o quiesce.o \
-	session.o log.o state.o xdg.o \
+	session.o log.o state.o xdg.o apparmor.o \
 	com.ubuntu.Upstart.o \
 	com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
 	$(NIH_LIBS) \

=== added file 'init/apparmor.c'
--- init/apparmor.c	1970-01-01 00:00:00 +0000
+++ init/apparmor.c	2013-05-16 11:57:33 +0000
@@ -0,0 +1,123 @@
+/* upstart
+ *
+ * apparmor.c - handle AppArmor profiles
+ *
+ * Copyright © 2013 Canonical Ltd.
+ * Author: Marc Deslauriers <[email protected]>.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <nih/signal.h>
+#include <nih/string.h>
+
+#include "apparmor.h"
+
+/**
+ * apparmor_switch:
+ * @job: job switching apparmor profile
+ *
+ * This function switches to an apparmor profile using the class details in
+ * @job
+ *
+ * Returns: zero on success, -1 on error
+ **/
+int
+apparmor_switch (Job *job)
+{
+	nih_local char *filename = NULL;
+	FILE           *f;
+	JobClass       *class;
+
+	nih_assert (job != NULL);
+	nih_assert (job->class != NULL);
+
+	class = job->class;
+
+	nih_assert (class->apparmor_switch != NULL);
+
+	/* Silently fail if AppArmor isn't enabled. */
+	if (! apparmor_available())
+		return 0;
+
+	filename = nih_sprintf (NULL, "/proc/%d/attr/exec", getpid());
+
+	if (! filename)
+		return -1;
+
+	f = fopen (filename, "w");
+
+	if (! f)
+		return -1;
+
+	fprintf (f, "exec %s\n", class->apparmor_switch);
+
+	if (fclose (f))
+		return -1;
+
+	return 0;
+}
+
+/**
+ * apparmor_available:
+ *
+ * This function checks to see if AppArmor is available and enabled
+ *
+ * Returns: TRUE if AppArmor is available, FALSE if it isn't
+ **/
+int
+apparmor_available (void)
+{
+	struct stat     statbuf;
+	FILE           *f;
+	int            value = 0;
+
+	/* Do not load if AppArmor is disabled.
+	 */
+	f = fopen ("/sys/module/apparmor/parameters/enabled", "r");
+
+	if (! f)
+		return FALSE;
+
+	value = fgetc (f);
+
+	if (fclose (f))
+		return FALSE;
+
+	if (value != 'Y')
+		return FALSE;
+
+	/* Do not load if AppArmor parser isn't available.
+	 */
+	if (stat (APPARMOR_PARSER, &statbuf) == 0) {
+		if(! (S_ISREG(statbuf.st_mode) && statbuf.st_mode & S_IXUSR))
+			return FALSE;
+	} else {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+

=== added file 'init/apparmor.h'
--- init/apparmor.h	1970-01-01 00:00:00 +0000
+++ init/apparmor.h	2013-05-16 11:57:33 +0000
@@ -0,0 +1,52 @@
+/* upstart
+ *
+ * Copyright © 2013 Canonical Ltd.
+ * Author: Marc Deslauriers <[email protected]>.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INIT_APPARMOR_H
+#define INIT_APPARMOR_H
+
+#include "job.h"
+
+/**
+ * APPARMOR_PARSER:
+ *
+ * Location of apparmor_parser binary
+ *
+ **/
+#define APPARMOR_PARSER "/sbin/apparmor_parser"
+
+/**
+ * APPARMOR_PARSER_OPTS:
+ *
+ * apparmor_parser options
+ *
+ **/
+#define APPARMOR_PARSER_OPTS "-r -W"
+
+
+NIH_BEGIN_EXTERN
+
+int    apparmor_switch (Job *job)
+	__attribute__ ((warn_unused_result));
+
+int    apparmor_available (void)
+	__attribute__ ((warn_unused_result));
+
+NIH_END_EXTERN
+
+#endif /* INIT_APPARMOR_H */

=== modified file 'init/errors.h'
--- init/errors.h	2012-09-09 21:38:59 +0000
+++ init/errors.h	2013-05-16 11:57:33 +0000
@@ -54,6 +54,9 @@
 
 	/* Errors while handling control requests */
 	CONTROL_NAME_TAKEN,
+
+	/* Errors while handling security profiles */
+	SECURITY_ERROR,
 };
 
 /* Error strings for defined messages */

=== modified file 'init/job.c'
--- init/job.c	2013-02-27 11:46:04 +0000
+++ init/job.c	2013-05-16 11:57:33 +0000
@@ -56,6 +56,7 @@
 #include "control.h"
 #include "parse_job.h"
 #include "state.h"
+#include "apparmor.h"
 
 #include "com.ubuntu.Upstart.Job.h"
 #include "com.ubuntu.Upstart.Instance.h"
@@ -92,6 +93,8 @@
 job_deserialise_kill_timer (json_object *json)
 	__attribute__ ((warn_unused_result));
 
+extern int           user_mode;
+
 /**
  * job_new:
  * @class: class of job,
@@ -397,9 +400,26 @@
 			job->blocker = job_emit_event (job);
 
 			break;
+		case JOB_SECURITY:
+			nih_assert (job->goal == JOB_START);
+			nih_assert (old_state == JOB_STARTING);
+
+			if (job->class->process[PROCESS_SECURITY]
+			    && apparmor_available()
+			    && user_mode == FALSE) {
+				if (job_process_run (job, PROCESS_SECURITY) < 0) {
+					job_failed (job, PROCESS_SECURITY, -1);
+					job_change_goal (job, JOB_STOP);
+					state = job_next_state (job);
+				}
+			} else {
+				state = job_next_state (job);
+			}
+
+			break;
 		case JOB_PRE_START:
 			nih_assert (job->goal == JOB_START);
-			nih_assert (old_state == JOB_STARTING);
+			nih_assert (old_state == JOB_SECURITY);
 
 			if (job->class->process[PROCESS_PRE_START]) {
 				if (job_process_run (job, PROCESS_PRE_START) < 0) {
@@ -480,6 +500,7 @@
 		case JOB_STOPPING:
 			nih_assert ((old_state == JOB_STARTING)
 				    || (old_state == JOB_PRE_START)
+				    || (old_state == JOB_SECURITY)
 				    || (old_state == JOB_SPAWNED)
 				    || (old_state == JOB_POST_START)
 				    || (old_state == JOB_RUNNING)
@@ -598,6 +619,15 @@
 		case JOB_STOP:
 			return JOB_STOPPING;
 		case JOB_START:
+			return JOB_SECURITY;
+		default:
+			nih_assert_not_reached ();
+		}
+	case JOB_SECURITY:
+		switch (job->goal) {
+		case JOB_STOP:
+			return JOB_STOPPING;
+		case JOB_START:
 			return JOB_PRE_START;
 		default:
 			nih_assert_not_reached ();
@@ -1072,6 +1102,8 @@
 		return N_("waiting");
 	case JOB_STARTING:
 		return N_("starting");
+	case JOB_SECURITY:
+		return N_("security");
 	case JOB_PRE_START:
 		return N_("pre-start");
 	case JOB_SPAWNED:
@@ -1110,6 +1142,8 @@
 		return JOB_WAITING;
 	} else if (! strcmp (state, "starting")) {
 		return JOB_STARTING;
+	} else if (! strcmp (state, "security")) {
+		return JOB_SECURITY;
 	} else if (! strcmp (state, "pre-start")) {
 		return JOB_PRE_START;
 	} else if (! strcmp (state, "spawned")) {
@@ -1926,8 +1960,21 @@
 	if (ret < 0)
 		goto error;
 
-	if (len != PROCESS_LAST)
+	/* If we are missing one, we're probably importing from a
+	 * previous version that didn't include PROCESS_SECURITY.
+	 * Simply add the missing one.
+	 */
+	if (len == PROCESS_LAST - 1) {
+		job->pid = nih_realloc (job->pid, job, sizeof (pid_t) * PROCESS_LAST);
+
+		if (! job->pid)
+			goto error;
+
+		job->pid[PROCESS_LAST - 1] = 0;
+
+	} else if (len != PROCESS_LAST) {
 		goto error;
+	}
 
 	if (! state_get_json_int_var_to_obj (json, job, trace_forks))
 			goto error;
@@ -1949,13 +1996,23 @@
 		json_object  *json_log;
 
 		json_log = json_object_array_get_idx (json_logs, process);
-		if (! json_log)
-			goto error;
 
-		/* NULL if there was no log configured, or we failed to
-		 * deserialise it; either way, this should be non-fatal.
-		 */
-		job->log[process] = log_deserialise (job->log, json_log);
+		if (json_log) {
+			/* NULL if there was no log configured, or we failed to
+			 * deserialise it; either way, this should be non-fatal.
+			 */
+			job->log[process] = log_deserialise (job->log, json_log);
+		} else {
+			/* If we are missing one, we're probably importing from a
+			 * previous version that didn't include PROCESS_SECURITY.
+			 * Simply ignore the missing one.
+			 */
+			if (process == PROCESS_LAST - 1) {
+				job->log[process] = NULL;
+			} else {
+				goto error;
+			}
+		}
 	}
 
 	return job;
@@ -2066,6 +2123,7 @@
 {
 	state_enum_to_str (JOB_WAITING, state);
 	state_enum_to_str (JOB_STARTING, state);
+	state_enum_to_str (JOB_SECURITY, state);
 	state_enum_to_str (JOB_PRE_START, state);
 	state_enum_to_str (JOB_SPAWNED, state);
 	state_enum_to_str (JOB_POST_START, state);
@@ -2092,6 +2150,7 @@
 {
 	state_str_to_enum (JOB_WAITING, state);
 	state_str_to_enum (JOB_STARTING, state);
+	state_str_to_enum (JOB_SECURITY, state);
 	state_str_to_enum (JOB_PRE_START, state);
 	state_str_to_enum (JOB_SPAWNED, state);
 	state_str_to_enum (JOB_POST_START, state);

=== modified file 'init/job.h'
--- init/job.h	2013-02-27 11:46:04 +0000
+++ init/job.h	2013-05-16 11:57:33 +0000
@@ -66,6 +66,7 @@
 typedef enum job_state {
 	JOB_WAITING,
 	JOB_STARTING,
+	JOB_SECURITY,
 	JOB_PRE_START,
 	JOB_SPAWNED,
 	JOB_POST_START,

=== modified file 'init/job_class.c'
--- init/job_class.c	2013-02-25 09:38:55 +0000
+++ init/job_class.c	2013-05-16 11:57:33 +0000
@@ -362,6 +362,8 @@
 
 	class->usage = NULL;
 
+	class->apparmor_switch = NULL;
+
 	return class;
 
 error:
@@ -1873,6 +1875,9 @@
 	if (! state_set_json_string_var_from_obj (json, class, usage))
 		goto error;
 
+	if (! state_set_json_string_var_from_obj (json, class, apparmor_switch))
+		goto error;
+
 	return json;
 
 error:
@@ -2109,6 +2114,14 @@
 	if (! state_get_json_string_var_to_obj (json, class, usage))
 		goto error;
 
+	/* If we are missing this, we're probably importing from a
+	 * previous version that didn't include PROCESS_SECURITY.
+	 */
+	if (json_object_object_get (json, "apparmor_switch")) {
+		if (! state_get_json_string_var_to_obj (json, class, apparmor_switch))
+			goto error;
+	}
+
 	json_normalexit = json_object_object_get (json, "normalexit");
 	if (! json_normalexit)
 		goto error;

=== modified file 'init/job_class.h'
--- init/job_class.h	2013-02-27 11:46:04 +0000
+++ init/job_class.h	2013-05-16 11:57:33 +0000
@@ -164,6 +164,7 @@
  * @setgid: group name to drop to before starting process,
  * @deleted: whether job should be deleted when finished.
  * @usage: usage text - how to control job
+ * @apparmor_switch: AppArmor profile to switch to before starting job
  *
  * This structure holds the configuration of a known task or service that
  * should be tracked by the init daemon; as tasks and services are
@@ -220,6 +221,8 @@
 	int             debug;
 
 	char           *usage;
+
+	char	       *apparmor_switch;
 } JobClass;
 
 

=== modified file 'init/job_process.c'
--- init/job_process.c	2013-03-28 17:07:54 +0000
+++ init/job_process.c	2013-05-16 11:57:33 +0000
@@ -67,6 +67,7 @@
 #include "errors.h"
 #include "control.h"
 #include "xdg.h"
+#include "apparmor.h"
 
 
 /**
@@ -701,184 +702,196 @@
 		close (pty_slave);
 	}
 
-	/* Set resource limits for the process, skipping over any that
-	 * aren't set in the job class such that they inherit from
-	 * ourselves (and we inherit from kernel defaults).
+	/* Switch to the specified AppArmor profile, but only for the main
+	   process, so we don't confine the pre- and post- processes.
 	 */
-	for (i = 0; i < RLIMIT_NLIMITS; i++) {
-		if (! class->limits[i])
-			continue;
-
-		if (setrlimit (i, class->limits[i]) < 0) {
+	if ((class->apparmor_switch) && (process == PROCESS_MAIN)) {
+		if (apparmor_switch (job) < 0) {
+			nih_error_raise_system ();
+			job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SECURITY, 0);
+		}
+	}
+
+	if (process != PROCESS_SECURITY) {
+		/* Set resource limits for the process, skipping over any that
+		 * aren't set in the job class such that they inherit from
+		 * ourselves (and we inherit from kernel defaults).
+		 */
+		for (i = 0; i < RLIMIT_NLIMITS; i++) {
+			if (! class->limits[i])
+				continue;
+
+			if (setrlimit (i, class->limits[i]) < 0) {
+				nih_error_raise_system ();
+				job_process_error_abort (fds[1],
+							 JOB_PROCESS_ERROR_RLIMIT, i);
+			}
+		}
+
+		/* Set the file mode creation mask; this is one of the few operations
+		 * that can never fail.
+		 */
+		umask (class->umask);
+
+		/* Adjust the process priority ("nice level").
+		 */
+		if (class->nice != JOB_NICE_INVALID &&
+		    setpriority (PRIO_PROCESS, 0, class->nice) < 0) {
 			nih_error_raise_system ();
 			job_process_error_abort (fds[1],
-						 JOB_PROCESS_ERROR_RLIMIT, i);
+						 JOB_PROCESS_ERROR_PRIORITY, 0);
 		}
-	}
-
-	/* Set the file mode creation mask; this is one of the few operations
-	 * that can never fail.
-	 */
-	umask (class->umask);
-
-	/* Adjust the process priority ("nice level").
-	 */
-	if (class->nice != JOB_NICE_INVALID &&
-	    setpriority (PRIO_PROCESS, 0, class->nice) < 0) {
-		nih_error_raise_system ();
-		job_process_error_abort (fds[1],
-					 JOB_PROCESS_ERROR_PRIORITY, 0);
-	}
-
-	/* Adjust the process OOM killer priority.
-	 */
-	if (class->oom_score_adj != JOB_DEFAULT_OOM_SCORE_ADJ) {
-		int oom_value;
-		snprintf (filename, sizeof (filename),
-			  "/proc/%d/oom_score_adj", getpid ());
-		oom_value = class->oom_score_adj;
-		fd = fopen (filename, "w");
-		if ((! fd) && (errno == ENOENT)) {
+
+		/* Adjust the process OOM killer priority.
+		 */
+		if (class->oom_score_adj != JOB_DEFAULT_OOM_SCORE_ADJ) {
+			int oom_value;
 			snprintf (filename, sizeof (filename),
-				  "/proc/%d/oom_adj", getpid ());
-			oom_value = (class->oom_score_adj
-				     * ((class->oom_score_adj < 0) ? 17 : 15)) / 1000;
+				  "/proc/%d/oom_score_adj", getpid ());
+			oom_value = class->oom_score_adj;
 			fd = fopen (filename, "w");
-		}
-		if (! fd) {
-			nih_error_raise_system ();
-			job_process_error_abort (fds[1], JOB_PROCESS_ERROR_OOM_ADJ, 0);
-		} else {
-			fprintf (fd, "%d\n", oom_value);
-
-			if (fclose (fd)) {
+			if ((! fd) && (errno == ENOENT)) {
+				snprintf (filename, sizeof (filename),
+					  "/proc/%d/oom_adj", getpid ());
+				oom_value = (class->oom_score_adj
+					     * ((class->oom_score_adj < 0) ? 17 : 15)) / 1000;
+				fd = fopen (filename, "w");
+			}
+			if (! fd) {
 				nih_error_raise_system ();
 				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_OOM_ADJ, 0);
-			}
-		}
-	}
-
-	/* Handle changing a chroot session job prior to dealing with
-	 * the 'chroot' stanza.
-	 */
-	if (class->session && class->session->chroot) {
-		if (chroot (class->session->chroot) < 0) {
-			nih_error_raise_system ();
-			job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHROOT, 0);
-		}
-	}
-
-	/* Change the root directory, confining path resolution within it;
-	 * we do this before the working directory call so that is always
-	 * relative to the new root.
-	 */
-	if (class->chroot) {
-		if (chroot (class->chroot) < 0) {
-			nih_error_raise_system ();
-			job_process_error_abort (fds[1],
-						 JOB_PROCESS_ERROR_CHROOT, 0);
-		}
-	}
-
-	/* Change the working directory of the process, either to the one
-	 * configured in the job, or to the root directory of the filesystem
-	 * (or at least relative to the chroot).
-	 */
-	if (class->chdir || user_mode == FALSE) {
-		if (chdir (class->chdir ? class->chdir : "/") < 0) {
-			nih_error_raise_system ();
-			job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHDIR, 0);
-		}
-	}
-
-	/* Change the user and group of the process to the one
-	 * configured in the job. We must wait until now to lookup the
-	 * UID and GID from the names to accommodate both chroot
-	 * session jobs and jobs with a chroot stanza.
-	 */
-	if (class->setuid) {
-		/* Without resetting errno, it's impossible to
-		 * distinguish between a non-existent user and and
-		 * error during lookup */
-		errno = 0;
-		pwd = getpwnam (class->setuid);
-		if (! pwd) {
-			if (errno != 0) {
-				nih_error_raise_system ();
-				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETPWNAM, 0);
-			} else {
-				nih_error_raise (JOB_PROCESS_INVALID_SETUID,
-						 JOB_PROCESS_INVALID_SETUID_STR);
-				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_BAD_SETUID, 0);
-			}
-		}
-
-		job_setuid = pwd->pw_uid;
-		/* This will be overridden if setgid is also set: */
-		job_setgid = pwd->pw_gid;
-	}
-
-	if (class->setgid) {
-		errno = 0;
-		grp = getgrnam (class->setgid);
-		if (! grp) {
-			if (errno != 0) {
-				nih_error_raise_system ();
-				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETGRNAM, 0);
-			} else {
-				nih_error_raise (JOB_PROCESS_INVALID_SETGID,
-						 JOB_PROCESS_INVALID_SETGID_STR);
-				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_BAD_SETGID, 0);
-			}
-		}
-
-		job_setgid = grp->gr_gid;
-	}
-
-	if (script_fd != -1 &&
-	    (job_setuid != (uid_t) -1 || job_setgid != (gid_t) -1) &&
-	    fchown (script_fd, job_setuid, job_setgid) < 0) {
-		nih_error_raise_system ();
-		job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHOWN, 0);
-	}
-
-	/* Make sure we always have the needed pwd and grp structs.
-	 * Then pass those to initgroups() to setup the user's group list.
-	 * Only do that if we're root as initgroups() won't work when non-root. */
-	if (geteuid () == 0) {
-		if (! pwd) {
-			pwd = getpwuid (geteuid ());
-			if (! pwd) {
-				nih_error_raise_system ();
-				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETPWUID, 0);
-			}
-		}
-
-		if (! grp) {
-			grp = getgrgid (getegid ());
-			if (! grp) {
-				nih_error_raise_system ();
-				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETGRGID, 0);
-			}
-		}
-
-		if (pwd && grp) {
-			if (initgroups (pwd->pw_name, grp->gr_gid) < 0) {
-				nih_error_raise_system ();
-				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_INITGROUPS, 0);
-			}
-		}
-	}
-
-	/* Start dropping privileges */
-	if (job_setgid != (gid_t) -1 && setgid (job_setgid) < 0) {
-		nih_error_raise_system ();
-		job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SETGID, 0);
-	}
-
-	if (job_setuid != (uid_t)-1 && setuid (job_setuid) < 0) {
-		nih_error_raise_system ();
-		job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SETUID, 0);
+			} else {
+				fprintf (fd, "%d\n", oom_value);
+
+				if (fclose (fd)) {
+					nih_error_raise_system ();
+					job_process_error_abort (fds[1], JOB_PROCESS_ERROR_OOM_ADJ, 0);
+				}
+			}
+		}
+
+		/* Handle changing a chroot session job prior to dealing with
+		 * the 'chroot' stanza.
+		 */
+		if (class->session && class->session->chroot) {
+			if (chroot (class->session->chroot) < 0) {
+				nih_error_raise_system ();
+				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHROOT, 0);
+			}
+		}
+
+		/* Change the root directory, confining path resolution within it;
+		 * we do this before the working directory call so that is always
+		 * relative to the new root.
+		 */
+		if (class->chroot) {
+			if (chroot (class->chroot) < 0) {
+				nih_error_raise_system ();
+				job_process_error_abort (fds[1],
+							 JOB_PROCESS_ERROR_CHROOT, 0);
+			}
+		}
+
+		/* Change the working directory of the process, either to the one
+		 * configured in the job, or to the root directory of the filesystem
+		 * (or at least relative to the chroot).
+		 */
+		if (class->chdir || user_mode == FALSE) {
+			if (chdir (class->chdir ? class->chdir : "/") < 0) {
+				nih_error_raise_system ();
+				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHDIR, 0);
+			}
+		}
+
+		/* Change the user and group of the process to the one
+		 * configured in the job. We must wait until now to lookup the
+		 * UID and GID from the names to accommodate both chroot
+		 * session jobs and jobs with a chroot stanza.
+		 */
+		if (class->setuid) {
+			/* Without resetting errno, it's impossible to
+			 * distinguish between a non-existent user and and
+			 * error during lookup */
+			errno = 0;
+			pwd = getpwnam (class->setuid);
+			if (! pwd) {
+				if (errno != 0) {
+					nih_error_raise_system ();
+					job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETPWNAM, 0);
+				} else {
+					nih_error_raise (JOB_PROCESS_INVALID_SETUID,
+							 JOB_PROCESS_INVALID_SETUID_STR);
+					job_process_error_abort (fds[1], JOB_PROCESS_ERROR_BAD_SETUID, 0);
+				}
+			}
+
+			job_setuid = pwd->pw_uid;
+			/* This will be overridden if setgid is also set: */
+			job_setgid = pwd->pw_gid;
+		}
+
+		if (class->setgid) {
+			errno = 0;
+			grp = getgrnam (class->setgid);
+			if (! grp) {
+				if (errno != 0) {
+					nih_error_raise_system ();
+					job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETGRNAM, 0);
+				} else {
+					nih_error_raise (JOB_PROCESS_INVALID_SETGID,
+							 JOB_PROCESS_INVALID_SETGID_STR);
+					job_process_error_abort (fds[1], JOB_PROCESS_ERROR_BAD_SETGID, 0);
+				}
+			}
+
+			job_setgid = grp->gr_gid;
+		}
+
+		if (script_fd != -1 &&
+		    (job_setuid != (uid_t) -1 || job_setgid != (gid_t) -1) &&
+		    fchown (script_fd, job_setuid, job_setgid) < 0) {
+			nih_error_raise_system ();
+			job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHOWN, 0);
+		}
+
+		/* Make sure we always have the needed pwd and grp structs.
+		 * Then pass those to initgroups() to setup the user's group list.
+		 * Only do that if we're root as initgroups() won't work when non-root. */
+		if (geteuid () == 0) {
+			if (! pwd) {
+				pwd = getpwuid (geteuid ());
+				if (! pwd) {
+					nih_error_raise_system ();
+					job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETPWUID, 0);
+				}
+			}
+
+			if (! grp) {
+				grp = getgrgid (getegid ());
+				if (! grp) {
+					nih_error_raise_system ();
+					job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETGRGID, 0);
+				}
+			}
+
+			if (pwd && grp) {
+				if (initgroups (pwd->pw_name, grp->gr_gid) < 0) {
+					nih_error_raise_system ();
+					job_process_error_abort (fds[1], JOB_PROCESS_ERROR_INITGROUPS, 0);
+				}
+			}
+		}
+
+		/* Start dropping privileges */
+		if (job_setgid != (gid_t) -1 && setgid (job_setgid) < 0) {
+			nih_error_raise_system ();
+			job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SETGID, 0);
+		}
+
+		if (job_setuid != (uid_t)-1 && setuid (job_setuid) < 0) {
+			nih_error_raise_system ();
+			job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SETUID, 0);
+		}
 	}
 
 	/* Reset all the signal handlers back to their default handling so
@@ -923,6 +936,7 @@
 	nih_assert_not_reached ();
 }
 
+
 /**
  * job_process_error_abort:
  * @fd: writing end of pipe,
@@ -1185,6 +1199,11 @@
 				  err, _("unable to initgroups: %s"),
 				  strerror (err->errnum)));
 		break;
+	case JOB_PROCESS_ERROR_SECURITY:
+		err->error.message = NIH_MUST (nih_sprintf (
+				  err, _("unable to switch security profile: %s"),
+				  strerror (err->errnum)));
+		break;
 	default:
 		nih_assert_not_reached ();
 	}
@@ -1640,6 +1659,17 @@
 		 */
 		stop = TRUE;
 		break;
+	case PROCESS_SECURITY:
+		nih_assert (job->state == JOB_SECURITY);
+
+		/* We should always fail the job if the security profile
+		 * failed to load
+		 */
+		if (status) {
+			failed = TRUE;
+			stop = TRUE;
+		}
+		break;
 	case PROCESS_PRE_START:
 		nih_assert (job->state == JOB_PRE_START);
 

=== modified file 'init/job_process.h'
--- init/job_process.h	2013-03-28 17:07:54 +0000
+++ init/job_process.h	2013-05-16 11:57:33 +0000
@@ -94,7 +94,8 @@
 	JOB_PROCESS_ERROR_SIGNAL,
 	JOB_PROCESS_ERROR_ALLOC,
 	JOB_PROCESS_ERROR_INITGROUPS,
-	JOB_PROCESS_ERROR_GETGRGID
+	JOB_PROCESS_ERROR_GETGRGID,
+	JOB_PROCESS_ERROR_SECURITY
 } JobProcessErrorType;
 
 /**

=== modified file 'init/man/init.5'
--- init/man/init.5	2013-03-25 12:55:08 +0000
+++ init/man/init.5	2013-05-16 11:57:33 +0000
@@ -916,6 +916,37 @@
 .P
 
 .\"
+.SS AppArmor support
+Upstart provides several stanzas for loading and switching to different
+AppArmor profiles. If AppArmor isn't enabled in the currently running
+kernel, the stanzas will be silently ignored.
+
+.TP
+.B apparmor load \fIPROFILE
+This stanza specifies an AppArmor profile to load into the Linux kernel at
+job start. The AppArmor profile will confine a main process automatically
+using path attachment, or manually by using the \fBapparmor switch\fP
+stanza.
+.I PROFILE
+must be an absolute path to a profile and a failure will occur if the file
+doesn't exist. This stanza has no effect in User Session mode.
+
+.nf
+apparmor load /etc/apparmor.d/usr.sbin.cupsd
+.fi
+.\"
+.TP
+.B apparmor switch \fINAME
+This stanza specifies the name of an AppArmor profile name to switch to
+before running the main process.
+.I NAME
+must be the name of a profile already loaded into the running Linux kernel,
+and will result in a failure if not available.
+
+.nf
+apparmor switch /usr/sbin/cupsd
+.fi
+.\"
 .SS Miscellaneous
 .TP
 .B kill signal \fISIGNAL
@@ -1073,3 +1104,4 @@
 .BR prctl (2)
 .BR pty (7)
 .BR sh (1)
+.BR apparmor (7)

=== modified file 'init/parse_job.c'
--- init/parse_job.c	2013-02-27 11:46:04 +0000
+++ init/parse_job.c	2013-05-16 11:57:33 +0000
@@ -45,6 +45,7 @@
 #include "event.h"
 #include "parse_job.h"
 #include "errors.h"
+#include "apparmor.h"
 
 
 /* Prototypes for static functions */
@@ -170,6 +171,11 @@
 			       size_t *pos, size_t *lineno)
 	__attribute__ ((warn_unused_result));
 
+static int stanza_apparmor    (JobClass *class, NihConfigStanza *stanza,
+			       const char *file, size_t len,
+			       size_t *pos, size_t *lineno)
+	__attribute__ ((warn_unused_result));
+
 static int stanza_respawn     (JobClass *class, NihConfigStanza *stanza,
 			       const char *file, size_t len,
 			       size_t *pos, size_t *lineno)
@@ -270,6 +276,7 @@
 	{ "debug",       (NihConfigHandler)stanza_debug       },
 	{ "manual",      (NihConfigHandler)stanza_manual      },
 	{ "usage",       (NihConfigHandler)stanza_usage       },
+	{ "apparmor",    (NihConfigHandler)stanza_apparmor    },
 
 	NIH_CONFIG_LAST
 };
@@ -1931,6 +1938,108 @@
 	return ret;
 }
 
+/**
+ * stanza_apparmor:
+ * @class: job class being parsed,
+ * @stanza: stanza found,
+ * @file: file or string to parse,
+ * @len: length of @file,
+ * @pos: offset within @file,
+ * @lineno: line number.
+ *
+ * Parse an apparmor stanza from @file, extracting a second-level stanza that
+ * states which value to set from its argument.
+ *
+ * Returns: zero on success, negative value on error.
+ **/
+static int
+stanza_apparmor (JobClass        *class,
+		 NihConfigStanza *stanza,
+		 const char      *file,
+		 size_t           len,
+		 size_t          *pos,
+		 size_t          *lineno)
+{
+	size_t          a_pos, a_lineno;
+	int             ret = -1;
+	nih_local char *arg = NULL;
+	Process        *process;
+
+	nih_assert (class != NULL);
+	nih_assert (stanza != NULL);
+	nih_assert (file != NULL);
+	nih_assert (pos != NULL);
+
+	a_pos = *pos;
+	a_lineno = (lineno ? *lineno : 1);
+
+	arg = nih_config_next_token (NULL, file, len, &a_pos, &a_lineno,
+				     NIH_CONFIG_CNLWS, FALSE);
+	if (! arg)
+		goto finish;
+
+	if (! strcmp (arg, "load")) {
+		nih_local char *aaarg = NULL;
+
+		/* Update error position to the load value */
+		*pos = a_pos;
+		if (lineno)
+			*lineno = a_lineno;
+
+		aaarg = nih_config_next_arg (NULL, file, len,
+					     &a_pos, &a_lineno);
+
+		if (! aaarg)
+			goto finish;
+
+		/* Allocate a new Process structure if we need to */
+		if (! class->process[PROCESS_SECURITY]) {
+			class->process[PROCESS_SECURITY] = process_new (class->process);
+			if (! class->process[PROCESS_SECURITY])
+				nih_return_system_error (-1);
+		}
+
+		process = class->process[PROCESS_SECURITY];
+
+		if (process->command)
+			nih_unref (process->command, process);
+
+		process->script = FALSE;
+		process->command = nih_sprintf (process, "%s %s %s",
+						APPARMOR_PARSER,
+						APPARMOR_PARSER_OPTS,
+						aaarg);
+
+		if (! process->command)
+			nih_return_system_error (-1);
+
+	} else if (! strcmp (arg, "switch")) {
+		/* Update error position to the switch value */
+		*pos = a_pos;
+		if (lineno)
+			*lineno = a_lineno;
+
+		class->apparmor_switch = nih_config_next_arg (class, file,
+							      len, &a_pos,
+							      &a_lineno);
+
+		if (! class->apparmor_switch)
+			goto finish;
+
+	} else {
+		nih_return_error (-1, NIH_CONFIG_UNKNOWN_STANZA,
+				  _(NIH_CONFIG_UNKNOWN_STANZA_STR));
+	}
+
+	ret = nih_config_skip_comment (file, len, &a_pos, &a_lineno);
+
+finish:
+	*pos = a_pos;
+	if (lineno)
+		*lineno = a_lineno;
+
+	return ret;
+}
 
 /**
  * stanza_respawn:

=== modified file 'init/process.c'
--- init/process.c	2012-11-14 14:47:19 +0000
+++ init/process.c	2013-05-16 11:57:33 +0000
@@ -86,6 +86,8 @@
 		return N_("pre-stop");
 	case PROCESS_POST_STOP:
 		return N_("post-stop");
+	case PROCESS_SECURITY:
+		return N_("security");
 	default:
 		return NULL;
 	}
@@ -114,6 +116,8 @@
 		return PROCESS_PRE_STOP;
 	} else if (! strcmp (process, "post-stop")) {
 		return PROCESS_POST_STOP;
+	} else if (! strcmp (process, "security")) {
+		return PROCESS_SECURITY;
 	} else {
 		return -1;
 	}
@@ -311,6 +315,7 @@
 	state_enum_to_str (PROCESS_POST_START, type);
 	state_enum_to_str (PROCESS_PRE_STOP, type);
 	state_enum_to_str (PROCESS_POST_STOP, type);
+	state_enum_to_str (PROCESS_SECURITY, type);
 
 	return NULL;
 }
@@ -335,6 +340,7 @@
 	state_str_to_enum (PROCESS_POST_START, type);
 	state_str_to_enum (PROCESS_PRE_STOP, type);
 	state_str_to_enum (PROCESS_POST_STOP, type);
+	state_str_to_enum (PROCESS_SECURITY, type);
 
 	return -1;
 }

=== modified file 'init/process.h'
--- init/process.h	2013-02-27 11:46:04 +0000
+++ init/process.h	2013-05-16 11:57:33 +0000
@@ -45,6 +45,7 @@
 	PROCESS_POST_START,
 	PROCESS_PRE_STOP,
 	PROCESS_POST_STOP,
+	PROCESS_SECURITY,
 	PROCESS_LAST,
 } ProcessType;
 

=== modified file 'init/tests/test_job.c'
--- init/tests/test_job.c	2012-09-13 16:20:10 +0000
+++ init/tests/test_job.c	2013-05-16 11:57:33 +0000
@@ -1047,7 +1047,7 @@
 		}
 
 		job->goal = JOB_START;
-		job->state = JOB_STARTING;
+		job->state = JOB_SECURITY;
 		job->pid[PROCESS_PRE_START] = 0;
 
 		job->blocker = NULL;
@@ -1113,7 +1113,7 @@
 		}
 
 		job->goal = JOB_START;
-		job->state = JOB_STARTING;
+		job->state = JOB_SECURITY;
 		job->pid[PROCESS_MAIN] = 0;
 
 		job->blocker = NULL;
@@ -1187,7 +1187,7 @@
 		}
 
 		job->goal = JOB_START;
-		job->state = JOB_STARTING;
+		job->state = JOB_SECURITY;
 		job->pid[PROCESS_PRE_START] = 0;
 
 		job->blocker = NULL;
@@ -4000,14 +4000,31 @@
 
 
 	/* Check that the next state if we're starting a starting job is
+	 * security.
+	 */
+	TEST_FEATURE ("with starting job and a goal of start");
+	job->goal = JOB_START;
+	job->state = JOB_STARTING;
+
+	TEST_EQ (job_next_state (job), JOB_SECURITY);
+
+	/* Check that the next state if we're starting a security job is
 	 * pre-start.
 	 */
-	TEST_FEATURE ("with starting job and a goal of start");
+	TEST_FEATURE ("with security job and a goal of start");
 	job->goal = JOB_START;
-	job->state = JOB_STARTING;
+	job->state = JOB_SECURITY;
 
 	TEST_EQ (job_next_state (job), JOB_PRE_START);
 
+	/* Check that the next state if we're stopping an security job is
+	 * stopping.
+	 */
+	TEST_FEATURE ("with security job and a goal of stop");
+	job->goal = JOB_STOP;
+	job->state = JOB_SECURITY;
+
+	TEST_EQ (job_next_state (job), JOB_STOPPING);
 
 	/* Check that the next state if we're stopping a pre-start job is
 	 * stopping.
@@ -5695,6 +5712,13 @@
 	TEST_EQ_STR (name, "starting");
 
 
+	/* Check that the JOB_SECURITY state returns the right string. */
+	TEST_FEATURE ("with security state");
+	name = job_state_name (JOB_SECURITY);
+
+	TEST_EQ_STR (name, "security");
+
+
 	/* Check that the JOB_PRE_START state returns the right string. */
 	TEST_FEATURE ("with pre-start state");
 	name = job_state_name (JOB_PRE_START);
@@ -5779,6 +5803,13 @@
 	TEST_EQ (state, JOB_STARTING);
 
 
+	/* Check that JOB_SECURITY is returned for the right string. */
+	TEST_FEATURE ("with security state");
+	state = job_state_from_name ("security");
+
+	TEST_EQ (state, JOB_SECURITY);
+
+
 	/* Check that JOB_PRE_START is returned for the right string. */
 	TEST_FEATURE ("with pre-start state");
 	state = job_state_from_name ("pre-start");

=== modified file 'init/tests/test_job_class.c'
--- init/tests/test_job_class.c	2013-01-10 17:01:56 +0000
+++ init/tests/test_job_class.c	2013-05-16 11:57:33 +0000
@@ -145,6 +145,8 @@
 		TEST_EQ_P (class->setuid, NULL);
 		TEST_EQ_P (class->setgid, NULL);
 
+		TEST_EQ_P (class->apparmor_switch, NULL);
+
 		TEST_FALSE (class->deleted);
 
 		nih_free (class);

=== modified file 'init/tests/test_parse_job.c'
--- init/tests/test_parse_job.c	2012-10-22 13:29:45 +0000
+++ init/tests/test_parse_job.c	2013-05-16 11:57:33 +0000
@@ -36,6 +36,7 @@
 #include "conf.h"
 #include "parse_job.h"
 #include "errors.h"
+#include "apparmor.h"
 
 
 void
@@ -467,6 +468,267 @@
 }
 
 void
+test_stanza_apparmor (void)
+{
+	JobClass *job;
+	Process  *process;
+	NihError *err;
+	size_t    pos, lineno;
+	char      buf[1024];
+
+	TEST_FUNCTION ("stanza_apparmor");
+
+
+	/* Check that an apparmor load stanza sets the process of the
+	 * job as a single string.
+	 */
+	TEST_FEATURE ("with load and profile");
+	strcpy (buf, "apparmor load /etc/apparmor.d/usr.sbin.cupsd\n");
+
+	/* TODO: investigate why we can't use TEST_ALLOC_FAIL here.
+	 * It fails when nih_sprintf() is used.
+	 */
+
+	pos = 0;
+	lineno = 1;
+	job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf),
+			 &pos, &lineno);
+
+	TEST_EQ (pos, strlen (buf));
+	TEST_EQ (lineno, 2);
+
+	TEST_ALLOC_SIZE (job, sizeof (JobClass));
+
+	process = job->process[PROCESS_SECURITY];
+	TEST_ALLOC_PARENT (process, job->process);
+	TEST_ALLOC_SIZE (process, sizeof (Process));
+	TEST_EQ (process->script, FALSE);
+	TEST_ALLOC_PARENT (process->command, process);
+	strcpy (buf, APPARMOR_PARSER);
+	strcat (buf, " ");
+	strcat (buf, APPARMOR_PARSER_OPTS);
+	strcat (buf, " /etc/apparmor.d/usr.sbin.cupsd");
+	TEST_EQ_STR (process->command, buf);
+
+	nih_free (job);
+
+
+	/* Check that the last of multiple apparmor load stanzas is used. */
+	TEST_FEATURE ("with multiple load");
+	strcpy (buf, "apparmor load /etc/apparmor.d/usr.sbin.rsyslogd\n");
+	strcat (buf, "apparmor load /etc/apparmor.d/usr.sbin.cupsd\n");
+
+	/* TODO: investigate why we can't use TEST_ALLOC_FAIL here.
+	 * It fails when nih_sprintf() is used.
+	 */
+
+	pos = 0;
+	lineno = 1;
+	job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf),
+			 &pos, &lineno);
+
+	TEST_EQ (pos, strlen (buf));
+	TEST_EQ (lineno, 3);
+
+	TEST_ALLOC_SIZE (job, sizeof (JobClass));
+
+	process = job->process[PROCESS_SECURITY];
+	TEST_ALLOC_PARENT (process, job->process);
+	TEST_ALLOC_SIZE (process, sizeof (Process));
+	TEST_EQ (process->script, FALSE);
+	TEST_ALLOC_PARENT (process->command, process);
+	strcpy (buf, APPARMOR_PARSER);
+	strcat (buf, " ");
+	strcat (buf, APPARMOR_PARSER_OPTS);
+	strcat (buf, " /etc/apparmor.d/usr.sbin.cupsd");
+	TEST_EQ_STR (process->command, buf);
+
+	nih_free (job);
+
+
+	/* Check that an apparmor load stanza without any arguments results
+	 * in a syntax error.
+	 */
+	TEST_FEATURE ("with load but no profile");
+	strcpy (buf, "apparmor load\n");
+
+	pos = 0;
+	lineno = 1;
+	job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
+
+	TEST_EQ_P (job, NULL);
+
+	err = nih_error_get ();
+	TEST_EQ (err->number, NIH_CONFIG_EXPECTED_TOKEN);
+	TEST_EQ (pos, 13);
+	TEST_EQ (lineno, 1);
+	nih_free (err);
+
+
+	/* Check that an apparmor load stanza with an extra argument
+	 * results in a syntax error.
+	 */
+	TEST_FEATURE ("with extra argument to load");
+	strcpy (buf, "apparmor load /etc/apparmor.d/usr.sbin.cupsd extra\n");
+
+	pos = 0;
+	lineno = 1;
+	job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
+
+	TEST_EQ_P (job, NULL);
+
+	err = nih_error_get ();
+	TEST_EQ (err->number, NIH_CONFIG_UNEXPECTED_TOKEN);
+	TEST_EQ (pos, 45);
+	TEST_EQ (lineno, 1);
+	nih_free (err);
+
+
+	/* Check that an apparmor stanza with an unknown second argument
+	 * results in a syntax error.
+	 */
+	TEST_FEATURE ("with unknown argument");
+	strcpy (buf, "apparmor foo\n");
+
+	pos = 0;
+	lineno = 1;
+	job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
+
+	TEST_EQ_P (job, NULL);
+
+	err = nih_error_get ();
+	TEST_EQ (err->number, NIH_CONFIG_UNKNOWN_STANZA);
+	TEST_EQ (pos, 9);
+	TEST_EQ (lineno, 1);
+	nih_free (err);
+
+
+	/* Check that an apparmor stanza with no second argument
+	 * results in a syntax error.
+	 */
+	TEST_FEATURE ("with missing argument");
+	strcpy (buf, "apparmor\n");
+
+	pos = 0;
+	lineno = 1;
+	job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
+
+	TEST_EQ_P (job, NULL);
+
+	err = nih_error_get ();
+	TEST_EQ (err->number, NIH_CONFIG_EXPECTED_TOKEN);
+	TEST_EQ (pos, 8);
+	TEST_EQ (lineno, 1);
+	nih_free (err);
+
+
+	/* Check that an apparmor switch stanza results in it
+	 * being stored in the job.
+	 */
+	TEST_FEATURE ("with switch and profile");
+	strcpy (buf, "apparmor switch /usr/sbin/cupsd\n");
+
+	TEST_ALLOC_FAIL {
+		pos = 0;
+		lineno = 1;
+		job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf),
+				 &pos, &lineno);
+
+		if (test_alloc_failed) {
+			TEST_EQ_P (job, NULL);
+
+			err = nih_error_get ();
+			TEST_EQ (err->number, ENOMEM);
+			nih_free (err);
+
+			continue;
+		}
+
+		TEST_EQ (pos, strlen (buf));
+		TEST_EQ (lineno, 2);
+
+		TEST_ALLOC_SIZE (job, sizeof (JobClass));
+
+		TEST_ALLOC_PARENT (job->apparmor_switch, job);
+		TEST_EQ_STR (job->apparmor_switch, "/usr/sbin/cupsd");
+
+		nih_free (job);
+	}
+
+
+	/* Check that the last of multiple apparmor switch stanzas is used. */
+	TEST_FEATURE ("with multiple apparmor switch stanzas");
+	strcpy (buf, "apparmor switch /usr/sbin/rsyslogd\n");
+	strcat (buf, "apparmor switch /usr/sbin/cupsd\n");
+
+	TEST_ALLOC_FAIL {
+		pos = 0;
+		lineno = 1;
+		job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf),
+				 &pos, &lineno);
+
+		if (test_alloc_failed) {
+			TEST_EQ_P (job, NULL);
+
+			err = nih_error_get ();
+			TEST_EQ (err->number, ENOMEM);
+			nih_free (err);
+
+			continue;
+		}
+
+		TEST_EQ (pos, strlen (buf));
+		TEST_EQ (lineno, 3);
+
+		TEST_ALLOC_SIZE (job, sizeof (JobClass));
+
+		TEST_ALLOC_PARENT (job->apparmor_switch, job);
+		TEST_EQ_STR (job->apparmor_switch, "/usr/sbin/cupsd");
+
+		nih_free (job);
+	}
+
+
+	/* Check that an apparmor switch stanza without a profile results in
+	 * a syntax error.
+	 */
+	TEST_FEATURE ("with switch and no profile");
+	strcpy (buf, "apparmor switch\n");
+
+	pos = 0;
+	lineno = 1;
+	job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
+
+	TEST_EQ_P (job, NULL);
+
+	err = nih_error_get ();
+	TEST_EQ (err->number, NIH_CONFIG_EXPECTED_TOKEN);
+	TEST_EQ (pos, 15);
+	TEST_EQ (lineno, 1);
+	nih_free (err);
+
+
+	/* Check that an apparmor switch stanza with an extra second argument
+	 * results in a syntax error.
+	 */
+	TEST_FEATURE ("with extra argument to switch");
+	strcpy (buf, "apparmor switch /usr/sbin/cupsd extra\n");
+
+	pos = 0;
+	lineno = 1;
+	job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
+
+	TEST_EQ_P (job, NULL);
+
+	err = nih_error_get ();
+	TEST_EQ (err->number, NIH_CONFIG_UNEXPECTED_TOKEN);
+	TEST_EQ (pos, 32);
+	TEST_EQ (lineno, 1);
+	nih_free (err);
+
+}
+
+void
 test_stanza_pre_start (void)
 {
 	JobClass *job;
@@ -8372,6 +8634,7 @@
 
 	test_stanza_exec ();
 	test_stanza_script ();
+	test_stanza_apparmor ();
 	test_stanza_pre_start ();
 	test_stanza_post_start ();
 	test_stanza_pre_stop ();

=== modified file 'init/tests/test_process.c'
--- init/tests/test_process.c	2011-05-15 12:53:17 +0000
+++ init/tests/test_process.c	2013-05-16 11:57:33 +0000
@@ -68,6 +68,13 @@
 	TEST_EQ_STR (name, "main");
 
 
+	/* Check that PROCESS_SECURITY returns the right string. */
+	TEST_FEATURE ("with security process");
+	name = process_name (PROCESS_SECURITY);
+
+	TEST_EQ_STR (name, "security");
+
+
 	/* Check that PROCESS_PRE_START returns the right string. */
 	TEST_FEATURE ("with pre-start process");
 	name = process_name (PROCESS_PRE_START);
@@ -117,6 +124,13 @@
 	TEST_EQ (process, PROCESS_MAIN);
 
 
+	/* Check that PROCESS_SECURITY is returned for the string. */
+	TEST_FEATURE ("with security process");
+	process = process_from_name ("security");
+
+	TEST_EQ (process, PROCESS_SECURITY);
+
+
 	/* Check that PROCESS_PRE_START is returned for the string. */
 	TEST_FEATURE ("with pre-start process");
 	process = process_from_name ("pre-start");

=== modified file 'init/tests/test_state.c'
--- init/tests/test_state.c	2013-02-27 11:46:04 +0000
+++ init/tests/test_state.c	2013-05-16 11:57:33 +0000
@@ -615,6 +615,8 @@
 	if (obj_string_check (a, b, usage))
 		goto fail;
 
+	if (obj_string_check (a, b, apparmor_switch))
+		goto fail;
 
 	return 0;
 
@@ -1013,6 +1015,12 @@
 	foo->process[PROCESS_MAIN]->command = NIH_MUST (nih_strdup (foo->process[PROCESS_MAIN],
 				"echo hello !£$%^&*()_+-={}:@~;'#<>?,./"));
 
+	foo->process[PROCESS_SECURITY] = process_new (foo->process);
+	TEST_NE_P (foo->process[PROCESS_SECURITY], NULL);
+	foo->process[PROCESS_SECURITY]->script = 0;
+	foo->process[PROCESS_SECURITY]->command = NIH_MUST (nih_strdup (foo->process[PROCESS_SECURITY],
+			"/bin/true"));
+
 	foo->process[PROCESS_PRE_START] = process_new (foo->process);
 	TEST_NE_P (foo->process[PROCESS_PRE_START], NULL);
 	foo->process[PROCESS_PRE_START]->script = 0;

-- 
upstart-devel mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/upstart-devel

Reply via email to